icydb_core/db/query/plan/
mod.rs1mod access_projection;
4pub(crate) mod planner;
5#[cfg(test)]
6mod tests;
7pub(crate) mod validate;
8
9use crate::{
10 db::{
11 access::{
12 AccessPlan, PushdownApplicability, SecondaryOrderPushdownEligibility,
13 assess_secondary_order_pushdown_from_parts,
14 assess_secondary_order_pushdown_if_applicable_validated_from_parts,
15 },
16 contracts::ReadConsistency,
17 direction::Direction,
18 query::predicate::Predicate,
19 },
20 model::entity::EntityModel,
21};
22use std::ops::{Deref, DerefMut};
23
24pub(in crate::db) use crate::db::query::fingerprint::canonical;
25pub(crate) use access_projection::{
26 AccessPlanProjection, project_access_plan, project_explain_access_path,
27};
28
29pub(crate) use validate::OrderPlanError;
30pub use validate::PlanError;
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub enum QueryMode {
44 Load(LoadSpec),
45 Delete(DeleteSpec),
46}
47
48impl QueryMode {
49 #[must_use]
51 pub const fn is_load(&self) -> bool {
52 match self {
53 Self::Load(_) => true,
54 Self::Delete(_) => false,
55 }
56 }
57
58 #[must_use]
60 pub const fn is_delete(&self) -> bool {
61 match self {
62 Self::Delete(_) => true,
63 Self::Load(_) => false,
64 }
65 }
66}
67
68#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
75pub struct LoadSpec {
76 pub limit: Option<u32>,
77 pub offset: u32,
78}
79
80impl LoadSpec {
81 #[must_use]
83 pub const fn new() -> Self {
84 Self {
85 limit: None,
86 offset: 0,
87 }
88 }
89}
90
91#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
98pub struct DeleteSpec {
99 pub limit: Option<u32>,
100}
101
102impl DeleteSpec {
103 #[must_use]
105 pub const fn new() -> Self {
106 Self { limit: None }
107 }
108}
109
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
115pub enum OrderDirection {
116 Asc,
117 Desc,
118}
119
120#[derive(Clone, Debug, Eq, PartialEq)]
125pub(crate) struct OrderSpec {
126 pub(crate) fields: Vec<(String, OrderDirection)>,
127}
128
129#[derive(Clone, Copy, Debug, Eq, PartialEq)]
134pub(crate) struct DeleteLimitSpec {
135 pub max_rows: u32,
136}
137
138#[derive(Clone, Debug, Eq, PartialEq)]
143pub(crate) struct PageSpec {
144 pub limit: Option<u32>,
145 pub offset: u32,
146}
147
148#[derive(Clone, Debug, Eq, PartialEq)]
168pub(crate) struct LogicalPlan {
169 pub(crate) mode: QueryMode,
171
172 pub(crate) predicate: Option<Predicate>,
174
175 pub(crate) order: Option<OrderSpec>,
177
178 pub(crate) distinct: bool,
180
181 pub(crate) delete_limit: Option<DeleteLimitSpec>,
183
184 pub(crate) page: Option<PageSpec>,
186
187 pub(crate) consistency: ReadConsistency,
189}
190
191#[derive(Clone, Debug, Eq, PartialEq)]
198pub(crate) struct AccessPlannedQuery<K> {
199 pub(crate) logical: LogicalPlan,
200 pub(crate) access: AccessPlan<K>,
201}
202
203impl<K> AccessPlannedQuery<K> {
204 #[must_use]
206 pub(crate) const fn from_parts(logical: LogicalPlan, access: AccessPlan<K>) -> Self {
207 Self { logical, access }
208 }
209
210 #[must_use]
212 pub(crate) fn into_parts(self) -> (LogicalPlan, AccessPlan<K>) {
213 (self.logical, self.access)
214 }
215
216 #[cfg(test)]
220 pub(crate) fn new(
221 access: crate::db::access::AccessPath<K>,
222 consistency: ReadConsistency,
223 ) -> Self {
224 Self {
225 logical: LogicalPlan {
226 mode: QueryMode::Load(LoadSpec::new()),
227 predicate: None,
228 order: None,
229 distinct: false,
230 delete_limit: None,
231 page: None,
232 consistency,
233 },
234 access: AccessPlan::path(access),
235 }
236 }
237}
238
239impl<K> Deref for AccessPlannedQuery<K> {
240 type Target = LogicalPlan;
241
242 fn deref(&self) -> &Self::Target {
243 &self.logical
244 }
245}
246
247impl<K> DerefMut for AccessPlannedQuery<K> {
248 fn deref_mut(&mut self) -> &mut Self::Target {
249 &mut self.logical
250 }
251}
252
253fn direction_from_order(direction: OrderDirection) -> Direction {
254 if direction == OrderDirection::Desc {
255 Direction::Desc
256 } else {
257 Direction::Asc
258 }
259}
260
261fn order_fields_as_direction_refs(
262 order_fields: &[(String, OrderDirection)],
263) -> Vec<(&str, Direction)> {
264 order_fields
265 .iter()
266 .map(|(field, direction)| (field.as_str(), direction_from_order(*direction)))
267 .collect()
268}
269
270pub(crate) fn assess_secondary_order_pushdown<K>(
272 model: &EntityModel,
273 plan: &AccessPlannedQuery<K>,
274) -> SecondaryOrderPushdownEligibility {
275 let order_fields = plan
276 .order
277 .as_ref()
278 .map(|order| order_fields_as_direction_refs(&order.fields));
279
280 assess_secondary_order_pushdown_from_parts(model, order_fields.as_deref(), &plan.access)
281}
282
283#[cfg(test)]
284pub(crate) fn assess_secondary_order_pushdown_if_applicable<K>(
286 model: &EntityModel,
287 plan: &AccessPlannedQuery<K>,
288) -> PushdownApplicability {
289 let order_fields = plan
290 .order
291 .as_ref()
292 .map(|order| order_fields_as_direction_refs(&order.fields));
293
294 crate::db::access::assess_secondary_order_pushdown_if_applicable_from_parts(
295 model,
296 order_fields.as_deref(),
297 &plan.access,
298 )
299}
300
301pub(crate) fn assess_secondary_order_pushdown_if_applicable_validated<K>(
304 model: &EntityModel,
305 plan: &AccessPlannedQuery<K>,
306) -> PushdownApplicability {
307 let order_fields = plan
308 .order
309 .as_ref()
310 .map(|order| order_fields_as_direction_refs(&order.fields));
311
312 assess_secondary_order_pushdown_if_applicable_validated_from_parts(
313 model,
314 order_fields.as_deref(),
315 &plan.access,
316 )
317}