blob: bbf6bc2443bbbd058acfc6f4a0f25b38e2bd7bbb [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/attribution_reporting/rate_limit_table.h"
#include <stdint.h>
#include <set>
#include <string>
#include <vector>
#include "base/check.h"
#include "base/check_op.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ref.h"
#include "base/notreached.h"
#include "base/ranges/functional.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "components/attribution_reporting/source_registration.h"
#include "components/attribution_reporting/suitable_origin.h"
#include "content/browser/attribution_reporting/attribution_config.h"
#include "content/browser/attribution_reporting/attribution_info.h"
#include "content/browser/attribution_reporting/attribution_resolver_delegate.h"
#include "content/browser/attribution_reporting/common_source_info.h"
#include "content/browser/attribution_reporting/rate_limit_result.h"
#include "content/browser/attribution_reporting/sql_queries.h"
#include "content/browser/attribution_reporting/sql_utils.h"
#include "content/browser/attribution_reporting/storable_source.h"
#include "content/browser/attribution_reporting/stored_source.h"
#include "net/base/schemeful_site.h"
#include "sql/database.h"
#include "sql/statement.h"
#include "sql/transaction.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/origin.h"
namespace content {
namespace {
bool IsAttribution(RateLimitTable::Scope scope) {
switch (scope) {
case RateLimitTable::Scope::kSource:
return false;
case RateLimitTable::Scope::kEventLevelAttribution:
case RateLimitTable::Scope::kAggregatableAttribution:
return true;
}
NOTREACHED_NORETURN();
}
struct DestinationLimitRecord {
std::string serialized_destination;
base::Time time;
int64_t priority;
StoredSource::Id source_id;
bool operator>(const DestinationLimitRecord& other) const {
if (priority > other.priority) {
return true;
}
if (priority < other.priority) {
return false;
}
if (time > other.time) {
return true;
}
if (time < other.time) {
return false;
}
return serialized_destination > other.serialized_destination;
}
};
} // namespace
RateLimitTable::RateLimitTable(const AttributionResolverDelegate* delegate)
: delegate_(
raw_ref<const AttributionResolverDelegate>::from_ptr(delegate)) {}
RateLimitTable::~RateLimitTable() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
bool RateLimitTable::CreateTable(sql::Database* db) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// All columns in this table are const.
// |source_id| is the primary key of a row in the |impressions| table,
// though the row may not exist.
// |scope| is a serialized `RateLimitTable::Scope`.
// |source_site| is the eTLD+1 of the impression.
// |destination_site| is the destination of the conversion.
// |context_origin| is the source origin for `kSource` or the destination
// origin for `kEventLevelAttribution` or `kAggregatableAttribution`.
// |reporting_origin| is the reporting origin of the impression/conversion.
// |time| is the time of the source registration.
// |source_expiry_or_attribution_time| is either the source's expiry time or
// the attribution time, depending on |scope|.
// |report_id| is the report ID for `kEventLevelAttribution` or
// `kAggregatableAttribution` and is set to -1 for `kSource`. Note that -1 is
// also set for `kEventLevelAttribution` records associated with fake reports,
// as well as the attribution records from migration.
// |deactivated_for_source_destination_limit| indicates whether the record
// should be considered for source destination limit. This is only relevant
// for `kSource` and is set to 0 for `kEventLevelAttribution` and
// `kAggregatableAttribution`.
// |destination_limit_priority| indicates the priority of the record in
// regards of source destination limit. This is only relevant for `kSource`
// and is set to 0 for `kEventLevelAttribution` and
// `kAggregatableAttribution`.
static constexpr char kRateLimitTableSql[] =
"CREATE TABLE rate_limits("
"id INTEGER PRIMARY KEY NOT NULL,"
"scope INTEGER NOT NULL,"
"source_id INTEGER NOT NULL,"
"source_site TEXT NOT NULL,"
"destination_site TEXT NOT NULL,"
"context_origin TEXT NOT NULL,"
"reporting_origin TEXT NOT NULL,"
"reporting_site TEXT NOT NULL,"
"time INTEGER NOT NULL,"
"source_expiry_or_attribution_time INTEGER NOT NULL,"
"report_id INTEGER NOT NULL,"
"deactivated_for_source_destination_limit INTEGER NOT NULL,"
"destination_limit_priority INTEGER NOT NULL)";
if (!db->Execute(kRateLimitTableSql)) {
return false;
}
// Optimizes calls to `AllowedForReportingOriginLimit()` and
// `AttributionAllowedForAttributionLimit()`.
static constexpr char kRateLimitReportingOriginIndexSql[] =
"CREATE INDEX rate_limit_reporting_origin_idx "
"ON rate_limits(scope,source_site,destination_site)";
if (!db->Execute(kRateLimitReportingOriginIndexSql)) {
return false;
}
// Optimizes calls to |DeleteExpiredRateLimits()|, |ClearAllDataInRange()|,
// |ClearDataForOriginsInRange()|.
static constexpr char kRateLimitTimeIndexSql[] =
"CREATE INDEX rate_limit_time_idx ON rate_limits(time)";
if (!db->Execute(kRateLimitTimeIndexSql)) {
return false;
}
// Optimizes calls to |ClearDataForSourceIds()|.
static constexpr char kRateLimitImpressionIdIndexSql[] =
"CREATE INDEX rate_limit_source_id_idx "
"ON rate_limits(source_id)";
if (!db->Execute(kRateLimitImpressionIdIndexSql)) {
return false;
}
// Optimizes calls to `DeleteAttributionRateLimit()`.
static constexpr char kRateLimitReportIdIndexSql[] =
"CREATE INDEX rate_limit_report_id_idx "
"ON rate_limits(scope,report_id)"
"WHERE " RATE_LIMIT_ATTRIBUTION_CONDITION
" AND " RATE_LIMIT_REPORT_ID_SET_CONDITION;
return db->Execute(kRateLimitReportIdIndexSql);
}
bool RateLimitTable::AddRateLimitForSource(sql::Database* db,
const StoredSource& source,
int64_t destination_limit_priority) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return AddRateLimit(db, source, /*trigger_time=*/std::nullopt,
/*context_origin=*/source.common_info().source_origin(),
Scope::kSource,
/*report_id=*/std::nullopt, destination_limit_priority);
}
bool RateLimitTable::AddRateLimitForAttribution(
sql::Database* db,
const AttributionInfo& attribution_info,
const StoredSource& source,
Scope scope,
AttributionReport::Id report_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return AddRateLimit(db, source, attribution_info.time,
attribution_info.context_origin, scope, report_id,
/*destination_limit_priority=*/std::nullopt);
}
bool RateLimitTable::AddRateLimit(
sql::Database* db,
const StoredSource& source,
std::optional<base::Time> trigger_time,
const attribution_reporting::SuitableOrigin& context_origin,
Scope scope,
std::optional<AttributionReport::Id> report_id,
std::optional<int64_t> destination_limit_priority) {
const bool is_attribution = IsAttribution(scope);
CHECK_EQ(trigger_time.has_value(), is_attribution);
CHECK_EQ(report_id.has_value(), is_attribution);
CHECK_NE(destination_limit_priority.has_value(), is_attribution);
const CommonSourceInfo& common_info = source.common_info();
// Only delete expired rate limits periodically to avoid excessive DB
// operations.
const base::TimeDelta delete_frequency =
delegate_->GetDeleteExpiredRateLimitsFrequency();
DCHECK_GE(delete_frequency, base::TimeDelta());
const base::Time now = base::Time::Now();
if (now - last_cleared_ >= delete_frequency) {
if (!DeleteExpiredRateLimits(db)) {
return false;
}
last_cleared_ = now;
}
base::Time source_expiry_or_attribution_time;
int64_t report_id_value = kUnsetRecordId;
int64_t destination_limit_priority_value = 0;
if (is_attribution) {
source_expiry_or_attribution_time = *trigger_time;
report_id_value = **report_id;
} else {
scope = Scope::kSource;
source_expiry_or_attribution_time = source.expiry_time();
destination_limit_priority_value = *destination_limit_priority;
}
static constexpr char kStoreRateLimitSql[] =
"INSERT INTO rate_limits"
"(scope,source_id,source_site,destination_site,context_origin,"
"reporting_origin,reporting_site,time,source_expiry_or_attribution_time,"
"report_id,deactivated_for_source_destination_limit,"
"destination_limit_priority)"
"VALUES(?,?,?,?,?,?,?,?,?,?,0,?)";
sql::Statement statement(
db->GetCachedStatement(SQL_FROM_HERE, kStoreRateLimitSql));
statement.BindInt(0, static_cast<int>(scope));
statement.BindInt64(1, *source.source_id());
statement.BindString(2, common_info.source_site().Serialize());
statement.BindString(4, context_origin.Serialize());
statement.BindString(5, common_info.reporting_origin().Serialize());
statement.BindString(
6, net::SchemefulSite(common_info.reporting_origin()).Serialize());
statement.BindTime(7, source.source_time());
statement.BindTime(8, source_expiry_or_attribution_time);
statement.BindInt64(9, report_id_value);
statement.BindInt64(10, destination_limit_priority_value);
const auto insert_row = [&](const net::SchemefulSite& site) {
statement.BindString(3, site.Serialize());
return statement.Run();
};
if (scope == Scope::kAggregatableAttribution ||
(source.attribution_logic() ==
StoredSource::AttributionLogic::kTruthfully &&
scope == Scope::kEventLevelAttribution)) {
return insert_row(net::SchemefulSite(context_origin));
}
sql::Transaction transaction(db);
if (!transaction.Begin()) {
return false;
}
for (const auto& site : source.destination_sites().destinations()) {
statement.Reset(/*clear_bound_vars=*/false);
if (!insert_row(site)) {
return false;
}
}
return transaction.Commit();
}
RateLimitResult RateLimitTable::AttributionAllowedForAttributionLimit(
sql::Database* db,
const AttributionInfo& attribution_info,
const StoredSource& source,
Scope scope) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
CHECK(IsAttribution(scope));
const CommonSourceInfo& common_info = source.common_info();
const AttributionConfig::RateLimitConfig& rate_limits =
delegate_->GetRateLimits();
DCHECK_GT(rate_limits.time_window, base::TimeDelta());
DCHECK_GT(rate_limits.max_attributions, 0);
base::Time min_timestamp = attribution_info.time - rate_limits.time_window;
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kRateLimitAttributionAllowedSql));
statement.BindInt(0, static_cast<int>(scope));
statement.BindString(
1, net::SchemefulSite(attribution_info.context_origin).Serialize());
statement.BindString(2, common_info.source_site().Serialize());
statement.BindString(
3, net::SchemefulSite(common_info.reporting_origin()).Serialize());
statement.BindTime(4, min_timestamp);
if (!statement.Step()) {
return RateLimitResult::kError;
}
int64_t count = statement.ColumnInt64(0);
return count < rate_limits.max_attributions ? RateLimitResult::kAllowed
: RateLimitResult::kNotAllowed;
}
RateLimitResult RateLimitTable::SourceAllowedForReportingOriginLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return AllowedForReportingOriginLimit(
db, /*is_source=*/true, source.common_info(), source_time,
source.registration().destination_set.destinations());
}
RateLimitResult RateLimitTable::SourceAllowedForReportingOriginPerSiteLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
size_t max_origins =
static_cast<size_t>(delegate_->GetRateLimits()
.max_reporting_origins_per_source_reporting_site);
base::Time min_timestamp =
source_time - delegate_->GetRateLimits().origins_per_site_window;
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kRateLimitSelectSourceReportingOriginsBySiteSql));
statement.BindString(0, source.common_info().source_site().Serialize());
statement.BindString(
1,
net::SchemefulSite(source.common_info().reporting_origin()).Serialize());
statement.BindTime(2, min_timestamp);
std::string serialized_reporting_origin =
source.common_info().reporting_origin().Serialize();
std::set<std::string> reporting_origins;
while (statement.Step()) {
std::string origin = statement.ColumnString(0);
if (origin == serialized_reporting_origin) {
return RateLimitResult::kAllowed;
}
reporting_origins.insert(std::move(origin));
if (reporting_origins.size() == max_origins) {
return RateLimitResult::kNotAllowed;
}
}
if (!statement.Succeeded()) {
return RateLimitResult::kError;
}
return RateLimitResult::kAllowed;
}
base::expected<std::vector<StoredSource::Id>, RateLimitTable::Error>
RateLimitTable::GetSourcesToDeactivateForDestinationLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Check the number of unique destinations covered by all source registrations
// whose [source_time, source_expiry_or_attribution_time] intersect with the
// current source_time.
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kRateLimitSourceAllowedSql));
const CommonSourceInfo& common_info = source.common_info();
statement.BindString(0, common_info.source_site().Serialize());
statement.BindString(
1, net::SchemefulSite(common_info.reporting_origin()).Serialize());
statement.BindTime(2, source_time);
const int limit = delegate_->GetMaxDestinationsPerSourceSiteReportingSite();
DCHECK_GT(limit, 0);
std::vector<DestinationLimitRecord> records;
for (const auto& destination :
source.registration().destination_set.destinations()) {
records.emplace_back(destination.Serialize(), source_time,
source.registration().destination_limit_priority,
StoredSource::Id(kUnsetRecordId));
}
while (statement.Step()) {
const int64_t source_id = statement.ColumnInt64(3);
// `source_id` should not be unset.
// Note that this could occur in practice, e.g. with deliberate DB
// modification or corruption, which would cause this to continue failing
// until the offending row expires.
if (source_id == kUnsetRecordId) {
return base::unexpected(Error());
}
records.emplace_back(/*serialized_destination=*/statement.ColumnString(0),
/*time=*/statement.ColumnTime(1),
/*priority=*/statement.ColumnInt64(2),
StoredSource::Id(source_id));
}
if (!statement.Succeeded()) {
return base::unexpected(Error());
}
base::ranges::sort(records, base::ranges::greater());
base::flat_set<net::SchemefulSite> destination_sites;
std::vector<StoredSource::Id> source_ids_to_deactivate;
for (const DestinationLimitRecord& record : records) {
net::SchemefulSite destination =
net::SchemefulSite::Deserialize(record.serialized_destination);
if (destination_sites.size() < static_cast<size_t>(limit)) {
destination_sites.emplace(std::move(destination));
} else if (!destination_sites.contains(destination)) {
source_ids_to_deactivate.push_back(record.source_id);
}
}
return base::flat_set<StoredSource::Id>(std::move(source_ids_to_deactivate))
.extract();
}
bool RateLimitTable::DeactivateSourcesForDestinationLimit(
sql::Database* db,
const std::vector<StoredSource::Id>& source_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sql::Transaction transaction(db);
if (!transaction.Begin()) {
return false;
}
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kDeactivateForSourceDestinationLimitSql));
for (StoredSource::Id id : source_ids) {
statement.Reset(/*clear_bound_vars=*/true);
statement.BindInt64(0, *id);
if (!statement.Run()) {
return false;
}
}
return transaction.Commit();
}
RateLimitTable::DestinationRateLimitResult
RateLimitTable::SourceAllowedForDestinationRateLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kRateLimitSourceAllowedDestinationRateLimitSql));
AttributionConfig::DestinationRateLimit destination_rate_limit =
delegate_->GetDestinationRateLimit();
const CommonSourceInfo& common_info = source.common_info();
statement.BindString(0, common_info.source_site().Serialize());
statement.BindTime(1, source_time);
statement.BindTime(2, source_time - destination_rate_limit.rate_limit_window);
base::flat_set<net::SchemefulSite> destination_sites =
source.registration().destination_set.destinations();
base::flat_set<net::SchemefulSite> same_reporting_destination_sites =
destination_sites;
const std::string serialized_reporting_site =
net::SchemefulSite(common_info.reporting_origin()).Serialize();
while (statement.Step()) {
net::SchemefulSite destination_site =
net::SchemefulSite::Deserialize(statement.ColumnString(0));
if (serialized_reporting_site == statement.ColumnString(1)) {
same_reporting_destination_sites.insert(destination_site);
}
destination_sites.insert(std::move(destination_site));
}
if (!statement.Succeeded()) {
return DestinationRateLimitResult::kError;
}
const int global_limit = destination_rate_limit.max_total;
DCHECK_GT(global_limit, 0);
const int reporting_limit = destination_rate_limit.max_per_reporting_site;
DCHECK_GT(reporting_limit, 0);
bool global_limit_hit =
destination_sites.size() > static_cast<size_t>(global_limit);
bool reporting_limit_hit = same_reporting_destination_sites.size() >
static_cast<size_t>(reporting_limit);
if (global_limit_hit && reporting_limit_hit) {
return DestinationRateLimitResult::kHitBothLimits;
}
if (!global_limit_hit && !reporting_limit_hit) {
return DestinationRateLimitResult::kAllowed;
}
return global_limit_hit ? DestinationRateLimitResult::kHitGlobalLimit
: DestinationRateLimitResult::kHitReportingLimit;
}
RateLimitResult RateLimitTable::SourceAllowedForDestinationPerDayRateLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::
kRateLimitSourceAllowedDestinationPerDayRateLimitSql));
const CommonSourceInfo& common_info = source.common_info();
statement.BindString(0, common_info.source_site().Serialize());
statement.BindString(
1, net::SchemefulSite(common_info.reporting_origin()).Serialize());
statement.BindTime(2, source_time);
statement.BindTime(
3, source_time -
AttributionConfig::DestinationRateLimit::kPerDayRateLimitWindow);
const int limit =
delegate_->GetDestinationRateLimit().max_per_reporting_site_per_day;
DCHECK_GT(limit, 0);
base::flat_set<net::SchemefulSite> destination_sites =
source.registration().destination_set.destinations();
while (statement.Step()) {
destination_sites.insert(
net::SchemefulSite::Deserialize(statement.ColumnString(0)));
if (destination_sites.size() > static_cast<size_t>(limit)) {
return RateLimitResult::kNotAllowed;
}
}
return statement.Succeeded() ? RateLimitResult::kAllowed
: RateLimitResult::kError;
}
RateLimitResult RateLimitTable::AttributionAllowedForReportingOriginLimit(
sql::Database* db,
const AttributionInfo& attribution_info,
const StoredSource& source) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return AllowedForReportingOriginLimit(
db, /*is_source=*/false, source.common_info(), attribution_info.time,
{net::SchemefulSite(attribution_info.context_origin)});
}
RateLimitResult RateLimitTable::AllowedForReportingOriginLimit(
sql::Database* db,
bool is_source,
const CommonSourceInfo& common_info,
base::Time time,
const base::flat_set<net::SchemefulSite>& destination_sites) {
const AttributionConfig::RateLimitConfig& rate_limits =
delegate_->GetRateLimits();
DCHECK_GT(rate_limits.time_window, base::TimeDelta());
sql::Statement statement;
int64_t max;
if (is_source) {
max = rate_limits.max_source_registration_reporting_origins;
statement.Assign(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kRateLimitSelectSourceReportingOriginsSql));
} else {
max = rate_limits.max_attribution_reporting_origins;
statement.Assign(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kRateLimitSelectAttributionReportingOriginsSql));
}
DCHECK_GT(max, 0);
const std::string serialized_reporting_origin =
common_info.reporting_origin().Serialize();
base::Time min_timestamp = time - rate_limits.time_window;
statement.BindString(0, common_info.source_site().Serialize());
statement.BindTime(2, min_timestamp);
for (const auto& destination : destination_sites) {
base::flat_set<std::string> reporting_origins;
statement.Reset(/*clear_bound_vars=*/false);
statement.BindString(1, destination.Serialize());
while (statement.Step()) {
std::string reporting_origin = statement.ColumnString(0);
// The origin isn't new, so it doesn't change the count.
if (reporting_origin == serialized_reporting_origin) {
break;
}
reporting_origins.insert(std::move(reporting_origin));
if (reporting_origins.size() == static_cast<size_t>(max)) {
return RateLimitResult::kNotAllowed;
}
}
if (!statement.Succeeded()) {
return RateLimitResult::kError;
}
}
return RateLimitResult::kAllowed;
}
bool RateLimitTable::DeleteAttributionRateLimit(
sql::Database* db,
Scope scope,
AttributionReport::Id report_id) {
CHECK(IsAttribution(scope));
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kDeleteAttributionRateLimitByReportIdSql));
statement.BindInt(0, static_cast<int>(scope));
statement.BindInt64(1, *report_id);
return statement.Run();
}
bool RateLimitTable::ClearAllDataInRange(sql::Database* db,
base::Time delete_begin,
base::Time delete_end) {
DCHECK(!((delete_begin.is_null() || delete_begin.is_min()) &&
delete_end.is_max()));
// TODO(linnan): Optimize using a more appropriate index.
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kDeleteRateLimitRangeSql));
statement.BindTime(0, delete_begin);
statement.BindTime(1, delete_end);
return statement.Run();
}
bool RateLimitTable::ClearAllDataAllTime(sql::Database* db) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
static constexpr char kDeleteAllRateLimitsSql[] = "DELETE FROM rate_limits";
sql::Statement statement(
db->GetCachedStatement(SQL_FROM_HERE, kDeleteAllRateLimitsSql));
return statement.Run();
}
bool RateLimitTable::ClearDataForOriginsInRange(
sql::Database* db,
base::Time delete_begin,
base::Time delete_end,
StoragePartition::StorageKeyMatcherFunction filter) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (filter.is_null()) {
return ClearAllDataInRange(db, delete_begin, delete_end);
}
static constexpr char kDeleteSql[] = "DELETE FROM rate_limits WHERE id=?";
sql::Statement delete_statement(
db->GetCachedStatement(SQL_FROM_HERE, kDeleteSql));
sql::Transaction transaction(db);
if (!transaction.Begin()) {
return false;
}
// TODO(linnan): Optimize using a more appropriate index.
sql::Statement select_statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kSelectRateLimitsForDeletionSql));
select_statement.BindTime(0, delete_begin);
select_statement.BindTime(1, delete_end);
while (select_statement.Step()) {
int64_t rate_limit_id = select_statement.ColumnInt64(0);
if (filter.Run(blink::StorageKey::CreateFirstParty(
DeserializeOrigin(select_statement.ColumnString(1))))) {
// See https://www.sqlite.org/isolation.html for why it's OK for this
// DELETE to be interleaved in the surrounding SELECT.
delete_statement.Reset(/*clear_bound_vars=*/false);
delete_statement.BindInt64(0, rate_limit_id);
if (!delete_statement.Run()) {
return false;
}
}
}
if (!select_statement.Succeeded()) {
return false;
}
return transaction.Commit();
}
bool RateLimitTable::DeleteExpiredRateLimits(sql::Database* db) {
base::Time now = base::Time::Now();
base::Time timestamp = now - delegate_->GetRateLimits().time_window;
// Attribution rate limit entries can be deleted as long as their time falls
// outside the rate limit window. For source entries, if the expiry time has
// not passed, keep entries around to ensure
// `SourceAllowedForDestinationLimit()` is computed properly.
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kDeleteExpiredRateLimitsSql));
statement.BindTime(0, timestamp);
statement.BindTime(1, now);
return statement.Run();
}
bool RateLimitTable::ClearDataForSourceIds(
sql::Database* db,
const std::vector<StoredSource::Id>& source_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sql::Transaction transaction(db);
if (!transaction.Begin()) {
return false;
}
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kDeleteRateLimitsBySourceIdSql));
for (StoredSource::Id id : source_ids) {
statement.Reset(/*clear_bound_vars=*/true);
statement.BindInt64(0, *id);
if (!statement.Run()) {
return false;
}
}
return transaction.Commit();
}
void RateLimitTable::AppendRateLimitDataKeys(
sql::Database* db,
std::set<AttributionDataModel::DataKey>& keys) {
sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE, attribution_queries::kGetRateLimitDataKeysSql));
while (statement.Step()) {
url::Origin reporting_origin = DeserializeOrigin(statement.ColumnString(0));
if (reporting_origin.opaque()) {
continue;
}
keys.emplace(std::move(reporting_origin));
}
}
void RateLimitTable::SetDelegate(const AttributionResolverDelegate& delegate) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
delegate_ = delegate;
}
} // namespace content