#[cfg(test)]
use crate::db::session::sql::write_policy::SqlWriteReturningBounds;
use crate::db::{
QueryError,
session::sql::write_policy::{
DEFAULT_PUBLIC_BOUNDED_WRITE_LIMIT, DEFAULT_PUBLIC_WRITE_RETURNING_RESPONSE_BYTES,
SqlWriteExecutionBounds, SqlWritePlanCore, SqlWritePolicyBounds,
SqlWriteShapePolicyRejection,
},
sql::parser::SqlUpdateStatement,
};
use icydb_diagnostic_code::SqlWriteBoundaryCode;
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
}
#[must_use]
pub(in crate::db) const fn max_affected_rows() -> u32 {
MAX_TRUSTED_EXACT_UPDATE_ROWS
}
}
#[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),
}
}
}
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()
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlUpdatePolicyRejection {
NotUpdate,
WriteShape(SqlWriteShapePolicyRejection),
PrimaryKeyMutation,
GeneratedFieldMutation,
ManagedFieldMutation,
ExactWindowUnsupported,
ResumableWindowUnsupported,
ResumableReturningUnsupported,
}
impl SqlUpdatePolicyRejection {
pub(in crate::db::session::sql) fn into_query_error(self) -> QueryError {
let boundary = match self {
Self::WriteShape(SqlWriteShapePolicyRejection::MissingWhere) => {
SqlWriteBoundaryCode::UpdateMissingWherePredicate
}
Self::PrimaryKeyMutation => SqlWriteBoundaryCode::UpdatePrimaryKeyMutation,
Self::GeneratedFieldMutation => SqlWriteBoundaryCode::ExplicitGeneratedField,
Self::ManagedFieldMutation => SqlWriteBoundaryCode::ExplicitManagedField,
Self::ExactWindowUnsupported => SqlWriteBoundaryCode::ExactUpdateWindowUnsupported,
Self::ResumableWindowUnsupported => {
SqlWriteBoundaryCode::ResumableUpdateWindowUnsupported
}
Self::ResumableReturningUnsupported => {
SqlWriteBoundaryCode::ResumableUpdateReturningUnsupported
}
Self::NotUpdate | Self::WriteShape(_) => return QueryError::unsupported_query(),
};
QueryError::sql_write_boundary(boundary)
}
}
#[doc(hidden)]
pub(in crate::db) type SqlUpdatePolicyResult =
Result<SqlValidatedUpdatePlan, SqlUpdatePolicyRejection>;