mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-24 00:22:57 +03:00
44d27207f8
* Column-only queries
121 lines
2.9 KiB
Go
121 lines
2.9 KiB
Go
package ast
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"github.com/neilotoole/sq/libsq/ast/internal/slq"
|
|
)
|
|
|
|
// RowRangeNode models a range, effectively {OFFSET,LIMIT}.
|
|
type RowRangeNode struct {
|
|
baseNode
|
|
Offset int
|
|
Limit int
|
|
}
|
|
|
|
func newRowRangeNode(ctx *slq.RowRangeContext, offset, limit int) *RowRangeNode {
|
|
rr := &RowRangeNode{}
|
|
rr.ctx = ctx
|
|
rr.text = ctx.GetText()
|
|
rr.Offset = offset
|
|
rr.Limit = limit
|
|
return rr
|
|
}
|
|
|
|
// String implements ast.Node.
|
|
func (rr *RowRangeNode) String() string {
|
|
return rr.Text()
|
|
}
|
|
|
|
// Range returns the offset and limit.
|
|
func (rr *RowRangeNode) Range() (offset, limit int) {
|
|
offset = rr.Offset
|
|
limit = rr.Limit
|
|
return offset, limit
|
|
}
|
|
|
|
// SetParent implements ast.Node.
|
|
func (rr *RowRangeNode) SetParent(parent Node) error {
|
|
seg, ok := parent.(*SegmentNode)
|
|
if !ok {
|
|
return errorf("%T requires parent of type *%s", rr, typeSegmentNode)
|
|
}
|
|
rr.parent = seg
|
|
return nil
|
|
}
|
|
|
|
// VisitRowRange implements slq.SLQVisitor.
|
|
func (v *parseTreeVisitor) VisitRowRange(ctx *slq.RowRangeContext) any {
|
|
// [] select all rows (no range)
|
|
// [1] select row[1]
|
|
// [10:15] select rows 10 thru 15
|
|
// [0:15] select rows 0 thru 15
|
|
// [:15] same as above (0 thru 15)
|
|
// [10:] select all rows from 10 onwards
|
|
|
|
if ctx.COLON() == nil && len(ctx.AllNN()) == 0 {
|
|
// [] select all rows, aka no range
|
|
return nil
|
|
}
|
|
|
|
if ctx.COLON() == nil {
|
|
// [1] -- select row[1]
|
|
if len(ctx.AllNN()) != 1 {
|
|
return errorf("row range: expected one integer but got %d", len(ctx.AllNN()))
|
|
}
|
|
|
|
i, _ := strconv.Atoi(ctx.AllNN()[0].GetText())
|
|
rr := newRowRangeNode(ctx, i, 1)
|
|
return v.cur.AddChild(rr)
|
|
}
|
|
|
|
// there's a colon... can only be one or two ints
|
|
if len(ctx.AllNN()) > 2 {
|
|
return errorf("row range: expected one or two integers but got %d", len(ctx.AllNN()))
|
|
}
|
|
|
|
if len(ctx.AllNN()) == 2 {
|
|
// [10:15] -- select rows 10 thru 15
|
|
offset, _ := strconv.Atoi(ctx.AllNN()[0].GetText())
|
|
finish, _ := strconv.Atoi(ctx.AllNN()[1].GetText())
|
|
limit := finish - offset
|
|
rr := newRowRangeNode(ctx, offset, limit)
|
|
return v.cur.AddChild(rr)
|
|
}
|
|
|
|
// it's one of these two cases:
|
|
// [:15] (0 thru 15)
|
|
// [10:] select all rows from 10 onwards
|
|
// so we need to determine if the INT is before or after the colon
|
|
var offset int
|
|
limit := -1
|
|
|
|
if ctx.COLON().GetSymbol().GetTokenIndex() < ctx.AllNN()[0].GetSymbol().GetTokenIndex() {
|
|
// [:15] (0 thru 15)
|
|
offset = 0
|
|
limit, _ = strconv.Atoi(ctx.AllNN()[0].GetText())
|
|
} else {
|
|
// [10:] select all rows from 10 onwards
|
|
offset, _ = strconv.Atoi(ctx.AllNN()[0].GetText())
|
|
}
|
|
|
|
rr := newRowRangeNode(ctx, offset, limit)
|
|
return v.cur.AddChild(rr)
|
|
}
|
|
|
|
// verifyRowRange validates the RowRangeNode element.
|
|
func verifyRowRange(w *Walker, node Node) error {
|
|
rr, ok := node.(*RowRangeNode)
|
|
if !ok {
|
|
return errorf("expected %s but got %T", typeRowRangeNode, node)
|
|
}
|
|
|
|
if w.state != nil {
|
|
return errorf("only one row range element permitted")
|
|
}
|
|
|
|
w.state = rr
|
|
// TODO: check that the row range is after a selectable
|
|
return nil
|
|
}
|