1use 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::license::Feature;
25use risingwave_common::secret::LocalSecretManager;
26use risingwave_common::types::{DataType, MapType, StructType};
27use risingwave_common::util::iter_util::ZipEqFast;
28use risingwave_expr::aggregate::AggType;
29use risingwave_expr::window_function::WindowFuncKind;
30use risingwave_sqlparser::ast::{
31 self, Expr as AstExpr, Function, FunctionArg, FunctionArgExpr, FunctionArgList, Ident,
32 OrderByExpr, SecretRefAsType, Statement, Window,
33};
34use risingwave_sqlparser::parser::Parser;
35
36use crate::binder::Binder;
37use crate::binder::bind_context::Clause;
38use crate::catalog::OwnedByUserCatalog;
39use crate::catalog::function_catalog::FunctionCatalog;
40use crate::error::{ErrorCode, Result, RwError};
41use crate::expr::{
42 Expr, ExprImpl, ExprType, FunctionCall, FunctionCallWithLambda, InputRef,
43 MSSQL_QUERY_ARG_COUNT, TableFunction, TableFunctionType, UserDefinedFunction,
44 expr_impl_to_string_fn,
45};
46use crate::handler::privilege::ObjectCheckItem;
47
48mod aggregate;
49mod builtin_scalar;
50mod window;
51
52const SYS_FUNCTION_WITHOUT_ARGS: &[&str] = &[
54 "session_user",
55 "user",
56 "current_user",
57 "current_role",
58 "current_catalog",
59 "current_schema",
60 "current_timestamp",
61];
62
63const INLINE_QUERY_ARG_LEN: usize = 6;
64const CDC_SOURCE_QUERY_ARG_LEN: usize = 2;
65
66pub(super) fn is_sys_function_without_args(ident: &Ident) -> bool {
67 SYS_FUNCTION_WITHOUT_ARGS
68 .iter()
69 .any(|e| ident.real_value().as_str() == *e && ident.quote_style().is_none())
70}
71
72macro_rules! reject_syntax {
73 ($pred:expr, $msg:expr) => {
74 if $pred {
75 return Err(ErrorCode::InvalidInputSyntax($msg.to_string()).into());
76 }
77 };
78
79 ($pred:expr, $fmt:expr, $($arg:tt)*) => {
80 if $pred {
81 return Err(ErrorCode::InvalidInputSyntax(
82 format!($fmt, $($arg)*)
83 ).into());
84 }
85 };
86}
87
88impl Binder {
89 fn bind_external_db_query_args(
108 &self,
109 schema_name: Option<&str>,
110 args: Vec<ExprImpl>,
111 inline_arg_len: usize,
112 expected_connector_name: &str,
113 ) -> Result<Vec<ExprImpl>> {
114 match args.len() {
115 n if n == inline_arg_len => {
116 let mut cast_args = Vec::with_capacity(n);
117 for arg in args {
118 cast_args.push(arg.cast_implicit(&DataType::Varchar)?);
119 }
120 Ok(cast_args)
121 }
122 CDC_SOURCE_QUERY_ARG_LEN => {
123 use thiserror_ext::AsReport as _;
124
125 use crate::catalog::root_catalog::SchemaPath;
126
127 let schema_path = self.bind_schema_path(schema_name);
128 let raw_source_name = expr_impl_to_string_fn(&args[0])?;
129 let object_name = Parser::parse_object_name_str(&raw_source_name).map_err(|e| {
134 ErrorCode::BindError(format!(
135 "invalid source name `{}`: {}",
136 raw_source_name,
137 e.to_report_string(),
138 ))
139 })?;
140 let (explicit_schema, source_name) =
141 Binder::resolve_schema_qualified_name(&self.db_name, &object_name)?;
142 let source_schema_path = match explicit_schema.as_deref() {
143 Some(schema) => SchemaPath::Name(schema),
144 None => schema_path,
145 };
146 let source_catalog = self
147 .catalog
148 .get_source_by_name(&self.db_name, source_schema_path, &source_name)?
149 .0;
150
151 self.check_privilege(
157 ObjectCheckItem::new(
158 source_catalog.owner,
159 AclMode::Select,
160 source_catalog.name.clone(),
161 source_catalog.id,
162 ),
163 source_catalog.database_id,
164 )?;
165
166 if source_catalog.is_cdc_table_source() {
167 return Err(ErrorCode::BindError(
168 "mysql_query and postgres_query only accept shared CDC sources, not CDC table sources"
169 .to_owned(),
170 )
171 .into());
172 }
173
174 if !source_catalog
175 .connector_name()
176 .eq_ignore_ascii_case(expected_connector_name)
177 {
178 return Err(ErrorCode::BindError(format!(
179 "TVF function only accepts `mysql-cdc`, `postgres-cdc` and `sqlserver-cdc` source. Expected: {}, but got: {}",
180 expected_connector_name,
181 source_catalog.connector_name()
182 ))
183 .into());
184 }
185
186 let (props, secret_refs) = source_catalog.with_properties.clone().into_parts();
187 let secret_resolved =
188 LocalSecretManager::global().fill_secrets(props, secret_refs)?;
189
190 let mut args_vec = vec![
191 ExprImpl::literal_varchar(secret_resolved["hostname"].clone()),
192 ExprImpl::literal_varchar(secret_resolved["port"].clone()),
193 ExprImpl::literal_varchar(secret_resolved["username"].clone()),
194 ExprImpl::literal_varchar(secret_resolved["password"].clone()),
195 ExprImpl::literal_varchar(secret_resolved["database.name"].clone()),
196 args[1].clone().cast_implicit(&DataType::Varchar)?,
197 ];
198
199 if expected_connector_name.eq_ignore_ascii_case("postgres-cdc") {
200 args_vec.push(ExprImpl::literal_varchar(
201 secret_resolved.get("ssl.mode").cloned().unwrap_or_default(),
202 ));
203 args_vec.push(ExprImpl::literal_varchar(
204 secret_resolved
205 .get("ssl.root.cert")
206 .cloned()
207 .unwrap_or_default(),
208 ));
209 } else if expected_connector_name.eq_ignore_ascii_case("sqlserver-cdc") {
210 let encrypt = secret_resolved
216 .get("database.encrypt")
217 .map(|v| v.eq_ignore_ascii_case("true").to_string())
218 .unwrap_or_else(|| "false".to_owned());
219 args_vec.push(ExprImpl::literal_varchar(encrypt));
220 args_vec.push(ExprImpl::literal_varchar("true".to_owned()));
225 }
226
227 Ok(args_vec)
228 }
229 _ => {
230 let func_name = match expected_connector_name.to_ascii_lowercase().as_str() {
231 "postgres-cdc" => "postgres_query",
232 "mysql-cdc" => "mysql_query",
233 "sqlserver-cdc" => "mssql_query",
234 _ => "external _query",
235 };
236 Err(ErrorCode::BindError(format!(
237 "{func_name} function accepts either 2 arguments: \
238 (cdc_source_name varchar, query varchar) or {inline_arg_len} arguments \
239 for the inline form (see mssql_query / postgres_query / mysql_query \
240 documentation)"
241 ))
242 .into())
243 }
244 }
245 }
246
247 pub(in crate::binder) fn bind_function(
248 &mut self,
249 Function {
250 scalar_as_agg,
251 name,
252 arg_list,
253 within_group,
254 filter,
255 over,
256 }: &Function,
257 ) -> Result<ExprImpl> {
258 let (schema_name, func_name) = match name.0.as_slice() {
259 [name] => (None, name.real_value()),
260 [schema, name] => {
261 let schema_name = schema.real_value();
262 let func_name = if schema_name == INFORMATION_SCHEMA_SCHEMA_NAME {
263 let function_name = name.real_value();
267 if function_name != "_pg_expandarray" {
268 bail_not_implemented!(
269 issue = 12422,
270 "Unsupported function name under schema: {}",
271 schema_name
272 );
273 }
274 function_name
275 } else {
276 name.real_value()
277 };
278 (Some(schema_name), func_name)
279 }
280 [database, schema, name] => {
281 let database_name = database.real_value();
283 if database_name != self.db_name {
284 return Err(ErrorCode::BindError(format!(
285 "Cross-database function call is not supported: {}",
286 name
287 ))
288 .into());
289 }
290 let schema_name = schema.real_value();
291 let func_name = name.real_value();
292 (Some(schema_name), func_name)
293 }
294 _ => bail_not_implemented!(issue = 112, "qualified function {}", name),
295 };
296
297 if func_name == "obj_description" || func_name == "col_description" {
304 return Ok(ExprImpl::literal_varchar("".to_owned()));
305 }
306
307 if func_name == "array_transform" || func_name == "map_filter" {
309 return self.validate_and_bind_special_function_params(
310 &func_name,
311 *scalar_as_agg,
312 arg_list,
313 within_group.as_deref(),
314 filter.as_deref(),
315 over.as_ref(),
316 );
317 }
318
319 let has_secret_ref_arg = arg_list.args.iter().any(|arg| {
322 matches!(
323 arg,
324 FunctionArg::Unnamed(FunctionArgExpr::SecretRef(_))
325 | FunctionArg::Named {
326 arg: FunctionArgExpr::SecretRef(_),
327 ..
328 }
329 )
330 });
331
332 if has_secret_ref_arg {
335 let has_udf_candidate = self
336 .catalog
337 .get_functions_by_name(
338 &self.db_name,
339 self.bind_schema_path(schema_name.as_deref()),
340 &func_name,
341 )
342 .map(|(funcs, _)| !funcs.is_empty())
343 .unwrap_or(false);
344 if !has_udf_candidate {
345 return Err(ErrorCode::InvalidInputSyntax(
346 "secret reference is only allowed in user-defined function arguments"
347 .to_owned(),
348 )
349 .into());
350 }
351 }
352
353 let bind_arg = if func_name.eq_ignore_ascii_case("jsonb_agg") {
354 Self::bind_jsonb_agg_arg
355 } else {
356 Self::bind_function_arg
357 };
358 let mut args: Vec<ExprImpl> = arg_list
359 .args
360 .iter()
361 .map(|arg| bind_arg(self, arg))
362 .flatten_ok()
363 .try_collect()?;
364
365 let mut referred_udfs = HashSet::new();
366 let mut is_udf_call = false;
367
368 let wrapped_agg_type = if *scalar_as_agg {
369 let mut array_args = args
372 .iter()
373 .enumerate()
374 .map(|(i, expr)| InputRef::new(i, DataType::list(expr.return_type())).into())
375 .collect_vec();
376 let schema_path = self.bind_schema_path(schema_name.as_deref());
377 let scalar_func_expr = if let Ok((func, _)) = self.catalog.get_function_by_name_inputs(
378 &self.db_name,
379 schema_path,
380 &func_name,
381 &mut array_args,
382 ) {
383 referred_udfs.insert(func.id);
385 is_udf_call = true;
386 self.check_privilege(
387 ObjectCheckItem::new(func.owner, AclMode::Execute, func.name.clone(), func.id),
388 self.database_id,
389 )?;
390
391 if !func.kind.is_scalar() {
392 return Err(ErrorCode::InvalidInputSyntax(
393 "expect a scalar function after `AGGREGATE:`".to_owned(),
394 )
395 .into());
396 }
397
398 if func.language == "sql" {
399 self.bind_sql_udf(func.clone(), array_args)?
400 } else {
401 UserDefinedFunction::new(func.clone(), array_args).into()
402 }
403 } else {
404 self.bind_builtin_scalar_function(&func_name, array_args, arg_list.variadic)?
405 };
406
407 let expr_node = match scalar_func_expr.try_to_expr_proto() {
410 Ok(expr_node) => expr_node,
411 Err(e) => {
412 return Err(ErrorCode::InvalidInputSyntax(format!(
413 "function {func_name} cannot be used after `AGGREGATE:`: {e}",
414 ))
415 .into());
416 }
417 };
418
419 Some(AggType::WrapScalar(expr_node))
421 } else {
422 None
423 };
424
425 let schema_path = self.bind_schema_path(schema_name.as_deref());
426 let udf = if wrapped_agg_type.is_none()
427 && let Ok((func, _)) = self.catalog.get_function_by_name_inputs(
428 &self.db_name,
429 schema_path,
430 &func_name,
431 &mut args,
432 ) {
433 referred_udfs.insert(func.id);
435 is_udf_call = true;
436 self.check_privilege(
437 ObjectCheckItem::new(func.owner, AclMode::Execute, func.name.clone(), func.id),
438 self.database_id,
439 )?;
440 Some(func.clone())
441 } else {
442 None
443 };
444
445 if has_secret_ref_arg && !is_udf_call {
446 return Err(ErrorCode::InvalidInputSyntax(
447 "secret reference is only allowed in user-defined function arguments".to_owned(),
448 )
449 .into());
450 }
451
452 self.included_udfs.extend(referred_udfs);
453
454 let agg_type = if wrapped_agg_type.is_some() {
455 wrapped_agg_type
456 } else if let Some(ref udf) = udf
457 && udf.kind.is_aggregate()
458 {
459 assert_ne!(udf.language, "sql", "SQL UDAF is not supported yet");
460 Some(AggType::UserDefined(udf.as_ref().into()))
461 } else {
462 AggType::from_str(&func_name).ok()
463 };
464
465 if let Some(over) = over {
467 reject_syntax!(
468 arg_list.distinct,
469 "`DISTINCT` is not allowed in window function call"
470 );
471 reject_syntax!(
472 arg_list.variadic,
473 "`VARIADIC` is not allowed in window function call"
474 );
475 reject_syntax!(
476 !arg_list.order_by.is_empty(),
477 "`ORDER BY` is not allowed in window function call argument list"
478 );
479 reject_syntax!(
480 within_group.is_some(),
481 "`WITHIN GROUP` is not allowed in window function call"
482 );
483
484 let kind = if let Some(agg_type) = agg_type {
485 WindowFuncKind::Aggregate(agg_type)
487 } else if let Ok(kind) = WindowFuncKind::from_str(&func_name) {
488 kind
489 } else {
490 bail_not_implemented!(issue = 8961, "Unrecognized window function: {}", func_name);
491 };
492 return self.bind_window_function(
493 kind,
494 args,
495 arg_list.ignore_nulls,
496 filter.as_deref(),
497 over,
498 );
499 }
500
501 reject_syntax!(
503 arg_list.ignore_nulls,
504 "`IGNORE NULLS` is not allowed in aggregate/scalar/table function call"
505 );
506
507 if let Some(agg_type) = agg_type {
509 reject_syntax!(
510 arg_list.variadic,
511 "`VARIADIC` is not allowed in aggregate function call"
512 );
513 return self.bind_aggregate_function(
514 agg_type,
515 arg_list.distinct,
516 args,
517 &arg_list.order_by,
518 within_group.as_deref(),
519 filter.as_deref(),
520 );
521 }
522
523 reject_syntax!(
525 arg_list.distinct,
526 "`DISTINCT` is not allowed in scalar/table function call"
527 );
528 reject_syntax!(
529 !arg_list.order_by.is_empty(),
530 "`ORDER BY` is not allowed in scalar/table function call"
531 );
532 reject_syntax!(
533 within_group.is_some(),
534 "`WITHIN GROUP` is not allowed in scalar/table function call"
535 );
536 reject_syntax!(
537 filter.is_some(),
538 "`FILTER` is not allowed in scalar/table function call"
539 );
540
541 {
543 if func_name.eq_ignore_ascii_case("file_scan") {
545 reject_syntax!(
546 arg_list.variadic,
547 "`VARIADIC` is not allowed in table function call"
548 );
549 self.ensure_table_function_allowed()?;
550 return Ok(TableFunction::new_file_scan(args)?.into());
551 }
552 if func_name.eq("postgres_query") {
554 reject_syntax!(
555 arg_list.variadic,
556 "`VARIADIC` is not allowed in table function call"
557 );
558 self.ensure_table_function_allowed()?;
559 let args = self
560 .bind_external_db_query_args(
561 schema_name.as_deref(),
562 args,
563 INLINE_QUERY_ARG_LEN,
564 "postgres-cdc",
565 )
566 .context("postgres_query error")?;
567 return Ok(TableFunction::new_postgres_query(args)
568 .context("postgres_query error")?
569 .into());
570 }
571 if func_name.eq("mysql_query") {
573 reject_syntax!(
574 arg_list.variadic,
575 "`VARIADIC` is not allowed in table function call"
576 );
577 self.ensure_table_function_allowed()?;
578 let args = self
579 .bind_external_db_query_args(
580 schema_name.as_deref(),
581 args,
582 INLINE_QUERY_ARG_LEN,
583 "mysql-cdc",
584 )
585 .context("mysql_query error")?;
586 return Ok(TableFunction::new_mysql_query(args)
587 .context("mysql_query error")?
588 .into());
589 }
590 if func_name.eq("mssql_query") {
592 reject_syntax!(
593 arg_list.variadic,
594 "`VARIADIC` is not allowed in table function call"
595 );
596 self.ensure_table_function_allowed()?;
597
598 Feature::SqlServerCdcSource.check_available()?;
601
602 let args = self
603 .bind_external_db_query_args(
604 schema_name.as_deref(),
605 args,
606 MSSQL_QUERY_ARG_COUNT,
607 "sqlserver-cdc",
608 )
609 .context("mssql_query error")?;
610 return Ok(TableFunction::new_mssql_query(args)
611 .context("mssql_query error")?
612 .into());
613 }
614 if func_name.eq("internal_backfill_progress") {
616 reject_syntax!(
617 arg_list.variadic,
618 "`VARIADIC` is not allowed in table function call"
619 );
620 self.ensure_table_function_allowed()?;
621 return Ok(TableFunction::new_internal_backfill_progress().into());
622 }
623 if func_name.eq("internal_source_backfill_progress") {
625 reject_syntax!(
626 arg_list.variadic,
627 "`VARIADIC` is not allowed in table function call"
628 );
629 self.ensure_table_function_allowed()?;
630 return Ok(TableFunction::new_internal_source_backfill_progress().into());
631 }
632 if func_name.eq("internal_get_channel_delta_stats") {
634 reject_syntax!(
635 arg_list.variadic,
636 "`VARIADIC` is not allowed in table function call"
637 );
638 self.ensure_table_function_allowed()?;
639
640 return Ok(TableFunction::new_internal_get_channel_delta_stats(args).into());
641 }
642 if let Some(ref udf) = udf
644 && udf.kind.is_table()
645 {
646 reject_syntax!(
647 arg_list.variadic,
648 "`VARIADIC` is not allowed in table function call"
649 );
650 self.ensure_table_function_allowed()?;
651 if udf.language == "sql" {
652 return self.bind_sql_udf(udf.clone(), args);
653 }
654 return Ok(TableFunction::new_user_defined(udf.clone(), args).into());
655 }
656 if let Ok(function_type) = TableFunctionType::from_str(&func_name) {
658 reject_syntax!(
659 arg_list.variadic,
660 "`VARIADIC` is not allowed in table function call"
661 );
662 self.ensure_table_function_allowed()?;
663 return Ok(TableFunction::new(function_type, args)?.into());
664 }
665 }
666
667 if let Some(ref udf) = udf {
669 assert!(udf.kind.is_scalar());
670 reject_syntax!(
671 arg_list.variadic,
672 "`VARIADIC` is not allowed in user-defined function call"
673 );
674 if udf.language == "sql" {
675 return self.bind_sql_udf(udf.clone(), args);
676 }
677 return Ok(UserDefinedFunction::new(udf.clone(), args).into());
678 }
679
680 self.bind_builtin_scalar_function(&func_name, args, arg_list.variadic)
681 }
682
683 fn validate_and_bind_special_function_params(
684 &mut self,
685 func_name: &str,
686 scalar_as_agg: bool,
687 arg_list: &FunctionArgList,
688 within_group: Option<&OrderByExpr>,
689 filter: Option<&risingwave_sqlparser::ast::Expr>,
690 over: Option<&Window>,
691 ) -> Result<ExprImpl> {
692 assert!(["array_transform", "map_filter"].contains(&func_name));
693
694 reject_syntax!(
695 scalar_as_agg,
696 "`AGGREGATE:` prefix is not allowed for `{}`",
697 func_name
698 );
699 reject_syntax!(
700 !arg_list.is_args_only(),
701 "keywords like `DISTINCT`, `ORDER BY` are not allowed in `{}` argument list",
702 func_name
703 );
704 reject_syntax!(
705 within_group.is_some(),
706 "`WITHIN GROUP` is not allowed in `{}` call",
707 func_name
708 );
709 reject_syntax!(
710 filter.is_some(),
711 "`FILTER` is not allowed in `{}` call",
712 func_name
713 );
714 reject_syntax!(
715 over.is_some(),
716 "`OVER` is not allowed in `{}` call",
717 func_name
718 );
719 if func_name == "array_transform" {
720 self.bind_array_transform(&arg_list.args)
721 } else {
722 self.bind_map_filter(&arg_list.args)
723 }
724 }
725
726 fn bind_array_transform(&mut self, args: &[FunctionArg]) -> Result<ExprImpl> {
727 let [array, lambda] = args else {
728 return Err(ErrorCode::BindError(format!(
729 "`array_transform` expect two inputs `array` and `lambda`, but {} were given",
730 args.len()
731 ))
732 .into());
733 };
734
735 let bound_array = self.bind_function_arg(array)?;
736 let [bound_array] = <[ExprImpl; 1]>::try_from(bound_array).map_err(|bound_array| -> RwError {
737 ErrorCode::BindError(format!("The `array` argument for `array_transform` should be bound to one argument, but {} were got", bound_array.len()))
738 .into()
739 })?;
740
741 let inner_ty = match bound_array.return_type() {
742 DataType::List(ty) => ty.into_elem(),
743 real_type => return Err(ErrorCode::BindError(format!(
744 "The `array` argument for `array_transform` should be an array, but {} were got",
745 real_type
746 ))
747 .into()),
748 };
749
750 let ast::FunctionArgExpr::Expr(ast::Expr::LambdaFunction {
751 args: lambda_args,
752 body: lambda_body,
753 }) = lambda.get_expr()
754 else {
755 return Err(ErrorCode::BindError(
756 "The `lambda` argument for `array_transform` should be a lambda function"
757 .to_owned(),
758 )
759 .into());
760 };
761
762 let [lambda_arg] = <[Ident; 1]>::try_from(lambda_args).map_err(|args| -> RwError {
763 ErrorCode::BindError(format!(
764 "The `lambda` argument for `array_transform` should be a lambda function with one argument, but {} were given",
765 args.len()
766 ))
767 .into()
768 })?;
769
770 let bound_lambda = self.bind_unary_lambda_function(inner_ty, lambda_arg, *lambda_body)?;
771
772 let lambda_ret_type = bound_lambda.return_type();
773 let transform_ret_type = DataType::list(lambda_ret_type);
774
775 Ok(ExprImpl::FunctionCallWithLambda(Box::new(
776 FunctionCallWithLambda::new_unchecked(
777 ExprType::ArrayTransform,
778 vec![bound_array],
779 bound_lambda,
780 transform_ret_type,
781 ),
782 )))
783 }
784
785 fn bind_unary_lambda_function(
786 &mut self,
787 input_ty: DataType,
788 arg: Ident,
789 body: ast::Expr,
790 ) -> Result<ExprImpl> {
791 let lambda_args = HashMap::from([(arg.real_value(), (0usize, input_ty))]);
792 let orig_lambda_args = self.context.lambda_args.replace(lambda_args);
793 let body = self.bind_expr_inner(&body)?;
794 self.context.lambda_args = orig_lambda_args;
795
796 Ok(body)
797 }
798
799 fn bind_map_filter(&mut self, args: &[FunctionArg]) -> Result<ExprImpl> {
800 let [input, lambda] = args else {
801 return Err(ErrorCode::BindError(format!(
802 "`map_filter` requires two arguments (input_map and lambda), got {}",
803 args.len()
804 ))
805 .into());
806 };
807
808 let bound_input = self.bind_function_arg(input)?;
809 let [bound_input] = <[ExprImpl; 1]>::try_from(bound_input).map_err(|e| {
810 ErrorCode::BindError(format!(
811 "Input argument should resolve to single expression, got {}",
812 e.len()
813 ))
814 })?;
815
816 let (key_type, value_type) = match bound_input.return_type() {
817 DataType::Map(map_type) => (map_type.key().clone(), map_type.value().clone()),
818 t => {
819 return Err(
820 ErrorCode::BindError(format!("Input must be Map type, got {}", t)).into(),
821 );
822 }
823 };
824
825 let ast::FunctionArgExpr::Expr(ast::Expr::LambdaFunction {
826 args: lambda_args,
827 body: lambda_body,
828 }) = lambda.get_expr()
829 else {
830 return Err(ErrorCode::BindError(
831 "Second argument must be a lambda function".to_owned(),
832 )
833 .into());
834 };
835
836 let [key_arg, value_arg] = <[Ident; 2]>::try_from(lambda_args).map_err(|args| {
837 ErrorCode::BindError(format!(
838 "Lambda must have exactly two parameters (key, value), got {}",
839 args.len()
840 ))
841 })?;
842
843 let bound_lambda = self.bind_binary_lambda_function(
844 key_arg,
845 key_type.clone(),
846 value_arg,
847 value_type.clone(),
848 *lambda_body,
849 )?;
850
851 let lambda_ret_type = bound_lambda.return_type();
852 if lambda_ret_type != DataType::Boolean {
853 return Err(ErrorCode::BindError(format!(
854 "Lambda must return Boolean type, got {}",
855 lambda_ret_type
856 ))
857 .into());
858 }
859
860 let map_type = MapType::from_kv(key_type, value_type);
861 let return_type = DataType::Map(map_type);
862
863 Ok(ExprImpl::FunctionCallWithLambda(Box::new(
864 FunctionCallWithLambda::new_unchecked(
865 ExprType::MapFilter,
866 vec![bound_input],
867 bound_lambda,
868 return_type,
869 ),
870 )))
871 }
872
873 fn bind_binary_lambda_function(
874 &mut self,
875 first_arg: Ident,
876 first_ty: DataType,
877 second_arg: Ident,
878 second_ty: DataType,
879 body: ast::Expr,
880 ) -> Result<ExprImpl> {
881 let lambda_args = HashMap::from([
882 (first_arg.real_value(), (0usize, first_ty)),
883 (second_arg.real_value(), (1usize, second_ty)),
884 ]);
885
886 let orig_ctx = self.context.lambda_args.replace(lambda_args);
887 let bound_body = self.bind_expr_inner(&body)?;
888 self.context.lambda_args = orig_ctx;
889
890 Ok(bound_body)
891 }
892
893 fn ensure_table_function_allowed(&self) -> Result<()> {
894 if let Some(clause) = self.context.clause {
895 match clause {
896 Clause::JoinOn
897 | Clause::Where
898 | Clause::Having
899 | Clause::Filter
900 | Clause::Values
901 | Clause::Insert
902 | Clause::GeneratedColumn => {
903 return Err(ErrorCode::InvalidInputSyntax(format!(
904 "table functions are not allowed in {}",
905 clause
906 ))
907 .into());
908 }
909 Clause::GroupBy | Clause::From => {}
910 }
911 }
912 Ok(())
913 }
914
915 pub(crate) fn extract_udf_expr(ast: Vec<Statement>) -> Result<AstExpr> {
917 if ast.len() != 1 {
918 return Err(ErrorCode::InvalidInputSyntax(
919 "the query for sql udf should contain only one statement".to_owned(),
920 )
921 .into());
922 }
923
924 let Statement::Query(query) = ast.into_iter().next().unwrap() else {
926 return Err(ErrorCode::InvalidInputSyntax(
927 "invalid function definition, please recheck the syntax".to_owned(),
928 )
929 .into());
930 };
931
932 if let Some(expr) = query.as_single_select_item() {
933 Ok(expr.clone())
935 } else {
936 Ok(AstExpr::Subquery(query))
938 }
939 }
940
941 pub fn bind_sql_udf_inner(
942 &mut self,
943 body: &str,
944 arg_names: &[String],
945 args: Vec<ExprImpl>,
946 ) -> Result<ExprImpl> {
947 let ast = Parser::parse_sql(body)?;
949
950 let stashed_arguments = self.context.sql_udf_arguments.take();
954
955 let mut arguments = HashMap::new();
957 for (i, arg) in args.into_iter().enumerate() {
958 if arg_names[i].is_empty() {
959 arguments.insert(format!("${}", i + 1), arg);
961 } else {
962 arguments.insert(arg_names[i].clone(), arg);
964 }
965 }
966 self.context.sql_udf_arguments = Some(arguments);
967
968 let Ok(expr) = Self::extract_udf_expr(ast) else {
969 return Err(ErrorCode::InvalidInputSyntax(
970 "failed to parse the input query and extract the udf expression, \
971 please recheck the syntax"
972 .to_owned(),
973 )
974 .into());
975 };
976
977 let bind_result = self.bind_expr(&expr);
978 self.context.sql_udf_arguments = stashed_arguments;
980
981 bind_result
982 }
983
984 fn bind_sql_udf(
985 &mut self,
986 func: Arc<FunctionCatalog>,
987 args: Vec<ExprImpl>,
988 ) -> Result<ExprImpl> {
989 let Some(body) = &func.body else {
990 return Err(
991 ErrorCode::InvalidInputSyntax("`body` must exist for sql udf".to_owned()).into(),
992 );
993 };
994
995 self.bind_sql_udf_inner(body, &func.arg_names, args)
996 }
997
998 pub(in crate::binder) fn bind_function_expr_arg(
999 &mut self,
1000 arg_expr: &FunctionArgExpr,
1001 ) -> Result<Vec<ExprImpl>> {
1002 match arg_expr {
1003 FunctionArgExpr::Expr(expr) => Ok(vec![self.bind_expr_inner(expr)?]),
1004 FunctionArgExpr::QualifiedWildcard(_, _)
1005 | FunctionArgExpr::ExprQualifiedWildcard(_, _) => Err(ErrorCode::InvalidInputSyntax(
1006 format!("unexpected wildcard {}", arg_expr),
1007 )
1008 .into()),
1009 FunctionArgExpr::Wildcard(None) => Ok(vec![]),
1010 FunctionArgExpr::Wildcard(Some(_)) => unreachable!(),
1011 FunctionArgExpr::SecretRef(secret_ref_value) => {
1012 let (schema_name, secret_name) = Binder::resolve_schema_qualified_name(
1013 &self.db_name,
1014 &secret_ref_value.secret_name,
1015 )?;
1016 let schema_path = self.bind_schema_path(schema_name.as_deref());
1017 let (secret_catalog, _) =
1018 self.catalog
1019 .get_secret_by_name(&self.db_name, schema_path, &secret_name)?;
1020
1021 self.check_privilege(
1022 ObjectCheckItem::new(
1023 secret_catalog.owner(),
1024 AclMode::Usage,
1025 secret_catalog.name.clone(),
1026 secret_catalog.id,
1027 ),
1028 self.database_id,
1029 )?;
1030
1031 self.included_secrets.insert(secret_catalog.id);
1032
1033 let ref_as = match secret_ref_value.ref_as {
1034 SecretRefAsType::Text => risingwave_pb::secret::secret_ref::RefAsType::Text,
1035 SecretRefAsType::File => risingwave_pb::secret::secret_ref::RefAsType::File,
1036 };
1037
1038 Ok(vec![
1039 crate::expr::SecretRef {
1040 secret_id: secret_catalog.id,
1041 ref_as,
1042 secret_name: secret_catalog.name.clone(),
1043 }
1044 .into(),
1045 ])
1046 }
1047 }
1048 }
1049
1050 pub(in crate::binder) fn bind_function_arg(
1051 &mut self,
1052 arg: &FunctionArg,
1053 ) -> Result<Vec<ExprImpl>> {
1054 match arg {
1055 FunctionArg::Unnamed(expr) => self.bind_function_expr_arg(expr),
1056 FunctionArg::Named { .. } => Err(ErrorCode::InvalidInputSyntax(
1057 "named function arguments are not supported yet".to_owned(),
1058 )
1059 .into()),
1060 }
1061 }
1062
1063 fn bind_jsonb_agg_arg(&mut self, arg: &FunctionArg) -> Result<Vec<ExprImpl>> {
1064 match arg {
1065 FunctionArg::Unnamed(FunctionArgExpr::QualifiedWildcard(prefix, except)) => {
1066 let (schema_name, table_name) =
1067 Binder::resolve_schema_qualified_name(&self.db_name, prefix)?;
1068 let except_indices = self.generate_except_indices(except.as_deref())?;
1069 let (begin, end) = self
1070 .context
1071 .resolve_relation_range(&table_name, &schema_name)?;
1072 let (exprs, names) = Self::iter_bound_columns(
1073 self.context.columns[begin..end]
1074 .iter()
1075 .filter(|c| !c.is_hidden && !except_indices.contains(&c.index)),
1076 );
1077 self.wrap_wildcard_exprs_as_named_row(exprs, names)
1078 }
1079 FunctionArg::Unnamed(FunctionArgExpr::ExprQualifiedWildcard(expr, prefix)) => {
1080 let (exprs, names) = self.bind_wildcard_field_column(expr, prefix)?;
1081 self.wrap_wildcard_exprs_as_named_row(exprs, names)
1082 }
1083 _ => self.bind_function_arg(arg),
1084 }
1085 }
1086
1087 fn wrap_wildcard_exprs_as_named_row(
1088 &self,
1089 exprs: Vec<ExprImpl>,
1090 names: Vec<Option<String>>,
1091 ) -> Result<Vec<ExprImpl>> {
1092 let return_type =
1093 DataType::Struct(StructType::new(
1094 names.into_iter().zip_eq_fast(exprs.iter()).enumerate().map(
1095 |(idx, (name, expr))| {
1096 (
1097 name.unwrap_or_else(|| format!("f{}", idx + 1)),
1098 expr.return_type(),
1099 )
1100 },
1101 ),
1102 ));
1103 Ok(vec![
1104 FunctionCall::new_unchecked(ExprType::Row, exprs, return_type).into(),
1105 ])
1106 }
1107}