• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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