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 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 55 MetaTable::MetaTable() : db_(NULL) { 56 } 57 58 MetaTable::~MetaTable() { 59 } 60 61 // static 62 bool MetaTable::DoesTableExist(sql::Connection* db) { 63 DCHECK(db); 64 return db->DoesTableExist("meta"); 65 } 66 67 // static 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 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 145 void MetaTable::Reset() { 146 db_ = NULL; 147 } 148 149 void MetaTable::SetVersionNumber(int version) { 150 DCHECK_GT(version, 0); 151 SetValue(kVersionKey, version); 152 } 153 154 int MetaTable::GetVersionNumber() { 155 int version = 0; 156 return GetValue(kVersionKey, &version) ? version : 0; 157 } 158 159 void MetaTable::SetCompatibleVersionNumber(int version) { 160 DCHECK_GT(version, 0); 161 SetValue(kCompatibleVersionKey, version); 162 } 163 164 int MetaTable::GetCompatibleVersionNumber() { 165 int version = 0; 166 return GetValue(kCompatibleVersionKey, &version) ? version : 0; 167 } 168 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 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 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 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 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 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 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 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 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