transaction.cpp revision 1.1.1.2.4.2
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
31extern "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
58namespace datetime = utils::datetime;
59namespace fs = utils::fs;
60namespace sqlite = utils::sqlite;
61namespace units = utils::units;
62
63using utils::none;
64using utils::optional;
65
66
67namespace {
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.
78static std::map< std::string, std::string >
79get_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.
104static engine::metadata
105get_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.
131static std::string
132get_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.
170static engine::test_cases_vector
171get_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.
206static engine::test_result
207parse_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.
249static void
250put_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.
273static int64_t
274last_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.
294static int64_t
295put_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.
326static optional< int64_t >
327put_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.
370engine::test_program_ptr
371store::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.
399struct 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.
439store::results_iterator::results_iterator(
440    std::shared_ptr< impl > pimpl_) :
441    _pimpl(pimpl_)
442{
443}
444
445
446/// Destructor.
447store::results_iterator::~results_iterator(void)
448{
449}
450
451
452/// Moves the iterator forward by one result.
453///
454/// \return The iterator itself.
455store::results_iterator&
456store::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.
466store::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.
475const engine::test_program_ptr
476store::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.
496std::string
497store::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.
506engine::test_result
507store::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.
516datetime::delta
517store::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.
537static std::string
538get_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.
557std::string
558store::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.
570std::string
571store::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.
580struct 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.
606store::transaction::transaction(backend& backend_) :
607    _pimpl(new impl(backend_))
608{
609}
610
611
612/// Destructor.
613store::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.
621void
622store::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.
635void
636store::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.
653engine::action
654store::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.
679store::results_iterator
680store::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.
696std::pair< int64_t, engine::action >
697store::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.
725engine::context
726store::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.
756int64_t
757store::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.
784int64_t
785store::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.
814int64_t
815store::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.
857int64_t
858store::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.
891optional< int64_t >
892store::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.
932int64_t
933store::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