Skip to content

Commit 60d421b

Browse files
committed
fix: support expressions (dotted column refs) in DISTINCT ON
parseDistinctOn() was calling parseIdent() which only handles simple identifiers. Queries like DISTINCT ON (t.id, t.name) would fail because the dot was not recognized. Changed to parseExpr() so that any valid column expression (including table.column references) is accepted.
1 parent d468e13 commit 60d421b

9 files changed

Lines changed: 184 additions & 13 deletions

parser/ast.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5201,7 +5201,7 @@ func (s *SelectQuery) Accept(visitor ASTVisitor) error {
52015201
}
52025202

52035203
type DistinctOn struct {
5204-
Idents []*Ident
5204+
Exprs []Expr
52055205
DistinctOnPos Pos
52065206
DistinctOnEnd Pos
52075207
}
@@ -5217,8 +5217,8 @@ func (s *DistinctOn) End() Pos {
52175217
func (s *DistinctOn) Accept(visitor ASTVisitor) error {
52185218
visitor.Enter(s)
52195219
defer visitor.Leave(s)
5220-
for _, ident := range s.Idents {
5221-
if err := ident.Accept(visitor); err != nil {
5220+
for _, expr := range s.Exprs {
5221+
if err := expr.Accept(visitor); err != nil {
52225222
return err
52235223
}
52245224
}

parser/format.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,11 +1344,11 @@ func (d *DictionarySourceClause) FormatSQL(formatter *Formatter) {
13441344

13451345
func (s *DistinctOn) FormatSQL(formatter *Formatter) {
13461346
formatter.WriteString("ON (")
1347-
for i, ident := range s.Idents {
1347+
for i, expr := range s.Exprs {
13481348
if i > 0 {
13491349
formatter.WriteString(", ")
13501350
}
1351-
formatter.WriteExpr(ident)
1351+
formatter.WriteExpr(expr)
13521352
}
13531353
formatter.WriteByte(')')
13541354
}

parser/parser_query.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,28 +89,28 @@ func (p *Parser) parseDistinctOn(pos Pos) (*DistinctOn, error) {
8989
return nil, err
9090
}
9191

92-
ident, err := p.parseIdent()
92+
expr, err := p.parseExpr(p.Pos())
9393
if err != nil {
9494
return nil, err
9595
}
96-
idents := []*Ident{ident}
96+
exprs := []Expr{expr}
9797

9898
for p.matchTokenKind(TokenKindComma) {
9999
_ = p.lexer.consumeToken()
100100

101-
ident, err = p.parseIdent()
101+
expr, err = p.parseExpr(p.Pos())
102102
if err != nil {
103103
return nil, err
104104
}
105-
idents = append(idents, ident)
105+
exprs = append(exprs, expr)
106106
}
107107

108108
if err := p.expectTokenKind(TokenKindRParen); err != nil {
109109
return nil, err
110110
}
111111

112112
return &DistinctOn{
113-
Idents: idents,
113+
Exprs: exprs,
114114
DistinctOnPos: pos,
115115
DistinctOnEnd: p.Pos(),
116116
}, nil
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Origin SQL:
2+
SELECT DISTINCT ON (t.id, t.name) t.id, t.name, t.value FROM test_table t
3+
4+
-- Beautify SQL:
5+
SELECT DISTINCT ON (t.id, t.name)
6+
t.id,
7+
t.name,
8+
t.value
9+
FROM
10+
test_table AS t;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Origin SQL:
2+
SELECT DISTINCT ON (t.id, t.name) t.id, t.name, t.value FROM test_table t
3+
4+
-- Format SQL:
5+
SELECT DISTINCT ON (t.id, t.name) t.id, t.name, t.value FROM test_table AS t;
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
[
2+
{
3+
"SelectPos": 0,
4+
"StatementEnd": 73,
5+
"With": null,
6+
"Top": null,
7+
"HasDistinct": true,
8+
"DistinctOn": {
9+
"Exprs": [
10+
{
11+
"Fields": [
12+
{
13+
"Name": "t",
14+
"QuoteType": 1,
15+
"NamePos": 20,
16+
"NameEnd": 21
17+
},
18+
{
19+
"Name": "id",
20+
"QuoteType": 1,
21+
"NamePos": 22,
22+
"NameEnd": 24
23+
}
24+
]
25+
},
26+
{
27+
"Fields": [
28+
{
29+
"Name": "t",
30+
"QuoteType": 1,
31+
"NamePos": 26,
32+
"NameEnd": 27
33+
},
34+
{
35+
"Name": "name",
36+
"QuoteType": 1,
37+
"NamePos": 28,
38+
"NameEnd": 32
39+
}
40+
]
41+
}
42+
],
43+
"DistinctOnPos": 16,
44+
"DistinctOnEnd": 34
45+
},
46+
"SelectItems": [
47+
{
48+
"Expr": {
49+
"Fields": [
50+
{
51+
"Name": "t",
52+
"QuoteType": 1,
53+
"NamePos": 34,
54+
"NameEnd": 35
55+
},
56+
{
57+
"Name": "id",
58+
"QuoteType": 1,
59+
"NamePos": 36,
60+
"NameEnd": 38
61+
}
62+
]
63+
},
64+
"Modifiers": [],
65+
"Alias": null
66+
},
67+
{
68+
"Expr": {
69+
"Fields": [
70+
{
71+
"Name": "t",
72+
"QuoteType": 1,
73+
"NamePos": 40,
74+
"NameEnd": 41
75+
},
76+
{
77+
"Name": "name",
78+
"QuoteType": 1,
79+
"NamePos": 42,
80+
"NameEnd": 46
81+
}
82+
]
83+
},
84+
"Modifiers": [],
85+
"Alias": null
86+
},
87+
{
88+
"Expr": {
89+
"Fields": [
90+
{
91+
"Name": "t",
92+
"QuoteType": 1,
93+
"NamePos": 48,
94+
"NameEnd": 49
95+
},
96+
{
97+
"Name": "value",
98+
"QuoteType": 1,
99+
"NamePos": 50,
100+
"NameEnd": 55
101+
}
102+
]
103+
},
104+
"Modifiers": [],
105+
"Alias": null
106+
}
107+
],
108+
"From": {
109+
"FromPos": 56,
110+
"Expr": {
111+
"Table": {
112+
"TablePos": 61,
113+
"TableEnd": 73,
114+
"Alias": null,
115+
"Expr": {
116+
"Expr": {
117+
"Database": null,
118+
"Table": {
119+
"Name": "test_table",
120+
"QuoteType": 1,
121+
"NamePos": 61,
122+
"NameEnd": 71
123+
}
124+
},
125+
"AliasPos": 72,
126+
"Alias": {
127+
"Name": "t",
128+
"QuoteType": 1,
129+
"NamePos": 72,
130+
"NameEnd": 73
131+
}
132+
},
133+
"HasFinal": false
134+
},
135+
"StatementEnd": 73,
136+
"SampleRatio": null,
137+
"HasFinal": false
138+
}
139+
},
140+
"Window": null,
141+
"Prewhere": null,
142+
"Where": null,
143+
"GroupBy": null,
144+
"WithTotal": false,
145+
"Having": null,
146+
"OrderBy": null,
147+
"LimitBy": null,
148+
"Limit": null,
149+
"Settings": null,
150+
"Format": null,
151+
"UnionAll": null,
152+
"UnionDistinct": null,
153+
"Except": null
154+
}
155+
]

parser/testdata/query/output/select_with_distinct_on_keyword.sql.golden.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"Top": null,
77
"HasDistinct": true,
88
"DistinctOn": {
9-
"Idents": [
9+
"Exprs": [
1010
{
1111
"Name": "album",
1212
"QuoteType": 1,
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT DISTINCT ON (t.id, t.name) t.id, t.name, t.value FROM test_table t

parser/walk.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,8 +1503,8 @@ func Walk(node Expr, fn WalkFunc) bool {
15031503
return false
15041504
}
15051505
case *DistinctOn:
1506-
for _, ident := range n.Idents {
1507-
if !Walk(ident, fn) {
1506+
for _, expr := range n.Exprs {
1507+
if !Walk(expr, fn) {
15081508
return false
15091509
}
15101510
}

0 commit comments

Comments
 (0)