uqa_sql/schema/
table_creation.rs1use crate::{
9 ast::{ColumnDef, CreateTable},
10 type_resolution::FunctionTypeResolver,
11 ColumnType, SQLError,
12};
13use std::collections::BTreeSet;
14
15pub fn existing_create_as_target(
17 name: &str,
18 if_not_exists: bool,
19) -> Result<crate::SQLNotice, SQLError> {
20 let (_, relation) =
21 uqa_core::RelationIdentity::parse_reference(name).map_err(SQLError::Unsupported)?;
22 if if_not_exists {
23 Ok(
24 crate::SQLNotice::notice(format!("relation \"{relation}\" already exists, skipping"))
25 .with_sqlstate("42P07"),
26 )
27 } else {
28 Err(SQLError::Routine {
29 sqlstate: "42P07".into(),
30 message: format!("relation \"{relation}\" already exists"),
31 })
32 }
33}
34
35pub fn validate_create_table_columns(table: &CreateTable) -> Result<(), SQLError> {
37 validate_relation_column_names_and_types(&table.columns)
38}
39
40fn validate_relation_column_names_and_types(columns: &[ColumnDef]) -> Result<(), SQLError> {
41 for column in columns {
42 super::columns::validate_postgres_column_name(&column.name)?;
43 }
44 for column in columns {
45 super::columns::validate_postgres_relation_column_type(&column.name, &column.ty)?;
46 }
47 Ok(())
48}
49
50pub fn validate_create_table_as_columns(
52 types: &dyn FunctionTypeResolver,
53 columns: &[ColumnDef],
54) -> Result<(), SQLError> {
55 let mut seen = BTreeSet::new();
56 for column in columns {
57 if !seen.insert(&column.name) {
58 return Err(SQLError::Routine {
59 sqlstate: "42701".into(),
60 message: format!("column \"{}\" specified more than once", column.name),
61 });
62 }
63 }
64 for column in columns {
65 types.require_type_usage(&column.ty)?;
66 }
67 validate_relation_column_names_and_types(columns)
68}
69
70pub fn create_table_as_columns(
72 query_schema: &crate::RowSchema,
73 column_names: &[String],
74) -> Result<Vec<ColumnDef>, SQLError> {
75 let names = query_schema
76 .columns()
77 .iter()
78 .enumerate()
79 .map(|(position, name)| {
80 column_names
81 .get(position)
82 .cloned()
83 .unwrap_or_else(|| name.clone())
84 })
85 .collect::<Vec<_>>();
86 let columns = names
87 .into_iter()
88 .enumerate()
89 .map(|(position, name)| {
90 let ty = query_schema
91 .column_type(position)
92 .cloned()
93 .unwrap_or(ColumnType::Text);
94 let collation = query_schema.column_collation(position).unwrap_or_else(|| {
95 u32::try_from(crate::catalog::type_metadata::pg_type_collation_oid(&ty))
96 .expect("column collation OID")
97 });
98 if collation == 0 && super::composites::type_is_collatable(&ty) {
99 return Err(SQLError::Diagnostic {
100 sqlstate: "42P22".into(),
101 message: format!(
102 "no collation was derived for column \"{name}\" with collatable type {}",
103 ty.display_name()
104 ),
105 detail: None,
106 hint: Some("Use the COLLATE clause to set the collation explicitly.".into()),
107 });
108 }
109 Ok(ColumnDef {
110 collation_oid: Some(collation),
111 name,
112 ty,
113 object_id: None,
114 attribute_number: None,
115 missing_value: None,
116 primary_key: false,
117 not_null: false,
118 not_null_explicit: false,
119 not_null_name: None,
120 not_null_identity: None,
121 not_null_validated: true,
122 not_null_no_inherit: false,
123 not_null_is_local: true,
124 auto_increment: None,
125 unique: false,
126 default: None,
127 generated: None,
128 check: None,
129 check_name: None,
130 check_enforced: true,
131 check_validated: true,
132 check_no_inherit: false,
133 check_is_local: true,
134 check_object_id: None,
135 check_catalog_oid: None,
136 default_catalog_oid: None,
137 references: None,
138 })
139 })
140 .collect::<Result<Vec<_>, SQLError>>()?;
141 if column_names.len() > query_schema.len() {
142 return Err(SQLError::Routine {
143 sqlstate: "42601".into(),
144 message: "too many column names were specified".into(),
145 });
146 }
147 Ok(columns)
148}
149
150pub mod checks;
151pub mod column_declarations;
152pub mod declaration;
153pub mod foreign_keys;
154pub mod keys;
155pub mod not_nulls;
156
157#[cfg(test)]
158mod tests;