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"
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.
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
);
55 MetaTable::MetaTable() : db_(NULL
) {
58 MetaTable::~MetaTable() {
62 bool MetaTable::DoesTableExist(sql::Connection
* db
) {
64 return db
->DoesTableExist("meta");
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"));
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.
82 RecordDeprecationEvent(DEPRECATION_DATABASE_UNKNOWN
);
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
);
93 RecordDeprecationEvent(DEPRECATION_FAILED_VERSION
);
95 RecordDeprecationEvent(DEPRECATION_NO_VERSION
);
100 int version
= s
.ColumnInt(0);
101 s
.Clear(); // Clear potential automatic transaction for Raze().
102 if (version
<= deprecated_version
) {
104 RecordDeprecationEvent(DEPRECATION_RAZED
);
106 RecordDeprecationEvent(DEPRECATION_RAZE_FAILED
);
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
) {
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())
129 if (!DoesTableExist(db
)) {
130 if (!db_
->Execute("CREATE TABLE meta"
131 "(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR)"))
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
);
140 db_
->AddTaggedHistogram("Sqlite.Version", GetVersionNumber());
142 return transaction
.Commit();
145 void MetaTable::Reset() {
149 void MetaTable::SetVersionNumber(int version
) {
150 DCHECK_GT(version
, 0);
151 SetValue(kVersionKey
, version
);
154 int MetaTable::GetVersionNumber() {
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() {
166 return GetValue(kCompatibleVersionKey
, &version
) ? version
: 0;
169 bool MetaTable::SetValue(const char* key
, const std::string
& value
) {
171 PrepareSetStatement(&s
, key
);
172 s
.BindString(1, value
);
176 bool MetaTable::SetValue(const char* key
, int value
) {
178 PrepareSetStatement(&s
, key
);
183 bool MetaTable::SetValue(const char* key
, int64_t value
) {
185 PrepareSetStatement(&s
, key
);
186 s
.BindInt64(1, value
);
190 bool MetaTable::GetValue(const char* key
, std::string
* value
) {
192 if (!PrepareGetStatement(&s
, key
))
195 *value
= s
.ColumnString(0);
199 bool MetaTable::GetValue(const char* key
, int* value
) {
201 if (!PrepareGetStatement(&s
, key
))
204 *value
= s
.ColumnInt(0);
208 bool MetaTable::GetValue(const char* key
, int64_t* value
) {
210 if (!PrepareGetStatement(&s
, key
))
213 *value
= s
.ColumnInt64(0);
217 bool MetaTable::DeleteKey(const char* key
) {
219 Statement
s(db_
->GetUniqueStatement("DELETE FROM meta WHERE key=?"));
220 s
.BindCString(0, 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
);
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();