// Copyright 2011 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "store/metadata.hpp"2930#include "store/exceptions.hpp"31#include "utils/format/macros.hpp"32#include "utils/sanity.hpp"33#include "utils/sqlite/database.hpp"34#include "utils/sqlite/exceptions.hpp"35#include "utils/sqlite/statement.ipp"3637namespace sqlite = utils::sqlite;383940namespace {414243/// Fetches an integer column from a statement of the 'metadata' table.44///45/// \param stmt The statement from which to get the column value.46/// \param column The name of the column to retrieve.47///48/// \return The value of the column.49///50/// \throw store::integrity_error If there is a problem fetching the value51/// caused by an invalid schema or data.52static int64_t53int64_column(sqlite::statement& stmt, const char* column)54{55int index;56try {57index = stmt.column_id(column);58} catch (const sqlite::invalid_column_error& e) {59UNREACHABLE_MSG("Invalid column specification; the SELECT statement "60"should have caught this");61}62if (stmt.column_type(index) != sqlite::type_integer)63throw store::integrity_error(F("The '%s' column in 'metadata' table "64"has an invalid type") % column);65return stmt.column_int64(index);66}676869} // anonymous namespace707172/// Constructs a new metadata object.73///74/// \param schema_version_ The schema version.75/// \param timestamp_ The time at which this version was created.76store::metadata::metadata(const int schema_version_, const int64_t timestamp_) :77_schema_version(schema_version_),78_timestamp(timestamp_)79{80}818283/// Returns the timestamp of this entry.84///85/// \return The timestamp in this metadata entry.86int64_t87store::metadata::timestamp(void) const88{89return _timestamp;90}919293/// Returns the schema version.94///95/// \return The schema version in this metadata entry.96int97store::metadata::schema_version(void) const98{99return _schema_version;100}101102103/// Reads the latest metadata entry from the database.104///105/// \param db The database from which to read the metadata from.106///107/// \return The current metadata of the database. It is not OK for the metadata108/// table to be empty, so this is guaranteed to return a value unless there is109/// an error.110///111/// \throw store::integrity_error If the metadata in the database is empty,112/// has an invalid schema or its contents are bogus.113store::metadata114store::metadata::fetch_latest(sqlite::database& db)115{116try {117sqlite::statement stmt = db.create_statement(118"SELECT schema_version, timestamp FROM metadata "119"ORDER BY schema_version DESC LIMIT 1");120if (!stmt.step())121throw store::integrity_error("The 'metadata' table is empty");122123const int schema_version_ =124static_cast< int >(int64_column(stmt, "schema_version"));125const int64_t timestamp_ = int64_column(stmt, "timestamp");126127if (stmt.step())128UNREACHABLE_MSG("Got more than one result from a query that "129"does not permit this; any pragmas defined?");130131return metadata(schema_version_, timestamp_);132} catch (const sqlite::error& e) {133throw store::integrity_error(F("Invalid metadata schema: %s") %134e.what());135}136}137138139