icydb_core/db/query/plan/
executable.rs1use crate::{
2 db::query::{
3 QueryMode,
4 plan::{
5 ContinuationSignature, CursorBoundary, ExplainPlan, LogicalPlan, PlanError,
6 PlanFingerprint,
7 continuation::{decode_primary_key_cursor_slot, decode_validated_cursor_boundary},
8 },
9 policy,
10 predicate::SchemaInfo,
11 },
12 traits::{EntityKind, FieldValue},
13};
14use std::marker::PhantomData;
15
16#[derive(Debug)]
23pub struct ExecutablePlan<E: EntityKind> {
24 plan: LogicalPlan<E::Key>,
25 _marker: PhantomData<E>,
26}
27
28impl<E: EntityKind> ExecutablePlan<E> {
29 pub(crate) const fn new(plan: LogicalPlan<E::Key>) -> Self {
30 Self {
31 plan,
32 _marker: PhantomData,
33 }
34 }
35
36 #[must_use]
38 pub fn explain(&self) -> ExplainPlan {
39 self.plan.explain()
40 }
41
42 #[must_use]
44 pub fn fingerprint(&self) -> PlanFingerprint {
45 self.plan.fingerprint()
46 }
47
48 #[must_use]
52 pub fn continuation_signature(&self) -> ContinuationSignature {
53 self.plan.continuation_signature(E::PATH)
54 }
55
56 pub(crate) fn plan_cursor_boundary(
61 &self,
62 cursor: Option<&[u8]>,
63 ) -> Result<Option<CursorBoundary>, PlanError>
64 where
65 E::Key: FieldValue,
66 {
67 let Some(cursor) = cursor else {
68 return Ok(None);
69 };
70 let order =
71 policy::require_cursor_order(self.plan.order.as_ref()).map_err(PlanError::from)?;
72
73 let boundary = decode_validated_cursor_boundary(
74 cursor,
75 E::PATH,
76 E::MODEL,
77 order,
78 self.continuation_signature(),
79 )?;
80
81 let pk_field = E::MODEL.primary_key.name;
83 let pk_index = order
84 .fields
85 .iter()
86 .position(|(field, _)| field == pk_field)
87 .ok_or_else(|| PlanError::MissingPrimaryKeyTieBreak {
88 field: pk_field.to_string(),
89 })?;
90 let expected = SchemaInfo::from_entity_model(E::MODEL)
91 .map_err(PlanError::PredicateInvalid)?
92 .field(pk_field)
93 .expect("primary key exists by model contract")
94 .to_string();
95 let pk_slot = &boundary.slots[pk_index];
96 let _pk_key = decode_primary_key_cursor_slot::<E::Key>(pk_slot).map_err(|err| {
97 PlanError::ContinuationCursorPrimaryKeyTypeMismatch {
98 field: pk_field.to_string(),
99 expected,
100 value: err.into_mismatch_value(),
101 }
102 })?;
103
104 Ok(Some(boundary))
105 }
106
107 #[must_use]
109 pub(crate) const fn mode(&self) -> QueryMode {
110 self.plan.mode
111 }
112
113 pub(crate) const fn access(&self) -> &crate::db::query::plan::AccessPlan<E::Key> {
114 &self.plan.access
115 }
116
117 pub(crate) fn into_inner(self) -> LogicalPlan<E::Key> {
118 self.plan
119 }
120}