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 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
get_test_cases(sqlite::database & db,const int64_t test_program_id,const engine::test_program & test_program,const std::string & interface)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
parse_result(sqlite::statement & stmt,const char * type_column,const char * reason_column)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
put_env_vars(sqlite::database & db,const int64_t context_id,const std::map<std::string,std::string> & env)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
last_rowid(sqlite::database & db,const std::string & table)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
put_metadata(sqlite::database & db,const engine::metadata & md)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 >
put_file(sqlite::database & db,const fs::path & path)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
get_test_program(backend & backend_,const int64_t id)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.
implstore::results_iterator::impl414 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.
results_iterator(std::shared_ptr<impl> pimpl_)439 store::results_iterator::results_iterator(
440 std::shared_ptr< impl > pimpl_) :
441 _pimpl(pimpl_)
442 {
443 }
444
445
446 /// Destructor.
~results_iterator(void)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&
operator ++(void)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.
operator bool(void) const466 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
test_program(void) const476 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
test_case_name(void) const497 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
result(void) const507 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
duration(void) const517 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
get_test_case_file(sqlite::database & db,const int64_t test_case_id,const char * filename)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
stdout_contents(void) const558 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
stderr_contents(void) const571 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.
implstore::transaction::impl594 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.
transaction(backend & backend_)606 store::transaction::transaction(backend& backend_) :
607 _pimpl(new impl(backend_))
608 {
609 }
610
611
612 /// Destructor.
~transaction(void)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
commit(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
rollback(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
get_action(const int64_t action_id)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
get_action_results(const int64_t action_id)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 >
get_latest_action(void)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
get_context(const int64_t context_id)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
put_action(const engine::action & UTILS_UNUSED_PARAM (action),const int64_t context_id)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
put_context(const engine::context & context)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
put_test_program(const engine::test_program & test_program,const int64_t action_id)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
put_test_case(const engine::test_case & test_case,const int64_t test_program_id)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 >
put_test_case_file(const std::string & name,const fs::path & path,const int64_t test_case_id)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
put_result(const engine::test_result & result,const int64_t test_case_id,const datetime::timestamp & start_time,const datetime::timestamp & end_time)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