1use crate::{
8 db::{
9 DbSession, EntityResponse, LoadQueryResult, PagedGroupedExecutionWithTrace,
10 PagedLoadExecutionWithTrace, PersistedRow, Query, QueryError, QueryTracePlan,
11 access::AccessStrategy,
12 cursor::{
13 CursorPlanError, decode_optional_cursor_token, decode_optional_grouped_cursor_token,
14 },
15 diagnostics::ExecutionTrace,
16 executor::{ExecutionFamily, GroupedCursorPage, LoadExecutor, PreparedExecutionPlan},
17 query::builder::{
18 PreparedFluentAggregateExplainStrategy, PreparedFluentProjectionStrategy,
19 },
20 query::explain::{
21 ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor, ExplainPlan,
22 },
23 query::intent::{CompiledQuery, PlannedQuery},
24 query::plan::QueryMode,
25 },
26 error::InternalError,
27 traits::{CanisterKind, EntityKind, EntityValue, Path},
28};
29
30impl<C: CanisterKind> DbSession<C> {
31 fn with_query_visible_indexes<E, T>(
34 &self,
35 query: &Query<E>,
36 op: impl FnOnce(
37 &Query<E>,
38 &crate::db::query::plan::VisibleIndexes<'static>,
39 ) -> Result<T, QueryError>,
40 ) -> Result<T, QueryError>
41 where
42 E: EntityKind<Canister = C>,
43 {
44 let visible_indexes = self.visible_indexes_for_store_model(E::Store::PATH, E::MODEL)?;
45
46 op(query, &visible_indexes)
47 }
48
49 pub(in crate::db) fn compile_query_with_visible_indexes<E>(
52 &self,
53 query: &Query<E>,
54 ) -> Result<CompiledQuery<E>, QueryError>
55 where
56 E: EntityKind<Canister = C>,
57 {
58 self.with_query_visible_indexes(query, |query, visible_indexes| {
59 query.plan_with_visible_indexes(visible_indexes)
60 })
61 }
62
63 pub(in crate::db) fn planned_query_with_visible_indexes<E>(
66 &self,
67 query: &Query<E>,
68 ) -> Result<PlannedQuery<E>, QueryError>
69 where
70 E: EntityKind<Canister = C>,
71 {
72 self.with_query_visible_indexes(query, |query, visible_indexes| {
73 query.planned_with_visible_indexes(visible_indexes)
74 })
75 }
76
77 pub(in crate::db) fn explain_query_with_visible_indexes<E>(
79 &self,
80 query: &Query<E>,
81 ) -> Result<ExplainPlan, QueryError>
82 where
83 E: EntityKind<Canister = C>,
84 {
85 self.with_query_visible_indexes(query, |query, visible_indexes| {
86 query.explain_with_visible_indexes(visible_indexes)
87 })
88 }
89
90 pub(in crate::db) fn query_plan_hash_hex_with_visible_indexes<E>(
93 &self,
94 query: &Query<E>,
95 ) -> Result<String, QueryError>
96 where
97 E: EntityKind<Canister = C>,
98 {
99 self.with_query_visible_indexes(query, |query, visible_indexes| {
100 query.plan_hash_hex_with_visible_indexes(visible_indexes)
101 })
102 }
103
104 pub(in crate::db) fn explain_query_execution_with_visible_indexes<E>(
107 &self,
108 query: &Query<E>,
109 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
110 where
111 E: EntityValue + EntityKind<Canister = C>,
112 {
113 self.with_query_visible_indexes(query, |query, visible_indexes| {
114 query.explain_execution_with_visible_indexes(visible_indexes)
115 })
116 }
117
118 pub(in crate::db) fn explain_query_execution_text_with_visible_indexes<E>(
121 &self,
122 query: &Query<E>,
123 ) -> Result<String, QueryError>
124 where
125 E: EntityValue + EntityKind<Canister = C>,
126 {
127 self.with_query_visible_indexes(query, |query, visible_indexes| {
128 query.explain_execution_text_with_visible_indexes(visible_indexes)
129 })
130 }
131
132 pub(in crate::db) fn explain_query_execution_json_with_visible_indexes<E>(
135 &self,
136 query: &Query<E>,
137 ) -> Result<String, QueryError>
138 where
139 E: EntityValue + EntityKind<Canister = C>,
140 {
141 self.with_query_visible_indexes(query, |query, visible_indexes| {
142 query.explain_execution_json_with_visible_indexes(visible_indexes)
143 })
144 }
145
146 pub(in crate::db) fn explain_query_execution_verbose_with_visible_indexes<E>(
149 &self,
150 query: &Query<E>,
151 ) -> Result<String, QueryError>
152 where
153 E: EntityValue + EntityKind<Canister = C>,
154 {
155 self.with_query_visible_indexes(query, |query, visible_indexes| {
156 query.explain_execution_verbose_with_visible_indexes(visible_indexes)
157 })
158 }
159
160 pub(in crate::db) fn explain_query_prepared_aggregate_terminal_with_visible_indexes<E, S>(
163 &self,
164 query: &Query<E>,
165 strategy: &S,
166 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
167 where
168 E: EntityValue + EntityKind<Canister = C>,
169 S: PreparedFluentAggregateExplainStrategy,
170 {
171 self.with_query_visible_indexes(query, |query, visible_indexes| {
172 query
173 .explain_prepared_aggregate_terminal_with_visible_indexes(visible_indexes, strategy)
174 })
175 }
176
177 pub(in crate::db) fn explain_query_bytes_by_with_visible_indexes<E>(
180 &self,
181 query: &Query<E>,
182 target_field: &str,
183 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
184 where
185 E: EntityValue + EntityKind<Canister = C>,
186 {
187 self.with_query_visible_indexes(query, |query, visible_indexes| {
188 query.explain_bytes_by_with_visible_indexes(visible_indexes, target_field)
189 })
190 }
191
192 pub(in crate::db) fn explain_query_prepared_projection_terminal_with_visible_indexes<E>(
195 &self,
196 query: &Query<E>,
197 strategy: &PreparedFluentProjectionStrategy,
198 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
199 where
200 E: EntityValue + EntityKind<Canister = C>,
201 {
202 self.with_query_visible_indexes(query, |query, visible_indexes| {
203 query.explain_prepared_projection_terminal_with_visible_indexes(
204 visible_indexes,
205 strategy,
206 )
207 })
208 }
209
210 fn ensure_scalar_paged_execution_family(family: ExecutionFamily) -> Result<(), QueryError> {
213 match family {
214 ExecutionFamily::PrimaryKey => Err(QueryError::invariant(
215 CursorPlanError::cursor_requires_explicit_or_grouped_ordering_message(),
216 )),
217 ExecutionFamily::Ordered => Ok(()),
218 ExecutionFamily::Grouped => Err(QueryError::invariant(
219 "grouped queries execute via execute(), not page().execute()",
220 )),
221 }
222 }
223
224 fn ensure_grouped_execution_family(family: ExecutionFamily) -> Result<(), QueryError> {
227 match family {
228 ExecutionFamily::Grouped => Ok(()),
229 ExecutionFamily::PrimaryKey | ExecutionFamily::Ordered => Err(QueryError::invariant(
230 "grouped execution requires grouped logical plans",
231 )),
232 }
233 }
234
235 pub fn execute_query<E>(&self, query: &Query<E>) -> Result<EntityResponse<E>, QueryError>
237 where
238 E: PersistedRow<Canister = C> + EntityValue,
239 {
240 let mode = query.mode();
242 let plan = self
243 .compile_query_with_visible_indexes(query)?
244 .into_prepared_execution_plan();
245
246 self.execute_query_dyn(mode, plan)
248 }
249
250 #[doc(hidden)]
253 pub fn execute_query_result<E>(
254 &self,
255 query: &Query<E>,
256 ) -> Result<LoadQueryResult<E>, QueryError>
257 where
258 E: PersistedRow<Canister = C> + EntityValue,
259 {
260 if query.has_grouping() {
261 return self
262 .execute_grouped(query, None)
263 .map(LoadQueryResult::Grouped);
264 }
265
266 self.execute_query(query).map(LoadQueryResult::Rows)
267 }
268
269 #[doc(hidden)]
271 pub fn execute_delete_count<E>(&self, query: &Query<E>) -> Result<u32, QueryError>
272 where
273 E: PersistedRow<Canister = C> + EntityValue,
274 {
275 if !query.mode().is_delete() {
277 return Err(QueryError::unsupported_query(
278 "delete count execution requires delete query mode",
279 ));
280 }
281
282 let plan = self
284 .compile_query_with_visible_indexes(query)?
285 .into_prepared_execution_plan();
286
287 self.with_metrics(|| self.delete_executor::<E>().execute_count(plan))
289 .map_err(QueryError::execute)
290 }
291
292 pub(in crate::db) fn execute_query_dyn<E>(
297 &self,
298 mode: QueryMode,
299 plan: PreparedExecutionPlan<E>,
300 ) -> Result<EntityResponse<E>, QueryError>
301 where
302 E: PersistedRow<Canister = C> + EntityValue,
303 {
304 let result = match mode {
305 QueryMode::Load(_) => self.with_metrics(|| self.load_executor::<E>().execute(plan)),
306 QueryMode::Delete(_) => self.with_metrics(|| self.delete_executor::<E>().execute(plan)),
307 };
308
309 result.map_err(QueryError::execute)
310 }
311
312 pub(in crate::db) fn execute_load_query_with<E, T>(
315 &self,
316 query: &Query<E>,
317 op: impl FnOnce(LoadExecutor<E>, PreparedExecutionPlan<E>) -> Result<T, InternalError>,
318 ) -> Result<T, QueryError>
319 where
320 E: PersistedRow<Canister = C> + EntityValue,
321 {
322 let plan = self
323 .compile_query_with_visible_indexes(query)?
324 .into_prepared_execution_plan();
325
326 self.with_metrics(|| op(self.load_executor::<E>(), plan))
327 .map_err(QueryError::execute)
328 }
329
330 pub fn trace_query<E>(&self, query: &Query<E>) -> Result<QueryTracePlan, QueryError>
335 where
336 E: EntityKind<Canister = C>,
337 {
338 let compiled = self.compile_query_with_visible_indexes(query)?;
339 let explain = compiled.explain();
340 let plan_hash = compiled.plan_hash_hex();
341
342 let executable = compiled.into_prepared_execution_plan();
343 let access_strategy = AccessStrategy::from_plan(executable.access()).debug_summary();
344 let execution_family = match query.mode() {
345 QueryMode::Load(_) => Some(executable.execution_family().map_err(QueryError::execute)?),
346 QueryMode::Delete(_) => None,
347 };
348
349 Ok(QueryTracePlan::new(
350 plan_hash,
351 access_strategy,
352 execution_family,
353 explain,
354 ))
355 }
356
357 pub(crate) fn execute_load_query_paged_with_trace<E>(
359 &self,
360 query: &Query<E>,
361 cursor_token: Option<&str>,
362 ) -> Result<PagedLoadExecutionWithTrace<E>, QueryError>
363 where
364 E: PersistedRow<Canister = C> + EntityValue,
365 {
366 let plan = self
368 .compile_query_with_visible_indexes(query)?
369 .into_prepared_execution_plan();
370 Self::ensure_scalar_paged_execution_family(
371 plan.execution_family().map_err(QueryError::execute)?,
372 )?;
373
374 let cursor_bytes = decode_optional_cursor_token(cursor_token)
376 .map_err(QueryError::from_cursor_plan_error)?;
377 let cursor = plan
378 .prepare_cursor(cursor_bytes.as_deref())
379 .map_err(QueryError::from_executor_plan_error)?;
380
381 let (page, trace) = self
383 .with_metrics(|| {
384 self.load_executor::<E>()
385 .execute_paged_with_cursor_traced(plan, cursor)
386 })
387 .map_err(QueryError::execute)?;
388 let next_cursor = page
389 .next_cursor
390 .map(|token| {
391 let Some(token) = token.as_scalar() else {
392 return Err(QueryError::scalar_paged_emitted_grouped_continuation());
393 };
394
395 token.encode().map_err(|err| {
396 QueryError::serialize_internal(format!(
397 "failed to serialize continuation cursor: {err}"
398 ))
399 })
400 })
401 .transpose()?;
402
403 Ok(PagedLoadExecutionWithTrace::new(
404 page.items,
405 next_cursor,
406 trace,
407 ))
408 }
409
410 pub(in crate::db) fn execute_grouped<E>(
415 &self,
416 query: &Query<E>,
417 cursor_token: Option<&str>,
418 ) -> Result<PagedGroupedExecutionWithTrace, QueryError>
419 where
420 E: PersistedRow<Canister = C> + EntityValue,
421 {
422 let (page, trace) = self.execute_grouped_page_with_trace(query, cursor_token)?;
423 let next_cursor = page
424 .next_cursor
425 .map(|token| {
426 let Some(token) = token.as_grouped() else {
427 return Err(QueryError::grouped_paged_emitted_scalar_continuation());
428 };
429
430 token.encode().map_err(|err| {
431 QueryError::serialize_internal(format!(
432 "failed to serialize grouped continuation cursor: {err}"
433 ))
434 })
435 })
436 .transpose()?;
437
438 Ok(PagedGroupedExecutionWithTrace::new(
439 page.rows,
440 next_cursor,
441 trace,
442 ))
443 }
444
445 fn execute_grouped_page_with_trace<E>(
448 &self,
449 query: &Query<E>,
450 cursor_token: Option<&str>,
451 ) -> Result<(GroupedCursorPage, Option<ExecutionTrace>), QueryError>
452 where
453 E: PersistedRow<Canister = C> + EntityValue,
454 {
455 let plan = self
457 .compile_query_with_visible_indexes(query)?
458 .into_prepared_execution_plan();
459 Self::ensure_grouped_execution_family(
460 plan.execution_family().map_err(QueryError::execute)?,
461 )?;
462
463 let cursor = decode_optional_grouped_cursor_token(cursor_token)
465 .map_err(QueryError::from_cursor_plan_error)?;
466 let cursor = plan
467 .prepare_grouped_cursor_token(cursor)
468 .map_err(QueryError::from_executor_plan_error)?;
469
470 self.with_metrics(|| {
473 self.load_executor::<E>()
474 .execute_grouped_paged_with_cursor_traced(plan, cursor)
475 })
476 .map_err(QueryError::execute)
477 }
478}