// 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 }