// 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 ( "github.com/cockroachdb/errors" "github.com/dolthub/doltgresql/core/id" ) // Operator is a unary or binary operator. type Operator byte const ( Operator_BinaryPlus Operator = iota // + Operator_BinaryMinus // - Operator_BinaryMultiply // * Operator_BinaryDivide // / Operator_BinaryMod // % Operator_BinaryShiftLeft // << Operator_BinaryShiftRight // >> Operator_BinaryLessThan // < Operator_BinaryGreaterThan // > Operator_BinaryLessOrEqual // <= Operator_BinaryGreaterOrEqual // >= Operator_BinaryEqual // = Operator_BinaryNotEqual // <> or != (they're equivalent in all cases) Operator_BinaryBitAnd // & Operator_BinaryBitOr // | Operator_BinaryBitXor // ^ Operator_BinaryConcatenate // || Operator_BinaryJSONExtractJson // -> Operator_BinaryJSONExtractText // ->> Operator_BinaryJSONExtractPathJson // #> Operator_BinaryJSONExtractPathText // #>> Operator_BinaryJSONContainsRight // @> Operator_BinaryJSONContainsLeft // <@ Operator_BinaryJSONTopLevel // ? Operator_BinaryJSONTopLevelAny // ?| Operator_BinaryJSONTopLevelAll // ?& Operator_UnaryPlus // + Operator_UnaryMinus // - // NOTE: Any new operator should also be added to Operator.String() and GetOperatorFromString() functions. ) // unaryFunction represents the signature for a unary function. type unaryFunction struct { Operator Operator TypeID id.Type } // binaryFunction represents the signature for a binary function. type binaryFunction struct { Operator Operator Left id.Type Right id.Type } var ( // unaryFunctions is a map from a unaryFunction signature to the associated function. unaryFunctions = map[unaryFunction]Function1{} // binaryFunctions is a map from a binaryFunction signature to the associated function. binaryFunctions = map[binaryFunction]Function2{} // unaryOperatorOverloads is a map from an operator to an Overload deducer that is the aggregate of all functions // for that operator. unaryOperatorOverloads = map[Operator]*Overloads{} // binaryOperatorOverloads is a map from an operator to an Overload deducer that is the aggregate of all functions // for that operator. binaryOperatorOverloads = map[Operator]*Overloads{} // unaryOperatorPermutations contains all of the permutations for each unary operator. unaryOperatorPermutations = map[Operator][]Overload{} // unaryOperatorPermutations contains all of the permutations for each binary operator. binaryOperatorPermutations = map[Operator][]Overload{} ) // RegisterUnaryFunction registers the given function, so that it will be usable from a running server. This should // only be used for unary functions, which are the underlying functions for unary operators such as negation, etc. This // should be called from within an init(). func RegisterUnaryFunction(operator Operator, f Function1) { if !operator.IsUnary() { panic("non-unary operator: " + operator.String()) } RegisterFunction(f) sig := unaryFunction{ Operator: operator, TypeID: f.Parameters[0].ID, } if existingFunction, ok := unaryFunctions[sig]; ok { panic(errors.Errorf("duplicate unary function for `%s`: `%s` and `%s`", operator.String(), existingFunction.Name, f.Name)) } unaryFunctions[sig] = f } // RegisterBinaryFunction registers the given function, so that it will be usable from a running server. This should // only be used for binary functions, which are the underlying functions for binary operators such as addition, // subtraction, etc. This should be called from within an init(). func RegisterBinaryFunction(operator Operator, f Function2) { if !operator.IsBinary() { panic("non-binary operator: " + operator.String()) } RegisterFunction(f) sig := binaryFunction{ Operator: operator, Left: f.Parameters[0].ID, Right: f.Parameters[1].ID, } if existingFunction, ok := binaryFunctions[sig]; ok { panic(errors.Errorf("duplicate binary function for `%s`: `%s` and `%s`", operator.String(), existingFunction.Name, f.Name)) } binaryFunctions[sig] = f } // GetUnaryFunction returns the unary function that matches the given operator. func GetUnaryFunction(operator Operator) IntermediateFunction { // Returns nil if not found, which is fine as IntermediateFunction will handle the nil deducer return IntermediateFunction{ Functions: unaryOperatorOverloads[operator], AllOverloads: unaryOperatorPermutations[operator], IsOperator: true, } } // GetBinaryFunction returns the binary function that matches the given operator. func GetBinaryFunction(operator Operator) IntermediateFunction { // Returns nil if not found, which is fine as IntermediateFunction will handle the nil deducer return IntermediateFunction{ Functions: binaryOperatorOverloads[operator], AllOverloads: binaryOperatorPermutations[operator], IsOperator: true, } } // String returns the string form of the operator. func (o Operator) String() string { switch o { case Operator_BinaryPlus, Operator_UnaryPlus: return "+" case Operator_BinaryMinus, Operator_UnaryMinus: return "-" case Operator_BinaryMultiply: return "*" case Operator_BinaryDivide: return "/" case Operator_BinaryMod: return "%" case Operator_BinaryShiftLeft: return "<<" case Operator_BinaryShiftRight: return ">>" case Operator_BinaryBitAnd: return "&" case Operator_BinaryBitOr: return "|" case Operator_BinaryBitXor: return "#" case Operator_BinaryConcatenate: return "||" case Operator_BinaryEqual: return "=" case Operator_BinaryNotEqual: return "<>" case Operator_BinaryGreaterThan: return ">" case Operator_BinaryGreaterOrEqual: return ">=" case Operator_BinaryLessThan: return "<" case Operator_BinaryLessOrEqual: return "<=" case Operator_BinaryJSONExtractJson: return "->" case Operator_BinaryJSONExtractText: return "->>" case Operator_BinaryJSONExtractPathJson: return "#>" case Operator_BinaryJSONExtractPathText: return "#>>" case Operator_BinaryJSONContainsRight: return "@>" case Operator_BinaryJSONContainsLeft: return "<@" case Operator_BinaryJSONTopLevel: return "?" case Operator_BinaryJSONTopLevelAny: return "?|" case Operator_BinaryJSONTopLevelAll: return "?&" default: return "unknown operator" } } // IsUnary returns whether the operator is a unary operator. func (o Operator) IsUnary() bool { switch o { case Operator_UnaryPlus, Operator_UnaryMinus: return true default: return false } } // IsBinary returns whether the operator is a binary operator. func (o Operator) IsBinary() bool { return !o.IsUnary() } // GetOperatorFromString returns the binary operator for the given subOperator. func GetOperatorFromString(op string) (Operator, error) { switch op { case "+": // TODO: Operator_UnaryPlus return Operator_BinaryPlus, nil case "-": // TODO: Operator_UnaryMinus return Operator_BinaryMinus, nil case "*": return Operator_BinaryMultiply, nil case "/": return Operator_BinaryDivide, nil case "%": return Operator_BinaryMod, nil case "<<": return Operator_BinaryShiftLeft, nil case ">>": return Operator_BinaryShiftRight, nil case "<": return Operator_BinaryLessThan, nil case ">": return Operator_BinaryGreaterThan, nil case "<=": return Operator_BinaryLessOrEqual, nil case ">=": return Operator_BinaryGreaterOrEqual, nil case "=": return Operator_BinaryEqual, nil case "<>": return Operator_BinaryNotEqual, nil case "&": return Operator_BinaryBitAnd, nil case "|": return Operator_BinaryBitOr, nil case "#": return Operator_BinaryBitXor, nil case "||": return Operator_BinaryConcatenate, nil case "->": return Operator_BinaryJSONExtractJson, nil case "->>": return Operator_BinaryJSONExtractText, nil case "#>": return Operator_BinaryJSONExtractPathJson, nil case "#>>": return Operator_BinaryJSONExtractPathText, nil case "@>": return Operator_BinaryJSONContainsRight, nil case "<@": return Operator_BinaryJSONContainsLeft, nil case "?": return Operator_BinaryJSONTopLevel, nil case "?|": return Operator_BinaryJSONTopLevelAny, nil case "?&": return Operator_BinaryJSONTopLevelAll, nil default: return 0, errors.Errorf("unhandled Operator `%s`", op) } }