Skip to content

Commit 5e79b05

Browse files
authored
Add support of accessing the tuple with index field (#114)
1 parent 693137d commit 5e79b05

11 files changed

+781
-22
lines changed

parser/ast.go

+34
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,40 @@ func (p *BinaryOperation) Accept(visitor ASTVisitor) error {
187187
return visitor.VisitBinaryExpr(p)
188188
}
189189

190+
type IndexOperation struct {
191+
LeftExpr Expr
192+
Operation TokenKind
193+
Index Expr
194+
}
195+
196+
func (i *IndexOperation) Accept(visitor ASTVisitor) error {
197+
visitor.enter(i)
198+
defer visitor.leave(i)
199+
if err := i.LeftExpr.Accept(visitor); err != nil {
200+
return err
201+
}
202+
if err := i.Index.Accept(visitor); err != nil {
203+
return err
204+
}
205+
return visitor.VisitIndexOperation(i)
206+
}
207+
208+
func (i *IndexOperation) Pos() Pos {
209+
return i.LeftExpr.Pos()
210+
}
211+
212+
func (i *IndexOperation) End() Pos {
213+
return i.Index.End()
214+
}
215+
216+
func (i *IndexOperation) String() string {
217+
var builder strings.Builder
218+
builder.WriteString(i.LeftExpr.String())
219+
builder.WriteString(string(i.Operation))
220+
builder.WriteString(i.Index.String())
221+
return builder.String()
222+
}
223+
190224
type JoinTableExpr struct {
191225
Table *TableExpr
192226
StatementEnd Pos

parser/ast_visitor.go

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ type ASTVisitor interface {
44
VisitOperationExpr(expr *OperationExpr) error
55
VisitTernaryExpr(expr *TernaryOperation) error
66
VisitBinaryExpr(expr *BinaryOperation) error
7+
VisitIndexOperation(expr *IndexOperation) error
78
VisitAlterTable(expr *AlterTable) error
89
VisitAlterTableAttachPartition(expr *AlterTableAttachPartition) error
910
VisitAlterTableDetachPartition(expr *AlterTableDetachPartition) error
@@ -188,6 +189,13 @@ func (v *DefaultASTVisitor) VisitBinaryExpr(expr *BinaryOperation) error {
188189
return nil
189190
}
190191

192+
func (v *DefaultASTVisitor) VisitIndexOperation(expr *IndexOperation) error {
193+
if v.Visit != nil {
194+
return v.Visit(expr)
195+
}
196+
return nil
197+
}
198+
191199
func (v *DefaultASTVisitor) VisitJoinTableExpr(expr *JoinTableExpr) error {
192200
if v.Visit != nil {
193201
return v.Visit(expr)

parser/lexer.go

+8-12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const (
1515
TokenInt TokenKind = "<int>"
1616
TokenFloat TokenKind = "<float>"
1717
TokenString TokenKind = "<string>"
18+
TokenDot = "."
1819
)
1920

2021
const (
@@ -328,19 +329,14 @@ func (l *Lexer) consumeToken() error {
328329
return nil
329330
}
330331
case '.':
331-
// check if the next token is a number. If so, parse it as a float number
332-
if l.peekOk(1) && IsDigit(l.peekN(1)) {
333-
return l.consumeNumber()
334-
}
335-
// check if the previous lastToken is an Ident. If so, it's a field name.
336-
if l.lastToken != nil && l.lastToken.Kind != TokenIdent {
337-
return fmt.Errorf("'.' should be after an Ident, but got <%q>", l.lastToken.Kind)
332+
l.lastToken = &Token{
333+
String: l.slice(0, 1),
334+
Kind: TokenDot,
335+
Pos: Pos(l.current),
336+
End: Pos(l.current + 1),
338337
}
339-
}
340-
341-
// The subsequent lastToken after the dot should be an Ident.
342-
if l.lastToken != nil && l.lastToken.Kind == "." && !IsIdentStart(l.peekN(0)) {
343-
return fmt.Errorf("'.' should follow with an Ident, but got <%q>", l.lastToken.Kind)
338+
l.skipN(1)
339+
return nil
344340
}
345341

346342
if IsIdentStart(l.peekN(0)) {

parser/lexer_test.go

-2
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ func TestConsumeNumber(t *testing.T) {
106106
t.Run("Float number", func(t *testing.T) {
107107
floats := []string{
108108
"123.456",
109-
".456",
110109
"123.456e+10",
111110
"123.456e-10",
112111
"123.456e10",
@@ -126,7 +125,6 @@ func TestConsumeNumber(t *testing.T) {
126125

127126
t.Run("Invalid float number", func(t *testing.T) {
128127
invalidFloats := []string{
129-
".456a",
130128
"123.456b",
131129
"123.456e",
132130
"123.456e+",

parser/parse_system.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ func (p *Parser) parseGrantSource(_ Pos) (*TableIdentifier, error) {
921921
return nil, err
922922
}
923923

924-
if p.tryConsumeTokenKind(".") == nil {
924+
if p.tryConsumeTokenKind(TokenDot) == nil {
925925
return &TableIdentifier{
926926
Table: ident,
927927
}, nil

parser/parser_column.go

+56-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const (
2020
PrecedenceMulDivMod
2121
PrecedenceBracket
2222
PrecedenceArrow
23+
PrecedenceDot
2324
PrecedenceDoubleColon
2425
)
2526

@@ -56,6 +57,8 @@ func (p *Parser) getNextPrecedence() int {
5657
return PrecedenceBracket
5758
case p.matchTokenKind(opTypeCast):
5859
return PrecedenceDoubleColon
60+
case p.matchTokenKind(TokenDot):
61+
return PrecedenceDot
5962
case p.matchKeyword(KeywordBetween), p.matchKeyword(KeywordLike), p.matchKeyword(KeywordIlike):
6063
return PrecedenceBetweenLike
6164
case p.matchKeyword(KeywordIn):
@@ -78,7 +81,7 @@ func (p *Parser) parseInfix(expr Expr, precedence int) (Expr, error) {
7881
p.matchTokenKind(opTypeDiv), p.matchTokenKind(opTypeMod),
7982
p.matchKeyword(KeywordIn), p.matchKeyword(KeywordLike),
8083
p.matchKeyword(KeywordIlike), p.matchKeyword(KeywordAnd), p.matchKeyword(KeywordOr),
81-
p.matchTokenKind(opTypeCast), p.matchTokenKind(opTypeArrow), p.matchTokenKind(opTypeDoubleEQ):
84+
p.matchTokenKind(opTypeArrow), p.matchTokenKind(opTypeDoubleEQ):
8285
op := p.last().ToString()
8386
_ = p.lexer.consumeToken()
8487
rightExpr, err := p.parseSubExpr(p.Pos(), precedence)
@@ -90,6 +93,38 @@ func (p *Parser) parseInfix(expr Expr, precedence int) (Expr, error) {
9093
Operation: TokenKind(op),
9194
RightExpr: rightExpr,
9295
}, nil
96+
case p.matchTokenKind(opTypeCast):
97+
_ = p.lexer.consumeToken()
98+
99+
if p.matchTokenKind(TokenIdent) && p.last().String == "Tuple" {
100+
name, err := p.parseIdent()
101+
if err != nil {
102+
return nil, err
103+
}
104+
if _, err = p.consumeTokenKind("("); err != nil {
105+
return nil, err
106+
}
107+
// it's a tuple type definition after "::" operator
108+
rightExpr, err := p.parseNestedType(name, p.Pos())
109+
if err != nil {
110+
return nil, err
111+
}
112+
return &BinaryOperation{
113+
LeftExpr: expr,
114+
Operation: opTypeCast,
115+
RightExpr: rightExpr,
116+
}, nil
117+
}
118+
119+
rightExpr, err := p.parseSubExpr(p.Pos(), precedence)
120+
if err != nil {
121+
return nil, err
122+
}
123+
return &BinaryOperation{
124+
LeftExpr: expr,
125+
Operation: opTypeCast,
126+
RightExpr: rightExpr,
127+
}, nil
93128
case p.matchKeyword(KeywordBetween):
94129
return p.parseBetweenClause(expr)
95130
case p.matchKeyword(KeywordGlobal):
@@ -106,7 +141,24 @@ func (p *Parser) parseInfix(expr Expr, precedence int) (Expr, error) {
106141
Operation: "GLOBAL IN",
107142
RightExpr: rightExpr,
108143
}, nil
109-
144+
case p.matchTokenKind(TokenDot):
145+
_ = p.lexer.consumeToken()
146+
// access column with dot notation
147+
var rightExpr Expr
148+
var err error
149+
if p.matchTokenKind(TokenIdent) {
150+
rightExpr, err = p.parseIdent()
151+
} else {
152+
rightExpr, err = p.parseDecimal(p.Pos())
153+
}
154+
if err != nil {
155+
return nil, err
156+
}
157+
return &IndexOperation{
158+
LeftExpr: expr,
159+
Operation: TokenDot,
160+
Index: rightExpr,
161+
}, nil
110162
case p.matchKeyword(KeywordNot):
111163
_ = p.lexer.consumeToken()
112164
switch {
@@ -331,6 +383,8 @@ func (p *Parser) parseColumnExpr(pos Pos) (Expr, error) { //nolint:funlen
331383
return p.parseQueryParam(p.Pos())
332384
}
333385
return p.parseMapLiteral(p.Pos())
386+
case p.matchTokenKind(TokenDot):
387+
return p.parseNumber(p.Pos())
334388
case p.matchTokenKind(opTypeQuery):
335389
// Placeholder `?`
336390
_ = p.lexer.consumeToken()

parser/parser_common.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (p *Parser) parseIdentOrStar() (*Ident, error) {
110110
}
111111

112112
func (p *Parser) tryParseDotIdent(_ Pos) (*Ident, error) {
113-
if p.tryConsumeTokenKind(".") == nil {
113+
if p.tryConsumeTokenKind(TokenDot) == nil {
114114
return nil, nil // nolint
115115
}
116116
return p.parseIdent()
@@ -211,6 +211,17 @@ func (p *Parser) parseNumber(pos Pos) (*NumberLiteral, error) {
211211
lastToken, err = p.consumeTokenKind(TokenInt)
212212
case p.matchTokenKind(TokenFloat):
213213
lastToken, err = p.consumeTokenKind(TokenFloat)
214+
case p.matchTokenKind(TokenDot):
215+
_ = p.lexer.consumeToken()
216+
lastToken, err = p.consumeTokenKind(TokenInt)
217+
if err != nil {
218+
return nil, err
219+
}
220+
if lastToken.Base != 10 {
221+
return nil, fmt.Errorf("invalid decimal literal: %q", lastToken.String)
222+
}
223+
lastToken.String = "." + lastToken.String
224+
lastToken.Kind = TokenFloat
214225
default:
215226
return nil, fmt.Errorf("expected <int> or <float>, but got %q", p.lastTokenKind())
216227
}

parser/parser_table.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,14 @@ func (p *Parser) parseIdentOrFunction(_ Pos) (Expr, error) {
223223
}, nil
224224
}
225225
return funcExpr, nil
226-
case p.tryConsumeTokenKind(".") != nil:
226+
case p.tryConsumeTokenKind(TokenDot) != nil:
227227
switch {
228228
case p.matchTokenKind(TokenIdent):
229229
nextIdent, err := p.parseIdent()
230230
if err != nil {
231231
return nil, err
232232
}
233-
if p.tryConsumeTokenKind(".") != nil {
233+
if p.tryConsumeTokenKind(TokenDot) != nil {
234234
thirdIdent, err := p.parseIdent()
235235
if err != nil {
236236
return nil, err
@@ -310,7 +310,7 @@ func (p *Parser) parseTableSchemaClause(pos Pos) (*TableSchemaClause, error) {
310310
return nil, err
311311
}
312312
switch {
313-
case p.matchTokenKind("."):
313+
case p.matchTokenKind(TokenDot):
314314
// it's a database.table
315315
dotIdent, err := p.tryParseDotIdent(p.Pos())
316316
if err != nil {
@@ -489,7 +489,7 @@ func (p *Parser) parseTableArgExpr(pos Pos) (Expr, error) {
489489
}
490490
switch {
491491
// nest identifier
492-
case p.matchTokenKind("."):
492+
case p.matchTokenKind(TokenDot):
493493
dotIdent, err := p.tryParseDotIdent(p.Pos())
494494
if err != nil {
495495
return nil, err
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
SELECT tuple('a','b','c').3, .1234;
2+
3+
SELECT toTypeName( tuple('a' as first,'b' as second ,'c' as third)::Tuple(first String,second String,third String)),
4+
(tuple('a' as first,'b' as second ,'c' as third)::Tuple(first String,second String,third String)).second,
5+
tuple('a','b','c').3,
6+
tupleElement(tuple('a','b','c'),1)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Origin SQL:
2+
SELECT tuple('a','b','c').3, .1234;
3+
4+
SELECT toTypeName( tuple('a' as first,'b' as second ,'c' as third)::Tuple(first String,second String,third String)),
5+
(tuple('a' as first,'b' as second ,'c' as third)::Tuple(first String,second String,third String)).second,
6+
tuple('a','b','c').3,
7+
tupleElement(tuple('a','b','c'),1)
8+
9+
-- Format SQL:
10+
SELECT tuple('a', 'b', 'c').3, .1234;
11+
SELECT toTypeName(tuple('a' AS first, 'b' AS second, 'c' AS third)::Tuple(first String, second String, third String)), (tuple('a' AS first, 'b' AS second, 'c' AS third)::Tuple(first String, second String, third String)).second, tuple('a', 'b', 'c').3, tupleElement(tuple('a', 'b', 'c'), 1);

0 commit comments

Comments
 (0)