From de78cec55bad97964d0ae2ea83f0c076f0dff4ee Mon Sep 17 00:00:00 2001 From: git-hulk Date: Wed, 5 Aug 2026 20:24:49 +0800 Subject: [PATCH] Allow CAST as a table function argument Table function arguments are parsed by parseTableArgPrimaryExpr, which had no case for CAST. It read the keyword as an ordinary function name, so CAST's own AS separator ended the argument list and numbers(CAST(1 + 1 AS UInt64)) failed with "expected ')'". Route CAST to parseColumnCastExpr, as parseColumnExpr already does. CAST is not reserved, so it also matches TokenKindIdent and the new case has to come before that one. Only the argument list of a table function reaches this parser, never the table name, so a table called cast still parses. Both CAST spellings are covered, since parseColumnCastExpr reads AS and ',' alike. Checked against clickhouse-local 26.7.1, which accepts the new statements and their formatted output. Assistant By Claude Opus 5 --- parser/parser_table.go | 6 + .../select_table_function_arg_exprs.sql | 15 + .../select_table_function_arg_exprs.sql | 6 + ...t_table_function_arg_exprs.sql.golden.json | 272 ++++++++++++++++++ .../query/select_table_function_arg_exprs.sql | 3 + 5 files changed, 302 insertions(+) diff --git a/parser/parser_table.go b/parser/parser_table.go index 10d3f8d..a29fd45 100644 --- a/parser/parser_table.go +++ b/parser/parser_table.go @@ -760,6 +760,12 @@ func (p *Parser) parseTableArgExpr(pos Pos) (Expr, error) { func (p *Parser) parseTableArgPrimaryExpr(pos Pos) (Expr, error) { switch { + case p.matchKeyword(KeywordCast): + // CAST has its own syntax, so reading it as an ordinary function call + // stops at the AS separator: numbers(CAST(1 + 1 AS UInt64)) failed + // looking for ')'. CAST is not reserved, so it also matches + // TokenKindIdent and this case has to come first. + return p.parseColumnCastExpr(pos) case p.matchTokenKind(TokenKindIdent): ident, err := p.parseIdent() if err != nil { diff --git a/parser/testdata/query/format/beautify/select_table_function_arg_exprs.sql b/parser/testdata/query/format/beautify/select_table_function_arg_exprs.sql index 1c60242..1f145e0 100644 --- a/parser/testdata/query/format/beautify/select_table_function_arg_exprs.sql +++ b/parser/testdata/query/format/beautify/select_table_function_arg_exprs.sql @@ -10,6 +10,9 @@ SELECT * FROM numbers((1 + 1)); SELECT * FROM numbers((a + b) * c); SELECT * FROM numbers((1)); SELECT * FROM remote('127.0.0.1', (SELECT 1)); +SELECT * FROM numbers(CAST(1 + 1 AS UInt64)); +SELECT * FROM numbers(CAST('10', 'UInt64')); +SELECT * FROM cluster('c', numbers(CAST(1 AS UInt64))); -- Beautify SQL: @@ -60,3 +63,15 @@ SELECT FROM remote('127.0.0.1', (SELECT 1)); +SELECT + * +FROM + numbers(CAST(1 + 1 AS UInt64)); +SELECT + * +FROM + numbers(CAST('10', 'UInt64')); +SELECT + * +FROM + cluster('c', numbers(CAST(1 AS UInt64))); diff --git a/parser/testdata/query/format/select_table_function_arg_exprs.sql b/parser/testdata/query/format/select_table_function_arg_exprs.sql index aad77e8..1ffa966 100644 --- a/parser/testdata/query/format/select_table_function_arg_exprs.sql +++ b/parser/testdata/query/format/select_table_function_arg_exprs.sql @@ -10,6 +10,9 @@ SELECT * FROM numbers((1 + 1)); SELECT * FROM numbers((a + b) * c); SELECT * FROM numbers((1)); SELECT * FROM remote('127.0.0.1', (SELECT 1)); +SELECT * FROM numbers(CAST(1 + 1 AS UInt64)); +SELECT * FROM numbers(CAST('10', 'UInt64')); +SELECT * FROM cluster('c', numbers(CAST(1 AS UInt64))); -- Format SQL: @@ -24,3 +27,6 @@ SELECT * FROM numbers((1 + 1)); SELECT * FROM numbers((a + b) * c); SELECT * FROM numbers((1)); SELECT * FROM remote('127.0.0.1', (SELECT 1)); +SELECT * FROM numbers(CAST(1 + 1 AS UInt64)); +SELECT * FROM numbers(CAST('10', 'UInt64')); +SELECT * FROM cluster('c', numbers(CAST(1 AS UInt64))); diff --git a/parser/testdata/query/output/select_table_function_arg_exprs.sql.golden.json b/parser/testdata/query/output/select_table_function_arg_exprs.sql.golden.json index 3307726..e27babc 100644 --- a/parser/testdata/query/output/select_table_function_arg_exprs.sql.golden.json +++ b/parser/testdata/query/output/select_table_function_arg_exprs.sql.golden.json @@ -994,5 +994,277 @@ "UnionDistinct": null, "Except": null, "Intersect": null + }, + { + "SelectPos": 391, + "StatementEnd": 434, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 398, + "NameEnd": 398 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 400, + "Expr": { + "Table": { + "TablePos": 405, + "TableEnd": 434, + "Alias": null, + "Expr": { + "Name": { + "Name": "numbers", + "QuoteType": 1, + "NamePos": 405, + "NameEnd": 412 + }, + "Args": { + "LeftParenPos": 412, + "RightParenPos": 434, + "Args": [ + { + "CastPos": 413, + "Expr": { + "LeftExpr": { + "NumPos": 418, + "NumEnd": 419, + "Literal": "1", + "Base": 10 + }, + "Operation": "+", + "RightExpr": { + "NumPos": 422, + "NumEnd": 423, + "Literal": "1", + "Base": 10 + }, + "HasGlobal": false, + "HasNot": false + }, + "Separator": "AS", + "AsPos": 424, + "AsType": { + "Name": { + "Name": "UInt64", + "QuoteType": 1, + "NamePos": 427, + "NameEnd": 433 + } + } + } + ] + } + }, + "HasFinal": false + }, + "StatementEnd": 434, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 437, + "StatementEnd": 479, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 444, + "NameEnd": 444 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 446, + "Expr": { + "Table": { + "TablePos": 451, + "TableEnd": 479, + "Alias": null, + "Expr": { + "Name": { + "Name": "numbers", + "QuoteType": 1, + "NamePos": 451, + "NameEnd": 458 + }, + "Args": { + "LeftParenPos": 458, + "RightParenPos": 479, + "Args": [ + { + "CastPos": 459, + "Expr": { + "LiteralPos": 465, + "LiteralEnd": 467, + "Literal": "10" + }, + "Separator": ",", + "AsPos": 468, + "AsType": { + "LiteralPos": 471, + "LiteralEnd": 477, + "Literal": "UInt64" + } + } + ] + } + }, + "HasFinal": false + }, + "StatementEnd": 479, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null + }, + { + "SelectPos": 482, + "StatementEnd": 535, + "With": null, + "Top": null, + "HasDistinct": false, + "DistinctOn": null, + "SelectItems": [ + { + "Expr": { + "Name": "*", + "QuoteType": 0, + "NamePos": 489, + "NameEnd": 489 + }, + "Modifiers": [], + "Alias": null + } + ], + "From": { + "FromPos": 491, + "Expr": { + "Table": { + "TablePos": 496, + "TableEnd": 535, + "Alias": null, + "Expr": { + "Name": { + "Name": "cluster", + "QuoteType": 1, + "NamePos": 496, + "NameEnd": 503 + }, + "Args": { + "LeftParenPos": 503, + "RightParenPos": 535, + "Args": [ + { + "LiteralPos": 505, + "LiteralEnd": 506, + "Literal": "c" + }, + { + "Name": { + "Name": "numbers", + "QuoteType": 1, + "NamePos": 509, + "NameEnd": 516 + }, + "Args": { + "LeftParenPos": 509, + "RightParenPos": 534, + "Args": [ + { + "CastPos": 517, + "Expr": { + "NumPos": 522, + "NumEnd": 523, + "Literal": "1", + "Base": 10 + }, + "Separator": "AS", + "AsPos": 524, + "AsType": { + "Name": { + "Name": "UInt64", + "QuoteType": 1, + "NamePos": 527, + "NameEnd": 533 + } + } + } + ] + } + } + ] + } + }, + "HasFinal": false + }, + "StatementEnd": 535, + "SampleRatio": null, + "HasFinal": false + } + }, + "Window": null, + "Prewhere": null, + "Where": null, + "GroupBy": null, + "WithTotal": false, + "Having": null, + "OrderBy": null, + "LimitBy": null, + "Limit": null, + "Settings": null, + "Format": null, + "UnionAll": null, + "UnionDistinct": null, + "Except": null, + "Intersect": null } ] \ No newline at end of file diff --git a/parser/testdata/query/select_table_function_arg_exprs.sql b/parser/testdata/query/select_table_function_arg_exprs.sql index d4e074f..cf04eaf 100644 --- a/parser/testdata/query/select_table_function_arg_exprs.sql +++ b/parser/testdata/query/select_table_function_arg_exprs.sql @@ -9,3 +9,6 @@ SELECT * FROM numbers((1 + 1)); SELECT * FROM numbers((a + b) * c); SELECT * FROM numbers((1)); SELECT * FROM remote('127.0.0.1', (SELECT 1)); +SELECT * FROM numbers(CAST(1 + 1 AS UInt64)); +SELECT * FROM numbers(CAST('10', 'UInt64')); +SELECT * FROM cluster('c', numbers(CAST(1 AS UInt64)));