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/metadata.hpp" 30 31 #include "store/exceptions.hpp" 32 #include "utils/format/macros.hpp" 33 #include "utils/sanity.hpp" 34 #include "utils/sqlite/database.hpp" 35 #include "utils/sqlite/exceptions.hpp" 36 #include "utils/sqlite/statement.ipp" 37 38 namespace sqlite = utils::sqlite; 39 40 41 namespace { 42 43 44 /// Fetches an integer column from a statement of the 'metadata' table. 45 /// 46 /// \param stmt The statement from which to get the column value. 47 /// \param column The name of the column to retrieve. 48 /// 49 /// \return The value of the column. 50 /// 51 /// \throw store::integrity_error If there is a problem fetching the value 52 /// caused by an invalid schema or data. 53 static int64_t 54 int64_column(sqlite::statement& stmt, const char* column) 55 { 56 int index; 57 try { 58 index = stmt.column_id(column); 59 } catch (const sqlite::invalid_column_error& e) { 60 UNREACHABLE_MSG("Invalid column specification; the SELECT statement " 61 "should have caught this"); 62 } 63 if (stmt.column_type(index) != sqlite::type_integer) 64 throw store::integrity_error(F("The '%s' column in 'metadata' table " 65 "has an invalid type") % column); 66 return stmt.column_int64(index); 67 } 68 69 70 } // anonymous namespace 71 72 73 /// Constructs a new metadata object. 74 /// 75 /// \param schema_version_ The schema version. 76 /// \param timestamp_ The time at which this version was created. 77 store::metadata::metadata(const int schema_version_, const int64_t timestamp_) : 78 _schema_version(schema_version_), 79 _timestamp(timestamp_) 80 { 81 } 82 83 84 /// Returns the timestamp of this entry. 85 /// 86 /// \return The timestamp in this metadata entry. 87 int64_t 88 store::metadata::timestamp(void) const 89 { 90 return _timestamp; 91 } 92 93 94 /// Returns the schema version. 95 /// 96 /// \return The schema version in this metadata entry. 97 int 98 store::metadata::schema_version(void) const 99 { 100 return _schema_version; 101 } 102 103 104 /// Reads the latest metadata entry from the database. 105 /// 106 /// \param db The database from which to read the metadata from. 107 /// 108 /// \return The current metadata of the database. It is not OK for the metadata 109 /// table to be empty, so this is guaranteed to return a value unless there is 110 /// an error. 111 /// 112 /// \throw store::integrity_error If the metadata in the database is empty, 113 /// has an invalid schema or its contents are bogus. 114 store::metadata 115 store::metadata::fetch_latest(sqlite::database& db) 116 { 117 try { 118 sqlite::statement stmt = db.create_statement( 119 "SELECT schema_version, timestamp FROM metadata " 120 "ORDER BY schema_version DESC LIMIT 1"); 121 if (!stmt.step()) 122 throw store::integrity_error("The 'metadata' table is empty"); 123 124 const int schema_version_ = 125 static_cast< int >(int64_column(stmt, "schema_version")); 126 const int64_t timestamp_ = int64_column(stmt, "timestamp"); 127 128 if (stmt.step()) 129 UNREACHABLE_MSG("Got more than one result from a query that " 130 "does not permit this; any pragmas defined?"); 131 132 return metadata(schema_version_, timestamp_); 133 } catch (const sqlite::error& e) { 134 throw store::integrity_error(F("Invalid metadata schema: %s") % 135 e.what()); 136 } 137 } 138