Commit 9243479d authored by Sky Malice's avatar Sky Malice Committed by Commit Bot

Increase precision on keywords db timestamps.

On disk timestamps were using second resolution, while in memory and
sync representations were using microsecond. This meant that by round
tripping data to disk, the actual values would chance slightly. There
were several places that compared timestamps, and these difference did
no fix themselves, but rather resulted in repeated churn. The database
columns were already 64 bit integers, so this change doesn't even
modify the schema, just changes the format of data stored.

Bug: 775049
Change-Id: I03886d249c4b9195f20b897d309da6063db4bf6f
Reviewed-on: https://chromium-review.googlesource.com/806646
Commit-Queue: Sky Malice <skym@chromium.org>
Reviewed-by: default avatarPeter Kasting <pkasting@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521965}
parent 757828ad
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <tuple>
#include "base/json/json_reader.h" #include "base/json/json_reader.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
...@@ -102,7 +103,6 @@ const std::string ColumnsForVersion(int version, bool concatenated) { ...@@ -102,7 +103,6 @@ const std::string ColumnsForVersion(int version, bool concatenated) {
return base::JoinString(columns, std::string(concatenated ? " || " : ", ")); return base::JoinString(columns, std::string(concatenated ? " || " : ", "));
} }
// Inserts the data from |data| into |s|. |s| is assumed to have slots for all // Inserts the data from |data| into |s|. |s| is assumed to have slots for all
// the columns in the keyword table. |id_column| is the slot number to bind // the columns in the keyword table. |id_column| is the slot number to bind
// |data|'s |id| to; |starting_column| is the slot number of the first of a // |data|'s |id| to; |starting_column| is the slot number of the first of a
...@@ -132,14 +132,16 @@ void BindURLToStatement(const TemplateURLData& data, ...@@ -132,14 +132,16 @@ void BindURLToStatement(const TemplateURLData& data,
s->BindString(starting_column + 5, data.originating_url.is_valid() ? s->BindString(starting_column + 5, data.originating_url.is_valid() ?
history::URLDatabase::GURLToDatabaseURL(data.originating_url) : history::URLDatabase::GURLToDatabaseURL(data.originating_url) :
std::string()); std::string());
s->BindInt64(starting_column + 6, data.date_created.ToTimeT()); s->BindInt64(starting_column + 6,
data.date_created.since_origin().InMicroseconds());
s->BindInt(starting_column + 7, data.usage_count); s->BindInt(starting_column + 7, data.usage_count);
s->BindString(starting_column + 8, s->BindString(starting_column + 8,
base::JoinString(data.input_encodings, ";")); base::JoinString(data.input_encodings, ";"));
s->BindString(starting_column + 9, data.suggestions_url); s->BindString(starting_column + 9, data.suggestions_url);
s->BindInt(starting_column + 10, data.prepopulate_id); s->BindInt(starting_column + 10, data.prepopulate_id);
s->BindBool(starting_column + 11, data.created_by_policy); s->BindBool(starting_column + 11, data.created_by_policy);
s->BindInt64(starting_column + 12, data.last_modified.ToTimeT()); s->BindInt64(starting_column + 12,
data.last_modified.since_origin().InMicroseconds());
s->BindString(starting_column + 13, data.sync_guid); s->BindString(starting_column + 13, data.sync_guid);
s->BindString(starting_column + 14, alternate_urls); s->BindString(starting_column + 14, alternate_urls);
s->BindString(starting_column + 15, data.image_url); s->BindString(starting_column + 15, data.image_url);
...@@ -147,7 +149,8 @@ void BindURLToStatement(const TemplateURLData& data, ...@@ -147,7 +149,8 @@ void BindURLToStatement(const TemplateURLData& data,
s->BindString(starting_column + 17, data.suggestions_url_post_params); s->BindString(starting_column + 17, data.suggestions_url_post_params);
s->BindString(starting_column + 18, data.image_url_post_params); s->BindString(starting_column + 18, data.image_url_post_params);
s->BindString(starting_column + 19, data.new_tab_url); s->BindString(starting_column + 19, data.new_tab_url);
s->BindInt64(starting_column + 20, data.last_visited.ToTimeT()); s->BindInt64(starting_column + 20,
data.last_visited.since_origin().InMicroseconds());
} }
WebDatabaseTable::TypeKey GetKey() { WebDatabaseTable::TypeKey GetKey() {
...@@ -222,6 +225,9 @@ bool KeywordTable::MigrateToVersion(int version, ...@@ -222,6 +225,9 @@ bool KeywordTable::MigrateToVersion(int version,
case 76: case 76:
*update_compatible_version = true; *update_compatible_version = true;
return MigrateToVersion76RemoveInstantColumns(); return MigrateToVersion76RemoveInstantColumns();
case 77:
*update_compatible_version = true;
return MigrateToVersion77IncreaseTimePrecision();
} }
return true; return true;
...@@ -396,6 +402,42 @@ bool KeywordTable::MigrateToVersion76RemoveInstantColumns() { ...@@ -396,6 +402,42 @@ bool KeywordTable::MigrateToVersion76RemoveInstantColumns() {
transaction.Commit(); transaction.Commit();
} }
bool KeywordTable::MigrateToVersion77IncreaseTimePrecision() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
std::string query(
"SELECT id, date_created, last_modified, last_visited FROM keywords");
sql::Statement s(db_->GetUniqueStatement(query.c_str()));
std::vector<std::tuple<TemplateURLID, Time, Time, Time>> updates;
while (s.Step()) {
updates.push_back(std::make_tuple(
s.ColumnInt64(0), Time::FromTimeT(s.ColumnInt64(1)),
Time::FromTimeT(s.ColumnInt64(2)), Time::FromTimeT(s.ColumnInt64(3))));
}
if (!s.Succeeded())
return false;
for (auto tuple : updates) {
sql::Statement update_statement(db_->GetCachedStatement(
SQL_FROM_HERE,
"UPDATE keywords SET date_created = ?, last_modified = ?, last_visited "
"= ? WHERE id = ? "));
update_statement.BindInt64(
0, std::get<1>(tuple).since_origin().InMicroseconds());
update_statement.BindInt64(
1, std::get<2>(tuple).since_origin().InMicroseconds());
update_statement.BindInt64(
2, std::get<3>(tuple).since_origin().InMicroseconds());
update_statement.BindInt64(3, std::get<0>(tuple));
if (!update_statement.Run()) {
return false;
}
}
return transaction.Commit();
}
// static // static
bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s, bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
TemplateURLData* data) { TemplateURLData* data) {
...@@ -422,8 +464,10 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s, ...@@ -422,8 +464,10 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
data->input_encodings = base::SplitString( data->input_encodings = base::SplitString(
s.ColumnString(9), ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); s.ColumnString(9), ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
data->id = s.ColumnInt64(0); data->id = s.ColumnInt64(0);
data->date_created = Time::FromTimeT(s.ColumnInt64(7)); data->date_created =
data->last_modified = Time::FromTimeT(s.ColumnInt64(13)); base::Time() + base::TimeDelta::FromMicroseconds(s.ColumnInt64(7));
data->last_modified =
base::Time() + base::TimeDelta::FromMicroseconds(s.ColumnInt64(13));
data->created_by_policy = s.ColumnBool(12); data->created_by_policy = s.ColumnBool(12);
data->usage_count = s.ColumnInt(8); data->usage_count = s.ColumnInt(8);
data->prepopulate_id = s.ColumnInt(11); data->prepopulate_id = s.ColumnInt(11);
...@@ -442,7 +486,8 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s, ...@@ -442,7 +486,8 @@ bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s,
} }
} }
data->last_visited = Time::FromTimeT(s.ColumnInt64(21)); data->last_visited =
base::Time() + base::TimeDelta::FromMicroseconds(s.ColumnInt64(21));
return true; return true;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
...@@ -126,6 +127,7 @@ class KeywordTable : public WebDatabaseTable { ...@@ -126,6 +127,7 @@ class KeywordTable : public WebDatabaseTable {
bool MigrateToVersion68RemoveShowInDefaultListColumn(); bool MigrateToVersion68RemoveShowInDefaultListColumn();
bool MigrateToVersion69AddLastVisitedColumn(); bool MigrateToVersion69AddLastVisitedColumn();
bool MigrateToVersion76RemoveInstantColumns(); bool MigrateToVersion76RemoveInstantColumns();
bool MigrateToVersion77IncreaseTimePrecision();
private: private:
friend class KeywordTableTest; friend class KeywordTableTest;
......
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);
INSERT INTO "meta" VALUES('mmap_status','-1');
INSERT INTO "meta" VALUES('version','76');
INSERT INTO "meta" VALUES('last_compatible_version','72');
CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB);
CREATE TABLE keywords (id INTEGER PRIMARY KEY,short_name VARCHAR NOT NULL,keyword VARCHAR NOT NULL,favicon_url VARCHAR NOT NULL,url VARCHAR NOT NULL,safe_for_autoreplace INTEGER,originating_url VARCHAR,date_created INTEGER DEFAULT 0,usage_count INTEGER DEFAULT 0,input_encodings VARCHAR,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR,last_visited INTEGER DEFAULT 0);
INSERT INTO "keywords" VALUES(2,'Google','google.com','https://www.google.com/favicon.ico','{google:baseURL}search?q={searchTerms}&{google:RLZ}{google:originalQueryForSuggestion}{google:assistedQueryStats}{google:searchFieldtrialParameter}{google:iOSSearchLanguage}{google:searchClient}{google:sourceId}{google:contextualSearchVersion}ie={inputEncoding}',1,'',123,0,'UTF-8','{google:baseSuggestURL}search?{google:searchFieldtrialParameter}client={google:suggestClient}&gs_ri={google:suggestRid}&xssi=t&q={searchTerms}&{google:inputType}{google:cursorPosition}{google:currentPageUrl}{google:pageClassification}{google:searchVersion}{google:sessionToken}{google:prefetchQuery}sugkey={google:suggestAPIKeyParameter}',1,0,456,'1f2ebc45-c5bc-4c15-afc1-e215b3a0ac96','["{google:baseURL}#q={searchTerms}","{google:baseURL}search#q={searchTerms}","{google:baseURL}webhp#q={searchTerms}","{google:baseURL}s#q={searchTerms}","{google:baseURL}s?q={searchTerms}"]','{google:baseURL}searchbyimage/upload','','','encoded_image={google:imageThumbnail},image_url={google:imageURL},sbisrc={google:imageSearchSource},original_width={google:imageOriginalWidth},original_height={google:imageOriginalHeight}','{google:baseURL}_/chrome/newtab?{google:RLZ}ie={inputEncoding}',789);
CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, date_created INTEGER DEFAULT 0, date_last_used INTEGER DEFAULT 0, count INTEGER DEFAULT 1, PRIMARY KEY (name, value));
CREATE TABLE credit_cards ( guid VARCHAR PRIMARY KEY, name_on_card VARCHAR, expiration_month INTEGER, expiration_year INTEGER, card_number_encrypted BLOB, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
CREATE TABLE autofill_profiles ( guid VARCHAR PRIMARY KEY, company_name VARCHAR, street_address VARCHAR, dependent_locality VARCHAR, city VARCHAR, state VARCHAR, zipcode VARCHAR, sorting_code VARCHAR, country_code VARCHAR, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT '', language_code VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, validity_bitfield UNSIGNED NOT NULL DEFAULT 0);
CREATE TABLE autofill_profile_names ( guid VARCHAR, first_name VARCHAR, middle_name VARCHAR, last_name VARCHAR, full_name VARCHAR);
CREATE TABLE autofill_profile_emails ( guid VARCHAR, email VARCHAR);
CREATE TABLE autofill_profile_phones ( guid VARCHAR, number VARCHAR);
CREATE TABLE autofill_profiles_trash ( guid VARCHAR);
CREATE TABLE masked_credit_cards (id VARCHAR,status VARCHAR,name_on_card VARCHAR,network VARCHAR,last_four VARCHAR,exp_month INTEGER DEFAULT 0,exp_year INTEGER DEFAULT 0, bank_name VARCHAR, type INTEGER DEFAULT 0);
CREATE TABLE unmasked_credit_cards (id VARCHAR,card_number_encrypted VARCHAR, use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, unmask_date INTEGER NOT NULL DEFAULT 0);
CREATE TABLE server_card_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, billing_address_id VARCHAR);
CREATE TABLE server_addresses (id VARCHAR,company_name VARCHAR,street_address VARCHAR,address_1 VARCHAR,address_2 VARCHAR,address_3 VARCHAR,address_4 VARCHAR,postal_code VARCHAR,sorting_code VARCHAR,country_code VARCHAR,language_code VARCHAR, recipient_name VARCHAR, phone_number VARCHAR);
CREATE TABLE server_address_metadata (id VARCHAR NOT NULL,use_count INTEGER NOT NULL DEFAULT 0, use_date INTEGER NOT NULL DEFAULT 0, has_converted BOOL NOT NULL DEFAULT FALSE);
CREATE TABLE autofill_sync_metadata (storage_key VARCHAR PRIMARY KEY NOT NULL,value BLOB);
CREATE TABLE autofill_model_type_state (id INTEGER PRIMARY KEY, value BLOB);
CREATE INDEX autofill_name ON autofill (name);
CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower);
COMMIT;
...@@ -63,6 +63,7 @@ bundle_data("unit_tests_bundle_data") { ...@@ -63,6 +63,7 @@ bundle_data("unit_tests_bundle_data") {
"//components/test/data/web_database/version_73_with_type_column.sql", "//components/test/data/web_database/version_73_with_type_column.sql",
"//components/test/data/web_database/version_74.sql", "//components/test/data/web_database/version_74.sql",
"//components/test/data/web_database/version_75.sql", "//components/test/data/web_database/version_75.sql",
"//components/test/data/web_database/version_76.sql",
] ]
outputs = [ outputs = [
"{{bundle_resources_dir}}/" + "{{bundle_resources_dir}}/" +
......
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
// corresponding changes must happen in the unit tests, and new migration test // corresponding changes must happen in the unit tests, and new migration test
// added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|. // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|.
// static // static
const int WebDatabase::kCurrentVersionNumber = 76; const int WebDatabase::kCurrentVersionNumber = 77;
const int WebDatabase::kDeprecatedVersionNumber = 51; const int WebDatabase::kDeprecatedVersionNumber = 51;
namespace { namespace {
const int kCompatibleVersionNumber = 72; const int kCompatibleVersionNumber = 77;
// Change the version number and possibly the compatibility version of // Change the version number and possibly the compatibility version of
// |meta_table_|. // |meta_table_|.
......
...@@ -130,7 +130,7 @@ class WebDatabaseMigrationTest : public testing::Test { ...@@ -130,7 +130,7 @@ class WebDatabaseMigrationTest : public testing::Test {
DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest); DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest);
}; };
const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 76; const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 77;
void WebDatabaseMigrationTest::LoadDatabase( void WebDatabaseMigrationTest::LoadDatabase(
const base::FilePath::StringType& file) { const base::FilePath::StringType& file) {
...@@ -1505,3 +1505,46 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion75ToCurrent) { ...@@ -1505,3 +1505,46 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion75ToCurrent) {
connection.DoesColumnExist("keywords", "search_terms_replacement_key")); connection.DoesColumnExist("keywords", "search_terms_replacement_key"));
} }
} }
// Tests changing format of three timestamp columns inside keywords.
TEST_F(WebDatabaseMigrationTest, MigrateVersion76ToCurrent) {
ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_76.sql")));
// Verify pre-conditions.
{
sql::Connection connection;
ASSERT_TRUE(connection.Open(GetDatabasePath()));
ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
sql::MetaTable meta_table;
ASSERT_TRUE(meta_table.Init(&connection, 76, 76));
sql::Statement s(connection.GetUniqueStatement(
"SELECT id, date_created, last_modified, last_visited FROM keywords"));
ASSERT_TRUE(s.Step());
EXPECT_EQ(2, s.ColumnInt64(0));
EXPECT_EQ(123, s.ColumnInt64(1));
EXPECT_EQ(456, s.ColumnInt64(2));
EXPECT_EQ(789, s.ColumnInt64(3));
}
DoMigration();
// Verify post-conditions.
{
sql::Connection connection;
ASSERT_TRUE(connection.Open(GetDatabasePath()));
ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection));
// Check version.
EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection));
sql::Statement s(connection.GetUniqueStatement(
"SELECT id, date_created, last_modified, last_visited FROM keywords"));
ASSERT_TRUE(s.Step());
EXPECT_EQ(2, s.ColumnInt64(0));
EXPECT_EQ(11644473723000000, s.ColumnInt64(1));
EXPECT_EQ(11644474056000000, s.ColumnInt64(2));
EXPECT_EQ(11644474389000000, s.ColumnInt64(3));
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment