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 38namespace sqlite = utils::sqlite; 39 40 41namespace { 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. 53static int64_t 54int64_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. 77store::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. 87int64_t 88store::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. 97int 98store::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. 114store::metadata 115store::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