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 for column in columns.iter_mut() {
110 for expression in [column.default.as_mut(), column.check.as_mut()]
111 .into_iter()
112 .flatten()
113 {
114 expression.upgrade_legacy_serialized_dispatches();
115 }
116 }
117 for check in checks.iter_mut() {
118 check.expr.upgrade_legacy_serialized_dispatches();
119 }
120 self.prepare_foreign_table_schema_inner(
121 table_name,
122 columns,
123 checks,
124 Some(dropped_attributes),
125 allocate,
126 &crate::schema::constraint_metadata::ConstraintNameScope::default(),
127 )
128 }
129 fn prepare_foreign_table_schema_inner(
130 &self,
131 table_name: &str,
132 columns: &mut [ColumnDef],
133 checks: &mut Vec<TableCheck>,
134 stored_attributes: Option<&[crate::catalog::relation_attributes::DroppedAttribute]>,
135 allocate: &mut CatalogIdentityAllocator<'_>,
136 names: &crate::schema::constraint_metadata::ConstraintNameScope,
137 ) -> Result<(), SQLError> {
138 let relation = RelationIdentity::from_legacy_name(table_name).map_err(|error| {
139 SQLError::Internal(format!("decode foreign table `{table_name}`: {error}"))
140 })?;
141 let stored = stored_attributes.is_some();
142 let qualifier = relation.name.clone();
143 let check_columns = columns.to_vec();
144 for column in columns.iter_mut() {
145 if let Some(default) = &mut column.default {
146 if stored {
147 prepare_foreign_table_sequence_references(
148 self.references,
149 self.sequences,
150 default,
151 true,
152 )?;
153 }
154 if !validate_default_expression(self, default, &column.ty, &column.name)? {
155 column.default = None;
156 }
157 }
158 if let Some(check) = &mut column.check {
159 if stored {
160 prepare_foreign_table_sequence_references(
161 self.references,
162 self.sequences,
163 check,
164 true,
165 )?;
166 }
167 validate_check_expression(self, table_name, &qualifier, &check_columns, check)?;
168 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
169 self.schema,
170 check,
171 None,
172 )?;
173 }
174 if let Some(generated) = &mut column.generated {
175 prepare_foreign_table_sequence_references(
176 self.references,
177 self.sequences,
178 &mut generated.expression,
179 stored,
180 )?;
181 }
182 }
183 for check in checks.iter_mut() {
184 if stored {
185 prepare_foreign_table_sequence_references(
186 self.references,
187 self.sequences,
188 &mut check.expr,
189 true,
190 )?;
191 }
192 validate_check_expression(
193 self,
194 table_name,
195 &qualifier,
196 &check_columns,
197 &mut check.expr,
198 )?;
199 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
200 self.schema,
201 &check.expr,
202 None,
203 )?;
204 }
205 let binding = self.bindings.binding_scope()?;
206 crate::schema::generated::prepare_generated_columns(
207 &SchemaBindingContext {
208 catalog: self.schema,
209 binding: &binding.context(),
210 },
211 &qualifier,
212 columns,
213 &[],
214 )?;
215 let mut constraints = crate::ast::TableConstraintSet {
216 checks: std::mem::take(checks),
217 dropped_attributes: stored_attributes.unwrap_or_default().to_vec(),
218 ..crate::ast::TableConstraintSet::default()
219 };
220 crate::schema::constraint_metadata::materialize_constraint_metadata_with_names(
221 &relation,
222 columns,
223 &mut constraints,
224 allocate,
225 names,
226 )
227 .map_err(|error| {
228 crate::catalog::errors::storage_error("foreign table constraint identity", &error)
229 })?;
230 *checks = constraints.checks;
231 Ok(())
232 }
233}
234fn validate_default_expression(
235 context: &ForeignSchemaContext<'_>,
236 expression: &mut Expr,
237 target: &ColumnType,
238 column: &str,
239) -> Result<bool, SQLError> {
240 let binding = context.bindings.binding_scope()?;
241 crate::schema::defaults::validate_default_expression(
242 &SchemaBindingContext {
243 catalog: context.schema,
244 binding: &binding.context(),
245 },
246 expression,
247 target,
248 column,
249 )
250}
251fn validate_check_expression(
252 context: &ForeignSchemaContext<'_>,
253 table: &str,
254 qualifier: &str,
255 columns: &[ColumnDef],
256 expression: &mut Expr,
257) -> Result<(), SQLError> {
258 let binding = context.bindings.binding_scope()?;
259 crate::schema::constraints::validate_check_expression(
260 &SchemaBindingContext {
261 catalog: context.schema,
262 binding: &binding.context(),
263 },
264 table,
265 qualifier,
266 columns,
267 expression,
268 )
269}
270
271fn prepare_foreign_table_sequence_references(
272 references: &dyn SchemaReferenceCatalog,
273 sequences: &dyn StoredSequenceNames,
274 expression: &mut crate::ast::Expr,
275 stored: bool,
276) -> Result<(), SQLError> {
277 crate::schema::dependencies::regclass::bind_schema_regclass_constants(
278 references, expression, stored,
279 )
280 .map_err(SQLError::Internal)?;
281 let result = if stored {
282 crate::schema::dependencies::regclass::bind_legacy_sequence_regclass_constants(
283 references, sequences, expression,
284 )
285 .map_err(SQLError::Internal)?;
286 crate::schema::dependencies::rewrites::rewrite_sequence_function_references(
287 expression,
288 &mut |reference| {
289 *reference = sequences.stored_sequence_name(reference)?;
290 Ok(())
291 },
292 )
293 } else {
294 crate::schema::dependencies::regclass::bind_sequence_references_in_expr(
295 references, expression,
296 )
297 };
298 result.map_err(SQLError::Internal)
299}
300
301#[cfg(test)]
302mod tests;
303
304pub mod dependencies;