466 lines
15 KiB
Go
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() {}
|