mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-18 05:31:38 +03:00
7c56377b40
* Field alignment
172 lines
4.2 KiB
Go
172 lines
4.2 KiB
Go
package ast
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/neilotoole/sq/libsq/ast/internal/slq"
|
|
)
|
|
|
|
// OrderByNode implements the SQL "ORDER BY" clause.
|
|
type OrderByNode struct {
|
|
baseNode
|
|
}
|
|
|
|
// String returns a log/debug-friendly representation.
|
|
func (n *OrderByNode) String() string {
|
|
return nodeString(n)
|
|
}
|
|
|
|
// Terms returns a new slice containing the ordering terms of the OrderByNode.
|
|
// The returned slice has at least one term.
|
|
func (n *OrderByNode) Terms() []*OrderByTermNode {
|
|
terms := make([]*OrderByTermNode, len(n.children))
|
|
for i := range n.children {
|
|
terms[i], _ = n.children[i].(*OrderByTermNode)
|
|
}
|
|
return terms
|
|
}
|
|
|
|
// AddChild implements Node.AddChild. It returns an error
|
|
// if child is nil or not type OrderByTermNode.
|
|
func (n *OrderByNode) AddChild(child Node) error {
|
|
term, ok := child.(*OrderByTermNode)
|
|
if !ok {
|
|
return errorf("can only add child of type %T to %T but got %T", term, n, child)
|
|
}
|
|
|
|
n.addChild(child)
|
|
return nil
|
|
}
|
|
|
|
// SetChildren implements ast.Node.
|
|
func (n *OrderByNode) SetChildren(children []Node) error {
|
|
if len(children) > 1 {
|
|
return errorf("%T can have only 1 child but attempt to set %d children",
|
|
n, len(children))
|
|
}
|
|
|
|
for i := range children {
|
|
if _, ok := children[i].(*OrderByTermNode); !ok {
|
|
return errorf("illegal child type %T {%s} for %T", children[i], children[i], n)
|
|
}
|
|
}
|
|
|
|
n.doSetChildren(children)
|
|
return nil
|
|
}
|
|
|
|
// OrderByDirection specifies the "ORDER BY" direction.
|
|
type OrderByDirection string
|
|
|
|
const (
|
|
// OrderByDirectionNone is the default order direction.
|
|
OrderByDirectionNone OrderByDirection = ""
|
|
|
|
// OrderByDirectionAsc is the ascending (DESC) order direction.
|
|
OrderByDirectionAsc OrderByDirection = "ASC"
|
|
|
|
// OrderByDirectionDesc is the descending (DESC) order direction.
|
|
OrderByDirectionDesc OrderByDirection = "DESC"
|
|
)
|
|
|
|
// OrderByTermNode is a child of OrderByNode.
|
|
type OrderByTermNode struct {
|
|
direction OrderByDirection
|
|
baseNode
|
|
}
|
|
|
|
// AddChild accepts a single child of type *SelectorNode.
|
|
func (n *OrderByTermNode) AddChild(child Node) error {
|
|
if len(n.children) > 0 {
|
|
return errorf("%T is only allowed a single child", n)
|
|
}
|
|
|
|
selNode, ok := child.(*SelectorNode)
|
|
if !ok {
|
|
return errorf("illegal %T child type %T: %s", n, child, child)
|
|
}
|
|
|
|
n.addChild(selNode)
|
|
return child.SetParent(n)
|
|
}
|
|
|
|
// SetChildren implements ast.Node.
|
|
func (n *OrderByTermNode) SetChildren(children []Node) error {
|
|
switch len(children) {
|
|
case 0:
|
|
// fallthrough
|
|
case 1:
|
|
if _, ok := children[0].(Selector); !ok {
|
|
return errorf("illegal child type %T {%s} for %T", children[0], children[0], n)
|
|
}
|
|
default:
|
|
return errorf("%T can have only 1 child but attempt to set %d children",
|
|
n, len(children))
|
|
}
|
|
|
|
n.doSetChildren(children)
|
|
return nil
|
|
}
|
|
|
|
// Selector returns the ordering term's selector.
|
|
func (n *OrderByTermNode) Selector() Node {
|
|
return n.children[0]
|
|
}
|
|
|
|
// Direction returns the ordering term's direction.
|
|
func (n *OrderByTermNode) Direction() OrderByDirection {
|
|
return n.direction
|
|
}
|
|
|
|
// String returns a log/debug-friendly representation.
|
|
func (n *OrderByTermNode) String() string {
|
|
return nodeString(n)
|
|
}
|
|
|
|
// VisitOrderBy implements slq.SLQVisitor.
|
|
func (v *parseTreeVisitor) VisitOrderBy(ctx *slq.OrderByContext) interface{} {
|
|
if existing := FindNodes[*OrderByNode](v.cur.ast()); len(existing) > 0 {
|
|
return errorf("only one order_by() clause allowed")
|
|
}
|
|
|
|
node := &OrderByNode{}
|
|
node.parent = v.cur
|
|
node.ctx = ctx
|
|
node.text = ctx.GetText()
|
|
|
|
if err := v.cur.AddChild(node); err != nil {
|
|
return err
|
|
}
|
|
|
|
return v.using(node, func() any {
|
|
// This will result in VisitOrderByTerm being called on the children.
|
|
return v.VisitChildren(ctx)
|
|
})
|
|
}
|
|
|
|
// VisitOrderByTerm implements slq.SLQVisitor.
|
|
func (v *parseTreeVisitor) VisitOrderByTerm(ctx *slq.OrderByTermContext) interface{} {
|
|
node := &OrderByTermNode{}
|
|
node.parent = v.cur
|
|
node.ctx = ctx
|
|
node.text = ctx.GetText()
|
|
if strings.HasSuffix(node.text, "+") {
|
|
node.text = strings.TrimSuffix(node.text, "+")
|
|
node.direction = OrderByDirectionAsc
|
|
} else if strings.HasSuffix(node.text, "-") {
|
|
node.text = strings.TrimSuffix(node.text, "-")
|
|
node.direction = OrderByDirectionDesc
|
|
}
|
|
|
|
selNode, err := newSelectorNode(node, ctx.Selector())
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
if err = node.AddChild(selNode); err != nil {
|
|
return err
|
|
}
|
|
|
|
return v.cur.AddChild(node)
|
|
}
|