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

214 lines
8.0 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 (
"strings"
"github.com/cockroachdb/errors"
"github.com/dolthub/doltgresql/core/casts"
pgtypes "github.com/dolthub/doltgresql/server/types"
)
// Overloads is the collection of all overloads for a given function name.
type Overloads struct {
// ByParamType contains all overloads for the function with this name, indexed by the key of the parameter types.
ByParamType map[string]FunctionInterface
// AllOverloads contains all overloads for the function with this name
AllOverloads []FunctionInterface
}
// NewOverloads creates a new empty overload collection.
func NewOverloads() *Overloads {
return &Overloads{
ByParamType: make(map[string]FunctionInterface),
AllOverloads: make([]FunctionInterface, 0),
}
}
// Add adds the given function to the overload collection. Returns an error if the there's a problem with the
// function's declaration.
func (o *Overloads) Add(function FunctionInterface) error {
key := keyForParamTypes(function.GetParameters())
if _, ok := o.ByParamType[key]; ok {
return errors.Errorf("duplicate function overload for `%s`", function.GetName())
}
if function.VariadicIndex() >= 0 {
varArgsType := function.GetParameters()[function.VariadicIndex()]
if !varArgsType.IsArrayType() {
return errors.Errorf("variadic parameter must be an array type for function `%s`", function.GetName())
}
}
o.ByParamType[key] = function
o.AllOverloads = append(o.AllOverloads, function)
return nil
}
// keyForParamTypes returns a string key to match an overload with the given parameter types.
func keyForParamTypes(types []*pgtypes.DoltgresType) string {
sb := strings.Builder{}
for i, typ := range types {
if i > 0 {
sb.WriteByte(',')
}
sb.WriteString(typ.String())
}
return sb.String()
}
// overloadsForParams returns all overloads matching the number of params given, without regard for types.
func (o *Overloads) overloadsForParams(numParams int) []Overload {
results := make([]Overload, 0, len(o.AllOverloads))
for _, overload := range o.AllOverloads {
params := overload.GetParameters()
variadicIndex := overload.VariadicIndex()
if overload.IsCVariadic() {
if len(params) <= numParams {
paramTypes := make([]*pgtypes.DoltgresType, numParams)
argTypes := make([]*pgtypes.DoltgresType, numParams)
copy(paramTypes, params)
copy(argTypes, params)
for i := len(params); i < numParams; i++ {
paramTypes[i] = pgtypes.Any
argTypes[i] = pgtypes.Any
}
results = append(results, Overload{
function: overload,
paramTypes: paramTypes,
argTypes: argTypes,
variadic: -1,
})
}
} else if variadicIndex >= 0 && len(params) <= numParams {
// Variadic functions may only match when the function is declared with parameters that are fewer or equal
// to our target length. If our target length is less, then we cannot expand, so we do not treat it as
// variadic.
extendedParams := make([]*pgtypes.DoltgresType, numParams)
copy(extendedParams, params[:variadicIndex])
// This is copying the parameters after the variadic index, so we need to add 1. We subtract the declared
// parameter count from the target parameter count to obtain the additional parameter count.
firstValueAfterVariadic := variadicIndex + 1 + (numParams - len(params))
copy(extendedParams[firstValueAfterVariadic:], params[variadicIndex+1:])
paramType := overload.GetParameters()[variadicIndex]
var variadicBaseType *pgtypes.DoltgresType
// special case: anyArray takes any args, pass as is
if paramType == pgtypes.AnyArray {
for variadicParamIdx := 0; variadicParamIdx < 1+(numParams-len(params)); variadicParamIdx++ {
variadicBaseType = pgtypes.AnyElement
}
} else {
// ToArrayType immediately followed by BaseType is a way to get the base type without having to cast.
// For array types, ToArrayType causes them to return themselves.
variadicBaseType = paramType.ToArrayType().ArrayBaseType()
}
for variadicParamIdx := 0; variadicParamIdx < 1+(numParams-len(params)); variadicParamIdx++ {
extendedParams[variadicParamIdx+variadicIndex] = variadicBaseType
}
results = append(results, Overload{
function: overload,
paramTypes: params,
argTypes: extendedParams,
variadic: variadicIndex,
})
} else if len(params) == numParams {
results = append(results, Overload{
function: overload,
paramTypes: params,
argTypes: params,
variadic: -1,
})
} else if sqlFunc, ok := overload.(SQLFunction); ok && len(params) > numParams {
// if it's SQL function and has fewer arguments than parameters, check for default values.
restIsDefaultValues := true
for _, d := range sqlFunc.ParameterDefaults[numParams:] {
if d == "" {
restIsDefaultValues = false
}
}
if restIsDefaultValues {
results = append(results, Overload{
function: overload,
paramTypes: params,
argTypes: params,
variadic: -1,
})
}
}
}
return results
}
// ExactMatchForTypes returns the function that exactly matches the given parameter types, or nil if no overload with
// those types exists.
func (o *Overloads) ExactMatchForTypes(types ...*pgtypes.DoltgresType) (FunctionInterface, bool) {
key := keyForParamTypes(types)
fn, ok := o.ByParamType[key]
return fn, ok
}
// Overload is a single overload of a given function, used during evaluation to match the arguments provided
// to a particular overload.
type Overload struct {
// function is the actual function to call to invoke this overload
function FunctionInterface
// paramTypes is the base IDs of the parameters that the function expects
paramTypes []*pgtypes.DoltgresType
// argTypes is the base IDs of the parameters that the function expects, extended to match the number of args
// provided in the case of a variadic function.
argTypes []*pgtypes.DoltgresType
// variadic is the index of the variadic parameter, or -1 if the function is not variadic
variadic int
}
// coalesceVariadicValues returns a new value set that coalesces all variadic parameters into an array parameter
func (o *Overload) coalesceVariadicValues(returnValues []any) []any {
// If the overload is not variadic, then we don't need to do anything
if o.variadic < 0 {
return returnValues
}
coalescedValues := make([]any, len(o.paramTypes))
copy(coalescedValues, returnValues[:o.variadic])
// This is for the values after the variadic index, so we need to add 1. We subtract the extended parameter count
// from the actual parameter count to obtain the additional parameter count.
firstValueAfterVariadic := o.variadic + 1 + (len(o.argTypes) - len(o.paramTypes))
copy(coalescedValues[o.variadic+1:], returnValues[firstValueAfterVariadic:])
// We can just take the relevant slice out of the given values to represent our array, since all arrays use []any
coalescedValues[o.variadic] = returnValues[o.variadic:firstValueAfterVariadic]
return coalescedValues
}
// overloadMatch is the result of a successful overload resolution, containing the types of the parameters as well
// as the type cast functions required to convert every argument to its appropriate parameter type
type overloadMatch struct {
params Overload
casts []casts.Cast
}
// Valid returns whether this overload is valid (has a callable function)
func (o overloadMatch) Valid() bool {
return o.params.function != nil
}
// Function returns the function for this overload
func (o overloadMatch) Function() FunctionInterface {
return o.params.function
}