Skip to main content

sql_fun_sqlast/sem/
cte.rs

1use std::collections::HashMap;
2
3use sql_fun_core::IVec;
4
5use crate::{
6    sem::{
7        AnalysisError, AstAndContextPair, DeleteStatement, InsertStatement, ParseContext,
8        SelectStatement, SemAst, UpdateStatement, analyze_delete_statement,
9        analyze_insert_statement, analyze_select, analyze_update_statement,
10        create_table::{ColumnDefinition, ColumnName},
11    },
12    syn::{ListOpt, Opt, ScanToken},
13};
14
15/// CTE name
16pub type CteName = String;
17
18/// CTE query item types
19#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
20pub enum CteStatement {
21    /// `SELECT` statement
22    Select(Box<SelectStatement>),
23    /// `INSERT` statement
24    Insert(InsertStatement),
25    /// `UPDATE` statement
26    Update(UpdateStatement),
27    /// `DELETE` statement
28    Delete(DeleteStatement),
29}
30
31impl CteStatement {
32    fn analyze_syn<TParseContext>(
33        context: TParseContext,
34        syn: crate::syn::CommonTableExpr,
35        tokens: &IVec<ScanToken>,
36    ) -> Result<(CteStatement, TParseContext), AnalysisError>
37    where
38        TParseContext: ParseContext,
39    {
40        let query = syn.get_ctequery();
41
42        if let Some(select) = query.as_select_stmt().as_inner() {
43            let AstAndContextPair(sem, new_context) = analyze_select(context, select, tokens)?;
44            let SemAst::SelectStatement(s) = sem else {
45                AnalysisError::raise_unexpected_none("SemAst::SelectStatement")?
46            };
47            Ok((CteStatement::Select(Box::new(s)), new_context))
48        } else if let Some(insert) = query.as_insert_stmt().as_inner() {
49            let (sem, new_context) = analyze_insert_statement(context, &None, insert)?;
50            let SemAst::InsertStatement(s) = sem else {
51                AnalysisError::raise_unexpected_none("SemAst::InsertStatement")?
52            };
53            Ok((CteStatement::Insert(s), new_context))
54        } else if let Some(update) = query.as_update_stmt().as_inner() {
55            let (sem, new_context) = analyze_update_statement(context, &None, update)?;
56            let SemAst::UpdateStatement(s) = sem else {
57                AnalysisError::raise_unexpected_none("SemAst::UpdateStatement")?
58            };
59            Ok((CteStatement::Update(s), new_context))
60        } else if let Some(delete) = query.as_delete_stmt().as_inner() {
61            let (sem, new_context) = analyze_delete_statement(context, &None, delete)?;
62            let SemAst::DeleteStatement(s) = sem else {
63                AnalysisError::raise_unexpected_none("SemAst::DeleteStatement")?
64            };
65            Ok((CteStatement::Delete(s), new_context))
66        } else {
67            AnalysisError::raise_unexpected_none("common_table_expr.query")?
68        }
69    }
70
71    /// get column from result set
72    pub fn get_column<TParseContext>(
73        &self,
74        _context: &TParseContext,
75        _name: &ColumnName,
76    ) -> Option<ColumnDefinition>
77    where
78        TParseContext: ParseContext,
79    {
80        todo!()
81    }
82
83    /// get column at index
84    pub fn get_column_at<TParseContext>(
85        &self,
86        _context: &TParseContext,
87        _index: usize,
88    ) -> Option<&ColumnDefinition>
89    where
90        TParseContext: ParseContext,
91    {
92        todo!()
93    }
94
95    /// get column definition with skip count
96    pub fn get_column_def_with_skip_count<TParseContext>(
97        &self,
98        _context: &TParseContext,
99        _name: &ColumnName,
100        _skip_count: usize,
101    ) -> Option<&ColumnDefinition>
102    where
103        TParseContext: ParseContext,
104    {
105        todo!()
106    }
107
108    /// test column existing
109    pub(crate) fn has_column(&self, _column: &ColumnName) -> bool {
110        todo!()
111    }
112}
113
114/// analyzed [`crate::syn::WithClauseOpt`]
115#[derive(Debug, Clone, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
116pub struct WithClause(HashMap<CteName, CteStatement>);
117
118impl std::hash::Hash for WithClause {
119    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
120        for (k, v) in &self.0 {
121            k.hash(state);
122            v.hash(state);
123        }
124    }
125}
126
127impl WithClause {
128    /// get CTE query by name
129    #[must_use]
130    pub fn get(&self, name: &CteName) -> Option<&CteStatement> {
131        self.0.get(name)
132    }
133
134    /// insert named CTE item
135    pub fn insert(&mut self, name: &CteName, statement: CteStatement) {
136        self.0.insert(name.clone(), statement);
137    }
138
139    /// analyze [`crate::syn::WithClauseOpt`]
140    pub fn analyze<TParseContext>(
141        mut context: TParseContext,
142        syn: crate::syn::WithClauseOpt,
143        tokens: &IVec<ScanToken>,
144    ) -> Result<(WithClause, TParseContext), AnalysisError>
145    where
146        TParseContext: ParseContext,
147    {
148        let mut with = WithClause::default();
149        let Some(ctes) = syn.get_ctes().as_inner() else {
150            return Ok((with, context));
151        };
152
153        for cte in ctes {
154            let Some(table_expr) = cte.as_common_table_expr().as_inner() else {
155                AnalysisError::raise_unexpected_none("cte.as_common_table_expr")?
156            };
157            let name = table_expr.get_ctename();
158            let (cte, new_context) = CteStatement::analyze_syn(context, table_expr, tokens)?;
159            with.insert(&name, cte);
160            context = new_context;
161        }
162        Ok((with, context))
163    }
164}