Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions cmd/gosqlx/cmd/sql_formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,28 @@ func (f *SQLFormatter) formatSelect(stmt *ast.SelectStatement) error {
f.writeNewline()
f.writeKeyword("ORDER BY")
f.builder.WriteString(" ")
f.formatExpressionList(stmt.OrderBy, ", ")
for i, orderBy := range stmt.OrderBy {
if i > 0 {
f.builder.WriteString(", ")
}
if orderBy.Expression != nil {
if err := f.formatExpression(orderBy.Expression); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to format ORDER BY expression: %v\n", err)
}
}
// Add ASC/DESC
if !orderBy.Ascending {
f.builder.WriteString(" DESC")
}
// Add NULLS FIRST/LAST if specified
if orderBy.NullsFirst != nil {
if *orderBy.NullsFirst {
f.builder.WriteString(" NULLS FIRST")
} else {
f.builder.WriteString(" NULLS LAST")
}
}
}
}

// LIMIT clause
Expand Down Expand Up @@ -537,7 +558,26 @@ func (f *SQLFormatter) formatWindowSpec(spec *ast.WindowSpec) error {
}
f.writeKeyword("ORDER BY")
f.builder.WriteString(" ")
f.formatExpressionList(spec.OrderBy, ", ")
for i, orderBy := range spec.OrderBy {
if i > 0 {
f.builder.WriteString(", ")
}
if orderBy.Expression != nil {
if err := f.formatExpression(orderBy.Expression); err != nil {
fmt.Fprintf(os.Stderr, "Warning: failed to format window ORDER BY expression: %v\n", err)
}
}
if !orderBy.Ascending {
f.builder.WriteString(" DESC")
}
if orderBy.NullsFirst != nil {
if *orderBy.NullsFirst {
f.builder.WriteString(" NULLS FIRST")
} else {
f.builder.WriteString(" NULLS LAST")
}
}
}
}

if spec.FrameClause != nil {
Expand Down
12 changes: 9 additions & 3 deletions pkg/gosqlx/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,9 @@ func (cc *columnCollector) collectFromNode(node ast.Node) {
cc.collectFromExpression(n.Having)
}
for _, ob := range n.OrderBy {
cc.collectFromExpression(ob)
if ob.Expression != nil {
cc.collectFromExpression(ob.Expression)
}
}
if n.With != nil {
cc.collectFromNode(n.With)
Expand Down Expand Up @@ -670,7 +672,9 @@ func (qcc *qualifiedColumnCollector) collectFromNode(node ast.Node) {
qcc.collectFromExpression(n.Having)
}
for _, ob := range n.OrderBy {
qcc.collectFromExpression(ob)
if ob.Expression != nil {
qcc.collectFromExpression(ob.Expression)
}
}
if n.With != nil {
qcc.collectFromNode(n.With)
Expand Down Expand Up @@ -833,7 +837,9 @@ func (fc *functionCollector) collectFromNode(node ast.Node) {
fc.collectFromExpression(n.Having)
}
for _, ob := range n.OrderBy {
fc.collectFromExpression(ob)
if ob.Expression != nil {
fc.collectFromExpression(ob.Expression)
}
}
if n.With != nil {
fc.collectFromNode(n.With)
Expand Down
30 changes: 26 additions & 4 deletions pkg/sql/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,27 @@ func (t *TableReference) statementNode() {}
func (t TableReference) TokenLiteral() string { return t.Name }
func (t TableReference) Children() []Node { return nil }

// OrderByExpression represents an ORDER BY clause element with direction and NULL ordering
type OrderByExpression struct {
Expression Expression // The expression to order by
Ascending bool // true for ASC (default), false for DESC
NullsFirst *bool // nil = default behavior, true = NULLS FIRST, false = NULLS LAST
}

func (*OrderByExpression) expressionNode() {}
func (o *OrderByExpression) TokenLiteral() string { return "ORDER BY" }
func (o *OrderByExpression) Children() []Node {
if o.Expression != nil {
return []Node{o.Expression}
}
return nil
}

// WindowSpec represents a window specification
type WindowSpec struct {
Name string
PartitionBy []Expression
OrderBy []Expression
OrderBy []OrderByExpression
FrameClause *WindowFrame
}

Expand All @@ -126,7 +142,10 @@ func (w WindowSpec) TokenLiteral() string { return "WINDOW" }
func (w WindowSpec) Children() []Node {
children := make([]Node, 0)
children = append(children, nodifyExpressions(w.PartitionBy)...)
children = append(children, nodifyExpressions(w.OrderBy)...)
for _, orderBy := range w.OrderBy {
orderBy := orderBy // G601: Create local copy to avoid memory aliasing
children = append(children, &orderBy)
}
if w.FrameClause != nil {
children = append(children, w.FrameClause)
}
Expand Down Expand Up @@ -162,7 +181,7 @@ type SelectStatement struct {
GroupBy []Expression
Having Expression
Windows []WindowSpec
OrderBy []Expression
OrderBy []OrderByExpression
Limit *int
Offset *int
}
Expand Down Expand Up @@ -195,7 +214,10 @@ func (s SelectStatement) Children() []Node {
window := window // G601: Create local copy to avoid memory aliasing
children = append(children, &window)
}
children = append(children, nodifyExpressions(s.OrderBy)...)
for _, orderBy := range s.OrderBy {
orderBy := orderBy // G601: Create local copy to avoid memory aliasing
children = append(children, &orderBy)
}
return children
}

Expand Down
7 changes: 5 additions & 2 deletions pkg/sql/ast/dml.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Select struct {
Where Expression
GroupBy []Expression
Having Expression
OrderBy []Expression
OrderBy []OrderByExpression
Limit *int64
Offset *int64
}
Expand All @@ -29,7 +29,10 @@ func (s Select) Children() []Node {
if s.Having != nil {
children = append(children, s.Having)
}
children = append(children, nodifyExpressions(s.OrderBy)...)
for _, orderBy := range s.OrderBy {
orderBy := orderBy // G601: Create local copy to avoid memory aliasing
children = append(children, &orderBy)
}
return children
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/ast/dml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestSelect(t *testing.T) {
From: []TableReference{
{Name: "users"},
},
OrderBy: []Expression{&Identifier{Name: "created_at"}},
OrderBy: []OrderByExpression{{Expression: &Identifier{Name: "created_at"}, Ascending: true}},
},
wantLiteral: "SELECT",
minChildren: 3,
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/ast/nodes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ func TestWindowSpec(t *testing.T) {
windowSpec: &WindowSpec{
Name: "w",
PartitionBy: []Expression{&Identifier{Name: "dept"}},
OrderBy: []Expression{&Identifier{Name: "salary"}},
OrderBy: []OrderByExpression{{Expression: &Identifier{Name: "salary"}, Ascending: true}},
},
wantLiteral: "WINDOW",
wantChildren: 2,
Expand Down
8 changes: 5 additions & 3 deletions pkg/sql/ast/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var (
New: func() interface{} {
return &SelectStatement{
Columns: make([]Expression, 0, 4),
OrderBy: make([]Expression, 0, 1),
OrderBy: make([]OrderByExpression, 0, 1),
}
},
}
Expand Down Expand Up @@ -237,8 +237,10 @@ func PutSelectStatement(stmt *SelectStatement) {
for _, col := range stmt.Columns {
PutExpression(col)
}
for _, expr := range stmt.OrderBy {
PutExpression(expr)
for _, orderBy := range stmt.OrderBy {
if orderBy.Expression != nil {
PutExpression(orderBy.Expression)
}
}
if stmt.Where != nil {
PutExpression(stmt.Where)
Expand Down
Loading
Loading