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