Skip to content

Commit a4896a1

Browse files
authored
feat: better display of retained identity (#19029)
1 parent 2f0a820 commit a4896a1

File tree

4 files changed

+78
-27
lines changed

4 files changed

+78
-27
lines changed

src/query/ast/src/parser/error.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -243,23 +243,24 @@ pub fn display_parser_error(error: Error, source: &str) -> String {
243243

244244
// Check for intelligent keyword suggestions first
245245
let has_suggestion = suggest_keyword_correction(span_text, source, &expected_tokens);
246+
let mut msg = if span_text.is_empty() {
247+
"unexpected end of input".to_string()
248+
} else if all_reserved_keywords()
249+
.iter()
250+
.any(|keyword| keyword.to_lowercase() == span_text.to_lowercase())
251+
&& has_suggestion.is_none()
252+
{
253+
format!("unexpected `{span_text}`. it's reserved keyword, you may avoid using it")
254+
} else {
255+
format!("unexpected `{span_text}`")
256+
};
246257
if let Some(suggestion) = has_suggestion {
247-
let mut msg = if span_text.is_empty() {
248-
"unexpected end of input".to_string()
249-
} else {
250-
format!("unexpected `{span_text}`")
251-
};
252258
msg += &format!(". {}", suggestion);
253259
labels = vec![(inner.span, msg)];
254260

255261
// Return early to skip context labels when we have intelligent suggestions
256262
return pretty_print_error(source, labels);
257263
} else {
258-
let mut msg = if span_text.is_empty() {
259-
"unexpected end of input".to_string()
260-
} else {
261-
format!("unexpected `{span_text}`")
262-
};
263264
let mut iter = expected_tokens.iter().enumerate().peekable();
264265
while let Some((i, error)) = iter.next() {
265266
if i == MAX_DISPLAY_ERROR_COUNT {

src/query/ast/tests/it/parser.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use databend_common_ast::parser::query::*;
2323
use databend_common_ast::parser::script::script_block;
2424
use databend_common_ast::parser::script::script_stmt;
2525
use databend_common_ast::parser::statement::insert_stmt;
26+
use databend_common_ast::parser::statement::statement_body;
2627
use databend_common_ast::parser::token::*;
2728
use databend_common_ast::parser::*;
2829
use goldenfile::Mint;
@@ -1287,6 +1288,28 @@ fn test_query_error() {
12871288
}
12881289
}
12891290

1291+
#[test]
1292+
fn test_reserved_error() {
1293+
let mut mint = Mint::new("tests/it/testdata");
1294+
let file = &mut mint.new_goldenfile("reserved-error.txt").unwrap();
1295+
let cases = &[r#"CREATE OR
1296+
REPLACE FUNCTION ai_list_files (data_stage STAGE_LOCATION, l INT) RETURNS TABLE (
1297+
stage VARCHAR,
1298+
relative_path VARCHAR,
1299+
path VARCHAR,
1300+
is_dir BOOLEAN,
1301+
size BIGINT,
1302+
mode VARCHAR,
1303+
content_type VARCHAR,
1304+
etag VARCHAR,
1305+
truncated BOOLEAN
1306+
) LANGUAGE python HANDLER='ai_list_files' address='https://api.bendml.com'"#];
1307+
1308+
for case in cases {
1309+
run_parser(file, statement_body, case);
1310+
}
1311+
}
1312+
12901313
#[test]
12911314
fn test_expr() {
12921315
let mut mint = Mint::new("tests/it/testdata");
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---------- Input ----------
2+
CREATE OR
3+
REPLACE FUNCTION ai_list_files (data_stage STAGE_LOCATION, l INT) RETURNS TABLE (
4+
stage VARCHAR,
5+
relative_path VARCHAR,
6+
path VARCHAR,
7+
is_dir BOOLEAN,
8+
size BIGINT,
9+
mode VARCHAR,
10+
content_type VARCHAR,
11+
etag VARCHAR,
12+
truncated BOOLEAN
13+
) LANGUAGE python HANDLER='ai_list_files' address='https://api.bendml.com'
14+
---------- Output ---------
15+
error:
16+
--> SQL:3:2
17+
|
18+
1 | CREATE OR
19+
| ------ while parsing `CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] <udf_name> <udf_definition> [DESC = <description>]`
20+
2 | REPLACE FUNCTION ai_list_files (data_stage STAGE_LOCATION, l INT) RETURNS TABLE (
21+
| - ----- while parsing TABLE (<return_type>, ...)
22+
| |
23+
| while parsing (<arg_name arg_type>, ...) RETURNS <return body> { AS <sql> | LANGUAGE <language> HANDLER=<handler> ADDRESS=<udf_server_address> } }
24+
3 | stage VARCHAR,
25+
| ^^^^^ unexpected `stage`. it's reserved keyword, you may avoid using it, expecting <LiteralString>, <Ident>, `IDENTIFIER`, or `)`
26+
27+

src/query/ast/tests/it/testdata/stmt-error.txt

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ error:
227227
--> SQL:1:15
228228
|
229229
1 | insert into t format
230-
| ------ ^^^^^^ unexpected `format`, expecting `FROM`, `ORDER`, `LIMIT`, `OFFSET`, `IGNORE_RESULT`, `WITH`, `VALUES`, `EXCEPT`, `SELECT`, `INTERSECT`, `(`, `UNION`, or `.`
230+
| ------ ^^^^^^ unexpected `format`. it's reserved keyword, you may avoid using it, expecting `FROM`, `ORDER`, `LIMIT`, `OFFSET`, `IGNORE_RESULT`, `WITH`, `VALUES`, `EXCEPT`, `SELECT`, `INTERSECT`, `(`, `UNION`, or `.`
231231
| |
232232
| while parsing `INSERT INTO [TABLE] <table> [(<column>, ...)] (VALUES <values> | <query>)`
233233

@@ -393,7 +393,7 @@ error:
393393
--> SQL:1:6
394394
|
395395
1 | SHOW GRANT FOR ROLE 'role1';
396-
| ^^^^^ unexpected `GRANT`, expecting `GRANTS`, `CREATE`, `VIRTUAL`, `NETWORK`, `CATALOGS`, `STREAMS`, `FUNCTIONS`, `DATABASES`, `CONNECTIONS`, `TABLE_FUNCTIONS`, `DROP`, `TASKS`, `TABLE`, `ROLES`, `ONLINE`, `INDEXES`, `COLUMNS`, `WORKLOAD`, `PASSWORD`, `SEQUENCES`, `PROCEDURES`, `PROCESSLIST`, `STAGES`, `TABLES`, `DICTIONARIES`, `METRICS`, `ENGINES`, `SETTINGS`, `VARIABLES`, `WAREHOUSES`, `STATISTICS`, `USER`, `LOCKS`, `SCHEMAS`, `FIELDS`, `VIEWS`, `USERS`, `FILE`, or `FULL`
396+
| ^^^^^ unexpected `GRANT`. it's reserved keyword, you may avoid using it, expecting `GRANTS`, `CREATE`, `VIRTUAL`, `NETWORK`, `CATALOGS`, `STREAMS`, `FUNCTIONS`, `DATABASES`, `CONNECTIONS`, `TABLE_FUNCTIONS`, `DROP`, `TASKS`, `TABLE`, `ROLES`, `ONLINE`, `INDEXES`, `COLUMNS`, `WORKLOAD`, `PASSWORD`, `SEQUENCES`, `PROCEDURES`, `PROCESSLIST`, `STAGES`, `TABLES`, `DICTIONARIES`, `METRICS`, `ENGINES`, `SETTINGS`, `VARIABLES`, `WAREHOUSES`, `STATISTICS`, `USER`, `LOCKS`, `SCHEMAS`, `FIELDS`, `VIEWS`, `USERS`, `FILE`, or `FULL`
397397

398398

399399
---------- Input ----------
@@ -413,7 +413,7 @@ error:
413413
--> SQL:1:15
414414
|
415415
1 | GRANT SELECT, ALL PRIVILEGES, CREATE ON * TO 'test-grant';
416-
| ----- ------ ^^^ unexpected `ALL`, expecting `ALTER`, `APPLY`, `SELECT`, `DELETE`, `ACCESS`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, or `SET`
416+
| ----- ------ ^^^ unexpected `ALL`. it's reserved keyword, you may avoid using it, expecting `ALTER`, `APPLY`, `SELECT`, `DELETE`, `ACCESS`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, or `SET`
417417
| | |
418418
| | while parsing <privileges> ON <privileges_level>
419419
| while parsing `GRANT { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } TO { [ROLE <role_name>] | [USER] <user> }`
@@ -438,7 +438,7 @@ error:
438438
--> SQL:1:17
439439
|
440440
1 | GRANT select ON UDF a TO 'test-grant';
441-
| ----- ------ ^^^ unexpected `UDF`, expecting `IDENTIFIER`, <Ident>, `FALSE`, `*`, <LiteralString>, <LiteralInteger>, <MySQLLiteralHex>, <PGLiteralHex>, `TRUE`, or `MASKING`
441+
| ----- ------ ^^^ unexpected `UDF`. it's reserved keyword, you may avoid using it, expecting `IDENTIFIER`, <Ident>, `FALSE`, `*`, <LiteralString>, <LiteralInteger>, <MySQLLiteralHex>, <PGLiteralHex>, `TRUE`, or `MASKING`
442442
| | |
443443
| | while parsing <privileges> ON <privileges_level>
444444
| while parsing `GRANT { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } TO { [ROLE <role_name>] | [USER] <user> }`
@@ -451,7 +451,7 @@ error:
451451
--> SQL:1:24
452452
|
453453
1 | REVOKE SELECT, CREATE, ALL PRIVILEGES ON * FROM 'test-grant';
454-
| ------ ------ ^^^ unexpected `ALL`, expecting `ALTER`, `APPLY`, `SELECT`, `DELETE`, `ACCESS`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, or `SET`
454+
| ------ ------ ^^^ unexpected `ALL`. it's reserved keyword, you may avoid using it, expecting `ALTER`, `APPLY`, `SELECT`, `DELETE`, `ACCESS`, `USAGE`, `INSERT`, `UPDATE`, `SUPER`, `CREATE`, `DROP`, `GRANT`, or `SET`
455455
| | |
456456
| | while parsing <privileges> ON <privileges_level>
457457
| while parsing `REVOKE { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } FROM { [ROLE <role_name>] | [USER] <user> }`
@@ -464,7 +464,7 @@ error:
464464
--> SQL:1:28
465465
|
466466
1 | REVOKE SELECT, CREATE ON * TO 'test-grant';
467-
| ------ ^^ unexpected `TO`, expecting `FROM` or `.`
467+
| ------ ^^ unexpected `TO`. it's reserved keyword, you may avoid using it, expecting `FROM` or `.`
468468
| |
469469
| while parsing `REVOKE { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } FROM { [ROLE <role_name>] | [USER] <user> }`
470470

@@ -486,7 +486,7 @@ error:
486486
--> SQL:1:33
487487
|
488488
1 | COPY INTO mytable FROM @mystage CONNECTION = ();
489-
| ^^^^^^^^^^ unexpected `CONNECTION`, expecting `ON_ERROR`, `COLUMN_MATCH_MODE`, `RETURN_FAILED_ONLY`, `FORMAT`, `FORCE`, `FILES`, `PURGE`, `SIZE_LIMIT`, `FILE_FORMAT`, `DISABLE_VARIANT_CHECK`, `PATTERN`, `MAX_FILES`, `SPLIT_SIZE`, or `;`
489+
| ^^^^^^^^^^ unexpected `CONNECTION`. it's reserved keyword, you may avoid using it, expecting `ON_ERROR`, `COLUMN_MATCH_MODE`, `RETURN_FAILED_ONLY`, `FORMAT`, `FORCE`, `FILES`, `PURGE`, `SIZE_LIMIT`, `FILE_FORMAT`, `DISABLE_VARIANT_CHECK`, `PATTERN`, `MAX_FILES`, `SPLIT_SIZE`, or `;`
490490

491491

492492
---------- Input ----------
@@ -598,7 +598,7 @@ error:
598598
--> SQL:1:35
599599
|
600600
1 | SELECT * FROM t GROUP BY GROUPING SETS a, b
601-
| ^^^^ unexpected `SETS`, expecting `SELECT`, `INTERSECT`, `WITH`, `EXCEPT`, `VALUES`, `OFFSET`, `IGNORE_RESULT`, `,`, `HAVING`, `WINDOW`, `QUALIFY`, `(`, `UNION`, `FROM`, `ORDER`, `LIMIT`, `FORMAT`, or `;`
601+
| ^^^^ unexpected `SETS`. it's reserved keyword, you may avoid using it, expecting `SELECT`, `INTERSECT`, `WITH`, `EXCEPT`, `VALUES`, `OFFSET`, `IGNORE_RESULT`, `,`, `HAVING`, `WINDOW`, `QUALIFY`, `(`, `UNION`, `FROM`, `ORDER`, `LIMIT`, `FORMAT`, or `;`
602602

603603

604604
---------- Input ----------
@@ -728,7 +728,7 @@ error:
728728
--> SQL:1:6
729729
|
730730
1 | with as t2(tt) as (select a from t) select t2.tt from t2
731-
| ^^ unexpected `as`, expecting <LiteralString>, <Ident>, `IDENTIFIER`, or `RECURSIVE`
731+
| ^^ unexpected `as`. it's reserved keyword, you may avoid using it, expecting <LiteralString>, <Ident>, `IDENTIFIER`, or `RECURSIVE`
732732

733733

734734
---------- Input ----------
@@ -976,7 +976,7 @@ error:
976976
--> SQL:1:34
977977
|
978978
1 | GRANT OWNERSHIP ON d20_0014.* TO USER A;
979-
| ----- ^^^^ unexpected `USER`, expecting `ROLE`
979+
| ----- ^^^^ unexpected `USER`. it's reserved keyword, you may avoid using it, expecting `ROLE`
980980
| |
981981
| while parsing GRANT OWNERSHIP ON <privileges_level> TO ROLE <role_name>
982982

@@ -988,7 +988,7 @@ error:
988988
--> SQL:1:8
989989
|
990990
1 | REVOKE OWNERSHIP, SELECT ON d20_0014.* FROM ROLE 'd20_0015_owner';
991-
| ------ ^^^^^^^^^ unexpected `OWNERSHIP`, expecting `INSERT`, `ALTER`, `SUPER`, `ROLE`, `ACCESS`, `WRITE`, `SET`, `SELECT`, `UPDATE`, `DELETE`, `DROP`, `READ`, `USAGE`, `GRANT`, `CREATE`, `ALL`, or `APPLY`
991+
| ------ ^^^^^^^^^ unexpected `OWNERSHIP`. it's reserved keyword, you may avoid using it, expecting `INSERT`, `ALTER`, `SUPER`, `ROLE`, `ACCESS`, `WRITE`, `SET`, `SELECT`, `UPDATE`, `DELETE`, `DROP`, `READ`, `USAGE`, `GRANT`, `CREATE`, `ALL`, or `APPLY`
992992
| |
993993
| while parsing `REVOKE { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } FROM { [ROLE <role_name>] | [USER] <user> }`
994994

@@ -1000,7 +1000,7 @@ error:
10001000
--> SQL:1:8
10011001
|
10021002
1 | REVOKE OWNERSHIP ON d20_0014.* FROM USER A;
1003-
| ------ ^^^^^^^^^ unexpected `OWNERSHIP`, expecting `INSERT`, `ALTER`, `SUPER`, `ROLE`, `ACCESS`, `WRITE`, `SET`, `SELECT`, `UPDATE`, `DELETE`, `DROP`, `READ`, `USAGE`, `GRANT`, `CREATE`, `ALL`, or `APPLY`
1003+
| ------ ^^^^^^^^^ unexpected `OWNERSHIP`. it's reserved keyword, you may avoid using it, expecting `INSERT`, `ALTER`, `SUPER`, `ROLE`, `ACCESS`, `WRITE`, `SET`, `SELECT`, `UPDATE`, `DELETE`, `DROP`, `READ`, `USAGE`, `GRANT`, `CREATE`, `ALL`, or `APPLY`
10041004
| |
10051005
| while parsing `REVOKE { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } FROM { [ROLE <role_name>] | [USER] <user> }`
10061006

@@ -1012,7 +1012,7 @@ error:
10121012
--> SQL:1:8
10131013
|
10141014
1 | REVOKE OWNERSHIP ON d20_0014.* FROM ROLE A;
1015-
| ------ ^^^^^^^^^ unexpected `OWNERSHIP`, expecting `INSERT`, `ALTER`, `SUPER`, `ROLE`, `ACCESS`, `WRITE`, `SET`, `SELECT`, `UPDATE`, `DELETE`, `DROP`, `READ`, `USAGE`, `GRANT`, `CREATE`, `ALL`, or `APPLY`
1015+
| ------ ^^^^^^^^^ unexpected `OWNERSHIP`. it's reserved keyword, you may avoid using it, expecting `INSERT`, `ALTER`, `SUPER`, `ROLE`, `ACCESS`, `WRITE`, `SET`, `SELECT`, `UPDATE`, `DELETE`, `DROP`, `READ`, `USAGE`, `GRANT`, `CREATE`, `ALL`, or `APPLY`
10161016
| |
10171017
| while parsing `REVOKE { ROLE <role_name> | schemaObjectPrivileges | ALL [ PRIVILEGES ] ON <privileges_level> } FROM { [ROLE <role_name>] | [USER] <user> }`
10181018

@@ -1051,7 +1051,7 @@ error:
10511051
--> SQL:1:85
10521052
|
10531053
1 | CREATE FUNCTION my_agg (INT) STATE { s STRING } RETURNS BOOLEAN LANGUAGE javascript HANDLER = 'my_agg' ADDRESS = 'http://0.0.0.0:8815';
1054-
| ------ - ^^^^^^^ unexpected `HANDLER`, expecting `HEADERS`, `ADDRESS`, `PACKAGES`, `AS`, or `IMPORTS`
1054+
| ------ - ^^^^^^^ unexpected `HANDLER`. it's reserved keyword, you may avoid using it, expecting `HEADERS`, `ADDRESS`, `PACKAGES`, `AS`, or `IMPORTS`
10551055
| | |
10561056
| | while parsing (<[arg_name] arg_type>, ...) STATE {<state_field>, ...} RETURNS <return_type> LANGUAGE <language> { ADDRESS=<udf_server_address> | AS <language_codes> }
10571057
| while parsing `CREATE [OR REPLACE] FUNCTION [IF NOT EXISTS] <udf_name> <udf_definition> [DESC = <description>]`
@@ -1113,7 +1113,7 @@ error:
11131113
--> SQL:1:16
11141114
|
11151115
1 | SHOW GRANTS ON task t1;
1116-
| ^^^^ unexpected `task`, expecting `MASKING`, `STAGE`, `TABLE`, `SEQUENCE`, `WAREHOUSE`, `DATABASE`, `UDF`, `CONNECTION`, or `PROCEDURE`
1116+
| ^^^^ unexpected `task`. it's reserved keyword, you may avoid using it, expecting `MASKING`, `STAGE`, `TABLE`, `SEQUENCE`, `WAREHOUSE`, `DATABASE`, `UDF`, `CONNECTION`, or `PROCEDURE`
11171117

11181118

11191119
---------- Input ----------
@@ -1235,7 +1235,7 @@ error:
12351235
--> SQL:1:44
12361236
|
12371237
1 | create PROCEDURE p1() returns table(string not null, int null) language sql comment = 'test' as $$
1238-
| ------ ----- ^^^ unexpected `not`, expecting `INT8`, `INT16`, `INT32`, `INT64`, `UINT16`, `UINT32`, `UINT64`, `INTEGER`, `FLOAT32`, `FLOAT64`, `INTERVAL`, `GEOMETRY`, `INT`, `BOOL`, `DATE`, `BLOB`, `TEXT`, `JSON`, `UINT8`, `FLOAT`, `TUPLE`, `DOUBLE`, `BITMAP`, `BINARY`, `STRING`, `VECTOR`, `BOOLEAN`, `NUMERIC`, `UNSIGNED`, `DATETIME`, `NULLABLE`, `TIMESTAMP`, `GEOGRAPHY`, `TIMESTAMP_TZ`, `TINYINT`, `LONGBLOB`, `TINYBLOB`, `STAGE_LOCATION`, `SMALLINT`, `BIGINT`, `SIGNED`, `REAL`, `DECIMAL`, `ARRAY`, `MAP`, `VARBINARY`, `MEDIUMBLOB`, `VARCHAR`, `CHAR`, `CHARACTER`, or `VARIANT`
1238+
| ------ ----- ^^^ unexpected `not`. it's reserved keyword, you may avoid using it, expecting `INT8`, `INT16`, `INT32`, `INT64`, `UINT16`, `UINT32`, `UINT64`, `INTEGER`, `FLOAT32`, `FLOAT64`, `INTERVAL`, `GEOMETRY`, `INT`, `BOOL`, `DATE`, `BLOB`, `TEXT`, `JSON`, `UINT8`, `FLOAT`, `TUPLE`, `DOUBLE`, `BITMAP`, `BINARY`, `STRING`, `VECTOR`, `BOOLEAN`, `NUMERIC`, `UNSIGNED`, `DATETIME`, `NULLABLE`, `TIMESTAMP`, `GEOGRAPHY`, `TIMESTAMP_TZ`, `TINYINT`, `LONGBLOB`, `TINYBLOB`, `STAGE_LOCATION`, `SMALLINT`, `BIGINT`, `SIGNED`, `REAL`, `DECIMAL`, `ARRAY`, `MAP`, `VARBINARY`, `MEDIUMBLOB`, `VARCHAR`, `CHAR`, `CHARACTER`, or `VARIANT`
12391239
| | |
12401240
| | while parsing TABLE(<var_name> <type_name>, ...)
12411241
| while parsing `CREATE [ OR REPLACE ] PROCEDURE <procedure_name>() RETURNS { <result_data_type> [ NOT NULL ] | TABLE(<var_name> <data_type>, ...)} LANGUAGE SQL [ COMMENT = '<string_literal>' ] AS <procedure_definition>`
@@ -1269,7 +1269,7 @@ error:
12691269
--> SQL:1:51
12701270
|
12711271
1 | copy into t1 from (select a from @data/not_exists where a = 1)
1272-
| ---- ^^^^^ unexpected `where`, expecting `IDENTIFIER`, <LiteralString>, <Ident>, `)`, or `AS`
1272+
| ---- ^^^^^ unexpected `where`. it's reserved keyword, you may avoid using it, expecting `IDENTIFIER`, <LiteralString>, <Ident>, `)`, or `AS`
12731273
| |
12741274
| while parsing `COPY
12751275
INTO { [<database_name>.]<table_name> { ( <columns> ) } }

0 commit comments

Comments
 (0)