uqa_sql/schema/dependencies/
regclass.rs1use super::walk_schema_expr_mut;
9use crate::ast::{ColumnDef, ColumnType, Expr, TableCheck};
10use crate::schema::sequences::implicit_ownership::StoredSequenceNames;
11use uqa_core::Value;
12
13#[cfg(test)]
14mod tests;
15
16pub trait SchemaReferenceCatalog {
17 fn loaded_relation_name(&self, reference: &str) -> Result<Option<String>, String>;
18 fn bound_relation_oid(&self, canonical: &str) -> Result<Option<i64>, String>;
19 fn visible_relation_oid(&self, reference: &str) -> Result<Option<i64>, String>;
20 fn sequence_for_binding(&self, reference: &str) -> Result<String, String>;
21}
22
23fn is_regclass(ty: &str) -> bool {
24 ColumnType::from_sql_name(ty).is_ok_and(|ty| matches!(ty, ColumnType::Regclass))
25}
26
27pub fn bind_schema_regclass_constants(
28 catalog: &dyn SchemaReferenceCatalog,
29 expression: &mut Expr,
30 loaded: bool,
31) -> Result<bool, String> {
32 let mut changed = false;
33 walk_schema_expr_mut(expression, &mut |node| {
34 let Expr::Cast { expr, ty, .. } = node else {
35 return Ok(());
36 };
37 if !is_regclass(ty) {
38 return Ok(());
39 }
40 let Expr::Literal(Value::Str(reference)) = expr.as_ref() else {
41 return Ok(());
42 };
43 let oid = if loaded {
44 match reference.parse::<u32>() {
45 Ok(oid) => Some(i64::from(oid)),
46 Err(_) => match catalog.loaded_relation_name(reference)? {
47 Some(canonical) => catalog.bound_relation_oid(&canonical)?,
48 None => None,
49 },
50 }
51 } else {
52 catalog.visible_relation_oid(reference)?
53 }
54 .ok_or_else(|| format!("relation \"{reference}\" does not exist"))?;
55 **expr = Expr::TypedLiteral {
56 collation_oid: None,
57 composite_source: None,
58 value: Value::Int(oid),
59 ty: ColumnType::Regclass.catalog_name(),
60 };
61 changed = true;
62 Ok(())
63 })?;
64 Ok(changed)
65}
66
67pub fn bind_table_schema_regclass_constants(
68 catalog: &dyn SchemaReferenceCatalog,
69 columns: &mut [ColumnDef],
70 checks: &mut [TableCheck],
71 loaded: bool,
72) -> Result<bool, String> {
73 let mut changed = false;
74 for column in columns {
75 for expression in [&mut column.default, &mut column.check]
76 .into_iter()
77 .flatten()
78 {
79 changed |= bind_schema_regclass_constants(catalog, expression, loaded)?;
80 }
81 if let Some(generated) = &mut column.generated {
82 changed |= bind_schema_regclass_constants(catalog, &mut generated.expression, loaded)?;
83 }
84 }
85 for check in checks {
86 changed |= bind_schema_regclass_constants(catalog, &mut check.expr, loaded)?;
87 }
88 Ok(changed)
89}
90pub fn bind_sequence_references_in_expr(
91 catalog: &dyn SchemaReferenceCatalog,
92 expression: &mut Expr,
93) -> Result<(), String> {
94 super::rewrites::rewrite_sequence_function_references(expression, &mut |reference| {
95 *reference = catalog.sequence_for_binding(reference)?;
96 Ok(())
97 })?;
98 bind_schema_regclass_constants(catalog, expression, false)?;
99 Ok(())
100}
101
102pub fn bind_legacy_sequence_regclass_constants(
106 catalog: &dyn SchemaReferenceCatalog,
107 sequences: &dyn StoredSequenceNames,
108 expression: &mut Expr,
109) -> Result<bool, String> {
110 let mut changed = false;
111 walk_schema_expr_mut(expression, &mut |node| {
112 let Expr::Func {
113 name,
114 binding: Some(binding),
115 args,
116 ..
117 } = node
118 else {
119 return Ok(());
120 };
121 if !super::rewrites::is_sequence_function(name, Some(binding)) {
122 return Ok(());
123 }
124 let Some(argument @ Expr::Literal(Value::Str(_))) = args.first_mut() else {
125 return Ok(());
126 };
127 let Expr::Literal(Value::Str(reference)) = &*argument else {
128 unreachable!("legacy sequence argument is a string literal");
129 };
130 let canonical = sequences.stored_sequence_name(reference)?;
131 let oid = catalog
132 .bound_relation_oid(&canonical)?
133 .ok_or_else(|| format!("relation \"{canonical}\" does not exist"))?;
134 *argument = Expr::TypedLiteral {
135 collation_oid: None,
136 composite_source: None,
137 value: Value::Int(oid),
138 ty: ColumnType::Regclass.catalog_name(),
139 };
140 if let Some(target) = binding.argument_types.first_mut() {
141 "regclass".clone_into(target);
142 }
143 changed = true;
144 Ok(())
145 })?;
146 Ok(changed)
147}