uqa_sql/schema/dependencies/
regclass.rs1use super::walk_schema_expr_mut;
9use crate::ast::{ColumnDef, Expr, TableCheck};
10use uqa_core::Value;
11
12pub trait SchemaReferenceCatalog {
13 fn loaded_relation_name(&self, reference: &str) -> Result<Option<String>, String>;
14 fn bound_relation_oid(&self, canonical: &str) -> Result<Option<i64>, String>;
15 fn visible_relation_oid(&self, reference: &str) -> Result<Option<i64>, String>;
16 fn sequence_for_binding(&self, reference: &str) -> Result<String, String>;
17}
18
19pub fn regclass_constant_oid(expression: &Expr) -> Option<i64> {
20 match expression {
21 Expr::TypedLiteral {
22 value: Value::Int(oid),
23 ty,
24 } if is_regclass(ty) => Some(*oid),
25 _ => None,
26 }
27}
28
29fn is_regclass(ty: &str) -> bool {
30 ty.eq_ignore_ascii_case("regclass") || ty.eq_ignore_ascii_case("pg_catalog.regclass")
31}
32
33pub fn bind_schema_regclass_constants(
34 catalog: &dyn SchemaReferenceCatalog,
35 expression: &mut Expr,
36 loaded: bool,
37) -> Result<bool, String> {
38 let mut changed = false;
39 walk_schema_expr_mut(expression, &mut |node| {
40 let Expr::Cast { expr, ty } = node else {
41 return Ok(());
42 };
43 if !is_regclass(ty) {
44 return Ok(());
45 }
46 let Expr::Literal(Value::Str(reference)) = expr.as_ref() else {
47 return Ok(());
48 };
49 let oid = if loaded {
50 match reference.parse::<u32>() {
51 Ok(oid) => Some(i64::from(oid)),
52 Err(_) => match catalog.loaded_relation_name(reference)? {
53 Some(canonical) => catalog.bound_relation_oid(&canonical)?,
54 None => None,
55 },
56 }
57 } else {
58 catalog.visible_relation_oid(reference)?
59 }
60 .ok_or_else(|| format!("relation \"{reference}\" does not exist"))?;
61 **expr = Expr::TypedLiteral {
62 value: Value::Int(oid),
63 ty: "regclass".into(),
64 };
65 changed = true;
66 Ok(())
67 })?;
68 Ok(changed)
69}
70
71pub fn bind_table_schema_regclass_constants(
72 catalog: &dyn SchemaReferenceCatalog,
73 columns: &mut [ColumnDef],
74 checks: &mut [TableCheck],
75 loaded: bool,
76) -> Result<bool, String> {
77 let mut changed = false;
78 for column in columns {
79 for expression in [&mut column.default, &mut column.check]
80 .into_iter()
81 .flatten()
82 {
83 changed |= bind_schema_regclass_constants(catalog, expression, loaded)?;
84 }
85 if let Some(generated) = &mut column.generated {
86 changed |= bind_schema_regclass_constants(catalog, &mut generated.expression, loaded)?;
87 }
88 }
89 for check in checks {
90 changed |= bind_schema_regclass_constants(catalog, &mut check.expr, loaded)?;
91 }
92 Ok(changed)
93}
94pub fn bind_sequence_references_in_expr(
95 catalog: &dyn SchemaReferenceCatalog,
96 expression: &mut Expr,
97) -> Result<(), String> {
98 super::rewrites::rewrite_sequence_function_references(expression, &mut |reference| {
99 *reference = catalog.sequence_for_binding(reference)?;
100 Ok(())
101 })?;
102 bind_schema_regclass_constants(catalog, expression, false)?;
103 Ok(())
104}