Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5804)

Unified Diff: chrome/browser/webdata/autofill_table.cc

Issue 6677124: Move migration code out of WebDatabase and into table-specific classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/webdata/autofill_table.cc
diff --git a/chrome/browser/webdata/autofill_table.cc b/chrome/browser/webdata/autofill_table.cc
index 33e048372e5aacee55c1df8a5ec9381adcbb3366..e8bd0e4f99941433bb850be4b596179fc77f922f 100644
--- a/chrome/browser/webdata/autofill_table.cc
+++ b/chrome/browser/webdata/autofill_table.cc
@@ -4,7 +4,16 @@
#include "chrome/browser/webdata/autofill_table.h"
+#include <algorithm>
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
#include "app/sql/statement.h"
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
#include "base/time.h"
#include "base/tuple.h"
#include "chrome/browser/autofill/autofill_country.h"
@@ -12,8 +21,8 @@
#include "chrome/browser/autofill/autofill_type.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/autofill/personal_data_manager.h"
+#include "chrome/browser/password_manager/encryptor.h"
#include "chrome/browser/webdata/autofill_change.h"
-#include "chrome/browser/webdata/autofill_util.h"
#include "chrome/common/guid.h"
#include "ui/base/l10n/l10n_util.h"
#include "webkit/glue/form_field.h"
@@ -22,7 +31,360 @@ using base::Time;
using webkit_glue::FormField;
namespace {
+
+// Constants for the |autofill_profile_phones| |type| column.
+enum AutofillPhoneType {
+ kAutofillPhoneNumber = 0,
+ kAutofillFaxNumber = 1
+};
+
typedef std::vector<Tuple3<int64, string16, string16> > AutofillElementList;
+
+// TODO(dhollowa): Find a common place for this. It is duplicated in
+// personal_data_manager.cc.
+template<typename T>
+T* address_of(T& v) {
+ return &v;
+}
+
+// The maximum length allowed for form data.
+const size_t kMaxDataLength = 1024;
+
+string16 LimitDataSize(const string16& data) {
+ if (data.size() > kMaxDataLength)
+ return data.substr(0, kMaxDataLength);
+
+ return data;
+}
+
+void BindAutofillProfileToStatement(const AutofillProfile& profile,
+ sql::Statement* s) {
+ DCHECK(guid::IsValidGUID(profile.guid()));
+ s->BindString(0, profile.guid());
+
+ string16 text = profile.GetInfo(COMPANY_NAME);
+ s->BindString16(1, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_LINE1);
+ s->BindString16(2, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_LINE2);
+ s->BindString16(3, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_CITY);
+ s->BindString16(4, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_STATE);
+ s->BindString16(5, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_ZIP);
+ s->BindString16(6, LimitDataSize(text));
+ text = profile.GetInfo(ADDRESS_HOME_COUNTRY);
+ s->BindString16(7, LimitDataSize(text));
+ std::string country_code = profile.CountryCode();
+ s->BindString(8, country_code);
+ s->BindInt64(9, Time::Now().ToTimeT());
+}
+
+AutofillProfile* AutofillProfileFromStatement(const sql::Statement& s) {
+ AutofillProfile* profile = new AutofillProfile;
+ profile->set_guid(s.ColumnString(0));
+ DCHECK(guid::IsValidGUID(profile->guid()));
+
+ profile->SetInfo(COMPANY_NAME, s.ColumnString16(1));
+ profile->SetInfo(ADDRESS_HOME_LINE1, s.ColumnString16(2));
+ profile->SetInfo(ADDRESS_HOME_LINE2, s.ColumnString16(3));
+ profile->SetInfo(ADDRESS_HOME_CITY, s.ColumnString16(4));
+ profile->SetInfo(ADDRESS_HOME_STATE, s.ColumnString16(5));
+ profile->SetInfo(ADDRESS_HOME_ZIP, s.ColumnString16(6));
+ // Intentionally skip column 7, which stores the localized country name.
+ profile->SetCountryCode(s.ColumnString(8));
+ // Intentionally skip column 9, which stores the profile's modification date.
+
+ return profile;
+}
+
+void BindCreditCardToStatement(const CreditCard& credit_card,
+ sql::Statement* s) {
+ DCHECK(guid::IsValidGUID(credit_card.guid()));
+ s->BindString(0, credit_card.guid());
+
+ string16 text = credit_card.GetInfo(CREDIT_CARD_NAME);
+ s->BindString16(1, LimitDataSize(text));
+ text = credit_card.GetInfo(CREDIT_CARD_EXP_MONTH);
+ s->BindString16(2, LimitDataSize(text));
+ text = credit_card.GetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR);
+ s->BindString16(3, LimitDataSize(text));
+ text = credit_card.GetInfo(CREDIT_CARD_NUMBER);
+ std::string encrypted_data;
+ Encryptor::EncryptString16(text, &encrypted_data);
+ s->BindBlob(4, encrypted_data.data(),
+ static_cast<int>(encrypted_data.length()));
+ s->BindInt64(5, Time::Now().ToTimeT());
+}
+
+CreditCard* CreditCardFromStatement(const sql::Statement& s) {
+ CreditCard* credit_card = new CreditCard;
+
+ credit_card->set_guid(s.ColumnString(0));
+ DCHECK(guid::IsValidGUID(credit_card->guid()));
+
+ credit_card->SetInfo(CREDIT_CARD_NAME, s.ColumnString16(1));
+ credit_card->SetInfo(CREDIT_CARD_EXP_MONTH,
+ s.ColumnString16(2));
+ credit_card->SetInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR,
+ s.ColumnString16(3));
+ int encrypted_number_len = s.ColumnByteLength(4);
+ string16 credit_card_number;
+ if (encrypted_number_len) {
+ std::string encrypted_number;
+ encrypted_number.resize(encrypted_number_len);
+ memcpy(&encrypted_number[0], s.ColumnBlob(4), encrypted_number_len);
+ Encryptor::DecryptString16(encrypted_number, &credit_card_number);
+ }
+ credit_card->SetInfo(CREDIT_CARD_NUMBER, credit_card_number);
+ // Intentionally skip column 5, which stores the modification date.
+
+ return credit_card;
+}
+
+bool AddAutofillProfileNamesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name "
+ "FROM autofill_profile_names "
+ "WHERE guid=?"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.BindString(0, profile->guid());
+
+ std::vector<string16> first_names;
+ std::vector<string16> middle_names;
+ std::vector<string16> last_names;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ first_names.push_back(s.ColumnString16(1));
+ middle_names.push_back(s.ColumnString16(2));
+ last_names.push_back(s.ColumnString16(3));
+ }
+ profile->SetMultiInfo(NAME_FIRST, first_names);
+ profile->SetMultiInfo(NAME_MIDDLE, middle_names);
+ profile->SetMultiInfo(NAME_LAST, last_names);
+ return true;
+}
+
+bool AddAutofillProfileEmailsToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, email "
+ "FROM autofill_profile_emails "
+ "WHERE guid=?"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.BindString(0, profile->guid());
+
+ std::vector<string16> emails;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ emails.push_back(s.ColumnString16(1));
+ }
+ profile->SetMultiInfo(EMAIL_ADDRESS, emails);
+ return true;
+}
+
+bool AddAutofillProfilePhonesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, type, number "
+ "FROM autofill_profile_phones "
+ "WHERE guid=? AND type=?"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.BindString(0, profile->guid());
+ s.BindInt(1, kAutofillPhoneNumber);
+
+ std::vector<string16> numbers;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ numbers.push_back(s.ColumnString16(2));
+ }
+ profile->SetMultiInfo(PHONE_HOME_WHOLE_NUMBER, numbers);
+ return true;
+}
+
+bool AddAutofillProfileFaxesToProfile(sql::Connection* db,
+ AutofillProfile* profile) {
+ sql::Statement s(db->GetUniqueStatement(
+ "SELECT guid, type, number "
+ "FROM autofill_profile_phones "
+ "WHERE guid=? AND type=?"));
+ if (!s) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+ s.BindString(0, profile->guid());
+ s.BindInt(1, kAutofillFaxNumber);
+
+ std::vector<string16> numbers;
+ while (s.Step()) {
+ DCHECK_EQ(profile->guid(), s.ColumnString(0));
+ numbers.push_back(s.ColumnString16(2));
+ }
+ profile->SetMultiInfo(PHONE_FAX_WHOLE_NUMBER, numbers);
+ return true;
+}
+
+
+bool AddAutofillProfileNames(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<string16> first_names;
+ profile.GetMultiInfo(NAME_FIRST, &first_names);
+ std::vector<string16> middle_names;
+ profile.GetMultiInfo(NAME_MIDDLE, &middle_names);
+ std::vector<string16> last_names;
+ profile.GetMultiInfo(NAME_LAST, &last_names);
+ DCHECK_EQ(first_names.size(), middle_names.size());
+ DCHECK_EQ(middle_names.size(), last_names.size());
+
+ for (size_t i = 0; i < first_names.size(); ++i) {
+ // Add the new name.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_names"
+ " (guid, first_name, middle_name, last_name) "
+ "VALUES (?,?,?,?)"));
+ if (!s) {
+ NOTREACHED();
+ return false;
+ }
+ s.BindString(0, profile.guid());
+ s.BindString16(1, first_names[i]);
+ s.BindString16(2, middle_names[i]);
+ s.BindString16(3, last_names[i]);
+
+ if (!s.Run()) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AddAutofillProfileEmails(const AutofillProfile& profile,
+ sql::Connection* db) {
+ std::vector<string16> emails;
+ profile.GetMultiInfo(EMAIL_ADDRESS, &emails);
+
+ for (size_t i = 0; i < emails.size(); ++i) {
+ // Add the new email.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_emails"
+ " (guid, email) "
+ "VALUES (?,?)"));
+ if (!s) {
+ NOTREACHED();
+ return false;
+ }
+ s.BindString(0, profile.guid());
+ s.BindString16(1, emails[i]);
+
+ if (!s.Run()) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AddAutofillProfilePhones(const AutofillProfile& profile,
+ AutofillPhoneType phone_type,
+ sql::Connection* db) {
+ AutofillFieldType field_type;
+ if (phone_type == kAutofillPhoneNumber) {
+ field_type = PHONE_HOME_WHOLE_NUMBER;
+ } else if (phone_type == kAutofillFaxNumber) {
+ field_type = PHONE_FAX_WHOLE_NUMBER;
+ } else {
+ NOTREACHED();
+ return false;
+ }
+
+ std::vector<string16> numbers;
+ profile.GetMultiInfo(field_type, &numbers);
+
+ for (size_t i = 0; i < numbers.size(); ++i) {
+ // Add the new number.
+ sql::Statement s(db->GetUniqueStatement(
+ "INSERT INTO autofill_profile_phones"
+ " (guid, type, number) "
+ "VALUES (?,?,?)"));
+ if (!s) {
+ NOTREACHED();
+ return false;
+ }
+ s.BindString(0, profile.guid());
+ s.BindInt(1, phone_type);
+ s.BindString16(2, numbers[i]);
+
+ if (!s.Run()) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ return true;
+}
+
+bool AddAutofillProfilePieces(const AutofillProfile& profile,
+ sql::Connection* db) {
+ if (!AddAutofillProfileNames(profile, db))
+ return false;
+
+ if (!AddAutofillProfileEmails(profile, db))
+ return false;
+
+ if (!AddAutofillProfilePhones(profile, kAutofillPhoneNumber, db))
+ return false;
+
+ if (!AddAutofillProfilePhones(profile, kAutofillFaxNumber, db))
+ return false;
+
+ return true;
+}
+
+bool RemoveAutofillProfilePieces(const std::string& guid, sql::Connection* db) {
+ sql::Statement s1(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_names WHERE guid = ?"));
+ if (!s1) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s1.BindString(0, guid);
+ if (!s1.Run())
+ return false;
+
+ sql::Statement s2(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_emails WHERE guid = ?"));
+ if (!s2) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s2.BindString(0, guid);
+ if (!s2.Run())
+ return false;
+
+ sql::Statement s3(db->GetUniqueStatement(
+ "DELETE FROM autofill_profile_phones WHERE guid = ?"));
+ if (!s3) {
+ NOTREACHED() << "Statement prepare failed";
+ return false;
+ }
+
+ s3.BindString(0, guid);
+ return s3.Run();
+}
+
} // anonymous namespace
dhollowa 2011/04/04 16:13:20 nit: s/anonymous namespace/namespace/
Bons 2011/04/05 11:05:13 Done.
bool AutofillTable::Init() {
@@ -537,7 +899,7 @@ bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
return false;
}
- autofill_util::BindAutofillProfileToStatement(profile, &s);
+ BindAutofillProfileToStatement(profile, &s);
if (!s.Run()) {
NOTREACHED();
@@ -547,7 +909,7 @@ bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
if (!s.Succeeded())
return false;
- return autofill_util::AddAutofillProfilePieces(profile, db_);
+ return AddAutofillProfilePieces(profile, db_);
}
bool AutofillTable::GetAutofillProfile(const std::string& guid,
@@ -571,19 +933,19 @@ bool AutofillTable::GetAutofillProfile(const std::string& guid,
if (!s.Succeeded())
return false;
- scoped_ptr<AutofillProfile> p(autofill_util::AutofillProfileFromStatement(s));
+ scoped_ptr<AutofillProfile> p(AutofillProfileFromStatement(s));
// Get associated name info.
- autofill_util::AddAutofillProfileNamesToProfile(db_, p.get());
+ AddAutofillProfileNamesToProfile(db_, p.get());
// Get associated email info.
- autofill_util::AddAutofillProfileEmailsToProfile(db_, p.get());
+ AddAutofillProfileEmailsToProfile(db_, p.get());
// Get associated phone info.
- autofill_util::AddAutofillProfilePhonesToProfile(db_, p.get());
+ AddAutofillProfilePhonesToProfile(db_, p.get());
// Get associated fax info.
- autofill_util::AddAutofillProfileFaxesToProfile(db_, p.get());
+ AddAutofillProfileFaxesToProfile(db_, p.get());
*profile = p.release();
return true;
@@ -680,7 +1042,7 @@ bool AutofillTable::UpdateAutofillProfileMulti(const AutofillProfile& profile) {
return false;
}
- autofill_util::BindAutofillProfileToStatement(profile, &s);
+ BindAutofillProfileToStatement(profile, &s);
s.BindString(10, profile.guid());
bool result = s.Run();
DCHECK_GT(db_->GetLastChangeCount(), 0);
@@ -688,10 +1050,10 @@ bool AutofillTable::UpdateAutofillProfileMulti(const AutofillProfile& profile) {
return result;
// Remove the old names, emails, and phone/fax numbers.
- if (!autofill_util::RemoveAutofillProfilePieces(profile.guid(), db_))
+ if (!RemoveAutofillProfilePieces(profile.guid(), db_))
return false;
- return autofill_util::AddAutofillProfilePieces(profile, db_);
+ return AddAutofillProfilePieces(profile, db_);
}
bool AutofillTable::RemoveAutofillProfile(const std::string& guid) {
@@ -724,7 +1086,7 @@ bool AutofillTable::RemoveAutofillProfile(const std::string& guid) {
if (!s.Run())
return false;
- return autofill_util::RemoveAutofillProfilePieces(guid, db_);
+ return RemoveAutofillProfilePieces(guid, db_);
}
bool AutofillTable::ClearAutofillProfiles() {
@@ -782,7 +1144,7 @@ bool AutofillTable::AddCreditCard(const CreditCard& credit_card) {
return false;
}
- autofill_util::BindCreditCardToStatement(credit_card, &s);
+ BindCreditCardToStatement(credit_card, &s);
if (!s.Run()) {
NOTREACHED();
@@ -810,7 +1172,7 @@ bool AutofillTable::GetCreditCard(const std::string& guid,
if (!s.Step())
return false;
- *credit_card = autofill_util::CreditCardFromStatement(s);
+ *credit_card = CreditCardFromStatement(s);
return s.Succeeded();
}
@@ -861,7 +1223,7 @@ bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) {
return false;
}
- autofill_util::BindCreditCardToStatement(credit_card, &s);
+ BindCreditCardToStatement(credit_card, &s);
s.BindString(6, credit_card.guid());
bool result = s.Run();
DCHECK_GT(db_->GetLastChangeCount(), 0);
@@ -1148,3 +1510,620 @@ bool AutofillTable::InitProfileTrashTable() {
}
return true;
}
+
+bool AutofillTable::AddCardNumberEncryptedColumnForVersion23() {
+ // Add the card_number_encrypted column if credit card table was not
dhollowa 2011/04/04 16:13:20 This comment block now describes the migration met
Bons 2011/04/05 11:05:13 Done.
+ // created in this build (otherwise the column already exists).
+ // WARNING: Do not change the order of the execution of the SQL
+ // statements in this case! Profile corruption and data migration
+ // issues WILL OCCUR. See http://crbug.com/10913
+ //
+ // The problem is that if a user has a profile which was created before
+ // r37036, when the credit_cards table was added, and then failed to
+ // update this profile between the credit card addition and the addition
+ // of the "encrypted" columns (44963), the next data migration will put
+ // the user's profile in an incoherent state: The user will update from
+ // a data profile set to be earlier than 22, and therefore pass through
+ // this update case. But because the user did not have a credit_cards
+ // table before starting Chrome, it will have just been initialized
+ // above, and so already have these columns -- and thus this data
+ // update step will have failed.
+ //
+ // The false assumption in this case is that at this step in the
+ // migration, the user has a credit card table, and that this
+ // table does not include encrypted columns!
+ // Because this case does not roll back the complete set of SQL
+ // transactions properly in case of failure (that is, it does not
+ // roll back the table initialization done above), the incoherent
+ // profile will now see itself as being at version 22 -- but include a
+ // fully initialized credit_cards table. Every time Chrome runs, it
+ // will try to update the web database and fail at this step, unless
+ // we allow for the faulty assumption described above by checking for
+ // the existence of the columns only AFTER we've executed the commands
+ // to add them.
+ if (!db_->DoesColumnExist("credit_cards", "card_number_encrypted")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "card_number_encrypted BLOB DEFAULT NULL")) {
+ LOG(WARNING) << "Could not add card_number_encrypted to "
+ "credit_cards table.";
+ return false;
+ }
+ }
+
+ if (!db_->DoesColumnExist("credit_cards", "verification_code_encrypted")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "verification_code_encrypted BLOB DEFAULT NULL")) {
+ LOG(WARNING) << "Could not add verification_code_encrypted to "
+ "credit_cards table.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AutofillTable::CleanupOversizedStringFieldsForVersion24() {
+ // One-time cleanup for http://crbug.com/38364 - In the presence of
+ // multi-byte UTF-8 characters, that bug could cause Autofill strings
+ // to grow larger and more corrupt with each save. The cleanup removes
+ // any row with a string field larger than a reasonable size. The string
+ // fields examined here are precisely the ones that were subject to
+ // corruption by the original bug.
+ const std::string autofill_is_too_big =
+ "max(length(name), length(value)) > 500";
+
+ const std::string credit_cards_is_too_big =
+ "max(length(label), length(name_on_card), length(type), "
+ " length(expiration_month), length(expiration_year), "
+ " length(billing_address), length(shipping_address) "
+ ") > 500";
+
+ const std::string autofill_profiles_is_too_big =
+ "max(length(label), length(first_name), "
+ " length(middle_name), length(last_name), length(email), "
+ " length(company_name), length(address_line_1), "
+ " length(address_line_2), length(city), length(state), "
+ " length(zipcode), length(country), length(phone), "
+ " length(fax)) > 500";
+
+ std::string query = "DELETE FROM autofill_dates WHERE pair_id IN ("
+ "SELECT pair_id FROM autofill WHERE " + autofill_is_too_big + ")";
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+
+ query = "DELETE FROM autofill WHERE " + autofill_is_too_big;
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+
+ // Only delete from legacy credit card tables where specific columns exist.
+ if (db_->DoesColumnExist("credit_cards", "label") &&
+ db_->DoesColumnExist("credit_cards", "name_on_card") &&
+ db_->DoesColumnExist("credit_cards", "type") &&
+ db_->DoesColumnExist("credit_cards", "expiration_month") &&
+ db_->DoesColumnExist("credit_cards", "expiration_year") &&
+ db_->DoesColumnExist("credit_cards", "billing_address") &&
+ db_->DoesColumnExist("credit_cards", "shipping_address") &&
+ db_->DoesColumnExist("autofill_profiles", "label")) {
+ query = "DELETE FROM credit_cards WHERE (" + credit_cards_is_too_big +
+ ") OR label IN (SELECT label FROM autofill_profiles WHERE " +
+ autofill_profiles_is_too_big + ")";
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+ }
+
+ if (db_->DoesColumnExist("autofill_profiles", "label")) {
+ query = "DELETE FROM autofill_profiles WHERE " +
+ autofill_profiles_is_too_big;
+
+ if (!db_->Execute(query.c_str()))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::UpdateLegacyCreditCardsForVersion27() {
+ // Only migrate from legacy credit card tables where specific columns
+ // exist.
+ if (!(db_->DoesColumnExist("credit_cards", "unique_id") &&
+ db_->DoesColumnExist("credit_cards", "billing_address") &&
+ db_->DoesColumnExist("autofill_profiles", "unique_id"))) {
+ return true;
+ }
+
+ // Change the credit_cards.billing_address column from a string to an
+ // int. The stored string is the label of an address, so we have to
+ // select the unique ID of this address using the label as a foreign
+ // key into the |autofill_profiles| table.
+ std::string stmt =
+ "SELECT credit_cards.unique_id, autofill_profiles.unique_id "
+ "FROM autofill_profiles, credit_cards "
+ "WHERE credit_cards.billing_address = autofill_profiles.label";
+ sql::Statement s(db_->GetUniqueStatement(stmt.c_str()));
+ if (!s)
+ return false;
+
+ std::map<int, int> cc_billing_map;
+ while (s.Step())
+ cc_billing_map[s.ColumnInt(0)] = s.ColumnInt(1);
+
+ // Windows already stores the IDs as strings in |billing_address|. Try
+ // to convert those.
+ if (cc_billing_map.empty()) {
+ std::string stmt = "SELECT unique_id,billing_address FROM credit_cards";
+ sql::Statement s(db_->GetUniqueStatement(stmt.c_str()));
+ if (!s)
+ return false;
+
+ while (s.Step()) {
+ int id = 0;
+ if (base::StringToInt(s.ColumnString(1), &id))
+ cc_billing_map[s.ColumnInt(0)] = id;
+ }
+ }
+
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "label VARCHAR, "
+ "unique_id INTEGER PRIMARY KEY, "
+ "name_on_card VARCHAR, "
+ "type VARCHAR, "
+ "card_number VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "verification_code VARCHAR, "
+ "billing_address INTEGER, "
+ "shipping_address VARCHAR, "
+ "card_number_encrypted BLOB, "
+ "verification_code_encrypted BLOB)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT label,unique_id,name_on_card,type,card_number,"
+ "expiration_month,expiration_year,verification_code,0,"
+ "shipping_address,card_number_encrypted,"
+ "verification_code_encrypted FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+
+ for (std::map<int, int>::const_iterator iter = cc_billing_map.begin();
+ iter != cc_billing_map.end(); ++iter) {
+ sql::Statement s(db_->GetCachedStatement(
+ SQL_FROM_HERE,
+ "UPDATE credit_cards SET billing_address=? WHERE unique_id=?"));
+ if (!s)
+ return false;
+
+ s.BindInt(0, (*iter).second);
+ s.BindInt(1, (*iter).first);
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::AddDateModifedForVersion30() {
+ // Add date_modified to autofill_profiles.
+ if (!db_->DoesColumnExist("autofill_profiles", "date_modified")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "date_modified INTEGER NON NULL DEFAULT 0")) {
+ return false;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified=?"));
+ if (!s)
+ return false;
+
+ s.BindInt64(0, Time::Now().ToTimeT());
+
+ if (!s.Run())
+ return false;
+ }
+
+ // Add date_modified to credit_cards.
+ if (!db_->DoesColumnExist("credit_cards", "date_modified")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "date_modified INTEGER NON NULL DEFAULT 0")) {
+ return false;
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE credit_cards SET date_modified=?"));
+ if (!s)
+ return false;
+
+ s.BindInt64(0, Time::Now().ToTimeT());
+
+ if (!s.Run())
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::AddGUIDToCreditCardsAndProfilesForVersion31() {
+ // Add |guid| column to |autofill_profiles| table.
+ // Note that we need to check for the guid column's existence due to the
+ // fact that for a version 22 database the |autofill_profiles| table
+ // gets created fresh with |InitAutofillProfilesTable|.
+ if (!db_->DoesColumnExist("autofill_profiles", "guid")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "guid VARCHAR NOT NULL DEFAULT \"\"")) {
+ return false;
+ }
+
+ // Set all the |guid| fields to valid values.
+
+ sql::Statement s(db_->GetUniqueStatement("SELECT unique_id "
+ "FROM autofill_profiles"));
+ if (!s)
+ return false;
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE autofill_profiles "
+ "SET guid=? WHERE unique_id=?"));
+ if (!update_s)
+ return false;
+ update_s.BindString(0, guid::GenerateGUID());
+ update_s.BindInt(1, s.ColumnInt(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ }
+
+ // Add |guid| column to |credit_cards| table.
+ // Note that we need to check for the guid column's existence due to the
+ // fact that for a version 22 database the |autofill_profiles| table
+ // gets created fresh with |InitAutofillProfilesTable|.
+ if (!db_->DoesColumnExist("credit_cards", "guid")) {
+ if (!db_->Execute("ALTER TABLE credit_cards ADD COLUMN "
+ "guid VARCHAR NOT NULL DEFAULT \"\"")) {
+ return false;
+ }
+
+ // Set all the |guid| fields to valid values.
+
+ sql::Statement s(db_->GetUniqueStatement("SELECT unique_id "
+ "FROM credit_cards"));
+ if (!s)
+ return false;
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE credit_cards "
+ "set guid=? WHERE unique_id=?"));
+ if (!update_s)
+ return false;
+ update_s.BindString(0, guid::GenerateGUID());
+ update_s.BindInt(1, s.ColumnInt(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AutofillTable::UpdateProfilesAndCreditCardsForVersion32() {
+ if (db_->DoesColumnExist("autofill_profiles", "unique_id")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "label VARCHAR, "
+ "first_name VARCHAR, "
+ "middle_name VARCHAR, "
+ "last_name VARCHAR, "
+ "email VARCHAR, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "phone VARCHAR, "
+ "fax VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO autofill_profiles_temp "
+ "SELECT guid, label, first_name, middle_name, last_name, email, "
+ "company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, fax, date_modified "
+ "FROM autofill_profiles")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE autofill_profiles"))
+ return false;
+
+ if (!db_->Execute(
+ "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
+ return false;
+ }
+ }
+
+ if (db_->DoesColumnExist("credit_cards", "unique_id")) {
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "label VARCHAR, "
+ "name_on_card VARCHAR, "
+ "expiration_month INTEGER, "
+ "expiration_year INTEGER, "
+ "card_number_encrypted BLOB, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT guid, label, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::UpdateProfilesBasedOnFirstNameForVersion33() {
+ // Test the existence of the |first_name| column as an indication that
+ // we need a migration. It is possible that the new |autofill_profiles|
+ // schema is in place because the table was newly created when migrating
+ // from a pre-version-22 database.
+ if (db_->DoesColumnExist("autofill_profiles", "first_name")) {
+ // Create autofill_profiles_temp table that will receive the data.
+ if (!db_->DoesTableExist("autofill_profiles_temp")) {
+ if (!db_->Execute("CREATE TABLE autofill_profiles_temp ( "
+ "guid VARCHAR PRIMARY KEY, "
+ "company_name VARCHAR, "
+ "address_line_1 VARCHAR, "
+ "address_line_2 VARCHAR, "
+ "city VARCHAR, "
+ "state VARCHAR, "
+ "zipcode VARCHAR, "
+ "country VARCHAR, "
+ "date_modified INTEGER NOT NULL DEFAULT 0)")) {
+ return false;
+ }
+ }
+
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, first_name, middle_name, last_name, email, "
+ "company_name, address_line_1, address_line_2, city, state, "
+ "zipcode, country, phone, fax, date_modified "
+ "FROM autofill_profiles"));
+ while (s.Step()) {
+ AutofillProfile profile;
+ profile.set_guid(s.ColumnString(0));
+ DCHECK(guid::IsValidGUID(profile.guid()));
+
+ profile.SetInfo(NAME_FIRST, s.ColumnString16(1));
+ profile.SetInfo(NAME_MIDDLE, s.ColumnString16(2));
+ profile.SetInfo(NAME_LAST, s.ColumnString16(3));
+ profile.SetInfo(EMAIL_ADDRESS, s.ColumnString16(4));
+ profile.SetInfo(COMPANY_NAME, s.ColumnString16(5));
+ profile.SetInfo(ADDRESS_HOME_LINE1, s.ColumnString16(6));
+ profile.SetInfo(ADDRESS_HOME_LINE2, s.ColumnString16(7));
+ profile.SetInfo(ADDRESS_HOME_CITY, s.ColumnString16(8));
+ profile.SetInfo(ADDRESS_HOME_STATE, s.ColumnString16(9));
+ profile.SetInfo(ADDRESS_HOME_ZIP, s.ColumnString16(10));
+ profile.SetInfo(ADDRESS_HOME_COUNTRY, s.ColumnString16(11));
+ profile.SetInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(12));
+ profile.SetInfo(PHONE_FAX_WHOLE_NUMBER, s.ColumnString16(13));
+ int64 date_modified = s.ColumnInt64(14);
+
+ sql::Statement s_insert(db_->GetUniqueStatement(
+ "INSERT INTO autofill_profiles_temp"
+ "(guid, company_name, address_line_1, address_line_2, city,"
+ " state, zipcode, country, date_modified)"
+ "VALUES (?,?,?,?,?,?,?,?,?)"));
+ if (!s)
+ return false;
+
+ s_insert.BindString(0, profile.guid());
+ s_insert.BindString16(1, profile.GetInfo(COMPANY_NAME));
+ s_insert.BindString16(2, profile.GetInfo(ADDRESS_HOME_LINE1));
+ s_insert.BindString16(3, profile.GetInfo(ADDRESS_HOME_LINE2));
+ s_insert.BindString16(4, profile.GetInfo(ADDRESS_HOME_CITY));
+ s_insert.BindString16(5, profile.GetInfo(ADDRESS_HOME_STATE));
+ s_insert.BindString16(6, profile.GetInfo(ADDRESS_HOME_ZIP));
+ s_insert.BindString16(7, profile.GetInfo(ADDRESS_HOME_COUNTRY));
+ s_insert.BindInt64(8, date_modified);
+
+ if (!s_insert.Run())
+ return false;
+
+ // Add the other bits: names, emails, and phone/fax.
+ if (!AddAutofillProfilePieces(profile, db_))
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE autofill_profiles"))
+ return false;
+
+ if (!db_->Execute(
+ "ALTER TABLE autofill_profiles_temp RENAME TO autofill_profiles")) {
+ return false;
+ }
+ }
+
+ // Remove the labels column from the credit_cards table.
+ if (db_->DoesColumnExist("credit_cards", "label")) {
+ if (!db_->Execute("CREATE TABLE credit_cards_temp ( "
+ "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)")) {
+ return false;
+ }
+
+ if (!db_->Execute(
+ "INSERT INTO credit_cards_temp "
+ "SELECT guid, name_on_card, expiration_month, "
+ "expiration_year, card_number_encrypted, date_modified "
+ "FROM credit_cards")) {
+ return false;
+ }
+
+ if (!db_->Execute("DROP TABLE credit_cards"))
+ return false;
+
+ if (!db_->Execute("ALTER TABLE credit_cards_temp RENAME TO credit_cards"))
+ return false;
+ }
+
+ return true;
+}
+
+bool AutofillTable::UpdatesProfilesBasedOnCountryCodeForVersion34() {
+ // Test the existence of the |country_code| column as an indication that
+ // we need a migration. It is possible that the new |autofill_profiles|
+ // schema is in place because the table was newly created when migrating
+ // from a pre-version-22 database.
+ if (!db_->DoesColumnExist("autofill_profiles", "country_code")) {
+ if (!db_->Execute("ALTER TABLE autofill_profiles ADD COLUMN "
+ "country_code VARCHAR")) {
+ return false;
+ }
+
+ // Set all the |country_code| fields to match existing |country| values.
+ sql::Statement s(db_->GetUniqueStatement("SELECT guid, country "
+ "FROM autofill_profiles"));
+
+ if (!s)
+ return false;
+
+ while (s.Step()) {
+ sql::Statement update_s(
+ db_->GetUniqueStatement("UPDATE autofill_profiles "
+ "SET country_code=? WHERE guid=?"));
+ if (!update_s)
+ return false;
+
+ string16 country = s.ColumnString16(1);
+ std::string app_locale = AutofillCountry::ApplicationLocale();
+ update_s.BindString(0, AutofillCountry::GetCountryCode(country,
+ app_locale));
+ update_s.BindString(1, s.ColumnString(0));
+
+ if (!update_s.Run())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool AutofillTable::CorrectGreatBritainCountryCodesForVersion35() {
+ // Correct all country codes with value "UK" to be "GB". This data
+ // was mistakenly introduced in build 686.0. This migration is to clean
+ // it up. See http://crbug.com/74511 for details.
+ sql::Statement s(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET country_code=\"GB\" "
+ "WHERE country_code=\"UK\""));
+
+ return s.Run();
+}
+
+bool AutofillTable::MergeAndCullOlderProfilesForVersion36() {
+ // Merge and cull older profiles where possible.
+ sql::Statement s(db_->GetUniqueStatement(
+ "SELECT guid, date_modified FROM autofill_profiles"));
+ if (!s)
+ return false;
+
+ // Accumulate the good profiles.
+ std::vector<AutofillProfile> accumulated_profiles;
+ std::vector<AutofillProfile*> accumulated_profiles_p;
+ std::map<std::string, int64> modification_map;
+ while (s.Step()) {
+ std::string guid = s.ColumnString(0);
+ int64 date_modified = s.ColumnInt64(1);
+ modification_map.insert(
+ std::pair<std::string, int64>(guid, date_modified));
+ AutofillProfile* profile = NULL;
+ if (!GetAutofillProfile(guid, &profile))
+ return false;
+
+ scoped_ptr<AutofillProfile> p(profile);
+
+ if (PersonalDataManager::IsValidLearnableProfile(*p)) {
+ std::vector<AutofillProfile> merged_profiles;
+ bool merged = PersonalDataManager::MergeProfile(
+ *p, accumulated_profiles_p, &merged_profiles);
+
+ std::swap(accumulated_profiles, merged_profiles);
+
+ accumulated_profiles_p.clear();
+ accumulated_profiles_p.resize(accumulated_profiles.size());
+ std::transform(accumulated_profiles.begin(),
+ accumulated_profiles.end(),
+ accumulated_profiles_p.begin(),
+ address_of<AutofillProfile>);
+
+ // If the profile got merged trash the original.
+ if (merged)
+ AddAutofillGUIDToTrash(p->guid());
+
+ } else {
+ // An invalid profile, so trash it.
+ AddAutofillGUIDToTrash(p->guid());
+ }
+ }
+
+ // Drop the current profiles.
+ if (!ClearAutofillProfiles())
+ return false;
+
+ // Add the newly merged profiles back in.
+ for (std::vector<AutofillProfile>::const_iterator
+ iter = accumulated_profiles.begin();
+ iter != accumulated_profiles.end();
+ ++iter) {
+ if (!AddAutofillProfile(*iter))
+ return false;
+
+ // Fix up the original modification date.
+ std::map<std::string, int64>::const_iterator date_item =
+ modification_map.find(iter->guid());
+ if (date_item == modification_map.end())
+ return false;
+
+ sql::Statement s_date(db_->GetUniqueStatement(
+ "UPDATE autofill_profiles SET date_modified=? "
+ "WHERE guid=?"));
+ s_date.BindInt64(0, date_item->second);
+ s_date.BindString(1, iter->guid());
+ if (!s_date.Run())
+ return false;
+ }
+
+ return true;
+}

Powered by Google App Engine
This is Rietveld 408576698