uqa_sql/compiler/
foreign_tables.rs1use super::{range_var_name, NodeEnum, Result, SQLError};
10use crate::ast::{ColumnType, CreateForeignTable, DeferredCreateForeignTable};
11use crate::schema::table_creation::column_declarations::{
12 check_foreign_column_declaration, check_serial_array, foreign_table_constraint_error,
13};
14use crate::type_resolution::{resolve_declared_column_type, FunctionTypeResolver};
15
16pub fn resolve_deferred_create_foreign_table(
18 deferred: &DeferredCreateForeignTable,
19 types: &dyn FunctionTypeResolver,
20) -> Result<CreateForeignTable> {
21 let parsed = crate::parser::parse(&deferred.definition_sql)?;
22 let [raw] = parsed.protobuf.stmts.as_slice() else {
23 return Err(SQLError::Internal(
24 "deferred CREATE FOREIGN TABLE did not contain exactly one statement".into(),
25 ));
26 };
27 let node = raw
28 .stmt
29 .as_deref()
30 .and_then(|node| node.node.as_ref())
31 .ok_or_else(|| SQLError::Internal("deferred CREATE FOREIGN TABLE is empty".into()))?;
32 let NodeEnum::CreateForeignTableStmt(statement) = node else {
33 return Err(SQLError::Internal(
34 "deferred CREATE FOREIGN TABLE changed statement kind".into(),
35 ));
36 };
37 let base = statement
38 .base_stmt
39 .as_ref()
40 .ok_or_else(|| SQLError::Internal("CREATE FOREIGN TABLE without base".into()))?;
41 let relation = base
42 .relation
43 .as_ref()
44 .ok_or_else(|| SQLError::Internal("CREATE FOREIGN TABLE without relation".into()))?;
45 if base.if_not_exists != deferred.if_not_exists
46 || range_var_name(relation) != deferred.name
47 || statement.servername != deferred.server_name
48 {
49 return Err(SQLError::Internal(
50 "deferred CREATE FOREIGN TABLE changed target identity".into(),
51 ));
52 }
53 super::validate_create_table_envelope(base, "CREATE FOREIGN TABLE")?;
54 let column_types = analyze_elements(base, types, &relation.relname)?;
55 let mut table = super::relations::compile_create_foreign_table(statement)?;
57 if column_types.len() != table.columns.len() {
58 return Err(SQLError::Internal(
59 "foreign-table declaration lost an analyzed column".into(),
60 ));
61 }
62 for (column, ty) in table.columns.iter_mut().zip(column_types) {
63 column.ty = ty;
64 }
65 Ok(table)
66}
67
68fn analyze_elements(
69 statement: &pg_query::protobuf::CreateStmt,
70 types: &dyn FunctionTypeResolver,
71 table: &str,
72) -> Result<Vec<ColumnType>> {
73 use pg_query::protobuf::ConstrType;
74 let mut column_types = Vec::new();
75 for element in &statement.table_elts {
76 match element.node.as_ref() {
77 Some(NodeEnum::ColumnDef(column)) => {
78 let declaration = super::tree::compile_column_declaration(column)?;
79 check_serial_array(&declaration)?;
80 let ty = super::types::compile_type_name(column)?;
81 let ty = resolve_declared_column_type(types, &ty)?;
82 check_foreign_column_declaration(&declaration, &column.colname, table)?;
83 column_types.push(ty);
84 }
85 Some(NodeEnum::Constraint(constraint)) => {
86 let kind = match constraint.contype() {
87 ConstrType::ConstrPrimary => "primary key",
88 ConstrType::ConstrUnique => "unique",
89 ConstrType::ConstrForeign => "foreign key",
90 ConstrType::ConstrExclusion => "exclusion",
91 ConstrType::ConstrCheck | ConstrType::ConstrNotnull => continue,
92 other => {
93 return Err(SQLError::Unsupported(format!(
94 "table constraint {other:?} is not supported"
95 )));
96 }
97 };
98 return Err(foreign_table_constraint_error(kind));
99 }
100 Some(other) => {
101 return Err(SQLError::Unsupported(format!(
102 "CREATE FOREIGN TABLE element {other:?} is not supported"
103 )));
104 }
105 None => {
106 return Err(SQLError::Internal(
107 "CREATE FOREIGN TABLE contains an empty element".into(),
108 ));
109 }
110 }
111 }
112 Ok(column_types)
113}
114
115pub(super) fn not_null_declarations(
116 table: &crate::ast::CreateTable,
117) -> Result<Vec<crate::ast::NotNullDeclaration>> {
118 use crate::ast::{DeclaredElement, NotNullDeclaration};
119 let mut declarations = Vec::new();
120 let mut columns = table.columns.iter();
121 for element in &table.element_order {
122 match element {
123 DeclaredElement::Column(declaration) => {
124 let column = columns.next().ok_or_else(|| {
125 SQLError::Internal("foreign-table declaration lost a column".into())
126 })?;
127 let before = declarations.len();
128 declarations.extend(
129 declaration
130 .clauses
131 .iter()
132 .filter(|clause| clause.kind == crate::ast::ColumnClauseKind::NotNull)
133 .map(|clause| NotNullDeclaration {
134 column: column.name.clone(),
135 name: clause.name.clone(),
136 no_inherit: clause.no_inherit,
137 explicit: true,
138 }),
139 );
140 if column.not_null && declarations.len() == before {
141 declarations.push(NotNullDeclaration {
142 column: column.name.clone(),
143 name: None,
144 no_inherit: false,
145 explicit: false,
146 });
147 }
148 }
149 DeclaredElement::NotNull(declaration) => declarations.push(declaration.clone()),
150 DeclaredElement::PrimaryKey { .. } | DeclaredElement::DeferrableKey => {
151 return Err(SQLError::Internal(
152 "foreign-table declaration retained an unsupported key".into(),
153 ));
154 }
155 }
156 }
157 Ok(declarations)
158}
159
160pub(super) fn column_drops(
162 commands: &[pg_query::protobuf::Node],
163) -> Option<Vec<crate::ast::DropColumnAction>> {
164 if commands.is_empty() {
165 return None;
166 }
167 commands
168 .iter()
169 .map(|node| {
170 let Some(NodeEnum::AlterTableCmd(command)) = node.node.as_ref() else {
171 return None;
172 };
173 (command.subtype() == pg_query::protobuf::AlterTableType::AtDropColumn).then(|| {
174 crate::ast::DropColumnAction {
175 name: command.name.clone(),
176 if_exists: command.missing_ok,
177 cascade: matches!(
178 command.behavior(),
179 pg_query::protobuf::DropBehavior::DropCascade
180 ),
181 }
182 })
183 })
184 .collect()
185}