query/sql/sql_parser.go (1,542 lines of code) (raw):
// Copyright (c) 2017-2018 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package sql
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/uber/aresdb/common"
queryCom "github.com/uber/aresdb/query/common"
"github.com/uber/aresdb/query/sql/antlrgen"
"github.com/uber/aresdb/query/sql/tree"
"github.com/uber/aresdb/query/sql/util"
)
const (
_aqlPrefix = "aql_"
// supported query level
maxlevelWith = 1
maxLevelQuery = 2
// slice default size
defaultSliceCap = 10
// query types
typeWithQuery = 1
typeSubQuery = 2
)
// ExprOrigin defines the expression origin
type ExprOrigin int
const (
// ExprOriginWhere => the expression origin is from where clause
ExprOriginWhere ExprOrigin = iota
// ExprOriginJoinOn => the expression origin is from join on clause
ExprOriginJoinOn
// ExprOriginGroupBy => the expression origin is from groupingElement clause
ExprOriginGroupBy
// ExprOriginOthers => the expression origin is from other clauses case
ExprOriginOthers
)
// SQL2AqlContext is the context of ASTVisitor
type SQL2AqlContext struct {
/*
Rules Of updating level, levelWith, levelQuery and mapXXX
1. level: follow Treeprinter indent. Init value: 0
2. levelWith: increase 1 if VisitWith is called. Init value: 0
3. levelQuery: increase 1 if withQuery is called in VisitWith or VisitTableSubquery is called. Init value: 0
4. mapXXX: create a new mapXXX[mapKey] if a new query is added (ie, VisitWithQuery or VisitTableSubquery). Init value: empty map table.
*/
// level is current tree level
level int
// levelWith is current with level
levelWith int
// levelQuery is current query level
levelQuery int
// MapQueryIdentifier is a mapping table. key=generateKey(...) value=arrayOfIdentifier.
// Identifier can be namedQuery identifier or aliasedRelation identifier
MapQueryIdentifier map[int]string
// MapMeasures is a mapping table. key=generateKey(...) value=arrayOfMeasure
MapMeasures map[int][]queryCom.Measure
// MapDimensions is a mapping table. key=generateKey(...) value=arrayOfDimension
MapDimensions map[int][]queryCom.Dimension
// MapJoinTables is a mapping table. key=generateKey(...) value=arrayOfJoin
MapJoinTables map[int][]queryCom.Join
// MapRowFilters is a mapping table. key=generateKey(...) value=arrayOfRowFilter
MapRowFilters map[int][]string
// MapOrderBy is a mapping table. key=generateKey(...) value=arrayOfSortField
MapOrderBy map[int][]queryCom.SortField
// MapLimit is a mapping table. key=generateKey(...) value=arrayOfLimit
MapLimit map[int]int
mapKey int
timeNow int64
timeFilter queryCom.TimeFilter
timezone string
exprOrigin ExprOrigin
fromJSON []byte
groupByJSON []byte
orderByJSON []byte
queryIdentifierSet map[string]int
exprCheck bool
disableMainGroupBy bool
exprLogicalOp tree.LogicalBinaryExpType
}
// ASTBuilder is a visitor
type ASTBuilder struct {
// Logger is a logger from appConfig
Logger common.Logger
// IStream is input antlr char stream
IStream *antlr.CommonTokenStream
// ParameterPosition is position in sql
ParameterPosition int
// SQL2AqlContext is the context of construncting AQL
SQL2AqlCtx *SQL2AqlContext
aql *queryCom.AQLQuery
// Flag that indicates whether aggregate function is seen
aggFuncExists bool
}
func (v *ASTBuilder) defaultResult() interface{} {
return nil
}
func (v *ASTBuilder) shouldVisitNextChild(node antlr.RuleNode, currentResult interface{}) bool {
return true
}
func (v *ASTBuilder) aggregateResult(node antlr.ParseTree, aggregate interface{}, nextResult interface{}) interface{} {
location := v.getLocation(node)
if nextResult == nil {
panic(fmt.Errorf("%v operation not yet implemented at (line:%d, col:%d)", node.GetText(), location.Line, location.CharPosition))
}
if aggregate == nil {
return nextResult
}
panic(fmt.Errorf("%v operation not yet implemented at (line:%d, col:%d)", node.GetText(), location.Line, location.CharPosition))
}
func (v *ASTBuilder) getQualifiedName(ctx antlrgen.IQualifiedNameContext) *tree.QualifiedName {
var result *tree.QualifiedName
if ctxQualifiedName, ok := ctx.(*antlrgen.QualifiedNameContext); ok {
ctxArr := ctxQualifiedName.AllIdentifier()
parts := make([]string, len(ctxArr))
for i, c := range ctxArr {
if value, ok := v.Visit(c).(*tree.Identifier); ok {
parts[i] = value.Value
}
}
result = tree.NewQualifiedName(parts, nil)
}
return result
}
// VisitTerminal visits the node
func (v *ASTBuilder) VisitTerminal(node antlr.TerminalNode) interface{} { return nil }
// VisitErrorNode visits the node
func (v *ASTBuilder) VisitErrorNode(node antlr.ErrorNode) interface{} { return nil }
// Visit visits the node
func (v *ASTBuilder) Visit(tree antlr.ParseTree) interface{} {
if tree == nil {
return nil
}
return tree.Accept(v)
}
// VisitChildren visits the node
func (v *ASTBuilder) VisitChildren(node antlr.RuleNode) interface{} {
result := v.defaultResult()
if !reflect.ValueOf(node).IsNil() {
n := node.GetChildCount()
for i := 0; i < n && v.shouldVisitNextChild(node, result); i++ {
var c antlr.ParseTree
c = node.GetChild(i).(antlr.ParseTree)
childResult := c.Accept(v)
if v.SQL2AqlCtx.exprCheck == false {
result = v.aggregateResult(c, result, childResult)
}
}
}
return result
}
func (v *ASTBuilder) visitIfPresent(ctx antlr.RuleContext, visitResult reflect.Type) interface{} {
if ctx == nil {
return reflect.Zero(visitResult).Interface()
}
return v.Visit(ctx)
}
func (v *ASTBuilder) visitList(ctxs []antlr.ParserRuleContext) interface{} {
var res = make([]interface{}, len(ctxs))
for i, ctx := range ctxs {
res[i] = v.Visit(ctx)
}
return res
}
// ********************** Visit SQL grammar starts ********************
// ********************** query expressions ********************
// VisitQuery visits the node
func (v *ASTBuilder) VisitQuery(ctx *antlrgen.QueryContext) interface{} {
v.Logger.Debugf("VisitQuery: %s", ctx.GetText())
location := v.getLocation(ctx)
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
if levelQuery >= maxLevelQuery {
panic(fmt.Errorf("only support %v level subquery at (line:%d, col:%d)",
maxLevelQuery, location.Line, location.CharPosition))
}
// handle with
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
classWith := reflect.TypeOf((*tree.With)(nil))
with := v.visitIfPresent(ctx.With(), classWith).(*tree.With)
// handle queryNoWith
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
body, _ := v.Visit(ctx.QueryNoWith()).(*tree.Query)
if body == nil {
panic(fmt.Errorf("missing queryNoWith body at (line:%d, col:%d)", location.Line, location.CharPosition))
}
if levelQuery == 0 {
if valid, err := v.isValidWithOrSubQuery(v.SQL2AqlCtx); !valid || err != nil {
panic(fmt.Errorf("line:%d, col:%d isValidWithOrSubQuery: %v, reason :%v",
location.Line, location.CharPosition, valid, err))
}
}
query := tree.NewQuery(
v.getLocation(ctx),
with,
body.QueryBody,
body.OrderBy,
body.Limit,
)
query.SetValue(fmt.Sprintf("Query: (%s)", v.getText(ctx.BaseParserRuleContext)))
// reset SQL2AqlContext
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return query
}
// VisitWith visits the node
func (v *ASTBuilder) VisitWith(ctx *antlrgen.WithContext) interface{} {
v.Logger.Debugf("VisitWith: %s", ctx.GetText())
location := v.getLocation(ctx)
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
mapKey := v.SQL2AqlCtx.mapKey
levelWith++
levelQuery++
if levelWith > maxlevelWith {
panic(fmt.Errorf("only support %v level with query at (line:%d, col:%d)",
maxlevelWith, location.Line, location.CharPosition))
}
if ctx.RECURSIVE() != nil {
panic(fmt.Errorf("RECURSIVE not yet supported at (line:%d, col:%d)",
location.Line, location.CharPosition))
}
ctxArr := ctx.AllNamedQuery()
arrWithQuery := make([]*tree.WithQuery, len(ctxArr))
for i, c := range ctxArr {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.mapKey = v.generateKey(levelQuery, typeWithQuery, i)
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey] = make([]queryCom.Measure, 0, defaultSliceCap)
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] = make([]queryCom.Dimension, 0, defaultSliceCap)
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey] = make([]queryCom.Join, 0, defaultSliceCap)
v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey] = make([]string, 0, defaultSliceCap)
v.SQL2AqlCtx.MapOrderBy[v.SQL2AqlCtx.mapKey] = make([]queryCom.SortField, 0, defaultSliceCap)
arrWithQuery[i], _ = v.VisitNamedQuery(c.(*antlrgen.NamedQueryContext)).(*tree.WithQuery)
}
with := tree.NewWith(v.getLocation(ctx), false, arrWithQuery)
with.SetValue(fmt.Sprintf("With: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith-1, levelQuery-1)
v.SQL2AqlCtx.mapKey = mapKey
return with
}
// VisitNamedQuery visits the node
func (v *ASTBuilder) VisitNamedQuery(ctx *antlrgen.NamedQueryContext) interface{} {
v.Logger.Debugf("VisitNamedQuery: %s", ctx.GetText())
location := v.getLocation(ctx)
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
// handle name
if ctx.GetName() == nil {
panic(fmt.Errorf("missing with identifier at (line:%d, col:%d)", location.Line, location.CharPosition))
}
identifier := v.getText(ctx.GetName())
v.SQL2AqlCtx.MapQueryIdentifier[v.SQL2AqlCtx.mapKey] = identifier
v.addQIdentifier(v.SQL2AqlCtx, identifier, v.SQL2AqlCtx.mapKey)
name, _ := v.Visit(ctx.GetName()).(*tree.Identifier)
// handle columnAliases
var columnAliases []*tree.Identifier
if ctxColumnAliase, ok := ctx.ColumnAliases().(*antlrgen.ColumnAliasesContext); ok {
ctxArr := ctxColumnAliase.AllIdentifier()
columnAliases = make([]*tree.Identifier, len(ctxArr))
for i, c := range ctxArr {
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey] =
append(v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey],
queryCom.Measure{
Alias: v.getText(c),
})
columnAliases[i], _ = v.Visit(c).(*tree.Identifier)
}
}
// handle query
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
ctxQuery, ok := ctx.Query().(*antlrgen.QueryContext)
if !ok {
panic(fmt.Errorf("missing with query body at (line:%d, col:%d)", location.Line, location.CharPosition))
}
query, _ := v.VisitQuery(ctxQuery).(*tree.Query)
withQuery := tree.NewWithQuery(v.getLocation(ctx),
name,
query,
columnAliases)
withQuery.SetValue(fmt.Sprintf("WithQuery: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return withQuery
}
// VisitQueryNoWith visits the node
func (v *ASTBuilder) VisitQueryNoWith(ctx *antlrgen.QueryNoWithContext) interface{} {
v.Logger.Debugf("VisitQueryNoWith: %s", ctx.GetText())
location := v.getLocation(ctx)
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
// handle queryTerm
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
term := v.Visit(ctx.QueryTerm())
// handle ORDER BY
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.exprOrigin = ExprOriginOthers
var orderBy = v.getOrderBy(ctx)
limit := 0
limitNode := ctx.GetLimit()
if limitParsed, err := strconv.Atoi(v.GetTextIfPresent(limitNode)); limitNode != nil && err != nil {
limitLoc := v.getLocation(limitNode)
v.Logger.Panicf("failed to parse limit %s at (line:%d, col:%d)", limitNode.GetText(), limitLoc.Line, limitLoc.CharPosition)
} else {
limit = limitParsed
}
var query *tree.Query
if qSpec, ok := term.(*tree.QuerySpecification); ok {
qSpecNew := tree.NewQuerySpecification(v.getLocation(ctx),
qSpec.Select, qSpec.From, qSpec.Where, qSpec.GroupBy, qSpec.Having, orderBy, v.GetTextIfPresent(ctx.GetLimit()))
qSpecNew.SetValue(fmt.Sprintf("QuerySpecification: (%s)", v.getText(ctx.QueryTerm())))
v.SQL2AqlCtx.MapLimit[v.SQL2AqlCtx.mapKey] = limit
query = tree.NewQuery(v.getLocation(ctx),
nil,
qSpecNew,
nil,
"")
} else if qBody, ok := term.(*tree.QueryBody); ok {
query = tree.NewQuery(v.getLocation(ctx),
nil,
qBody,
orderBy,
v.GetTextIfPresent(ctx.GetLimit()))
if ctx.GetLimit() != nil {
if levelQuery == 0 {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.MapLimit[v.SQL2AqlCtx.mapKey] = limit
} else {
panic(fmt.Errorf("limit on query level %d > 0 not supported at (line:%d, col:%d)",
levelQuery, location.Line, location.CharPosition))
}
}
} else {
panic(fmt.Errorf("invalid query term: %v at (line:%d, col:%d)", term, location.Line, location.CharPosition))
}
query.SetValue(fmt.Sprintf("Query: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return query
}
// VisitQuerySpecification visits the node
func (v *ASTBuilder) VisitQuerySpecification(ctx *antlrgen.QuerySpecificationContext) interface{} {
v.Logger.Debugf("VisitQuerySpecification: %s", ctx.GetText())
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
// handle from => join/table
// first process from clause so that subquery/withQuery identifier can be found in expression
v.SQL2AqlCtx.exprOrigin = ExprOriginJoinOn
ctxArrRelation := ctx.AllRelation()
arrRelations := make([]tree.IRelation, len(ctxArrRelation))
for i, c := range ctxArrRelation {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
arrRelations[i], _ = v.Visit(c).(tree.IRelation)
}
var myFrom tree.IRelation
if len(arrRelations) > 0 {
relationL := arrRelations[0]
// synthesize implicit join nodes
for i := 1; i < len(arrRelations); i++ {
relationR := arrRelations[i]
relationL = tree.NewJoin(v.getLocation(ctx), tree.IMPLICIT, relationL, relationR, nil)
relationL.SetValue(fmt.Sprintf("Join: (%s)", v.getText(ctxArrRelation[i])))
}
myFrom = relationL
}
// handle select => measure
v.SQL2AqlCtx.exprOrigin = ExprOriginOthers
ctxArrSelectItem := ctx.AllSelectItem()
arrSelectItems := make([]tree.ISelectItem, len(ctxArrSelectItem))
for i, c := range ctxArrSelectItem {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
arrSelectItems[i], _ = v.Visit(c).(tree.ISelectItem)
if i < len(v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey]) {
// handle subquery/withQuery with columnAliases,8
// subquery/withQuery columnalias has higher priority, ignore subquery/withQuery selectSingle identifier
switch item := arrSelectItems[i].(type) {
case *tree.SingleColumn:
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey][i].Expr = util.GetSubstring(item.Expression.GetValue())
case *tree.AllColumns:
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey][i].Expr = v.getText(c)
}
} else {
// handle query or subquery/withQuery w/o columnAliases
switch item := arrSelectItems[i].(type) {
case *tree.SingleColumn:
var alias string
if item.Alias != nil {
alias = util.GetSubstring(item.Alias.GetValue())
}
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey] = append(v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey],
queryCom.Measure{
Alias: alias,
Expr: util.GetSubstring(item.Expression.GetValue()),
})
case *tree.AllColumns:
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey] = append(v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey],
queryCom.Measure{
Expr: v.getText(c),
})
}
}
}
// handle where => rowfilter/timefilter
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.exprOrigin = ExprOriginWhere
v.SQL2AqlCtx.exprCheck = true
v.visitIfPresent(ctx.GetWhere(), reflect.TypeOf((*tree.Expression)(nil)))
v.SQL2AqlCtx.exprCheck = false
myWhere := v.visitIfPresent(ctx.GetWhere(), reflect.TypeOf((*tree.Expression)(nil))).(tree.IExpression)
// handle group by => dimension
if v.SQL2AqlCtx.disableMainGroupBy && levelQuery == 0 && ctx.GroupBy() != nil {
// disable group by clause in manin query if with/subquery exists
location := v.getLocation(ctx.GroupBy())
panic(fmt.Errorf("group by is not allowed at (line:%d, col:%d) since with/subQuery already has group by",
location.Line, location.CharPosition))
}
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.exprOrigin = ExprOriginGroupBy
myGroupBy := v.visitIfPresent(ctx.GroupBy(), reflect.TypeOf((*tree.GroupBy)(nil))).(*tree.GroupBy)
if ctx.GroupBy() != nil && levelQuery > 0 {
v.SQL2AqlCtx.disableMainGroupBy = true
}
// handle having => not support in AQL
if ctx.GetHaving() != nil {
location := v.getLocation(ctx.GetHaving())
panic(fmt.Errorf("having not yet supported at (line:%d, col:%d)", location.Line, location.CharPosition))
}
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.exprOrigin = ExprOriginOthers
v.SQL2AqlCtx.exprCheck = true
v.visitIfPresent(ctx.GetHaving(), reflect.TypeOf((*tree.Expression)(nil)))
v.SQL2AqlCtx.exprCheck = false
myHaving := v.visitIfPresent(ctx.GetHaving(), reflect.TypeOf((*tree.Expression)(nil))).(tree.IExpression)
querySpec := tree.NewQuerySpecification(
v.getLocation(ctx),
tree.NewSelect(v.getLocation(ctx), v.isDistinct(ctx.SetQuantifier()), arrSelectItems),
myFrom,
myWhere,
myGroupBy,
myHaving,
nil,
"")
querySpec.SetValue(fmt.Sprintf("QuerySpecification: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return querySpec
}
// VisitSelectAll visits the node
func (v *ASTBuilder) VisitSelectAll(ctx *antlrgen.SelectAllContext) interface{} {
v.Logger.Debugf("VisitSelectAll: %s", ctx.GetText())
var allColumns *tree.AllColumns
if ctx.QualifiedName() != nil {
allColumns = tree.NewAllColumns(v.getLocation(ctx), v.getQualifiedName(ctx.QualifiedName()))
} else {
allColumns = tree.NewAllColumns(v.getLocation(ctx), nil)
}
allColumns.SetValue(fmt.Sprintf("AllColumns: (%s)", v.getText(ctx.BaseParserRuleContext)))
return allColumns
}
// VisitSelectSingle visits the node
func (v *ASTBuilder) VisitSelectSingle(ctx *antlrgen.SelectSingleContext) interface{} {
v.Logger.Debugf("VisitSelectSingle: %s", ctx.GetText())
v.SQL2AqlCtx.exprCheck = true
v.Visit(ctx.Expression())
v.SQL2AqlCtx.exprCheck = false
expr, _ := v.Visit(ctx.Expression()).(tree.IExpression)
singleColumn := tree.NewSingleColumn(v.getLocation(ctx),
expr,
v.visitIfPresent(ctx.Identifier(), reflect.TypeOf((*tree.Identifier)(nil))).(*tree.Identifier))
singleColumn.SetValue(fmt.Sprintf("SingleColumn: (%s)", v.getText(ctx.BaseParserRuleContext)))
return singleColumn
}
// VisitGroupBy visits the node
func (v *ASTBuilder) VisitGroupBy(ctx *antlrgen.GroupByContext) interface{} {
v.Logger.Debugf("VisitGroupBy: %s", ctx.GetText())
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
ctxArr := ctx.AllGroupingElement()
groupingElements := make([]tree.IGroupingElement, len(ctxArr))
for i, c := range ctxArr {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
groupingElements[i], _ = v.Visit(c).(tree.IGroupingElement)
}
groupBy := tree.NewGroupBy(
v.getLocation(ctx),
v.isDistinct(ctx.SetQuantifier()),
groupingElements)
groupBy.SetValue(fmt.Sprintf("GroupBy: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return groupBy
}
// VisitSingleGroupingSet visits the node
func (v *ASTBuilder) VisitSingleGroupingSet(ctx *antlrgen.SingleGroupingSetContext) interface{} {
v.Logger.Debugf("VisitSingleGroupingSet: %s", ctx.GetText())
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
ctxArr := ctx.GroupingExpressions().(*antlrgen.GroupingExpressionsContext).AllExpression()
columns := make([]tree.IExpression, len(ctxArr))
offset := len(v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey])
for i, c := range ctxArr {
v.SQL2AqlCtx.exprCheck = true
v.Visit(c)
v.SQL2AqlCtx.exprCheck = false
columns[i], _ = v.Visit(c).(tree.IExpression)
alias, expr := v.lookupSQLExpr(v.SQL2AqlCtx, v.SQL2AqlCtx.mapKey, util.GetSubstring(columns[i].GetValue()))
if len(v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey])-offset == i {
// timeBucket or numbericBucket is added into
// v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] via visitFunctionCall
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] =
append(v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey],
queryCom.Dimension{
Alias: alias,
Expr: expr,
})
}
}
simpleGroupBy := tree.NewSimpleGroupBy(
v.getLocation(ctx),
columns)
simpleGroupBy.SetValue(fmt.Sprintf("SimpleGroupBy: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return simpleGroupBy
}
// VisitSortItem visits the node
func (v *ASTBuilder) VisitSortItem(ctx *antlrgen.SortItemContext) interface{} {
v.Logger.Debugf("VisitSortItem: %s", ctx.GetText())
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
if ctx.Expression() == nil {
return nil
}
var ordering tree.OrderType
if ctx.GetOrdering() != nil {
if ctx.GetOrdering().GetText() == tree.OrderTypes[tree.ASC] {
ordering = tree.ASC
} else if ctx.GetOrdering().GetText() == tree.OrderTypes[tree.DESC] {
ordering = tree.DESC
}
}
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.exprCheck = true
v.Visit(ctx.Expression())
v.SQL2AqlCtx.exprCheck = false
expr := v.Visit(ctx.Expression()).(tree.IExpression)
_, name := v.lookupSQLExpr(v.SQL2AqlCtx, v.SQL2AqlCtx.mapKey, util.GetSubstring(expr.GetValue()))
v.SQL2AqlCtx.MapOrderBy[v.SQL2AqlCtx.mapKey] =
append(v.SQL2AqlCtx.MapOrderBy[v.SQL2AqlCtx.mapKey],
queryCom.SortField{
Name: name,
Order: tree.OrderTypes[ordering],
})
sortItem := tree.NewSortItem(v.getLocation(ctx), expr, ordering)
sortItem.SetValue(fmt.Sprintf("SortItem: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return sortItem
}
// ***************** boolean expressions ******************
// VisitExpression visits the node
func (v *ASTBuilder) VisitExpression(ctx *antlrgen.ExpressionContext) interface{} {
v.Logger.Debugf("VisitExpression %s\n", v.getText(ctx))
v.SQL2AqlCtx.exprLogicalOp = tree.NOOP
return v.VisitChildren(ctx)
}
// VisitLogicalBinary visits the node
func (v *ASTBuilder) VisitLogicalBinary(ctx *antlrgen.LogicalBinaryContext) interface{} {
location := v.getLocation(ctx)
if v.SQL2AqlCtx.exprCheck {
v.Logger.Debugf("VisitLogicalBinary check: %s", ctx.GetText())
if ctx.GetOperator() == nil {
panic(fmt.Errorf("missing logicalBinary operator, (line:%d, col:%d)",
location.Line, location.CharPosition))
}
v.Visit(ctx.GetLeft())
v.Visit(ctx.GetRight())
return tree.NewExpression(v.getLocation(ctx))
}
v.Logger.Debugf("VisitLogicalBinary: %s", ctx.GetText())
operator := v.getLogicalBinaryOperator(ctx.GetOperator().GetTokenType())
if operator == tree.OR {
if v.SQL2AqlCtx.exprOrigin == ExprOriginWhere {
v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey] =
append(v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey], v.getText(ctx.BaseParserRuleContext))
} else if v.SQL2AqlCtx.exprOrigin == ExprOriginJoinOn {
last := len(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey]) - 1
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Conditions =
append(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Conditions, v.getText(ctx.BaseParserRuleContext))
}
expr := tree.NewExpression(v.getLocation(ctx))
expr.SetValue(fmt.Sprintf("LogicalBinaryExpression: (%s)", v.getText(ctx.BaseParserRuleContext)))
return expr
}
left, _ := v.Visit(ctx.GetLeft()).(tree.IExpression)
right, _ := v.Visit(ctx.GetRight()).(tree.IExpression)
logicalBinaryExpr := tree.NewLogicalBinaryExpression(
v.getLocation(ctx),
operator,
left,
right)
logicalBinaryExpr.SetValue(fmt.Sprintf("LogicalBinaryExpression: (%s)", v.getText(ctx.BaseParserRuleContext)))
return logicalBinaryExpr
}
// VisitBooleanDefault visits the node
func (v *ASTBuilder) VisitBooleanDefault(ctx *antlrgen.BooleanDefaultContext) interface{} {
if v.SQL2AqlCtx.exprCheck {
v.Logger.Debugf("VisitBooleanDefault check: %s", ctx.GetText())
return v.VisitChildren(ctx)
}
v.Logger.Debugf("VisitBooleanDefault: %s", ctx.GetText())
if v.SQL2AqlCtx.exprOrigin == ExprOriginWhere && !strings.HasPrefix(v.getText(ctx), _aqlPrefix) {
v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey] =
append(v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey], v.getText(ctx.BaseParserRuleContext))
} else if v.SQL2AqlCtx.exprOrigin == ExprOriginJoinOn {
last := len(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey]) - 1
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Conditions =
append(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Conditions, v.getText(ctx.BaseParserRuleContext))
}
expr := tree.NewExpression(v.getLocation(ctx))
expr.SetValue(fmt.Sprintf("BooleanDefault: (%s)", v.getText(ctx.BaseParserRuleContext)))
return expr
}
// VisitLogicalNot visits the node
func (v *ASTBuilder) VisitLogicalNot(ctx *antlrgen.LogicalNotContext) interface{} {
v.Logger.Debugf("VisitLogicalNot check: %s", ctx.GetText())
if v.SQL2AqlCtx.exprCheck {
return v.VisitChildren(ctx)
}
v.Logger.Debugf("VisitLogicalNot: %s", ctx.GetText())
if v.SQL2AqlCtx.exprOrigin == ExprOriginWhere {
v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey] =
append(v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey], v.getText(ctx.BaseParserRuleContext))
} else if v.SQL2AqlCtx.exprOrigin == ExprOriginJoinOn {
last := len(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey]) - 1
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Conditions =
append(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Conditions, v.getText(ctx.BaseParserRuleContext))
}
expr := tree.NewExpression(v.getLocation(ctx))
expr.SetValue(fmt.Sprintf("LogicalNot: (%s)", v.getText(ctx.BaseParserRuleContext)))
return expr
}
// *************** from clause *****************
// VisitJoinRelation visits the node
func (v *ASTBuilder) VisitJoinRelation(ctx *antlrgen.JoinRelationContext) interface{} {
v.Logger.Debugf("VisitJoinRelation: %s", ctx.GetText())
location := v.getLocation(ctx)
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
left, _ := v.Visit(ctx.GetLeft()).(tree.IRelation)
var right tree.IRelation
if ctx.CROSS() != nil {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
right, _ = v.VisitSampledRelation(ctx.GetRight().(*antlrgen.SampledRelationContext)).(tree.IRelation)
join := tree.NewJoin(v.getLocation(ctx), tree.CROSS, left, right, nil)
join.SetValue(fmt.Sprintf("Join: (%s)", v.getText(ctx.BaseParserRuleContext)))
return join
}
var criteria tree.IJoinCriteria
if ctx.NATURAL() != nil {
if levelQuery != 0 {
panic(fmt.Errorf("natural join not supported at subquery/withQuery at (line:%d, col:%d)",
location.Line, location.CharPosition))
}
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
right, _ = v.VisitSampledRelation(ctx.GetRight().(*antlrgen.SampledRelationContext)).(tree.IRelation)
criteria = tree.NewNaturalJoin()
} else {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
right, _ = v.Visit(ctx.GetRightRelation()).(tree.IRelation)
ctxJoinCriteria, ok := ctx.JoinCriteria().(*antlrgen.JoinCriteriaContext)
if !ok {
panic(fmt.Errorf("missing join criteria at (line:%d, col:%d)", location.Line, location.CharPosition))
}
if ctxJoinCriteria.ON() != nil {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
v.SQL2AqlCtx.exprCheck = true
v.Visit(ctx.JoinCriteria().(*antlrgen.JoinCriteriaContext).
BooleanExpression())
v.SQL2AqlCtx.exprCheck = false
joinOn, _ := v.Visit(ctx.JoinCriteria().(*antlrgen.JoinCriteriaContext).
BooleanExpression()).(tree.IExpression)
criteria = tree.NewJoinOn(joinOn)
} else if ctxJoinCriteria.USING() != nil {
ctxArr := ctx.JoinCriteria().(*antlrgen.JoinCriteriaContext).AllIdentifier()
expressions := make([]*tree.Identifier, len(ctxArr))
for i, c := range ctxArr {
expressions[i], _ = v.Visit(c).(*tree.Identifier)
}
criteria = tree.NewJoinUsing(expressions)
}
}
joinType := v.getJoinType(ctx)
if joinType != tree.LEFT {
panic(fmt.Errorf("join type %v not supported yet at (line:%d, col:%d)",
tree.JoinTypes[joinType], location.Line, location.CharPosition))
}
join := tree.NewJoin(v.getLocation(ctx), joinType, left, right, criteria)
join.SetValue(fmt.Sprintf("Join: (%s)", v.getText(ctx.BaseParserRuleContext)))
return join
}
// VisitSampledRelation visits the node
func (v *ASTBuilder) VisitSampledRelation(ctx *antlrgen.SampledRelationContext) interface{} {
v.Logger.Debugf("VisitSampledRelation: %s", ctx.GetText())
location := v.getLocation(ctx)
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
child, _ := v.VisitAliasedRelation(ctx.AliasedRelation().(*antlrgen.AliasedRelationContext)).(tree.IRelation)
if ctx.TABLESAMPLE() != nil {
panic(fmt.Errorf("TABLESAMPLE not implemented at (line:%d, col:%d)", location.Line, location.CharPosition))
}
if child != nil {
child.SetValue(fmt.Sprintf("SampledRelation: (%s)", v.getText(ctx.BaseParserRuleContext)))
}
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
return child
}
// VisitAliasedRelation visits the node
func (v *ASTBuilder) VisitAliasedRelation(ctx *antlrgen.AliasedRelationContext) interface{} {
v.Logger.Debugf("VisitAliasedRelation: %s", ctx.GetText())
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
mapKey := v.SQL2AqlCtx.mapKey
// handle identifier
if ctx.Identifier() != nil {
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey] = append(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey],
queryCom.Join{
Alias: v.getText(ctx.Identifier()),
})
} else {
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey] = append(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey],
queryCom.Join{})
}
// handle relationPrimary
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
child, _ := v.Visit(ctx.RelationPrimary()).(tree.IRelation)
if ctx.Identifier() == nil {
child.SetValue(fmt.Sprintf("Relation: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
v.SQL2AqlCtx.mapKey = mapKey
return child
}
// handle columnAliases
var aliasedRelation *tree.AliasedRelation
identifier, _ := v.Visit(ctx.Identifier()).(*tree.Identifier)
if ctxColumnAliases, ok := ctx.ColumnAliases().(*antlrgen.ColumnAliasesContext); ok {
ctxArr := ctxColumnAliases.AllIdentifier()
columnAliases := make([]*tree.Identifier, len(ctxArr))
last := len(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey]) - 1
subqueryKey := v.generateKey(levelQuery+1, typeSubQuery, last)
for i, c := range ctxArr {
v.setCtxLevels(v.SQL2AqlCtx, level, levelWith, levelQuery)
if i < len(v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey]) {
v.SQL2AqlCtx.MapMeasures[subqueryKey][i].Alias = v.getText(c)
} else {
v.SQL2AqlCtx.MapMeasures[subqueryKey] =
append(v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey],
queryCom.Measure{
Alias: v.getText(c),
})
}
columnAliases[i], _ = v.Visit(c).(*tree.Identifier)
}
aliasedRelation = tree.NewAliasedRelation(
v.getLocation(ctx),
child,
identifier,
columnAliases)
} else {
aliasedRelation = tree.NewAliasedRelation(
v.getLocation(ctx),
child,
identifier,
nil)
}
aliasedRelation.SetValue(fmt.Sprintf("AliasedRelation: (%s)", v.getText(ctx.BaseParserRuleContext)))
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery)
v.SQL2AqlCtx.mapKey = mapKey
return aliasedRelation
}
// VisitTableName visits the node
func (v *ASTBuilder) VisitTableName(ctx *antlrgen.TableNameContext) interface{} {
v.Logger.Debugf("VisitTableName: %s", ctx.GetText())
last := len(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey]) - 1
// check if the table name is a withQ identifier
name := v.getText(ctx.BaseParserRuleContext)
qLevel, _, _ := v.getInfoByKey(v.SQL2AqlCtx.mapKey)
if qLevel == 0 && v.isWithQueryIdentifier(v.SQL2AqlCtx, name) {
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Alias = name
} else {
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey][last].Table = name
}
table := tree.NewTable(v.getLocation(ctx), v.getQualifiedName(ctx.QualifiedName()))
table.SetValue(fmt.Sprintf("Table: (%s)", v.getText(ctx.BaseParserRuleContext)))
return table
}
// VisitSubqueryRelation visits the node
func (v *ASTBuilder) VisitSubqueryRelation(ctx *antlrgen.SubqueryRelationContext) interface{} {
v.Logger.Debugf("VisitSubqueryRelation: %s", ctx.GetText())
level, levelWith, levelQuery := v.getCtxLevels(v.SQL2AqlCtx)
// mapKey is the mapKey of parent query
mapKey := v.SQL2AqlCtx.mapKey
last := len(v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey]) - 1
v.setCtxLevels(v.SQL2AqlCtx, level-1, levelWith, levelQuery+1)
// the index in v.SQL2AqlCtx.mapKey is the index of the aliasedRelation in parent from clause
v.SQL2AqlCtx.mapKey = v.generateKey(levelQuery+1, typeSubQuery, last)
v.SQL2AqlCtx.MapQueryIdentifier[v.SQL2AqlCtx.mapKey] = v.SQL2AqlCtx.MapJoinTables[mapKey][last].Alias
v.addQIdentifier(v.SQL2AqlCtx, v.SQL2AqlCtx.MapJoinTables[mapKey][last].Alias, v.SQL2AqlCtx.mapKey)
v.SQL2AqlCtx.MapMeasures[v.SQL2AqlCtx.mapKey] = make([]queryCom.Measure, 0, defaultSliceCap)
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] = make([]queryCom.Dimension, 0, defaultSliceCap)
v.SQL2AqlCtx.MapJoinTables[v.SQL2AqlCtx.mapKey] = make([]queryCom.Join, 0, defaultSliceCap)
v.SQL2AqlCtx.MapRowFilters[v.SQL2AqlCtx.mapKey] = make([]string, 0, defaultSliceCap)
v.SQL2AqlCtx.MapOrderBy[v.SQL2AqlCtx.mapKey] = make([]queryCom.SortField, 0, defaultSliceCap)
query, _ := v.VisitQuery(ctx.Query().(*antlrgen.QueryContext)).(*tree.Query)
tableSubquery := tree.NewTableSubquery(v.getLocation(ctx), query)
tableSubquery.SetValue(fmt.Sprintf("TableSubquery: (%s)", v.getText(ctx.BaseParserRuleContext)))
return tableSubquery
}
// ********************* primary expressions **********************
// VisitFunctionCall visits the node
func (v *ASTBuilder) VisitFunctionCall(ctx *antlrgen.FunctionCallContext) interface{} {
v.Logger.Debugf("VisitFunctionCall: %s", ctx.GetText())
// 1. timebucket and numbericbucket are only from groupBy clause
// 2. timefilter is only from where clause
location := v.getLocation(ctx)
name := v.getText(ctx.QualifiedName())
udfDef, ok := util.UdfTable[name]
if ok {
if ctx.SetQuantifier() != nil || ctx.Filter() != nil || len(ctx.AllSortItem()) != 0 || ctx.AllExpression() == nil {
panic(fmt.Errorf("quantifier/filter/over/sort not supported in %s function at (line:%d, col:%d)",
name, location.Line, location.CharPosition))
}
if len(ctx.AllExpression()) != udfDef.ArgsNum {
panic(fmt.Errorf("%s should have %d parameters at (line:%d, col:%d)",
name, udfDef.ArgsNum, location.Line, location.CharPosition))
}
if v.hasORInPath(ctx.BaseParserRuleContext) == tree.OR {
panic(fmt.Errorf("function %s can not appear in OR logicalBinaryExpression at (line:%d, col:%d)",
name, location.Line, location.CharPosition))
}
if (udfDef.Type == util.Timebucket || udfDef.Type == util.Numericbucket) && v.SQL2AqlCtx.exprOrigin != ExprOriginGroupBy {
panic(fmt.Errorf("function %s at (line:%d, col:%d) can only appear in group by clause",
name, location.Line, location.CharPosition))
}
if udfDef.Type == util.Timefilter && v.SQL2AqlCtx.exprOrigin != ExprOriginWhere {
panic(fmt.Errorf("function %s at (line:%d, col:%d) can only appear in where clause",
name, location.Line, location.CharPosition))
}
switch udfDef.Type {
case util.Timefilter:
v.setTimefilter(ctx.AllExpression())
case util.TimeNow:
v.setTimeNow(ctx.AllExpression())
case util.Timebucket:
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] = append(
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey],
queryCom.Dimension{
Expr: util.TrimQuote(v.getText(ctx.Expression(0))),
TimeBucketizer: util.TrimQuote(udfDef.Definition),
TimeUnit: util.TrimQuote(v.getText(ctx.Expression(1))),
})
if len(v.SQL2AqlCtx.timezone) == 0 {
v.SQL2AqlCtx.timezone = util.TrimQuote(v.getText(ctx.Expression(2)))
} else if v.SQL2AqlCtx.timezone != util.TrimQuote(v.getText(ctx.Expression(2))) {
panic(fmt.Errorf("different timebucket timezone %s at (line:%d, col:%d)",
v.getText(ctx.Expression(2)), location.Line, location.CharPosition))
}
case util.Numericbucket:
v.setNumericBucketizer(ctx.AllExpression(), udfDef.Definition)
}
} else if strings.HasPrefix(name, _aqlPrefix) {
panic(fmt.Errorf("function %s at (line:%d, col:%d) is not registered in AQL udf",
name, location.Line, location.CharPosition))
}
if exist := util.AggregateFunctions[name]; exist {
v.aggFuncExists = true
}
return tree.NewExpression(v.getLocation(ctx))
}
// VisitUnquotedIdentifier visits the node
func (v *ASTBuilder) VisitUnquotedIdentifier(ctx *antlrgen.UnquotedIdentifierContext) interface{} {
v.Logger.Debugf("VisitUnquotedIdentifier: %s", ctx.GetText())
identifier := tree.NewIdentifier(v.getLocation(ctx), v.getText(ctx.BaseParserRuleContext), false)
identifier.SetValue(fmt.Sprintf("Identifier: (%s)", v.getText(ctx.BaseParserRuleContext)))
return identifier
}
// VisitQuotedIdentifier visits the node
func (v *ASTBuilder) VisitQuotedIdentifier(ctx *antlrgen.QuotedIdentifierContext) interface{} {
v.Logger.Debugf("VisitQuotedIdentifier: %s", ctx.GetText())
token := v.getText(ctx.BaseParserRuleContext)
identifier := strings.Replace(token[1:len(token)-1], "\"\"", "\"", -1)
quotedIdentifier := tree.NewIdentifier(v.getLocation(ctx), identifier, true)
quotedIdentifier.SetValue(fmt.Sprintf("QuotedIdentifier: (%s)", v.getText(ctx.BaseParserRuleContext)))
return quotedIdentifier
}
// VisitDereference visits the node
func (v *ASTBuilder) VisitDereference(ctx *antlrgen.DereferenceContext) interface{} {
v.Logger.Debugf("VisitDereference: %s", ctx.GetText())
location := v.getLocation(ctx.GetBase())
if v.SQL2AqlCtx.mapKey == 0 {
// reject the expression if subquery/withQuery identifier is used in level 0 query
primaryExpr := v.getText(ctx.GetBase())
key := v.isSubOrWithQueryIdentifier(v.SQL2AqlCtx, primaryExpr)
if key > 0 {
panic(fmt.Errorf("subquery/withQuery identifier in expression not supported yet. (line:%d, col:%d)",
location.Line, location.CharPosition))
}
}
return v.VisitChildren(ctx)
}
// ***************** Reserved *****************
// VisitStatementDefault visits the node
func (v *ASTBuilder) VisitStatementDefault(ctx *antlrgen.StatementDefaultContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitQueryTermDefault visits the node
func (v *ASTBuilder) VisitQueryTermDefault(ctx *antlrgen.QueryTermDefaultContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitSetOperation visits the node
func (v *ASTBuilder) VisitSetOperation(ctx *antlrgen.SetOperationContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("setOperation at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitQueryPrimaryDefault visits the node
func (v *ASTBuilder) VisitQueryPrimaryDefault(ctx *antlrgen.QueryPrimaryDefaultContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitTable visits the node
func (v *ASTBuilder) VisitTable(ctx *antlrgen.TableContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("table at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitInlineTable visits the node
func (v *ASTBuilder) VisitInlineTable(ctx *antlrgen.InlineTableContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("inlineTable at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitSubquery visits the node
func (v *ASTBuilder) VisitSubquery(ctx *antlrgen.SubqueryContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("subquery at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitGroupingExpressions visits the node
func (v *ASTBuilder) VisitGroupingExpressions(ctx *antlrgen.GroupingExpressionsContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitSetQuantifier visits the node
func (v *ASTBuilder) VisitSetQuantifier(ctx *antlrgen.SetQuantifierContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitRelationDefault visits the node
func (v *ASTBuilder) VisitRelationDefault(ctx *antlrgen.RelationDefaultContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitJoinType visits the node
func (v *ASTBuilder) VisitJoinType(ctx *antlrgen.JoinTypeContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitJoinCriteria visits the node
func (v *ASTBuilder) VisitJoinCriteria(ctx *antlrgen.JoinCriteriaContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitSampleType visits the node
func (v *ASTBuilder) VisitSampleType(ctx *antlrgen.SampleTypeContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitColumnAliases visits the node
func (v *ASTBuilder) VisitColumnAliases(ctx *antlrgen.ColumnAliasesContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitParenthesizedRelation visits the node
func (v *ASTBuilder) VisitParenthesizedRelation(ctx *antlrgen.ParenthesizedRelationContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("parenthesizedRelation at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitQuantifiedComparison visits the node
func (v *ASTBuilder) VisitQuantifiedComparison(ctx *antlrgen.QuantifiedComparisonContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("quantifiedComparison at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitBetween visits the node
func (v *ASTBuilder) VisitBetween(ctx *antlrgen.BetweenContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitInList visits the node
func (v *ASTBuilder) VisitInList(ctx *antlrgen.InListContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitInSubquery visits the node
func (v *ASTBuilder) VisitInSubquery(ctx *antlrgen.InSubqueryContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("inSubquery at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitValueExpressionDefault visits the node
func (v *ASTBuilder) VisitValueExpressionDefault(ctx *antlrgen.ValueExpressionDefaultContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitConcatenation visits the node
func (v *ASTBuilder) VisitConcatenation(ctx *antlrgen.ConcatenationContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitArithmeticBinary visits the node
func (v *ASTBuilder) VisitArithmeticBinary(ctx *antlrgen.ArithmeticBinaryContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitArithmeticUnary visits the node
func (v *ASTBuilder) VisitArithmeticUnary(ctx *antlrgen.ArithmeticUnaryContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitAtTimeZone visits the node
func (v *ASTBuilder) VisitAtTimeZone(ctx *antlrgen.AtTimeZoneContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitTypeConstructor visits the node
func (v *ASTBuilder) VisitTypeConstructor(ctx *antlrgen.TypeConstructorContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitSpecialDateTimeFunction visits the node
func (v *ASTBuilder) VisitSpecialDateTimeFunction(ctx *antlrgen.SpecialDateTimeFunctionContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitPredicated visits the node
func (v *ASTBuilder) VisitPredicated(ctx *antlrgen.PredicatedContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitComparison visits the node
func (v *ASTBuilder) VisitComparison(ctx *antlrgen.ComparisonContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitParenthesizedExpression visits the node
func (v *ASTBuilder) VisitParenthesizedExpression(ctx *antlrgen.ParenthesizedExpressionContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitIntervalLiteral visits the node
func (v *ASTBuilder) VisitIntervalLiteral(ctx *antlrgen.IntervalLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitNumericLiteral visits the node
func (v *ASTBuilder) VisitNumericLiteral(ctx *antlrgen.NumericLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitBooleanLiteral visits the node
func (v *ASTBuilder) VisitBooleanLiteral(ctx *antlrgen.BooleanLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitColumnReference \visits the node
func (v *ASTBuilder) VisitColumnReference(ctx *antlrgen.ColumnReferenceContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitNullLiteral visits the node
func (v *ASTBuilder) VisitNullLiteral(ctx *antlrgen.NullLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitRowConstructor visits the node
func (v *ASTBuilder) VisitRowConstructor(ctx *antlrgen.RowConstructorContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("rowConstructor at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitSubscript visits the node
func (v *ASTBuilder) VisitSubscript(ctx *antlrgen.SubscriptContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitSubqueryExpression visits the node
func (v *ASTBuilder) VisitSubqueryExpression(ctx *antlrgen.SubqueryExpressionContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("subqueryExpression at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitBinaryLiteral visits the node
func (v *ASTBuilder) VisitBinaryLiteral(ctx *antlrgen.BinaryLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitCurrentUser visits the node
func (v *ASTBuilder) VisitCurrentUser(ctx *antlrgen.CurrentUserContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitStringLiteral visits the node
func (v *ASTBuilder) VisitStringLiteral(ctx *antlrgen.StringLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitArrayConstructor visits the node
func (v *ASTBuilder) VisitArrayConstructor(ctx *antlrgen.ArrayConstructorContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitGroupingOperation visits the node
func (v *ASTBuilder) VisitGroupingOperation(ctx *antlrgen.GroupingOperationContext) interface{} {
location := v.getLocation(ctx)
panic(fmt.Errorf("groupingOperation at (line:%d, col:%d) not supported yet", location.Line, location.CharPosition))
}
// VisitBasicStringLiteral visits the node
func (v *ASTBuilder) VisitBasicStringLiteral(ctx *antlrgen.BasicStringLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitUnicodeStringLiteral visits the node
func (v *ASTBuilder) VisitUnicodeStringLiteral(ctx *antlrgen.UnicodeStringLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitTimeZoneInterval visits the node
func (v *ASTBuilder) VisitTimeZoneInterval(ctx *antlrgen.TimeZoneIntervalContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitTimeZoneString visits the node
func (v *ASTBuilder) VisitTimeZoneString(ctx *antlrgen.TimeZoneStringContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitComparisonOperator visits the node
func (v *ASTBuilder) VisitComparisonOperator(ctx *antlrgen.ComparisonOperatorContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitComparisonQuantifier visits the node
func (v *ASTBuilder) VisitComparisonQuantifier(ctx *antlrgen.ComparisonQuantifierContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitBooleanValue visits the node
func (v *ASTBuilder) VisitBooleanValue(ctx *antlrgen.BooleanValueContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitInterval visits the node
func (v *ASTBuilder) VisitInterval(ctx *antlrgen.IntervalContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitIntervalField visits the node
func (v *ASTBuilder) VisitIntervalField(ctx *antlrgen.IntervalFieldContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitNormalForm visits the node
func (v *ASTBuilder) VisitNormalForm(ctx *antlrgen.NormalFormContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitSqltype visits the node
func (v *ASTBuilder) VisitSqltype(ctx *antlrgen.SqltypeContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitTypeParameter visits the node
func (v *ASTBuilder) VisitTypeParameter(ctx *antlrgen.TypeParameterContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitBaseType visits the node
func (v *ASTBuilder) VisitBaseType(ctx *antlrgen.BaseTypeContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitWhenClause visits the node
func (v *ASTBuilder) VisitWhenClause(ctx *antlrgen.WhenClauseContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitFilter visits the node
func (v *ASTBuilder) VisitFilter(ctx *antlrgen.FilterContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitQualifiedName visits the node
func (v *ASTBuilder) VisitQualifiedName(ctx *antlrgen.QualifiedNameContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitBackQuotedIdentifier visits the node
func (v *ASTBuilder) VisitBackQuotedIdentifier(ctx *antlrgen.BackQuotedIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitDigitIdentifier visits the node
func (v *ASTBuilder) VisitDigitIdentifier(ctx *antlrgen.DigitIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitDecimalLiteral visits the node
func (v *ASTBuilder) VisitDecimalLiteral(ctx *antlrgen.DecimalLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitDoubleLiteral visits the node
func (v *ASTBuilder) VisitDoubleLiteral(ctx *antlrgen.DoubleLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitIntegerLiteral visits the node
func (v *ASTBuilder) VisitIntegerLiteral(ctx *antlrgen.IntegerLiteralContext) interface{} {
return v.VisitChildren(ctx)
}
// VisitNonReserved visits the node
func (v *ASTBuilder) VisitNonReserved(ctx *antlrgen.NonReservedContext) interface{} {
return v.VisitChildren(ctx)
}
// ***************** helpers *****************
type orderByContext interface {
ORDER() antlr.TerminalNode
AllSortItem() []antlrgen.ISortItemContext
}
func (v *ASTBuilder) getOrderBy(ctx orderByContext) *tree.OrderBy {
var orderBy *tree.OrderBy
if ctx.ORDER() != nil {
ctxArr := ctx.AllSortItem()
arrSortItem := make([]*tree.SortItem, len(ctxArr))
for i, c := range ctxArr {
arrSortItem[i] = v.Visit(c.(*antlrgen.SortItemContext)).(*tree.SortItem)
}
orderBy = tree.NewOrderBy(v.getLocation(ctx.ORDER()), arrSortItem)
orderBy.SetValue(fmt.Sprintf("OrderBy: (%s)", ctx.ORDER().GetText()))
}
return orderBy
}
func (v *ASTBuilder) getLocation(input interface{}) *tree.NodeLocation {
var token antlr.Token
switch i := input.(type) {
case antlr.ParserRuleContext:
util.RequireNonNull(input, "antlrgenRuleContext is null")
token = i.GetStart()
case antlr.TerminalNode:
util.RequireNonNull(input, "terminalNode is null")
token = i.GetSymbol()
case antlr.Token:
token = i
default:
token = nil
}
util.RequireNonNull(token, "token is null")
return &tree.NodeLocation{
Line: token.GetLine(),
CharPosition: token.GetColumn(),
}
}
// getText extracts string from original input sql
func (v *ASTBuilder) getText(ctx antlr.ParserRuleContext) string {
return v.IStream.GetTextFromTokens(ctx.GetStart(), ctx.GetStop())
}
func (v *ASTBuilder) setTimefilter(ctx []antlrgen.IExpressionContext) {
column := util.TrimQuote(v.getText(ctx[0]))
from := util.TrimQuote(v.getText(ctx[1]))
to := util.TrimQuote(v.getText(ctx[2]))
timezone := util.TrimQuote(v.getText(ctx[3]))
if v.SQL2AqlCtx.timeFilter != (queryCom.TimeFilter{}) {
if v.SQL2AqlCtx.timeFilter.Column != column {
location := v.getLocation(ctx[0])
panic(fmt.Errorf("different timefilter on %s at (line:%d, col:%d)",
column, location.Line, location.CharPosition))
}
if v.SQL2AqlCtx.timeFilter.From != from {
location := v.getLocation(ctx[1])
panic(fmt.Errorf("different timefilter from %s at (line:%d, col:%d)",
from, location.Line, location.CharPosition))
}
if v.SQL2AqlCtx.timeFilter.To != to {
location := v.getLocation(ctx[2])
panic(fmt.Errorf("different timefilter to %s at (line:%d, col:%d)",
to, location.Line, location.CharPosition))
}
if len(v.SQL2AqlCtx.timezone) != 0 && v.SQL2AqlCtx.timezone != timezone {
location := v.getLocation(ctx[2])
panic(fmt.Errorf("different timefilter timezone %s at (line:%d, col:%d)",
timezone, location.Line, location.CharPosition))
}
return
}
v.SQL2AqlCtx.timeFilter = queryCom.TimeFilter{
Column: column,
From: from,
To: to,
}
v.SQL2AqlCtx.timezone = timezone
}
func (v *ASTBuilder) setTimeNow(ctx []antlrgen.IExpressionContext) {
column := util.TrimQuote(v.getText(ctx[0]))
now := util.TrimQuote(v.getText(ctx[1]))
var tsNow int64
var err error
if tsNow, err = strconv.ParseInt(now, 110, 64); err != nil {
location := v.getLocation(ctx[1])
panic(fmt.Errorf("invalid timestamp now on %s at (line:%d, col:%d)",
column, location.Line, location.CharPosition))
}
if v.SQL2AqlCtx.timeNow < tsNow {
v.SQL2AqlCtx.timeNow = tsNow
}
return
}
func (v *ASTBuilder) setNumericBucketizer(ctx []antlrgen.IExpressionContext, def string) {
switch def {
case util.NumericBucketTypeBucketWidth:
location := v.getLocation(ctx[1])
value, err := strconv.ParseFloat(util.TrimQuote(v.getText(ctx[1])), 64)
if err != nil {
panic(fmt.Errorf("expect float value at (line:%d, col:%d)",
location.Line, location.CharPosition))
}
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] = append(
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey],
queryCom.Dimension{
Expr: util.TrimQuote(v.getText(ctx[0])),
NumericBucketizer: queryCom.NumericBucketizerDef{BucketWidth: value},
})
case util.NumericBucketTypeLogBase:
location := v.getLocation(ctx[1])
value, err := strconv.ParseFloat(util.TrimQuote(v.getText(ctx[1])), 64)
if err != nil {
panic(fmt.Errorf("expect float value at (line:%d, col:%d)",
location.Line, location.CharPosition))
}
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] = append(
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey],
queryCom.Dimension{
Expr: util.TrimQuote(v.getText(ctx[0])),
NumericBucketizer: queryCom.NumericBucketizerDef{LogBase: value},
})
case util.NumericBucketTypeManualPartitions:
location := v.getLocation(ctx[1])
values := strings.Split(util.TrimQuote(v.getText(ctx[1])), ",")
partitions := make([]float64, len(values), len(values))
for i, value := range values {
partition, err := strconv.ParseFloat(value, 64)
if err != nil {
panic(fmt.Errorf("expect float value at (line:%d, col:%d)",
location.Line, location.CharPosition))
}
partitions[i] = partition
}
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey] = append(
v.SQL2AqlCtx.MapDimensions[v.SQL2AqlCtx.mapKey],
queryCom.Dimension{
Expr: util.TrimQuote(v.getText(ctx[0])),
NumericBucketizer: queryCom.NumericBucketizerDef{ManualPartitions: partitions},
})
}
}
// mergeWithOrSubQueries merge all subquery/withQuery's information into v.aql
func (v *ASTBuilder) mergeWithOrSubQueries() {
for i, join := range v.SQL2AqlCtx.MapJoinTables[0] {
withOrSubQKey, _ := v.SQL2AqlCtx.queryIdentifierSet[join.Alias]
if i == 0 {
v.mergeWithOrSubQuery(withOrSubQKey, false)
} else {
v.mergeWithOrSubQuery(withOrSubQKey, true)
}
}
v.aql.Measures = v.SQL2AqlCtx.MapMeasures[0]
v.aql.Dimensions = v.SQL2AqlCtx.MapDimensions[0]
if len(v.SQL2AqlCtx.MapOrderBy[0]) != 0 {
v.aql.Sorts = v.SQL2AqlCtx.MapOrderBy[0]
}
v.aql.Filters = v.SQL2AqlCtx.MapRowFilters[0]
v.aql.TimeFilter = v.SQL2AqlCtx.timeFilter
v.aql.Timezone = v.SQL2AqlCtx.timezone
v.aql.Limit = v.SQL2AqlCtx.MapLimit[0]
}
// mergeWithOrSubQuery merge one subquery/withQuery information into v.aql
func (v *ASTBuilder) mergeWithOrSubQuery(key int, ignoreJoin bool) {
if !ignoreJoin {
v.aql.Table = v.SQL2AqlCtx.MapJoinTables[key][0].Table
v.aql.Joins = v.SQL2AqlCtx.MapJoinTables[key][1:]
}
for i, measure := range v.SQL2AqlCtx.MapMeasures[key] {
if index := v.isMeasureInMain(key, i); index > -1 {
// case1: this measure of subquery/withQuery is not a supportingMeasure
v.SQL2AqlCtx.MapMeasures[0][index].Filters = v.SQL2AqlCtx.MapRowFilters[key]
if len(v.SQL2AqlCtx.MapDimensions[0]) == 0 {
v.SQL2AqlCtx.MapDimensions[0] = v.SQL2AqlCtx.MapDimensions[key]
}
} else {
// case2: this measure of subquery/withQuery is a supportingMeasure
measure.Filters = v.SQL2AqlCtx.MapRowFilters[key]
v.aql.SupportingMeasures = append(v.aql.SupportingMeasures, measure)
}
if len(v.SQL2AqlCtx.MapDimensions[0]) == 0 && len(v.SQL2AqlCtx.MapDimensions[key]) != 0 {
v.SQL2AqlCtx.MapDimensions[0] = v.SQL2AqlCtx.MapDimensions[key]
}
if len(v.aql.Sorts) == 0 && len(v.SQL2AqlCtx.MapOrderBy[key]) != 0 {
v.aql.Sorts = v.SQL2AqlCtx.MapOrderBy[key]
}
}
}
// isMeasureInMain check a measure of subquery/withQuery is also a measure of level 0 query
// return the index of the measure in level 0 query; otherwise return -1
func (v *ASTBuilder) isMeasureInMain(key, index int) int {
if len(v.SQL2AqlCtx.MapMeasures[key][index].Alias) != 0 {
for i, measure := range v.SQL2AqlCtx.MapMeasures[0] {
if measure.Expr == v.SQL2AqlCtx.MapMeasures[key][index].Alias {
v.SQL2AqlCtx.MapMeasures[0][i].Expr = v.SQL2AqlCtx.MapMeasures[key][index].Expr
v.SQL2AqlCtx.MapMeasures[0][i].Alias = v.SQL2AqlCtx.MapMeasures[key][index].Alias
return i
}
}
} else {
for i, measure := range v.SQL2AqlCtx.MapMeasures[0] {
if measure.Expr == v.SQL2AqlCtx.MapMeasures[key][index].Expr {
return i
}
}
}
return -1
}
// GetAQL construct AQLQuery via read through SQL2AqlCtx
func (v *ASTBuilder) GetAQL() *queryCom.AQLQuery {
var (
table string
joins []queryCom.Join
)
if len(v.SQL2AqlCtx.MapQueryIdentifier) == 0 {
// there is no subquery/withQuery in sql
table = v.SQL2AqlCtx.MapJoinTables[0][0].Table
if len(v.SQL2AqlCtx.MapJoinTables[0]) > 1 {
joins = v.SQL2AqlCtx.MapJoinTables[0][1:]
}
v.aql = &queryCom.AQLQuery{
Table: table,
Joins: joins,
Measures: v.SQL2AqlCtx.MapMeasures[0],
Dimensions: v.SQL2AqlCtx.MapDimensions[0],
Filters: v.SQL2AqlCtx.MapRowFilters[0],
TimeFilter: v.SQL2AqlCtx.timeFilter,
Timezone: v.SQL2AqlCtx.timezone,
Now: v.SQL2AqlCtx.timeNow,
Limit: v.SQL2AqlCtx.MapLimit[0],
Sorts: v.SQL2AqlCtx.MapOrderBy[0],
}
// remove measures that should be dimensions
dimsMap := make(map[string]bool)
for _, d := range v.aql.Dimensions {
dimsMap[d.Expr] = true
}
measuresOld := v.aql.Measures
v.aql.Measures = []queryCom.Measure{}
for _, m := range measuresOld {
if !dimsMap[m.Expr] {
v.aql.Measures = append(v.aql.Measures, m)
}
}
} else {
v.aql = &queryCom.AQLQuery{
SupportingMeasures: make([]queryCom.Measure, 0, defaultSliceCap),
SupportingDimensions: make([]queryCom.Dimension, 0, defaultSliceCap),
}
v.mergeWithOrSubQueries()
}
return v.aql
}
// GetTextIfPresent visits the node
func (v *ASTBuilder) GetTextIfPresent(token antlr.Token) string {
var text string
if token != nil {
text = token.GetText()
}
return text
}
// isDistinct check if DISTINCT quantifier is set
func (v *ASTBuilder) isDistinct(setQuantifier antlrgen.ISetQuantifierContext) bool {
if setQuantifier != nil && setQuantifier.(*antlrgen.SetQuantifierContext).DISTINCT() != nil {
return true
}
return false
}
// getLogicalBinaryOperator returns an input token's logicalBinaryExpression operator type
func (v *ASTBuilder) getLogicalBinaryOperator(token int) tree.LogicalBinaryExpType {
switch token {
case antlrgen.SqlBaseLexerAND:
return tree.AND
case antlrgen.SqlBaseLexerOR:
return tree.OR
default:
panic(fmt.Errorf("Unsupported operator: %v", token))
}
}
func (v *ASTBuilder) getJoinType(ctx *antlrgen.JoinRelationContext) tree.JoinType {
var joinType tree.JoinType
if ctx.JoinType() == nil {
joinType = tree.INNER
} else if ctx.JoinType().(*antlrgen.JoinTypeContext).LEFT() != nil {
joinType = tree.LEFT
} else if ctx.JoinType().(*antlrgen.JoinTypeContext).RIGHT() != nil {
joinType = tree.RIGHT
} else if ctx.JoinType().(*antlrgen.JoinTypeContext).FULL() != nil {
joinType = tree.FULL
} else {
joinType = tree.INNER
}
return joinType
}
func (v *ASTBuilder) getCtxLevels(s2aCtx *SQL2AqlContext) (level, levelWith, levelQuery int) {
level = s2aCtx.level + 1
levelWith = s2aCtx.levelWith
levelQuery = s2aCtx.levelQuery
return
}
func (v *ASTBuilder) setCtxLevels(s2aCtx *SQL2AqlContext, level, levelWith, levelQuery int) {
s2aCtx.level = level
s2aCtx.levelWith = levelWith
s2aCtx.levelQuery = levelQuery
}
// generateKey constructs mapKey based on levelQuery and index of the query at the current levelQuery
func (v *ASTBuilder) generateKey(qLevel, qType, index int) int {
return qLevel*1000 + qType*100 + index
}
func (v *ASTBuilder) getInfoByKey(mapKey int) (qLevel, qType, index int) {
qLevel = mapKey / 1000
qType = (mapKey % 1000) / 100
index = (mapKey % 1000) % 100
return
}
func (v *ASTBuilder) isValidWithOrSubQuery(s2aCtx *SQL2AqlContext) (bool, error) {
var isTrue, exist bool
var err error
var mapKey int
// check if from clause in main query(ie. qLevel = 0) mix table with subquery/withQuery
if isTrue, err = v.isQueryFromMixed(s2aCtx); isTrue {
return false, err
}
// check if all subquery/withQuery from clauses are same
for i, value := range s2aCtx.MapJoinTables[0] {
// exit if no subquery/withQuery
if len(value.Table) > 0 {
break
}
if len(value.Alias) == 0 { // subquery has no identifier
mapKey = v.generateKey(1, typeSubQuery, i)
} else {
mapKey, exist = s2aCtx.queryIdentifierSet[value.Alias]
if !exist {
err = fmt.Errorf("cannot find withQuery identifier: %s", value.Alias)
return false, err
}
}
isTrue, err = v.isSameFromTables(s2aCtx, mapKey)
if !isTrue {
return false, err
}
isTrue, err = v.isSameGroupBy(s2aCtx, mapKey)
if !isTrue {
return false, err
}
isTrue, err = v.isSameOrderBy(s2aCtx, mapKey)
if !isTrue {
return false, err
}
}
return true, nil
}
// AQL requires that the first level query is either from tables or from subqueries/withQuery
func (v *ASTBuilder) isQueryFromMixed(s2aCtx *SQL2AqlContext) (bool, error) {
var flagTable bool
var err error
if len(s2aCtx.MapJoinTables[0][0].Table) != 0 {
flagTable = true
}
for i, join := range s2aCtx.MapJoinTables[0] {
if flagTable && len(join.Table) == 0 {
err = fmt.Errorf("# %d should be a tablename in from clause", i)
return true, err
} else if !flagTable && len(join.Table) != 0 {
err = fmt.Errorf("# %d should be a subquery/withQuery in from clause", i)
return true, err
}
}
return false, nil
}
// AQL requires that all subqueries or withQuery from clauses are same
func (v *ASTBuilder) isSameFromTables(s2aCtx *SQL2AqlContext, mapKey int) (bool, error) {
qLevel, _, index := v.getInfoByKey(mapKey)
// generte from clause json bytes based on the first subquery/withQuery
if s2aCtx.fromJSON == nil {
withKeyMin := v.generateKey(qLevel, typeWithQuery, 0)
subQKeyMin := v.generateKey(qLevel, typeSubQuery, 0)
var err error
if s2aCtx.MapJoinTables[withKeyMin] != nil {
s2aCtx.fromJSON, err = json.Marshal(s2aCtx.MapJoinTables[withKeyMin])
} else {
s2aCtx.fromJSON, err = json.Marshal(s2aCtx.MapJoinTables[subQKeyMin])
}
if err != nil {
return false, errors.New("unable to encode from clause of the first subquery/withQuery")
}
if index == 0 {
return true, nil
}
}
// compare current from clause with ctx.fromJSON
joins, err := json.Marshal(s2aCtx.MapJoinTables[mapKey])
if err != nil {
return false, errors.New("unable to encode from clause of this subquery/withQuery")
}
return bytes.Equal(s2aCtx.fromJSON, joins), nil
}
// AQL requires that all subqueries or withQuery groupBy clauses are sameo
func (v *ASTBuilder) isSameGroupBy(s2aCtx *SQL2AqlContext, mapKey int) (bool, error) {
qLevel, _, index := v.getInfoByKey(mapKey)
// generte group by clause json bytes based on the first subquery/withQuery
if s2aCtx.groupByJSON == nil {
withKeyMin := v.generateKey(qLevel, typeWithQuery, 0)
subQKeyMin := v.generateKey(qLevel, typeSubQuery, 0)
var err error
if s2aCtx.MapJoinTables[withKeyMin] != nil {
s2aCtx.groupByJSON, err = json.Marshal(s2aCtx.MapDimensions[withKeyMin])
} else {
s2aCtx.groupByJSON, err = json.Marshal(s2aCtx.MapDimensions[subQKeyMin])
}
if err != nil {
return false, errors.New("unable to encode group by clause of the first subquery/withQuery")
}
if index == 0 {
return true, nil
}
}
// compare current groupBy clause with ctx.groupByJSON
groupBy, err := json.Marshal(s2aCtx.MapDimensions[mapKey])
if err != nil {
return false, errors.New("unable to encode group by clause of this subquery/withQuery")
}
return bytes.EqualFold(s2aCtx.groupByJSON, groupBy), nil
}
// AQL requires that all subqueries or withQuery orderBy clauses are same
func (v *ASTBuilder) isSameOrderBy(s2aCtx *SQL2AqlContext, mapKey int) (bool, error) {
qLevel, _, index := v.getInfoByKey(mapKey)
// generte group by clause json bytes based on the first subquery/withQuery
if s2aCtx.orderByJSON == nil {
withKeyMin := v.generateKey(qLevel, typeWithQuery, 0)
subQKeyMin := v.generateKey(qLevel, typeSubQuery, 0)
var err error
if s2aCtx.MapOrderBy[withKeyMin] != nil {
s2aCtx.orderByJSON, err = json.Marshal(s2aCtx.MapOrderBy[withKeyMin])
} else {
s2aCtx.orderByJSON, err = json.Marshal(s2aCtx.MapOrderBy[subQKeyMin])
}
if err != nil {
return false, errors.New("unable to encode order by clause of the first subquery/withQuery")
}
if index == 0 {
return true, nil
}
}
// compare current groupBy clause with ctx.groupByJSON
orderBy, err := json.Marshal(s2aCtx.MapOrderBy[mapKey])
if err != nil {
return false, errors.New("unable to encode order by clause of this subquery/withQuery")
}
return bytes.EqualFold(s2aCtx.orderByJSON, orderBy), nil
}
// addQIdentifier adds subquery/withQuery identifier and its mapKey into queryIdentifierSet
func (v *ASTBuilder) addQIdentifier(s2aCtx *SQL2AqlContext, indentifier string, key int) error {
if s2aCtx.queryIdentifierSet == nil {
s2aCtx.queryIdentifierSet = make(map[string]int)
}
if _, exist := s2aCtx.queryIdentifierSet[indentifier]; exist {
return fmt.Errorf("subquery/withQuery identifier: %v already exist", indentifier)
}
s2aCtx.queryIdentifierSet[indentifier] = key
return nil
}
// isWithQueryIdentifier check if name is a withQuery identifier
func (v *ASTBuilder) isWithQueryIdentifier(s2aCtx *SQL2AqlContext, name string) bool {
withKeyMin := v.generateKey(1, typeWithQuery, 0)
if s2aCtx.queryIdentifierSet != nil {
k, exist := s2aCtx.queryIdentifierSet[name]
if exist && k/withKeyMin == 1 {
return true
}
}
return false
}
// isSubOrWithQueryIdentifier check if name is a subquery/withQuery identifier
func (v *ASTBuilder) isSubOrWithQueryIdentifier(s2aCtx *SQL2AqlContext, name string) int {
if s2aCtx.queryIdentifierSet != nil {
if k, exist := s2aCtx.queryIdentifierSet[name]; exist {
return k
}
}
return -1
}
// lookupSQLExpr is used by groupBy, orderBy, having clause whose sql expression is select column alias.
// It returns the select column alias and sql expresion.
func (v *ASTBuilder) lookupSQLExpr(s2aCtx *SQL2AqlContext, mapKey int, str string) (alias, sqlExpr string) {
for _, measure := range s2aCtx.MapMeasures[mapKey] {
if len(measure.Alias) > 0 && strings.Compare(measure.Alias, str) == 0 {
alias = str
sqlExpr = measure.Expr
return
}
}
sqlExpr = str
return
}
// check path from expression node to leaf node has logicalBinaryOperator OR
func (v *ASTBuilder) hasORInPath(node *antlr.BaseParserRuleContext) tree.LogicalBinaryExpType {
parent := node.GetParent()
op := tree.NOOP
for parent != nil {
switch p := parent.(type) {
case antlrgen.IExpressionContext:
return op
case *antlrgen.LogicalBinaryContext:
if p.GetOperator() != nil && v.getLogicalBinaryOperator(p.GetOperator().GetTokenType()) == tree.OR {
return tree.OR
}
op = tree.AND
}
parent = parent.GetParent()
}
return op
}
// Parse parses input sql
func Parse(sql string, logger common.Logger) (aql *queryCom.AQLQuery, err error) {
defer func() {
if r := recover(); r != nil {
var ok bool
err, ok = r.(error)
if !ok {
err = fmt.Errorf("unkonwn error, reason: %v", r)
}
}
}()
// Setup the input sql
is := util.NewCaseChangingStream(antlr.NewInputStream(sql), true)
// Create the Lexer
lexer := antlrgen.NewSqlBaseLexer(is)
stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
// Create the Parser
p := antlrgen.NewSqlBaseParser(stream)
// Finally parse the sql
p.GetInterpreter().SetPredictionMode(antlr.PredictionModeSLL)
parseTree, ok := p.Query().(*antlrgen.QueryContext)
if !ok {
err = fmt.Errorf("not a query")
return nil, err
}
// Construct ASTBuilder
v := &ASTBuilder{
Logger: logger,
IStream: stream,
ParameterPosition: 0,
SQL2AqlCtx: &SQL2AqlContext{
MapQueryIdentifier: make(map[int]string),
MapMeasures: make(map[int][]queryCom.Measure),
MapDimensions: make(map[int][]queryCom.Dimension),
MapJoinTables: make(map[int][]queryCom.Join),
MapRowFilters: make(map[int][]string),
MapOrderBy: make(map[int][]queryCom.SortField),
MapLimit: make(map[int]int),
},
}
node := v.VisitQuery(parseTree)
if _, ok := node.(*tree.Query); ok {
aql = v.GetAQL()
aql.SQLQuery = sql
aqlJSON, _ := json.Marshal(aql)
logger.Infof("convert SQL:\n%v\nto AQL:\n%v", sql, string(aqlJSON))
}
if len(aql.SupportingDimensions) > 0 || len(aql.SupportingMeasures) > 0 {
err = fmt.Errorf("sub query not supported yet")
return
}
// non agg query overwrite
if len(aql.Dimensions) == 0 {
if v.aggFuncExists {
err = fmt.Errorf("no aggregate functions allowed when no group by specified")
return
}
for _, measure := range aql.Measures {
aql.Dimensions = append(aql.Dimensions, queryCom.Dimension{
Expr: measure.Expr,
})
}
aql.Measures = []queryCom.Measure{{
Expr: "1",
}}
}
return
}