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

Function validate_self_ref_count

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

Validate that the CTE name appears exactly once in the recursive arm and not inside a subquery, aggregate function, or the nullable side of an outer join. Returns `Ok(())` if the reference is valid, or a typed error otherwise.

(expr: &SetExpr, cte_name: &str)

Source from the content-addressed store, hash-verified

258///
259/// Returns `Ok(())` if the reference is valid, or a typed error otherwise.
260fn validate_self_ref_count(expr: &SetExpr, cte_name: &str) -> Result<()> {
261 let select = match expr {
262 SetExpr::Select(s) => s,
263 // Non-SELECT arm: no self-ref needed.
264 _ => return Ok(()),
265 };
266
267 let mut count = 0usize;
268
269 for from in &select.from {
270 if table_ref_matches(&from.relation, cte_name) {
271 count += 1;
272 }
273 for join in &from.joins {
274 if table_ref_matches(&join.relation, cte_name) {
275 // Reject self-ref on the nullable side of an outer join.
276 if is_nullable_join_side(&join.join_operator) {
277 return Err(SqlError::InvalidRecursiveSelfRef {
278 cte_name: cte_name.to_owned(),
279 reason: "self-reference on the nullable side of an outer join is not \
280 permitted; use INNER JOIN or move the CTE reference to the \
281 driving table position"
282 .into(),
283 });
284 }
285 count += 1;
286 }
287 }
288 }
289
290 // Subquery self-references are not permitted.
291 if where_contains_subquery_ref(&select.selection, cte_name) {
292 return Err(SqlError::InvalidRecursiveSelfRef {
293 cte_name: cte_name.to_owned(),
294 reason: "self-reference inside a subquery is not permitted".into(),
295 });
296 }
297
298 if count > 1 {
299 return Err(SqlError::InvalidRecursiveSelfRef {
300 cte_name: cte_name.to_owned(),
301 reason: format!("self-reference appears {count} times; exactly one is required"),
302 });
303 }
304
305 // count == 0 is fine for the value-generating case (no table ref at all).
306 Ok(())
307}
308
309fn table_ref_matches(factor: &ast::TableFactor, cte_name: &str) -> bool {
310 match factor {

Callers 1

plan_recursive_cteFunction · 0.85

Calls 3

table_ref_matchesFunction · 0.85
is_nullable_join_sideFunction · 0.85

Tested by

no test coverage detected