Skip to main content

uqa_sql/schema/
table_creation.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! CREATE TABLE column validation and CREATE TABLE AS result declarations.
8use crate::{
9    ast::{ColumnDef, CreateTable},
10    type_resolution::FunctionTypeResolver,
11    ColumnType, SQLError,
12};
13use std::collections::BTreeSet;
14
15/// `CreateTableAsRelExists` reports the written relation's unqualified name for both CTAS and materialized views.
16pub 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
35/// `CheckAttributeNamesTypes`: no column may take a system column's name, and then no column may have a pseudo-type.
36pub 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
50/// `DefineRelation` for a table whose columns a query defines: `MergeAttributes` rejects a repeated name, `BuildDescForRelation` requires `USAGE` on every column's type, and `CheckAttributeNamesTypes` rejects system column names and pseudo-types.
51pub 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
70/// The columns of a table that a query defines, named by the column list and then by the query, as `intorel_startup` names them: more names than the query has columns are rejected.
71pub fn create_table_as_columns(
72    query_schema: &crate::RowSchema,
73    column_names: &[String],
74) -> Result<Vec<ColumnDef>, SQLError> {
75    if column_names.len() > query_schema.len() {
76        return Err(SQLError::Routine {
77            sqlstate: "42601".into(),
78            message: "too many column names were specified".into(),
79        });
80    }
81    let names = query_schema
82        .columns()
83        .iter()
84        .enumerate()
85        .map(|(position, name)| {
86            column_names
87                .get(position)
88                .cloned()
89                .unwrap_or_else(|| name.clone())
90        })
91        .collect::<Vec<_>>();
92    let columns = names
93        .into_iter()
94        .enumerate()
95        .map(|(position, name)| ColumnDef {
96            name,
97            ty: query_schema
98                .column_type(position)
99                .cloned()
100                .unwrap_or(ColumnType::Text),
101            object_id: None,
102            attribute_number: None,
103            missing_value: None,
104            primary_key: false,
105            not_null: false,
106            not_null_explicit: false,
107            not_null_name: None,
108            not_null_identity: None,
109            not_null_validated: true,
110            not_null_no_inherit: false,
111            not_null_is_local: true,
112            auto_increment: None,
113            unique: false,
114            default: None,
115            generated: None,
116            check: None,
117            check_name: None,
118            check_enforced: true,
119            check_validated: true,
120            check_no_inherit: false,
121            check_is_local: true,
122            check_object_id: None,
123            check_catalog_oid: None,
124            default_catalog_oid: None,
125            references: None,
126        })
127        .collect::<Vec<_>>();
128    Ok(columns)
129}
130
131pub mod checks;
132pub mod column_declarations;
133pub mod declaration;
134pub mod foreign_keys;
135pub mod keys;
136pub mod not_nulls;
137
138#[cfg(test)]
139mod tests;