Skip to main content

nodedb_sql/visitor/plan_visitor/
dispatch.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Exhaustive [`SqlPlan`] → [`PlanVisitor`] dispatcher; missing arms fail to compile.
3//!
4//! Split across two files to stay under the file-size limit: this file
5//! handles the read/DML/search variant family and forwards everything else
6//! to [`super::dispatch_rest::dispatch_rest`], which exhaustively matches
7//! the remaining variants (set-ops, array, merge, lateral, index DDL).
8use super::args::{
9    AggregateVisitArgs, DocumentIndexLookupVisitArgs, HybridSearchTripleVisitArgs,
10    HybridSearchVisitArgs, InsertVisitArgs, JoinVisitArgs, RecursiveScanVisitArgs,
11    RecursiveValueVisitArgs, ScanVisitArgs, SpatialScanVisitArgs, TextSearchVisitArgs,
12    TimeseriesScanVisitArgs, UpdateFromVisitArgs, UpsertVisitArgs, VectorSearchVisitArgs,
13};
14use super::dispatch_rest::dispatch_rest;
15use super::trait_def::PlanVisitor;
16use crate::types::SqlPlan;
17use crate::types::{
18    DocumentIndexLookupPlan, HybridSearchPlan, HybridSearchTriplePlan, InsertPlan, KvInsertPlan,
19    RangeScanPlan, RecursiveScanPlan, RecursiveValuePlan, TextSearchPlan, TimeseriesIngestPlan,
20    TimeseriesScanPlan, UpsertPlan,
21};
22
23pub fn dispatch<V: PlanVisitor>(visitor: &mut V, plan: &SqlPlan) -> Result<V::Output, V::Error> {
24    match plan {
25        SqlPlan::ConstantResult {
26            columns, values, ..
27        } => visitor.constant_result(columns, values),
28        SqlPlan::Scan {
29            collection,
30            alias,
31            engine,
32            filters,
33            projection,
34            sort_keys,
35            limit,
36            offset,
37            distinct,
38            window_functions,
39            temporal,
40        } => visitor.scan(ScanVisitArgs {
41            collection,
42            alias: alias.as_deref(),
43            engine: *engine,
44            filters,
45            projection,
46            sort_keys,
47            limit: *limit,
48            offset: *offset,
49            distinct: *distinct,
50            window_functions,
51            temporal,
52        }),
53        SqlPlan::PointGet {
54            collection,
55            alias,
56            engine,
57            key_column,
58            key_value,
59            projection: _,
60        } => visitor.point_get(collection, alias.as_deref(), *engine, key_column, key_value),
61        SqlPlan::DocumentIndexLookup(DocumentIndexLookupPlan {
62            collection,
63            alias,
64            engine,
65            field,
66            value,
67            filters,
68            projection,
69            sort_keys,
70            limit,
71            offset,
72            distinct,
73            window_functions,
74            case_insensitive,
75            temporal,
76        }) => visitor.document_index_lookup(DocumentIndexLookupVisitArgs {
77            collection,
78            alias: alias.as_deref(),
79            engine: *engine,
80            field,
81            value,
82            filters,
83            projection,
84            sort_keys,
85            limit: *limit,
86            offset: *offset,
87            distinct: *distinct,
88            window_functions,
89            case_insensitive: *case_insensitive,
90            temporal,
91        }),
92        SqlPlan::RangeScan(RangeScanPlan {
93            collection,
94            field,
95            lower,
96            upper,
97            limit,
98            projection: _,
99        }) => visitor.range_scan(collection, field, lower.as_ref(), upper.as_ref(), *limit),
100        SqlPlan::Insert(InsertPlan {
101            collection,
102            engine,
103            route,
104            rows,
105            volatile_defaults: _,
106            if_absent,
107            column_schema,
108            primary_key,
109        }) => visitor.insert(InsertVisitArgs {
110            collection,
111            engine: *engine,
112            route: *route,
113            rows,
114            if_absent: *if_absent,
115            column_schema,
116            primary_key: primary_key.as_deref(),
117        }),
118        SqlPlan::KvInsert(KvInsertPlan {
119            collection,
120            entries,
121            ttl_secs,
122            intent,
123            on_conflict_updates,
124            ..
125        }) => visitor.kv_insert(collection, entries, *ttl_secs, *intent, on_conflict_updates),
126        SqlPlan::Upsert(UpsertPlan {
127            collection,
128            engine,
129            route,
130            rows,
131            volatile_defaults: _,
132            on_conflict_updates,
133            column_schema,
134            primary_key,
135        }) => visitor.upsert(UpsertVisitArgs {
136            collection,
137            engine: *engine,
138            route: *route,
139            rows,
140            on_conflict_updates,
141            column_schema,
142            primary_key: primary_key.as_deref(),
143        }),
144        SqlPlan::InsertSelect {
145            target,
146            source,
147            column_map,
148            source_columns,
149        } => visitor.insert_select(target, source, column_map, source_columns),
150        SqlPlan::Update {
151            collection,
152            engine,
153            assignments,
154            filters,
155            target_keys,
156            returning,
157        } => visitor.update(
158            collection,
159            *engine,
160            assignments,
161            filters,
162            target_keys,
163            *returning,
164        ),
165        SqlPlan::UpdateFrom {
166            collection,
167            engine,
168            source,
169            source_alias,
170            target_join_col,
171            source_join_col,
172            assignments,
173            target_filters,
174            returning,
175        } => visitor.update_from(UpdateFromVisitArgs {
176            collection,
177            engine: *engine,
178            source,
179            source_alias,
180            target_join_col,
181            source_join_col,
182            assignments,
183            target_filters,
184            returning: *returning,
185        }),
186        SqlPlan::Delete {
187            collection,
188            engine,
189            filters,
190            target_keys,
191        } => visitor.delete(collection, *engine, filters, target_keys),
192        SqlPlan::Truncate {
193            collection,
194            engine,
195            restart_identity,
196        } => visitor.truncate(collection, *engine, *restart_identity),
197        SqlPlan::Join {
198            left,
199            right,
200            on,
201            join_type,
202            condition,
203            limit,
204            projection,
205            filters,
206        } => visitor.join(JoinVisitArgs {
207            left,
208            right,
209            on,
210            join_type: *join_type,
211            condition: condition.as_ref(),
212            limit: *limit,
213            projection,
214            filters,
215        }),
216        SqlPlan::Aggregate {
217            input,
218            group_by,
219            group_by_aliases: _,
220            output_order: _,
221            aggregates,
222            having,
223            limit,
224            grouping_sets,
225            sort_keys,
226        } => visitor.aggregate(AggregateVisitArgs {
227            input,
228            group_by,
229            aggregates,
230            having,
231            limit: *limit,
232            grouping_sets: grouping_sets.as_deref(),
233            sort_keys,
234        }),
235        SqlPlan::TimeseriesScan(TimeseriesScanPlan {
236            collection,
237            time_range,
238            bucket_interval_ms,
239            group_by,
240            aggregates,
241            filters,
242            having,
243            projection,
244            gap_fill,
245            limit,
246            sort_keys,
247            tiered,
248            temporal,
249        }) => visitor.timeseries_scan(TimeseriesScanVisitArgs {
250            collection,
251            time_range: *time_range,
252            bucket_interval_ms: *bucket_interval_ms,
253            group_by,
254            aggregates,
255            filters,
256            having,
257            projection,
258            gap_fill,
259            limit: *limit,
260            sort_keys,
261            tiered: *tiered,
262            temporal,
263        }),
264        SqlPlan::TimeseriesIngest(TimeseriesIngestPlan {
265            collection,
266            rows,
267            volatile_defaults: _,
268        }) => visitor.timeseries_ingest(collection, rows),
269        SqlPlan::VectorSearch {
270            collection,
271            field,
272            query_vector,
273            top_k,
274            ef_search,
275            metric,
276            filters,
277            array_prefilter,
278            ann_options,
279            skip_payload_fetch,
280            payload_filters,
281            pk_prefilter,
282            projection: _,
283        } => visitor.vector_search(VectorSearchVisitArgs {
284            collection,
285            field,
286            query_vector,
287            top_k: *top_k,
288            ef_search: *ef_search,
289            metric: *metric,
290            filters,
291            array_prefilter: array_prefilter.as_ref(),
292            ann_options,
293            skip_payload_fetch: *skip_payload_fetch,
294            payload_filters,
295            pk_prefilter: pk_prefilter.as_deref(),
296        }),
297        SqlPlan::MultiVectorSearch {
298            collection,
299            query_vector,
300            top_k,
301            ef_search,
302            projection: _,
303        } => visitor.multi_vector_search(collection, query_vector, *top_k, *ef_search),
304        SqlPlan::SparseSearch {
305            collection,
306            field,
307            query_entries,
308            top_k,
309            projection: _,
310        } => visitor.sparse_search(collection, field, query_entries, *top_k),
311        SqlPlan::TextSearch(TextSearchPlan {
312            collection,
313            shape,
314            filters,
315            scores,
316            projection: _,
317        }) => visitor.text_search(TextSearchVisitArgs {
318            collection,
319            shape,
320            filters,
321            scores,
322        }),
323        SqlPlan::HybridSearch(HybridSearchPlan {
324            collection,
325            vector_field,
326            query_vector,
327            text_field,
328            query_text,
329            filters,
330            top_k,
331            ef_search,
332            vector_weight,
333            mode,
334            fuzzy,
335            score_alias,
336            projection: _,
337        }) => visitor.hybrid_search(HybridSearchVisitArgs {
338            collection,
339            vector_field,
340            query_vector,
341            text_field: text_field.as_deref(),
342            query_text,
343            filters,
344            top_k: *top_k,
345            ef_search: *ef_search,
346            vector_weight: *vector_weight,
347            mode: *mode,
348            fuzzy: *fuzzy,
349            score_alias: score_alias.as_deref(),
350        }),
351        SqlPlan::HybridSearchTriple(HybridSearchTriplePlan {
352            collection,
353            vector_field,
354            query_vector,
355            text_field,
356            query_text,
357            filters,
358            graph_seed_id,
359            graph_depth,
360            graph_edge_label,
361            top_k,
362            ef_search,
363            mode,
364            fuzzy,
365            rrf_k,
366            score_alias,
367            projection: _,
368        }) => visitor.hybrid_search_triple(HybridSearchTripleVisitArgs {
369            collection,
370            vector_field,
371            query_vector,
372            text_field: text_field.as_deref(),
373            query_text,
374            filters,
375            graph_seed_id,
376            graph_depth: *graph_depth,
377            graph_edge_label: graph_edge_label.as_deref(),
378            top_k: *top_k,
379            ef_search: *ef_search,
380            mode: *mode,
381            fuzzy: *fuzzy,
382            rrf_k: *rrf_k,
383            score_alias: score_alias.as_deref(),
384        }),
385        SqlPlan::SpatialScan {
386            collection,
387            field,
388            predicate,
389            query_geometry,
390            distance_meters,
391            attribute_filters,
392            limit,
393            projection,
394        } => visitor.spatial_scan(SpatialScanVisitArgs {
395            collection,
396            field,
397            predicate,
398            query_geometry,
399            distance_meters: *distance_meters,
400            attribute_filters,
401            limit: *limit,
402            projection,
403        }),
404        SqlPlan::RecursiveScan(RecursiveScanPlan {
405            collection,
406            base_filters,
407            recursive_filters,
408            join_link,
409            max_iterations,
410            distinct,
411            limit,
412            projection: _,
413        }) => visitor.recursive_scan(RecursiveScanVisitArgs {
414            collection,
415            base_filters,
416            recursive_filters,
417            join_link: join_link.as_ref(),
418            max_iterations: *max_iterations,
419            distinct: *distinct,
420            limit: *limit,
421        }),
422        SqlPlan::RecursiveValue(RecursiveValuePlan {
423            cte_name,
424            columns,
425            init_exprs,
426            step_exprs,
427            condition,
428            max_depth,
429            distinct,
430        }) => visitor.recursive_value(RecursiveValueVisitArgs {
431            cte_name,
432            columns,
433            init_exprs,
434            step_exprs,
435            condition: condition.as_deref(),
436            max_depth: *max_depth,
437            distinct: *distinct,
438        }),
439        other => dispatch_rest(visitor, other),
440    }
441}