Commit b868465e authored by sauski's avatar sauski Committed by Commit Bot

Access Context Auditing: Inline table names into queries

CL stops appending table names into queries and inlines them directly
instead.

Bug: 1117381
Change-Id: I4cb5adc389f1685606ffa519383493a594e6bfac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2485061Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Commit-Queue: Theodore Olsauskas-Warren <sauski@google.com>
Cr-Commit-Position: refs/heads/master@{#819261}
parent 163e41de
...@@ -19,8 +19,6 @@ namespace { ...@@ -19,8 +19,6 @@ namespace {
const base::FilePath::CharType kDatabaseName[] = const base::FilePath::CharType kDatabaseName[] =
FILE_PATH_LITERAL("AccessContextAudit"); FILE_PATH_LITERAL("AccessContextAudit");
const char kCookieTableName[] = "cookies";
const char kStorageAPITableName[] = "originStorageAPIs";
static const int kVersionNumber = 1; static const int kVersionNumber = 1;
// Callback that is fired upon an SQLite error, razes the database if the error // Callback that is fired upon an SQLite error, razes the database if the error
...@@ -54,10 +52,9 @@ void DatabaseErrorCallback(sql::Database* db, ...@@ -54,10 +52,9 @@ void DatabaseErrorCallback(sql::Database* db,
// Returns true if a cookie table already exists in |db|, but is missing the // Returns true if a cookie table already exists in |db|, but is missing the
// is_persistent field. // is_persistent field.
bool CookieTableMissingIsPersistent(sql::Database* db) { bool CookieTableMissingIsPersistent(sql::Database* db) {
std::string select = "SELECT sql FROM sqlite_master WHERE name = '"; const char kSelectCookieTable[] =
select.append(kCookieTableName); "SELECT sql FROM sqlite_master WHERE name = 'cookies' AND type = 'table'";
select.append("' AND type = 'table'"); sql::Statement statement(db->GetUniqueStatement(kSelectCookieTable));
sql::Statement statement(db->GetUniqueStatement(select.c_str()));
// Unable to step implies cookies table does not exist. // Unable to step implies cookies table does not exist.
if (!statement.Step()) if (!statement.Step())
...@@ -69,10 +66,9 @@ bool CookieTableMissingIsPersistent(sql::Database* db) { ...@@ -69,10 +66,9 @@ bool CookieTableMissingIsPersistent(sql::Database* db) {
// Removes all cookie records in |db| with is_persistent = false. // Removes all cookie records in |db| with is_persistent = false.
bool DeleteNonPersistentCookies(sql::Database* db) { bool DeleteNonPersistentCookies(sql::Database* db) {
std::string remove = "DELETE FROM "; const char kRemoveNonPersistent[] =
remove.append(kCookieTableName); "DELETE FROM cookies WHERE is_persistent != 1";
remove.append(" WHERE is_persistent != 1"); return db->Execute(kRemoveNonPersistent);
return db->Execute(remove.c_str());
} }
bool IsContentSettingSessionOnly( bool IsContentSettingSessionOnly(
...@@ -192,38 +188,33 @@ bool AccessContextAuditDatabase::InitializeSchema() { ...@@ -192,38 +188,33 @@ bool AccessContextAuditDatabase::InitializeSchema() {
// Simply remove the table in this case. Due to a flag misconfiguration this // Simply remove the table in this case. Due to a flag misconfiguration this
// version of the table was pushed to all canary users for a short period. // version of the table was pushed to all canary users for a short period.
// TODO(crbug.com/1102006): Remove this code before M86 branch point. // TODO(crbug.com/1102006): Remove this code before M86 branch point.
std::string drop_table = "DROP TABLE "; const char kDropCookiesTable[] = "DROP TABLE cookies";
drop_table.append(kCookieTableName); if (!db_.Execute(kDropCookiesTable))
if (!db_.Execute(drop_table.c_str()))
return false; return false;
} }
std::string create_table; const char kCreateCookiesTable[] =
create_table.append("CREATE TABLE IF NOT EXISTS "); "CREATE TABLE IF NOT EXISTS cookies "
create_table.append(kCookieTableName);
create_table.append(
"(top_frame_origin TEXT NOT NULL," "(top_frame_origin TEXT NOT NULL,"
"name TEXT NOT NULL," "name TEXT NOT NULL,"
"domain TEXT NOT NULL," "domain TEXT NOT NULL,"
"path TEXT NOT NULL," "path TEXT NOT NULL,"
"access_utc INTEGER NOT NULL," "access_utc INTEGER NOT NULL,"
"is_persistent INTEGER NOT NULL," "is_persistent INTEGER NOT NULL,"
"PRIMARY KEY (top_frame_origin, name, domain, path))"); "PRIMARY KEY (top_frame_origin, name, domain, path))";
if (!db_.Execute(create_table.c_str())) if (!db_.Execute(kCreateCookiesTable))
return false; return false;
create_table.clear(); const char kCreateStorageApiTable[] =
create_table.append("CREATE TABLE IF NOT EXISTS "); "CREATE TABLE IF NOT EXISTS originStorageAPIs"
create_table.append(kStorageAPITableName);
create_table.append(
"(top_frame_origin TEXT NOT NULL," "(top_frame_origin TEXT NOT NULL,"
"type INTEGER NOT NULL," "type INTEGER NOT NULL,"
"origin TEXT NOT NULL," "origin TEXT NOT NULL,"
"access_utc INTEGER NOT NULL," "access_utc INTEGER NOT NULL,"
"PRIMARY KEY (top_frame_origin, origin, type))"); "PRIMARY KEY (top_frame_origin, origin, type))";
return db_.Execute(create_table.c_str()); return db_.Execute(kCreateStorageApiTable);
} }
void AccessContextAuditDatabase::ComputeDatabaseMetrics() { void AccessContextAuditDatabase::ComputeDatabaseMetrics() {
...@@ -286,23 +277,19 @@ void AccessContextAuditDatabase::AddRecords( ...@@ -286,23 +277,19 @@ void AccessContextAuditDatabase::AddRecords(
// Create both insert statements ahead of iterating over records. These are // Create both insert statements ahead of iterating over records. These are
// highly likely to both be used, and should be in the statement cache. // highly likely to both be used, and should be in the statement cache.
std::string insert; const char kInsertCookieRecord[] =
insert.append("INSERT OR REPLACE INTO "); "INSERT OR REPLACE INTO cookies "
insert.append(kCookieTableName);
insert.append(
"(top_frame_origin, name, domain, path, access_utc, is_persistent) " "(top_frame_origin, name, domain, path, access_utc, is_persistent) "
"VALUES (?, ?, ?, ?, ?, ?)"); "VALUES (?, ?, ?, ?, ?, ?)";
sql::Statement insert_cookie( sql::Statement insert_cookie(
db_.GetCachedStatement(SQL_FROM_HERE, insert.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kInsertCookieRecord));
insert.clear(); const char kInsertStorageApiRecord[] =
insert.append("INSERT OR REPLACE INTO "); "INSERT OR REPLACE INTO originStorageAPIs"
insert.append(kStorageAPITableName);
insert.append(
"(top_frame_origin, type, origin, access_utc) " "(top_frame_origin, type, origin, access_utc) "
"VALUES (?, ?, ?, ?)"); "VALUES (?, ?, ?, ?)";
sql::Statement insert_storage_api( sql::Statement insert_storage_api(
db_.GetCachedStatement(SQL_FROM_HERE, insert.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kInsertStorageApiRecord));
for (const auto& record : records) { for (const auto& record : records) {
if (record.type == StorageAPIType::kCookie) { if (record.type == StorageAPIType::kCookie) {
...@@ -340,23 +327,22 @@ void AccessContextAuditDatabase::AddRecords( ...@@ -340,23 +327,22 @@ void AccessContextAuditDatabase::AddRecords(
void AccessContextAuditDatabase::RemoveRecord(const AccessRecord& record) { void AccessContextAuditDatabase::RemoveRecord(const AccessRecord& record) {
sql::Statement remove_statement; sql::Statement remove_statement;
std::string remove;
remove.append("DELETE FROM ");
if (record.type == StorageAPIType::kCookie) { if (record.type == StorageAPIType::kCookie) {
remove.append(kCookieTableName); const char kRemoveCookieRecord[] =
remove.append( "DELETE FROM cookies WHERE top_frame_origin = ? AND name = ? AND "
" WHERE top_frame_origin = ? AND name = ? AND domain = ? AND path = ?"); "domain = ? AND path = ?";
remove_statement.Assign( remove_statement.Assign(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveCookieRecord));
remove_statement.BindString(0, record.top_frame_origin.Serialize()); remove_statement.BindString(0, record.top_frame_origin.Serialize());
remove_statement.BindString(1, record.name); remove_statement.BindString(1, record.name);
remove_statement.BindString(2, record.domain); remove_statement.BindString(2, record.domain);
remove_statement.BindString(3, record.path); remove_statement.BindString(3, record.path);
} else { } else {
remove.append(kStorageAPITableName); const char kRemoveStorageApiRecord[] =
remove.append(" WHERE top_frame_origin = ? AND type = ? AND origin = ?"); "DELETE FROM originStorageAPIs WHERE top_frame_origin = ? AND type = ? "
"AND origin = ?";
remove_statement.Assign( remove_statement.Assign(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveStorageApiRecord));
remove_statement.BindString(0, record.top_frame_origin.Serialize()); remove_statement.BindString(0, record.top_frame_origin.Serialize());
remove_statement.BindInt(1, static_cast<int>(record.type)); remove_statement.BindInt(1, static_cast<int>(record.type));
remove_statement.BindString(2, record.origin.Serialize()); remove_statement.BindString(2, record.origin.Serialize());
...@@ -371,14 +357,12 @@ void AccessContextAuditDatabase::RemoveAllRecords() { ...@@ -371,14 +357,12 @@ void AccessContextAuditDatabase::RemoveAllRecords() {
if (!transaction.Begin()) if (!transaction.Begin())
return; return;
std::string delete_cookies_table = "DELETE FROM "; const char kClearCookiesTable[] = "DELETE FROM cookies";
delete_cookies_table.append(kCookieTableName); if (!db_.Execute(kClearCookiesTable))
if (!db_.Execute(delete_cookies_table.c_str()))
return; return;
std::string delete_storage_api_table = "DELETE FROM "; const char kClearStorageApiTable[] = "DELETE FROM originStorageAPIs";
delete_storage_api_table.append(kStorageAPITableName); if (!db_.Execute(kClearStorageApiTable))
if (!db_.Execute(delete_storage_api_table.c_str()))
return; return;
transaction.Commit(); transaction.Commit();
...@@ -390,22 +374,20 @@ void AccessContextAuditDatabase::RemoveAllRecordsForTimeRange(base::Time begin, ...@@ -390,22 +374,20 @@ void AccessContextAuditDatabase::RemoveAllRecordsForTimeRange(base::Time begin,
if (!transaction.Begin()) if (!transaction.Begin())
return; return;
std::string remove = "DELETE FROM "; const char kRemoveCookieRecords[] =
remove.append(kCookieTableName); "DELETE FROM cookies WHERE access_utc BETWEEN ? AND ?";
remove.append(" WHERE access_utc BETWEEN ? AND ?");
sql::Statement remove_cookies( sql::Statement remove_cookies(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveCookieRecords));
remove_cookies.BindInt64(0, remove_cookies.BindInt64(0,
begin.ToDeltaSinceWindowsEpoch().InMicroseconds()); begin.ToDeltaSinceWindowsEpoch().InMicroseconds());
remove_cookies.BindInt64(1, end.ToDeltaSinceWindowsEpoch().InMicroseconds()); remove_cookies.BindInt64(1, end.ToDeltaSinceWindowsEpoch().InMicroseconds());
if (!remove_cookies.Run()) if (!remove_cookies.Run())
return; return;
remove = "DELETE FROM "; const char kRemoveStorageApiRecords[] =
remove.append(kStorageAPITableName); "DELETE FROM originStorageAPIs WHERE access_utc BETWEEN ? AND ?";
remove.append(" WHERE access_utc BETWEEN ? AND ?");
sql::Statement remove_storage_apis( sql::Statement remove_storage_apis(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveStorageApiRecords));
remove_storage_apis.BindInt64( remove_storage_apis.BindInt64(
0, begin.ToDeltaSinceWindowsEpoch().InMicroseconds()); 0, begin.ToDeltaSinceWindowsEpoch().InMicroseconds());
remove_storage_apis.BindInt64( remove_storage_apis.BindInt64(
...@@ -441,10 +423,9 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords( ...@@ -441,10 +423,9 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords(
// Extract the set of all domains from the cookies table, determine the // Extract the set of all domains from the cookies table, determine the
// effective content setting, and store for removal if appropriate. // effective content setting, and store for removal if appropriate.
std::string select = "SELECT DISTINCT domain FROM "; const char kSelectCookieDomains[] = "SELECT DISTINCT domain FROM cookies";
select.append(kCookieTableName);
sql::Statement select_cookie_domains( sql::Statement select_cookie_domains(
db_.GetCachedStatement(SQL_FROM_HERE, select.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kSelectCookieDomains));
std::vector<std::string> cookie_domains_for_removal; std::vector<std::string> cookie_domains_for_removal;
while (select_cookie_domains.Step()) { while (select_cookie_domains.Step()) {
...@@ -460,10 +441,10 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords( ...@@ -460,10 +441,10 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords(
} }
// Repeat the above, but for the origin keyed storage API table. // Repeat the above, but for the origin keyed storage API table.
select = "SELECT DISTINCT origin FROM "; const char kSelectStorageOrigins[] =
select.append(kStorageAPITableName); "SELECT DISTINCT origin FROM originStorageAPIs";
sql::Statement select_storage_origins( sql::Statement select_storage_origins(
db_.GetCachedStatement(SQL_FROM_HERE, select.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kSelectStorageOrigins));
std::vector<std::string> storage_origins_for_removal; std::vector<std::string> storage_origins_for_removal;
while (select_storage_origins.Step()) { while (select_storage_origins.Step()) {
...@@ -474,11 +455,9 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords( ...@@ -474,11 +455,9 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords(
// Remove entries belonging to cookie domains and origins identified as having // Remove entries belonging to cookie domains and origins identified as having
// a SESSION_ONLY content setting. // a SESSION_ONLY content setting.
std::string remove = "DELETE FROM "; const char kRemoveCookieRecords[] = "DELETE FROM cookies WHERE domain = ?";
remove.append(kCookieTableName);
remove.append(" WHERE domain = ?");
sql::Statement remove_cookies( sql::Statement remove_cookies(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveCookieRecords));
for (const auto& domain : cookie_domains_for_removal) { for (const auto& domain : cookie_domains_for_removal) {
remove_cookies.BindString(0, domain); remove_cookies.BindString(0, domain);
...@@ -487,11 +466,10 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords( ...@@ -487,11 +466,10 @@ void AccessContextAuditDatabase::RemoveSessionOnlyRecords(
remove_cookies.Reset(true); remove_cookies.Reset(true);
} }
remove = "DELETE FROM "; const char kRemoveStorageApiRecords[] =
remove.append(kStorageAPITableName); "DELETE FROM originStorageAPIs WHERE origin = ?";
remove.append(" WHERE origin = ?");
sql::Statement remove_storage_apis( sql::Statement remove_storage_apis(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveStorageApiRecords));
for (const auto& origin : storage_origins_for_removal) { for (const auto& origin : storage_origins_for_removal) {
remove_storage_apis.BindString(0, origin); remove_storage_apis.BindString(0, origin);
...@@ -507,12 +485,10 @@ void AccessContextAuditDatabase::RemoveAllRecordsForCookie( ...@@ -507,12 +485,10 @@ void AccessContextAuditDatabase::RemoveAllRecordsForCookie(
const std::string& name, const std::string& name,
const std::string& domain, const std::string& domain,
const std::string& path) { const std::string& path) {
std::string remove; const char kRemoveCookieRecords[] =
remove.append("DELETE FROM "); "DELETE FROM cookies WHERE name = ? AND domain = ? AND path = ?";
remove.append(kCookieTableName);
remove.append(" WHERE name = ? AND domain = ? AND path = ?");
sql::Statement remove_statement( sql::Statement remove_statement(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveCookieRecords));
remove_statement.BindString(0, name); remove_statement.BindString(0, name);
remove_statement.BindString(1, domain); remove_statement.BindString(1, domain);
remove_statement.BindString(2, path); remove_statement.BindString(2, path);
...@@ -522,12 +498,10 @@ void AccessContextAuditDatabase::RemoveAllRecordsForCookie( ...@@ -522,12 +498,10 @@ void AccessContextAuditDatabase::RemoveAllRecordsForCookie(
void AccessContextAuditDatabase::RemoveAllRecordsForOriginKeyedStorage( void AccessContextAuditDatabase::RemoveAllRecordsForOriginKeyedStorage(
const url::Origin& origin, const url::Origin& origin,
StorageAPIType type) { StorageAPIType type) {
std::string remove; const char kRemoveStorageApiRecords[] =
remove.append("DELETE FROM "); "DELETE FROM originStorageAPIs WHERE origin = ? AND type = ?";
remove.append(kStorageAPITableName);
remove.append(" WHERE origin = ? AND type = ?");
sql::Statement remove_statement( sql::Statement remove_statement(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveStorageApiRecords));
remove_statement.BindString(0, origin.Serialize()); remove_statement.BindString(0, origin.Serialize());
remove_statement.BindInt(1, static_cast<int>(type)); remove_statement.BindInt(1, static_cast<int>(type));
remove_statement.Run(); remove_statement.Run();
...@@ -541,17 +515,15 @@ void AccessContextAuditDatabase::RemoveAllRecordsForTopFrameOrigins( ...@@ -541,17 +515,15 @@ void AccessContextAuditDatabase::RemoveAllRecordsForTopFrameOrigins(
// Remove all records with a top frame origin present in |origins| from both // Remove all records with a top frame origin present in |origins| from both
// the cookies and storage API tables. // the cookies and storage API tables.
std::string remove = "DELETE FROM "; const char kRemoveTopFrameFromCookies[] =
remove.append(kCookieTableName); "DELETE FROM cookies WHERE top_frame_origin = ?";
remove.append(" WHERE top_frame_origin = ?");
sql::Statement remove_cookies( sql::Statement remove_cookies(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveTopFrameFromCookies));
remove = "DELETE FROM "; const char kRemoveTopFrameFromStorageApis[] =
remove.append(kStorageAPITableName); "DELETE FROM originStorageAPIs WHERE top_frame_origin = ?";
remove.append(" WHERE top_frame_origin = ?");
sql::Statement remove_storage_apis( sql::Statement remove_storage_apis(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kRemoveTopFrameFromStorageApis));
for (const auto& origin : origins) { for (const auto& origin : origins) {
remove_storage_apis.BindString(0, origin.Serialize()); remove_storage_apis.BindString(0, origin.Serialize());
...@@ -572,13 +544,11 @@ std::vector<AccessContextAuditDatabase::AccessRecord> ...@@ -572,13 +544,11 @@ std::vector<AccessContextAuditDatabase::AccessRecord>
AccessContextAuditDatabase::GetAllRecords() { AccessContextAuditDatabase::GetAllRecords() {
std::vector<AccessContextAuditDatabase::AccessRecord> records; std::vector<AccessContextAuditDatabase::AccessRecord> records;
std::string select; const char kSelectCookieRecords[] =
select.append(
"SELECT top_frame_origin, name, domain, path, access_utc, is_persistent " "SELECT top_frame_origin, name, domain, path, access_utc, is_persistent "
"FROM "); "FROM cookies";
select.append(kCookieTableName);
sql::Statement select_cookies( sql::Statement select_cookies(
db_.GetCachedStatement(SQL_FROM_HERE, select.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kSelectCookieRecords));
while (select_cookies.Step()) { while (select_cookies.Step()) {
records.emplace_back( records.emplace_back(
...@@ -590,11 +560,11 @@ AccessContextAuditDatabase::GetAllRecords() { ...@@ -590,11 +560,11 @@ AccessContextAuditDatabase::GetAllRecords() {
select_cookies.ColumnBool(5)); select_cookies.ColumnBool(5));
} }
select.clear(); const char kSelectStorageApiRecords[] =
select.append("SELECT top_frame_origin, type, origin, access_utc FROM "); "SELECT top_frame_origin, type, origin, access_utc FROM "
select.append(kStorageAPITableName); "originStorageAPIs";
sql::Statement select_storage_api( sql::Statement select_storage_api(
db_.GetCachedStatement(SQL_FROM_HERE, select.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kSelectStorageApiRecords));
while (select_storage_api.Step()) { while (select_storage_api.Step()) {
records.emplace_back( records.emplace_back(
...@@ -624,11 +594,11 @@ void AccessContextAuditDatabase::RemoveStorageApiRecords( ...@@ -624,11 +594,11 @@ void AccessContextAuditDatabase::RemoveStorageApiRecords(
// The number of records retrieved is sub-optimal by at most a factor of // The number of records retrieved is sub-optimal by at most a factor of
// StorageAPIType::kMaxType, so we're not missing sub-linear optimization // StorageAPIType::kMaxType, so we're not missing sub-linear optimization
// opportunity here. // opportunity here.
std::string select = "SELECT origin, type FROM "; const char kSelectOriginAndType[] =
select.append(kStorageAPITableName); "SELECT origin, type FROM originStorageAPIs WHERE access_utc BETWEEN ? "
select.append(" WHERE access_utc BETWEEN ? AND ?"); "AND ?";
sql::Statement select_storage_api( sql::Statement select_storage_api(
db_.GetCachedStatement(SQL_FROM_HERE, select.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kSelectOriginAndType));
select_storage_api.BindInt64( select_storage_api.BindInt64(
0, begin.ToDeltaSinceWindowsEpoch().InMicroseconds()); 0, begin.ToDeltaSinceWindowsEpoch().InMicroseconds());
select_storage_api.BindInt64(1, select_storage_api.BindInt64(1,
...@@ -647,11 +617,10 @@ void AccessContextAuditDatabase::RemoveStorageApiRecords( ...@@ -647,11 +617,10 @@ void AccessContextAuditDatabase::RemoveStorageApiRecords(
} }
} }
std::string remove = "DELETE FROM "; const char kDeleteOnOriginAndType[] =
remove.append(kStorageAPITableName); "DELETE FROM originStorageAPIs WHERE origin = ? AND type = ?";
remove.append(" WHERE origin = ? AND type = ?");
sql::Statement remove_statement( sql::Statement remove_statement(
db_.GetCachedStatement(SQL_FROM_HERE, remove.c_str())); db_.GetCachedStatement(SQL_FROM_HERE, kDeleteOnOriginAndType));
for (const auto& origin_type : origin_type_pairs_for_removal) { for (const auto& origin_type : origin_type_pairs_for_removal) {
remove_statement.BindString(0, origin_type.first.Serialize()); remove_statement.BindString(0, origin_type.first.Serialize());
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment