MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / plan_recursive_cte

Function plan_recursive_cte

nodedb-sql/src/planner/cte.rs:20–98  ·  view source on GitHub ↗

Plan a WITH RECURSIVE query. Dispatches to either `plan_recursive_scan` (collection-backed) or `plan_recursive_value` (pure expression / value-generating) based on whether the anchor arm references a real collection.

(
    query: &Query,
    catalog: &dyn SqlCatalog,
    functions: &FunctionRegistry,
    temporal: crate::TemporalScope,
)

Source from the content-addressed store, hash-verified

18/// `plan_recursive_value` (pure expression / value-generating) based on
19/// whether the anchor arm references a real collection.
20pub fn plan_recursive_cte(
21 query: &Query,
22 catalog: &dyn SqlCatalog,
23 functions: &FunctionRegistry,
24 temporal: crate::TemporalScope,
25) -> Result<SqlPlan> {
26 let with = query.with.as_ref().ok_or_else(|| SqlError::Parse {
27 detail: "expected WITH clause".into(),
28 })?;
29
30 let cte = with.cte_tables.first().ok_or_else(|| SqlError::Parse {
31 detail: "empty WITH clause".into(),
32 })?;
33
34 let cte_name = normalize_ident(&cte.alias.name);
35 let declared_columns: Vec<String> = cte
36 .alias
37 .columns
38 .iter()
39 .map(|c| normalize_ident(&c.name))
40 .collect();
41
42 let cte_query = &cte.query;
43
44 // Validate set operator: only UNION / UNION ALL permitted.
45 let (left, right, set_quantifier) = match &*cte_query.body {
46 SetExpr::SetOperation {
47 op: ast::SetOperator::Union,
48 left,
49 right,
50 set_quantifier,
51 } => (left, right, set_quantifier),
52 SetExpr::SetOperation { op, .. } => {
53 return Err(SqlError::InvalidRecursiveSetOp {
54 op: format!("{op}"),
55 });
56 }
57 _ => {
58 return Err(SqlError::InvalidRecursiveSetOp {
59 op: "non-set-operation".into(),
60 });
61 }
62 };
63
64 // Validate self-reference count in the recursive arm.
65 validate_self_ref_count(right, &cte_name)?;
66
67 let distinct = !matches!(set_quantifier, ast::SetQuantifier::All);
68
69 // Try to detect whether this is a collection-backed or value-generating CTE
70 // by attempting to plan the anchor arm against the catalog.
71 match plan_cte_branch(left, catalog, functions, temporal) {
72 Ok(base) => {
73 let collection = extract_collection(&base);
74 if collection.is_empty() {
75 // Anchor planned but produced no collection → treat as value-gen.
76 plan_recursive_value(left, right, &cte_name, &declared_columns, distinct)
77 } else {

Callers 1

plan_queryFunction · 0.85

Calls 11

normalize_identFunction · 0.85
validate_self_ref_countFunction · 0.85
plan_cte_branchFunction · 0.85
plan_recursive_valueFunction · 0.85
firstMethod · 0.80
collectMethod · 0.80
extract_collectionFunction · 0.70
as_refMethod · 0.45
iterMethod · 0.45
is_emptyMethod · 0.45

Tested by

no test coverage detected