Files
2026-07-13 12:32:25 +08:00

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
}