1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sql/meta_table.h"
10 #include "base/check_op.h"
11 #include "base/strings/string_piece.h"
12 #include "sql/database.h"
13 #include "sql/statement.h"
14 #include "sql/statement_id.h"
15 #include "sql/transaction.h"
21 // Keys understood directly by sql:MetaTable.
22 constexpr char kVersionKey[] = "version";
23 constexpr char kCompatibleVersionKey[] = "last_compatible_version";
24 constexpr char kMmapStatusKey[] = "mmap_status";
26 void PrepareSetStatement(base::StringPiece key,
28 Statement& insert_statement) {
29 insert_statement.Assign(db.GetCachedStatement(
30 SQL_FROM_HERE, "INSERT OR REPLACE INTO meta(key,value) VALUES(?,?)"));
31 insert_statement.BindString(0, key);
34 bool PrepareGetStatement(base::StringPiece key,
36 Statement& select_statement) {
37 select_statement.Assign(db.GetCachedStatement(
38 SQL_FROM_HERE, "SELECT value FROM meta WHERE key=?"));
39 if (!select_statement.is_valid())
42 select_statement.BindString(0, key);
43 return select_statement.Step();
48 MetaTable::MetaTable() = default;
50 MetaTable::~MetaTable() = default;
53 constexpr int64_t MetaTable::kMmapFailure;
54 constexpr int64_t MetaTable::kMmapSuccess;
57 bool MetaTable::DoesTableExist(sql::Database* db) {
59 return db->DoesTableExist("meta");
63 bool MetaTable::DeleteTableForTesting(sql::Database* db) {
65 return db->Execute("DROP TABLE IF EXISTS meta");
69 bool MetaTable::GetMmapStatus(Database* db, int64_t* status) {
73 // It is fine for the status to be missing entirely, but any error prevents
76 if (!PrepareGetStatement(kMmapStatusKey, *db, select)) {
81 *status = select.ColumnInt64(0);
82 return select.Succeeded();
86 bool MetaTable::SetMmapStatus(Database* db, int64_t status) {
88 DCHECK(status == kMmapFailure || status == kMmapSuccess || status >= 0);
91 PrepareSetStatement(kMmapStatusKey, *db, insert);
92 insert.BindInt64(1, status);
97 bool MetaTable::RazeIfIncompatible(Database* db,
98 int lowest_supported_version,
99 int current_version) {
102 if (!DoesTableExist(db)) {
106 sql::Statement select;
107 if (!PrepareGetStatement(kVersionKey, *db, select)) {
110 int64_t on_disk_schema_version = select.ColumnInt64(0);
112 if (!PrepareGetStatement(kCompatibleVersionKey, *db, select)) {
115 int64_t on_disk_compatible_version = select.ColumnInt(0);
117 select.Clear(); // Clear potential automatic transaction for Raze().
119 if ((lowest_supported_version != kNoLowestSupportedVersion &&
120 lowest_supported_version > on_disk_schema_version) ||
121 (current_version < on_disk_compatible_version)) {
127 bool MetaTable::Init(Database* db, int version, int compatible_version) {
131 // If values stored are nullptr or missing entirely, 0 will be reported.
132 // Require new clients to start with a greater initial version.
133 DCHECK_GT(version, 0);
134 DCHECK_GT(compatible_version, 0);
136 // Make sure the table is created and populated atomically.
137 sql::Transaction transaction(db_);
138 if (!transaction.Begin())
141 if (!DoesTableExist(db)) {
142 if (!db_->Execute("CREATE TABLE meta"
143 "(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value "
148 // Newly-created databases start out with mmap'ed I/O, but have no place to
149 // store the setting. Set here so that later opens don't need to validate.
150 if (!SetMmapStatus(db_, kMmapSuccess)) {
154 // Note: there is no index over the meta table. We currently only have a
155 // couple of keys, so it doesn't matter. If we start storing more stuff in
156 // there, we should create an index.
158 // If setting either version number fails, return early to avoid likely
159 // crashes or incorrect behavior with respect to migrations.
160 if (!SetVersionNumber(version) ||
161 !SetCompatibleVersionNumber(compatible_version)) {
165 return transaction.Commit();
168 void MetaTable::Reset() {
172 bool MetaTable::SetVersionNumber(int version) {
173 DCHECK_GT(version, 0);
174 return SetValue(kVersionKey, version);
177 int MetaTable::GetVersionNumber() {
179 return GetValue(kVersionKey, &version) ? version : 0;
182 bool MetaTable::SetCompatibleVersionNumber(int version) {
183 DCHECK_GT(version, 0);
184 return SetValue(kCompatibleVersionKey, version);
187 int MetaTable::GetCompatibleVersionNumber() {
189 return GetValue(kCompatibleVersionKey, &version) ? version : 0;
192 bool MetaTable::SetValue(base::StringPiece key, const std::string& value) {
196 PrepareSetStatement(key, *db_, insert);
197 insert.BindString(1, value);
201 bool MetaTable::SetValue(base::StringPiece key, int64_t value) {
205 PrepareSetStatement(key, *db_, insert);
206 insert.BindInt64(1, value);
210 bool MetaTable::GetValue(base::StringPiece key, std::string* value) {
215 if (!PrepareGetStatement(key, *db_, select))
218 *value = select.ColumnString(0);
222 bool MetaTable::GetValue(base::StringPiece key, int* value) {
227 if (!PrepareGetStatement(key, *db_, select))
230 *value = select.ColumnInt64(0);
234 bool MetaTable::GetValue(base::StringPiece key, int64_t* value) {
239 if (!PrepareGetStatement(key, *db_, select))
242 *value = select.ColumnInt64(0);
246 bool MetaTable::DeleteKey(base::StringPiece key) {
249 Statement delete_statement(
250 db_->GetUniqueStatement("DELETE FROM meta WHERE key=?"));
251 delete_statement.BindString(0, key);
252 return delete_statement.Run();