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