diff --git a/src/frontend/planner_test/tests/testdata/input/subquery.yaml b/src/frontend/planner_test/tests/testdata/input/subquery.yaml index b1d58c5e422ed..47785f0234271 100644 --- a/src/frontend/planner_test/tests/testdata/input/subquery.yaml +++ b/src/frontend/planner_test/tests/testdata/input/subquery.yaml @@ -291,3 +291,13 @@ expected_outputs: - batch_plan - stream_plan +- name: Only table-in-out functions can have subquery parameters. + sql: | + SELECT * FROM generate_series(1, (select 1)); + expected_outputs: + - binder_error +- name: While this one is allowed. + sql: | + SELECT generate_series(1, (select 1)); + expected_outputs: + - batch_plan \ No newline at end of file diff --git a/src/frontend/planner_test/tests/testdata/output/subquery.yaml b/src/frontend/planner_test/tests/testdata/output/subquery.yaml index e07e84e040929..914d9b764c5e1 100644 --- a/src/frontend/planner_test/tests/testdata/output/subquery.yaml +++ b/src/frontend/planner_test/tests/testdata/output/subquery.yaml @@ -864,3 +864,16 @@ └─StreamHopWindow { time_col: t1.ts, slide: 00:10:00, size: 00:30:00, output: all } └─StreamFilter { predicate: IsNotNull(t1.ts) } └─StreamTableScan { table: t1, columns: [t1.k, t1.ts], pk: [t1.k], dist: UpstreamHashShard(t1.k) } +- name: Only table-in-out functions can have subquery parameters. + sql: | + SELECT * FROM generate_series(1, (select 1)); + binder_error: 'Invalid input syntax: Only table-in-out functions can have subquery parameters, generate_series only accepts constant parameters' +- name: While this one is allowed. + sql: | + SELECT generate_series(1, (select 1)); + batch_plan: |- + BatchProject { exprs: [GenerateSeries(1:Int32, $0)] } + └─BatchProjectSet { select_list: [GenerateSeries(1:Int32, $0)] } + └─BatchNestedLoopJoin { type: LeftOuter, predicate: true, output: all } + ├─BatchValues { rows: [[]] } + └─BatchValues { rows: [[1:Int32]] } diff --git a/src/frontend/src/binder/relation/table_function.rs b/src/frontend/src/binder/relation/table_function.rs index 988ea0561a860..032791bfab30c 100644 --- a/src/frontend/src/binder/relation/table_function.rs +++ b/src/frontend/src/binder/relation/table_function.rs @@ -28,7 +28,7 @@ use crate::binder::bind_context::Clause; use crate::catalog::system_catalog::pg_catalog::{ PG_GET_KEYWORDS_FUNC_NAME, PG_KEYWORDS_TABLE_NAME, }; -use crate::expr::Expr; +use crate::expr::{Expr, ExprImpl}; impl Binder { /// Binds a table function AST, which is a function call in a relation position. @@ -125,6 +125,20 @@ impl Binder { self.pop_context()?; let func = func?; + if let ExprImpl::TableFunction(func) = &func { + if func + .args + .iter() + .any(|arg| matches!(arg, ExprImpl::Subquery(_))) + { + // Same error reports as DuckDB. + return Err(ErrorCode::InvalidInputSyntax( + format!("Only table-in-out functions can have subquery parameters, {} only accepts constant parameters", func.name()), + ) + .into()); + } + } + // bool indicates if the field is hidden let mut columns = if let DataType::Struct(s) = func.return_type() { // If the table function returns a struct, it will be flattened into multiple columns.