Skip to main content

uqa_sql/schema/
columns.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! `PostgreSQL` column-name and stored-type declaration rules.
8
9use crate::{ColumnType, SQLError};
10
11pub const POSTGRES_SYSTEM_COLUMNS: [&str; 6] = ["tableoid", "xmin", "cmin", "xmax", "cmax", "ctid"];
12
13pub fn validate_postgres_column_name(name: &str) -> Result<(), SQLError> {
14    if POSTGRES_SYSTEM_COLUMNS.contains(&name) {
15        return Err(SQLError::Routine {
16            sqlstate: "42701".into(),
17            message: format!("column name \"{name}\" conflicts with a system column name"),
18        });
19    }
20    Ok(())
21}
22
23pub fn validate_postgres_relation_column_type(name: &str, ty: &ColumnType) -> Result<(), SQLError> {
24    let pseudo_type = match ty {
25        ColumnType::Void | ColumnType::AnyArray | ColumnType::Record => Some(ty.sql_name()),
26        ColumnType::Array(element)
27            if matches!(
28                element.as_ref(),
29                ColumnType::Void | ColumnType::AnyArray | ColumnType::Record
30            ) =>
31        {
32            Some(ty.sql_name())
33        }
34        _ => None,
35    };
36    if let Some(pseudo_type) = pseudo_type {
37        return Err(SQLError::Routine {
38            sqlstate: "42P16".into(),
39            message: format!("column \"{name}\" has pseudo-type {pseudo_type}"),
40        });
41    }
42    Ok(())
43}
44
45/// Append a declared column after checking its name against the existing schema.
46pub fn append_registered_column(
47    table: &str,
48    columns: &mut Vec<crate::ast::ColumnDef>,
49    column: crate::ast::ColumnDef,
50) -> Result<(), String> {
51    if columns.iter().any(|existing| existing.name == column.name) {
52        return Err(format!(
53            "column `{}` already exists on table `{table}`",
54            column.name
55        ));
56    }
57    columns.push(column);
58    Ok(())
59}
60
61pub fn reject_default_change_on_generated_column(
62    catalog: &dyn crate::assignment::columns::AssignmentColumnCatalog,
63    table: &str,
64    column: &str,
65) -> Result<(), SQLError> {
66    if crate::assignment::columns::generated_column_kind(catalog, table, column)?.is_some() {
67        return Err(SQLError::TypeMismatch(format!(
68            "column `{column}` of relation `{table}` is a generated column; use SET EXPRESSION or DROP EXPRESSION"
69        )));
70    }
71    Ok(())
72}
73
74pub mod addition;
75
76pub mod publication;
77
78pub mod alteration;
79
80pub mod removal;
81
82pub mod removal_metadata;