Skip to main content

risingwave_frontend/binder/expr/
mod.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::slice;
16
17use itertools::Itertools;
18use risingwave_common::catalog::PG_CATALOG_SCHEMA_NAME;
19use risingwave_common::types::{DataType, MapType, StructType};
20use risingwave_common::util::iter_util::zip_eq_fast;
21use risingwave_common::{bail_no_function, bail_not_implemented, not_implemented};
22use risingwave_sqlparser::ast::{
23    Array, BinaryOperator, DataType as AstDataType, EscapeChar, Expr, Function, JsonPredicateType,
24    ObjectName, Query, TrimWhereField, UnaryOperator,
25};
26
27use crate::binder::Binder;
28use crate::binder::expr::function::is_sys_function_without_args;
29use crate::error::{ErrorCode, Result, RwError};
30use crate::expr::{
31    Expr as _, ExprImpl, ExprRewriter as _, ExprType, FunctionCall, InputRef,
32    InputRefDepthRewriter, Parameter, SubqueryKind,
33};
34
35mod binary_op;
36mod column;
37mod function;
38mod order_by;
39mod subquery;
40mod value;
41
42/// The limit arms for case-when expression
43/// When the number of condition arms exceed
44/// this limit, we will try optimize the case-when
45/// expression to `ConstantLookupExpression`
46/// Check `case.rs` for details.
47const CASE_WHEN_ARMS_OPTIMIZE_LIMIT: usize = 30;
48
49impl Binder {
50    /// Bind an expression with `bind_expr_inner`, attach the original expression
51    /// to the error message.
52    ///
53    /// This may only be called at the root of the expression tree or when crossing
54    /// the boundary of a subquery. Otherwise, the source chain might be too deep
55    /// and confusing to the user.
56    // TODO(error-handling): use a dedicated error type during binding to make it clear.
57    pub fn bind_expr(&mut self, expr: &Expr) -> Result<ExprImpl> {
58        self.bind_expr_inner(expr).map_err(|e| {
59            RwError::from(ErrorCode::BindErrorRoot {
60                expr: expr.to_string(),
61                error: Box::new(e),
62            })
63        })
64    }
65
66    fn bind_expr_inner(&mut self, expr: &Expr) -> Result<ExprImpl> {
67        match expr {
68            // literal
69            Expr::Value(v) => Ok(ExprImpl::Literal(Box::new(self.bind_value(v)?))),
70            Expr::TypedString { data_type, value } => {
71                let s: ExprImpl = self.bind_string(value)?.into();
72                s.cast_explicit(&bind_data_type(data_type)?)
73                    .map_err(Into::into)
74            }
75            Expr::Row(exprs) => self.bind_row(exprs),
76            // input ref
77            Expr::Identifier(ident) => {
78                if is_sys_function_without_args(ident) {
79                    // Rewrite a system variable to a function call, e.g. `SELECT current_schema;`
80                    // will be rewritten to `SELECT current_schema();`.
81                    // NOTE: Here we don't 100% follow the behavior of Postgres, as it doesn't
82                    // allow `session_user()` while we do.
83                    self.bind_function(&Function::no_arg(ObjectName(vec![ident.clone()])))
84                } else if let Some(ref lambda_args) = self.context.lambda_args {
85                    // We don't support capture, so if the expression is in the lambda context,
86                    // we'll not bind it for table columns.
87                    if let Some((arg_idx, arg_type)) = lambda_args.get(&ident.real_value()) {
88                        Ok(InputRef::new(*arg_idx, arg_type.clone()).into())
89                    } else {
90                        Err(
91                            ErrorCode::ItemNotFound(format!("Unknown arg: {}", ident.real_value()))
92                                .into(),
93                        )
94                    }
95                } else if let Some(ctx) = self.secure_compare_context.as_ref() {
96                    // Currently, the generated columns are not supported yet. So the ident here should only be one of the following
97                    // - `headers`
98                    // - secret name
99                    // - the identifier bound to the raw webhook payload
100                    // TODO(Kexiang): Generated columns or INCLUDE clause should be supported.
101                    if ident.real_value() == *"headers" {
102                        Ok(InputRef::new(0, DataType::Jsonb).into())
103                    } else if ctx.secret_name.is_some()
104                        && ident.real_value() == *ctx.secret_name.as_ref().unwrap()
105                    {
106                        Ok(InputRef::new(1, DataType::Varchar).into())
107                    } else if ident.real_value() == ctx.payload_name {
108                        Ok(InputRef::new(2, DataType::Bytea).into())
109                    } else {
110                        Err(
111                            ErrorCode::ItemNotFound(format!("Unknown arg: {}", ident.real_value()))
112                                .into(),
113                        )
114                    }
115                } else {
116                    self.bind_column(slice::from_ref(ident))
117                }
118            }
119            Expr::CompoundIdentifier(idents) => self.bind_column(idents),
120            Expr::FieldIdentifier(field_expr, idents) => {
121                self.bind_single_field_column(field_expr, idents)
122            }
123            // operators & functions
124            Expr::UnaryOp { op, expr } => self.bind_unary_expr(op, expr),
125            Expr::BinaryOp { left, op, right } => self.bind_binary_op(left, op, right),
126            Expr::Nested(expr) => self.bind_expr_inner(expr),
127            Expr::Array(Array { elem: exprs, .. }) => self.bind_array(exprs),
128            Expr::Index { obj, index } => self.bind_index(obj, index),
129            Expr::ArrayRangeIndex { obj, start, end } => {
130                self.bind_array_range_index(obj, start.as_deref(), end.as_deref())
131            }
132            Expr::Function(f) => self.bind_function(f),
133            Expr::Subquery(q) => self.bind_subquery_expr(q, SubqueryKind::Scalar),
134            Expr::Exists(q) => self.bind_subquery_expr(q, SubqueryKind::Existential),
135            Expr::InSubquery {
136                expr,
137                subquery,
138                negated,
139            } => self.bind_in_subquery(expr, subquery, *negated),
140            // special syntax (except date/time or string)
141            Expr::Cast { expr, data_type } => self.bind_cast(expr, data_type),
142            Expr::IsNull(expr) => self.bind_is_operator(ExprType::IsNull, expr),
143            Expr::IsNotNull(expr) => self.bind_is_operator(ExprType::IsNotNull, expr),
144            Expr::IsTrue(expr) => self.bind_is_operator(ExprType::IsTrue, expr),
145            Expr::IsNotTrue(expr) => self.bind_is_operator(ExprType::IsNotTrue, expr),
146            Expr::IsFalse(expr) => self.bind_is_operator(ExprType::IsFalse, expr),
147            Expr::IsNotFalse(expr) => self.bind_is_operator(ExprType::IsNotFalse, expr),
148            Expr::IsUnknown(expr) => self.bind_is_unknown(ExprType::IsNull, expr),
149            Expr::IsNotUnknown(expr) => self.bind_is_unknown(ExprType::IsNotNull, expr),
150            Expr::IsDistinctFrom(left, right) => self.bind_distinct_from(left, right),
151            Expr::IsNotDistinctFrom(left, right) => self.bind_not_distinct_from(left, right),
152            Expr::IsJson {
153                expr,
154                negated,
155                item_type,
156                unique_keys: false,
157            } => self.bind_is_json(expr, *negated, *item_type),
158            Expr::Case {
159                operand,
160                conditions,
161                results,
162                else_result,
163            } => self.bind_case(
164                operand.as_deref(),
165                conditions,
166                results,
167                else_result.as_deref(),
168            ),
169            Expr::Between {
170                expr,
171                negated,
172                low,
173                high,
174            } => self.bind_between(expr, *negated, low, high),
175            Expr::Like {
176                negated,
177                expr,
178                pattern,
179                escape_char,
180            } => self.bind_like(ExprType::Like, expr, *negated, pattern, *escape_char),
181            Expr::ILike {
182                negated,
183                expr,
184                pattern,
185                escape_char,
186            } => self.bind_like(ExprType::ILike, expr, *negated, pattern, *escape_char),
187            Expr::SimilarTo {
188                expr,
189                negated,
190                pattern,
191                escape_char,
192            } => self.bind_similar_to(expr, *negated, pattern, *escape_char),
193            Expr::InList {
194                expr,
195                list,
196                negated,
197            } => self.bind_in_list(expr, list, *negated),
198            // special syntax for date/time
199            Expr::Extract { field, expr } => self.bind_extract(field, expr),
200            Expr::AtTimeZone {
201                timestamp,
202                time_zone,
203            } => self.bind_at_time_zone(timestamp, time_zone),
204            // special syntax for string
205            Expr::Trim {
206                expr,
207                trim_where,
208                trim_what,
209            } => self.bind_trim(expr, trim_where.as_ref(), trim_what.as_deref()),
210            Expr::Substring {
211                expr,
212                substring_from,
213                substring_for,
214            } => self.bind_substring(expr, substring_from.as_deref(), substring_for.as_deref()),
215            Expr::Position { substring, string } => self.bind_position(substring, string),
216            Expr::Overlay {
217                expr,
218                new_substring,
219                start,
220                count,
221            } => self.bind_overlay(expr, new_substring, start, count.as_deref()),
222            Expr::Parameter { index } => self.bind_parameter(*index),
223            Expr::Collate { expr, collation } => self.bind_collate(expr, collation),
224            Expr::ArraySubquery(q) => self.bind_subquery_expr(q, SubqueryKind::Array),
225            Expr::Map { entries } => self.bind_map(entries),
226            Expr::IsJson {
227                unique_keys: true, ..
228            }
229            | Expr::SomeOp(_)
230            | Expr::AllOp(_)
231            | Expr::TryCast { .. }
232            | Expr::GroupingSets(_)
233            | Expr::Cube(_)
234            | Expr::Rollup(_)
235            | Expr::LambdaFunction { .. } => {
236                bail_not_implemented!(issue = 112, "unsupported expression {:?}", expr)
237            }
238        }
239    }
240
241    pub(super) fn bind_extract(&mut self, field: &String, expr: &Expr) -> Result<ExprImpl> {
242        let arg = self.bind_expr_inner(expr)?;
243        let arg_type = arg.return_type();
244        Ok(FunctionCall::new(
245            ExprType::Extract,
246            vec![self.bind_string(field)?.into(), arg],
247        )
248        .map_err(|_| {
249            not_implemented!(
250                issue = 112,
251                "function extract({} from {:?}) doesn't exist",
252                field,
253                arg_type
254            )
255        })?
256        .into())
257    }
258
259    pub(super) fn bind_at_time_zone(&mut self, input: &Expr, time_zone: &Expr) -> Result<ExprImpl> {
260        let input = self.bind_expr_inner(input)?;
261        let time_zone = self.bind_expr_inner(time_zone)?;
262        FunctionCall::new(ExprType::AtTimeZone, vec![input, time_zone]).map(Into::into)
263    }
264
265    pub(super) fn bind_in_list(
266        &mut self,
267        expr: &Expr,
268        list: &[Expr],
269        negated: bool,
270    ) -> Result<ExprImpl> {
271        let left = self.bind_expr_inner(expr)?;
272        let mut in_list_exprs = vec![left.clone()];
273        let mut non_const_exprs = vec![];
274        for elem in list {
275            let expr = self.bind_expr_inner(elem)?;
276            match expr.is_const() || matches!(&expr, ExprImpl::Parameter(_)) {
277                true => in_list_exprs.push(expr),
278                false => non_const_exprs.push(expr),
279            }
280        }
281
282        let mut ret = if in_list_exprs.len() == 1 {
283            None
284        } else {
285            Some(FunctionCall::new(ExprType::In, in_list_exprs)?.into())
286        };
287        // Row-dependent list items are not part of IN-expr in backend and rewritten into
288        // OR-Equal-exprs.
289        for expr in non_const_exprs {
290            if let Some(inner_ret) = ret {
291                ret = Some(
292                    FunctionCall::new(
293                        ExprType::Or,
294                        vec![
295                            inner_ret,
296                            FunctionCall::new(ExprType::Equal, vec![left.clone(), expr])?.into(),
297                        ],
298                    )?
299                    .into(),
300                );
301            } else {
302                ret = Some(FunctionCall::new(ExprType::Equal, vec![left.clone(), expr])?.into());
303            }
304        }
305        if negated {
306            Ok(
307                FunctionCall::new_unchecked(ExprType::Not, vec![ret.unwrap()], DataType::Boolean)
308                    .into(),
309            )
310        } else {
311            Ok(ret.unwrap())
312        }
313    }
314
315    pub(super) fn bind_in_subquery(
316        &mut self,
317        expr: &Expr,
318        subquery: &Query,
319        negated: bool,
320    ) -> Result<ExprImpl> {
321        let bound_expr = self.bind_expr_inner(expr)?;
322        let bound_subquery = self.bind_subquery_expr(subquery, SubqueryKind::In(bound_expr))?;
323        if negated {
324            Ok(
325                FunctionCall::new_unchecked(ExprType::Not, vec![bound_subquery], DataType::Boolean)
326                    .into(),
327            )
328        } else {
329            Ok(bound_subquery)
330        }
331    }
332
333    pub(super) fn bind_is_json(
334        &mut self,
335        expr: &Expr,
336        negated: bool,
337        item_type: JsonPredicateType,
338    ) -> Result<ExprImpl> {
339        let mut args = vec![self.bind_expr_inner(expr)?];
340        // Avoid `JsonPredicateType::to_string` so that we decouple sqlparser from expr execution
341        let type_symbol = match item_type {
342            JsonPredicateType::Value => None,
343            JsonPredicateType::Array => Some("ARRAY"),
344            JsonPredicateType::Object => Some("OBJECT"),
345            JsonPredicateType::Scalar => Some("SCALAR"),
346        };
347        if let Some(s) = type_symbol {
348            args.push(ExprImpl::literal_varchar(s.into()));
349        }
350
351        let is_json = FunctionCall::new(ExprType::IsJson, args)?.into();
352        if negated {
353            Ok(FunctionCall::new(ExprType::Not, vec![is_json])?.into())
354        } else {
355            Ok(is_json)
356        }
357    }
358
359    pub(super) fn bind_unary_expr(&mut self, op: &UnaryOperator, expr: &Expr) -> Result<ExprImpl> {
360        let func_type = match &op {
361            UnaryOperator::Not => ExprType::Not,
362            UnaryOperator::Minus => ExprType::Neg,
363            UnaryOperator::Plus => {
364                return self.rewrite_positive(expr);
365            }
366            UnaryOperator::Custom(name) => match name.as_str() {
367                "~" => ExprType::BitwiseNot,
368                "@" => ExprType::Abs,
369                "|/" => ExprType::Sqrt,
370                "||/" => ExprType::Cbrt,
371                _ => bail_not_implemented!(issue = 112, "unsupported unary expression: {:?}", op),
372            },
373            UnaryOperator::PGQualified(_) => {
374                bail_not_implemented!(issue = 112, "unsupported unary expression: {:?}", op)
375            }
376        };
377        let expr = self.bind_expr_inner(expr)?;
378        FunctionCall::new(func_type, vec![expr]).map(|f| f.into())
379    }
380
381    /// Directly returns the expression itself if it is a positive number.
382    fn rewrite_positive(&mut self, expr: &Expr) -> Result<ExprImpl> {
383        let expr = self.bind_expr_inner(expr)?;
384        let return_type = expr.return_type();
385        if return_type.is_numeric() {
386            return Ok(expr);
387        }
388        Err(ErrorCode::InvalidInputSyntax(format!("+ {:?}", return_type)).into())
389    }
390
391    pub(super) fn bind_trim(
392        &mut self,
393        expr: &Expr,
394        // BOTH | LEADING | TRAILING
395        trim_where: Option<&TrimWhereField>,
396        trim_what: Option<&Expr>,
397    ) -> Result<ExprImpl> {
398        let mut inputs = vec![self.bind_expr_inner(expr)?];
399        let func_type = match trim_where {
400            Some(TrimWhereField::Both) => ExprType::Trim,
401            Some(TrimWhereField::Leading) => ExprType::Ltrim,
402            Some(TrimWhereField::Trailing) => ExprType::Rtrim,
403            None => ExprType::Trim,
404        };
405        if let Some(t) = trim_what {
406            inputs.push(self.bind_expr_inner(t)?);
407        }
408        Ok(FunctionCall::new(func_type, inputs)?.into())
409    }
410
411    fn bind_substring(
412        &mut self,
413        expr: &Expr,
414        substring_from: Option<&Expr>,
415        substring_for: Option<&Expr>,
416    ) -> Result<ExprImpl> {
417        let mut args = vec![
418            self.bind_expr_inner(expr)?,
419            match substring_from {
420                Some(expr) => self.bind_expr_inner(expr)?,
421                None => ExprImpl::literal_int(1),
422            },
423        ];
424        if let Some(expr) = substring_for {
425            args.push(self.bind_expr_inner(expr)?);
426        }
427        FunctionCall::new(ExprType::Substr, args).map(|f| f.into())
428    }
429
430    fn bind_position(&mut self, substring: &Expr, string: &Expr) -> Result<ExprImpl> {
431        let args = vec![
432            // Note that we reverse the order of arguments.
433            self.bind_expr_inner(string)?,
434            self.bind_expr_inner(substring)?,
435        ];
436        FunctionCall::new(ExprType::Position, args).map(Into::into)
437    }
438
439    fn bind_overlay(
440        &mut self,
441        expr: &Expr,
442        new_substring: &Expr,
443        start: &Expr,
444        count: Option<&Expr>,
445    ) -> Result<ExprImpl> {
446        let mut args = vec![
447            self.bind_expr_inner(expr)?,
448            self.bind_expr_inner(new_substring)?,
449            self.bind_expr_inner(start)?,
450        ];
451        if let Some(count) = count {
452            args.push(self.bind_expr_inner(count)?);
453        }
454        FunctionCall::new(ExprType::Overlay, args).map(|f| f.into())
455    }
456
457    fn is_binding_inline_sql_udf(&self) -> bool {
458        self.context.sql_udf_arguments.is_some()
459    }
460
461    /// Returns whether we're binding SQL UDF by checking if any of the upper subquery context has
462    /// `sql_udf_arguments` set.
463    fn is_binding_subquery_sql_udf(&self) -> bool {
464        self.upper_subquery_contexts
465            .iter()
466            .any(|(context, _)| context.sql_udf_arguments.is_some())
467    }
468
469    /// Bind a parameter for SQL UDF.
470    fn bind_sql_udf_parameter(&mut self, name: &str) -> Result<ExprImpl> {
471        for (depth, context) in std::iter::once(&self.context)
472            .chain((self.upper_subquery_contexts.iter().rev()).map(|(context, _)| context))
473            .enumerate()
474        {
475            // Only lookup the first non-empty udf context. If the parameter is not found in the
476            // current context, we will continue to the upper context.
477            if let Some(args) = &context.sql_udf_arguments {
478                if let Some(expr) = args.get(name) {
479                    // The arguments recorded in the context is relative to the that context.
480                    // We need to shift the depth to the current context.
481                    let mut rewriter = InputRefDepthRewriter::new(depth);
482                    return Ok(rewriter.rewrite_expr(expr.clone()));
483                } else {
484                    // A UDF cannot access parameters from outer UDFs. Do not continue but directly
485                    // return an error.
486                    break;
487                }
488            }
489        }
490
491        Err(ErrorCode::BindError(format!(
492            "failed to find {} parameter {name}",
493            if name.starts_with('$') {
494                "unnamed"
495            } else {
496                "named"
497            }
498        ))
499        .into())
500    }
501
502    fn bind_parameter(&mut self, index: u64) -> Result<ExprImpl> {
503        // Special check for sql udf
504        // Note: This is specific to sql udf with unnamed parameters, since the
505        // parameters will be parsed and treated as `Parameter`.
506        // For detailed explanation, consider checking `bind_column`.
507        if self.is_binding_inline_sql_udf() || self.is_binding_subquery_sql_udf() {
508            let column_name = format!("${index}");
509            return self.bind_sql_udf_parameter(&column_name);
510        }
511
512        Ok(Parameter::new(index, self.param_types.clone()).into())
513    }
514
515    /// Bind `expr (not) between low and high`
516    pub(super) fn bind_between(
517        &mut self,
518        expr: &Expr,
519        negated: bool,
520        low: &Expr,
521        high: &Expr,
522    ) -> Result<ExprImpl> {
523        let expr = self.bind_expr_inner(expr)?;
524        let low = self.bind_expr_inner(low)?;
525        let high = self.bind_expr_inner(high)?;
526
527        let func_call = if negated {
528            // negated = true: expr < low or expr > high
529            FunctionCall::new_unchecked(
530                ExprType::Or,
531                vec![
532                    FunctionCall::new(ExprType::LessThan, vec![expr.clone(), low])?.into(),
533                    FunctionCall::new(ExprType::GreaterThan, vec![expr, high])?.into(),
534                ],
535                DataType::Boolean,
536            )
537        } else {
538            // negated = false: expr >= low and expr <= high
539            FunctionCall::new_unchecked(
540                ExprType::And,
541                vec![
542                    FunctionCall::new(ExprType::GreaterThanOrEqual, vec![expr.clone(), low])?
543                        .into(),
544                    FunctionCall::new(ExprType::LessThanOrEqual, vec![expr, high])?.into(),
545                ],
546                DataType::Boolean,
547            )
548        };
549
550        Ok(func_call.into())
551    }
552
553    fn bind_like(
554        &mut self,
555        expr_type: ExprType,
556        expr: &Expr,
557        negated: bool,
558        pattern: &Expr,
559        escape_char: Option<EscapeChar>,
560    ) -> Result<ExprImpl> {
561        if matches!(pattern, Expr::AllOp(_) | Expr::SomeOp(_)) {
562            if escape_char.is_some() {
563                // PostgreSQL also don't support the pattern due to the complexity of implementation.
564                // The SQL will failed on PostgreSQL 16.1:
565                // ```sql
566                // select 'a' like any(array[null]) escape '';
567                // ```
568                bail_not_implemented!(
569                    "LIKE with both ALL|ANY pattern and escape character is not supported"
570                )
571            }
572            // Use the `bind_binary_op` path to handle the ALL|ANY pattern.
573            let op = match (expr_type, negated) {
574                (ExprType::Like, false) => BinaryOperator::Custom("~~".to_owned()),
575                (ExprType::Like, true) => BinaryOperator::Custom("!~~".to_owned()),
576                (ExprType::ILike, false) => BinaryOperator::Custom("~~*".to_owned()),
577                (ExprType::ILike, true) => BinaryOperator::Custom("!~~*".to_owned()),
578                _ => unreachable!(),
579            };
580            return self.bind_binary_op(expr, &op, pattern);
581        }
582        let expr = self.bind_expr_inner(expr)?;
583        let pattern = self.bind_expr_inner(pattern)?;
584        match (expr.return_type(), pattern.return_type()) {
585            (DataType::Varchar, DataType::Varchar) => {}
586            (string_ty, pattern_ty) => match expr_type {
587                ExprType::Like => bail_no_function!("like({}, {})", string_ty, pattern_ty),
588                ExprType::ILike => bail_no_function!("ilike({}, {})", string_ty, pattern_ty),
589                _ => unreachable!(),
590            },
591        }
592        let args = match escape_char {
593            Some(escape_char) => {
594                let escape_char = ExprImpl::literal_varchar(escape_char.to_string());
595                vec![expr, pattern, escape_char]
596            }
597            None => vec![expr, pattern],
598        };
599        let func_call = FunctionCall::new_unchecked(expr_type, args, DataType::Boolean);
600        let func_call = if negated {
601            FunctionCall::new_unchecked(ExprType::Not, vec![func_call.into()], DataType::Boolean)
602        } else {
603            func_call
604        };
605        Ok(func_call.into())
606    }
607
608    /// Bind `<expr> [ NOT ] SIMILAR TO <pat> ESCAPE <esc_text>`
609    pub(super) fn bind_similar_to(
610        &mut self,
611        expr: &Expr,
612        negated: bool,
613        pattern: &Expr,
614        escape_char: Option<EscapeChar>,
615    ) -> Result<ExprImpl> {
616        let expr = self.bind_expr_inner(expr)?;
617        let pattern = self.bind_expr_inner(pattern)?;
618
619        let esc_inputs = if let Some(escape_char) = escape_char {
620            let escape_char = ExprImpl::literal_varchar(escape_char.to_string());
621            vec![pattern, escape_char]
622        } else {
623            vec![pattern]
624        };
625
626        let esc_call =
627            FunctionCall::new_unchecked(ExprType::SimilarToEscape, esc_inputs, DataType::Varchar);
628
629        let regex_call = FunctionCall::new_unchecked(
630            ExprType::RegexpEq,
631            vec![expr, esc_call.into()],
632            DataType::Boolean,
633        );
634        let func_call = if negated {
635            FunctionCall::new_unchecked(ExprType::Not, vec![regex_call.into()], DataType::Boolean)
636        } else {
637            regex_call
638        };
639
640        Ok(func_call.into())
641    }
642
643    /// The optimization check for the following case-when expression pattern
644    /// e.g., select case 1 when (...) then (...) else (...) end;
645    fn check_constant_case_when_optimization(
646        &mut self,
647        conditions: &[Expr],
648        results_expr: &[ExprImpl],
649        operand: Option<&Expr>,
650        fallback: Option<&ExprImpl>,
651        constant_case_when_eval_inputs: &mut Vec<ExprImpl>,
652    ) -> bool {
653        // The operand value to be compared later
654        let operand_value;
655
656        if let Some(operand) = operand {
657            let Ok(operand) = self.bind_expr_inner(operand) else {
658                return false;
659            };
660            if !operand.is_const() {
661                return false;
662            }
663            operand_value = operand;
664        } else {
665            return false;
666        }
667
668        for (condition, result) in zip_eq_fast(conditions, results_expr) {
669            if let Expr::Value(_) = condition.clone() {
670                let Ok(res) = self.bind_expr_inner(condition) else {
671                    return false;
672                };
673                // Found a match
674                if res == operand_value {
675                    constant_case_when_eval_inputs.push(result.clone());
676                    return true;
677                }
678            } else {
679                return false;
680            }
681        }
682
683        // Otherwise this will eventually go through fallback arm
684        debug_assert!(
685            constant_case_when_eval_inputs.is_empty(),
686            "expect `inputs` to be empty"
687        );
688
689        let Some(fallback) = fallback else {
690            return false;
691        };
692
693        constant_case_when_eval_inputs.push(fallback.clone());
694        true
695    }
696
697    /// Helper function to compare or set column identifier
698    /// used in `check_convert_simple_form`
699    fn compare_or_set(col_expr: &mut Option<Expr>, test_expr: &Expr) -> bool {
700        let Expr::Identifier(test_ident) = test_expr else {
701            return false;
702        };
703        if let Some(expr) = col_expr {
704            let Expr::Identifier(ident) = expr else {
705                return false;
706            };
707            if ident.real_value() != test_ident.real_value() {
708                return false;
709            }
710        } else {
711            *col_expr = Some(Expr::Identifier(test_ident.clone()));
712        }
713        true
714    }
715
716    /// left expression and right expression must be either:
717    /// `<constant> <Eq> <identifier>` or `<identifier> <Eq> <constant>`
718    /// used in `check_convert_simple_form`
719    fn check_invariant(left: &Expr, op: &BinaryOperator, right: &Expr) -> bool {
720        if op != &BinaryOperator::Eq {
721            return false;
722        }
723        if let Expr::Identifier(_) = left {
724            // <identifier> <Eq> <constant>
725            let Expr::Value(_) = right else {
726                return false;
727            };
728        } else {
729            // <constant> <Eq> <identifier>
730            let Expr::Value(_) = left else {
731                return false;
732            };
733            let Expr::Identifier(_) = right else {
734                return false;
735            };
736        }
737        true
738    }
739
740    /// Helper function to extract expression out and insert
741    /// the corresponding bound version to `inputs`
742    /// used in `check_convert_simple_form`
743    /// Note: this function will be invoked per arm
744    fn try_extract_simple_form(
745        &mut self,
746        ident_expr: &Expr,
747        constant_expr: &Expr,
748        column_expr: &mut Option<Expr>,
749        inputs: &mut Vec<ExprImpl>,
750    ) -> bool {
751        if !Self::compare_or_set(column_expr, ident_expr) {
752            return false;
753        }
754        let Ok(bound_expr) = self.bind_expr_inner(constant_expr) else {
755            return false;
756        };
757        inputs.push(bound_expr);
758        true
759    }
760
761    /// See if the case when expression in form
762    /// `select case when <expr_1 = constant> (...with same pattern...) else <constant> end;`
763    /// If so, this expression could also be converted to constant lookup
764    fn check_convert_simple_form(
765        &mut self,
766        conditions: &[Expr],
767        results_expr: &[ExprImpl],
768        fallback: Option<ExprImpl>,
769        constant_lookup_inputs: &mut Vec<ExprImpl>,
770    ) -> bool {
771        let mut column_expr = None;
772
773        for (condition, result) in zip_eq_fast(conditions, results_expr) {
774            if let Expr::BinaryOp { left, op, right } = condition {
775                if !Self::check_invariant(left, op, right) {
776                    return false;
777                }
778                if let Expr::Identifier(_) = &**left {
779                    if !self.try_extract_simple_form(
780                        left,
781                        right,
782                        &mut column_expr,
783                        constant_lookup_inputs,
784                    ) {
785                        return false;
786                    }
787                } else if !self.try_extract_simple_form(
788                    right,
789                    left,
790                    &mut column_expr,
791                    constant_lookup_inputs,
792                ) {
793                    return false;
794                }
795                constant_lookup_inputs.push(result.clone());
796            } else {
797                return false;
798            }
799        }
800
801        // Insert operand first
802        let Some(operand) = column_expr else {
803            return false;
804        };
805        let Ok(bound_operand) = self.bind_expr_inner(&operand) else {
806            return false;
807        };
808        constant_lookup_inputs.insert(0, bound_operand);
809
810        // fallback insertion
811        if let Some(expr) = fallback {
812            constant_lookup_inputs.push(expr);
813        }
814
815        true
816    }
817
818    /// The helper function to check if the current case-when
819    /// expression in `bind_case` could be optimized
820    /// into `ConstantLookupExpression`
821    fn check_bind_case_optimization(
822        &mut self,
823        conditions: &[Expr],
824        results_expr: &[ExprImpl],
825        operand: Option<&Expr>,
826        fallback: Option<ExprImpl>,
827        constant_lookup_inputs: &mut Vec<ExprImpl>,
828    ) -> bool {
829        if conditions.len() < CASE_WHEN_ARMS_OPTIMIZE_LIMIT {
830            return false;
831        }
832
833        if let Some(operand) = operand {
834            let Ok(operand) = self.bind_expr_inner(operand) else {
835                return false;
836            };
837            // This optimization should be done in subsequent optimization phase
838            // if the operand is const
839            // e.g., select case 1 when 1 then 114514 else 1919810 end;
840            if operand.is_const() {
841                return false;
842            }
843            constant_lookup_inputs.push(operand);
844        } else {
845            // Try converting to simple form
846            // see the example as illustrated in `check_convert_simple_form`
847            return self.check_convert_simple_form(
848                conditions,
849                results_expr,
850                fallback,
851                constant_lookup_inputs,
852            );
853        }
854
855        for (condition, result) in zip_eq_fast(conditions, results_expr) {
856            if let Expr::Value(_) = condition {
857                let Ok(input) = self.bind_expr_inner(condition) else {
858                    return false;
859                };
860                constant_lookup_inputs.push(input);
861            } else {
862                // If at least one condition is not in the simple form / not constant,
863                // we can NOT do the subsequent optimization pass
864                return false;
865            }
866
867            constant_lookup_inputs.push(result.clone());
868        }
869
870        // The fallback arm for case-when expression
871        if let Some(expr) = fallback {
872            constant_lookup_inputs.push(expr);
873        }
874
875        true
876    }
877
878    pub(super) fn bind_case(
879        &mut self,
880        operand: Option<&Expr>,
881        conditions: &[Expr],
882        results: &[Expr],
883        else_result: Option<&Expr>,
884    ) -> Result<ExprImpl> {
885        let mut inputs = Vec::new();
886        let results_expr: Vec<ExprImpl> = results
887            .iter()
888            .map(|expr| self.bind_expr_inner(expr))
889            .collect::<Result<_>>()?;
890        let else_result_expr = else_result
891            .map(|expr| self.bind_expr_inner(expr))
892            .transpose()?;
893
894        let mut constant_lookup_inputs = Vec::new();
895        let mut constant_case_when_eval_inputs = Vec::new();
896
897        let constant_case_when_flag = self.check_constant_case_when_optimization(
898            conditions,
899            &results_expr,
900            operand,
901            else_result_expr.as_ref(),
902            &mut constant_case_when_eval_inputs,
903        );
904
905        if constant_case_when_flag {
906            // Sanity check
907            if constant_case_when_eval_inputs.len() != 1 {
908                return Err(ErrorCode::BindError(
909                    "expect `constant_case_when_eval_inputs` only contains a single bound expression".to_owned()
910                )
911                    .into());
912            }
913            // Directly return the first element of the vector
914            return Ok(constant_case_when_eval_inputs[0].take());
915        }
916
917        // See if the case-when expression can be optimized
918        let optimize_flag = self.check_bind_case_optimization(
919            conditions,
920            &results_expr,
921            operand,
922            else_result_expr.clone(),
923            &mut constant_lookup_inputs,
924        );
925
926        if optimize_flag {
927            return Ok(FunctionCall::new(ExprType::ConstantLookup, constant_lookup_inputs)?.into());
928        }
929
930        for (condition, result) in zip_eq_fast(conditions, results_expr) {
931            let condition = condition.clone();
932            let condition = match operand {
933                Some(t) => Expr::BinaryOp {
934                    left: t.clone().into(),
935                    op: BinaryOperator::Eq,
936                    right: Box::new(condition),
937                },
938                None => condition,
939            };
940            inputs.push(
941                self.bind_expr_inner(&condition)
942                    .and_then(|expr| expr.enforce_bool_clause("CASE WHEN"))?,
943            );
944            inputs.push(result);
945        }
946
947        // The fallback arm for case-when expression
948        if let Some(expr) = else_result_expr {
949            inputs.push(expr);
950        }
951
952        if inputs.iter().any(ExprImpl::has_table_function) {
953            return Err(
954                ErrorCode::BindError("table functions are not allowed in CASE".into()).into(),
955            );
956        }
957
958        Ok(FunctionCall::new(ExprType::Case, inputs)?.into())
959    }
960
961    pub(super) fn bind_is_operator(
962        &mut self,
963        func_type: ExprType,
964        expr: &Expr,
965    ) -> Result<ExprImpl> {
966        let expr = self.bind_expr_inner(expr)?;
967        Ok(FunctionCall::new(func_type, vec![expr])?.into())
968    }
969
970    pub(super) fn bind_is_unknown(&mut self, func_type: ExprType, expr: &Expr) -> Result<ExprImpl> {
971        let expr = self
972            .bind_expr_inner(expr)?
973            .cast_implicit(&DataType::Boolean)?;
974        Ok(FunctionCall::new(func_type, vec![expr])?.into())
975    }
976
977    pub(super) fn bind_distinct_from(&mut self, left: &Expr, right: &Expr) -> Result<ExprImpl> {
978        let left = self.bind_expr_inner(left)?;
979        let right = self.bind_expr_inner(right)?;
980        let func_call = FunctionCall::new(ExprType::IsDistinctFrom, vec![left, right]);
981        Ok(func_call?.into())
982    }
983
984    pub(super) fn bind_not_distinct_from(&mut self, left: &Expr, right: &Expr) -> Result<ExprImpl> {
985        let left = self.bind_expr_inner(left)?;
986        let right = self.bind_expr_inner(right)?;
987        let func_call = FunctionCall::new(ExprType::IsNotDistinctFrom, vec![left, right]);
988        Ok(func_call?.into())
989    }
990
991    pub(super) fn bind_cast(&mut self, expr: &Expr, data_type: &AstDataType) -> Result<ExprImpl> {
992        match &data_type {
993            // Casting to Regclass type means getting the oid of expr.
994            // See https://www.postgresql.org/docs/current/datatype-oid.html.
995            AstDataType::Regclass => {
996                let input = self.bind_expr_inner(expr)?;
997                Ok(input.cast_to_regclass()?)
998            }
999            AstDataType::Regproc => {
1000                let lhs = self.bind_expr_inner(expr)?;
1001                let lhs_ty = lhs.return_type();
1002                if lhs_ty == DataType::Varchar {
1003                    // FIXME: Currently, we only allow VARCHAR to be casted to Regproc.
1004                    // FIXME: Check whether it's a valid proc
1005                    // FIXME: The return type should be casted to Regproc, but we don't have this type.
1006                    Ok(lhs)
1007                } else {
1008                    Err(ErrorCode::BindError(format!("Can't cast {} to regproc", lhs_ty)).into())
1009                }
1010            }
1011            // Redirect cast char to varchar to make system like Metabase happy.
1012            // Char is not supported in RisingWave, but some ecosystem tools like Metabase will use it.
1013            // Notice that the behavior of `char` and `varchar` is different in PostgreSQL.
1014            // The following sql result should be different in PostgreSQL:
1015            // ```
1016            // select 'a'::char(2) = 'a '::char(2);
1017            // ----------
1018            // t
1019            //
1020            // select 'a'::varchar = 'a '::varchar;
1021            // ----------
1022            // f
1023            // ```
1024            AstDataType::Char(_) => self.bind_cast_inner(expr, &DataType::Varchar),
1025            _ => self.bind_cast_inner(expr, &bind_data_type(data_type)?),
1026        }
1027    }
1028
1029    pub fn bind_cast_inner(&mut self, expr: &Expr, data_type: &DataType) -> Result<ExprImpl> {
1030        match (expr, data_type) {
1031            (Expr::Array(Array { elem: expr, .. }), DataType::List(list_type)) => {
1032                self.bind_array_cast(expr, list_type.elem())
1033            }
1034            (Expr::Map { entries }, DataType::Map(m)) => self.bind_map_cast(entries, m),
1035            (expr, data_type) => {
1036                let lhs = self.bind_expr_inner(expr)?;
1037                lhs.cast_explicit(data_type).map_err(Into::into)
1038            }
1039        }
1040    }
1041
1042    pub fn bind_collate(&mut self, expr: &Expr, collation: &ObjectName) -> Result<ExprImpl> {
1043        if !["C", "POSIX"].contains(&collation.real_value().as_str()) {
1044            bail_not_implemented!("Collate collation other than `C` or `POSIX` is not implemented");
1045        }
1046
1047        let bound_inner = self.bind_expr_inner(expr)?;
1048        let ret_type = bound_inner.return_type();
1049
1050        match ret_type {
1051            DataType::Varchar => {}
1052            _ => {
1053                return Err(ErrorCode::NotSupported(
1054                    format!("{} is not a collatable data type", ret_type),
1055                    "The only built-in collatable data types are `varchar`, please check your type"
1056                        .into(),
1057                )
1058                .into());
1059            }
1060        }
1061
1062        Ok(bound_inner)
1063    }
1064}
1065
1066pub fn bind_data_type(data_type: &AstDataType) -> Result<DataType> {
1067    let new_err = || not_implemented!("unsupported data type: {:}", data_type);
1068    let data_type = match data_type {
1069        AstDataType::Boolean => DataType::Boolean,
1070        AstDataType::SmallInt => DataType::Int16,
1071        AstDataType::Int => DataType::Int32,
1072        AstDataType::BigInt => DataType::Int64,
1073        AstDataType::Real | AstDataType::Float(Some(1..=24)) => DataType::Float32,
1074        AstDataType::Double | AstDataType::Float(Some(25..=53) | None) => DataType::Float64,
1075        AstDataType::Float(Some(0 | 54..)) => unreachable!(),
1076        AstDataType::Decimal(None, None) => DataType::Decimal,
1077        AstDataType::Varchar | AstDataType::Text => DataType::Varchar,
1078        AstDataType::Date => DataType::Date,
1079        AstDataType::Time(false) => DataType::Time,
1080        AstDataType::Timestamp(false) => DataType::Timestamp,
1081        AstDataType::Timestamp(true) => DataType::Timestamptz,
1082        AstDataType::Interval => DataType::Interval,
1083        AstDataType::Array(datatype) => DataType::list(bind_data_type(datatype)?),
1084        AstDataType::Char(..) => {
1085            bail_not_implemented!("CHAR is not supported, please use VARCHAR instead")
1086        }
1087        AstDataType::Struct(types) => StructType::new(
1088            types
1089                .iter()
1090                .map(|f| Ok((f.name.real_value(), bind_data_type(&f.data_type)?)))
1091                .collect::<Result<Vec<_>>>()?,
1092        )
1093        .into(),
1094        AstDataType::Map(kv) => {
1095            let key = bind_data_type(&kv.0)?;
1096            let value = bind_data_type(&kv.1)?;
1097            DataType::Map(MapType::try_from_kv(key, value).map_err(ErrorCode::BindError)?)
1098        }
1099        AstDataType::Custom(qualified_type_name) => {
1100            let idents = qualified_type_name
1101                .0
1102                .iter()
1103                .map(|n| n.real_value())
1104                .collect_vec();
1105            let name = if idents.len() == 1 {
1106                idents[0].as_str() // `int2`
1107            } else if idents.len() == 2 && idents[0] == PG_CATALOG_SCHEMA_NAME {
1108                idents[1].as_str() // `pg_catalog.text`
1109            } else {
1110                return Err(new_err().into());
1111            };
1112
1113            // In PostgreSQL, these are non-keywords or non-reserved keywords but pre-defined
1114            // names that could be extended by `CREATE TYPE`.
1115            match name {
1116                "int2" => DataType::Int16,
1117                "int4" => DataType::Int32,
1118                "int8" => DataType::Int64,
1119                "rw_int256" => DataType::Int256,
1120                "float4" => DataType::Float32,
1121                "float8" => DataType::Float64,
1122                "timestamptz" => DataType::Timestamptz,
1123                "text" => DataType::Varchar,
1124                "serial" => {
1125                    return Err(ErrorCode::NotSupported(
1126                        "Column type SERIAL is not supported".into(),
1127                        "Please remove the SERIAL column".into(),
1128                    )
1129                    .into());
1130                }
1131                _ => return Err(new_err().into()),
1132            }
1133        }
1134        AstDataType::Bytea => DataType::Bytea,
1135        AstDataType::Jsonb => DataType::Jsonb,
1136        AstDataType::Variant => DataType::Variant,
1137        AstDataType::Vector(size) => match (1..=DataType::VEC_MAX_SIZE).contains(&(*size as _)) {
1138            true => DataType::Vector(*size as _),
1139            false => {
1140                return Err(ErrorCode::BindError(format!(
1141                    "vector size {} is out of range [1, {}]",
1142                    size,
1143                    DataType::VEC_MAX_SIZE
1144                ))
1145                .into());
1146            }
1147        },
1148        AstDataType::Regclass
1149        | AstDataType::Regproc
1150        | AstDataType::Uuid
1151        | AstDataType::Decimal(_, _)
1152        | AstDataType::Time(true) => return Err(new_err().into()),
1153    };
1154    Ok(data_type)
1155}