Skip to main content

uqa_sql/routines/lifecycle/
relations.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Stored relation, sequence OID, column, and namespace references in routine definitions.
8
9use crate::{
10    ast::{CreateFunction, Expr, FunctionBody},
11    SQLError,
12};
13use std::collections::BTreeSet;
14use uqa_core::Value;
15
16pub trait RoutineRelationOids {
17    fn bound_regclass_oid(&self, name: &str) -> Result<Option<i64>, SQLError>;
18}
19
20pub fn is_regclass(name: &str) -> bool {
21    name.eq_ignore_ascii_case("regclass") || name.eq_ignore_ascii_case("pg_catalog.regclass")
22}
23
24pub fn regclass_oid(expression: &Expr) -> Option<i64> {
25    match expression {
26        Expr::TypedLiteral {
27            value: Value::Int(oid),
28            ty,
29            ..
30        } if is_regclass(ty) => Some(*oid),
31        Expr::Cast { expr, ty, .. } if is_regclass(ty) => regclass_oid(expr),
32        _ => None,
33    }
34}
35
36pub fn stored_routine_references_relations(
37    catalog: &dyn RoutineRelationOids,
38    definition: &CreateFunction,
39    relations: &BTreeSet<String>,
40) -> Result<bool, SQLError> {
41    if relations.is_empty() {
42        return Ok(false);
43    }
44    let mut oids = BTreeSet::new();
45    for relation in relations {
46        if let Some(oid) = catalog.bound_regclass_oid(relation)? {
47            oids.insert(oid);
48        }
49    }
50    let mut depends = false;
51    let mut visit = |expression: &mut crate::ast::Expr| {
52        depends |= regclass_oid(expression).is_some_and(|oid| oids.contains(&oid));
53        Ok(())
54    };
55    for default in definition
56        .params
57        .iter()
58        .filter_map(|parameter| parameter.default.as_ref())
59    {
60        crate::catalog::stored_ast::visit_stored_expression(&mut default.clone(), &mut visit)?;
61    }
62    if let FunctionBody::Statements(statements) = &definition.body {
63        for statement in statements {
64            if crate::catalog::stored_ast::stored_statement_relation_names(statement)?
65                .iter()
66                .any(|name| relations.contains(name))
67            {
68                return Ok(true);
69            }
70            crate::catalog::stored_ast::visit_stored_statement_expressions(
71                &mut statement.clone(),
72                &mut visit,
73            )?;
74        }
75    }
76    Ok(depends)
77}