sql_fun_sqlast/sem/scalar_expr/
coalesce.rs1use 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#[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 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}