use super::bounds::{
bounded_write_policy_rejection, combined_optional_row_bound, sql_write_execution_bounds,
};
pub(in crate::db::session::sql) const DEFAULT_PUBLIC_BOUNDED_WRITE_LIMIT: u32 = 100;
pub(in crate::db::session::sql) const DEFAULT_PUBLIC_WRITE_RETURNING_RESPONSE_BYTES: u32 =
1_048_576;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlWriteWhereProof {
Missing,
PrimaryKeyEquality,
Other,
}
impl SqlWriteWhereProof {
#[must_use]
pub(in crate::db) const fn has_where(self) -> bool {
!matches!(self, Self::Missing)
}
#[must_use]
pub(in crate::db) const fn is_primary_key_equality(self) -> bool {
matches!(self, Self::PrimaryKeyEquality)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlWriteOrderProof {
Missing,
CanonicalPrimaryKey,
DescendingPrimaryKey,
Other,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) enum SqlWriteReturningShape {
None,
NarrowAll,
NarrowFields,
}
impl SqlWriteReturningShape {
#[must_use]
pub(in crate::db) const fn is_requested(self) -> bool {
!matches!(self, Self::None)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum SqlWriteBoundedPolicyRejection {
MissingCanonicalPrimaryKeyOrder,
DescendingOrder,
MissingLimit,
OffsetUnsupported,
LimitTooHigh,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::session::sql) enum SqlWriteExposureClass {
PublicPrimaryKeyOnly,
PublicBoundedDeterministic,
}
impl SqlWriteExposureClass {
const fn max_staged_rows(self, limit: Option<u32>) -> Option<u32> {
match self {
Self::PublicPrimaryKeyOnly => Some(1),
Self::PublicBoundedDeterministic => limit,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum SqlWriteShapePolicyRejection {
MissingWhere,
PrimaryKeyProofFailed,
Bounded(SqlWriteBoundedPolicyRejection),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlWriteReturningBounds {
pub max_rows: Option<u32>,
pub max_response_bytes: Option<u32>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlWriteExecutionBounds {
pub max_staged_rows: Option<u32>,
pub returning: SqlWriteReturningBounds,
}
impl SqlWriteExecutionBounds {
pub(in crate::db::session::sql) const fn max_candidate_rows(self) -> Option<u32> {
combined_optional_row_bound(self.max_staged_rows, self.returning.max_rows)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[doc(hidden)]
pub(in crate::db) struct SqlWriteStatementShape {
pub where_proof: SqlWriteWhereProof,
pub order_proof: SqlWriteOrderProof,
pub limit: Option<u32>,
pub offset: Option<u32>,
pub returning_shape: SqlWriteReturningShape,
}
impl SqlWriteStatementShape {
const fn bounded_policy_rejection(
&self,
max_limit: u32,
) -> Option<SqlWriteBoundedPolicyRejection> {
bounded_write_policy_rejection(self.offset, self.limit, max_limit, self.order_proof)
}
pub(in crate::db::session::sql) const fn bounded_policy_rejection_for_bounds(
&self,
bounds: SqlWritePolicyBounds,
) -> Option<SqlWriteBoundedPolicyRejection> {
self.bounded_policy_rejection(bounds.public_bounded_limit)
}
pub(in crate::db::session::sql) const fn required_where_rejection(
&self,
) -> Option<SqlWriteShapePolicyRejection> {
if self.where_proof.has_where() {
None
} else {
Some(SqlWriteShapePolicyRejection::MissingWhere)
}
}
pub(in crate::db::session::sql) const fn primary_key_policy_rejection(
&self,
) -> Option<SqlWriteShapePolicyRejection> {
if let Some(rejection) = self.required_where_rejection() {
return Some(rejection);
}
if self.where_proof.is_primary_key_equality() {
None
} else {
Some(SqlWriteShapePolicyRejection::PrimaryKeyProofFailed)
}
}
pub(in crate::db::session::sql) const fn bounded_deterministic_policy_rejection(
&self,
bounds: SqlWritePolicyBounds,
) -> Option<SqlWriteShapePolicyRejection> {
if let Some(rejection) = self.required_where_rejection() {
return Some(rejection);
}
match self.bounded_policy_rejection_for_bounds(bounds) {
Some(rejection) => Some(SqlWriteShapePolicyRejection::Bounded(rejection)),
None => None,
}
}
pub(in crate::db::session::sql) const fn execution_bounds_for_exposure_class(
&self,
exposure_class: SqlWriteExposureClass,
bounds: SqlWritePolicyBounds,
) -> SqlWriteExecutionBounds {
sql_write_execution_bounds(
exposure_class.max_staged_rows(self.limit),
self.returning_shape.is_requested(),
bounds.returning_rows,
bounds.returning_response_bytes,
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::session::sql) struct SqlWritePolicyBounds {
pub(in crate::db::session::sql) public_bounded_limit: u32,
pub(in crate::db::session::sql) returning_rows: Option<u32>,
pub(in crate::db::session::sql) returning_response_bytes: Option<u32>,
}
impl SqlWritePolicyBounds {
pub(in crate::db::session::sql) const fn new(
public_bounded_limit: u32,
returning_rows: Option<u32>,
returning_response_bytes: Option<u32>,
) -> Self {
Self {
public_bounded_limit,
returning_rows,
returning_response_bytes,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db::session::sql) struct SqlWritePlanCore<S> {
statement: S,
execution_bounds: SqlWriteExecutionBounds,
}
impl<S> SqlWritePlanCore<S> {
pub(in crate::db::session::sql) const fn new(
statement: S,
execution_bounds: SqlWriteExecutionBounds,
) -> Self {
Self {
statement,
execution_bounds,
}
}
pub(in crate::db::session::sql) const fn statement(&self) -> &S {
&self.statement
}
pub(in crate::db::session::sql) const fn execution_bounds(&self) -> SqlWriteExecutionBounds {
self.execution_bounds
}
}
impl<S: Clone> SqlWritePlanCore<S> {
pub(in crate::db::session::sql) fn from_borrowed(
statement: &S,
execution_bounds: SqlWriteExecutionBounds,
) -> Self {
Self::new(statement.clone(), execution_bounds)
}
}
#[cfg(test)]
mod tests {
use super::SqlWriteExposureClass;
#[test]
fn exposure_class_owns_its_staged_row_bound() {
assert_eq!(
SqlWriteExposureClass::PublicPrimaryKeyOnly.max_staged_rows(Some(10)),
Some(1),
);
assert_eq!(
SqlWriteExposureClass::PublicBoundedDeterministic.max_staged_rows(Some(10)),
Some(10),
);
assert_eq!(
SqlWriteExposureClass::PublicBoundedDeterministic.max_staged_rows(None),
None,
);
}
}