use crate::{ColumnType, SQLError};
pub const POSTGRES_SYSTEM_COLUMNS: [&str; 6] = ["tableoid", "xmin", "cmin", "xmax", "cmax", "ctid"];
pub fn validate_postgres_column_name(name: &str) -> Result<(), SQLError> {
if POSTGRES_SYSTEM_COLUMNS.contains(&name) {
return Err(SQLError::Routine {
sqlstate: "42701".into(),
message: format!("column name \"{name}\" conflicts with a system column name"),
});
}
Ok(())
}
pub fn undefined_relation_column(table: &str, column: &str) -> SQLError {
match uqa_core::RelationIdentity::from_legacy_name(table) {
Ok(relation) => SQLError::Routine {
sqlstate: "42703".into(),
message: format!(
"column \"{column}\" of relation \"{}\" does not exist",
relation.name
),
},
Err(error) => SQLError::Internal(format!("resolve ALTER TABLE target `{table}`: {error}")),
}
}
pub fn validate_postgres_relation_column_type(name: &str, ty: &ColumnType) -> Result<(), SQLError> {
let pseudo_type = match ty {
ColumnType::Void | ColumnType::AnyArray | ColumnType::Record => Some(ty.sql_name()),
ColumnType::Array(element)
if matches!(
element.as_ref(),
ColumnType::Void | ColumnType::AnyArray | ColumnType::Record
) =>
{
Some(ty.sql_name())
}
_ => None,
};
if let Some(pseudo_type) = pseudo_type {
return Err(SQLError::Routine {
sqlstate: "42P16".into(),
message: format!("column \"{name}\" has pseudo-type {pseudo_type}"),
});
}
Ok(())
}
pub fn append_registered_column(
table: &str,
columns: &mut Vec<crate::ast::ColumnDef>,
column: crate::ast::ColumnDef,
) -> Result<(), String> {
if columns.iter().any(|existing| existing.name == column.name) {
return Err(format!(
"column `{}` already exists on table `{table}`",
column.name
));
}
columns.push(column);
Ok(())
}
pub fn validate_drop_column(
columns: &[crate::ast::ColumnDef],
table: &str,
column: &str,
if_exists: bool,
) -> Result<bool, SQLError> {
if POSTGRES_SYSTEM_COLUMNS.contains(&column) {
return Err(SQLError::Routine {
sqlstate: "0A000".into(),
message: format!("cannot drop system column \"{column}\""),
});
}
if columns.iter().any(|definition| definition.name == column) {
return Ok(true);
}
if if_exists {
Ok(false)
} else {
Err(undefined_relation_column(table, column))
}
}
pub fn missing_drop_column_notice(table: &str, column: &str) -> crate::SQLNotice {
crate::SQLNotice::notice(format!(
"column \"{column}\" of relation \"{table}\" does not exist, skipping"
))
}
pub fn altered_column<'a>(
table: &str,
columns: &'a [crate::ast::ColumnDef],
column: &str,
) -> Result<&'a crate::ast::ColumnDef, SQLError> {
columns
.iter()
.find(|definition| definition.name == column)
.ok_or_else(|| missing_altered_column(table, column))
}
pub fn missing_altered_column(table: &str, column: &str) -> SQLError {
if POSTGRES_SYSTEM_COLUMNS.contains(&column) {
return SQLError::Routine {
sqlstate: "0A000".into(),
message: format!("cannot alter system column \"{column}\""),
};
}
undefined_relation_column(table, column)
}
pub fn validate_default_change(
table: &str,
column: &crate::ast::ColumnDef,
setting: bool,
) -> Result<(), SQLError> {
let relation = uqa_core::RelationIdentity::from_legacy_name(table).map_err(|error| {
SQLError::Internal(format!("resolve ALTER TABLE target `{table}`: {error}"))
})?;
if column
.auto_increment
.as_ref()
.is_some_and(crate::ast::AutoIncrement::is_identity)
{
return Err(SQLError::Diagnostic {
sqlstate: "42601".into(),
message: format!(
"column \"{}\" of relation \"{}\" is an identity column",
column.name, relation.name
),
detail: None,
hint: (!setting)
.then(|| "Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.".into()),
});
}
if let Some(generated) = &column.generated {
let hint = if setting {
Some("Use ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION instead.")
} else if generated.kind == crate::ast::GeneratedColumnKind::Stored {
Some("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")
} else {
None
};
return Err(SQLError::Diagnostic {
sqlstate: "42601".into(),
message: format!(
"column \"{}\" of relation \"{}\" is a generated column",
column.name, relation.name
),
detail: None,
hint: hint.map(Into::into),
});
}
Ok(())
}
pub mod addition;
pub mod publication;
pub mod alteration;
pub mod removal_metadata;
pub mod type_target;
pub mod type_transform;