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
16#[cfg(test)]
17mod tests;
18
19pub trait RoutineRelationOids {
20    fn bound_regclass_oid(&self, name: &str) -> Result<Option<i64>, SQLError>;
21}
22
23pub fn is_regclass(name: &str) -> bool {
24    crate::ast::ColumnType::from_sql_name(name)
25        .is_ok_and(|ty| matches!(ty, crate::ast::ColumnType::Regclass))
26}
27
28pub fn regclass_oid(expression: &Expr) -> Option<i64> {
29    match expression {
30        Expr::TypedLiteral {
31            value: Value::Int(oid),
32            ty,
33            ..
34        } if is_regclass(ty) => Some(*oid),
35        Expr::Cast { expr, ty, .. } if is_regclass(ty) => regclass_oid(expr),
36        _ => None,
37    }
38}
39
40pub fn stored_routine_references_relations(
41    catalog: &dyn RoutineRelationOids,
42    definition: &CreateFunction,
43    relations: &BTreeSet<String>,
44) -> Result<bool, SQLError> {
45    if relations.is_empty() {
46        return Ok(false);
47    }
48    let mut oids = BTreeSet::new();
49    for relation in relations {
50        if let Some(oid) = catalog.bound_regclass_oid(relation)? {
51            oids.insert(oid);
52        }
53    }
54    let mut depends = false;
55    let mut visit = |expression: &mut crate::ast::Expr| {
56        depends |= regclass_oid(expression).is_some_and(|oid| oids.contains(&oid));
57        Ok(())
58    };
59    for default in definition
60        .params
61        .iter()
62        .filter_map(|parameter| parameter.default.as_ref())
63    {
64        crate::catalog::stored_ast::visit_stored_expression(&mut default.clone(), &mut visit)?;
65    }
66    if let FunctionBody::Statements(statements) = &definition.body {
67        for statement in statements {
68            if crate::catalog::stored_ast::stored_statement_relation_names(statement)?
69                .iter()
70                .any(|name| relations.contains(name))
71            {
72                return Ok(true);
73            }
74            crate::catalog::stored_ast::visit_stored_statement_expressions(
75                &mut statement.clone(),
76                &mut visit,
77            )?;
78        }
79    }
80    Ok(depends)
81}