use crate::db::{
session::sql::write_policy::{
DEFAULT_PUBLIC_BOUNDED_WRITE_LIMIT, DEFAULT_PUBLIC_WRITE_RETURNING_RESPONSE_BYTES,
SqlWriteBoundedPlanProof, SqlWriteBoundedPolicyRejection, SqlWriteExecutionBounds,
SqlWriteExposureClass, SqlWritePlanCore, SqlWritePolicyBounds, SqlWritePrimaryKeyPlanProof,
SqlWriteReturningBounds, SqlWriteShapePolicyRejection, SqlWriteStatementShape,
},
sql::parser::SqlUpdateStatement,
};
#[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 enum SqlUpdateExposurePolicy {
SessionWriteCurrent,
GeneratedQuery,
GeneratedDdl,
PublicPrimaryKeyOnly,
PublicBoundedDeterministic,
AdminBulk,
}
impl SqlUpdateExposurePolicy {
pub(super) const fn exposure_class(self) -> SqlWriteExposureClass {
match self {
Self::SessionWriteCurrent => SqlWriteExposureClass::SessionWriteCurrent,
Self::GeneratedQuery => SqlWriteExposureClass::GeneratedQuery,
Self::GeneratedDdl => SqlWriteExposureClass::GeneratedDdl,
Self::PublicPrimaryKeyOnly => SqlWriteExposureClass::PublicPrimaryKeyOnly,
Self::PublicBoundedDeterministic => SqlWriteExposureClass::PublicBoundedDeterministic,
Self::AdminBulk => SqlWriteExposureClass::AdminBulk,
}
}
}
#[derive(Clone, Copy, Debug)]
#[doc(hidden)]
pub 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> {
#[must_use]
pub 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 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 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, SqlUpdateStatementClassification>;
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub enum SqlValidatedUpdatePlan {
SessionCurrent(SqlSessionCurrentUpdatePlan),
PublicPrimaryKeyOnly(SqlPublicPrimaryKeyUpdatePlan),
PublicBoundedDeterministic(SqlPublicBoundedUpdatePlan),
AdminBulk(SqlAdminBulkUpdatePlan),
}
impl SqlValidatedUpdatePlan {
#[must_use]
pub const fn classification(&self) -> &SqlUpdateStatementClassification {
match self {
Self::SessionCurrent(plan) => plan.core.classification(),
Self::PublicPrimaryKeyOnly(plan) => plan.core.classification(),
Self::PublicBoundedDeterministic(plan) => plan.core.classification(),
Self::AdminBulk(plan) => plan.core.classification(),
}
}
#[must_use]
pub const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
match self {
Self::SessionCurrent(plan) => plan.core.execution_bounds(),
Self::PublicPrimaryKeyOnly(plan) => plan.core.execution_bounds(),
Self::PublicBoundedDeterministic(plan) => plan.core.execution_bounds(),
Self::AdminBulk(plan) => plan.core.execution_bounds(),
}
}
#[must_use]
pub const fn returning_bounds(&self) -> SqlWriteReturningBounds {
self.execution_bounds().returning
}
#[must_use]
pub const fn statement_entity(&self) -> &str {
match self {
Self::SessionCurrent(plan) => plan.core.statement().entity.as_str(),
Self::PublicPrimaryKeyOnly(plan) => plan.core.statement().entity.as_str(),
Self::PublicBoundedDeterministic(plan) => plan.core.statement().entity.as_str(),
Self::AdminBulk(plan) => plan.core.statement().entity.as_str(),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub struct SqlSessionCurrentUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub struct SqlPublicPrimaryKeyUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
pub(super) primary_key_proof: SqlWritePrimaryKeyPlanProof,
}
impl SqlPublicPrimaryKeyUpdatePlan {
pub(in crate::db::session::sql) const fn statement(&self) -> &SqlUpdateStatement {
self.core.statement()
}
#[must_use]
pub const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
self.core.execution_bounds()
}
#[must_use]
pub const fn primary_key_fields(&self) -> &[String] {
self.primary_key_proof.primary_key_fields()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub struct SqlPublicBoundedUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
pub(super) bounded_proof: SqlWriteBoundedPlanProof,
}
impl SqlPublicBoundedUpdatePlan {
pub(in crate::db::session::sql) const fn statement(&self) -> &SqlUpdateStatement {
self.core.statement()
}
#[must_use]
pub 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);
}
#[must_use]
pub const fn limit(&self) -> u32 {
self.bounded_proof.limit()
}
#[must_use]
pub const fn ordered_primary_key_fields(&self) -> &[String] {
self.bounded_proof.ordered_primary_key_fields()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub struct SqlAdminBulkUpdatePlan {
pub(super) core: SqlUpdatePlanCore,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub enum SqlUpdatePolicyRejection {
NotUpdate,
GeneratedQueryRejectsUpdate,
GeneratedDdlRejectsUpdate,
MissingWhere,
PrimaryKeyMutation,
GeneratedFieldMutation,
ManagedFieldMutation,
PrimaryKeyProofFailed,
MissingCanonicalPrimaryKeyOrder,
DescendingOrder,
MissingLimit,
OffsetUnsupported,
LimitTooHigh,
}
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 struct SqlUpdatePolicyReport {
pub classification: Option<SqlUpdateStatementClassification>,
pub plan: Option<SqlValidatedUpdatePlan>,
pub rejection: Option<SqlUpdatePolicyRejection>,
}
impl SqlUpdatePolicyReport {
#[must_use]
pub const fn is_admitted(&self) -> bool {
self.rejection.is_none()
}
pub(super) const fn rejected(rejection: SqlUpdatePolicyRejection) -> Self {
Self {
classification: None,
plan: None,
rejection: Some(rejection),
}
}
}