1 // Copyright 2011 The Kyua Authors. 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/read_transaction.hpp" 30 31 extern "C" { 32 #include <stdint.h> 33 } 34 35 #include <map> 36 #include <utility> 37 38 #include "model/context.hpp" 39 #include "model/metadata.hpp" 40 #include "model/test_case.hpp" 41 #include "model/test_program.hpp" 42 #include "model/test_result.hpp" 43 #include "store/dbtypes.hpp" 44 #include "store/exceptions.hpp" 45 #include "store/read_backend.hpp" 46 #include "utils/datetime.hpp" 47 #include "utils/format/macros.hpp" 48 #include "utils/fs/path.hpp" 49 #include "utils/logging/macros.hpp" 50 #include "utils/noncopyable.hpp" 51 #include "utils/optional.ipp" 52 #include "utils/sanity.hpp" 53 #include "utils/sqlite/database.hpp" 54 #include "utils/sqlite/exceptions.hpp" 55 #include "utils/sqlite/statement.ipp" 56 #include "utils/sqlite/transaction.hpp" 57 58 namespace datetime = utils::datetime; 59 namespace fs = utils::fs; 60 namespace sqlite = utils::sqlite; 61 62 using utils::optional; 63 64 65 namespace { 66 67 68 /// Retrieves the environment variables of the context. 69 /// 70 /// \param db The SQLite database. 71 /// 72 /// \return The environment variables of the specified context. 73 /// 74 /// \throw sqlite::error If there is a problem loading the variables. 75 static std::map< std::string, std::string > 76 get_env_vars(sqlite::database& db) 77 { 78 std::map< std::string, std::string > env; 79 80 sqlite::statement stmt = db.create_statement( 81 "SELECT var_name, var_value FROM env_vars"); 82 83 while (stmt.step()) { 84 const std::string name = stmt.safe_column_text("var_name"); 85 const std::string value = stmt.safe_column_text("var_value"); 86 env[name] = value; 87 } 88 89 return env; 90 } 91 92 93 /// Retrieves a metadata object. 94 /// 95 /// \param db The SQLite database. 96 /// \param metadata_id The identifier of the metadata. 97 /// 98 /// \return A new metadata object. 99 static model::metadata 100 get_metadata(sqlite::database& db, const int64_t metadata_id) 101 { 102 model::metadata_builder builder; 103 104 sqlite::statement stmt = db.create_statement( 105 "SELECT * FROM metadatas WHERE metadata_id == :metadata_id"); 106 stmt.bind(":metadata_id", metadata_id); 107 while (stmt.step()) { 108 const std::string name = stmt.safe_column_text("property_name"); 109 const std::string value = stmt.safe_column_text("property_value"); 110 builder.set_string(name, value); 111 } 112 113 return builder.build(); 114 } 115 116 117 /// Gets a file from the database. 118 /// 119 /// \param db The database to query the file from. 120 /// \param file_id The identifier of the file to be queried. 121 /// 122 /// \return A textual representation of the file contents. 123 /// 124 /// \throw integrity_error If there is any problem in the loaded data or if the 125 /// file cannot be found. 126 static std::string 127 get_file(sqlite::database& db, const int64_t file_id) 128 { 129 sqlite::statement stmt = db.create_statement( 130 "SELECT contents FROM files WHERE file_id == :file_id"); 131 stmt.bind(":file_id", file_id); 132 if (!stmt.step()) 133 throw store::integrity_error(F("Cannot find referenced file %s") % 134 file_id); 135 136 try { 137 const sqlite::blob raw_contents = stmt.safe_column_blob("contents"); 138 const std::string contents( 139 static_cast< const char *>(raw_contents.memory), raw_contents.size); 140 141 const bool more = stmt.step(); 142 INV(!more); 143 144 return contents; 145 } catch (const sqlite::error& e) { 146 throw store::integrity_error(e.what()); 147 } 148 } 149 150 151 /// Gets all the test cases within a particular test program. 152 /// 153 /// \param db The database to query the information from. 154 /// \param test_program_id The identifier of the test program whose test cases 155 /// to query. 156 /// 157 /// \return The collection of loaded test cases. 158 /// 159 /// \throw integrity_error If there is any problem in the loaded data. 160 static model::test_cases_map 161 get_test_cases(sqlite::database& db, const int64_t test_program_id) 162 { 163 model::test_cases_map_builder test_cases; 164 165 sqlite::statement stmt = db.create_statement( 166 "SELECT name, metadata_id " 167 "FROM test_cases WHERE test_program_id == :test_program_id"); 168 stmt.bind(":test_program_id", test_program_id); 169 while (stmt.step()) { 170 const std::string name = stmt.safe_column_text("name"); 171 const int64_t metadata_id = stmt.safe_column_int64("metadata_id"); 172 173 const model::metadata metadata = get_metadata(db, metadata_id); 174 LD(F("Loaded test case '%s'") % name); 175 test_cases.add(name, metadata); 176 } 177 178 return test_cases.build(); 179 } 180 181 182 /// Retrieves a result from the database. 183 /// 184 /// \param stmt The statement with the data for the result to load. 185 /// \param type_column The name of the column containing the type of the result. 186 /// \param reason_column The name of the column containing the reason for the 187 /// result, if any. 188 /// 189 /// \return The loaded result. 190 /// 191 /// \throw integrity_error If the data in the database is invalid. 192 static model::test_result 193 parse_result(sqlite::statement& stmt, const char* type_column, 194 const char* reason_column) 195 { 196 try { 197 const model::test_result_type type = 198 store::column_test_result_type(stmt, type_column); 199 if (type == model::test_result_passed) { 200 if (stmt.column_type(stmt.column_id(reason_column)) != 201 sqlite::type_null) 202 throw store::integrity_error("Result of type 'passed' has a " 203 "non-NULL reason"); 204 return model::test_result(type); 205 } else { 206 return model::test_result(type, 207 stmt.safe_column_text(reason_column)); 208 } 209 } catch (const sqlite::error& e) { 210 throw store::integrity_error(e.what()); 211 } 212 } 213 214 215 } // anonymous namespace 216 217 218 /// Loads a specific test program from the database. 219 /// 220 /// \param backend_ The store backend we are dealing with. 221 /// \param id The identifier of the test program to load. 222 /// 223 /// \return The instantiated test program. 224 /// 225 /// \throw integrity_error If the data read from the database cannot be properly 226 /// interpreted. 227 model::test_program_ptr 228 store::detail::get_test_program(read_backend& backend_, const int64_t id) 229 { 230 sqlite::database& db = backend_.database(); 231 232 model::test_program_ptr test_program; 233 sqlite::statement stmt = db.create_statement( 234 "SELECT * FROM test_programs WHERE test_program_id == :id"); 235 stmt.bind(":id", id); 236 stmt.step(); 237 const std::string interface = stmt.safe_column_text("interface"); 238 test_program.reset(new model::test_program( 239 interface, 240 fs::path(stmt.safe_column_text("relative_path")), 241 fs::path(stmt.safe_column_text("root")), 242 stmt.safe_column_text("test_suite_name"), 243 get_metadata(db, stmt.safe_column_int64("metadata_id")), 244 get_test_cases(db, id))); 245 const bool more = stmt.step(); 246 INV(!more); 247 248 LD(F("Loaded test program '%s'") % test_program->relative_path()); 249 return test_program; 250 } 251 252 253 /// Internal implementation for a results iterator. 254 struct store::results_iterator::impl : utils::noncopyable { 255 /// The store backend we are dealing with. 256 store::read_backend _backend; 257 258 /// The statement to iterate on. 259 sqlite::statement _stmt; 260 261 /// A cache for the last loaded test program. 262 optional< std::pair< int64_t, model::test_program_ptr > > 263 _last_test_program; 264 265 /// Whether the iterator is still valid or not. 266 bool _valid; 267 268 /// Constructor. 269 /// 270 /// \param backend_ The store backend implementation. 271 impl(store::read_backend& backend_) : 272 _backend(backend_), 273 _stmt(backend_.database().create_statement( 274 "SELECT test_programs.test_program_id, " 275 " test_programs.interface, " 276 " test_cases.test_case_id, test_cases.name, " 277 " test_results.result_type, test_results.result_reason, " 278 " test_results.start_time, test_results.end_time " 279 "FROM test_programs " 280 " JOIN test_cases " 281 " ON test_programs.test_program_id = test_cases.test_program_id " 282 " JOIN test_results " 283 " ON test_cases.test_case_id = test_results.test_case_id " 284 "ORDER BY test_programs.absolute_path, test_cases.name")) 285 { 286 _valid = _stmt.step(); 287 } 288 }; 289 290 291 /// Constructor. 292 /// 293 /// \param pimpl_ The internal implementation details of the iterator. 294 store::results_iterator::results_iterator( 295 std::shared_ptr< impl > pimpl_) : 296 _pimpl(pimpl_) 297 { 298 } 299 300 301 /// Destructor. 302 store::results_iterator::~results_iterator(void) 303 { 304 } 305 306 307 /// Moves the iterator forward by one result. 308 /// 309 /// \return The iterator itself. 310 store::results_iterator& 311 store::results_iterator::operator++(void) 312 { 313 _pimpl->_valid = _pimpl->_stmt.step(); 314 return *this; 315 } 316 317 318 /// Checks whether the iterator is still valid. 319 /// 320 /// \return True if there is more elements to iterate on, false otherwise. 321 store::results_iterator::operator bool(void) const 322 { 323 return _pimpl->_valid; 324 } 325 326 327 /// Gets the test program this result belongs to. 328 /// 329 /// \return The representation of a test program. 330 const model::test_program_ptr 331 store::results_iterator::test_program(void) const 332 { 333 const int64_t id = _pimpl->_stmt.safe_column_int64("test_program_id"); 334 if (!_pimpl->_last_test_program || 335 _pimpl->_last_test_program.get().first != id) 336 { 337 const model::test_program_ptr tp = detail::get_test_program( 338 _pimpl->_backend, id); 339 _pimpl->_last_test_program = std::make_pair(id, tp); 340 } 341 return _pimpl->_last_test_program.get().second; 342 } 343 344 345 /// Gets the name of the test case pointed by the iterator. 346 /// 347 /// The caller can look up the test case data by using the find() method on the 348 /// test program returned by test_program(). 349 /// 350 /// \return A test case name, unique within the test program. 351 std::string 352 store::results_iterator::test_case_name(void) const 353 { 354 return _pimpl->_stmt.safe_column_text("name"); 355 } 356 357 358 /// Gets the result of the test case pointed by the iterator. 359 /// 360 /// \return A test case result. 361 model::test_result 362 store::results_iterator::result(void) const 363 { 364 return parse_result(_pimpl->_stmt, "result_type", "result_reason"); 365 } 366 367 368 /// Gets the start time of the test case execution. 369 /// 370 /// \return The time when the test started execution. 371 datetime::timestamp 372 store::results_iterator::start_time(void) const 373 { 374 return column_timestamp(_pimpl->_stmt, "start_time"); 375 } 376 377 378 /// Gets the end time of the test case execution. 379 /// 380 /// \return The time when the test finished execution. 381 datetime::timestamp 382 store::results_iterator::end_time(void) const 383 { 384 return column_timestamp(_pimpl->_stmt, "end_time"); 385 } 386 387 388 /// Gets a file from a test case. 389 /// 390 /// \param db The database to query the file from. 391 /// \param test_case_id The identifier of the test case. 392 /// \param filename The name of the file to be retrieved. 393 /// 394 /// \return A textual representation of the file contents. 395 /// 396 /// \throw integrity_error If there is any problem in the loaded data or if the 397 /// file cannot be found. 398 static std::string 399 get_test_case_file(sqlite::database& db, const int64_t test_case_id, 400 const char* filename) 401 { 402 sqlite::statement stmt = db.create_statement( 403 "SELECT file_id FROM test_case_files " 404 "WHERE test_case_id == :test_case_id AND file_name == :file_name"); 405 stmt.bind(":test_case_id", test_case_id); 406 stmt.bind(":file_name", filename); 407 if (stmt.step()) 408 return get_file(db, stmt.safe_column_int64("file_id")); 409 else 410 return ""; 411 } 412 413 414 /// Gets the contents of stdout of a test case. 415 /// 416 /// \return A textual representation of the stdout contents of the test case. 417 /// This may of course be empty if the test case didn't print anything. 418 std::string 419 store::results_iterator::stdout_contents(void) const 420 { 421 return get_test_case_file(_pimpl->_backend.database(), 422 _pimpl->_stmt.safe_column_int64("test_case_id"), 423 "__STDOUT__"); 424 } 425 426 427 /// Gets the contents of stderr of a test case. 428 /// 429 /// \return A textual representation of the stderr contents of the test case. 430 /// This may of course be empty if the test case didn't print anything. 431 std::string 432 store::results_iterator::stderr_contents(void) const 433 { 434 return get_test_case_file(_pimpl->_backend.database(), 435 _pimpl->_stmt.safe_column_int64("test_case_id"), 436 "__STDERR__"); 437 } 438 439 440 /// Internal implementation for a store read-only transaction. 441 struct store::read_transaction::impl : utils::noncopyable { 442 /// The backend instance. 443 store::read_backend& _backend; 444 445 /// The SQLite database this transaction deals with. 446 sqlite::database _db; 447 448 /// The backing SQLite transaction. 449 sqlite::transaction _tx; 450 451 /// Opens a transaction. 452 /// 453 /// \param backend_ The backend this transaction is connected to. 454 impl(read_backend& backend_) : 455 _backend(backend_), 456 _db(backend_.database()), 457 _tx(backend_.database().begin_transaction()) 458 { 459 } 460 }; 461 462 463 /// Creates a new read-only transaction. 464 /// 465 /// \param backend_ The backend this transaction belongs to. 466 store::read_transaction::read_transaction(read_backend& backend_) : 467 _pimpl(new impl(backend_)) 468 { 469 } 470 471 472 /// Destructor. 473 store::read_transaction::~read_transaction(void) 474 { 475 } 476 477 478 /// Finishes the transaction. 479 /// 480 /// This actually commits the result of the transaction, but because the 481 /// transaction is read-only, we use a different term to denote that there is no 482 /// distinction between commit and rollback. 483 /// 484 /// \throw error If there is any problem when talking to the database. 485 void 486 store::read_transaction::finish(void) 487 { 488 try { 489 _pimpl->_tx.commit(); 490 } catch (const sqlite::error& e) { 491 throw error(e.what()); 492 } 493 } 494 495 496 /// Retrieves an context from the database. 497 /// 498 /// \return The retrieved context. 499 /// 500 /// \throw error If there is a problem loading the context. 501 model::context 502 store::read_transaction::get_context(void) 503 { 504 try { 505 sqlite::statement stmt = _pimpl->_db.create_statement( 506 "SELECT cwd FROM contexts"); 507 if (!stmt.step()) 508 throw error("Error loading context: no data"); 509 510 return model::context(fs::path(stmt.safe_column_text("cwd")), 511 get_env_vars(_pimpl->_db)); 512 } catch (const sqlite::error& e) { 513 throw error(F("Error loading context: %s") % e.what()); 514 } 515 } 516 517 518 /// Creates a new iterator to scan tests results. 519 /// 520 /// \return The constructed iterator. 521 /// 522 /// \throw error If there is any problem constructing the iterator. 523 store::results_iterator 524 store::read_transaction::get_results(void) 525 { 526 try { 527 return results_iterator(std::shared_ptr< results_iterator::impl >( 528 new results_iterator::impl(_pimpl->_backend))); 529 } catch (const sqlite::error& e) { 530 throw error(e.what()); 531 } 532 } 533