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    InputRefScopeRewriter, 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 (level, 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 are bound in the calling context. From a subquery, its
480                    // columns are correlated references owned by that context.
481                    if level == 0 {
482                        return Ok(expr.clone());
483                    }
484                    let mut rewriter = InputRefScopeRewriter::new(context.scope);
485                    return Ok(rewriter.rewrite_expr(expr.clone()));
486                } else {
487                    // A UDF cannot access parameters from outer UDFs. Do not continue but directly
488                    // return an error.
489                    break;
490                }
491            }
492        }
493
494        Err(ErrorCode::BindError(format!(
495            "failed to find {} parameter {name}",
496            if name.starts_with('$') {
497                "unnamed"
498            } else {
499                "named"
500            }
501        ))
502        .into())
503    }
504
505    fn bind_parameter(&mut self, index: u64) -> Result<ExprImpl> {
506        // Special check for sql udf
507        // Note: This is specific to sql udf with unnamed parameters, since the
508        // parameters will be parsed and treated as `Parameter`.
509        // For detailed explanation, consider checking `bind_column`.
510        if self.is_binding_inline_sql_udf() || self.is_binding_subquery_sql_udf() {
511            let column_name = format!("${index}");
512            return self.bind_sql_udf_parameter(&column_name);
513        }
514
515        Ok(Parameter::new(index, self.param_types.clone()).into())
516    }
517
518    /// Bind `expr (not) between low and high`
519    pub(super) fn bind_between(
520        &mut self,
521        expr: &Expr,
522        negated: bool,
523        low: &Expr,
524        high: &Expr,
525    ) -> Result<ExprImpl> {
526        let expr = self.bind_expr_inner(expr)?;
527        let low = self.bind_expr_inner(low)?;
528        let high = self.bind_expr_inner(high)?;
529
530        let func_call = if negated {
531            // negated = true: expr < low or expr > high
532            FunctionCall::new_unchecked(
533                ExprType::Or,
534                vec![
535                    FunctionCall::new(ExprType::LessThan, vec![expr.clone(), low])?.into(),
536                    FunctionCall::new(ExprType::GreaterThan, vec![expr, high])?.into(),
537                ],
538                DataType::Boolean,
539            )
540        } else {
541            // negated = false: expr >= low and expr <= high
542            FunctionCall::new_unchecked(
543                ExprType::And,
544                vec![
545                    FunctionCall::new(ExprType::GreaterThanOrEqual, vec![expr.clone(), low])?
546                        .into(),
547                    FunctionCall::new(ExprType::LessThanOrEqual, vec![expr, high])?.into(),
548                ],
549                DataType::Boolean,
550            )
551        };
552
553        Ok(func_call.into())
554    }
555
556    fn bind_like(
557        &mut self,
558        expr_type: ExprType,
559        expr: &Expr,
560        negated: bool,
561        pattern: &Expr,
562        escape_char: Option<EscapeChar>,
563    ) -> Result<ExprImpl> {
564        if matches!(pattern, Expr::AllOp(_) | Expr::SomeOp(_)) {
565            if escape_char.is_some() {
566                // PostgreSQL also don't support the pattern due to the complexity of implementation.
567                // The SQL will failed on PostgreSQL 16.1:
568                // ```sql
569                // select 'a' like any(array[null]) escape '';
570                // ```
571                bail_not_implemented!(
572                    "LIKE with both ALL|ANY pattern and escape character is not supported"
573                )
574            }
575            // Use the `bind_binary_op` path to handle the ALL|ANY pattern.
576            let op = match (expr_type, negated) {
577                (ExprType::Like, false) => BinaryOperator::Custom("~~".to_owned()),
578                (ExprType::Like, true) => BinaryOperator::Custom("!~~".to_owned()),
579                (ExprType::ILike, false) => BinaryOperator::Custom("~~*".to_owned()),
580                (ExprType::ILike, true) => BinaryOperator::Custom("!~~*".to_owned()),
581                _ => unreachable!(),
582            };
583            return self.bind_binary_op(expr, &op, pattern);
584        }
585        let expr = self.bind_expr_inner(expr)?;
586        let pattern = self.bind_expr_inner(pattern)?;
587        match (expr.return_type(), pattern.return_type()) {
588            (DataType::Varchar, DataType::Varchar) => {}
589            (string_ty, pattern_ty) => match expr_type {
590                ExprType::Like => bail_no_function!("like({}, {})", string_ty, pattern_ty),
591                ExprType::ILike => bail_no_function!("ilike({}, {})", string_ty, pattern_ty),
592                _ => unreachable!(),
593            },
594        }
595        let args = match escape_char {
596            Some(escape_char) => {
597                let escape_char = ExprImpl::literal_varchar(escape_char.to_string());
598                vec![expr, pattern, escape_char]
599            }
600            None => vec![expr, pattern],
601        };
602        let func_call = FunctionCall::new_unchecked(expr_type, args, DataType::Boolean);
603        let func_call = if negated {
604            FunctionCall::new_unchecked(ExprType::Not, vec![func_call.into()], DataType::Boolean)
605        } else {
606            func_call
607        };
608        Ok(func_call.into())
609    }
610
611    /// Bind `<expr> [ NOT ] SIMILAR TO <pat> ESCAPE <esc_text>`
612    pub(super) fn bind_similar_to(
613        &mut self,
614        expr: &Expr,
615        negated: bool,
616        pattern: &Expr,
617        escape_char: Option<EscapeChar>,
618    ) -> Result<ExprImpl> {
619        let expr = self.bind_expr_inner(expr)?;
620        let pattern = self.bind_expr_inner(pattern)?;
621
622        let esc_inputs = if let Some(escape_char) = escape_char {
623            let escape_char = ExprImpl::literal_varchar(escape_char.to_string());
624            vec![pattern, escape_char]
625        } else {
626            vec![pattern]
627        };
628
629        let esc_call =
630            FunctionCall::new_unchecked(ExprType::SimilarToEscape, esc_inputs, DataType::Varchar);
631
632        let regex_call = FunctionCall::new_unchecked(
633            ExprType::RegexpEq,
634            vec![expr, esc_call.into()],
635            DataType::Boolean,
636        );
637        let func_call = if negated {
638            FunctionCall::new_unchecked(ExprType::Not, vec![regex_call.into()], DataType::Boolean)
639        } else {
640            regex_call
641        };
642
643        Ok(func_call.into())
644    }
645
646    /// The optimization check for the following case-when expression pattern
647    /// e.g., select case 1 when (...) then (...) else (...) end;
648    fn check_constant_case_when_optimization(
649        &mut self,
650        conditions: &[Expr],
651        results_expr: &[ExprImpl],
652        operand: Option<&Expr>,
653        fallback: Option<&ExprImpl>,
654        constant_case_when_eval_inputs: &mut Vec<ExprImpl>,
655    ) -> bool {
656        // The operand value to be compared later
657        let operand_value;
658
659        if let Some(operand) = operand {
660            let Ok(operand) = self.bind_expr_inner(operand) else {
661                return false;
662            };
663            if !operand.is_const() {
664                return false;
665            }
666            operand_value = operand;
667        } else {
668            return false;
669        }
670
671        for (condition, result) in zip_eq_fast(conditions, results_expr) {
672            if let Expr::Value(_) = condition.clone() {
673                let Ok(res) = self.bind_expr_inner(condition) else {
674                    return false;
675                };
676                // Found a match
677                if res == operand_value {
678                    constant_case_when_eval_inputs.push(result.clone());
679                    return true;
680                }
681            } else {
682                return false;
683            }
684        }
685
686        // Otherwise this will eventually go through fallback arm
687        debug_assert!(
688            constant_case_when_eval_inputs.is_empty(),
689            "expect `inputs` to be empty"
690        );
691
692        let Some(fallback) = fallback else {
693            return false;
694        };
695
696        constant_case_when_eval_inputs.push(fallback.clone());
697        true
698    }
699
700    /// Helper function to compare or set column identifier
701    /// used in `check_convert_simple_form`
702    fn compare_or_set(col_expr: &mut Option<Expr>, test_expr: &Expr) -> bool {
703        let Expr::Identifier(test_ident) = test_expr else {
704            return false;
705        };
706        if let Some(expr) = col_expr {
707            let Expr::Identifier(ident) = expr else {
708                return false;
709            };
710            if ident.real_value() != test_ident.real_value() {
711                return false;
712            }
713        } else {
714            *col_expr = Some(Expr::Identifier(test_ident.clone()));
715        }
716        true
717    }
718
719    /// left expression and right expression must be either:
720    /// `<constant> <Eq> <identifier>` or `<identifier> <Eq> <constant>`
721    /// used in `check_convert_simple_form`
722    fn check_invariant(left: &Expr, op: &BinaryOperator, right: &Expr) -> bool {
723        if op != &BinaryOperator::Eq {
724            return false;
725        }
726        if let Expr::Identifier(_) = left {
727            // <identifier> <Eq> <constant>
728            let Expr::Value(_) = right else {
729                return false;
730            };
731        } else {
732            // <constant> <Eq> <identifier>
733            let Expr::Value(_) = left else {
734                return false;
735            };
736            let Expr::Identifier(_) = right else {
737                return false;
738            };
739        }
740        true
741    }
742
743    /// Helper function to extract expression out and insert
744    /// the corresponding bound version to `inputs`
745    /// used in `check_convert_simple_form`
746    /// Note: this function will be invoked per arm
747    fn try_extract_simple_form(
748        &mut self,
749        ident_expr: &Expr,
750        constant_expr: &Expr,
751        column_expr: &mut Option<Expr>,
752        inputs: &mut Vec<ExprImpl>,
753    ) -> bool {
754        if !Self::compare_or_set(column_expr, ident_expr) {
755            return false;
756        }
757        let Ok(bound_expr) = self.bind_expr_inner(constant_expr) else {
758            return false;
759        };
760        inputs.push(bound_expr);
761        true
762    }
763
764    /// See if the case when expression in form
765    /// `select case when <expr_1 = constant> (...with same pattern...) else <constant> end;`
766    /// If so, this expression could also be converted to constant lookup
767    fn check_convert_simple_form(
768        &mut self,
769        conditions: &[Expr],
770        results_expr: &[ExprImpl],
771        fallback: Option<ExprImpl>,
772        constant_lookup_inputs: &mut Vec<ExprImpl>,
773    ) -> bool {
774        let mut column_expr = None;
775
776        for (condition, result) in zip_eq_fast(conditions, results_expr) {
777            if let Expr::BinaryOp { left, op, right } = condition {
778                if !Self::check_invariant(left, op, right) {
779                    return false;
780                }
781                if let Expr::Identifier(_) = &**left {
782                    if !self.try_extract_simple_form(
783                        left,
784                        right,
785                        &mut column_expr,
786                        constant_lookup_inputs,
787                    ) {
788                        return false;
789                    }
790                } else if !self.try_extract_simple_form(
791                    right,
792                    left,
793                    &mut column_expr,
794                    constant_lookup_inputs,
795                ) {
796                    return false;
797                }
798                constant_lookup_inputs.push(result.clone());
799            } else {
800                return false;
801            }
802        }
803
804        // Insert operand first
805        let Some(operand) = column_expr else {
806            return false;
807        };
808        let Ok(bound_operand) = self.bind_expr_inner(&operand) else {
809            return false;
810        };
811        constant_lookup_inputs.insert(0, bound_operand);
812
813        // fallback insertion
814        if let Some(expr) = fallback {
815            constant_lookup_inputs.push(expr);
816        }
817
818        true
819    }
820
821    /// The helper function to check if the current case-when
822    /// expression in `bind_case` could be optimized
823    /// into `ConstantLookupExpression`
824    fn check_bind_case_optimization(
825        &mut self,
826        conditions: &[Expr],
827        results_expr: &[ExprImpl],
828        operand: Option<&Expr>,
829        fallback: Option<ExprImpl>,
830        constant_lookup_inputs: &mut Vec<ExprImpl>,
831    ) -> bool {
832        if conditions.len() < CASE_WHEN_ARMS_OPTIMIZE_LIMIT {
833            return false;
834        }
835
836        if let Some(operand) = operand {
837            let Ok(operand) = self.bind_expr_inner(operand) else {
838                return false;
839            };
840            // This optimization should be done in subsequent optimization phase
841            // if the operand is const
842            // e.g., select case 1 when 1 then 114514 else 1919810 end;
843            if operand.is_const() {
844                return false;
845            }
846            constant_lookup_inputs.push(operand);
847        } else {
848            // Try converting to simple form
849            // see the example as illustrated in `check_convert_simple_form`
850            return self.check_convert_simple_form(
851                conditions,
852                results_expr,
853                fallback,
854                constant_lookup_inputs,
855            );
856        }
857
858        for (condition, result) in zip_eq_fast(conditions, results_expr) {
859            if let Expr::Value(_) = condition {
860                let Ok(input) = self.bind_expr_inner(condition) else {
861                    return false;
862                };
863                constant_lookup_inputs.push(input);
864            } else {
865                // If at least one condition is not in the simple form / not constant,
866                // we can NOT do the subsequent optimization pass
867                return false;
868            }
869
870            constant_lookup_inputs.push(result.clone());
871        }
872
873        // The fallback arm for case-when expression
874        if let Some(expr) = fallback {
875            constant_lookup_inputs.push(expr);
876        }
877
878        true
879    }
880
881    pub(super) fn bind_case(
882        &mut self,
883        operand: Option<&Expr>,
884        conditions: &[Expr],
885        results: &[Expr],
886        else_result: Option<&Expr>,
887    ) -> Result<ExprImpl> {
888        let mut inputs = Vec::new();
889        let results_expr: Vec<ExprImpl> = results
890            .iter()
891            .map(|expr| self.bind_expr_inner(expr))
892            .collect::<Result<_>>()?;
893        let else_result_expr = else_result
894            .map(|expr| self.bind_expr_inner(expr))
895            .transpose()?;
896
897        let mut constant_lookup_inputs = Vec::new();
898        let mut constant_case_when_eval_inputs = Vec::new();
899
900        let constant_case_when_flag = self.check_constant_case_when_optimization(
901            conditions,
902            &results_expr,
903            operand,
904            else_result_expr.as_ref(),
905            &mut constant_case_when_eval_inputs,
906        );
907
908        if constant_case_when_flag {
909            // Sanity check
910            if constant_case_when_eval_inputs.len() != 1 {
911                return Err(ErrorCode::BindError(
912                    "expect `constant_case_when_eval_inputs` only contains a single bound expression".to_owned()
913                )
914                    .into());
915            }
916            // Directly return the first element of the vector
917            return Ok(constant_case_when_eval_inputs[0].take());
918        }
919
920        // See if the case-when expression can be optimized
921        let optimize_flag = self.check_bind_case_optimization(
922            conditions,
923            &results_expr,
924            operand,
925            else_result_expr.clone(),
926            &mut constant_lookup_inputs,
927        );
928
929        if optimize_flag {
930            return Ok(FunctionCall::new(ExprType::ConstantLookup, constant_lookup_inputs)?.into());
931        }
932
933        for (condition, result) in zip_eq_fast(conditions, results_expr) {
934            let condition = condition.clone();
935            let condition = match operand {
936                Some(t) => Expr::BinaryOp {
937                    left: t.clone().into(),
938                    op: BinaryOperator::Eq,
939                    right: Box::new(condition),
940                },
941                None => condition,
942            };
943            inputs.push(
944                self.bind_expr_inner(&condition)
945                    .and_then(|expr| expr.enforce_bool_clause("CASE WHEN"))?,
946            );
947            inputs.push(result);
948        }
949
950        // The fallback arm for case-when expression
951        if let Some(expr) = else_result_expr {
952            inputs.push(expr);
953        }
954
955        if inputs.iter().any(ExprImpl::has_table_function) {
956            return Err(
957                ErrorCode::BindError("table functions are not allowed in CASE".into()).into(),
958            );
959        }
960
961        Ok(FunctionCall::new(ExprType::Case, inputs)?.into())
962    }
963
964    pub(super) fn bind_is_operator(
965        &mut self,
966        func_type: ExprType,
967        expr: &Expr,
968    ) -> Result<ExprImpl> {
969        let expr = self.bind_expr_inner(expr)?;
970        Ok(FunctionCall::new(func_type, vec![expr])?.into())
971    }
972
973    pub(super) fn bind_is_unknown(&mut self, func_type: ExprType, expr: &Expr) -> Result<ExprImpl> {
974        let expr = self
975            .bind_expr_inner(expr)?
976            .cast_implicit(&DataType::Boolean)?;
977        Ok(FunctionCall::new(func_type, vec![expr])?.into())
978    }
979
980    pub(super) fn bind_distinct_from(&mut self, left: &Expr, right: &Expr) -> Result<ExprImpl> {
981        let left = self.bind_expr_inner(left)?;
982        let right = self.bind_expr_inner(right)?;
983        let func_call = FunctionCall::new(ExprType::IsDistinctFrom, vec![left, right]);
984        Ok(func_call?.into())
985    }
986
987    pub(super) fn bind_not_distinct_from(&mut self, left: &Expr, right: &Expr) -> Result<ExprImpl> {
988        let left = self.bind_expr_inner(left)?;
989        let right = self.bind_expr_inner(right)?;
990        let func_call = FunctionCall::new(ExprType::IsNotDistinctFrom, vec![left, right]);
991        Ok(func_call?.into())
992    }
993
994    pub(super) fn bind_cast(&mut self, expr: &Expr, data_type: &AstDataType) -> Result<ExprImpl> {
995        match &data_type {
996            // Casting to Regclass type means getting the oid of expr.
997            // See https://www.postgresql.org/docs/current/datatype-oid.html.
998            AstDataType::Regclass => {
999                let input = self.bind_expr_inner(expr)?;
1000                Ok(input.cast_to_regclass()?)
1001            }
1002            AstDataType::Regproc => {
1003                let lhs = self.bind_expr_inner(expr)?;
1004                let lhs_ty = lhs.return_type();
1005                if lhs_ty == DataType::Varchar {
1006                    // FIXME: Currently, we only allow VARCHAR to be casted to Regproc.
1007                    // FIXME: Check whether it's a valid proc
1008                    // FIXME: The return type should be casted to Regproc, but we don't have this type.
1009                    Ok(lhs)
1010                } else {
1011                    Err(ErrorCode::BindError(format!("Can't cast {} to regproc", lhs_ty)).into())
1012                }
1013            }
1014            // Redirect cast char to varchar to make system like Metabase happy.
1015            // Char is not supported in RisingWave, but some ecosystem tools like Metabase will use it.
1016            // Notice that the behavior of `char` and `varchar` is different in PostgreSQL.
1017            // The following sql result should be different in PostgreSQL:
1018            // ```
1019            // select 'a'::char(2) = 'a '::char(2);
1020            // ----------
1021            // t
1022            //
1023            // select 'a'::varchar = 'a '::varchar;
1024            // ----------
1025            // f
1026            // ```
1027            AstDataType::Char(_) => self.bind_cast_inner(expr, &DataType::Varchar),
1028            _ => self.bind_cast_inner(expr, &bind_data_type(data_type)?),
1029        }
1030    }
1031
1032    pub fn bind_cast_inner(&mut self, expr: &Expr, data_type: &DataType) -> Result<ExprImpl> {
1033        match (expr, data_type) {
1034            (Expr::Array(Array { elem: expr, .. }), DataType::List(list_type)) => {
1035                self.bind_array_cast(expr, list_type.elem())
1036            }
1037            (Expr::Map { entries }, DataType::Map(m)) => self.bind_map_cast(entries, m),
1038            (expr, data_type) => {
1039                let lhs = self.bind_expr_inner(expr)?;
1040                lhs.cast_explicit(data_type).map_err(Into::into)
1041            }
1042        }
1043    }
1044
1045    pub fn bind_collate(&mut self, expr: &Expr, collation: &ObjectName) -> Result<ExprImpl> {
1046        if !["C", "POSIX"].contains(&collation.real_value().as_str()) {
1047            bail_not_implemented!("Collate collation other than `C` or `POSIX` is not implemented");
1048        }
1049
1050        let bound_inner = self.bind_expr_inner(expr)?;
1051        let ret_type = bound_inner.return_type();
1052
1053        match ret_type {
1054            DataType::Varchar => {}
1055            _ => {
1056                return Err(ErrorCode::NotSupported(
1057                    format!("{} is not a collatable data type", ret_type),
1058                    "The only built-in collatable data types are `varchar`, please check your type"
1059                        .into(),
1060                )
1061                .into());
1062            }
1063        }
1064
1065        Ok(bound_inner)
1066    }
1067}
1068
1069pub fn bind_data_type(data_type: &AstDataType) -> Result<DataType> {
1070    let new_err = || not_implemented!("unsupported data type: {:}", data_type);
1071    let data_type = match data_type {
1072        AstDataType::Boolean => DataType::Boolean,
1073        AstDataType::SmallInt => DataType::Int16,
1074        AstDataType::Int => DataType::Int32,
1075        AstDataType::BigInt => DataType::Int64,
1076        AstDataType::Real | AstDataType::Float(Some(1..=24)) => DataType::Float32,
1077        AstDataType::Double | AstDataType::Float(Some(25..=53) | None) => DataType::Float64,
1078        AstDataType::Float(Some(0 | 54..)) => unreachable!(),
1079        AstDataType::Decimal(None, None) => DataType::Decimal,
1080        AstDataType::Varchar | AstDataType::Text => DataType::Varchar,
1081        AstDataType::Date => DataType::Date,
1082        AstDataType::Time(false) => DataType::Time,
1083        AstDataType::Timestamp(false) => DataType::Timestamp,
1084        AstDataType::Timestamp(true) => DataType::Timestamptz,
1085        AstDataType::Interval => DataType::Interval,
1086        AstDataType::Array(datatype) => DataType::list(bind_data_type(datatype)?),
1087        AstDataType::Char(..) => {
1088            bail_not_implemented!("CHAR is not supported, please use VARCHAR instead")
1089        }
1090        AstDataType::Struct(types) => bind_struct_type(
1091            types
1092                .iter()
1093                .map(|f| Ok((f.name.real_value(), bind_data_type(&f.data_type)?)))
1094                .collect::<Result<Vec<_>>>()?,
1095        )?
1096        .into(),
1097        AstDataType::Map(kv) => {
1098            let key = bind_data_type(&kv.0)?;
1099            let value = bind_data_type(&kv.1)?;
1100            DataType::Map(MapType::try_from_kv(key, value).map_err(ErrorCode::BindError)?)
1101        }
1102        AstDataType::Custom(qualified_type_name) => {
1103            let idents = qualified_type_name
1104                .0
1105                .iter()
1106                .map(|n| n.real_value())
1107                .collect_vec();
1108            let name = if idents.len() == 1 {
1109                idents[0].as_str() // `int2`
1110            } else if idents.len() == 2 && idents[0] == PG_CATALOG_SCHEMA_NAME {
1111                idents[1].as_str() // `pg_catalog.text`
1112            } else {
1113                return Err(new_err().into());
1114            };
1115
1116            // In PostgreSQL, these are non-keywords or non-reserved keywords but pre-defined
1117            // names that could be extended by `CREATE TYPE`.
1118            match name {
1119                "int2" => DataType::Int16,
1120                "int4" => DataType::Int32,
1121                "int8" => DataType::Int64,
1122                "rw_int256" => DataType::Int256,
1123                "float4" => DataType::Float32,
1124                "float8" => DataType::Float64,
1125                "timestamptz" => DataType::Timestamptz,
1126                "text" => DataType::Varchar,
1127                "serial" => {
1128                    return Err(ErrorCode::NotSupported(
1129                        "Column type SERIAL is not supported".into(),
1130                        "Please remove the SERIAL column".into(),
1131                    )
1132                    .into());
1133                }
1134                _ => return Err(new_err().into()),
1135            }
1136        }
1137        AstDataType::Bytea => DataType::Bytea,
1138        AstDataType::Jsonb => DataType::Jsonb,
1139        AstDataType::Variant => DataType::Variant,
1140        AstDataType::Vector(size) => match (1..=DataType::VEC_MAX_SIZE).contains(&(*size as _)) {
1141            true => DataType::Vector(*size as _),
1142            false => {
1143                return Err(ErrorCode::BindError(format!(
1144                    "vector size {} is out of range [1, {}]",
1145                    size,
1146                    DataType::VEC_MAX_SIZE
1147                ))
1148                .into());
1149            }
1150        },
1151        AstDataType::Regclass
1152        | AstDataType::Regproc
1153        | AstDataType::Uuid
1154        | AstDataType::Decimal(_, _)
1155        | AstDataType::Time(true) => return Err(new_err().into()),
1156    };
1157    Ok(data_type)
1158}
1159
1160pub(crate) fn bind_struct_type(
1161    fields: impl IntoIterator<Item = (impl Into<String>, DataType)>,
1162) -> Result<StructType> {
1163    let struct_type = StructType::new(fields);
1164    if let Some(name) = struct_type.find_duplicate_field_name() {
1165        return Err(ErrorCode::InvalidInputSyntax(format!(
1166            "struct field \"{name}\" specified more than once"
1167        ))
1168        .into());
1169    }
1170    Ok(struct_type)
1171}
1172
1173#[cfg(test)]
1174mod tests {
1175    use risingwave_sqlparser::ast::{Ident, StructField};
1176
1177    use super::*;
1178
1179    fn field(name: Ident, data_type: AstDataType) -> StructField {
1180        StructField { name, data_type }
1181    }
1182
1183    fn unquoted(name: &str, data_type: AstDataType) -> StructField {
1184        field(Ident::new_unchecked(name), data_type)
1185    }
1186
1187    fn quoted(name: &str, data_type: AstDataType) -> StructField {
1188        field(Ident::with_quote_unchecked('"', name), data_type)
1189    }
1190
1191    fn assert_duplicate_field_rejected(data_type: AstDataType) {
1192        assert!(bind_data_type(&data_type).is_err());
1193    }
1194
1195    #[test]
1196    fn bind_struct_rejects_duplicate_field_names() {
1197        assert_duplicate_field_rejected(AstDataType::Struct(vec![
1198            unquoted("a", AstDataType::Int),
1199            unquoted("a", AstDataType::Varchar),
1200        ]));
1201
1202        // Unquoted identifiers are folded to lowercase before comparison.
1203        assert_duplicate_field_rejected(AstDataType::Struct(vec![
1204            unquoted("Value", AstDataType::Int),
1205            unquoted("VALUE", AstDataType::BigInt),
1206        ]));
1207
1208        // Quoted identifiers preserve case, but exact quoted duplicates are still invalid.
1209        assert_duplicate_field_rejected(AstDataType::Struct(vec![
1210            quoted("Value", AstDataType::Int),
1211            quoted("Value", AstDataType::BigInt),
1212        ]));
1213    }
1214
1215    #[test]
1216    fn bind_struct_rejects_duplicates_in_nested_types() {
1217        let duplicate_struct = || {
1218            AstDataType::Struct(vec![
1219                unquoted("a", AstDataType::Int),
1220                unquoted("a", AstDataType::Varchar),
1221            ])
1222        };
1223
1224        assert_duplicate_field_rejected(AstDataType::Struct(vec![unquoted(
1225            "nested",
1226            duplicate_struct(),
1227        )]));
1228        assert_duplicate_field_rejected(AstDataType::Array(Box::new(duplicate_struct())));
1229        assert_duplicate_field_rejected(AstDataType::Map(Box::new((
1230            AstDataType::Varchar,
1231            duplicate_struct(),
1232        ))));
1233    }
1234
1235    #[test]
1236    fn bind_struct_accepts_distinct_quoted_field_names() {
1237        let data_type = bind_data_type(&AstDataType::Struct(vec![
1238            unquoted("Value", AstDataType::Int),
1239            quoted("Value", AstDataType::Varchar),
1240            quoted("VALUE", AstDataType::Boolean),
1241        ]))
1242        .unwrap();
1243
1244        let struct_type = data_type.as_struct();
1245        assert_eq!(
1246            struct_type.names().collect_vec(),
1247            ["value", "Value", "VALUE"]
1248        );
1249        assert_eq!(
1250            struct_type.types().cloned().collect_vec(),
1251            [DataType::Int32, DataType::Varchar, DataType::Boolean]
1252        );
1253    }
1254
1255    #[test]
1256    fn bind_generated_struct_rejects_duplicate_field_names() {
1257        assert!(bind_struct_type([("a", DataType::Int32), ("a", DataType::Varchar)]).is_err());
1258        assert!(bind_struct_type([("a", DataType::Int32), ("A", DataType::Varchar)]).is_ok());
1259    }
1260}