1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "sql/meta_table.h"
#include <cstdint>
#include <string>
#include "base/check_op.h"
#include "base/strings/string_piece.h"
#include "sql/database.h"
#include "sql/statement.h"
#include "sql/statement_id.h"
#include "sql/transaction.h"
namespace sql {
namespace {
// Keys understood directly by sql:MetaTable.
constexpr char kVersionKey[] = "version";
constexpr char kCompatibleVersionKey[] = "last_compatible_version";
constexpr char kMmapStatusKey[] = "mmap_status";
void PrepareSetStatement(base::StringPiece key,
Database& db,
Statement& insert_statement) {
insert_statement.Assign(db.GetCachedStatement(
SQL_FROM_HERE, "INSERT OR REPLACE INTO meta(key,value) VALUES(?,?)"));
insert_statement.BindString(0, key);
}
bool PrepareGetStatement(base::StringPiece key,
Database& db,
Statement& select_statement) {
select_statement.Assign(db.GetCachedStatement(
SQL_FROM_HERE, "SELECT value FROM meta WHERE key=?"));
if (!select_statement.is_valid())
return false;
select_statement.BindString(0, key);
return select_statement.Step();
}
} // namespace
MetaTable::MetaTable() = default;
MetaTable::~MetaTable() = default;
// static
constexpr int64_t MetaTable::kMmapFailure;
constexpr int64_t MetaTable::kMmapSuccess;
// static
bool MetaTable::DoesTableExist(sql::Database* db) {
DCHECK(db);
return db->DoesTableExist("meta");
}
// static
bool MetaTable::DeleteTableForTesting(sql::Database* db) {
DCHECK(db);
return db->Execute("DROP TABLE IF EXISTS meta");
}
// static
bool MetaTable::GetMmapStatus(Database* db, int64_t* status) {
DCHECK(db);
DCHECK(status);
// It is fine for the status to be missing entirely, but any error prevents
// memory-mapping.
Statement select;
if (!PrepareGetStatement(kMmapStatusKey, *db, select)) {
*status = 0;
return true;
}
*status = select.ColumnInt64(0);
return select.Succeeded();
}
// static
bool MetaTable::SetMmapStatus(Database* db, int64_t status) {
DCHECK(db);
DCHECK(status == kMmapFailure || status == kMmapSuccess || status >= 0);
Statement insert;
PrepareSetStatement(kMmapStatusKey, *db, insert);
insert.BindInt64(1, status);
return insert.Run();
}
// static
bool MetaTable::RazeIfIncompatible(Database* db,
int lowest_supported_version,
int current_version) {
DCHECK(db);
if (!DoesTableExist(db)) {
return true;
}
sql::Statement select;
if (!PrepareGetStatement(kVersionKey, *db, select)) {
return false;
}
int64_t on_disk_schema_version = select.ColumnInt64(0);
if (!PrepareGetStatement(kCompatibleVersionKey, *db, select)) {
return false;
}
int64_t on_disk_compatible_version = select.ColumnInt(0);
select.Clear(); // Clear potential automatic transaction for Raze().
if ((lowest_supported_version != kNoLowestSupportedVersion &&
lowest_supported_version > on_disk_schema_version) ||
(current_version < on_disk_compatible_version)) {
return db->Raze();
}
return true;
}
bool MetaTable::Init(Database* db, int version, int compatible_version) {
DCHECK(!db_ && db);
db_ = db;
// If values stored are nullptr or missing entirely, 0 will be reported.
// Require new clients to start with a greater initial version.
DCHECK_GT(version, 0);
DCHECK_GT(compatible_version, 0);
// Make sure the table is created and populated atomically.
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
if (!DoesTableExist(db)) {
if (!db_->Execute("CREATE TABLE meta"
"(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value "
"LONGVARCHAR)")) {
return false;
}
// Newly-created databases start out with mmap'ed I/O, but have no place to
// store the setting. Set here so that later opens don't need to validate.
if (!SetMmapStatus(db_, kMmapSuccess)) {
return false;
}
// Note: there is no index over the meta table. We currently only have a
// couple of keys, so it doesn't matter. If we start storing more stuff in
// there, we should create an index.
// If setting either version number fails, return early to avoid likely
// crashes or incorrect behavior with respect to migrations.
if (!SetVersionNumber(version) ||
!SetCompatibleVersionNumber(compatible_version)) {
return false;
}
}
return transaction.Commit();
}
void MetaTable::Reset() {
db_ = nullptr;
}
bool MetaTable::SetVersionNumber(int version) {
DCHECK_GT(version, 0);
return SetValue(kVersionKey, version);
}
int MetaTable::GetVersionNumber() {
int64_t version = 0;
return GetValue(kVersionKey, &version) ? version : 0;
}
bool MetaTable::SetCompatibleVersionNumber(int version) {
DCHECK_GT(version, 0);
return SetValue(kCompatibleVersionKey, version);
}
int MetaTable::GetCompatibleVersionNumber() {
int version = 0;
return GetValue(kCompatibleVersionKey, &version) ? version : 0;
}
bool MetaTable::SetValue(base::StringPiece key, const std::string& value) {
DCHECK(db_);
Statement insert;
PrepareSetStatement(key, *db_, insert);
insert.BindString(1, value);
return insert.Run();
}
bool MetaTable::SetValue(base::StringPiece key, int64_t value) {
DCHECK(db_);
Statement insert;
PrepareSetStatement(key, *db_, insert);
insert.BindInt64(1, value);
return insert.Run();
}
bool MetaTable::GetValue(base::StringPiece key, std::string* value) {
DCHECK(value);
DCHECK(db_);
Statement select;
if (!PrepareGetStatement(key, *db_, select))
return false;
*value = select.ColumnString(0);
return true;
}
bool MetaTable::GetValue(base::StringPiece key, int* value) {
DCHECK(value);
DCHECK(db_);
Statement select;
if (!PrepareGetStatement(key, *db_, select))
return false;
*value = select.ColumnInt64(0);
return true;
}
bool MetaTable::GetValue(base::StringPiece key, int64_t* value) {
DCHECK(value);
DCHECK(db_);
Statement select;
if (!PrepareGetStatement(key, *db_, select))
return false;
*value = select.ColumnInt64(0);
return true;
}
bool MetaTable::DeleteKey(base::StringPiece key) {
DCHECK(db_);
Statement delete_statement(
db_->GetUniqueStatement("DELETE FROM meta WHERE key=?"));
delete_statement.BindString(0, key);
return delete_statement.Run();
}
} // namespace sql
|