milvus/internal/parser/planparserv2/parser_visitor.go

1384 lines
39 KiB
Go

package planparserv2
import (
"fmt"
"strconv"
"strings"
"github.com/antlr4-go/antlr/v4"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
parser "github.com/milvus-io/milvus/internal/parser/planparserv2/generated"
"github.com/milvus-io/milvus/internal/proto/planpb"
"github.com/milvus-io/milvus/pkg/util/typeutil"
)
type ParserVisitor struct {
parser.BasePlanVisitor
schema *typeutil.SchemaHelper
}
func NewParserVisitor(schema *typeutil.SchemaHelper) *ParserVisitor {
return &ParserVisitor{schema: schema}
}
// VisitParens unpack the parentheses.
func (v *ParserVisitor) VisitParens(ctx *parser.ParensContext) interface{} {
return ctx.Expr().Accept(v)
}
func (v *ParserVisitor) translateIdentifier(identifier string) (*ExprWithType, error) {
field, err := v.schema.GetFieldFromNameDefaultJSON(identifier)
if err != nil {
return nil, err
}
var nestedPath []string
if identifier != field.Name {
nestedPath = append(nestedPath, identifier)
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_ColumnExpr{
ColumnExpr: &planpb.ColumnExpr{
Info: &planpb.ColumnInfo{
FieldId: field.FieldID,
DataType: field.DataType,
IsPrimaryKey: field.IsPrimaryKey,
IsAutoID: field.AutoID,
NestedPath: nestedPath,
IsPartitionKey: field.IsPartitionKey,
IsClusteringKey: field.IsClusteringKey,
ElementType: field.GetElementType(),
},
},
},
},
dataType: field.DataType,
nodeDependent: true,
}, nil
}
// VisitIdentifier translates expr to column plan.
func (v *ParserVisitor) VisitIdentifier(ctx *parser.IdentifierContext) interface{} {
identifier := ctx.GetText()
expr, err := v.translateIdentifier(identifier)
if err != nil {
return err
}
return expr
}
// VisitBoolean translates expr to GenericValue.
func (v *ParserVisitor) VisitBoolean(ctx *parser.BooleanContext) interface{} {
literal := ctx.BooleanConstant().GetText()
b, err := strconv.ParseBool(literal)
if err != nil {
return err
}
return &ExprWithType{
dataType: schemapb.DataType_Bool,
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: NewBool(b),
},
},
},
nodeDependent: true,
}
}
// VisitInteger translates expr to GenericValue.
func (v *ParserVisitor) VisitInteger(ctx *parser.IntegerContext) interface{} {
literal := ctx.IntegerConstant().GetText()
i, err := strconv.ParseInt(literal, 0, 64)
if err != nil {
return err
}
return &ExprWithType{
dataType: schemapb.DataType_Int64,
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: NewInt(i),
},
},
},
nodeDependent: true,
}
}
// VisitFloating translates expr to GenericValue.
func (v *ParserVisitor) VisitFloating(ctx *parser.FloatingContext) interface{} {
literal := ctx.FloatingConstant().GetText()
f, err := strconv.ParseFloat(literal, 64)
if err != nil {
return err
}
return &ExprWithType{
dataType: schemapb.DataType_Double,
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: NewFloat(f),
},
},
},
nodeDependent: true,
}
}
// VisitString translates expr to GenericValue.
func (v *ParserVisitor) VisitString(ctx *parser.StringContext) interface{} {
pattern, err := convertEscapeSingle(ctx.GetText())
if err != nil {
return err
}
return &ExprWithType{
dataType: schemapb.DataType_VarChar,
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: NewString(pattern),
},
},
},
nodeDependent: true,
}
}
func checkDirectComparisonBinaryField(columnInfo *planpb.ColumnInfo) error {
if typeutil.IsArrayType(columnInfo.GetDataType()) && len(columnInfo.GetNestedPath()) == 0 {
return fmt.Errorf("can not comparisons array fields directly")
}
return nil
}
// VisitAddSub translates expr to arithmetic plan.
func (v *ParserVisitor) VisitAddSub(ctx *parser.AddSubContext) interface{} {
var err error
left := ctx.Expr(0).Accept(v)
if err = getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err = getError(right); err != nil {
return err
}
leftValueExpr, rightValueExpr := getValueExpr(left), getValueExpr(right)
if leftValueExpr != nil && rightValueExpr != nil {
if isTemplateExpr(leftValueExpr) || isTemplateExpr(rightValueExpr) {
return fmt.Errorf("placeholder was not supported between two constants with operator: %s", ctx.GetOp().GetText())
}
leftValue, rightValue := leftValueExpr.GetValue(), rightValueExpr.GetValue()
switch ctx.GetOp().GetTokenType() {
case parser.PlanParserADD:
return Add(leftValue, rightValue)
case parser.PlanParserSUB:
return Subtract(leftValue, rightValue)
default:
return fmt.Errorf("unexpected op: %s", ctx.GetOp().GetText())
}
}
leftExpr, rightExpr := getExpr(left), getExpr(right)
reverse := true
if leftValueExpr == nil {
reverse = false
}
if leftExpr == nil || rightExpr == nil {
return fmt.Errorf("invalid arithmetic expression, left: %s, op: %s, right: %s", ctx.Expr(0).GetText(), ctx.GetOp(), ctx.Expr(1).GetText())
}
if err = checkDirectComparisonBinaryField(toColumnInfo(leftExpr)); err != nil {
return err
}
if err = checkDirectComparisonBinaryField(toColumnInfo(rightExpr)); err != nil {
return err
}
var dataType schemapb.DataType
if leftExpr.expr.GetIsTemplate() {
dataType = rightExpr.dataType
} else if rightExpr.expr.GetIsTemplate() {
dataType = leftExpr.dataType
} else {
if !canArithmetic(leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr)) {
return fmt.Errorf("'%s' can only be used between integer or floating or json field expressions", arithNameMap[ctx.GetOp().GetTokenType()])
}
dataType, err = calcDataType(leftExpr, rightExpr, reverse)
if err != nil {
return err
}
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryArithExpr{
BinaryArithExpr: &planpb.BinaryArithExpr{
Left: leftExpr.expr,
Right: rightExpr.expr,
Op: arithExprMap[ctx.GetOp().GetTokenType()],
},
},
IsTemplate: leftExpr.expr.GetIsTemplate() || rightExpr.expr.GetIsTemplate(),
}
return &ExprWithType{
expr: expr,
dataType: dataType,
nodeDependent: true,
}
}
// VisitMulDivMod translates expr to arithmetic plan.
func (v *ParserVisitor) VisitMulDivMod(ctx *parser.MulDivModContext) interface{} {
var err error
left := ctx.Expr(0).Accept(v)
if err := getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err := getError(right); err != nil {
return err
}
leftValueExpr, rightValueExpr := getValueExpr(left), getValueExpr(right)
if leftValueExpr != nil && rightValueExpr != nil {
if isTemplateExpr(leftValueExpr) || isTemplateExpr(rightValueExpr) {
return fmt.Errorf("placeholder was not supported between two constants with operator: %s", ctx.GetOp().GetText())
}
leftValue, rightValue := getGenericValue(left), getGenericValue(right)
switch ctx.GetOp().GetTokenType() {
case parser.PlanParserMUL:
return Multiply(leftValue, rightValue)
case parser.PlanParserDIV:
n, err := Divide(leftValue, rightValue)
if err != nil {
return err
}
return n
case parser.PlanParserMOD:
n, err := Modulo(leftValue, rightValue)
if err != nil {
return err
}
return n
default:
return fmt.Errorf("unexpected op: %s", ctx.GetOp().GetText())
}
}
leftExpr, rightExpr := getExpr(left), getExpr(right)
reverse := true
if leftValueExpr == nil {
reverse = false
}
if leftExpr == nil || rightExpr == nil {
return fmt.Errorf("invalid arithmetic expression, left: %s, op: %s, right: %s", ctx.Expr(0).GetText(), ctx.GetOp(), ctx.Expr(1).GetText())
}
if err := checkDirectComparisonBinaryField(toColumnInfo(leftExpr)); err != nil {
return err
}
if err := checkDirectComparisonBinaryField(toColumnInfo(rightExpr)); err != nil {
return err
}
var dataType schemapb.DataType
if leftExpr.expr.GetIsTemplate() {
dataType = rightExpr.dataType
} else if rightExpr.expr.GetIsTemplate() {
dataType = leftExpr.dataType
} else {
if !canArithmetic(leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr)) {
return fmt.Errorf("'%s' can only be used between integer or floating or json field expressions", arithNameMap[ctx.GetOp().GetTokenType()])
}
if err = checkValidModArith(arithExprMap[ctx.GetOp().GetTokenType()], leftExpr.dataType, getArrayElementType(leftExpr), rightExpr.dataType, getArrayElementType(rightExpr)); err != nil {
return err
}
dataType, err = calcDataType(leftExpr, rightExpr, reverse)
if err != nil {
return err
}
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryArithExpr{
BinaryArithExpr: &planpb.BinaryArithExpr{
Left: leftExpr.expr,
Right: rightExpr.expr,
Op: arithExprMap[ctx.GetOp().GetTokenType()],
},
},
IsTemplate: leftExpr.expr.GetIsTemplate() || rightExpr.expr.GetIsTemplate(),
}
return &ExprWithType{
expr: expr,
dataType: dataType,
nodeDependent: true,
}
}
// VisitEquality translates expr to compare/range plan.
func (v *ParserVisitor) VisitEquality(ctx *parser.EqualityContext) interface{} {
left := ctx.Expr(0).Accept(v)
if err := getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err := getError(right); err != nil {
return err
}
leftValueExpr, rightValueExpr := getValueExpr(left), getValueExpr(right)
if leftValueExpr != nil && rightValueExpr != nil {
if isTemplateExpr(leftValueExpr) || isTemplateExpr(rightValueExpr) {
return fmt.Errorf("placeholder was not supported between two constants with operator: %s", ctx.GetOp().GetText())
}
leftValue, rightValue := leftValueExpr.GetValue(), rightValueExpr.GetValue()
var ret *ExprWithType
switch ctx.GetOp().GetTokenType() {
case parser.PlanParserEQ:
ret = Equal(leftValue, rightValue)
case parser.PlanParserNE:
ret = NotEqual(leftValue, rightValue)
default:
return fmt.Errorf("unexpected op: %s", ctx.GetOp().GetText())
}
if ret == nil {
return fmt.Errorf("comparison operations cannot be applied to two incompatible operands: %s", ctx.GetText())
}
return ret
}
leftExpr, rightExpr := getExpr(left), getExpr(right)
expr, err := HandleCompare(ctx.GetOp().GetTokenType(), leftExpr, rightExpr)
if err != nil {
return err
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
// VisitRelational translates expr to range/compare plan.
func (v *ParserVisitor) VisitRelational(ctx *parser.RelationalContext) interface{} {
left := ctx.Expr(0).Accept(v)
if err := getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err := getError(right); err != nil {
return err
}
leftValueExpr, rightValueExpr := getValueExpr(left), getValueExpr(right)
if leftValueExpr != nil && rightValueExpr != nil {
if isTemplateExpr(leftValueExpr) || isTemplateExpr(rightValueExpr) {
return fmt.Errorf("placeholder was not supported between two constants with operator: %s", ctx.GetOp().GetText())
}
leftValue, rightValue := getGenericValue(left), getGenericValue(right)
var ret *ExprWithType
switch ctx.GetOp().GetTokenType() {
case parser.PlanParserLT:
ret = Less(leftValue, rightValue)
case parser.PlanParserLE:
ret = LessEqual(leftValue, rightValue)
case parser.PlanParserGT:
ret = Greater(leftValue, rightValue)
case parser.PlanParserGE:
ret = GreaterEqual(leftValue, rightValue)
default:
return fmt.Errorf("unexpected op: %s", ctx.GetOp().GetText())
}
if ret == nil {
return fmt.Errorf("comparison operations cannot be applied to two incompatible operands: %s", ctx.GetText())
}
return ret
}
leftExpr, rightExpr := getExpr(left), getExpr(right)
if err := checkDirectComparisonBinaryField(toColumnInfo(leftExpr)); err != nil {
return err
}
if err := checkDirectComparisonBinaryField(toColumnInfo(rightExpr)); err != nil {
return err
}
expr, err := HandleCompare(ctx.GetOp().GetTokenType(), leftExpr, rightExpr)
if err != nil {
return err
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
// VisitLike handles match operations.
func (v *ParserVisitor) VisitLike(ctx *parser.LikeContext) interface{} {
left := ctx.Expr().Accept(v)
if err := getError(left); err != nil {
return err
}
leftExpr := getExpr(left)
if leftExpr == nil {
return fmt.Errorf("the left operand of like is invalid")
}
column := toColumnInfo(leftExpr)
if column == nil {
return fmt.Errorf("like operation on complicated expr is unsupported")
}
if err := checkDirectComparisonBinaryField(column); err != nil {
return err
}
if !typeutil.IsStringType(leftExpr.dataType) && !typeutil.IsJSONType(leftExpr.dataType) &&
!(typeutil.IsArrayType(leftExpr.dataType) && typeutil.IsStringType(column.GetElementType())) {
return fmt.Errorf("like operation on non-string or no-json field is unsupported")
}
pattern, err := convertEscapeSingle(ctx.StringLiteral().GetText())
if err != nil {
return err
}
op, operand, err := translatePatternMatch(pattern)
if err != nil {
return err
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_UnaryRangeExpr{
UnaryRangeExpr: &planpb.UnaryRangeExpr{
ColumnInfo: column,
Op: op,
Value: NewString(operand),
},
},
},
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitTextMatch(ctx *parser.TextMatchContext) interface{} {
column, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
if !typeutil.IsStringType(column.dataType) {
return fmt.Errorf("text match operation on non-string is unsupported")
}
queryText, err := convertEscapeSingle(ctx.StringLiteral().GetText())
if err != nil {
return err
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_UnaryRangeExpr{
UnaryRangeExpr: &planpb.UnaryRangeExpr{
ColumnInfo: toColumnInfo(column),
Op: planpb.OpType_TextMatch,
Value: NewString(queryText),
},
},
},
dataType: schemapb.DataType_Bool,
}
}
// VisitTerm translates expr to term plan.
func (v *ParserVisitor) VisitTerm(ctx *parser.TermContext) interface{} {
child := ctx.Expr(0).Accept(v)
if err := getError(child); err != nil {
return err
}
if childValue := getGenericValue(child); childValue != nil {
return fmt.Errorf("'term' can only be used on non-const expression, but got: %s", ctx.Expr(0).GetText())
}
childExpr := getExpr(child)
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil {
return fmt.Errorf("'term' can only be used on single field, but got: %s", ctx.Expr(0).GetText())
}
dataType := columnInfo.GetDataType()
if typeutil.IsArrayType(dataType) && len(columnInfo.GetNestedPath()) != 0 {
dataType = columnInfo.GetElementType()
}
term := ctx.Expr(1).Accept(v)
if getError(term) != nil {
return term
}
valueExpr := getValueExpr(term)
var placeholder string
var isTemplate bool
var values []*planpb.GenericValue
if valueExpr.GetValue() == nil && valueExpr.GetTemplateVariableName() != "" {
placeholder = valueExpr.GetTemplateVariableName()
values = nil
isTemplate = true
} else {
elementValue := valueExpr.GetValue()
if elementValue == nil {
return fmt.Errorf("value '%s' in list cannot be a non-const expression", ctx.Expr(1).GetText())
}
if !IsArray(elementValue) {
return fmt.Errorf("the right-hand side of 'in' must be a list, but got: %s", ctx.Expr(1).GetText())
}
array := elementValue.GetArrayVal().GetArray()
values = make([]*planpb.GenericValue, len(array))
for i, e := range array {
castedValue, err := castValue(dataType, e)
if err != nil {
return fmt.Errorf("value '%s' in list cannot be casted to %s", e.String(), dataType.String())
}
values[i] = castedValue
}
}
expr := &planpb.Expr{
Expr: &planpb.Expr_TermExpr{
TermExpr: &planpb.TermExpr{
ColumnInfo: columnInfo,
Values: values,
TemplateVariableName: placeholder,
},
},
IsTemplate: isTemplate,
}
if ctx.GetOp() != nil {
expr = &planpb.Expr{
Expr: &planpb.Expr_UnaryExpr{
UnaryExpr: &planpb.UnaryExpr{
Op: planpb.UnaryExpr_Not,
Child: expr,
},
},
IsTemplate: isTemplate,
}
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) getChildColumnInfo(identifier, child antlr.TerminalNode) (*planpb.ColumnInfo, error) {
if identifier != nil {
childExpr, err := v.translateIdentifier(identifier.GetText())
if err != nil {
return nil, err
}
return toColumnInfo(childExpr), nil
}
return v.getColumnInfoFromJSONIdentifier(child.GetText())
}
// VisitCall parses the expr to call plan.
func (v *ParserVisitor) VisitCall(ctx *parser.CallContext) interface{} {
functionName := strings.ToLower(ctx.Identifier().GetText())
numParams := len(ctx.AllExpr())
funcParameters := make([]*planpb.Expr, 0, numParams)
for _, param := range ctx.AllExpr() {
paramExpr := getExpr(param.Accept(v))
funcParameters = append(funcParameters, paramExpr.expr)
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_CallExpr{
CallExpr: &planpb.CallExpr{
FunctionName: functionName,
FunctionParameters: funcParameters,
},
},
},
dataType: schemapb.DataType_Bool,
}
}
// VisitRange translates expr to range plan.
func (v *ParserVisitor) VisitRange(ctx *parser.RangeContext) interface{} {
columnInfo, err := v.getChildColumnInfo(ctx.Identifier(), ctx.JSONIdentifier())
if err != nil {
return err
}
if columnInfo == nil {
return fmt.Errorf("range operations are only supported on single fields now, got: %s", ctx.Expr(1).GetText())
}
if err := checkDirectComparisonBinaryField(columnInfo); err != nil {
return err
}
lower := ctx.Expr(0).Accept(v)
upper := ctx.Expr(1).Accept(v)
if err := getError(lower); err != nil {
return err
}
if err := getError(upper); err != nil {
return err
}
lowerValueExpr, upperValueExpr := getValueExpr(lower), getValueExpr(upper)
if lowerValueExpr == nil {
return fmt.Errorf("lowerbound cannot be a non-const expression: %s", ctx.Expr(0).GetText())
}
if upperValueExpr == nil {
return fmt.Errorf("upperbound cannot be a non-const expression: %s", ctx.Expr(1).GetText())
}
fieldDataType := columnInfo.GetDataType()
if typeutil.IsArrayType(columnInfo.GetDataType()) {
fieldDataType = columnInfo.GetElementType()
}
lowerValue := lowerValueExpr.GetValue()
upperValue := upperValueExpr.GetValue()
if !isTemplateExpr(lowerValueExpr) {
if lowerValue, err = castRangeValue(fieldDataType, lowerValue); err != nil {
return err
}
}
if !isTemplateExpr(upperValueExpr) {
if upperValue, err = castRangeValue(fieldDataType, upperValue); err != nil {
return err
}
}
lowerInclusive := ctx.GetOp1().GetTokenType() == parser.PlanParserLE
upperInclusive := ctx.GetOp2().GetTokenType() == parser.PlanParserLE
if !isTemplateExpr(lowerValueExpr) && !isTemplateExpr(upperValueExpr) {
if !(lowerInclusive && upperInclusive) {
if getGenericValue(GreaterEqual(lowerValue, upperValue)).GetBoolVal() {
return fmt.Errorf("invalid range: lowerbound is greater than upperbound")
}
} else {
if getGenericValue(Greater(lowerValue, upperValue)).GetBoolVal() {
return fmt.Errorf("invalid range: lowerbound is greater than upperbound")
}
}
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryRangeExpr{
BinaryRangeExpr: &planpb.BinaryRangeExpr{
ColumnInfo: columnInfo,
LowerInclusive: lowerInclusive,
UpperInclusive: upperInclusive,
LowerValue: lowerValue,
UpperValue: upperValue,
LowerTemplateVariableName: lowerValueExpr.GetTemplateVariableName(),
UpperTemplateVariableName: upperValueExpr.GetTemplateVariableName(),
},
},
IsTemplate: isTemplateExpr(lowerValueExpr) || isTemplateExpr(upperValueExpr),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
// VisitReverseRange parses the expression like "1 > a > 0".
func (v *ParserVisitor) VisitReverseRange(ctx *parser.ReverseRangeContext) interface{} {
columnInfo, err := v.getChildColumnInfo(ctx.Identifier(), ctx.JSONIdentifier())
if err != nil {
return err
}
if columnInfo == nil {
return fmt.Errorf("range operations are only supported on single fields now, got: %s", ctx.Expr(1).GetText())
}
if err := checkDirectComparisonBinaryField(columnInfo); err != nil {
return err
}
lower := ctx.Expr(1).Accept(v)
upper := ctx.Expr(0).Accept(v)
if err := getError(lower); err != nil {
return err
}
if err := getError(upper); err != nil {
return err
}
lowerValueExpr, upperValueExpr := getValueExpr(lower), getValueExpr(upper)
if lowerValueExpr == nil {
return fmt.Errorf("lowerbound cannot be a non-const expression: %s", ctx.Expr(0).GetText())
}
if upperValueExpr == nil {
return fmt.Errorf("upperbound cannot be a non-const expression: %s", ctx.Expr(1).GetText())
}
fieldDataType := columnInfo.GetDataType()
if typeutil.IsArrayType(columnInfo.GetDataType()) {
fieldDataType = columnInfo.GetElementType()
}
lowerValue := lowerValueExpr.GetValue()
upperValue := upperValueExpr.GetValue()
if !isTemplateExpr(lowerValueExpr) {
if lowerValue, err = castRangeValue(fieldDataType, lowerValue); err != nil {
return err
}
}
if !isTemplateExpr(upperValueExpr) {
if upperValue, err = castRangeValue(fieldDataType, upperValue); err != nil {
return err
}
}
lowerInclusive := ctx.GetOp2().GetTokenType() == parser.PlanParserGE
upperInclusive := ctx.GetOp1().GetTokenType() == parser.PlanParserGE
if !isTemplateExpr(lowerValueExpr) && !isTemplateExpr(upperValueExpr) {
if !(lowerInclusive && upperInclusive) {
if getGenericValue(GreaterEqual(lowerValue, upperValue)).GetBoolVal() {
return fmt.Errorf("invalid range: lowerbound is greater than upperbound")
}
} else {
if getGenericValue(Greater(lowerValue, upperValue)).GetBoolVal() {
return fmt.Errorf("invalid range: lowerbound is greater than upperbound")
}
}
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryRangeExpr{
BinaryRangeExpr: &planpb.BinaryRangeExpr{
ColumnInfo: columnInfo,
LowerInclusive: lowerInclusive,
UpperInclusive: upperInclusive,
LowerValue: lowerValue,
UpperValue: upperValue,
LowerTemplateVariableName: lowerValueExpr.GetTemplateVariableName(),
UpperTemplateVariableName: upperValueExpr.GetTemplateVariableName(),
},
},
IsTemplate: isTemplateExpr(lowerValueExpr) || isTemplateExpr(upperValueExpr),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
// VisitUnary unpack the +expr to expr.
func (v *ParserVisitor) VisitUnary(ctx *parser.UnaryContext) interface{} {
child := ctx.Expr().Accept(v)
if err := getError(child); err != nil {
return err
}
childValue := getGenericValue(child)
if childValue != nil {
switch ctx.GetOp().GetTokenType() {
case parser.PlanParserADD:
return child
case parser.PlanParserSUB:
return Negative(childValue)
case parser.PlanParserNOT:
return Not(childValue)
default:
return fmt.Errorf("unexpected op: %s", ctx.GetOp().GetText())
}
}
childExpr := getExpr(child)
if childExpr == nil {
return fmt.Errorf("failed to parse unary expressions")
}
if err := checkDirectComparisonBinaryField(toColumnInfo(childExpr)); err != nil {
return err
}
switch ctx.GetOp().GetTokenType() {
case parser.PlanParserADD:
return childExpr
case parser.PlanParserNOT:
if !canBeExecuted(childExpr) {
return fmt.Errorf("%s op can only be applied on boolean expression", unaryLogicalNameMap[parser.PlanParserNOT])
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_UnaryExpr{
UnaryExpr: &planpb.UnaryExpr{
Op: unaryLogicalOpMap[parser.PlanParserNOT],
Child: childExpr.expr,
},
},
},
dataType: schemapb.DataType_Bool,
}
default:
return fmt.Errorf("unexpected op: %s", ctx.GetOp().GetText())
}
}
// VisitLogicalOr apply logical or to two boolean expressions.
func (v *ParserVisitor) VisitLogicalOr(ctx *parser.LogicalOrContext) interface{} {
left := ctx.Expr(0).Accept(v)
if err := getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err := getError(right); err != nil {
return err
}
leftValue, rightValue := getGenericValue(left), getGenericValue(right)
if leftValue != nil && rightValue != nil {
n, err := Or(leftValue, rightValue)
if err != nil {
return err
}
return n
}
if leftValue != nil || rightValue != nil {
return fmt.Errorf("'or' can only be used between boolean expressions")
}
var leftExpr *ExprWithType
var rightExpr *ExprWithType
leftExpr = getExpr(left)
rightExpr = getExpr(right)
if !canBeExecuted(leftExpr) || !canBeExecuted(rightExpr) {
return fmt.Errorf("'or' can only be used between boolean expressions")
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryExpr{
BinaryExpr: &planpb.BinaryExpr{
Left: leftExpr.expr,
Right: rightExpr.expr,
Op: planpb.BinaryExpr_LogicalOr,
},
},
IsTemplate: leftExpr.expr.GetIsTemplate() || rightExpr.expr.GetIsTemplate(),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
// VisitLogicalAnd apply logical and to two boolean expressions.
func (v *ParserVisitor) VisitLogicalAnd(ctx *parser.LogicalAndContext) interface{} {
left := ctx.Expr(0).Accept(v)
if err := getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err := getError(right); err != nil {
return err
}
leftValue, rightValue := getGenericValue(left), getGenericValue(right)
if leftValue != nil && rightValue != nil {
n, err := And(leftValue, rightValue)
if err != nil {
return err
}
return n
}
if leftValue != nil || rightValue != nil {
return fmt.Errorf("'and' can only be used between boolean expressions")
}
var leftExpr *ExprWithType
var rightExpr *ExprWithType
leftExpr = getExpr(left)
rightExpr = getExpr(right)
if !canBeExecuted(leftExpr) || !canBeExecuted(rightExpr) {
return fmt.Errorf("'and' can only be used between boolean expressions")
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryExpr{
BinaryExpr: &planpb.BinaryExpr{
Left: leftExpr.expr,
Right: rightExpr.expr,
Op: planpb.BinaryExpr_LogicalAnd,
},
},
IsTemplate: leftExpr.expr.GetIsTemplate() || rightExpr.expr.GetIsTemplate(),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
// VisitBitXor not supported.
func (v *ParserVisitor) VisitBitXor(ctx *parser.BitXorContext) interface{} {
return fmt.Errorf("BitXor is not supported: %s", ctx.GetText())
}
// VisitBitAnd not supported.
func (v *ParserVisitor) VisitBitAnd(ctx *parser.BitAndContext) interface{} {
return fmt.Errorf("BitAnd is not supported: %s", ctx.GetText())
}
// VisitPower parses power expression.
func (v *ParserVisitor) VisitPower(ctx *parser.PowerContext) interface{} {
left := ctx.Expr(0).Accept(v)
if err := getError(left); err != nil {
return err
}
right := ctx.Expr(1).Accept(v)
if err := getError(right); err != nil {
return err
}
leftValue, rightValue := getGenericValue(left), getGenericValue(right)
if leftValue != nil && rightValue != nil {
return Power(leftValue, rightValue)
}
return fmt.Errorf("power can only apply on constants: %s", ctx.GetText())
}
// VisitShift unsupported.
func (v *ParserVisitor) VisitShift(ctx *parser.ShiftContext) interface{} {
return fmt.Errorf("shift is not supported: %s", ctx.GetText())
}
// VisitBitOr unsupported.
func (v *ParserVisitor) VisitBitOr(ctx *parser.BitOrContext) interface{} {
return fmt.Errorf("BitOr is not supported: %s", ctx.GetText())
}
// getColumnInfoFromJSONIdentifier parse JSON field name and JSON nested path.
// input: user["name"]["first"],
// output: if user is JSON field name, and fieldID is 102
/*
&planpb.ColumnInfo{
FieldId: 102,
DataType: JSON,
NestedPath: []string{"name", "first"},
}, nil
*/
// if user is not JSON field name, and $SYS_META fieldID is 102:
/*
&planpb.ColumnInfo{
FieldId: 102,
DataType: JSON,
NestedPath: []string{"user", "name", "first"},
}, nil
*/
// input: user,
// output: if user is JSON field name, return error.
// if user is not JSON field name, and $SYS_META fieldID is 102:
/*
&planpb.ColumnInfo{
FieldId: 102,
DataType: JSON,
NestedPath: []string{"user"},
}, nil
*/
// More tests refer to plan_parser_v2_test.go::Test_JSONExpr
func (v *ParserVisitor) getColumnInfoFromJSONIdentifier(identifier string) (*planpb.ColumnInfo, error) {
fieldName := strings.Split(identifier, "[")[0]
nestedPath := make([]string, 0)
field, err := v.schema.GetFieldFromNameDefaultJSON(fieldName)
if err != nil {
return nil, err
}
if field.GetDataType() != schemapb.DataType_JSON &&
field.GetDataType() != schemapb.DataType_Array {
errMsg := fmt.Sprintf("%s data type not supported accessed with []", field.GetDataType())
return nil, fmt.Errorf(errMsg)
}
if fieldName != field.Name {
nestedPath = append(nestedPath, fieldName)
}
jsonKeyStr := identifier[len(fieldName):]
ss := strings.Split(jsonKeyStr, "][")
for i := 0; i < len(ss); i++ {
path := strings.Trim(ss[i], "[]")
if path == "" {
return nil, fmt.Errorf("invalid identifier: %s", identifier)
}
if (strings.HasPrefix(path, "\"") && strings.HasSuffix(path, "\"")) ||
(strings.HasPrefix(path, "'") && strings.HasSuffix(path, "'")) {
path = path[1 : len(path)-1]
if path == "" {
return nil, fmt.Errorf("invalid identifier: %s", identifier)
}
if typeutil.IsArrayType(field.DataType) {
return nil, fmt.Errorf("can only access array field with integer index")
}
} else if _, err := strconv.ParseInt(path, 10, 64); err != nil {
return nil, fmt.Errorf("json key must be enclosed in double quotes or single quotes: \"%s\"", path)
}
nestedPath = append(nestedPath, path)
}
return &planpb.ColumnInfo{
FieldId: field.FieldID,
DataType: field.DataType,
NestedPath: nestedPath,
ElementType: field.GetElementType(),
}, nil
}
func (v *ParserVisitor) VisitJSONIdentifier(ctx *parser.JSONIdentifierContext) interface{} {
field, err := v.getColumnInfoFromJSONIdentifier(ctx.JSONIdentifier().GetText())
if err != nil {
return err
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_ColumnExpr{
ColumnExpr: &planpb.ColumnExpr{
Info: &planpb.ColumnInfo{
FieldId: field.GetFieldId(),
DataType: field.GetDataType(),
NestedPath: field.GetNestedPath(),
ElementType: field.GetElementType(),
},
},
},
},
dataType: field.GetDataType(),
nodeDependent: true,
}
}
func (v *ParserVisitor) VisitExists(ctx *parser.ExistsContext) interface{} {
child := ctx.Expr().Accept(v)
if err := getError(child); err != nil {
return err
}
columnInfo := toColumnInfo(child.(*ExprWithType))
if columnInfo == nil {
return fmt.Errorf(
"exists operations are only supported on single fields now, got: %s", ctx.Expr().GetText())
}
if columnInfo.GetDataType() != schemapb.DataType_JSON {
return fmt.Errorf(
"exists operations are only supportted on json field, got:%s", columnInfo.GetDataType())
}
if len(columnInfo.GetNestedPath()) == 0 {
return fmt.Errorf(
"exists operations are only supportted on json key")
}
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_ExistsExpr{
ExistsExpr: &planpb.ExistsExpr{
Info: &planpb.ColumnInfo{
FieldId: columnInfo.GetFieldId(),
DataType: columnInfo.GetDataType(),
NestedPath: columnInfo.GetNestedPath(),
},
},
},
},
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitArray(ctx *parser.ArrayContext) interface{} {
allExpr := ctx.AllExpr()
array := make([]*planpb.GenericValue, len(allExpr))
dType := schemapb.DataType_None
sameType := true
for i := 0; i < len(allExpr); i++ {
element := allExpr[i].Accept(v)
if err := getError(element); err != nil {
return err
}
elementValue := getGenericValue(element)
if elementValue == nil {
return fmt.Errorf("array element type must be generic value, but got: %s", allExpr[i].GetText())
}
array[i] = elementValue
if dType == schemapb.DataType_None {
dType = element.(*ExprWithType).dataType
} else if dType != element.(*ExprWithType).dataType {
sameType = false
}
}
if !sameType {
dType = schemapb.DataType_None
}
return &ExprWithType{
dataType: schemapb.DataType_Array,
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: &planpb.GenericValue{
Val: &planpb.GenericValue_ArrayVal{
ArrayVal: &planpb.Array{
Array: array,
SameType: sameType,
ElementType: dType,
},
},
},
},
},
},
nodeDependent: true,
}
}
func (v *ParserVisitor) VisitEmptyArray(ctx *parser.EmptyArrayContext) interface{} {
return &ExprWithType{
dataType: schemapb.DataType_Array,
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: &planpb.GenericValue{
Val: &planpb.GenericValue_ArrayVal{
ArrayVal: &planpb.Array{
Array: nil,
SameType: true,
ElementType: schemapb.DataType_None,
},
},
},
},
},
},
nodeDependent: true,
}
}
func (v *ParserVisitor) VisitJSONContains(ctx *parser.JSONContainsContext) interface{} {
field := ctx.Expr(0).Accept(v)
if err := getError(field); err != nil {
return err
}
columnInfo := toColumnInfo(field.(*ExprWithType))
if columnInfo == nil ||
(!typeutil.IsJSONType(columnInfo.GetDataType()) && !typeutil.IsArrayType(columnInfo.GetDataType())) {
return fmt.Errorf(
"contains operation are only supported on json or array fields now, got: %s", ctx.Expr(0).GetText())
}
element := ctx.Expr(1).Accept(v)
if err := getError(element); err != nil {
return err
}
elementExpr := getValueExpr(element)
if elementExpr == nil {
return fmt.Errorf(
"contains operation are only supported explicitly specified element, got: %s", ctx.Expr(1).GetText())
}
var elements []*planpb.GenericValue
if !isTemplateExpr(elementExpr) {
elements = make([]*planpb.GenericValue, 1)
elementValue := elementExpr.GetValue()
if err := checkContainsElement(field.(*ExprWithType), planpb.JSONContainsExpr_Contains, elementValue); err != nil {
return err
}
elements[0] = elementValue
}
expr := &planpb.Expr{
Expr: &planpb.Expr_JsonContainsExpr{
JsonContainsExpr: &planpb.JSONContainsExpr{
ColumnInfo: columnInfo,
Elements: elements,
Op: planpb.JSONContainsExpr_Contains,
ElementsSameType: true,
TemplateVariableName: elementExpr.GetTemplateVariableName(),
},
},
IsTemplate: isTemplateExpr(elementExpr),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitJSONContainsAll(ctx *parser.JSONContainsAllContext) interface{} {
field := ctx.Expr(0).Accept(v)
if err := getError(field); err != nil {
return err
}
columnInfo := toColumnInfo(field.(*ExprWithType))
if columnInfo == nil ||
(!typeutil.IsJSONType(columnInfo.GetDataType()) && !typeutil.IsArrayType(columnInfo.GetDataType())) {
return fmt.Errorf(
"contains_all operation are only supported on json or array fields now, got: %s", ctx.Expr(0).GetText())
}
element := ctx.Expr(1).Accept(v)
if err := getError(element); err != nil {
return err
}
elementExpr := getValueExpr(element)
if elementExpr == nil {
return fmt.Errorf(
"contains_all operation are only supported explicitly specified element, got: %s", ctx.Expr(1).GetText())
}
var elements []*planpb.GenericValue
var sameType bool
if !isTemplateExpr(elementExpr) {
elementValue := elementExpr.GetValue()
if err := checkContainsElement(field.(*ExprWithType), planpb.JSONContainsExpr_ContainsAll, elementValue); err != nil {
return err
}
elements = elementValue.GetArrayVal().GetArray()
sameType = elementValue.GetArrayVal().GetSameType()
}
expr := &planpb.Expr{
Expr: &planpb.Expr_JsonContainsExpr{
JsonContainsExpr: &planpb.JSONContainsExpr{
ColumnInfo: columnInfo,
Elements: elements,
Op: planpb.JSONContainsExpr_ContainsAll,
ElementsSameType: sameType,
TemplateVariableName: elementExpr.GetTemplateVariableName(),
},
},
IsTemplate: isTemplateExpr(elementExpr),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitJSONContainsAny(ctx *parser.JSONContainsAnyContext) interface{} {
field := ctx.Expr(0).Accept(v)
if err := getError(field); err != nil {
return err
}
columnInfo := toColumnInfo(field.(*ExprWithType))
if columnInfo == nil ||
(!typeutil.IsJSONType(columnInfo.GetDataType()) && !typeutil.IsArrayType(columnInfo.GetDataType())) {
return fmt.Errorf(
"contains_any operation are only supported on json or array fields now, got: %s", ctx.Expr(0).GetText())
}
element := ctx.Expr(1).Accept(v)
if err := getError(element); err != nil {
return err
}
valueExpr := getValueExpr(element)
if valueExpr == nil {
return fmt.Errorf(
"contains_any operation are only supported explicitly specified element, got: %s", ctx.Expr(1).GetText())
}
var elements []*planpb.GenericValue
var sameType bool
if !isTemplateExpr(valueExpr) {
elementValue := valueExpr.GetValue()
if err := checkContainsElement(field.(*ExprWithType), planpb.JSONContainsExpr_ContainsAny, elementValue); err != nil {
return err
}
elements = elementValue.GetArrayVal().GetArray()
sameType = elementValue.GetArrayVal().GetSameType()
}
expr := &planpb.Expr{
Expr: &planpb.Expr_JsonContainsExpr{
JsonContainsExpr: &planpb.JSONContainsExpr{
ColumnInfo: columnInfo,
Elements: elements,
Op: planpb.JSONContainsExpr_ContainsAny,
ElementsSameType: sameType,
TemplateVariableName: valueExpr.GetTemplateVariableName(),
},
},
IsTemplate: isTemplateExpr(valueExpr),
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitArrayLength(ctx *parser.ArrayLengthContext) interface{} {
columnInfo, err := v.getChildColumnInfo(ctx.Identifier(), ctx.JSONIdentifier())
if err != nil {
return err
}
if columnInfo == nil ||
(!typeutil.IsJSONType(columnInfo.GetDataType()) && !typeutil.IsArrayType(columnInfo.GetDataType())) {
return fmt.Errorf(
"array_length operation are only supported on json or array fields now, got: %s", ctx.GetText())
}
expr := &planpb.Expr{
Expr: &planpb.Expr_BinaryArithExpr{
BinaryArithExpr: &planpb.BinaryArithExpr{
Left: &planpb.Expr{
Expr: &planpb.Expr_ColumnExpr{
ColumnExpr: &planpb.ColumnExpr{
Info: columnInfo,
},
},
},
Right: nil,
Op: planpb.ArithOpType_ArrayLength,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Int64,
nodeDependent: true,
}
}
func (v *ParserVisitor) VisitTemplateVariable(ctx *parser.TemplateVariableContext) interface{} {
return &ExprWithType{
expr: &planpb.Expr{
Expr: &planpb.Expr_ValueExpr{
ValueExpr: &planpb.ValueExpr{
Value: nil,
TemplateVariableName: ctx.Identifier().GetText(),
},
},
IsTemplate: true,
},
}
}