mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-18 21:52:28 +03:00
189 lines
4.8 KiB
Go
189 lines
4.8 KiB
Go
|
package ast
|
||
|
|
||
|
// narrowTblSel takes a generic selector, and if appropriate, converts it
|
||
|
// to an *ast.TblSelectorNode.
|
||
|
func narrowTblSel(_ *Walker, node Node) error {
|
||
|
// node is guaranteed to be a selector node
|
||
|
sel, ok := node.(*SelectorNode)
|
||
|
if !ok {
|
||
|
return errorf("expected %T but got %T", sel, node)
|
||
|
}
|
||
|
|
||
|
seg, ok := sel.Parent().(*SegmentNode)
|
||
|
if !ok {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
tblSel *TblSelectorNode
|
||
|
err error
|
||
|
)
|
||
|
|
||
|
if seg.SegIndex() == 0 {
|
||
|
// If this is the first segment, then the selector MUST refer
|
||
|
// to a table. E.g.
|
||
|
//
|
||
|
// $ sq '.data'
|
||
|
//
|
||
|
// ".data" must be a table.
|
||
|
if tblSel, err = newTblSelector(sel); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nodeReplace(sel, tblSel)
|
||
|
}
|
||
|
|
||
|
prevType, err := seg.Prev().ChildType()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if prevType == typeHandleNode {
|
||
|
var handleNode *HandleNode
|
||
|
if handleNode, ok = seg.Prev().Children()[0].(*HandleNode); !ok {
|
||
|
return errorf("syntax error: expected %T, but got %T", handleNode, seg.Prev().Children()[0])
|
||
|
}
|
||
|
|
||
|
// this means that this selector must be a table selector
|
||
|
if tblSel, err = newTblSelector(sel); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
tblSel.handle = handleNode.Text()
|
||
|
return nodeReplace(sel, tblSel)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// narrowTblColSel takes a generic selector, and if appropriate, replaces it
|
||
|
// with a TblColSelectorNode.
|
||
|
func narrowTblColSel(w *Walker, node Node) error {
|
||
|
// node is guaranteed to be type SelectorNode
|
||
|
sel, ok := node.(*SelectorNode)
|
||
|
if !ok {
|
||
|
return errorf("expected %T but got %T", sel, node)
|
||
|
}
|
||
|
|
||
|
parent := sel.Parent()
|
||
|
switch parent := parent.(type) {
|
||
|
case *JoinConstraint, *FuncNode, *OrderByTermNode, *GroupByNode, *ExprNode:
|
||
|
if sel.name1 == "" {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
tblColSelNode, err := newTblColSelectorNode(sel)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nodeReplace(sel, tblColSelNode)
|
||
|
case *SegmentNode:
|
||
|
// if the parent is a segment, this is a "top-level" selector.
|
||
|
// Only top-level selectors after the final tabler seg are
|
||
|
// convert to TblColSelectorNode.
|
||
|
tablerSeg, err := NewInspector(w.root.(*AST)).FindFinalTablerSegment()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if parent.SegIndex() <= tablerSeg.SegIndex() {
|
||
|
// Skipping this selector because it's not after the final selectable segment
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if sel.name1 == "" {
|
||
|
// It's only a TblColSelectorNode if name1 is set.
|
||
|
// So, it's either a ColSelectorNode or a TblSelectorNode.
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
tblColSelNode, err := newTblColSelectorNode(sel)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nodeReplace(sel, tblColSelNode)
|
||
|
|
||
|
default:
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// narrowColSel takes a generic selector, and if appropriate, converts it to a ColSel.
|
||
|
func narrowColSel(w *Walker, node Node) error {
|
||
|
// node is guaranteed to be type SelectorNode
|
||
|
sel, ok := node.(*SelectorNode)
|
||
|
if !ok {
|
||
|
return errorf("expected %T but got %T", sel, node)
|
||
|
}
|
||
|
|
||
|
parent := sel.Parent()
|
||
|
|
||
|
switch parent := parent.(type) {
|
||
|
case *JoinConstraint, *FuncNode, *OrderByTermNode, *GroupByNode, *ExprNode:
|
||
|
colSel, err := newColSelectorNode(sel)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nodeReplace(sel, colSel)
|
||
|
case *SegmentNode:
|
||
|
// if the parent is a segment, this is a "top-level" selector.
|
||
|
// Only top-level selectors after the final tabler seg are
|
||
|
// convert to colSels.
|
||
|
tablerSeg, err := NewInspector(w.root.(*AST)).FindFinalTablerSegment()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
if parent.SegIndex() <= tablerSeg.SegIndex() {
|
||
|
// Skipping this selector because it's not after the final selectable segment
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
colSel, err := newColSelectorNode(sel)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return nodeReplace(sel, colSel)
|
||
|
|
||
|
default:
|
||
|
// Skipping this selector, as parent is not of a relevant type
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// determineJoinTables attempts to determine the tables that a JOIN refers to.
|
||
|
func determineJoinTables(_ *Walker, node Node) error {
|
||
|
// node is guaranteed to be FnJoin
|
||
|
fnJoin, ok := node.(*JoinNode)
|
||
|
if !ok {
|
||
|
return errorf("expected *FnJoin but got %T", node)
|
||
|
}
|
||
|
|
||
|
seg, ok := fnJoin.Parent().(*SegmentNode)
|
||
|
if !ok {
|
||
|
return errorf("JOIN() must have a *SegmentNode parent, but got %T", fnJoin.Parent())
|
||
|
}
|
||
|
|
||
|
prevSeg := seg.Prev()
|
||
|
if prevSeg == nil {
|
||
|
return errorf("JOIN() must not be in the first segment")
|
||
|
}
|
||
|
|
||
|
if len(prevSeg.Children()) != 2 || len(nodesWithType(prevSeg.Children(), typeTblSelectorNode)) != 2 {
|
||
|
return errorf("JOIN() must have two table selectors in the preceding segment")
|
||
|
}
|
||
|
|
||
|
fnJoin.leftTbl, ok = prevSeg.Children()[0].(*TblSelectorNode)
|
||
|
if !ok {
|
||
|
return errorf("JOIN() expected table selector in previous segment, but was %T(%s)", prevSeg.Children()[0],
|
||
|
prevSeg.Children()[0].Text())
|
||
|
}
|
||
|
fnJoin.rightTbl, ok = prevSeg.Children()[1].(*TblSelectorNode)
|
||
|
if !ok {
|
||
|
return errorf("JOIN() expected table selector in previous segment, but was %T(%s)", prevSeg.Children()[1],
|
||
|
prevSeg.Children()[1].Text())
|
||
|
}
|
||
|
return nil
|
||
|
}
|