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.
4
5 #include "sql/meta_table.h"
6
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"
13
14 namespace {
15
16 // Key used in our meta table for version numbers.
17 const char kVersionKey[] = "version";
18 const char kCompatibleVersionKey[] = "last_compatible_version";
19
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,
24
25 // No meta, unable to query sqlite_master. This is probably bad.
26 DEPRECATION_DATABASE_UNKNOWN,
27
28 // Failure querying meta table, corruption or similar problem likely.
29 DEPRECATION_FAILED_VERSION,
30
31 // Version key not found in meta table. Some sort of update error likely.
32 DEPRECATION_NO_VERSION,
33
34 // Version was out-dated, database successfully razed. Should only
35 // happen once per long-idle user, low volume expected.
36 DEPRECATION_RAZED,
37
38 // Version was out-dated, database raze failed. This user's
39 // database will be stuck.
40 DEPRECATION_RAZE_FAILED,
41
42 // Always keep this at the end.
43 DEPRECATION_EVENT_MAX,
44 };
45
RecordDeprecationEvent(DeprecationEventType deprecation_event)46 void RecordDeprecationEvent(DeprecationEventType deprecation_event) {
47 UMA_HISTOGRAM_ENUMERATION("Sqlite.DeprecationVersionResult",
48 deprecation_event, DEPRECATION_EVENT_MAX);
49 }
50
51 } // namespace
52
53 namespace sql {
54
MetaTable()55 MetaTable::MetaTable() : db_(NULL) {
56 }
57
~MetaTable()58 MetaTable::~MetaTable() {
59 }
60
61 // static
DoesTableExist(sql::Connection * db)62 bool MetaTable::DoesTableExist(sql::Connection* db) {
63 DCHECK(db);
64 return db->DoesTableExist("meta");
65 }
66
67 // static
RazeIfDeprecated(Connection * db,int deprecated_version)68 void MetaTable::RazeIfDeprecated(Connection* db, int deprecated_version) {
69 DCHECK_GT(deprecated_version, 0);
70 DCHECK_EQ(0, db->transaction_nesting());
71
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);
78 }
79 // NOTE(shess): Empty database at first run is expected, so
80 // don't histogram that case.
81 } else {
82 RecordDeprecationEvent(DEPRECATION_DATABASE_UNKNOWN);
83 }
84 return;
85 }
86
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);
96 }
97 return;
98 }
99
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);
107 }
108 return;
109 }
110
111 // NOTE(shess): Successfully getting a version which is not
112 // deprecated is expected, so don't histogram that case.
113 }
114
Init(Connection * db,int version,int compatible_version)115 bool MetaTable::Init(Connection* db, int version, int compatible_version) {
116 DCHECK(!db_ && db);
117 db_ = db;
118
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);
123
124 // Make sure the table is created an populated atomically.
125 sql::Transaction transaction(db_);
126 if (!transaction.Begin())
127 return false;
128
129 if (!DoesTableExist(db)) {
130 if (!db_->Execute("CREATE TABLE meta"
131 "(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR)"))
132 return false;
133
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());
141 }
142 return transaction.Commit();
143 }
144
Reset()145 void MetaTable::Reset() {
146 db_ = NULL;
147 }
148
SetVersionNumber(int version)149 void MetaTable::SetVersionNumber(int version) {
150 DCHECK_GT(version, 0);
151 SetValue(kVersionKey, version);
152 }
153
GetVersionNumber()154 int MetaTable::GetVersionNumber() {
155 int version = 0;
156 return GetValue(kVersionKey, &version) ? version : 0;
157 }
158
SetCompatibleVersionNumber(int version)159 void MetaTable::SetCompatibleVersionNumber(int version) {
160 DCHECK_GT(version, 0);
161 SetValue(kCompatibleVersionKey, version);
162 }
163
GetCompatibleVersionNumber()164 int MetaTable::GetCompatibleVersionNumber() {
165 int version = 0;
166 return GetValue(kCompatibleVersionKey, &version) ? version : 0;
167 }
168
SetValue(const char * key,const std::string & value)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();
174 }
175
SetValue(const char * key,int value)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();
181 }
182
SetValue(const char * key,int64 value)183 bool MetaTable::SetValue(const char* key, int64 value) {
184 Statement s;
185 PrepareSetStatement(&s, key);
186 s.BindInt64(1, value);
187 return s.Run();
188 }
189
GetValue(const char * key,std::string * value)190 bool MetaTable::GetValue(const char* key, std::string* value) {
191 Statement s;
192 if (!PrepareGetStatement(&s, key))
193 return false;
194
195 *value = s.ColumnString(0);
196 return true;
197 }
198
GetValue(const char * key,int * value)199 bool MetaTable::GetValue(const char* key, int* value) {
200 Statement s;
201 if (!PrepareGetStatement(&s, key))
202 return false;
203
204 *value = s.ColumnInt(0);
205 return true;
206 }
207
GetValue(const char * key,int64 * value)208 bool MetaTable::GetValue(const char* key, int64* value) {
209 Statement s;
210 if (!PrepareGetStatement(&s, key))
211 return false;
212
213 *value = s.ColumnInt64(0);
214 return true;
215 }
216
DeleteKey(const char * key)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();
222 }
223
PrepareSetStatement(Statement * statement,const char * key)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);
229 }
230
PrepareGetStatement(Statement * statement,const char * 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();
237 }
238
239 } // namespace sql
240