375 lines
12 KiB
Go
375 lines
12 KiB
Go
// Copyright 2024 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 expression
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/cockroachdb/errors"
|
|
"github.com/dolthub/go-mysql-server/sql"
|
|
"github.com/dolthub/go-mysql-server/sql/expression"
|
|
"github.com/dolthub/go-mysql-server/sql/plan"
|
|
|
|
"github.com/dolthub/doltgresql/server/functions/framework"
|
|
pgtypes "github.com/dolthub/doltgresql/server/types"
|
|
)
|
|
|
|
// AnyExpr represents the ANY/SOME expression.
|
|
type AnyExpr struct {
|
|
leftExpr sql.Expression
|
|
rightExpr sql.Expression
|
|
subOperator string
|
|
name string // ANY or SOME
|
|
|
|
subqueryAnyExpr *subqueryAnyExpr
|
|
expressionAnyExpr *expressionAnyExpr
|
|
}
|
|
|
|
// subqueryAnyExpr represents the resolved comparison functions for a plan.Subquery.
|
|
type subqueryAnyExpr struct {
|
|
rightSub *plan.Subquery
|
|
staticLiteral *expression.Literal
|
|
arrayLiterals []*expression.Literal
|
|
compFuncs []framework.Function
|
|
}
|
|
|
|
// expressionAnyExpr represents the resolved comparison function for a sql.Expression.
|
|
type expressionAnyExpr struct {
|
|
rightExpr sql.Expression
|
|
staticLiteral *expression.Literal
|
|
arrayLiteral *expression.Literal
|
|
compFunc framework.Function
|
|
}
|
|
|
|
// NewAnyExpr creates a new AnyExpr expression.
|
|
func NewAnyExpr(subOperator string) *AnyExpr {
|
|
return &AnyExpr{
|
|
leftExpr: nil,
|
|
rightExpr: nil,
|
|
subOperator: subOperator,
|
|
name: "ANY",
|
|
}
|
|
}
|
|
|
|
// Children implements the Expression interface.
|
|
func (a *AnyExpr) Children() []sql.Expression {
|
|
return []sql.Expression{a.leftExpr, a.rightExpr}
|
|
}
|
|
|
|
// Resolved implements the Expression interface.
|
|
func (a *AnyExpr) Resolved() bool {
|
|
if a.leftExpr == nil || !a.leftExpr.Resolved() || a.rightExpr == nil || !a.rightExpr.Resolved() {
|
|
return false
|
|
}
|
|
if a.subqueryAnyExpr != nil {
|
|
return a.subqueryAnyExpr.resolved()
|
|
}
|
|
if a.expressionAnyExpr != nil {
|
|
return a.expressionAnyExpr.resolved()
|
|
}
|
|
return true
|
|
}
|
|
|
|
// IsNullable implements the Expression interface.
|
|
func (a *AnyExpr) IsNullable(ctx *sql.Context) bool {
|
|
return a.leftExpr.IsNullable(ctx) || a.rightExpr.IsNullable(ctx)
|
|
}
|
|
|
|
// Type implements the Expression interface.
|
|
func (a *AnyExpr) Type(ctx *sql.Context) sql.Type {
|
|
return pgtypes.Bool
|
|
}
|
|
|
|
// resolved checks if the comparison functions for subqueryAnyExpr is resolved.
|
|
func (a *subqueryAnyExpr) resolved() bool {
|
|
if len(a.compFuncs) == 0 {
|
|
return false
|
|
}
|
|
for _, compFunc := range a.compFuncs {
|
|
if !compFunc.Resolved() {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// eval evaluates the comparison functions for subqueryAnyExpr.
|
|
func (a *subqueryAnyExpr) eval(ctx *sql.Context, subOperator string, row sql.Row, left interface{}) (interface{}, error) {
|
|
if len(a.compFuncs) == 0 {
|
|
return nil, errors.Errorf("%T: cannot Eval as it has not been fully resolved", a)
|
|
}
|
|
|
|
// TODO: This sometimes panics in `evalMultiple` for subqueries that return
|
|
// more than one row, when len(row) > len(iter.Next())
|
|
rightValues, err := a.rightSub.EvalMultiple(ctx, row)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(rightValues) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// TODO: This is a workaround some subqueries where the schema length does not
|
|
// match the row length
|
|
if len(a.arrayLiterals) == 1 && len(rightValues) != 1 {
|
|
op, err := framework.GetOperatorFromString(subOperator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := len(a.arrayLiterals); i < len(rightValues); i++ {
|
|
arrayLiteral := expression.NewLiteral(nil, a.arrayLiterals[0].Type(ctx))
|
|
a.arrayLiterals = append(a.arrayLiterals, arrayLiteral)
|
|
compFunc := framework.GetBinaryFunction(op).Compile(ctx, "internal_any_comparison", a.staticLiteral, a.arrayLiterals[i])
|
|
a.compFuncs = append(a.compFuncs, compFunc)
|
|
}
|
|
}
|
|
|
|
if len(a.arrayLiterals) != len(rightValues) {
|
|
return nil, errors.Errorf("%T: expected right child to return `%d` values but returned `%d`", a, len(a.arrayLiterals), len(rightValues))
|
|
}
|
|
|
|
// Next we'll assign our evaluated values to the expressions that the comparison functions reference
|
|
// Note that the compiled function has a reference to the staticLiteral and arrayLiterals, so we must alter them in place
|
|
a.staticLiteral.Val = left
|
|
for i, rightValue := range rightValues {
|
|
a.arrayLiterals[i].Val = rightValue
|
|
}
|
|
// Now we can loop over all comparison functions, as they'll reference their respective values
|
|
for _, compFunc := range a.compFuncs {
|
|
result, err := compFunc.Eval(ctx, row)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result.(bool) {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// resolved checks if the comparison function for expressionAnyExpr is resolved.
|
|
func (a *expressionAnyExpr) resolved() bool {
|
|
if a.compFunc == nil || !a.compFunc.Resolved() {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
// eval evaluates the comparison function for expressionAnyExpr.
|
|
func (a *expressionAnyExpr) eval(ctx *sql.Context, row sql.Row, left interface{}) (interface{}, error) {
|
|
if a.compFunc == nil {
|
|
return nil, errors.Errorf("%T: cannot Eval as it has not been fully resolved", a)
|
|
}
|
|
|
|
rightInterface, err := a.rightExpr.Eval(ctx, row)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if rightInterface == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
rightValues, ok := rightInterface.([]any)
|
|
if !ok {
|
|
return nil, errors.Errorf("%T: expected right child to return `%T` but returned `%T`", a, []any{}, rightInterface)
|
|
}
|
|
if len(rightValues) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// Next we'll assign our evaluated values to the expressions that the comparison function reference
|
|
// Note that the compiled function has a reference to the staticLiteral and arrayLiteral, so we must alter them in place
|
|
a.staticLiteral.Val = left
|
|
for _, rightValue := range rightValues {
|
|
a.arrayLiteral.Val = rightValue
|
|
result, err := a.compFunc.Eval(ctx, row)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if result == nil {
|
|
return nil, nil
|
|
}
|
|
if result.(bool) {
|
|
return true, nil
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
// Eval implements the Expression interface.
|
|
func (a *AnyExpr) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
|
|
left, err := a.leftExpr.Eval(ctx, row)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if a.subqueryAnyExpr != nil {
|
|
return a.subqueryAnyExpr.eval(ctx, a.subOperator, row, left)
|
|
}
|
|
|
|
if a.expressionAnyExpr != nil {
|
|
return a.expressionAnyExpr.eval(ctx, row, left)
|
|
}
|
|
|
|
return nil, errors.Errorf("%T: cannot Eval as it has not been fully resolved", a)
|
|
}
|
|
|
|
// WithChildren implements the Expression interface.
|
|
func (a *AnyExpr) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
|
|
if len(children) != 2 {
|
|
return nil, sql.ErrInvalidChildrenNumber.New(a, len(children), 2)
|
|
}
|
|
|
|
leftExpr := children[0]
|
|
rightExpr := children[1]
|
|
// Unmodified BindVars use deferred type resolution, so we replace the deference with the left's type in array form
|
|
if bv, ok := rightExpr.(*expression.BindVar); ok {
|
|
if _, ok = bv.Typ.(*pgtypes.DoltgresType); !ok {
|
|
if leftType, ok := leftExpr.Type(ctx).(*pgtypes.DoltgresType); ok {
|
|
bv.Typ = leftType.ToArrayType()
|
|
}
|
|
}
|
|
}
|
|
anyExpr := &AnyExpr{
|
|
leftExpr: leftExpr,
|
|
rightExpr: rightExpr,
|
|
subOperator: a.subOperator,
|
|
name: a.name,
|
|
}
|
|
|
|
if sub, ok := children[1].(*plan.Subquery); ok {
|
|
return anySubqueryWithChildren(ctx, anyExpr, sub)
|
|
}
|
|
|
|
return anyExpressionWithChildren(ctx, anyExpr)
|
|
}
|
|
|
|
// WithResolvedChildren implements the Expression interface.
|
|
func (a *AnyExpr) WithResolvedChildren(ctx context.Context, children []any) (any, error) {
|
|
if len(children) != 2 {
|
|
return nil, errors.Errorf("invalid vitess child count, expected `2` but got `%d`", len(children))
|
|
}
|
|
left, ok := children[0].(sql.Expression)
|
|
if !ok {
|
|
return nil, errors.Errorf("expected vitess child to be an expression but has type `%T`", children[0])
|
|
}
|
|
right, ok := children[1].(sql.Expression)
|
|
if !ok {
|
|
return nil, errors.Errorf("expected vitess child to be an expression but has type `%T`", children[1])
|
|
}
|
|
return a.WithChildren(ctx.(*sql.Context), left, right)
|
|
}
|
|
|
|
// String implements the fmt.Stringer interface.
|
|
func (a *AnyExpr) String() string {
|
|
if a.leftExpr == nil || a.rightExpr == nil {
|
|
return fmt.Sprintf("? %s (?)", a.name)
|
|
}
|
|
return fmt.Sprintf("%s = %s (%s)", a.leftExpr, a.name, a.rightExpr)
|
|
}
|
|
|
|
// DebugString implements the Expression interface.
|
|
func (a *AnyExpr) DebugString(ctx *sql.Context) string {
|
|
return fmt.Sprintf("%s %s (%s)", sql.DebugString(ctx, a.leftExpr), a.name, sql.DebugString(ctx, a.rightExpr))
|
|
}
|
|
|
|
// anySubqueryWithChildren resolves the comparison functions for a plan.Subquery.
|
|
func anySubqueryWithChildren(ctx *sql.Context, anyExpr *AnyExpr, sub *plan.Subquery) (sql.Expression, error) {
|
|
schema := sub.Query.Schema(ctx)
|
|
subTypes := make([]*pgtypes.DoltgresType, len(schema))
|
|
for i, col := range schema {
|
|
dgType, ok := col.Type.(*pgtypes.DoltgresType)
|
|
if !ok {
|
|
return nil, errors.Errorf("expected right child to be a DoltgresType but got `%T`", sub)
|
|
}
|
|
subTypes[i] = dgType
|
|
}
|
|
|
|
op, err := framework.GetOperatorFromString(anyExpr.subOperator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if leftType, ok := anyExpr.leftExpr.Type(ctx).(*pgtypes.DoltgresType); ok {
|
|
// Resolve comparison functions once and reuse the functions in Eval.
|
|
staticLiteral := expression.NewLiteral(nil, leftType)
|
|
arrayLiterals := make([]*expression.Literal, len(subTypes))
|
|
// Each expression may be a different type (which is valid), so we need a comparison function for each expression.
|
|
compFuncs := make([]framework.Function, len(subTypes))
|
|
for i, rightType := range subTypes {
|
|
arrayLiterals[i] = expression.NewLiteral(nil, rightType)
|
|
compFuncs[i] = framework.GetBinaryFunction(op).Compile(ctx, "internal_any_comparison", staticLiteral, arrayLiterals[i])
|
|
if compFuncs[i] == nil {
|
|
return nil, errors.Errorf("operator does not exist: %s = %s", leftType.String(), rightType.String())
|
|
}
|
|
if compFuncs[i].Type(ctx).(*pgtypes.DoltgresType).ID != pgtypes.Bool.ID {
|
|
// This should never happen, but this is just to be safe
|
|
return nil, errors.Errorf("%T: found equality comparison that does not return a bool", anyExpr)
|
|
}
|
|
}
|
|
|
|
anyExpr.subqueryAnyExpr = &subqueryAnyExpr{
|
|
rightSub: sub,
|
|
staticLiteral: staticLiteral,
|
|
arrayLiterals: arrayLiterals,
|
|
compFuncs: compFuncs,
|
|
}
|
|
}
|
|
|
|
return anyExpr, nil
|
|
}
|
|
|
|
// anyExpressionWithChildren resolves the comparison functions for a sql.Expression.
|
|
func anyExpressionWithChildren(ctx *sql.Context, anyExpr *AnyExpr) (sql.Expression, error) {
|
|
arrType, ok := anyExpr.rightExpr.Type(ctx).(*pgtypes.DoltgresType)
|
|
if !ok {
|
|
return nil, errors.Errorf("expected right child to be a DoltgresType but got `%T`", anyExpr.rightExpr)
|
|
}
|
|
rightType := arrType.ArrayBaseType()
|
|
op, err := framework.GetOperatorFromString(anyExpr.subOperator)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if leftType, ok := anyExpr.leftExpr.Type(ctx).(*pgtypes.DoltgresType); ok {
|
|
// Resolve comparison function once and reuse the function in Eval.
|
|
staticLiteral := expression.NewLiteral(nil, leftType)
|
|
arrayLiteral := expression.NewLiteral(nil, rightType)
|
|
compFunc := framework.GetBinaryFunction(op).Compile(ctx, "internal_any_comparison", staticLiteral, arrayLiteral)
|
|
if compFunc == nil || compFunc.StashedError() != nil {
|
|
return nil, errors.Errorf("operator does not exist: %s = %s", leftType.String(), rightType.String())
|
|
}
|
|
compFuncType := compFunc.Type(ctx)
|
|
if compFuncType.(*pgtypes.DoltgresType).ID != pgtypes.Bool.ID {
|
|
// This should never happen, but this is just to be safe
|
|
return nil, errors.Errorf("%T: found equality comparison that does not return a bool", anyExpr)
|
|
}
|
|
anyExpr.expressionAnyExpr = &expressionAnyExpr{
|
|
rightExpr: anyExpr.rightExpr,
|
|
staticLiteral: staticLiteral,
|
|
arrayLiteral: arrayLiteral,
|
|
compFunc: compFunc,
|
|
}
|
|
}
|
|
|
|
return anyExpr, nil
|
|
}
|