Index: components/password_manager/core/browser/login_database.cc |
diff --git a/components/password_manager/core/browser/login_database.cc b/components/password_manager/core/browser/login_database.cc |
index c4384129839ee4d6a1bc58698affb1d7818c5ada..b599dc832f90abe57a9bc3203b5bc28aab59f652 100644 |
--- a/components/password_manager/core/browser/login_database.cc |
+++ b/components/password_manager/core/browser/login_database.cc |
@@ -17,6 +17,7 @@ |
#include "base/macros.h" |
#include "base/metrics/histogram_macros.h" |
#include "base/metrics/sparse_histogram.h" |
+#include "base/numerics/safe_conversions.h" |
#include "base/pickle.h" |
#include "base/stl_util.h" |
#include "base/strings/string_util.h" |
@@ -27,6 +28,7 @@ |
#include "components/password_manager/core/browser/affiliation_utils.h" |
#include "components/password_manager/core/browser/password_manager_client.h" |
#include "components/password_manager/core/browser/password_manager_metrics_util.h" |
+#include "components/password_manager/core/browser/sql_table_builder.h" |
#include "google_apis/gaia/gaia_auth_util.h" |
#include "google_apis/gaia/gaia_urls.h" |
#include "sql/connection.h" |
@@ -91,6 +93,7 @@ enum LoginTableColumns { |
COLUMN_FEDERATION_URL, |
COLUMN_SKIP_ZERO_CLICK, |
COLUMN_GENERATION_UPLOAD_STATUS, |
+ COLUMN_NUM // Keep this last. |
}; |
enum class HistogramSize { SMALL, LARGE }; |
@@ -335,45 +338,166 @@ void LogPasswordReuseMetrics(const std::vector<std::string>& signon_realms) { |
} |
} |
-// Creates a table named |table_name| using our current schema. |
-bool CreateNewTable(sql::Connection* db, |
- const char* table_name, |
- const char* extra_columns) { |
- std::string query = base::StringPrintf( |
- "CREATE TABLE %s (" |
- "origin_url VARCHAR NOT NULL, " |
- "action_url VARCHAR, " |
- "username_element VARCHAR, " |
- "username_value VARCHAR, " |
- "password_element VARCHAR, " |
- "password_value BLOB, " |
- "submit_element VARCHAR, " |
- "signon_realm VARCHAR NOT NULL," |
- "ssl_valid INTEGER NOT NULL," |
- "preferred INTEGER NOT NULL," |
- "date_created INTEGER NOT NULL," |
- "blacklisted_by_user INTEGER NOT NULL," |
- "scheme INTEGER NOT NULL," |
- "password_type INTEGER," |
- "possible_usernames BLOB," |
- "times_used INTEGER," |
- "form_data BLOB," |
- "date_synced INTEGER," |
- "display_name VARCHAR," |
- "icon_url VARCHAR," |
- "federation_url VARCHAR," |
- "skip_zero_click INTEGER," |
- "%s" |
- "UNIQUE (origin_url, username_element, username_value, " |
- "password_element, signon_realm))", |
- table_name, extra_columns); |
- return db->Execute(query.c_str()); |
+// Teaches |builder| about the different DB schemes in different versions. |
+void InitializeBuilder(SQLTableBuilder* builder) { |
+ // Versions 0 and 1, which are the same. |
+ builder->AddColumnToUniqueKey("origin_url", "VARCHAR NOT NULL"); |
+ builder->AddColumn("action_url", "VARCHAR"); |
+ builder->AddColumnToUniqueKey("username_element", "VARCHAR"); |
+ builder->AddColumnToUniqueKey("username_value", "VARCHAR"); |
+ builder->AddColumnToUniqueKey("password_element", "VARCHAR"); |
+ builder->AddColumn("password_value", "BLOB"); |
+ builder->AddColumn("submit_element", "VARCHAR"); |
+ builder->AddColumnToUniqueKey("signon_realm", "VARCHAR NOT NULL"); |
+ builder->AddColumn("ssl_valid", "INTEGER NOT NULL"); |
+ builder->AddColumn("preferred", "INTEGER NOT NULL"); |
+ builder->AddColumn("date_created", "INTEGER NOT NULL"); |
+ builder->AddColumn("blacklisted_by_user", "INTEGER NOT NULL"); |
+ builder->AddColumn("scheme", "INTEGER NOT NULL"); |
+ builder->SealVersion(); |
+ unsigned version = builder->SealVersion(); |
+ DCHECK_EQ(1u, version); |
+ |
+ // Version 2. |
+ builder->AddColumn("password_type", "INTEGER"); |
+ builder->AddColumn("possible_usernames", "BLOB"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(2u, version); |
+ |
+ // Version 3. |
+ builder->AddColumn("times_used", "INTEGER"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(3u, version); |
+ |
+ // Version 4. |
+ builder->AddColumn("form_data", "BLOB"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(4u, version); |
+ |
+ // Version 5. |
+ builder->AddColumn("use_additional_auth", "INTEGER"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(5u, version); |
+ |
+ // Version 6. |
+ builder->AddColumn("date_synced", "INTEGER"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(6u, version); |
+ |
+ // Version 7. |
+ builder->AddColumn("display_name", "VARCHAR"); |
+ builder->AddColumn("avatar_url", "VARCHAR"); |
+ builder->AddColumn("federation_url", "VARCHAR"); |
+ builder->AddColumn("is_zero_click", "INTEGER"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(7u, version); |
+ |
+ // Version 8. |
+ builder->SealVersion(); |
+ // Version 9. |
+ version = builder->SealVersion(); |
+ // Version 10. |
+ builder->DropColumn("use_additional_auth"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(10u, version); |
+ |
+ // Version 11. |
+ builder->RenameColumn("is_zero_click", "skip_zero_click"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(11u, version); |
+ |
+ // Version 12. |
+ builder->AddColumn("generation_upload_status", "INTEGER"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(12u, version); |
+ |
+ // Version 13. |
+ builder->SealVersion(); |
+ // Version 14. |
+ builder->RenameColumn("avatar_url", "icon_url"); |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(14u, version); |
+ |
+ // Version 15. |
+ builder->SealVersion(); |
+ // Version 16. |
+ builder->SealVersion(); |
+ // Version 17. |
+ version = builder->SealVersion(); |
+ DCHECK_EQ(17u, version); |
+ |
+ DCHECK_EQ(static_cast<size_t>(COLUMN_NUM), builder->NumberOfColumns()) |
+ << "Adjust LoginTableColumns if you change column definitions here."; |
} |
-bool CreateIndexOnSignonRealm(sql::Connection* db, const char* table_name) { |
- std::string query = base::StringPrintf( |
- "CREATE INDEX logins_signon ON %s (signon_realm)", table_name); |
- return db->Execute(query.c_str()); |
+// Call this after having called InitializeBuilder, to migrate the database from |
+// the current version to kCurrentVersionNumber. |
+bool MigrateLogins(unsigned current_version, |
+ SQLTableBuilder* builder, |
+ sql::Connection* db) { |
+ if (!builder->MigrateFrom(current_version, db)) |
+ return false; |
+ |
+ // Data changes, not covered by the schema migration above. |
+ if (current_version <= 8) { |
+ sql::Statement fix_time_format; |
+ fix_time_format.Assign(db->GetCachedStatement( |
+ SQL_FROM_HERE, |
+ "UPDATE logins SET date_created = (date_created * ?) + ?")); |
+ fix_time_format.BindInt64(0, base::Time::kMicrosecondsPerSecond); |
+ fix_time_format.BindInt64(1, base::Time::kTimeTToMicrosecondsOffset); |
+ if (!fix_time_format.Run()) |
+ return false; |
+ } |
+ |
+ if (current_version <= 16) { |
+ sql::Statement reset_zero_click; |
+ reset_zero_click.Assign(db->GetCachedStatement( |
+ SQL_FROM_HERE, "UPDATE logins SET skip_zero_click = 1")); |
+ if (!reset_zero_click.Run()) |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+// Because of https://crbug.com/295851, some early version numbers might be |
+// wrong. This function detects that and fixes the version. |
+bool FixVersionIfNeeded(sql::Connection* db, int* current_version) { |
+ if (*current_version == 1) { |
+ int extra_columns = 0; |
+ if (db->DoesColumnExist("logins", "password_type")) |
+ ++extra_columns; |
+ if (db->DoesColumnExist("logins", "possible_usernames")) |
+ ++extra_columns; |
+ if (extra_columns == 2) { |
+ *current_version = 2; |
+ } else if (extra_columns == 1) { |
+ // If this is https://crbug.com/295851 then either both columns exist |
+ // or none. |
+ return false; |
+ } |
+ } |
+ if (*current_version == 2) { |
+ if (db->DoesColumnExist("logins", "times_used")) |
+ *current_version = 3; |
+ } |
+ if (*current_version == 3) { |
+ if (db->DoesColumnExist("logins", "form_data")) |
+ *current_version = 4; |
+ } |
+ return true; |
+} |
+ |
+// Generates the string "(?,?,...,?)" with |count| repetitions of "?". |
+std::string GeneratePlaceholders(size_t count) { |
+ std::string result(2 * count + 1, ','); |
+ result.front() = '('; |
+ result.back() = ')'; |
+ for (size_t i = 1; i < 2 * count + 1; i += 2) { |
+ result[i] = '?'; |
+ } |
+ return result; |
} |
} // namespace |
@@ -424,18 +548,97 @@ bool LoginDatabase::Init() { |
return false; |
} |
- // Initialize the tables. |
- if (!InitLoginsTable()) { |
- LogDatabaseInitError(INIT_LOGINS_ERROR); |
- LOG(ERROR) << "Unable to initialize the logins table."; |
- db_.Close(); |
- return false; |
+ SQLTableBuilder builder; |
+ InitializeBuilder(&builder); |
+ |
+ // Initialize the cached strings. |
dvadym
2016/07/12 14:48:58
Nit: it looks like all next strings can be moved i
vabr (Chromium)
2016/07/13 09:12:13
Done.
|
+ std::string all_column_names = builder.ListAllNames(); |
+ std::string right_amount_of_placeholders = |
+ GeneratePlaceholders(builder.NumberOfColumns()); |
+ std::string all_unique_key_column_names = builder.ListAllUniqueKeyNames(); |
+ std::string all_nonunique_key_column_names = |
+ builder.ListAllNonuniqueKeyNames(); |
+ |
+ // This method may be called multiple times, if Chrome switches backends and |
+ // LoginDatabase::DeleteAndRecreateDatabaseFile ends up being called. In those |
+ // case do not recompute the SQL statements, because they would end up the |
+ // same. |
+ if (add_statement_.empty()) { |
dvadym
2016/07/12 14:48:58
Nit: I'd prefer to extract initialization *stateme
vabr (Chromium)
2016/07/13 09:12:13
Done.
|
+ add_statement_ = "INSERT INTO logins (" + all_column_names + ") VALUES " + |
+ right_amount_of_placeholders; |
+ DCHECK(add_replace_statement_.empty()); |
+ add_replace_statement_ = "INSERT OR REPLACE INTO logins (" + |
+ all_column_names + ") VALUES " + |
+ right_amount_of_placeholders; |
+ DCHECK(update_statement_.empty()); |
+ update_statement_ = "UPDATE OR REPLACE logins SET " + |
+ all_nonunique_key_column_names + " WHERE " + |
+ all_unique_key_column_names; |
+ DCHECK(delete_statement_.empty()); |
+ delete_statement_ = |
+ "DELETE FROM logins WHERE " + all_unique_key_column_names; |
+ DCHECK(autosignin_statement_.empty()); |
+ autosignin_statement_ = "SELECT " + all_column_names + |
+ " FROM logins " |
+ "WHERE skip_zero_click = 0 ORDER BY origin_url"; |
+ DCHECK(get_statement_.empty()); |
+ get_statement_ = "SELECT " + all_column_names + |
+ " FROM logins " |
+ "WHERE signon_realm == ?"; |
+ std::string psl_statement = "OR signon_realm REGEXP ? "; |
+ std::string federated_statement = |
+ "OR (signon_realm LIKE ? AND password_type == 2) "; |
+ DCHECK(get_statement_psl_.empty()); |
+ get_statement_psl_ = get_statement_ + psl_statement; |
+ DCHECK(get_statement_federated_.empty()); |
+ get_statement_federated_ = get_statement_ + federated_statement; |
+ DCHECK(get_statement_psl_federated_.empty()); |
+ get_statement_psl_federated_ = |
+ get_statement_ + psl_statement + federated_statement; |
+ DCHECK(created_statement_.empty()); |
+ created_statement_ = |
+ "SELECT " + all_column_names + |
+ " FROM logins WHERE date_created >= ? AND date_created < " |
+ "? ORDER BY origin_url"; |
+ DCHECK(synced_statement_.empty()); |
+ synced_statement_ = "SELECT " + all_column_names + |
+ " FROM logins WHERE date_synced >= ? AND date_synced < " |
+ "? ORDER BY origin_url"; |
+ DCHECK(blacklisted_statement_.empty()); |
+ blacklisted_statement_ = |
+ "SELECT " + all_column_names + |
+ " FROM logins WHERE blacklisted_by_user == ? ORDER BY origin_url"; |
+ DCHECK(encrypted_statement_.empty()); |
+ encrypted_statement_ = "SELECT password_value FROM logins WHERE " + |
+ all_unique_key_column_names; |
} |
+ |
+ if (!db_.DoesTableExist("logins")) { |
+ if (!builder.CreateTable(&db_)) { |
+ VLOG(0) << "Failed to create the 'logins' table"; |
+ db_.Close(); |
+ return false; |
+ } |
+ } |
+ |
stats_table_.Init(&db_); |
+ int current_version = meta_table_.GetVersionNumber(); |
+ bool migration_success = FixVersionIfNeeded(&db_, ¤t_version); |
+ DCHECK_LE(current_version, kCurrentVersionNumber); |
+ |
// If the file on disk is an older database version, bring it up to date. |
- if (meta_table_.GetVersionNumber() < kCurrentVersionNumber && |
- !MigrateOldVersionsAsNeeded()) { |
+ if (migration_success && current_version < kCurrentVersionNumber) { |
+ migration_success = MigrateLogins( |
+ base::checked_cast<unsigned>(current_version), &builder, &db_); |
+ } |
+ if (migration_success && current_version <= 15) { |
+ migration_success = stats_table_.MigrateToVersion(16); |
+ } |
+ if (migration_success) { |
+ meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); |
+ meta_table_.SetVersionNumber(kCurrentVersionNumber); |
+ } else { |
LogDatabaseInitError(MIGRATION_ERROR); |
UMA_HISTOGRAM_SPARSE_SLOWLY("PasswordManager.LoginDatabaseFailedVersion", |
meta_table_.GetVersionNumber()); |
@@ -464,204 +667,6 @@ bool LoginDatabase::Init() { |
return true; |
} |
-bool LoginDatabase::MigrateOldVersionsAsNeeded() { |
- const int original_version = meta_table_.GetVersionNumber(); |
- switch (original_version) { |
- case 1: |
- // Column could exist because of https://crbug.com/295851 |
- if (!db_.DoesColumnExist("logins", "password_type") && |
- !db_.Execute("ALTER TABLE logins " |
- "ADD COLUMN password_type INTEGER")) { |
- return false; |
- } |
- if (!db_.DoesColumnExist("logins", "possible_usernames") && |
- !db_.Execute("ALTER TABLE logins " |
- "ADD COLUMN possible_usernames BLOB")) { |
- return false; |
- } |
- // Fall through. |
- case 2: |
- // Column could exist because of https://crbug.com/295851 |
- if (!db_.DoesColumnExist("logins", "times_used") && |
- !db_.Execute("ALTER TABLE logins ADD COLUMN times_used INTEGER")) { |
- return false; |
- } |
- // Fall through. |
- case 3: |
- // Column could exist because of https://crbug.com/295851 |
- if (!db_.DoesColumnExist("logins", "form_data") && |
- !db_.Execute("ALTER TABLE logins ADD COLUMN form_data BLOB")) { |
- return false; |
- } |
- // Fall through. |
- case 4: |
- if (!db_.Execute( |
- "ALTER TABLE logins ADD COLUMN use_additional_auth INTEGER")) { |
- return false; |
- } |
- // Fall through. |
- case 5: |
- if (!db_.Execute("ALTER TABLE logins ADD COLUMN date_synced INTEGER")) { |
- return false; |
- } |
- // Fall through. |
- case 6: |
- if (!db_.Execute("ALTER TABLE logins ADD COLUMN display_name VARCHAR") || |
- !db_.Execute("ALTER TABLE logins ADD COLUMN avatar_url VARCHAR") || |
- !db_.Execute("ALTER TABLE logins " |
- "ADD COLUMN federation_url VARCHAR") || |
- !db_.Execute("ALTER TABLE logins ADD COLUMN is_zero_click INTEGER")) { |
- return false; |
- } |
- // Fall through. |
- case 7: { |
- // Keep version 8 around even though no changes are made. See |
- // crbug.com/423716 for context. |
- // Fall through. |
- } |
- case 8: { |
- sql::Statement s; |
- s.Assign(db_.GetCachedStatement(SQL_FROM_HERE, |
- "UPDATE logins SET " |
- "date_created = " |
- "(date_created * ?) + ?")); |
- s.BindInt64(0, base::Time::kMicrosecondsPerSecond); |
- s.BindInt64(1, base::Time::kTimeTToMicrosecondsOffset); |
- if (!s.Run()) |
- return false; |
- // Fall through. |
- } |
- case 9: { |
- // Remove use_additional_auth column from database schema |
- // crbug.com/423716 for context. |
- std::string fields_to_copy = |
- "origin_url, action_url, username_element, username_value, " |
- "password_element, password_value, submit_element, " |
- "signon_realm, ssl_valid, preferred, date_created, " |
- "blacklisted_by_user, scheme, password_type, possible_usernames, " |
- "times_used, form_data, date_synced, display_name, avatar_url, " |
- "federation_url, is_zero_click"; |
- auto copy_data_query = |
- [&fields_to_copy](const std::string& from, const std::string& to) { |
- return "INSERT INTO " + to + " SELECT " + fields_to_copy + " FROM " + |
- from; |
- }; |
- |
- if (!db_.Execute(("CREATE TEMPORARY TABLE logins_data(" + fields_to_copy + |
- ")").c_str()) || |
- !db_.Execute(copy_data_query("logins", "logins_data").c_str()) || |
- !db_.Execute("DROP TABLE logins") || |
- !db_.Execute( |
- ("CREATE TABLE logins(" + fields_to_copy + ")").c_str()) || |
- !db_.Execute(copy_data_query("logins_data", "logins").c_str()) || |
- !db_.Execute("DROP TABLE logins_data") || |
- !CreateIndexOnSignonRealm(&db_, "logins")) { |
- return false; |
- } |
- // Fall through. |
- } |
- case 10: { |
- // Rename is_zero_click -> skip_zero_click. Note that previous versions |
- // may have incorrectly used a 6-column key (origin_url, username_element, |
- // username_value, password_element, signon_realm, submit_element). |
- // In that case, this step also restores the correct 5-column key; |
- // that is, the above without "submit_element". |
- const char copy_query[] = "INSERT OR REPLACE INTO logins_new SELECT " |
- "origin_url, action_url, username_element, username_value, " |
- "password_element, password_value, submit_element, signon_realm, " |
- "ssl_valid, preferred, date_created, blacklisted_by_user, scheme, " |
- "password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, avatar_url, federation_url, is_zero_click" |
- " FROM logins"; |
- if (!CreateNewTable(&db_, "logins_new", "") || |
- !db_.Execute(copy_query) || |
- !db_.Execute("DROP TABLE logins") || |
- !db_.Execute("ALTER TABLE logins_new RENAME TO logins") || |
- !CreateIndexOnSignonRealm(&db_, "logins")) { |
- return false; |
- } |
- // Fall through. |
- } |
- case 11: |
- if (!db_.Execute( |
- "ALTER TABLE logins ADD COLUMN " |
- "generation_upload_status INTEGER")) |
- return false; |
- // Fall through. |
- case 12: |
- // The stats table was added. Nothing to do really. |
- // Fall through. |
- case 13: { |
- // Rename avatar_url -> icon_url. Note that if the original version was |
- // at most 10, this renaming would have already happened in step 10, |
- // as |CreateNewTable| would create a table with the new column name. |
- if (original_version > 10) { |
- const char copy_query[] = "INSERT OR REPLACE INTO logins_new SELECT " |
- "origin_url, action_url, username_element, username_value, " |
- "password_element, password_value, submit_element, signon_realm, " |
- "ssl_valid, preferred, date_created, blacklisted_by_user, scheme, " |
- "password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, avatar_url, federation_url, " |
- "skip_zero_click, generation_upload_status FROM logins"; |
- if (!CreateNewTable( |
- &db_, "logins_new", "generation_upload_status INTEGER,") || |
- !db_.Execute(copy_query) || |
- !db_.Execute("DROP TABLE logins") || |
- !db_.Execute("ALTER TABLE logins_new RENAME TO logins") || |
- !CreateIndexOnSignonRealm(&db_, "logins")) { |
- return false; |
- } |
- } |
- // Fall through. |
- } |
- case 14: |
- // No change of schema. Version 15 was introduced to force all databases |
- // through an otherwise no-op migration process that will, however, now |
- // correctly set the 'compatible version number'. Previously, it was |
- // always being set to (and forever left at) version 1. |
- meta_table_.SetCompatibleVersionNumber(kCompatibleVersionNumber); |
- case 15: |
- // Recreate the statistics. |
- if (!stats_table_.MigrateToVersion(16)) |
- return false; |
- case 16: { |
- // No change in scheme: just disable auto sign-in by default in |
- // preparation to launch the credential management API. |
- if (!db_.Execute("UPDATE logins SET skip_zero_click = 1")) |
- return false; |
- // Fall through. |
- } |
- |
- // ------------------------------------------------------------------------- |
- // DO NOT FORGET to update |kCompatibleVersionNumber| if you add a migration |
- // step that is a breaking change. This is needed so that an older version |
- // of the browser can fail with a meaningful error when opening a newer |
- // database, as opposed to failing on the first database operation. |
- // ------------------------------------------------------------------------- |
- case kCurrentVersionNumber: |
- // Already up to date. |
- meta_table_.SetVersionNumber(kCurrentVersionNumber); |
- return true; |
- default: |
- NOTREACHED(); |
- return false; |
- } |
-} |
- |
-bool LoginDatabase::InitLoginsTable() { |
- if (!db_.DoesTableExist("logins")) { |
- if (!CreateNewTable(&db_, "logins", "generation_upload_status INTEGER,")) { |
- NOTREACHED(); |
- return false; |
- } |
- if (!CreateIndexOnSignonRealm(&db_, "logins")) { |
- NOTREACHED(); |
- return false; |
- } |
- } |
- return true; |
-} |
- |
void LoginDatabase::ReportMetrics(const std::string& sync_username, |
bool custom_passphrase_sync_enabled) { |
sql::Statement s(db_.GetCachedStatement( |
@@ -856,17 +861,9 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { |
&encrypted_password) != ENCRYPTION_RESULT_SUCCESS) |
return list; |
- // You *must* change LoginTableColumns if this query changes. |
- sql::Statement s(db_.GetCachedStatement( |
- SQL_FROM_HERE, |
- "INSERT INTO logins " |
- "(origin_url, action_url, username_element, username_value, " |
- " password_element, password_value, submit_element, " |
- " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
- " scheme, password_type, possible_usernames, times_used, form_data, " |
- " date_synced, display_name, icon_url," |
- " federation_url, skip_zero_click, generation_upload_status) VALUES " |
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
+ DCHECK(!add_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, add_statement_.c_str())); |
BindAddStatement(form, encrypted_password, &s); |
db_.set_error_callback(base::Bind(&AddCallback)); |
const bool success = s.Run(); |
@@ -876,16 +873,9 @@ PasswordStoreChangeList LoginDatabase::AddLogin(const PasswordForm& form) { |
return list; |
} |
// Repeat the same statement but with REPLACE semantic. |
- s.Assign(db_.GetCachedStatement( |
- SQL_FROM_HERE, |
- "INSERT OR REPLACE INTO logins " |
- "(origin_url, action_url, username_element, username_value, " |
- " password_element, password_value, submit_element, " |
- " signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
- " scheme, password_type, possible_usernames, times_used, form_data, " |
- " date_synced, display_name, icon_url," |
- " federation_url, skip_zero_click, generation_upload_status) VALUES " |
- "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); |
+ DCHECK(!add_replace_statement_.empty()); |
+ s.Assign( |
+ db_.GetCachedStatement(SQL_FROM_HERE, add_replace_statement_.c_str())); |
BindAddStatement(form, encrypted_password, &s); |
if (s.Run()) { |
list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form)); |
@@ -906,59 +896,41 @@ PasswordStoreChangeList LoginDatabase::UpdateLogin(const PasswordForm& form) { |
#endif |
// Replacement is necessary to deal with updating imported credentials. See |
// crbug.com/349138 for details. |
- sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, |
- "UPDATE OR REPLACE logins SET " |
- "action_url = ?, " |
- "password_value = ?, " |
- "ssl_valid = ?, " |
- "preferred = ?, " |
- "possible_usernames = ?, " |
- "times_used = ?, " |
- "submit_element = ?, " |
- "date_synced = ?, " |
- "date_created = ?, " |
- "blacklisted_by_user = ?, " |
- "scheme = ?, " |
- "password_type = ?, " |
- "display_name = ?, " |
- "icon_url = ?, " |
- "federation_url = ?, " |
- "skip_zero_click = ?, " |
- "generation_upload_status = ? " |
- "WHERE origin_url = ? AND " |
- "username_element = ? AND " |
- "username_value = ? AND " |
- "password_element = ? AND " |
- "signon_realm = ?")); |
+ DCHECK(!update_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, update_statement_.c_str())); |
s.BindString(0, form.action.spec()); |
s.BindBlob(1, encrypted_password.data(), |
static_cast<int>(encrypted_password.length())); |
- s.BindInt(2, form.ssl_valid); |
- s.BindInt(3, form.preferred); |
+ s.BindString16(2, form.submit_element); |
+ s.BindInt(3, form.ssl_valid); |
+ s.BindInt(4, form.preferred); |
+ s.BindInt64(5, form.date_created.ToInternalValue()); |
+ s.BindInt(6, form.blacklisted_by_user); |
+ s.BindInt(7, form.scheme); |
+ s.BindInt(8, form.type); |
base::Pickle pickle = SerializeVector(form.other_possible_usernames); |
- s.BindBlob(4, pickle.data(), pickle.size()); |
- s.BindInt(5, form.times_used); |
- s.BindString16(6, form.submit_element); |
- s.BindInt64(7, form.date_synced.ToInternalValue()); |
- s.BindInt64(8, form.date_created.ToInternalValue()); |
- s.BindInt(9, form.blacklisted_by_user); |
- s.BindInt(10, form.scheme); |
- s.BindInt(11, form.type); |
- s.BindString16(12, form.display_name); |
- s.BindString(13, form.icon_url.spec()); |
+ s.BindBlob(9, pickle.data(), pickle.size()); |
+ s.BindInt(10, form.times_used); |
+ base::Pickle form_data_pickle; |
+ autofill::SerializeFormData(form.form_data, &form_data_pickle); |
+ s.BindBlob(11, form_data_pickle.data(), form_data_pickle.size()); |
+ s.BindInt64(12, form.date_synced.ToInternalValue()); |
+ s.BindString16(13, form.display_name); |
+ s.BindString(14, form.icon_url.spec()); |
// An empty Origin serializes as "null" which would be strange to store here. |
- s.BindString(14, form.federation_origin.unique() |
+ s.BindString(15, form.federation_origin.unique() |
? std::string() |
: form.federation_origin.Serialize()); |
- s.BindInt(15, form.skip_zero_click); |
- s.BindInt(16, form.generation_upload_status); |
+ s.BindInt(16, form.skip_zero_click); |
+ s.BindInt(17, form.generation_upload_status); |
// WHERE starts here. |
- s.BindString(17, form.origin.spec()); |
- s.BindString16(18, form.username_element); |
- s.BindString16(19, form.username_value); |
- s.BindString16(20, form.password_element); |
- s.BindString(21, form.signon_realm); |
+ s.BindString(18, form.origin.spec()); |
+ s.BindString16(19, form.username_element); |
+ s.BindString16(20, form.username_value); |
+ s.BindString16(21, form.password_element); |
+ s.BindString(22, form.signon_realm); |
if (!s.Run()) |
return PasswordStoreChangeList(); |
@@ -980,20 +952,14 @@ bool LoginDatabase::RemoveLogin(const PasswordForm& form) { |
DeleteEncryptedPassword(form); |
#endif |
// Remove a login by UNIQUE-constrained fields. |
- sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE, |
- "DELETE FROM logins WHERE " |
- "origin_url = ? AND " |
- "username_element = ? AND " |
- "username_value = ? AND " |
- "password_element = ? AND " |
- "submit_element = ? AND " |
- "signon_realm = ? ")); |
+ DCHECK(!delete_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, delete_statement_.c_str())); |
s.BindString(0, form.origin.spec()); |
s.BindString16(1, form.username_element); |
s.BindString16(2, form.username_value); |
s.BindString16(3, form.password_element); |
- s.BindString16(4, form.submit_element); |
- s.BindString(5, form.signon_realm); |
+ s.BindString(4, form.signon_realm); |
return s.Run() && db_.GetLastChangeCount() > 0; |
} |
@@ -1035,15 +1001,9 @@ bool LoginDatabase::RemoveLoginsSyncedBetween(base::Time delete_begin, |
bool LoginDatabase::GetAutoSignInLogins( |
ScopedVector<autofill::PasswordForm>* forms) const { |
DCHECK(forms); |
- sql::Statement s(db_.GetCachedStatement( |
- SQL_FROM_HERE, |
- "SELECT origin_url, action_url, username_element, username_value, " |
- "password_element, password_value, submit_element, signon_realm, " |
- "ssl_valid, preferred, date_created, blacklisted_by_user, " |
- "scheme, password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, icon_url, " |
- "federation_url, skip_zero_click, generation_upload_status FROM logins " |
- "WHERE skip_zero_click = 0 ORDER BY origin_url")); |
+ DCHECK(!autosignin_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, autosignin_statement_.c_str())); |
return StatementToForms(&s, nullptr, forms); |
} |
@@ -1133,30 +1093,27 @@ bool LoginDatabase::GetLogins( |
const PasswordForm& form, |
ScopedVector<autofill::PasswordForm>* forms) const { |
DCHECK(forms); |
- // You *must* change LoginTableColumns if this query changes. |
- std::string sql_query = |
- "SELECT origin_url, action_url, " |
- "username_element, username_value, " |
- "password_element, password_value, submit_element, " |
- "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
- "scheme, password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, icon_url, " |
- "federation_url, skip_zero_click, generation_upload_status " |
- "FROM logins WHERE signon_realm == ? "; |
const GURL signon_realm(form.signon_realm); |
std::string registered_domain = GetRegistryControlledDomain(signon_realm); |
const bool should_PSL_matching_apply = |
form.scheme == PasswordForm::SCHEME_HTML && |
ShouldPSLDomainMatchingApply(registered_domain); |
const bool should_federated_apply = form.scheme == PasswordForm::SCHEME_HTML; |
- if (should_PSL_matching_apply) |
- sql_query += "OR signon_realm REGEXP ? "; |
- if (should_federated_apply) |
- sql_query += "OR (signon_realm LIKE ? AND password_type == 2) "; |
+ DCHECK(!get_statement_.empty()); |
+ DCHECK(!get_statement_psl_.empty()); |
+ DCHECK(!get_statement_federated_.empty()); |
+ DCHECK(!get_statement_psl_federated_.empty()); |
+ const std::string* sql_query = &get_statement_; |
+ if (should_PSL_matching_apply && should_federated_apply) |
+ sql_query = &get_statement_psl_federated_; |
+ else if (should_PSL_matching_apply) |
+ sql_query = &get_statement_psl_; |
+ else if (should_federated_apply) |
+ sql_query = &get_statement_federated_; |
// TODO(nyquist) Consider usage of GetCachedStatement when |
// http://crbug.com/248608 is fixed. |
- sql::Statement s(db_.GetUniqueStatement(sql_query.c_str())); |
+ sql::Statement s(db_.GetUniqueStatement(sql_query->c_str())); |
s.BindString(0, form.signon_realm); |
int placeholder = 1; |
@@ -1208,17 +1165,9 @@ bool LoginDatabase::GetLoginsCreatedBetween( |
const base::Time end, |
ScopedVector<autofill::PasswordForm>* forms) const { |
DCHECK(forms); |
- sql::Statement s(db_.GetCachedStatement( |
- SQL_FROM_HERE, |
- "SELECT origin_url, action_url, " |
- "username_element, username_value, " |
- "password_element, password_value, submit_element, " |
- "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
- "scheme, password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, icon_url, " |
- "federation_url, skip_zero_click, generation_upload_status FROM logins " |
- "WHERE date_created >= ? AND date_created < ?" |
- "ORDER BY origin_url")); |
+ DCHECK(!created_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, created_statement_.c_str())); |
s.BindInt64(0, begin.ToInternalValue()); |
s.BindInt64(1, end.is_null() ? std::numeric_limits<int64_t>::max() |
: end.ToInternalValue()); |
@@ -1231,16 +1180,9 @@ bool LoginDatabase::GetLoginsSyncedBetween( |
const base::Time end, |
ScopedVector<autofill::PasswordForm>* forms) const { |
DCHECK(forms); |
- sql::Statement s(db_.GetCachedStatement( |
- SQL_FROM_HERE, |
- "SELECT origin_url, action_url, username_element, username_value, " |
- "password_element, password_value, submit_element, signon_realm, " |
- "ssl_valid, preferred, date_created, blacklisted_by_user, " |
- "scheme, password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, icon_url, " |
- "federation_url, skip_zero_click, generation_upload_status FROM logins " |
- "WHERE date_synced >= ? AND date_synced < ?" |
- "ORDER BY origin_url")); |
+ DCHECK(!synced_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, synced_statement_.c_str())); |
s.BindInt64(0, begin.ToInternalValue()); |
s.BindInt64(1, |
end.is_null() ? base::Time::Max().ToInternalValue() |
@@ -1263,17 +1205,9 @@ bool LoginDatabase::GetAllLoginsWithBlacklistSetting( |
bool blacklisted, |
ScopedVector<autofill::PasswordForm>* forms) const { |
DCHECK(forms); |
- // You *must* change LoginTableColumns if this query changes. |
- sql::Statement s(db_.GetCachedStatement( |
- SQL_FROM_HERE, |
- "SELECT origin_url, action_url, " |
- "username_element, username_value, " |
- "password_element, password_value, submit_element, " |
- "signon_realm, ssl_valid, preferred, date_created, blacklisted_by_user, " |
- "scheme, password_type, possible_usernames, times_used, form_data, " |
- "date_synced, display_name, icon_url, " |
- "federation_url, skip_zero_click, generation_upload_status FROM logins " |
- "WHERE blacklisted_by_user == ? ORDER BY origin_url")); |
+ DCHECK(!blacklisted_statement_.empty()); |
+ sql::Statement s( |
+ db_.GetCachedStatement(SQL_FROM_HERE, blacklisted_statement_.c_str())); |
s.BindInt(0, blacklisted ? 1 : 0); |
return StatementToForms(&s, nullptr, forms); |
@@ -1289,22 +1223,15 @@ bool LoginDatabase::DeleteAndRecreateDatabaseFile() { |
std::string LoginDatabase::GetEncryptedPassword( |
const autofill::PasswordForm& form) const { |
+ DCHECK(!encrypted_statement_.empty()); |
sql::Statement s( |
- db_.GetCachedStatement(SQL_FROM_HERE, |
- "SELECT password_value FROM logins WHERE " |
- "origin_url = ? AND " |
- "username_element = ? AND " |
- "username_value = ? AND " |
- "password_element = ? AND " |
- "submit_element = ? AND " |
- "signon_realm = ? ")); |
+ db_.GetCachedStatement(SQL_FROM_HERE, encrypted_statement_.c_str())); |
s.BindString(0, form.origin.spec()); |
s.BindString16(1, form.username_element); |
s.BindString16(2, form.username_value); |
s.BindString16(3, form.password_element); |
- s.BindString16(4, form.submit_element); |
- s.BindString(5, form.signon_realm); |
+ s.BindString(4, form.signon_realm); |
std::string encrypted_password; |
if (s.Step()) { |