From 5c64632af9badc5ccc63dc9bba12cbfc498dd722 Mon Sep 17 00:00:00 2001 From: apstndb <803393+apstndb@users.noreply.github.com> Date: Sun, 15 Feb 2026 10:05:05 +0900 Subject: [PATCH] Implement group hints --- ast/ast.go | 4 +- ast/sql.go | 6 +- ast/walk_internal.go | 1 + parser.go | 2 + ...select_singer_with_groupby_group_hints.sql | 6 ++ ...ct_singer_with_groupby_group_hints.sql.txt | 79 +++++++++++++++++++ ...ct_singer_with_groupby_group_hints.sql.txt | 79 +++++++++++++++++++ 7 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 testdata/input/query/select_singer_with_groupby_group_hints.sql create mode 100644 testdata/result/query/select_singer_with_groupby_group_hints.sql.txt create mode 100644 testdata/result/statement/select_singer_with_groupby_group_hints.sql.txt diff --git a/ast/ast.go b/ast/ast.go index 243c9f6a..b7c1c031 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -992,13 +992,14 @@ type Where struct { // GroupBy is GROUP BY clause node. // -// GROUP BY {{.Exprs | sqlJoin ","}} +// GROUP {{.Hint | sqlOpt}} BY {{.Exprs | sqlJoin ","}} type GroupBy struct { // pos = Group // end = Exprs[$].end Group token.Pos // position of "GROUP" keyword + Hint *Hint // optional Exprs []Expr // len(Exprs) > 0 } @@ -2302,7 +2303,6 @@ type DropSchema struct { Name *Ident } - // CreateDatabase is CREATE DATABASE statement node. // // CREATE DATABASE {{.Name | sql}} diff --git a/ast/sql.go b/ast/sql.go index dd365134..5161e53c 100644 --- a/ast/sql.go +++ b/ast/sql.go @@ -281,7 +281,7 @@ func (w *Where) SQL() string { } func (g *GroupBy) SQL() string { - return "GROUP BY " + sqlJoin(g.Exprs, ", ") + return "GROUP " + sqlOpt("", g.Hint, " ") + "BY " + sqlJoin(g.Exprs, ", ") } func (h *Having) SQL() string { @@ -776,7 +776,9 @@ func (s *CreateSchema) SQL() string { return "CREATE" + strOpt(s.OrReplace, " OR REPLACE") + " SCHEMA " + strOpt(s.IfNotExists, "IF NOT EXISTS ") + s.Name.SQL() } -func (s *DropSchema) SQL() string { return "DROP SCHEMA " + strOpt(s.IfExists, "IF EXISTS ") + s.Name.SQL() } +func (s *DropSchema) SQL() string { + return "DROP SCHEMA " + strOpt(s.IfExists, "IF EXISTS ") + s.Name.SQL() +} func (d *AlterDatabase) SQL() string { return "ALTER DATABASE " + d.Name.SQL() + " SET " + d.Options.SQL() diff --git a/ast/walk_internal.go b/ast/walk_internal.go index fce47748..94b09f19 100644 --- a/ast/walk_internal.go +++ b/ast/walk_internal.go @@ -120,6 +120,7 @@ func walkInternal(node Node, v Visitor, stack []*stackItem) []*stackItem { case *GroupBy: stack = append(stack, &stackItem{nodes: wrapNodes(n.Exprs), visitor: v.Field("Exprs")}) + stack = append(stack, &stackItem{node: wrapNode(n.Hint), visitor: v.Field("Hint")}) case *Having: stack = append(stack, &stackItem{node: wrapNode(n.Expr), visitor: v.Field("Expr")}) diff --git a/parser.go b/parser.go index 9fd1c9b9..766b9703 100644 --- a/parser.go +++ b/parser.go @@ -794,11 +794,13 @@ func (p *Parser) tryParseGroupBy() *ast.GroupBy { return nil } pos := p.expect("GROUP").Pos + hint := p.tryParseHint() p.expect("BY") exprs := parseCommaSeparatedList(p, p.parseExpr) return &ast.GroupBy{ Group: pos, + Hint: hint, Exprs: exprs, } } diff --git a/testdata/input/query/select_singer_with_groupby_group_hints.sql b/testdata/input/query/select_singer_with_groupby_group_hints.sql new file mode 100644 index 00000000..24d5d9dd --- /dev/null +++ b/testdata/input/query/select_singer_with_groupby_group_hints.sql @@ -0,0 +1,6 @@ +SELECT + FirstName, BirthDate +FROM + Singers +GROUP @{GROUP_METHOD=HASH_GROUP} BY + FirstName, BirthDate diff --git a/testdata/result/query/select_singer_with_groupby_group_hints.sql.txt b/testdata/result/query/select_singer_with_groupby_group_hints.sql.txt new file mode 100644 index 00000000..315dafa9 --- /dev/null +++ b/testdata/result/query/select_singer_with_groupby_group_hints.sql.txt @@ -0,0 +1,79 @@ +--- select_singer_with_groupby_group_hints.sql +SELECT + FirstName, BirthDate +FROM + Singers +GROUP @{GROUP_METHOD=HASH_GROUP} BY + FirstName, BirthDate + +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 9, + NameEnd: 18, + Name: "FirstName", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 20, + NameEnd: 29, + Name: "BirthDate", + }, + }, + }, + From: &ast.From{ + From: 30, + Source: &ast.TableName{ + Table: &ast.Ident{ + NamePos: 37, + NameEnd: 44, + Name: "Singers", + }, + }, + }, + GroupBy: &ast.GroupBy{ + Group: 45, + Hint: &ast.Hint{ + Atmark: 51, + Rbrace: 76, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 53, + NameEnd: 65, + Name: "GROUP_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 66, + NameEnd: 76, + Name: "HASH_GROUP", + }, + }, + }, + }, + Exprs: []ast.Expr{ + &ast.Ident{ + NamePos: 83, + NameEnd: 92, + Name: "FirstName", + }, + &ast.Ident{ + NamePos: 94, + NameEnd: 103, + Name: "BirthDate", + }, + }, + }, + }, +} + +--- SQL +SELECT FirstName, BirthDate FROM Singers GROUP @{GROUP_METHOD=HASH_GROUP} BY FirstName, BirthDate diff --git a/testdata/result/statement/select_singer_with_groupby_group_hints.sql.txt b/testdata/result/statement/select_singer_with_groupby_group_hints.sql.txt new file mode 100644 index 00000000..315dafa9 --- /dev/null +++ b/testdata/result/statement/select_singer_with_groupby_group_hints.sql.txt @@ -0,0 +1,79 @@ +--- select_singer_with_groupby_group_hints.sql +SELECT + FirstName, BirthDate +FROM + Singers +GROUP @{GROUP_METHOD=HASH_GROUP} BY + FirstName, BirthDate + +--- AST +&ast.QueryStatement{ + Query: &ast.Select{ + Results: []ast.SelectItem{ + &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 9, + NameEnd: 18, + Name: "FirstName", + }, + }, + &ast.ExprSelectItem{ + Expr: &ast.Ident{ + NamePos: 20, + NameEnd: 29, + Name: "BirthDate", + }, + }, + }, + From: &ast.From{ + From: 30, + Source: &ast.TableName{ + Table: &ast.Ident{ + NamePos: 37, + NameEnd: 44, + Name: "Singers", + }, + }, + }, + GroupBy: &ast.GroupBy{ + Group: 45, + Hint: &ast.Hint{ + Atmark: 51, + Rbrace: 76, + Records: []*ast.HintRecord{ + &ast.HintRecord{ + Key: &ast.Path{ + Idents: []*ast.Ident{ + &ast.Ident{ + NamePos: 53, + NameEnd: 65, + Name: "GROUP_METHOD", + }, + }, + }, + Value: &ast.Ident{ + NamePos: 66, + NameEnd: 76, + Name: "HASH_GROUP", + }, + }, + }, + }, + Exprs: []ast.Expr{ + &ast.Ident{ + NamePos: 83, + NameEnd: 92, + Name: "FirstName", + }, + &ast.Ident{ + NamePos: 94, + NameEnd: 103, + Name: "BirthDate", + }, + }, + }, + }, +} + +--- SQL +SELECT FirstName, BirthDate FROM Singers GROUP @{GROUP_METHOD=HASH_GROUP} BY FirstName, BirthDate