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

466 lines
15 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 framework
import (
"fmt"
"github.com/cockroachdb/errors"
"github.com/dolthub/go-mysql-server/sql"
pgtypes "github.com/dolthub/doltgresql/server/types"
)
// QuickFunction represents an optimized function expression that has specific criteria in order to streamline
// evaluation. This will only apply to very specific functions that are generally performance-critical.
type QuickFunction interface {
Function
sql.RowIterExpression
// CallVariadic is the variadic form of the Call function that is specific to each implementation of QuickFunction.
// The implementation will not verify that the correct number of arguments have been passed.
CallVariadic(ctx *sql.Context, args ...any) (interface{}, error)
// ResolvedTypes returns the types that were resolved with this function.
ResolvedTypes() []*pgtypes.DoltgresType
// WithResolvedTypes returns a new QuickFunction with the replaced resolved types. The implementation will not
// verify that the new types are correct in any way. This returns a QuickFunction, however it's typed as "any" due
// to potential import cycles.
WithResolvedTypes(newTypes []*pgtypes.DoltgresType) any
}
// QuickFunction1 is an implementation of QuickFunction that handles a single parameter.
type QuickFunction1 struct {
Name string
Argument sql.Expression
IsStrict bool
IsSRF bool
callResolved [2]*pgtypes.DoltgresType
function Function1
}
var _ QuickFunction = (*QuickFunction1)(nil)
// FunctionName implements the interface sql.Expression.
func (q *QuickFunction1) FunctionName() string {
return q.Name
}
// Description implements the interface sql.Expression.
func (q *QuickFunction1) Description() string {
return fmt.Sprintf("The PostgreSQL function `%s`", q.Name)
}
// Resolved implements the interface sql.Expression.
func (q *QuickFunction1) Resolved() bool {
return true
}
// String implements the interface sql.Expression.
func (q *QuickFunction1) String() string {
// We'll reuse the compiled function's output so that the logic is centralized
c := CompiledFunction{
Name: q.Name,
Arguments: []sql.Expression{q.Argument},
}
return c.String()
}
// Type implements the interface sql.Expression.
func (q *QuickFunction1) Type(ctx *sql.Context) sql.Type {
return getTypeIfRowType(q.IsSRF, q.callResolved[1])
}
// IsNullable implements the interface sql.Expression.
func (q *QuickFunction1) IsNullable(ctx *sql.Context) bool {
return true
}
// IsNonDeterministic implements the interface sql.NonDeterministicExpression.
func (q *QuickFunction1) IsNonDeterministic() bool {
return q.function.IsNonDeterministic
}
// Eval implements the interface sql.Expression.
func (q *QuickFunction1) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
arg, err := q.Argument.Eval(ctx, row)
if err != nil {
return nil, err
}
if arg == nil && q.IsStrict {
return nil, nil
}
return q.function.Callable(ctx, q.callResolved, arg)
}
// EvalRowIter implements the interface sql.RowIterExpression.
func (q *QuickFunction1) EvalRowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) {
eval, err := q.Eval(ctx, r)
if err != nil {
return nil, err
}
switch eval := eval.(type) {
case sql.RowIter:
return eval, nil
case nil:
return nil, nil
default:
return nil, errors.Errorf("function %s returned a value of type %T, which is not a RowIter", q.Name, eval)
}
}
// ReturnsRowIter implements the interface sql.RowIterExpression.
func (q *QuickFunction1) ReturnsRowIter() bool {
return q.IsSRF
}
// Call directly calls the underlying function with the given arguments. This does not perform any form of NULL checking
// as it is assumed that it was done prior to this call. It also does not validate any types. This exists purely for
// performance, when we can guarantee that the input is always valid and well-formed.
func (q *QuickFunction1) Call(ctx *sql.Context, arg0 any) (interface{}, error) {
return q.function.Callable(ctx, q.callResolved, arg0)
}
// CallVariadic implements the interface QuickFunction.
func (q *QuickFunction1) CallVariadic(ctx *sql.Context, args ...any) (interface{}, error) {
return q.function.Callable(ctx, q.callResolved, args[0])
}
// ResolvedTypes implements the interface QuickFunction.
func (q *QuickFunction1) ResolvedTypes() []*pgtypes.DoltgresType {
return q.callResolved[:]
}
// WithResolvedTypes implements the interface QuickFunction.
func (q *QuickFunction1) WithResolvedTypes(newTypes []*pgtypes.DoltgresType) any {
return &QuickFunction1{
Name: q.Name,
Argument: q.Argument,
IsStrict: q.IsStrict,
IsSRF: q.IsSRF,
callResolved: [2]*pgtypes.DoltgresType(newTypes),
function: q.function,
}
}
// Children implements the interface sql.Expression.
func (q *QuickFunction1) Children() []sql.Expression {
return []sql.Expression{q.Argument}
}
// WithChildren implements the interface sql.Expression.
func (q *QuickFunction1) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
if len(children) != 1 {
return nil, sql.ErrInvalidChildrenNumber.New(q, len(children), 1)
}
if children[0].Type(ctx).Equals(q.Argument.Type(ctx)) {
nq := *q
nq.Argument = children[0]
return &nq, nil
}
return nil, errors.Errorf("cannot change the types of children for `%T`", q)
}
// specificFuncImpl implements the interface sql.Expression.
func (*QuickFunction1) specificFuncImpl() {}
// QuickFunction2 is an implementation of QuickFunction that handles two parameters.
type QuickFunction2 struct {
Name string
Arguments [2]sql.Expression
IsStrict bool
IsSRF bool
callResolved [3]*pgtypes.DoltgresType
function Function2
}
var _ QuickFunction = (*QuickFunction2)(nil)
// FunctionName implements the interface sql.Expression.
func (q *QuickFunction2) FunctionName() string {
return q.Name
}
// Description implements the interface sql.Expression.
func (q *QuickFunction2) Description() string {
return fmt.Sprintf("The PostgreSQL function `%s`", q.Name)
}
// Resolved implements the interface sql.Expression.
func (q *QuickFunction2) Resolved() bool {
return true
}
// String implements the interface sql.Expression.
func (q *QuickFunction2) String() string {
// We'll reuse the compiled function's output so that the logic is centralized
c := CompiledFunction{
Name: q.Name,
Arguments: q.Arguments[:],
}
return c.String()
}
// Type implements the interface sql.Expression.
func (q *QuickFunction2) Type(ctx *sql.Context) sql.Type {
return getTypeIfRowType(q.IsSRF, q.callResolved[2])
}
// IsNullable implements the interface sql.Expression.
func (q *QuickFunction2) IsNullable(ctx *sql.Context) bool {
return true
}
// IsNonDeterministic implements the interface sql.NonDeterministicExpression.
func (q *QuickFunction2) IsNonDeterministic() bool {
return q.function.IsNonDeterministic
}
// Eval implements the interface sql.Expression.
func (q *QuickFunction2) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
var args [2]any
for i := range q.Arguments {
var err error
args[i], err = q.Arguments[i].Eval(ctx, row)
if err != nil {
return nil, err
}
if args[i] == nil && q.IsStrict {
return nil, nil
}
}
return q.function.Callable(ctx, q.callResolved, args[0], args[1])
}
// EvalRowIter implements the interface sql.RowIterExpression.
func (q *QuickFunction2) EvalRowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) {
eval, err := q.Eval(ctx, r)
if err != nil {
return nil, err
}
switch eval := eval.(type) {
case sql.RowIter:
return eval, nil
case nil:
return nil, nil
default:
return nil, errors.Errorf("function %s returned a value of type %T, which is not a RowIter", q.Name, eval)
}
}
// ReturnsRowIter implements the interface sql.RowIterExpression.
func (q *QuickFunction2) ReturnsRowIter() bool {
return q.IsSRF
}
// Call directly calls the underlying function with the given arguments. This does not perform any form of NULL checking
// as it is assumed that it was done prior to this call. It also does not validate any types. This exists purely for
// performance, when we can guarantee that the input is always valid and well-formed.
func (q *QuickFunction2) Call(ctx *sql.Context, arg0 any, arg1 any) (interface{}, error) {
return q.function.Callable(ctx, q.callResolved, arg0, arg1)
}
// CallVariadic implements the interface QuickFunction.
func (q *QuickFunction2) CallVariadic(ctx *sql.Context, args ...any) (interface{}, error) {
return q.function.Callable(ctx, q.callResolved, args[0], args[1])
}
// ResolvedTypes implements the interface QuickFunction.
func (q *QuickFunction2) ResolvedTypes() []*pgtypes.DoltgresType {
return q.callResolved[:]
}
// WithResolvedTypes implements the interface QuickFunction.
func (q *QuickFunction2) WithResolvedTypes(newTypes []*pgtypes.DoltgresType) any {
return &QuickFunction2{
Name: q.Name,
Arguments: q.Arguments,
IsStrict: q.IsStrict,
IsSRF: q.IsSRF,
callResolved: [3]*pgtypes.DoltgresType(newTypes),
function: q.function,
}
}
// Children implements the interface sql.Expression.
func (q *QuickFunction2) Children() []sql.Expression {
return q.Arguments[:]
}
// WithChildren implements the interface sql.Expression.
func (q *QuickFunction2) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
if len(children) != 2 {
return nil, sql.ErrInvalidChildrenNumber.New(q, len(children), 2)
}
if children[0].Type(ctx).Equals(q.Arguments[0].Type(ctx)) &&
children[1].Type(ctx).Equals(q.Arguments[1].Type(ctx)) {
nq := *q
nq.Arguments = [2]sql.Expression{children[0], children[1]}
return &nq, nil
}
return nil, errors.Errorf("cannot change the types of children for `%T`", q)
}
// specificFuncImpl implements the interface sql.Expression.
func (*QuickFunction2) specificFuncImpl() {}
// QuickFunction3 is an implementation of QuickFunction that handles three parameters.
type QuickFunction3 struct {
Name string
Arguments [3]sql.Expression
IsStrict bool
IsSRF bool
callResolved [4]*pgtypes.DoltgresType
function Function3
}
var _ QuickFunction = (*QuickFunction3)(nil)
// FunctionName implements the interface sql.Expression.
func (q *QuickFunction3) FunctionName() string {
return q.Name
}
// Description implements the interface sql.Expression.
func (q *QuickFunction3) Description() string {
return fmt.Sprintf("The PostgreSQL function `%s`", q.Name)
}
// Resolved implements the interface sql.Expression.
func (q *QuickFunction3) Resolved() bool {
return true
}
// String implements the interface sql.Expression.
func (q *QuickFunction3) String() string {
// We'll reuse the compiled function's output so that the logic is centralized
c := CompiledFunction{
Name: q.Name,
Arguments: q.Arguments[:],
}
return c.String()
}
// Type implements the interface sql.Expression.
func (q *QuickFunction3) Type(ctx *sql.Context) sql.Type {
return getTypeIfRowType(q.IsSRF, q.callResolved[3])
}
// IsNullable implements the interface sql.Expression.
func (q *QuickFunction3) IsNullable(ctx *sql.Context) bool {
return true
}
// IsNonDeterministic implements the interface sql.NonDeterministicExpression.
func (q *QuickFunction3) IsNonDeterministic() bool {
return q.function.IsNonDeterministic
}
// Eval implements the interface sql.Expression.
func (q *QuickFunction3) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
var args [3]any
for i := range q.Arguments {
var err error
args[i], err = q.Arguments[i].Eval(ctx, row)
if err != nil {
return nil, err
}
if args[i] == nil && q.IsStrict {
return nil, nil
}
}
return q.function.Callable(ctx, q.callResolved, args[0], args[1], args[2])
}
// EvalRowIter implements the interface sql.RowIterExpression.
func (q *QuickFunction3) EvalRowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) {
eval, err := q.Eval(ctx, r)
if err != nil {
return nil, err
}
switch eval := eval.(type) {
case sql.RowIter:
return eval, nil
case nil:
return nil, nil
default:
return nil, errors.Errorf("function %s returned a value of type %T, which is not a RowIter", q.Name, eval)
}
}
// ReturnsRowIter implements the interface sql.RowIterExpression.
func (q *QuickFunction3) ReturnsRowIter() bool {
return q.IsSRF
}
// Call directly calls the underlying function with the given arguments. This does not perform any form of NULL checking
// as it is assumed that it was done prior to this call. It also does not validate any types. This exists purely for
// performance, when we can guarantee that the input is always valid and well-formed.
func (q *QuickFunction3) Call(ctx *sql.Context, arg0 any, arg1 any, arg2 any) (interface{}, error) {
return q.function.Callable(ctx, q.callResolved, arg0, arg1, arg2)
}
// CallVariadic implements the interface QuickFunction.
func (q *QuickFunction3) CallVariadic(ctx *sql.Context, args ...any) (interface{}, error) {
return q.function.Callable(ctx, q.callResolved, args[0], args[1], args[2])
}
// ResolvedTypes implements the interface QuickFunction.
func (q *QuickFunction3) ResolvedTypes() []*pgtypes.DoltgresType {
return q.callResolved[:]
}
// WithResolvedTypes implements the interface QuickFunction.
func (q *QuickFunction3) WithResolvedTypes(newTypes []*pgtypes.DoltgresType) any {
return &QuickFunction3{
Name: q.Name,
Arguments: q.Arguments,
IsStrict: q.IsStrict,
IsSRF: q.IsSRF,
callResolved: [4]*pgtypes.DoltgresType(newTypes),
function: q.function,
}
}
// Children implements the interface sql.Expression.
func (q *QuickFunction3) Children() []sql.Expression {
return q.Arguments[:]
}
// WithChildren implements the interface sql.Expression.
func (q *QuickFunction3) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
if len(children) != 3 {
return nil, sql.ErrInvalidChildrenNumber.New(q, len(children), 3)
}
if children[0].Type(ctx).Equals(q.Arguments[0].Type(ctx)) &&
children[1].Type(ctx).Equals(q.Arguments[1].Type(ctx)) &&
children[2].Type(ctx).Equals(q.Arguments[2].Type(ctx)) {
nq := *q
nq.Arguments = [3]sql.Expression{children[0], children[1], children[2]}
return &nq, nil
}
return nil, errors.Errorf("cannot change the types of children for `%T`", q)
}
// specificFuncImpl implements the interface sql.Expression.
func (*QuickFunction3) specificFuncImpl() {}