Skip to content

Commit b8659ca

Browse files
authored
feat: add support for SELECT DISTINCT ON(…) (#194)
1 parent 4350d2e commit b8659ca

File tree

60 files changed

+273
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+273
-0
lines changed

parser/ast.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6739,6 +6739,7 @@ type SelectQuery struct {
67396739
With *WithClause
67406740
Top *TopClause
67416741
HasDistinct bool
6742+
DistinctOn *DistinctOn
67426743
SelectItems []*SelectItem
67436744
From *FromClause
67446745
ArrayJoin *ArrayJoinClause
@@ -6782,6 +6783,11 @@ func (s *SelectQuery) String() string { // nolint: funlen
67826783
builder.WriteString("SELECT ")
67836784
if s.HasDistinct {
67846785
builder.WriteString("DISTINCT ")
6786+
6787+
if s.DistinctOn != nil {
6788+
builder.WriteString(s.DistinctOn.String())
6789+
builder.WriteString(" ")
6790+
}
67856791
}
67866792
if s.Top != nil {
67876793
builder.WriteString(s.Top.String())
@@ -6952,6 +6958,44 @@ func (s *SelectQuery) Accept(visitor ASTVisitor) error {
69526958
return visitor.VisitSelectQuery(s)
69536959
}
69546960

6961+
type DistinctOn struct {
6962+
Idents []*Ident
6963+
DistinctOnPos Pos
6964+
DistinctOnEnd Pos
6965+
}
6966+
6967+
func (s *DistinctOn) Pos() Pos {
6968+
return s.DistinctOnPos
6969+
}
6970+
6971+
func (s *DistinctOn) End() Pos {
6972+
return s.DistinctOnEnd
6973+
}
6974+
6975+
func (s *DistinctOn) String() string {
6976+
var builder strings.Builder
6977+
builder.WriteString("ON (")
6978+
for i, ident := range s.Idents {
6979+
if i > 0 {
6980+
builder.WriteString(", ")
6981+
}
6982+
builder.WriteString(ident.String())
6983+
}
6984+
builder.WriteByte(')')
6985+
return builder.String()
6986+
}
6987+
6988+
func (s *DistinctOn) Accept(visitor ASTVisitor) error {
6989+
visitor.Enter(s)
6990+
defer visitor.Leave(s)
6991+
for _, ident := range s.Idents {
6992+
if err := ident.Accept(visitor); err != nil {
6993+
return err
6994+
}
6995+
}
6996+
return visitor.VisitDistinctOn(s)
6997+
}
6998+
69556999
type SubQuery struct {
69567000
HasParen bool
69577001
Select *SelectQuery

parser/ast_visitor.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ type ASTVisitor interface {
184184
VisitDescribeExpr(expr *DescribeStmt) error
185185
VisitSelectItem(expr *SelectItem) error
186186
VisitTargetPairExpr(expr *TargetPair) error
187+
VisitDistinctOn(expr *DistinctOn) error
187188

188189
Enter(expr Expr)
189190
Leave(expr Expr)
@@ -1476,6 +1477,13 @@ func (v *DefaultASTVisitor) VisitTargetPairExpr(expr *TargetPair) error {
14761477
return nil
14771478
}
14781479

1480+
func (v *DefaultASTVisitor) VisitDistinctOn(expr *DistinctOn) error {
1481+
if v.Visit != nil {
1482+
return v.Visit(expr)
1483+
}
1484+
return nil
1485+
}
1486+
14791487
func (v *DefaultASTVisitor) Enter(expr Expr) {}
14801488

14811489
func (v *DefaultASTVisitor) Leave(expr Expr) {}

parser/parser_query.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,49 @@ func (p *Parser) parseTopClause(pos Pos) (*TopClause, error) {
7171
}, nil
7272
}
7373

74+
func (p *Parser) tryParseDistinctOn(pos Pos) (*DistinctOn, error) {
75+
if !p.matchKeyword(KeywordOn) {
76+
return nil, nil
77+
}
78+
return p.parseDistinctOn(pos)
79+
}
80+
81+
func (p *Parser) parseDistinctOn(pos Pos) (*DistinctOn, error) {
82+
if err := p.expectKeyword(KeywordOn); err != nil {
83+
return nil, err
84+
}
85+
86+
if err := p.expectTokenKind(TokenKindLParen); err != nil {
87+
return nil, err
88+
}
89+
90+
ident, err := p.parseIdent()
91+
if err != nil {
92+
return nil, err
93+
}
94+
idents := []*Ident{ident}
95+
96+
for p.matchTokenKind(TokenKindComma) {
97+
_ = p.lexer.consumeToken()
98+
99+
ident, err = p.parseIdent()
100+
if err != nil {
101+
return nil, err
102+
}
103+
idents = append(idents, ident)
104+
}
105+
106+
if err := p.expectTokenKind(TokenKindRParen); err != nil {
107+
return nil, err
108+
}
109+
110+
return &DistinctOn{
111+
Idents: idents,
112+
DistinctOnPos: pos,
113+
DistinctOnEnd: p.Pos(),
114+
}, nil
115+
}
116+
74117
func (p *Parser) tryParseFromClause(pos Pos) (*FromClause, error) {
75118
if !p.matchKeyword(KeywordFrom) {
76119
return nil, nil
@@ -835,6 +878,10 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
835878
}
836879
// DISTINCT?
837880
hasDistinct := p.tryConsumeKeywords(KeywordDistinct)
881+
distinctOn, err := p.tryParseDistinctOn(p.Pos())
882+
if err != nil {
883+
return nil, err
884+
}
838885

839886
top, err := p.tryParseTopClause(p.Pos())
840887
if err != nil {
@@ -961,6 +1008,7 @@ func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: fun
9611008
StatementEnd: statementEnd,
9621009
Top: top,
9631010
HasDistinct: hasDistinct,
1011+
DistinctOn: distinctOn,
9641012
SelectItems: selectItems,
9651013
From: from,
9661014
ArrayJoin: arrayJoin,

parser/testdata/basic/output/quantile_functions.sql.golden.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"With": null,
66
"Top": null,
77
"HasDistinct": false,
8+
"DistinctOn": null,
89
"SelectItems": [
910
{
1011
"Expr": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"With": null,
5959
"Top": null,
6060
"HasDistinct": false,
61+
"DistinctOn": null,
6162
"SelectItems": [
6263
{
6364
"Expr": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"With": null,
8484
"Top": null,
8585
"HasDistinct": false,
86+
"DistinctOn": null,
8687
"SelectItems": [
8788
{
8889
"Expr": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@
255255
"With": null,
256256
"Top": null,
257257
"HasDistinct": false,
258+
"DistinctOn": null,
258259
"SelectItems": [
259260
{
260261
"Expr": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@
181181
"With": null,
182182
"Top": null,
183183
"HasDistinct": false,
184+
"DistinctOn": null,
184185
"SelectItems": [
185186
{
186187
"Expr": {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@
152152
"With": null,
153153
"Top": null,
154154
"HasDistinct": false,
155+
"DistinctOn": null,
155156
"SelectItems": [
156157
{
157158
"Expr": {
@@ -247,6 +248,7 @@
247248
"With": null,
248249
"Top": null,
249250
"HasDistinct": false,
251+
"DistinctOn": null,
250252
"SelectItems": [
251253
{
252254
"Expr": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
"With": null,
149149
"Top": null,
150150
"HasDistinct": false,
151+
"DistinctOn": null,
151152
"SelectItems": [
152153
{
153154
"Expr": {

0 commit comments

Comments
 (0)