Home | History | Annotate | Line # | Download | only in store
      1 // Copyright 2011 Google Inc.
      2 // All rights reserved.
      3 //
      4 // Redistribution and use in source and binary forms, with or without
      5 // modification, are permitted provided that the following conditions are
      6 // met:
      7 //
      8 // * Redistributions of source code must retain the above copyright
      9 //   notice, this list of conditions and the following disclaimer.
     10 // * Redistributions in binary form must reproduce the above copyright
     11 //   notice, this list of conditions and the following disclaimer in the
     12 //   documentation and/or other materials provided with the distribution.
     13 // * Neither the name of Google Inc. nor the names of its contributors
     14 //   may be used to endorse or promote products derived from this software
     15 //   without specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 #include "store/transaction.hpp"
     30 
     31 extern "C" {
     32 #include <stdint.h>
     33 }
     34 
     35 #include <fstream>
     36 #include <map>
     37 #include <utility>
     38 
     39 #include "engine/action.hpp"
     40 #include "engine/context.hpp"
     41 #include "engine/test_result.hpp"
     42 #include "store/backend.hpp"
     43 #include "store/dbtypes.hpp"
     44 #include "store/exceptions.hpp"
     45 #include "utils/datetime.hpp"
     46 #include "utils/defs.hpp"
     47 #include "utils/format/macros.hpp"
     48 #include "utils/logging/macros.hpp"
     49 #include "utils/optional.ipp"
     50 #include "utils/sanity.hpp"
     51 #include "utils/stream.hpp"
     52 #include "utils/sqlite/database.hpp"
     53 #include "utils/sqlite/exceptions.hpp"
     54 #include "utils/sqlite/statement.ipp"
     55 #include "utils/sqlite/transaction.hpp"
     56 #include "utils/units.hpp"
     57 
     58 namespace datetime = utils::datetime;
     59 namespace fs = utils::fs;
     60 namespace sqlite = utils::sqlite;
     61 namespace units = utils::units;
     62 
     63 using utils::none;
     64 using utils::optional;
     65 
     66 
     67 namespace {
     68 
     69 
     70 /// Retrieves the environment variables of a context.
     71 ///
     72 /// \param db The SQLite database.
     73 /// \param context_id The identifier of the context.
     74 ///
     75 /// \return The environment variables of the specified context.
     76 ///
     77 /// \throw sqlite::error If there is a problem storing the variables.
     78 static std::map< std::string, std::string >
     79 get_env_vars(sqlite::database& db, const int64_t context_id)
     80 {
     81     std::map< std::string, std::string > env;
     82 
     83     sqlite::statement stmt = db.create_statement(
     84         "SELECT var_name, var_value FROM env_vars "
     85         "WHERE context_id == :context_id");
     86     stmt.bind(":context_id", context_id);
     87 
     88     while (stmt.step()) {
     89         const std::string name = stmt.safe_column_text("var_name");
     90         const std::string value = stmt.safe_column_text("var_value");
     91         env[name] = value;
     92     }
     93 
     94     return env;
     95 }
     96 
     97 
     98 /// Retrieves a metadata object.
     99 ///
    100 /// \param db The SQLite database.
    101 /// \param metadata_id The identifier of the metadata.
    102 ///
    103 /// \return A new metadata object.
    104 static engine::metadata
    105 get_metadata(sqlite::database& db, const int64_t metadata_id)
    106 {
    107     engine::metadata_builder builder;
    108 
    109     sqlite::statement stmt = db.create_statement(
    110         "SELECT * FROM metadatas WHERE metadata_id == :metadata_id");
    111     stmt.bind(":metadata_id", metadata_id);
    112     while (stmt.step()) {
    113         const std::string name = stmt.safe_column_text("property_name");
    114         const std::string value = stmt.safe_column_text("property_value");
    115         builder.set_string(name, value);
    116     }
    117 
    118     return builder.build();
    119 }
    120 
    121 
    122 /// Gets a file from the database.
    123 ///
    124 /// \param db The database to query the file from.
    125 /// \param file_id The identifier of the file to be queried.
    126 ///
    127 /// \return A textual representation of the file contents.
    128 ///
    129 /// \throw integrity_error If there is any problem in the loaded data or if the
    130 ///     file cannot be found.
    131 static std::string
    132 get_file(sqlite::database& db, const int64_t file_id)
    133 {
    134     sqlite::statement stmt = db.create_statement(
    135         "SELECT contents FROM files WHERE file_id == :file_id");
    136     stmt.bind(":file_id", file_id);
    137     if (!stmt.step())
    138         throw store::integrity_error(F("Cannot find referenced file %s") %
    139                                      file_id);
    140 
    141     try {
    142         const sqlite::blob raw_contents = stmt.safe_column_blob("contents");
    143         const std::string contents(
    144             static_cast< const char *>(raw_contents.memory), raw_contents.size);
    145 
    146         const bool more = stmt.step();
    147         INV(!more);
    148 
    149         return contents;
    150     } catch (const sqlite::error& e) {
    151         throw store::integrity_error(e.what());
    152     }
    153 }
    154 
    155 
    156 /// Gets all the test cases within a particular test program.
    157 ///
    158 /// \param db The database to query the information from.
    159 /// \param test_program_id The identifier of the test program whose test cases
    160 ///     to query.
    161 /// \param test_program The test program itself, needed to establish a binding
    162 ///     between the loaded test cases and the test program.
    163 /// \param interface The interface type of the test cases to be loaded.  This
    164 ///     assumes that all test cases within a test program share the same
    165 ///     interface, which is a pretty reasonable assumption.
    166 ///
    167 /// \return The collection of loaded test cases.
    168 ///
    169 /// \throw integrity_error If there is any problem in the loaded data.
    170 static engine::test_cases_vector
    171 get_test_cases(sqlite::database& db, const int64_t test_program_id,
    172                const engine::test_program& test_program,
    173                const std::string& interface)
    174 {
    175     engine::test_cases_vector test_cases;
    176 
    177     sqlite::statement stmt = db.create_statement(
    178         "SELECT name, metadata_id "
    179         "FROM test_cases WHERE test_program_id == :test_program_id");
    180     stmt.bind(":test_program_id", test_program_id);
    181     while (stmt.step()) {
    182         const std::string name = stmt.safe_column_text("name");
    183         const int64_t metadata_id = stmt.safe_column_int64("metadata_id");
    184 
    185         const engine::metadata metadata = get_metadata(db, metadata_id);
    186         engine::test_case_ptr test_case(
    187             new engine::test_case(interface, test_program, name, metadata));
    188         LD(F("Loaded test case '%s'") % test_case->name());
    189         test_cases.push_back(test_case);
    190     }
    191 
    192     return test_cases;
    193 }
    194 
    195 
    196 /// Retrieves a result from the database.
    197 ///
    198 /// \param stmt The statement with the data for the result to load.
    199 /// \param type_column The name of the column containing the type of the result.
    200 /// \param reason_column The name of the column containing the reason for the
    201 ///     result, if any.
    202 ///
    203 /// \return The loaded result.
    204 ///
    205 /// \throw integrity_error If the data in the database is invalid.
    206 static engine::test_result
    207 parse_result(sqlite::statement& stmt, const char* type_column,
    208              const char* reason_column)
    209 {
    210     using engine::test_result;
    211 
    212     try {
    213         const std::string type = stmt.safe_column_text(type_column);
    214         if (type == "passed") {
    215             if (stmt.column_type(stmt.column_id(reason_column)) !=
    216                 sqlite::type_null)
    217                 throw store::integrity_error("Result of type 'passed' has a "
    218                                              "non-NULL reason");
    219             return test_result(test_result::passed);
    220         } else if (type == "broken") {
    221             return test_result(test_result::broken,
    222                                stmt.safe_column_text(reason_column));
    223         } else if (type == "expected_failure") {
    224             return test_result(test_result::expected_failure,
    225                                stmt.safe_column_text(reason_column));
    226         } else if (type == "failed") {
    227             return test_result(test_result::failed,
    228                                stmt.safe_column_text(reason_column));
    229         } else if (type == "skipped") {
    230             return test_result(test_result::skipped,
    231                                stmt.safe_column_text(reason_column));
    232         } else {
    233             throw store::integrity_error(F("Unknown test result type %s") %
    234                                          type);
    235         }
    236     } catch (const sqlite::error& e) {
    237         throw store::integrity_error(e.what());
    238     }
    239 }
    240 
    241 
    242 /// Stores the environment variables of a context.
    243 ///
    244 /// \param db The SQLite database.
    245 /// \param context_id The identifier of the context.
    246 /// \param env The environment variables to store.
    247 ///
    248 /// \throw sqlite::error If there is a problem storing the variables.
    249 static void
    250 put_env_vars(sqlite::database& db, const int64_t context_id,
    251              const std::map< std::string, std::string >& env)
    252 {
    253     sqlite::statement stmt = db.create_statement(
    254         "INSERT INTO env_vars (context_id, var_name, var_value) "
    255         "VALUES (:context_id, :var_name, :var_value)");
    256     stmt.bind(":context_id", context_id);
    257     for (std::map< std::string, std::string >::const_iterator iter =
    258              env.begin(); iter != env.end(); iter++) {
    259         stmt.bind(":var_name", (*iter).first);
    260         stmt.bind(":var_value", (*iter).second);
    261         stmt.step_without_results();
    262         stmt.reset();
    263     }
    264 }
    265 
    266 
    267 /// Calculates the last rowid of a table.
    268 ///
    269 /// \param db The SQLite database.
    270 /// \param table Name of the table.
    271 ///
    272 /// \return The last rowid; 0 if the table is empty.
    273 static int64_t
    274 last_rowid(sqlite::database& db, const std::string& table)
    275 {
    276     sqlite::statement stmt = db.create_statement(
    277         F("SELECT MAX(ROWID) AS max_rowid FROM %s") % table);
    278     stmt.step();
    279     if (stmt.column_type(0) == sqlite::type_null) {
    280         return 0;
    281     } else {
    282         INV(stmt.column_type(0) == sqlite::type_integer);
    283         return stmt.column_int64(0);
    284     }
    285 }
    286 
    287 
    288 /// Stores a metadata object.
    289 ///
    290 /// \param db The database into which to store the information.
    291 /// \param md The metadata to store.
    292 ///
    293 /// \return The identifier of the new metadata object.
    294 static int64_t
    295 put_metadata(sqlite::database& db, const engine::metadata& md)
    296 {
    297     const engine::properties_map props = md.to_properties();
    298 
    299     const int64_t metadata_id = last_rowid(db, "metadatas");
    300 
    301     sqlite::statement stmt = db.create_statement(
    302         "INSERT INTO metadatas (metadata_id, property_name, property_value) "
    303         "VALUES (:metadata_id, :property_name, :property_value)");
    304     stmt.bind(":metadata_id", metadata_id);
    305 
    306     for (engine::properties_map::const_iterator iter = props.begin();
    307          iter != props.end(); ++iter) {
    308         stmt.bind(":property_name", (*iter).first);
    309         stmt.bind(":property_value", (*iter).second);
    310         stmt.step_without_results();
    311         stmt.reset();
    312     }
    313 
    314     return metadata_id;
    315 }
    316 
    317 
    318 /// Stores an arbitrary file into the database as a BLOB.
    319 ///
    320 /// \param db The database into which to store the file.
    321 /// \param path Path to the file to be stored.
    322 ///
    323 /// \return The identifier of the stored file, or none if the file was empty.
    324 ///
    325 /// \throw sqlite::error If there are problems writing to the database.
    326 static optional< int64_t >
    327 put_file(sqlite::database& db, const fs::path& path)
    328 {
    329     std::ifstream input(path.c_str());
    330     if (!input)
    331         throw store::error(F("Cannot open file %s") % path);
    332 
    333     try {
    334         if (utils::stream_length(input) == 0)
    335             return none;
    336     } catch (const std::runtime_error& e) {
    337         // Skipping empty files is an optimization.  If we fail to calculate the
    338         // size of the file, just ignore the problem.  If there are real issues
    339         // with the file, the read below will fail anyway.
    340         LD(F("Cannot determine if file is empty: %s") % e.what());
    341     }
    342 
    343     // TODO(jmmv): This will probably cause an unreasonable amount of memory
    344     // consumption if we decide to store arbitrary files in the database (other
    345     // than stdout or stderr).  Should this happen, we need to investigate a
    346     // better way to feel blobs into SQLite.
    347     const std::string contents = utils::read_stream(input);
    348 
    349     sqlite::statement stmt = db.create_statement(
    350         "INSERT INTO files (contents) VALUES (:contents)");
    351     stmt.bind(":contents", sqlite::blob(contents.c_str(), contents.length()));
    352     stmt.step_without_results();
    353 
    354     return optional< int64_t >(db.last_insert_rowid());
    355 }
    356 
    357 
    358 }  // anonymous namespace
    359 
    360 
    361 /// Loads a specific test program from the database.
    362 ///
    363 /// \param backend_ The store backend we are dealing with.
    364 /// \param id The identifier of the test program to load.
    365 ///
    366 /// \return The instantiated test program.
    367 ///
    368 /// \throw integrity_error If the data read from the database cannot be properly
    369 ///     interpreted.
    370 engine::test_program_ptr
    371 store::detail::get_test_program(backend& backend_, const int64_t id)
    372 {
    373     sqlite::database& db = backend_.database();
    374 
    375     engine::test_program_ptr test_program;
    376     sqlite::statement stmt = db.create_statement(
    377         "SELECT * FROM test_programs WHERE test_program_id == :id");
    378     stmt.bind(":id", id);
    379     stmt.step();
    380     const std::string interface = stmt.safe_column_text("interface");
    381     test_program.reset(new engine::test_program(
    382         interface,
    383         fs::path(stmt.safe_column_text("relative_path")),
    384         fs::path(stmt.safe_column_text("root")),
    385         stmt.safe_column_text("test_suite_name"),
    386         get_metadata(db, stmt.safe_column_int64("metadata_id"))));
    387     const bool more = stmt.step();
    388     INV(!more);
    389 
    390     LD(F("Loaded test program '%s'; getting test cases") %
    391        test_program->relative_path());
    392     test_program->set_test_cases(get_test_cases(db, id, *test_program,
    393                                                 interface));
    394     return test_program;
    395 }
    396 
    397 
    398 /// Internal implementation for a results iterator.
    399 struct store::results_iterator::impl {
    400     /// The store backend we are dealing with.
    401     store::backend _backend;
    402 
    403     /// The statement to iterate on.
    404     sqlite::statement _stmt;
    405 
    406     /// A cache for the last loaded test program.
    407     optional< std::pair< int64_t, engine::test_program_ptr > >
    408         _last_test_program;
    409 
    410     /// Whether the iterator is still valid or not.
    411     bool _valid;
    412 
    413     /// Constructor.
    414     impl(store::backend& backend_, const int64_t action_id_) :
    415         _backend(backend_),
    416         _stmt(backend_.database().create_statement(
    417             "SELECT test_programs.test_program_id, "
    418             "    test_programs.interface, "
    419             "    test_cases.test_case_id, test_cases.name, "
    420             "    test_results.result_type, test_results.result_reason, "
    421             "    test_results.start_time, test_results.end_time "
    422             "FROM test_programs "
    423             "    JOIN test_cases "
    424             "    ON test_programs.test_program_id = test_cases.test_program_id "
    425             "    JOIN test_results "
    426             "    ON test_cases.test_case_id = test_results.test_case_id "
    427             "WHERE test_programs.action_id == :action_id "
    428             "ORDER BY test_programs.absolute_path, test_cases.name"))
    429     {
    430         _stmt.bind(":action_id", action_id_);
    431         _valid = _stmt.step();
    432     }
    433 };
    434 
    435 
    436 /// Constructor.
    437 ///
    438 /// \param pimpl_ The internal implementation details of the iterator.
    439 store::results_iterator::results_iterator(
    440     std::shared_ptr< impl > pimpl_) :
    441     _pimpl(pimpl_)
    442 {
    443 }
    444 
    445 
    446 /// Destructor.
    447 store::results_iterator::~results_iterator(void)
    448 {
    449 }
    450 
    451 
    452 /// Moves the iterator forward by one result.
    453 ///
    454 /// \return The iterator itself.
    455 store::results_iterator&
    456 store::results_iterator::operator++(void)
    457 {
    458     _pimpl->_valid = _pimpl->_stmt.step();
    459     return *this;
    460 }
    461 
    462 
    463 /// Checks whether the iterator is still valid.
    464 ///
    465 /// \return True if there is more elements to iterate on, false otherwise.
    466 store::results_iterator::operator bool(void) const
    467 {
    468     return _pimpl->_valid;
    469 }
    470 
    471 
    472 /// Gets the test program this result belongs to.
    473 ///
    474 /// \return The representation of a test program.
    475 const engine::test_program_ptr
    476 store::results_iterator::test_program(void) const
    477 {
    478     const int64_t id = _pimpl->_stmt.safe_column_int64("test_program_id");
    479     if (!_pimpl->_last_test_program ||
    480         _pimpl->_last_test_program.get().first != id)
    481     {
    482         const engine::test_program_ptr tp = detail::get_test_program(
    483             _pimpl->_backend, id);
    484         _pimpl->_last_test_program = std::make_pair(id, tp);
    485     }
    486     return _pimpl->_last_test_program.get().second;
    487 }
    488 
    489 
    490 /// Gets the name of the test case pointed by the iterator.
    491 ///
    492 /// The caller can look up the test case data by using the find() method on the
    493 /// test program returned by test_program().
    494 ///
    495 /// \return A test case name, unique within the test program.
    496 std::string
    497 store::results_iterator::test_case_name(void) const
    498 {
    499     return _pimpl->_stmt.safe_column_text("name");
    500 }
    501 
    502 
    503 /// Gets the result of the test case pointed by the iterator.
    504 ///
    505 /// \return A test case result.
    506 engine::test_result
    507 store::results_iterator::result(void) const
    508 {
    509     return parse_result(_pimpl->_stmt, "result_type", "result_reason");
    510 }
    511 
    512 
    513 /// Gets the duration of the test case execution.
    514 ///
    515 /// \return A time delta representing the run time of the test case.
    516 datetime::delta
    517 store::results_iterator::duration(void) const
    518 {
    519     const datetime::timestamp start_time = column_timestamp(
    520         _pimpl->_stmt, "start_time");
    521     const datetime::timestamp end_time = column_timestamp(
    522         _pimpl->_stmt, "end_time");
    523     return end_time - start_time;
    524 }
    525 
    526 
    527 /// Gets a file from a test case.
    528 ///
    529 /// \param db The database to query the file from.
    530 /// \param test_case_id The identifier of the test case.
    531 /// \param filename The name of the file to be retrieved.
    532 ///
    533 /// \return A textual representation of the file contents.
    534 ///
    535 /// \throw integrity_error If there is any problem in the loaded data or if the
    536 ///     file cannot be found.
    537 static std::string
    538 get_test_case_file(sqlite::database& db, const int64_t test_case_id,
    539                    const char* filename)
    540 {
    541     sqlite::statement stmt = db.create_statement(
    542         "SELECT file_id FROM test_case_files "
    543         "WHERE test_case_id == :test_case_id AND file_name == :file_name");
    544     stmt.bind(":test_case_id", test_case_id);
    545     stmt.bind(":file_name", filename);
    546     if (stmt.step())
    547         return get_file(db, stmt.safe_column_int64("file_id"));
    548     else
    549         return "";
    550 }
    551 
    552 
    553 /// Gets the contents of stdout of a test case.
    554 ///
    555 /// \return A textual representation of the stdout contents of the test case.
    556 /// This may of course be empty if the test case didn't print anything.
    557 std::string
    558 store::results_iterator::stdout_contents(void) const
    559 {
    560     return get_test_case_file(_pimpl->_backend.database(),
    561                               _pimpl->_stmt.safe_column_int64("test_case_id"),
    562                               "__STDOUT__");
    563 }
    564 
    565 
    566 /// Gets the contents of stderr of a test case.
    567 ///
    568 /// \return A textual representation of the stderr contents of the test case.
    569 /// This may of course be empty if the test case didn't print anything.
    570 std::string
    571 store::results_iterator::stderr_contents(void) const
    572 {
    573     return get_test_case_file(_pimpl->_backend.database(),
    574                               _pimpl->_stmt.safe_column_int64("test_case_id"),
    575                               "__STDERR__");
    576 }
    577 
    578 
    579 /// Internal implementation for a store transaction.
    580 struct store::transaction::impl {
    581     /// The backend instance.
    582     store::backend& _backend;
    583 
    584 
    585     /// The SQLite database this transaction deals with.
    586     sqlite::database _db;
    587 
    588     /// The backing SQLite transaction.
    589     sqlite::transaction _tx;
    590 
    591     /// Opens a transaction.
    592     ///
    593     /// \param backend_ The backend this transaction is connected to.
    594     impl(backend& backend_) :
    595         _backend(backend_),
    596         _db(backend_.database()),
    597         _tx(backend_.database().begin_transaction())
    598     {
    599     }
    600 };
    601 
    602 
    603 /// Creates a new transaction.
    604 ///
    605 /// \param backend_ The backend this transaction belongs to.
    606 store::transaction::transaction(backend& backend_) :
    607     _pimpl(new impl(backend_))
    608 {
    609 }
    610 
    611 
    612 /// Destructor.
    613 store::transaction::~transaction(void)
    614 {
    615 }
    616 
    617 
    618 /// Commits the transaction.
    619 ///
    620 /// \throw error If there is any problem when talking to the database.
    621 void
    622 store::transaction::commit(void)
    623 {
    624     try {
    625         _pimpl->_tx.commit();
    626     } catch (const sqlite::error& e) {
    627         throw error(e.what());
    628     }
    629 }
    630 
    631 
    632 /// Rolls the transaction back.
    633 ///
    634 /// \throw error If there is any problem when talking to the database.
    635 void
    636 store::transaction::rollback(void)
    637 {
    638     try {
    639         _pimpl->_tx.rollback();
    640     } catch (const sqlite::error& e) {
    641         throw error(e.what());
    642     }
    643 }
    644 
    645 
    646 /// Retrieves an action from the database.
    647 ///
    648 /// \param action_id The identifier of the action to retrieve.
    649 ///
    650 /// \return The retrieved action.
    651 ///
    652 /// \throw error If there is a problem loading the action.
    653 engine::action
    654 store::transaction::get_action(const int64_t action_id)
    655 {
    656     try {
    657         sqlite::statement stmt = _pimpl->_db.create_statement(
    658             "SELECT context_id FROM actions WHERE action_id == :action_id");
    659         stmt.bind(":action_id", action_id);
    660         if (!stmt.step())
    661             throw error(F("Error loading action %s: does not exist") %
    662                         action_id);
    663 
    664         return engine::action(
    665             get_context(stmt.safe_column_int64("context_id")));
    666     } catch (const sqlite::error& e) {
    667         throw error(F("Error loading action %s: %s") % action_id % e.what());
    668     }
    669 }
    670 
    671 
    672 /// Creates a new iterator to scan the test results of an action.
    673 ///
    674 /// \param action_id The identifier of the action for which to get the results.
    675 ///
    676 /// \return The constructed iterator.
    677 ///
    678 /// \throw error If there is any problem constructing the iterator.
    679 store::results_iterator
    680 store::transaction::get_action_results(const int64_t action_id)
    681 {
    682     try {
    683         return results_iterator(std::shared_ptr< results_iterator::impl >(
    684            new results_iterator::impl(_pimpl->_backend, action_id)));
    685     } catch (const sqlite::error& e) {
    686         throw error(e.what());
    687     }
    688 }
    689 
    690 
    691 /// Retrieves the latest action from the database.
    692 ///
    693 /// \return The retrieved action.
    694 ///
    695 /// \throw error If there is a problem loading the action.
    696 std::pair< int64_t, engine::action >
    697 store::transaction::get_latest_action(void)
    698 {
    699     try {
    700         sqlite::statement stmt = _pimpl->_db.create_statement(
    701             "SELECT action_id, context_id FROM actions WHERE "
    702             "action_id == (SELECT max(action_id) FROM actions)");
    703         if (!stmt.step())
    704             throw error("No actions in the database");
    705 
    706         const int64_t action_id = stmt.safe_column_int64("action_id");
    707         const engine::context context = get_context(
    708             stmt.safe_column_int64("context_id"));
    709 
    710         return std::pair< int64_t, engine::action >(
    711             action_id, engine::action(context));
    712     } catch (const sqlite::error& e) {
    713         throw error(F("Error loading latest action: %s") % e.what());
    714     }
    715 }
    716 
    717 
    718 /// Retrieves an context from the database.
    719 ///
    720 /// \param context_id The identifier of the context to retrieve.
    721 ///
    722 /// \return The retrieved context.
    723 ///
    724 /// \throw error If there is a problem loading the context.
    725 engine::context
    726 store::transaction::get_context(const int64_t context_id)
    727 {
    728     try {
    729         sqlite::statement stmt = _pimpl->_db.create_statement(
    730             "SELECT cwd FROM contexts WHERE context_id == :context_id");
    731         stmt.bind(":context_id", context_id);
    732         if (!stmt.step())
    733             throw error(F("Error loading context %s: does not exist") %
    734                         context_id);
    735 
    736         return engine::context(fs::path(stmt.safe_column_text("cwd")),
    737                                get_env_vars(_pimpl->_db, context_id));
    738     } catch (const sqlite::error& e) {
    739         throw error(F("Error loading context %s: %s") % context_id % e.what());
    740     }
    741 }
    742 
    743 
    744 /// Puts an action into the database.
    745 ///
    746 /// \pre The action has not been put yet.
    747 /// \pre The dependent objects have already been put.
    748 /// \post The action is stored into the database with a new identifier.
    749 ///
    750 /// \param unused_action The action to put.
    751 /// \param context_id The identifier for the action's context.
    752 ///
    753 /// \return The identifier of the inserted action.
    754 ///
    755 /// \throw error If there is any problem when talking to the database.
    756 int64_t
    757 store::transaction::put_action(const engine::action& UTILS_UNUSED_PARAM(action),
    758                                const int64_t context_id)
    759 {
    760     try {
    761         sqlite::statement stmt = _pimpl->_db.create_statement(
    762             "INSERT INTO actions (context_id) VALUES (:context_id)");
    763         stmt.bind(":context_id", context_id);
    764         stmt.step_without_results();
    765         const int64_t action_id = _pimpl->_db.last_insert_rowid();
    766 
    767         return action_id;
    768     } catch (const sqlite::error& e) {
    769         throw error(e.what());
    770     }
    771 }
    772 
    773 
    774 /// Puts a context into the database.
    775 ///
    776 /// \pre The context has not been put yet.
    777 /// \post The context is stored into the database with a new identifier.
    778 ///
    779 /// \param context The context to put.
    780 ///
    781 /// \return The identifier of the inserted context.
    782 ///
    783 /// \throw error If there is any problem when talking to the database.
    784 int64_t
    785 store::transaction::put_context(const engine::context& context)
    786 {
    787     try {
    788         sqlite::statement stmt = _pimpl->_db.create_statement(
    789             "INSERT INTO contexts (cwd) VALUES (:cwd)");
    790         stmt.bind(":cwd", context.cwd().str());
    791         stmt.step_without_results();
    792         const int64_t context_id = _pimpl->_db.last_insert_rowid();
    793 
    794         put_env_vars(_pimpl->_db, context_id, context.env());
    795 
    796         return context_id;
    797     } catch (const sqlite::error& e) {
    798         throw error(e.what());
    799     }
    800 }
    801 
    802 
    803 /// Puts a test program into the database.
    804 ///
    805 /// \pre The test program has not been put yet.
    806 /// \post The test program is stored into the database with a new identifier.
    807 ///
    808 /// \param test_program The test program to put.
    809 /// \param action_id The action this test program belongs to.
    810 ///
    811 /// \return The identifier of the inserted test program.
    812 ///
    813 /// \throw error If there is any problem when talking to the database.
    814 int64_t
    815 store::transaction::put_test_program(const engine::test_program& test_program,
    816                                      const int64_t action_id)
    817 {
    818     try {
    819         const int64_t metadata_id = put_metadata(
    820             _pimpl->_db, test_program.get_metadata());
    821 
    822         sqlite::statement stmt = _pimpl->_db.create_statement(
    823             "INSERT INTO test_programs (action_id, absolute_path, "
    824             "                           root, relative_path, test_suite_name, "
    825             "                           metadata_id, interface) "
    826             "VALUES (:action_id, :absolute_path, :root, :relative_path, "
    827             "        :test_suite_name, :metadata_id, :interface)");
    828         stmt.bind(":action_id", action_id);
    829         stmt.bind(":absolute_path", test_program.absolute_path().str());
    830         // TODO(jmmv): The root is not necessarily absolute.  We need to ensure
    831         // that we can recover the absolute path of the test program.  Maybe we
    832         // need to change the test_program to always ensure root is absolute?
    833         stmt.bind(":root", test_program.root().str());
    834         stmt.bind(":relative_path", test_program.relative_path().str());
    835         stmt.bind(":test_suite_name", test_program.test_suite_name());
    836         stmt.bind(":metadata_id", metadata_id);
    837         stmt.bind(":interface", test_program.interface_name());
    838         stmt.step_without_results();
    839         return _pimpl->_db.last_insert_rowid();
    840     } catch (const sqlite::error& e) {
    841         throw error(e.what());
    842     }
    843 }
    844 
    845 
    846 /// Puts a test case into the database.
    847 ///
    848 /// \pre The test case has not been put yet.
    849 /// \post The test case is stored into the database with a new identifier.
    850 ///
    851 /// \param test_case The test case to put.
    852 /// \param test_program_id The test program this test case belongs to.
    853 ///
    854 /// \return The identifier of the inserted test case.
    855 ///
    856 /// \throw error If there is any problem when talking to the database.
    857 int64_t
    858 store::transaction::put_test_case(const engine::test_case& test_case,
    859                                   const int64_t test_program_id)
    860 {
    861     try {
    862         const int64_t metadata_id = put_metadata(
    863             _pimpl->_db, test_case.get_metadata());
    864 
    865         sqlite::statement stmt = _pimpl->_db.create_statement(
    866             "INSERT INTO test_cases (test_program_id, name, metadata_id) "
    867             "VALUES (:test_program_id, :name, :metadata_id)");
    868         stmt.bind(":test_program_id", test_program_id);
    869         stmt.bind(":name", test_case.name());
    870         stmt.bind(":metadata_id", metadata_id);
    871         stmt.step_without_results();
    872         return _pimpl->_db.last_insert_rowid();
    873     } catch (const sqlite::error& e) {
    874         throw error(e.what());
    875     }
    876 }
    877 
    878 
    879 /// Stores a file generated by a test case into the database as a BLOB.
    880 ///
    881 /// \param name The name of the file to store in the database.  This needs to be
    882 ///     unique per test case.  The caller is free to decide what names to use
    883 ///     for which files.  For example, it might make sense to always call
    884 ///     __STDOUT__ the stdout of the test case so that it is easy to locate.
    885 /// \param path The path to the file to be stored.
    886 /// \param test_case_id The identifier of the test case this file belongs to.
    887 ///
    888 /// \return The identifier of the stored file, or none if the file was empty.
    889 ///
    890 /// \throw store::error If there are problems writing to the database.
    891 optional< int64_t >
    892 store::transaction::put_test_case_file(const std::string& name,
    893                                        const fs::path& path,
    894                                        const int64_t test_case_id)
    895 {
    896     LD(F("Storing %s (%s) of test case %s") % name % path % test_case_id);
    897     try {
    898         const optional< int64_t > file_id = put_file(_pimpl->_db, path);
    899         if (!file_id) {
    900             LD("Not storing empty file");
    901             return none;
    902         }
    903 
    904         sqlite::statement stmt = _pimpl->_db.create_statement(
    905             "INSERT INTO test_case_files (test_case_id, file_name, file_id) "
    906             "VALUES (:test_case_id, :file_name, :file_id)");
    907         stmt.bind(":test_case_id", test_case_id);
    908         stmt.bind(":file_name", name);
    909         stmt.bind(":file_id", file_id.get());
    910         stmt.step_without_results();
    911 
    912         return optional< int64_t >(_pimpl->_db.last_insert_rowid());
    913     } catch (const sqlite::error& e) {
    914         throw error(e.what());
    915     }
    916 }
    917 
    918 
    919 /// Puts a result into the database.
    920 ///
    921 /// \pre The result has not been put yet.
    922 /// \post The result is stored into the database with a new identifier.
    923 ///
    924 /// \param result The result to put.
    925 /// \param test_case_id The test case this result corresponds to.
    926 /// \param start_time The time when the test started to run.
    927 /// \param end_time The time when the test finished running.
    928 ///
    929 /// \return The identifier of the inserted result.
    930 ///
    931 /// \throw error If there is any problem when talking to the database.
    932 int64_t
    933 store::transaction::put_result(const engine::test_result& result,
    934                                const int64_t test_case_id,
    935                                const datetime::timestamp& start_time,
    936                                const datetime::timestamp& end_time)
    937 {
    938     try {
    939         sqlite::statement stmt = _pimpl->_db.create_statement(
    940             "INSERT INTO test_results (test_case_id, result_type, "
    941             "                          result_reason, start_time, "
    942             "                          end_time) "
    943             "VALUES (:test_case_id, :result_type, :result_reason, "
    944             "        :start_time, :end_time)");
    945         stmt.bind(":test_case_id", test_case_id);
    946 
    947         switch (result.type()) {
    948         case engine::test_result::broken:
    949             stmt.bind(":result_type", "broken");
    950             break;
    951 
    952         case engine::test_result::expected_failure:
    953             stmt.bind(":result_type", "expected_failure");
    954             break;
    955 
    956         case engine::test_result::failed:
    957             stmt.bind(":result_type", "failed");
    958             break;
    959 
    960         case engine::test_result::passed:
    961             stmt.bind(":result_type", "passed");
    962             break;
    963 
    964         case engine::test_result::skipped:
    965             stmt.bind(":result_type", "skipped");
    966             break;
    967 
    968         default:
    969             UNREACHABLE;
    970         }
    971 
    972         if (result.reason().empty())
    973             stmt.bind(":result_reason", sqlite::null());
    974         else
    975             stmt.bind(":result_reason", result.reason());
    976 
    977         store::bind_timestamp(stmt, ":start_time", start_time);
    978         store::bind_timestamp(stmt, ":end_time", end_time);
    979 
    980         stmt.step_without_results();
    981         const int64_t result_id = _pimpl->_db.last_insert_rowid();
    982 
    983         return result_id;
    984     } catch (const sqlite::error& e) {
    985         throw error(e.what());
    986     }
    987 }
    988