Skip to content

Commit

Permalink
Support path function calls in CallExpr (#232)
Browse files Browse the repository at this point in the history
  • Loading branch information
apstndb authored Dec 20, 2024
1 parent 8c62184 commit 11a7b9c
Show file tree
Hide file tree
Showing 44 changed files with 2,057 additions and 274 deletions.
2 changes: 1 addition & 1 deletion ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,7 @@ type CallExpr struct {

Rparen token.Pos // position of ")"

Func *Ident
Func *Path
Distinct bool
Args []Arg
NamedArgs []*NamedArg
Expand Down
46 changes: 34 additions & 12 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions testdata/input/query/select_function_calls_safe_prefix.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SELECT SAFE.SUBSTR('foo', 0, -2) AS safe_output UNION ALL
SELECT SAFE.SUBSTR('bar', 0, 2) AS safe_output
21 changes: 21 additions & 0 deletions testdata/input/query/select_net_functions_with_safe.sql
Original file line number Diff line number Diff line change
@@ -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:[email protected]: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"
)
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
24 changes: 16 additions & 8 deletions testdata/result/ddl/create_table.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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",
},
},
},
},
},
Expand Down
24 changes: 16 additions & 8 deletions testdata/result/ddl/create_table_for_format_test.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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",
},
},
},
},
},
Expand Down
24 changes: 16 additions & 8 deletions testdata/result/ddl/create_table_fulltext_albums.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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{
Expand Down
12 changes: 8 additions & 4 deletions testdata/result/ddl/create_table_with_sequence_function.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
12 changes: 8 additions & 4 deletions testdata/result/dml/insert_with_sequence_function.sql.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
Loading

0 comments on commit 11a7b9c

Please sign in to comment.