diff options
7 files changed, 522 insertions, 879 deletions
diff --git a/components/autofill/core/browser/webdata/autofill_table.cc b/components/autofill/core/browser/webdata/autofill_table.cc index 23c5b05..039942f 100644 --- a/components/autofill/core/browser/webdata/autofill_table.cc +++ b/components/autofill/core/browser/webdata/autofill_table.cc @@ -5,7 +5,6 @@ #include "components/autofill/core/browser/webdata/autofill_table.h" #include <algorithm> -#include <cmath> #include <limits> #include <map> #include <set> @@ -15,7 +14,6 @@ #include "base/guid.h" #include "base/i18n/case_conversion.h" #include "base/logging.h" -#include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -40,27 +38,14 @@ using base::Time; namespace autofill { namespace { +typedef std::vector<Tuple3<int64, base::string16, base::string16> > + AutofillElementList; + template<typename T> T* address_of(T& v) { return &v; } -// Helper struct for AutofillTable::RemoveFormElementsAddedBetween(). -// Contains all the necessary fields to update a row in the 'autofill' table. -struct AutofillUpdate { - base::string16 name; - base::string16 value; - time_t date_created; - time_t date_last_used; - int count; -}; - -// Rounds a positive floating point number to the nearest integer. -int Round(float f) { - DCHECK_GE(f, 0.f); - return base::checked_cast<int>(std::floor(f + 0.5f)); -} - // Returns the |data_model|'s value corresponding to the |type|, trimmed to the // maximum length that can be stored in a column of the Autofill database. base::string16 GetInfo(const AutofillDataModel& data_model, @@ -379,9 +364,10 @@ WebDatabaseTable::TypeKey AutofillTable::GetTypeKey() const { bool AutofillTable::Init(sql::Connection* db, sql::MetaTable* meta_table) { WebDatabaseTable::Init(db, meta_table); - return (InitMainTable() && InitCreditCardsTable() && InitProfilesTable() && - InitProfileNamesTable() && InitProfileEmailsTable() && - InitProfilePhonesTable() && InitProfileTrashTable()); + return (InitMainTable() && InitCreditCardsTable() && InitDatesTable() && + InitProfilesTable() && InitProfileNamesTable() && + InitProfileEmailsTable() && InitProfilePhonesTable() && + InitProfileTrashTable()); } bool AutofillTable::IsSyncable() { @@ -393,7 +379,7 @@ bool AutofillTable::MigrateToVersion(int version, // Migrate if necessary. switch (version) { case 22: - return MigrateToVersion22ClearAutofillEmptyValueElements(); + return ClearAutofillEmptyValueElements(); case 23: return MigrateToVersion23AddCardNumberEncryptedColumn(); case 24: @@ -437,9 +423,6 @@ bool AutofillTable::MigrateToVersion(int version, case 54: *update_compatible_version = true; return MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields(); - case 55: - *update_compatible_version = true; - return MigrateToVersion55MergeAutofillDatesTable(); } return true; } @@ -496,7 +479,8 @@ bool AutofillTable::GetFormValuesForElementName( } bool AutofillTable::HasFormElements() { - sql::Statement s(db_->GetUniqueStatement("SELECT COUNT(*) FROM autofill")); + sql::Statement s(db_->GetUniqueStatement( + "SELECT COUNT(*) FROM autofill")); if (!s.Step()) { NOTREACHED(); return false; @@ -508,139 +492,264 @@ bool AutofillTable::RemoveFormElementsAddedBetween( const Time& delete_begin, const Time& delete_end, std::vector<AutofillChange>* changes) { - const time_t delete_begin_time_t = delete_begin.ToTimeT(); - const time_t delete_end_time_t = GetEndTime(delete_end); - - // Query for the name, value, count, and access dates of all form elements - // that were used between the given times. + DCHECK(changes); + // Query for the pair_id, name, and value of all form elements that + // were used between the given times. sql::Statement s(db_->GetUniqueStatement( - "SELECT name, value, count, date_created, date_last_used FROM autofill " - "WHERE (date_created >= ? AND date_created < ?) OR " - " (date_last_used >= ? AND date_last_used < ?)")); - s.BindInt64(0, delete_begin_time_t); - s.BindInt64(1, delete_end_time_t); - s.BindInt64(2, delete_begin_time_t); - s.BindInt64(3, delete_end_time_t); - - std::vector<AutofillUpdate> updates; - std::vector<AutofillChange> tentative_changes; + "SELECT DISTINCT a.pair_id, a.name, a.value " + "FROM autofill_dates ad JOIN autofill a ON ad.pair_id = a.pair_id " + "WHERE ad.date_created >= ? AND ad.date_created < ?")); + s.BindInt64(0, delete_begin.ToTimeT()); + s.BindInt64(1, + (delete_end.is_null() || delete_end == base::Time::Max()) ? + std::numeric_limits<int64>::max() : + delete_end.ToTimeT()); + + AutofillElementList elements; while (s.Step()) { - base::string16 name = s.ColumnString16(0); - base::string16 value = s.ColumnString16(1); - int count = s.ColumnInt(2); - time_t date_created_time_t = s.ColumnInt64(3); - time_t date_last_used_time_t = s.ColumnInt64(4); - - // If *all* uses of the element were between |delete_begin| and - // |delete_end|, then delete the element. Otherwise, update the use - // timestamps and use count. - AutofillChange::Type change_type; - if (date_created_time_t >= delete_begin_time_t && - date_last_used_time_t < delete_end_time_t) { - change_type = AutofillChange::REMOVE; - } else { - change_type = AutofillChange::UPDATE; - - // For all updated elements, set either date_created or date_last_used so - // that the range [date_created, date_last_used] no longer overlaps with - // [delete_begin, delete_end). Update the count by interpolating. - // Precisely, compute the average amount of time between increments to the - // count in the original range [date_created, date_last_used]: - // avg_delta = (date_last_used_orig - date_created_orig) / (count - 1) - // The count can be exressed as - // count = 1 + (date_last_used - date_created) / avg_delta - // Hence, update the count to - // count_new = 1 + (date_last_used_new - date_created_new) / avg_delta - // = 1 + ((count - 1) * - // (date_last_used_new - date_created_new) / - // (date_last_used_orig - date_created_orig)) - // Interpolating might not give a result that completely accurately - // reflects the user's history, but it's the best that can be done given - // the information in the database. - AutofillUpdate updated_entry; - updated_entry.name = name; - updated_entry.value = value; - updated_entry.date_created = - date_created_time_t < delete_begin_time_t ? - date_created_time_t : - delete_end_time_t; - updated_entry.date_last_used = - date_last_used_time_t >= delete_end_time_t ? - date_last_used_time_t : - delete_begin_time_t - 1; - updated_entry.count = - 1 + - Round(1.0 * (count - 1) * - (updated_entry.date_last_used - updated_entry.date_created) / - (date_last_used_time_t - date_created_time_t)); - updates.push_back(updated_entry); - } - - tentative_changes.push_back( - AutofillChange(change_type, AutofillKey(name, value))); + elements.push_back(MakeTuple(s.ColumnInt64(0), + s.ColumnString16(1), + s.ColumnString16(2))); } if (!s.Succeeded()) return false; - // As a single transaction, remove or update the elements appropriately. - sql::Statement s_delete(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE date_created >= ? AND date_last_used < ?")); - s_delete.BindInt64(0, delete_begin_time_t); - s_delete.BindInt64(1, delete_end_time_t); - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - if (!s_delete.Run()) - return false; - for (size_t i = 0; i < updates.size(); ++i) { - sql::Statement s_update(db_->GetUniqueStatement( - "UPDATE autofill SET date_created = ?, date_last_used = ?, count = ?" - "WHERE name = ? AND value = ?")); - s_update.BindInt64(0, updates[i].date_created); - s_update.BindInt64(1, updates[i].date_last_used); - s_update.BindInt(2, updates[i].count); - s_update.BindString16(3, updates[i].name); - s_update.BindString16(4, updates[i].value); - if (!s_update.Run()) + for (AutofillElementList::iterator itr = elements.begin(); + itr != elements.end(); ++itr) { + int how_many = 0; + if (!RemoveFormElementForTimeRange(itr->a, delete_begin, delete_end, + &how_many)) { return false; + } + // We store at most 2 time stamps. If we remove both of them we should + // delete the corresponding data. If we delete only one it could still be + // the last timestamp for the data, so check how many timestamps do remain. + bool should_remove = (CountTimestampsData(itr->a) == 0); + if (should_remove) { + if (!RemoveFormElementForID(itr->a)) + return false; + } else { + if (!AddToCountOfFormElement(itr->a, -how_many)) + return false; + } + AutofillChange::Type change_type = + should_remove ? AutofillChange::REMOVE : AutofillChange::UPDATE; + changes->push_back(AutofillChange(change_type, + AutofillKey(itr->b, itr->c))); } - if (!transaction.Commit()) - return false; - *changes = tentative_changes; return true; } bool AutofillTable::RemoveExpiredFormElements( std::vector<AutofillChange>* changes) { - base::Time expiration_time = AutofillEntry::ExpirationTime(); + DCHECK(changes); - // Query for the name and value of all form elements that were last used - // before the |expiration_time|. + base::Time delete_end = AutofillEntry::ExpirationTime(); + // Query for the pair_id, name, and value of all form elements that + // were last used before the |delete_end|. sql::Statement select_for_delete(db_->GetUniqueStatement( - "SELECT name, value FROM autofill WHERE date_last_used < ?")); - select_for_delete.BindInt64(0, expiration_time.ToTimeT()); - std::vector<AutofillChange> tentative_changes; + "SELECT DISTINCT pair_id, name, value " + "FROM autofill WHERE pair_id NOT IN " + "(SELECT DISTINCT pair_id " + "FROM autofill_dates WHERE date_created >= ?)")); + select_for_delete.BindInt64(0, delete_end.ToTimeT()); + AutofillElementList entries_to_delete; while (select_for_delete.Step()) { - base::string16 name = select_for_delete.ColumnString16(0); - base::string16 value = select_for_delete.ColumnString16(1); - tentative_changes.push_back( - AutofillChange(AutofillChange::REMOVE, AutofillKey(name, value))); + entries_to_delete.push_back(MakeTuple(select_for_delete.ColumnInt64(0), + select_for_delete.ColumnString16(1), + select_for_delete.ColumnString16(2))); } if (!select_for_delete.Succeeded()) return false; sql::Statement delete_data_statement(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE date_last_used < ?")); - delete_data_statement.BindInt64(0, expiration_time.ToTimeT()); + "DELETE FROM autofill WHERE pair_id NOT IN (" + "SELECT pair_id FROM autofill_dates WHERE date_created >= ?)")); + delete_data_statement.BindInt64(0, delete_end.ToTimeT()); if (!delete_data_statement.Run()) return false; - *changes = tentative_changes; + sql::Statement delete_times_statement(db_->GetUniqueStatement( + "DELETE FROM autofill_dates WHERE pair_id NOT IN (" + "SELECT pair_id FROM autofill_dates WHERE date_created >= ?)")); + delete_times_statement.BindInt64(0, delete_end.ToTimeT()); + if (!delete_times_statement.Run()) + return false; + + // Cull remaining entries' timestamps. + std::vector<AutofillEntry> entries; + if (!GetAllAutofillEntries(&entries)) + return false; + sql::Statement cull_date_entry(db_->GetUniqueStatement( + "DELETE FROM autofill_dates " + "WHERE pair_id == (SELECT pair_id FROM autofill " + "WHERE name = ? and value = ?)" + "AND date_created != ? AND date_created != ?")); + for (size_t i = 0; i < entries.size(); ++i) { + cull_date_entry.BindString16(0, entries[i].key().name()); + cull_date_entry.BindString16(1, entries[i].key().value()); + cull_date_entry.BindInt64(2, + entries[i].timestamps().empty() ? 0 : + entries[i].timestamps().front().ToTimeT()); + cull_date_entry.BindInt64(3, + entries[i].timestamps().empty() ? 0 : + entries[i].timestamps().back().ToTimeT()); + if (!cull_date_entry.Run()) + return false; + cull_date_entry.Reset(true); + } + + changes->clear(); + changes->reserve(entries_to_delete.size()); + + for (AutofillElementList::iterator it = entries_to_delete.begin(); + it != entries_to_delete.end(); ++it) { + changes->push_back(AutofillChange( + AutofillChange::REMOVE, AutofillKey(it->b, it->c))); + } return true; } +bool AutofillTable::RemoveFormElementForTimeRange(int64 pair_id, + const Time& delete_begin, + const Time& delete_end, + int* how_many) { + sql::Statement s(db_->GetUniqueStatement( + "DELETE FROM autofill_dates WHERE pair_id = ? AND " + "date_created >= ? AND date_created < ?")); + s.BindInt64(0, pair_id); + s.BindInt64(1, delete_begin.is_null() ? 0 : delete_begin.ToTimeT()); + s.BindInt64(2, delete_end.is_null() ? std::numeric_limits<int64>::max() : + delete_end.ToTimeT()); + + bool result = s.Run(); + if (how_many) + *how_many = db_->GetLastChangeCount(); + + return result; +} + +int AutofillTable::CountTimestampsData(int64 pair_id) { + sql::Statement s(db_->GetUniqueStatement( + "SELECT COUNT(*) FROM autofill_dates WHERE pair_id = ?")); + s.BindInt64(0, pair_id); + if (!s.Step()) { + NOTREACHED(); + return 0; + } else { + return s.ColumnInt(0); + } +} + +bool AutofillTable::AddToCountOfFormElement(int64 pair_id, + int delta) { + int count = 0; + + if (!GetCountOfFormElement(pair_id, &count)) + return false; + + if (count + delta == 0) { + // Should remove the element earlier in the code. + NOTREACHED(); + return false; + } else { + if (!SetCountOfFormElement(pair_id, count + delta)) + return false; + } + return true; +} + +bool AutofillTable::GetIDAndCountOfFormElement( + const FormFieldData& element, + int64* pair_id, + int* count) { + DCHECK(pair_id); + DCHECK(count); + + sql::Statement s(db_->GetUniqueStatement( + "SELECT pair_id, count FROM autofill " + "WHERE name = ? AND value = ?")); + s.BindString16(0, element.name); + s.BindString16(1, element.value); + + if (!s.is_valid()) + return false; + + *pair_id = 0; + *count = 0; + + if (s.Step()) { + *pair_id = s.ColumnInt64(0); + *count = s.ColumnInt(1); + } + + return true; +} + +bool AutofillTable::GetCountOfFormElement(int64 pair_id, int* count) { + DCHECK(count); + sql::Statement s(db_->GetUniqueStatement( + "SELECT count FROM autofill WHERE pair_id = ?")); + s.BindInt64(0, pair_id); + + if (s.Step()) { + *count = s.ColumnInt(0); + return true; + } + return false; +} + +bool AutofillTable::SetCountOfFormElement(int64 pair_id, int count) { + sql::Statement s(db_->GetUniqueStatement( + "UPDATE autofill SET count = ? WHERE pair_id = ?")); + s.BindInt(0, count); + s.BindInt64(1, pair_id); + + return s.Run(); +} + +bool AutofillTable::InsertFormElement(const FormFieldData& element, + int64* pair_id) { + DCHECK(pair_id); + sql::Statement s(db_->GetUniqueStatement( + "INSERT INTO autofill (name, value, value_lower) VALUES (?,?,?)")); + s.BindString16(0, element.name); + s.BindString16(1, element.value); + s.BindString16(2, base::i18n::ToLower(element.value)); + + if (!s.Run()) + return false; + + *pair_id = db_->GetLastInsertRowId(); + return true; +} + +bool AutofillTable::InsertPairIDAndDate(int64 pair_id, + const Time& date_created) { + sql::Statement s(db_->GetUniqueStatement( + "INSERT INTO autofill_dates " + "(pair_id, date_created) VALUES (?, ?)")); + s.BindInt64(0, pair_id); + s.BindInt64(1, date_created.ToTimeT()); + + return s.Run(); +} + +bool AutofillTable::DeleteLastAccess(int64 pair_id) { + // Inner SELECT selects the newest |date_created| for a given |pair_id|. + // DELETE deletes only that entry. + sql::Statement s(db_->GetUniqueStatement( + "DELETE FROM autofill_dates WHERE pair_id = ? and date_created IN " + "(SELECT date_created FROM autofill_dates WHERE pair_id = ? " + "ORDER BY date_created DESC LIMIT 1)")); + s.BindInt64(0, pair_id); + s.BindInt64(1, pair_id); + + return s.Run(); +} + bool AutofillTable::AddFormFieldValuesTime( const std::vector<FormFieldData>& elements, std::vector<AutofillChange>* changes, @@ -662,20 +771,75 @@ bool AutofillTable::AddFormFieldValuesTime( return result; } -bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { +bool AutofillTable::ClearAutofillEmptyValueElements() { sql::Statement s(db_->GetUniqueStatement( - "SELECT name, value, date_created, date_last_used FROM autofill")); + "SELECT pair_id FROM autofill WHERE TRIM(value)= \"\"")); + if (!s.is_valid()) + return false; + std::set<int64> ids; + while (s.Step()) + ids.insert(s.ColumnInt64(0)); + if (!s.Succeeded()) + return false; + + bool success = true; + for (std::set<int64>::const_iterator iter = ids.begin(); iter != ids.end(); + ++iter) { + if (!RemoveFormElementForID(*iter)) + success = false; + } + + return success; +} + +bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { + DCHECK(entries); + sql::Statement s(db_->GetUniqueStatement( + "SELECT name, value, date_created FROM autofill a JOIN " + "autofill_dates ad ON a.pair_id=ad.pair_id")); + + bool first_entry = true; + AutofillKey* current_key_ptr = NULL; + std::vector<Time>* timestamps_ptr = NULL; + base::string16 name, value; + Time time; while (s.Step()) { - base::string16 name = s.ColumnString16(0); - base::string16 value = s.ColumnString16(1); + name = s.ColumnString16(0); + value = s.ColumnString16(1); + time = Time::FromTimeT(s.ColumnInt64(2)); + + if (first_entry) { + current_key_ptr = new AutofillKey(name, value); + + timestamps_ptr = new std::vector<Time>; + timestamps_ptr->push_back(time); + + first_entry = false; + } else { + // we've encountered the next entry + if (current_key_ptr->name().compare(name) != 0 || + current_key_ptr->value().compare(value) != 0) { + AutofillEntry entry(*current_key_ptr, *timestamps_ptr); + entries->push_back(entry); + + delete current_key_ptr; + delete timestamps_ptr; + + current_key_ptr = new AutofillKey(name, value); + timestamps_ptr = new std::vector<Time>; + } + timestamps_ptr->push_back(time); + } + } - std::vector<Time> timestamps; - timestamps.push_back(Time::FromTimeT(s.ColumnInt64(2))); - timestamps.push_back(Time::FromTimeT(s.ColumnInt64(3))); - if (timestamps.front() == timestamps.back()) - timestamps.pop_back(); - entries->push_back(AutofillEntry(AutofillKey(name, value), timestamps)); + // If there is at least one result returned, first_entry will be false. + // For this case we need to do a final cleanup step. + if (!first_entry) { + AutofillEntry entry(*current_key_ptr, *timestamps_ptr); + entries->push_back(entry); + delete current_key_ptr; + delete timestamps_ptr; } return s.Succeeded(); @@ -684,22 +848,18 @@ bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) { bool AutofillTable::GetAutofillTimestamps(const base::string16& name, const base::string16& value, std::vector<Time>* timestamps) { + DCHECK(timestamps); sql::Statement s(db_->GetUniqueStatement( - "SELECT date_created, date_last_used FROM autofill " - "WHERE name = ? AND value = ?")); + "SELECT date_created FROM autofill a JOIN " + "autofill_dates ad ON a.pair_id=ad.pair_id " + "WHERE a.name = ? AND a.value = ?")); s.BindString16(0, name); s.BindString16(1, value); - if (!s.Step()) - return false; - timestamps->clear(); - timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0))); - timestamps->push_back(Time::FromTimeT(s.ColumnInt64(1))); - if (timestamps->front() == timestamps->back()) - timestamps->pop_back(); + while (s.Step()) + timestamps->push_back(Time::FromTimeT(s.ColumnInt64(0))); - DCHECK(!s.Step()); - return true; + return s.Succeeded(); } bool AutofillTable::UpdateAutofillEntries( @@ -708,17 +868,24 @@ bool AutofillTable::UpdateAutofillEntries( return true; // Remove all existing entries. - for (size_t i = 0; i < entries.size(); ++i) { - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE name = ? AND value = ?")); + for (size_t i = 0; i < entries.size(); i++) { + std::string sql = "SELECT pair_id FROM autofill " + "WHERE name = ? AND value = ?"; + sql::Statement s(db_->GetUniqueStatement(sql.c_str())); s.BindString16(0, entries[i].key().name()); s.BindString16(1, entries[i].key().value()); - if (!s.Run()) + + if (!s.is_valid()) return false; + + if (s.Step()) { + if (!RemoveFormElementForID(s.ColumnInt64(0))) + return false; + } } // Insert all the supplied autofill entries. - for (size_t i = 0; i < entries.size(); ++i) { + for (size_t i = 0; i < entries.size(); i++) { if (!InsertAutofillEntry(entries[i])) return false; } @@ -727,86 +894,69 @@ bool AutofillTable::UpdateAutofillEntries( } bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) { - if (entry.timestamps().empty()) - return false; - - Time date_created = entry.timestamps().front(); - Time date_last_used = date_created; - for (size_t i = 1; i < entry.timestamps().size(); ++i) { - const Time& timestamp = entry.timestamps()[i]; - date_created = std::min(date_created, timestamp); - date_last_used = std::max(date_last_used, timestamp); - } - - std::string sql = - "INSERT INTO autofill " - "(name, value, value_lower, date_created, date_last_used, count) " - "VALUES (?, ?, ?, ?, ?, ?)"; + std::string sql = "INSERT INTO autofill (name, value, value_lower, count) " + "VALUES (?, ?, ?, ?)"; sql::Statement s(db_->GetUniqueStatement(sql.c_str())); s.BindString16(0, entry.key().name()); s.BindString16(1, entry.key().value()); s.BindString16(2, base::i18n::ToLower(entry.key().value())); - s.BindInt64(3, date_created.ToTimeT()); - s.BindInt64(4, date_last_used.ToTimeT()); - // TODO(isherman): The counts column is currently synced implicitly as the - // number of timestamps. Sync the value explicitly instead, since the DB now - // only saves the first and last timestamp, which makes counting timestamps - // completely meaningless as a way to track frequency of usage. - s.BindInt(5, entry.timestamps().size()); - return s.Run(); + s.BindInt(3, entry.timestamps().size()); + + if (!s.Run()) + return false; + + int64 pair_id = db_->GetLastInsertRowId(); + for (size_t i = 0; i < entry.timestamps().size(); i++) { + if (!InsertPairIDAndDate(pair_id, entry.timestamps()[i])) + return false; + } + + return true; } bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element, std::vector<AutofillChange>* changes, Time time) { - sql::Statement s_exists(db_->GetUniqueStatement( - "SELECT COUNT(*) FROM autofill WHERE name = ? AND value = ?")); - s_exists.BindString16(0, element.name); - s_exists.BindString16(1, element.value); - if (!s_exists.Step()) + int count = 0; + int64 pair_id; + + if (!GetIDAndCountOfFormElement(element, &pair_id, &count)) return false; - bool already_exists = s_exists.ColumnInt(0) > 0; - if (already_exists) { - sql::Statement s(db_->GetUniqueStatement( - "UPDATE autofill SET date_last_used = ?, count = count + 1 " - "WHERE name = ? AND value = ?")); - s.BindInt64(0, time.ToTimeT()); - s.BindString16(1, element.name); - s.BindString16(2, element.value); - if (!s.Run()) - return false; - } else { - time_t time_as_time_t = time.ToTimeT(); - sql::Statement s(db_->GetUniqueStatement( - "INSERT INTO autofill " - "(name, value, value_lower, date_created, date_last_used, count) " - "VALUES (?, ?, ?, ?, ?, ?)")); - s.BindString16(0, element.name); - s.BindString16(1, element.value); - s.BindString16(2, base::i18n::ToLower(element.value)); - s.BindInt64(3, time_as_time_t); - s.BindInt64(4, time_as_time_t); - s.BindInt(5, 1); - if (!s.Run()) - return false; - } + if (count == 0 && !InsertFormElement(element, &pair_id)) + return false; + + if (!SetCountOfFormElement(pair_id, count + 1)) + return false; + + // If we already have more than 2 times delete last one, before adding new + // one. + if (count >= 2 && !DeleteLastAccess(pair_id)) + return false; + + if (!InsertPairIDAndDate(pair_id, time)) + return false; AutofillChange::Type change_type = - already_exists ? AutofillChange::UPDATE : AutofillChange::ADD; + count == 0 ? AutofillChange::ADD : AutofillChange::UPDATE; changes->push_back( - AutofillChange(change_type, AutofillKey(element.name, element.value))); + AutofillChange(change_type, + AutofillKey(element.name, element.value))); return true; } bool AutofillTable::RemoveFormElement(const base::string16& name, const base::string16& value) { + // Find the id for that pair. sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM autofill WHERE name = ? AND value= ?")); + "SELECT pair_id FROM autofill WHERE name = ? AND value= ?")); s.BindString16(0, name); s.BindString16(1, value); - return s.Run(); + + if (s.Step()) + return RemoveFormElementForID(s.ColumnInt64(0)); + return false; } bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) { @@ -1205,6 +1355,17 @@ bool AutofillTable::EmptyAutofillProfilesTrash() { } +bool AutofillTable::RemoveFormElementForID(int64 pair_id) { + sql::Statement s(db_->GetUniqueStatement( + "DELETE FROM autofill WHERE pair_id = ?")); + s.BindInt64(0, pair_id); + + if (s.Run()) + return RemoveFormElementForTimeRange(pair_id, Time(), Time(), NULL); + + return false; +} + bool AutofillTable::AddAutofillGUIDToTrash(const std::string& guid) { sql::Statement s(db_->GetUniqueStatement( "INSERT INTO autofill_profiles_trash" @@ -1239,12 +1400,16 @@ bool AutofillTable::InitMainTable() { "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))") || - !db_->Execute("CREATE INDEX autofill_name ON autofill (name)") || - !db_->Execute("CREATE INDEX autofill_name_value_lower ON " + "pair_id INTEGER PRIMARY KEY, " + "count INTEGER DEFAULT 1)")) { + NOTREACHED(); + return false; + } + if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)")) { + NOTREACHED(); + return false; + } + if (!db_->Execute("CREATE INDEX autofill_name_value_lower ON " "autofill (name, value_lower)")) { NOTREACHED(); return false; @@ -1271,6 +1436,23 @@ bool AutofillTable::InitCreditCardsTable() { return true; } +bool AutofillTable::InitDatesTable() { + if (!db_->DoesTableExist("autofill_dates")) { + if (!db_->Execute("CREATE TABLE autofill_dates ( " + "pair_id INTEGER DEFAULT 0, " + "date_created INTEGER DEFAULT 0)")) { + NOTREACHED(); + return false; + } + if (!db_->Execute("CREATE INDEX autofill_dates_pair_id ON " + "autofill_dates (pair_id)")) { + NOTREACHED(); + return false; + } + } + return true; +} + bool AutofillTable::InitProfilesTable() { if (!db_->DoesTableExist("autofill_profiles")) { if (!db_->Execute("CREATE TABLE autofill_profiles ( " @@ -1341,33 +1523,6 @@ bool AutofillTable::InitProfileTrashTable() { return true; } -bool AutofillTable::MigrateToVersion22ClearAutofillEmptyValueElements() { - sql::Statement s(db_->GetUniqueStatement( - "SELECT pair_id FROM autofill WHERE TRIM(value) = \"\"")); - if (!s.is_valid()) - return false; - - std::set<int64> ids; - while (s.Step()) - ids.insert(s.ColumnInt64(0)); - if (!s.Succeeded()) - return false; - - if (!db_->Execute("DELETE FROM autofill WHERE TRIM(value) = \"\"")) - return false; - - for (std::set<int64>::const_iterator it = ids.begin(); it != ids.end(); - ++it) { - sql::Statement s(db_->GetUniqueStatement( - "DELETE FROM autofill_dates WHERE pair_id = ?")); - s.BindInt64(0, *it); - if (!s.Run()) - return false; - } - - return true; -} - // Add the card_number_encrypted column if credit card table was not // created in this build (otherwise the column already exists). // WARNING: Do not change the order of the execution of the SQL @@ -2136,65 +2291,4 @@ bool AutofillTable::MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields() { return transaction.Commit(); } -bool AutofillTable::MigrateToVersion55MergeAutofillDatesTable() { - sql::Transaction transaction(db_); - if (!transaction.Begin()) - return false; - - if (db_->DoesTableExist("autofill_temp") || - !db_->Execute("CREATE TABLE autofill_temp (" - "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))")) { - return false; - } - - // Slurp up the data from the existing table and write it to the new table. - sql::Statement s(db_->GetUniqueStatement( - "SELECT name, value, value_lower, count, MIN(date_created)," - " MAX(date_created) " - "FROM autofill a JOIN autofill_dates ad ON a.pair_id=ad.pair_id " - "GROUP BY name, value, value_lower, count")); - while (s.Step()) { - sql::Statement s_insert(db_->GetUniqueStatement( - "INSERT INTO autofill_temp " - "(name, value, value_lower, count, date_created, date_last_used) " - "VALUES (?, ?, ?, ?, ?, ?)")); - s_insert.BindString16(0, s.ColumnString16(0)); - s_insert.BindString16(1, s.ColumnString16(1)); - s_insert.BindString16(2, s.ColumnString16(2)); - s_insert.BindInt(3, s.ColumnInt(3)); - s_insert.BindInt64(4, s.ColumnInt64(4)); - s_insert.BindInt64(5, s.ColumnInt64(5)); - if (!s_insert.Run()) - return false; - } - - if (!s.Succeeded()) - return false; - - // Delete the existing (version 54) tables and replace them with the contents - // of the temporary table. - if (!db_->Execute("DROP TABLE autofill") || - !db_->Execute("DROP TABLE autofill_dates") || - !db_->Execute("ALTER TABLE autofill_temp " - "RENAME TO autofill")) { - return false; - } - - // Create indices on the new table, for fast lookups. - if (!db_->Execute("CREATE INDEX autofill_name ON autofill (name)") || - !db_->Execute("CREATE INDEX autofill_name_value_lower ON " - "autofill (name, value_lower)")) { - return false; - } - - - return transaction.Commit(); -} - } // namespace autofill diff --git a/components/autofill/core/browser/webdata/autofill_table.h b/components/autofill/core/browser/webdata/autofill_table.h index 83aded5..bcacdcd 100644 --- a/components/autofill/core/browser/webdata/autofill_table.h +++ b/components/autofill/core/browser/webdata/autofill_table.h @@ -38,13 +38,17 @@ struct FormFieldData; // name The name of the input as specified in the html. // value The literal contents of the text field. // value_lower The contents of the text field made lower_case. -// date_created The date on which the user first entered the string -// |value| into a field of name |name|. -// date_last_used The date on which the user last entered the string -// |value| into a field of name |name|. +// pair_id An ID number unique to the row in the table. // count How many times the user has entered the string |value| // in a field of name |name|. // +// autofill_dates This table associates a row to each separate time the +// user submits a form containing a certain name/value +// pair. The |pair_id| should match the |pair_id| field +// in the appropriate row of the autofill table. +// pair_id +// date_created +// // autofill_profiles This table contains Autofill profile data added by the // user with the Autofill dialog. Most of the columns are // standard entries in a contact information form. @@ -172,6 +176,48 @@ class AutofillTable : public WebDatabaseTable { // culling in future versions. bool RemoveExpiredFormElements(std::vector<AutofillChange>* changes); + // Removes from autofill_dates rows with given pair_id where date_created lies + // between |delete_begin| and |delete_end|. + bool RemoveFormElementForTimeRange(int64 pair_id, + const base::Time& delete_begin, + const base::Time& delete_end, + int* how_many); + + // Increments the count in the row corresponding to |pair_id| by |delta|. + bool AddToCountOfFormElement(int64 pair_id, int delta); + + // Counts how many timestamp data rows are in the |autofill_dates| table for + // a given |pair_id|. GetCountOfFormElement() on the other hand gives the + // |count| property for a given id. + int CountTimestampsData(int64 pair_id); + + // Gets the pair_id and count entries from name and value specified in + // |element|. Sets *pair_id and *count to 0 if there is no such row in + // the table. + bool GetIDAndCountOfFormElement(const FormFieldData& element, + int64* pair_id, + int* count); + + // Gets the count only given the pair_id. + bool GetCountOfFormElement(int64 pair_id, int* count); + + // Updates the count entry in the row corresponding to |pair_id| to |count|. + bool SetCountOfFormElement(int64 pair_id, int count); + + // Adds a new row to the autofill table with name and value given in + // |element|. Sets *pair_id to the pair_id of the new row. + bool InsertFormElement(const FormFieldData& element, + int64* pair_id); + + // Adds a new row to the autofill_dates table. + bool InsertPairIDAndDate(int64 pair_id, const base::Time& date_created); + + // Deletes last access to the Autofill data from the autofill_dates table. + bool DeleteLastAccess(int64 pair_id); + + // Removes row from the autofill tables given |pair_id|. + bool RemoveFormElementForID(int64 pair_id); + // Removes row from the autofill tables for the given |name| |value| pair. virtual bool RemoveFormElement(const base::string16& name, const base::string16& value); @@ -253,6 +299,10 @@ class AutofillTable : public WebDatabaseTable { // Empties the Autofill profiles "trash can". bool EmptyAutofillProfilesTrash(); + // Removes empty values for autofill that were incorrectly stored in the DB + // See bug http://crbug.com/6111 + bool ClearAutofillEmptyValueElements(); + // Retrieves all profiles in the database that have been deleted since last // "empty" of the trash. bool AddAutofillGUIDToTrash(const std::string& guid); @@ -261,9 +311,6 @@ class AutofillTable : public WebDatabaseTable { bool ClearAutofillProfiles(); // Table migration functions. - // Removes empty values for autofill that were incorrectly stored in the DB - // See bug http://crbug.com/6111 - bool MigrateToVersion22ClearAutofillEmptyValueElements(); bool MigrateToVersion23AddCardNumberEncryptedColumn(); bool MigrateToVersion24CleanupOversizedStringFields(); bool MigrateToVersion27UpdateLegacyCreditCards(); @@ -276,7 +323,6 @@ class AutofillTable : public WebDatabaseTable { bool MigrateToVersion37MergeAndCullOlderProfiles(); bool MigrateToVersion51AddOriginColumn(); bool MigrateToVersion54AddI18nFieldsAndRemoveDeprecatedFields(); - bool MigrateToVersion55MergeAutofillDatesTable(); // Max data length saved in the table; static const size_t kMaxDataLength; @@ -285,22 +331,8 @@ class AutofillTable : public WebDatabaseTable { FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill); FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddChanges); FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_RemoveBetweenChanges); + FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_UpdateDontReplace); - FRIEND_TEST_ALL_PREFIXES( - AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedOnlyBefore); - FRIEND_TEST_ALL_PREFIXES( - AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedOnlyAfter); - FRIEND_TEST_ALL_PREFIXES( - AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedOnlyDuring); - FRIEND_TEST_ALL_PREFIXES( - AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedBeforeAndDuring); - FRIEND_TEST_ALL_PREFIXES( - AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedDuringAndAfter); FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, Autofill_AddFormFieldValues); FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, AutofillProfile); FRIEND_TEST_ALL_PREFIXES(AutofillTableTest, UpdateAutofillProfile); diff --git a/components/autofill/core/browser/webdata/autofill_table_unittest.cc b/components/autofill/core/browser/webdata/autofill_table_unittest.cc index daa0a63..649eec3 100644 --- a/components/autofill/core/browser/webdata/autofill_table_unittest.cc +++ b/components/autofill/core/browser/webdata/autofill_table_unittest.cc @@ -108,17 +108,6 @@ void CompareAutofillEntrySets(const AutofillEntrySet& actual, EXPECT_EQ(actual.size(), count); } -int GetAutofillEntryCount(const base::string16& name, - const base::string16& value, - WebDatabase* db) { - sql::Statement s(db->GetSQLConnection()->GetUniqueStatement( - "SELECT count FROM autofill WHERE name = ? AND value = ?")); - s.BindString16(0, name); - s.BindString16(1, value); - s.Step(); - return s.ColumnInt(0); -} - } // namespace class AutofillTableTest : public testing::Test { @@ -181,17 +170,27 @@ TEST_F(AutofillTableTest, Autofill) { now + i * two_seconds)); } - // We have added the name Clark Kent 5 times, so count should be 5. - EXPECT_EQ(5, GetAutofillEntryCount(ASCIIToUTF16("Name"), - ASCIIToUTF16("Clark Kent"), db_.get())); + int count = 0; + int64 pair_id = 0; + + // We have added the name Clark Kent 5 times, so count should be 5 and pair_id + // should be somthing non-zero. + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Clark Kent"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(5, count); + EXPECT_NE(0, pair_id); // Storing in the data base should be case sensitive, so there should be no // database entry for clark kent lowercase. - EXPECT_EQ(0, GetAutofillEntryCount(ASCIIToUTF16("Name"), - ASCIIToUTF16("clark kent"), db_.get())); + field.value = ASCIIToUTF16("clark kent"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(0, count); - EXPECT_EQ(2, GetAutofillEntryCount(ASCIIToUTF16("Favorite Color"), - ASCIIToUTF16("Green"), db_.get())); + field.name = ASCIIToUTF16("Favorite Color"); + field.value = ASCIIToUTF16("Green"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(2, count); // This is meant to get a list of suggestions for Name. The empty prefix // in the second argument means it should return all suggestions for a name @@ -249,8 +248,10 @@ TEST_F(AutofillTableTest, Autofill) { EXPECT_EQ(kExpectedChanges[i], changes[i]); } - EXPECT_EQ(0, GetAutofillEntryCount(ASCIIToUTF16("Name"), - ASCIIToUTF16("Clark Kent"), db_.get())); + field.name = ASCIIToUTF16("Name"); + field.value = ASCIIToUTF16("Clark Kent"); + EXPECT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_EQ(0, count); EXPECT_TRUE(table_->GetFormValuesForElementName( ASCIIToUTF16("Name"), base::string16(), &v, 6)); @@ -277,6 +278,16 @@ TEST_F(AutofillTableTest, Autofill) { EXPECT_TRUE(table_->GetFormValuesForElementName( ASCIIToUTF16("blank"), base::string16(), &v, 10)); EXPECT_EQ(4U, v.size()); + + // Now we'll check that ClearAutofillEmptyValueElements() works as expected. + table_->ClearAutofillEmptyValueElements(); + + v.clear(); + EXPECT_TRUE(table_->GetFormValuesForElementName( + ASCIIToUTF16("blank"), base::string16(), &v, 10)); + ASSERT_EQ(1U, v.size()); + + EXPECT_EQ(kValue, v[0]); } TEST_F(AutofillTableTest, Autofill_RemoveBetweenChanges) { @@ -341,8 +352,14 @@ TEST_F(AutofillTableTest, Autofill_UpdateOneWithOneTimestamp) { entries.push_back(entry); ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); - EXPECT_EQ(1, GetAutofillEntryCount(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"), - db_.get())); + FormFieldData field; + field.name = ASCIIToUTF16("foo"); + field.value = ASCIIToUTF16("bar"); + int64 pair_id; + int count; + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(1, count); std::vector<AutofillEntry> all_entries; ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); @@ -356,8 +373,14 @@ TEST_F(AutofillTableTest, Autofill_UpdateOneWithTwoTimestamps) { entries.push_back(entry); ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); - EXPECT_EQ(2, GetAutofillEntryCount(ASCIIToUTF16("foo"), ASCIIToUTF16("bar"), - db_.get())); + FormFieldData field; + field.name = ASCIIToUTF16("foo"); + field.value = ASCIIToUTF16("bar"); + int64 pair_id; + int count; + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(2, count); std::vector<AutofillEntry> all_entries; ASSERT_TRUE(table_->GetAllAutofillEntries(&all_entries)); @@ -388,10 +411,21 @@ TEST_F(AutofillTableTest, Autofill_UpdateTwo) { entries.push_back(entry1); ASSERT_TRUE(table_->UpdateAutofillEntries(entries)); - EXPECT_EQ(1, GetAutofillEntryCount(ASCIIToUTF16("foo"), ASCIIToUTF16("bar0"), - db_.get())); - EXPECT_EQ(2, GetAutofillEntryCount(ASCIIToUTF16("foo"), ASCIIToUTF16("bar1"), - db_.get())); + FormFieldData field0; + field0.name = ASCIIToUTF16("foo"); + field0.value = ASCIIToUTF16("bar0"); + int64 pair_id; + int count; + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field0, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(1, count); + + FormFieldData field1; + field1.name = ASCIIToUTF16("foo"); + field1.value = ASCIIToUTF16("bar1"); + ASSERT_TRUE(table_->GetIDAndCountOfFormElement(field1, &pair_id, &count)); + EXPECT_LE(0, pair_id); + EXPECT_EQ(2, count); } TEST_F(AutofillTableTest, Autofill_UpdateReplace) { @@ -479,167 +513,6 @@ TEST_F(AutofillTableTest, Autofill_AddFormFieldValues) { ASSERT_EQ(2U, all_entries.size()); } -TEST_F(AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedOnlyBefore) { - // Add an entry used only before the targetted range. - AutofillChangeList changes; - FormFieldData field; - field.name = ASCIIToUTF16("Name"); - field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(10))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(20))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(30))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(40))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(50))); - - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); - - changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(51), - base::Time::FromTimeT(60), - &changes)); - EXPECT_TRUE(changes.empty()); - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); -} - -TEST_F(AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedOnlyAfter) { - // Add an entry used only after the targetted range. - AutofillChangeList changes; - FormFieldData field; - field.name = ASCIIToUTF16("Name"); - field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(50))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(60))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(70))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(80))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(90))); - - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); - - changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(40), - base::Time::FromTimeT(50), - &changes)); - EXPECT_TRUE(changes.empty()); - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); -} - -TEST_F(AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedOnlyDuring) { - // Add an entry used entirely during the targetted range. - AutofillChangeList changes; - FormFieldData field; - field.name = ASCIIToUTF16("Name"); - field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(10))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(20))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(30))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(40))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(50))); - - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); - - changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(10), - base::Time::FromTimeT(51), - &changes)); - ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::REMOVE, - AutofillKey(field.name, field.value)), - changes[0]); - EXPECT_EQ(0, GetAutofillEntryCount(field.name, field.value, db_.get())); -} - -TEST_F(AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedBeforeAndDuring) { - // Add an entry used both before and during the targetted range. - AutofillChangeList changes; - FormFieldData field; - field.name = ASCIIToUTF16("Name"); - field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(10))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(20))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(30))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(40))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(50))); - - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); - - changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(40), - base::Time::FromTimeT(60), - &changes)); - ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, - AutofillKey(field.name, field.value)), - changes[0]); - EXPECT_EQ(4, GetAutofillEntryCount(field.name, field.value, db_.get())); - std::vector<base::Time> timestamps; - EXPECT_TRUE( - table_->GetAutofillTimestamps(field.name, field.value, ×tamps)); - ASSERT_EQ(2U, timestamps.size()); - EXPECT_EQ(base::Time::FromTimeT(10), timestamps[0]); - EXPECT_EQ(base::Time::FromTimeT(39), timestamps[1]); -} - -TEST_F(AutofillTableTest, - Autofill_RemoveFormElementsAddedBetween_UsedDuringAndAfter) { - // Add an entry used both during and after the targetted range. - AutofillChangeList changes; - FormFieldData field; - field.name = ASCIIToUTF16("Name"); - field.value = ASCIIToUTF16("Superman"); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(50))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(60))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(70))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(80))); - EXPECT_TRUE(table_->AddFormFieldValueTime(field, &changes, - base::Time::FromTimeT(90))); - - EXPECT_EQ(5, GetAutofillEntryCount(field.name, field.value, db_.get())); - - changes.clear(); - EXPECT_TRUE(table_->RemoveFormElementsAddedBetween(base::Time::FromTimeT(40), - base::Time::FromTimeT(80), - &changes)); - ASSERT_EQ(1U, changes.size()); - EXPECT_EQ(AutofillChange(AutofillChange::UPDATE, - AutofillKey(field.name, field.value)), - changes[0]); - EXPECT_EQ(2, GetAutofillEntryCount(field.name, field.value, db_.get())); - std::vector<base::Time> timestamps; - EXPECT_TRUE( - table_->GetAutofillTimestamps(field.name, field.value, ×tamps)); - ASSERT_EQ(2U, timestamps.size()); - EXPECT_EQ(base::Time::FromTimeT(80), timestamps[0]); - EXPECT_EQ(base::Time::FromTimeT(90), timestamps[1]); -} - TEST_F(AutofillTableTest, AutofillProfile) { // Add a 'Home' profile. AutofillProfile home_profile; diff --git a/components/test/data/web_database/version_21.sql b/components/test/data/web_database/version_21.sql deleted file mode 100644 index a77ab7e..0000000 --- a/components/test/data/web_database/version_21.sql +++ /dev/null @@ -1,40 +0,0 @@ -PRAGMA foreign_keys=OFF; -BEGIN TRANSACTION; -CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); -INSERT INTO "meta" VALUES('version','21'); -INSERT INTO "meta" VALUES('last_compatible_version','21'); -INSERT INTO "meta" VALUES('Builtin Keyword Version','27'); -INSERT INTO "meta" VALUES('Default Search Provider ID','7'); -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,show_in_default_list INTEGER,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,autogenerate_keyword INTEGER DEFAULT 0); -INSERT INTO "keywords" VALUES(2,'Google','google.com','http://www.google.com/favicon.ico','{google:baseURL}search?{google:RLZ}{google:acceptedSuggestion}{google:originalQueryForSuggestion}sourceid=chrome&ie={inputEncoding}&q={searchTerms}',1,1,'',0,0,'UTF-8','{google:baseSuggestURL}search?client=chrome&hl={language}&q={searchTerms}',1,1); -INSERT INTO "keywords" VALUES(3,'Yahoo!','yahoo.com','http://search.yahoo.com/favicon.ico','http://search.yahoo.com/search?ei={inputEncoding}&fr=crmas&p={searchTerms}',1,1,'',0,0,'UTF-8','http://ff.search.yahoo.com/gossip?output=fxjson&command={searchTerms}',2,0); -INSERT INTO "keywords" VALUES(4,'Bing','bing.com','http://www.bing.com/s/wlflag.ico','http://www.bing.com/search?setmkt=en-US&q={searchTerms}',1,1,'',0,0,'UTF-8','http://api.bing.com/osjson.aspx?query={searchTerms}&language={language}',3,0); -INSERT INTO "keywords" VALUES(5,'Wikipedia (en)','en.wikipedia.org','','http://en.wikipedia.org/w/index.php?title=Special:Search&search={searchTerms}',1,0,'',1283287335,0,'','',0,0); -INSERT INTO "keywords" VALUES(6,'NYTimes','query.nytimes.com','','http://query.nytimes.com/gst/handler.html?query={searchTerms}&opensearch=1',1,0,'',1283287335,0,'','',0,0); -INSERT INTO "keywords" VALUES(7,'eBay','rover.ebay.com','','http://rover.ebay.com/rover/1/711-43047-14818-1/4?satitle={searchTerms}',1,0,'',1283287335,0,'','',0,0); -INSERT INTO "keywords" VALUES(8,'ff','ff','','http://ff{searchTerms}',0,0,'',1283287356,0,'','',0,0); -CREATE TABLE logins (origin_url VARCHAR NOT NULL, action_url VARCHAR, username_element VARCHAR, username_value VARCHAR, password_element VARCHAR, password_value BLOB, submit_element VARCHAR,signon_realm VARCHAR NOT NULL,ssl_valid INTEGER NOT NULL,preferred INTEGER NOT NULL,date_created INTEGER NOT NULL,blacklisted_by_user INTEGER NOT NULL,scheme INTEGER NOT NULL,UNIQUE (origin_url, username_element, username_value, password_element, submit_element, signon_realm)); -CREATE TABLE ie7_logins (url_hash VARCHAR NOT NULL, password_value BLOB, date_created INTEGER NOT NULL,UNIQUE (url_hash)); -CREATE TABLE web_app_icons (url LONGVARCHAR,width int,height int,image BLOB, UNIQUE (url, width, height)); -CREATE TABLE web_apps (url LONGVARCHAR UNIQUE,has_all_images INTEGER NOT NULL); -CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, pair_id INTEGER PRIMARY KEY, count INTEGER DEFAULT 1); -INSERT INTO "autofill" VALUES('Name','John Doe','john doe',10,1); -INSERT INTO "autofill" VALUES('Name','','',11,1); -INSERT INTO "autofill" VALUES('Email','jane@example.com','jane@example.com',20,3); -INSERT INTO "autofill" VALUES('Email','','',21,4); -CREATE TABLE autofill_dates ( pair_id INTEGER DEFAULT 0,date_created INTEGER DEFAULT 0); -INSERT INTO "autofill_dates" VALUES(10,1384299100); -INSERT INTO "autofill_dates" VALUES(11,1384299200); -INSERT INTO "autofill_dates" VALUES(20,1384299300); -INSERT INTO "autofill_dates" VALUES(20,1384299301); -INSERT INTO "autofill_dates" VALUES(21,1384299401); -INSERT INTO "autofill_dates" VALUES(21,1384299400); -INSERT INTO "autofill_dates" VALUES(21,1384299403); -INSERT INTO "autofill_dates" VALUES(21,1384299402); -CREATE INDEX logins_signon ON logins (signon_realm); -CREATE INDEX ie7_logins_hash ON ie7_logins (url_hash); -CREATE INDEX web_apps_url_index ON web_apps (url); -CREATE INDEX autofill_name ON autofill (name); -CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower); -CREATE INDEX autofill_dates_pair_id ON autofill_dates (pair_id); -COMMIT; diff --git a/components/test/data/web_database/version_54.sql b/components/test/data/web_database/version_54.sql deleted file mode 100644 index 17e5b68..0000000 --- a/components/test/data/web_database/version_54.sql +++ /dev/null @@ -1,40 +0,0 @@ -PRAGMA foreign_keys=OFF; -BEGIN TRANSACTION; -CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR); -INSERT INTO "meta" VALUES('version','54'); -INSERT INTO "meta" VALUES('last_compatible_version','54'); -INSERT INTO "meta" VALUES('Builtin Keyword Version','69'); -INSERT INTO "meta" VALUES('Default Search Provider ID','7'); -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,show_in_default_list INTEGER,suggest_url VARCHAR,prepopulate_id INTEGER DEFAULT 0,created_by_policy INTEGER DEFAULT 0,instant_url VARCHAR,last_modified INTEGER DEFAULT 0,sync_guid VARCHAR,alternate_urls VARCHAR,search_terms_replacement_key VARCHAR,image_url VARCHAR,search_url_post_params VARCHAR,suggest_url_post_params VARCHAR,instant_url_post_params VARCHAR,image_url_post_params VARCHAR,new_tab_url VARCHAR); -CREATE TABLE token_service (service VARCHAR PRIMARY KEY NOT NULL,encrypted_token BLOB); -CREATE TABLE web_app_icons (url LONGVARCHAR,width int,height int,image BLOB, UNIQUE (url, width, height)); -CREATE TABLE web_apps (url LONGVARCHAR UNIQUE,has_all_images INTEGER NOT NULL); -CREATE TABLE web_intents ( service_url LONGVARCHAR, action VARCHAR, type VARCHAR, title LONGVARCHAR, disposition VARCHAR, scheme VARCHAR, UNIQUE (service_url, action, scheme, type)); -CREATE TABLE web_intents_defaults ( action VARCHAR, type VARCHAR, url_pattern LONGVARCHAR, user_date INTEGER, suppression INTEGER, service_url LONGVARCHAR, scheme VARCHAR, UNIQUE (action, scheme, type, url_pattern)); -CREATE TABLE autofill (name VARCHAR, value VARCHAR, value_lower VARCHAR, pair_id INTEGER PRIMARY KEY, count INTEGER DEFAULT 1); -INSERT INTO "autofill" VALUES('Name','John Doe','john doe',10,1); -INSERT INTO "autofill" VALUES('Name','john doe','john doe',11,1); -INSERT INTO "autofill" VALUES('Email','jane@example.com','jane@example.com',20,3); -INSERT INTO "autofill" VALUES('Email','jane.doe@example.org','jane.doe@example.org',21,4); -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 ''); -CREATE TABLE autofill_dates ( pair_id INTEGER DEFAULT 0, date_created INTEGER DEFAULT 0); -INSERT INTO "autofill_dates" VALUES(10,1384299100); -INSERT INTO "autofill_dates" VALUES(11,1384299200); -INSERT INTO "autofill_dates" VALUES(20,1384299300); -INSERT INTO "autofill_dates" VALUES(20,1384299301); -INSERT INTO "autofill_dates" VALUES(21,1384299401); -INSERT INTO "autofill_dates" VALUES(21,1384299400); -INSERT INTO "autofill_dates" VALUES(21,1384299403); -INSERT INTO "autofill_dates" VALUES(21,1384299402); -CREATE TABLE autofill_profiles ( guid VARCHAR PRIMARY KEY, company_name VARCHAR, address_line_1 VARCHAR, address_line_2 VARCHAR, city VARCHAR, state VARCHAR, zipcode VARCHAR, country VARCHAR, country_code VARCHAR, date_modified INTEGER NOT NULL DEFAULT 0, origin VARCHAR DEFAULT ''); -CREATE TABLE autofill_profile_names ( guid VARCHAR, first_name VARCHAR, middle_name VARCHAR, last_name VARCHAR); -CREATE TABLE autofill_profile_emails ( guid VARCHAR, email VARCHAR); -CREATE TABLE autofill_profile_phones ( guid VARCHAR, type INTEGER DEFAULT 0, number VARCHAR); -CREATE TABLE autofill_profiles_trash ( guid VARCHAR); -CREATE INDEX web_apps_url_index ON web_apps (url); -CREATE INDEX web_intents_index ON web_intents (action); -CREATE INDEX web_intents_default_index ON web_intents_defaults (action); -CREATE INDEX autofill_name ON autofill (name); -CREATE INDEX autofill_name_value_lower ON autofill (name, value_lower); -CREATE INDEX autofill_dates_pair_id ON autofill_dates (pair_id); -COMMIT; diff --git a/components/webdata/common/web_database.cc b/components/webdata/common/web_database.cc index 230eae1..44b6790 100644 --- a/components/webdata/common/web_database.cc +++ b/components/webdata/common/web_database.cc @@ -14,11 +14,11 @@ // corresponding changes must happen in the unit tests, and new migration test // added. See |WebDatabaseMigrationTest::kCurrentTestedVersionNumber|. // static -const int WebDatabase::kCurrentVersionNumber = 55; +const int WebDatabase::kCurrentVersionNumber = 54; namespace { -const int kCompatibleVersionNumber = 55; +const int kCompatibleVersionNumber = 54; // Change the version number and possibly the compatibility version of // |meta_table_|. diff --git a/components/webdata/common/web_database_migration_unittest.cc b/components/webdata/common/web_database_migration_unittest.cc index a72140c..0f22cb5 100644 --- a/components/webdata/common/web_database_migration_unittest.cc +++ b/components/webdata/common/web_database_migration_unittest.cc @@ -248,7 +248,7 @@ class WebDatabaseMigrationTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(WebDatabaseMigrationTest); }; -const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 55; +const int WebDatabaseMigrationTest::kCurrentTestedVersionNumber = 54; void WebDatabaseMigrationTest::LoadDatabase( const base::FilePath::StringType& file) { @@ -275,9 +275,7 @@ TEST_F(WebDatabaseMigrationTest, MigrateEmptyToCurrent) { // Check that expected tables are present. EXPECT_TRUE(connection.DoesTableExist("autofill")); - // The autofill_dates table is obsolete. (It's been merged into the autofill - // table.) - EXPECT_FALSE(connection.DoesTableExist("autofill_dates")); + EXPECT_TRUE(connection.DoesTableExist("autofill_dates")); EXPECT_TRUE(connection.DoesTableExist("autofill_profiles")); EXPECT_TRUE(connection.DoesTableExist("credit_cards")); EXPECT_TRUE(connection.DoesTableExist("keywords")); @@ -292,125 +290,6 @@ TEST_F(WebDatabaseMigrationTest, MigrateEmptyToCurrent) { } } -// Tests that rows with empty values get removed from the autofill tables. -TEST_F(WebDatabaseMigrationTest, MigrateVersion21ToCurrent) { - ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_21.sql"))); - - // Verify pre-conditions. - { - sql::Connection connection; - ASSERT_TRUE(connection.Open(GetDatabasePath())); - - // Both empty and non-empty values are allowed in a version 21 database. - sql::Statement s_autofill(connection.GetUniqueStatement( - "SELECT name, value, value_lower, pair_id, count FROM autofill")); - sql::Statement s_dates(connection.GetUniqueStatement( - "SELECT pair_id, date_created FROM autofill_dates")); - - // An entry with a non-empty value. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s_autofill.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("John Doe"), s_autofill.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s_autofill.ColumnString16(2)); - EXPECT_EQ(10, s_autofill.ColumnInt(3)); - EXPECT_EQ(1, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(10, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299100, s_dates.ColumnInt64(1)); - - // An entry with an empty value. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s_autofill.ColumnString16(0)); - EXPECT_EQ(base::string16(), s_autofill.ColumnString16(1)); - EXPECT_EQ(base::string16(), s_autofill.ColumnString16(2)); - EXPECT_EQ(11, s_autofill.ColumnInt(3)); - EXPECT_EQ(1, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(11, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299200, s_dates.ColumnInt64(1)); - - // Another entry with a non-empty value. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s_autofill.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s_autofill.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s_autofill.ColumnString16(2)); - EXPECT_EQ(20, s_autofill.ColumnInt(3)); - EXPECT_EQ(3, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(20, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299300, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(20, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299301, s_dates.ColumnInt64(1)); - - // Another entry with an empty value. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s_autofill.ColumnString16(0)); - EXPECT_EQ(base::string16(), s_autofill.ColumnString16(1)); - EXPECT_EQ(base::string16(), s_autofill.ColumnString16(2)); - EXPECT_EQ(21, s_autofill.ColumnInt(3)); - EXPECT_EQ(4, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299401, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299400, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299403, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299402, s_dates.ColumnInt64(1)); - - // No more entries expected. - ASSERT_FALSE(s_autofill.Step()); - ASSERT_FALSE(s_dates.Step()); - } - - DoMigration(); - - // Verify post-conditions. These are expectations for current version of the - // database. - { - sql::Connection connection; - ASSERT_TRUE(connection.Open(GetDatabasePath())); - - // Check version. - EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); - - // Entries with empty values should have been dropped. The remaining - // entries should have been preserved. - sql::Statement s( - connection.GetUniqueStatement( - "SELECT name, value, value_lower, date_created, date_last_used," - " count " - "FROM autofill " - "ORDER BY name, value ASC")); - - // "jane@example.com" - ASSERT_TRUE(s.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s.ColumnString16(2)); - EXPECT_EQ(1384299300, s.ColumnInt64(3)); - EXPECT_EQ(1384299301, s.ColumnInt64(4)); - EXPECT_EQ(3, s.ColumnInt(5)); - - // "John Doe" - ASSERT_TRUE(s.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("John Doe"), s.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s.ColumnString16(2)); - EXPECT_EQ(1384299100, s.ColumnInt64(3)); - EXPECT_EQ(1384299100, s.ColumnInt64(4)); - EXPECT_EQ(1, s.ColumnInt(5)); - - // No more entries expected. - ASSERT_FALSE(s.Step()); - } -} - // Tests that the |credit_card| table gets added to the schema for a version 22 // database. TEST_F(WebDatabaseMigrationTest, MigrateVersion22ToCurrent) { @@ -2391,158 +2270,3 @@ TEST_F(WebDatabaseMigrationTest, MigrateVersion53ToCurrent) { EXPECT_FALSE(s_phones.Step()); } } - -// Tests that migrating from version 54 to version 55 drops the autofill_dates -// table, and merges the appropriate dates into the autofill table. -TEST_F(WebDatabaseMigrationTest, MigrateVersion54ToCurrent) { - ASSERT_NO_FATAL_FAILURE(LoadDatabase(FILE_PATH_LITERAL("version_54.sql"))); - - // Verify pre-conditions. These are expectations for version 54 of the - // database. - { - sql::Connection connection; - ASSERT_TRUE(connection.Open(GetDatabasePath())); - - EXPECT_TRUE(connection.DoesTableExist("autofill_dates")); - EXPECT_FALSE(connection.DoesColumnExist("autofill", "date_created")); - EXPECT_FALSE(connection.DoesColumnExist("autofill", "date_last_used")); - - // Verify the incoming data. - sql::Statement s_autofill(connection.GetUniqueStatement( - "SELECT name, value, value_lower, pair_id, count FROM autofill")); - sql::Statement s_dates(connection.GetUniqueStatement( - "SELECT pair_id, date_created FROM autofill_dates")); - - // An entry with one timestamp. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s_autofill.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("John Doe"), s_autofill.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s_autofill.ColumnString16(2)); - EXPECT_EQ(10, s_autofill.ColumnInt(3)); - EXPECT_EQ(1, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(10, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299100, s_dates.ColumnInt64(1)); - - // Another entry with one timestamp, differing from the previous one in case - // only. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s_autofill.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s_autofill.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s_autofill.ColumnString16(2)); - EXPECT_EQ(11, s_autofill.ColumnInt(3)); - EXPECT_EQ(1, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(11, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299200, s_dates.ColumnInt64(1)); - - // An entry with two timestamps (with count > 2; this is realistic). - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s_autofill.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s_autofill.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s_autofill.ColumnString16(2)); - EXPECT_EQ(20, s_autofill.ColumnInt(3)); - EXPECT_EQ(3, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(20, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299300, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(20, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299301, s_dates.ColumnInt64(1)); - - // An entry with more than two timestamps, which are stored out of order. - ASSERT_TRUE(s_autofill.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s_autofill.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("jane.doe@example.org"), - s_autofill.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("jane.doe@example.org"), - s_autofill.ColumnString16(2)); - EXPECT_EQ(21, s_autofill.ColumnInt(3)); - EXPECT_EQ(4, s_autofill.ColumnInt(4)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299401, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299400, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299403, s_dates.ColumnInt64(1)); - ASSERT_TRUE(s_dates.Step()); - EXPECT_EQ(21, s_dates.ColumnInt(0)); - EXPECT_EQ(1384299402, s_dates.ColumnInt64(1)); - - // No more entries expected. - ASSERT_FALSE(s_autofill.Step()); - ASSERT_FALSE(s_dates.Step()); - } - - DoMigration(); - - // Verify post-conditions. These are expectations for current version of the - // database. - { - sql::Connection connection; - ASSERT_TRUE(connection.Open(GetDatabasePath())); - ASSERT_TRUE(sql::MetaTable::DoesTableExist(&connection)); - - // Check version. - EXPECT_EQ(kCurrentTestedVersionNumber, VersionFromConnection(&connection)); - - // The autofill_dates table should have been dropped, and its columns should - // have been migrated to the autofill table. - EXPECT_FALSE(connection.DoesTableExist("autofill_dates")); - EXPECT_TRUE(connection.DoesColumnExist("autofill", "date_created")); - EXPECT_TRUE(connection.DoesColumnExist("autofill", "date_last_used")); - - // Data should have been preserved. Note that it appears out of order - // relative to the previous table, as it's been alphabetized. That's ok. - sql::Statement s( - connection.GetUniqueStatement( - "SELECT name, value, value_lower, date_created, date_last_used," - " count " - "FROM autofill " - "ORDER BY name, value ASC")); - - // "jane.doe@example.org": Timestamps should be parsed correctly, and only - // the first and last should be kept. - ASSERT_TRUE(s.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("jane.doe@example.org"), s.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("jane.doe@example.org"), s.ColumnString16(2)); - EXPECT_EQ(1384299400, s.ColumnInt64(3)); - EXPECT_EQ(1384299403, s.ColumnInt64(4)); - EXPECT_EQ(4, s.ColumnInt(5)); - - // "jane@example.com": Timestamps should be parsed correctly. - ASSERT_TRUE(s.Step()); - EXPECT_EQ(ASCIIToUTF16("Email"), s.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("jane@example.com"), s.ColumnString16(2)); - EXPECT_EQ(1384299300, s.ColumnInt64(3)); - EXPECT_EQ(1384299301, s.ColumnInt64(4)); - EXPECT_EQ(3, s.ColumnInt(5)); - - // "John Doe": The single timestamp should be assigned as both the creation - // and the last use timestamp. - ASSERT_TRUE(s.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("John Doe"), s.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s.ColumnString16(2)); - EXPECT_EQ(1384299100, s.ColumnInt64(3)); - EXPECT_EQ(1384299100, s.ColumnInt64(4)); - EXPECT_EQ(1, s.ColumnInt(5)); - - // "john doe": Should not be merged with "John Doe" (case-sensitivity). - ASSERT_TRUE(s.Step()); - EXPECT_EQ(ASCIIToUTF16("Name"), s.ColumnString16(0)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s.ColumnString16(1)); - EXPECT_EQ(ASCIIToUTF16("john doe"), s.ColumnString16(2)); - EXPECT_EQ(1384299200, s.ColumnInt64(3)); - EXPECT_EQ(1384299200, s.ColumnInt64(4)); - EXPECT_EQ(1, s.ColumnInt(5)); - - // No more entries expected. - ASSERT_FALSE(s.Step()); - } -} |