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