#[cfg(test)]
use crate::db::session::sql::write_policy::SqlWriteReturningBounds;
use crate::db::{
session::sql::write_policy::{
DEFAULT_PUBLIC_BOUNDED_WRITE_LIMIT, DEFAULT_PUBLIC_WRITE_RETURNING_RESPONSE_BYTES,
SqlWriteBoundedPolicyRejection, SqlWriteExecutionBounds, SqlWritePlanCore,
SqlWritePolicyBounds, SqlWriteShapePolicyRejection, SqlWriteStatementShape,
},
sql::parser::SqlUpdateStatement,
};
use std::num::NonZeroU32;
pub(in crate::db) const MAX_TRUSTED_EXACT_UPDATE_ROWS: u32 = 4_096;
const MAX_TRUSTED_EXACT_UPDATE_SCANNED_KEYS: usize = 4_096;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlExactUpdatePolicy {
require_affected_at_most: NonZeroU32,
}
impl SqlExactUpdatePolicy {
pub(in crate::db) const fn try_new(
require_affected_at_most: u32,
) -> Result<Self, SqlExactUpdatePolicyRejection> {
let Some(require_affected_at_most) = NonZeroU32::new(require_affected_at_most) else {
return Err(SqlExactUpdatePolicyRejection::AssertionRequired);
};
if require_affected_at_most.get() > MAX_TRUSTED_EXACT_UPDATE_ROWS {
return Err(SqlExactUpdatePolicyRejection::AssertionTooHigh);
}
Ok(Self {
require_affected_at_most,
})
}
#[must_use]
pub(in crate::db) const fn require_affected_at_most(self) -> u32 {
self.require_affected_at_most.get()
}
#[must_use]
pub(in crate::db) const fn selection_limit(self) -> u32 {
self.require_affected_at_most.get() + 1
}
#[must_use]
pub(in crate::db) const fn scan_budget() -> usize {
MAX_TRUSTED_EXACT_UPDATE_SCANNED_KEYS
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlExactUpdatePolicyRejection {
AssertionRequired,
AssertionTooHigh,
}
#[doc(hidden)]
pub(in crate::db) const DEFAULT_PUBLIC_BOUNDED_UPDATE_LIMIT: u32 =
DEFAULT_PUBLIC_BOUNDED_WRITE_LIMIT;
#[doc(hidden)]
pub(in crate::db) const DEFAULT_PUBLIC_UPDATE_RETURNING_RESPONSE_BYTES: u32 =
DEFAULT_PUBLIC_WRITE_RETURNING_RESPONSE_BYTES;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlUpdateExposurePolicy {
PublicPrimaryKeyOnly,
PublicBoundedDeterministic,
TrustedExact(SqlExactUpdatePolicy),
}
#[derive(Clone, Copy, Debug)]
#[doc(hidden)]
pub(in crate::db) struct SqlUpdatePolicyContext<'a> {
pub primary_key_fields: &'a [&'a str],
pub generated_fields: &'a [&'a str],
pub managed_fields: &'a [&'a str],
pub max_public_bounded_limit: u32,
pub max_returning_rows: Option<u32>,
pub max_returning_response_bytes: Option<u32>,
}
impl<'a> SqlUpdatePolicyContext<'a> {
#[cfg(test)]
#[must_use]
pub(in crate::db) const fn new(primary_key_fields: &'a [&'a str]) -> Self {
Self {
primary_key_fields,
generated_fields: &[],
managed_fields: &[],
max_public_bounded_limit: DEFAULT_PUBLIC_BOUNDED_UPDATE_LIMIT,
max_returning_rows: None,
max_returning_response_bytes: None,
}
}
pub(super) const fn write_bounds(self) -> SqlWritePolicyBounds {
SqlWritePolicyBounds::new(
self.max_public_bounded_limit,
self.max_returning_rows,
self.max_returning_response_bytes,
)
}
#[must_use]
pub(in crate::db) const fn public_generated(
primary_key_fields: &'a [&'a str],
generated_fields: &'a [&'a str],
managed_fields: &'a [&'a str],
) -> Self {
Self {
primary_key_fields,
generated_fields,
managed_fields,
max_public_bounded_limit: DEFAULT_PUBLIC_BOUNDED_UPDATE_LIMIT,
max_returning_rows: None,
max_returning_response_bytes: Some(DEFAULT_PUBLIC_UPDATE_RETURNING_RESPONSE_BYTES),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlUpdateAssignmentPolicy {
pub mutates_primary_key: bool,
pub mutates_generated: bool,
pub mutates_managed: bool,
}
impl SqlUpdateAssignmentPolicy {
pub(super) const fn admitted(self) -> bool {
!self.mutates_primary_key && !self.mutates_generated && !self.mutates_managed
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlUpdateStatementClassification {
pub target_entity: String,
pub assigned_fields: Vec<String>,
pub assignment_policy: SqlUpdateAssignmentPolicy,
pub write_shape: SqlWriteStatementShape,
}
pub(super) type SqlUpdatePlanCore = SqlWritePlanCore<SqlUpdateStatement>;
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlValidatedUpdatePlan {
PublicPrimaryKeyOnly(SqlPublicPrimaryKeyUpdatePlan),
PublicBoundedDeterministic(SqlPublicBoundedUpdatePlan),
TrustedExact(SqlTrustedExactUpdatePlan),
}
impl SqlValidatedUpdatePlan {
#[cfg(test)]
#[must_use]
pub(in crate::db) const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
match self {
Self::PublicPrimaryKeyOnly(plan) => plan.core.execution_bounds(),
Self::PublicBoundedDeterministic(plan) => plan.core.execution_bounds(),
Self::TrustedExact(plan) => plan.core.execution_bounds(),
}
}
#[cfg(test)]
#[must_use]
pub(in crate::db) const fn returning_bounds(&self) -> SqlWriteReturningBounds {
self.execution_bounds().returning
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlTrustedExactUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
pub(super) policy: SqlExactUpdatePolicy,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlTrustedResumableUpdatePlan {
pub(super) statement: SqlUpdateStatement,
}
impl SqlTrustedResumableUpdatePlan {
#[must_use]
pub(in crate::db::session::sql) const fn statement(&self) -> &SqlUpdateStatement {
&self.statement
}
}
pub(in crate::db) type SqlResumableUpdatePolicyReport =
Result<SqlTrustedResumableUpdatePlan, SqlUpdatePolicyRejection>;
impl SqlTrustedExactUpdatePlan {
pub(in crate::db::session::sql) const fn statement(&self) -> &SqlUpdateStatement {
self.core.statement()
}
#[must_use]
pub(in crate::db) const fn policy(&self) -> SqlExactUpdatePolicy {
self.policy
}
#[must_use]
pub(in crate::db) const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
self.core.execution_bounds()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlPublicPrimaryKeyUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
}
impl SqlPublicPrimaryKeyUpdatePlan {
pub(in crate::db::session::sql) const fn statement(&self) -> &SqlUpdateStatement {
self.core.statement()
}
#[must_use]
pub(in crate::db) const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
self.core.execution_bounds()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlPublicBoundedUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
}
impl SqlPublicBoundedUpdatePlan {
pub(in crate::db::session::sql) const fn statement(&self) -> &SqlUpdateStatement {
self.core.statement()
}
#[must_use]
pub(in crate::db) const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
self.core.execution_bounds()
}
#[cfg(test)]
pub(in crate::db) const fn set_execution_bounds_for_tests(
&mut self,
execution_bounds: SqlWriteExecutionBounds,
) {
self.core.set_execution_bounds_for_tests(execution_bounds);
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlUpdatePolicyRejection {
NotUpdate,
MissingWhere,
PrimaryKeyMutation,
GeneratedFieldMutation,
ManagedFieldMutation,
PrimaryKeyProofFailed,
MissingCanonicalPrimaryKeyOrder,
DescendingOrder,
MissingLimit,
OffsetUnsupported,
LimitTooHigh,
ExactWindowUnsupported,
ResumableWindowUnsupported,
ResumableReturningUnsupported,
}
impl SqlUpdatePolicyRejection {
const fn from_bounded_write_rejection(rejection: SqlWriteBoundedPolicyRejection) -> Self {
match rejection {
SqlWriteBoundedPolicyRejection::MissingCanonicalPrimaryKeyOrder => {
Self::MissingCanonicalPrimaryKeyOrder
}
SqlWriteBoundedPolicyRejection::DescendingOrder => Self::DescendingOrder,
SqlWriteBoundedPolicyRejection::MissingLimit => Self::MissingLimit,
SqlWriteBoundedPolicyRejection::OffsetUnsupported => Self::OffsetUnsupported,
SqlWriteBoundedPolicyRejection::LimitTooHigh => Self::LimitTooHigh,
}
}
pub(super) const fn from_write_shape_rejection(
rejection: SqlWriteShapePolicyRejection,
) -> Self {
match rejection {
SqlWriteShapePolicyRejection::MissingWhere => Self::MissingWhere,
SqlWriteShapePolicyRejection::PrimaryKeyProofFailed => Self::PrimaryKeyProofFailed,
SqlWriteShapePolicyRejection::Bounded(rejection) => {
Self::from_bounded_write_rejection(rejection)
}
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlUpdatePolicyReport {
pub classification: Option<SqlUpdateStatementClassification>,
pub plan: Option<SqlValidatedUpdatePlan>,
pub rejection: Option<SqlUpdatePolicyRejection>,
}
impl SqlUpdatePolicyReport {
#[cfg(test)]
#[must_use]
pub(in crate::db) const fn is_admitted(&self) -> bool {
self.rejection.is_none()
}
pub(super) const fn admitted(
classification: SqlUpdateStatementClassification,
plan: SqlValidatedUpdatePlan,
) -> Self {
Self {
classification: Some(classification),
plan: Some(plan),
rejection: None,
}
}
pub(super) const fn classified_rejection(
classification: SqlUpdateStatementClassification,
rejection: SqlUpdatePolicyRejection,
) -> Self {
Self {
classification: Some(classification),
plan: None,
rejection: Some(rejection),
}
}
pub(super) const fn rejected(rejection: SqlUpdatePolicyRejection) -> Self {
Self {
classification: None,
plan: None,
rejection: Some(rejection),
}
}
}