milvus/internal/parser/planparserv2/parser_visitor.go
Hao Tan 67c4340565
feat: Geospatial Data Type and GIS Function Support for milvus server (#35990)
issue:https://github.com/milvus-io/milvus/issues/27576

# Main Goals
1. Create and describe collections with geospatial fields, enabling both
client and server to recognize and process geo fields.
2. Insert geospatial data as payload values in the insert binlog, and
print the values for verification.
3. Load segments containing geospatial data into memory.
4. Ensure query outputs can display geospatial data.
5. Support filtering on GIS functions for geospatial columns.

# Solution
1. **Add Type**: Modify the Milvus core by adding a Geospatial type in
both the C++ and Go code layers, defining the Geospatial data structure
and the corresponding interfaces.
2. **Dependency Libraries**: Introduce necessary geospatial data
processing libraries. In the C++ source code, use Conan package
management to include the GDAL library. In the Go source code, add the
go-geom library to the go.mod file.
3. **Protocol Interface**: Revise the Milvus protocol to provide
mechanisms for Geospatial message serialization and deserialization.
4. **Data Pipeline**: Facilitate interaction between the client and
proxy using the WKT format for geospatial data. The proxy will convert
all data into WKB format for downstream processing, providing column
data interfaces, segment encapsulation, segment loading, payload
writing, and cache block management.
5. **Query Operators**: Implement simple display and support for filter
queries. Initially, focus on filtering based on spatial relationships
for a single column of geospatial literal values, providing parsing and
execution for query expressions.
6. **Client Modification**: Enable the client to handle user input for
geospatial data and facilitate end-to-end testing.Check the modification
in pymilvus.

---------

Signed-off-by: tasty-gumi <1021989072@qq.com>
2024-10-31 20:58:20 +08:00

1600 lines
45 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(
"contains_any operation are only supported explicitly specified element, got: %s", 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 err = checkRangeCompared(fieldDataType, lowerValue); err != nil {
return err
}
}
if !isTemplateExpr(upperValueExpr) {
if err = checkRangeCompared(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 err = checkRangeCompared(fieldDataType, lowerValue); err != nil {
return err
}
}
if !isTemplateExpr(upperValueExpr) {
if err = checkRangeCompared(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,
},
}
}
func (v *ParserVisitor) VisitSTEuqals(ctx *parser.STEuqalsContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STEuqals operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText()
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Equals,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitSTTouches(ctx *parser.STTouchesContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STTouches operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText()
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Touches,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitSTOverlaps(ctx *parser.STOverlapsContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STOverlaps operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText()
// log.Warn(element)
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Overlaps,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitSTCrosses(ctx *parser.STCrossesContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STCrosses operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText()
// log.Warn(element)
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Crosses,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitSTContains(ctx *parser.STContainsContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STContains operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText() // the wkt input
// log.Warn(element)
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Contains,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitSTIntersects(ctx *parser.STIntersectsContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STIntersects operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText() // the wkt input
// log.Warn(element)
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Intersects,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}
func (v *ParserVisitor) VisitSTWithin(ctx *parser.STWithinContext) interface{} {
childExpr, err := v.translateIdentifier(ctx.Identifier().GetText())
if err != nil {
return err
}
columnInfo := toColumnInfo(childExpr)
if columnInfo == nil ||
(!typeutil.IsGeometryType(columnInfo.GetDataType())) {
return fmt.Errorf(
"STWithin operation are only supported on geometry fields now, got: %s", ctx.GetText())
}
element := ctx.StringLiteral().GetText() // the wkt input
// log.Warn(element)
if err := getError(element); err != nil {
return err
}
expr := &planpb.Expr{
Expr: &planpb.Expr_GisfunctionFilterExpr{
GisfunctionFilterExpr: &planpb.GISFunctionFilterExpr{
ColumnInfo: columnInfo,
WktString: element[1 : len(element)-1],
Op: planpb.GISFunctionFilterExpr_Within,
},
},
}
return &ExprWithType{
expr: expr,
dataType: schemapb.DataType_Bool,
}
}