github.com/dolthub/go-mysql-server@v0.18.0/sql/external_stored_procedure_registry.go (about) 1 // Copyright 2022 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sql 16 17 import ( 18 "math" 19 "reflect" 20 "strings" 21 ) 22 23 // ExternalStoredProcedureRegistry manages a collection of ExternalStoredProcedures and encapsulates 24 // the logic for looking up external stored procedures based on name and number of arguments. 25 type ExternalStoredProcedureRegistry struct { 26 procedures map[string]map[int]ExternalStoredProcedureDetails 27 } 28 29 // NewExternalStoredProcedureRegistry creates a new, empty instance of ExternalStoredProcedureRegistry. 30 func NewExternalStoredProcedureRegistry() ExternalStoredProcedureRegistry { 31 return ExternalStoredProcedureRegistry{ 32 procedures: make(map[string]map[int]ExternalStoredProcedureDetails), 33 } 34 } 35 36 // Register adds an external stored procedure to this registry. 37 func (epd *ExternalStoredProcedureRegistry) Register(procedureDetails ExternalStoredProcedureDetails) { 38 numOfParams := epd.countNumberOfParams(procedureDetails) 39 40 if _, ok := epd.procedures[procedureDetails.Name]; !ok { 41 epd.procedures[procedureDetails.Name] = make(map[int]ExternalStoredProcedureDetails) 42 } 43 epd.procedures[procedureDetails.Name][numOfParams] = procedureDetails 44 } 45 46 // LookupByName returns all stored procedure variants registered with the specified name, no matter 47 // how many parameters they require. If no external stored procedures are registered with the specified 48 // name, nil is returned, with no error. If an unexpected error occurs, it is returned as the error 49 // parameter. 50 func (epd *ExternalStoredProcedureRegistry) LookupByName(name string) ([]ExternalStoredProcedureDetails, error) { 51 procedureVariants, ok := epd.procedures[strings.ToLower(name)] 52 if !ok { 53 return nil, nil 54 } 55 56 procedures := make([]ExternalStoredProcedureDetails, 0, len(procedureVariants)) 57 for _, procedure := range procedureVariants { 58 procedures = append(procedures, procedure) 59 } 60 return procedures, nil 61 } 62 63 // LookupByNameAndParamCount returns the external stored procedure registered with the specified name 64 // and able to accept the specified number of parameters. If no external stored procedures are 65 // registered with the specified name and able to accept the specified number of parameters, nil 66 // is returned with no error. If an unexpected error occurs, it is returned as the error param. 67 func (epd *ExternalStoredProcedureRegistry) LookupByNameAndParamCount(name string, numOfParams int) (*ExternalStoredProcedureDetails, error) { 68 procedureVariants, ok := epd.procedures[strings.ToLower(name)] 69 if !ok { 70 return nil, nil 71 } 72 73 // If we find an exact match on param count, return that stored procedure 74 procedure, ok := procedureVariants[numOfParams] 75 if ok { 76 return &procedure, nil 77 } 78 79 // Otherwise, find the largest param length and return that stored procedure 80 var largestParamLen int 81 var largestParamProc ExternalStoredProcedureDetails 82 for paramLen, procedure := range procedureVariants { 83 if largestParamLen < paramLen { 84 largestParamProc = procedure 85 largestParamLen = paramLen 86 } 87 } 88 return &largestParamProc, nil 89 } 90 91 // countNumberOfParams returns the number of parameters accepted by the specified external stored 92 // procedure, including allowing variadic return types to expand to accept at most MaxInt parameters. 93 func (epd *ExternalStoredProcedureRegistry) countNumberOfParams(externalProcedure ExternalStoredProcedureDetails) int { 94 funcVal := reflect.ValueOf(externalProcedure.Function) 95 funcType := funcVal.Type() 96 97 // Return MaxInt for variadic types, since they can accommodate any number of params 98 if funcVal.Type().IsVariadic() { 99 return math.MaxInt 100 } 101 102 // We subtract one because ctx is required to always be the first parameter to a function, but 103 // customers won't actually pass that in to the stored procedure. 104 return funcType.NumIn() - 1 105 }