(
cte_name: &str,
base: &SqlPlan,
parts: &RecursiveParts<'_>,
catalog: &dyn SqlCatalog,
functions: &FunctionRegistry,
temporal: crate::TemporalScope,
)
| 107 | } |
| 108 | |
| 109 | fn plan_recursive_scan_from_parts( |
| 110 | cte_name: &str, |
| 111 | base: &SqlPlan, |
| 112 | parts: &RecursiveParts<'_>, |
| 113 | catalog: &dyn SqlCatalog, |
| 114 | functions: &FunctionRegistry, |
| 115 | temporal: crate::TemporalScope, |
| 116 | ) -> Result<SqlPlan> { |
| 117 | let RecursiveParts { |
| 118 | left, |
| 119 | right, |
| 120 | declared_columns, |
| 121 | distinct, |
| 122 | } = parts; |
| 123 | let collection = extract_collection(base); |
| 124 | |
| 125 | // Validate column count if columns were declared. |
| 126 | if !declared_columns.is_empty() { |
| 127 | let anchor_cols = count_select_cols(left); |
| 128 | if anchor_cols != 0 && anchor_cols != declared_columns.len() { |
| 129 | return Err(SqlError::RecursiveColumnMismatch { |
| 130 | cte_name: cte_name.to_owned(), |
| 131 | anchor_cols, |
| 132 | declared_cols: declared_columns.len(), |
| 133 | }); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | let (recursive_filters, join_link) = match plan_cte_branch(right, catalog, functions, temporal) |
| 138 | { |
| 139 | Ok(plan) => (extract_filters(&plan), None), |
| 140 | Err(_) => extract_recursive_info(right, cte_name)?, |
| 141 | }; |
| 142 | |
| 143 | Ok(SqlPlan::RecursiveScan { |
| 144 | collection, |
| 145 | base_filters: extract_filters(base), |
| 146 | recursive_filters, |
| 147 | join_link, |
| 148 | max_iterations: DEFAULT_MAX_RECURSION_DEPTH, |
| 149 | distinct: *distinct, |
| 150 | limit: 10000, |
| 151 | }) |
| 152 | } |
| 153 | |
| 154 | // ── Value-generating recursive CTE ─────────────────────────────────────────── |
| 155 |
no test coverage detected