938 lines
28 KiB
Go
938 lines
28 KiB
Go
// Copyright 2023 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package ast
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"go/constant"
|
|
"strings"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
"github.com/dolthub/go-mysql-server/sql/expression"
|
|
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/dolthub/doltgresql/core/id"
|
|
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
|
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
|
"github.com/dolthub/doltgresql/postgres/parser/types"
|
|
pgexprs "github.com/dolthub/doltgresql/server/expression"
|
|
"github.com/dolthub/doltgresql/server/functions/framework"
|
|
pgnodes "github.com/dolthub/doltgresql/server/node"
|
|
pgtypes "github.com/dolthub/doltgresql/server/types"
|
|
)
|
|
|
|
// nodeExprs handles tree.Exprs nodes.
|
|
func nodeExprs(ctx *Context, node tree.Exprs) (vitess.Exprs, error) {
|
|
if len(node) == 0 {
|
|
return nil, nil
|
|
}
|
|
exprs := make(vitess.Exprs, len(node))
|
|
for i := range node {
|
|
var err error
|
|
if exprs[i], err = nodeExpr(ctx, node[i]); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return exprs, nil
|
|
}
|
|
|
|
// nodeCompositeDatum handles tree.CompositeDatum nodes.
|
|
func nodeCompositeDatum(ctx *Context, node tree.CompositeDatum) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeConstant handles tree.Constant nodes.
|
|
func nodeConstant(ctx *Context, node tree.Constant) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeDatum handles tree.Datum nodes.
|
|
func nodeDatum(ctx *Context, node tree.Datum) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeSubqueryExpr handles tree.SubqueryExpr nodes.
|
|
func nodeSubqueryExpr(ctx *Context, node tree.SubqueryExpr) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeTypedExpr handles tree.TypedExpr nodes.
|
|
func nodeTypedExpr(ctx *Context, node tree.TypedExpr) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeVariableExpr handles tree.VariableExpr nodes.
|
|
func nodeVariableExpr(ctx *Context, node tree.VariableExpr) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeVarName handles tree.VarName nodes.
|
|
func nodeVarName(ctx *Context, node tree.VarName) (vitess.Expr, error) {
|
|
return nodeExpr(ctx, node)
|
|
}
|
|
|
|
// nodeExpr handles tree.Expr nodes.
|
|
func nodeExpr(ctx *Context, node tree.Expr) (vitess.Expr, error) {
|
|
switch node := node.(type) {
|
|
case *tree.AllColumnsSelector:
|
|
return nil, errors.Errorf("table.* syntax is not yet supported in this context")
|
|
case *tree.AndExpr:
|
|
left, err := nodeExpr(ctx, node.Left)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
right, err := nodeExpr(ctx, node.Right)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.AndExpr{
|
|
Left: left,
|
|
Right: right,
|
|
}, nil
|
|
case *tree.AnnotateTypeExpr:
|
|
return nil, errors.Errorf("ANNOTATE_TYPE is not yet supported")
|
|
case *tree.Array:
|
|
unresolvedChildren := make([]vitess.Expr, len(node.Exprs))
|
|
var coercedType *pgtypes.DoltgresType
|
|
if node.HasResolvedType() {
|
|
_, resolvedType, err := nodeResolvableTypeReference(ctx, node.ResolvedType(), false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resolvedType.IsArrayType() {
|
|
coercedType = resolvedType
|
|
} else {
|
|
return nil, errors.Errorf("array has invalid resolved type")
|
|
}
|
|
}
|
|
for i, arrayExpr := range node.Exprs {
|
|
var err error
|
|
unresolvedChildren[i], err = nodeExpr(ctx, arrayExpr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
arrayExpr, err := pgexprs.NewArray(coercedType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: arrayExpr,
|
|
Children: unresolvedChildren,
|
|
}, nil
|
|
case *tree.ArrayFlatten:
|
|
subquery, err := nodeExpr(ctx, node.Subquery)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.ArrayFlatten{},
|
|
Children: vitess.Exprs{subquery},
|
|
}, nil
|
|
case *tree.BinaryExpr:
|
|
// We will eventually support operators in other schemas, but for now we only can handle built-ins
|
|
if len(node.Schema) > 0 && node.Schema != "pg_catalog" {
|
|
return nil, errors.Errorf("schema %q not allowed in OPERATOR syntax", node.Schema)
|
|
}
|
|
|
|
left, err := nodeExpr(ctx, node.Left)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
right, err := nodeExpr(ctx, node.Right)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var operator framework.Operator
|
|
switch node.Operator {
|
|
case tree.Bitand:
|
|
operator = framework.Operator_BinaryBitAnd
|
|
case tree.Bitor:
|
|
operator = framework.Operator_BinaryBitOr
|
|
case tree.Bitxor:
|
|
operator = framework.Operator_BinaryBitXor
|
|
case tree.Plus:
|
|
operator = framework.Operator_BinaryPlus
|
|
case tree.Minus:
|
|
operator = framework.Operator_BinaryMinus
|
|
case tree.Mult:
|
|
operator = framework.Operator_BinaryMultiply
|
|
case tree.Div:
|
|
operator = framework.Operator_BinaryDivide
|
|
case tree.FloorDiv:
|
|
// TODO: replace with floor divide function
|
|
return nil, errors.Errorf("the floor divide operator is not yet supported")
|
|
case tree.Mod:
|
|
operator = framework.Operator_BinaryMod
|
|
case tree.Pow:
|
|
// TODO: replace with power function
|
|
return nil, errors.Errorf("the power operator is not yet supported")
|
|
case tree.Concat:
|
|
operator = framework.Operator_BinaryConcatenate
|
|
case tree.LShift:
|
|
operator = framework.Operator_BinaryShiftLeft
|
|
case tree.RShift:
|
|
operator = framework.Operator_BinaryShiftRight
|
|
case tree.JSONFetchVal:
|
|
operator = framework.Operator_BinaryJSONExtractJson
|
|
case tree.JSONFetchText:
|
|
operator = framework.Operator_BinaryJSONExtractText
|
|
case tree.JSONFetchValPath:
|
|
operator = framework.Operator_BinaryJSONExtractPathJson
|
|
case tree.JSONFetchTextPath:
|
|
operator = framework.Operator_BinaryJSONExtractPathText
|
|
default:
|
|
return nil, errors.Errorf("the binary operator used is not yet supported")
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(operator),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case *tree.CaseExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
whens := make([]*vitess.When, len(node.Whens))
|
|
for i := range node.Whens {
|
|
val, err := nodeExpr(ctx, node.Whens[i].Val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cond, err := nodeExpr(ctx, node.Whens[i].Cond)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
whens[i] = &vitess.When{
|
|
Val: val,
|
|
Cond: cond,
|
|
}
|
|
}
|
|
else_, err := nodeExpr(ctx, node.Else)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.CaseExpr{
|
|
Expr: expr,
|
|
Whens: whens,
|
|
Else: else_,
|
|
}, nil
|
|
case *tree.CastExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch node.SyntaxMode {
|
|
case tree.CastExplicit, tree.CastShort:
|
|
// Both of these are acceptable
|
|
case tree.CastPrepend:
|
|
// used for typed literals
|
|
strVal, isStrVal := node.Expr.(*tree.StrVal)
|
|
t, isT := node.Type.(*types.T)
|
|
if isStrVal && isT {
|
|
typedExpr, err := strVal.ResolveAsType(context.TODO(), nil, t)
|
|
if err != nil {
|
|
return nil, errors.Errorf("cannot resolve '%s' as type %s", strVal.String(), t.Name())
|
|
}
|
|
expr, err = nodeExpr(ctx, typedExpr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
default:
|
|
return nil, errors.Errorf("unknown cast syntax")
|
|
}
|
|
|
|
convertType, resolvedType, err := nodeResolvableTypeReference(ctx, node.Type, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// If we have the resolved type, then we've got a Doltgres type instead of a GMS type
|
|
if !resolvedType.IsEmptyType() {
|
|
cast, err := pgexprs.NewExplicitCastInjectable(resolvedType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: cast,
|
|
Children: vitess.Exprs{expr},
|
|
}, nil
|
|
} else {
|
|
convertType, err = translateConvertType(convertType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.ConvertExpr{
|
|
Name: "CAST",
|
|
Expr: expr,
|
|
Type: convertType,
|
|
}, nil
|
|
}
|
|
|
|
case *tree.CoalesceExpr:
|
|
exprs, err := nodeExprsToSelectExprs(ctx, node.Exprs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &vitess.FuncExpr{
|
|
Name: vitess.NewColIdent("COALESCE"),
|
|
Exprs: exprs,
|
|
}, nil
|
|
case *tree.CollateExpr:
|
|
logrus.Warnf("collate is not yet supported, ignoring")
|
|
return nodeExpr(ctx, node.Expr)
|
|
case *tree.ColumnAccessExpr:
|
|
colAccess, err := pgexprs.NewColumnAccess(node.ColName, node.ColIndex)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: colAccess,
|
|
Children: vitess.Exprs{expr},
|
|
}, nil
|
|
case *tree.ColumnItem:
|
|
var tableName vitess.TableName
|
|
if node.TableName != nil {
|
|
if node.TableName.NumParts > 2 {
|
|
return nil, errors.Errorf("referencing items outside the database is not yet supported")
|
|
}
|
|
tableName.Name = vitess.NewTableIdent(node.TableName.Parts[0])
|
|
tableName.SchemaQualifier = vitess.NewTableIdent(node.TableName.Parts[1])
|
|
}
|
|
return &vitess.ColName{
|
|
Name: vitess.NewColIdent(string(node.ColumnName)),
|
|
Qualifier: tableName,
|
|
}, nil
|
|
case *tree.CommentOnColumn:
|
|
return nil, errors.Errorf("comment on column is not yet supported")
|
|
case *tree.ComparisonExpr:
|
|
// We will eventually support operators in other schemas, but for now we only can handle built-ins
|
|
if len(node.Schema) > 0 && node.Schema != "pg_catalog" {
|
|
return nil, errors.Errorf("schema %q not allowed in OPERATOR syntax", node.Schema)
|
|
}
|
|
|
|
left, err := nodeExpr(ctx, node.Left)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
right, err := nodeExpr(ctx, node.Right)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var operator string
|
|
switch node.Operator {
|
|
case tree.EQ:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryEqual),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.LT:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessThan),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.GT:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterThan),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.LE:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessOrEqual),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.GE:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterOrEqual),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.NE:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryNotEqual),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.In, tree.NotIn:
|
|
var innerExpression vitess.InjectedExpr
|
|
switch right := right.(type) {
|
|
case vitess.ValTuple:
|
|
innerExpression = vitess.InjectedExpr{
|
|
Expression: pgexprs.NewInTuple(),
|
|
Children: vitess.Exprs{left, right},
|
|
}
|
|
case *vitess.Subquery:
|
|
innerExpression = vitess.InjectedExpr{
|
|
Expression: pgexprs.NewInSubquery(),
|
|
Children: vitess.Exprs{left, right},
|
|
}
|
|
case vitess.InjectedExpr:
|
|
if _, ok := right.Expression.(*pgexprs.RecordExpr); ok {
|
|
innerExpression = vitess.InjectedExpr{
|
|
Expression: pgexprs.NewInTuple(),
|
|
Children: vitess.Exprs{left, vitess.ValTuple(right.Children)},
|
|
}
|
|
}
|
|
}
|
|
|
|
if innerExpression.Expression == nil {
|
|
return nil, errors.Errorf("right side of IN expression is not a tuple or subquery, got %T", right)
|
|
}
|
|
|
|
switch node.Operator {
|
|
case tree.In:
|
|
return innerExpression, nil
|
|
case tree.NotIn:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewNot(),
|
|
Children: vitess.Exprs{innerExpression},
|
|
}, nil
|
|
default:
|
|
return nil, errors.Errorf("unknown comparison operator used")
|
|
}
|
|
case tree.Like:
|
|
operator = vitess.LikeStr
|
|
case tree.NotLike:
|
|
operator = vitess.NotLikeStr
|
|
case tree.ILike:
|
|
return nil, errors.Errorf("ILIKE is not yet supported")
|
|
case tree.NotILike:
|
|
return nil, errors.Errorf("ILIKE is not yet supported")
|
|
case tree.SimilarTo:
|
|
return nil, errors.Errorf("similar to is not yet supported")
|
|
case tree.NotSimilarTo:
|
|
return nil, errors.Errorf("not similar to is not yet supported")
|
|
case tree.RegMatch:
|
|
operator = vitess.RegexpStr
|
|
case tree.NotRegMatch:
|
|
operator = vitess.NotRegexpStr
|
|
case tree.RegIMatch:
|
|
return nil, errors.Errorf("~* is not yet supported")
|
|
case tree.NotRegIMatch:
|
|
return nil, errors.Errorf("!~* is not yet supported")
|
|
case tree.TextSearchMatch:
|
|
return nil, errors.Errorf("@@ is not yet supported")
|
|
case tree.IsDistinctFrom:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewIsDistinctFrom(),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.IsNotDistinctFrom:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewIsNotDistinctFrom(),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.Contains:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONContainsRight),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.ContainedBy:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONContainsLeft),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.JSONExists:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONTopLevel),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.JSONSomeExists:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONTopLevelAny),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.JSONAllExists:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryJSONTopLevelAll),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.Overlaps:
|
|
return nil, errors.Errorf("&& is not yet supported")
|
|
case tree.Any:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewAnyExpr(node.SubOperator.String()),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.Some:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewSomeExpr(node.SubOperator.String()),
|
|
Children: vitess.Exprs{left, right},
|
|
}, nil
|
|
case tree.All:
|
|
return nil, errors.Errorf("ALL is not yet supported")
|
|
default:
|
|
return nil, errors.Errorf("unknown comparison operator used")
|
|
}
|
|
return &vitess.ComparisonExpr{
|
|
Operator: operator,
|
|
Left: left,
|
|
Right: right,
|
|
Escape: nil, // TODO: is '\' the default in Postgres as well?
|
|
}, nil
|
|
case *tree.DArray:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DBitArray:
|
|
// We convert bitarray to string representation for engine representation purposes so that we don't have to
|
|
// represent another fundamental golang type. This means our representation in memory is more verbose.
|
|
bitStr := tree.AsStringWithFlags(node, tree.FmtPgwireText)
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewUnsafeLiteral(bitStr, pgtypes.Bit),
|
|
}, nil
|
|
case *tree.DBool:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralBool(bool(*node)),
|
|
}, nil
|
|
case *tree.DBox2D:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DBytes:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DCollatedString:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DDate:
|
|
t, err := node.Date.ToTime()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralDate(t),
|
|
}, nil
|
|
case *tree.DDecimal:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralNumeric(&node.Decimal)}, nil
|
|
case *tree.DEnum:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DFloat:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralFloat64(float64(*node)),
|
|
}, nil
|
|
case *tree.DGeography:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DGeometry:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DIPAddr:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DInt:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralInt64(int64(*node)),
|
|
}, nil
|
|
case *tree.DInterval:
|
|
cast, err := pgexprs.NewExplicitCastInjectable(pgtypes.Interval)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr := pgexprs.NewIntervalLiteral(node.Duration)
|
|
return vitess.InjectedExpr{
|
|
Expression: cast,
|
|
Children: vitess.Exprs{vitess.InjectedExpr{Expression: expr}},
|
|
}, nil
|
|
case *tree.DJSON:
|
|
// JSON type is handled in string format
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralJSON(node.JSON.String()),
|
|
}, nil
|
|
case *tree.DOid:
|
|
internalID := id.Cache().ToInternal(uint32(node.DInt))
|
|
if !internalID.IsValid() {
|
|
internalID = id.NewOID(uint32(node.DInt)).AsId()
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralOid(internalID),
|
|
}, nil
|
|
case *tree.DOidWrapper:
|
|
return nodeExpr(ctx, node.Wrapped)
|
|
case *tree.DString:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewUnknownLiteral(string(*node)),
|
|
}, nil
|
|
case *tree.DTime:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralTime(timeofday.TimeOfDay(*node)),
|
|
}, nil
|
|
case *tree.DTimeTZ:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralTimeTZ(node.TimeTZ),
|
|
}, nil
|
|
case *tree.DTimestamp:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralTimestamp(node.Time),
|
|
}, nil
|
|
case *tree.DTimestampTZ:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralTimestampTZ(node.Time),
|
|
}, nil
|
|
case *tree.DTuple:
|
|
return nil, errors.Errorf("the statement is not yet supported")
|
|
case *tree.DUuid:
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRawLiteralUuid(node.UUID),
|
|
}, nil
|
|
case tree.DefaultVal:
|
|
// TODO: can we use this?
|
|
defVal := &vitess.Default{ColName: ""}
|
|
return defVal, nil
|
|
case tree.DomainColumn:
|
|
_, dataType, err := nodeResolvableTypeReference(ctx, node.Typ, false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: &pgnodes.DomainColumn{Typ: dataType},
|
|
}, nil
|
|
case tree.FunctionColumn:
|
|
if !node.FromCreate {
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewUnsafeLiteral(node.Val, node.Typ),
|
|
}, nil
|
|
} else {
|
|
return vitess.InjectedExpr{
|
|
Expression: &pgnodes.FunctionColumn{Name: node.Name, Typ: node.Typ, Idx: node.Idx},
|
|
}, nil
|
|
}
|
|
case *tree.FuncExpr:
|
|
return nodeFuncExpr(ctx, node)
|
|
case *tree.IfErrExpr:
|
|
return nil, errors.Errorf("IFERROR is not yet supported")
|
|
case *tree.IfExpr:
|
|
cond, err := nodeExpr(ctx, node.Cond)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
trueVal, err := nodeExpr(ctx, node.True)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
falseVal, err := nodeExpr(ctx, node.Else)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// TODO: this could be a postgres func, but postgres doesn't have an IF function, this is an extension from cockroach
|
|
return &vitess.FuncExpr{
|
|
Name: vitess.NewColIdent("IF"),
|
|
Exprs: vitess.SelectExprs{
|
|
&vitess.AliasedExpr{
|
|
Expr: cond,
|
|
},
|
|
&vitess.AliasedExpr{
|
|
Expr: trueVal,
|
|
},
|
|
&vitess.AliasedExpr{
|
|
Expr: falseVal,
|
|
},
|
|
},
|
|
}, nil
|
|
case *tree.IndexedVar:
|
|
// TODO: figure out if I can delete this
|
|
return nil, errors.Errorf("this should probably be deleted (internal error, IndexedVar)")
|
|
case *tree.IndirectionExpr:
|
|
childExpr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(node.Indirection) > 1 {
|
|
return nil, errors.Errorf("multi dimensional array subscripts are not yet supported")
|
|
} else if node.Indirection[0].Slice {
|
|
return nil, errors.Errorf("slice subscripts are not yet supported")
|
|
}
|
|
|
|
indexExpr, err := nodeExpr(ctx, node.Indirection[0].Begin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return vitess.InjectedExpr{
|
|
Expression: &pgexprs.Subscript{},
|
|
Children: vitess.Exprs{childExpr, indexExpr},
|
|
}, nil
|
|
case *tree.IsNotNullExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.IsExpr{
|
|
Operator: vitess.IsNotNullStr,
|
|
Expr: expr,
|
|
}, nil
|
|
case *tree.IsNullExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.IsExpr{
|
|
Operator: vitess.IsNullStr,
|
|
Expr: expr,
|
|
}, nil
|
|
case *tree.IsOfTypeExpr:
|
|
return nil, errors.Errorf("IS OF is not yet supported")
|
|
case *tree.NotExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.NotExpr{
|
|
Expr: expr,
|
|
}, nil
|
|
case *tree.NullIfExpr:
|
|
expr1, err := nodeExprToSelectExpr(ctx, node.Expr1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expr2, err := nodeExprToSelectExpr(ctx, node.Expr2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &vitess.FuncExpr{
|
|
Name: vitess.NewColIdent("NULLIF"),
|
|
Exprs: vitess.SelectExprs{expr1, expr2},
|
|
}, nil
|
|
case tree.NullLiteral:
|
|
return &vitess.NullVal{}, nil
|
|
case *tree.NumVal:
|
|
switch node.Kind() {
|
|
case constant.Int:
|
|
intLiteral, err := pgexprs.NewIntegerLiteral(node.FormattedString())
|
|
return vitess.InjectedExpr{
|
|
Expression: intLiteral,
|
|
}, err
|
|
case constant.Float:
|
|
numericLiteral, err := pgexprs.NewNumericLiteral(node.FormattedString())
|
|
return vitess.InjectedExpr{
|
|
Expression: numericLiteral,
|
|
}, err
|
|
default:
|
|
return nil, errors.Errorf("unknown number format")
|
|
}
|
|
case *tree.OrExpr:
|
|
left, err := nodeExpr(ctx, node.Left)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
right, err := nodeExpr(ctx, node.Right)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.OrExpr{
|
|
Left: left,
|
|
Right: right,
|
|
}, nil
|
|
case *tree.ParenExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &vitess.ParenExpr{
|
|
Expr: expr,
|
|
}, nil
|
|
case *tree.PartitionMaxVal:
|
|
return nil, errors.Errorf("MAXVALUE is not yet supported")
|
|
case *tree.PartitionMinVal:
|
|
return nil, errors.Errorf("MINVALUE is not yet supported")
|
|
case *tree.Placeholder:
|
|
// TODO: deal with type annotation
|
|
mysqlBindVarIdx := node.Idx + 1
|
|
return vitess.NewValArg([]byte(fmt.Sprintf(":v%d", mysqlBindVarIdx))), nil
|
|
case *tree.RangeCond:
|
|
left, err := nodeExpr(ctx, node.Left)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
from, err := nodeExpr(ctx, node.From)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
to, err := nodeExpr(ctx, node.To)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
retExpr := vitess.Expr(&vitess.AndExpr{
|
|
Left: vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterOrEqual),
|
|
Children: vitess.Exprs{left, from},
|
|
},
|
|
Right: vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessOrEqual),
|
|
Children: vitess.Exprs{left, to},
|
|
},
|
|
})
|
|
if node.Symmetric {
|
|
retExpr = &vitess.OrExpr{
|
|
Left: retExpr,
|
|
Right: &vitess.AndExpr{
|
|
Left: vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryGreaterOrEqual),
|
|
Children: vitess.Exprs{left, to},
|
|
},
|
|
Right: vitess.InjectedExpr{
|
|
Expression: pgexprs.NewBinaryOperator(framework.Operator_BinaryLessOrEqual),
|
|
Children: vitess.Exprs{left, from},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
if node.Not {
|
|
retExpr = vitess.InjectedExpr{
|
|
Expression: pgexprs.NewNot(),
|
|
Children: vitess.Exprs{retExpr},
|
|
}
|
|
}
|
|
return retExpr, nil
|
|
case *tree.StrVal:
|
|
// TODO: determine what to do when node.WasScannedAsBytes() is true
|
|
// For string literals, we mark the type as unknown, because Postgres has
|
|
// more permissive implicit casting rules for literals than it does for strongly
|
|
// typed values from a schema for example.
|
|
unknownLiteral := pgexprs.NewUnknownLiteral(node.RawString())
|
|
return vitess.InjectedExpr{
|
|
Expression: unknownLiteral,
|
|
}, nil
|
|
case *tree.Subquery:
|
|
return nodeSubqueryOrExists(ctx, node)
|
|
case *tree.Tuple:
|
|
if len(node.Labels) > 0 {
|
|
return nil, errors.Errorf("tuple labels are not yet supported")
|
|
}
|
|
|
|
valTuple, err := nodeExprs(ctx, node.Exprs)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewRecordExpr(),
|
|
Children: valTuple,
|
|
}, nil
|
|
case *tree.TupleStar:
|
|
return nil, errors.Errorf("(E).* is not yet supported")
|
|
case *tree.UnaryExpr:
|
|
expr, err := nodeExpr(ctx, node.Expr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var operator framework.Operator
|
|
switch node.Operator {
|
|
// TODO: need to add UnaryPlus, it's like a no-op but Postgres actually implements it and it affects coercion
|
|
case tree.UnaryMinus:
|
|
operator = framework.Operator_UnaryMinus
|
|
case tree.UnaryComplement:
|
|
return &vitess.UnaryExpr{
|
|
Operator: vitess.TildaStr,
|
|
Expr: expr,
|
|
}, nil
|
|
case tree.UnarySqrt:
|
|
// TODO: replace with a function
|
|
return nil, errors.Errorf("square root operator is not yet supported")
|
|
case tree.UnaryCbrt:
|
|
// TODO: replace with a function
|
|
return nil, errors.Errorf("cube root operator is not yet supported")
|
|
case tree.UnaryAbsolute:
|
|
// TODO: replace with a function
|
|
return nil, errors.Errorf("absolute operator is not yet supported")
|
|
default:
|
|
return nil, errors.Errorf("the unary operator used is not yet supported")
|
|
}
|
|
return vitess.InjectedExpr{
|
|
Expression: pgexprs.NewUnaryOperator(operator),
|
|
Children: vitess.Exprs{expr},
|
|
}, nil
|
|
case tree.UnqualifiedStar:
|
|
return nil, errors.Errorf("* syntax is not yet supported in this context")
|
|
case *tree.UnresolvedName:
|
|
if node.Star {
|
|
return nil, errors.Errorf("* syntax is not yet supported in this context")
|
|
}
|
|
return unresolvedNameToColName(node)
|
|
case nil:
|
|
return nil, nil
|
|
default:
|
|
return nil, errors.Errorf("unknown expression: `%T`", node)
|
|
}
|
|
}
|
|
|
|
// unresolvedNameToColName converts a tree.UnresolvedName to a vitess.ColName with the appropriate name qualifiers set.
|
|
func unresolvedNameToColName(name *tree.UnresolvedName) (*vitess.ColName, error) {
|
|
var tableName vitess.TableName
|
|
switch name.NumParts {
|
|
case 4:
|
|
tableName = vitess.TableName{
|
|
Name: vitess.NewTableIdent(name.Parts[1]),
|
|
SchemaQualifier: vitess.NewTableIdent(name.Parts[2]),
|
|
DbQualifier: vitess.NewTableIdent(name.Parts[3]),
|
|
}
|
|
case 3:
|
|
tableName = vitess.TableName{
|
|
Name: vitess.NewTableIdent(name.Parts[1]),
|
|
SchemaQualifier: vitess.NewTableIdent(name.Parts[2]),
|
|
}
|
|
case 2:
|
|
tableName = vitess.TableName{
|
|
Name: vitess.NewTableIdent(name.Parts[1]),
|
|
}
|
|
case 1:
|
|
// no table name
|
|
default:
|
|
return nil, errors.Errorf("invalid name: %s", name)
|
|
}
|
|
|
|
return &vitess.ColName{
|
|
Name: vitess.NewColIdent(name.Parts[0]),
|
|
Qualifier: tableName,
|
|
}, nil
|
|
}
|
|
|
|
// translateConvertType translates the *vitess.ConvertType expression given to a new one, substituting type names as
|
|
// appropriate. An error is returned if the type named cannot be supported.
|
|
func translateConvertType(convertType *vitess.ConvertType) (*vitess.ConvertType, error) {
|
|
switch strings.ToLower(convertType.Type) {
|
|
// passthrough types that need no conversion
|
|
case expression.ConvertToBinary, expression.ConvertToChar, expression.ConvertToNChar, expression.ConvertToDate,
|
|
expression.ConvertToDatetime, expression.ConvertToFloat, expression.ConvertToDouble, expression.ConvertToJSON,
|
|
expression.ConvertToReal, expression.ConvertToSigned, expression.ConvertToTime, expression.ConvertToUnsigned:
|
|
return convertType, nil
|
|
case "text", "character varying", "varchar":
|
|
return &vitess.ConvertType{
|
|
Type: expression.ConvertToChar,
|
|
}, nil
|
|
case "integer", "bigint":
|
|
return &vitess.ConvertType{
|
|
Type: expression.ConvertToSigned,
|
|
}, nil
|
|
case "decimal", "numeric":
|
|
return &vitess.ConvertType{
|
|
Type: expression.ConvertToFloat,
|
|
}, nil
|
|
case "boolean":
|
|
return &vitess.ConvertType{
|
|
Type: expression.ConvertToSigned,
|
|
}, nil
|
|
case "timestamp", "timestamp with time zone", "timestamp without time zone":
|
|
return &vitess.ConvertType{
|
|
Type: expression.ConvertToDatetime,
|
|
}, nil
|
|
default:
|
|
return nil, errors.Errorf("unknown convert type: `%T`", convertType.Type)
|
|
}
|
|
}
|