blob: 7590369c49fc0e67ca70337b17c5396e2299ac1c [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/core/browser/webdata/autofill_table.h"
#include <stdint.h>
#include <algorithm>
#include <initializer_list>
#include <limits>
#include <map>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/guid.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/autofill_type.h"
#include "components/autofill/core/browser/data_model/autofill_metadata.h"
#include "components/autofill/core/browser/data_model/autofill_offer_data.h"
#include "components/autofill/core/browser/data_model/autofill_wallet_usage_data.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/data_model/credit_card_cloud_token_data.h"
#include "components/autofill/core/browser/data_model/iban.h"
#include "components/autofill/core/browser/geo/autofill_country.h"
#include "components/autofill/core/browser/payments/payments_customer_data.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/webdata/autofill_change.h"
#include "components/autofill/core/browser/webdata/autofill_entry.h"
#include "components/autofill/core/browser/webdata/autofill_table_encryptor.h"
#include "components/autofill/core/browser/webdata/autofill_table_encryptor_factory.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_constants.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_field_data.h"
#include "components/sync/base/model_type.h"
#include "components/sync/model/metadata_batch.h"
#include "components/sync/protocol/entity_metadata.pb.h"
#include "components/sync/protocol/model_type_state.pb.h"
#include "components/webdata/common/web_database.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "ui/base/l10n/l10n_util.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace autofill {
namespace {
constexpr base::StringPiece kAutofillTable = "autofill";
constexpr base::StringPiece kName = "name";
constexpr base::StringPiece kValue = "value";
constexpr base::StringPiece kValueLower = "value_lower";
constexpr base::StringPiece kDateCreated = "date_created";
constexpr base::StringPiece kDateLastUsed = "date_last_used";
constexpr base::StringPiece kCount = "count";
constexpr base::StringPiece kAutofillProfilesTable = "autofill_profiles";
constexpr base::StringPiece kGuid = "guid";
constexpr base::StringPiece kLabel = "label";
constexpr base::StringPiece kCompanyName = "company_name";
constexpr base::StringPiece kStreetAddress = "street_address";
constexpr base::StringPiece kDependentLocality = "dependent_locality";
constexpr base::StringPiece kCity = "city";
constexpr base::StringPiece kState = "state";
constexpr base::StringPiece kZipcode = "zipcode";
constexpr base::StringPiece kSortingCode = "sorting_code";
constexpr base::StringPiece kCountryCode = "country_code";
constexpr base::StringPiece kUseCount = "use_count";
constexpr base::StringPiece kUseDate = "use_date";
constexpr base::StringPiece kDateModified = "date_modified";
constexpr base::StringPiece kOrigin = "origin";
constexpr base::StringPiece kLanguageCode = "language_code";
constexpr base::StringPiece kDisallowSettingsVisibleUpdates =
"disallow_settings_visible_updates";
constexpr base::StringPiece kAutofillProfileAddressesTable =
"autofill_profile_addresses";
// kGuid = "guid"
// kStreetAddress = "street_address"
constexpr base::StringPiece kStreetName = "street_name";
constexpr base::StringPiece kDependentStreetName = "dependent_street_name";
constexpr base::StringPiece kHouseNumber = "house_number";
constexpr base::StringPiece kSubpremise = "subpremise";
// kDependentLocality = "dependent_locality"
// kCity = "city"
// kState = "state"
constexpr base::StringPiece kZipCode = "zip_code";
// kCountryCode = "country_code"
// kSortingCode = "sorting_code"
constexpr base::StringPiece kPremiseName = "premise_name";
constexpr base::StringPiece kApartmentNumber = "apartment_number";
constexpr base::StringPiece kFloor = "floor";
constexpr base::StringPiece kStreetAddressStatus = "street_address_status";
constexpr base::StringPiece kStreetNameStatus = "street_name_status";
constexpr base::StringPiece kDependentStreetNameStatus =
"dependent_street_name_status";
constexpr base::StringPiece kHouseNumberStatus = "house_number_status";
constexpr base::StringPiece kSubpremiseStatus = "subpremise_status";
constexpr base::StringPiece kPremiseNameStatus = "premise_name_status";
constexpr base::StringPiece kDependentLocalityStatus =
"dependent_locality_status";
constexpr base::StringPiece kCityStatus = "city_status";
constexpr base::StringPiece kStateStatus = "state_status";
constexpr base::StringPiece kZipCodeStatus = "zip_code_status";
constexpr base::StringPiece kCountryCodeStatus = "country_code_status";
constexpr base::StringPiece kSortingCodeStatus = "sorting_code_status";
constexpr base::StringPiece kApartmentNumberStatus = "apartment_number_status";
constexpr base::StringPiece kFloorStatus = "floor_status";
constexpr base::StringPiece kAutofillProfileNamesTable =
"autofill_profile_names";
// kGuid = "guid"
constexpr base::StringPiece kHonorificPrefix = "honorific_prefix";
constexpr base::StringPiece kFirstName = "first_name";
constexpr base::StringPiece kMiddleName = "middle_name";
constexpr base::StringPiece kLastName = "last_name";
constexpr base::StringPiece kFirstLastName = "first_last_name";
constexpr base::StringPiece kConjunctionLastName = "conjunction_last_name";
constexpr base::StringPiece kSecondLastName = "second_last_name";
constexpr base::StringPiece kFullName = "full_name";
constexpr base::StringPiece kFullNameWithHonorificPrefix =
"full_name_with_honorific_prefix";
constexpr base::StringPiece kHonorificPrefixStatus = "honorific_prefix_status";
constexpr base::StringPiece kFirstNameStatus = "first_name_status";
constexpr base::StringPiece kMiddleNameStatus = "middle_name_status";
constexpr base::StringPiece kLastNameStatus = "last_name_status";
constexpr base::StringPiece kFirstLastNameStatus = "first_last_name_status";
constexpr base::StringPiece kConjunctionLastNameStatus =
"conjunction_last_name_status";
constexpr base::StringPiece kSecondLastNameStatus = "second_last_name_status";
constexpr base::StringPiece kFullNameStatus = "full_name_status";
constexpr base::StringPiece kFullNameWithHonorificPrefixStatus =
"full_name_with_honorific_prefix_status";
constexpr base::StringPiece kAutofillProfileEmailsTable =
"autofill_profile_emails";
// kGuid = "guid"
constexpr base::StringPiece kEmail = "email";
constexpr base::StringPiece kAutofillProfilePhonesTable =
"autofill_profile_phones";
// kGuid = "guid"
constexpr base::StringPiece kNumber = "number";
constexpr base::StringPiece kAutofillProfileBirthdatesTable =
"autofill_profile_birthdates";
// kGuid = "guid"
constexpr base::StringPiece kDay = "day";
constexpr base::StringPiece kMonth = "month";
constexpr base::StringPiece kYear = "year";
constexpr base::StringPiece kCreditCardsTable = "credit_cards";
// kGuid = "guid"
constexpr base::StringPiece kNameOnCard = "name_on_card";
constexpr base::StringPiece kExpirationMonth = "expiration_month";
constexpr base::StringPiece kExpirationYear = "expiration_year";
constexpr base::StringPiece kCardNumberEncrypted = "card_number_encrypted";
// kUseCount = "use_count"
// kUseDate = "use_date"
// kDateModified = "date_modified"
// kOrigin = "origin"
constexpr base::StringPiece kBillingAddressId = "billing_address_id";
constexpr base::StringPiece kNickname = "nickname";
constexpr base::StringPiece kMaskedCreditCardsTable = "masked_credit_cards";
constexpr base::StringPiece kId = "id";
constexpr base::StringPiece kStatus = "status";
// kNameOnCard = "name_on_card"
constexpr base::StringPiece kNetwork = "network";
constexpr base::StringPiece kLastFour = "last_four";
constexpr base::StringPiece kExpMonth = "exp_month";
constexpr base::StringPiece kExpYear = "exp_year";
constexpr base::StringPiece kBankName = "bank_name";
// kNickname = "nickname"
constexpr base::StringPiece kCardIssuer = "card_issuer";
constexpr base::StringPiece kCardIssuerId = "card_issuer_id";
constexpr base::StringPiece kInstrumentId = "instrument_id";
constexpr base::StringPiece kVirtualCardEnrollmentState =
"virtual_card_enrollment_state";
constexpr base::StringPiece kCardArtUrl = "card_art_url";
constexpr base::StringPiece kProductDescription = "product_description";
constexpr base::StringPiece kUnmaskedCreditCardsTable = "unmasked_credit_cards";
// kId = "id"
// kCardNumberEncrypted = "card_number_encrypted"
constexpr base::StringPiece kUnmaskDate = "unmask_date";
constexpr base::StringPiece kServerCardCloudTokenDataTable =
"server_card_cloud_token_data";
// kId = "id"
constexpr base::StringPiece kSuffix = "suffix";
// kExpMonth = "exp_month"
// kExpYear = "exp_year"
// kCardArtUrl = "card_art_url"
constexpr base::StringPiece kInstrumentToken = "instrument_token";
constexpr base::StringPiece kServerCardMetadataTable = "server_card_metadata";
// kId = "id"
// kUseCount = "use_count"
// kUseDate = "use_date"
// kBillingAddressId = "billing_address_id"
constexpr base::StringPiece kIBANsTable = "ibans";
// kGuid = "guid"
// kUseCount = "use_count"
// kUseDate = "use_date"
// kValue = "value"
// kNickname = "nickname"
constexpr base::StringPiece kServerAddressesTable = "server_addresses";
// kId = "id"
constexpr base::StringPiece kRecipientName = "recipient_name";
// kCompanyName = "company_name"
// kStreetAddress = "street_address"
constexpr base::StringPiece kAddress1 = "address_1";
constexpr base::StringPiece kAddress2 = "address_2";
constexpr base::StringPiece kAddress3 = "address_3";
constexpr base::StringPiece kAddress4 = "address_4";
constexpr base::StringPiece kPostalCode = "postal_code";
// kSortingCode = "sorting_code"
// kCountryCode = "country_code"
// kLanguageCode = "language_code"
constexpr base::StringPiece kPhoneNumber = "phone_number";
constexpr base::StringPiece kServerAddressMetadataTable =
"server_address_metadata";
// kId = "id"
// kUseCount = "use_count"
// kUseDate = "use_date"
constexpr base::StringPiece kHasConverted = "has_converted";
constexpr base::StringPiece kAutofillSyncMetadataTable =
"autofill_sync_metadata";
constexpr base::StringPiece kModelType = "model_type";
constexpr base::StringPiece kStorageKey = "storage_key";
// kValue = "value"
constexpr base::StringPiece kAutofillModelTypeStateTable =
"autofill_model_type_state";
// kModelType = "model_type"
// kValue = "value"
constexpr base::StringPiece kPaymentsCustomerDataTable =
"payments_customer_data";
constexpr base::StringPiece kCustomerId = "customer_id";
constexpr base::StringPiece kPaymentsUpiVpaTable = "payments_upi_vpa";
constexpr base::StringPiece kVpa = "vpa";
constexpr base::StringPiece kOfferDataTable = "offer_data";
constexpr base::StringPiece kOfferId = "offer_id";
constexpr base::StringPiece kOfferRewardAmount = "offer_reward_amount";
constexpr base::StringPiece kExpiry = "expiry";
constexpr base::StringPiece kOfferDetailsUrl = "offer_details_url";
constexpr base::StringPiece kPromoCode = "promo_code";
constexpr base::StringPiece kValuePropText = "value_prop_text";
constexpr base::StringPiece kSeeDetailsText = "see_details_text";
constexpr base::StringPiece kUsageInstructionsText = "usage_instructions_text";
constexpr base::StringPiece kOfferEligibleInstrumentTable =
"offer_eligible_instrument";
// kOfferId = "offer_id"
// kInstrumentId = "instrument_id"
constexpr base::StringPiece kOfferMerchantDomainTable = "offer_merchant_domain";
// kOfferId = "offer_id"
constexpr base::StringPiece kMerchantDomain = "merchant_domain";
constexpr base::StringPiece kContactInfoTable = "contact_info";
// kGuid = "guid"
// kUseCount = "use_count"
// kUseDate = "use_date"
// kDateModified = "date_modified"
// kLanguageCode = "language_code"
// kLabel = "label"
constexpr base::StringPiece kContactInfoTypeTokensTable =
"contact_info_type_tokens";
// kGuid = "guid"
constexpr base::StringPiece kType = "type";
// kValue = "value"
constexpr base::StringPiece kVerificationStatus = "verification_status";
constexpr base::StringPiece kVirtualCardUsageDataTable =
"virtual_card_usage_data";
// kId = "id"
// kInstrumentId = "instrument_id"
// kMerchantDomain = "merchant_domain"
// kLastFour = "last_four"
// Helper functions to construct SQL statements from string constants.
// - Functions with names corresponding to SQL keywords execute the statement
// directly and return if it was successful.
// - Builder functions only assign the statement, which enables binding
// values to placeholders before running it.
// Executes a CREATE TABLE statement on `db` which the provided
// `table_name`. The columns are described in `column_names_and_types` as
// pairs of (name, type), where type can include modifiers such as NOT NULL.
// By specifying `compositive_primary_key`, a PRIMARY KEY (col1, col2, ..)
// clause is generated.
// Returns true if successful.
bool CreateTable(
sql::Database* db,
base::StringPiece table_name,
std::initializer_list<std::pair<base::StringPiece, base::StringPiece>>
column_names_and_types,
std::initializer_list<base::StringPiece> composite_primary_key = {}) {
DCHECK(composite_primary_key.size() == 0 ||
composite_primary_key.size() >= 2);
std::vector<std::string> combined_names_and_types;
combined_names_and_types.reserve(column_names_and_types.size());
for (const auto& [name, type] : column_names_and_types)
combined_names_and_types.push_back(base::StrCat({name, " ", type}));
auto primary_key_clause =
composite_primary_key.size() == 0
? ""
: base::StrCat({", PRIMARY KEY (",
base::JoinString(composite_primary_key, ", "), ")"});
return db->Execute(
base::StrCat({"CREATE TABLE ", table_name, " (",
base::JoinString(combined_names_and_types, ", "),
primary_key_clause, ")"})
.c_str());
}
// Wrapper around `CreateTable()` that condition the creation on the
// `table_name` not existing.
// Returns true if the table now exists.
bool CreateTableIfNotExists(
sql::Database* db,
base::StringPiece table_name,
std::initializer_list<std::pair<base::StringPiece, base::StringPiece>>
column_names_and_types,
std::initializer_list<base::StringPiece> composite_primary_key = {}) {
return db->DoesTableExist(table_name) ||
CreateTable(db, table_name, column_names_and_types,
composite_primary_key);
}
// Creates and index on `table_name` for the provided `columns`.
// The index is named after the table and columns, separated by '_'.
// Returns true if successful.
bool CreateIndex(sql::Database* db,
base::StringPiece table_name,
std::initializer_list<base::StringPiece> columns) {
auto index_name =
base::StrCat({table_name, "_", base::JoinString(columns, "_")});
return db->Execute(
base::StrCat({"CREATE INDEX ", index_name, " ON ", table_name, "(",
base::JoinString(columns, ", "), ")"})
.c_str());
}
// Initializes `statement` with INSERT INTO `table_name`, with placeholders for
// all `column_names`.
// By setting `or_replace`, INSERT OR REPLACE INTO is used instead.
void InsertBuilder(sql::Database* db,
sql::Statement& statement,
base::StringPiece table_name,
std::initializer_list<base::StringPiece> column_names,
bool or_replace = false) {
auto insert_or_replace =
base::StrCat({"INSERT ", or_replace ? "OR REPLACE " : ""});
auto placeholders = base::JoinString(
std::vector<std::string>(column_names.size(), "?"), ", ");
statement.Assign(db->GetUniqueStatement(
base::StrCat({insert_or_replace, "INTO ", table_name, " (",
base::JoinString(column_names, ", "), ") VALUES (",
placeholders, ")"})
.c_str()));
}
// Renames the table `from` into `to` and returns true if successful.
bool RenameTable(sql::Database* db,
base::StringPiece from,
base::StringPiece to) {
return db->Execute(
base::StrCat({"ALTER TABLE ", from, " RENAME TO ", to}).c_str());
}
// Wrapper around `sql::Database::DoesColumnExist()`, because that function
// only accepts const char* parameters.
bool DoesColumnExist(sql::Database* db,
base::StringPiece table_name,
base::StringPiece column_name) {
return db->DoesColumnExist(std::string(table_name).c_str(),
std::string(column_name).c_str());
}
// Adds a column named `column_name` of `type` to `table_name` and returns true
// if successful.
bool AddColumn(sql::Database* db,
base::StringPiece table_name,
base::StringPiece column_name,
base::StringPiece type) {
return db->Execute(base::StrCat({"ALTER TABLE ", table_name, " ADD COLUMN ",
column_name, " ", type})
.c_str());
}
// Like `AddColumn()`, but conditioned on `column` not existing in `table_name`.
// Returns true if the column is now part of the table
bool AddColumnIfNotExists(sql::Database* db,
base::StringPiece table_name,
base::StringPiece column_name,
base::StringPiece type) {
return DoesColumnExist(db, table_name, column_name) ||
AddColumn(db, table_name, column_name, type);
}
// Drops `table_name` and returns true if successful.
bool DropTable(sql::Database* db, base::StringPiece table_name) {
return db->Execute(base::StrCat({"DROP TABLE ", table_name}).c_str());
}
// Initializes `statement` with DELETE FROM `table_name`. A WHERE clause
// can optionally be specified in `where_clause`.
void DeleteBuilder(sql::Database* db,
sql::Statement& statement,
base::StringPiece table_name,
base::StringPiece where_clause = "") {
auto where =
where_clause.empty() ? "" : base::StrCat({" WHERE ", where_clause});
statement.Assign(db->GetUniqueStatement(
base::StrCat({"DELETE FROM ", table_name, where}).c_str()));
}
// Like `DeleteBuilder()`, but runs the statement and returns true if it was
// successful.
bool Delete(sql::Database* db,
base::StringPiece table_name,
base::StringPiece where_clause = "") {
sql::Statement statement;
DeleteBuilder(db, statement, table_name, where_clause);
return statement.Run();
}
// Wrapper around `DeleteBuilder()`, which initializes the where clause as
// `column` = `value`.
// Runs the statement and returns true if it was successful.
bool DeleteWhereColumnEq(sql::Database* db,
base::StringPiece table_name,
base::StringPiece column,
base::StringPiece value) {
sql::Statement statement;
DeleteBuilder(db, statement, table_name, base::StrCat({column, " = ?"}));
statement.BindString(0, value);
return statement.Run();
}
// Initializes `statement` with UPDATE `table_name` SET `column_names` = ?, with
// a placeholder for every `column_names`. A WHERE clause can optionally be
// specified in `where_clause`.
void UpdateBuilder(sql::Database* db,
sql::Statement& statement,
base::StringPiece table_name,
std::initializer_list<base::StringPiece> column_names,
base::StringPiece where_clause = "") {
auto columns_with_placeholders =
base::JoinString(column_names, " = ?, ") + " = ?";
auto where =
where_clause.empty() ? "" : base::StrCat({" WHERE ", where_clause});
statement.Assign(
db->GetUniqueStatement(base::StrCat({"UPDATE ", table_name, " SET ",
columns_with_placeholders, where})
.c_str()));
}
// Initializes `statement` with SELECT `columns` FROM `table_name` and
// optionally further `modifiers`, such as WHERE, ORDER BY, etc.
void SelectBuilder(sql::Database* db,
sql::Statement& statement,
base::StringPiece table_name,
std::initializer_list<base::StringPiece> columns,
base::StringPiece modifiers = "") {
statement.Assign(db->GetUniqueStatement(
base::StrCat({"SELECT ", base::JoinString(columns, ", "), " FROM ",
table_name, " ", modifiers})
.c_str()));
}
// Wrapper around `SelectBuilder()` that restricts the it to the provided
// `guid`. Returns `statement.is_valid() && statement.Step()`.
bool SelectByGuid(sql::Database* db,
sql::Statement& statement,
base::StringPiece table_name,
std::initializer_list<base::StringPiece> columns,
base::StringPiece guid) {
SelectBuilder(db, statement, table_name, columns, "WHERE guid=?");
statement.BindString(0, guid);
return statement.is_valid() && statement.Step();
}
// Wrapper around `SelectBuilder()` that restricts it to the half-open interval
// [low, high[ of `column_between`.
void SelectBetween(sql::Database* db,
sql::Statement& statement,
base::StringPiece table_name,
std::initializer_list<base::StringPiece> columns,
base::StringPiece column_between,
int64_t low,
int64_t high) {
auto between_selector = base::StrCat(
{"WHERE ", column_between, " >= ? AND ", column_between, " < ?"});
SelectBuilder(db, statement, table_name, columns, between_selector);
statement.BindInt64(0, low);
statement.BindInt64(1, high);
}
// Helper struct for AutofillTable::RemoveFormElementsAddedBetween().
// Contains all the necessary fields to update a row in the 'autofill' table.
struct AutofillUpdate {
std::u16string name;
std::u16string value;
time_t date_created;
time_t date_last_used;
int count;
};
// Truncates `data` to the maximum length that can be stored in a column of the
// Autofill database. Shorter strings are left as-is.
std::u16string Truncate(const std::u16string& data) {
return data.substr(0, AutofillTable::kMaxDataLength);
}
void BindAutofillProfileToStatement(const AutofillProfile& profile,
const base::Time& modification_date,
sql::Statement* s) {
DCHECK(base::IsValidGUID(profile.guid()));
int index = 0;
s->BindString(index++, profile.guid());
for (ServerFieldType type :
{COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS,
ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE,
ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) {
s->BindString16(index++, Truncate(profile.GetRawInfo(type)));
}
s->BindInt64(index++, profile.use_count());
s->BindInt64(index++, profile.use_date().ToTimeT());
s->BindInt64(index++, modification_date.ToTimeT());
s->BindString(index++, profile.origin());
s->BindString(index++, profile.language_code());
s->BindString(index++, profile.profile_label());
s->BindBool(index++, profile.disallow_settings_visible_updates());
}
void AddAutofillProfileDetailsFromStatement(sql::Statement& s,
AutofillProfile* profile) {
int index = 1; // 0 is for the origin.
for (ServerFieldType type :
{COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS,
ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE,
ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) {
profile->SetRawInfo(type, s.ColumnString16(index++));
}
profile->set_use_count(s.ColumnInt64(index++));
profile->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
profile->set_modification_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
profile->set_language_code(s.ColumnString(index++));
profile->set_profile_label(s.ColumnString(index++));
profile->set_disallow_settings_visible_updates(s.ColumnBool(index++));
}
void BindEncryptedCardToColumn(sql::Statement* s,
int column_index,
const std::u16string& number,
const AutofillTableEncryptor& encryptor) {
std::string encrypted_data;
encryptor.EncryptString16(number, &encrypted_data);
s->BindBlob(column_index, encrypted_data);
}
void BindCreditCardToStatement(const CreditCard& credit_card,
const base::Time& modification_date,
sql::Statement* s,
const AutofillTableEncryptor& encryptor) {
DCHECK(base::IsValidGUID(credit_card.guid()));
int index = 0;
s->BindString(index++, credit_card.guid());
for (ServerFieldType type : {CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_MONTH,
CREDIT_CARD_EXP_4_DIGIT_YEAR}) {
s->BindString16(index++, Truncate(credit_card.GetRawInfo(type)));
}
BindEncryptedCardToColumn(
s, index++, credit_card.GetRawInfo(CREDIT_CARD_NUMBER), encryptor);
s->BindInt64(index++, credit_card.use_count());
s->BindInt64(index++, credit_card.use_date().ToTimeT());
s->BindInt64(index++, modification_date.ToTimeT());
s->BindString(index++, credit_card.origin());
s->BindString(index++, credit_card.billing_address_id());
s->BindString16(index++, credit_card.nickname());
}
void BindIBANToStatement(const IBAN& iban,
sql::Statement* s,
const AutofillTableEncryptor& encryptor) {
DCHECK(base::IsValidGUID(iban.guid()));
int index = 0;
s->BindString(index++, iban.guid());
s->BindInt64(index++, iban.use_count());
s->BindInt64(index++, iban.use_date().ToTimeT());
s->BindString16(index++, iban.value());
s->BindString16(index++, iban.nickname());
}
std::u16string UnencryptedCardFromColumn(
sql::Statement& s,
int column_index,
const AutofillTableEncryptor& encryptor) {
std::u16string credit_card_number;
std::string encrypted_number;
s.ColumnBlobAsString(column_index, &encrypted_number);
if (!encrypted_number.empty())
encryptor.DecryptString16(encrypted_number, &credit_card_number);
return credit_card_number;
}
std::unique_ptr<CreditCard> CreditCardFromStatement(
sql::Statement& s,
const AutofillTableEncryptor& encryptor) {
auto credit_card = std::make_unique<CreditCard>();
int index = 0;
credit_card->set_guid(s.ColumnString(index++));
DCHECK(base::IsValidGUID(credit_card->guid()));
for (ServerFieldType type : {CREDIT_CARD_NAME_FULL, CREDIT_CARD_EXP_MONTH,
CREDIT_CARD_EXP_4_DIGIT_YEAR}) {
credit_card->SetRawInfo(type, s.ColumnString16(index++));
}
credit_card->SetRawInfo(CREDIT_CARD_NUMBER,
UnencryptedCardFromColumn(s, index++, encryptor));
credit_card->set_use_count(s.ColumnInt64(index++));
credit_card->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
credit_card->set_modification_date(
base::Time::FromTimeT(s.ColumnInt64(index++)));
credit_card->set_origin(s.ColumnString(index++));
credit_card->set_billing_address_id(s.ColumnString(index++));
credit_card->SetNickname(s.ColumnString16(index++));
return credit_card;
}
std::unique_ptr<IBAN> IBANFromStatement(
sql::Statement& s,
const AutofillTableEncryptor& encryptor) {
auto iban = std::make_unique<IBAN>();
int index = 0;
iban->set_guid(s.ColumnString(index++));
DCHECK(base::IsValidGUID(iban->guid()));
iban->set_use_count(s.ColumnInt64(index++));
iban->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
iban->SetRawInfo(IBAN_VALUE, s.ColumnString16(index++));
iban->set_nickname(s.ColumnString16(index++));
return iban;
}
bool AddAutofillProfileNames(const AutofillProfile& profile,
sql::Database* db) {
sql::Statement s;
InsertBuilder(
db, s, kAutofillProfileNamesTable,
{kGuid, kHonorificPrefix, kHonorificPrefixStatus, kFirstName,
kFirstNameStatus, kMiddleName, kMiddleNameStatus, kFirstLastName,
kFirstLastNameStatus, kConjunctionLastName, kConjunctionLastNameStatus,
kSecondLastName, kSecondLastNameStatus, kLastName, kLastNameStatus,
kFullName, kFullNameStatus, kFullNameWithHonorificPrefix,
kFullNameWithHonorificPrefixStatus});
s.BindString(0, profile.guid());
int index = 1;
for (ServerFieldType type :
{NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST_FIRST,
NAME_LAST_CONJUNCTION, NAME_LAST_SECOND, NAME_LAST, NAME_FULL,
NAME_FULL_WITH_HONORIFIC_PREFIX}) {
s.BindString16(index++, profile.GetRawInfo(type));
s.BindInt(index++, profile.GetVerificationStatusInt(type));
}
return s.Run();
}
bool AddAutofillProfileAddresses(const AutofillProfile& profile,
sql::Database* db) {
sql::Statement s;
InsertBuilder(db, s, kAutofillProfileAddressesTable,
{kGuid,
kStreetAddress,
kStreetAddressStatus,
kStreetName,
kStreetNameStatus,
kDependentStreetName,
kDependentStreetNameStatus,
kHouseNumber,
kHouseNumberStatus,
kSubpremise,
kSubpremiseStatus,
kPremiseName,
kPremiseNameStatus,
kDependentLocality,
kDependentLocalityStatus,
kCity,
kCityStatus,
kState,
kStateStatus,
kZipCode,
kZipCodeStatus,
kSortingCode,
kSortingCodeStatus,
kCountryCode,
kCountryCodeStatus,
kApartmentNumber,
kApartmentNumberStatus,
kFloor,
kFloorStatus});
s.BindString(0, profile.guid());
int index = 1;
for (ServerFieldType type :
{ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STREET_NAME,
ADDRESS_HOME_DEPENDENT_STREET_NAME, ADDRESS_HOME_HOUSE_NUMBER,
ADDRESS_HOME_SUBPREMISE, ADDRESS_HOME_PREMISE_NAME,
ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE,
ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY,
ADDRESS_HOME_APT_NUM, ADDRESS_HOME_FLOOR}) {
s.BindString16(index++, profile.GetRawInfo(type));
s.BindInt(index++, profile.GetVerificationStatusInt(type));
}
return s.Run();
}
bool AddAutofillProfileNamesToProfile(sql::Database* db,
AutofillProfile* profile) {
sql::Statement s;
if (SelectByGuid(
db, s, kAutofillProfileNamesTable,
{kGuid, kHonorificPrefix, kHonorificPrefixStatus, kFirstName,
kFirstNameStatus, kMiddleName, kMiddleNameStatus, kFirstLastName,
kFirstLastNameStatus, kConjunctionLastName,
kConjunctionLastNameStatus, kSecondLastName, kSecondLastNameStatus,
kLastName, kLastNameStatus, kFullName, kFullNameStatus,
kFullNameWithHonorificPrefix, kFullNameWithHonorificPrefixStatus},
profile->guid())) {
DCHECK_EQ(profile->guid(), s.ColumnString(0));
int index = 1;
for (ServerFieldType type :
{NAME_HONORIFIC_PREFIX, NAME_FIRST, NAME_MIDDLE, NAME_LAST_FIRST,
NAME_LAST_CONJUNCTION, NAME_LAST_SECOND, NAME_LAST, NAME_FULL,
NAME_FULL_WITH_HONORIFIC_PREFIX}) {
profile->SetRawInfoWithVerificationStatusInt(
type, s.ColumnString16(index), s.ColumnInt(index + 1));
index += 2;
}
}
return s.Succeeded();
}
bool AddAutofillProfileAddressesToProfile(sql::Database* db,
AutofillProfile* profile) {
sql::Statement s;
if (SelectByGuid(db, s, kAutofillProfileAddressesTable,
{kGuid,
kStreetAddress,
kStreetAddressStatus,
kStreetName,
kStreetNameStatus,
kDependentStreetName,
kDependentStreetNameStatus,
kHouseNumber,
kHouseNumberStatus,
kSubpremise,
kSubpremiseStatus,
kPremiseName,
kPremiseNameStatus,
kDependentLocality,
kDependentLocalityStatus,
kCity,
kCityStatus,
kState,
kStateStatus,
kZipCode,
kZipCodeStatus,
kSortingCode,
kSortingCodeStatus,
kCountryCode,
kCountryCodeStatus,
kApartmentNumber,
kApartmentNumberStatus,
kFloor,
kFloorStatus},
profile->guid())) {
DCHECK_EQ(profile->guid(), s.ColumnString(0));
std::u16string street_address = s.ColumnString16(1);
std::u16string dependent_locality = s.ColumnString16(13);
std::u16string city = s.ColumnString16(15);
std::u16string state = s.ColumnString16(17);
std::u16string zip_code = s.ColumnString16(19);
std::u16string sorting_code = s.ColumnString16(21);
std::u16string country = s.ColumnString16(23);
std::u16string street_address_legacy =
profile->GetRawInfo(ADDRESS_HOME_STREET_ADDRESS);
std::u16string dependent_locality_legacy =
profile->GetRawInfo(ADDRESS_HOME_DEPENDENT_LOCALITY);
std::u16string city_legacy = profile->GetRawInfo(ADDRESS_HOME_CITY);
std::u16string state_legacy = profile->GetRawInfo(ADDRESS_HOME_STATE);
std::u16string zip_code_legacy = profile->GetRawInfo(ADDRESS_HOME_ZIP);
std::u16string sorting_code_legacy =
profile->GetRawInfo(ADDRESS_HOME_SORTING_CODE);
std::u16string country_legacy = profile->GetRawInfo(ADDRESS_HOME_COUNTRY);
// At this stage, the unstructured address was already written to
// the profile. If the address was changed by a legacy client, the
// information diverged from the one in this table that is only written by
// new clients. In this case remove the corresponding row from this table.
// Otherwise, read the new structured tokens and set the verification
// statuses for all tokens.
if (street_address == street_address_legacy &&
dependent_locality == dependent_locality_legacy &&
city == city_legacy && state == state_legacy &&
zip_code == zip_code_legacy && sorting_code == sorting_code_legacy &&
country == country_legacy) {
int index = 1;
for (ServerFieldType type :
{ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STREET_NAME,
ADDRESS_HOME_DEPENDENT_STREET_NAME, ADDRESS_HOME_HOUSE_NUMBER,
ADDRESS_HOME_SUBPREMISE, ADDRESS_HOME_PREMISE_NAME,
ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY,
ADDRESS_HOME_STATE, ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE,
ADDRESS_HOME_COUNTRY, ADDRESS_HOME_APT_NUM, ADDRESS_HOME_FLOOR}) {
profile->SetRawInfoWithVerificationStatusInt(
type, s.ColumnString16(index), s.ColumnInt(index + 1));
index += 2;
}
} else {
// Remove the structured information from the table for
// eventual deletion consistency.
DeleteWhereColumnEq(db, kAutofillProfileAddressesTable, kGuid,
profile->guid());
}
}
return s.Succeeded();
}
bool AddAutofillProfileEmailsToProfile(sql::Database* db,
AutofillProfile* profile) {
// TODO(estade): update schema so that multiple emails are not associated
// per unique profile guid. Please refer https://crbug.com/497934.
sql::Statement s;
if (SelectByGuid(db, s, kAutofillProfileEmailsTable, {kGuid, kEmail},
profile->guid())) {
DCHECK_EQ(profile->guid(), s.ColumnString(0));
profile->SetRawInfo(EMAIL_ADDRESS, s.ColumnString16(1));
}
return s.Succeeded();
}
bool AddAutofillProfilePhonesToProfile(sql::Database* db,
AutofillProfile* profile) {
// TODO(estade): update schema so that multiple phone numbers are not
// associated per unique profile guid. Please refer
// https://crbug.com/497934.
sql::Statement s;
if (SelectByGuid(db, s, kAutofillProfilePhonesTable, {kGuid, kNumber},
profile->guid())) {
DCHECK_EQ(profile->guid(), s.ColumnString(0));
profile->SetRawInfo(PHONE_HOME_WHOLE_NUMBER, s.ColumnString16(1));
}
return s.Succeeded();
}
bool AddAutofillProfileBirthdateToProfile(sql::Database* db,
AutofillProfile* profile) {
sql::Statement s;
if (SelectByGuid(db, s, kAutofillProfileBirthdatesTable,
{kGuid, kDay, kMonth, kYear}, profile->guid())) {
DCHECK_EQ(profile->guid(), s.ColumnString(0));
profile->SetRawInfoAsInt(BIRTHDATE_DAY, s.ColumnInt(1));
profile->SetRawInfoAsInt(BIRTHDATE_MONTH, s.ColumnInt(2));
profile->SetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR, s.ColumnInt(3));
}
return s.Succeeded();
}
bool AddAutofillProfileEmails(const AutofillProfile& profile,
sql::Database* db) {
// Add the new email.
sql::Statement s;
InsertBuilder(db, s, kAutofillProfileEmailsTable, {kGuid, kEmail});
s.BindString(0, profile.guid());
s.BindString16(1, profile.GetRawInfo(EMAIL_ADDRESS));
return s.Run();
}
bool AddAutofillProfilePhones(const AutofillProfile& profile,
sql::Database* db) {
// Add the new number.
sql::Statement s;
InsertBuilder(db, s, kAutofillProfilePhonesTable, {kGuid, kNumber});
s.BindString(0, profile.guid());
s.BindString16(1, profile.GetRawInfo(PHONE_HOME_WHOLE_NUMBER));
return s.Run();
}
bool AddAutofillProfileBirthdate(const AutofillProfile& profile,
sql::Database* db) {
// Add the new birthdate.
sql::Statement s;
InsertBuilder(db, s, kAutofillProfileBirthdatesTable,
{kGuid, kDay, kMonth, kYear});
s.BindString(0, profile.guid());
s.BindInt(1, profile.GetRawInfoAsInt(BIRTHDATE_DAY));
s.BindInt(2, profile.GetRawInfoAsInt(BIRTHDATE_MONTH));
s.BindInt(3, profile.GetRawInfoAsInt(BIRTHDATE_4_DIGIT_YEAR));
return s.Run();
}
bool AddAutofillProfilePieces(const AutofillProfile& profile,
sql::Database* db) {
return AddAutofillProfileNames(profile, db) &&
AddAutofillProfileEmails(profile, db) &&
AddAutofillProfilePhones(profile, db) &&
AddAutofillProfileAddresses(profile, db) &&
AddAutofillProfileBirthdate(profile, db);
}
bool RemoveAutofillProfilePieces(const std::string& guid, sql::Database* db) {
return DeleteWhereColumnEq(db, kAutofillProfileNamesTable, kGuid, guid) &&
DeleteWhereColumnEq(db, kAutofillProfileEmailsTable, kGuid, guid) &&
DeleteWhereColumnEq(db, kAutofillProfilePhonesTable, kGuid, guid) &&
DeleteWhereColumnEq(db, kAutofillProfileAddressesTable, kGuid, guid) &&
DeleteWhereColumnEq(db, kAutofillProfileBirthdatesTable, kGuid, guid);
}
WebDatabaseTable::TypeKey GetKey() {
// We just need a unique constant. Use the address of a static that
// COMDAT folding won't touch in an optimizing linker.
static int table_key = 0;
return reinterpret_cast<void*>(&table_key);
}
time_t GetEndTime(const base::Time& end) {
if (end.is_null() || end == base::Time::Max())
return std::numeric_limits<time_t>::max();
return end.ToTimeT();
}
// Returns |s| with |escaper| in front of each of occurrence of a character
// from |special_chars|. Any occurrence of |escaper| in |s| is doubled. For
// example, Substitute("hello_world!", "_%", '!'') returns "hello!_world!!".
std::u16string Substitute(const std::u16string& s,
const std::u16string& special_chars,
const char16_t& escaper) {
// Prepend |escaper| to the list of |special_chars|.
std::u16string escape_wildcards(special_chars);
escape_wildcards.insert(escape_wildcards.begin(), escaper);
// Prepend the |escaper| just before |special_chars| in |s|.
std::u16string result(s);
for (char16_t c : escape_wildcards) {
for (size_t pos = 0; (pos = result.find(c, pos)) != std::u16string::npos;
pos += 2) {
result.insert(result.begin() + pos, escaper);
}
}
return result;
}
// All ServerFieldTypes stored for an AutofillProfile in `kContactInfoTable`.
// When introducing a new field type, it suffices to add it here. When removing
// a field type, removing it from the list suffices (no additional clean-up in
// the table necessary).
// This is not reusing `AutofillProfile::SupportedTypes()` for three reasons:
// - Due to the table design, the stored types are already ambiguous, so we
// prefer the explicitness here.
// - Some supported types (like PHONE_HOME_CITY_CODE) are not stored.
// - Some non-supported types are stored (usually types that don't have filling
// support yet).
std::vector<ServerFieldType> GetStoredContactInfoTypes() {
return {COMPANY_NAME,
NAME_HONORIFIC_PREFIX,
NAME_FIRST,
NAME_MIDDLE,
NAME_LAST_FIRST,
NAME_LAST_CONJUNCTION,
NAME_LAST_SECOND,
NAME_LAST,
NAME_FULL,
NAME_FULL_WITH_HONORIFIC_PREFIX,
ADDRESS_HOME_STREET_ADDRESS,
ADDRESS_HOME_STREET_NAME,
ADDRESS_HOME_DEPENDENT_STREET_NAME,
ADDRESS_HOME_HOUSE_NUMBER,
ADDRESS_HOME_SUBPREMISE,
ADDRESS_HOME_PREMISE_NAME,
ADDRESS_HOME_DEPENDENT_LOCALITY,
ADDRESS_HOME_CITY,
ADDRESS_HOME_STATE,
ADDRESS_HOME_ZIP,
ADDRESS_HOME_SORTING_CODE,
ADDRESS_HOME_COUNTRY,
ADDRESS_HOME_APT_NUM,
ADDRESS_HOME_FLOOR,
EMAIL_ADDRESS,
PHONE_HOME_WHOLE_NUMBER,
BIRTHDATE_DAY,
BIRTHDATE_MONTH,
BIRTHDATE_4_DIGIT_YEAR};
}
// This helper function binds the `profile`s properties to the placeholders in
// `s`, in the order the columns are defined in the header file.
// Instead of `profile.modification_date()`, `modification_date` is used. This
// makes the function useful for updates as well.
void BindAutofillProfileToContactInfoStatement(
const AutofillProfile& profile,
const base::Time& modification_date,
sql::Statement& s) {
int index = 0;
s.BindString(index++, profile.guid());
s.BindInt64(index++, profile.use_count());
s.BindInt64(index++, profile.use_date().ToTimeT());
s.BindInt64(index++, modification_date.ToTimeT());
s.BindString(index++, profile.language_code());
s.BindString(index++, profile.profile_label());
}
// Inserts `profile` into `kContactInfoTable` and `kContactInfoTypeTokensTable`.
bool AddAutofillProfileToContactInfoTable(sql::Database* db,
const AutofillProfile& profile,
const base::Time& modification_date) {
sql::Statement s;
InsertBuilder(
db, s, kContactInfoTable,
{kGuid, kUseCount, kUseDate, kDateModified, kLanguageCode, kLabel});
BindAutofillProfileToContactInfoStatement(profile, modification_date, s);
if (!s.Run())
return false;
for (ServerFieldType type : GetStoredContactInfoTypes()) {
InsertBuilder(db, s, kContactInfoTypeTokensTable,
{kGuid, kType, kValue, kVerificationStatus});
s.BindString(0, profile.guid());
s.BindInt(1, type);
s.BindString16(2, Truncate(profile.GetRawInfo(type)));
s.BindInt(3, profile.GetVerificationStatusInt(type));
if (!s.Run())
return false;
}
return true;
}
// Reads the profile with `guid` from `kContactInfoTable`. The profile's source
// is set to `kAccount`.
std::unique_ptr<AutofillProfile> GetAutofillProfileFromContactInfoTable(
sql::Database* db,
const std::string& guid) {
sql::Statement s;
if (!SelectByGuid(db, s, kContactInfoTable,
{kUseCount, kUseDate, kDateModified, kLanguageCode, kLabel},
guid)) {
return nullptr;
}
auto profile = std::make_unique<AutofillProfile>(
guid, /*origin=*/"", AutofillProfile::Source::kAccount);
int index = 0;
profile->set_use_count(s.ColumnInt64(index++));
profile->set_use_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
profile->set_modification_date(base::Time::FromTimeT(s.ColumnInt64(index++)));
profile->set_language_code(s.ColumnString(index++));
profile->set_profile_label(s.ColumnString(index++));
if (!SelectByGuid(db, s, kContactInfoTypeTokensTable,
{kType, kValue, kVerificationStatus}, guid)) {
return nullptr;
}
// As `SelectByGuid()` already calls `s.Step()`, do-while is used here.
do {
ServerFieldType type = ToSafeServerFieldType(s.ColumnInt(0), UNKNOWN_TYPE);
DCHECK(type != UNKNOWN_TYPE);
profile->SetRawInfoWithVerificationStatusInt(type, s.ColumnString16(1),
s.ColumnInt(2));
} while (s.Step());
profile->FinalizeAfterImport();
return profile;
}
} // namespace
// static
const size_t AutofillTable::kMaxDataLength = 1024;
AutofillTable::AutofillTable()
: autofill_table_encryptor_(
AutofillTableEncryptorFactory::GetInstance()->Create()) {
DCHECK(autofill_table_encryptor_);
}
AutofillTable::~AutofillTable() = default;
AutofillTable* AutofillTable::FromWebDatabase(WebDatabase* db) {
return static_cast<AutofillTable*>(db->GetTable(GetKey()));
}
WebDatabaseTable::TypeKey AutofillTable::GetTypeKey() const {
return GetKey();
}
bool AutofillTable::CreateTablesIfNecessary() {
return InitMainTable() && InitCreditCardsTable() && InitIBANsTable() &&
InitProfilesTable() && InitProfileAddressesTable() &&
InitProfileNamesTable() && InitProfileEmailsTable() &&
InitProfilePhonesTable() && InitProfileBirthdatesTable() &&
InitMaskedCreditCardsTable() && InitUnmaskedCreditCardsTable() &&
InitServerCardMetadataTable() && InitServerAddressesTable() &&
InitServerAddressMetadataTable() && InitAutofillSyncMetadataTable() &&
InitModelTypeStateTable() && InitPaymentsCustomerDataTable() &&
InitPaymentsUPIVPATable() &&
InitServerCreditCardCloudTokenDataTable() && InitOfferDataTable() &&
InitOfferEligibleInstrumentTable() && InitOfferMerchantDomainTable() &&
InitContactInfoTable() && InitContactInfoTypeTokensTable() &&
InitVirtualCardUsageDataTable();
}
bool AutofillTable::IsSyncable() {
return true;
}
bool AutofillTable::MigrateToVersion(int version,
bool* update_compatible_version) {
// Migrate if necessary.
switch (version) {
case 83:
*update_compatible_version = true;
return MigrateToVersion83RemoveServerCardTypeColumn();
case 84:
*update_compatible_version = false;
return MigrateToVersion84AddNicknameColumn();
case 85:
*update_compatible_version = false;
return MigrateToVersion85AddCardIssuerColumnToMaskedCreditCard();
case 86:
*update_compatible_version = false;
return MigrateToVersion86RemoveUnmaskedCreditCardsUseColumns();
case 87:
*update_compatible_version = false;
return MigrateToVersion87AddCreditCardNicknameColumn();
case 88:
*update_compatible_version = false;
return MigrateToVersion88AddNewNameColumns();
case 89:
*update_compatible_version = false;
return MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard();
case 90:
*update_compatible_version = false;
return MigrateToVersion90AddNewStructuredAddressColumns();
case 91:
*update_compatible_version = false;
return MigrateToVersion91AddMoreStructuredAddressColumns();
case 92:
*update_compatible_version = false;
return MigrateToVersion92AddNewPrefixedNameColumn();
case 93:
*update_compatible_version = false;
return MigrateToVersion93AddAutofillProfileLabelColumn();
case 94:
*update_compatible_version = false;
return MigrateToVersion94AddPromoCodeColumnsToOfferData();
case 95:
*update_compatible_version = false;
return MigrateToVersion95AddVirtualCardMetadata();
case 96:
*update_compatible_version = false;
return MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn();
case 98:
*update_compatible_version = true;
return MigrateToVersion98RemoveStatusColumnMaskedCreditCards();
case 99:
*update_compatible_version = true;
return MigrateToVersion99RemoveAutofillProfilesTrashTable();
case 100:
*update_compatible_version = true;
return MigrateToVersion100RemoveProfileValidityBitfieldColumn();
case 101:
// update_compatible_version is set to false because this table is not
// used since M99.
*update_compatible_version = false;
return MigrateToVersion101RemoveCreditCardArtImageTable();
case 102:
*update_compatible_version = false;
return MigrateToVersion102AddAutofillBirthdatesTable();
case 104:
*update_compatible_version = false;
return MigrateToVersion104AddProductDescriptionColumn();
case 105:
*update_compatible_version = false;
return MigrateToVersion105AddAutofillIBANTable();
case 106:
*update_compatible_version = true;
return MigrateToVersion106RecreateAutofillIBANTable();
case 107:
*update_compatible_version = false;
return MigrateToVersion107AddContactInfoTables();
case 108:
*update_compatible_version = false;
return MigrateToVersion108AddCardIssuerIdColumn();
case 109:
*update_compatible_version = false;
return MigrateToVersion109AddVirtualCardUsageDataTable();
}
return true;
}
bool AutofillTable::AddFormFieldValues(
const std::vector<FormFieldData>& elements,
std::vector<AutofillChange>* changes) {
return AddFormFieldValuesTime(elements, changes, AutofillClock::Now());
}
bool AutofillTable::AddFormFieldValue(const FormFieldData& element,
std::vector<AutofillChange>* changes) {
return AddFormFieldValueTime(element, changes, AutofillClock::Now());
}
bool AutofillTable::GetFormValuesForElementName(
const std::u16string& name,
const std::u16string& prefix,
std::vector<AutofillEntry>* entries,
int limit) {
DCHECK(entries);
bool succeeded = false;
if (prefix.empty()) {
sql::Statement s;
SelectBuilder(db_, s, kAutofillTable,
{kName, kValue, kDateCreated, kDateLastUsed},
"WHERE name = ? ORDER BY count DESC LIMIT ?");
s.BindString16(0, name);
s.BindInt(1, limit);
entries->clear();
while (s.Step()) {
entries->push_back(AutofillEntry(
AutofillKey(/*name=*/s.ColumnString16(0),
/*value=*/s.ColumnString16(1)),
/*date_created=*/base::Time::FromTimeT(s.ColumnInt64(2)),
/*date_last_used=*/base::Time::FromTimeT(s.ColumnInt64(3))));
}
succeeded = s.Succeeded();
} else {
std::u16string prefix_lower = base::i18n::ToLower(prefix);
std::u16string next_prefix = prefix_lower;
next_prefix.back()++;
sql::Statement s1;
SelectBuilder(db_, s1, kAutofillTable,
{kName, kValue, kDateCreated, kDateLastUsed},
"WHERE name = ? AND "
"value_lower >= ? AND "
"value_lower < ? "
"ORDER BY count DESC "
"LIMIT ?");
s1.BindString16(0, name);
s1.BindString16(1, prefix_lower);
s1.BindString16(2, next_prefix);
s1.BindInt(3, limit);
entries->clear();
while (s1.Step()) {
entries->push_back(AutofillEntry(
AutofillKey(/*name=*/s1.ColumnString16(0),
/*value=*/s1.ColumnString16(1)),
/*date_created=*/base::Time::FromTimeT(s1.ColumnInt64(2)),
/*date_last_used=*/base::Time::FromTimeT(s1.ColumnInt64(3))));
}
succeeded = s1.Succeeded();
if (IsFeatureSubstringMatchEnabled()) {
sql::Statement s2;
SelectBuilder(db_, s2, kAutofillTable,
{kName, kValue, kDateCreated, kDateLastUsed},
"WHERE name = ? AND ("
" value LIKE '% ' || :prefix || '%' ESCAPE '!' OR "
" value LIKE '%.' || :prefix || '%' ESCAPE '!' OR "
" value LIKE '%,' || :prefix || '%' ESCAPE '!' OR "
" value LIKE '%-' || :prefix || '%' ESCAPE '!' OR "
" value LIKE '%@' || :prefix || '%' ESCAPE '!' OR "
" value LIKE '%!_' || :prefix || '%' ESCAPE '!' ) "
"ORDER BY count DESC "
"LIMIT ?");
s2.BindString16(0, name);
// escaper as L'!' -> 0x21.
s2.BindString16(1, Substitute(prefix_lower, u"_%", 0x21));
s2.BindInt(2, limit);
while (s2.Step()) {
entries->push_back(AutofillEntry(
AutofillKey(/*name=*/s2.ColumnString16(0),
/*value=*/s2.ColumnString16(1)),
/*date_created=*/base::Time::FromTimeT(s2.ColumnInt64(2)),
/*date_last_used=*/base::Time::FromTimeT(s2.ColumnInt64(3))));
}
succeeded &= s2.Succeeded();
}
}
return succeeded;
}
bool AutofillTable::RemoveFormElementsAddedBetween(
const base::Time& delete_begin,
const base::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.
sql::Statement s;
SelectBuilder(db_, s, kAutofillTable,
{kName, kValue, kCount, kDateCreated, kDateLastUsed},
"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;
while (s.Step()) {
std::u16string name = s.ColumnString16(0);
std::u16string 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 expressed 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 + base::ClampRound(
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.emplace_back(change_type, AutofillKey(name, value));
}
if (!s.Succeeded())
return false;
// As a single transaction, remove or update the elements appropriately.
sql::Statement s_delete;
DeleteBuilder(db_, s_delete, kAutofillTable,
"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 (const auto& update : updates) {
sql::Statement s_update;
UpdateBuilder(db_, s_update, kAutofillTable,
{kDateCreated, kDateLastUsed, kCount},
"name = ? AND value = ?");
s_update.BindInt64(0, update.date_created);
s_update.BindInt64(1, update.date_last_used);
s_update.BindInt(2, update.count);
s_update.BindString16(3, update.name);
s_update.BindString16(4, update.value);
if (!s_update.Run())
return false;
}
if (!transaction.Commit())
return false;
*changes = tentative_changes;
return true;
}
bool AutofillTable::RemoveExpiredFormElements(
std::vector<AutofillChange>* changes) {
const auto change_type = AutofillChange::EXPIRE;
base::Time expiration_time =
AutofillClock::Now() - kAutocompleteRetentionPolicyPeriod;
// Query for the name and value of all form elements that were last used
// before the |expiration_time|.
sql::Statement select_for_delete;
SelectBuilder(db_, select_for_delete, kAutofillTable, {kName, kValue},
"WHERE date_last_used < ?");
select_for_delete.BindInt64(0, expiration_time.ToTimeT());
std::vector<AutofillChange> tentative_changes;
while (select_for_delete.Step()) {
std::u16string name = select_for_delete.ColumnString16(0);
std::u16string value = select_for_delete.ColumnString16(1);
tentative_changes.emplace_back(change_type, AutofillKey(name, value));
}
if (!select_for_delete.Succeeded())
return false;
sql::Statement delete_data_statement;
DeleteBuilder(db_, delete_data_statement, kAutofillTable,
"date_last_used < ?");
delete_data_statement.BindInt64(0, expiration_time.ToTimeT());
if (!delete_data_statement.Run())
return false;
*changes = tentative_changes;
return true;
}
bool AutofillTable::RemoveFormElement(const std::u16string& name,
const std::u16string& value) {
sql::Statement s;
DeleteBuilder(db_, s, kAutofillTable, "name = ? AND value= ?");
s.BindString16(0, name);
s.BindString16(1, value);
return s.Run();
}
int AutofillTable::GetCountOfValuesContainedBetween(const base::Time& begin,
const base::Time& end) {
const time_t begin_time_t = begin.ToTimeT();
const time_t end_time_t = GetEndTime(end);
sql::Statement s(db_->GetUniqueStatement(
"SELECT COUNT(DISTINCT(value1)) FROM ( "
" SELECT value AS value1 FROM autofill "
" WHERE NOT EXISTS ( "
" SELECT value AS value2, date_created, date_last_used FROM autofill "
" WHERE value1 = value2 AND "
" (date_created < ? OR date_last_used >= ?)))"));
s.BindInt64(0, begin_time_t);
s.BindInt64(1, end_time_t);
if (!s.Step()) {
NOTREACHED();
return false;
}
return s.ColumnInt(0);
}
bool AutofillTable::GetAllAutofillEntries(std::vector<AutofillEntry>* entries) {
sql::Statement s;
SelectBuilder(db_, s, kAutofillTable,
{kName, kValue, kDateCreated, kDateLastUsed});
while (s.Step()) {
std::u16string name = s.ColumnString16(0);
std::u16string value = s.ColumnString16(1);
base::Time date_created = base::Time::FromTimeT(s.ColumnInt64(2));
base::Time date_last_used = base::Time::FromTimeT(s.ColumnInt64(3));
entries->push_back(
AutofillEntry(AutofillKey(name, value), date_created, date_last_used));
}
return s.Succeeded();
}
bool AutofillTable::GetAutofillTimestamps(const std::u16string& name,
const std::u16string& value,
base::Time* date_created,
base::Time* date_last_used) {
sql::Statement s;
SelectBuilder(db_, s, kAutofillTable, {kDateCreated, kDateLastUsed},
"WHERE name = ? AND value = ?");
s.BindString16(0, name);
s.BindString16(1, value);
if (!s.Step())
return false;
*date_created = base::Time::FromTimeT(s.ColumnInt64(0));
*date_last_used = base::Time::FromTimeT(s.ColumnInt64(1));
DCHECK(!s.Step());
return true;
}
bool AutofillTable::UpdateAutofillEntries(
const std::vector<AutofillEntry>& entries) {
if (entries.empty())
return true;
// Remove all existing entries.
for (const auto& entry : entries) {
sql::Statement s;
DeleteBuilder(db_, s, kAutofillTable, "name = ? AND value = ?");
s.BindString16(0, entry.key().name());
s.BindString16(1, entry.key().value());
if (!s.Run())
return false;
}
// Insert all the supplied autofill entries.
for (const auto& entry : entries) {
if (!InsertAutofillEntry(entry))
return false;
}
return true;
}
bool AutofillTable::AddAutofillProfile(const AutofillProfile& profile) {
if (profile.source() == AutofillProfile::Source::kAccount) {
sql::Transaction transaction(db_);
return transaction.Begin() &&
AddAutofillProfileToContactInfoTable(
db_, profile, /*modification_date=*/AutofillClock::Now()) &&
transaction.Commit();
}
DCHECK(profile.source() == AutofillProfile::Source::kLocalOrSyncable);
sql::Statement s;
InsertBuilder(
db_, s, kAutofillProfilesTable,
{kGuid, kCompanyName, kStreetAddress, kDependentLocality, kCity, kState,
kZipcode, kSortingCode, kCountryCode, kUseCount, kUseDate, kDateModified,
kOrigin, kLanguageCode, kLabel, kDisallowSettingsVisibleUpdates});
BindAutofillProfileToStatement(profile, AutofillClock::Now(), &s);
if (!s.Run())
return false;
return AddAutofillProfilePieces(profile, db_);
}
bool AutofillTable::UpdateAutofillProfile(const AutofillProfile& profile) {
DCHECK(base::IsValidGUID(profile.guid()));
std::unique_ptr<AutofillProfile> old_profile =
GetAutofillProfile(profile.guid(), profile.source());
if (!old_profile)
return false;
base::Time new_modification_date = *old_profile != profile
? AutofillClock::Now()
: old_profile->modification_date();
if (profile.source() == AutofillProfile::Source::kAccount) {
// Implementing an update as remove + add has multiple advantages:
// - Prevents outdated (ServerFieldType, value) pairs from remaining in the
// `kContactInfoTypeTokensTables`, in case field types are removed.
// - Simpler code.
// The possible downside is performance. This is not an issue, as updates
// happen rarely and asynchronously.
sql::Transaction transaction(db_);
return transaction.Begin() &&
RemoveAutofillProfile(profile.guid(), profile.source()) &&
AddAutofillProfileToContactInfoTable(db_, profile,
new_modification_date) &&
transaction.Commit();
}
DCHECK(profile.source() == AutofillProfile::Source::kLocalOrSyncable);
sql::Statement s;
UpdateBuilder(
db_, s, kAutofillProfilesTable,
{kGuid, kCompanyName, kStreetAddress, kDependentLocality, kCity, kState,
kZipcode, kSortingCode, kCountryCode, kUseCount, kUseDate, kDateModified,
kOrigin, kLanguageCode, kLabel, kDisallowSettingsVisibleUpdates},
"guid = ?1");
BindAutofillProfileToStatement(profile, new_modification_date, &s);
bool result = s.Run();
DCHECK_GT(db_->GetLastChangeCount(), 0);
if (!result)
return result;
// Remove the old names, emails, and phone numbers.
if (!RemoveAutofillProfilePieces(profile.guid(), db_))
return false;
return AddAutofillProfilePieces(profile, db_);
}
bool AutofillTable::RemoveAutofillProfile(
const std::string& guid,
AutofillProfile::Source profile_source) {
DCHECK(base::IsValidGUID(guid));
if (profile_source == AutofillProfile::Source::kAccount) {
sql::Transaction transaction(db_);
return transaction.Begin() &&
DeleteWhereColumnEq(db_, kContactInfoTable, kGuid, guid) &&
DeleteWhereColumnEq(db_, kContactInfoTypeTokensTable, kGuid, guid) &&
transaction.Commit();
}
DCHECK(profile_source == AutofillProfile::Source::kLocalOrSyncable);
return DeleteWhereColumnEq(db_, kAutofillProfilesTable, kGuid, guid) &&
RemoveAutofillProfilePieces(guid, db_);
}
bool AutofillTable::RemoveAllAutofillProfiles(
AutofillProfile::Source profile_source) {
DCHECK(profile_source == AutofillProfile::Source::kAccount);
sql::Transaction transaction(db_);
return transaction.Begin() && Delete(db_, kContactInfoTable) &&
Delete(db_, kContactInfoTypeTokensTable) && transaction.Commit();
}
std::unique_ptr<AutofillProfile> AutofillTable::GetAutofillProfile(
const std::string& guid,
AutofillProfile::Source profile_source) {
DCHECK(base::IsValidGUID(guid));
if (profile_source == AutofillProfile::Source::kAccount)
return GetAutofillProfileFromContactInfoTable(db_, guid);
DCHECK(profile_source == AutofillProfile::Source::kLocalOrSyncable);
sql::Statement s;
if (!SelectByGuid(db_, s, kAutofillProfilesTable,
{kOrigin, kCompanyName, kStreetAddress, kDependentLocality,
kCity, kState, kZipcode, kSortingCode, kCountryCode,
kUseCount, kUseDate, kDateModified, kLanguageCode, kLabel,
kDisallowSettingsVisibleUpdates},
guid)) {
return nullptr;
}
auto profile = std::make_unique<AutofillProfile>(
guid, /*origin=*/s.ColumnString(0),
AutofillProfile::Source::kLocalOrSyncable);
DCHECK(base::IsValidGUID(profile->guid()));
// Get associated name info using guid.
AddAutofillProfileNamesToProfile(db_, profile.get());
// Get associated email info using guid.
AddAutofillProfileEmailsToProfile(db_, profile.get());
// Get associated phone info using guid.
AddAutofillProfilePhonesToProfile(db_, profile.get());
// Get associated birthdate info using guid.
AddAutofillProfileBirthdateToProfile(db_, profile.get());
// The details should be added after the other info to make sure they don't
// change when we change the names/emails/phones.
AddAutofillProfileDetailsFromStatement(s, profile.get());
// The structured address information should be added after the street_address
// from the query above was written because this information is used to
// detect changes by a legacy client.
AddAutofillProfileAddressesToProfile(db_, profile.get());
// For more-structured profiles, the profile must be finalized to fully
// populate the name fields.
profile->FinalizeAfterImport();
return profile;
}
bool AutofillTable::GetAutofillProfiles(
std::vector<std::unique_ptr<AutofillProfile>>* profiles,
AutofillProfile::Source profile_source) {
DCHECK(profiles);
profiles->clear();
sql::Statement s;
SelectBuilder(db_, s,
profile_source == AutofillProfile::Source::kAccount
? kContactInfoTable
: kAutofillProfilesTable,
{kGuid}, "ORDER BY date_modified DESC, guid");
while (s.Step()) {
std::string guid = s.ColumnString(0);
std::unique_ptr<AutofillProfile> profile =
GetAutofillProfile(guid, profile_source);
if (!profile)
continue;
profiles->push_back(std::move(profile));
}
return s.Succeeded();
}
bool AutofillTable::GetServerProfiles(
std::vector<std::unique_ptr<AutofillProfile>>* profiles) const {
profiles->clear();
sql::Statement s;
SelectBuilder(
db_, s, kServerAddressesTable,
{kId, kUseCount, kUseDate, kRecipientName, kCompanyName, kStreetAddress,
kAddress1, // ADDRESS_HOME_STATE
kAddress2, // ADDRESS_HOME_CITY
kAddress3, // ADDRESS_HOME_DEPENDENT_LOCALITY
kAddress4, // Not supported in AutofillProfile yet.
kPostalCode, // ADDRESS_HOME_ZIP
kSortingCode, // ADDRESS_HOME_SORTING_CODE
kCountryCode, // ADDRESS_HOME_COUNTRY
kPhoneNumber, // PHONE_HOME_WHOLE_NUMBER
kLanguageCode, kHasConverted},
"LEFT OUTER JOIN server_address_metadata USING (id)");
while (s.Step()) {
int index = 0;
std::unique_ptr<AutofillProfile> profile =
std::make_unique<AutofillProfile>(AutofillProfile::SERVER_PROFILE,
s.ColumnString(index++));
profile->set_use_count(s.ColumnInt64(index++));
profile->set_use_date(
base::Time::FromInternalValue(s.ColumnInt64(index++)));
// Modification date is not tracked for server profiles. Explicitly set it
// here to override the default value of AutofillClock::Now().
profile->set_modification_date(base::Time());
std::u16string recipient_name = s.ColumnString16(index++);
for (ServerFieldType type :
{COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_STATE,
ADDRESS_HOME_CITY, ADDRESS_HOME_DEPENDENT_LOCALITY}) {
profile->SetRawInfo(type, s.ColumnString16(index++));
}
index++; // Skip address_4 which we haven't added to AutofillProfile yet.
for (ServerFieldType type :
{ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY}) {
profile->SetRawInfo(type, s.ColumnString16(index++));
}
std::u16string phone_number = s.ColumnString16(index++);
profile->set_language_code(s.ColumnString(index++));
profile->set_has_converted(s.ColumnBool(index++));
// SetInfo instead of SetRawInfo so the constituent pieces will be parsed
// for these data types.
profile->SetInfo(NAME_FULL, recipient_name, profile->language_code());
profile->SetInfo(PHONE_HOME_WHOLE_NUMBER, phone_number,
profile->language_code());
// For more-structured profiles, the profile must be finalized to fully
// populate the name fields.
profile->FinalizeAfterImport();
profiles->push_back(std::move(profile));
}
return s.Succeeded();
}
void AutofillTable::SetServerProfilesAndMetadata(
const std::vector<AutofillProfile>& profiles,
bool update_metadata) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return;
// Delete all old ones first.
Delete(db_, kServerAddressesTable);
sql::Statement insert;
InsertBuilder(db_, insert, kServerAddressesTable,
{kId, kRecipientName, kCompanyName, kStreetAddress,
kAddress1, // ADDRESS_HOME_STATE
kAddress2, // ADDRESS_HOME_CITY
kAddress3, // ADDRESS_HOME_DEPENDENT_LOCALITY
kAddress4, // Not supported in AutofillProfile yet.
kPostalCode, // ADDRESS_HOME_ZIP
kSortingCode, // ADDRESS_HOME_SORTING_CODE
kCountryCode, // ADDRESS_HOME_COUNTRY
kPhoneNumber, // PHONE_HOME_WHOLE_NUMBER
kLanguageCode});
for (const auto& profile : profiles) {
DCHECK(profile.record_type() == AutofillProfile::SERVER_PROFILE);
int index = 0;
insert.BindString(index++, profile.server_id());
for (ServerFieldType type :
{NAME_FULL, COMPANY_NAME, ADDRESS_HOME_STREET_ADDRESS,
ADDRESS_HOME_STATE, ADDRESS_HOME_CITY,
ADDRESS_HOME_DEPENDENT_LOCALITY}) {
insert.BindString16(index++, profile.GetRawInfo(type));
}
index++; // Skip address_4 which we haven't added to AutofillProfile yet.
for (ServerFieldType type :
{ADDRESS_HOME_ZIP, ADDRESS_HOME_SORTING_CODE, ADDRESS_HOME_COUNTRY,
PHONE_HOME_WHOLE_NUMBER}) {
insert.BindString16(index++, profile.GetRawInfo(type));
}
insert.BindString(index++, profile.language_code());
insert.Run();
insert.Reset(true);
if (update_metadata) {
// Save the use count and use date of the profile.
UpdateServerAddressMetadata(profile);
}
}
if (update_metadata) {
// Delete metadata that's no longer relevant.
Delete(db_, kServerAddressMetadataTable,
"id NOT IN (SELECT id FROM server_addresses)");
}
transaction.Commit();
}
void AutofillTable::SetServerProfiles(
const std::vector<AutofillProfile>& profiles) {
SetServerProfilesAndMetadata(profiles, /*update_metadata=*/true);
}
bool AutofillTable::AddIBAN(const IBAN& iban) {
sql::Statement s;
InsertBuilder(db_, s, kIBANsTable,
{kGuid, kUseCount, kUseDate, kValue, kNickname});
BindIBANToStatement(iban, &s, *autofill_table_encryptor_);
if (!s.Run())
return false;
DCHECK_GT(db_->GetLastChangeCount(), 0);
return true;
}
bool AutofillTable::UpdateIBAN(const IBAN& iban) {
DCHECK(base::IsValidGUID(iban.guid()));
std::unique_ptr<IBAN> old_iban = GetIBAN(iban.guid());
if (!old_iban) {
return false;
}
if (*old_iban == iban) {
return true;
}
sql::Statement s;
UpdateBuilder(db_, s, kIBANsTable,
{kGuid, kUseCount, kUseDate, kValue, kNickname}, "guid=?1");
BindIBANToStatement(iban, &s, *autofill_table_encryptor_);
bool result = s.Run();
DCHECK_GT(db_->GetLastChangeCount(), 0);
return result;
}
bool AutofillTable::RemoveIBAN(const std::string& guid) {
DCHECK(base::IsValidGUID(guid));
return DeleteWhereColumnEq(db_, kIBANsTable, kGuid, guid);
}
std::unique_ptr<IBAN> AutofillTable::GetIBAN(const std::string& guid) {
DCHECK(base::IsValidGUID(guid));
sql::Statement s;
SelectBuilder(db_, s, kIBANsTable,
{kGuid, kUseCount, kUseDate, kValue, kNickname},
"WHERE guid = ?");
s.BindString(0, guid);
if (!s.Step())
return nullptr;
return IBANFromStatement(s, *autofill_table_encryptor_);
}
bool AutofillTable::GetIBANs(std::vector<std::unique_ptr<IBAN>>* ibans) {
DCHECK(ibans);
ibans->clear();
sql::Statement s;
SelectBuilder(db_, s, kIBANsTable, {kGuid}, "ORDER BY use_date DESC, guid");
while (s.Step()) {
std::string guid = s.ColumnString(0);
std::unique_ptr<IBAN> iban = GetIBAN(guid);
if (!iban)
return false;
ibans->push_back(std::move(iban));
}
return s.Succeeded();
}
bool AutofillTable::AddCreditCard(const CreditCard& credit_card) {
sql::Statement s;
InsertBuilder(db_, s, kCreditCardsTable,
{kGuid, kNameOnCard, kExpirationMonth, kExpirationYear,
kCardNumberEncrypted, kUseCount, kUseDate, kDateModified,
kOrigin, kBillingAddressId, kNickname});
BindCreditCardToStatement(credit_card, AutofillClock::Now(), &s,
*autofill_table_encryptor_);
if (!s.Run())
return false;
DCHECK_GT(db_->GetLastChangeCount(), 0);
return true;
}
bool AutofillTable::UpdateCreditCard(const CreditCard& credit_card) {
DCHECK(base::IsValidGUID(credit_card.guid()));
std::unique_ptr<CreditCard> old_credit_card =
GetCreditCard(credit_card.guid());
if (!old_credit_card)
return false;
bool update_modification_date = *old_credit_card != credit_card;
sql::Statement s;
UpdateBuilder(db_, s, kCreditCardsTable,
{kGuid, kNameOnCard, kExpirationMonth, kExpirationYear,
kCardNumberEncrypted, kUseCount, kUseDate, kDateModified,
kOrigin, kBillingAddressId, kNickname},
"guid=?1");
BindCreditCardToStatement(credit_card,
update_modification_date
? AutofillClock::Now()
: old_credit_card->modification_date(),
&s, *autofill_table_encryptor_);
bool result = s.Run();
DCHECK_GT(db_->GetLastChangeCount(), 0);
return result;
}
bool AutofillTable::RemoveCreditCard(const std::string& guid) {
DCHECK(base::IsValidGUID(guid));
return DeleteWhereColumnEq(db_, kCreditCardsTable, kGuid, guid);
}
bool AutofillTable::AddFullServerCreditCard(const CreditCard& credit_card) {
DCHECK_EQ(CreditCard::FULL_SERVER_CARD, credit_card.record_type());
DCHECK(!credit_card.number().empty());
DCHECK(!credit_card.server_id().empty());
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
// Make sure there aren't duplicates for this card.
DeleteFromUnmaskedCreditCards(credit_card.server_id());
DeleteFromMaskedCreditCards(credit_card.server_id());
CreditCard masked(credit_card);
masked.set_record_type(CreditCard::MASKED_SERVER_CARD);
masked.SetNumber(credit_card.LastFourDigits());
masked.RecordAndLogUse();
DCHECK(!masked.network().empty());
AddMaskedCreditCards({masked});
AddUnmaskedCreditCard(credit_card.server_id(), credit_card.number());
transaction.Commit();
return db_->GetLastChangeCount() > 0;
}
std::unique_ptr<CreditCard> AutofillTable::GetCreditCard(
const std::string& guid) {
DCHECK(base::IsValidGUID(guid));
sql::Statement s;
SelectBuilder(db_, s, kCreditCardsTable,
{kGuid, kNameOnCard, kExpirationMonth, kExpirationYear,
kCardNumberEncrypted, kUseCount, kUseDate, kDateModified,
kOrigin, kBillingAddressId, kNickname},
"WHERE guid = ?");
s.BindString(0, guid);
if (!s.Step())
return nullptr;
return CreditCardFromStatement(s, *autofill_table_encryptor_);
}
bool AutofillTable::GetCreditCards(
std::vector<std::unique_ptr<CreditCard>>* credit_cards) {
DCHECK(credit_cards);
credit_cards->clear();
sql::Statement s;
SelectBuilder(db_, s, kCreditCardsTable, {kGuid},
"ORDER BY date_modified DESC, guid");
while (s.Step()) {
std::string guid = s.ColumnString(0);
std::unique_ptr<CreditCard> credit_card = GetCreditCard(guid);
if (!credit_card)
return false;
credit_cards->push_back(std::move(credit_card));
}
return s.Succeeded();
}
bool AutofillTable::GetServerCreditCards(
std::vector<std::unique_ptr<CreditCard>>* credit_cards) const {
credit_cards->clear();
sql::Statement s;
SelectBuilder(
db_, s, base::StrCat({kMaskedCreditCardsTable, " AS masked"}),
{kCardNumberEncrypted, kLastFour, base::StrCat({"masked.", kId}),
base::StrCat({"metadata.", kUseCount}),
base::StrCat({"metadata.", kUseDate}), kNetwork, kNameOnCard, kExpMonth,
kExpYear, base::StrCat({"metadata.", kBillingAddressId}), kBankName,
kNickname, kCardIssuer, kCardIssuerId, kInstrumentId,
kVirtualCardEnrollmentState, kCardArtUrl, kProductDescription},
"LEFT OUTER JOIN unmasked_credit_cards USING (id) "
"LEFT OUTER JOIN server_card_metadata AS metadata USING (id)");
while (s.Step()) {
int index = 0;
// If the card_number_encrypted field is nonempty, we can assume this card
// is a full card, otherwise it's masked.
std::u16string full_card_number =
UnencryptedCardFromColumn(s, index++, *autofill_table_encryptor_);
std::u16string last_four = s.ColumnString16(index++);
CreditCard::RecordType record_type = full_card_number.empty()
? CreditCard::MASKED_SERVER_CARD
: CreditCard::FULL_SERVER_CARD;
std::string server_id = s.ColumnString(index++);
std::unique_ptr<CreditCard> card =
std::make_unique<CreditCard>(record_type, server_id);
card->SetRawInfo(CREDIT_CARD_NUMBER,
record_type == CreditCard::MASKED_SERVER_CARD
? last_four
: full_card_number);
card->set_use_count(s.ColumnInt64(index++));
card->set_use_date(base::Time::FromInternalValue(s.ColumnInt64(index++)));
// Modification date is not tracked for server cards. Explicitly set it here
// to override the default value of AutofillClock::Now().
card->set_modification_date(base::Time());
std::string card_network = s.ColumnString(index++);
if (record_type == CreditCard::MASKED_SERVER_CARD) {
// The issuer network must be set after setting the number to override the
// autodetected issuer network.
card->SetNetworkForMaskedCard(card_network.c_str());
} else {
DCHECK_EQ(CreditCard::GetCardNetwork(full_card_number), card_network);
}
card->SetRawInfo(CREDIT_CARD_NAME_FULL, s.ColumnString16(index++));
card->SetRawInfo(CREDIT_CARD_EXP_MONTH, s.ColumnString16(index++));
card->SetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR, s.ColumnString16(index++));
card->set_billing_address_id(s.ColumnString(index++));
card->set_bank_name(s.ColumnString(index++));
card->SetNickname(s.ColumnString16(index++));
card->set_card_issuer(
static_cast<CreditCard::Issuer>(s.ColumnInt(index++)));
card->set_issuer_id(s.ColumnString(index++));
card->set_instrument_id(s.ColumnInt64(index++));
card->set_virtual_card_enrollment_state(
static_cast<CreditCard::VirtualCardEnrollmentState>(
s.ColumnInt(index++)));
card->set_card_art_url(GURL(s.ColumnString(index++)));
card->set_product_description(s.ColumnString16(index++));
credit_cards->push_back(std::move(card));
}
return s.Succeeded();
}
void AutofillTable::SetServerCreditCards(
const std::vector<CreditCard>& credit_cards) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return;
// Delete all old values.
Delete(db_, kMaskedCreditCardsTable);
AddMaskedCreditCards(credit_cards);
// Delete all items in the unmasked table that aren't in the new set.
Delete(db_, kUnmaskedCreditCardsTable,
"id NOT IN (SELECT id FROM masked_credit_cards)");
// Do the same for metadata.
Delete(db_, kServerCardMetadataTable,
"id NOT IN (SELECT id FROM masked_credit_cards)");
transaction.Commit();
}
bool AutofillTable::UnmaskServerCreditCard(const CreditCard& masked,
const std::u16string& full_number) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
// Make sure there aren't duplicates for this card.
DeleteFromUnmaskedCreditCards(masked.server_id());
AddUnmaskedCreditCard(masked.server_id(), full_number);
CreditCard unmasked = masked;
unmasked.set_record_type(CreditCard::FULL_SERVER_CARD);
unmasked.SetNumber(full_number);
unmasked.RecordAndLogUse();
UpdateServerCardMetadata(unmasked);
transaction.Commit();
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::MaskServerCreditCard(const std::string& id) {
return DeleteFromUnmaskedCreditCards(id);
}
bool AutofillTable::AddServerCardMetadata(
const AutofillMetadata& card_metadata) {
sql::Statement s;
InsertBuilder(db_, s, kServerCardMetadataTable,
{kUseCount, kUseDate, kBillingAddressId, kId});
s.BindInt64(0, card_metadata.use_count);
s.BindTime(1, card_metadata.use_date);
s.BindString(2, card_metadata.billing_address_id);
s.BindString(3, card_metadata.id);
s.Run();
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::UpdateServerCardMetadata(const CreditCard& credit_card) {
DCHECK_NE(CreditCard::LOCAL_CARD, credit_card.record_type());
DeleteWhereColumnEq(db_, kServerCardMetadataTable, kId,
credit_card.server_id());
sql::Statement s;
InsertBuilder(db_, s, kServerCardMetadataTable,
{kUseCount, kUseDate, kBillingAddressId, kId});
s.BindInt64(0, credit_card.use_count());
s.BindTime(1, credit_card.use_date());
s.BindString(2, credit_card.billing_address_id());
s.BindString(3, credit_card.server_id());
s.Run();
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::UpdateServerCardMetadata(
const AutofillMetadata& card_metadata) {
// Do not check if there was a record that got deleted. Inserting a new one is
// also fine.
RemoveServerCardMetadata(card_metadata.id);
sql::Statement s;
InsertBuilder(db_, s, kServerCardMetadataTable,
{kUseCount, kUseDate, kBillingAddressId, kId});
s.BindInt64(0, card_metadata.use_count);
s.BindTime(1, card_metadata.use_date);
s.BindString(2, card_metadata.billing_address_id);
s.BindString(3, card_metadata.id);
s.Run();
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::RemoveServerCardMetadata(const std::string& id) {
DeleteWhereColumnEq(db_, kServerCardMetadataTable, kId, id);
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::GetServerCardsMetadata(
std::map<std::string, AutofillMetadata>* cards_metadata) const {
cards_metadata->clear();
sql::Statement s;
SelectBuilder(db_, s, kServerCardMetadataTable,
{kId, kUseCount, kUseDate, kBillingAddressId});
while (s.Step()) {
int index = 0;
AutofillMetadata card_metadata;
card_metadata.id = s.ColumnString(index++);
card_metadata.use_count = s.ColumnInt64(index++);
card_metadata.use_date =
base::Time::FromInternalValue(s.ColumnInt64(index++));
card_metadata.billing_address_id = s.ColumnString(index++);
(*cards_metadata)[card_metadata.id] = card_metadata;
}
return s.Succeeded();
}
bool AutofillTable::AddServerAddressMetadata(
const AutofillMetadata& address_metadata) {
sql::Statement s;
InsertBuilder(db_, s, kServerAddressMetadataTable,
{kUseCount, kUseDate, kHasConverted, kId});
s.BindInt64(0, address_metadata.use_count);
s.BindInt64(1, address_metadata.use_date.ToInternalValue());
s.BindBool(2, address_metadata.has_converted);
s.BindString(3, address_metadata.id);
s.Run();
return db_->GetLastChangeCount() > 0;
}
// TODO(crbug.com/680182): Record the address conversion status when a server
// address gets converted.
bool AutofillTable::UpdateServerAddressMetadata(
const AutofillProfile& profile) {
DCHECK_EQ(AutofillProfile::SERVER_PROFILE, profile.record_type());
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
DeleteWhereColumnEq(db_, kServerAddressMetadataTable, kId,
profile.server_id());
sql::Statement s;
InsertBuilder(db_, s, kServerAddressMetadataTable,
{kUseCount, kUseDate, kHasConverted, kId});
s.BindInt64(0, profile.use_count());
s.BindInt64(1, profile.use_date().ToInternalValue());
s.BindBool(2, profile.has_converted());
s.BindString(3, profile.server_id());
s.Run();
transaction.Commit();
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::UpdateServerAddressMetadata(
const AutofillMetadata& address_metadata) {
// Do not check if there was a record that got deleted. Inserting a new one is
// also fine.
RemoveServerAddressMetadata(address_metadata.id);
sql::Statement s;
InsertBuilder(db_, s, kServerAddressMetadataTable,
{kUseCount, kUseDate, kHasConverted, kId});
s.BindInt64(0, address_metadata.use_count);
s.BindInt64(1, address_metadata.use_date.ToInternalValue());
s.BindBool(2, address_metadata.has_converted);
s.BindString(3, address_metadata.id);
s.Run();
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::RemoveServerAddressMetadata(const std::string& id) {
DeleteWhereColumnEq(db_, kServerAddressMetadataTable, kId, id);
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::GetServerAddressesMetadata(
std::map<std::string, AutofillMetadata>* addresses_metadata) const {
addresses_metadata->clear();
sql::Statement s;
SelectBuilder(db_, s, kServerAddressMetadataTable,
{kId, kUseCount, kUseDate, kHasConverted});
while (s.Step()) {
int index = 0;
AutofillMetadata address_metadata;
address_metadata.id = s.ColumnString(index++);
address_metadata.use_count = s.ColumnInt64(index++);
address_metadata.use_date =
base::Time::FromInternalValue(s.ColumnInt64(index++));
address_metadata.has_converted = s.ColumnBool(index++);
(*addresses_metadata)[address_metadata.id] = address_metadata;
}
return s.Succeeded();
}
void AutofillTable::SetServerCardsData(
const std::vector<CreditCard>& credit_cards) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return;
// Delete all old values.
Delete(db_, kMaskedCreditCardsTable);
// Add all the masked cards.
sql::Statement masked_insert;
InsertBuilder(
db_, masked_insert, kMaskedCreditCardsTable,
{kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear, kBankName,
kNickname, kCardIssuer, kCardIssuerId, kInstrumentId,
kVirtualCardEnrollmentState, kCardArtUrl, kProductDescription});
int index;
for (const CreditCard& card : credit_cards) {
DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
index = 0;
masked_insert.BindString(index++, card.server_id());
masked_insert.BindString(index++, card.network());
masked_insert.BindString16(index++, card.GetRawInfo(CREDIT_CARD_NAME_FULL));
masked_insert.BindString16(index++, card.LastFourDigits());
masked_insert.BindString16(index++, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
masked_insert.BindString16(index++,
card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
masked_insert.BindString(index++, card.bank_name());
masked_insert.BindString16(index++, card.nickname());
masked_insert.BindInt(index++, static_cast<int>(card.card_issuer()));
masked_insert.BindString(index++, card.issuer_id());
masked_insert.BindInt64(index++, card.instrument_id());
masked_insert.BindInt(
index++, static_cast<int>(card.virtual_card_enrollment_state()));
masked_insert.BindString(index++, card.card_art_url().spec());
masked_insert.BindString16(index++, card.product_description());
masked_insert.Run();
masked_insert.Reset(true);
}
// Delete all items in the unmasked table that aren't in the new set.
Delete(db_, kUnmaskedCreditCardsTable,
"id NOT IN (SELECT id FROM masked_credit_cards)");
transaction.Commit();
}
void AutofillTable::SetServerAddressesData(
const std::vector<AutofillProfile>& profiles) {
SetServerProfilesAndMetadata(profiles, /*update_metadata=*/false);
}
void AutofillTable::SetCreditCardCloudTokenData(
const std::vector<CreditCardCloudTokenData>& credit_card_cloud_token_data) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return;
// Deletes all old values.
Delete(db_, kServerCardCloudTokenDataTable);
// Inserts new values.
sql::Statement insert_cloud_token;
InsertBuilder(
db_, insert_cloud_token, kServerCardCloudTokenDataTable,
{kId, kSuffix, kExpMonth, kExpYear, kCardArtUrl, kInstrumentToken});
for (const CreditCardCloudTokenData& data : credit_card_cloud_token_data) {
insert_cloud_token.BindString(0, data.masked_card_id);
insert_cloud_token.BindString16(1, data.suffix);
insert_cloud_token.BindString16(2, data.ExpirationMonthAsString());
insert_cloud_token.BindString16(3, data.Expiration4DigitYearAsString());
insert_cloud_token.BindString(4, data.card_art_url);
insert_cloud_token.BindString(5, data.instrument_token);
insert_cloud_token.Run();
insert_cloud_token.Reset(true);
}
transaction.Commit();
}
bool AutofillTable::GetCreditCardCloudTokenData(
std::vector<std::unique_ptr<CreditCardCloudTokenData>>*
credit_card_cloud_token_data) {
credit_card_cloud_token_data->clear();
sql::Statement s;
SelectBuilder(
db_, s, kServerCardCloudTokenDataTable,
{kId, kSuffix, kExpMonth, kExpYear, kCardArtUrl, kInstrumentToken});
while (s.Step()) {
int index = 0;
std::unique_ptr<CreditCardCloudTokenData> data =
std::make_unique<CreditCardCloudTokenData>();
data->masked_card_id = s.ColumnString(index++);
data->suffix = s.ColumnString16(index++);
data->SetExpirationMonthFromString(s.ColumnString16(index++));
data->SetExpirationYearFromString(s.ColumnString16(index++));
data->card_art_url = s.ColumnString(index++);
data->instrument_token = s.ColumnString(index++);
credit_card_cloud_token_data->push_back(std::move(data));
}
return s.Succeeded();
}
void AutofillTable::SetPaymentsCustomerData(
const PaymentsCustomerData* customer_data) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return;
// Delete all old values.
Delete(db_, kPaymentsCustomerDataTable);
if (customer_data) {
sql::Statement insert_customer_data;
InsertBuilder(db_, insert_customer_data, kPaymentsCustomerDataTable,
{kCustomerId});
insert_customer_data.BindString(0, customer_data->customer_id);
insert_customer_data.Run();
}
transaction.Commit();
}
bool AutofillTable::GetPaymentsCustomerData(
std::unique_ptr<PaymentsCustomerData>* customer_data) const {
sql::Statement s;
SelectBuilder(db_, s, kPaymentsCustomerDataTable, {kCustomerId});
if (s.Step()) {
*customer_data = std::make_unique<PaymentsCustomerData>(
/*customer_id=*/s.ColumnString(0));
}
return s.Succeeded();
}
void AutofillTable::SetAutofillOffers(
const std::vector<AutofillOfferData>& autofill_offer_data) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return;
// Delete all old values.
Delete(db_, kOfferDataTable);
Delete(db_, kOfferEligibleInstrumentTable);
Delete(db_, kOfferMerchantDomainTable);
// Insert new values.
sql::Statement insert_offers;
InsertBuilder(
db_, insert_offers, kOfferDataTable,
{kOfferId, kOfferRewardAmount, kExpiry, kOfferDetailsUrl, kPromoCode,
kValuePropText, kSeeDetailsText, kUsageInstructionsText});
for (const AutofillOfferData& data : autofill_offer_data) {
insert_offers.BindInt64(0, data.GetOfferId());
insert_offers.BindString(1, data.GetOfferRewardAmount());
insert_offers.BindInt64(
2, data.GetExpiry().ToDeltaSinceWindowsEpoch().InMilliseconds());
insert_offers.BindString(3, data.GetOfferDetailsUrl().spec());
insert_offers.BindString(4, data.GetPromoCode());
insert_offers.BindString(5, data.GetDisplayStrings().value_prop_text);
insert_offers.BindString(6, data.GetDisplayStrings().see_details_text);
insert_offers.BindString(7,
data.GetDisplayStrings().usage_instructions_text);
insert_offers.Run();
insert_offers.Reset(true);
for (const int64_t instrument_id : data.GetEligibleInstrumentIds()) {
// Insert new offer_eligible_instrument values.
sql::Statement insert_offer_eligible_instruments;
InsertBuilder(db_, insert_offer_eligible_instruments,
kOfferEligibleInstrumentTable, {kOfferId, kInstrumentId});
insert_offer_eligible_instruments.BindInt64(0, data.GetOfferId());
insert_offer_eligible_instruments.BindInt64(1, instrument_id);
insert_offer_eligible_instruments.Run();
}
for (const GURL& merchant_origin : data.GetMerchantOrigins()) {
// Insert new offer_merchant_domain values.
sql::Statement insert_offer_merchant_domains;
InsertBuilder(db_, insert_offer_merchant_domains,
kOfferMerchantDomainTable, {kOfferId, kMerchantDomain});
insert_offer_merchant_domains.BindInt64(0, data.GetOfferId());
insert_offer_merchant_domains.BindString(1, merchant_origin.spec());
insert_offer_merchant_domains.Run();
}
}
transaction.Commit();
}
bool AutofillTable::GetAutofillOffers(
std::vector<std::unique_ptr<AutofillOfferData>>* autofill_offer_data) {
autofill_offer_data->clear();
sql::Statement s;
SelectBuilder(
db_, s, kOfferDataTable,
{kOfferId, kOfferRewardAmount, kExpiry, kOfferDetailsUrl, kPromoCode,
kValuePropText, kSeeDetailsText, kUsageInstructionsText});
while (s.Step()) {
int index = 0;
int64_t offer_id = s.ColumnInt64(index++);
std::string offer_reward_amount = s.ColumnString(index++);
base::Time expiry = base::Time::FromDeltaSinceWindowsEpoch(
base::Milliseconds(s.ColumnInt64(index++)));
GURL offer_details_url = GURL(s.ColumnString(index++));
std::string promo_code = s.ColumnString(index++);
std::string value_prop_text = s.ColumnString(index++);
std::string see_details_text = s.ColumnString(index++);
std::string usage_instructions_text = s.ColumnString(index++);
DisplayStrings display_strings = {value_prop_text, see_details_text,
usage_instructions_text};
std::vector<int64_t> eligible_instrument_id;
std::vector<GURL> merchant_origins;
sql::Statement s_offer_eligible_instrument;
SelectBuilder(db_, s_offer_eligible_instrument,
kOfferEligibleInstrumentTable, {kOfferId, kInstrumentId},
"WHERE offer_id = ?");
s_offer_eligible_instrument.BindInt64(0, offer_id);
while (s_offer_eligible_instrument.Step()) {
const int64_t instrument_id = s_offer_eligible_instrument.ColumnInt64(1);
if (instrument_id != 0) {
eligible_instrument_id.push_back(instrument_id);
}
}
sql::Statement s_offer_merchant_domain;
SelectBuilder(db_, s_offer_merchant_domain, kOfferMerchantDomainTable,
{kOfferId, kMerchantDomain}, "WHERE offer_id = ?");
s_offer_merchant_domain.BindInt64(0, offer_id);
while (s_offer_merchant_domain.Step()) {
const std::string merchant_domain =
s_offer_merchant_domain.ColumnString(1);
if (!merchant_domain.empty()) {
merchant_origins.emplace_back(merchant_domain);
}
}
if (promo_code.empty()) {
auto data = std::make_unique<AutofillOfferData>(
AutofillOfferData::GPayCardLinkedOffer(
offer_id, expiry, merchant_origins, offer_details_url,
display_strings, eligible_instrument_id, offer_reward_amount));
autofill_offer_data->emplace_back(std::move(data));
} else {
auto data = std::make_unique<AutofillOfferData>(
AutofillOfferData::GPayPromoCodeOffer(
offer_id, expiry, merchant_origins, offer_details_url,
display_strings, promo_code));
autofill_offer_data->emplace_back(std::move(data));
}
}
return s.Succeeded();
}
void AutofillTable::SetVirtualCardUsageData(
const std::vector<VirtualCardUsageData>& virtual_card_usage_data) {
sql::Transaction transaction(db_);
if (!transaction.Begin()) {
return;
}
// Delete old table.
Delete(db_, kVirtualCardUsageDataTable);
// Insert new values.
sql::Statement insert_data;
InsertBuilder(db_, insert_data, kVirtualCardUsageDataTable,
{kId, kInstrumentId, kMerchantDomain, kLastFour});
for (const VirtualCardUsageData& data : virtual_card_usage_data) {
// usage_data_id should be consistent with the sync server logic.
std::string usage_data_id = base::JoinString(
{"VirtualCardUsageData",
base::NumberToString(data.instrument_id.value()),
data.merchant_app_package, data.merchant_origin.Serialize()},
"|");
insert_data.BindString(0, usage_data_id);
insert_data.BindInt64(1, data.instrument_id.value());
insert_data.BindString(2, data.merchant_origin.Serialize());
insert_data.BindString(3, data.virtual_card_last_four.value());
insert_data.Run();
insert_data.Reset(true);
}
transaction.Commit();
}
bool AutofillTable::GetVirtualCardUsageData(
std::vector<std::unique_ptr<VirtualCardUsageData>>*
virtual_card_usage_data) {
virtual_card_usage_data->clear();
sql::Statement s;
SelectBuilder(db_, s, kVirtualCardUsageDataTable,
{kId, kInstrumentId, kMerchantDomain, kLastFour});
while (s.Step()) {
int index = 1; // UsageDataId is unused.
int64_t instrument_id = s.ColumnInt64(index++);
std::string merchant_domain = s.ColumnString(index++);
std::string last_four = s.ColumnString(index++);
auto data = std::make_unique<VirtualCardUsageData>();
data->instrument_id = VirtualCardUsageData::InstrumentId(instrument_id);
data->virtual_card_last_four =
VirtualCardUsageData::VirtualCardLastFour(last_four);
data->merchant_origin = url::Origin::Create(GURL(merchant_domain));
virtual_card_usage_data->push_back(std::move(data));
}
return s.Succeeded();
}
bool AutofillTable::InsertUpiId(const std::string& upi_id) {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
sql::Statement insert_upi_id_statement;
InsertBuilder(db_, insert_upi_id_statement, kPaymentsUpiVpaTable, {kVpa});
insert_upi_id_statement.BindString(0, upi_id);
insert_upi_id_statement.Run();
transaction.Commit();
return db_->GetLastChangeCount() > 0;
}
std::vector<std::string> AutofillTable::GetAllUpiIds() {
sql::Statement select_upi_id_statement;
SelectBuilder(db_, select_upi_id_statement, kPaymentsUpiVpaTable, {kVpa});
std::vector<std::string> upi_ids;
while (select_upi_id_statement.Step()) {
upi_ids.push_back(select_upi_id_statement.ColumnString(0));
}
return upi_ids;
}
bool AutofillTable::ClearAllServerData() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false; // Some error, nothing was changed.
bool changed = false;
for (base::StringPiece table_name :
{kMaskedCreditCardsTable, kUnmaskedCreditCardsTable,
kServerAddressesTable, kServerCardMetadataTable,
kServerAddressMetadataTable, kPaymentsCustomerDataTable,
kServerCardCloudTokenDataTable, kOfferDataTable,
kOfferEligibleInstrumentTable, kOfferMerchantDomainTable}) {
Delete(db_, table_name);
changed |= db_->GetLastChangeCount() > 0;
}
transaction.Commit();
return changed;
}
bool AutofillTable::ClearAllLocalData() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false; // Some error, nothing was changed.
ClearAutofillProfiles();
bool changed = db_->GetLastChangeCount() > 0;
ClearCreditCards();
changed |= db_->GetLastChangeCount() > 0;
transaction.Commit();
return changed;
}
bool AutofillTable::RemoveAutofillDataModifiedBetween(
const base::Time& delete_begin,
const base::Time& delete_end,
std::vector<std::unique_ptr<AutofillProfile>>* profiles,
std::vector<std::unique_ptr<CreditCard>>* credit_cards) {
DCHECK(delete_end.is_null() || delete_begin < delete_end);
time_t delete_begin_t = delete_begin.ToTimeT();
time_t delete_end_t = GetEndTime(delete_end);
// Remember Autofill profiles in the time range.
sql::Statement s_profiles_get;
SelectBetween(db_, s_profiles_get, kAutofillProfilesTable, {kGuid},
kDateModified, delete_begin_t, delete_end_t);
profiles->clear();
while (s_profiles_get.Step()) {
std::string guid = s_profiles_get.ColumnString(0);
std::unique_ptr<AutofillProfile> profile =
GetAutofillProfile(guid, AutofillProfile::Source::kLocalOrSyncable);
if (!profile)
return false;
profiles->push_back(std::move(profile));
}
if (!s_profiles_get.Succeeded())
return false;
// Remove the profile pieces.
for (const std::unique_ptr<AutofillProfile>& profile : *profiles) {
if (!RemoveAutofillProfilePieces(profile->guid(), db_))
return false;
}
// Remove Autofill profiles in the time range.
sql::Statement s_profiles;
DeleteBuilder(db_, s_profiles, kAutofillProfilesTable,
"date_modified >= ? AND date_modified < ?");
s_profiles.BindInt64(0, delete_begin_t);
s_profiles.BindInt64(1, delete_end_t);
if (!s_profiles.Run())
return false;
// Remember Autofill credit cards in the time range.
sql::Statement s_credit_cards_get;
SelectBetween(db_, s_credit_cards_get, kCreditCardsTable, {kGuid},
kDateModified, delete_begin_t, delete_end_t);
credit_cards->clear();
while (s_credit_cards_get.Step()) {
std::string guid = s_credit_cards_get.ColumnString(0);
std::unique_ptr<CreditCard> credit_card = GetCreditCard(guid);
if (!credit_card)
return false;
credit_cards->push_back(std::move(credit_card));
}
if (!s_credit_cards_get.Succeeded())
return false;
// Remove Autofill credit cards in the time range.
sql::Statement s_credit_cards;
DeleteBuilder(db_, s_credit_cards, kCreditCardsTable,
"date_modified >= ? AND date_modified < ?");
s_credit_cards.BindInt64(0, delete_begin_t);
s_credit_cards.BindInt64(1, delete_end_t);
if (!s_credit_cards.Run())
return false;
// Remove unmasked credit cards in the time range.
sql::Statement s_unmasked_cards;
DeleteBuilder(db_, s_unmasked_cards, kUnmaskedCreditCardsTable,
"unmask_date >= ? AND unmask_date < ?");
s_unmasked_cards.BindInt64(0, delete_begin.ToInternalValue());
s_unmasked_cards.BindInt64(1, delete_end.ToInternalValue());
return s_unmasked_cards.Run();
}
bool AutofillTable::RemoveOriginURLsModifiedBetween(
const base::Time& delete_begin,
const base::Time& delete_end,
std::vector<std::unique_ptr<AutofillProfile>>* profiles) {
DCHECK(delete_end.is_null() || delete_begin < delete_end);
time_t delete_begin_t = delete_begin.ToTimeT();
time_t delete_end_t = GetEndTime(delete_end);
// Remember Autofill profiles with URL origins in the time range.
sql::Statement s_profiles_get;
SelectBetween(db_, s_profiles_get, kAutofillProfilesTable, {kGuid, kOrigin},
kDateModified, delete_begin_t, delete_end_t);
std::vector<std::string> profile_guids;
while (s_profiles_get.Step()) {
std::string guid = s_profiles_get.ColumnString(0);
std::string origin = s_profiles_get.ColumnString(1);
if (GURL(origin).is_valid())
profile_guids.push_back(guid);
}
if (!s_profiles_get.Succeeded())
return false;
// Clear out the origins for the found Autofill profiles.
for (const std::string& guid : profile_guids) {
sql::Statement s_profile;
UpdateBuilder(db_, s_profile, kAutofillProfilesTable, {kOrigin}, "guid=?");
s_profile.BindString(0, "");
s_profile.BindString(1, guid);
if (!s_profile.Run())
return false;
std::unique_ptr<AutofillProfile> profile =
GetAutofillProfile(guid, AutofillProfile::Source::kLocalOrSyncable);
if (!profile)
return false;
profiles->push_back(std::move(profile));
}
// Remember Autofill credit cards with URL origins in the time range.
sql::Statement s_credit_cards_get;
SelectBetween(db_, s_credit_cards_get, kCreditCardsTable, {kGuid, kOrigin},
kDateModified, delete_begin_t, delete_end_t);
std::vector<std::string> credit_card_guids;
while (s_credit_cards_get.Step()) {
std::string guid = s_credit_cards_get.ColumnString(0);
std::string origin = s_credit_cards_get.ColumnString(1);
if (GURL(origin).is_valid())
credit_card_guids.push_back(guid);
}
if (!s_credit_cards_get.Succeeded())
return false;
// Clear out the origins for the found credit cards.
for (const std::string& guid : credit_card_guids) {
sql::Statement s_credit_card;
UpdateBuilder(db_, s_credit_card, kCreditCardsTable, {kOrigin}, "guid=?");
s_credit_card.BindString(0, "");
s_credit_card.BindString(1, guid);
if (!s_credit_card.Run())
return false;
}
return true;
}
bool AutofillTable::ClearAutofillProfiles() {
return Delete(db_, kAutofillProfilesTable) &&
Delete(db_, kAutofillProfileNamesTable) &&
Delete(db_, kAutofillProfileEmailsTable) &&
Delete(db_, kAutofillProfileAddressesTable) &&
Delete(db_, kAutofillProfilePhonesTable) &&
Delete(db_, kAutofillProfileBirthdatesTable);
}
bool AutofillTable::ClearCreditCards() {
return Delete(db_, kCreditCardsTable);
}
bool AutofillTable::GetAllSyncMetadata(syncer::ModelType model_type,
syncer::MetadataBatch* metadata_batch) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
DCHECK(metadata_batch);
if (!GetAllSyncEntityMetadata(model_type, metadata_batch)) {
return false;
}
sync_pb::ModelTypeState model_type_state;
if (!GetModelTypeState(model_type, &model_type_state))
return false;
metadata_batch->SetModelTypeState(model_type_state);
return true;
}
bool AutofillTable::UpdateEntityMetadata(
syncer::ModelType model_type,
const std::string& storage_key,
const sync_pb::EntityMetadata& metadata) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
sql::Statement s;
InsertBuilder(db_, s, kAutofillSyncMetadataTable,
{kModelType, kStorageKey, kValue},
/*or_replace=*/true);
s.BindInt(0, GetKeyValueForModelType(model_type));
s.BindString(1, storage_key);
s.BindString(2, metadata.SerializeAsString());
return s.Run();
}
bool AutofillTable::ClearEntityMetadata(syncer::ModelType model_type,
const std::string& storage_key) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
sql::Statement s;
DeleteBuilder(db_, s, kAutofillSyncMetadataTable,
"model_type=? AND storage_key=?");
s.BindInt(0, GetKeyValueForModelType(model_type));
s.BindString(1, storage_key);
return s.Run();
}
bool AutofillTable::UpdateModelTypeState(
syncer::ModelType model_type,
const sync_pb::ModelTypeState& model_type_state) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
// Hardcode the id to force a collision, ensuring that there remains only a
// single entry.
sql::Statement s;
InsertBuilder(db_, s, kAutofillModelTypeStateTable, {kModelType, kValue},
/*or_replace=*/true);
s.BindInt(0, GetKeyValueForModelType(model_type));
s.BindString(1, model_type_state.SerializeAsString());
return s.Run();
}
bool AutofillTable::ClearModelTypeState(syncer::ModelType model_type) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
sql::Statement s;
DeleteBuilder(db_, s, kAutofillModelTypeStateTable, "model_type=?");
s.BindInt(0, GetKeyValueForModelType(model_type));
return s.Run();
}
bool AutofillTable::RemoveOrphanAutofillTableRows() {
// Get all the orphan guids.
std::set<std::string> orphan_guids;
sql::Statement s_orphan_profile_pieces_get(db_->GetUniqueStatement(
"SELECT guid FROM (SELECT guid FROM autofill_profile_names UNION SELECT "
"guid FROM autofill_profile_emails UNION SELECT guid FROM "
"autofill_profile_phones UNION SELECT guid FROM "
"autofill_profile_addresses UNION SELECT guid FROM "
"autofill_profile_birthdates) "
"WHERE guid NOT IN (SELECT guid FROM "
"autofill_profiles)"));
// Put the orphan guids in a set.
while (s_orphan_profile_pieces_get.Step())
orphan_guids.insert(s_orphan_profile_pieces_get.ColumnString(0));
if (!s_orphan_profile_pieces_get.Succeeded())
return false;
// Remove the profile pieces for the orphan guids.
for (const std::string& guid : orphan_guids) {
if (!RemoveAutofillProfilePieces(guid, db_))
return false;
}
return true;
}
bool AutofillTable::MigrateToVersion83RemoveServerCardTypeColumn() {
// Sqlite does not support "alter table drop column" syntax, so it has be done
// manually.
constexpr base::StringPiece kMaskedCreditCardsTempTable =
"masked_credit_cards_temp";
sql::Transaction transaction(db_);
return transaction.Begin() &&
CreateTable(db_, kMaskedCreditCardsTempTable,
{{kId, "VARCHAR"},
{kStatus, "VARCHAR"},
{kNameOnCard, "VARCHAR"},
{kNetwork, "VARCHAR"},
{kLastFour, "VARCHAR"},
{kExpMonth, "INTEGER DEFAULT 0"},
{kExpYear, "INTEGER DEFAULT 0"},
{kBankName, "VARCHAR"}}) &&
db_->Execute(
"INSERT INTO masked_credit_cards_temp "
"SELECT id, status, name_on_card, network, last_four, exp_month,"
"exp_year, bank_name "
"FROM masked_credit_cards") &&
DropTable(db_, kMaskedCreditCardsTable) &&
RenameTable(db_, kMaskedCreditCardsTempTable,
kMaskedCreditCardsTable) &&
transaction.Commit();
}
bool AutofillTable::MigrateToVersion84AddNicknameColumn() {
// Add the nickname column to the masked_credit_cards table.
return AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kNickname,
"VARCHAR");
}
bool AutofillTable::MigrateToVersion85AddCardIssuerColumnToMaskedCreditCard() {
// Add the new card_issuer column to the masked_credit_cards table and set the
// default value to ISSUER_UNKNOWN.
return AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kCardIssuer,
"INTEGER DEFAULT 0");
}
bool AutofillTable::MigrateToVersion88AddNewNameColumns() {
for (base::StringPiece column : {kHonorificPrefix, kFirstLastName,
kConjunctionLastName, kSecondLastName}) {
if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column,
"VARCHAR")) {
return false;
}
}
for (base::StringPiece column :
{kHonorificPrefixStatus, kFirstNameStatus, kMiddleNameStatus,
kLastNameStatus, kFirstLastNameStatus, kConjunctionLastNameStatus,
kSecondLastNameStatus, kFullNameStatus}) {
// The default value of 0 corresponds to the verification status
// |kNoStatus|.
if (!AddColumnIfNotExists(db_, kAutofillProfileNamesTable, column,
"INTEGER DEFAULT 0")) {
return false;
}
}
return true;
}
bool AutofillTable::MigrateToVersion92AddNewPrefixedNameColumn() {
return AddColumnIfNotExists(db_, kAutofillProfileNamesTable,
kFullNameWithHonorificPrefix, "VARCHAR") &&
AddColumnIfNotExists(db_, kAutofillProfileNamesTable,
kFullNameWithHonorificPrefixStatus,
"INTEGER DEFAULT 0");
}
bool AutofillTable::MigrateToVersion86RemoveUnmaskedCreditCardsUseColumns() {
// Sqlite does not support "alter table drop column" syntax, so it has be
// done manually.
constexpr base::StringPiece kUnmaskedCreditCardsTempTable =
"unmasked_credit_cards_temp";
sql::Transaction transaction(db_);
return transaction.Begin() &&
CreateTable(db_, kUnmaskedCreditCardsTempTable,
{{kId, "VARCHAR"},
{kCardNumberEncrypted, "VARCHAR"},
{kUnmaskDate, "INTEGER NOT NULL DEFAULT 0"}}) &&
db_->Execute(
"INSERT INTO unmasked_credit_cards_temp "
"SELECT id, card_number_encrypted, unmask_date "
"FROM unmasked_credit_cards") &&
DropTable(db_, kUnmaskedCreditCardsTable) &&
RenameTable(db_, kUnmaskedCreditCardsTempTable,
kUnmaskedCreditCardsTable) &&
transaction.Commit();
}
bool AutofillTable::MigrateToVersion87AddCreditCardNicknameColumn() {
// Add the nickname column to the credit_card table.
return AddColumnIfNotExists(db_, kCreditCardsTable, kNickname, "VARCHAR");
}
bool AutofillTable::MigrateToVersion90AddNewStructuredAddressColumns() {
if (!db_->DoesTableExist("autofill_profile_addresses"))
InitProfileAddressesTable();
for (base::StringPiece column : {kDependentLocality, kCity, kState, kZipCode,
kSortingCode, kCountryCode}) {
if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
"VARCHAR")) {
return false;
}
}
for (base::StringPiece column :
{kDependentLocalityStatus, kCityStatus, kStateStatus, kZipCodeStatus,
kSortingCodeStatus, kCountryCodeStatus}) {
// The default value of 0 corresponds to the verification status
// |kNoStatus|.
if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
"INTEGER DEFAULT 0")) {
return false;
}
}
return true;
}
bool AutofillTable::MigrateToVersion91AddMoreStructuredAddressColumns() {
if (!db_->DoesTableExist(kAutofillProfileAddressesTable))
InitProfileAddressesTable();
for (base::StringPiece column : {kApartmentNumber, kFloor}) {
if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
"VARCHAR")) {
return false;
}
}
for (base::StringPiece column : {kApartmentNumberStatus, kFloorStatus}) {
// The default value of 0 corresponds to the verification status
// |kNoStatus|.
if (!AddColumnIfNotExists(db_, kAutofillProfileAddressesTable, column,
"INTEGER DEFAULT 0")) {
return false;
}
}
return true;
}
bool AutofillTable::MigrateToVersion93AddAutofillProfileLabelColumn() {
if (!db_->DoesTableExist(kAutofillProfilesTable))
InitProfileAddressesTable();
return AddColumnIfNotExists(db_, kAutofillProfilesTable, kLabel, "VARCHAR");
}
bool AutofillTable::
MigrateToVersion96AddAutofillProfileDisallowConfirmableMergesColumn() {
if (!db_->DoesTableExist(kAutofillProfilesTable))
InitProfileAddressesTable();
return AddColumnIfNotExists(db_, kAutofillProfilesTable,
kDisallowSettingsVisibleUpdates,
"INTEGER NOT NULL DEFAULT 0");
}
bool AutofillTable::
MigrateToVersion89AddInstrumentIdColumnToMaskedCreditCard() {
// Add the new instrument_id column to the masked_credit_cards table and set
// the default value to 0.
return AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kInstrumentId,
"INTEGER DEFAULT 0");
}
bool AutofillTable::MigrateToVersion94AddPromoCodeColumnsToOfferData() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
if (!db_->DoesTableExist(kOfferDataTable))
InitOfferDataTable();
// Add the new promo_code and DisplayStrings text columns to the offer_data
// table.
for (base::StringPiece column :
{kPromoCode, kValuePropText, kSeeDetailsText, kUsageInstructionsText}) {
if (!AddColumnIfNotExists(db_, kOfferDataTable, column, "VARCHAR")) {
return false;
}
}
return transaction.Commit();
}
bool AutofillTable::MigrateToVersion95AddVirtualCardMetadata() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
if (!db_->DoesTableExist(kMaskedCreditCardsTable))
InitMaskedCreditCardsTable();
// Add virtual_card_enrollment_state to masked_credit_cards.
if (!AddColumnIfNotExists(db_, kMaskedCreditCardsTable,
kVirtualCardEnrollmentState, "INTEGER DEFAULT 0")) {
return false;
}
// Add card_art_url to masked_credit_cards.
if (!AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kCardArtUrl,
"VARCHAR")) {
return false;
}
return transaction.Commit();
}
bool AutofillTable::MigrateToVersion98RemoveStatusColumnMaskedCreditCards() {
// Sqlite does not support "alter table drop column" syntax, so it has be done
// manually.
constexpr base::StringPiece kMaskedCreditCardsTempTable =
"masked_credit_cards_temp";
sql::Transaction transaction(db_);
return transaction.Begin() &&
CreateTable(db_, kMaskedCreditCardsTempTable,
{{kId, "VARCHAR"},
{kNameOnCard, "VARCHAR"},
{kNetwork, "VARCHAR"},
{kLastFour, "VARCHAR"},
{kExpMonth, "INTEGER DEFAULT 0"},
{kExpYear, "INTEGER DEFAULT 0"},
{kBankName, "VARCHAR"},
{kNickname, "VARCHAR"},
{kCardIssuer, "INTEGER DEFAULT 0"},
{kInstrumentId, "INTEGER DEFAULT 0"},
{kVirtualCardEnrollmentState, "INTEGER DEFAULT 0"},
{kCardArtUrl, "VARCHAR"}}) &&
db_->Execute(
"INSERT INTO masked_credit_cards_temp "
"SELECT id, name_on_card, network, last_four, exp_month, "
"exp_year, bank_name, nickname, card_issuer, instrument_id, "
"virtual_card_enrollment_state, card_art_url "
"FROM masked_credit_cards") &&
DropTable(db_, kMaskedCreditCardsTable) &&
RenameTable(db_, kMaskedCreditCardsTempTable,
kMaskedCreditCardsTable) &&
transaction.Commit();
}
bool AutofillTable::MigrateToVersion99RemoveAutofillProfilesTrashTable() {
return DropTable(db_, "autofill_profiles_trash");
}
bool AutofillTable::MigrateToVersion100RemoveProfileValidityBitfieldColumn() {
// Sqlite does not support "alter table drop column" syntax, so it has be done
// manually.
sql::Transaction transaction(db_);
return transaction.Begin() &&
CreateTable(db_, "autofill_profiles_tmp",
{{kGuid, "VARCHAR PRIMARY KEY"},
{kCompanyName, "VARCHAR"},
{kStreetAddress, "VARCHAR"},
{kDependentLocality, "VARCHAR"},
{kCity, "VARCHAR"},
{kState, "VARCHAR"},
{kZipcode, "VARCHAR"},
{kSortingCode, "VARCHAR"},
{kCountryCode, "VARCHAR"},
{kDateModified, "INTEGER NOT NULL DEFAULT 0"},
{kOrigin, "VARCHAR DEFAULT ''"},
{kLanguageCode, "VARCHAR"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kLabel, "VARCHAR"},
{kDisallowSettingsVisibleUpdates,
"INTEGER NOT NULL DEFAULT 0"}}) &&
db_->Execute(
"INSERT INTO autofill_profiles_tmp "
"SELECT guid, company_name, street_address, dependent_locality, "
"city, state, zipcode, sorting_code, country_code, date_modified, "
"origin, language_code, use_count, use_date, label, "
"disallow_settings_visible_updates "
" FROM autofill_profiles") &&
DropTable(db_, kAutofillProfilesTable) &&
RenameTable(db_, "autofill_profiles_tmp", kAutofillProfilesTable) &&
transaction.Commit();
}
bool AutofillTable::MigrateToVersion101RemoveCreditCardArtImageTable() {
return db_->Execute("DROP TABLE IF EXISTS credit_card_art_images");
}
bool AutofillTable::MigrateToVersion102AddAutofillBirthdatesTable() {
return CreateTable(db_, kAutofillProfileBirthdatesTable,
{{kGuid, "VARCHAR"},
{kDay, "INTEGER DEFAULT 0"},
{kMonth, "INTEGER DEFAULT 0"},
{kYear, "INTEGER DEFAULT 0"}});
}
bool AutofillTable::MigrateToVersion104AddProductDescriptionColumn() {
sql::Transaction transaction(db_);
if (!transaction.Begin())
return false;
if (!db_->DoesTableExist(kMaskedCreditCardsTable))
InitMaskedCreditCardsTable();
// Add product_description to masked_credit_cards.
if (!AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kProductDescription,
"VARCHAR")) {
return false;
}
return transaction.Commit();
}
bool AutofillTable::MigrateToVersion105AddAutofillIBANTable() {
return CreateTable(db_, kIBANsTable,
{{kGuid, "VARCHAR"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kValue, "VARCHAR"},
{kNickname, "VARCHAR"}});
}
bool AutofillTable::MigrateToVersion106RecreateAutofillIBANTable() {
sql::Transaction transaction(db_);
return transaction.Begin() && DropTable(db_, kIBANsTable) &&
CreateTable(db_, kIBANsTable,
{{kGuid, "VARCHAR PRIMARY KEY"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kValue, "VARCHAR"},
{kNickname, "VARCHAR"}}) &&
transaction.Commit();
}
bool AutofillTable::MigrateToVersion107AddContactInfoTables() {
sql::Transaction transaction(db_);
return transaction.Begin() &&
CreateTable(db_, kContactInfoTable,
{{kGuid, "VARCHAR PRIMARY KEY"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kDateModified, "INTEGER NOT NULL DEFAULT 0"},
{kLanguageCode, "VARCHAR"},
{kLabel, "VARCHAR"}}) &&
CreateTable(db_, kContactInfoTypeTokensTable,
{{kGuid, "VARCHAR"},
{kType, "INTEGER"},
{kValue, "VARCHAR"},
{kVerificationStatus, "INTEGER DEFAULT 0"}},
/*composite_primary_key=*/{kGuid, kType}) &&
transaction.Commit();
}
bool AutofillTable::MigrateToVersion108AddCardIssuerIdColumn() {
// Add card_issuer_id to masked_credit_cards.
return db_->DoesTableExist(kMaskedCreditCardsTable) &&
AddColumnIfNotExists(db_, kMaskedCreditCardsTable, kCardIssuerId,
"VARCHAR");
}
bool AutofillTable::MigrateToVersion109AddVirtualCardUsageDataTable() {
return CreateTable(db_, kVirtualCardUsageDataTable,
{{kId, "VARCHAR PRIMARY KEY"},
{kInstrumentId, "INTEGER DEFAULT 0"},
{kMerchantDomain, "VARCHAR"},
{kLastFour, "VARCHAR"}});
}
bool AutofillTable::AddFormFieldValuesTime(
const std::vector<FormFieldData>& elements,
std::vector<AutofillChange>* changes,
base::Time time) {
// Only add one new entry for each unique element name. Use |seen_names|
// to track this. Add up to |kMaximumUniqueNames| unique entries per
// form.
const size_t kMaximumUniqueNames = 256;
std::set<std::u16string> seen_names;
bool result = true;
for (const FormFieldData& element : elements) {
if (seen_names.size() >= kMaximumUniqueNames)
break;
if (base::Contains(seen_names, element.name))
continue;
result = result && AddFormFieldValueTime(element, changes, time);
seen_names.insert(element.name);
}
return result;
}
bool AutofillTable::AddFormFieldValueTime(const FormFieldData& element,
std::vector<AutofillChange>* changes,
base::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())
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;
InsertBuilder(
db_, s, kAutofillTable,
{kName, kValue, kValueLower, kDateCreated, kDateLastUsed, kCount});
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;
}
AutofillChange::Type change_type =
already_exists ? AutofillChange::UPDATE : AutofillChange::ADD;
changes->push_back(
AutofillChange(change_type, AutofillKey(element.name, element.value)));
return true;
}
bool AutofillTable::SupportsMetadataForModelType(
syncer::ModelType model_type) const {
return (model_type == syncer::AUTOFILL ||
model_type == syncer::AUTOFILL_PROFILE ||
model_type == syncer::AUTOFILL_WALLET_DATA ||
model_type == syncer::AUTOFILL_WALLET_METADATA ||
model_type == syncer::AUTOFILL_WALLET_OFFER ||
model_type == syncer::AUTOFILL_WALLET_USAGE ||
model_type == syncer::CONTACT_INFO);
}
int AutofillTable::GetKeyValueForModelType(syncer::ModelType model_type) const {
return syncer::ModelTypeToStableIdentifier(model_type);
}
bool AutofillTable::GetAllSyncEntityMetadata(
syncer::ModelType model_type,
syncer::MetadataBatch* metadata_batch) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
DCHECK(metadata_batch);
sql::Statement s;
SelectBuilder(db_, s, kAutofillSyncMetadataTable, {kStorageKey, kValue},
"WHERE model_type=?");
s.BindInt(0, GetKeyValueForModelType(model_type));
while (s.Step()) {
std::string storage_key = s.ColumnString(0);
std::string serialized_metadata = s.ColumnString(1);
auto entity_metadata = std::make_unique<sync_pb::EntityMetadata>();
if (entity_metadata->ParseFromString(serialized_metadata)) {
metadata_batch->AddMetadata(storage_key, std::move(entity_metadata));
} else {
DLOG(WARNING) << "Failed to deserialize AUTOFILL model type "
"sync_pb::EntityMetadata.";
return false;
}
}
return true;
}
bool AutofillTable::GetModelTypeState(syncer::ModelType model_type,
sync_pb::ModelTypeState* state) {
DCHECK(SupportsMetadataForModelType(model_type))
<< "Model type " << model_type << " not supported for metadata";
sql::Statement s;
SelectBuilder(db_, s, kAutofillModelTypeStateTable, {kValue},
"WHERE model_type=?");
s.BindInt(0, GetKeyValueForModelType(model_type));
if (!s.Step()) {
return true;
}
std::string serialized_state = s.ColumnString(0);
return state->ParseFromString(serialized_state);
}
bool AutofillTable::InsertAutofillEntry(const AutofillEntry& entry) {
sql::Statement s;
InsertBuilder(
db_, s, kAutofillTable,
{kName, kValue, kValueLower, kDateCreated, kDateLastUsed, kCount});
s.BindString16(0, entry.key().name());
s.BindString16(1, entry.key().value());
s.BindString16(2, base::i18n::ToLower(entry.key().value()));
s.BindInt64(3, entry.date_created().ToTimeT());
s.BindInt64(4, entry.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.date_last_used() == entry.date_created() ? 1 : 2);
return s.Run();
}
void AutofillTable::AddMaskedCreditCards(
const std::vector<CreditCard>& credit_cards) {
DCHECK_GT(db_->transaction_nesting(), 0);
sql::Statement masked_insert;
InsertBuilder(
db_, masked_insert, kMaskedCreditCardsTable,
{kId, kNetwork, kNameOnCard, kLastFour, kExpMonth, kExpYear, kBankName,
kNickname, kCardIssuer, kCardIssuerId, kInstrumentId,
kVirtualCardEnrollmentState, kCardArtUrl, kProductDescription});
int index;
for (const CreditCard& card : credit_cards) {
DCHECK_EQ(CreditCard::MASKED_SERVER_CARD, card.record_type());
index = 0;
masked_insert.BindString(index++, card.server_id());
masked_insert.BindString(index++, card.network());
masked_insert.BindString16(index++, card.GetRawInfo(CREDIT_CARD_NAME_FULL));
masked_insert.BindString16(index++, card.LastFourDigits());
masked_insert.BindString16(index++, card.GetRawInfo(CREDIT_CARD_EXP_MONTH));
masked_insert.BindString16(index++,
card.GetRawInfo(CREDIT_CARD_EXP_4_DIGIT_YEAR));
masked_insert.BindString(index++, card.bank_name());
masked_insert.BindString16(index++, card.nickname());
masked_insert.BindInt(index++, static_cast<int>(card.card_issuer()));
masked_insert.BindString(index++, card.issuer_id());
masked_insert.BindInt64(index++, card.instrument_id());
masked_insert.BindInt(index++, card.virtual_card_enrollment_state());
masked_insert.BindString(index++, card.card_art_url().spec());
masked_insert.BindString16(index++, card.product_description());
masked_insert.Run();
masked_insert.Reset(true);
// Save the use count and use date of the card.
UpdateServerCardMetadata(card);
}
}
void AutofillTable::AddUnmaskedCreditCard(const std::string& id,
const std::u16string& full_number) {
sql::Statement s;
InsertBuilder(db_, s, kUnmaskedCreditCardsTable,
{kId, kCardNumberEncrypted, kUnmaskDate});
s.BindString(0, id);
std::string encrypted_data;
autofill_table_encryptor_->EncryptString16(full_number, &encrypted_data);
s.BindBlob(1, encrypted_data);
s.BindInt64(2, AutofillClock::Now().ToInternalValue()); // unmask_date
s.Run();
}
bool AutofillTable::DeleteFromMaskedCreditCards(const std::string& id) {
DeleteWhereColumnEq(db_, kMaskedCreditCardsTable, kId, id);
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::DeleteFromUnmaskedCreditCards(const std::string& id) {
DeleteWhereColumnEq(db_, kUnmaskedCreditCardsTable, kId, id);
return db_->GetLastChangeCount() > 0;
}
bool AutofillTable::InitMainTable() {
if (!db_->DoesTableExist(kAutofillTable)) {
return CreateTable(db_, kAutofillTable,
{{kName, "VARCHAR"},
{kValue, "VARCHAR"},
{kValueLower, "VARCHAR"},
{kDateCreated, "INTEGER DEFAULT 0"},
{kDateLastUsed, "INTEGER DEFAULT 0"},
{kCount, "INTEGER DEFAULT 1"}},
{kName, kValue}) &&
CreateIndex(db_, kAutofillTable, {kName}) &&
CreateIndex(db_, kAutofillTable, {kName, kValueLower});
}
return true;
}
bool AutofillTable::InitCreditCardsTable() {
return CreateTableIfNotExists(db_, kCreditCardsTable,
{{kGuid, "VARCHAR PRIMARY KEY"},
{kNameOnCard, "VARCHAR"},
{kExpirationMonth, "INTEGER"},
{kExpirationYear, "INTEGER"},
{kCardNumberEncrypted, "BLOB"},
{kDateModified, "INTEGER NOT NULL DEFAULT 0"},
{kOrigin, "VARCHAR DEFAULT ''"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kBillingAddressId, "VARCHAR"},
{kNickname, "VARCHAR"}});
}
bool AutofillTable::InitIBANsTable() {
return CreateTableIfNotExists(db_, kIBANsTable,
{{kGuid, "VARCHAR PRIMARY KEY"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kValue, "VARCHAR"},
{kNickname, "VARCHAR"}});
}
bool AutofillTable::InitProfilesTable() {
return CreateTableIfNotExists(
db_, kAutofillProfilesTable,
{{kGuid, "VARCHAR PRIMARY KEY"},
{kCompanyName, "VARCHAR"},
{kStreetAddress, "VARCHAR"},
{kDependentLocality, "VARCHAR"},
{kCity, "VARCHAR"},
{kState, "VARCHAR"},
{kZipcode, "VARCHAR"},
{kSortingCode, "VARCHAR"},
{kCountryCode, "VARCHAR"},
{kDateModified, "INTEGER NOT NULL DEFAULT 0"},
{kOrigin, "VARCHAR DEFAULT ''"},
{kLanguageCode, "VARCHAR"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kLabel, "VARCHAR"},
{kDisallowSettingsVisibleUpdates, "INTEGER NOT NULL DEFAULT 0"}});
}
bool AutofillTable::InitProfileNamesTable() {
// The default value of 0 corresponds to the verification status
// |kNoStatus|.
return CreateTableIfNotExists(
db_, kAutofillProfileNamesTable,
{{kGuid, "VARCHAR"},
{kFirstName, "VARCHAR"},
{kMiddleName, "VARCHAR"},
{kLastName, "VARCHAR"},
{kFullName, "VARCHAR"},
{kHonorificPrefix, "VARCHAR"},
{kFirstLastName, "VARCHAR"},
{kConjunctionLastName, "VARCHAR"},
{kSecondLastName, "VARCHAR"},
{kHonorificPrefixStatus, "INTEGER DEFAULT 0"},
{kFirstNameStatus, "INTEGER DEFAULT 0"},
{kMiddleNameStatus, "INTEGER DEFAULT 0"},
{kLastNameStatus, "INTEGER DEFAULT 0"},
{kFirstLastNameStatus, "INTEGER DEFAULT 0"},
{kConjunctionLastNameStatus, "INTEGER DEFAULT 0"},
{kSecondLastNameStatus, "INTEGER DEFAULT 0"},
{kFullNameStatus, "INTEGER DEFAULT 0"},
{kFullNameWithHonorificPrefix, "VARCHAR"},
{kFullNameWithHonorificPrefixStatus, "INTEGER DEFAULT 0"}});
}
bool AutofillTable::InitProfileAddressesTable() {
// The default value of 0 corresponds to the verification status
// |kNoStatus|.
return CreateTableIfNotExists(
db_, kAutofillProfileAddressesTable,
{{kGuid, "VARCHAR"},
{kStreetAddress, "VARCHAR"},
{kStreetName, "VARCHAR"},
{kDependentStreetName, "VARCHAR"},
{kHouseNumber, "VARCHAR"},
{kSubpremise, "VARCHAR"},
{kPremiseName, "VARCHAR"},
{kStreetAddressStatus, "INTEGER DEFAULT 0"},
{kStreetNameStatus, "INTEGER DEFAULT 0"},
{kDependentStreetNameStatus, "INTEGER DEFAULT 0"},
{kHouseNumberStatus, "INTEGER DEFAULT 0"},
{kSubpremiseStatus, "INTEGER DEFAULT 0"},
{kPremiseNameStatus, "INTEGER DEFAULT 0"},
{kDependentLocality, "VARCHAR"},
{kCity, "VARCHAR"},
{kState, "VARCHAR"},
{kZipCode, "VARCHAR"},
{kSortingCode, "VARCHAR"},
{kCountryCode, "VARCHAR"},
{kDependentLocalityStatus, "INTEGER DEFAULT 0"},
{kCityStatus, "INTEGER DEFAULT 0"},
{kStateStatus, "INTEGER DEFAULT 0"},
{kZipCodeStatus, "INTEGER DEFAULT 0"},
{kSortingCodeStatus, "INTEGER DEFAULT 0"},
{kCountryCodeStatus, "INTEGER DEFAULT 0"},
{kApartmentNumber, "VARCHAR"},
{kFloor, "VARCHAR"},
{kApartmentNumberStatus, "INTEGER DEFAULT 0"},
{kFloorStatus, "INTEGER DEFAULT 0"}});
}
bool AutofillTable::InitProfileEmailsTable() {
return CreateTableIfNotExists(db_, kAutofillProfileEmailsTable,
{{kGuid, "VARCHAR"}, {kEmail, "VARCHAR"}});
}
bool AutofillTable::InitProfilePhonesTable() {
return CreateTableIfNotExists(db_, kAutofillProfilePhonesTable,
{{kGuid, "VARCHAR"}, {kNumber, "VARCHAR"}});
}
bool AutofillTable::InitProfileBirthdatesTable() {
return CreateTableIfNotExists(db_, kAutofillProfileBirthdatesTable,
{{kGuid, "VARCHAR"},
{kDay, "INTEGER DEFAULT 0"},
{kMonth, "INTEGER DEFAULT 0"},
{kYear, "INTEGER DEFAULT 0"}});
}
bool AutofillTable::InitMaskedCreditCardsTable() {
return CreateTableIfNotExists(
db_, kMaskedCreditCardsTable,
{{kId, "VARCHAR"},
{kNameOnCard, "VARCHAR"},
{kNetwork, "VARCHAR"},
{kLastFour, "VARCHAR"},
{kExpMonth, "INTEGER DEFAULT 0"},
{kExpYear, "INTEGER DEFAULT 0"},
{kBankName, "VARCHAR"},
{kNickname, "VARCHAR"},
{kCardIssuer, "INTEGER DEFAULT 0"},
{kInstrumentId, "INTEGER DEFAULT 0"},
{kVirtualCardEnrollmentState, "INTEGER DEFAULT 0"},
{kCardArtUrl, "VARCHAR"},
{kProductDescription, "VARCHAR"},
{kCardIssuerId, "VARCHAR"}});
}
bool AutofillTable::InitUnmaskedCreditCardsTable() {
return CreateTableIfNotExists(db_, kUnmaskedCreditCardsTable,
{{kId, "VARCHAR"},
{kCardNumberEncrypted, "VARCHAR"},
{kUnmaskDate, "INTEGER NOT NULL DEFAULT 0"}});
}
bool AutofillTable::InitServerCardMetadataTable() {
return CreateTableIfNotExists(db_, kServerCardMetadataTable,
{{kId, "VARCHAR NOT NULL"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kBillingAddressId, "VARCHAR"}});
}
bool AutofillTable::InitServerAddressesTable() {
return CreateTableIfNotExists(db_, kServerAddressesTable,
{{kId, "VARCHAR"},
{kCompanyName, "VARCHAR"},
{kStreetAddress, "VARCHAR"},
{kAddress1, "VARCHAR"},
{kAddress2, "VARCHAR"},
{kAddress3, "VARCHAR"},
{kAddress4, "VARCHAR"},
{kPostalCode, "VARCHAR"},
{kSortingCode, "VARCHAR"},
{kCountryCode, "VARCHAR"},
{kLanguageCode, "VARCHAR"},
{kRecipientName, "VARCHAR"},
{kPhoneNumber, "VARCHAR"}});
}
bool AutofillTable::InitServerAddressMetadataTable() {
return CreateTableIfNotExists(
db_, kServerAddressMetadataTable,
{{kId, "VARCHAR NOT NULL"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kHasConverted, "BOOL NOT NULL DEFAULT FALSE"}});
}
bool AutofillTable::InitAutofillSyncMetadataTable() {
return CreateTableIfNotExists(db_, kAutofillSyncMetadataTable,
{{kModelType, "INTEGER NOT NULL"},
{kStorageKey, "VARCHAR NOT NULL"},
{kValue, "BLOB"}},
{kModelType, kStorageKey});
}
bool AutofillTable::InitModelTypeStateTable() {
return CreateTableIfNotExists(
db_, kAutofillModelTypeStateTable,
{{kModelType, "INTEGER NOT NULL PRIMARY KEY"}, {kValue, "BLOB"}});
}
bool AutofillTable::InitPaymentsCustomerDataTable() {
return CreateTableIfNotExists(db_, kPaymentsCustomerDataTable,
{{kCustomerId, "VARCHAR"}});
}
bool AutofillTable::InitPaymentsUPIVPATable() {
return CreateTableIfNotExists(db_, kPaymentsUpiVpaTable, {{kVpa, "VARCHAR"}});
}
bool AutofillTable::InitServerCreditCardCloudTokenDataTable() {
return CreateTableIfNotExists(db_, kServerCardCloudTokenDataTable,
{{kId, "VARCHAR"},
{kSuffix, "VARCHAR"},
{kExpMonth, "INTEGER DEFAULT 0"},
{kExpYear, "INTEGER DEFAULT 0"},
{kCardArtUrl, "VARCHAR"},
{kInstrumentToken, "VARCHAR"}});
}
bool AutofillTable::InitOfferDataTable() {
return CreateTableIfNotExists(db_, kOfferDataTable,
{{kOfferId, "UNSIGNED LONG"},
{kOfferRewardAmount, "VARCHAR"},
{kExpiry, "UNSIGNED LONG"},
{kOfferDetailsUrl, "VARCHAR"},
{kMerchantDomain, "VARCHAR"},
{kPromoCode, "VARCHAR"},
{kValuePropText, "VARCHAR"},
{kSeeDetailsText, "VARCHAR"},
{kUsageInstructionsText, "VARCHAR"}});
}
bool AutofillTable::InitOfferEligibleInstrumentTable() {
return CreateTableIfNotExists(
db_, kOfferEligibleInstrumentTable,
{{kOfferId, "UNSIGNED LONG"}, {kInstrumentId, "UNSIGNED LONG"}});
}
bool AutofillTable::InitOfferMerchantDomainTable() {
return CreateTableIfNotExists(
db_, kOfferMerchantDomainTable,
{{kOfferId, "UNSIGNED LONG"}, {kMerchantDomain, "VARCHAR"}});
}
bool AutofillTable::InitContactInfoTable() {
return CreateTableIfNotExists(db_, kContactInfoTable,
{{kGuid, "VARCHAR PRIMARY KEY"},
{kUseCount, "INTEGER NOT NULL DEFAULT 0"},
{kUseDate, "INTEGER NOT NULL DEFAULT 0"},
{kDateModified, "INTEGER NOT NULL DEFAULT 0"},
{kLanguageCode, "VARCHAR"},
{kLabel, "VARCHAR"}});
}
bool AutofillTable::InitContactInfoTypeTokensTable() {
return CreateTableIfNotExists(db_, kContactInfoTypeTokensTable,
{{kGuid, "VARCHAR"},
{kType, "INTEGER"},
{kValue, "VARCHAR"},
{kVerificationStatus, "INTEGER DEFAULT 0"}},
/*composite_primary_key=*/{kGuid, kType});
}
bool AutofillTable::InitVirtualCardUsageDataTable() {
return CreateTableIfNotExists(db_, kVirtualCardUsageDataTable,
{{kId, "VARCHAR PRIMARY KEY"},
{kInstrumentId, "INTEGER DEFAULT 0"},
{kMerchantDomain, "VARCHAR"},
{kLastFour, "VARCHAR"}});
}
} // namespace autofill