Skip to content

Commit c1f72b5

Browse files
authored
Add support of SELECT modifiers REPLACE|EXCEPT|APPLY (#100)
1 parent 04f353e commit c1f72b5

File tree

40 files changed

+1027
-529
lines changed

40 files changed

+1027
-529
lines changed

parser/ast.go

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,47 @@ type DDL interface {
2424
Type() string
2525
}
2626

27+
type SelectItem struct {
28+
Expr Expr
29+
// Please refer: https://clickhouse.com/docs/en/sql-reference/statements/select#select-modifiers
30+
Modifiers []*FunctionExpr
31+
}
32+
33+
func (s *SelectItem) Pos() Pos {
34+
return s.Expr.Pos()
35+
}
36+
37+
func (s *SelectItem) End() Pos {
38+
if len(s.Modifiers) > 0 {
39+
return s.Modifiers[len(s.Modifiers)-1].End()
40+
}
41+
return s.Expr.End()
42+
}
43+
44+
func (s *SelectItem) String() string {
45+
var builder strings.Builder
46+
builder.WriteString(s.Expr.String())
47+
for _, modifier := range s.Modifiers {
48+
builder.WriteByte(' ')
49+
builder.WriteString(modifier.String())
50+
}
51+
return builder.String()
52+
}
53+
54+
func (s *SelectItem) Accept(visitor ASTVisitor) error {
55+
visitor.enter(s)
56+
defer visitor.leave(s)
57+
if err := s.Expr.Accept(visitor); err != nil {
58+
return err
59+
}
60+
for _, modifier := range s.Modifiers {
61+
if err := modifier.Accept(visitor); err != nil {
62+
return err
63+
}
64+
}
65+
return visitor.VisitSelectItem(s)
66+
}
67+
2768
type OperationExpr struct {
2869
OperationPos Pos
2970
Kind TokenKind
@@ -5047,7 +5088,7 @@ type SelectQuery struct {
50475088
StatementEnd Pos
50485089
With *WithClause
50495090
Top *TopClause
5050-
SelectColumns *ColumnExprList
5091+
SelectItems []*SelectItem
50515092
From *FromClause
50525093
ArrayJoin *ArrayJoinClause
50535094
Window *WindowClause
@@ -5092,10 +5133,9 @@ func (s *SelectQuery) String() string { // nolint: funlen
50925133
builder.WriteString(s.Top.String())
50935134
builder.WriteString(" ")
50945135
}
5095-
columns := s.SelectColumns.Items
5096-
for i, column := range columns {
5097-
builder.WriteString(column.String())
5098-
if i != len(columns)-1 {
5136+
for i, selectItem := range s.SelectItems {
5137+
builder.WriteString(selectItem.String())
5138+
if i != len(s.SelectItems)-1 {
50995139
builder.WriteString(", ")
51005140
}
51015141
}
@@ -5173,9 +5213,11 @@ func (s *SelectQuery) Accept(visitor ASTVisitor) error {
51735213
return err
51745214
}
51755215
}
5176-
if s.SelectColumns != nil {
5177-
if err := s.SelectColumns.Accept(visitor); err != nil {
5178-
return err
5216+
if s.SelectItems != nil {
5217+
for _, item := range s.SelectItems {
5218+
if err := item.Accept(visitor); err != nil {
5219+
return err
5220+
}
51795221
}
51805222
}
51815223
if s.From != nil {

parser/ast_visitor.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ type ASTVisitor interface {
154154
VisitExplainExpr(expr *ExplainStmt) error
155155
VisitPrivilegeExpr(expr *PrivilegeClause) error
156156
VisitGrantPrivilegeExpr(expr *GrantPrivilegeStmt) error
157+
VisitSelectItem(expr *SelectItem) error
157158

158159
enter(expr Expr)
159160
leave(expr Expr)
@@ -1236,6 +1237,13 @@ func (v *DefaultASTVisitor) VisitGrantPrivilegeExpr(expr *GrantPrivilegeStmt) er
12361237
return nil
12371238
}
12381239

1240+
func (v *DefaultASTVisitor) VisitSelectItem(expr *SelectItem) error {
1241+
if v.Visit != nil {
1242+
return v.Visit(expr)
1243+
}
1244+
return nil
1245+
}
1246+
12391247
func (v *DefaultASTVisitor) enter(expr Expr) {}
12401248

12411249
func (v *DefaultASTVisitor) leave(expr Expr) {}

parser/keyword.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const (
1010
KeywordAnd = "AND"
1111
KeywordAnti = "ANTI"
1212
KeywordAny = "ANY"
13+
KeywordApply = "APPLY"
1314
KeywordArray = "ARRAY"
1415
KeywordAs = "AS"
1516
KeywordAsc = "ASC"
@@ -233,6 +234,7 @@ var keywords = NewSet(
233234
KeywordAnd,
234235
KeywordAnti,
235236
KeywordAny,
237+
KeywordApply,
236238
KeywordArray,
237239
KeywordAs,
238240
KeywordAsc,

parser/parser_column.go

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,24 @@ func (p *Parser) parseColumnExprListWithTerm(term TokenKind, pos Pos) (*ColumnEx
477477
return columnExprList, nil
478478
}
479479

480+
func (p *Parser) parseSelectItems() ([]*SelectItem, error) {
481+
selectItems := make([]*SelectItem, 0)
482+
for !p.lexer.isEOF() || p.last() != nil {
483+
selectItem, err := p.parseSelectItem()
484+
if err != nil {
485+
return nil, err
486+
}
487+
if selectItem == nil {
488+
break
489+
}
490+
selectItems = append(selectItems, selectItem)
491+
if p.tryConsumeTokenKind(",") == nil {
492+
break
493+
}
494+
}
495+
return selectItems, nil
496+
}
497+
480498
// Syntax: INTERVAL expr interval
481499
func (p *Parser) parseColumnExprInterval(pos Pos) (Expr, error) {
482500
if err := p.consumeKeyword(KeywordInterval); err != nil {
@@ -504,10 +522,7 @@ func (p *Parser) parseColumnExprInterval(pos Pos) (Expr, error) {
504522
}, nil
505523
}
506524

507-
func (p *Parser) parseFunctionExpr(_ Pos) (Expr, error) {
508-
if _, err := p.consumeTokenKind(TokenIdent); err != nil {
509-
return nil, err
510-
}
525+
func (p *Parser) parseFunctionExpr(_ Pos) (*FunctionExpr, error) {
511526
// parse function name
512527
name, err := p.parseIdent()
513528
if err != nil {
@@ -674,6 +689,30 @@ func (p *Parser) parseColumnsExpr(pos Pos) (Expr, error) {
674689
return p.parseExpr(pos)
675690
}
676691

692+
func (p *Parser) parseSelectItem() (*SelectItem, error) {
693+
expr, err := p.parseExpr(p.Pos())
694+
if err != nil {
695+
return nil, err
696+
}
697+
698+
modifiers := make([]*FunctionExpr, 0)
699+
for {
700+
if p.matchKeyword(KeywordExcept) || p.matchKeyword(KeywordApply) || p.matchKeyword(KeywordReplace) {
701+
modifier, err := p.parseFunctionExpr(p.Pos())
702+
if err != nil {
703+
return nil, err
704+
}
705+
modifiers = append(modifiers, modifier)
706+
} else {
707+
break
708+
}
709+
}
710+
return &SelectItem{
711+
Expr: expr,
712+
Modifiers: modifiers,
713+
}, nil
714+
}
715+
677716
func (p *Parser) parseColumnCaseExpr(pos Pos) (*CaseExpr, error) {
678717
// CASE expr
679718
caseExpr := &CaseExpr{CasePos: pos}

parser/parser_query.go

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -803,11 +803,15 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
803803
if err != nil {
804804
return nil, err
805805
}
806-
selectColumns, err := p.parseColumnExprListWithRoundBracket(p.Pos())
806+
selectItems, err := p.parseSelectItems()
807807
if err != nil {
808808
return nil, err
809809
}
810-
statementEnd := selectColumns.End()
810+
811+
statementEnd := pos
812+
if len(selectItems) > 0 {
813+
statementEnd = selectItems[len(selectItems)-1].End()
814+
}
811815
from, err := p.tryParseFromClause(p.Pos())
812816
if err != nil {
813817
return nil, err
@@ -916,24 +920,24 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
916920
}
917921

918922
return &SelectQuery{
919-
With: withClause,
920-
SelectPos: pos,
921-
StatementEnd: statementEnd,
922-
Top: top,
923-
SelectColumns: selectColumns,
924-
From: from,
925-
ArrayJoin: arrayJoin,
926-
Window: window,
927-
Prewhere: prewhere,
928-
Where: where,
929-
GroupBy: groupBy,
930-
Having: having,
931-
OrderBy: orderBy,
932-
LimitBy: limitBy,
933-
Limit: limit,
934-
Settings: settings,
935-
Format: format,
936-
WithTotal: withTotal,
923+
With: withClause,
924+
SelectPos: pos,
925+
StatementEnd: statementEnd,
926+
Top: top,
927+
SelectItems: selectItems,
928+
From: from,
929+
ArrayJoin: arrayJoin,
930+
Window: window,
931+
Prewhere: prewhere,
932+
Where: where,
933+
GroupBy: groupBy,
934+
Having: having,
935+
OrderBy: orderBy,
936+
LimitBy: limitBy,
937+
Limit: limit,
938+
Settings: settings,
939+
Format: format,
940+
WithTotal: withTotal,
937941
}, nil
938942
}
939943

parser/testdata/ddl/output/bug_001.sql.golden.json

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,27 @@
5151
"StatementEnd": 635,
5252
"With": null,
5353
"Top": null,
54-
"SelectColumns": {
55-
"ListPos": 118,
56-
"ListEnd": 591,
57-
"HasDistinct": false,
58-
"Items": [
59-
{
54+
"SelectItems": [
55+
{
56+
"Expr": {
6057
"Name": "event_ts",
6158
"QuoteType": 1,
6259
"NamePos": 118,
6360
"NameEnd": 126
6461
},
65-
{
62+
"Modifiers": []
63+
},
64+
{
65+
"Expr": {
6666
"Name": "org_id",
6767
"QuoteType": 1,
6868
"NamePos": 132,
6969
"NameEnd": 138
7070
},
71-
{
71+
"Modifiers": []
72+
},
73+
{
74+
"Expr": {
7275
"Expr": {
7376
"Name": {
7477
"Name": "visitParamExtractString",
@@ -108,7 +111,10 @@
108111
"NameEnd": 189
109112
}
110113
},
111-
{
114+
"Modifiers": []
115+
},
116+
{
117+
"Expr": {
112118
"Expr": {
113119
"Name": {
114120
"Name": "visitParamExtractString",
@@ -148,7 +154,10 @@
148154
"NameEnd": 240
149155
}
150156
},
151-
{
157+
"Modifiers": []
158+
},
159+
{
160+
"Expr": {
152161
"Expr": {
153162
"Name": {
154163
"Name": "visitParamExtractString",
@@ -188,7 +197,10 @@
188197
"NameEnd": 291
189198
}
190199
},
191-
{
200+
"Modifiers": []
201+
},
202+
{
203+
"Expr": {
192204
"Expr": {
193205
"Name": {
194206
"Name": "visitParamExtractString",
@@ -228,7 +240,10 @@
228240
"NameEnd": 342
229241
}
230242
},
231-
{
243+
"Modifiers": []
244+
},
245+
{
246+
"Expr": {
232247
"Expr": {
233248
"Name": {
234249
"Name": "visitParamExtractString",
@@ -268,7 +283,10 @@
268283
"NameEnd": 393
269284
}
270285
},
271-
{
286+
"Modifiers": []
287+
},
288+
{
289+
"Expr": {
272290
"Expr": {
273291
"Name": {
274292
"Name": "visitParamExtractString",
@@ -308,7 +326,10 @@
308326
"NameEnd": 444
309327
}
310328
},
311-
{
329+
"Modifiers": []
330+
},
331+
{
332+
"Expr": {
312333
"Expr": {
313334
"Name": {
314335
"Name": "visitParamExtractString",
@@ -348,7 +369,10 @@
348369
"NameEnd": 495
349370
}
350371
},
351-
{
372+
"Modifiers": []
373+
},
374+
{
375+
"Expr": {
352376
"Expr": {
353377
"Name": {
354378
"Name": "visitParamExtractInt",
@@ -388,7 +412,10 @@
388412
"NameEnd": 543
389413
}
390414
},
391-
{
415+
"Modifiers": []
416+
},
417+
{
418+
"Expr": {
392419
"Expr": {
393420
"Name": {
394421
"Name": "visitParamExtractInt",
@@ -427,9 +454,10 @@
427454
"NamePos": 590,
428455
"NameEnd": 591
429456
}
430-
}
431-
]
432-
},
457+
},
458+
"Modifiers": []
459+
}
460+
],
433461
"From": {
434462
"FromPos": 592,
435463
"Expr": {

0 commit comments

Comments
 (0)