diff --git a/ast/ast.go b/ast/ast.go index 449d7d7f..61408b1d 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -1312,7 +1312,7 @@ type CallExpr struct { Rparen token.Pos // position of ")" - Func *Ident + Func *Path Distinct bool Args []Arg NamedArgs []*NamedArg diff --git a/parser.go b/parser.go index f1d2b314..912fd800 100644 --- a/parser.go +++ b/parser.go @@ -1753,10 +1753,13 @@ func (p *Parser) parseLit() ast.Expr { case id.IsKeywordLike("REPLACE_FIELDS"): return p.parseReplaceFieldsExpr() } + + if p.lookaheadCallExpr() { + return p.parseCallLike() + } + p.nextToken() switch p.Token.Kind { - case "(": - return p.parseCallLike(id) case token.TokenString: if id.IsKeywordLike("DATE") { return p.parseDateLiteral(id) @@ -1781,24 +1784,43 @@ func (p *Parser) parseLit() ast.Expr { panic(p.errorfAtToken(&p.Token, "unexpected token: %s", p.Token.Kind)) } +func (p *Parser) lookaheadCallExpr() bool { + lexer := p.Lexer.Clone() + defer func() { + p.Lexer = lexer + }() + + for { + if p.Token.Kind != token.TokenIdent { + return false + } + + p.nextToken() + switch p.Token.Kind { + case "(": + return true + case ".": + p.nextToken() + default: + return false + } + } +} + // parseCallLike parses after identifier part of function call like structures. -func (p *Parser) parseCallLike(id token.Token) ast.Expr { +func (p *Parser) parseCallLike() ast.Expr { + id := p.Token + path := p.parsePath() p.expect("(") - if id.IsIdent("COUNT") && p.Token.Kind == "*" { + if len(path.Idents) == 1 && id.IsIdent("COUNT") && p.Token.Kind == "*" { p.nextToken() rparen := p.expect(")").Pos return &ast.CountStarExpr{ - Count: id.Pos, + Count: path.Pos(), Rparen: rparen, } } - fn := &ast.Ident{ - NamePos: id.Pos, - NameEnd: id.End, - Name: id.AsString, - } - distinct := false if p.Token.Kind == "DISTINCT" { p.nextToken() @@ -1839,7 +1861,7 @@ func (p *Parser) parseCallLike(id token.Token) ast.Expr { return &ast.CallExpr{ Rparen: rparen, - Func: fn, + Func: path, Distinct: distinct, Args: args, NamedArgs: namedArgs, diff --git a/testdata/input/query/select_function_calls_safe_prefix.sql b/testdata/input/query/select_function_calls_safe_prefix.sql new file mode 100644 index 00000000..9b5fe749 --- /dev/null +++ b/testdata/input/query/select_function_calls_safe_prefix.sql @@ -0,0 +1,2 @@ +SELECT SAFE.SUBSTR('foo', 0, -2) AS safe_output UNION ALL +SELECT SAFE.SUBSTR('bar', 0, 2) AS safe_output \ No newline at end of file diff --git a/testdata/input/query/select_net_functions_with_safe.sql b/testdata/input/query/select_net_functions_with_safe.sql new file mode 100644 index 00000000..27d952a7 --- /dev/null +++ b/testdata/input/query/select_net_functions_with_safe.sql @@ -0,0 +1,21 @@ +-- original: https://cloud.google.com/spanner/docs/reference/standard-sql/net_functions#nethost +SELECT + FORMAT("%T", input) AS input, + description, + FORMAT("%T", NET.HOST(input)) AS host, + FORMAT("%T", NET.PUBLIC_SUFFIX(input)) AS suffix, + FORMAT("%T", NET.REG_DOMAIN(input)) AS domain, + FORMAT("%T", SAFE.NET.HOST(input)) AS safe_host, + FORMAT("%T", SAFE.NET.PUBLIC_SUFFIX(input)) AS safe_suffix, + FORMAT("%T", SAFE.NET.REG_DOMAIN(input)) AS safe_domain +FROM ( + SELECT "" AS input, "invalid input" AS description + UNION ALL SELECT "http://abc.xyz", "standard URL" + UNION ALL SELECT "//user:password@a.b:80/path?query", + "standard URL with relative scheme, port, path and query, but no public suffix" + UNION ALL SELECT "https://[::1]:80", "standard URL with IPv6 host" + UNION ALL SELECT "http://例子.卷筒纸.中国", "standard URL with internationalized domain name" + UNION ALL SELECT " www.Example.Co.UK ", + "non-standard URL with spaces, upper case letters, and without scheme" + UNION ALL SELECT "mailto:?to=&subject=&body=", "URI rather than URL--unsupported" +) diff --git a/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt b/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt index 84a2e1b3..b38b882c 100644 --- a/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt +++ b/testdata/result/ddl/alter_table_add_column_with_if_expression.sql.txt @@ -62,10 +62,14 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta }, TrueResult: &ast.CallExpr{ Rparen: 143, - Func: &ast.Ident{ - NamePos: 101, - NameEnd: 114, - Name: "TIMESTAMP_ADD", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 101, + NameEnd: 114, + Name: "TIMESTAMP_ADD", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/ddl/create_table.sql.txt b/testdata/result/ddl/create_table.sql.txt index 1d528823..1e3f66bf 100644 --- a/testdata/result/ddl/create_table.sql.txt +++ b/testdata/result/ddl/create_table.sql.txt @@ -116,10 +116,14 @@ create table foo ( Rparen: 170, Expr: &ast.CallExpr{ Rparen: 169, - Func: &ast.Ident{ - NamePos: 154, - NameEnd: 160, - Name: "concat", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 154, + NameEnd: 160, + Name: "concat", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -171,10 +175,14 @@ create table foo ( Rparen: 548, Expr: &ast.CallExpr{ Rparen: 547, - Func: &ast.Ident{ - NamePos: 529, - NameEnd: 546, - Name: "current_timestamp", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 529, + NameEnd: 546, + Name: "current_timestamp", + }, + }, }, }, }, diff --git a/testdata/result/ddl/create_table_for_format_test.sql.txt b/testdata/result/ddl/create_table_for_format_test.sql.txt index bba51007..e5c4f5f0 100644 --- a/testdata/result/ddl/create_table_for_format_test.sql.txt +++ b/testdata/result/ddl/create_table_for_format_test.sql.txt @@ -116,10 +116,14 @@ create table if not exists foo ( Rparen: 192, Expr: &ast.CallExpr{ Rparen: 191, - Func: &ast.Ident{ - NamePos: 176, - NameEnd: 182, - Name: "concat", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 176, + NameEnd: 182, + Name: "concat", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -158,10 +162,14 @@ create table if not exists foo ( Rparen: 441, Expr: &ast.CallExpr{ Rparen: 440, - Func: &ast.Ident{ - NamePos: 422, - NameEnd: 439, - Name: "current_timestamp", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 422, + NameEnd: 439, + Name: "current_timestamp", + }, + }, }, }, }, diff --git a/testdata/result/ddl/create_table_fulltext_albums.sql.txt b/testdata/result/ddl/create_table_fulltext_albums.sql.txt index 5182c317..d3bf3aad 100644 --- a/testdata/result/ddl/create_table_fulltext_albums.sql.txt +++ b/testdata/result/ddl/create_table_fulltext_albums.sql.txt @@ -112,10 +112,14 @@ CREATE TABLE Albums ( Rparen: 460, Expr: &ast.CallExpr{ Rparen: 459, - Func: &ast.Ident{ - NamePos: 431, - NameEnd: 448, - Name: "TOKENIZE_FULLTEXT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 431, + NameEnd: 448, + Name: "TOKENIZE_FULLTEXT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -147,10 +151,14 @@ CREATE TABLE Albums ( Rparen: 545, Expr: &ast.CallExpr{ Rparen: 544, - Func: &ast.Ident{ - NamePos: 522, - NameEnd: 537, - Name: "TOKENIZE_NUMBER", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 522, + NameEnd: 537, + Name: "TOKENIZE_NUMBER", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/ddl/create_table_with_sequence_function.sql.txt b/testdata/result/ddl/create_table_with_sequence_function.sql.txt index 3c747bb4..90b45eb5 100644 --- a/testdata/result/ddl/create_table_with_sequence_function.sql.txt +++ b/testdata/result/ddl/create_table_with_sequence_function.sql.txt @@ -35,10 +35,14 @@ CREATE TABLE foo Rparen: 88, Expr: &ast.CallExpr{ Rparen: 87, - Func: &ast.Ident{ - NamePos: 42, - NameEnd: 65, - Name: "GET_NEXT_SEQUENCE_VALUE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 42, + NameEnd: 65, + Name: "GET_NEXT_SEQUENCE_VALUE", + }, + }, }, Args: []ast.Arg{ &ast.SequenceArg{ diff --git a/testdata/result/dml/insert_with_sequence_function.sql.txt b/testdata/result/dml/insert_with_sequence_function.sql.txt index 0183ca51..dc8fe0dc 100644 --- a/testdata/result/dml/insert_with_sequence_function.sql.txt +++ b/testdata/result/dml/insert_with_sequence_function.sql.txt @@ -26,10 +26,14 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence)) DefaultPos: -1, Expr: &ast.CallExpr{ Rparen: 73, - Func: &ast.Ident{ - NamePos: 29, - NameEnd: 52, - Name: "GET_NEXT_SEQUENCE_VALUE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 52, + Name: "GET_NEXT_SEQUENCE_VALUE", + }, + }, }, Args: []ast.Arg{ &ast.SequenceArg{ diff --git a/testdata/result/expr/array_functions_array_filter_one_arg_with_paren_lambda.sql.txt b/testdata/result/expr/array_functions_array_filter_one_arg_with_paren_lambda.sql.txt index 54b635c4..8a05ea95 100644 --- a/testdata/result/expr/array_functions_array_filter_one_arg_with_paren_lambda.sql.txt +++ b/testdata/result/expr/array_functions_array_filter_one_arg_with_paren_lambda.sql.txt @@ -3,9 +3,13 @@ ARRAY_FILTER([1 ,2, 3], (e) -> e > 1) --- AST &ast.CallExpr{ Rparen: 36, - Func: &ast.Ident{ - NameEnd: 12, - Name: "ARRAY_FILTER", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NameEnd: 12, + Name: "ARRAY_FILTER", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/expr/array_functions_array_filter_parenless_lambda.sql.txt b/testdata/result/expr/array_functions_array_filter_parenless_lambda.sql.txt index b03a4a55..937ab1ce 100644 --- a/testdata/result/expr/array_functions_array_filter_parenless_lambda.sql.txt +++ b/testdata/result/expr/array_functions_array_filter_parenless_lambda.sql.txt @@ -3,9 +3,13 @@ ARRAY_FILTER([1 ,2, 3], e -> e > 1) --- AST &ast.CallExpr{ Rparen: 34, - Func: &ast.Ident{ - NameEnd: 12, - Name: "ARRAY_FILTER", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NameEnd: 12, + Name: "ARRAY_FILTER", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/expr/array_functions_array_filter_two_args_lambda.sql.txt b/testdata/result/expr/array_functions_array_filter_two_args_lambda.sql.txt index 4f8bafb8..53f128c0 100644 --- a/testdata/result/expr/array_functions_array_filter_two_args_lambda.sql.txt +++ b/testdata/result/expr/array_functions_array_filter_two_args_lambda.sql.txt @@ -3,9 +3,13 @@ ARRAY_FILTER([0, 2, 3], (e, i) -> e > i) --- AST &ast.CallExpr{ Rparen: 39, - Func: &ast.Ident{ - NameEnd: 12, - Name: "ARRAY_FILTER", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NameEnd: 12, + Name: "ARRAY_FILTER", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/aggregate_function_calls.sql.txt b/testdata/result/query/aggregate_function_calls.sql.txt index dc06d262..cfe05a32 100644 --- a/testdata/result/query/aggregate_function_calls.sql.txt +++ b/testdata/result/query/aggregate_function_calls.sql.txt @@ -13,10 +13,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 43, - Func: &ast.Ident{ - NamePos: 11, - NameEnd: 20, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 11, + NameEnd: 20, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -40,10 +44,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 82, - Func: &ast.Ident{ - NamePos: 50, - NameEnd: 59, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 50, + NameEnd: 59, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -67,10 +75,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 118, - Func: &ast.Ident{ - NamePos: 89, - NameEnd: 98, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 89, + NameEnd: 98, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -90,10 +102,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 155, - Func: &ast.Ident{ - NamePos: 125, - NameEnd: 134, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 125, + NameEnd: 134, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -113,10 +129,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 208, - Func: &ast.Ident{ - NamePos: 162, - NameEnd: 171, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 162, + NameEnd: 171, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_call_with_named_expr.sql.txt b/testdata/result/query/select_call_with_named_expr.sql.txt index 11f82c13..134adc90 100644 --- a/testdata/result/query/select_call_with_named_expr.sql.txt +++ b/testdata/result/query/select_call_with_named_expr.sql.txt @@ -86,10 +86,14 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q }, Right: &ast.CallExpr{ Rparen: 138, - Func: &ast.Ident{ - NamePos: 71, - NameEnd: 77, - Name: "SEARCH", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 71, + NameEnd: 77, + Name: "SEARCH", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_count_distinct.sql.txt b/testdata/result/query/select_count_distinct.sql.txt index f32ac131..d5e4cc52 100644 --- a/testdata/result/query/select_count_distinct.sql.txt +++ b/testdata/result/query/select_count_distinct.sql.txt @@ -7,10 +7,14 @@ select count(distinct x) from foo &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 23, - Func: &ast.Ident{ - NamePos: 7, - NameEnd: 12, - Name: "count", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 12, + Name: "count", + }, + }, }, Distinct: true, Args: []ast.Arg{ diff --git a/testdata/result/query/select_expr.sql.txt b/testdata/result/query/select_expr.sql.txt index 817c41bf..392a6cce 100644 --- a/testdata/result/query/select_expr.sql.txt +++ b/testdata/result/query/select_expr.sql.txt @@ -580,10 +580,14 @@ select 1 + 2, 1 - 2, &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 556, - Func: &ast.Ident{ - NamePos: 514, - NameEnd: 522, - Name: "date_add", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 514, + NameEnd: 522, + Name: "date_add", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -616,10 +620,14 @@ select 1 + 2, 1 - 2, &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 628, - Func: &ast.Ident{ - NamePos: 566, - NameEnd: 579, - Name: "timestamp_add", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 566, + NameEnd: 579, + Name: "timestamp_add", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_from_ml_predict_textbison.sql.txt b/testdata/result/query/select_from_ml_predict_textbison.sql.txt index 9d2cd598..5c05bdcb 100644 --- a/testdata/result/query/select_from_ml_predict_textbison.sql.txt +++ b/testdata/result/query/select_from_ml_predict_textbison.sql.txt @@ -129,10 +129,14 @@ FROM ML.PREDICT( &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 393, - Func: &ast.Ident{ - NamePos: 174, - NameEnd: 180, - Name: "CONCAT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 174, + NameEnd: 180, + Name: "CONCAT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_function_calls_safe_prefix.sql.txt b/testdata/result/query/select_function_calls_safe_prefix.sql.txt new file mode 100644 index 00000000..f6a5f624 --- /dev/null +++ b/testdata/result/query/select_function_calls_safe_prefix.sql.txt @@ -0,0 +1,128 @@ +--- select_function_calls_safe_prefix.sql +SELECT SAFE.SUBSTR('foo', 0, -2) AS safe_output UNION ALL +SELECT SAFE.SUBSTR('bar', 0, 2) AS safe_output +--- AST +&ast.QueryStatement{ + Query: &ast.CompoundQuery{ + Op: "UNION", + AllOrDistinct: "ALL", + Queries: []ast.QueryExpr{ + &ast.Select{ + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 31, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 11, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 12, + NameEnd: 18, + Name: "SUBSTR", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 19, + ValueEnd: 24, + Value: "foo", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 26, + ValueEnd: 27, + Base: 10, + Value: "0", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 29, + ValueEnd: 31, + Base: 10, + Value: "-2", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 33, + Alias: &ast.Ident{ + NamePos: 36, + NameEnd: 47, + Name: "safe_output", + }, + }, + }, + }, + }, + &ast.Select{ + Select: 58, + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 88, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 65, + NameEnd: 69, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 70, + NameEnd: 76, + Name: "SUBSTR", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 77, + ValueEnd: 82, + Value: "bar", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 84, + ValueEnd: 85, + Base: 10, + Value: "0", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 87, + ValueEnd: 88, + Base: 10, + Value: "2", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 90, + Alias: &ast.Ident{ + NamePos: 93, + NameEnd: 104, + Name: "safe_output", + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +SELECT SAFE.SUBSTR("foo", 0, -2) AS safe_output UNION ALL SELECT SAFE.SUBSTR("bar", 0, 2) AS safe_output diff --git a/testdata/result/query/select_function_hints.sql.txt b/testdata/result/query/select_function_hints.sql.txt index 0aeff394..75ab03be 100644 --- a/testdata/result/query/select_function_hints.sql.txt +++ b/testdata/result/query/select_function_hints.sql.txt @@ -12,10 +12,14 @@ FROM (SELECT SHA512(z) @{DISABLE_INLINE = TRUE} AS x FROM t) &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 143, - Func: &ast.Ident{ - NamePos: 110, - NameEnd: 119, - Name: "SUBSTRING", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 110, + NameEnd: 119, + Name: "SUBSTRING", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -63,10 +67,14 @@ FROM (SELECT SHA512(z) @{DISABLE_INLINE = TRUE} AS x FROM t) &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 188, - Func: &ast.Ident{ - NamePos: 155, - NameEnd: 164, - Name: "SUBSTRING", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 155, + NameEnd: 164, + Name: "SUBSTRING", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -123,10 +131,14 @@ FROM (SELECT SHA512(z) @{DISABLE_INLINE = TRUE} AS x FROM t) &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 216, - Func: &ast.Ident{ - NamePos: 208, - NameEnd: 214, - Name: "SHA512", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 208, + NameEnd: 214, + Name: "SHA512", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_literals_all.sql.txt b/testdata/result/query/select_literals_all.sql.txt index 2e488079..3b6bc49d 100644 --- a/testdata/result/query/select_literals_all.sql.txt +++ b/testdata/result/query/select_literals_all.sql.txt @@ -466,10 +466,14 @@ lines''', &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 552, - Func: &ast.Ident{ - NamePos: 535, - NameEnd: 539, - Name: "DATE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 535, + NameEnd: 539, + Name: "DATE", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -515,10 +519,14 @@ lines''', &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 703, - Func: &ast.Ident{ - NamePos: 681, - NameEnd: 690, - Name: "TIMESTAMP", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 681, + NameEnd: 690, + Name: "TIMESTAMP", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_literals_date.sql.txt b/testdata/result/query/select_literals_date.sql.txt index fafc2985..1edf3ed8 100644 --- a/testdata/result/query/select_literals_date.sql.txt +++ b/testdata/result/query/select_literals_date.sql.txt @@ -28,10 +28,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 46, - Func: &ast.Ident{ - NamePos: 29, - NameEnd: 33, - Name: "DATE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 33, + Name: "DATE", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -87,10 +91,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 219, - Func: &ast.Ident{ - NamePos: 197, - NameEnd: 206, - Name: "TIMESTAMP", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 197, + NameEnd: 206, + Name: "TIMESTAMP", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -116,10 +124,14 @@ SELECT &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 268, - Func: &ast.Ident{ - NamePos: 251, - NameEnd: 255, - Name: "DATE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 251, + NameEnd: 255, + Name: "DATE", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -143,10 +155,14 @@ SELECT &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 302, - Func: &ast.Ident{ - NamePos: 280, - NameEnd: 289, - Name: "TIMESTAMP", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 280, + NameEnd: 289, + Name: "TIMESTAMP", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_net_functions_with_safe.sql.txt b/testdata/result/query/select_net_functions_with_safe.sql.txt new file mode 100644 index 00000000..a7b5d024 --- /dev/null +++ b/testdata/result/query/select_net_functions_with_safe.sql.txt @@ -0,0 +1,609 @@ +--- select_net_functions_with_safe.sql +-- original: https://cloud.google.com/spanner/docs/reference/standard-sql/net_functions#nethost +SELECT + FORMAT("%T", input) AS input, + description, + FORMAT("%T", NET.HOST(input)) AS host, + FORMAT("%T", NET.PUBLIC_SUFFIX(input)) AS suffix, + FORMAT("%T", NET.REG_DOMAIN(input)) AS domain, + FORMAT("%T", SAFE.NET.HOST(input)) AS safe_host, + FORMAT("%T", SAFE.NET.PUBLIC_SUFFIX(input)) AS safe_suffix, + FORMAT("%T", SAFE.NET.REG_DOMAIN(input)) AS safe_domain +FROM ( + SELECT "" AS input, "invalid input" AS description + UNION ALL SELECT "http://abc.xyz", "standard URL" + UNION ALL SELECT "//user:password@a.b:80/path?query", + "standard URL with relative scheme, port, path and query, but no public suffix" + UNION ALL SELECT "https://[::1]:80", "standard URL with IPv6 host" + UNION ALL SELECT "http://例子.卷筒纸.中国", "standard URL with internationalized domain name" + UNION ALL SELECT " www.Example.Co.UK ", + "non-standard URL with spaces, upper case letters, and without scheme" + UNION ALL SELECT "mailto:?to=&subject=&body=", "URI rather than URL--unsupported" +) + +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Select: 96, + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 123, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 105, + NameEnd: 111, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 112, + ValueEnd: 116, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 118, + NameEnd: 123, + Name: "input", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 125, + Alias: &ast.Ident{ + NamePos: 128, + NameEnd: 133, + Name: "input", + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 137, + NameEnd: 148, + Name: "description", + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 180, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 152, + NameEnd: 158, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 159, + ValueEnd: 163, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 179, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 165, + NameEnd: 168, + Name: "NET", + }, + &ast.Ident{ + NamePos: 169, + NameEnd: 173, + Name: "HOST", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 174, + NameEnd: 179, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 182, + Alias: &ast.Ident{ + NamePos: 185, + NameEnd: 189, + Name: "host", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 230, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 193, + NameEnd: 199, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 200, + ValueEnd: 204, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 229, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 206, + NameEnd: 209, + Name: "NET", + }, + &ast.Ident{ + NamePos: 210, + NameEnd: 223, + Name: "PUBLIC_SUFFIX", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 224, + NameEnd: 229, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 232, + Alias: &ast.Ident{ + NamePos: 235, + NameEnd: 241, + Name: "suffix", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 279, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 245, + NameEnd: 251, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 252, + ValueEnd: 256, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 278, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 258, + NameEnd: 261, + Name: "NET", + }, + &ast.Ident{ + NamePos: 262, + NameEnd: 272, + Name: "REG_DOMAIN", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 273, + NameEnd: 278, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 281, + Alias: &ast.Ident{ + NamePos: 284, + NameEnd: 290, + Name: "domain", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 327, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 294, + NameEnd: 300, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 301, + ValueEnd: 305, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 326, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 307, + NameEnd: 311, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 312, + NameEnd: 315, + Name: "NET", + }, + &ast.Ident{ + NamePos: 316, + NameEnd: 320, + Name: "HOST", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 321, + NameEnd: 326, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 329, + Alias: &ast.Ident{ + NamePos: 332, + NameEnd: 341, + Name: "safe_host", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 387, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 345, + NameEnd: 351, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 352, + ValueEnd: 356, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 386, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 358, + NameEnd: 362, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 363, + NameEnd: 366, + Name: "NET", + }, + &ast.Ident{ + NamePos: 367, + NameEnd: 380, + Name: "PUBLIC_SUFFIX", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 381, + NameEnd: 386, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 389, + Alias: &ast.Ident{ + NamePos: 392, + NameEnd: 403, + Name: "safe_suffix", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 446, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 407, + NameEnd: 413, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 414, + ValueEnd: 418, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 445, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 420, + NameEnd: 424, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 425, + NameEnd: 428, + Name: "NET", + }, + &ast.Ident{ + NamePos: 429, + NameEnd: 439, + Name: "REG_DOMAIN", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 440, + NameEnd: 445, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 448, + Alias: &ast.Ident{ + NamePos: 451, + NameEnd: 462, + Name: "safe_domain", + }, + }, + }, + }, + From: &ast.From{ + From: 463, + Source: &ast.SubQueryTableExpr{ + Lparen: 468, + Rparen: 1108, + Query: &ast.CompoundQuery{ + Op: "UNION", + AllOrDistinct: "ALL", + Queries: []ast.QueryExpr{ + &ast.Select{ + Select: 474, + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.StringLiteral{ + ValuePos: 481, + ValueEnd: 483, + }, + As: &ast.AsAlias{ + As: 484, + Alias: &ast.Ident{ + NamePos: 487, + NameEnd: 492, + Name: "input", + }, + }, + }, + &ast.Alias{ + Expr: &ast.StringLiteral{ + ValuePos: 494, + ValueEnd: 509, + Value: "invalid input", + }, + As: &ast.AsAlias{ + As: 510, + Alias: &ast.Ident{ + NamePos: 513, + NameEnd: 524, + Name: "description", + }, + }, + }, + }, + }, + &ast.Select{ + Select: 539, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 546, + ValueEnd: 562, + Value: "http://abc.xyz", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 564, + ValueEnd: 578, + Value: "standard URL", + }, + }, + }, + }, + &ast.Select{ + Select: 593, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 600, + ValueEnd: 635, + Value: "//user:password@a.b:80/path?query", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 641, + ValueEnd: 720, + Value: "standard URL with relative scheme, port, path and query, but no public suffix", + }, + }, + }, + }, + &ast.Select{ + Select: 735, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 742, + ValueEnd: 760, + Value: "https://[::1]:80", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 762, + ValueEnd: 791, + Value: "standard URL with IPv6 host", + }, + }, + }, + }, + &ast.Select{ + Select: 806, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 813, + ValueEnd: 845, + Value: "http://例子.卷筒纸.中国", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 847, + ValueEnd: 896, + Value: "standard URL with internationalized domain name", + }, + }, + }, + }, + &ast.Select{ + Select: 911, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 918, + ValueEnd: 945, + Value: " www.Example.Co.UK ", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 951, + ValueEnd: 1021, + Value: "non-standard URL with spaces, upper case letters, and without scheme", + }, + }, + }, + }, + &ast.Select{ + Select: 1036, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 1043, + ValueEnd: 1071, + Value: "mailto:?to=&subject=&body=", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 1073, + ValueEnd: 1107, + Value: "URI rather than URL--unsupported", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +SELECT FORMAT("%T", input) AS input, description, FORMAT("%T", NET.HOST(input)) AS host, FORMAT("%T", NET.PUBLIC_SUFFIX(input)) AS suffix, FORMAT("%T", NET.REG_DOMAIN(input)) AS domain, FORMAT("%T", SAFE.NET.HOST(input)) AS safe_host, FORMAT("%T", SAFE.NET.PUBLIC_SUFFIX(input)) AS safe_suffix, FORMAT("%T", SAFE.NET.REG_DOMAIN(input)) AS safe_domain FROM (SELECT "" AS input, "invalid input" AS description UNION ALL SELECT "http://abc.xyz", "standard URL" UNION ALL SELECT "//user:password@a.b:80/path\?query", "standard URL with relative scheme, port, path and query, but no public suffix" UNION ALL SELECT "https://[::1]:80", "standard URL with IPv6 host" UNION ALL SELECT "http://例子.卷筒纸.中国", "standard URL with internationalized domain name" UNION ALL SELECT " www.Example.Co.UK ", "non-standard URL with spaces, upper case letters, and without scheme" UNION ALL SELECT "mailto:\?to=&subject=&body=", "URI rather than URL--unsupported") diff --git a/testdata/result/query/select_with.sql.txt b/testdata/result/query/select_with.sql.txt index 58348b5b..6cae9dff 100644 --- a/testdata/result/query/select_with.sql.txt +++ b/testdata/result/query/select_with.sql.txt @@ -34,10 +34,14 @@ SELECT WITH(a AS '123', -- a is '123' }, Expr: &ast.CallExpr{ Rparen: 158, - Func: &ast.Ident{ - NamePos: 143, - NameEnd: 149, - Name: "CONCAT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 143, + NameEnd: 149, + Name: "CONCAT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -72,10 +76,14 @@ SELECT WITH(a AS '123', -- a is '123' }, Expr: &ast.CallExpr{ Rparen: 240, - Func: &ast.Ident{ - NamePos: 229, - NameEnd: 235, - Name: "CONCAT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 229, + NameEnd: 235, + Name: "CONCAT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/query/select_with_sequence_function.sql.txt b/testdata/result/query/select_with_sequence_function.sql.txt index c5a07b7a..e5d565a4 100644 --- a/testdata/result/query/select_with_sequence_function.sql.txt +++ b/testdata/result/query/select_with_sequence_function.sql.txt @@ -8,10 +8,14 @@ SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence) as next_id &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 51, - Func: &ast.Ident{ - NamePos: 7, - NameEnd: 30, - Name: "GET_NEXT_SEQUENCE_VALUE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 30, + Name: "GET_NEXT_SEQUENCE_VALUE", + }, + }, }, Args: []ast.Arg{ &ast.SequenceArg{ diff --git a/testdata/result/statement/aggregate_function_calls.sql.txt b/testdata/result/statement/aggregate_function_calls.sql.txt index dc06d262..cfe05a32 100644 --- a/testdata/result/statement/aggregate_function_calls.sql.txt +++ b/testdata/result/statement/aggregate_function_calls.sql.txt @@ -13,10 +13,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 43, - Func: &ast.Ident{ - NamePos: 11, - NameEnd: 20, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 11, + NameEnd: 20, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -40,10 +44,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 82, - Func: &ast.Ident{ - NamePos: 50, - NameEnd: 59, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 50, + NameEnd: 59, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -67,10 +75,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 118, - Func: &ast.Ident{ - NamePos: 89, - NameEnd: 98, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 89, + NameEnd: 98, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -90,10 +102,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 155, - Func: &ast.Ident{ - NamePos: 125, - NameEnd: 134, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 125, + NameEnd: 134, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -113,10 +129,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 208, - Func: &ast.Ident{ - NamePos: 162, - NameEnd: 171, - Name: "ARRAY_AGG", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 162, + NameEnd: 171, + Name: "ARRAY_AGG", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt b/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt index 84a2e1b3..b38b882c 100644 --- a/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt +++ b/testdata/result/statement/alter_table_add_column_with_if_expression.sql.txt @@ -62,10 +62,14 @@ ALTER TABLE foo ADD COLUMN expired_at TIMESTAMP AS (IF (status != "OPEN" AND sta }, TrueResult: &ast.CallExpr{ Rparen: 143, - Func: &ast.Ident{ - NamePos: 101, - NameEnd: 114, - Name: "TIMESTAMP_ADD", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 101, + NameEnd: 114, + Name: "TIMESTAMP_ADD", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/call_complex_args.sql.txt b/testdata/result/statement/call_complex_args.sql.txt index 929d3d61..f8f6de9c 100644 --- a/testdata/result/statement/call_complex_args.sql.txt +++ b/testdata/result/statement/call_complex_args.sql.txt @@ -68,10 +68,14 @@ call myprocedure(TABLE my.table, (SELECT * FROM my.another_table), mytvf(1, 2)) &ast.ExprArg{ Expr: &ast.CallExpr{ Rparen: 206, - Func: &ast.Ident{ - NamePos: 196, - NameEnd: 201, - Name: "mytvf", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 196, + NameEnd: 201, + Name: "mytvf", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/create_table.sql.txt b/testdata/result/statement/create_table.sql.txt index 1d528823..1e3f66bf 100644 --- a/testdata/result/statement/create_table.sql.txt +++ b/testdata/result/statement/create_table.sql.txt @@ -116,10 +116,14 @@ create table foo ( Rparen: 170, Expr: &ast.CallExpr{ Rparen: 169, - Func: &ast.Ident{ - NamePos: 154, - NameEnd: 160, - Name: "concat", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 154, + NameEnd: 160, + Name: "concat", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -171,10 +175,14 @@ create table foo ( Rparen: 548, Expr: &ast.CallExpr{ Rparen: 547, - Func: &ast.Ident{ - NamePos: 529, - NameEnd: 546, - Name: "current_timestamp", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 529, + NameEnd: 546, + Name: "current_timestamp", + }, + }, }, }, }, diff --git a/testdata/result/statement/create_table_for_format_test.sql.txt b/testdata/result/statement/create_table_for_format_test.sql.txt index bba51007..e5c4f5f0 100644 --- a/testdata/result/statement/create_table_for_format_test.sql.txt +++ b/testdata/result/statement/create_table_for_format_test.sql.txt @@ -116,10 +116,14 @@ create table if not exists foo ( Rparen: 192, Expr: &ast.CallExpr{ Rparen: 191, - Func: &ast.Ident{ - NamePos: 176, - NameEnd: 182, - Name: "concat", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 176, + NameEnd: 182, + Name: "concat", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -158,10 +162,14 @@ create table if not exists foo ( Rparen: 441, Expr: &ast.CallExpr{ Rparen: 440, - Func: &ast.Ident{ - NamePos: 422, - NameEnd: 439, - Name: "current_timestamp", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 422, + NameEnd: 439, + Name: "current_timestamp", + }, + }, }, }, }, diff --git a/testdata/result/statement/create_table_fulltext_albums.sql.txt b/testdata/result/statement/create_table_fulltext_albums.sql.txt index 5182c317..d3bf3aad 100644 --- a/testdata/result/statement/create_table_fulltext_albums.sql.txt +++ b/testdata/result/statement/create_table_fulltext_albums.sql.txt @@ -112,10 +112,14 @@ CREATE TABLE Albums ( Rparen: 460, Expr: &ast.CallExpr{ Rparen: 459, - Func: &ast.Ident{ - NamePos: 431, - NameEnd: 448, - Name: "TOKENIZE_FULLTEXT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 431, + NameEnd: 448, + Name: "TOKENIZE_FULLTEXT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -147,10 +151,14 @@ CREATE TABLE Albums ( Rparen: 545, Expr: &ast.CallExpr{ Rparen: 544, - Func: &ast.Ident{ - NamePos: 522, - NameEnd: 537, - Name: "TOKENIZE_NUMBER", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 522, + NameEnd: 537, + Name: "TOKENIZE_NUMBER", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/create_table_with_sequence_function.sql.txt b/testdata/result/statement/create_table_with_sequence_function.sql.txt index 3c747bb4..90b45eb5 100644 --- a/testdata/result/statement/create_table_with_sequence_function.sql.txt +++ b/testdata/result/statement/create_table_with_sequence_function.sql.txt @@ -35,10 +35,14 @@ CREATE TABLE foo Rparen: 88, Expr: &ast.CallExpr{ Rparen: 87, - Func: &ast.Ident{ - NamePos: 42, - NameEnd: 65, - Name: "GET_NEXT_SEQUENCE_VALUE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 42, + NameEnd: 65, + Name: "GET_NEXT_SEQUENCE_VALUE", + }, + }, }, Args: []ast.Arg{ &ast.SequenceArg{ diff --git a/testdata/result/statement/insert_with_sequence_function.sql.txt b/testdata/result/statement/insert_with_sequence_function.sql.txt index 0183ca51..dc8fe0dc 100644 --- a/testdata/result/statement/insert_with_sequence_function.sql.txt +++ b/testdata/result/statement/insert_with_sequence_function.sql.txt @@ -26,10 +26,14 @@ INSERT INTO foo(bar) VALUES (GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence)) DefaultPos: -1, Expr: &ast.CallExpr{ Rparen: 73, - Func: &ast.Ident{ - NamePos: 29, - NameEnd: 52, - Name: "GET_NEXT_SEQUENCE_VALUE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 52, + Name: "GET_NEXT_SEQUENCE_VALUE", + }, + }, }, Args: []ast.Arg{ &ast.SequenceArg{ diff --git a/testdata/result/statement/select_call_with_named_expr.sql.txt b/testdata/result/statement/select_call_with_named_expr.sql.txt index 11f82c13..134adc90 100644 --- a/testdata/result/statement/select_call_with_named_expr.sql.txt +++ b/testdata/result/statement/select_call_with_named_expr.sql.txt @@ -86,10 +86,14 @@ WHERE a.SingerId = 1 AND SEARCH(a.DescriptionTokens, 'classic albums', enhance_q }, Right: &ast.CallExpr{ Rparen: 138, - Func: &ast.Ident{ - NamePos: 71, - NameEnd: 77, - Name: "SEARCH", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 71, + NameEnd: 77, + Name: "SEARCH", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_count_distinct.sql.txt b/testdata/result/statement/select_count_distinct.sql.txt index f32ac131..d5e4cc52 100644 --- a/testdata/result/statement/select_count_distinct.sql.txt +++ b/testdata/result/statement/select_count_distinct.sql.txt @@ -7,10 +7,14 @@ select count(distinct x) from foo &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 23, - Func: &ast.Ident{ - NamePos: 7, - NameEnd: 12, - Name: "count", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 12, + Name: "count", + }, + }, }, Distinct: true, Args: []ast.Arg{ diff --git a/testdata/result/statement/select_expr.sql.txt b/testdata/result/statement/select_expr.sql.txt index 817c41bf..392a6cce 100644 --- a/testdata/result/statement/select_expr.sql.txt +++ b/testdata/result/statement/select_expr.sql.txt @@ -580,10 +580,14 @@ select 1 + 2, 1 - 2, &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 556, - Func: &ast.Ident{ - NamePos: 514, - NameEnd: 522, - Name: "date_add", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 514, + NameEnd: 522, + Name: "date_add", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -616,10 +620,14 @@ select 1 + 2, 1 - 2, &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 628, - Func: &ast.Ident{ - NamePos: 566, - NameEnd: 579, - Name: "timestamp_add", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 566, + NameEnd: 579, + Name: "timestamp_add", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_from_ml_predict_textbison.sql.txt b/testdata/result/statement/select_from_ml_predict_textbison.sql.txt index 9d2cd598..5c05bdcb 100644 --- a/testdata/result/statement/select_from_ml_predict_textbison.sql.txt +++ b/testdata/result/statement/select_from_ml_predict_textbison.sql.txt @@ -129,10 +129,14 @@ FROM ML.PREDICT( &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 393, - Func: &ast.Ident{ - NamePos: 174, - NameEnd: 180, - Name: "CONCAT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 174, + NameEnd: 180, + Name: "CONCAT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_function_calls_safe_prefix.sql.txt b/testdata/result/statement/select_function_calls_safe_prefix.sql.txt new file mode 100644 index 00000000..f6a5f624 --- /dev/null +++ b/testdata/result/statement/select_function_calls_safe_prefix.sql.txt @@ -0,0 +1,128 @@ +--- select_function_calls_safe_prefix.sql +SELECT SAFE.SUBSTR('foo', 0, -2) AS safe_output UNION ALL +SELECT SAFE.SUBSTR('bar', 0, 2) AS safe_output +--- AST +&ast.QueryStatement{ + Query: &ast.CompoundQuery{ + Op: "UNION", + AllOrDistinct: "ALL", + Queries: []ast.QueryExpr{ + &ast.Select{ + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 31, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 11, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 12, + NameEnd: 18, + Name: "SUBSTR", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 19, + ValueEnd: 24, + Value: "foo", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 26, + ValueEnd: 27, + Base: 10, + Value: "0", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 29, + ValueEnd: 31, + Base: 10, + Value: "-2", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 33, + Alias: &ast.Ident{ + NamePos: 36, + NameEnd: 47, + Name: "safe_output", + }, + }, + }, + }, + }, + &ast.Select{ + Select: 58, + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 88, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 65, + NameEnd: 69, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 70, + NameEnd: 76, + Name: "SUBSTR", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 77, + ValueEnd: 82, + Value: "bar", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 84, + ValueEnd: 85, + Base: 10, + Value: "0", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 87, + ValueEnd: 88, + Base: 10, + Value: "2", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 90, + Alias: &ast.Ident{ + NamePos: 93, + NameEnd: 104, + Name: "safe_output", + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +SELECT SAFE.SUBSTR("foo", 0, -2) AS safe_output UNION ALL SELECT SAFE.SUBSTR("bar", 0, 2) AS safe_output diff --git a/testdata/result/statement/select_function_hints.sql.txt b/testdata/result/statement/select_function_hints.sql.txt index 0aeff394..75ab03be 100644 --- a/testdata/result/statement/select_function_hints.sql.txt +++ b/testdata/result/statement/select_function_hints.sql.txt @@ -12,10 +12,14 @@ FROM (SELECT SHA512(z) @{DISABLE_INLINE = TRUE} AS x FROM t) &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 143, - Func: &ast.Ident{ - NamePos: 110, - NameEnd: 119, - Name: "SUBSTRING", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 110, + NameEnd: 119, + Name: "SUBSTRING", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -63,10 +67,14 @@ FROM (SELECT SHA512(z) @{DISABLE_INLINE = TRUE} AS x FROM t) &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 188, - Func: &ast.Ident{ - NamePos: 155, - NameEnd: 164, - Name: "SUBSTRING", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 155, + NameEnd: 164, + Name: "SUBSTRING", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -123,10 +131,14 @@ FROM (SELECT SHA512(z) @{DISABLE_INLINE = TRUE} AS x FROM t) &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 216, - Func: &ast.Ident{ - NamePos: 208, - NameEnd: 214, - Name: "SHA512", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 208, + NameEnd: 214, + Name: "SHA512", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_literals_all.sql.txt b/testdata/result/statement/select_literals_all.sql.txt index 2e488079..3b6bc49d 100644 --- a/testdata/result/statement/select_literals_all.sql.txt +++ b/testdata/result/statement/select_literals_all.sql.txt @@ -466,10 +466,14 @@ lines''', &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 552, - Func: &ast.Ident{ - NamePos: 535, - NameEnd: 539, - Name: "DATE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 535, + NameEnd: 539, + Name: "DATE", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -515,10 +519,14 @@ lines''', &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 703, - Func: &ast.Ident{ - NamePos: 681, - NameEnd: 690, - Name: "TIMESTAMP", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 681, + NameEnd: 690, + Name: "TIMESTAMP", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_literals_date.sql.txt b/testdata/result/statement/select_literals_date.sql.txt index fafc2985..1edf3ed8 100644 --- a/testdata/result/statement/select_literals_date.sql.txt +++ b/testdata/result/statement/select_literals_date.sql.txt @@ -28,10 +28,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 46, - Func: &ast.Ident{ - NamePos: 29, - NameEnd: 33, - Name: "DATE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 29, + NameEnd: 33, + Name: "DATE", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -87,10 +91,14 @@ SELECT &ast.ExprSelectItem{ Expr: &ast.CallExpr{ Rparen: 219, - Func: &ast.Ident{ - NamePos: 197, - NameEnd: 206, - Name: "TIMESTAMP", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 197, + NameEnd: 206, + Name: "TIMESTAMP", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -116,10 +124,14 @@ SELECT &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 268, - Func: &ast.Ident{ - NamePos: 251, - NameEnd: 255, - Name: "DATE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 251, + NameEnd: 255, + Name: "DATE", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -143,10 +155,14 @@ SELECT &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 302, - Func: &ast.Ident{ - NamePos: 280, - NameEnd: 289, - Name: "TIMESTAMP", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 280, + NameEnd: 289, + Name: "TIMESTAMP", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_net_functions_with_safe.sql.txt b/testdata/result/statement/select_net_functions_with_safe.sql.txt new file mode 100644 index 00000000..a7b5d024 --- /dev/null +++ b/testdata/result/statement/select_net_functions_with_safe.sql.txt @@ -0,0 +1,609 @@ +--- select_net_functions_with_safe.sql +-- original: https://cloud.google.com/spanner/docs/reference/standard-sql/net_functions#nethost +SELECT + FORMAT("%T", input) AS input, + description, + FORMAT("%T", NET.HOST(input)) AS host, + FORMAT("%T", NET.PUBLIC_SUFFIX(input)) AS suffix, + FORMAT("%T", NET.REG_DOMAIN(input)) AS domain, + FORMAT("%T", SAFE.NET.HOST(input)) AS safe_host, + FORMAT("%T", SAFE.NET.PUBLIC_SUFFIX(input)) AS safe_suffix, + FORMAT("%T", SAFE.NET.REG_DOMAIN(input)) AS safe_domain +FROM ( + SELECT "" AS input, "invalid input" AS description + UNION ALL SELECT "http://abc.xyz", "standard URL" + UNION ALL SELECT "//user:password@a.b:80/path?query", + "standard URL with relative scheme, port, path and query, but no public suffix" + UNION ALL SELECT "https://[::1]:80", "standard URL with IPv6 host" + UNION ALL SELECT "http://例子.卷筒纸.中国", "standard URL with internationalized domain name" + UNION ALL SELECT " www.Example.Co.UK ", + "non-standard URL with spaces, upper case letters, and without scheme" + UNION ALL SELECT "mailto:?to=&subject=&body=", "URI rather than URL--unsupported" +) + +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Select: 96, + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 123, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 105, + NameEnd: 111, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 112, + ValueEnd: 116, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 118, + NameEnd: 123, + Name: "input", + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 125, + Alias: &ast.Ident{ + NamePos: 128, + NameEnd: 133, + Name: "input", + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 137, + NameEnd: 148, + Name: "description", + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 180, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 152, + NameEnd: 158, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 159, + ValueEnd: 163, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 179, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 165, + NameEnd: 168, + Name: "NET", + }, + &ast.Ident{ + NamePos: 169, + NameEnd: 173, + Name: "HOST", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 174, + NameEnd: 179, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 182, + Alias: &ast.Ident{ + NamePos: 185, + NameEnd: 189, + Name: "host", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 230, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 193, + NameEnd: 199, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 200, + ValueEnd: 204, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 229, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 206, + NameEnd: 209, + Name: "NET", + }, + &ast.Ident{ + NamePos: 210, + NameEnd: 223, + Name: "PUBLIC_SUFFIX", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 224, + NameEnd: 229, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 232, + Alias: &ast.Ident{ + NamePos: 235, + NameEnd: 241, + Name: "suffix", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 279, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 245, + NameEnd: 251, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 252, + ValueEnd: 256, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 278, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 258, + NameEnd: 261, + Name: "NET", + }, + &ast.Ident{ + NamePos: 262, + NameEnd: 272, + Name: "REG_DOMAIN", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 273, + NameEnd: 278, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 281, + Alias: &ast.Ident{ + NamePos: 284, + NameEnd: 290, + Name: "domain", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 327, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 294, + NameEnd: 300, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 301, + ValueEnd: 305, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 326, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 307, + NameEnd: 311, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 312, + NameEnd: 315, + Name: "NET", + }, + &ast.Ident{ + NamePos: 316, + NameEnd: 320, + Name: "HOST", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 321, + NameEnd: 326, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 329, + Alias: &ast.Ident{ + NamePos: 332, + NameEnd: 341, + Name: "safe_host", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 387, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 345, + NameEnd: 351, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 352, + ValueEnd: 356, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 386, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 358, + NameEnd: 362, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 363, + NameEnd: 366, + Name: "NET", + }, + &ast.Ident{ + NamePos: 367, + NameEnd: 380, + Name: "PUBLIC_SUFFIX", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 381, + NameEnd: 386, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 389, + Alias: &ast.Ident{ + NamePos: 392, + NameEnd: 403, + Name: "safe_suffix", + }, + }, + }, + &ast.Alias{ + Expr: &ast.CallExpr{ + Rparen: 446, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 407, + NameEnd: 413, + Name: "FORMAT", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 414, + ValueEnd: 418, + Value: "%T", + }, + }, + &ast.ExprArg{ + Expr: &ast.CallExpr{ + Rparen: 445, + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 420, + NameEnd: 424, + Name: "SAFE", + }, + &ast.Ident{ + NamePos: 425, + NameEnd: 428, + Name: "NET", + }, + &ast.Ident{ + NamePos: 429, + NameEnd: 439, + Name: "REG_DOMAIN", + }, + }, + }, + Args: []ast.Arg{ + &ast.ExprArg{ + Expr: &ast.Ident{ + NamePos: 440, + NameEnd: 445, + Name: "input", + }, + }, + }, + }, + }, + }, + }, + As: &ast.AsAlias{ + As: 448, + Alias: &ast.Ident{ + NamePos: 451, + NameEnd: 462, + Name: "safe_domain", + }, + }, + }, + }, + From: &ast.From{ + From: 463, + Source: &ast.SubQueryTableExpr{ + Lparen: 468, + Rparen: 1108, + Query: &ast.CompoundQuery{ + Op: "UNION", + AllOrDistinct: "ALL", + Queries: []ast.QueryExpr{ + &ast.Select{ + Select: 474, + Results: []ast.SelectItem{ + &ast.Alias{ + Expr: &ast.StringLiteral{ + ValuePos: 481, + ValueEnd: 483, + }, + As: &ast.AsAlias{ + As: 484, + Alias: &ast.Ident{ + NamePos: 487, + NameEnd: 492, + Name: "input", + }, + }, + }, + &ast.Alias{ + Expr: &ast.StringLiteral{ + ValuePos: 494, + ValueEnd: 509, + Value: "invalid input", + }, + As: &ast.AsAlias{ + As: 510, + Alias: &ast.Ident{ + NamePos: 513, + NameEnd: 524, + Name: "description", + }, + }, + }, + }, + }, + &ast.Select{ + Select: 539, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 546, + ValueEnd: 562, + Value: "http://abc.xyz", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 564, + ValueEnd: 578, + Value: "standard URL", + }, + }, + }, + }, + &ast.Select{ + Select: 593, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 600, + ValueEnd: 635, + Value: "//user:password@a.b:80/path?query", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 641, + ValueEnd: 720, + Value: "standard URL with relative scheme, port, path and query, but no public suffix", + }, + }, + }, + }, + &ast.Select{ + Select: 735, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 742, + ValueEnd: 760, + Value: "https://[::1]:80", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 762, + ValueEnd: 791, + Value: "standard URL with IPv6 host", + }, + }, + }, + }, + &ast.Select{ + Select: 806, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 813, + ValueEnd: 845, + Value: "http://例子.卷筒纸.中国", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 847, + ValueEnd: 896, + Value: "standard URL with internationalized domain name", + }, + }, + }, + }, + &ast.Select{ + Select: 911, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 918, + ValueEnd: 945, + Value: " www.Example.Co.UK ", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 951, + ValueEnd: 1021, + Value: "non-standard URL with spaces, upper case letters, and without scheme", + }, + }, + }, + }, + &ast.Select{ + Select: 1036, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 1043, + ValueEnd: 1071, + Value: "mailto:?to=&subject=&body=", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.StringLiteral{ + ValuePos: 1073, + ValueEnd: 1107, + Value: "URI rather than URL--unsupported", + }, + }, + }, + }, + }, + }, + }, + }, + }, +} + +--- SQL +SELECT FORMAT("%T", input) AS input, description, FORMAT("%T", NET.HOST(input)) AS host, FORMAT("%T", NET.PUBLIC_SUFFIX(input)) AS suffix, FORMAT("%T", NET.REG_DOMAIN(input)) AS domain, FORMAT("%T", SAFE.NET.HOST(input)) AS safe_host, FORMAT("%T", SAFE.NET.PUBLIC_SUFFIX(input)) AS safe_suffix, FORMAT("%T", SAFE.NET.REG_DOMAIN(input)) AS safe_domain FROM (SELECT "" AS input, "invalid input" AS description UNION ALL SELECT "http://abc.xyz", "standard URL" UNION ALL SELECT "//user:password@a.b:80/path\?query", "standard URL with relative scheme, port, path and query, but no public suffix" UNION ALL SELECT "https://[::1]:80", "standard URL with IPv6 host" UNION ALL SELECT "http://例子.卷筒纸.中国", "standard URL with internationalized domain name" UNION ALL SELECT " www.Example.Co.UK ", "non-standard URL with spaces, upper case letters, and without scheme" UNION ALL SELECT "mailto:\?to=&subject=&body=", "URI rather than URL--unsupported") diff --git a/testdata/result/statement/select_with.sql.txt b/testdata/result/statement/select_with.sql.txt index 58348b5b..6cae9dff 100644 --- a/testdata/result/statement/select_with.sql.txt +++ b/testdata/result/statement/select_with.sql.txt @@ -34,10 +34,14 @@ SELECT WITH(a AS '123', -- a is '123' }, Expr: &ast.CallExpr{ Rparen: 158, - Func: &ast.Ident{ - NamePos: 143, - NameEnd: 149, - Name: "CONCAT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 143, + NameEnd: 149, + Name: "CONCAT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ @@ -72,10 +76,14 @@ SELECT WITH(a AS '123', -- a is '123' }, Expr: &ast.CallExpr{ Rparen: 240, - Func: &ast.Ident{ - NamePos: 229, - NameEnd: 235, - Name: "CONCAT", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 229, + NameEnd: 235, + Name: "CONCAT", + }, + }, }, Args: []ast.Arg{ &ast.ExprArg{ diff --git a/testdata/result/statement/select_with_sequence_function.sql.txt b/testdata/result/statement/select_with_sequence_function.sql.txt index c5a07b7a..e5d565a4 100644 --- a/testdata/result/statement/select_with_sequence_function.sql.txt +++ b/testdata/result/statement/select_with_sequence_function.sql.txt @@ -8,10 +8,14 @@ SELECT GET_NEXT_SEQUENCE_VALUE(SEQUENCE my_sequence) as next_id &ast.Alias{ Expr: &ast.CallExpr{ Rparen: 51, - Func: &ast.Ident{ - NamePos: 7, - NameEnd: 30, - Name: "GET_NEXT_SEQUENCE_VALUE", + Func: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 7, + NameEnd: 30, + Name: "GET_NEXT_SEQUENCE_VALUE", + }, + }, }, Args: []ast.Arg{ &ast.SequenceArg{