sq/libsq/ast/orderby.go
Neil O'Toole 7c56377b40
Struct alignment (#369)
* Field alignment
2024-01-27 00:11:24 -07:00

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)
}