Edgewall Software

Changes between Version 3 and Version 4 of Data Storage


Ignore:
Timestamp:
Sep 23, 2005, 3:27:14 PM (19 years ago)
Author:
cmlenz
Comment:

Updated and enhanced to reflect SQL storage

Legend:

Unmodified
Added
Removed
Modified
  • Data Storage

    v3 v4  
    2020Other data such as compilation errors/warnings or style checks would consist of the message of the error or warning.
    2121
    22 == Database Layout ==
     22== Database Schema ==
    2323
    2424As Bitten should support the storage of arbritray metrics, the tables for storing this data need to be fairly generic. Versions of Bitten prior to [milestone:0.5] used a Berkeley DB XML backend to store reports, but for various reasons the report data is now mapped to tables in the relational database already provided by the Trac environment.
    2525
    26 A report is received in XML format from the build slave, for example:
     26As depicated in the following diagram, report data is stored in two tables in the database: `bitten_report` and `bitten_report_item`.
     27
     28[[Image(tables.png)]]
     29
     30There is one `bitten_report` record per report. This record associates the report with a step of a specific build (see ObjectModel), and stores the ''category'' and the ''generator'' of the report. The category can be seen as indicator of the schema with which the report data complies. For example, there's a category "test" for test results, and a category "coverage" for code coverage. Test results data may be generated by different recipe commands and tools, but as long as they comply with the schema of the "test" category, the data can be analyzed and visualized by the same generic components.
     31
     32The report data itself is stored in the `bitten_report_item` table. The basic assumption is that a report consists of a flat, unordered list of data points, each having a variable set of named properties. The names and values of these properties are storeds in the `name` and `value` columns of the `bitten_report_item` table, respectively. The `item` column simply contains a number that groups the individual properties into "report items".
     33
     34== Mapping XML Reports to Database Tables ==
     35
     36A report is received in XML format from the build slave. For example, a report generated by the `<python:unittest>` [wiki:RecipeCommands recipe command] might look like this:
    2737
    2838{{{
    2939#!xml
    30 <report type="unittest">
    31   <test duration="0.073" status="success" fixture="bitten.tests.model.BuildConfigTestCase" name="test_config_update_name" file="bitten/tests/model.py"/>
     40<report category="test" generator="http://bitten.cmlenz.net/tools/python#unittest">
     41  ...
     42  <test duration="0.073" status="success" fixture="bitten.tests.model.BuildConfigTestCase"
     43        name="test_config_update_name" file="bitten/tests/model.py">
     44    <stdout>Renaming build configuration</stdout>
     45  </test>
    3246  ...
    3347</report>
    3448}}}
    3549
     50For such a report, Bitten first inserts a record into the `bitten_report` table, taking the category and generator from the corresponding attributes of the `<report>` element. Next, a couple of records are inserted into the `bitten_report_item` table for every immediate child element of `<report>`; one for every attribute of the child element. In addition, the tag name is stored as a property named `type`.
     51
     52Children of the child element are also mapped to properties: here the tag name of the "grandchild" is used as the property name, and its´ body text is used as the property value.
     53
     54So the following snippets would be stored in exactly the same way:
     55
     56{{{
     57#!xml
     58  <test duration="0.073" status="success" fixture="bitten.tests.model.BuildConfigTestCase"
     59        name="test_config_update_name" file="bitten/tests/model.py"
     60        stdout="Renaming build configuration">
     61  </test>
     62}}}
     63{{{
     64#!xml
     65  <test>
     66    <duration>0.073</duration>
     67    <status>success</status>
     68    <fixture>bitten.tests.model.BuildConfigTestCase</fixture>
     69    <name>test_config_update_name</name>
     70    <file>bitten/tests/model.py</file>
     71    <stdout>Renaming build configuration</stdout>
     72  </test>
     73}}}
     74
     75These two methods can be mixed. When multiple child elements with the same tag name are encountered, only the last one is stored.
     76
     77The tables will contain the following data for this report:
     78
     79'''`bitten_report`''':
     80|| id   || build || step   || category || generator                                        ||
     81|| 123  || 456   || `foo`  || `test`   || `http://bitten.cmlenz.net/tools/python#unittest` ||
     82
     83'''`bitten_report_item`''':
     84|| report || item || name       || value                                     ||
     85|| 123    || 1    || `type`     || `test`                                    ||
     86|| 123    || 1    || `duration` || `0.073`                                   ||
     87|| 123    || 1    || `status`   || `success`                                 ||
     88|| 123    || 1    || `fixture`  || `bitten.tests.model.BuildConfigTestCase`  ||
     89|| 123    || 1    || `name`     || `test_config_update_name`                 ||
     90|| 123    || 1    || `file`     || `bitten/tests/model.py`                   ||
     91|| 123    || 1    || `stdout`   || `Renaming build configuration`            ||
     92
    3693== Querying the Report Store ==
    3794
    38 TBW
     95Querying the report data is not straight-forward due to the generic nature of the tables. This usually requires a number of self-joins.
     96
     97For example, the following query aggregates the number of failed/succeeded unit tests by fixture:
     98
     99{{{
     100#!sql
     101SELECT item_fixture.value AS fixture, item_file.value AS file,
     102       COUNT(item_success.value) AS num_success,
     103       COUNT(item_failure.value) AS num_failure,
     104       COUNT(item_error.value) AS num_error
     105FROM bitten_report AS report
     106 LEFT OUTER JOIN bitten_report_item AS item_fixture
     107  ON (item_fixture.report=report.id AND item_fixture.name='fixture')
     108 LEFT OUTER JOIN bitten_report_item AS item_file
     109  ON (item_file.report=report.id AND item_file.item=item_fixture.item AND
     110      item_file.name='file')
     111 LEFT OUTER JOIN bitten_report_item AS item_success
     112  ON (item_success.report=report.id AND item_success.item=item_fixture.item AND
     113      item_success.name='status' AND item_success.value='success')
     114 LEFT OUTER JOIN bitten_report_item AS item_failure
     115  ON (item_failure.report=report.id AND item_failure.item=item_fixture.item AND
     116      item_failure.name='status' AND item_failure.value='failure')
     117 LEFT OUTER JOIN bitten_report_item AS item_error
     118  ON (item_error.report=report.id AND item_error.item=item_fixture.item AND
     119      item_error.name='status' AND item_error.value='error')
     120WHERE category='test' AND build=%s
     121GROUP BY file, fixture ORDER BY fixture
     122}}}
     123
     124  ''(This code is from the [source://trunk/bitten/trac_ext/summarizers.py TestResultsSummarizer].)''