Skip to main content

sql_fun_sqlast/sem/scalar_expr/
coalesce.rs

1use std::collections::HashSet;
2
3use sql_fun_core::IVec;
4
5use crate::{
6    sem::{
7        AnalysisError, AnalysisProblem, FromClause, ParseContext, SemScalarExpr, TypeReference,
8        WithClause, analyze_scaler_expr,
9    },
10    syn::{ListOpt, ScanToken},
11};
12
13use super::{AnalyzeScalarExpr, SemScalarExprNode};
14
15/// COALASCE expression
16#[derive(Debug, Clone, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize)]
17pub struct CoalesceExpr {
18    arg_exprs: Vec<SemScalarExpr>,
19    coalesce_type: Option<TypeReference>,
20}
21
22impl SemScalarExprNode for CoalesceExpr {
23    fn get_type(&self) -> Option<TypeReference> {
24        self.coalesce_type.clone()
25    }
26
27    fn is_not_null(&self) -> Option<bool> {
28        Some(false)
29    }
30}
31
32impl<TParseContext> AnalyzeScalarExpr<TParseContext, crate::syn::CoalesceExpr> for CoalesceExpr
33where
34    TParseContext: ParseContext,
35{
36    fn analyze_scalar_expr(
37        mut context: TParseContext,
38        with_clause: &WithClause,
39        from_clause: &FromClause,
40        syn: crate::syn::CoalesceExpr,
41        tokens: &IVec<ScanToken>,
42    ) -> Result<(SemScalarExpr, TParseContext), AnalysisError> {
43        let Some(args) = syn.get_args().as_inner() else {
44            AnalysisError::raise_unexpected_none("CoalesceExpr.args")?
45        };
46
47        let mut arg_exprs = Vec::with_capacity(args.len());
48        let mut expr_types = HashSet::new();
49        let mut first_non_null: Option<SemScalarExpr> = None;
50
51        for arg in args {
52            let (arg_expr, new_context) =
53                analyze_scaler_expr(context, with_clause, from_clause, arg, tokens)?;
54            context = new_context;
55
56            if let Some(fnn) = &first_non_null {
57                context.report_problem(AnalysisProblem::unreachable_coalesce_argument(
58                    &arg_expr, fnn,
59                ))?;
60            } else if let Some(nn) = &arg_expr.is_not_null()
61                && *nn
62            {
63                first_non_null = Some(arg_expr.clone());
64            }
65
66            if let Some(expr_type) = arg_expr.get_type() {
67                let Some(type_ref) = context.canonicalize_type_ref(&expr_type) else {
68                    Err(AnalysisError::type_canonicalize_failed(&expr_type))?
69                };
70                expr_types.insert(type_ref);
71            } else {
72                context
73                    .report_problem(AnalysisProblem::unknown_type_in_coalesce_expr(&arg_expr))?;
74            }
75            arg_exprs.push(arg_expr);
76        }
77
78        if expr_types.len() == 1 {
79            // ALL expressions returns same type no cast required
80            let coalesce_type = expr_types.iter().next().cloned().unwrap();
81            return Ok((
82                SemScalarExpr::Coalesce(Self {
83                    arg_exprs,
84                    coalesce_type: Some(coalesce_type),
85                }),
86                context,
87            ));
88        }
89
90        let mut candiates: Option<HashSet<TypeReference>> = None;
91        for expr_type in &expr_types {
92            if let Some(can) = candiates {
93                candiates = Some(
94                    can.intersection(&context.get_implicit_castable(expr_type))
95                        .cloned()
96                        .collect(),
97                );
98            } else {
99                candiates = Some(context.get_implicit_castable(expr_type));
100            }
101        }
102        let mut final_candiates = HashSet::new();
103        let coalesce_type = if let Some(ref candiates) = candiates
104            && !candiates.is_empty()
105        {
106            for candiate1 in candiates {
107                for candiate2 in candiates {
108                    if candiate1 == candiate2 {
109                        continue;
110                    }
111
112                    if let Some(cast) = context.get_implicit_cast(candiate1, candiate2) {
113                        if cast.is_no_coversion() || cast.is_implicit_preferred() {
114                            final_candiates.insert(candiate2.clone());
115                        }
116                    } else if let Some(cast) = context.get_implicit_cast(candiate2, candiate1) {
117                        if cast.is_no_coversion() || cast.is_implicit_preferred() {
118                            final_candiates.insert(candiate1.clone());
119                        }
120                    } else {
121                        AnalysisError::raise_unexpected_none("cast candiate lookup")?;
122                    }
123                }
124            }
125            let choose = final_candiates.iter().next();
126            if final_candiates.len() > 1 {
127                context.report_problem(AnalysisProblem::coalesce_expr_type_ambiguous(
128                    &arg_exprs,
129                    &final_candiates,
130                    choose,
131                ))?;
132            }
133            choose
134        } else {
135            context.report_problem(AnalysisProblem::coalesce_expr_type_unknown(&arg_exprs))?;
136            None
137        };
138
139        Ok((
140            SemScalarExpr::Coalesce(Self {
141                arg_exprs,
142                coalesce_type: coalesce_type.cloned(),
143            }),
144            context,
145        ))
146    }
147}