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 struct ForeignTableNotNulls {
30 pub relation_oid: u32,
31 pub declarations: Vec<crate::ast::NotNullDeclaration>,
32}
33
34pub fn validate_foreign_table_schema_envelope(columns: &[ColumnDef]) -> Result<(), SQLError> {
35 let mut names = std::collections::BTreeSet::new();
36 for column in columns {
37 if !names.insert(column.name.as_str()) {
38 return Err(SQLError::Routine {
39 sqlstate: "42701".into(),
40 message: format!("column \"{}\" specified more than once", column.name),
41 });
42 }
43 crate::schema::columns::validate_postgres_column_name(&column.name)?;
44 crate::schema::columns::validate_postgres_relation_column_type(&column.name, &column.ty)?;
45 if column.primary_key || column.unique {
46 let kind = if column.primary_key {
47 "primary key"
48 } else {
49 "unique"
50 };
51 return Err(SQLError::Unsupported(format!(
52 "{kind} constraints are not supported on foreign tables"
53 )));
54 }
55 if column.references.is_some() {
56 return Err(SQLError::Unsupported(
57 "foreign key constraints are not supported on foreign tables".into(),
58 ));
59 }
60 }
61 Ok(())
62}
63impl ForeignSchemaContext<'_> {
64 pub fn prepare_foreign_table_schema(
65 &self,
66 table_name: &str,
67 columns: &mut [ColumnDef],
68 checks: &mut Vec<TableCheck>,
69 allocate: &mut CatalogIdentityAllocator<'_>,
70 names: &crate::schema::constraint_metadata::ConstraintNameScope,
71 not_nulls: Option<ForeignTableNotNulls>,
72 ) -> Result<(), SQLError> {
73 if not_nulls.is_some() {
74 for column in columns.iter_mut() {
75 column.not_null = false;
76 column.not_null_name = None;
77 column.not_null_identity = None;
78 }
79 }
80 self.prepare_foreign_table_schema_inner(
81 table_name, columns, checks, None, allocate, names,
82 )?;
83 if let Some(not_nulls) = not_nulls {
84 let relation =
85 RelationIdentity::from_legacy_name(table_name).map_err(SQLError::Internal)?;
86 crate::schema::table_creation::not_nulls::define_foreign_not_null_constraints(
87 crate::schema::table_creation::not_nulls::ForeignNotNullContext {
88 relation: &relation,
89 relation_oid: not_nulls.relation_oid,
90 checks,
91 names,
92 },
93 columns,
94 not_nulls.declarations,
95 allocate,
96 )?;
97 }
98 Ok(())
99 }
100 pub fn prepare_stored_foreign_table_schema(
101 &self,
102 table_name: &str,
103 columns: &mut [ColumnDef],
104 checks: &mut Vec<TableCheck>,
105 dropped_attributes: &[crate::catalog::relation_attributes::DroppedAttribute],
106 allocate: &mut CatalogIdentityAllocator<'_>,
107 ) -> Result<(), SQLError> {
108 validate_foreign_table_schema_envelope(columns)?;
109 crate::binding::legacy_patterns::restore_schema(columns, checks, self.types)
110 .map_err(SQLError::Internal)?;
111 for column in columns.iter_mut() {
112 for expression in [column.default.as_mut(), column.check.as_mut()]
113 .into_iter()
114 .flatten()
115 {
116 expression.upgrade_legacy_serialized_dispatches();
117 }
118 }
119 for check in checks.iter_mut() {
120 check.expr.upgrade_legacy_serialized_dispatches();
121 }
122 self.prepare_foreign_table_schema_inner(
123 table_name,
124 columns,
125 checks,
126 Some(dropped_attributes),
127 allocate,
128 &crate::schema::constraint_metadata::ConstraintNameScope::default(),
129 )
130 }
131 fn prepare_foreign_table_schema_inner(
132 &self,
133 table_name: &str,
134 columns: &mut [ColumnDef],
135 checks: &mut Vec<TableCheck>,
136 stored_attributes: Option<&[crate::catalog::relation_attributes::DroppedAttribute]>,
137 allocate: &mut CatalogIdentityAllocator<'_>,
138 names: &crate::schema::constraint_metadata::ConstraintNameScope,
139 ) -> Result<(), SQLError> {
140 let relation = RelationIdentity::from_legacy_name(table_name).map_err(|error| {
141 SQLError::Internal(format!("decode foreign table `{table_name}`: {error}"))
142 })?;
143 let stored = stored_attributes.is_some();
144 let qualifier = relation.name.clone();
145 let check_columns = columns.to_vec();
146 for column in columns.iter_mut() {
147 if let Some(default) = &mut column.default {
148 if stored {
149 prepare_foreign_table_sequence_references(
150 self.references,
151 self.sequences,
152 default,
153 true,
154 )?;
155 }
156 if !validate_default_expression(self, default, &column.ty, &column.name)? {
157 column.default = None;
158 }
159 }
160 if let Some(check) = &mut column.check {
161 if stored {
162 prepare_foreign_table_sequence_references(
163 self.references,
164 self.sequences,
165 check,
166 true,
167 )?;
168 }
169 validate_check_expression(self, table_name, &qualifier, &check_columns, check)?;
170 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
171 self.schema,
172 check,
173 None,
174 )?;
175 }
176 if let Some(generated) = &mut column.generated {
177 prepare_foreign_table_sequence_references(
178 self.references,
179 self.sequences,
180 &mut generated.expression,
181 stored,
182 )?;
183 }
184 }
185 for check in checks.iter_mut() {
186 if stored {
187 prepare_foreign_table_sequence_references(
188 self.references,
189 self.sequences,
190 &mut check.expr,
191 true,
192 )?;
193 }
194 validate_check_expression(
195 self,
196 table_name,
197 &qualifier,
198 &check_columns,
199 &mut check.expr,
200 )?;
201 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
202 self.schema,
203 &check.expr,
204 None,
205 )?;
206 }
207 let binding = self.bindings.binding_scope()?;
208 crate::schema::generated::prepare_generated_columns(
209 &SchemaBindingContext {
210 catalog: self.schema,
211 binding: &binding.context(),
212 },
213 &qualifier,
214 columns,
215 &[],
216 )?;
217 let mut constraints = crate::ast::TableConstraintSet {
218 checks: std::mem::take(checks),
219 dropped_attributes: stored_attributes.unwrap_or_default().to_vec(),
220 ..crate::ast::TableConstraintSet::default()
221 };
222 crate::schema::constraint_metadata::materialize_constraint_metadata_with_names(
223 &relation,
224 columns,
225 &mut constraints,
226 allocate,
227 names,
228 )
229 .map_err(|error| {
230 crate::catalog::errors::storage_error("foreign table constraint identity", &error)
231 })?;
232 *checks = constraints.checks;
233 Ok(())
234 }
235}
236fn validate_default_expression(
237 context: &ForeignSchemaContext<'_>,
238 expression: &mut Expr,
239 target: &ColumnType,
240 column: &str,
241) -> Result<bool, SQLError> {
242 let binding = context.bindings.binding_scope()?;
243 crate::schema::defaults::validate_default_expression(
244 &SchemaBindingContext {
245 catalog: context.schema,
246 binding: &binding.context(),
247 },
248 expression,
249 target,
250 column,
251 )
252}
253fn validate_check_expression(
254 context: &ForeignSchemaContext<'_>,
255 table: &str,
256 qualifier: &str,
257 columns: &[ColumnDef],
258 expression: &mut Expr,
259) -> Result<(), SQLError> {
260 let binding = context.bindings.binding_scope()?;
261 crate::schema::constraints::validate_check_expression(
262 &SchemaBindingContext {
263 catalog: context.schema,
264 binding: &binding.context(),
265 },
266 table,
267 qualifier,
268 columns,
269 expression,
270 )
271}
272
273fn prepare_foreign_table_sequence_references(
274 references: &dyn SchemaReferenceCatalog,
275 sequences: &dyn StoredSequenceNames,
276 expression: &mut crate::ast::Expr,
277 stored: bool,
278) -> Result<(), SQLError> {
279 crate::schema::dependencies::regclass::bind_schema_regclass_constants(
280 references, expression, stored,
281 )
282 .map_err(SQLError::Internal)?;
283 let result = if stored {
284 crate::schema::dependencies::regclass::bind_legacy_sequence_regclass_constants(
285 references, sequences, expression,
286 )
287 .map_err(SQLError::Internal)?;
288 crate::schema::dependencies::rewrites::rewrite_sequence_function_references(
289 expression,
290 &mut |reference| {
291 *reference = sequences.stored_sequence_name(reference)?;
292 Ok(())
293 },
294 )
295 } else {
296 crate::schema::dependencies::regclass::bind_sequence_references_in_expr(
297 references, expression,
298 )
299 };
300 result.map_err(SQLError::Internal)
301}
302
303#[cfg(test)]
304mod tests;
305
306pub mod dependencies;