transaction.cpp revision 1.1
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::tr1::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::tr1::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