Skip to main content

risingwave_frontend/binder/expr/function/
mod.rs

1// Copyright 2024 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::collections::{HashMap, HashSet};
16use std::str::FromStr;
17use std::sync::Arc;
18
19use anyhow::Context;
20use itertools::Itertools;
21use risingwave_common::acl::AclMode;
22use risingwave_common::bail_not_implemented;
23use risingwave_common::catalog::INFORMATION_SCHEMA_SCHEMA_NAME;
24use risingwave_common::secret::LocalSecretManager;
25use risingwave_common::types::{DataType, MapType, StructType};
26use risingwave_common::util::iter_util::ZipEqFast;
27use risingwave_expr::aggregate::AggType;
28use risingwave_expr::window_function::WindowFuncKind;
29use risingwave_sqlparser::ast::{
30    self, Expr as AstExpr, Function, FunctionArg, FunctionArgExpr, FunctionArgList, Ident,
31    OrderByExpr, SecretRefAsType, Statement, Window,
32};
33use risingwave_sqlparser::parser::Parser;
34
35use crate::binder::Binder;
36use crate::binder::bind_context::Clause;
37use crate::catalog::OwnedByUserCatalog;
38use crate::catalog::function_catalog::FunctionCatalog;
39use crate::error::{ErrorCode, Result, RwError};
40use crate::expr::{
41    Expr, ExprImpl, ExprType, FunctionCall, FunctionCallWithLambda, InputRef, TableFunction,
42    TableFunctionType, UserDefinedFunction, expr_impl_to_string_fn,
43};
44use crate::handler::privilege::ObjectCheckItem;
45
46mod aggregate;
47mod builtin_scalar;
48mod window;
49
50// Defines system functions that without args, ref: https://www.postgresql.org/docs/current/functions-info.html
51const SYS_FUNCTION_WITHOUT_ARGS: &[&str] = &[
52    "session_user",
53    "user",
54    "current_user",
55    "current_role",
56    "current_catalog",
57    "current_schema",
58    "current_timestamp",
59];
60
61const INLINE_QUERY_ARG_LEN: usize = 6;
62const CDC_SOURCE_QUERY_ARG_LEN: usize = 2;
63
64pub(super) fn is_sys_function_without_args(ident: &Ident) -> bool {
65    SYS_FUNCTION_WITHOUT_ARGS
66        .iter()
67        .any(|e| ident.real_value().as_str() == *e && ident.quote_style().is_none())
68}
69
70macro_rules! reject_syntax {
71    ($pred:expr, $msg:expr) => {
72        if $pred {
73            return Err(ErrorCode::InvalidInputSyntax($msg.to_string()).into());
74        }
75    };
76
77    ($pred:expr, $fmt:expr, $($arg:tt)*) => {
78        if $pred {
79            return Err(ErrorCode::InvalidInputSyntax(
80                format!($fmt, $($arg)*)
81            ).into());
82        }
83    };
84}
85
86impl Binder {
87    fn bind_postgres_or_mysql_query_args(
88        &self,
89        schema_name: Option<&str>,
90        args: Vec<ExprImpl>,
91        expected_connector_name: &str,
92    ) -> Result<Vec<ExprImpl>> {
93        match args.len() {
94            INLINE_QUERY_ARG_LEN => {
95                let mut cast_args = Vec::with_capacity(INLINE_QUERY_ARG_LEN);
96                for arg in args {
97                    cast_args.push(arg.cast_implicit(&DataType::Varchar)?);
98                }
99                Ok(cast_args)
100            }
101            CDC_SOURCE_QUERY_ARG_LEN => {
102                let source_name = expr_impl_to_string_fn(&args[0])?;
103                let source_catalog = self
104                    .catalog
105                    .get_source_by_name(
106                        &self.db_name,
107                        self.bind_schema_path(schema_name),
108                        &source_name,
109                    )?
110                    .0;
111
112                self.check_privilege(
113                    ObjectCheckItem::new(
114                        source_catalog.owner,
115                        AclMode::Select,
116                        source_catalog.name.clone(),
117                        source_catalog.id,
118                    ),
119                    source_catalog.database_id,
120                )?;
121
122                if !source_catalog
123                    .connector_name()
124                    .eq_ignore_ascii_case(expected_connector_name)
125                {
126                    return Err(ErrorCode::BindError(format!(
127                        "TVF function only accepts `mysql-cdc` and `postgres-cdc` source. Expected: {}, but got: {}",
128                        expected_connector_name,
129                        source_catalog.connector_name()
130                    ))
131                    .into());
132                }
133
134                let (props, secret_refs) = source_catalog.with_properties.clone().into_parts();
135                let secret_resolved =
136                    LocalSecretManager::global().fill_secrets(props, secret_refs)?;
137
138                let mut args_vec = vec![
139                    ExprImpl::literal_varchar(secret_resolved["hostname"].clone()),
140                    ExprImpl::literal_varchar(secret_resolved["port"].clone()),
141                    ExprImpl::literal_varchar(secret_resolved["username"].clone()),
142                    ExprImpl::literal_varchar(secret_resolved["password"].clone()),
143                    ExprImpl::literal_varchar(secret_resolved["database.name"].clone()),
144                    args[1].clone().cast_implicit(&DataType::Varchar)?,
145                ];
146
147                if expected_connector_name.eq_ignore_ascii_case("postgres-cdc") {
148                    args_vec.push(ExprImpl::literal_varchar(
149                        secret_resolved.get("ssl.mode").cloned().unwrap_or_default(),
150                    ));
151                    args_vec.push(ExprImpl::literal_varchar(
152                        secret_resolved
153                            .get("ssl.root.cert")
154                            .cloned()
155                            .unwrap_or_default(),
156                    ));
157                }
158
159                Ok(args_vec)
160            }
161            _ => Err(ErrorCode::BindError("postgres_query function and mysql_query function accept either 2 arguments: (cdc_source_name varchar, query varchar) or 6 arguments: (hostname varchar, port varchar, username varchar, password varchar, database_name varchar, query varchar)".to_owned()).into()),
162        }
163    }
164
165    pub(in crate::binder) fn bind_function(
166        &mut self,
167        Function {
168            scalar_as_agg,
169            name,
170            arg_list,
171            within_group,
172            filter,
173            over,
174        }: &Function,
175    ) -> Result<ExprImpl> {
176        let (schema_name, func_name) = match name.0.as_slice() {
177            [name] => (None, name.real_value()),
178            [schema, name] => {
179                let schema_name = schema.real_value();
180                let func_name = if schema_name == INFORMATION_SCHEMA_SCHEMA_NAME {
181                    // definition of information_schema: https://github.com/postgres/postgres/blob/e0b2eed047df9045664da6f724cb42c10f8b12f0/src/backend/catalog/information_schema.sql
182                    //
183                    // FIXME: handle schema correctly, so that the functions are hidden if the schema is not in the search path.
184                    let function_name = name.real_value();
185                    if function_name != "_pg_expandarray" {
186                        bail_not_implemented!(
187                            issue = 12422,
188                            "Unsupported function name under schema: {}",
189                            schema_name
190                        );
191                    }
192                    function_name
193                } else {
194                    name.real_value()
195                };
196                (Some(schema_name), func_name)
197            }
198            [database, schema, name] => {
199                // Support database.schema.function qualified names when database matches current database
200                let database_name = database.real_value();
201                if database_name != self.db_name {
202                    return Err(ErrorCode::BindError(format!(
203                        "Cross-database function call is not supported: {}",
204                        name
205                    ))
206                    .into());
207                }
208                let schema_name = schema.real_value();
209                let func_name = name.real_value();
210                (Some(schema_name), func_name)
211            }
212            _ => bail_not_implemented!(issue = 112, "qualified function {}", name),
213        };
214
215        // FIXME: This is a hack to support [Bytebase queries](https://github.com/TennyZhuang/bytebase/blob/4a26f7c62b80e86e58ad2f77063138dc2f420623/backend/plugin/db/pg/sync.go#L549).
216        // Bytebase widely used the pattern like `obj_description(format('%s.%s',
217        // quote_ident(idx.schemaname), quote_ident(idx.indexname))::regclass) AS comment` to
218        // retrieve object comment, however we don't support casting a non-literal expression to
219        // regclass. We just hack the `obj_description` and `col_description` here, to disable it to
220        // bind its arguments.
221        if func_name == "obj_description" || func_name == "col_description" {
222            return Ok(ExprImpl::literal_varchar("".to_owned()));
223        }
224
225        // special binding logic for `array_transform` and `map_filter`
226        if func_name == "array_transform" || func_name == "map_filter" {
227            return self.validate_and_bind_special_function_params(
228                &func_name,
229                *scalar_as_agg,
230                arg_list,
231                within_group.as_deref(),
232                filter.as_deref(),
233                over.as_ref(),
234            );
235        }
236
237        // Bind function arguments. Secret references are bound as ExprImpl::SecretRef first,
238        // and then restricted to UDF calls below.
239        let has_secret_ref_arg = arg_list.args.iter().any(|arg| {
240            matches!(
241                arg,
242                FunctionArg::Unnamed(FunctionArgExpr::SecretRef(_))
243                    | FunctionArg::Named {
244                        arg: FunctionArgExpr::SecretRef(_),
245                        ..
246                    }
247            )
248        });
249
250        // Return a clear usage error before secret lookup when there is no UDF candidate by name.
251        // This avoids leaking "not found" for secret refs used in clearly invalid contexts.
252        if has_secret_ref_arg {
253            let has_udf_candidate = self
254                .catalog
255                .get_functions_by_name(
256                    &self.db_name,
257                    self.bind_schema_path(schema_name.as_deref()),
258                    &func_name,
259                )
260                .map(|(funcs, _)| !funcs.is_empty())
261                .unwrap_or(false);
262            if !has_udf_candidate {
263                return Err(ErrorCode::InvalidInputSyntax(
264                    "secret reference is only allowed in user-defined function arguments"
265                        .to_owned(),
266                )
267                .into());
268            }
269        }
270
271        let bind_arg = if func_name.eq_ignore_ascii_case("jsonb_agg") {
272            Self::bind_jsonb_agg_arg
273        } else {
274            Self::bind_function_arg
275        };
276        let mut args: Vec<ExprImpl> = arg_list
277            .args
278            .iter()
279            .map(|arg| bind_arg(self, arg))
280            .flatten_ok()
281            .try_collect()?;
282
283        let mut referred_udfs = HashSet::new();
284        let mut is_udf_call = false;
285
286        let wrapped_agg_type = if *scalar_as_agg {
287            // Let's firstly try to apply the `AGGREGATE:` prefix.
288            // We will reject functions that are not able to be wrapped as aggregate function.
289            let mut array_args = args
290                .iter()
291                .enumerate()
292                .map(|(i, expr)| InputRef::new(i, DataType::list(expr.return_type())).into())
293                .collect_vec();
294            let schema_path = self.bind_schema_path(schema_name.as_deref());
295            let scalar_func_expr = if let Ok((func, _)) = self.catalog.get_function_by_name_inputs(
296                &self.db_name,
297                schema_path,
298                &func_name,
299                &mut array_args,
300            ) {
301                // record the dependency upon the UDF
302                referred_udfs.insert(func.id);
303                is_udf_call = true;
304                self.check_privilege(
305                    ObjectCheckItem::new(func.owner, AclMode::Execute, func.name.clone(), func.id),
306                    self.database_id,
307                )?;
308
309                if !func.kind.is_scalar() {
310                    return Err(ErrorCode::InvalidInputSyntax(
311                        "expect a scalar function after `AGGREGATE:`".to_owned(),
312                    )
313                    .into());
314                }
315
316                if func.language == "sql" {
317                    self.bind_sql_udf(func.clone(), array_args)?
318                } else {
319                    UserDefinedFunction::new(func.clone(), array_args).into()
320                }
321            } else {
322                self.bind_builtin_scalar_function(&func_name, array_args, arg_list.variadic)?
323            };
324
325            // `AggType::WrapScalar` requires the inner expression to be convertible to `ExprNode`
326            // and directly executable. If there's any subquery in the expression, this will fail.
327            let expr_node = match scalar_func_expr.try_to_expr_proto() {
328                Ok(expr_node) => expr_node,
329                Err(e) => {
330                    return Err(ErrorCode::InvalidInputSyntax(format!(
331                        "function {func_name} cannot be used after `AGGREGATE:`: {e}",
332                    ))
333                    .into());
334                }
335            };
336
337            // now this is either an aggregate/window function call
338            Some(AggType::WrapScalar(expr_node))
339        } else {
340            None
341        };
342
343        let schema_path = self.bind_schema_path(schema_name.as_deref());
344        let udf = if wrapped_agg_type.is_none()
345            && let Ok((func, _)) = self.catalog.get_function_by_name_inputs(
346                &self.db_name,
347                schema_path,
348                &func_name,
349                &mut args,
350            ) {
351            // record the dependency upon the UDF
352            referred_udfs.insert(func.id);
353            is_udf_call = true;
354            self.check_privilege(
355                ObjectCheckItem::new(func.owner, AclMode::Execute, func.name.clone(), func.id),
356                self.database_id,
357            )?;
358            Some(func.clone())
359        } else {
360            None
361        };
362
363        if has_secret_ref_arg && !is_udf_call {
364            return Err(ErrorCode::InvalidInputSyntax(
365                "secret reference is only allowed in user-defined function arguments".to_owned(),
366            )
367            .into());
368        }
369
370        self.included_udfs.extend(referred_udfs);
371
372        let agg_type = if wrapped_agg_type.is_some() {
373            wrapped_agg_type
374        } else if let Some(ref udf) = udf
375            && udf.kind.is_aggregate()
376        {
377            assert_ne!(udf.language, "sql", "SQL UDAF is not supported yet");
378            Some(AggType::UserDefined(udf.as_ref().into()))
379        } else {
380            AggType::from_str(&func_name).ok()
381        };
382
383        // try to bind it as a window function call
384        if let Some(over) = over {
385            reject_syntax!(
386                arg_list.distinct,
387                "`DISTINCT` is not allowed in window function call"
388            );
389            reject_syntax!(
390                arg_list.variadic,
391                "`VARIADIC` is not allowed in window function call"
392            );
393            reject_syntax!(
394                !arg_list.order_by.is_empty(),
395                "`ORDER BY` is not allowed in window function call argument list"
396            );
397            reject_syntax!(
398                within_group.is_some(),
399                "`WITHIN GROUP` is not allowed in window function call"
400            );
401
402            let kind = if let Some(agg_type) = agg_type {
403                // aggregate as window function
404                WindowFuncKind::Aggregate(agg_type)
405            } else if let Ok(kind) = WindowFuncKind::from_str(&func_name) {
406                kind
407            } else {
408                bail_not_implemented!(issue = 8961, "Unrecognized window function: {}", func_name);
409            };
410            return self.bind_window_function(
411                kind,
412                args,
413                arg_list.ignore_nulls,
414                filter.as_deref(),
415                over,
416            );
417        }
418
419        // now it's an aggregate/scalar/table function call
420        reject_syntax!(
421            arg_list.ignore_nulls,
422            "`IGNORE NULLS` is not allowed in aggregate/scalar/table function call"
423        );
424
425        // try to bind it as an aggregate function call
426        if let Some(agg_type) = agg_type {
427            reject_syntax!(
428                arg_list.variadic,
429                "`VARIADIC` is not allowed in aggregate function call"
430            );
431            return self.bind_aggregate_function(
432                agg_type,
433                arg_list.distinct,
434                args,
435                &arg_list.order_by,
436                within_group.as_deref(),
437                filter.as_deref(),
438            );
439        }
440
441        // now it's a scalar/table function call
442        reject_syntax!(
443            arg_list.distinct,
444            "`DISTINCT` is not allowed in scalar/table function call"
445        );
446        reject_syntax!(
447            !arg_list.order_by.is_empty(),
448            "`ORDER BY` is not allowed in scalar/table function call"
449        );
450        reject_syntax!(
451            within_group.is_some(),
452            "`WITHIN GROUP` is not allowed in scalar/table function call"
453        );
454        reject_syntax!(
455            filter.is_some(),
456            "`FILTER` is not allowed in scalar/table function call"
457        );
458
459        // try to bind it as a table function call
460        {
461            // `file_scan` table function
462            if func_name.eq_ignore_ascii_case("file_scan") {
463                reject_syntax!(
464                    arg_list.variadic,
465                    "`VARIADIC` is not allowed in table function call"
466                );
467                self.ensure_table_function_allowed()?;
468                return Ok(TableFunction::new_file_scan(args)?.into());
469            }
470            // `postgres_query` table function
471            if func_name.eq("postgres_query") {
472                reject_syntax!(
473                    arg_list.variadic,
474                    "`VARIADIC` is not allowed in table function call"
475                );
476                self.ensure_table_function_allowed()?;
477                let args = self
478                    .bind_postgres_or_mysql_query_args(schema_name.as_deref(), args, "postgres-cdc")
479                    .context("postgres_query error")?;
480                return Ok(TableFunction::new_postgres_query(args)
481                    .context("postgres_query error")?
482                    .into());
483            }
484            // `mysql_query` table function
485            if func_name.eq("mysql_query") {
486                reject_syntax!(
487                    arg_list.variadic,
488                    "`VARIADIC` is not allowed in table function call"
489                );
490                self.ensure_table_function_allowed()?;
491                let args = self
492                    .bind_postgres_or_mysql_query_args(schema_name.as_deref(), args, "mysql-cdc")
493                    .context("mysql_query error")?;
494                return Ok(TableFunction::new_mysql_query(args)
495                    .context("mysql_query error")?
496                    .into());
497            }
498            // `internal_backfill_progress` table function
499            if func_name.eq("internal_backfill_progress") {
500                reject_syntax!(
501                    arg_list.variadic,
502                    "`VARIADIC` is not allowed in table function call"
503                );
504                self.ensure_table_function_allowed()?;
505                return Ok(TableFunction::new_internal_backfill_progress().into());
506            }
507            // `internal_source_backfill_progress` table function
508            if func_name.eq("internal_source_backfill_progress") {
509                reject_syntax!(
510                    arg_list.variadic,
511                    "`VARIADIC` is not allowed in table function call"
512                );
513                self.ensure_table_function_allowed()?;
514                return Ok(TableFunction::new_internal_source_backfill_progress().into());
515            }
516            // `internal_get_channel_delta_stats` table function
517            if func_name.eq("internal_get_channel_delta_stats") {
518                reject_syntax!(
519                    arg_list.variadic,
520                    "`VARIADIC` is not allowed in table function call"
521                );
522                self.ensure_table_function_allowed()?;
523
524                return Ok(TableFunction::new_internal_get_channel_delta_stats(args).into());
525            }
526            // UDTF
527            if let Some(ref udf) = udf
528                && udf.kind.is_table()
529            {
530                reject_syntax!(
531                    arg_list.variadic,
532                    "`VARIADIC` is not allowed in table function call"
533                );
534                self.ensure_table_function_allowed()?;
535                if udf.language == "sql" {
536                    return self.bind_sql_udf(udf.clone(), args);
537                }
538                return Ok(TableFunction::new_user_defined(udf.clone(), args).into());
539            }
540            // builtin table function
541            if let Ok(function_type) = TableFunctionType::from_str(&func_name) {
542                reject_syntax!(
543                    arg_list.variadic,
544                    "`VARIADIC` is not allowed in table function call"
545                );
546                self.ensure_table_function_allowed()?;
547                return Ok(TableFunction::new(function_type, args)?.into());
548            }
549        }
550
551        // try to bind it as a scalar function call
552        if let Some(ref udf) = udf {
553            assert!(udf.kind.is_scalar());
554            reject_syntax!(
555                arg_list.variadic,
556                "`VARIADIC` is not allowed in user-defined function call"
557            );
558            if udf.language == "sql" {
559                return self.bind_sql_udf(udf.clone(), args);
560            }
561            return Ok(UserDefinedFunction::new(udf.clone(), args).into());
562        }
563
564        self.bind_builtin_scalar_function(&func_name, args, arg_list.variadic)
565    }
566
567    fn validate_and_bind_special_function_params(
568        &mut self,
569        func_name: &str,
570        scalar_as_agg: bool,
571        arg_list: &FunctionArgList,
572        within_group: Option<&OrderByExpr>,
573        filter: Option<&risingwave_sqlparser::ast::Expr>,
574        over: Option<&Window>,
575    ) -> Result<ExprImpl> {
576        assert!(["array_transform", "map_filter"].contains(&func_name));
577
578        reject_syntax!(
579            scalar_as_agg,
580            "`AGGREGATE:` prefix is not allowed for `{}`",
581            func_name
582        );
583        reject_syntax!(
584            !arg_list.is_args_only(),
585            "keywords like `DISTINCT`, `ORDER BY` are not allowed in `{}` argument list",
586            func_name
587        );
588        reject_syntax!(
589            within_group.is_some(),
590            "`WITHIN GROUP` is not allowed in `{}` call",
591            func_name
592        );
593        reject_syntax!(
594            filter.is_some(),
595            "`FILTER` is not allowed in `{}` call",
596            func_name
597        );
598        reject_syntax!(
599            over.is_some(),
600            "`OVER` is not allowed in `{}` call",
601            func_name
602        );
603        if func_name == "array_transform" {
604            self.bind_array_transform(&arg_list.args)
605        } else {
606            self.bind_map_filter(&arg_list.args)
607        }
608    }
609
610    fn bind_array_transform(&mut self, args: &[FunctionArg]) -> Result<ExprImpl> {
611        let [array, lambda] = args else {
612            return Err(ErrorCode::BindError(format!(
613                "`array_transform` expect two inputs `array` and `lambda`, but {} were given",
614                args.len()
615            ))
616            .into());
617        };
618
619        let bound_array = self.bind_function_arg(array)?;
620        let [bound_array] = <[ExprImpl; 1]>::try_from(bound_array).map_err(|bound_array| -> RwError {
621            ErrorCode::BindError(format!("The `array` argument for `array_transform` should be bound to one argument, but {} were got", bound_array.len()))
622                .into()
623        })?;
624
625        let inner_ty = match bound_array.return_type() {
626            DataType::List(ty) => ty.into_elem(),
627            real_type => return Err(ErrorCode::BindError(format!(
628                "The `array` argument for `array_transform` should be an array, but {} were got",
629                real_type
630            ))
631            .into()),
632        };
633
634        let ast::FunctionArgExpr::Expr(ast::Expr::LambdaFunction {
635            args: lambda_args,
636            body: lambda_body,
637        }) = lambda.get_expr()
638        else {
639            return Err(ErrorCode::BindError(
640                "The `lambda` argument for `array_transform` should be a lambda function"
641                    .to_owned(),
642            )
643            .into());
644        };
645
646        let [lambda_arg] = <[Ident; 1]>::try_from(lambda_args).map_err(|args| -> RwError {
647            ErrorCode::BindError(format!(
648                "The `lambda` argument for `array_transform` should be a lambda function with one argument, but {} were given",
649                args.len()
650            ))
651            .into()
652        })?;
653
654        let bound_lambda = self.bind_unary_lambda_function(inner_ty, lambda_arg, *lambda_body)?;
655
656        let lambda_ret_type = bound_lambda.return_type();
657        let transform_ret_type = DataType::list(lambda_ret_type);
658
659        Ok(ExprImpl::FunctionCallWithLambda(Box::new(
660            FunctionCallWithLambda::new_unchecked(
661                ExprType::ArrayTransform,
662                vec![bound_array],
663                bound_lambda,
664                transform_ret_type,
665            ),
666        )))
667    }
668
669    fn bind_unary_lambda_function(
670        &mut self,
671        input_ty: DataType,
672        arg: Ident,
673        body: ast::Expr,
674    ) -> Result<ExprImpl> {
675        let lambda_args = HashMap::from([(arg.real_value(), (0usize, input_ty))]);
676        let orig_lambda_args = self.context.lambda_args.replace(lambda_args);
677        let body = self.bind_expr_inner(&body)?;
678        self.context.lambda_args = orig_lambda_args;
679
680        Ok(body)
681    }
682
683    fn bind_map_filter(&mut self, args: &[FunctionArg]) -> Result<ExprImpl> {
684        let [input, lambda] = args else {
685            return Err(ErrorCode::BindError(format!(
686                "`map_filter` requires two arguments (input_map and lambda), got {}",
687                args.len()
688            ))
689            .into());
690        };
691
692        let bound_input = self.bind_function_arg(input)?;
693        let [bound_input] = <[ExprImpl; 1]>::try_from(bound_input).map_err(|e| {
694            ErrorCode::BindError(format!(
695                "Input argument should resolve to single expression, got {}",
696                e.len()
697            ))
698        })?;
699
700        let (key_type, value_type) = match bound_input.return_type() {
701            DataType::Map(map_type) => (map_type.key().clone(), map_type.value().clone()),
702            t => {
703                return Err(
704                    ErrorCode::BindError(format!("Input must be Map type, got {}", t)).into(),
705                );
706            }
707        };
708
709        let ast::FunctionArgExpr::Expr(ast::Expr::LambdaFunction {
710            args: lambda_args,
711            body: lambda_body,
712        }) = lambda.get_expr()
713        else {
714            return Err(ErrorCode::BindError(
715                "Second argument must be a lambda function".to_owned(),
716            )
717            .into());
718        };
719
720        let [key_arg, value_arg] = <[Ident; 2]>::try_from(lambda_args).map_err(|args| {
721            ErrorCode::BindError(format!(
722                "Lambda must have exactly two parameters (key, value), got {}",
723                args.len()
724            ))
725        })?;
726
727        let bound_lambda = self.bind_binary_lambda_function(
728            key_arg,
729            key_type.clone(),
730            value_arg,
731            value_type.clone(),
732            *lambda_body,
733        )?;
734
735        let lambda_ret_type = bound_lambda.return_type();
736        if lambda_ret_type != DataType::Boolean {
737            return Err(ErrorCode::BindError(format!(
738                "Lambda must return Boolean type, got {}",
739                lambda_ret_type
740            ))
741            .into());
742        }
743
744        let map_type = MapType::from_kv(key_type, value_type);
745        let return_type = DataType::Map(map_type);
746
747        Ok(ExprImpl::FunctionCallWithLambda(Box::new(
748            FunctionCallWithLambda::new_unchecked(
749                ExprType::MapFilter,
750                vec![bound_input],
751                bound_lambda,
752                return_type,
753            ),
754        )))
755    }
756
757    fn bind_binary_lambda_function(
758        &mut self,
759        first_arg: Ident,
760        first_ty: DataType,
761        second_arg: Ident,
762        second_ty: DataType,
763        body: ast::Expr,
764    ) -> Result<ExprImpl> {
765        let lambda_args = HashMap::from([
766            (first_arg.real_value(), (0usize, first_ty)),
767            (second_arg.real_value(), (1usize, second_ty)),
768        ]);
769
770        let orig_ctx = self.context.lambda_args.replace(lambda_args);
771        let bound_body = self.bind_expr_inner(&body)?;
772        self.context.lambda_args = orig_ctx;
773
774        Ok(bound_body)
775    }
776
777    fn ensure_table_function_allowed(&self) -> Result<()> {
778        if let Some(clause) = self.context.clause {
779            match clause {
780                Clause::JoinOn
781                | Clause::Where
782                | Clause::Having
783                | Clause::Filter
784                | Clause::Values
785                | Clause::Insert
786                | Clause::GeneratedColumn => {
787                    return Err(ErrorCode::InvalidInputSyntax(format!(
788                        "table functions are not allowed in {}",
789                        clause
790                    ))
791                    .into());
792                }
793                Clause::GroupBy | Clause::From => {}
794            }
795        }
796        Ok(())
797    }
798
799    /// A common utility function to extract sql udf expression out from the input `ast`.
800    pub(crate) fn extract_udf_expr(ast: Vec<Statement>) -> Result<AstExpr> {
801        if ast.len() != 1 {
802            return Err(ErrorCode::InvalidInputSyntax(
803                "the query for sql udf should contain only one statement".to_owned(),
804            )
805            .into());
806        }
807
808        // Extract the expression out
809        let Statement::Query(query) = ast.into_iter().next().unwrap() else {
810            return Err(ErrorCode::InvalidInputSyntax(
811                "invalid function definition, please recheck the syntax".to_owned(),
812            )
813            .into());
814        };
815
816        if let Some(expr) = query.as_single_select_item() {
817            // Inline SQL UDF.
818            Ok(expr.clone())
819        } else {
820            // Subquery SQL UDF.
821            Ok(AstExpr::Subquery(query))
822        }
823    }
824
825    pub fn bind_sql_udf_inner(
826        &mut self,
827        body: &str,
828        arg_names: &[String],
829        args: Vec<ExprImpl>,
830    ) -> Result<ExprImpl> {
831        // This represents the current user defined function is `language sql`
832        let ast = Parser::parse_sql(body)?;
833
834        // Stash the current arguments.
835        // For subquery SQL UDF, as we always push a new context, there should be no arguments to stash.
836        // For inline SQL UDF, we need to stash the arguments in case of nesting.
837        let stashed_arguments = self.context.sql_udf_arguments.take();
838
839        // The actual inline logic for sql udf.
840        let mut arguments = HashMap::new();
841        for (i, arg) in args.into_iter().enumerate() {
842            if arg_names[i].is_empty() {
843                // unnamed argument, use `$1`, `$2` as the name
844                arguments.insert(format!("${}", i + 1), arg);
845            } else {
846                // named argument
847                arguments.insert(arg_names[i].clone(), arg);
848            }
849        }
850        self.context.sql_udf_arguments = Some(arguments);
851
852        let Ok(expr) = Self::extract_udf_expr(ast) else {
853            return Err(ErrorCode::InvalidInputSyntax(
854                "failed to parse the input query and extract the udf expression, \
855                please recheck the syntax"
856                    .to_owned(),
857            )
858            .into());
859        };
860
861        let bind_result = self.bind_expr(&expr);
862        // Restore arguments information for subsequent binding.
863        self.context.sql_udf_arguments = stashed_arguments;
864
865        bind_result
866    }
867
868    fn bind_sql_udf(
869        &mut self,
870        func: Arc<FunctionCatalog>,
871        args: Vec<ExprImpl>,
872    ) -> Result<ExprImpl> {
873        let Some(body) = &func.body else {
874            return Err(
875                ErrorCode::InvalidInputSyntax("`body` must exist for sql udf".to_owned()).into(),
876            );
877        };
878
879        self.bind_sql_udf_inner(body, &func.arg_names, args)
880    }
881
882    pub(in crate::binder) fn bind_function_expr_arg(
883        &mut self,
884        arg_expr: &FunctionArgExpr,
885    ) -> Result<Vec<ExprImpl>> {
886        match arg_expr {
887            FunctionArgExpr::Expr(expr) => Ok(vec![self.bind_expr_inner(expr)?]),
888            FunctionArgExpr::QualifiedWildcard(_, _)
889            | FunctionArgExpr::ExprQualifiedWildcard(_, _) => Err(ErrorCode::InvalidInputSyntax(
890                format!("unexpected wildcard {}", arg_expr),
891            )
892            .into()),
893            FunctionArgExpr::Wildcard(None) => Ok(vec![]),
894            FunctionArgExpr::Wildcard(Some(_)) => unreachable!(),
895            FunctionArgExpr::SecretRef(secret_ref_value) => {
896                let (schema_name, secret_name) = Binder::resolve_schema_qualified_name(
897                    &self.db_name,
898                    &secret_ref_value.secret_name,
899                )?;
900                let schema_path = self.bind_schema_path(schema_name.as_deref());
901                let (secret_catalog, _) =
902                    self.catalog
903                        .get_secret_by_name(&self.db_name, schema_path, &secret_name)?;
904
905                self.check_privilege(
906                    ObjectCheckItem::new(
907                        secret_catalog.owner(),
908                        AclMode::Usage,
909                        secret_catalog.name.clone(),
910                        secret_catalog.id,
911                    ),
912                    self.database_id,
913                )?;
914
915                self.included_secrets.insert(secret_catalog.id);
916
917                let ref_as = match secret_ref_value.ref_as {
918                    SecretRefAsType::Text => risingwave_pb::secret::secret_ref::RefAsType::Text,
919                    SecretRefAsType::File => risingwave_pb::secret::secret_ref::RefAsType::File,
920                };
921
922                Ok(vec![
923                    crate::expr::SecretRef {
924                        secret_id: secret_catalog.id,
925                        ref_as,
926                        secret_name: secret_catalog.name.clone(),
927                    }
928                    .into(),
929                ])
930            }
931        }
932    }
933
934    pub(in crate::binder) fn bind_function_arg(
935        &mut self,
936        arg: &FunctionArg,
937    ) -> Result<Vec<ExprImpl>> {
938        match arg {
939            FunctionArg::Unnamed(expr) => self.bind_function_expr_arg(expr),
940            FunctionArg::Named { .. } => Err(ErrorCode::InvalidInputSyntax(
941                "named function arguments are not supported yet".to_owned(),
942            )
943            .into()),
944        }
945    }
946
947    fn bind_jsonb_agg_arg(&mut self, arg: &FunctionArg) -> Result<Vec<ExprImpl>> {
948        match arg {
949            FunctionArg::Unnamed(FunctionArgExpr::QualifiedWildcard(prefix, except)) => {
950                let (schema_name, table_name) =
951                    Binder::resolve_schema_qualified_name(&self.db_name, prefix)?;
952                let except_indices = self.generate_except_indices(except.as_deref())?;
953                let (begin, end) = self
954                    .context
955                    .resolve_relation_range(&table_name, &schema_name)?;
956                let (exprs, names) = Self::iter_bound_columns(
957                    self.context.columns[begin..end]
958                        .iter()
959                        .filter(|c| !c.is_hidden && !except_indices.contains(&c.index)),
960                );
961                self.wrap_wildcard_exprs_as_named_row(exprs, names)
962            }
963            FunctionArg::Unnamed(FunctionArgExpr::ExprQualifiedWildcard(expr, prefix)) => {
964                let (exprs, names) = self.bind_wildcard_field_column(expr, prefix)?;
965                self.wrap_wildcard_exprs_as_named_row(exprs, names)
966            }
967            _ => self.bind_function_arg(arg),
968        }
969    }
970
971    fn wrap_wildcard_exprs_as_named_row(
972        &self,
973        exprs: Vec<ExprImpl>,
974        names: Vec<Option<String>>,
975    ) -> Result<Vec<ExprImpl>> {
976        let return_type =
977            DataType::Struct(StructType::new(
978                names.into_iter().zip_eq_fast(exprs.iter()).enumerate().map(
979                    |(idx, (name, expr))| {
980                        (
981                            name.unwrap_or_else(|| format!("f{}", idx + 1)),
982                            expr.return_type(),
983                        )
984                    },
985                ),
986            ));
987        Ok(vec![
988            FunctionCall::new_unchecked(ExprType::Row, exprs, return_type).into(),
989        ])
990    }
991}