Skip to main content

sql_fun_sqlast/sem/constraint/
primary.rs

1use sql_fun_core::IVec;
2
3use crate::{
4    sem::{
5        AnalysisError, AnalysisProblem, ColumnName, Constraint, CreateIndex, ImplicitChange,
6        ParseContext, TableName, create_index::IndexElementCollection,
7    },
8    syn::ListOpt,
9};
10
11use super::AnalizeConstraint;
12
13/// analyzed primary key constraint
14#[derive(Debug, Clone)]
15pub struct PrimaryKeyConstraint {
16    name: String,
17    table: Option<TableName>,
18    keys: IVec<ColumnName>,
19}
20
21impl PrimaryKeyConstraint {
22    /// primary key name
23    #[must_use]
24    pub fn name(&self) -> &str {
25        &self.name
26    }
27
28    /// returns index definition for this primary key
29    pub fn implicit_changes(&self) -> Result<Vec<ImplicitChange>, AnalysisError> {
30        let Some(table) = &self.table else {
31            AnalysisError::raise_unexpected_none("constraint.table")?
32        };
33        let key_columns = IndexElementCollection::from_column_names(&self.keys);
34
35        Ok(vec![ImplicitChange::from(CreateIndex::new(
36            &self.name,
37            table,
38            &key_columns,
39        ))])
40    }
41
42    /// list of primary key columns
43    #[must_use]
44    pub fn keys(&self) -> &IVec<ColumnName> {
45        &self.keys
46    }
47}
48
49impl<TParseContext> AnalizeConstraint<TParseContext> for PrimaryKeyConstraint
50where
51    TParseContext: ParseContext,
52{
53    fn analyze(
54        context: &mut TParseContext,
55        target_table: Option<&TableName>,
56        constraint: crate::syn::Constraint,
57    ) -> Result<Constraint, AnalysisError> {
58        let name = constraint.get_conname();
59        let Some(keys) = constraint
60            .get_keys()
61            .map_values(|v| v.as_string().get_sval())
62        else {
63            AnalysisError::raise_unexpected_none("constraint.keys")?
64        };
65        let keys: Vec<_> = keys.into_iter().map(ColumnName::from).collect();
66
67        if let Some(table_name) = target_table {
68            if let Some(table) = context.get_table(table_name).cloned() {
69                for key_col_name in &keys {
70                    if !table.has_column(key_col_name) {
71                        context.report_problem(AnalysisProblem::column_not_found_in_table(
72                            table_name,
73                            key_col_name,
74                        ))?;
75                    }
76                }
77            } else {
78                context.report_problem(AnalysisProblem::relation_not_found(table_name))?;
79            }
80        }
81
82        Ok(Constraint::PrimaryKey(Self {
83            name,
84            table: target_table.cloned(),
85            keys: keys.into(),
86        }))
87    }
88}