mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
FB-2041 Added tests for typeIsTimeQuantum and typeIsSet and made them pass. Added tests for DataTypeTuple cases in typesAreAssignmentCompatible. Timestamp conversion checking is handled before it gets to that point but I left those branches in as a backstop. Checking to see if DataType[String,ID]SetQuantum can be assigned to themselves doesn't appear to be reachable currently but left those branches in, because something may use them in future. Added one test to the DAX skip list since it's the IDSetQ version of a StringSetQ test that was already on there, changed skip list to refer to both tests by name instead of by number.
2993 lines
79 KiB
Go
2993 lines
79 KiB
Go
// Copyright 2022 Molecula Corp. All rights reserved.
|
|
|
|
package planner
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"regexp"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/featurebasedb/featurebase/v3/pql"
|
|
"github.com/featurebasedb/featurebase/v3/sql3"
|
|
"github.com/featurebasedb/featurebase/v3/sql3/parser"
|
|
"github.com/featurebasedb/featurebase/v3/sql3/planner/types"
|
|
)
|
|
|
|
// coerceValue coerces a value from a source type to a target type. If the types do not allow a conversion
|
|
// an error is produced
|
|
func coerceValue(sourceType parser.ExprDataType, targetType parser.ExprDataType, value interface{}, atPos parser.Pos) (interface{}, error) {
|
|
switch sourceType.(type) {
|
|
|
|
case *parser.DataTypeInt:
|
|
switch t := targetType.(type) {
|
|
case *parser.DataTypeInt:
|
|
return value, nil
|
|
|
|
case *parser.DataTypeID:
|
|
val, ok := value.(int64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected value type '%T'", value)
|
|
}
|
|
return val, nil
|
|
|
|
case *parser.DataTypeDecimal:
|
|
val, ok := value.(int64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected value type '%T'", value)
|
|
}
|
|
return pql.NewDecimal(val*int64(math.Pow(10, float64(t.Scale))), t.Scale), nil
|
|
case *parser.DataTypeTimestamp:
|
|
val, ok := value.(int64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected value type '%T'", value)
|
|
}
|
|
tm := time.Unix(val, 0).UTC()
|
|
return tm, nil
|
|
}
|
|
|
|
case *parser.DataTypeID:
|
|
switch t := targetType.(type) {
|
|
case *parser.DataTypeID:
|
|
return value, nil
|
|
|
|
case *parser.DataTypeInt:
|
|
return value, nil
|
|
|
|
case *parser.DataTypeDecimal:
|
|
val, ok := value.(int64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected value type '%T'", value)
|
|
}
|
|
return pql.NewDecimal(int64(val)*int64(math.Pow(10, float64(t.Scale))), t.Scale), nil
|
|
case *parser.DataTypeTimestamp:
|
|
val, ok := value.(int64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected value type '%T'", value)
|
|
}
|
|
tm := time.Unix(val, 0).UTC()
|
|
return tm, nil
|
|
}
|
|
|
|
case *parser.DataTypeDecimal:
|
|
switch targetType.(type) {
|
|
case *parser.DataTypeDecimal:
|
|
return value, nil
|
|
}
|
|
|
|
case *parser.DataTypeString:
|
|
switch targetType.(type) {
|
|
case *parser.DataTypeString:
|
|
return value, nil
|
|
case *parser.DataTypeTimestamp:
|
|
// try to coerce to a date
|
|
val, ok := value.(string)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected value type '%T'", value)
|
|
}
|
|
if tm, err := time.ParseInLocation(time.RFC3339Nano, val, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else if tm, err := time.ParseInLocation(time.RFC3339, val, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else if tm, err := time.ParseInLocation("2006-01-02", val, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else {
|
|
return nil, sql3.NewErrInvalidTypeCoercion(0, 0, val, targetType.TypeDescription())
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeTimestamp:
|
|
switch targetType.(type) {
|
|
case *parser.DataTypeTimestamp:
|
|
return value, nil
|
|
}
|
|
|
|
case *parser.DataTypeIDSet:
|
|
switch targetType.(type) {
|
|
case *parser.DataTypeIDSet:
|
|
return value, nil
|
|
case *parser.DataTypeIDSetQuantum:
|
|
return []interface{}{
|
|
nil, // no timestamp
|
|
value,
|
|
}, nil
|
|
}
|
|
|
|
case *parser.DataTypeStringSet:
|
|
switch targetType.(type) {
|
|
case *parser.DataTypeStringSet:
|
|
return value, nil
|
|
case *parser.DataTypeStringSetQuantum:
|
|
return []interface{}{
|
|
nil, // no timestamp
|
|
value,
|
|
}, nil
|
|
}
|
|
|
|
case *parser.DataTypeTuple:
|
|
switch targetType.(type) {
|
|
case *parser.DataTypeIDSetQuantum:
|
|
return value, nil
|
|
|
|
case *parser.DataTypeStringSetQuantum:
|
|
return value, nil
|
|
}
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled source type '%T'", sourceType)
|
|
}
|
|
return nil, sql3.NewErrTypeMismatch(atPos.Line, atPos.Column, targetType.TypeDescription(), sourceType.TypeDescription())
|
|
}
|
|
|
|
// unaryOpPlanExpression is a unary op
|
|
type unaryOpPlanExpression struct {
|
|
op parser.Token
|
|
rhs types.PlanExpression
|
|
|
|
resultDataType parser.ExprDataType
|
|
}
|
|
|
|
func newUnaryOpPlanExpression(op parser.Token, rhs types.PlanExpression, dataType parser.ExprDataType) *unaryOpPlanExpression {
|
|
return &unaryOpPlanExpression{
|
|
op: op,
|
|
rhs: rhs,
|
|
resultDataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
evalRhs, err := n.rhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch n.op {
|
|
case parser.BITNOT:
|
|
return n.bitNotWithTypeCheck(evalRhs)
|
|
case parser.PLUS:
|
|
return n.plusWithTypeCheck(evalRhs)
|
|
case parser.MINUS:
|
|
return n.minusWithTypeCheck(evalRhs)
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) Type() parser.ExprDataType {
|
|
return n.resultDataType
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) String() string {
|
|
return fmt.Sprintf("%s%s", n.op.String(), n.rhs.String())
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["op"] = n.op
|
|
result["rhs"] = n.rhs.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.rhs,
|
|
}
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 1 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newUnaryOpPlanExpression(n.op, children[0], n.resultDataType), nil
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) bitNotWithTypeCheck(rhs interface{}) (interface{}, error) {
|
|
switch n.resultDataType.(type) {
|
|
case *parser.DataTypeID:
|
|
nr, nrok := rhs.(int64)
|
|
if nrok {
|
|
return ^nr, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
case *parser.DataTypeInt:
|
|
nr, nrok := rhs.(int64)
|
|
if nrok {
|
|
return ^nr, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected type '%T", n.resultDataType)
|
|
}
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) plusWithTypeCheck(rhs interface{}) (interface{}, error) {
|
|
switch n.resultDataType.(type) {
|
|
case *parser.DataTypeID:
|
|
nr, nrok := rhs.(int64)
|
|
if nrok {
|
|
return +nr, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
case *parser.DataTypeInt:
|
|
coercedRhs, err := coerceValue(n.rhs.Type(), n.resultDataType, rhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nr, nrok := coercedRhs.(int64)
|
|
if nrok {
|
|
return +nr, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
case *parser.DataTypeDecimal:
|
|
nr, nrok := rhs.(pql.Decimal)
|
|
if nrok {
|
|
val := nr.Value()
|
|
if !val.IsInt64() {
|
|
return nil, sql3.NewErrInternalf("decimal value overflow: %v", rhs)
|
|
}
|
|
return pql.NewDecimal(+val.Int64(), nr.Scale), nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected type '%T", n.resultDataType)
|
|
}
|
|
}
|
|
|
|
func (n *unaryOpPlanExpression) minusWithTypeCheck(rhs interface{}) (interface{}, error) {
|
|
switch n.resultDataType.(type) {
|
|
case *parser.DataTypeID:
|
|
nr, nrok := rhs.(int64)
|
|
if nrok {
|
|
return -nr, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
case *parser.DataTypeInt:
|
|
coercedRhs, err := coerceValue(n.rhs.Type(), n.resultDataType, rhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nr, nrok := coercedRhs.(int64)
|
|
if nrok {
|
|
return -nr, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
case *parser.DataTypeDecimal:
|
|
nr, nrok := rhs.(pql.Decimal)
|
|
if nrok {
|
|
val := nr.Value()
|
|
if !val.IsInt64() {
|
|
return nil, sql3.NewErrInternalf("decimal value overflow: %v", rhs)
|
|
}
|
|
return pql.NewDecimal(-val.Int64(), nr.Scale), nil
|
|
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected incompatible types '%T", rhs)
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected type '%T", n.resultDataType)
|
|
}
|
|
}
|
|
|
|
// binOpPlanExpression is a binary op
|
|
type binOpPlanExpression struct {
|
|
lhs types.PlanExpression
|
|
op parser.Token
|
|
rhs types.PlanExpression
|
|
|
|
resultDataType parser.ExprDataType
|
|
}
|
|
|
|
func newBinOpPlanExpression(lhs types.PlanExpression, op parser.Token, rhs types.PlanExpression, dataType parser.ExprDataType) *binOpPlanExpression {
|
|
return &binOpPlanExpression{
|
|
lhs: lhs,
|
|
op: op,
|
|
rhs: rhs,
|
|
resultDataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *binOpPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
evalLhs, err := n.lhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
evalRhs, err := n.rhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if n.op == parser.IS || n.op == parser.ISNOT {
|
|
isNull := evalLhs == nil
|
|
if n.op == parser.ISNOT {
|
|
isNull = !isNull
|
|
}
|
|
return isNull, nil
|
|
}
|
|
|
|
coercedDataType, err := typeCoerceType(n.lhs.Type(), n.rhs.Type(), parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch coercedDataType.(type) {
|
|
case *parser.DataTypeBool:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
nl, nlok := evalLhs.(bool)
|
|
nr, nrok := evalRhs.(bool)
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return nl != nr, nil
|
|
case parser.EQ:
|
|
return nl == nr, nil
|
|
case parser.AND:
|
|
return nl && nr, nil
|
|
case parser.OR:
|
|
return nl || nr, nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
case *parser.DataTypeInt:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
coercedLhs, err := coerceValue(n.lhs.Type(), coercedDataType, evalLhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coercedRhs, err := coerceValue(n.rhs.Type(), coercedDataType, evalRhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nl, nlok := coercedLhs.(int64)
|
|
nr, nrok := coercedRhs.(int64)
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return nl != nr, nil
|
|
case parser.EQ:
|
|
return nl == nr, nil
|
|
case parser.LE:
|
|
return nl <= nr, nil
|
|
case parser.GE:
|
|
return nl >= nr, nil
|
|
case parser.GT:
|
|
return nl > nr, nil
|
|
case parser.LT:
|
|
return nl < nr, nil
|
|
|
|
case parser.BITAND:
|
|
return nl & nr, nil
|
|
case parser.BITOR:
|
|
return nl | nr, nil
|
|
|
|
case parser.LSHIFT:
|
|
return nl << nr, nil
|
|
case parser.RSHIFT:
|
|
return nl >> nr, nil
|
|
|
|
case parser.PLUS:
|
|
return nl + nr, nil
|
|
case parser.MINUS:
|
|
return nl - nr, nil
|
|
case parser.STAR:
|
|
return nl * nr, nil
|
|
case parser.SLASH:
|
|
if nr == 0 {
|
|
return nil, sql3.NewErrDivideByZero(0, 0)
|
|
}
|
|
return nl / nr, nil
|
|
case parser.REM:
|
|
if nr == 0 {
|
|
return nil, sql3.NewErrDivideByZero(0, 0)
|
|
}
|
|
return nl % nr, nil
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
case *parser.DataTypeID:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
coercedLhs, err := coerceValue(n.lhs.Type(), coercedDataType, evalLhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coercedRhs, err := coerceValue(n.rhs.Type(), coercedDataType, evalRhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nl, nlok := coercedLhs.(int64)
|
|
nr, nrok := coercedRhs.(int64)
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return nl != nr, nil
|
|
case parser.EQ:
|
|
return nl == nr, nil
|
|
case parser.LE:
|
|
return nl <= nr, nil
|
|
case parser.GE:
|
|
return nl >= nr, nil
|
|
case parser.GT:
|
|
return nl > nr, nil
|
|
case parser.LT:
|
|
return nl < nr, nil
|
|
|
|
case parser.BITAND:
|
|
return nl & nr, nil
|
|
case parser.BITOR:
|
|
return nl | nr, nil
|
|
|
|
case parser.LSHIFT:
|
|
return nl << nr, nil
|
|
case parser.RSHIFT:
|
|
return nl >> nr, nil
|
|
|
|
case parser.PLUS:
|
|
return nl + nr, nil
|
|
case parser.MINUS:
|
|
return nl - nr, nil
|
|
case parser.STAR:
|
|
return nl * nr, nil
|
|
case parser.SLASH:
|
|
return nl / nr, nil
|
|
|
|
case parser.REM:
|
|
return nl % nr, nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
case *parser.DataTypeDecimal:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
coercedLhs, err := coerceValue(n.lhs.Type(), coercedDataType, evalLhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coercedRhs, err := coerceValue(n.rhs.Type(), coercedDataType, evalRhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nld, nlok := coercedLhs.(pql.Decimal)
|
|
nrd, nrok := coercedRhs.(pql.Decimal)
|
|
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return !nld.EqualTo(nrd), nil
|
|
case parser.EQ:
|
|
return nld.EqualTo(nrd), nil
|
|
case parser.LE:
|
|
return nld.LessThanOrEqualTo(nrd), nil
|
|
case parser.GE:
|
|
return nld.GreaterThanOrEqualTo(nrd), nil
|
|
case parser.GT:
|
|
return nld.GreaterThan(nrd), nil
|
|
case parser.LT:
|
|
return nld.LessThan(nrd), nil
|
|
|
|
case parser.PLUS:
|
|
return pql.AddDecimal(nld, nrd), nil
|
|
case parser.MINUS:
|
|
return pql.SubtractDecimal(nld, nrd), nil
|
|
case parser.STAR:
|
|
return pql.MultiplyDecimal(nld, nrd), nil
|
|
case parser.SLASH:
|
|
return pql.DivideDecimal(nld, nrd), nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%T', '%T'", coercedLhs, coercedRhs)
|
|
|
|
case *parser.DataTypeTimestamp:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
coercedLhs, err := coerceValue(n.lhs.Type(), coercedDataType, evalLhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
coercedRhs, err := coerceValue(n.rhs.Type(), coercedDataType, evalRhs, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nl, nlok := coercedLhs.(time.Time)
|
|
nr, nrok := coercedRhs.(time.Time)
|
|
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return nl != nr, nil
|
|
case parser.EQ:
|
|
return nl == nr, nil
|
|
case parser.LE:
|
|
return nl == nr || nl.Before(nr), nil
|
|
case parser.GE:
|
|
return nl == nr || nl.After(nr), nil
|
|
case parser.GT:
|
|
return nl.After(nr), nil
|
|
case parser.LT:
|
|
return nl.Before(nr), nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
case *parser.DataTypeIDSet:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
nl, nlok := evalLhs.([]int64)
|
|
nr, nrok := evalRhs.([]int64)
|
|
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return !intSetContainsAll(nl, nr), nil
|
|
case parser.EQ:
|
|
return intSetContainsAll(nl, nr), nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
case *parser.DataTypeString:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
nl, nlok := evalLhs.(string)
|
|
nr, nrok := evalRhs.(string)
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
|
|
case parser.NE:
|
|
return nl != nr, nil
|
|
|
|
case parser.EQ:
|
|
return nl == nr, nil
|
|
|
|
case parser.CONCAT:
|
|
return nl + nr, nil
|
|
|
|
case parser.LIKE:
|
|
regexPattern := wildCardToRegexp(nr)
|
|
|
|
matched, err := regexp.MatchString(regexPattern, nl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return matched, nil
|
|
|
|
case parser.NOTLIKE:
|
|
regexPattern := wildCardToRegexp(nr)
|
|
matched, err := regexp.MatchString(regexPattern, nl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return !matched, nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
case *parser.DataTypeStringSet:
|
|
// if either side is nil, return nil
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
nl, nlok := evalLhs.([]string)
|
|
nr, nrok := evalRhs.([]string)
|
|
|
|
if nlok && nrok {
|
|
switch n.op {
|
|
case parser.NE:
|
|
return !stringSetContainsAll(nl, nr), nil
|
|
case parser.EQ:
|
|
return stringSetContainsAll(nl, nr), nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled operator %d", n.op)
|
|
}
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%s'", coercedDataType.TypeDescription())
|
|
}
|
|
}
|
|
|
|
func (n *binOpPlanExpression) Type() parser.ExprDataType {
|
|
return n.resultDataType
|
|
}
|
|
|
|
func (n *binOpPlanExpression) String() string {
|
|
return fmt.Sprintf("%s%s%s", n.lhs.String(), n.op.String(), n.rhs.String())
|
|
}
|
|
|
|
func (n *binOpPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["op"] = n.op
|
|
result["lhs"] = n.lhs.Plan()
|
|
result["rhs"] = n.rhs.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *binOpPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.lhs,
|
|
n.rhs,
|
|
}
|
|
}
|
|
|
|
func (n *binOpPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 2 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newBinOpPlanExpression(children[0], n.op, children[1], n.resultDataType), nil
|
|
}
|
|
|
|
// rangePlanExpression is a range expression
|
|
type rangePlanExpression struct {
|
|
lhs types.PlanExpression
|
|
rhs types.PlanExpression
|
|
|
|
resultDataType parser.ExprDataType
|
|
}
|
|
|
|
func newRangeOpPlanExpression(lhs types.PlanExpression, rhs types.PlanExpression, dataType parser.ExprDataType) *rangePlanExpression {
|
|
return &rangePlanExpression{
|
|
lhs: lhs,
|
|
rhs: rhs,
|
|
resultDataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *rangePlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
evalLhs, err := n.lhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
evalRhs, err := n.rhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if evalLhs == nil || evalRhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
/*nl*/
|
|
_, nlok := evalLhs.(int64)
|
|
/*nr*/ _, nrok := evalRhs.(int64)
|
|
if nlok && nrok {
|
|
return true, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
}
|
|
|
|
func (n *rangePlanExpression) Type() parser.ExprDataType {
|
|
return n.resultDataType
|
|
}
|
|
|
|
func (n *rangePlanExpression) String() string {
|
|
return fmt.Sprintf("between %s and %s", n.lhs.String(), n.rhs.String())
|
|
}
|
|
|
|
func (n *rangePlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["lhs"] = n.lhs.Plan()
|
|
result["rhs"] = n.rhs.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *rangePlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.lhs,
|
|
n.rhs,
|
|
}
|
|
}
|
|
|
|
func (n *rangePlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 2 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newRangeOpPlanExpression(children[0], children[1], n.resultDataType), nil
|
|
}
|
|
|
|
// casePlanExpression is a case expr
|
|
type casePlanExpression struct {
|
|
baseExpr types.PlanExpression
|
|
blocks []types.PlanExpression
|
|
elseExpr types.PlanExpression
|
|
|
|
resultDataType parser.ExprDataType
|
|
}
|
|
|
|
func newCasePlanExpression(baseExpr types.PlanExpression, blocks []types.PlanExpression, elseExpr types.PlanExpression, dataType parser.ExprDataType) *casePlanExpression {
|
|
return &casePlanExpression{
|
|
baseExpr: baseExpr,
|
|
blocks: blocks,
|
|
elseExpr: elseExpr,
|
|
resultDataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *casePlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
if n.baseExpr != nil {
|
|
evalBase, err := n.baseExpr.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if evalBase == nil {
|
|
return nil, nil
|
|
}
|
|
for _, block := range n.blocks {
|
|
caseBlock, ok := block.(*caseBlockPlanExpression)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected block type '%T'", block)
|
|
}
|
|
|
|
evalBlock, err := caseBlock.condition.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch n.baseExpr.Type().(type) {
|
|
case *parser.DataTypeInt:
|
|
nl, nlok := evalBase.(int64)
|
|
nr, nrok := evalBlock.(int64)
|
|
if nlok && nrok {
|
|
if nl == nr {
|
|
evalBlockBody, err := caseBlock.body.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if evalBlockBody == nil {
|
|
return nil, nil
|
|
}
|
|
switch caseBlock.body.Type().(type) {
|
|
case *parser.DataTypeInt:
|
|
b, bok := evalBlockBody.(int64)
|
|
if bok {
|
|
return b, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", bok)
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%s'", n.baseExpr.Type())
|
|
}
|
|
}
|
|
} else {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t'", nlok, nrok)
|
|
}
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%s'", n.baseExpr.Type())
|
|
}
|
|
}
|
|
// if we get to here, we're falling back to else
|
|
if n.elseExpr != nil {
|
|
evalElse, err := n.elseExpr.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if evalElse == nil {
|
|
return nil, nil
|
|
}
|
|
switch n.elseExpr.Type().(type) {
|
|
case *parser.DataTypeInt:
|
|
el, elok := evalElse.(int64)
|
|
if elok {
|
|
return el, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", elok)
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%s'", n.elseExpr.Type())
|
|
|
|
}
|
|
}
|
|
return nil, nil
|
|
} else {
|
|
for _, block := range n.blocks {
|
|
caseBlock, ok := block.(*caseBlockPlanExpression)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected block type '%T'", block)
|
|
}
|
|
|
|
evalBlock, err := caseBlock.condition.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bl, blok := evalBlock.(bool)
|
|
if !blok {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", blok)
|
|
}
|
|
if bl {
|
|
evalBlockBody, err := caseBlock.body.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if evalBlockBody == nil {
|
|
return nil, nil
|
|
}
|
|
switch caseBlock.body.Type().(type) {
|
|
case *parser.DataTypeInt:
|
|
b, bok := evalBlockBody.(int64)
|
|
if bok {
|
|
return b, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", bok)
|
|
|
|
case *parser.DataTypeBool:
|
|
b, bok := evalBlockBody.(bool)
|
|
if bok {
|
|
return b, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", bok)
|
|
|
|
case *parser.DataTypeString:
|
|
s, sok := evalBlockBody.(string)
|
|
if sok {
|
|
return s, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", sok)
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%T'", caseBlock.body.Type())
|
|
}
|
|
}
|
|
}
|
|
// if we get to here, we're falling back to else
|
|
if n.elseExpr != nil {
|
|
evalElse, err := n.elseExpr.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if evalElse == nil {
|
|
return nil, nil
|
|
}
|
|
switch n.elseExpr.Type().(type) {
|
|
case *parser.DataTypeInt:
|
|
el, elok := evalElse.(int64)
|
|
if elok {
|
|
return el, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", elok)
|
|
case *parser.DataTypeString:
|
|
s, sok := evalElse.(string)
|
|
if sok {
|
|
return s, nil
|
|
}
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", sok)
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%T'", n.elseExpr.Type())
|
|
|
|
}
|
|
}
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func (n *casePlanExpression) Type() parser.ExprDataType {
|
|
return n.resultDataType
|
|
}
|
|
|
|
func (n *casePlanExpression) String() string {
|
|
var result string
|
|
if n.baseExpr != nil {
|
|
result = fmt.Sprintf("case %s", n.baseExpr)
|
|
} else {
|
|
result = "case"
|
|
}
|
|
for _, blk := range n.blocks {
|
|
result += fmt.Sprintf(" %s", blk.String())
|
|
}
|
|
if n.elseExpr != nil {
|
|
result += fmt.Sprintf(" else %s end", n.elseExpr)
|
|
} else {
|
|
result += " end"
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (n *casePlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
if n.baseExpr != nil {
|
|
result["baseExpr"] = n.baseExpr.Plan()
|
|
}
|
|
if n.elseExpr != nil {
|
|
result["elseExpr"] = n.elseExpr.Plan()
|
|
}
|
|
ps := make([]interface{}, 0)
|
|
for _, e := range n.blocks {
|
|
ps = append(ps, e.Plan())
|
|
}
|
|
result["blocks"] = ps
|
|
return result
|
|
}
|
|
|
|
func (n *casePlanExpression) Children() []types.PlanExpression {
|
|
result := make([]types.PlanExpression, 0)
|
|
if n.baseExpr != nil {
|
|
result = append(result, n.baseExpr)
|
|
}
|
|
result = append(result, n.blocks...)
|
|
if n.elseExpr != nil {
|
|
result = append(result, n.elseExpr)
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (n *casePlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
currentLen := 0
|
|
if n.baseExpr != nil {
|
|
currentLen += 1
|
|
}
|
|
currentLen += len(n.blocks)
|
|
if n.elseExpr != nil {
|
|
currentLen += 1
|
|
}
|
|
if len(children) != currentLen {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
|
|
offset := 0
|
|
var newBaseExpr types.PlanExpression
|
|
if n.baseExpr != nil {
|
|
newBaseExpr = children[offset]
|
|
offset += 1
|
|
}
|
|
newBlocks := make([]types.PlanExpression, len(n.blocks))
|
|
copy(newBlocks[0:], children[offset:offset+len(n.blocks)])
|
|
offset += len(n.blocks)
|
|
|
|
var newElseExpr types.PlanExpression
|
|
if n.elseExpr != nil {
|
|
newElseExpr = children[offset]
|
|
}
|
|
return newCasePlanExpression(newBaseExpr, newBlocks, newElseExpr, n.resultDataType), nil
|
|
}
|
|
|
|
// caseBlockPlanExpression is for case blocks
|
|
type caseBlockPlanExpression struct {
|
|
condition types.PlanExpression
|
|
body types.PlanExpression
|
|
}
|
|
|
|
func newCaseBlockPlanExpression(condition types.PlanExpression, body types.PlanExpression) *caseBlockPlanExpression {
|
|
return &caseBlockPlanExpression{
|
|
condition: condition,
|
|
body: body,
|
|
}
|
|
}
|
|
|
|
func (n *caseBlockPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *caseBlockPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeBool()
|
|
}
|
|
|
|
func (n *caseBlockPlanExpression) String() string {
|
|
return fmt.Sprintf("when %s then %s end", n.condition.String(), n.body.String())
|
|
}
|
|
|
|
func (n *caseBlockPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["condition"] = n.condition.Plan()
|
|
result["body"] = n.body.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *caseBlockPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.condition,
|
|
n.body,
|
|
}
|
|
}
|
|
|
|
func (n *caseBlockPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 2 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newCaseBlockPlanExpression(children[0], children[1]), nil
|
|
}
|
|
|
|
// subqueryPlanExpression is a select statement (when used in an expression)
|
|
type subqueryPlanExpression struct {
|
|
op types.PlanOperator
|
|
}
|
|
|
|
func newSubqueryPlanExpression(op types.PlanOperator) *subqueryPlanExpression {
|
|
return &subqueryPlanExpression{
|
|
op: op,
|
|
}
|
|
}
|
|
|
|
func (n *subqueryPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
ctx := context.Background()
|
|
|
|
// get an iterator
|
|
iter, err := n.op.Iterator(ctx, currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// get the first row
|
|
row, err := iter.Next(ctx)
|
|
if err != nil {
|
|
if err == types.ErrNoMoreRows {
|
|
// no rows, so return null
|
|
// TODO(pok) - check that this is the right behavior
|
|
return nil, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
result := row[0]
|
|
|
|
// make sure we don't have a next row - this is an error
|
|
_, err = iter.Next(ctx)
|
|
if err != nil && err == types.ErrNoMoreRows {
|
|
return result, nil
|
|
}
|
|
return nil, sql3.NewErrSingleRowExpected(0, 0)
|
|
}
|
|
|
|
func (n *subqueryPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeBool()
|
|
}
|
|
|
|
func (n *subqueryPlanExpression) String() string {
|
|
return n.op.String()
|
|
}
|
|
|
|
func (n *subqueryPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["subquery"] = n.op.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *subqueryPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *subqueryPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// betweenOpPlanExpression is a 'between/not between' op
|
|
type betweenOpPlanExpression struct {
|
|
lhs types.PlanExpression
|
|
op parser.Token
|
|
rhs types.PlanExpression
|
|
}
|
|
|
|
func newBetweenOpPlanExpression(lhs types.PlanExpression, op parser.Token, rhs types.PlanExpression) *betweenOpPlanExpression {
|
|
return &betweenOpPlanExpression{
|
|
lhs: lhs,
|
|
op: op,
|
|
rhs: rhs,
|
|
}
|
|
}
|
|
|
|
func (n *betweenOpPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
evalLhs, err := n.lhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
exprRange, ok := n.rhs.(*rangePlanExpression)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternal("range expression expected")
|
|
}
|
|
|
|
rangeLower, err := exprRange.lhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rangeUpper, err := exprRange.rhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if evalLhs == nil || rangeLower == nil || rangeUpper == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
switch rType := n.rhs.Type().(type) {
|
|
case *parser.DataTypeRange:
|
|
switch sType := rType.SubscriptType.(type) {
|
|
case *parser.DataTypeInt:
|
|
|
|
nl, nlok := evalLhs.(int64)
|
|
rl, rlok := rangeLower.(int64)
|
|
ru, ruok := rangeUpper.(int64)
|
|
|
|
if !(nlok && rlok && ruok) {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t', '%t'", nlok, rlok, ruok)
|
|
}
|
|
result := nl >= rl && nl <= ru
|
|
if n.op == parser.NOTBETWEEN {
|
|
result = !result
|
|
}
|
|
return result, nil
|
|
|
|
case *parser.DataTypeTimestamp:
|
|
|
|
nl, nlok := evalLhs.(time.Time)
|
|
rl, rlok := rangeLower.(time.Time)
|
|
ru, ruok := rangeUpper.(time.Time)
|
|
|
|
if !(nlok && rlok && ruok) {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t', '%t', '%t'", nlok, rlok, ruok)
|
|
}
|
|
result := (nl == rl || nl.After(rl)) && (nl == ru || nl.Before(ru))
|
|
if n.op == parser.NOTBETWEEN {
|
|
result = !result
|
|
}
|
|
return result, nil
|
|
|
|
case *parser.DataTypeDecimal:
|
|
|
|
nl, nlok := evalLhs.(pql.Decimal)
|
|
if !(nlok) {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", nlok)
|
|
}
|
|
|
|
crl, err := coerceValue(exprRange.lhs.Type(), sType, rangeLower, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rl, ok := crl.(pql.Decimal)
|
|
if !(ok) {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", crl)
|
|
}
|
|
|
|
cru, err := coerceValue(exprRange.rhs.Type(), sType, rangeUpper, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ru, ok := cru.(pql.Decimal)
|
|
if !(ok) {
|
|
return nil, sql3.NewErrInternalf("unexpected type conversion error '%t'", cru)
|
|
}
|
|
|
|
result := nl.GreaterThanOrEqualTo(rl) && nl.LessThanOrEqualTo(ru)
|
|
if n.op == parser.NOTBETWEEN {
|
|
result = !result
|
|
}
|
|
return result, nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected range type '%T'", sType)
|
|
}
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected range type '%T'", n.rhs.Type())
|
|
}
|
|
}
|
|
|
|
func (n *betweenOpPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeBool()
|
|
}
|
|
|
|
func (n *betweenOpPlanExpression) String() string {
|
|
if n.op == parser.BETWEEN {
|
|
return fmt.Sprintf("between %s and %s", n.lhs.String(), n.rhs.String())
|
|
}
|
|
return fmt.Sprintf("not between %s and %s", n.lhs.String(), n.rhs.String())
|
|
}
|
|
|
|
func (n *betweenOpPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["lhs"] = n.lhs.Plan()
|
|
result["rhs"] = n.rhs.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *betweenOpPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.lhs,
|
|
n.rhs,
|
|
}
|
|
}
|
|
|
|
func (n *betweenOpPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 2 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newBetweenOpPlanExpression(children[0], n.op, children[1]), nil
|
|
}
|
|
|
|
// inOpPlanExpression is an 'in/not in' op
|
|
type inOpPlanExpression struct {
|
|
lhs types.PlanExpression
|
|
op parser.Token
|
|
rhs types.PlanExpression
|
|
}
|
|
|
|
func newInOpPlanExpression(lhs types.PlanExpression, op parser.Token, rhs types.PlanExpression) *inOpPlanExpression {
|
|
return &inOpPlanExpression{
|
|
lhs: lhs,
|
|
op: op,
|
|
rhs: rhs,
|
|
}
|
|
}
|
|
|
|
func (n *inOpPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
evalLhs, err := n.lhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if lhs is nil, bail
|
|
if evalLhs == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
exprList, ok := n.rhs.(*exprListPlanExpression)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternal("expression list expected")
|
|
}
|
|
|
|
listMembers := []interface{}{}
|
|
|
|
// evaluate all the list members
|
|
for _, lm := range exprList.exprs {
|
|
lv, err := lm.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// if any of the list members eval to nil, bail
|
|
if lv == nil {
|
|
return nil, nil
|
|
}
|
|
listMembers = append(listMembers, lv)
|
|
}
|
|
|
|
result := false
|
|
|
|
switch n.lhs.Type().(type) {
|
|
|
|
case *parser.DataTypeInt, *parser.DataTypeID:
|
|
nl, nlok := evalLhs.(int64)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.(int64)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if nl == l {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeBool:
|
|
nl, nlok := evalLhs.(bool)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.(bool)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if nl == l {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeDecimal:
|
|
nl, nlok := evalLhs.(pql.Decimal)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.(pql.Decimal)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if nl.EqualTo(l) {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeIDSet:
|
|
nl, nlok := evalLhs.([]int64)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.([]int64)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if intSetContainsAll(nl, l) {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeString:
|
|
nl, nlok := evalLhs.(string)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.(string)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if nl == l {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeStringSet:
|
|
nl, nlok := evalLhs.([]string)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.([]string)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if stringSetContainsAll(nl, l) {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeTimestamp:
|
|
nl, nlok := evalLhs.(time.Time)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to convert lhs expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
|
|
for _, lm := range listMembers {
|
|
l, lok := lm.(time.Time)
|
|
if !lok {
|
|
return nil, sql3.NewErrInternalf("unable to convert list expression to type '%s'", n.lhs.Type().TypeDescription())
|
|
}
|
|
if nl == l {
|
|
result = true
|
|
break
|
|
}
|
|
}
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled type '%T'", n.lhs.Type())
|
|
}
|
|
|
|
if n.op == parser.NOTIN {
|
|
return !result, nil
|
|
} else {
|
|
return result, nil
|
|
}
|
|
}
|
|
|
|
func (n *inOpPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeBool()
|
|
}
|
|
|
|
func (n *inOpPlanExpression) String() string {
|
|
s := n.lhs.String()
|
|
if n.op == parser.NOTIN {
|
|
s += " not "
|
|
}
|
|
s += " in ("
|
|
s += n.rhs.String()
|
|
s += ")"
|
|
return s
|
|
}
|
|
|
|
func (n *inOpPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["lhs"] = n.lhs.Plan()
|
|
result["rhs"] = n.rhs.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *inOpPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.lhs,
|
|
n.rhs,
|
|
}
|
|
}
|
|
|
|
func (n *inOpPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 2 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newInOpPlanExpression(children[0], n.op, children[1]), nil
|
|
}
|
|
|
|
// callPlanExpression is a function call
|
|
type callPlanExpression struct {
|
|
name string
|
|
args []types.PlanExpression
|
|
dataType parser.ExprDataType
|
|
}
|
|
|
|
func newCallPlanExpression(name string, args []types.PlanExpression, dataType parser.ExprDataType) *callPlanExpression {
|
|
return &callPlanExpression{
|
|
name: name,
|
|
args: args,
|
|
dataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *callPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
switch strings.ToUpper(n.name) {
|
|
case "SETCONTAINS":
|
|
return n.EvaluateSetContains(currentRow)
|
|
case "SETCONTAINSANY":
|
|
return n.EvaluateSetContainsAny(currentRow)
|
|
case "SETCONTAINSALL":
|
|
return n.EvaluateSetContainsAll(currentRow)
|
|
case "DATETIMEPART":
|
|
return n.EvaluateDateTimePart(currentRow)
|
|
case "REVERSE":
|
|
return n.EvaluateReverse(currentRow)
|
|
case "UPPER":
|
|
return n.EvaluateUpper(currentRow)
|
|
case "STRINGSPLIT":
|
|
return n.EvaluateStringSplit(currentRow)
|
|
case "CHAR":
|
|
return n.EvaluateChar(currentRow)
|
|
case "ASCII":
|
|
return n.EvaluateAscii(currentRow)
|
|
case "SUBSTRING":
|
|
return n.EvaluateSubstring(currentRow)
|
|
case "LOWER":
|
|
return n.EvaluateLower(currentRow)
|
|
case "REPLACEALL":
|
|
return n.EvaluateReplaceAll(currentRow)
|
|
case "TRIM":
|
|
return n.EvaluateTrim(currentRow)
|
|
case "RTRIM":
|
|
return n.EvaluateRTrim(currentRow)
|
|
case "LTRIM":
|
|
return n.EvaluateLTrim(currentRow)
|
|
case "SUFFIX":
|
|
return n.EvaluateSuffix(currentRow)
|
|
case "PREFIX":
|
|
return n.EvaluatePrefix(currentRow)
|
|
case "SPACE":
|
|
return n.EvaluateSpace(currentRow)
|
|
case "LEN":
|
|
return n.EvaluateLen(currentRow)
|
|
case "REPLICATE":
|
|
return n.EvaluateReplicate(currentRow)
|
|
case "FORMAT":
|
|
return n.EvaluateFormat(currentRow)
|
|
case "CHARINDEX":
|
|
return n.EvaluateCharIndex(currentRow)
|
|
case "TOTIMESTAMP":
|
|
return n.EvaluateToTimestamp(currentRow)
|
|
case "STR":
|
|
return n.EvaluateStr(currentRow)
|
|
case "DATETIMENAME":
|
|
return n.EvaluateDateTimeName(currentRow)
|
|
case "DATE_TRUNC":
|
|
return n.EvaluateDateTrunc(currentRow)
|
|
// time quantum functions
|
|
case "RANGEQ":
|
|
return n.EvaluateRangeQ(currentRow)
|
|
case "DATETIMEFROMPARTS":
|
|
return n.EvaluateDateTimeFromParts(currentRow)
|
|
case "DATETIMEADD":
|
|
return n.EvaluateDatetimeAdd(currentRow)
|
|
case "DATETIMEDIFF":
|
|
return n.EvaluateDatetimeDiff(currentRow)
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled function name '%s'", n.name)
|
|
}
|
|
}
|
|
|
|
func (n *callPlanExpression) Type() parser.ExprDataType {
|
|
return n.dataType
|
|
}
|
|
|
|
func (n *callPlanExpression) String() string {
|
|
args := ""
|
|
for idx, arg := range n.args {
|
|
if idx > 0 {
|
|
args += ", "
|
|
}
|
|
args += arg.String()
|
|
}
|
|
return fmt.Sprintf("%s(%s)", n.name, args)
|
|
}
|
|
|
|
func (n *callPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["name"] = n.name
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
ps := make([]interface{}, 0)
|
|
for _, e := range n.args {
|
|
ps = append(ps, e.Plan())
|
|
}
|
|
result["args"] = ps
|
|
return result
|
|
}
|
|
|
|
func (n *callPlanExpression) Children() []types.PlanExpression {
|
|
return n.args
|
|
}
|
|
|
|
func (n *callPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != len(n.args) {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newCallPlanExpression(n.name, children, n.dataType), nil
|
|
}
|
|
|
|
// aliasPlanExpression is a alias ref
|
|
type aliasPlanExpression struct {
|
|
types.IdentifiableByName
|
|
aliasName string
|
|
expr types.PlanExpression
|
|
}
|
|
|
|
func newAliasPlanExpression(aliasName string, expr types.PlanExpression) *aliasPlanExpression {
|
|
return &aliasPlanExpression{
|
|
aliasName: aliasName,
|
|
expr: expr,
|
|
}
|
|
}
|
|
|
|
func (n *aliasPlanExpression) Name() string {
|
|
return n.aliasName
|
|
}
|
|
|
|
// evaluates expression based on current row and column
|
|
func (n *aliasPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return n.expr.Evaluate(currentRow)
|
|
}
|
|
|
|
// returns the type of the expression
|
|
func (n *aliasPlanExpression) Type() parser.ExprDataType {
|
|
return n.expr.Type()
|
|
}
|
|
|
|
func (n *aliasPlanExpression) String() string {
|
|
return fmt.Sprintf("%s as %s", n.expr.String(), n.aliasName)
|
|
}
|
|
|
|
func (n *aliasPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["aliasName"] = n.aliasName
|
|
result["expr"] = n.expr.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *aliasPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.expr,
|
|
}
|
|
}
|
|
|
|
func (n *aliasPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 1 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newAliasPlanExpression(n.aliasName, children[0]), nil
|
|
}
|
|
|
|
// qualifiedRefPlanExpression is a qualified ref
|
|
type qualifiedRefPlanExpression struct {
|
|
types.IdentifiableByName
|
|
tableName string
|
|
columnName string
|
|
columnIndex int
|
|
dataType parser.ExprDataType
|
|
}
|
|
|
|
func newQualifiedRefPlanExpression(tableName string, columnName string, columnIndex int, dataType parser.ExprDataType) *qualifiedRefPlanExpression {
|
|
return &qualifiedRefPlanExpression{
|
|
tableName: tableName,
|
|
columnName: columnName,
|
|
columnIndex: columnIndex,
|
|
dataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
if n.columnIndex < 0 || n.columnIndex >= len(currentRow) {
|
|
return nil, sql3.NewErrInternalf("unable to to find column '%d' in currentColumns", n.columnIndex)
|
|
}
|
|
|
|
if currentRow[n.columnIndex] == nil {
|
|
return currentRow[n.columnIndex], nil
|
|
}
|
|
|
|
switch n.dataType.(type) {
|
|
case *parser.DataTypeIDSet, *parser.DataTypeIDSetQuantum:
|
|
row, ok := currentRow[n.columnIndex].([]uint64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected type for current row '%T'", currentRow[n.columnIndex])
|
|
}
|
|
result := make([]int64, len(row))
|
|
for i, v := range row {
|
|
result[i] = int64(v)
|
|
}
|
|
return result, nil
|
|
|
|
case *parser.DataTypeID:
|
|
// this could be an int64 or a uint64 internally
|
|
iv, iok := currentRow[n.columnIndex].(int64)
|
|
if iok {
|
|
return iv, nil
|
|
}
|
|
v, ok := currentRow[n.columnIndex].(uint64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unexpected type for current row '%T'", currentRow[n.columnIndex])
|
|
}
|
|
return int64(v), nil
|
|
|
|
default:
|
|
return currentRow[n.columnIndex], nil
|
|
}
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) Name() string {
|
|
return n.columnName
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) Type() parser.ExprDataType {
|
|
return n.dataType
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) String() string {
|
|
if len(n.tableName) > 0 {
|
|
return fmt.Sprintf("%s.%s", n.tableName, n.columnName)
|
|
}
|
|
return n.columnName
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["tableName"] = n.tableName
|
|
result["columnName"] = n.columnName
|
|
result["columnIndex"] = n.columnIndex
|
|
result["dataType"] = n.dataType.TypeDescription()
|
|
return result
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *qualifiedRefPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// variableRefPlanExpression is a variable ref
|
|
type variableRefPlanExpression struct {
|
|
types.IdentifiableByName
|
|
name string
|
|
variableIndex int
|
|
dataType parser.ExprDataType
|
|
}
|
|
|
|
func newVariableRefPlanExpression(name string, variableIndex int, dataType parser.ExprDataType) *variableRefPlanExpression {
|
|
return &variableRefPlanExpression{
|
|
name: name,
|
|
variableIndex: variableIndex,
|
|
dataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
if n.variableIndex < 0 || n.variableIndex >= len(currentRow) {
|
|
return nil, sql3.NewErrInternalf("unable to to find variable '%d'", n.variableIndex)
|
|
}
|
|
|
|
if currentRow[n.variableIndex] == nil {
|
|
return currentRow[n.variableIndex], nil
|
|
}
|
|
|
|
switch n.dataType.(type) {
|
|
default:
|
|
return currentRow[n.variableIndex], nil
|
|
}
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) Name() string {
|
|
return n.name
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) Type() parser.ExprDataType {
|
|
return n.dataType
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) String() string {
|
|
return fmt.Sprintf("@%s", n.name)
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["name"] = n.name
|
|
result["dataType"] = n.dataType.TypeDescription()
|
|
return result
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *variableRefPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// nullLiteralPlanExpression is a null literal
|
|
type nullLiteralPlanExpression struct{}
|
|
|
|
func newNullLiteralPlanExpression() *nullLiteralPlanExpression {
|
|
return &nullLiteralPlanExpression{}
|
|
}
|
|
|
|
func (n *nullLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *nullLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeVoid()
|
|
}
|
|
|
|
func (n *nullLiteralPlanExpression) String() string {
|
|
return "null"
|
|
}
|
|
|
|
func (n *nullLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
return result
|
|
}
|
|
|
|
func (n *nullLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *nullLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// intLiteralPlanExpression is an integer literal
|
|
type intLiteralPlanExpression struct {
|
|
value int64
|
|
}
|
|
|
|
func newIntLiteralPlanExpression(value int64) *intLiteralPlanExpression {
|
|
return &intLiteralPlanExpression{
|
|
value: value,
|
|
}
|
|
}
|
|
|
|
func (n *intLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return n.value, nil
|
|
}
|
|
|
|
func (n *intLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeInt()
|
|
}
|
|
|
|
func (n *intLiteralPlanExpression) String() string {
|
|
return fmt.Sprintf("%d", n.value)
|
|
}
|
|
|
|
func (n *intLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["value"] = n.value
|
|
return result
|
|
}
|
|
|
|
func (n *intLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *intLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// floatLiteralPlanExpression is a float literal
|
|
type floatLiteralPlanExpression struct {
|
|
value string
|
|
}
|
|
|
|
func newFloatLiteralPlanExpression(value string) *floatLiteralPlanExpression {
|
|
return &floatLiteralPlanExpression{
|
|
value: value,
|
|
}
|
|
}
|
|
|
|
func (n *floatLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return pql.ParseDecimal(n.value)
|
|
}
|
|
|
|
func (n *floatLiteralPlanExpression) Type() parser.ExprDataType {
|
|
scale := parser.NumDecimalPlaces(n.value)
|
|
return parser.NewDataTypeDecimal(int64(scale))
|
|
}
|
|
|
|
func (n *floatLiteralPlanExpression) String() string {
|
|
return n.value
|
|
}
|
|
|
|
func (n *floatLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["value"] = n.value
|
|
return result
|
|
}
|
|
|
|
func (n *floatLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *floatLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// boolLiteralPlanExpression is a bool literal
|
|
type boolLiteralPlanExpression struct {
|
|
value bool
|
|
}
|
|
|
|
func newBoolLiteralPlanExpression(value bool) *boolLiteralPlanExpression {
|
|
return &boolLiteralPlanExpression{
|
|
value: value,
|
|
}
|
|
}
|
|
|
|
func (n *boolLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return n.value, nil
|
|
}
|
|
|
|
func (n *boolLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeBool()
|
|
}
|
|
|
|
func (n *boolLiteralPlanExpression) String() string {
|
|
return fmt.Sprintf("%v", n.value)
|
|
}
|
|
|
|
func (n *boolLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["value"] = n.value
|
|
return result
|
|
}
|
|
|
|
func (n *boolLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *boolLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// represents system variables such as CURRENT_DATE and CURRENT_DATETIME
|
|
type sysVariablePlanExpression struct {
|
|
name string // name of the system variable
|
|
token parser.Token // token mapped to the system variable name
|
|
}
|
|
|
|
func newSysVariablePlanExpression(name string, token parser.Token) *sysVariablePlanExpression {
|
|
return &sysVariablePlanExpression{
|
|
name: name,
|
|
token: token,
|
|
}
|
|
}
|
|
|
|
func (n *sysVariablePlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
switch n.token {
|
|
case parser.CURRENT_DATE:
|
|
dt := time.Now().UTC()
|
|
return time.Date(dt.Year(), dt.Month(), dt.Day(), 0, 0, 0, 0, dt.Location()), nil
|
|
case parser.CURRENT_TIMESTAMP:
|
|
return time.Now().UTC(), nil
|
|
}
|
|
return nil, sql3.NewErrInternal(fmt.Sprintf("Mising plan expression implementation for system variable '%s'", n.name))
|
|
}
|
|
|
|
func (n *sysVariablePlanExpression) Type() parser.ExprDataType {
|
|
switch n.token {
|
|
case parser.CURRENT_DATE, parser.CURRENT_TIMESTAMP:
|
|
return parser.NewDataTypeTimestamp()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (n *sysVariablePlanExpression) String() string {
|
|
return n.name
|
|
}
|
|
|
|
func (n *sysVariablePlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["value"], _ = n.Evaluate(nil)
|
|
return result
|
|
}
|
|
|
|
func (n *sysVariablePlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *sysVariablePlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// dateLiteralPlanExpression is a date literal
|
|
type dateLiteralPlanExpression struct {
|
|
value time.Time
|
|
}
|
|
|
|
func newDateLiteralPlanExpression(value time.Time) *dateLiteralPlanExpression {
|
|
return &dateLiteralPlanExpression{
|
|
value: value,
|
|
}
|
|
}
|
|
|
|
func (n *dateLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return n.value, nil
|
|
}
|
|
|
|
func (n *dateLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeTimestamp()
|
|
}
|
|
|
|
func (n *dateLiteralPlanExpression) String() string {
|
|
return n.value.Format(time.RFC3339Nano)
|
|
}
|
|
|
|
func (n *dateLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["value"] = n.value
|
|
return result
|
|
}
|
|
|
|
func (n *dateLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *dateLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
// stringLiteralPlanExpression is a string literal
|
|
type stringLiteralPlanExpression struct {
|
|
value string
|
|
}
|
|
|
|
func newStringLiteralPlanExpression(value string) *stringLiteralPlanExpression {
|
|
return &stringLiteralPlanExpression{
|
|
value: value,
|
|
}
|
|
}
|
|
|
|
func (n *stringLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return n.value, nil
|
|
}
|
|
|
|
func (n *stringLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeString()
|
|
}
|
|
|
|
func (n *stringLiteralPlanExpression) String() string {
|
|
return fmt.Sprintf("'%s'", n.value)
|
|
}
|
|
|
|
func (n *stringLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["value"] = n.value
|
|
return result
|
|
}
|
|
|
|
func (n *stringLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{}
|
|
}
|
|
|
|
func (n *stringLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
return n, nil
|
|
}
|
|
|
|
func (expr *stringLiteralPlanExpression) ConvertToTimestamp() *time.Time {
|
|
// try to coerce to a date
|
|
if tm, err := time.ParseInLocation(time.RFC3339Nano, expr.value, time.UTC); err == nil {
|
|
return &tm
|
|
} else if tm, err := time.ParseInLocation(time.RFC3339, expr.value, time.UTC); err == nil {
|
|
return &tm
|
|
} else if tm, err := time.ParseInLocation("2006-01-02", expr.value, time.UTC); err == nil {
|
|
return &tm
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// castPlanExpressionis a cast op
|
|
type castPlanExpression struct {
|
|
lhs types.PlanExpression
|
|
targetType parser.ExprDataType
|
|
}
|
|
|
|
func newCastPlanExpression(lhs types.PlanExpression, targetType parser.ExprDataType) *castPlanExpression {
|
|
return &castPlanExpression{
|
|
lhs: lhs,
|
|
targetType: targetType,
|
|
}
|
|
}
|
|
|
|
func (n *castPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
evalLhs, err := n.lhs.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch sourceType := n.lhs.Type().(type) {
|
|
case *parser.DataTypeInt:
|
|
nl, nlok := evalLhs.(int64)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch tt := n.targetType.(type) {
|
|
case *parser.DataTypeInt, *parser.DataTypeID:
|
|
return nl, nil
|
|
case *parser.DataTypeBool:
|
|
return nl > 0, nil
|
|
case *parser.DataTypeDecimal:
|
|
return pql.NewDecimal(nl*int64(math.Pow(10, float64(tt.Scale))), tt.Scale), nil
|
|
case *parser.DataTypeString:
|
|
return fmt.Sprintf("%d", nl), nil
|
|
case *parser.DataTypeTimestamp:
|
|
tm := time.Unix(nl, 0).UTC()
|
|
return tm, nil
|
|
}
|
|
|
|
case *parser.DataTypeID:
|
|
nl, nlok := evalLhs.(int64)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch tt := n.targetType.(type) {
|
|
case *parser.DataTypeInt, *parser.DataTypeID:
|
|
return nl, nil
|
|
case *parser.DataTypeBool:
|
|
return nl > 0, nil
|
|
case *parser.DataTypeDecimal:
|
|
return pql.NewDecimal(nl*int64(math.Pow(10, float64(tt.Scale))), tt.Scale), nil
|
|
case *parser.DataTypeString:
|
|
return fmt.Sprintf("%d", nl), nil
|
|
case *parser.DataTypeTimestamp:
|
|
tm := time.Unix(nl, 0).UTC()
|
|
return tm, nil
|
|
}
|
|
|
|
case *parser.DataTypeBool:
|
|
nl, nlok := evalLhs.(bool)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch n.targetType.(type) {
|
|
case *parser.DataTypeInt, *parser.DataTypeID:
|
|
if nl {
|
|
return int64(1), nil
|
|
}
|
|
return int64(0), nil
|
|
case *parser.DataTypeBool:
|
|
return nl, nil
|
|
case *parser.DataTypeString:
|
|
return fmt.Sprintf("%v", nl), nil
|
|
}
|
|
|
|
case *parser.DataTypeDecimal:
|
|
nl, nlok := evalLhs.(pql.Decimal)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch n.targetType.(type) {
|
|
case *parser.DataTypeDecimal:
|
|
return nl, nil
|
|
case *parser.DataTypeString:
|
|
return fmt.Sprintf("%v", nl), nil
|
|
}
|
|
|
|
case *parser.DataTypeIDSet:
|
|
nl, nlok := evalLhs.([]int64)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch n.targetType.(type) {
|
|
case *parser.DataTypeIDSet:
|
|
return nl, nil
|
|
case *parser.DataTypeString:
|
|
// TODO(pok) come up with a better string representation of idset
|
|
return fmt.Sprintf("%v", nl), nil
|
|
}
|
|
|
|
case *parser.DataTypeString:
|
|
nl, nlok := evalLhs.(string)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch tt := n.targetType.(type) {
|
|
case *parser.DataTypeInt, *parser.DataTypeID:
|
|
i, err := strconv.Atoi(nl)
|
|
if err != nil {
|
|
// TODO(pok) need to push location into here
|
|
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
|
}
|
|
return int64(i), nil
|
|
|
|
case *parser.DataTypeBool:
|
|
i, err := strconv.ParseBool(nl)
|
|
if err != nil {
|
|
// TODO(pok) need to push location into here
|
|
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
|
}
|
|
return i, nil
|
|
|
|
case *parser.DataTypeDecimal:
|
|
castValue, err := pql.ParseDecimal(nl)
|
|
if err != nil {
|
|
// TODO(pok) need to push location into here
|
|
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
|
}
|
|
if tt.Scale < castValue.Scale {
|
|
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
|
}
|
|
|
|
return castValue, nil
|
|
|
|
case *parser.DataTypeString:
|
|
return nl, nil
|
|
|
|
case *parser.DataTypeTimestamp:
|
|
if tm, err := time.ParseInLocation(time.RFC3339Nano, nl, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else if tm, err := time.ParseInLocation(time.RFC3339, nl, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else if tm, err := time.ParseInLocation("2006-01-02", nl, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else {
|
|
return nil, sql3.NewErrInvalidCast(0, 0, nl, n.targetType.TypeDescription())
|
|
}
|
|
}
|
|
|
|
case *parser.DataTypeStringSet:
|
|
nl, nlok := evalLhs.([]string)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch n.targetType.(type) {
|
|
case *parser.DataTypeStringSet:
|
|
return nl, nil
|
|
case *parser.DataTypeString:
|
|
sort.Strings(nl)
|
|
|
|
var ret strings.Builder
|
|
|
|
// open bracket
|
|
ret.WriteString("[")
|
|
|
|
// elements
|
|
var afterFirst bool
|
|
for i := range nl {
|
|
if afterFirst {
|
|
ret.WriteString(",")
|
|
}
|
|
ret.WriteString(`"` + strings.ReplaceAll(nl[i], `"`, `\"`) + `"`)
|
|
afterFirst = true
|
|
}
|
|
|
|
// close braket
|
|
ret.WriteString("]")
|
|
|
|
return ret.String(), nil
|
|
}
|
|
|
|
case *parser.DataTypeTimestamp:
|
|
nl, nlok := evalLhs.(time.Time)
|
|
if !nlok {
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
switch n.targetType.(type) {
|
|
case *parser.DataTypeTimestamp:
|
|
return nl, nil
|
|
case *parser.DataTypeInt:
|
|
return nl.Unix(), nil
|
|
case *parser.DataTypeString:
|
|
return nl.Format(time.RFC3339), nil
|
|
}
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unhandled cast type '%T'", sourceType)
|
|
}
|
|
return nil, sql3.NewErrInternalf("unable to cast expression of type '%T' to type '%T'", n.lhs.Type(), n.targetType)
|
|
}
|
|
|
|
func (n *castPlanExpression) Type() parser.ExprDataType {
|
|
return n.targetType
|
|
}
|
|
|
|
func (n *castPlanExpression) String() string {
|
|
return fmt.Sprintf("cast(%s as %s)", n.lhs.String(), n.targetType.TypeDescription())
|
|
}
|
|
|
|
func (n *castPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
result["dataType"] = n.Type().TypeDescription()
|
|
result["lhs"] = n.lhs.Plan()
|
|
return result
|
|
}
|
|
|
|
func (n *castPlanExpression) Children() []types.PlanExpression {
|
|
return []types.PlanExpression{
|
|
n.lhs,
|
|
}
|
|
}
|
|
|
|
func (n *castPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != 1 {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newCastPlanExpression(children[0], n.targetType), nil
|
|
}
|
|
|
|
// exprListPlanExpression is an expression list
|
|
type exprListPlanExpression struct {
|
|
exprs []types.PlanExpression
|
|
}
|
|
|
|
func newExprListExpression(exprs []types.PlanExpression) *exprListPlanExpression {
|
|
return &exprListPlanExpression{
|
|
exprs: exprs,
|
|
}
|
|
}
|
|
|
|
func (n *exprListPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *exprListPlanExpression) Type() parser.ExprDataType {
|
|
return parser.NewDataTypeVoid()
|
|
}
|
|
|
|
func (n *exprListPlanExpression) String() string {
|
|
var s string
|
|
for idx, expr := range n.exprs {
|
|
if idx > 0 {
|
|
s += ", "
|
|
}
|
|
s += expr.String()
|
|
}
|
|
return fmt.Sprintf("(%s)", s)
|
|
}
|
|
|
|
func (n *exprListPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
ps := make([]interface{}, 0)
|
|
for _, e := range n.exprs {
|
|
ps = append(ps, e.Plan())
|
|
}
|
|
result["exprs"] = ps
|
|
return result
|
|
}
|
|
|
|
func (n *exprListPlanExpression) Children() []types.PlanExpression {
|
|
return n.exprs
|
|
}
|
|
|
|
func (n *exprListPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != len(n.exprs) {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newExprListExpression(children), nil
|
|
}
|
|
|
|
// exprSetLiteralPlanExpression is a set literal
|
|
type exprSetLiteralPlanExpression struct {
|
|
members []types.PlanExpression
|
|
dataType parser.ExprDataType
|
|
}
|
|
|
|
func newExprSetLiteralPlanExpression(members []types.PlanExpression, dataType parser.ExprDataType) *exprSetLiteralPlanExpression {
|
|
return &exprSetLiteralPlanExpression{
|
|
members: members,
|
|
dataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *exprSetLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
switch typ := n.dataType.(type) {
|
|
case *parser.DataTypeIDSet:
|
|
result := []int64{}
|
|
for _, e := range n.members {
|
|
er, err := e.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
coercedEr, err := coerceValue(e.Type(), &parser.DataTypeID{}, er, parser.Pos{Line: 0, Column: 0})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
eri, ok := coercedEr.(int64)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unable to convert element result")
|
|
}
|
|
result = append(result, eri)
|
|
}
|
|
return result, nil
|
|
|
|
case *parser.DataTypeStringSet:
|
|
result := []string{}
|
|
for _, e := range n.members {
|
|
er, err := e.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ers, ok := er.(string)
|
|
if !ok {
|
|
return nil, sql3.NewErrInternalf("unable to convert element result")
|
|
}
|
|
result = append(result, ers)
|
|
}
|
|
return result, nil
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected set literal type '%T'", typ)
|
|
}
|
|
}
|
|
|
|
func (n *exprSetLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return n.dataType
|
|
}
|
|
|
|
func (n *exprSetLiteralPlanExpression) String() string {
|
|
var members string
|
|
for idx, m := range n.members {
|
|
if idx > 0 {
|
|
members += ", "
|
|
}
|
|
members += m.String()
|
|
}
|
|
return fmt.Sprintf("[%s]", members)
|
|
}
|
|
|
|
func (n *exprSetLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
ps := make([]interface{}, 0)
|
|
for _, e := range n.members {
|
|
ps = append(ps, e.Plan())
|
|
}
|
|
result["members"] = ps
|
|
return result
|
|
}
|
|
|
|
func (n *exprSetLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return n.members
|
|
}
|
|
|
|
func (n *exprSetLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != len(n.members) {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newExprSetLiteralPlanExpression(children, n.dataType), nil
|
|
}
|
|
|
|
// exprTupleLiteralPlanExpression is a tuple literal
|
|
type exprTupleLiteralPlanExpression struct {
|
|
members []types.PlanExpression
|
|
dataType parser.ExprDataType
|
|
}
|
|
|
|
func newExprTupleLiteralPlanExpression(members []types.PlanExpression, dataType parser.ExprDataType) *exprTupleLiteralPlanExpression {
|
|
return &exprTupleLiteralPlanExpression{
|
|
members: members,
|
|
dataType: dataType,
|
|
}
|
|
}
|
|
|
|
func (n *exprTupleLiteralPlanExpression) Evaluate(currentRow []interface{}) (interface{}, error) {
|
|
result := make([]interface{}, len(n.members))
|
|
for i, m := range n.members {
|
|
v, err := m.Evaluate(currentRow)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result[i] = v
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (n *exprTupleLiteralPlanExpression) Type() parser.ExprDataType {
|
|
return n.dataType
|
|
}
|
|
|
|
func (n *exprTupleLiteralPlanExpression) String() string {
|
|
members := ""
|
|
for idx, m := range n.members {
|
|
if idx > 0 {
|
|
members += ", "
|
|
}
|
|
members += m.String()
|
|
}
|
|
return fmt.Sprintf("{%s}", members)
|
|
}
|
|
|
|
func (n *exprTupleLiteralPlanExpression) Plan() map[string]interface{} {
|
|
result := make(map[string]interface{})
|
|
result["_expr"] = fmt.Sprintf("%T", n)
|
|
result["description"] = n.String()
|
|
ps := make([]interface{}, 0)
|
|
for _, e := range n.members {
|
|
ps = append(ps, e.Plan())
|
|
}
|
|
result["members"] = ps
|
|
return result
|
|
}
|
|
|
|
func (n *exprTupleLiteralPlanExpression) Children() []types.PlanExpression {
|
|
return n.members
|
|
}
|
|
|
|
func (n *exprTupleLiteralPlanExpression) WithChildren(children ...types.PlanExpression) (types.PlanExpression, error) {
|
|
if len(children) != len(n.members) {
|
|
return nil, sql3.NewErrInternalf("unexpected number of children '%d'", len(children))
|
|
}
|
|
return newExprTupleLiteralPlanExpression(children, n.dataType), nil
|
|
}
|
|
|
|
// compileExpr returns a types.PlanExpression tree for a given parser.Expr
|
|
func (p *ExecutionPlanner) compileExpr(expr parser.Expr) (_ types.PlanExpression, err error) {
|
|
if expr == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
switch expr := expr.(type) {
|
|
case *parser.BinaryExpr:
|
|
return p.compileBinaryExpr(expr)
|
|
|
|
case *parser.BoolLit:
|
|
return newBoolLiteralPlanExpression(expr.Value), nil
|
|
|
|
case *parser.Call:
|
|
return p.compileCallExpr(expr)
|
|
|
|
case *parser.CastExpr:
|
|
castExpr, err := p.compileExpr(expr.X)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dataType, err := dataTypeFromParserType(expr.Type)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newCastPlanExpression(castExpr, dataType), nil
|
|
|
|
case *parser.Exists:
|
|
return nil, sql3.NewErrInternal("exists expressions are not supported")
|
|
|
|
case *parser.ExprList:
|
|
exprList := []types.PlanExpression{}
|
|
for _, e := range expr.Exprs {
|
|
listExpr, err := p.compileExpr(e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
exprList = append(exprList, listExpr)
|
|
}
|
|
return newExprListExpression(exprList), nil
|
|
|
|
case *parser.SetLiteralExpr:
|
|
exprList := []types.PlanExpression{}
|
|
for _, e := range expr.Members {
|
|
listExpr, err := p.compileExpr(e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
exprList = append(exprList, listExpr)
|
|
}
|
|
return newExprSetLiteralPlanExpression(exprList, expr.DataType()), nil
|
|
|
|
case *parser.TupleLiteralExpr:
|
|
exprList := []types.PlanExpression{}
|
|
for _, e := range expr.Members {
|
|
listExpr, err := p.compileExpr(e)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
exprList = append(exprList, listExpr)
|
|
}
|
|
return newExprTupleLiteralPlanExpression(exprList, expr.DataType()), nil
|
|
|
|
case *parser.Ident:
|
|
return nil, sql3.NewErrInternal("identifiers are not supported")
|
|
|
|
case *parser.NullLit:
|
|
return newNullLiteralPlanExpression(), nil
|
|
|
|
case *parser.IntegerLit:
|
|
|
|
val, err := strconv.ParseInt(expr.Value, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newIntLiteralPlanExpression(val), nil
|
|
|
|
case *parser.FloatLit:
|
|
return newFloatLiteralPlanExpression(expr.Value), nil
|
|
|
|
case *parser.DateLit:
|
|
return newDateLiteralPlanExpression(expr.Value), nil
|
|
|
|
case *parser.SysVariable:
|
|
return newSysVariablePlanExpression(expr.Name(), expr.Token), nil
|
|
|
|
case *parser.ParenExpr:
|
|
return p.compileExpr(expr.X)
|
|
|
|
case *parser.Variable:
|
|
ref := newVariableRefPlanExpression(expr.Name, expr.VariableIndex, expr.DataType())
|
|
return ref, nil
|
|
|
|
case *parser.QualifiedRef:
|
|
ref := newQualifiedRefPlanExpression(strings.ToLower(parser.IdentName(expr.Table)), strings.ToLower(parser.IdentName(expr.Column)), expr.ColumnIndex, expr.DataType())
|
|
return ref, nil
|
|
|
|
case *parser.Range:
|
|
lhs, err := p.compileExpr(expr.X)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rhs, err := p.compileExpr(expr.Y)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newRangeOpPlanExpression(lhs, rhs, expr.ResultDataType), nil
|
|
|
|
case *parser.StringLit:
|
|
return newStringLiteralPlanExpression(expr.Value), nil
|
|
|
|
case *parser.UnaryExpr:
|
|
return p.compileUnaryExpr(expr)
|
|
|
|
case *parser.CaseExpr:
|
|
operand, err := p.compileExpr(expr.Operand)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blocks := []types.PlanExpression{}
|
|
for _, b := range expr.Blocks {
|
|
block, err := p.compileExpr(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
blocks = append(blocks, block)
|
|
}
|
|
|
|
elseExpr, err := p.compileExpr(expr.ElseExpr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newCasePlanExpression(operand, blocks, elseExpr, expr.DataType()), nil
|
|
|
|
case *parser.CaseBlock:
|
|
|
|
condition, err := p.compileExpr(expr.Condition)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
body, err := p.compileExpr(expr.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newCaseBlockPlanExpression(condition, body), nil
|
|
|
|
case *parser.SelectStatement:
|
|
selOp, err := p.compileSelectStatement(expr, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newSubqueryPlanExpression(selOp), nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected SQL expression type: %T", expr)
|
|
}
|
|
}
|
|
|
|
func (p *ExecutionPlanner) compileUnaryExpr(expr *parser.UnaryExpr) (_ types.PlanExpression, err error) {
|
|
switch op := expr.Op; op {
|
|
|
|
// bitwise operators
|
|
case parser.BITNOT:
|
|
x, err := p.compileExpr(expr.X)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newUnaryOpPlanExpression(expr.Op, x, expr.ResultDataType), nil
|
|
|
|
// arithmetic operators
|
|
case parser.PLUS, parser.MINUS:
|
|
x, err := p.compileExpr(expr.X)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return newUnaryOpPlanExpression(expr.Op, x, expr.ResultDataType), nil
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected unary expression operator: %s", expr.Op)
|
|
}
|
|
}
|
|
|
|
func (p *ExecutionPlanner) compileBinaryExpr(expr *parser.BinaryExpr) (_ types.PlanExpression, err error) {
|
|
x, err := p.compileExpr(expr.X)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
y, err := p.compileExpr(expr.Y)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch op := expr.Op; op {
|
|
|
|
// logical operators
|
|
case parser.AND, parser.OR:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
// equality operators
|
|
case parser.EQ, parser.NE:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
// comparison operators
|
|
case parser.LT, parser.LE, parser.GT, parser.GE:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
// arithmetic operators
|
|
case parser.PLUS, parser.MINUS, parser.STAR, parser.SLASH, parser.REM:
|
|
|
|
// TODO(pok) move constant folding to optimizer
|
|
opx, okx := x.(*intLiteralPlanExpression)
|
|
opy, oky := y.(*intLiteralPlanExpression)
|
|
if okx && oky {
|
|
// both literals so we can fold
|
|
numx := opx.value
|
|
numy := opy.value
|
|
|
|
switch op {
|
|
case parser.PLUS:
|
|
value := numx + numy
|
|
return newIntLiteralPlanExpression(value), nil
|
|
|
|
case parser.MINUS:
|
|
value := numx - numy
|
|
return newIntLiteralPlanExpression(value), nil
|
|
|
|
case parser.STAR:
|
|
value := numx * numy
|
|
return newIntLiteralPlanExpression(value), nil
|
|
|
|
case parser.SLASH:
|
|
if numy == 0 {
|
|
return nil, sql3.NewErrDivideByZero(expr.OpPos.Line, expr.OpPos.Column)
|
|
}
|
|
value := numx / numy
|
|
return newIntLiteralPlanExpression(value), nil
|
|
|
|
case parser.REM:
|
|
if numy == 0 {
|
|
return nil, sql3.NewErrDivideByZero(expr.OpPos.Line, expr.OpPos.Column)
|
|
}
|
|
value := numx % numy
|
|
return newIntLiteralPlanExpression(value), nil
|
|
|
|
default:
|
|
// run home to momma
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
}
|
|
} else {
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
}
|
|
|
|
// bitwise operators
|
|
case parser.BITAND, parser.BITOR, parser.LSHIFT, parser.RSHIFT:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
// null test
|
|
case parser.IS, parser.ISNOT:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
case parser.IN, parser.NOTIN:
|
|
return newInOpPlanExpression(x, expr.Op, y), nil
|
|
|
|
case parser.BETWEEN, parser.NOTBETWEEN:
|
|
return newBetweenOpPlanExpression(x, expr.Op, y), nil
|
|
|
|
case parser.CONCAT:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
case parser.LIKE, parser.NOTLIKE:
|
|
return newBinOpPlanExpression(x, expr.Op, y, expr.ResultDataType), nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected binary expression operator: %s", expr.Op)
|
|
}
|
|
}
|
|
|
|
func (p *ExecutionPlanner) compileCallExpr(expr *parser.Call) (_ types.PlanExpression, err error) {
|
|
args := []types.PlanExpression{}
|
|
for _, a := range expr.Args {
|
|
arg, err := p.compileExpr(a)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
args = append(args, arg)
|
|
}
|
|
|
|
callName := strings.ToUpper(parser.IdentName(expr.Name))
|
|
switch callName {
|
|
case "COUNT":
|
|
var agg types.PlanExpression
|
|
if expr.Distinct.IsValid() {
|
|
agg = newCountDistinctPlanExpression(args[0], expr.ResultDataType)
|
|
} else {
|
|
if expr.Star.IsValid() {
|
|
agg = newCountStarPlanExpression(expr.ResultDataType)
|
|
} else {
|
|
agg = newCountPlanExpression(args[0], expr.ResultDataType)
|
|
}
|
|
}
|
|
return agg, nil
|
|
|
|
case "SUM":
|
|
agg := newSumPlanExpression(args[0], expr.ResultDataType)
|
|
return agg, nil
|
|
|
|
case "AVG":
|
|
agg := newAvgPlanExpression(args[0], expr.ResultDataType)
|
|
return agg, nil
|
|
|
|
case "PERCENTILE":
|
|
agg := newPercentilePlanExpression(args[0], args[1], expr.ResultDataType)
|
|
return agg, nil
|
|
|
|
case "MIN":
|
|
agg := newMinPlanExpression(args[0], expr.ResultDataType)
|
|
return agg, nil
|
|
|
|
case "MAX":
|
|
agg := newMaxPlanExpression(args[0], expr.ResultDataType)
|
|
return agg, nil
|
|
|
|
default:
|
|
return newCallPlanExpression(parser.IdentName(expr.Name), args, expr.ResultDataType), nil
|
|
}
|
|
}
|
|
|
|
func (p *ExecutionPlanner) compileOrderingTermExpr(expr parser.Expr, projections []types.PlanExpression, source parser.Source) (types.PlanExpression, error) {
|
|
if expr == nil {
|
|
return nil, nil
|
|
}
|
|
switch thisExpr := expr.(type) {
|
|
case *parser.Ident:
|
|
for _, proj := range projections {
|
|
switch p := proj.(type) {
|
|
case *qualifiedRefPlanExpression:
|
|
if strings.EqualFold(thisExpr.Name, p.columnName) {
|
|
if !typeCanBeSortedOn(p.Type()) {
|
|
return nil, sql3.NewErrExpectedSortableExpression(0, 0, p.Type().TypeDescription())
|
|
}
|
|
return p, nil
|
|
}
|
|
case *aliasPlanExpression:
|
|
if strings.EqualFold(thisExpr.Name, p.aliasName) {
|
|
if !typeCanBeSortedOn(p.expr.Type()) {
|
|
return nil, sql3.NewErrExpectedSortableExpression(0, 0, p.expr.Type().TypeDescription())
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// we didn't find in projection list so go look in the source columns
|
|
for _, col := range source.PossibleOutputColumns() {
|
|
if strings.EqualFold(thisExpr.Name, col.ColumnName) {
|
|
orderExpr := newQualifiedRefPlanExpression(col.TableName, col.ColumnName, col.ColumnIndex, col.Datatype)
|
|
if !typeCanBeSortedOn(orderExpr.Type()) {
|
|
return nil, sql3.NewErrExpectedSortableExpression(0, 0, orderExpr.Type().TypeDescription())
|
|
}
|
|
return orderExpr, nil
|
|
}
|
|
}
|
|
|
|
return nil, sql3.NewErrColumnNotFound(thisExpr.NamePos.Line, thisExpr.NamePos.Column, thisExpr.Name)
|
|
|
|
case *parser.IntegerLit:
|
|
val, err := strconv.ParseInt(thisExpr.Value, 10, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// subtract one because ordering terms are 1 based, not 0 based
|
|
index := int(val - 1)
|
|
// get the expr from the projection
|
|
orderExpr := projections[index]
|
|
if !typeCanBeSortedOn(orderExpr.Type()) {
|
|
return nil, sql3.NewErrExpectedSortableExpression(0, 0, orderExpr.Type().TypeDescription())
|
|
}
|
|
return orderExpr, nil
|
|
|
|
default:
|
|
return nil, sql3.NewErrInternalf("unexpected ordering expression type: %T", expr)
|
|
}
|
|
}
|
|
|
|
// wildCardToRegexp converts a wildcard pattern to a regular expression pattern.
|
|
// used by the LIKE/NOT LIKE operator
|
|
func wildCardToRegexp(pattern string) string {
|
|
var result strings.Builder
|
|
result.WriteString("(?i)")
|
|
|
|
rpattern := strings.Replace(pattern, "%", ".*", -1)
|
|
rpattern = strings.Replace(rpattern, "_", ".+", -1)
|
|
result.WriteString(rpattern)
|
|
|
|
return result.String()
|
|
}
|
|
|
|
// timeFromString attempts to parse the string to a time.Time using a series of
|
|
// time formats.
|
|
func timestampFromString(s string) (time.Time, error) {
|
|
if tm, err := time.ParseInLocation(time.RFC3339Nano, s, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else if tm, err := time.ParseInLocation(time.RFC3339, s, time.UTC); err == nil {
|
|
return tm, nil
|
|
} else if tm, err := time.ParseInLocation("2006-01-02", s, time.UTC); err == nil {
|
|
return tm, nil
|
|
}
|
|
|
|
return time.Time{}, sql3.NewErrInvalidTypeCoercion(0, 0, s, "time.Time")
|
|
}
|