use crate::db::{
access::AccessPlanError,
cursor::CursorPlanError,
predicate::CompareOp,
query::plan::{
AggregateKind,
expr::{BinaryOp, ExprType, Function, UnaryOp},
},
schema::ValidateError,
};
#[derive(Debug)]
pub enum PlanError {
User(Box<PlanUserError>),
Policy(Box<PlanPolicyError>),
Cursor(Box<CursorPlanError>),
}
impl PlanError {
#[must_use]
pub fn is_unordered_pagination(&self) -> bool {
matches!(
self,
Self::Policy(inner)
if matches!(
inner.as_ref(),
PlanPolicyError::Policy(policy)
if matches!(policy.as_ref(), PolicyPlanError::UnorderedPagination)
)
)
}
}
#[derive(Debug)]
pub enum PlanUserError {
PredicateInvalid(Box<ValidateError>),
Order(Box<OrderPlanError>),
Access(Box<AccessPlanError>),
Group(Box<GroupPlanError>),
Expr(Box<ExprPlanError>),
}
#[derive(Debug)]
pub enum PlanPolicyError {
Policy(Box<PolicyPlanError>),
Group(Box<GroupPlanError>),
}
#[derive(Debug)]
pub enum OrderPlanError {
UnknownField { term_index: usize },
UnorderableField { term_index: usize },
DuplicateOrderField {
first_term_index: usize,
duplicate_term_index: usize,
},
MissingPrimaryKeyTieBreak { primary_key_index: usize },
}
impl OrderPlanError {
pub(in crate::db::query) const fn unknown_field(term_index: usize) -> Self {
Self::UnknownField { term_index }
}
pub(in crate::db::query) const fn unorderable_field(term_index: usize) -> Self {
Self::UnorderableField { term_index }
}
pub(in crate::db::query) const fn duplicate_order_field(
first_term_index: usize,
duplicate_term_index: usize,
) -> Self {
Self::DuplicateOrderField {
first_term_index,
duplicate_term_index,
}
}
pub(in crate::db::query) const fn missing_primary_key_tie_break(
primary_key_index: usize,
) -> Self {
Self::MissingPrimaryKeyTieBreak { primary_key_index }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PolicyPlanError {
EmptyOrderSpec,
DeletePlanWithGrouping,
DeletePlanWithPagination,
LoadPlanWithDeleteLimit,
DeleteWindowRequiresOrder,
UnorderedPagination,
ExpressionOrderRequiresIndexSatisfiedAccess,
}
impl PolicyPlanError {
pub(in crate::db::query) const fn empty_order_spec() -> Self {
Self::EmptyOrderSpec
}
pub(in crate::db::query) const fn delete_plan_with_grouping() -> Self {
Self::DeletePlanWithGrouping
}
pub(in crate::db::query) const fn delete_plan_with_pagination() -> Self {
Self::DeletePlanWithPagination
}
pub(in crate::db::query) const fn load_plan_with_delete_limit() -> Self {
Self::LoadPlanWithDeleteLimit
}
pub(in crate::db::query) const fn delete_window_requires_order() -> Self {
Self::DeleteWindowRequiresOrder
}
pub(in crate::db::query) const fn unordered_pagination() -> Self {
Self::UnorderedPagination
}
pub(in crate::db::query) const fn expression_order_requires_index_satisfied_access() -> Self {
Self::ExpressionOrderRequiresIndexSatisfiedAccess
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CursorPagingPolicyError {
CursorRequiresOrder,
CursorRequiresLimit,
}
impl CursorPagingPolicyError {
pub(in crate::db::query) const fn cursor_requires_order() -> Self {
Self::CursorRequiresOrder
}
pub(in crate::db::query) const fn cursor_requires_limit() -> Self {
Self::CursorRequiresLimit
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum GroupPlanError {
HavingRequiresGroupBy,
GroupedLogicalPlanRequired,
EmptyGroupFields,
GlobalDistinctAggregateShapeUnsupported,
EmptyAggregates,
UnknownGroupField { field: String },
DuplicateGroupField { field: String },
DistinctAdjacencyEligibilityRequired,
OrderPrefixNotAlignedWithGroupKeys,
OrderExpressionNotAdmissible { term: String },
OrderRequiresLimit,
DistinctHavingUnsupported,
HavingUnsupportedCompareOp { index: usize, op: CompareOp },
HavingNonGroupFieldReference { index: usize, field: String },
HavingAggregateIndexOutOfBounds {
index: usize,
aggregate_index: usize,
aggregate_count: usize,
},
DistinctAggregateKindUnsupported {
index: usize,
kind: Option<AggregateKind>,
},
DistinctAggregateFieldTargetUnsupported {
index: usize,
kind: AggregateKind,
field: String,
},
UnknownAggregateTargetField { index: usize, field: String },
GlobalDistinctSumTargetNotNumeric { index: usize, field: String },
FieldTargetAggregatesUnsupported {
index: usize,
kind: AggregateKind,
field: String,
},
}
impl GroupPlanError {
pub(in crate::db::query) const fn grouped_logical_plan_required() -> Self {
Self::GroupedLogicalPlanRequired
}
pub(in crate::db::query) const fn global_distinct_aggregate_shape_unsupported() -> Self {
Self::GlobalDistinctAggregateShapeUnsupported
}
pub(in crate::db::query) const fn distinct_adjacency_eligibility_required() -> Self {
Self::DistinctAdjacencyEligibilityRequired
}
pub(in crate::db::query) const fn distinct_having_unsupported() -> Self {
Self::DistinctHavingUnsupported
}
pub(in crate::db::query) fn unknown_group_field(field: impl Into<String>) -> Self {
Self::UnknownGroupField {
field: field.into(),
}
}
pub(in crate::db::query) fn duplicate_group_field(field: impl Into<String>) -> Self {
Self::DuplicateGroupField {
field: field.into(),
}
}
pub(in crate::db::query) const fn order_requires_limit() -> Self {
Self::OrderRequiresLimit
}
pub(in crate::db::query) const fn order_prefix_not_aligned_with_group_keys() -> Self {
Self::OrderPrefixNotAlignedWithGroupKeys
}
pub(in crate::db::query) fn order_expression_not_admissible(term: impl Into<String>) -> Self {
Self::OrderExpressionNotAdmissible { term: term.into() }
}
pub(in crate::db::query) const fn empty_aggregates() -> Self {
Self::EmptyAggregates
}
pub(in crate::db::query) fn having_non_group_field_reference(
index: usize,
field: impl Into<String>,
) -> Self {
Self::HavingNonGroupFieldReference {
index,
field: field.into(),
}
}
pub(in crate::db::query) const fn having_aggregate_index_out_of_bounds(
index: usize,
aggregate_index: usize,
aggregate_count: usize,
) -> Self {
Self::HavingAggregateIndexOutOfBounds {
index,
aggregate_index,
aggregate_count,
}
}
pub(in crate::db::query) const fn having_unsupported_compare_op(
index: usize,
op: CompareOp,
) -> Self {
Self::HavingUnsupportedCompareOp { index, op }
}
pub(in crate::db::query) const fn distinct_aggregate_kind_unsupported(
index: usize,
kind: Option<AggregateKind>,
) -> Self {
Self::DistinctAggregateKindUnsupported { index, kind }
}
pub(in crate::db::query) fn distinct_aggregate_field_target_unsupported(
index: usize,
kind: AggregateKind,
field: impl Into<String>,
) -> Self {
Self::DistinctAggregateFieldTargetUnsupported {
index,
kind,
field: field.into(),
}
}
pub(in crate::db::query) fn field_target_aggregates_unsupported(
index: usize,
kind: AggregateKind,
field: impl Into<String>,
) -> Self {
Self::FieldTargetAggregatesUnsupported {
index,
kind,
field: field.into(),
}
}
pub(in crate::db::query) fn global_distinct_sum_target_not_numeric(
index: usize,
field: impl Into<String>,
) -> Self {
Self::GlobalDistinctSumTargetNotNumeric {
index,
field: field.into(),
}
}
pub(in crate::db::query) fn unknown_aggregate_target_field(
index: usize,
field: impl Into<String>,
) -> Self {
Self::UnknownAggregateTargetField {
index,
field: field.into(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExprPlanTypeClass {
Blob,
Bool,
Collection,
#[cfg(test)]
Null,
Numeric,
Opaque,
Structured,
Text,
Unknown,
}
impl ExprPlanTypeClass {
pub(in crate::db) const fn from_expr_type(expr_type: &ExprType) -> Self {
match expr_type {
ExprType::Blob => Self::Blob,
ExprType::Bool => Self::Bool,
ExprType::Collection => Self::Collection,
#[cfg(test)]
ExprType::Null => Self::Null,
ExprType::Numeric(_) => Self::Numeric,
ExprType::Opaque => Self::Opaque,
ExprType::Structured => Self::Structured,
ExprType::Text => Self::Text,
ExprType::Unknown => Self::Unknown,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExprPlanUnaryOpCode(u8);
impl ExprPlanUnaryOpCode {
pub const NOT: Self = Self(0);
pub(in crate::db) const fn from_unary_op(op: UnaryOp) -> Self {
match op {
UnaryOp::Not => Self::NOT,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExprPlanBinaryOpCode(u8);
impl ExprPlanBinaryOpCode {
pub const ADD: Self = Self(0);
pub const AND: Self = Self(1);
pub const DIV: Self = Self(2);
pub const EQ: Self = Self(3);
pub const GT: Self = Self(4);
pub const GTE: Self = Self(5);
pub const LT: Self = Self(6);
pub const LTE: Self = Self(7);
pub const MUL: Self = Self(8);
pub const NE: Self = Self(9);
pub const OR: Self = Self(10);
pub const SUB: Self = Self(11);
pub(in crate::db) const fn from_binary_op(op: BinaryOp) -> Self {
match op {
BinaryOp::Add => Self::ADD,
BinaryOp::And => Self::AND,
BinaryOp::Div => Self::DIV,
BinaryOp::Eq => Self::EQ,
BinaryOp::Gt => Self::GT,
BinaryOp::Gte => Self::GTE,
BinaryOp::Lt => Self::LT,
BinaryOp::Lte => Self::LTE,
BinaryOp::Mul => Self::MUL,
BinaryOp::Ne => Self::NE,
BinaryOp::Or => Self::OR,
BinaryOp::Sub => Self::SUB,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ExprPlanFunctionCode(u8);
impl ExprPlanFunctionCode {
pub const ABS: Self = Self(0);
pub const CBRT: Self = Self(1);
pub const CEILING: Self = Self(2);
pub const COALESCE: Self = Self(3);
pub const COLLECTION_CONTAINS: Self = Self(4);
pub const CONTAINS: Self = Self(5);
pub const ENDS_WITH: Self = Self(6);
pub const EXP: Self = Self(7);
pub const FLOOR: Self = Self(8);
pub const IS_EMPTY: Self = Self(9);
pub const IS_MISSING: Self = Self(10);
pub const IS_NOT_EMPTY: Self = Self(11);
pub const IS_NOT_NULL: Self = Self(12);
pub const IS_NULL: Self = Self(13);
pub const LEFT: Self = Self(14);
pub const LENGTH: Self = Self(15);
pub const LN: Self = Self(16);
pub const LOG: Self = Self(17);
pub const LOG2: Self = Self(18);
pub const LOG10: Self = Self(19);
pub const LOWER: Self = Self(20);
pub const LTRIM: Self = Self(21);
pub const MOD: Self = Self(22);
pub const NULLIF: Self = Self(23);
pub const OCTET_LENGTH: Self = Self(24);
pub const POSITION: Self = Self(25);
pub const POWER: Self = Self(26);
pub const REPLACE: Self = Self(27);
pub const RIGHT: Self = Self(28);
pub const ROUND: Self = Self(29);
pub const RTRIM: Self = Self(30);
pub const SIGN: Self = Self(31);
pub const SQRT: Self = Self(32);
pub const STARTS_WITH: Self = Self(33);
pub const SUBSTRING: Self = Self(34);
pub const TRIM: Self = Self(35);
pub const TRUNC: Self = Self(36);
pub const UPPER: Self = Self(37);
pub(in crate::db) const fn from_function(function: Function) -> Self {
match function {
Function::Abs => Self::ABS,
Function::Cbrt => Self::CBRT,
Function::Ceiling => Self::CEILING,
Function::Coalesce => Self::COALESCE,
Function::CollectionContains => Self::COLLECTION_CONTAINS,
Function::Contains => Self::CONTAINS,
Function::EndsWith => Self::ENDS_WITH,
Function::Exp => Self::EXP,
Function::Floor => Self::FLOOR,
Function::IsEmpty => Self::IS_EMPTY,
Function::IsMissing => Self::IS_MISSING,
Function::IsNotEmpty => Self::IS_NOT_EMPTY,
Function::IsNotNull => Self::IS_NOT_NULL,
Function::IsNull => Self::IS_NULL,
Function::Left => Self::LEFT,
Function::Length => Self::LENGTH,
Function::Ln => Self::LN,
Function::Log => Self::LOG,
Function::Log2 => Self::LOG2,
Function::Log10 => Self::LOG10,
Function::Lower => Self::LOWER,
Function::Ltrim => Self::LTRIM,
Function::Mod => Self::MOD,
Function::NullIf => Self::NULLIF,
Function::OctetLength => Self::OCTET_LENGTH,
Function::Position => Self::POSITION,
Function::Power => Self::POWER,
Function::Replace => Self::REPLACE,
Function::Right => Self::RIGHT,
Function::Round => Self::ROUND,
Function::Rtrim => Self::RTRIM,
Function::Sign => Self::SIGN,
Function::Sqrt => Self::SQRT,
Function::StartsWith => Self::STARTS_WITH,
Function::Substring => Self::SUBSTRING,
Function::Trim => Self::TRIM,
Function::Trunc => Self::TRUNC,
Function::Upper => Self::UPPER,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ExprPlanError {
UnknownField { field: String },
UnknownExprField { field: String },
NonNumericAggregateTarget {
kind: AggregateKind,
found: ExprPlanTypeClass,
},
AggregateTargetRequired { kind: AggregateKind },
InvalidFunctionArity {
function: ExprPlanFunctionCode,
expected: usize,
actual: usize,
},
InvalidFunctionArgument {
function: ExprPlanFunctionCode,
argument_index: usize,
found: ExprPlanTypeClass,
},
IncompatibleFunctionArguments {
function: ExprPlanFunctionCode,
left_argument_index: usize,
right_argument_index: usize,
left: ExprPlanTypeClass,
right: ExprPlanTypeClass,
},
InvalidUnaryOperand {
op: ExprPlanUnaryOpCode,
found: ExprPlanTypeClass,
},
InvalidCaseConditionType {
arm_index: usize,
found: ExprPlanTypeClass,
},
IncompatibleCaseBranchTypes {
left_branch_index: Option<usize>,
right_branch_index: Option<usize>,
left: ExprPlanTypeClass,
right: ExprPlanTypeClass,
},
InvalidBinaryOperands {
op: ExprPlanBinaryOpCode,
left: ExprPlanTypeClass,
right: ExprPlanTypeClass,
},
GroupedProjectionReferencesNonGroupField { index: usize },
}
impl ExprPlanError {
#[cfg(feature = "sql")]
pub(in crate::db::query) fn unknown_field(field: impl Into<String>) -> Self {
Self::UnknownField {
field: field.into(),
}
}
pub(in crate::db::query) fn unknown_expr_field(field: impl Into<String>) -> Self {
Self::UnknownExprField {
field: field.into(),
}
}
pub(in crate::db::query) const fn aggregate_target_required(kind: AggregateKind) -> Self {
Self::AggregateTargetRequired { kind }
}
pub(in crate::db::query) const fn non_numeric_aggregate_target(
kind: AggregateKind,
found: ExprPlanTypeClass,
) -> Self {
Self::NonNumericAggregateTarget { kind, found }
}
pub(in crate::db::query) const fn invalid_function_arity(
function: Function,
expected: usize,
actual: usize,
) -> Self {
Self::InvalidFunctionArity {
function: ExprPlanFunctionCode::from_function(function),
expected,
actual,
}
}
pub(in crate::db::query) const fn invalid_function_argument(
function: Function,
argument_index: usize,
found: ExprPlanTypeClass,
) -> Self {
Self::InvalidFunctionArgument {
function: ExprPlanFunctionCode::from_function(function),
argument_index,
found,
}
}
pub(in crate::db::query) const fn incompatible_function_arguments(
function: Function,
left_argument_index: usize,
right_argument_index: usize,
left: ExprPlanTypeClass,
right: ExprPlanTypeClass,
) -> Self {
Self::IncompatibleFunctionArguments {
function: ExprPlanFunctionCode::from_function(function),
left_argument_index,
right_argument_index,
left,
right,
}
}
pub(in crate::db::query) const fn invalid_unary_operand(
op: UnaryOp,
found: ExprPlanTypeClass,
) -> Self {
Self::InvalidUnaryOperand {
op: ExprPlanUnaryOpCode::from_unary_op(op),
found,
}
}
pub(in crate::db::query) const fn invalid_case_condition_type(
arm_index: usize,
found: ExprPlanTypeClass,
) -> Self {
Self::InvalidCaseConditionType { arm_index, found }
}
pub(in crate::db::query) const fn incompatible_case_branch_types(
left_branch_index: Option<usize>,
right_branch_index: Option<usize>,
left: ExprPlanTypeClass,
right: ExprPlanTypeClass,
) -> Self {
Self::IncompatibleCaseBranchTypes {
left_branch_index,
right_branch_index,
left,
right,
}
}
pub(in crate::db::query) const fn invalid_binary_operands(
op: BinaryOp,
left: ExprPlanTypeClass,
right: ExprPlanTypeClass,
) -> Self {
Self::InvalidBinaryOperands {
op: ExprPlanBinaryOpCode::from_binary_op(op),
left,
right,
}
}
pub(in crate::db::query) const fn grouped_projection_references_non_group_field(
index: usize,
) -> Self {
Self::GroupedProjectionReferencesNonGroupField { index }
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db) enum CursorOrderPlanShapeError {
MissingExplicitOrder,
EmptyOrderSpec,
}
impl CursorOrderPlanShapeError {
pub(in crate::db) const fn missing_explicit_order() -> Self {
Self::MissingExplicitOrder
}
pub(in crate::db) const fn empty_order_spec() -> Self {
Self::EmptyOrderSpec
}
pub(in crate::db) const fn to_cursor_plan_error(self) -> CursorPlanError {
match self {
Self::MissingExplicitOrder => CursorPlanError::continuation_cursor_invariant(),
Self::EmptyOrderSpec => CursorPlanError::cursor_requires_non_empty_order(),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::query) enum IntentKeyAccessKind {
Single,
Many,
Only,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::query) enum IntentKeyAccessPolicyViolation {
KeyAccessConflict,
ByIdsWithPredicate,
OnlyWithPredicate,
}
impl IntentKeyAccessPolicyViolation {
pub(in crate::db::query) const fn key_access_conflict() -> Self {
Self::KeyAccessConflict
}
pub(in crate::db::query) const fn by_ids_with_predicate() -> Self {
Self::ByIdsWithPredicate
}
pub(in crate::db::query) const fn only_with_predicate() -> Self {
Self::OnlyWithPredicate
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::query) enum FluentLoadPolicyViolation {
CursorRequiresPagedExecution,
GroupedRequiresDirectExecute,
CursorRequiresOrder,
CursorRequiresLimit,
}
impl FluentLoadPolicyViolation {
pub(in crate::db::query) const fn cursor_requires_paged_execution() -> Self {
Self::CursorRequiresPagedExecution
}
pub(in crate::db::query) const fn grouped_requires_direct_execute() -> Self {
Self::GroupedRequiresDirectExecute
}
pub(in crate::db::query) const fn cursor_requires_order() -> Self {
Self::CursorRequiresOrder
}
pub(in crate::db::query) const fn cursor_requires_limit() -> Self {
Self::CursorRequiresLimit
}
}
impl From<CursorPagingPolicyError> for FluentLoadPolicyViolation {
fn from(err: CursorPagingPolicyError) -> Self {
match err {
CursorPagingPolicyError::CursorRequiresOrder => Self::cursor_requires_order(),
CursorPagingPolicyError::CursorRequiresLimit => Self::cursor_requires_limit(),
}
}
}
impl From<ValidateError> for PlanError {
fn from(err: ValidateError) -> Self {
Self::from(PlanUserError::from(err))
}
}
impl From<OrderPlanError> for PlanError {
fn from(err: OrderPlanError) -> Self {
Self::from(PlanUserError::from(err))
}
}
impl From<AccessPlanError> for PlanError {
fn from(err: AccessPlanError) -> Self {
Self::from(PlanUserError::from(err))
}
}
impl From<PolicyPlanError> for PlanError {
fn from(err: PolicyPlanError) -> Self {
Self::from(PlanPolicyError::from(err))
}
}
impl From<CursorPlanError> for PlanError {
fn from(err: CursorPlanError) -> Self {
Self::Cursor(Box::new(err))
}
}
impl From<GroupPlanError> for PlanError {
fn from(err: GroupPlanError) -> Self {
if err.belongs_to_policy_axis() {
return Self::from(PlanPolicyError::from(err));
}
Self::from(PlanUserError::from(err))
}
}
impl From<ExprPlanError> for PlanError {
fn from(err: ExprPlanError) -> Self {
Self::from(PlanUserError::from(err))
}
}
impl From<PlanUserError> for PlanError {
fn from(err: PlanUserError) -> Self {
Self::User(Box::new(err))
}
}
impl From<PlanPolicyError> for PlanError {
fn from(err: PlanPolicyError) -> Self {
Self::Policy(Box::new(err))
}
}
impl From<ValidateError> for PlanUserError {
fn from(err: ValidateError) -> Self {
Self::PredicateInvalid(Box::new(err))
}
}
impl From<OrderPlanError> for PlanUserError {
fn from(err: OrderPlanError) -> Self {
Self::Order(Box::new(err))
}
}
impl From<AccessPlanError> for PlanUserError {
fn from(err: AccessPlanError) -> Self {
Self::Access(Box::new(err))
}
}
impl From<GroupPlanError> for PlanUserError {
fn from(err: GroupPlanError) -> Self {
Self::Group(Box::new(err))
}
}
impl From<ExprPlanError> for PlanUserError {
fn from(err: ExprPlanError) -> Self {
Self::Expr(Box::new(err))
}
}
impl From<PolicyPlanError> for PlanPolicyError {
fn from(err: PolicyPlanError) -> Self {
Self::Policy(Box::new(err))
}
}
impl From<GroupPlanError> for PlanPolicyError {
fn from(err: GroupPlanError) -> Self {
Self::Group(Box::new(err))
}
}
impl GroupPlanError {
const fn belongs_to_policy_axis(&self) -> bool {
matches!(
self,
Self::GlobalDistinctAggregateShapeUnsupported
| Self::DistinctAdjacencyEligibilityRequired
| Self::OrderPrefixNotAlignedWithGroupKeys
| Self::OrderExpressionNotAdmissible { .. }
| Self::OrderRequiresLimit
| Self::DistinctHavingUnsupported
| Self::HavingUnsupportedCompareOp { .. }
| Self::DistinctAggregateKindUnsupported { .. }
| Self::DistinctAggregateFieldTargetUnsupported { .. }
| Self::FieldTargetAggregatesUnsupported { .. }
)
}
}