Skip to content

Commit 9ba1813

Browse files
authored
fix(sql): improve table function named arg hints (#19751)
fix(sql): improve table function named arg hints (#12691)
1 parent 55fd955 commit 9ba1813

5 files changed

Lines changed: 86 additions & 6 deletions

File tree

src/query/sql/src/planner/binder/bind_table_reference/bind_obfuscate.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use crate::BindContext;
3737
use crate::ScalarBinder;
3838
use crate::binder::Binder;
3939
use crate::binder::table_args::bind_table_args;
40+
use crate::binder::table_args::malformed_named_table_arg_error;
4041
use crate::binder::util::TableIdentifier;
4142
use crate::optimizer::ir::SExpr;
4243

@@ -47,20 +48,32 @@ impl Binder {
4748
params: &[Expr],
4849
named_params: &[(Identifier, Expr)],
4950
) -> Result<(SExpr, BindContext)> {
50-
let param = match params {
51-
[] => Err(None),
52-
[param @ Expr::ColumnRef { .. }] => Ok(param.clone()),
53-
_ => Err(params[0].span()),
51+
let (param, extra_params) = match params {
52+
[] => (Err(None), &[][..]),
53+
[param @ Expr::ColumnRef { .. }] => (Ok(param.clone()), &[][..]),
54+
[param, extra_params @ ..] => (Err(param.span()), extra_params),
5455
};
5556

57+
if let Some(extra_param) = extra_params.first() {
58+
if let Some(err) = malformed_named_table_arg_error("obfuscate", extra_param) {
59+
return Err(err);
60+
}
61+
62+
return Err(ErrorCode::InvalidArgument(
63+
"The `OBFUSCATE` function accepts one table_name positional argument and an optional `seed => ...` named argument.",
64+
)
65+
.set_span(extra_param.span()));
66+
}
67+
5668
let mut scalar_binder = ScalarBinder::new(
5769
bind_context,
5870
self.ctx.clone(),
5971
&self.name_resolution_ctx,
6072
self.metadata.clone(),
6173
&[],
6274
);
63-
let mut named_args = bind_table_args(&mut scalar_binder, &[], named_params, &None)?.named;
75+
let mut named_args =
76+
bind_table_args("obfuscate", &mut scalar_binder, &[], named_params, &None)?.named;
6477
let seed = match named_args.remove("seed") {
6578
Some(v) => u64_value(&v).ok_or(ErrorCode::BadArguments("invalid seed"))?,
6679
None => {

src/query/sql/src/planner/binder/bind_table_reference/bind_table_function.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ impl Binder {
137137
&[],
138138
);
139139
let table_args = bind_table_args(
140+
&func_name.name,
140141
&mut scalar_binder,
141142
params,
142143
named_params,

src/query/sql/src/planner/binder/table_args.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use std::collections::HashMap;
1616
use std::sync::Arc;
1717

18+
use databend_common_ast::ast::BinaryOperator;
19+
use databend_common_ast::ast::ColumnID;
20+
use databend_common_ast::ast::ColumnRef;
1821
use databend_common_ast::ast::Expr;
1922
use databend_common_ast::ast::Identifier;
2023
use databend_common_catalog::table_args::TableArgs;
@@ -142,15 +145,52 @@ fn try_fold_to_scalar(
142145
}
143146
}
144147

148+
pub(crate) fn malformed_named_table_arg_error(func_name: &str, expr: &Expr) -> Option<ErrorCode> {
149+
let Expr::BinaryOp {
150+
op: BinaryOperator::Eq,
151+
left,
152+
..
153+
} = expr
154+
else {
155+
return None;
156+
};
157+
158+
let Expr::ColumnRef {
159+
column:
160+
ColumnRef {
161+
database: None,
162+
table: None,
163+
column: ColumnID::Name(name),
164+
},
165+
..
166+
} = left.as_ref()
167+
else {
168+
return None;
169+
};
170+
171+
ErrorCode::SemanticError(format!(
172+
"Table function '{}' uses `=>` for named arguments; replace `{} = ...` with `{} => ...`.",
173+
func_name, name.name, name.name
174+
))
175+
.set_span(expr.span())
176+
.into()
177+
}
178+
145179
pub fn bind_table_args(
180+
func_name: &str,
146181
scalar_binder: &mut ScalarBinder<'_>,
147182
params: &[Expr],
148183
named_params: &[(Identifier, Expr)],
149184
subquery_executor: &Option<Arc<dyn QueryExecutor>>,
150185
) -> Result<TableArgs> {
151186
let mut args = Vec::with_capacity(params.len());
152187
for arg in params.iter() {
153-
args.push((scalar_binder.bind(arg)?.0, arg));
188+
if let Some(err) = malformed_named_table_arg_error(func_name, arg) {
189+
return Err(err);
190+
}
191+
192+
let scalar = scalar_binder.bind(arg)?.0;
193+
args.push((scalar, arg));
154194
}
155195

156196
let mut named_args = Vec::with_capacity(named_params.len());

src/query/sql/tests/it/semantic/binder.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,18 @@ async fn test_binder_clauses_and_ordering() -> Result<()> {
196196
setup_sqls: &["CREATE TABLE t(number UInt64)"],
197197
sql: "SELECT number % 3 AS c1, sum(c1) FROM t GROUP BY number % 3",
198198
},
199+
SqlTestCase {
200+
name: "table_function_named_arguments_require_fat_arrow",
201+
description: "A table function named argument written with '=' should produce a direct hint to use '=>'.",
202+
setup_sqls: &[],
203+
sql: "SELECT * FROM infer_schema(location = '@data/parquet/int96.parquet')",
204+
},
205+
SqlTestCase {
206+
name: "obfuscate_named_arguments_require_fat_arrow",
207+
description: "OBFUSCATE should surface the same '=>' hint when a named argument is written with '='.",
208+
setup_sqls: &["CREATE TABLE t1(a String)"],
209+
sql: "SELECT * FROM obfuscate(t1, seed = 20)",
210+
},
199211
];
200212

201213
run_binder_cases("binder_clauses.txt", &cases).await

src/query/sql/tests/it/semantic/binder_clauses.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,3 +257,17 @@ EvalScalar
257257
└── limit: NONE
258258

259259

260+
=== table_function_named_arguments_require_fat_arrow ===
261+
description: A table function named argument written with '=' should produce a direct hint to use '=>'.
262+
sql: SELECT * FROM infer_schema(location = '@data/parquet/int96.parquet')
263+
status: error
264+
code: 1065
265+
message: Table function 'infer_schema' uses `=>` for named arguments; replace `location = ...` with `location => ...`.
266+
267+
=== obfuscate_named_arguments_require_fat_arrow ===
268+
description: OBFUSCATE should surface the same '=>' hint when a named argument is written with '='.
269+
sql: SELECT * FROM obfuscate(t1, seed = 20)
270+
status: error
271+
code: 1065
272+
message: Table function 'obfuscate' uses `=>` for named arguments; replace `seed = ...` with `seed => ...`.
273+

0 commit comments

Comments
 (0)