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/backend.hpp" 30 31 #include <fstream> 32 33 #include "store/exceptions.hpp" 34 #include "store/metadata.hpp" 35 #include "store/transaction.hpp" 36 #include "utils/env.hpp" 37 #include "utils/format/macros.hpp" 38 #include "utils/logging/macros.hpp" 39 #include "utils/sanity.hpp" 40 #include "utils/stream.hpp" 41 #include "utils/sqlite/database.hpp" 42 #include "utils/sqlite/exceptions.hpp" 43 #include "utils/sqlite/statement.ipp" 44 45 namespace fs = utils::fs; 46 namespace sqlite = utils::sqlite; 47 48 49 /// The current schema version. 50 /// 51 /// Any new database gets this schema version. Existing databases with an older 52 /// schema version must be first migrated to the current schema with 53 /// migrate_schema() before they can be used. 54 /// 55 /// This must be kept in sync with the value in the corresponding schema_vX.sql 56 /// file, where X matches this version number. 57 /// 58 /// This variable is not const to allow tests to modify it. No other code 59 /// should change its value. 60 int store::detail::current_schema_version = 2; 61 62 63 namespace { 64 65 66 /// Opens a database and defines session pragmas. 67 /// 68 /// This auxiliary function ensures that, every time we open a SQLite database, 69 /// we define the same set of pragmas for it. 70 /// 71 /// \param file The database file to be opened. 72 /// \param flags The flags for the open; see sqlite::database::open. 73 /// 74 /// \return The opened database. 75 /// 76 /// \throw store::error If there is a problem opening or creating the database. 77 static sqlite::database 78 do_open(const fs::path& file, const int flags) 79 { 80 try { 81 sqlite::database database = sqlite::database::open(file, flags); 82 database.exec("PRAGMA foreign_keys = ON"); 83 return database; 84 } catch (const sqlite::error& e) { 85 throw store::error(F("Cannot open '%s': %s") % file % e.what()); 86 } 87 } 88 89 90 /// Checks if a database is empty (i.e. if it is new). 91 /// 92 /// \param db The database to check. 93 /// 94 /// \return True if the database is empty. 95 static bool 96 empty_database(sqlite::database& db) 97 { 98 sqlite::statement stmt = db.create_statement("SELECT * FROM sqlite_master"); 99 return !stmt.step(); 100 } 101 102 103 /// Performs a single migration step. 104 /// 105 /// \param db Open database to which to apply the migration step. 106 /// \param version_from Current schema version in the database. 107 /// \param version_to Schema version to migrate to. 108 /// 109 /// \throw error If there is a problem applying the migration. 110 static void 111 migrate_schema_step(sqlite::database& db, const int version_from, 112 const int version_to) 113 { 114 PRE(version_to == version_from + 1); 115 116 const fs::path migration = store::detail::migration_file(version_from, 117 version_to); 118 119 std::ifstream input(migration.c_str()); 120 if (!input) 121 throw store::error(F("Cannot open migration file '%s'") % migration); 122 123 const std::string migration_string = utils::read_stream(input); 124 try { 125 db.exec(migration_string); 126 } catch (const sqlite::error& e) { 127 throw store::error(F("Schema migration failed: %s") % e.what()); 128 } 129 } 130 131 132 } // anonymous namespace 133 134 135 /// Calculates the path to a schema migration file. 136 /// 137 /// \param version_from The version from which the database is being upgraded. 138 /// \param version_to The version to which the database is being upgraded. 139 /// 140 /// \return The path to the installed migrate_vX_vY.sql file. 141 fs::path 142 store::detail::migration_file(const int version_from, const int version_to) 143 { 144 return fs::path(utils::getenv_with_default("KYUA_STOREDIR", KYUA_STOREDIR)) 145 / (F("migrate_v%s_v%s.sql") % version_from % version_to); 146 } 147 148 149 /// Calculates the path to the schema file for the database. 150 /// 151 /// \return The path to the installed schema_vX.sql file that matches the 152 /// current_schema_version. 153 fs::path 154 store::detail::schema_file(void) 155 { 156 return fs::path(utils::getenv_with_default("KYUA_STOREDIR", KYUA_STOREDIR)) 157 / (F("schema_v%s.sql") % current_schema_version); 158 } 159 160 161 /// Initializes an empty database. 162 /// 163 /// \param db The database to initialize. 164 /// 165 /// \return The metadata record written into the new database. 166 /// 167 /// \throw store::error If there is a problem initializing the database. 168 store::metadata 169 store::detail::initialize(sqlite::database& db) 170 { 171 PRE(empty_database(db)); 172 173 const fs::path schema = schema_file(); 174 175 std::ifstream input(schema.c_str()); 176 if (!input) 177 throw error(F("Cannot open database schema '%s'") % schema); 178 179 LI(F("Populating new database with schema from %s") % schema); 180 const std::string schema_string = utils::read_stream(input); 181 try { 182 db.exec(schema_string); 183 184 const metadata metadata = metadata::fetch_latest(db); 185 LI(F("New metadata entry %s") % metadata.timestamp()); 186 if (metadata.schema_version() != detail::current_schema_version) { 187 UNREACHABLE_MSG(F("current_schema_version is out of sync with " 188 "%s") % schema); 189 } 190 return metadata; 191 } catch (const store::integrity_error& e) { 192 // Could be raised by metadata::fetch_latest. 193 UNREACHABLE_MSG("Inconsistent code while creating a database"); 194 } catch (const sqlite::error& e) { 195 throw error(F("Failed to initialize database: %s") % e.what()); 196 } 197 } 198 199 200 /// Backs up a database for schema migration purposes. 201 /// 202 /// \todo We should probably use the SQLite backup API instead of doing a raw 203 /// file copy. We issue our backup call with the database already open, but 204 /// because it is quiescent, it's OK to do so. 205 /// 206 /// \param source Location of the database to be backed up. 207 /// \param old_version Version of the database's CURRENT schema, used to 208 /// determine the name of the backup file. 209 /// 210 /// \throw error If there is a problem during the backup. 211 void 212 store::detail::backup_database(const fs::path& source, const int old_version) 213 { 214 const fs::path target(F("%s.v%s.backup") % source.str() % old_version); 215 216 LI(F("Backing up database %s to %s") % source % target); 217 218 std::ifstream input(source.c_str()); 219 if (!input) 220 throw error(F("Cannot open database file %s") % source); 221 222 std::ofstream output(target.c_str()); 223 if (!output) 224 throw error(F("Cannot create database backup file %s") % target); 225 226 char buffer[1024]; 227 while (input.good()) { 228 input.read(buffer, sizeof(buffer)); 229 if (input.good() || input.eof()) 230 output.write(buffer, input.gcount()); 231 } 232 if (!input.good() && !input.eof()) 233 throw error(F("Error while reading input file %s") % source); 234 } 235 236 237 /// Internal implementation for the backend. 238 struct store::backend::impl { 239 /// The SQLite database this backend talks to. 240 sqlite::database database; 241 242 /// Constructor. 243 /// 244 /// \param database_ The SQLite database instance. 245 /// \param metadata_ The metadata for the loaded database. This must match 246 /// the schema version we implement in this module; otherwise, a 247 /// migration is necessary. 248 /// 249 /// \throw integrity_error If the schema in the database is too modern, 250 /// which might indicate some form of corruption or an old binary. 251 /// \throw old_schema_error If the schema in the database is older than our 252 /// currently-implemented version and needs an upgrade. The caller can 253 /// use migrate_schema() to fix this problem. 254 impl(sqlite::database& database_, const metadata& metadata_) : 255 database(database_) 256 { 257 const int database_version = metadata_.schema_version(); 258 259 if (database_version == detail::current_schema_version) { 260 // OK. 261 } else if (database_version < detail::current_schema_version) { 262 throw old_schema_error(database_version); 263 } else if (database_version > detail::current_schema_version) { 264 throw integrity_error( 265 F("Database at schema version %s, which is newer than the " 266 "supported version %s") 267 % database_version % detail::current_schema_version); 268 } 269 } 270 }; 271 272 273 /// Constructs a new backend. 274 /// 275 /// \param pimpl_ The internal data. 276 store::backend::backend(impl* pimpl_) : 277 _pimpl(pimpl_) 278 { 279 } 280 281 282 /// Destructor. 283 store::backend::~backend(void) 284 { 285 } 286 287 288 /// Opens a database in read-only mode. 289 /// 290 /// \param file The database file to be opened. 291 /// 292 /// \return The backend representation. 293 /// 294 /// \throw store::error If there is any problem opening the database. 295 store::backend 296 store::backend::open_ro(const fs::path& file) 297 { 298 sqlite::database db = do_open(file, sqlite::open_readonly); 299 return backend(new impl(db, metadata::fetch_latest(db))); 300 } 301 302 303 /// Opens a database in read-write mode and creates it if necessary. 304 /// 305 /// \param file The database file to be opened. 306 /// 307 /// \return The backend representation. 308 /// 309 /// \throw store::error If there is any problem opening or creating 310 /// the database. 311 store::backend 312 store::backend::open_rw(const fs::path& file) 313 { 314 sqlite::database db = do_open(file, sqlite::open_readwrite | 315 sqlite::open_create); 316 if (empty_database(db)) 317 return backend(new impl(db, detail::initialize(db))); 318 else 319 return backend(new impl(db, metadata::fetch_latest(db))); 320 } 321 322 323 /// Gets the connection to the SQLite database. 324 /// 325 /// \return A database connection. 326 sqlite::database& 327 store::backend::database(void) 328 { 329 return _pimpl->database; 330 } 331 332 333 /// Opens a transaction. 334 /// 335 /// \return A new transaction. 336 store::transaction 337 store::backend::start(void) 338 { 339 return transaction(*this); 340 } 341 342 343 /// Migrates the schema of a database to the current version. 344 /// 345 /// The algorithm implemented here performs a migration step for every 346 /// intermediate version between the schema version in the database to the 347 /// version implemented in this file. This should permit upgrades from 348 /// arbitrary old databases. 349 /// 350 /// \param file The database whose schema to upgrade. 351 /// 352 /// \throw error If there is a problem with the migration. 353 void 354 store::migrate_schema(const utils::fs::path& file) 355 { 356 sqlite::database db = do_open(file, sqlite::open_readwrite); 357 358 const int version_from = metadata::fetch_latest(db).schema_version(); 359 const int version_to = detail::current_schema_version; 360 if (version_from == version_to) { 361 throw error(F("Database already at schema version %s; migration not " 362 "needed") % version_from); 363 } else if (version_from > version_to) { 364 throw error(F("Database at schema version %s, which is newer than the " 365 "supported version %s") % version_from % version_to); 366 } 367 368 detail::backup_database(file, version_from); 369 370 for (int i = version_from; i < version_to; ++i) { 371 LI(F("Migrating schema from version %s to %s") % i % (i + 1)); 372 migrate_schema_step(db, i, i + 1); 373 } 374 } 375