1use crate::schema::constraint_metadata::CatalogIdentityAllocator;
9use crate::schema::dependencies::regclass::SchemaReferenceCatalog;
10use crate::schema::sequences::implicit_ownership::StoredSequenceNames;
11use crate::schema::{SchemaBindingContext, SchemaExpressionCatalog};
12use crate::semantics::conflict::InferenceBindingScope;
13use crate::type_resolution::FunctionTypeResolver;
14use crate::{
15 ast::{ColumnDef, ColumnType, Expr, TableCheck},
16 SQLError,
17};
18use uqa_core::RelationIdentity;
19
20pub struct ForeignSchemaContext<'a> {
21 pub types: &'a dyn FunctionTypeResolver,
22 pub schema: &'a dyn SchemaExpressionCatalog,
23 pub bindings: &'a dyn InferenceBindingScope,
24 pub references: &'a dyn SchemaReferenceCatalog,
25 pub sequences: &'a dyn StoredSequenceNames,
26}
27
28pub fn validate_foreign_table_schema_envelope(columns: &[ColumnDef]) -> Result<(), SQLError> {
29 let mut names = std::collections::BTreeSet::new();
30 for column in columns {
31 if !names.insert(column.name.as_str()) {
32 return Err(SQLError::Routine {
33 sqlstate: "42701".into(),
34 message: format!("column \"{}\" specified more than once", column.name),
35 });
36 }
37 crate::schema::columns::validate_postgres_column_name(&column.name)?;
38 crate::schema::columns::validate_postgres_relation_column_type(&column.name, &column.ty)?;
39 if column.primary_key || column.unique {
40 let kind = if column.primary_key {
41 "primary key"
42 } else {
43 "unique"
44 };
45 return Err(SQLError::Unsupported(format!(
46 "{kind} constraints are not supported on foreign tables"
47 )));
48 }
49 if column.references.is_some() {
50 return Err(SQLError::Unsupported(
51 "foreign key constraints are not supported on foreign tables".into(),
52 ));
53 }
54 }
55 Ok(())
56}
57impl ForeignSchemaContext<'_> {
58 pub fn prepare_foreign_table_schema(
59 &self,
60 table_name: &str,
61 columns: &mut [ColumnDef],
62 checks: &mut Vec<TableCheck>,
63 allocate: &mut CatalogIdentityAllocator<'_>,
64 names: &crate::schema::constraint_metadata::ConstraintNameScope,
65 ) -> Result<(), SQLError> {
66 self.prepare_foreign_table_schema_inner(table_name, columns, checks, false, allocate, names)
67 }
68 pub fn prepare_stored_foreign_table_schema(
69 &self,
70 table_name: &str,
71 columns: &mut [ColumnDef],
72 checks: &mut Vec<TableCheck>,
73 allocate: &mut CatalogIdentityAllocator<'_>,
74 ) -> Result<(), SQLError> {
75 validate_foreign_table_schema_envelope(columns)?;
76 self.prepare_foreign_table_schema_inner(
77 table_name,
78 columns,
79 checks,
80 true,
81 allocate,
82 &crate::schema::constraint_metadata::ConstraintNameScope::default(),
83 )
84 }
85 fn prepare_foreign_table_schema_inner(
86 &self,
87 table_name: &str,
88 columns: &mut [ColumnDef],
89 checks: &mut Vec<TableCheck>,
90 stored: bool,
91 allocate: &mut CatalogIdentityAllocator<'_>,
92 names: &crate::schema::constraint_metadata::ConstraintNameScope,
93 ) -> Result<(), SQLError> {
94 let relation = RelationIdentity::from_legacy_name(table_name).map_err(|error| {
95 SQLError::Internal(format!("decode foreign table `{table_name}`: {error}"))
96 })?;
97 let qualifier = relation.name.clone();
98 let check_columns = columns.to_vec();
99 for column in columns.iter_mut() {
100 if let Some(default) = &mut column.default {
101 prepare_foreign_table_sequence_references(
102 self.references,
103 self.sequences,
104 default,
105 stored,
106 )?;
107 validate_default_expression(self, default, &column.ty)?;
108 }
109 if let Some(check) = &mut column.check {
110 prepare_foreign_table_sequence_references(
111 self.references,
112 self.sequences,
113 check,
114 stored,
115 )?;
116 validate_check_expression(self, table_name, &qualifier, &check_columns, check)?;
117 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
118 self.schema,
119 check,
120 None,
121 )?;
122 }
123 if let Some(generated) = &mut column.generated {
124 prepare_foreign_table_sequence_references(
125 self.references,
126 self.sequences,
127 &mut generated.expression,
128 stored,
129 )?;
130 }
131 }
132 for check in checks.iter_mut() {
133 prepare_foreign_table_sequence_references(
134 self.references,
135 self.sequences,
136 &mut check.expr,
137 stored,
138 )?;
139 validate_check_expression(
140 self,
141 table_name,
142 &qualifier,
143 &check_columns,
144 &mut check.expr,
145 )?;
146 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
147 self.schema,
148 &check.expr,
149 None,
150 )?;
151 }
152 crate::schema::generated::prepare_generated_columns(
153 self.schema,
154 &qualifier,
155 columns,
156 &[],
157 &[],
158 )?;
159 let mut constraints = crate::ast::TableConstraintSet {
160 checks: std::mem::take(checks),
161 ..crate::ast::TableConstraintSet::default()
162 };
163 crate::schema::constraint_metadata::materialize_constraint_metadata_with_names(
164 &relation,
165 columns,
166 &mut constraints,
167 allocate,
168 names,
169 )
170 .map_err(|error| {
171 crate::catalog::errors::storage_error("foreign table constraint identity", &error)
172 })?;
173 *checks = constraints.checks;
174 Ok(())
175 }
176}
177fn validate_default_expression(
178 context: &ForeignSchemaContext<'_>,
179 expression: &mut Expr,
180 target: &ColumnType,
181) -> Result<(), SQLError> {
182 let binding = context.bindings.binding_scope()?;
183 crate::schema::defaults::validate_default_expression(
184 &SchemaBindingContext {
185 catalog: context.schema,
186 binding: &binding.context(),
187 },
188 expression,
189 target,
190 )
191}
192fn validate_check_expression(
193 context: &ForeignSchemaContext<'_>,
194 table: &str,
195 qualifier: &str,
196 columns: &[ColumnDef],
197 expression: &mut Expr,
198) -> Result<(), SQLError> {
199 let binding = context.bindings.binding_scope()?;
200 crate::schema::constraints::validate_check_expression(
201 &SchemaBindingContext {
202 catalog: context.schema,
203 binding: &binding.context(),
204 },
205 table,
206 qualifier,
207 columns,
208 expression,
209 )
210}
211
212fn prepare_foreign_table_sequence_references(
213 references: &dyn SchemaReferenceCatalog,
214 sequences: &dyn StoredSequenceNames,
215 expression: &mut crate::ast::Expr,
216 stored: bool,
217) -> Result<(), SQLError> {
218 crate::schema::dependencies::regclass::bind_schema_regclass_constants(
219 references, expression, stored,
220 )
221 .map_err(|error| SQLError::Internal(error.to_string()))?;
222 let result = if stored {
223 crate::schema::dependencies::rewrites::rewrite_sequence_function_references(
224 expression,
225 &mut |reference| {
226 *reference = sequences.stored_sequence_name(reference)?;
227 Ok(())
228 },
229 )
230 } else {
231 crate::schema::dependencies::regclass::bind_sequence_references_in_expr(
232 references, expression,
233 )
234 };
235 result.map_err(|error| SQLError::Internal(error.to_string()))
236}
237
238#[cfg(test)]
239mod tests;
240
241pub mod dependencies;