summaryrefslogtreecommitdiffstats
path: root/sql
diff options
context:
space:
mode:
authorshess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 02:19:17 +0000
committershess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 02:19:17 +0000
commitfe4e3de5276edc05c36979ee5383cddb425e35f1 (patch)
treef20c3e55a6f935b0b35fffe4cbaffe6eb38387b8 /sql
parentf8feb29903155d73053e7ea5cca63c523c2bd215 (diff)
downloadchromium_src-fe4e3de5276edc05c36979ee5383cddb425e35f1.zip
chromium_src-fe4e3de5276edc05c36979ee5383cddb425e35f1.tar.gz
chromium_src-fe4e3de5276edc05c36979ee5383cddb425e35f1.tar.bz2
Deprecate old Favicons database versions.
Histograms show a non-zero number of users with very old databases at startup. These old databases likely result from some sort of corruption or filesystem problem, and are unlikely to spontaneously progress forward. Since the number of databases involved is very low (<.03% of users), just delete them in hopes that that allows forward progress. Arbitrarily set a 2-year deprecation deadline for this database. This is a few weeks early, as version 5 landed on 2011-10-12. BUG=240396 Review URL: https://codereview.chromium.org/24443002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227433 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sql')
-rw-r--r--sql/meta_table.cc90
-rw-r--r--sql/meta_table.h14
-rw-r--r--sql/meta_table_unittest.cc47
-rw-r--r--sql/test/test_helpers.cc14
-rw-r--r--sql/test/test_helpers.h4
5 files changed, 166 insertions, 3 deletions
diff --git a/sql/meta_table.cc b/sql/meta_table.cc
index c7e803c..b1b9562 100644
--- a/sql/meta_table.cc
+++ b/sql/meta_table.cc
@@ -5,16 +5,52 @@
#include "sql/meta_table.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "sql/connection.h"
#include "sql/statement.h"
#include "sql/transaction.h"
-namespace sql {
+namespace {
// Key used in our meta table for version numbers.
-static const char kVersionKey[] = "version";
-static const char kCompatibleVersionKey[] = "last_compatible_version";
+const char kVersionKey[] = "version";
+const char kCompatibleVersionKey[] = "last_compatible_version";
+
+// Used to track success/failure of deprecation checks.
+enum DeprecationEventType {
+ // Database has info, but no meta table. This is probably bad.
+ DEPRECATION_DATABASE_NOT_EMPTY = 0,
+
+ // No meta, unable to query sqlite_master. This is probably bad.
+ DEPRECATION_DATABASE_UNKNOWN,
+
+ // Failure querying meta table, corruption or similar problem likely.
+ DEPRECATION_FAILED_VERSION,
+
+ // Version key not found in meta table. Some sort of update error likely.
+ DEPRECATION_NO_VERSION,
+
+ // Version was out-dated, database successfully razed. Should only
+ // happen once per long-idle user, low volume expected.
+ DEPRECATION_RAZED,
+
+ // Version was out-dated, database raze failed. This user's
+ // database will be stuck.
+ DEPRECATION_RAZE_FAILED,
+
+ // Always keep this at the end.
+ DEPRECATION_EVENT_MAX,
+};
+
+void RecordDeprecationEvent(DeprecationEventType deprecation_event) {
+ UMA_HISTOGRAM_ENUMERATION("Sqlite.DeprecationVersionResult",
+ deprecation_event, DEPRECATION_EVENT_MAX);
+}
+
+} // namespace
+
+namespace sql {
MetaTable::MetaTable() : db_(NULL) {
}
@@ -28,6 +64,54 @@ bool MetaTable::DoesTableExist(sql::Connection* db) {
return db->DoesTableExist("meta");
}
+// static
+void MetaTable::RazeIfDeprecated(Connection* db, int deprecated_version) {
+ DCHECK_GT(deprecated_version, 0);
+ DCHECK_EQ(0, db->transaction_nesting());
+
+ if (!DoesTableExist(db)) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT COUNT(*) FROM sqlite_master"));
+ if (s.Step()) {
+ if (s.ColumnInt(0) != 0) {
+ RecordDeprecationEvent(DEPRECATION_DATABASE_NOT_EMPTY);
+ }
+ // NOTE(shess): Empty database at first run is expected, so
+ // don't histogram that case.
+ } else {
+ RecordDeprecationEvent(DEPRECATION_DATABASE_UNKNOWN);
+ }
+ return;
+ }
+
+ // TODO(shess): Share sql with PrepareGetStatement().
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT value FROM meta WHERE key=?"));
+ s.BindCString(0, kVersionKey);
+ if (!s.Step()) {
+ if (!s.Succeeded()) {
+ RecordDeprecationEvent(DEPRECATION_FAILED_VERSION);
+ } else {
+ RecordDeprecationEvent(DEPRECATION_NO_VERSION);
+ }
+ return;
+ }
+
+ int version = s.ColumnInt(0);
+ s.Clear(); // Clear potential automatic transaction for Raze().
+ if (version <= deprecated_version) {
+ if (db->Raze()) {
+ RecordDeprecationEvent(DEPRECATION_RAZED);
+ } else {
+ RecordDeprecationEvent(DEPRECATION_RAZE_FAILED);
+ }
+ return;
+ }
+
+ // NOTE(shess): Successfully getting a version which is not
+ // deprecated is expected, so don't histogram that case.
+}
+
bool MetaTable::Init(Connection* db, int version, int compatible_version) {
DCHECK(!db_ && db);
db_ = db;
diff --git a/sql/meta_table.h b/sql/meta_table.h
index 0f4ee72..918a261 100644
--- a/sql/meta_table.h
+++ b/sql/meta_table.h
@@ -23,6 +23,20 @@ class SQL_EXPORT MetaTable {
// Returns true if the 'meta' table exists.
static bool DoesTableExist(Connection* db);
+ // If the current version of the database is less than or equal to
+ // |deprecated_version|, raze the database. Must be called outside
+ // of a transaction.
+ // TODO(shess): At this time the database is razed IFF meta exists
+ // and contains a version row with value <= deprecated_version. It
+ // may make sense to also raze if meta exists but has no version
+ // row, or if meta doesn't exist. In those cases if the database is
+ // not already empty, it probably resulted from a broken
+ // initialization.
+ // TODO(shess): Folding this into Init() would allow enforcing
+ // |deprecated_version|<|version|. But Init() is often called in a
+ // transaction.
+ static void RazeIfDeprecated(Connection* db, int deprecated_version);
+
// Initializes the MetaTableHelper, creating the meta table if necessary. For
// new tables, it will initialize the version number to |version| and the
// compatible version number to |compatible_version|. Versions must be
diff --git a/sql/meta_table_unittest.cc b/sql/meta_table_unittest.cc
index 3fbc499..2ffb4bd 100644
--- a/sql/meta_table_unittest.cc
+++ b/sql/meta_table_unittest.cc
@@ -41,6 +41,53 @@ TEST_F(SQLMetaTableTest, DoesTableExist) {
EXPECT_TRUE(sql::MetaTable::DoesTableExist(&db()));
}
+TEST_F(SQLMetaTableTest, RazeIfDeprecated) {
+ const int kDeprecatedVersion = 1;
+ const int kVersion = 2;
+
+ // Setup a current database.
+ {
+ sql::MetaTable meta_table;
+ EXPECT_TRUE(meta_table.Init(&db(), kVersion, kVersion));
+ EXPECT_TRUE(db().Execute("CREATE TABLE t(c)"));
+ EXPECT_TRUE(db().DoesTableExist("t"));
+ }
+
+ // Table should should still exist if the database version is new enough.
+ sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
+ EXPECT_TRUE(db().DoesTableExist("t"));
+
+ // TODO(shess): It may make sense to Raze() if meta isn't present or
+ // version isn't present. See meta_table.h TODO on RazeIfDeprecated().
+
+ // Table should still exist if the version is not available.
+ EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'"));
+ {
+ sql::MetaTable meta_table;
+ EXPECT_TRUE(meta_table.Init(&db(), kVersion, kVersion));
+ EXPECT_EQ(0, meta_table.GetVersionNumber());
+ }
+ sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
+ EXPECT_TRUE(db().DoesTableExist("t"));
+
+ // Table should still exist if meta table is missing.
+ EXPECT_TRUE(db().Execute("DROP TABLE meta"));
+ sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
+ EXPECT_TRUE(db().DoesTableExist("t"));
+
+ // Setup meta with deprecated version.
+ {
+ sql::MetaTable meta_table;
+ EXPECT_TRUE(meta_table.Init(&db(), kDeprecatedVersion, kDeprecatedVersion));
+ }
+
+ // Deprecation check should remove the table.
+ EXPECT_TRUE(db().DoesTableExist("t"));
+ sql::MetaTable::RazeIfDeprecated(&db(), kDeprecatedVersion);
+ EXPECT_FALSE(sql::MetaTable::DoesTableExist(&db()));
+ EXPECT_FALSE(db().DoesTableExist("t"));
+}
+
TEST_F(SQLMetaTableTest, VersionNumber) {
// Compatibility versions one less than the main versions to make
// sure the values aren't being crossed with each other.
diff --git a/sql/test/test_helpers.cc b/sql/test/test_helpers.cc
index 607f146..20471dc 100644
--- a/sql/test/test_helpers.cc
+++ b/sql/test/test_helpers.cc
@@ -55,6 +55,20 @@ size_t CountTableColumns(sql::Connection* db, const char* table) {
return rows;
}
+bool CountTableRows(sql::Connection* db, const char* table, size_t* count) {
+ // TODO(shess): Table should probably be quoted with [] or "". See
+ // http://www.sqlite.org/lang_keywords.html . Meanwhile, odd names
+ // will throw an error.
+ std::string sql = "SELECT COUNT(*) FROM ";
+ sql += table;
+ sql::Statement s(db->GetUniqueStatement(sql.c_str()));
+ if (!s.Step())
+ return false;
+
+ *count = s.ColumnInt64(0);
+ return true;
+}
+
bool CreateDatabaseFromSQL(const base::FilePath& db_path,
const base::FilePath& sql_path) {
if (base::PathExists(db_path) || !base::PathExists(sql_path))
diff --git a/sql/test/test_helpers.h b/sql/test/test_helpers.h
index 2e01ecd..330f59a 100644
--- a/sql/test/test_helpers.h
+++ b/sql/test/test_helpers.h
@@ -32,6 +32,10 @@ size_t CountSQLIndices(sql::Connection* db) WARN_UNUSED_RESULT;
size_t CountTableColumns(sql::Connection* db, const char* table)
WARN_UNUSED_RESULT;
+// Sets |*count| to the number of rows in |table|. Returns false in
+// case of error, such as the table not existing.
+bool CountTableRows(sql::Connection* db, const char* table, size_t* count);
+
// Creates a SQLite database at |db_path| from the sqlite .dump output
// at |sql_path|. Returns false if |db_path| already exists, or if
// sql_path does not exist or cannot be read, or if there is an error