chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
// Copyright 2025 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 compare
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// CompareRecords compares two record values using the specified comparison operator, |op| and returns a result
|
||||
// indicating if the comparison was true, false, or indeterminate (nil).
|
||||
//
|
||||
// More info on rules for comparing records:
|
||||
// https://www.postgresql.org/docs/current/functions-comparisons.html#ROW-WISE-COMPARISON
|
||||
func CompareRecords(ctx *sql.Context, op framework.Operator, v1 interface{}, v2 interface{}) (res any, err error) {
|
||||
leftRecord, rightRecord, err := checkRecordArgs(v1, v2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: This can be a hot path when filtering a large table against a tuple, for example.
|
||||
// We can reduce branching by splitting each case into their individual functions, which will also make the code
|
||||
// more readable. A downside would be a ton of repeated code.
|
||||
var hasNull bool
|
||||
var leftLiteral, rightLiteral expression.Literal
|
||||
for i := 0; i < len(leftRecord); i++ {
|
||||
// NULL values are by definition not comparable
|
||||
if leftRecord[i].Value == nil || rightRecord[i].Value == nil {
|
||||
switch op {
|
||||
case framework.Operator_BinaryEqual, framework.Operator_BinaryNotEqual:
|
||||
hasNull = true
|
||||
continue
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
leftLiteral.Val = leftRecord[i].Value
|
||||
leftLiteral.Typ = leftRecord[i].Type
|
||||
rightLiteral.Val = rightRecord[i].Value
|
||||
rightLiteral.Typ = rightRecord[i].Type
|
||||
|
||||
switch op {
|
||||
case framework.Operator_BinaryEqual:
|
||||
res, err = callComparisonFunction(ctx, framework.Operator_BinaryEqual, &leftLiteral, &rightLiteral)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if res == false {
|
||||
return false, nil
|
||||
}
|
||||
case framework.Operator_BinaryNotEqual:
|
||||
res, err = callComparisonFunction(ctx, framework.Operator_BinaryNotEqual, &leftLiteral, &rightLiteral)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if res == true {
|
||||
return true, nil
|
||||
}
|
||||
// Records are compared by evaluating each field in order of significance, so if the fields are equal, we need
|
||||
// to compare the next field.
|
||||
case framework.Operator_BinaryLessThan, framework.Operator_BinaryLessOrEqual,
|
||||
framework.Operator_BinaryGreaterThan, framework.Operator_BinaryGreaterOrEqual:
|
||||
switch op {
|
||||
case framework.Operator_BinaryLessThan, framework.Operator_BinaryLessOrEqual:
|
||||
res, err = callComparisonFunction(ctx, framework.Operator_BinaryLessThan, &leftLiteral, &rightLiteral)
|
||||
default:
|
||||
res, err = callComparisonFunction(ctx, framework.Operator_BinaryGreaterThan, &leftLiteral, &rightLiteral)
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if res == true {
|
||||
return true, nil
|
||||
}
|
||||
// If equals fields are equal, move onto the next field, else return false
|
||||
res, err = callComparisonFunction(ctx, framework.Operator_BinaryEqual, &leftLiteral, &rightLiteral)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if res == false {
|
||||
return false, nil
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("unsupported binary operator: %s", op)
|
||||
}
|
||||
}
|
||||
|
||||
if hasNull {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Every field is equal
|
||||
switch op {
|
||||
case framework.Operator_BinaryEqual, framework.Operator_BinaryLessOrEqual, framework.Operator_BinaryGreaterOrEqual:
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// checkRecordArgs asserts that |v1| and |v2| are both []pgtypes.RecordValue, and that they have the same number of
|
||||
// elements, then returns them. If any problems were detected, an error is returned instead.
|
||||
func checkRecordArgs(v1, v2 interface{}) (leftRecord, rightRecord []pgtypes.RecordValue, err error) {
|
||||
var ok bool
|
||||
leftRecord, ok = v1.([]pgtypes.RecordValue)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("expected a RecordValue, but got %T", v1)
|
||||
}
|
||||
rightRecord, ok = v2.([]pgtypes.RecordValue)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("expected a RecordValue, but got %T", v2)
|
||||
}
|
||||
if len(leftRecord) != len(rightRecord) {
|
||||
return nil, nil, fmt.Errorf("unequal number of entries in row expressions")
|
||||
}
|
||||
|
||||
return leftRecord, rightRecord, nil
|
||||
}
|
||||
|
||||
// callComparisonFunction invokes the binary comparison function for the specified operator |op| with the two arguments
|
||||
// |leftLiteral| and |rightLiteral|. The result and any error are returned.
|
||||
func callComparisonFunction(ctx *sql.Context, op framework.Operator, leftLiteral, rightLiteral sql.Expression) (result any, err error) {
|
||||
intermediateFunction := framework.GetBinaryFunction(op)
|
||||
compiledFunction := intermediateFunction.Compile(
|
||||
ctx, "_internal_record_comparison_function", leftLiteral, rightLiteral)
|
||||
return compiledFunction.Eval(ctx, nil)
|
||||
}
|
||||
Reference in New Issue
Block a user