diff --git a/ast/ast.go b/ast/ast.go index ef35ee80..1de8aac0 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -199,6 +199,15 @@ func (NewConstructor) isExpr() {} func (BracedNewConstructor) isExpr() {} func (BracedConstructor) isExpr() {} +// SubscriptSpecifier represents specifier of subscript operators. +type SubscriptSpecifier interface { + Node + isSubscriptSpecifier() +} + +func (ExprArg) isSubscriptSpecifier() {} +func (SubscriptSpecifierKeyword) isSubscriptSpecifier() {} + // Arg represents argument of function call. type Arg interface { Node @@ -1092,17 +1101,36 @@ type SelectorExpr struct { Ident *Ident } -// IndexExpr is array item access expression node. +// IndexExpr is a subscript operator expression node. +// This node can be: +// - array subscript operator +// - struct subscript operator +// - JSON subscript operator +// Note: The name IndexExpr is a historical reason, maybe better to rename to SubscriptExpr. // -// {{.Expr | sql}}[{{if .Ordinal}}ORDINAL{{else}}OFFSET{{end}}({{.Index | sql}})] +// {{.Expr | sql}}[{{.Index | sql}}] type IndexExpr struct { // pos = Expr.pos // end = Rbrack + 1 Rbrack token.Pos // position of "]" - Ordinal bool - Expr, Index Expr + Expr Expr + Index SubscriptSpecifier +} + +// SubscriptSpecifierKeyword is subscript specifier with position keyword. +// +// {{string(.Keyword)}}({{.Expr | sql}}) +type SubscriptSpecifierKeyword struct { + // pos = KeywordPos + // end = Rparen + 1 + + KeywordPos token.Pos // position of Keyword + Rparen token.Pos // position of ")" + + Keyword PositionKeyword + Expr Expr } // CallExpr is function call expression node. diff --git a/ast/const.go b/ast/const.go index 5c564515..eff6d893 100644 --- a/ast/const.go +++ b/ast/const.go @@ -22,6 +22,15 @@ const ( LoopJoinMethod JoinMethod = "LOOP" // Undocumented, but the Spanner accept this value at least. ) +type PositionKeyword string + +const ( + PositionKeywordOffset PositionKeyword = "OFFSET" + PositionKeywordSafeOffset PositionKeyword = "SAFE_OFFSET" + PositionKeywordOrdinal PositionKeyword = "ORDINAL" + PositionKeywordSafeOrdinal PositionKeyword = "SAFE_ORDINAL" +) + type SetOp string const ( diff --git a/ast/pos.go b/ast/pos.go index e4c4f313..dd0cd882 100644 --- a/ast/pos.go +++ b/ast/pos.go @@ -382,6 +382,14 @@ func (i *IndexExpr) End() token.Pos { return posAdd(i.Rbrack, 1) } +func (s *SubscriptSpecifierKeyword) Pos() token.Pos { + return s.KeywordPos +} + +func (s *SubscriptSpecifierKeyword) End() token.Pos { + return posAdd(s.Rparen, 1) +} + func (c *CallExpr) Pos() token.Pos { return nodePos(wrapNode(c.Func)) } diff --git a/ast/sql.go b/ast/sql.go index 85db7609..12f864db 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -77,7 +77,10 @@ const ( func exprPrec(e Expr) prec { switch e := e.(type) { - case *CallExpr, *CountStarExpr, *CastExpr, *ExtractExpr, *CaseExpr, *IfExpr, *ParenExpr, *ScalarSubQuery, *ArraySubQuery, *ExistsSubQuery, *Param, *Ident, *Path, *ArrayLiteral, *TupleStructLiteral, *TypedStructLiteral, *TypelessStructLiteral, *NullLiteral, *BoolLiteral, *IntLiteral, *FloatLiteral, *StringLiteral, *BytesLiteral, *DateLiteral, *TimestampLiteral, *NumericLiteral: + case *CallExpr, *CountStarExpr, *CastExpr, *ExtractExpr, *CaseExpr, *IfExpr, *ParenExpr, *ScalarSubQuery, + *ArraySubQuery, *ExistsSubQuery, *Param, *Ident, *Path, *ArrayLiteral, *TupleStructLiteral, *TypedStructLiteral, + *TypelessStructLiteral, *NullLiteral, *BoolLiteral, *IntLiteral, *FloatLiteral, *StringLiteral, *BytesLiteral, + *DateLiteral, *TimestampLiteral, *NumericLiteral, *JSONLiteral: return precLit case *IndexExpr, *SelectorExpr: return precSelector @@ -486,14 +489,11 @@ func (s *SelectorExpr) SQL() string { func (i *IndexExpr) SQL() string { p := exprPrec(i) - sql := paren(p, i.Expr) + "[" - if i.Ordinal { - sql += "ORDINAL" - } else { - sql += "OFFSET" - } - sql += "(" + i.Index.SQL() + ")]" - return sql + return paren(p, i.Expr) + "[" + i.Index.SQL() + "]" +} + +func (s *SubscriptSpecifierKeyword) SQL() string { + return string(s.Keyword) + "(" + s.Expr.SQL() + ")" } func (c *CallExpr) SQL() string { diff --git a/parser.go b/parser.go index c8ce110a..c2a2595f 100644 --- a/parser.go +++ b/parser.go @@ -1382,24 +1382,12 @@ func (p *Parser) parseSelector() ast.Expr { } case "[": p.nextToken() - id := p.expect(token.TokenIdent) - ordinal := false - if char.EqualFold(id.AsString, "ORDINAL") { - ordinal = true - } else if char.EqualFold(id.AsString, "OFFSET") { - ordinal = false - } else { - p.panicfAtToken(id, "expected identifier: ORDINAL, OFFSET, but: %s", id.Raw) - } - p.expect("(") - index := p.parseExpr() - p.expect(")") + index := p.parseIndexSpecifier() rbrack := p.expect("]").Pos expr = &ast.IndexExpr{ - Rbrack: rbrack, - Ordinal: ordinal, - Expr: expr, - Index: index, + Rbrack: rbrack, + Expr: expr, + Index: index, } default: return expr @@ -1407,6 +1395,40 @@ func (p *Parser) parseSelector() ast.Expr { } } +func (p *Parser) parseIndexSpecifier() ast.SubscriptSpecifier { + pos := p.Token.Pos + switch { + case p.Token.IsIdent("OFFSET"), p.Token.IsIdent("ORDINAL"), + p.Token.IsIdent("SAFE_OFFSET"), p.Token.IsIdent("SAFE_ORDINAL"): + var keyword ast.PositionKeyword + switch { + case p.Token.IsIdent("OFFSET"): + keyword = ast.PositionKeywordOffset + case p.Token.IsIdent("ORDINAL"): + keyword = ast.PositionKeywordOrdinal + case p.Token.IsIdent("SAFE_OFFSET"): + keyword = ast.PositionKeywordSafeOffset + case p.Token.IsIdent("SAFE_ORDINAL"): + keyword = ast.PositionKeywordSafeOrdinal + + } + p.nextToken() + p.expect("(") + expr := p.parseExpr() + rparen := p.expect(")").Pos + + return &ast.SubscriptSpecifierKeyword{ + KeywordPos: pos, + Keyword: keyword, + Rparen: rparen, + Expr: expr, + } + default: + expr := p.parseExpr() + return &ast.ExprArg{Expr: expr} + } +} + func (p *Parser) parseLit() ast.Expr { switch p.Token.Kind { case "NULL": diff --git a/testdata/input/query/select_subscript_operators.sql b/testdata/input/query/select_subscript_operators.sql new file mode 100644 index 00000000..7736f84b --- /dev/null +++ b/testdata/input/query/select_subscript_operators.sql @@ -0,0 +1,13 @@ +select + [1, 2, 3][offset(1)], + [1, 2, 3][ordinal(1)], + [1, 2, 3][safe_offset(1)], + [1, 2, 3][ordinal(1)], + [1, 2, 3][1], + STRUCT(1, 2, 3)[offset(1)], + STRUCT(1, 2, 3)[ordinal(1)], + STRUCT(1, 2, 3)[safe_offset(1)], + STRUCT(1, 2, 3)[ordinal(1)], + STRUCT(1, 2, 3)[1], + JSON '[1, 2, 3]'[1], + JSON '{"a": 1, "b": 2, "c": 3}'['a'] diff --git a/testdata/result/query/select_expr.sql.txt b/testdata/result/query/select_expr.sql.txt index 00da7193..21b2ce8d 100644 --- a/testdata/result/query/select_expr.sql.txt +++ b/testdata/result/query/select_expr.sql.txt @@ -380,9 +380,8 @@ select 1 + 2, 1 - 2, }, &ast.ExprSelectItem{ Expr: &ast.IndexExpr{ - Rbrack: 259, - Ordinal: false, - Expr: &ast.ArrayLiteral{ + Rbrack: 259, + Expr: &ast.ArrayLiteral{ Array: -1, Lbrack: 240, Rbrack: 248, @@ -408,19 +407,23 @@ select 1 + 2, 1 - 2, }, }, }, - Index: &ast.IntLiteral{ - ValuePos: 257, - ValueEnd: 258, - Base: 10, - Value: "1", + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 250, + Rparen: 258, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 257, + ValueEnd: 258, + Base: 10, + Value: "1", + }, }, }, }, &ast.ExprSelectItem{ Expr: &ast.IndexExpr{ - Rbrack: 290, - Ordinal: false, - Expr: &ast.ArrayLiteral{ + Rbrack: 290, + Expr: &ast.ArrayLiteral{ Array: -1, Lbrack: 269, Rbrack: 277, @@ -446,19 +449,23 @@ select 1 + 2, 1 - 2, }, }, }, - Index: &ast.IntLiteral{ - ValuePos: 288, - ValueEnd: 289, - Base: 10, - Value: "1", + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 279, + Rparen: 289, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 288, + ValueEnd: 289, + Base: 10, + Value: "1", + }, }, }, }, &ast.ExprSelectItem{ Expr: &ast.IndexExpr{ - Rbrack: 320, - Ordinal: true, - Expr: &ast.ArrayLiteral{ + Rbrack: 320, + Expr: &ast.ArrayLiteral{ Array: -1, Lbrack: 300, Rbrack: 308, @@ -484,11 +491,16 @@ select 1 + 2, 1 - 2, }, }, }, - Index: &ast.IntLiteral{ - ValuePos: 318, - ValueEnd: 319, - Base: 10, - Value: "1", + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 310, + Rparen: 319, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 318, + ValueEnd: 319, + Base: 10, + Value: "1", + }, }, }, }, diff --git a/testdata/result/query/select_subscript_operators.sql.txt b/testdata/result/query/select_subscript_operators.sql.txt new file mode 100644 index 00000000..64776a1d --- /dev/null +++ b/testdata/result/query/select_subscript_operators.sql.txt @@ -0,0 +1,511 @@ +--- select_subscript_operators.sql +select + [1, 2, 3][offset(1)], + [1, 2, 3][ordinal(1)], + [1, 2, 3][safe_offset(1)], + [1, 2, 3][ordinal(1)], + [1, 2, 3][1], + STRUCT(1, 2, 3)[offset(1)], + STRUCT(1, 2, 3)[ordinal(1)], + STRUCT(1, 2, 3)[safe_offset(1)], + STRUCT(1, 2, 3)[ordinal(1)], + STRUCT(1, 2, 3)[1], + JSON '[1, 2, 3]'[1], + JSON '{"a": 1, "b": 2, "c": 3}'['a'] + +--- AST +&ast.QueryStatement{ + Hint: (*ast.Hint)(nil), + With: (*ast.With)(nil), + Query: &ast.Select{ + Select: 0, + Distinct: false, + As: nil, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 30, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 11, + Rbrack: 19, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 12, + ValueEnd: 13, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 15, + ValueEnd: 16, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 18, + ValueEnd: 19, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 21, + Rparen: 29, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 28, + ValueEnd: 29, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 57, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 37, + Rbrack: 45, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 38, + ValueEnd: 39, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 41, + ValueEnd: 42, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 44, + ValueEnd: 45, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 47, + Rparen: 56, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 55, + ValueEnd: 56, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 88, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 64, + Rbrack: 72, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 65, + ValueEnd: 66, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 68, + ValueEnd: 69, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 71, + ValueEnd: 72, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 74, + Rparen: 87, + Keyword: "SAFE_OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 86, + ValueEnd: 87, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 115, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 95, + Rbrack: 103, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 96, + ValueEnd: 97, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 99, + ValueEnd: 100, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 102, + ValueEnd: 103, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 105, + Rparen: 114, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 113, + ValueEnd: 114, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 133, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 122, + Rbrack: 130, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 123, + ValueEnd: 124, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 126, + ValueEnd: 127, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 129, + ValueEnd: 130, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 132, + ValueEnd: 133, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 165, + Expr: &ast.TypelessStructLiteral{ + Struct: 140, + Rparen: 154, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 147, + ValueEnd: 148, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 150, + ValueEnd: 151, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 153, + ValueEnd: 154, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 156, + Rparen: 164, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 163, + ValueEnd: 164, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 198, + Expr: &ast.TypelessStructLiteral{ + Struct: 172, + Rparen: 186, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 179, + ValueEnd: 180, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 182, + ValueEnd: 183, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 185, + ValueEnd: 186, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 188, + Rparen: 197, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 196, + ValueEnd: 197, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 235, + Expr: &ast.TypelessStructLiteral{ + Struct: 205, + Rparen: 219, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 212, + ValueEnd: 213, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 215, + ValueEnd: 216, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 218, + ValueEnd: 219, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 221, + Rparen: 234, + Keyword: "SAFE_OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 233, + ValueEnd: 234, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 268, + Expr: &ast.TypelessStructLiteral{ + Struct: 242, + Rparen: 256, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 249, + ValueEnd: 250, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 252, + ValueEnd: 253, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 255, + ValueEnd: 256, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 258, + Rparen: 267, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 266, + ValueEnd: 267, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 292, + Expr: &ast.TypelessStructLiteral{ + Struct: 275, + Rparen: 289, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 282, + ValueEnd: 283, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 285, + ValueEnd: 286, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 288, + ValueEnd: 289, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 291, + ValueEnd: 292, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 317, + Expr: &ast.JSONLiteral{ + JSON: 299, + Value: &ast.StringLiteral{ + ValuePos: 304, + ValueEnd: 315, + Value: "[1, 2, 3]", + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 316, + ValueEnd: 317, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 359, + Expr: &ast.JSONLiteral{ + JSON: 324, + Value: &ast.StringLiteral{ + ValuePos: 329, + ValueEnd: 355, + Value: "{\"a\": 1, \"b\": 2, \"c\": 3}", + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 356, + ValueEnd: 359, + Value: "a", + }, + }, + }, + }, + }, + From: (*ast.From)(nil), + Where: (*ast.Where)(nil), + GroupBy: (*ast.GroupBy)(nil), + Having: (*ast.Having)(nil), + OrderBy: (*ast.OrderBy)(nil), + Limit: (*ast.Limit)(nil), + }, +} + +--- SQL +SELECT [1, 2, 3][OFFSET(1)], [1, 2, 3][ORDINAL(1)], [1, 2, 3][SAFE_OFFSET(1)], [1, 2, 3][ORDINAL(1)], [1, 2, 3][1], STRUCT(1, 2, 3)[OFFSET(1)], STRUCT(1, 2, 3)[ORDINAL(1)], STRUCT(1, 2, 3)[SAFE_OFFSET(1)], STRUCT(1, 2, 3)[ORDINAL(1)], STRUCT(1, 2, 3)[1], JSON "[1, 2, 3]"[1], JSON "{\"a\": 1, \"b\": 2, \"c\": 3}"["a"] diff --git a/testdata/result/statement/select_expr.sql.txt b/testdata/result/statement/select_expr.sql.txt index 00da7193..21b2ce8d 100644 --- a/testdata/result/statement/select_expr.sql.txt +++ b/testdata/result/statement/select_expr.sql.txt @@ -380,9 +380,8 @@ select 1 + 2, 1 - 2, }, &ast.ExprSelectItem{ Expr: &ast.IndexExpr{ - Rbrack: 259, - Ordinal: false, - Expr: &ast.ArrayLiteral{ + Rbrack: 259, + Expr: &ast.ArrayLiteral{ Array: -1, Lbrack: 240, Rbrack: 248, @@ -408,19 +407,23 @@ select 1 + 2, 1 - 2, }, }, }, - Index: &ast.IntLiteral{ - ValuePos: 257, - ValueEnd: 258, - Base: 10, - Value: "1", + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 250, + Rparen: 258, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 257, + ValueEnd: 258, + Base: 10, + Value: "1", + }, }, }, }, &ast.ExprSelectItem{ Expr: &ast.IndexExpr{ - Rbrack: 290, - Ordinal: false, - Expr: &ast.ArrayLiteral{ + Rbrack: 290, + Expr: &ast.ArrayLiteral{ Array: -1, Lbrack: 269, Rbrack: 277, @@ -446,19 +449,23 @@ select 1 + 2, 1 - 2, }, }, }, - Index: &ast.IntLiteral{ - ValuePos: 288, - ValueEnd: 289, - Base: 10, - Value: "1", + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 279, + Rparen: 289, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 288, + ValueEnd: 289, + Base: 10, + Value: "1", + }, }, }, }, &ast.ExprSelectItem{ Expr: &ast.IndexExpr{ - Rbrack: 320, - Ordinal: true, - Expr: &ast.ArrayLiteral{ + Rbrack: 320, + Expr: &ast.ArrayLiteral{ Array: -1, Lbrack: 300, Rbrack: 308, @@ -484,11 +491,16 @@ select 1 + 2, 1 - 2, }, }, }, - Index: &ast.IntLiteral{ - ValuePos: 318, - ValueEnd: 319, - Base: 10, - Value: "1", + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 310, + Rparen: 319, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 318, + ValueEnd: 319, + Base: 10, + Value: "1", + }, }, }, }, diff --git a/testdata/result/statement/select_subscript_operators.sql.txt b/testdata/result/statement/select_subscript_operators.sql.txt new file mode 100644 index 00000000..64776a1d --- /dev/null +++ b/testdata/result/statement/select_subscript_operators.sql.txt @@ -0,0 +1,511 @@ +--- select_subscript_operators.sql +select + [1, 2, 3][offset(1)], + [1, 2, 3][ordinal(1)], + [1, 2, 3][safe_offset(1)], + [1, 2, 3][ordinal(1)], + [1, 2, 3][1], + STRUCT(1, 2, 3)[offset(1)], + STRUCT(1, 2, 3)[ordinal(1)], + STRUCT(1, 2, 3)[safe_offset(1)], + STRUCT(1, 2, 3)[ordinal(1)], + STRUCT(1, 2, 3)[1], + JSON '[1, 2, 3]'[1], + JSON '{"a": 1, "b": 2, "c": 3}'['a'] + +--- AST +&ast.QueryStatement{ + Hint: (*ast.Hint)(nil), + With: (*ast.With)(nil), + Query: &ast.Select{ + Select: 0, + Distinct: false, + As: nil, + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 30, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 11, + Rbrack: 19, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 12, + ValueEnd: 13, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 15, + ValueEnd: 16, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 18, + ValueEnd: 19, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 21, + Rparen: 29, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 28, + ValueEnd: 29, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 57, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 37, + Rbrack: 45, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 38, + ValueEnd: 39, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 41, + ValueEnd: 42, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 44, + ValueEnd: 45, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 47, + Rparen: 56, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 55, + ValueEnd: 56, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 88, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 64, + Rbrack: 72, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 65, + ValueEnd: 66, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 68, + ValueEnd: 69, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 71, + ValueEnd: 72, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 74, + Rparen: 87, + Keyword: "SAFE_OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 86, + ValueEnd: 87, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 115, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 95, + Rbrack: 103, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 96, + ValueEnd: 97, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 99, + ValueEnd: 100, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 102, + ValueEnd: 103, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 105, + Rparen: 114, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 113, + ValueEnd: 114, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 133, + Expr: &ast.ArrayLiteral{ + Array: -1, + Lbrack: 122, + Rbrack: 130, + Type: nil, + Values: []ast.Expr{ + &ast.IntLiteral{ + ValuePos: 123, + ValueEnd: 124, + Base: 10, + Value: "1", + }, + &ast.IntLiteral{ + ValuePos: 126, + ValueEnd: 127, + Base: 10, + Value: "2", + }, + &ast.IntLiteral{ + ValuePos: 129, + ValueEnd: 130, + Base: 10, + Value: "3", + }, + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 132, + ValueEnd: 133, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 165, + Expr: &ast.TypelessStructLiteral{ + Struct: 140, + Rparen: 154, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 147, + ValueEnd: 148, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 150, + ValueEnd: 151, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 153, + ValueEnd: 154, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 156, + Rparen: 164, + Keyword: "OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 163, + ValueEnd: 164, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 198, + Expr: &ast.TypelessStructLiteral{ + Struct: 172, + Rparen: 186, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 179, + ValueEnd: 180, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 182, + ValueEnd: 183, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 185, + ValueEnd: 186, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 188, + Rparen: 197, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 196, + ValueEnd: 197, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 235, + Expr: &ast.TypelessStructLiteral{ + Struct: 205, + Rparen: 219, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 212, + ValueEnd: 213, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 215, + ValueEnd: 216, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 218, + ValueEnd: 219, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 221, + Rparen: 234, + Keyword: "SAFE_OFFSET", + Expr: &ast.IntLiteral{ + ValuePos: 233, + ValueEnd: 234, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 268, + Expr: &ast.TypelessStructLiteral{ + Struct: 242, + Rparen: 256, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 249, + ValueEnd: 250, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 252, + ValueEnd: 253, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 255, + ValueEnd: 256, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.SubscriptSpecifierKeyword{ + KeywordPos: 258, + Rparen: 267, + Keyword: "ORDINAL", + Expr: &ast.IntLiteral{ + ValuePos: 266, + ValueEnd: 267, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 292, + Expr: &ast.TypelessStructLiteral{ + Struct: 275, + Rparen: 289, + Values: []ast.TypelessStructLiteralArg{ + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 282, + ValueEnd: 283, + Base: 10, + Value: "1", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 285, + ValueEnd: 286, + Base: 10, + Value: "2", + }, + }, + &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 288, + ValueEnd: 289, + Base: 10, + Value: "3", + }, + }, + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 291, + ValueEnd: 292, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 317, + Expr: &ast.JSONLiteral{ + JSON: 299, + Value: &ast.StringLiteral{ + ValuePos: 304, + ValueEnd: 315, + Value: "[1, 2, 3]", + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.IntLiteral{ + ValuePos: 316, + ValueEnd: 317, + Base: 10, + Value: "1", + }, + }, + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.IndexExpr{ + Rbrack: 359, + Expr: &ast.JSONLiteral{ + JSON: 324, + Value: &ast.StringLiteral{ + ValuePos: 329, + ValueEnd: 355, + Value: "{\"a\": 1, \"b\": 2, \"c\": 3}", + }, + }, + Index: &ast.ExprArg{ + Expr: &ast.StringLiteral{ + ValuePos: 356, + ValueEnd: 359, + Value: "a", + }, + }, + }, + }, + }, + From: (*ast.From)(nil), + Where: (*ast.Where)(nil), + GroupBy: (*ast.GroupBy)(nil), + Having: (*ast.Having)(nil), + OrderBy: (*ast.OrderBy)(nil), + Limit: (*ast.Limit)(nil), + }, +} + +--- SQL +SELECT [1, 2, 3][OFFSET(1)], [1, 2, 3][ORDINAL(1)], [1, 2, 3][SAFE_OFFSET(1)], [1, 2, 3][ORDINAL(1)], [1, 2, 3][1], STRUCT(1, 2, 3)[OFFSET(1)], STRUCT(1, 2, 3)[ORDINAL(1)], STRUCT(1, 2, 3)[SAFE_OFFSET(1)], STRUCT(1, 2, 3)[ORDINAL(1)], STRUCT(1, 2, 3)[1], JSON "[1, 2, 3]"[1], JSON "{\"a\": 1, \"b\": 2, \"c\": 3}"["a"]