Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / sql / meta_table.cc
blob319f73d24db60396d5b9e16148a4388ffb265825
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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"
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/string_util.h"
10 #include "sql/connection.h"
11 #include "sql/statement.h"
12 #include "sql/transaction.h"
14 namespace {
16 // Key used in our meta table for version numbers.
17 const char kVersionKey[] = "version";
18 const char kCompatibleVersionKey[] = "last_compatible_version";
20 // Used to track success/failure of deprecation checks.
21 enum DeprecationEventType {
22 // Database has info, but no meta table. This is probably bad.
23 DEPRECATION_DATABASE_NOT_EMPTY = 0,
25 // No meta, unable to query sqlite_master. This is probably bad.
26 DEPRECATION_DATABASE_UNKNOWN,
28 // Failure querying meta table, corruption or similar problem likely.
29 DEPRECATION_FAILED_VERSION,
31 // Version key not found in meta table. Some sort of update error likely.
32 DEPRECATION_NO_VERSION,
34 // Version was out-dated, database successfully razed. Should only
35 // happen once per long-idle user, low volume expected.
36 DEPRECATION_RAZED,
38 // Version was out-dated, database raze failed. This user's
39 // database will be stuck.
40 DEPRECATION_RAZE_FAILED,
42 // Always keep this at the end.
43 DEPRECATION_EVENT_MAX,
46 void RecordDeprecationEvent(DeprecationEventType deprecation_event) {
47 UMA_HISTOGRAM_ENUMERATION("Sqlite.DeprecationVersionResult",
48 deprecation_event, DEPRECATION_EVENT_MAX);
51 } // namespace
53 namespace sql {
55 MetaTable::MetaTable() : db_(NULL) {
58 MetaTable::~MetaTable() {
61 // static
62 bool MetaTable::DoesTableExist(sql::Connection* db) {
63 DCHECK(db);
64 return db->DoesTableExist("meta");
67 // static
68 void MetaTable::RazeIfDeprecated(Connection* db, int deprecated_version) {
69 DCHECK_GT(deprecated_version, 0);
70 DCHECK_EQ(0, db->transaction_nesting());
72 if (!DoesTableExist(db)) {
73 sql::Statement s(db->GetUniqueStatement(
74 "SELECT COUNT(*) FROM sqlite_master"));
75 if (s.Step()) {
76 if (s.ColumnInt(0) != 0) {
77 RecordDeprecationEvent(DEPRECATION_DATABASE_NOT_EMPTY);
79 // NOTE(shess): Empty database at first run is expected, so
80 // don't histogram that case.
81 } else {
82 RecordDeprecationEvent(DEPRECATION_DATABASE_UNKNOWN);
84 return;
87 // TODO(shess): Share sql with PrepareGetStatement().
88 sql::Statement s(db->GetUniqueStatement(
89 "SELECT value FROM meta WHERE key=?"));
90 s.BindCString(0, kVersionKey);
91 if (!s.Step()) {
92 if (!s.Succeeded()) {
93 RecordDeprecationEvent(DEPRECATION_FAILED_VERSION);
94 } else {
95 RecordDeprecationEvent(DEPRECATION_NO_VERSION);
97 return;
100 int version = s.ColumnInt(0);
101 s.Clear(); // Clear potential automatic transaction for Raze().
102 if (version <= deprecated_version) {
103 if (db->Raze()) {
104 RecordDeprecationEvent(DEPRECATION_RAZED);
105 } else {
106 RecordDeprecationEvent(DEPRECATION_RAZE_FAILED);
108 return;
111 // NOTE(shess): Successfully getting a version which is not
112 // deprecated is expected, so don't histogram that case.
115 bool MetaTable::Init(Connection* db, int version, int compatible_version) {
116 DCHECK(!db_ && db);
117 db_ = db;
119 // If values stored are null or missing entirely, 0 will be reported.
120 // Require new clients to start with a greater initial version.
121 DCHECK_GT(version, 0);
122 DCHECK_GT(compatible_version, 0);
124 // Make sure the table is created an populated atomically.
125 sql::Transaction transaction(db_);
126 if (!transaction.Begin())
127 return false;
129 if (!DoesTableExist(db)) {
130 if (!db_->Execute("CREATE TABLE meta"
131 "(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR)"))
132 return false;
134 // Note: there is no index over the meta table. We currently only have a
135 // couple of keys, so it doesn't matter. If we start storing more stuff in
136 // there, we should create an index.
137 SetVersionNumber(version);
138 SetCompatibleVersionNumber(compatible_version);
139 } else {
140 db_->AddTaggedHistogram("Sqlite.Version", GetVersionNumber());
142 return transaction.Commit();
145 void MetaTable::Reset() {
146 db_ = NULL;
149 void MetaTable::SetVersionNumber(int version) {
150 DCHECK_GT(version, 0);
151 SetValue(kVersionKey, version);
154 int MetaTable::GetVersionNumber() {
155 int version = 0;
156 return GetValue(kVersionKey, &version) ? version : 0;
159 void MetaTable::SetCompatibleVersionNumber(int version) {
160 DCHECK_GT(version, 0);
161 SetValue(kCompatibleVersionKey, version);
164 int MetaTable::GetCompatibleVersionNumber() {
165 int version = 0;
166 return GetValue(kCompatibleVersionKey, &version) ? version : 0;
169 bool MetaTable::SetValue(const char* key, const std::string& value) {
170 Statement s;
171 PrepareSetStatement(&s, key);
172 s.BindString(1, value);
173 return s.Run();
176 bool MetaTable::SetValue(const char* key, int value) {
177 Statement s;
178 PrepareSetStatement(&s, key);
179 s.BindInt(1, value);
180 return s.Run();
183 bool MetaTable::SetValue(const char* key, int64_t value) {
184 Statement s;
185 PrepareSetStatement(&s, key);
186 s.BindInt64(1, value);
187 return s.Run();
190 bool MetaTable::GetValue(const char* key, std::string* value) {
191 Statement s;
192 if (!PrepareGetStatement(&s, key))
193 return false;
195 *value = s.ColumnString(0);
196 return true;
199 bool MetaTable::GetValue(const char* key, int* value) {
200 Statement s;
201 if (!PrepareGetStatement(&s, key))
202 return false;
204 *value = s.ColumnInt(0);
205 return true;
208 bool MetaTable::GetValue(const char* key, int64_t* value) {
209 Statement s;
210 if (!PrepareGetStatement(&s, key))
211 return false;
213 *value = s.ColumnInt64(0);
214 return true;
217 bool MetaTable::DeleteKey(const char* key) {
218 DCHECK(db_);
219 Statement s(db_->GetUniqueStatement("DELETE FROM meta WHERE key=?"));
220 s.BindCString(0, key);
221 return s.Run();
224 void MetaTable::PrepareSetStatement(Statement* statement, const char* key) {
225 DCHECK(db_ && statement);
226 statement->Assign(db_->GetCachedStatement(SQL_FROM_HERE,
227 "INSERT OR REPLACE INTO meta (key,value) VALUES (?,?)"));
228 statement->BindCString(0, key);
231 bool MetaTable::PrepareGetStatement(Statement* statement, const char* key) {
232 DCHECK(db_ && statement);
233 statement->Assign(db_->GetCachedStatement(SQL_FROM_HERE,
234 "SELECT value FROM meta WHERE key=?"));
235 statement->BindCString(0, key);
236 return statement->Step();
239 } // namespace sql