Home | History | Annotate | Download | only in sql
      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