Skip to main content

sql_fun_sqlast/sem/scalar_expr/
boolean.rs

1use sql_fun_core::IVec;
2
3use crate::{
4    sem::{
5        AnalysisError, FromClause, ParseContext, PgBuiltInType, SemScalarExpr, TypeReference,
6        WithClause, analyze_scaler_expr,
7    },
8    syn::{BoolExprType, ListOpt, Opt, ScanToken},
9};
10
11use super::{AnalyzeScalarExpr, SemScalarExprNode};
12
13#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
14pub enum BooleanExprBody {
15    And(Vec<SemScalarExpr>),
16    Or(Vec<SemScalarExpr>),
17    Not(Box<SemScalarExpr>),
18}
19
20impl BooleanExprBody {
21    pub fn and(items: &[SemScalarExpr]) -> Self {
22        Self::And(items.to_vec())
23    }
24
25    pub fn or(items: &[SemScalarExpr]) -> Self {
26        Self::Or(items.to_vec())
27    }
28
29    pub fn not(expr: &SemScalarExpr) -> Self {
30        Self::Not(Box::new(expr.clone()))
31    }
32}
33
34/// boolean expression
35#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
36pub struct BooleanExpr {
37    body: BooleanExprBody,
38}
39
40impl BooleanExpr {
41    fn analyze_args<TParseContext>(
42        mut context: TParseContext,
43        with_clause: &WithClause,
44        from_clause: &FromClause,
45        nodes: Vec<crate::syn::Node>,
46        tokens: &IVec<ScanToken>,
47    ) -> Result<(Vec<SemScalarExpr>, TParseContext), AnalysisError>
48    where
49        TParseContext: ParseContext,
50    {
51        let mut results = Vec::new();
52        for node in nodes {
53            let (arg_expr, new_context) =
54                analyze_scaler_expr(context, with_clause, from_clause, node, tokens)?;
55            context = new_context;
56            results.push(arg_expr);
57        }
58        Ok((results, context))
59    }
60}
61
62impl SemScalarExprNode for BooleanExpr {
63    fn get_type(&self) -> Option<TypeReference> {
64        Some(PgBuiltInType::bool())
65    }
66
67    fn is_not_null(&self) -> Option<bool> {
68        match &self.body {
69            BooleanExprBody::And(items) => {
70                for item in items {
71                    let is_not_null = item.is_not_null()?;
72                    if !is_not_null {
73                        return Some(false);
74                    }
75                }
76                Some(true)
77            }
78            BooleanExprBody::Or(items) => {
79                for item in items {
80                    let is_not_null = item.is_not_null()?;
81                    if !is_not_null {
82                        return Some(false);
83                    }
84                }
85                Some(true)
86            }
87            BooleanExprBody::Not(expr) => expr.is_not_null(),
88        }
89    }
90}
91
92impl<TParseContext> AnalyzeScalarExpr<TParseContext, crate::syn::BoolExpr> for BooleanExpr
93where
94    TParseContext: ParseContext,
95{
96    fn analyze_scalar_expr(
97        mut context: TParseContext,
98        with_clause: &WithClause,
99        from_clause: &FromClause,
100        syn: crate::syn::BoolExpr,
101        tokens: &IVec<ScanToken>,
102    ) -> Result<(SemScalarExpr, TParseContext), AnalysisError> {
103        let Some(boolop) = syn.get_boolop().as_inner() else {
104            AnalysisError::raise_unexpected_none("boolexpr.boolop")?
105        };
106        let Some(args) = syn.get_args().as_inner() else {
107            AnalysisError::raise_unexpected_none("boolexpr.args")?
108        };
109        let (args, new_context) =
110            Self::analyze_args(context, with_clause, from_clause, args, tokens)?;
111        context = new_context;
112
113        let body = match boolop {
114            BoolExprType::AndExpr => BooleanExprBody::and(&args),
115            BoolExprType::OrExpr => BooleanExprBody::or(&args),
116            BoolExprType::NotExpr => {
117                if args.len() != 1 {
118                    AnalysisError::raise_unexpected_input("boolexpr.args len")?;
119                }
120                BooleanExprBody::not(&args[0])
121            }
122            _ => AnalysisError::raise_unexpected_input("boolexpr.boolop unexpected value")?,
123        };
124        let expr = Self { body };
125        Ok((SemScalarExpr::Boolean(expr), context))
126    }
127}
128
129/// `NullTestExpr`
130#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
131pub struct NullTestExpr {
132    null_test_type: NullTestType,
133    arg_expr: Box<SemScalarExpr>,
134}
135
136#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
137pub enum NullTestType {
138    IsNull,
139    IsNotNull,
140}
141
142impl TryFrom<crate::syn::NullTestType> for NullTestType {
143    type Error = AnalysisError;
144
145    fn try_from(value: crate::syn::NullTestType) -> Result<Self, Self::Error> {
146        match value {
147            crate::syn::NullTestType::OptionNone | crate::syn::NullTestType::Undefined => {
148                AnalysisError::raise_unexpected_input("nulltesttype")?
149            }
150            crate::syn::NullTestType::IsNull => Ok(Self::IsNull),
151            crate::syn::NullTestType::IsNotNull => Ok(Self::IsNotNull),
152        }
153    }
154}
155
156impl SemScalarExprNode for NullTestExpr {
157    fn get_type(&self) -> Option<TypeReference> {
158        Some(PgBuiltInType::bool())
159    }
160
161    fn is_not_null(&self) -> Option<bool> {
162        Some(true)
163    }
164}
165
166impl<TParseContext> AnalyzeScalarExpr<TParseContext, crate::syn::NullTest> for NullTestExpr
167where
168    TParseContext: ParseContext,
169{
170    fn analyze_scalar_expr(
171        mut context: TParseContext,
172        with_clause: &WithClause,
173        from_clause: &FromClause,
174        syn: crate::syn::NullTest,
175        tokens: &IVec<ScanToken>,
176    ) -> Result<(SemScalarExpr, TParseContext), AnalysisError> {
177        let Some(null_test_type) = syn.get_nulltesttype().as_inner() else {
178            AnalysisError::raise_unexpected_none("NullTest.nulltesttype")?
179        };
180        let Some(arg) = syn.get_arg().as_inner() else {
181            AnalysisError::raise_unexpected_none("NullTest.arg")?
182        };
183        let (arg_expr, new_context) =
184            analyze_scaler_expr(context, with_clause, from_clause, arg, tokens)?;
185        context = new_context;
186        Ok((
187            SemScalarExpr::NullTest(Self {
188                null_test_type: NullTestType::try_from(null_test_type)?,
189                arg_expr: Box::new(arg_expr),
190            }),
191            context,
192        ))
193    }
194}