use crate::ast::{ColumnDef, ColumnType, CreateTable, TableKeyConstraint, TableKeyConstraintKind};
use crate::schema::columns::POSTGRES_SYSTEM_COLUMNS;
use crate::schema::constraint_metadata::{
identity::materialize_key_identity, materialize_foreign_key_identity, CatalogIdentityAllocator,
ConstraintMetadataError,
};
use crate::schema::indexes::names::{ConstraintIndexNamer, IndexNameCatalog};
use crate::schema::indexes::unique::{
validate_partitioned_key_constraint, validate_partitioned_unique_key, PartitionedUniqueKey,
};
use crate::schema::inheritance::InheritanceContext;
use crate::schema::keys::definition::{
index_order, missing_key_column, multiple_primary_keys, repeated_key_column,
validate_key_definition, validate_overlaps_column, validate_overlaps_key_length, KeyRelation,
};
use crate::SQLError;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct InheritedDefinitions {
pub expressions: Vec<String>,
pub not_nulls: Vec<super::not_nulls::InheritedNotNull>,
pub keys: usize,
pub foreign_keys: usize,
pub unique_indexes: Vec<Vec<crate::ast::IndexKey>>,
}
pub fn transform_declared_keys(
context: &InheritanceContext<'_>,
table: &mut CreateTable,
) -> Result<(), SQLError> {
let mut columns = KeyColumns {
context,
declared: &table.columns,
parents: &table.hierarchy.parents,
inherited: Vec::new(),
};
let mut primary_key = false;
for key in &table.key_constraints {
if key.kind == TableKeyConstraintKind::PrimaryKey {
if primary_key {
return Err(multiple_primary_keys(&table.qualifier));
}
primary_key = true;
}
validate_declared_key(&mut columns, key)?;
}
table.key_constraints = index_order(std::mem::take(&mut table.key_constraints));
for column in &mut table.columns {
column.primary_key = false;
column.unique = false;
}
Ok(())
}
struct KeyColumns<'s, 'c> {
context: &'s InheritanceContext<'c>,
declared: &'s [ColumnDef],
parents: &'s [String],
inherited: Vec<Vec<ColumnDef>>,
}
enum KeyColumn {
Column(ColumnType),
System,
}
impl KeyColumns<'_, '_> {
fn find(&mut self, name: &str) -> Result<Option<KeyColumn>, SQLError> {
if let Some(column) = self.declared.iter().find(|column| column.name == name) {
return Ok(Some(KeyColumn::Column(column.ty.clone())));
}
if POSTGRES_SYSTEM_COLUMNS.contains(&name) {
return Ok(Some(KeyColumn::System));
}
for (position, requested) in self.parents.iter().enumerate() {
if position == self.inherited.len() {
let parent = self.context.catalog.resolve_parent(requested)?;
let columns = self
.context
.partitions
.catalog
.try_describe_table(&parent)
.map_err(|error| {
SQLError::Internal(format!("read inherited row type: {error}"))
})?
.ok_or_else(|| SQLError::UnknownTable(parent.clone()))?;
self.inherited.push(columns);
}
if let Some(column) = self.inherited[position]
.iter()
.find(|column| column.name == name)
{
return Ok(Some(KeyColumn::Column(column.ty.clone())));
}
}
Ok(None)
}
}
fn validate_declared_key(
columns: &mut KeyColumns<'_, '_>,
key: &TableKeyConstraint,
) -> Result<(), SQLError> {
for (position, name) in key.columns.iter().enumerate() {
let Some(column) = columns.find(name)? else {
return Err(missing_key_column(name));
};
if key.columns[..position].contains(name) {
return Err(repeated_key_column(key.kind, name));
}
if key.without_overlaps && position + 1 == key.columns.len() {
let ty = match &column {
KeyColumn::Column(ty) => Some(ty),
KeyColumn::System => None,
};
validate_overlaps_column(name, ty)?;
}
}
validate_overlaps_key_length(key)?;
for name in &key.included_columns {
if columns.find(name)?.is_none() {
return Err(missing_key_column(name));
}
}
Ok(())
}
pub fn declare_primary_key_not_null(columns: &mut [ColumnDef], keys: &[TableKeyConstraint]) {
for key in keys
.iter()
.filter(|key| key.kind == TableKeyConstraintKind::PrimaryKey)
{
for name in &key.columns {
let Some(column) = columns.iter_mut().find(|column| column.name == *name) else {
continue;
};
if column.not_null && column.not_null_is_local {
continue;
}
if column.not_null {
column.not_null_name = None;
column.not_null_identity = None;
}
column.not_null = true;
column.not_null_is_local = true;
column.not_null_validated = true;
}
}
}
pub fn clone_parent_keys<'a>(
catalog: &'a dyn IndexNameCatalog,
table: &mut CreateTable,
inherited: &InheritedDefinitions,
allocate: &mut CatalogIdentityAllocator<'_>,
) -> Result<ConstraintIndexNamer<'a>, SQLError> {
let mut indexes = ConstraintIndexNamer::new(catalog, &table.name)?;
indexes.occupy(
table
.checks
.iter()
.filter(|check| !check.is_local)
.filter_map(|check| check.name.clone()),
);
let partition = table.hierarchy.partition_spec.as_ref();
for key in &mut table.key_constraints[..inherited.keys] {
if let Some(partition) = partition {
validate_partitioned_key_constraint(&table.name, key, partition)?;
}
indexes.name(key)?;
materialize_key_identity(key, allocate).map_err(ConstraintMetadataError::into_sql_error)?;
}
for foreign_key in &mut table.foreign_keys[..inherited.foreign_keys] {
materialize_foreign_key_identity(
&mut foreign_key.object_id,
&mut foreign_key.catalog_identity,
allocate,
)
.map_err(ConstraintMetadataError::into_sql_error)?;
}
if let Some(partition) = partition {
for keys in &inherited.unique_indexes {
let columns = keys
.iter()
.map(crate::ast::IndexKey::column)
.collect::<Vec<_>>();
validate_partitioned_unique_key(
&table.name,
&PartitionedUniqueKey {
constraint_type: TableKeyConstraintKind::Unique,
columns: &columns,
without_overlaps: false,
},
partition,
)?;
}
}
Ok(indexes)
}
pub fn define_declared_keys(
mut indexes: ConstraintIndexNamer<'_>,
table: &mut CreateTable,
inherited: &InheritedDefinitions,
allocate: &mut CatalogIdentityAllocator<'_>,
) -> Result<(), SQLError> {
indexes.occupy(
table
.columns
.iter()
.flat_map(|column| [column.not_null_name.clone(), column.check_name.clone()])
.chain(table.checks.iter().map(|check| check.name.clone()))
.flatten(),
);
let inherited_primary_key = table.key_constraints[..inherited.keys]
.iter()
.any(|key| key.kind == TableKeyConstraintKind::PrimaryKey);
let partition = table.hierarchy.partition_spec.as_ref();
let declared = &mut table.key_constraints[inherited.keys..];
for key in declared.iter_mut() {
validate_key_definition(
&KeyRelation {
table: &table.name,
columns: &table.columns,
partition,
has_primary_key: inherited_primary_key,
},
key,
)?;
indexes.name(key)?;
materialize_key_identity(key, allocate).map_err(ConstraintMetadataError::into_sql_error)?;
}
mark_single_column_keys(&mut table.columns, &table.key_constraints[inherited.keys..]);
Ok(())
}
fn mark_single_column_keys(columns: &mut [ColumnDef], keys: &[TableKeyConstraint]) {
for key in keys {
let [name] = key.columns.as_slice() else {
continue;
};
let Some(column) = columns.iter_mut().find(|column| column.name == *name) else {
continue;
};
match key.kind {
TableKeyConstraintKind::PrimaryKey => column.primary_key = true,
TableKeyConstraintKind::Unique => column.unique = true,
}
}
}