Files
dolthub--doltgresql/server/ast/expr.go
T
2026-07-13 12:32:25 +08:00

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)
}
}