Replace autofill table methods add/update/remove with set/get

On receiving an update, Chrome Sync rewrites the entire bank accounts table, thus we no longer need the individual add/update/remove methods
and instead we just need the set/get methods.

Bug: 1472125
Change-Id: I9a1c0a424d96795f44af46bb47d25f076be42b90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5252088
Commit-Queue: Siddharth Shah <siashah@chromium.org>
Reviewed-by: Rouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: Olivia Saul <jsaul@google.com>
Cr-Commit-Position: refs/heads/main@{#1258302}
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc b/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
index e6eef00..5b28049 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table.cc
@@ -533,94 +533,58 @@
   return true;
 }
 
-std::unique_ptr<BankAccount> PaymentsAutofillTable::GetMaskedBankAccount(
-    int64_t instrument_id) {
-  sql::Statement s;
-  SelectBuilder(db_, s, kMaskedBankAccountsTable,
-                {kBankName, kAccountNumberSuffix, kAccountType, kNickname,
-                 kDisplayIconUrl},
-                "WHERE instrument_id = ?");
-  s.BindInt64(0, instrument_id);
-
-  if (!s.Step()) {
-    return nullptr;
-  }
-  int index = 0;
-  auto bank_name = s.ColumnString16(index++);
-  auto account_number_suffix = s.ColumnString16(index++);
-  int account_type = s.ColumnInt(index++);
-  auto nickname = s.ColumnString16(index++);
-  auto display_icon_url = s.ColumnString16(index++);
-  if (account_type >
-          static_cast<int>(BankAccount::AccountType::kTransactingAccount) ||
-      account_type < static_cast<int>(BankAccount::AccountType::kUnknown)) {
-    return nullptr;
-  }
-  return std::make_unique<BankAccount>(
-      instrument_id, nickname, GURL(display_icon_url), bank_name,
-      account_number_suffix,
-      static_cast<BankAccount::AccountType>(account_type));
-}
-
-bool PaymentsAutofillTable::AddMaskedBankAccount(
-    const BankAccount& bank_account) {
+bool PaymentsAutofillTable::SetMaskedBankAccounts(
+    const std::vector<BankAccount>& bank_accounts) {
   sql::Transaction transaction(db_);
   if (!transaction.Begin()) {
     return false;
   }
 
-  // Add bank account.
+  // Deletes all old values.
+  Delete(db_, kMaskedBankAccountsTable);
+
+  // Add bank accounts.
   sql::Statement insert;
   InsertBuilder(db_, insert, kMaskedBankAccountsTable,
                 {kInstrumentId, kBankName, kAccountNumberSuffix, kAccountType,
                  kNickname, kDisplayIconUrl});
-  BindMaskedBankAccountToStatement(bank_account, &insert);
-  if (!insert.Run()) {
-    return false;
+  for (BankAccount bank_account : bank_accounts) {
+    BindMaskedBankAccountToStatement(bank_account, &insert);
+    if (!insert.Run()) {
+      return false;
+    }
+    insert.Reset(/*clear_bound_vars=*/true);
   }
-
   return transaction.Commit();
 }
 
-bool PaymentsAutofillTable::UpdateMaskedBankAccount(
-    const BankAccount& bank_account) {
-  sql::Transaction transaction(db_);
-  if (!transaction.Begin()) {
-    return false;
-  }
+bool PaymentsAutofillTable::GetMaskedBankAccounts(
+    std::vector<std::unique_ptr<BankAccount>>& bank_accounts) {
+  sql::Statement s;
+  bank_accounts.clear();
 
-  // Update bank account.
-  sql::Statement update;
-  UpdateBuilder(db_, update, kMaskedBankAccountsTable,
+  SelectBuilder(db_, s, kMaskedBankAccountsTable,
                 {kInstrumentId, kBankName, kAccountNumberSuffix, kAccountType,
-                 kNickname, kDisplayIconUrl},
-                base::StrCat({kInstrumentId, "=?1"}));
-  BindMaskedBankAccountToStatement(bank_account, &update);
-  if (!update.Run()) {
-    return false;
+                 kNickname, kDisplayIconUrl});
+  while (s.Step()) {
+    int index = 0;
+    auto instrument_id = s.ColumnInt64(index++);
+    auto bank_name = s.ColumnString16(index++);
+    auto account_number_suffix = s.ColumnString16(index++);
+    int account_type = s.ColumnInt(index++);
+    auto nickname = s.ColumnString16(index++);
+    auto display_icon_url = s.ColumnString16(index++);
+    if (account_type >
+            static_cast<int>(BankAccount::AccountType::kTransactingAccount) ||
+        account_type < static_cast<int>(BankAccount::AccountType::kUnknown)) {
+      continue;
+    }
+    bank_accounts.push_back(std::make_unique<BankAccount>(
+        instrument_id, nickname, GURL(display_icon_url), bank_name,
+        account_number_suffix,
+        static_cast<BankAccount::AccountType>(account_type)));
   }
-
-  return transaction.Commit();
-}
-
-bool PaymentsAutofillTable::RemoveMaskedBankAccount(
-    const BankAccount& bank_account) {
-  sql::Transaction transaction(db_);
-  if (!transaction.Begin()) {
-    return false;
-  }
-
-  sql::Statement bank_accounts_delete;
-  DeleteBuilder(db_, bank_accounts_delete, kMaskedBankAccountsTable,
-                base::StrCat({kInstrumentId, "=?"}));
-  bank_accounts_delete.BindInt64(
-      0, bank_account.payment_instrument().instrument_id());
-  if (!bank_accounts_delete.Run()) {
-    return false;
-  }
-  // TODO(crbug.com/1475426) : Delete row from `masked_bank_accounts_metadata`
-  // table.
-  return transaction.Commit();
+  return s.Succeeded();
 }
 
 bool PaymentsAutofillTable::AddLocalIban(const Iban& iban) {
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table.h b/components/autofill/core/browser/webdata/payments/payments_autofill_table.h
index 3fc71b1..0b269252 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table.h
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table.h
@@ -352,16 +352,12 @@
   bool CreateTablesIfNecessary() override;
   bool MigrateToVersion(int version, bool* update_compatible_version) override;
 
-  // Records a single BankAccount in the bank accounts table. Returns true if
-  // the BankAccount was successfully added to the database.
-  bool AddMaskedBankAccount(const BankAccount& bank_account);
-  // Returns true if the BankAccount was successfully updated in the database.
-  bool UpdateMaskedBankAccount(const BankAccount& bank_account);
-  // Delete the bank account from the database.
-  bool RemoveMaskedBankAccount(const BankAccount& bank_account);
-  // Retrieve the data from the `masked_bank_accounts` table and return a
-  // BankAccount object.
-  std::unique_ptr<BankAccount> GetMaskedBankAccount(int64_t instrument_id);
+  // Rewrites the bank accounts table. Returns true if all bank accounts were
+  // successfully added to the database.
+  bool SetMaskedBankAccounts(const std::vector<BankAccount>& bank_accounts);
+  // Retrieve all bank accounts from the database.
+  bool GetMaskedBankAccounts(
+      std::vector<std::unique_ptr<BankAccount>>& bank_accounts);
 
   // Records a single IBAN in the local_ibans table.
   bool AddLocalIban(const Iban& iban);
diff --git a/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc b/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc
index c3e728cd..85c39dc6 100644
--- a/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc
+++ b/components/autofill/core/browser/webdata/payments/payments_autofill_table_unittest.cc
@@ -1590,89 +1590,103 @@
   EXPECT_TRUE(usage_data.empty());
 }
 
-TEST_F(PaymentsAutofillTableTest, AddMaskedBankAccount) {
-  BankAccount bank_account_to_store_1 = test::CreatePixBankAccount(100);
-  BankAccount bank_account_to_store_2 = test::CreatePixBankAccount(200);
-  table_.get()->AddMaskedBankAccount(bank_account_to_store_1);
-  table_.get()->AddMaskedBankAccount(bank_account_to_store_2);
+TEST_F(PaymentsAutofillTableTest, GetMaskedBankAccounts) {
+  // Populate masked_bank_accounts table.
+  ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+      "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
+      "account_number_suffix, account_type, nickname, display_icon_url) "
+      "VALUES(100, 'bank_name', 'account_number_suffix', 1, 'nickname', "
+      "'http://display-icon-url.com');"
+      "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
+      "account_number_suffix, account_type, nickname, display_icon_url) "
+      "VALUES(200, 'bank_name_2', 'account_number_suffix_2', 3, 'nickname_2', "
+      "'http://display-icon-url2.com');"));
 
-  // Verify bank account 1 is correctly retrieved.
-  std::unique_ptr<BankAccount> bank_account_from_db_1 =
-      table_->GetMaskedBankAccount(
-          bank_account_to_store_1.payment_instrument().instrument_id());
+  std::vector<std::unique_ptr<BankAccount>> bank_accounts_from_db;
+  table_->GetMaskedBankAccounts(bank_accounts_from_db);
 
-  ASSERT_TRUE(bank_account_from_db_1);
-  EXPECT_EQ(bank_account_to_store_1, *bank_account_from_db_1);
+  EXPECT_EQ(2u, bank_accounts_from_db.size());
 
-  // Verify bank account 2 is correctly retrieved.
-  std::unique_ptr<BankAccount> bank_account_from_db_2 =
-      table_->GetMaskedBankAccount(
-          bank_account_to_store_2.payment_instrument().instrument_id());
-
-  ASSERT_TRUE(bank_account_from_db_2);
-  EXPECT_EQ(bank_account_to_store_2, *bank_account_from_db_2);
-}
-
-TEST_F(PaymentsAutofillTableTest, UpdateMaskedBankAccount) {
-  BankAccount bank_account_to_store = test::CreatePixBankAccount(100);
-  ASSERT_TRUE(table_.get()->AddMaskedBankAccount(bank_account_to_store));
-
-  BankAccount updated_bank_account_to_store(
-      100, u"updated_nickname", GURL("http://www.updated-example.com"),
-      u"updated_bank_name", u"updated_account_number_suffix",
-      BankAccount::AccountType::kSalary);
-  ASSERT_TRUE(
-      table_.get()->UpdateMaskedBankAccount(updated_bank_account_to_store));
-
-  std::unique_ptr<BankAccount> bank_account_from_db =
-      table_->GetMaskedBankAccount(
-          bank_account_to_store.payment_instrument().instrument_id());
-
-  ASSERT_TRUE(bank_account_from_db);
-  EXPECT_EQ(updated_bank_account_to_store, *bank_account_from_db);
-}
-
-TEST_F(PaymentsAutofillTableTest, RemoveMaskedBankAccount) {
-  BankAccount bank_account_to_store = test::CreatePixBankAccount(100);
-  ASSERT_TRUE(table_.get()->AddMaskedBankAccount(bank_account_to_store));
-
-  // Remove bank account from db.
-  ASSERT_TRUE(table_.get()->RemoveMaskedBankAccount(bank_account_to_store));
-
-  // Verify row is deleted from bank_accounts table.
-  sql::Statement bank_accounts_select(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "SELECT COUNT(*) "
-          "FROM masked_bank_accounts WHERE instrument_id = ?"));
-  bank_accounts_select.BindInt64(
-      0, bank_account_to_store.payment_instrument().instrument_id());
-  EXPECT_TRUE(bank_accounts_select.Step());
-  EXPECT_EQ(0, bank_accounts_select.ColumnInt(0));
-}
-
-TEST_F(PaymentsAutofillTableTest, GetPaymentInstrument) {
-  sql::Statement bank_accounts_insert(
-      db_->GetSQLConnection()->GetUniqueStatement(
-          "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
-          "account_number_suffix, account_type, nickname, display_icon_url) "
-          "VALUES(100, 'bank_name', "
-          "'account_number_suffix', 1, 'nickname', "
-          "'http://display-icon-url.com')"));
-  EXPECT_TRUE(bank_accounts_insert.Run());
-
-  std::unique_ptr<BankAccount> bank_account_from_db =
-      table_->GetMaskedBankAccount(100);
-
-  ASSERT_TRUE(bank_account_from_db);
-  EXPECT_EQ(100, bank_account_from_db->payment_instrument().instrument_id());
-  EXPECT_EQ(u"nickname", bank_account_from_db->payment_instrument().nickname());
-  EXPECT_EQ(static_cast<BankAccount::AccountType>(1),
-            bank_account_from_db->account_type());
+  BankAccount bank_account_from_db_1 = *bank_accounts_from_db.at(0).get();
+  EXPECT_EQ(100, bank_account_from_db_1.payment_instrument().instrument_id());
+  EXPECT_EQ(u"bank_name", bank_account_from_db_1.bank_name());
   EXPECT_EQ(u"account_number_suffix",
-            bank_account_from_db->account_number_suffix());
+            bank_account_from_db_1.account_number_suffix());
+  EXPECT_EQ(static_cast<BankAccount::AccountType>(1),
+            bank_account_from_db_1.account_type());
+  EXPECT_EQ(u"nickname",
+            bank_account_from_db_1.payment_instrument().nickname());
   EXPECT_EQ(GURL("http://display-icon-url.com"),
-            bank_account_from_db->payment_instrument().display_icon_url());
-  EXPECT_EQ(u"bank_name", bank_account_from_db->bank_name());
+            bank_account_from_db_1.payment_instrument().display_icon_url());
+
+  BankAccount bank_account_from_db_2 = *bank_accounts_from_db.at(1).get();
+  EXPECT_EQ(200, bank_account_from_db_2.payment_instrument().instrument_id());
+  EXPECT_EQ(u"bank_name_2", bank_account_from_db_2.bank_name());
+  EXPECT_EQ(u"account_number_suffix_2",
+            bank_account_from_db_2.account_number_suffix());
+  EXPECT_EQ(static_cast<BankAccount::AccountType>(3),
+            bank_account_from_db_2.account_type());
+  EXPECT_EQ(u"nickname_2",
+            bank_account_from_db_2.payment_instrument().nickname());
+  EXPECT_EQ(GURL("http://display-icon-url2.com"),
+            bank_account_from_db_2.payment_instrument().display_icon_url());
+}
+
+TEST_F(PaymentsAutofillTableTest,
+       GetMaskedBankAccounts_BankAccountTypeOutOfBounds) {
+  // Populate masked_bank_accounts table with the first row to have an invalid
+  // bank account type with value 100.
+  ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+      "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
+      "account_number_suffix, account_type, nickname, display_icon_url) "
+      "VALUES(100, 'bank_name', 'account_number_suffix', 100, 'nickname', "
+      "'http://display-icon-url.com');"
+      "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
+      "account_number_suffix, account_type, nickname, display_icon_url) "
+      "VALUES(200, 'bank_name_2', 'account_number_suffix_2', 3, 'nickname_2', "
+      "'http://display-icon-url2.com');"));
+
+  std::vector<std::unique_ptr<BankAccount>> bank_accounts_from_db;
+  table_->GetMaskedBankAccounts(bank_accounts_from_db);
+
+  // Expect only one bank account since the other one has an invalid bank
+  // account type.
+  EXPECT_EQ(1u, bank_accounts_from_db.size());
+  // Verify that the returned bank account maps to the second row in the table.
+  BankAccount bank_account_from_db = *bank_accounts_from_db.at(0).get();
+  EXPECT_EQ(200, bank_account_from_db.payment_instrument().instrument_id());
+}
+
+TEST_F(PaymentsAutofillTableTest, SetMaskedBankAccounts) {
+  ASSERT_TRUE(db_->GetSQLConnection()->Execute(
+      "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
+      "account_number_suffix, account_type, nickname, display_icon_url) "
+      "VALUES(100, 'bank_name', 'account_number_suffix', 1, 'nickname', "
+      "'http://display-icon-url.com');"
+      "INSERT INTO masked_bank_accounts (instrument_id, bank_name, "
+      "account_number_suffix, account_type, nickname, display_icon_url) "
+      "VALUES(200, 'bank_name_2', 'account_number_suffix_2', 3, 'nickname_2', "
+      "'http://display-icon-url2.com');"));
+
+  // Verify that GetMaskedBankAccounts returns 2 bank accounts.
+  std::vector<std::unique_ptr<BankAccount>> bank_accounts_from_db;
+  table_->GetMaskedBankAccounts(bank_accounts_from_db);
+  EXPECT_EQ(2u, bank_accounts_from_db.size());
+
+  // Create bank account with different id from the ones above.
+  BankAccount bank_account_to_store = test::CreatePixBankAccount(8000);
+  std::vector<BankAccount> bank_accounts_to_store;
+  bank_accounts_to_store.push_back(bank_account_to_store);
+  table_->SetMaskedBankAccounts(bank_accounts_to_store);
+
+  // Verify that GetMaskedBankAccounts returns 1 bank account.
+  table_->GetMaskedBankAccounts(bank_accounts_from_db);
+  EXPECT_EQ(1u, bank_accounts_from_db.size());
+
+  // Verify that the instrument id of the returned bank account matches the one
+  // that was stored.
+  BankAccount bank_account_from_db = *bank_accounts_from_db.at(0).get();
+  EXPECT_EQ(8000, bank_account_from_db.payment_instrument().instrument_id());
 }
 
 }  // namespace autofill