chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initAbs registers the functions to the catalog.
|
||||
func initAbs() {
|
||||
framework.RegisterFunction(abs_int16)
|
||||
framework.RegisterFunction(abs_int32)
|
||||
framework.RegisterFunction(abs_int64)
|
||||
framework.RegisterFunction(abs_float64)
|
||||
framework.RegisterFunction(abs_numeric)
|
||||
}
|
||||
|
||||
// abs_int16 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var abs_int16 = framework.Function1{
|
||||
Name: "abs",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return utils.Abs(val1.(int16)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// abs_int32 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var abs_int32 = framework.Function1{
|
||||
Name: "abs",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return utils.Abs(val1.(int32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// abs_int64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var abs_int64 = framework.Function1{
|
||||
Name: "abs",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return utils.Abs(val1.(int64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// abs_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var abs_float64 = framework.Function1{
|
||||
Name: "abs",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return utils.Abs(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// abs_numeric represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var abs_numeric = framework.Function1{
|
||||
Name: "abs",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
dec := val1.(*apd.Decimal)
|
||||
res := new(apd.Decimal)
|
||||
return res.Abs(dec), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAcos registers the functions to the catalog.
|
||||
func initAcos() {
|
||||
framework.RegisterFunction(acos_float64)
|
||||
}
|
||||
|
||||
// acos_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var acos_float64 = framework.Function1{
|
||||
Name: "acos",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Acos(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAcosd registers the functions to the catalog.
|
||||
func initAcosd() {
|
||||
framework.RegisterFunction(acosd_float64)
|
||||
}
|
||||
|
||||
// acosd_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var acosd_float64 = framework.Function1{
|
||||
Name: "acosd",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Acos(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return toDegrees(r), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAcosh registers the functions to the catalog.
|
||||
func initAcosh() {
|
||||
framework.RegisterFunction(acosh_float64)
|
||||
}
|
||||
|
||||
// acosh_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var acosh_float64 = framework.Function1{
|
||||
Name: "acosh",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Acosh(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqlserver"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAdvisoryLockFunctions registers the advisory lock functions to the catalog.
|
||||
func initAdvisoryLockFunctions() {
|
||||
framework.RegisterFunction(pg_advisory_lock_bigint)
|
||||
framework.RegisterFunction(pg_advisory_unlock_bigint)
|
||||
framework.RegisterFunction(pg_try_advisory_lock_bigint)
|
||||
}
|
||||
|
||||
// pg_advisory_lock_bigint represents the pg_advisory_lock(bigint) function.
|
||||
// https://www.postgresql.org/docs/9.1/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
|
||||
var pg_advisory_lock_bigint = framework.Function1{
|
||||
Name: "pg_advisory_lock",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
lockNumericId := val1.(int64)
|
||||
lockName := fmt.Sprintf("%v", lockNumericId)
|
||||
|
||||
lockSubsystem := getLockSubsystem()
|
||||
if lockSubsystem == nil {
|
||||
return false, errors.Errorf("lock subsystem not available")
|
||||
}
|
||||
|
||||
// TODO: Postgres supports reentrant locks, meaning if pg_advisory_lock(123) is called multiple times,
|
||||
// pg_advisory_unlock(123) must be called the same number of times to fully release a lock. This
|
||||
// is different from MySQL's locking behavior, so LockSubsystem should be updated to support
|
||||
// a reentrant mode in addition to the current mode.
|
||||
err := lockSubsystem.Lock(ctx, lockName, time.Millisecond*-1)
|
||||
return err == nil, err
|
||||
},
|
||||
}
|
||||
|
||||
// pg_try_advisory_lock_bigint represents the pg_try_advisory_lock(bigint) function.
|
||||
// https://www.postgresql.org/docs/9.1/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
|
||||
var pg_try_advisory_lock_bigint = framework.Function1{
|
||||
Name: "pg_try_advisory_lock",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
lockNumericId := val1.(int64)
|
||||
lockName := fmt.Sprintf("%v", lockNumericId)
|
||||
|
||||
lockSubsystem := getLockSubsystem()
|
||||
if lockSubsystem == nil {
|
||||
return false, errors.Errorf("lock subsystem not available")
|
||||
}
|
||||
|
||||
// TODO: We currently need to specify a timeout, but it may be a better mapping to
|
||||
// this function if we had a lockSubsystem.TryLock function that would try
|
||||
// to grab the lock once and then return immediately. Until then, we set a
|
||||
// short timeout and translate any timeout errors into a false return value.
|
||||
err := lockSubsystem.Lock(ctx, lockName, time.Millisecond*1)
|
||||
if sql.ErrLockTimeout.Is(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return err == nil, err
|
||||
},
|
||||
}
|
||||
|
||||
// pg_advisory_unlock_bigint represents the pg_advisory_unlock(bigint) function.
|
||||
// https://www.postgresql.org/docs/9.1/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
|
||||
var pg_advisory_unlock_bigint = framework.Function1{
|
||||
Name: "pg_advisory_unlock",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int64},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
lockNumericId := val1.(int64)
|
||||
lockName := fmt.Sprintf("%v", lockNumericId)
|
||||
|
||||
lockSubsystem := getLockSubsystem()
|
||||
if lockSubsystem == nil {
|
||||
return false, errors.Errorf("lock subsystem not available")
|
||||
}
|
||||
|
||||
err := lockSubsystem.Unlock(ctx, lockName)
|
||||
if sql.ErrLockDoesNotExist.Is(err) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return err == nil, err
|
||||
},
|
||||
}
|
||||
|
||||
// getLockSubsystem returns the active lock system for the SQL engine.
|
||||
func getLockSubsystem() *sql.LockSubsystem {
|
||||
engine := sqlserver.GetRunningServer().Engine
|
||||
// This should be impossible if the server was initialized correctly, but for some test harnesses we
|
||||
// take shortcuts that might invalidate that assumption
|
||||
if engine == nil {
|
||||
return nil
|
||||
}
|
||||
return engine.LS
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAge registers the functions to the catalog.
|
||||
func initAge() {
|
||||
framework.RegisterFunction(age_timestamp_timestamp)
|
||||
framework.RegisterFunction(age_timestamp)
|
||||
}
|
||||
|
||||
// age_timestamp_timestamp represents the PostgreSQL date/time function.
|
||||
var age_timestamp_timestamp = framework.Function2{
|
||||
Name: "age",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
t1 := val1.(time.Time)
|
||||
t2 := val2.(time.Time)
|
||||
return diffTimes(t1, t2), nil
|
||||
},
|
||||
}
|
||||
|
||||
// age_timestamp_timestamp represents the PostgreSQL date/time function.
|
||||
var age_timestamp = framework.Function1{
|
||||
Name: "age",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Timestamp},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
t := val.(time.Time)
|
||||
// current_date (at midnight)
|
||||
cur, err := time.Parse("2006-01-02", time.Now().Format("2006-01-02"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return diffTimes(cur, t), nil
|
||||
},
|
||||
}
|
||||
|
||||
// diffTimes returns the duration t1-t2. It subtracts each time component separately,
|
||||
// unlike time.Sub() function.
|
||||
func diffTimes(t1, t2 time.Time) duration.Duration {
|
||||
// if t1 is before t2, then negate the result.
|
||||
negate := t1.Before(t2)
|
||||
if negate {
|
||||
t1, t2 = t2, t1
|
||||
}
|
||||
|
||||
// Calculate difference in each unit
|
||||
years := int64(t1.Year() - t2.Year())
|
||||
months := int64(t1.Month() - t2.Month())
|
||||
days := int64(t1.Day() - t2.Day())
|
||||
hours := int64(t1.Hour() - t2.Hour())
|
||||
minutes := int64(t1.Minute() - t2.Minute())
|
||||
seconds := int64(t1.Second() - t2.Second())
|
||||
nanoseconds := int64(t1.Nanosecond() - t2.Nanosecond())
|
||||
|
||||
// Adjust for any negative values
|
||||
if nanoseconds < 0 {
|
||||
nanoseconds += 1e9
|
||||
seconds--
|
||||
}
|
||||
if seconds < 0 {
|
||||
seconds += 60
|
||||
minutes--
|
||||
}
|
||||
if minutes < 0 {
|
||||
minutes += 60
|
||||
hours--
|
||||
}
|
||||
if hours < 0 {
|
||||
hours += 24
|
||||
days--
|
||||
}
|
||||
if days < 0 {
|
||||
days += 30
|
||||
months--
|
||||
}
|
||||
if months < 0 {
|
||||
months += 12
|
||||
years--
|
||||
}
|
||||
|
||||
dur := GetIntervalDurationFromTimeComponents(years, months, days, hours, minutes, seconds, nanoseconds)
|
||||
if negate {
|
||||
return dur.Mul(-1)
|
||||
}
|
||||
return dur
|
||||
}
|
||||
|
||||
func GetIntervalDurationFromTimeComponents(years, months, days, hours, minutes, seconds, nanos int64) duration.Duration {
|
||||
durNanos := nanos + seconds*NanosPerSec + minutes*NanosPerSec*duration.SecsPerMinute + hours*NanosPerSec*duration.SecsPerHour
|
||||
durDays := days
|
||||
durMonths := months + years*duration.MonthsPerYear
|
||||
|
||||
return duration.MakeDuration(durNanos, durDays, durMonths)
|
||||
}
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// 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 aggregate
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initBoolAggs registers the functions to the catalog.
|
||||
func initBoolAggs() {
|
||||
framework.RegisterAggregateFunction(boolAnd)
|
||||
framework.RegisterAggregateFunction(boolOr)
|
||||
}
|
||||
|
||||
// boolAnd represents the PostgreSQL bool_and function.
|
||||
var boolAnd = framework.Func1Aggregate{
|
||||
Function1: framework.Function1{
|
||||
Name: "bool_and",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{
|
||||
pgtypes.Bool,
|
||||
},
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
NewAggBuffer: newBoolAndBuffer,
|
||||
}
|
||||
|
||||
// boolOr represents the PostgreSQL bool_or function.
|
||||
var boolOr = framework.Func1Aggregate{
|
||||
Function1: framework.Function1{
|
||||
Name: "bool_or",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{
|
||||
pgtypes.Bool,
|
||||
},
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return nil, nil
|
||||
},
|
||||
},
|
||||
NewAggBuffer: newBoolOrBuffer,
|
||||
}
|
||||
|
||||
// boolAggBuffer is an aggregation buffer for boolean logic aggregates
|
||||
type boolAggBuffer struct {
|
||||
expr sql.Expression
|
||||
b bool
|
||||
sawOne bool
|
||||
isAnd bool
|
||||
}
|
||||
|
||||
var _ sql.AggregationBuffer = (*boolAggBuffer)(nil)
|
||||
|
||||
// newBoolAndBuffer creates a new aggregation buffer for the bool_and aggregate function.
|
||||
func newBoolAndBuffer(exprs []sql.Expression) (sql.AggregationBuffer, error) {
|
||||
return &boolAggBuffer{
|
||||
expr: exprs[0],
|
||||
b: true,
|
||||
isAnd: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// newBoolOrBuffer creates a new aggregation buffer for the bool_or aggregate function.
|
||||
func newBoolOrBuffer(exprs []sql.Expression) (sql.AggregationBuffer, error) {
|
||||
return &boolAggBuffer{
|
||||
expr: exprs[0],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Dispose implements the sql.AggregationBuffer interface.
|
||||
func (a *boolAggBuffer) Dispose(ctx *sql.Context) {}
|
||||
|
||||
// Eval implements the sql.AggregationBuffer interface.
|
||||
func (a *boolAggBuffer) Eval(context *sql.Context) (interface{}, error) {
|
||||
if !a.sawOne {
|
||||
return nil, nil
|
||||
}
|
||||
return a.b, nil
|
||||
}
|
||||
|
||||
// Update implements the sql.AggregationBuffer interface.
|
||||
func (a *boolAggBuffer) Update(ctx *sql.Context, row sql.Row) error {
|
||||
eval, err := a.expr.Eval(ctx, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if eval == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
a.sawOne = true
|
||||
if a.isAnd {
|
||||
a.b = a.b && eval.(bool)
|
||||
} else {
|
||||
a.b = a.b || eval.(bool)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
// 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 aggregate
|
||||
|
||||
func Init() {
|
||||
initBoolAggs()
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAny registers the functions to the catalog.
|
||||
func initAny() {
|
||||
framework.RegisterFunction(any_in)
|
||||
framework.RegisterFunction(any_out)
|
||||
}
|
||||
|
||||
// any_in represents the PostgreSQL function of any type IO input.
|
||||
var any_in = framework.Function1{
|
||||
Name: "any_in",
|
||||
Return: pgtypes.Any,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
// any_out represents the PostgreSQL function of any type IO output.
|
||||
var any_out = framework.Function1{
|
||||
Name: "any_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Any},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return "", nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAnyArray registers the functions to the catalog.
|
||||
func initAnyArray() {
|
||||
framework.RegisterFunction(anyarray_in)
|
||||
framework.RegisterFunction(anyarray_out)
|
||||
framework.RegisterFunction(anyarray_recv)
|
||||
framework.RegisterFunction(anyarray_send)
|
||||
}
|
||||
|
||||
// anyarray_in represents the PostgreSQL function of anyarray type IO input.
|
||||
var anyarray_in = framework.Function1{
|
||||
Name: "anyarray_in",
|
||||
Return: pgtypes.AnyArray,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return []any{}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// anyarray_out represents the PostgreSQL function of anyarray type IO output.
|
||||
var anyarray_out = framework.Function1{
|
||||
Name: "anyarray_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return "", nil
|
||||
},
|
||||
}
|
||||
|
||||
// anyarray_recv represents the PostgreSQL function of anyarray type IO receive.
|
||||
var anyarray_recv = framework.Function1{
|
||||
Name: "anyarray_recv",
|
||||
Return: pgtypes.AnyArray,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return []any{}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// anyarray_send represents the PostgreSQL function of anyarray type IO send.
|
||||
var anyarray_send = framework.Function1{
|
||||
Name: "anyarray_send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return []byte{}, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAnyElement registers the functions to the catalog.
|
||||
func initAnyElement() {
|
||||
framework.RegisterFunction(anyelement_in)
|
||||
framework.RegisterFunction(anyelement_out)
|
||||
}
|
||||
|
||||
// anyelement_in represents the PostgreSQL function of anyelement type IO input.
|
||||
var anyelement_in = framework.Function1{
|
||||
Name: "anyelement_in",
|
||||
Return: pgtypes.AnyElement,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO: need underlying type information
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
// anyelement_out represents the PostgreSQL function of anyelement type IO output.
|
||||
var anyelement_out = framework.Function1{
|
||||
Name: "anyelement_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyElement},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO: need underlying type information
|
||||
return "", nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAnyEnum registers the functions to the catalog.
|
||||
func initAnyEnum() {
|
||||
framework.RegisterFunction(anyenum_in)
|
||||
framework.RegisterFunction(anyenum_out)
|
||||
}
|
||||
|
||||
// anyenum_in represents the PostgreSQL function of anyenum type IO input.
|
||||
var anyenum_in = framework.Function1{
|
||||
Name: "anyenum_in",
|
||||
Return: pgtypes.AnyEnum,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return val, nil
|
||||
},
|
||||
}
|
||||
|
||||
// anyenum_out represents the PostgreSQL function of anyenum type IO output.
|
||||
var anyenum_out = framework.Function1{
|
||||
Name: "anyenum_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return val, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAnyNonArray registers the functions to the catalog.
|
||||
func initAnyNonArray() {
|
||||
framework.RegisterFunction(anynonarray_in)
|
||||
framework.RegisterFunction(anynonarray_out)
|
||||
}
|
||||
|
||||
// anynonarray_in represents the PostgreSQL function of anynonarray type IO input.
|
||||
var anynonarray_in = framework.Function1{
|
||||
Name: "anynonarray_in",
|
||||
Return: pgtypes.AnyNonArray,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
// anynonarray_out represents the PostgreSQL function of anynonarray type IO output.
|
||||
var anynonarray_out = framework.Function1{
|
||||
Name: "anynonarray_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyNonArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return "", nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initArray registers the functions to the catalog.
|
||||
func initArray() {
|
||||
framework.RegisterFunction(array_in)
|
||||
framework.RegisterFunction(array_out)
|
||||
framework.RegisterFunction(array_recv)
|
||||
framework.RegisterFunction(array_send)
|
||||
framework.RegisterFunction(btarraycmp)
|
||||
framework.RegisterFunction(array_subscript_handler)
|
||||
}
|
||||
|
||||
// array_in represents the PostgreSQL function of array type IO input.
|
||||
var array_in = framework.Function3{
|
||||
Name: "array_in",
|
||||
Return: pgtypes.AnyArray,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Cstring, pgtypes.Oid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
input := val1.(string)
|
||||
baseTypeOid := val2.(id.Id)
|
||||
baseType := pgtypes.IDToBuiltInDoltgresType[id.Type(baseTypeOid)]
|
||||
if baseType == nil {
|
||||
if typColl, err := pgtypes.GetTypesCollectionFromContext(ctx, ""); err == nil && typColl != nil {
|
||||
if t, err := typColl.GetType(ctx, id.Type(baseTypeOid)); err == nil {
|
||||
baseType = t
|
||||
}
|
||||
}
|
||||
}
|
||||
if baseType == nil {
|
||||
return nil, errors.Errorf("unknown array element type: %s", string(baseTypeOid))
|
||||
}
|
||||
typmod := val3.(int32)
|
||||
baseType = baseType.WithAttTypMod(typmod)
|
||||
if len(input) < 2 || input[0] != '{' || input[len(input)-1] != '}' {
|
||||
// This error is regarded as a critical error, and thus we immediately return the error alongside a nil
|
||||
// value. Returning a nil value is a signal to not ignore the error.
|
||||
return nil, errors.Errorf(`malformed array literal: "%s"`, input)
|
||||
}
|
||||
// We'll remove the surrounding braces since we've already verified that they're there
|
||||
input = input[1 : len(input)-1]
|
||||
var values []any
|
||||
var err error
|
||||
sb := strings.Builder{}
|
||||
quoteStartCount := 0
|
||||
quoteEndCount := 0
|
||||
escaped := false
|
||||
// Iterate over each rune in the input to collect and process the rune elements
|
||||
for _, r := range input {
|
||||
if escaped {
|
||||
sb.WriteRune(r)
|
||||
escaped = false
|
||||
} else if quoteStartCount > quoteEndCount {
|
||||
switch r {
|
||||
case '\\':
|
||||
escaped = true
|
||||
case '"':
|
||||
quoteEndCount++
|
||||
default:
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
} else {
|
||||
switch r {
|
||||
case ' ', '\t', '\n', '\r':
|
||||
continue
|
||||
case '\\':
|
||||
escaped = true
|
||||
case '"':
|
||||
quoteStartCount++
|
||||
case ',':
|
||||
if quoteStartCount >= 2 {
|
||||
// This is a malformed string, thus we treat it as a critical error.
|
||||
return nil, errors.Errorf(`malformed array literal: "%s"`, input)
|
||||
}
|
||||
str := sb.String()
|
||||
var innerValue any
|
||||
if quoteStartCount == 0 && strings.EqualFold(str, "null") {
|
||||
// An unquoted case-insensitive NULL is treated as an actual null value
|
||||
innerValue = nil
|
||||
} else {
|
||||
var nErr error
|
||||
innerValue, nErr = baseType.IoInput(ctx, str)
|
||||
if nErr != nil && err == nil {
|
||||
// This is a non-critical error, therefore the error may be ignored at a higher layer (such as
|
||||
// an explicit cast) and the inner type will still return a valid result, so we must allow the
|
||||
// values to propagate.
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
values = append(values, innerValue)
|
||||
sb.Reset()
|
||||
quoteStartCount = 0
|
||||
quoteEndCount = 0
|
||||
default:
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Use anything remaining in the buffer as the last element
|
||||
if sb.Len() > 0 {
|
||||
if escaped || quoteStartCount > quoteEndCount || quoteStartCount >= 2 {
|
||||
// These errors are regarded as critical errors, and thus we immediately return the error alongside a nil
|
||||
// value. Returning a nil value is a signal to not ignore the error.
|
||||
return nil, errors.Errorf(`malformed array literal: "%s"`, input)
|
||||
} else {
|
||||
str := sb.String()
|
||||
var innerValue any
|
||||
if quoteStartCount == 0 && strings.EqualFold(str, "NULL") {
|
||||
// An unquoted case-insensitive NULL is treated as an actual null value
|
||||
innerValue = nil
|
||||
} else {
|
||||
var nErr error
|
||||
innerValue, nErr = baseType.IoInput(ctx, str)
|
||||
if nErr != nil && err == nil {
|
||||
// This is a non-critical error, therefore the error may be ignored at a higher layer (such as
|
||||
// an explicit cast) and the inner type will still return a valid result, so we must allow the
|
||||
// values to propagate.
|
||||
err = nErr
|
||||
}
|
||||
}
|
||||
values = append(values, innerValue)
|
||||
}
|
||||
}
|
||||
|
||||
return values, err
|
||||
},
|
||||
}
|
||||
|
||||
// array_out represents the PostgreSQL function of array type IO output.
|
||||
var array_out = framework.Function1{
|
||||
Name: "array_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
arrType := t[0]
|
||||
baseType := arrType.ArrayBaseType()
|
||||
return pgtypes.ArrToString(ctx, val.([]any), baseType, false)
|
||||
},
|
||||
}
|
||||
|
||||
// array_recv represents the PostgreSQL function of array type IO receive.
|
||||
var array_recv = framework.Function3{
|
||||
Name: "array_recv",
|
||||
Return: pgtypes.AnyArray,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Internal, pgtypes.Oid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: array_recv_callable,
|
||||
}
|
||||
|
||||
// array_recv_callable is the function definition of array_recv.
|
||||
func array_recv_callable(ctx *sql.Context, t [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
data := val1.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
typeColl, err := core.GetTypesCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader := utils.NewWireReader(data)
|
||||
dimensions := reader.ReadInt32()
|
||||
_ = reader.ReadInt32() // Whether the array has a null, doesn't seem useful
|
||||
baseTypeID := id.Type(id.Cache().ToInternal(reader.ReadUint32()))
|
||||
baseType, err := typeColl.GetType(ctx, baseTypeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if baseType == nil {
|
||||
return nil, pgtypes.ErrTypeDoesNotExist.New(baseTypeID.TypeName())
|
||||
}
|
||||
// TODO: handle more than 1 dimension
|
||||
if dimensions > 1 {
|
||||
return nil, errors.Errorf("array dimensions greater than 1 are not yet supported")
|
||||
}
|
||||
var vals []any
|
||||
for dimensionIdx := int32(0); dimensionIdx < dimensions; dimensionIdx++ {
|
||||
elementsCount := reader.ReadInt32()
|
||||
_ = reader.ReadInt32() // Lower bound, not sure what to do with this
|
||||
for i := int32(0); i < elementsCount; i++ {
|
||||
elementLen := reader.ReadInt32()
|
||||
if elementLen != -1 {
|
||||
valBytes := reader.ReadBytes(uint32(elementLen))
|
||||
val, err := baseType.CallReceive(ctx, valBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vals = append(vals, val)
|
||||
} else {
|
||||
vals = append(vals, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// array_send represents the PostgreSQL function of array type IO send.
|
||||
var array_send = framework.Function1{
|
||||
Name: "array_send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if wrapper, ok := val.(sql.AnyWrapper); ok {
|
||||
var err error
|
||||
val, err = wrapper.UnwrapAny(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
vals := val.([]any)
|
||||
// Check for nulls first
|
||||
hasNull := false
|
||||
for _, val := range vals {
|
||||
if val == nil {
|
||||
hasNull = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// Count the number of dimensions
|
||||
dimensions := int32(0)
|
||||
innerVals := vals
|
||||
for len(innerVals) > 0 {
|
||||
dimensions++
|
||||
slice, ok := innerVals[0].([]any)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
innerVals = slice
|
||||
}
|
||||
if dimensions > 1 {
|
||||
return nil, errors.Errorf("arrays with %d dimensions are not yet supported using the binary format", dimensions)
|
||||
}
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteInt32(dimensions) // Write the number of dimensions
|
||||
if hasNull {
|
||||
writer.WriteInt32(1)
|
||||
} else {
|
||||
writer.WriteInt32(0)
|
||||
}
|
||||
writer.WriteUint32(id.Cache().ToOID(t[0].BaseType().ID.AsId())) // Element OID
|
||||
for i := int32(0); i < dimensions; i++ {
|
||||
writer.WriteInt32(int32(len(vals))) // Elements in this dimension
|
||||
if t[0].IsArrayType() {
|
||||
writer.WriteInt32(1) // Lower bound, or what index number we start at (seems to always be 1?)
|
||||
} else {
|
||||
writer.WriteInt32(0)
|
||||
}
|
||||
for _, val := range vals {
|
||||
if val == nil {
|
||||
writer.WriteInt32(-1)
|
||||
} else {
|
||||
valBytes, err := t[0].BaseType().CallSend(ctx, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer.WriteInt32(int32(len(valBytes)))
|
||||
writer.WriteBytes(valBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// btarraycmp represents the PostgreSQL function of array type byte compare.
|
||||
var btarraycmp = framework.Function2{
|
||||
Name: "btarraycmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.AnyArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
at := t[0]
|
||||
bt := t[1]
|
||||
if !at.Equals(bt) {
|
||||
// TODO: currently, types should match.
|
||||
// Technically, does not have to e.g.: float4 vs float8
|
||||
return nil, errors.Errorf("different type comparison is not supported yet")
|
||||
}
|
||||
|
||||
ab := val1.([]any)
|
||||
bb := val2.([]any)
|
||||
minLength := utils.Min(len(ab), len(bb))
|
||||
for i := 0; i < minLength; i++ {
|
||||
res, err := at.ArrayBaseType().Compare(ctx, ab[i], bb[i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if res != 0 {
|
||||
return res, nil
|
||||
}
|
||||
}
|
||||
if len(ab) == len(bb) {
|
||||
return int32(0), nil
|
||||
} else if len(ab) < len(bb) {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// array_subscript_handler represents the PostgreSQL function of array type subscript handler.
|
||||
var array_subscript_handler = framework.Function1{
|
||||
Name: "array_subscript_handler",
|
||||
Return: pgtypes.Internal,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO
|
||||
return []byte{}, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initArrayLength registers the functions to the catalog.
|
||||
func initArrayLength() {
|
||||
framework.RegisterFunction(array_length_anyarray_int32)
|
||||
}
|
||||
|
||||
// array_length_anyarray_int32 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_length_anyarray_int32 = framework.Function2{
|
||||
Name: "array_length",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
array := val1.([]any)
|
||||
dimension := val2.(int32)
|
||||
|
||||
// PostgreSQL arrays are 1-dimensional in this implementation
|
||||
// Dimension 0 is invalid, dimensions > 1 return null for 1D arrays
|
||||
if dimension <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if dimension == 1 {
|
||||
if len(array) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return int32(len(array)), nil
|
||||
}
|
||||
|
||||
// For dimensions other than 1, return null (multi-dimensional arrays not fully supported)
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initArrayPosition registers the functions to the catalog.
|
||||
func initArrayPosition() {
|
||||
framework.RegisterFunction(array_position_anyarray_anyelement)
|
||||
framework.RegisterFunction(array_position_anyarray_anyelement_int32)
|
||||
framework.RegisterFunction(array_positions_anyarray_anyelement)
|
||||
}
|
||||
|
||||
// array_position_anyarray_anyelement represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_position_anyarray_anyelement = framework.Function2{
|
||||
Name: "array_position",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.AnyElement},
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val1 == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
array := val1.([]any)
|
||||
searchElement := val2
|
||||
arrayType := t[0]
|
||||
baseType := arrayType.ArrayBaseType()
|
||||
|
||||
// Search for the element starting from position 1 (1-indexed)
|
||||
for i, element := range array {
|
||||
cmp, err := baseType.Compare(ctx, element, searchElement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmp == 0 {
|
||||
return int32(i + 1), nil // PostgreSQL uses 1-indexed arrays
|
||||
}
|
||||
}
|
||||
|
||||
// Element not found
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
// array_position_anyarray_anyelement_int32 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_position_anyarray_anyelement_int32 = framework.Function3{
|
||||
Name: "array_position",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.AnyElement, pgtypes.Int32},
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, t [4]*pgtypes.DoltgresType, val1 any, val2 any, val3 any) (any, error) {
|
||||
if val1 == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
array := val1.([]any)
|
||||
searchElement := val2
|
||||
start := val3.(int32)
|
||||
arrayType := t[0]
|
||||
baseType := arrayType.ArrayBaseType()
|
||||
|
||||
// Convert 1-indexed start position to 0-indexed
|
||||
startIdx := int(start - 1)
|
||||
if startIdx < 0 {
|
||||
startIdx = 0
|
||||
}
|
||||
if startIdx >= len(array) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Search for the element starting from the specified position
|
||||
for i := startIdx; i < len(array); i++ {
|
||||
cmp, err := baseType.Compare(ctx, array[i], searchElement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmp == 0 {
|
||||
return int32(i + 1), nil // PostgreSQL uses 1-indexed arrays
|
||||
}
|
||||
}
|
||||
|
||||
// Element not found
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
|
||||
// array_positions_anyarray_anyelement represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_positions_anyarray_anyelement = framework.Function2{
|
||||
Name: "array_positions",
|
||||
Return: pgtypes.Int32Array,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.AnyElement},
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val1 == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
array := val1.([]any)
|
||||
searchElement := val2
|
||||
arrayType := t[0]
|
||||
baseType := arrayType.ArrayBaseType()
|
||||
var positions []any
|
||||
|
||||
// Search for all occurrences of the element
|
||||
for i, element := range array {
|
||||
cmp, err := baseType.Compare(ctx, element, searchElement)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmp == 0 {
|
||||
positions = append(positions, int32(i+1)) // PostgreSQL uses 1-indexed arrays
|
||||
}
|
||||
}
|
||||
|
||||
// Return array of positions, or empty array if no matches
|
||||
return positions, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright 2026 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
"github.com/goccy/go-json"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initArrayToJson registers the functions to the catalog.
|
||||
func initArrayToJson() {
|
||||
framework.RegisterFunction(array_to_json_anyarray)
|
||||
framework.RegisterFunction(array_to_json_anyarray_bool)
|
||||
}
|
||||
|
||||
// array_to_json_anyarray represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_to_json_anyarray = framework.Function1{
|
||||
Name: "array_to_json",
|
||||
Return: pgtypes.Json,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyArray},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
arr := val.([]any)
|
||||
raw, err := arrayToJsonRaw(ctx, paramsAndReturn[0], arr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(raw), nil
|
||||
},
|
||||
}
|
||||
|
||||
// array_to_json_anyarray_bool represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_to_json_anyarray_bool = framework.Function2{
|
||||
Name: "array_to_json",
|
||||
Return: pgtypes.Json,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.Bool},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
arr := val1.([]any)
|
||||
pretty := val2.(bool)
|
||||
if !pretty {
|
||||
raw, err := arrayToJsonRaw(ctx, paramsAndReturn[0], arr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(raw), nil
|
||||
}
|
||||
return arrayToJsonPretty(ctx, paramsAndReturn[0], arr)
|
||||
},
|
||||
}
|
||||
|
||||
// arrayToJsonRaw converts an anyarray to a JSON byte slice.
|
||||
func arrayToJsonRaw(ctx *sql.Context, arrType *pgtypes.DoltgresType, arr []any) (json.RawMessage, error) {
|
||||
baseType := arrType.ArrayBaseType()
|
||||
elements := make([]json.RawMessage, len(arr))
|
||||
for i, el := range arr {
|
||||
raw, err := valueToJsonRaw(ctx, baseType, el)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
elements[i] = raw
|
||||
}
|
||||
return json.Marshal(elements)
|
||||
}
|
||||
|
||||
// valueToJsonRaw converts a single value to a JSON byte slice.
|
||||
func valueToJsonRaw(ctx *sql.Context, elemType *pgtypes.DoltgresType, val any) (json.RawMessage, error) {
|
||||
if val == nil {
|
||||
return json.RawMessage("null"), nil
|
||||
}
|
||||
switch v := val.(type) {
|
||||
case *apd.Decimal:
|
||||
return json.RawMessage(v.Text('f')), nil
|
||||
case pgtypes.JsonDocument:
|
||||
sb := strings.Builder{}
|
||||
pgtypes.JsonValueFormatter(&sb, v.Value)
|
||||
return json.RawMessage(sb.String()), nil
|
||||
case types.JSONDocument:
|
||||
return json.Marshal(v.Val)
|
||||
case sql.JSONWrapper:
|
||||
jsonVal, err := v.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return json.Marshal(jsonVal)
|
||||
case []any:
|
||||
return arrayToJsonRaw(ctx, elemType, v)
|
||||
case string:
|
||||
// JSON-typed values are already valid JSON and should be embedded without re-quoting.
|
||||
if elemType != nil && elemType.ID.TypeName() == "json" {
|
||||
return json.RawMessage(v), nil
|
||||
}
|
||||
return json.Marshal(v)
|
||||
default:
|
||||
return json.Marshal(val)
|
||||
}
|
||||
}
|
||||
|
||||
// arrayToJsonPretty produces a pretty-printed JSON array where dimension-1 elements are
|
||||
// separated by a comma and newline.
|
||||
func arrayToJsonPretty(ctx *sql.Context, arrType *pgtypes.DoltgresType, arr []any) (string, error) {
|
||||
baseType := arrType.ArrayBaseType()
|
||||
sb := strings.Builder{}
|
||||
sb.WriteRune('[')
|
||||
for i, el := range arr {
|
||||
if i > 0 {
|
||||
sb.WriteString(",\n ")
|
||||
}
|
||||
raw, err := valueToJsonRaw(ctx, baseType, el)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
sb.Write(raw)
|
||||
}
|
||||
sb.WriteRune(']')
|
||||
return sb.String(), nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initArrayToString registers the functions to the catalog.
|
||||
func initArrayToString() {
|
||||
framework.RegisterFunction(array_to_string_anyarray_text)
|
||||
framework.RegisterFunction(array_to_string_anyarray_text_text)
|
||||
}
|
||||
|
||||
// array_to_string_anyarray_text represents the PostgreSQL array function taking 2 parameters.
|
||||
var array_to_string_anyarray_text = framework.Function2{
|
||||
Name: "array_to_string",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.Text},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
arr := val1.([]any)
|
||||
delimiter := val2.(string)
|
||||
return getStringArrFromAnyArray(ctx, paramsAndReturn[0], arr, delimiter, nil)
|
||||
},
|
||||
}
|
||||
|
||||
// array_to_string_anyarray_text_text represents the PostgreSQL array function taking 3 parameter.
|
||||
var array_to_string_anyarray_text_text = framework.Function3{
|
||||
Name: "array_to_string",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.Text, pgtypes.Text},
|
||||
IsNonDeterministic: true,
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
if val1 == nil {
|
||||
return nil, errors.Errorf("could not determine polymorphic type because input has type unknown")
|
||||
} else if val2 == nil {
|
||||
return nil, nil
|
||||
}
|
||||
arr := val1.([]any)
|
||||
delimiter := val2.(string)
|
||||
return getStringArrFromAnyArray(ctx, paramsAndReturn[0], arr, delimiter, val3)
|
||||
},
|
||||
}
|
||||
|
||||
// getStringArrFromAnyArray takes inputs of any array, delimiter and null entry replacement. It uses the IoOutput() of the
|
||||
// base type of the AnyArray type to get string representation of array elements.
|
||||
func getStringArrFromAnyArray(ctx *sql.Context, arrType *pgtypes.DoltgresType, arr []any, delimiter string, nullEntry any) (string, error) {
|
||||
baseType := arrType.ArrayBaseType()
|
||||
strs := make([]string, 0)
|
||||
for _, el := range arr {
|
||||
if el != nil {
|
||||
v, err := baseType.IoOutput(ctx, el)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
strs = append(strs, v)
|
||||
} else if nullEntry != nil {
|
||||
strs = append(strs, nullEntry.(string))
|
||||
}
|
||||
}
|
||||
return strings.Join(strs, delimiter), nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initArrayUpper registers the functions to the catalog.
|
||||
func initArrayUpper() {
|
||||
framework.RegisterFunction(array_upper_anyarray_int32)
|
||||
}
|
||||
|
||||
// array_upper_anyarray_int32 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_upper_anyarray_int32 = framework.Function2{
|
||||
Name: "array_upper",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
array := val1.([]any)
|
||||
dimension := val2.(int32)
|
||||
|
||||
// PostgreSQL arrays are 1-dimensional in this implementation
|
||||
// For dimension 1, return the upper bound (length of array)
|
||||
if dimension == 1 {
|
||||
if len(array) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return int32(len(array)), nil
|
||||
}
|
||||
|
||||
// For dimensions other than 1, return null (multi-dimensional arrays not fully supported)
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAscii registers the functions to the catalog.
|
||||
func initAscii() {
|
||||
framework.RegisterFunction(ascii_text)
|
||||
}
|
||||
|
||||
// ascii_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var ascii_text = framework.Function1{
|
||||
Name: "ascii",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1Interface any) (any, error) {
|
||||
val1 := val1Interface.(string)
|
||||
if len(val1) == 0 {
|
||||
return int32(0), nil
|
||||
}
|
||||
runes := []rune(val1)
|
||||
return int32(runes[0]), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAsin registers the functions to the catalog.
|
||||
func initAsin() {
|
||||
framework.RegisterFunction(asin_float64)
|
||||
}
|
||||
|
||||
// asin_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var asin_float64 = framework.Function1{
|
||||
Name: "asin",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Asin(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAsind registers the functions to the catalog.
|
||||
func initAsind() {
|
||||
framework.RegisterFunction(asind_float64)
|
||||
}
|
||||
|
||||
// asind_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var asind_float64 = framework.Function1{
|
||||
Name: "asind",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Asin(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return toDegrees(r), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAsinh registers the functions to the catalog.
|
||||
func initAsinh() {
|
||||
framework.RegisterFunction(asinh_float64)
|
||||
}
|
||||
|
||||
// asinh_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var asinh_float64 = framework.Function1{
|
||||
Name: "asinh",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Asinh(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAtan registers the functions to the catalog.
|
||||
func initAtan() {
|
||||
framework.RegisterFunction(atan_float64)
|
||||
}
|
||||
|
||||
// atan_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var atan_float64 = framework.Function1{
|
||||
Name: "atan",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Atan(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAtan2 registers the functions to the catalog.
|
||||
func initAtan2() {
|
||||
framework.RegisterFunction(atan2_float64)
|
||||
}
|
||||
|
||||
// atan2_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var atan2_float64 = framework.Function2{
|
||||
Name: "atan2",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, y any, x any) (any, error) {
|
||||
r := math.Atan2(y.(float64), x.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAtan2d registers the functions to the catalog.
|
||||
func initAtan2d() {
|
||||
framework.RegisterFunction(atan2d_float64)
|
||||
}
|
||||
|
||||
// atan2_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var atan2d_float64 = framework.Function2{
|
||||
Name: "atan2d",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, y any, x any) (any, error) {
|
||||
r := math.Atan2(y.(float64), x.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return toDegrees(r), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAtand registers the functions to the catalog.
|
||||
func initAtand() {
|
||||
framework.RegisterFunction(atand_float64)
|
||||
}
|
||||
|
||||
// atan_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var atand_float64 = framework.Function1{
|
||||
Name: "atand",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Atan(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return toDegrees(r), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initAtanh registers the functions to the catalog.
|
||||
func initAtanh() {
|
||||
framework.RegisterFunction(atanh_float64)
|
||||
}
|
||||
|
||||
// atanh_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var atanh_float64 = framework.Function1{
|
||||
Name: "atanh",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
r := math.Atanh(val1.(float64))
|
||||
if math.IsNaN(r) {
|
||||
return nil, errors.Errorf("input is out of range")
|
||||
}
|
||||
return r, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '&' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryBitAnd registers the functions to the catalog.
|
||||
func initBinaryBitAnd() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitAnd, int2and)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitAnd, int4and)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitAnd, int8and)
|
||||
}
|
||||
|
||||
// int2and_callable is the callable logic for the int2and function.
|
||||
func int2and_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int16(val1.(int16) & val2.(int16)), nil
|
||||
}
|
||||
|
||||
// int2and represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2and = framework.Function2{
|
||||
Name: "int2and",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int2and_callable,
|
||||
}
|
||||
|
||||
// int4and_callable is the callable logic for the int4and function.
|
||||
func int4and_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int32(val1.(int32) & val2.(int32)), nil
|
||||
}
|
||||
|
||||
// int4and represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4and = framework.Function2{
|
||||
Name: "int4and",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int4and_callable,
|
||||
}
|
||||
|
||||
// int8and_callable is the callable logic for the int8and function.
|
||||
func int8and_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int64(val1.(int64) & val2.(int64)), nil
|
||||
}
|
||||
|
||||
// int8and represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8and = framework.Function2{
|
||||
Name: "int8and",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int8and_callable,
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '|' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryBitOr registers the functions to the catalog.
|
||||
func initBinaryBitOr() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitOr, int2or)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitOr, int4or)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitOr, int8or)
|
||||
}
|
||||
|
||||
// int2or_callable is the callable logic for the int2or function.
|
||||
func int2or_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int16(val1.(int16) | val2.(int16)), nil
|
||||
}
|
||||
|
||||
// int2or represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2or = framework.Function2{
|
||||
Name: "int2or",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int2or_callable,
|
||||
}
|
||||
|
||||
// int4or_callable is the callable logic for the int4or function.
|
||||
func int4or_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int32(val1.(int32) | val2.(int32)), nil
|
||||
}
|
||||
|
||||
// int4or represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4or = framework.Function2{
|
||||
Name: "int4or",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int4or_callable,
|
||||
}
|
||||
|
||||
// int8or_callable is the callable logic for the int8or function.
|
||||
func int8or_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int64(val1.(int64) | val2.(int64)), nil
|
||||
}
|
||||
|
||||
// int8or represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8or = framework.Function2{
|
||||
Name: "int8or",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int8or_callable,
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '#' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryBitXor registers the functions to the catalog.
|
||||
func initBinaryBitXor() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitXor, int2xor)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitXor, int4xor)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryBitXor, int8xor)
|
||||
}
|
||||
|
||||
// int2xor_callable is the callable logic for the int2xor function.
|
||||
func int2xor_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int16(val1.(int16) ^ val2.(int16)), nil
|
||||
}
|
||||
|
||||
// int2xor represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2xor = framework.Function2{
|
||||
Name: "int2xor",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int2xor_callable,
|
||||
}
|
||||
|
||||
// int4xor_callable is the callable logic for the int4xor function.
|
||||
func int4xor_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int32(val1.(int32) ^ val2.(int32)), nil
|
||||
}
|
||||
|
||||
// int4xor represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4xor = framework.Function2{
|
||||
Name: "int4xor",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int4xor_callable,
|
||||
}
|
||||
|
||||
// int8xor_callable is the callable logic for the int8xor function.
|
||||
func int8xor_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int64(val1.(int64) ^ val2.(int64)), nil
|
||||
}
|
||||
|
||||
// int8xor represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8xor = framework.Function2{
|
||||
Name: "int8xor",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int8xor_callable,
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
gmstypes "github.com/dolthub/go-mysql-server/sql/types"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '||' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryConcatenate registers the functions to the catalog.
|
||||
func initBinaryConcatenate() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, anytextcat)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, array_append)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, array_cat)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, array_prepend)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, byteacat)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, jsonb_concat)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, textanycat)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryConcatenate, textcat)
|
||||
// TODO: bitcat, tsquery_or, tsvector_concat
|
||||
}
|
||||
|
||||
// anytextcat_callable is the callable logic for the anytextcat function.
|
||||
func anytextcat_callable(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
valType := paramsAndReturn[0]
|
||||
val1String, err := valType.IoOutput(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return val1String + val2.(string), nil
|
||||
}
|
||||
|
||||
// anytextcat represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var anytextcat = framework.Function2{
|
||||
Name: "anytextcat",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyNonArray, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: anytextcat_callable,
|
||||
}
|
||||
|
||||
// array_append represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_append = framework.Function2{
|
||||
Name: "array_append",
|
||||
Return: pgtypes.AnyArray, // TODO: should be anycompatiblearray
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.AnyElement}, // TODO: should be anycompatiblearray, anycompatible
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val1 == nil {
|
||||
return []any{val2}, nil
|
||||
}
|
||||
array := val1.([]any)
|
||||
returnArray := make([]any, len(array)+1)
|
||||
copy(returnArray, array)
|
||||
returnArray[len(returnArray)-1] = val2
|
||||
return returnArray, nil
|
||||
},
|
||||
}
|
||||
|
||||
// array_cat represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_cat = framework.Function2{
|
||||
Name: "array_cat",
|
||||
Return: pgtypes.AnyArray, // TODO: should be anycompatiblearray
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyArray, pgtypes.AnyArray}, // TODO: should be anycompatiblearray, anycompatiblearray
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val1 == nil && val2 == nil {
|
||||
return nil, nil
|
||||
} else if val1 == nil {
|
||||
return val2, nil
|
||||
} else if val2 == nil {
|
||||
return val1, nil
|
||||
}
|
||||
|
||||
array1 := val1.([]any)
|
||||
array2 := val2.([]any)
|
||||
|
||||
// Concatenate the arrays
|
||||
result := make([]any, len(array1)+len(array2))
|
||||
copy(result, array1)
|
||||
copy(result[len(array1):], array2)
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// array_prepend represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var array_prepend = framework.Function2{
|
||||
Name: "array_prepend",
|
||||
Return: pgtypes.AnyArray, // TODO: should be anycompatiblearray
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyElement, pgtypes.AnyArray}, // TODO: should be anycompatible, anycompatiblearray
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2 == nil {
|
||||
return []any{val1}, nil
|
||||
}
|
||||
return append([]any{val1}, val2.([]any)...), nil
|
||||
},
|
||||
}
|
||||
|
||||
// byteacat_callable is the callable logic for the byteacat function.
|
||||
func byteacat_callable(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
v1 := val1.([]byte)
|
||||
v2 := val2.([]byte)
|
||||
copied := make([]byte, len(v1)+len(v2))
|
||||
copy(copied, v1)
|
||||
copy(copied[len(v1):], v2)
|
||||
return copied, nil
|
||||
}
|
||||
|
||||
// byteacat represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var byteacat = framework.Function2{
|
||||
Name: "byteacat",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: byteacat_callable,
|
||||
}
|
||||
|
||||
// jsonb_concat_callable is the callable logic for the jsonb_concat function.
|
||||
func jsonb_concat_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1Interface any, val2Interface any) (any, error) {
|
||||
// TODO: for two IndexedJsonDocuments, we could get much faster results on large documents by merging the underlying
|
||||
// JSON trees instead of loading them into memory. This would require a new method on sql.MutableJSON
|
||||
wrapper1, ok1 := val1Interface.(sql.JSONWrapper)
|
||||
wrapper2, ok2 := val2Interface.(sql.JSONWrapper)
|
||||
if !ok1 || !ok2 {
|
||||
return nil, fmt.Errorf("jsonb_concat: unexpected types %T, %T", val1Interface, val2Interface)
|
||||
}
|
||||
v1, err := wrapper1.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v2, err := wrapper2.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Merge objects if both are objects
|
||||
obj1, isObj1 := v1.(map[string]interface{})
|
||||
obj2, isObj2 := v2.(map[string]interface{})
|
||||
if isObj1 && isObj2 {
|
||||
newObj := make(map[string]interface{}, len(obj1)+len(obj2))
|
||||
for k, v := range obj1 {
|
||||
newObj[k] = v
|
||||
}
|
||||
for k, v := range obj2 {
|
||||
newObj[k] = v
|
||||
}
|
||||
return gmstypes.JSONDocument{Val: newObj}, nil
|
||||
}
|
||||
// Not both objects: wrap non-arrays in single-element arrays and concatenate
|
||||
arr1, isArr1 := v1.([]interface{})
|
||||
arr2, isArr2 := v2.([]interface{})
|
||||
if !isArr1 {
|
||||
arr1 = []interface{}{v1}
|
||||
}
|
||||
if !isArr2 {
|
||||
arr2 = []interface{}{v2}
|
||||
}
|
||||
newArray := make([]interface{}, len(arr1)+len(arr2))
|
||||
copy(newArray, arr1)
|
||||
copy(newArray[len(arr1):], arr2)
|
||||
return gmstypes.JSONDocument{Val: newArray}, nil
|
||||
}
|
||||
|
||||
// jsonb_concat represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_concat = framework.Function2{
|
||||
Name: "jsonb_concat",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: jsonb_concat_callable,
|
||||
}
|
||||
|
||||
// textanycat_callable is the callable logic for the textanycat function.
|
||||
func textanycat_callable(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
valType := paramsAndReturn[1]
|
||||
val2String, err := valType.IoOutput(ctx, val2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return val1.(string) + val2String, nil
|
||||
}
|
||||
|
||||
// textanycat represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textanycat = framework.Function2{
|
||||
Name: "textanycat",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.AnyNonArray},
|
||||
Strict: true,
|
||||
Callable: textanycat_callable,
|
||||
}
|
||||
|
||||
// textcat_callable is the callable logic for the textcat function.
|
||||
func textcat_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(string) + val2.(string), nil
|
||||
}
|
||||
|
||||
// textcat represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textcat = framework.Function2{
|
||||
Name: "textcat",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: textcat_callable,
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '/' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryDivide registers the functions to the catalog.
|
||||
func initBinaryDivide() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, float4div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, float48div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, float8div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, float84div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int2div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int24div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int28div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int4div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int42div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int48div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int8div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int82div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, int84div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, interval_div)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryDivide, numeric_div)
|
||||
}
|
||||
|
||||
// float4div_callable is the callable logic for the float4div function.
|
||||
func float4div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(float32) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(float32) / val2.(float32), nil
|
||||
}
|
||||
|
||||
// float4div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4div = framework.Function2{
|
||||
Name: "float4div",
|
||||
Return: pgtypes.Float32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: float4div_callable,
|
||||
}
|
||||
|
||||
// float48div_callable is the callable logic for the float48div function.
|
||||
func float48div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(float64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return float64(val1.(float32)) / val2.(float64), nil
|
||||
}
|
||||
|
||||
// float48div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48div = framework.Function2{
|
||||
Name: "float48div",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: float48div_callable,
|
||||
}
|
||||
|
||||
// float8div_callable is the callable logic for the float8div function.
|
||||
func float8div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(float64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(float64) / val2.(float64), nil
|
||||
}
|
||||
|
||||
// float8div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8div = framework.Function2{
|
||||
Name: "float8div",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: float8div_callable,
|
||||
}
|
||||
|
||||
// float84div_callable is the callable logic for the float84div function.
|
||||
func float84div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(float32) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(float64) / float64(val2.(float32)), nil
|
||||
}
|
||||
|
||||
// float84div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84div = framework.Function2{
|
||||
Name: "float84div",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: float84div_callable,
|
||||
}
|
||||
|
||||
// int2div_callable is the callable logic for the int2div function.
|
||||
func int2div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int16) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int16) / val2.(int16), nil
|
||||
}
|
||||
|
||||
// int2div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2div = framework.Function2{
|
||||
Name: "int2div",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int2div_callable,
|
||||
}
|
||||
|
||||
// int24div_callable is the callable logic for the int24div function.
|
||||
func int24div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int32) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return int32(val1.(int16)) / val2.(int32), nil
|
||||
}
|
||||
|
||||
// int24div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24div = framework.Function2{
|
||||
Name: "int24div",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int24div_callable,
|
||||
}
|
||||
|
||||
// int28div_callable is the callable logic for the int28div function.
|
||||
func int28div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return int64(val1.(int16)) / val2.(int64), nil
|
||||
}
|
||||
|
||||
// int28div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28div = framework.Function2{
|
||||
Name: "int28div",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int28div_callable,
|
||||
}
|
||||
|
||||
// int4div_callable is the callable logic for the int4div function.
|
||||
func int4div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int32) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int32) / val2.(int32), nil
|
||||
}
|
||||
|
||||
// int4div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4div = framework.Function2{
|
||||
Name: "int4div",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int4div_callable,
|
||||
}
|
||||
|
||||
// int42div_callable is the callable logic for the int42div function.
|
||||
func int42div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int16) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int32) / int32(val2.(int16)), nil
|
||||
}
|
||||
|
||||
// int42div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42div = framework.Function2{
|
||||
Name: "int42div",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int42div_callable,
|
||||
}
|
||||
|
||||
// int48div_callable is the callable logic for the int48div function.
|
||||
func int48div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return int64(val1.(int32)) / val2.(int64), nil
|
||||
}
|
||||
|
||||
// int48div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48div = framework.Function2{
|
||||
Name: "int48div",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int48div_callable,
|
||||
}
|
||||
|
||||
// int8div_callable is the callable logic for the int8div function.
|
||||
func int8div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int64) / val2.(int64), nil
|
||||
}
|
||||
|
||||
// int8div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8div = framework.Function2{
|
||||
Name: "int8div",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int8div_callable,
|
||||
}
|
||||
|
||||
// int82div_callable is the callable logic for the int82div function.
|
||||
func int82div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int16) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int64) / int64(val2.(int16)), nil
|
||||
}
|
||||
|
||||
// int82div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82div = framework.Function2{
|
||||
Name: "int82div",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int82div_callable,
|
||||
}
|
||||
|
||||
// int84div_callable is the callable logic for the int84div function.
|
||||
func int84div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int32) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int64) / int64(val2.(int32)), nil
|
||||
}
|
||||
|
||||
// int84div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84div = framework.Function2{
|
||||
Name: "int84div",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int84div_callable,
|
||||
}
|
||||
|
||||
// interval_div_callable is the callable logic for the interval_div function.
|
||||
func interval_div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(float64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(duration.Duration).DivFloat(val2.(float64)), nil
|
||||
}
|
||||
|
||||
// interval_div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_div = framework.Function2{
|
||||
Name: "interval_div",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: interval_div_callable,
|
||||
}
|
||||
|
||||
// numeric_div_callable is the callable logic for the numeric_div function.
|
||||
func numeric_div_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
num1 := val1.(*apd.Decimal)
|
||||
num2 := val2.(*apd.Decimal)
|
||||
if num1.Form == apd.NaN || num2.Form == apd.NaN ||
|
||||
(num1.Form == apd.Infinite && num2.Form == apd.Infinite) {
|
||||
return pgtypes.NumericNaN, nil
|
||||
}
|
||||
if num2.IsZero() {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
if num1.Form == apd.Infinite {
|
||||
return num1, nil
|
||||
}
|
||||
if num2.Form == apd.Infinite {
|
||||
return apd.New(0, 0), nil
|
||||
}
|
||||
|
||||
res := new(apd.Decimal)
|
||||
// enough precision to scale to at most 16 decimal places
|
||||
p := num1.NumDigits() + int64(math.Abs(float64(num1.Exponent))) + 16
|
||||
_, err := sql.DecimalCtx.WithPrecision(uint32(p)).Quo(res, num1, num2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// the exponent value depends on the number of digits before decimal places.
|
||||
parts := strings.Split(res.Text('f'), ".")
|
||||
whole := (len(parts[0]) + 4 - 1) / 4
|
||||
exp := int32(16 - (whole-1)*4)
|
||||
|
||||
return sql.DecimalRound(res, exp)
|
||||
}
|
||||
|
||||
// numeric_div represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_div = framework.Function2{
|
||||
Name: "numeric_div",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: numeric_div_callable,
|
||||
}
|
||||
@@ -0,0 +1,739 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
"github.com/dolthub/doltgresql/server/compare"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '=' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryEqual registers the functions to the catalog.
|
||||
func initBinaryEqual() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, biteq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, booleq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, bpchareq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, byteaeq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, chareq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, date_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, date_eq_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, date_eq_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, enum_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, float4eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, float48eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, float84eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, float8eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int2eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int24eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int28eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int42eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int4eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int48eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int82eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int84eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, int8eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, interval_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, jsonb_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, nameeq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, nameeqtext)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, numeric_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, oideq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, oidvectoreq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, texteqname)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, text_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, record_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, time_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timestamp_eq_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timestamp_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timestamp_eq_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timestamptz_eq_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timestamptz_eq_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timestamptz_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, timetz_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, uuid_eq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, varbiteq)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, xideqint4)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryEqual, xideq)
|
||||
}
|
||||
|
||||
// booleq_callable is the callable logic for the booleq function.
|
||||
func booleq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bool.Compare(ctx, val1.(bool), val2.(bool))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// booleq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var booleq = framework.Function2{
|
||||
Name: "booleq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: booleq_callable,
|
||||
}
|
||||
|
||||
// bpchareq_callable is the callable logic for the bpchareq function.
|
||||
func bpchareq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.BpChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// bpchareq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bpchareq = framework.Function2{
|
||||
Name: "bpchareq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: bpchareq_callable,
|
||||
}
|
||||
|
||||
// byteaeq_callable is the callable logic for the byteaeq function.
|
||||
func byteaeq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bytea.Compare(ctx, val1, val2)
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// byteaeq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var byteaeq = framework.Function2{
|
||||
Name: "byteaeq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: byteaeq_callable,
|
||||
}
|
||||
|
||||
// chareq_callable is the callable logic for the chareq function.
|
||||
func chareq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.InternalChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// chareq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var chareq = framework.Function2{
|
||||
Name: "chareq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: chareq_callable,
|
||||
}
|
||||
|
||||
// date_eq_callable is the callable logic for the date_eq function.
|
||||
func date_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Date.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// date_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_eq = framework.Function2{
|
||||
Name: "date_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: date_eq_callable,
|
||||
}
|
||||
|
||||
// date_eq_timestamp_callable is the callable logic for the date_eq_timestamp function.
|
||||
func date_eq_timestamp_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 0, nil
|
||||
}
|
||||
|
||||
// date_eq_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_eq_timestamp = framework.Function2{
|
||||
Name: "date_eq_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: date_eq_timestamp_callable,
|
||||
}
|
||||
|
||||
// date_eq_timestamptz_callable is the callable logic for the date_eq_timestamptz function.
|
||||
func date_eq_timestamptz_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 0, nil
|
||||
}
|
||||
|
||||
// date_eq_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_eq_timestamptz = framework.Function2{
|
||||
Name: "date_eq_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: date_eq_timestamptz_callable,
|
||||
}
|
||||
|
||||
// enum_eq_callable is the callable logic for the enum_eq function.
|
||||
func enum_eq_callable(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: if given types are not the same enum type, it cannot compare
|
||||
res, err := t[0].Compare(ctx, val1, val2)
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// enum_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var enum_eq = framework.Function2{
|
||||
Name: "enum_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: enum_eq_callable,
|
||||
}
|
||||
|
||||
// float4eq_callable is the callable logic for the float4eq function.
|
||||
func float4eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float32.Compare(ctx, val1.(float32), val2.(float32))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// float4eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4eq = framework.Function2{
|
||||
Name: "float4eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: float4eq_callable,
|
||||
}
|
||||
|
||||
// float48eq_callable is the callable logic for the float48eq function.
|
||||
func float48eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, float64(val1.(float32)), val2.(float64))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// float48eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48eq = framework.Function2{
|
||||
Name: "float48eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: float48eq_callable,
|
||||
}
|
||||
|
||||
// float84eq_callable is the callable logic for the float84eq function.
|
||||
func float84eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), float64(val2.(float32)))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// float84eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84eq = framework.Function2{
|
||||
Name: "float84eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: float84eq_callable,
|
||||
}
|
||||
|
||||
// float8eq_callable is the callable logic for the float8eq function.
|
||||
func float8eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), val2.(float64))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// float8eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8eq = framework.Function2{
|
||||
Name: "float8eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: float8eq_callable,
|
||||
}
|
||||
|
||||
// int2eq_callable is the callable logic for the int2eq function.
|
||||
func int2eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int16.Compare(ctx, val1.(int16), val2.(int16))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int2eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2eq = framework.Function2{
|
||||
Name: "int2eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int2eq_callable,
|
||||
}
|
||||
|
||||
// int24eq_callable is the callable logic for the int24eq function.
|
||||
func int24eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, int32(val1.(int16)), val2.(int32))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int24eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24eq = framework.Function2{
|
||||
Name: "int24eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int24eq_callable,
|
||||
}
|
||||
|
||||
// int28eq_callable is the callable logic for the int28eq function.
|
||||
func int28eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int16)), val2.(int64))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int28eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28eq = framework.Function2{
|
||||
Name: "int28eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int28eq_callable,
|
||||
}
|
||||
|
||||
// int42eq_callable is the callable logic for the int42eq function.
|
||||
func int42eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), int32(val2.(int16)))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int42eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42eq = framework.Function2{
|
||||
Name: "int42eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int42eq_callable,
|
||||
}
|
||||
|
||||
// int4eq_callable is the callable logic for the int4eq function.
|
||||
func int4eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), val2.(int32))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int4eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4eq = framework.Function2{
|
||||
Name: "int4eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int4eq_callable,
|
||||
}
|
||||
|
||||
// int48eq_callable is the callable logic for the int48eq function.
|
||||
func int48eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int32)), val2.(int64))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int48eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48eq = framework.Function2{
|
||||
Name: "int48eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int48eq_callable,
|
||||
}
|
||||
|
||||
// int82eq_callable is the callable logic for the int82eq function.
|
||||
func int82eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int16)))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int82eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82eq = framework.Function2{
|
||||
Name: "int82eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int82eq_callable,
|
||||
}
|
||||
|
||||
// int84eq_callable is the callable logic for the int84eq function.
|
||||
func int84eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int32)))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int84eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84eq = framework.Function2{
|
||||
Name: "int84eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int84eq_callable,
|
||||
}
|
||||
|
||||
// int8eq_callable is the callable logic for the int8eq function.
|
||||
func int8eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), val2.(int64))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// int8eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8eq = framework.Function2{
|
||||
Name: "int8eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int8eq_callable,
|
||||
}
|
||||
|
||||
// interval_eq_callable is the callable logic for the interval_eq function.
|
||||
func interval_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Interval.Compare(ctx, val1.(duration.Duration), val2.(duration.Duration))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// interval_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_eq = framework.Function2{
|
||||
Name: "interval_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: interval_eq_callable,
|
||||
}
|
||||
|
||||
// jsonb_eq_callable is the callable logic for the jsonb_eq function.
|
||||
func jsonb_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.JsonB.Compare(ctx, val1, val2)
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// jsonb_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_eq = framework.Function2{
|
||||
Name: "jsonb_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: jsonb_eq_callable,
|
||||
}
|
||||
|
||||
// nameeq_callable is the callable logic for the nameeq function.
|
||||
func nameeq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Name.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// nameeq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var nameeq = framework.Function2{
|
||||
Name: "nameeq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: nameeq_callable,
|
||||
}
|
||||
|
||||
// nameeqtext_callable is the callable logic for the nameeqtext function.
|
||||
func nameeqtext_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// nameeqtext represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var nameeqtext = framework.Function2{
|
||||
Name: "nameeqtext",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: nameeqtext_callable,
|
||||
}
|
||||
|
||||
// numeric_eq_callable is the callable logic for the numeric_eq function.
|
||||
func numeric_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Numeric.Compare(ctx, val1.(*apd.Decimal), val2.(*apd.Decimal))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// numeric_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_eq = framework.Function2{
|
||||
Name: "numeric_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: numeric_eq_callable,
|
||||
}
|
||||
|
||||
// oideq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oideq = framework.Function2{
|
||||
Name: "oideq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// This method doesn't use DoltgresType.Compare because it's on the critical path for many tooling queries that
|
||||
// examine the pg_catalog tables.
|
||||
val1id, val2id := val1.(id.Id), val2.(id.Id)
|
||||
return val1id == val2id, nil
|
||||
},
|
||||
}
|
||||
|
||||
// oidvectoreq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidvectoreq = framework.Function2{
|
||||
Name: "oidvectoreq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oidvector, pgtypes.Oidvector},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oidvector.Compare(ctx, val1.([]any), val2.([]any))
|
||||
return res == 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// texteqname_callable is the callable logic for the texteqname function.
|
||||
func texteqname_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// texteqname represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var texteqname = framework.Function2{
|
||||
Name: "texteqname",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: texteqname_callable,
|
||||
}
|
||||
|
||||
// text_eq_callable is the callable logic for the text_eq function.
|
||||
func text_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1, val2)
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// text_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var text_eq = framework.Function2{
|
||||
Name: "text_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: text_eq_callable,
|
||||
}
|
||||
|
||||
// record_eq_callable is the callable logic for the record_eq function.
|
||||
func record_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return compare.CompareRecords(ctx, framework.Operator_BinaryEqual, val1, val2)
|
||||
}
|
||||
|
||||
// varbiteq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var varbiteq = framework.Function2{
|
||||
Name: "varbiteq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.VarBit, pgtypes.VarBit},
|
||||
Strict: true,
|
||||
Callable: varbit_eq_callable,
|
||||
}
|
||||
|
||||
// biteq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var biteq = framework.Function2{
|
||||
Name: "biteq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bit, pgtypes.Bit},
|
||||
Strict: true,
|
||||
Callable: bit_eq_callable,
|
||||
}
|
||||
|
||||
// varbit_eq_callable is the callable logic for the varbiteq function.
|
||||
func varbit_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.VarBit.Compare(ctx, val1, val2)
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// bit_eq_callable is the callable logic for the varbiteq function.
|
||||
func bit_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bit.Compare(ctx, val1, val2)
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// record_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var record_eq = framework.Function2{
|
||||
Name: "record_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Record, pgtypes.Record},
|
||||
Strict: true,
|
||||
Callable: record_eq_callable,
|
||||
}
|
||||
|
||||
// time_eq_callable is the callable logic for the time_eq function.
|
||||
func time_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Time.Compare(ctx, val1.(timeofday.TimeOfDay), val2.(timeofday.TimeOfDay))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// time_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_eq = framework.Function2{
|
||||
Name: "time_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: time_eq_callable,
|
||||
}
|
||||
|
||||
// timestamp_eq_date_callable is the callable logic for the timestamp_eq_date function.
|
||||
func timestamp_eq_date_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 0, nil
|
||||
}
|
||||
|
||||
// timestamp_eq_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_eq_date = framework.Function2{
|
||||
Name: "timestamp_eq_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: timestamp_eq_date_callable,
|
||||
}
|
||||
|
||||
// timestamp_eq_callable is the callable logic for the timestamp_eq function.
|
||||
func timestamp_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Timestamp.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// timestamp_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_eq = framework.Function2{
|
||||
Name: "timestamp_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: timestamp_eq_callable,
|
||||
}
|
||||
|
||||
// timestamp_eq_timestamptz_callable is the callable logic for the timestamp_eq_timestamptz function.
|
||||
func timestamp_eq_timestamptz_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// timestamp_eq_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_eq_timestamptz = framework.Function2{
|
||||
Name: "timestamp_eq_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: timestamp_eq_timestamptz_callable,
|
||||
}
|
||||
|
||||
// timestamptz_eq_date_callable is the callable logic for the timestamptz_eq_date function.
|
||||
func timestamptz_eq_date_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 0, nil
|
||||
}
|
||||
|
||||
// timestamptz_eq_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_eq_date = framework.Function2{
|
||||
Name: "timestamptz_eq_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: timestamptz_eq_date_callable,
|
||||
}
|
||||
|
||||
// timestamptz_eq_timestamp_callable is the callable logic for the timestamptz_eq_timestamp function.
|
||||
func timestamptz_eq_timestamp_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// timestamptz_eq_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_eq_timestamp = framework.Function2{
|
||||
Name: "timestamptz_eq_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: timestamptz_eq_timestamp_callable,
|
||||
}
|
||||
|
||||
// timestamptz_eq_callable is the callable logic for the timestamptz_eq function.
|
||||
func timestamptz_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// timestamptz_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_eq = framework.Function2{
|
||||
Name: "timestamptz_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: timestamptz_eq_callable,
|
||||
}
|
||||
|
||||
// timetz_eq_callable is the callable logic for the timetz_eq function.
|
||||
func timetz_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimeTZ.Compare(ctx, val1.(timetz.TimeTZ), val2.(timetz.TimeTZ))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// timetz_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_eq = framework.Function2{
|
||||
Name: "timetz_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: timetz_eq_callable,
|
||||
}
|
||||
|
||||
// uuid_eq_callable is the callable logic for the uuid_eq function.
|
||||
func uuid_eq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Uuid.Compare(ctx, val1.(uuid.UUID), val2.(uuid.UUID))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// uuid_eq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var uuid_eq = framework.Function2{
|
||||
Name: "uuid_eq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Uuid, pgtypes.Uuid},
|
||||
Strict: true,
|
||||
Callable: uuid_eq_callable,
|
||||
}
|
||||
|
||||
// xideqint4_callable is the callable logic for the xideqint4 function.
|
||||
func xideqint4_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: investigate the edge cases
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(uint32)), int64(val2.(int32)))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// xideqint4 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var xideqint4 = framework.Function2{
|
||||
Name: "xideqint4",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Xid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: xideqint4_callable,
|
||||
}
|
||||
|
||||
// xideq_callable is the callable logic for the xideq function.
|
||||
func xideq_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Xid.Compare(ctx, val1.(uint32), val2.(uint32))
|
||||
return res == 0, err
|
||||
}
|
||||
|
||||
// xideq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var xideq = framework.Function2{
|
||||
Name: "xideq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Xid, pgtypes.Xid},
|
||||
Strict: true,
|
||||
Callable: xideq_callable,
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
"github.com/dolthub/doltgresql/server/compare"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '>' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryGreaterThan registers the functions to the catalog.
|
||||
func initBinaryGreaterThan() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, boolgt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, bpchargt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, byteagt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, chargt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, date_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, date_gt_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, date_gt_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, enum_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, float4gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, float48gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, float84gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, float8gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int2gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int24gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int28gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int42gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int4gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int48gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int82gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int84gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, int8gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, interval_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, jsonb_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, namegt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, namegttext)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, numeric_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, oidgt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, oidvectorgt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, textgtname)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, text_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, time_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timestamp_gt_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timestamp_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timestamp_gt_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timestamptz_gt_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timestamptz_gt_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timestamptz_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, timetz_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, record_gt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterThan, uuid_gt)
|
||||
}
|
||||
|
||||
// boolgt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var boolgt = framework.Function2{
|
||||
Name: "boolgt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bool.Compare(ctx, val1.(bool), val2.(bool))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// bpchargt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bpchargt = framework.Function2{
|
||||
Name: "bpchargt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.BpChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// byteagt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var byteagt = framework.Function2{
|
||||
Name: "byteagt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bytea.Compare(ctx, val1, val2)
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// chargt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var chargt = framework.Function2{
|
||||
Name: "chargt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.InternalChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_gt = framework.Function2{
|
||||
Name: "date_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Date.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_gt_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_gt_timestamp = framework.Function2{
|
||||
Name: "date_gt_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_gt_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_gt_timestamptz = framework.Function2{
|
||||
Name: "date_gt_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var enum_gt = framework.Function2{
|
||||
Name: "enum_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := t[0].Compare(ctx, val1, val2)
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float4gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4gt = framework.Function2{
|
||||
Name: "float4gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float32.Compare(ctx, val1.(float32), val2.(float32))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float48gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48gt = framework.Function2{
|
||||
Name: "float48gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, float64(val1.(float32)), val2.(float64))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float84gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84gt = framework.Function2{
|
||||
Name: "float84gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), float64(val2.(float32)))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float8gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8gt = framework.Function2{
|
||||
Name: "float8gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), val2.(float64))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int2gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2gt = framework.Function2{
|
||||
Name: "int2gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int16.Compare(ctx, val1.(int16), val2.(int16))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int24gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24gt = framework.Function2{
|
||||
Name: "int24gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, int32(val1.(int16)), val2.(int32))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int28gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28gt = framework.Function2{
|
||||
Name: "int28gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int16)), val2.(int64))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int42gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42gt = framework.Function2{
|
||||
Name: "int42gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), int32(val2.(int16)))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int4gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4gt = framework.Function2{
|
||||
Name: "int4gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), val2.(int32))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int48gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48gt = framework.Function2{
|
||||
Name: "int48gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int32)), val2.(int64))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int82gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82gt = framework.Function2{
|
||||
Name: "int82gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int16)))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int84gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84gt = framework.Function2{
|
||||
Name: "int84gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int32)))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int8gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8gt = framework.Function2{
|
||||
Name: "int8gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), val2.(int64))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// interval_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_gt = framework.Function2{
|
||||
Name: "interval_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Interval.Compare(ctx, val1.(duration.Duration), val2.(duration.Duration))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_gt = framework.Function2{
|
||||
Name: "jsonb_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.JsonB.Compare(ctx, val1, val2)
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// namegt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namegt = framework.Function2{
|
||||
Name: "namegt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Name.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// namegttext represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namegttext = framework.Function2{
|
||||
Name: "namegttext",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_gt = framework.Function2{
|
||||
Name: "numeric_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Numeric.Compare(ctx, val1.(*apd.Decimal), val2.(*apd.Decimal))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// oidgt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidgt = framework.Function2{
|
||||
Name: "oidgt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := cmp.Compare(id.Cache().ToOID(val1.(id.Id)), id.Cache().ToOID(val2.(id.Id)))
|
||||
return res == 1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// oidvectorgt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidvectorgt = framework.Function2{
|
||||
Name: "oidvectorgt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oidvector, pgtypes.Oidvector},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oidvector.Compare(ctx, val1.([]any), val2.([]any))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// textgtname represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textgtname = framework.Function2{
|
||||
Name: "textgtname",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// text_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var text_gt = framework.Function2{
|
||||
Name: "text_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1, val2)
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// time_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_gt = framework.Function2{
|
||||
Name: "time_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Time.Compare(ctx, val1.(timeofday.TimeOfDay), val2.(timeofday.TimeOfDay))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_gt_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_gt_date = framework.Function2{
|
||||
Name: "timestamp_gt_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_gt = framework.Function2{
|
||||
Name: "timestamp_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Timestamp.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_gt_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_gt_timestamptz = framework.Function2{
|
||||
Name: "timestamp_gt_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_gt_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_gt_date = framework.Function2{
|
||||
Name: "timestamptz_gt_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == 1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_gt_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_gt_timestamp = framework.Function2{
|
||||
Name: "timestamptz_gt_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_gt = framework.Function2{
|
||||
Name: "timestamptz_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_gt = framework.Function2{
|
||||
Name: "timetz_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimeTZ.Compare(ctx, val1.(timetz.TimeTZ), val2.(timetz.TimeTZ))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
|
||||
// record_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var record_gt = framework.Function2{
|
||||
Name: "record_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Record, pgtypes.Record},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return compare.CompareRecords(ctx, framework.Operator_BinaryGreaterThan, val1, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// uuid_gt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var uuid_gt = framework.Function2{
|
||||
Name: "uuid_gt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Uuid, pgtypes.Uuid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Uuid.Compare(ctx, val1.(uuid.UUID), val2.(uuid.UUID))
|
||||
return res == 1, err
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
"github.com/dolthub/doltgresql/server/compare"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '>=' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryGreaterOrEqual registers the functions to the catalog.
|
||||
func initBinaryGreaterOrEqual() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, boolge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, bpcharge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, byteage)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, charge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, date_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, date_ge_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, date_ge_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, enum_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, float4ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, float48ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, float84ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, float8ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int2ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int24ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int28ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int42ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int4ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int48ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int82ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int84ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, int8ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, interval_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, jsonb_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, namege)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, namegetext)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, numeric_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, oidge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, oidvectorge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, textgename)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, text_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, time_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timestamp_ge_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timestamp_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timestamp_ge_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timestamptz_ge_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timestamptz_ge_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timestamptz_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, timetz_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, record_ge)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryGreaterOrEqual, uuid_ge)
|
||||
}
|
||||
|
||||
// boolge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var boolge = framework.Function2{
|
||||
Name: "boolge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bool.Compare(ctx, val1.(bool), val2.(bool))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bpcharge = framework.Function2{
|
||||
Name: "bpcharge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.BpChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// byteage represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var byteage = framework.Function2{
|
||||
Name: "byteage",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bytea.Compare(ctx, val1.([]byte), val2.([]byte))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// charge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var charge = framework.Function2{
|
||||
Name: "charge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.InternalChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_ge = framework.Function2{
|
||||
Name: "date_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Date.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_ge_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_ge_timestamp = framework.Function2{
|
||||
Name: "date_ge_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res >= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_ge_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_ge_timestamptz = framework.Function2{
|
||||
Name: "date_ge_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res >= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var enum_ge = framework.Function2{
|
||||
Name: "enum_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := t[0].Compare(ctx, val1, val2)
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float4ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4ge = framework.Function2{
|
||||
Name: "float4ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float32.Compare(ctx, val1.(float32), val2.(float32))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float48ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48ge = framework.Function2{
|
||||
Name: "float48ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, float64(val1.(float32)), val2.(float64))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float84ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84ge = framework.Function2{
|
||||
Name: "float84ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), float64(val2.(float32)))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float8ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8ge = framework.Function2{
|
||||
Name: "float8ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), val2.(float64))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int2ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2ge = framework.Function2{
|
||||
Name: "int2ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int16.Compare(ctx, val1.(int16), val2.(int16))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int24ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24ge = framework.Function2{
|
||||
Name: "int24ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, int32(val1.(int16)), val2.(int32))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int28ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28ge = framework.Function2{
|
||||
Name: "int28ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int16)), val2.(int64))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int42ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42ge = framework.Function2{
|
||||
Name: "int42ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), int32(val2.(int16)))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int4ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4ge = framework.Function2{
|
||||
Name: "int4ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), val2.(int32))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int48ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48ge = framework.Function2{
|
||||
Name: "int48ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int32)), val2.(int64))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int82ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82ge = framework.Function2{
|
||||
Name: "int82ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int16)))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int84ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84ge = framework.Function2{
|
||||
Name: "int84ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int32)))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int8ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8ge = framework.Function2{
|
||||
Name: "int8ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), val2.(int64))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// interval_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_ge = framework.Function2{
|
||||
Name: "interval_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Interval.Compare(ctx, val1.(duration.Duration), val2.(duration.Duration))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_ge = framework.Function2{
|
||||
Name: "jsonb_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.JsonB.Compare(ctx, val1, val2)
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// namege represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namege = framework.Function2{
|
||||
Name: "namege",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Name.Compare(ctx, val1.(string), val2.(string))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// namegetext represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namegetext = framework.Function2{
|
||||
Name: "namegetext",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_ge = framework.Function2{
|
||||
Name: "numeric_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Numeric.Compare(ctx, val1.(*apd.Decimal), val2.(*apd.Decimal))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// oidge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidge = framework.Function2{
|
||||
Name: "oidge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := cmp.Compare(id.Cache().ToOID(val1.(id.Id)), id.Cache().ToOID(val2.(id.Id)))
|
||||
return res >= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// oidvectorge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidvectorge = framework.Function2{
|
||||
Name: "oidvectorge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oidvector, pgtypes.Oidvector},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oidvector.Compare(ctx, val1.([]any), val2.([]any))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// textgename represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textgename = framework.Function2{
|
||||
Name: "textgename",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// text_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var text_ge = framework.Function2{
|
||||
Name: "text_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// time_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_ge = framework.Function2{
|
||||
Name: "time_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Time.Compare(ctx, val1.(timeofday.TimeOfDay), val2.(timeofday.TimeOfDay))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_ge_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_ge_date = framework.Function2{
|
||||
Name: "timestamp_ge_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res >= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_ge = framework.Function2{
|
||||
Name: "timestamp_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Timestamp.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_ge_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_ge_timestamptz = framework.Function2{
|
||||
Name: "timestamp_ge_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_ge_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_ge_date = framework.Function2{
|
||||
Name: "timestamptz_ge_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res >= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_ge_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_ge_timestamp = framework.Function2{
|
||||
Name: "timestamptz_ge_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_ge = framework.Function2{
|
||||
Name: "timestamptz_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_ge = framework.Function2{
|
||||
Name: "timetz_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimeTZ.Compare(ctx, val1.(timetz.TimeTZ), val2.(timetz.TimeTZ))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// record_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var record_ge = framework.Function2{
|
||||
Name: "record_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Record, pgtypes.Record},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return compare.CompareRecords(ctx, framework.Operator_BinaryGreaterOrEqual, val1, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// uuid_ge represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var uuid_ge = framework.Function2{
|
||||
Name: "uuid_ge",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Uuid, pgtypes.Uuid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Uuid.Compare(ctx, val1.(uuid.UUID), val2.(uuid.UUID))
|
||||
return res >= 0, err
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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 binary
|
||||
|
||||
// Init initializes all binary operators in this package.
|
||||
func Init() {
|
||||
initBinaryBitAnd()
|
||||
initBinaryBitOr()
|
||||
initBinaryBitXor()
|
||||
initBinaryConcatenate()
|
||||
initBinaryDivide()
|
||||
initBinaryEqual()
|
||||
initBinaryGreaterOrEqual()
|
||||
initBinaryGreaterThan()
|
||||
initBinaryLessOrEqual()
|
||||
initBinaryLessThan()
|
||||
initBinaryMinus()
|
||||
initBinaryMod()
|
||||
initBinaryMultiply()
|
||||
initBinaryNotEqual()
|
||||
initBinaryPlus()
|
||||
initBinaryShiftLeft()
|
||||
initBinaryShiftRight()
|
||||
initJSON()
|
||||
}
|
||||
@@ -0,0 +1,849 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// makeObjectKeyPath builds a JSON path that selects the given top-level
|
||||
// object key, quoting and escaping the key so any characters are allowed.
|
||||
func makeObjectKeyPath(key string) string {
|
||||
return "$" + objectKeyPathSegment(key)
|
||||
}
|
||||
|
||||
// objectKeyPathSegment builds a single object-key step (e.g. `."key"`), quoting
|
||||
// and escaping the key so any characters are allowed. Used both on its own and
|
||||
// when composing a multi-step JSON path.
|
||||
func objectKeyPathSegment(key string) string {
|
||||
return `."` + strings.ReplaceAll(key, `"`, `\"`) + `"`
|
||||
}
|
||||
|
||||
// makeArrayIndexPath builds a JSON path that selects the given top-level
|
||||
// array element by non-negative index.
|
||||
func makeArrayIndexPath(idx int) string {
|
||||
return "$" + arrayIndexPathSegment(idx)
|
||||
}
|
||||
|
||||
// arrayIndexPathSegment builds a single array-index step (e.g. `[3]`). Used both
|
||||
// on its own and when composing a multi-step JSON path.
|
||||
func arrayIndexPathSegment(idx int) string {
|
||||
return "[" + strconv.Itoa(idx) + "]"
|
||||
}
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = <OPERATOR> ORDER BY o.oprcode::varchar;
|
||||
// Replace <OPERATOR> with the desired JSON operator
|
||||
|
||||
// initJSON registers the functions to the catalog.
|
||||
func initJSON() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractJson, json_array_element)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractJson, jsonb_array_element)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractJson, json_object_field)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractJson, jsonb_object_field)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractText, json_array_element_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractText, jsonb_array_element_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractText, json_object_field_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractText, jsonb_object_field_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractPathJson, json_extract_path)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractPathJson, jsonb_extract_path)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractPathText, json_extract_path_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONExtractPathText, jsonb_extract_path_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONContainsRight, jsonb_contains)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONContainsLeft, jsonb_contained)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONTopLevel, jsonb_exists)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONTopLevelAny, jsonb_exists_any)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryJSONTopLevelAll, jsonb_exists_all)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, jsonb_delete_text)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, jsonb_delete_text_array)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, jsonb_delete_int32)
|
||||
}
|
||||
|
||||
// toJSONWrapper converts a JSON value (either string or sql.JSONWrapper) to a sql.JSONWrapper.
|
||||
func toJSONWrapper(ctx *sql.Context, val any) (sql.JSONWrapper, error) {
|
||||
switch v := val.(type) {
|
||||
case sql.JSONWrapper:
|
||||
return v, nil
|
||||
case sql.StringWrapper:
|
||||
s, err := v.Unwrap(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc, err := pgtypes.JsonB.IoInput(ctx, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if doc == nil {
|
||||
return nil, nil
|
||||
}
|
||||
w, ok := doc.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected type from IoInput: %T", doc)
|
||||
}
|
||||
return w, nil
|
||||
case string:
|
||||
doc, err := pgtypes.JsonB.IoInput(ctx, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if doc == nil {
|
||||
return nil, nil
|
||||
}
|
||||
w, ok := doc.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected type from IoInput: %T", doc)
|
||||
}
|
||||
return w, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unexpected type for JSON operation: %T", val)
|
||||
}
|
||||
}
|
||||
|
||||
// jsonWrapperElementToText converts a JSON element (sql.JSONWrapper) to its text representation.
|
||||
// For string values, it returns the raw string without quotes. For other types, it returns the JSON representation.
|
||||
func jsonWrapperElementToText(ctx *sql.Context, wrapper sql.JSONWrapper) (string, error) {
|
||||
v, err := wrapper.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if v == nil {
|
||||
return "", nil
|
||||
}
|
||||
if s, ok := v.(string); ok {
|
||||
return s, nil
|
||||
}
|
||||
return types.JSONDocument{Val: v}.JSONString()
|
||||
}
|
||||
|
||||
// json_array_element represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var json_array_element = framework.Function2{
|
||||
Name: "json_array_element",
|
||||
Return: pgtypes.Json,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Json, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
newVal, err := toJSONWrapper(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var unusedTypes [3]*pgtypes.DoltgresType
|
||||
retVal, err := jsonb_array_element_callable(ctx, unusedTypes, newVal, val2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if retVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return pgtypes.JsonB.IoOutput(ctx, retVal)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_array_element represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_array_element = framework.Function2{
|
||||
Name: "jsonb_array_element",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: jsonb_array_element_callable,
|
||||
}
|
||||
|
||||
func jsonb_array_element_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
wrapper, ok := val1.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
idx := int(val2.(int32))
|
||||
// Fast path: for a ComparableJSON wrapper backed by an indexed JSON
|
||||
// array, use Lookup to fetch the element without materializing the
|
||||
// entire array. Negative indices need the array length, so they fall
|
||||
// through to the materialized path even on indexed values.
|
||||
if comparable, ok := wrapper.(types.ComparableJSON); ok {
|
||||
jt, err := comparable.JsonType(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if jt != "ARRAY" {
|
||||
return nil, nil
|
||||
}
|
||||
if idx >= 0 {
|
||||
result, err := types.LookupJSONValue(ctx, wrapper, makeArrayIndexPath(idx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
// Materialized fallback: covers wrappers that don't implement
|
||||
// ComparableJSON (e.g. literal jsonb values) and the negative-index
|
||||
// case on ComparableJSON arrays.
|
||||
v, err := wrapper.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
array, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
if idx < 0 {
|
||||
idx += len(array)
|
||||
}
|
||||
if idx < 0 || idx >= len(array) {
|
||||
return nil, nil
|
||||
}
|
||||
return types.JSONDocument{Val: array[idx]}, nil
|
||||
}
|
||||
|
||||
// json_object_field represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var json_object_field = framework.Function2{
|
||||
Name: "json_object_field",
|
||||
Return: pgtypes.Json,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Json, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: make a bespoke implementation that preserves whitespace
|
||||
newVal, err := toJSONWrapper(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var unusedTypes [3]*pgtypes.DoltgresType
|
||||
retVal, err := jsonb_object_field.Callable(ctx, unusedTypes, newVal, val2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if retVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return pgtypes.JsonB.IoOutput(ctx, retVal)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_object_field represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_object_field = framework.Function2{
|
||||
Name: "jsonb_object_field",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
wrapper, ok := val1.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
key := val2.(string)
|
||||
// Fast path: for a ComparableJSON wrapper backed by an indexed JSON
|
||||
// object, use Lookup to fetch the value without materializing the
|
||||
// entire document.
|
||||
if isObj, err := isComparableJsonObject(ctx, wrapper); err != nil {
|
||||
return nil, err
|
||||
} else if isObj {
|
||||
result, err := types.LookupJSONValue(ctx, wrapper, makeObjectKeyPath(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
// Materialized fallback: covers wrappers that don't implement
|
||||
// ComparableJSON (e.g. literal jsonb values), where the embedded
|
||||
// jsonpath library has trouble with edge cases like keys that
|
||||
// contain escaped quotes, or array operands with text paths.
|
||||
v, err := wrapper.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
val, ok := obj[key]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return types.JSONDocument{Val: val}, nil
|
||||
},
|
||||
}
|
||||
|
||||
// json_array_element_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var json_array_element_text = framework.Function2{
|
||||
Name: "json_array_element_text",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Json, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: make a bespoke implementation that preserves whitespace
|
||||
newVal, err := toJSONWrapper(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var unusedTypes [3]*pgtypes.DoltgresType
|
||||
return jsonb_array_element_text.Callable(ctx, unusedTypes, newVal, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_array_element_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_array_element_text = framework.Function2{
|
||||
Name: "jsonb_array_element_text",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, dt [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
elem, err := jsonb_array_element_callable(ctx, dt, val1, val2)
|
||||
if err != nil || elem == nil {
|
||||
return nil, err
|
||||
}
|
||||
wrapper, ok := elem.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return jsonWrapperElementToText(ctx, wrapper)
|
||||
},
|
||||
}
|
||||
|
||||
// json_object_field_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var json_object_field_text = framework.Function2{
|
||||
Name: "json_object_field_text",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Json, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: make a bespoke implementation that preserves whitespace
|
||||
newVal, err := toJSONWrapper(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var unusedTypes [3]*pgtypes.DoltgresType
|
||||
return jsonb_object_field_text.Callable(ctx, unusedTypes, newVal, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_object_field_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_object_field_text = framework.Function2{
|
||||
Name: "jsonb_object_field_text",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, dt [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
elem, err := jsonb_object_field.Callable(ctx, dt, val1, val2)
|
||||
if err != nil || elem == nil {
|
||||
return nil, err
|
||||
}
|
||||
wrapper, ok := elem.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return jsonWrapperElementToText(ctx, wrapper)
|
||||
},
|
||||
}
|
||||
|
||||
// json_extract_path represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var json_extract_path = framework.Function2{
|
||||
Name: "json_extract_path",
|
||||
Return: pgtypes.Json,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Json, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: make a bespoke implementation that preserves whitespace
|
||||
newVal, err := toJSONWrapper(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var unusedTypes [3]*pgtypes.DoltgresType
|
||||
retVal, err := jsonb_extract_path_callable(ctx, unusedTypes, newVal, val2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if retVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return pgtypes.JsonB.IoOutput(ctx, retVal)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_extract_path represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_extract_path = framework.Function2{
|
||||
Name: "jsonb_extract_path",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: jsonb_extract_path_callable,
|
||||
}
|
||||
|
||||
func jsonb_extract_path_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
cur, ok := val1.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
paths := val2.([]interface{})
|
||||
|
||||
// Fast path: for an indexed JSON document, build a single JSON path spanning
|
||||
// every step and resolve it in one traversal via SearchableJSON.Lookup,
|
||||
if _, ok := cur.(types.SearchableJSON); ok && len(paths) > 0 {
|
||||
result, authoritative, err := extractJsonPathBySingleLookup(ctx, cur, paths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if authoritative {
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
if cur == nil {
|
||||
return nil, nil
|
||||
}
|
||||
textPath, ok := path.(string)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
next, err := extractOneJsonPathStep(ctx, cur, textPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if next == nil {
|
||||
return nil, nil
|
||||
}
|
||||
cur = next
|
||||
}
|
||||
return cur, nil
|
||||
}
|
||||
|
||||
// extractJsonPathBySingleLookup resolves returns the path in a JSON document by building a JSON path expression
|
||||
// from the text slice provided.
|
||||
// For numeric values, there's ambiguity as to whether they should be interpreted as array indices or object keys,
|
||||
// so a nil result in that case is not authoritative and the caller should fall back to the step-by-step walk.
|
||||
func extractJsonPathBySingleLookup(ctx *sql.Context, doc sql.JSONWrapper, paths []interface{}) (sql.JSONWrapper, bool, error) {
|
||||
var sb strings.Builder
|
||||
sb.WriteString("$")
|
||||
ambiguous := false
|
||||
for _, path := range paths {
|
||||
var textPath string
|
||||
switch path := path.(type) {
|
||||
case string:
|
||||
textPath = path
|
||||
case sql.StringWrapper:
|
||||
var err error
|
||||
textPath, err = path.Unwrap(ctx)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
case nil:
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
if idx, err := strconv.Atoi(textPath); err == nil {
|
||||
// An integer element could be an array index or a numeric object
|
||||
// key (or a negative index), so the lookup result isn't conclusive.
|
||||
ambiguous = true
|
||||
if idx >= 0 {
|
||||
sb.WriteString(arrayIndexPathSegment(idx))
|
||||
continue
|
||||
}
|
||||
}
|
||||
sb.WriteString(objectKeyPathSegment(textPath))
|
||||
}
|
||||
result, err := types.LookupJSONValue(ctx, doc, sb.String())
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
// A non-nil result could still be semantically NULL
|
||||
i, err := result.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if ia, ok := i.([]interface{}); ok && len(ia) == 0 {
|
||||
return nil, !ambiguous, nil
|
||||
}
|
||||
|
||||
return result, true, nil
|
||||
}
|
||||
|
||||
return nil, !ambiguous, nil
|
||||
}
|
||||
|
||||
// extractOneJsonPathStep performs a single Postgres-style extract step against
|
||||
// the given JSON wrapper. The text is treated as a key when the wrapper is an
|
||||
// object and as an integer index when the wrapper is an array. Returns nil if
|
||||
// the step cannot be resolved (missing key, out-of-range index, scalar
|
||||
// wrapper, or non-integer text on an array).
|
||||
func extractOneJsonPathStep(ctx *sql.Context, cur sql.JSONWrapper, textPath string) (sql.JSONWrapper, error) {
|
||||
// Fast path: use the ComparableJSON.JsonType / SearchableJSON.Lookup
|
||||
// interfaces to avoid materializing the entire document.
|
||||
if comparable, ok := cur.(types.ComparableJSON); ok {
|
||||
jt, err := comparable.JsonType(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch jt {
|
||||
case "OBJECT":
|
||||
return types.LookupJSONValue(ctx, cur, makeObjectKeyPath(textPath))
|
||||
case "ARRAY":
|
||||
idx, err := strconv.Atoi(textPath)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if idx >= 0 {
|
||||
return types.LookupJSONValue(ctx, cur, makeArrayIndexPath(idx))
|
||||
}
|
||||
// Negative indices count from the end and require knowing the
|
||||
// array length, so fall through to the materialized path below.
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
// Materialized fallback for wrappers that don't implement ComparableJSON,
|
||||
// and for negative array indices on ones that do.
|
||||
v, err := cur.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch currentValue := v.(type) {
|
||||
case map[string]interface{}:
|
||||
next, ok := currentValue[textPath]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return types.JSONDocument{Val: next}, nil
|
||||
case []interface{}:
|
||||
idx, err := strconv.Atoi(textPath)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
if idx < 0 {
|
||||
idx += len(currentValue)
|
||||
}
|
||||
if idx < 0 || idx >= len(currentValue) {
|
||||
return nil, nil
|
||||
}
|
||||
return types.JSONDocument{Val: currentValue[idx]}, nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// json_extract_path_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var json_extract_path_text = framework.Function2{
|
||||
Name: "json_extract_path_text",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Json, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: make a bespoke implementation that preserves whitespace
|
||||
newVal, err := toJSONWrapper(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if newVal == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var unusedTypes [3]*pgtypes.DoltgresType
|
||||
return jsonb_extract_path_text_callable(ctx, unusedTypes, newVal, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_extract_path_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_extract_path_text = framework.Function2{
|
||||
Name: "jsonb_extract_path_text",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: jsonb_extract_path_text_callable,
|
||||
}
|
||||
|
||||
func jsonb_extract_path_text_callable(ctx *sql.Context, dt [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
elem, err := jsonb_extract_path_callable(ctx, dt, val1, val2)
|
||||
if err != nil || elem == nil {
|
||||
return nil, err
|
||||
}
|
||||
wrapper, ok := elem.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
return jsonWrapperElementToText(ctx, wrapper)
|
||||
}
|
||||
|
||||
// jsonb_contains represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_contains = framework.Function2{
|
||||
Name: "jsonb_contains",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return nil, errors.Errorf("JSON contains is not yet supported")
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_contained represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_contained = framework.Function2{
|
||||
Name: "jsonb_contained",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, dt [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return jsonb_contains.Callable(ctx, dt, val2, val1)
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_exists represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_exists = framework.Function2{
|
||||
Name: "jsonb_exists",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
wrapper, ok := val1.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
key := val2.(string)
|
||||
// Fast path: for an indexed JSON object, use Lookup to test for the
|
||||
// key without materializing the document.
|
||||
if isObj, err := isComparableJsonObject(ctx, wrapper); err != nil {
|
||||
return nil, err
|
||||
} else if isObj {
|
||||
found, err := types.LookupJSONValue(ctx, wrapper, makeObjectKeyPath(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return found != nil, nil
|
||||
}
|
||||
value, err := wrapper.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case map[string]interface{}:
|
||||
_, ok := v[key]
|
||||
return ok, nil
|
||||
case []interface{}:
|
||||
for _, item := range v {
|
||||
if s, ok := item.(string); ok && s == key {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
case string:
|
||||
return v == key, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// isComparableJsonObject reports whether the JSON wrapper is a JSON object and
|
||||
// also implements types.ComparableJSON
|
||||
func isComparableJsonObject(ctx *sql.Context, wrapper sql.JSONWrapper) (bool, error) {
|
||||
comparable, ok := wrapper.(types.ComparableJSON)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
jt, err := comparable.JsonType(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return jt == "OBJECT", nil
|
||||
}
|
||||
|
||||
// jsonb_exists_any represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_exists_any = framework.Function2{
|
||||
Name: "jsonb_exists_any",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
wrapper, ok := val1.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
keys := val2.([]interface{})
|
||||
// Fast path: for an indexed JSON object, test each key with Lookup
|
||||
// instead of materializing the document.
|
||||
if isObj, err := isComparableJsonObject(ctx, wrapper); err != nil {
|
||||
return nil, err
|
||||
} else if isObj {
|
||||
for _, key := range keys {
|
||||
found, err := types.LookupJSONValue(ctx, wrapper, makeObjectKeyPath(key.(string)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found != nil {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
value, err := wrapper.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case map[string]interface{}:
|
||||
for _, key := range keys {
|
||||
if _, ok := v[key.(string)]; ok {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
case []interface{}:
|
||||
for _, key := range keys {
|
||||
for _, item := range v {
|
||||
if s, ok := item.(string); ok && s == key.(string) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
case string:
|
||||
for _, key := range keys {
|
||||
if v == key.(string) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_exists_all represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_exists_all = framework.Function2{
|
||||
Name: "jsonb_exists_all",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
wrapper, ok := val1.(sql.JSONWrapper)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
keys := val2.([]interface{})
|
||||
// Fast path: for an indexed JSON object, test each key with Lookup
|
||||
// instead of materializing the document.
|
||||
if isObj, err := isComparableJsonObject(ctx, wrapper); err != nil {
|
||||
return nil, err
|
||||
} else if isObj {
|
||||
for _, key := range keys {
|
||||
found, err := types.LookupJSONValue(ctx, wrapper, makeObjectKeyPath(key.(string)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found == nil {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
value, err := wrapper.ToInterface(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch v := value.(type) {
|
||||
case map[string]interface{}:
|
||||
for _, key := range keys {
|
||||
if _, ok := v[key.(string)]; !ok {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case []interface{}:
|
||||
for _, key := range keys {
|
||||
found := false
|
||||
for _, item := range v {
|
||||
if s, ok := item.(string); ok && s == key.(string) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
case string:
|
||||
for _, key := range keys {
|
||||
if v != key.(string) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
return false, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_delete_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_delete_text = framework.Function2{
|
||||
Name: "jsonb_delete",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return nil, errors.Errorf("JSON deletions are not yet supported")
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_delete_text_array represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_delete_text_array = framework.Function2{
|
||||
Name: "jsonb_delete",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.TextArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return nil, errors.Errorf("JSON deletions are not yet supported")
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_delete_int32 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_delete_int32 = framework.Function2{
|
||||
Name: "jsonb_delete",
|
||||
Return: pgtypes.JsonB,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return nil, errors.Errorf("JSON deletions are not yet supported")
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
"github.com/dolthub/doltgresql/server/compare"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '<' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryLessThan registers the functions to the catalog.
|
||||
func initBinaryLessThan() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, boollt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, bpcharlt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, bytealt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, charlt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, date_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, date_lt_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, date_lt_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, enum_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, float4lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, float48lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, float84lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, float8lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int2lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int24lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int28lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int42lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int4lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int48lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int82lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int84lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, int8lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, interval_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, jsonb_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, namelt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, namelttext)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, numeric_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, oidlt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, oidvectorlt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, textltname)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, text_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, time_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timestamp_lt_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timestamp_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timestamp_lt_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timestamptz_lt_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timestamptz_lt_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timestamptz_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, timetz_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, record_lt)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessThan, uuid_lt)
|
||||
}
|
||||
|
||||
// boollt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var boollt = framework.Function2{
|
||||
Name: "boollt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bool.Compare(ctx, val1.(bool), val2.(bool))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharlt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bpcharlt = framework.Function2{
|
||||
Name: "bpcharlt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.BpChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// bytealt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bytealt = framework.Function2{
|
||||
Name: "bytealt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bytea.Compare(ctx, val1, val2)
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// charlt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var charlt = framework.Function2{
|
||||
Name: "charlt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.InternalChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_lt = framework.Function2{
|
||||
Name: "date_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Date.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_lt_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_lt_timestamp = framework.Function2{
|
||||
Name: "date_lt_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == -1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_lt_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_lt_timestamptz = framework.Function2{
|
||||
Name: "date_lt_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == -1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var enum_lt = framework.Function2{
|
||||
Name: "enum_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := t[0].Compare(ctx, val1, val2)
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float4lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4lt = framework.Function2{
|
||||
Name: "float4lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float32.Compare(ctx, val1.(float32), val2.(float32))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float48lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48lt = framework.Function2{
|
||||
Name: "float48lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, float64(val1.(float32)), val2.(float64))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float84lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84lt = framework.Function2{
|
||||
Name: "float84lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), float64(val2.(float32)))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// float8lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8lt = framework.Function2{
|
||||
Name: "float8lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), val2.(float64))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int2lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2lt = framework.Function2{
|
||||
Name: "int2lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int16.Compare(ctx, val1.(int16), val2.(int16))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int24lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24lt = framework.Function2{
|
||||
Name: "int24lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, int32(val1.(int16)), val2.(int32))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int28lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28lt = framework.Function2{
|
||||
Name: "int28lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int16)), val2.(int64))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int42lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42lt = framework.Function2{
|
||||
Name: "int42lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), int32(val2.(int16)))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int4lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4lt = framework.Function2{
|
||||
Name: "int4lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), val2.(int32))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int48lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48lt = framework.Function2{
|
||||
Name: "int48lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int32)), val2.(int64))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int82lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82lt = framework.Function2{
|
||||
Name: "int82lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int16)))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int84lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84lt = framework.Function2{
|
||||
Name: "int84lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int32)))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// int8lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8lt = framework.Function2{
|
||||
Name: "int8lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), val2.(int64))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// interval_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_lt = framework.Function2{
|
||||
Name: "interval_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Interval.Compare(ctx, val1.(duration.Duration), val2.(duration.Duration))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_lt = framework.Function2{
|
||||
Name: "jsonb_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.JsonB.Compare(ctx, val1, val2)
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// namelt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namelt = framework.Function2{
|
||||
Name: "namelt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Name.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// namelttext represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namelttext = framework.Function2{
|
||||
Name: "namelttext",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_lt = framework.Function2{
|
||||
Name: "numeric_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Numeric.Compare(ctx, val1.(*apd.Decimal), val2.(*apd.Decimal))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// oidlt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidlt = framework.Function2{
|
||||
Name: "oidlt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := cmp.Compare(id.Cache().ToOID(val1.(id.Id)), id.Cache().ToOID(val2.(id.Id)))
|
||||
return res == -1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// oidvectorlt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidvectorlt = framework.Function2{
|
||||
Name: "oidvectorlt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oidvector, pgtypes.Oidvector},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oidvector.Compare(ctx, val1.([]any), val2.([]any))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// textltname represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textltname = framework.Function2{
|
||||
Name: "textltname",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// text_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var text_lt = framework.Function2{
|
||||
Name: "text_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1, val2)
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// time_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_lt = framework.Function2{
|
||||
Name: "time_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Time.Compare(ctx, val1.(timeofday.TimeOfDay), val2.(timeofday.TimeOfDay))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_lt_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_lt_date = framework.Function2{
|
||||
Name: "timestamp_lt_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == -1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_lt = framework.Function2{
|
||||
Name: "timestamp_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Timestamp.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_lt_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_lt_timestamptz = framework.Function2{
|
||||
Name: "timestamp_lt_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_lt_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_lt_date = framework.Function2{
|
||||
Name: "timestamptz_lt_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res == -1, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_lt_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_lt_timestamp = framework.Function2{
|
||||
Name: "timestamptz_lt_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_lt = framework.Function2{
|
||||
Name: "timestamptz_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_lt = framework.Function2{
|
||||
Name: "timetz_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimeTZ.Compare(ctx, val1.(timetz.TimeTZ), val2.(timetz.TimeTZ))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
|
||||
// record_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var record_lt = framework.Function2{
|
||||
Name: "record_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Record, pgtypes.Record},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return compare.CompareRecords(ctx, framework.Operator_BinaryLessThan, val1, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// uuid_lt represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var uuid_lt = framework.Function2{
|
||||
Name: "uuid_lt",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Uuid, pgtypes.Uuid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Uuid.Compare(ctx, val1.(uuid.UUID), val2.(uuid.UUID))
|
||||
return res == -1, err
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,558 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
"github.com/dolthub/doltgresql/server/compare"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '<=' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryLessOrEqual registers the functions to the catalog.
|
||||
func initBinaryLessOrEqual() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, boolle)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, bpcharle)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, byteale)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, charle)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, date_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, date_le_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, date_le_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, enum_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, float4le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, float48le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, float84le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, float8le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int2le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int24le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int28le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int42le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int4le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int48le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int82le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int84le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, int8le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, interval_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, jsonb_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, namele)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, nameletext)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, numeric_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, oidle)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, oidvectorle)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, textlename)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, text_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, time_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timestamp_le_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timestamp_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timestamp_le_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timestamptz_le_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timestamptz_le_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timestamptz_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, timetz_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, record_le)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryLessOrEqual, uuid_le)
|
||||
}
|
||||
|
||||
// boolle represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var boolle = framework.Function2{
|
||||
Name: "boolle",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bool.Compare(ctx, val1.(bool), val2.(bool))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharle represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bpcharle = framework.Function2{
|
||||
Name: "bpcharle",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.BpChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// byteale represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var byteale = framework.Function2{
|
||||
Name: "byteale",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bytea.Compare(ctx, val1.([]byte), val2.([]byte))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// charle represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var charle = framework.Function2{
|
||||
Name: "charle",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.InternalChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_le = framework.Function2{
|
||||
Name: "date_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Date.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_le_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_le_timestamp = framework.Function2{
|
||||
Name: "date_le_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res <= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_le_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_le_timestamptz = framework.Function2{
|
||||
Name: "date_le_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res <= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var enum_le = framework.Function2{
|
||||
Name: "enum_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := t[0].Compare(ctx, val1, val2)
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float4le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4le = framework.Function2{
|
||||
Name: "float4le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float32.Compare(ctx, val1.(float32), val2.(float32))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float48le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48le = framework.Function2{
|
||||
Name: "float48le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, float64(val1.(float32)), val2.(float64))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float84le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84le = framework.Function2{
|
||||
Name: "float84le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), float64(val2.(float32)))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float8le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8le = framework.Function2{
|
||||
Name: "float8le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), val2.(float64))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int2le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2le = framework.Function2{
|
||||
Name: "int2le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int16.Compare(ctx, val1.(int16), val2.(int16))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int24le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24le = framework.Function2{
|
||||
Name: "int24le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, int32(val1.(int16)), val2.(int32))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int28le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28le = framework.Function2{
|
||||
Name: "int28le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int16)), val2.(int64))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int42le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42le = framework.Function2{
|
||||
Name: "int42le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), int32(val2.(int16)))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int4le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4le = framework.Function2{
|
||||
Name: "int4le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), val2.(int32))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int48le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48le = framework.Function2{
|
||||
Name: "int48le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int32)), val2.(int64))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int82le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82le = framework.Function2{
|
||||
Name: "int82le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int16)))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int84le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84le = framework.Function2{
|
||||
Name: "int84le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int32)))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int8le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8le = framework.Function2{
|
||||
Name: "int8le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), val2.(int64))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// interval_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_le = framework.Function2{
|
||||
Name: "interval_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Interval.Compare(ctx, val1.(duration.Duration), val2.(duration.Duration))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_le = framework.Function2{
|
||||
Name: "jsonb_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.JsonB.Compare(ctx, val1, val2)
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// namele represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namele = framework.Function2{
|
||||
Name: "namele",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Name.Compare(ctx, val1.(string), val2.(string))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// nameletext represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var nameletext = framework.Function2{
|
||||
Name: "nameletext",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_le = framework.Function2{
|
||||
Name: "numeric_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Numeric.Compare(ctx, val1.(*apd.Decimal), val2.(*apd.Decimal))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// oidle represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidle = framework.Function2{
|
||||
Name: "oidle",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := cmp.Compare(id.Cache().ToOID(val1.(id.Id)), id.Cache().ToOID(val2.(id.Id)))
|
||||
return res <= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// oidvectorle represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidvectorle = framework.Function2{
|
||||
Name: "oidvectorle",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oidvector, pgtypes.Oidvector},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oidvector.Compare(ctx, val1.([]any), val2.([]any))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// textlename represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textlename = framework.Function2{
|
||||
Name: "textlename",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// text_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var text_le = framework.Function2{
|
||||
Name: "text_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// time_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_le = framework.Function2{
|
||||
Name: "time_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Time.Compare(ctx, val1.(timeofday.TimeOfDay), val2.(timeofday.TimeOfDay))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_le_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_le_date = framework.Function2{
|
||||
Name: "timestamp_le_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res <= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_le = framework.Function2{
|
||||
Name: "timestamp_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Timestamp.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_le_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_le_timestamptz = framework.Function2{
|
||||
Name: "timestamp_le_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_le_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_le_date = framework.Function2{
|
||||
Name: "timestamptz_le_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res <= 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_le_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_le_timestamp = framework.Function2{
|
||||
Name: "timestamptz_le_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_le = framework.Function2{
|
||||
Name: "timestamptz_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_le = framework.Function2{
|
||||
Name: "timetz_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimeTZ.Compare(ctx, val1.(timetz.TimeTZ), val2.(timetz.TimeTZ))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// record_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var record_le = framework.Function2{
|
||||
Name: "record_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Record, pgtypes.Record},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return compare.CompareRecords(ctx, framework.Operator_BinaryLessOrEqual, val1, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// uuid_le represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var uuid_le = framework.Function2{
|
||||
Name: "uuid_le",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Uuid, pgtypes.Uuid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Uuid.Compare(ctx, val1.(uuid.UUID), val2.(uuid.UUID))
|
||||
return res <= 0, err
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,429 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '-' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryMinus registers the functions to the catalog.
|
||||
func initBinaryMinus() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, date_mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, date_mii)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, date_mi_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, float4mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, float48mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, float8mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, float84mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int2mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int24mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int28mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int4mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int42mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int48mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int8mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int82mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, int84mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, interval_mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, numeric_sub)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, time_mi_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, time_mi_time)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, timetz_mi_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, timestamp_mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, timestamp_mi_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, timestamptz_mi)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMinus, timestamptz_mi_interval)
|
||||
}
|
||||
|
||||
// float4mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4mi = framework.Function2{
|
||||
Name: "float4mi",
|
||||
Return: pgtypes.Float32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float32) - val2.(float32), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float48mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48mi = framework.Function2{
|
||||
Name: "float48mi",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return float64(val1.(float32)) - val2.(float64), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float8mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8mi = framework.Function2{
|
||||
Name: "float8mi",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float64) - val2.(float64), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float84mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84mi = framework.Function2{
|
||||
Name: "float84mi",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float64) - float64(val2.(float32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int2mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2mi = framework.Function2{
|
||||
Name: "int2mi",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int16)) - int64(val2.(int16))
|
||||
if result > math.MaxInt16 || result < math.MinInt16 {
|
||||
return nil, errors.Errorf("smallint out of range")
|
||||
}
|
||||
return int16(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int24mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24mi = framework.Function2{
|
||||
Name: "int24mi",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int16)) - int64(val2.(int32))
|
||||
if result > math.MaxInt16 || result < math.MinInt16 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int28mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28mi = framework.Function2{
|
||||
Name: "int28mi",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return minusOverflow(int64(val1.(int16)), val2.(int64))
|
||||
},
|
||||
}
|
||||
|
||||
// int4mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4mi = framework.Function2{
|
||||
Name: "int4mi",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int32)) - int64(val2.(int32))
|
||||
if result > math.MaxInt32 || result < math.MinInt32 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int42mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42mi = framework.Function2{
|
||||
Name: "int42mi",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int32)) - int64(val2.(int16))
|
||||
if result > math.MaxInt32 || result < math.MinInt32 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int48mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48mi = framework.Function2{
|
||||
Name: "int48mi",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return minusOverflow(int64(val1.(int32)), val2.(int64))
|
||||
},
|
||||
}
|
||||
|
||||
// int8mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8mi = framework.Function2{
|
||||
Name: "int8mi",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return minusOverflow(val1.(int64), val2.(int64))
|
||||
},
|
||||
}
|
||||
|
||||
// int82mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82mi = framework.Function2{
|
||||
Name: "int82mi",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return minusOverflow(val1.(int64), int64(val2.(int16)))
|
||||
},
|
||||
}
|
||||
|
||||
// int84mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84mi = framework.Function2{
|
||||
Name: "int84mi",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return minusOverflow(val1.(int64), int64(val2.(int32)))
|
||||
},
|
||||
}
|
||||
|
||||
// interval_mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_mi = framework.Function2{
|
||||
Name: "interval_mi",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
dur1 := val1.(duration.Duration)
|
||||
dur2 := val2.(duration.Duration)
|
||||
return dur1.Sub(dur2), nil
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_sub represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_sub = framework.Function2{
|
||||
Name: "numeric_sub",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
num1 := val1.(*apd.Decimal)
|
||||
num2 := val2.(*apd.Decimal)
|
||||
res := new(apd.Decimal)
|
||||
p := uint32(math.Max(float64(num1.NumDigits()), float64(num2.NumDigits())+
|
||||
math.Max(math.Abs(float64(num1.Exponent)), math.Abs(float64(num2.Exponent)))))
|
||||
_, err := sql.DecimalCtx.WithPrecision(p).Sub(res, num1, num2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_mi = framework.Function2{
|
||||
Name: "date_mi",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date1 := val1.(time.Time)
|
||||
date2 := val2.(time.Time)
|
||||
|
||||
// Calculate the difference in days
|
||||
duration := date1.Sub(date2)
|
||||
days := int32(duration.Hours() / 24)
|
||||
|
||||
return days, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_mii represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_mii = framework.Function2{
|
||||
Name: "date_mii",
|
||||
Return: pgtypes.Date,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date := val1.(time.Time)
|
||||
days := val2.(int32)
|
||||
|
||||
// Subtract the specified number of days from the date
|
||||
result := date.AddDate(0, 0, -int(days))
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_mi_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_mi_interval = framework.Function2{
|
||||
Name: "date_mi_interval",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date := val1.(time.Time)
|
||||
interval := val2.(duration.Duration)
|
||||
seconds, ok := interval.AsInt64()
|
||||
if !ok {
|
||||
return nil, errors.New("overflown interval")
|
||||
}
|
||||
// above truncates partial seconds.
|
||||
nanos := seconds*duration.NanosPerMicro*duration.MicrosPerMilli*duration.MillisPerSec + interval.Nanos()%int64(time.Second)
|
||||
// Subtract the interval from the date using negative duration
|
||||
result := date.Add(-time.Duration(nanos))
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_mi = framework.Function2{
|
||||
Name: "timestamptz_mi",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
ts1 := val1.(time.Time)
|
||||
ts2 := val2.(time.Time)
|
||||
|
||||
// Calculate the difference and return as interval
|
||||
diff := ts1.Sub(ts2)
|
||||
return duration.MakeDuration(diff.Nanoseconds(), 0, 0), nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_mi_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_mi_interval = framework.Function2{
|
||||
Name: "timestamptz_mi_interval",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timestamptz := val1.(time.Time)
|
||||
interval := val2.(duration.Duration)
|
||||
|
||||
// Subtract the interval from the timestamptz
|
||||
return timestamptz.Add(-time.Duration(interval.Nanos())), nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_mi represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_mi = framework.Function2{
|
||||
Name: "timestamp_mi",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
ts1 := val1.(time.Time)
|
||||
ts2 := val2.(time.Time)
|
||||
|
||||
// Calculate the difference and return as interval
|
||||
diff := ts1.Sub(ts2)
|
||||
return duration.MakeDuration(diff.Nanoseconds(), 0, 0), nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_mi_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_mi_interval = framework.Function2{
|
||||
Name: "timestamp_mi_interval",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timestamp := val1.(time.Time)
|
||||
interval := val2.(duration.Duration)
|
||||
|
||||
// Subtract the interval from the timestamp
|
||||
return timestamp.Add(-time.Duration(interval.Nanos())), nil
|
||||
},
|
||||
}
|
||||
|
||||
// time_mi_time represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_mi_time = framework.Function2{
|
||||
Name: "time_mi_time",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
time1 := val1.(timeofday.TimeOfDay)
|
||||
time2 := val2.(timeofday.TimeOfDay)
|
||||
|
||||
// Calculate the difference and return as interval
|
||||
return timeofday.TimeOfDay(int64(time1) - int64(time2)).Round(time.Microsecond), nil
|
||||
},
|
||||
}
|
||||
|
||||
// time_mi_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_mi_interval = framework.Function2{
|
||||
Name: "time_mi_interval",
|
||||
Return: pgtypes.Time,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timeVal := val1.(timeofday.TimeOfDay)
|
||||
interval := val2.(duration.Duration)
|
||||
return timeVal.Sub(interval), nil
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_mi_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_mi_interval = framework.Function2{
|
||||
Name: "timetz_mi_interval",
|
||||
Return: pgtypes.TimeTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
interval := val1.(duration.Duration)
|
||||
timetzVal := val2.(timetz.TimeTZ)
|
||||
timetzVal.TimeOfDay = timetzVal.TimeOfDay.Sub(interval)
|
||||
return timetzVal, nil
|
||||
},
|
||||
}
|
||||
|
||||
// minusOverflow is a convenience function that checks for overflow for int64 subtraction.
|
||||
func minusOverflow(val1 int64, val2 int64) (any, error) {
|
||||
if val2 > 0 {
|
||||
if val1 < math.MinInt64+val2 {
|
||||
return nil, errors.Errorf("bigint out of range")
|
||||
}
|
||||
} else {
|
||||
if val1 > math.MaxInt64+val2 {
|
||||
return nil, errors.Errorf("bigint out of range")
|
||||
}
|
||||
}
|
||||
return val1 - val2, nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '%' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryMod registers the functions to the catalog.
|
||||
func initBinaryMod() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMod, int2mod)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMod, int4mod)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMod, int8mod)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMod, numeric_mod)
|
||||
}
|
||||
|
||||
// int2mod represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2mod = framework.Function2{
|
||||
Name: "int2mod",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int16) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int16) % val2.(int16), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int4mod represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4mod = framework.Function2{
|
||||
Name: "int4mod",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int32) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int32) % val2.(int32), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int8mod represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8mod = framework.Function2{
|
||||
Name: "int8mod",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if val2.(int64) == 0 {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
return val1.(int64) % val2.(int64), nil
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_mod represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_mod = framework.Function2{
|
||||
Name: "numeric_mod",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
num1 := val1.(*apd.Decimal)
|
||||
num2 := val2.(*apd.Decimal)
|
||||
if num1.Form == apd.NaN || num2.Form == apd.NaN ||
|
||||
(num1.Form == apd.Infinite && num2.Form == apd.Infinite) {
|
||||
return pgtypes.NumericNaN, nil
|
||||
}
|
||||
if num2.IsZero() {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
if num1.Form == apd.Infinite {
|
||||
return num1, nil
|
||||
}
|
||||
if num2.Form == apd.Infinite {
|
||||
return apd.New(0, 0), nil
|
||||
}
|
||||
return types.DecimalMod(num1, num2)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '*' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryMultiply registers the functions to the catalog.
|
||||
func initBinaryMultiply() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, float4mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, float48mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, float8mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, float84mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int2mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int24mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int28mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int4mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int42mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int48mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int8mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int82mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, int84mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, interval_mul)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryMultiply, numeric_mul)
|
||||
}
|
||||
|
||||
// float4mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4mul = framework.Function2{
|
||||
Name: "float4mul",
|
||||
Return: pgtypes.Float32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float32) * val2.(float32), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float48mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48mul = framework.Function2{
|
||||
Name: "float48mul",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return float64(val1.(float32)) * val2.(float64), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float8mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8mul = framework.Function2{
|
||||
Name: "float8mul",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float64) * val2.(float64), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float84mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84mul = framework.Function2{
|
||||
Name: "float84mul",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float64) * float64(val2.(float32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int2mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2mul = framework.Function2{
|
||||
Name: "int2mul",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int16)) * int64(val2.(int16))
|
||||
if result > math.MaxInt16 || result < math.MinInt16 {
|
||||
return nil, errors.Errorf("smallint out of range")
|
||||
}
|
||||
return int16(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int24mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24mul = framework.Function2{
|
||||
Name: "int24mul",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int16)) * int64(val2.(int32))
|
||||
if result > math.MaxInt16 || result < math.MinInt16 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int28mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28mul = framework.Function2{
|
||||
Name: "int28mul",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return multiplyOverflow(int64(val1.(int16)), val2.(int64))
|
||||
},
|
||||
}
|
||||
|
||||
// int4mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4mul = framework.Function2{
|
||||
Name: "int4mul",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int32)) * int64(val2.(int32))
|
||||
if result > math.MaxInt32 || result < math.MinInt32 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int42mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42mul = framework.Function2{
|
||||
Name: "int42mul",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int32)) * int64(val2.(int16))
|
||||
if result > math.MaxInt32 || result < math.MinInt32 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int48mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48mul = framework.Function2{
|
||||
Name: "int48mul",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return multiplyOverflow(int64(val1.(int32)), val2.(int64))
|
||||
},
|
||||
}
|
||||
|
||||
// int8mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8mul = framework.Function2{
|
||||
Name: "int8mul",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return multiplyOverflow(val1.(int64), val2.(int64))
|
||||
},
|
||||
}
|
||||
|
||||
// int82mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82mul = framework.Function2{
|
||||
Name: "int82mul",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return multiplyOverflow(val1.(int64), int64(val2.(int16)))
|
||||
},
|
||||
}
|
||||
|
||||
// int84mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84mul = framework.Function2{
|
||||
Name: "int84mul",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return multiplyOverflow(val1.(int64), int64(val2.(int32)))
|
||||
},
|
||||
}
|
||||
|
||||
// interval_mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_mul = framework.Function2{
|
||||
Name: "interval_mul",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(duration.Duration).MulFloat(val2.(float64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_mul represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_mul = framework.Function2{
|
||||
Name: "numeric_mul",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
num1 := val1.(*apd.Decimal)
|
||||
num2 := val2.(*apd.Decimal)
|
||||
if (num1.Form == apd.Infinite || num2.Form == apd.Infinite) && (num1.IsZero() || num2.IsZero()) {
|
||||
return pgtypes.NumericNaN, nil
|
||||
}
|
||||
res := new(apd.Decimal)
|
||||
p := num1.NumDigits() + num2.NumDigits() + int64(math.Max(math.Abs(float64(num1.Exponent)), math.Abs(float64(num2.Exponent))))
|
||||
_, err := sql.DecimalCtx.WithPrecision(uint32(p)).Mul(res, num1, num2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
}
|
||||
|
||||
// multiplyOverflow is a convenience function that checks for overflow for int64 multiplication.
|
||||
func multiplyOverflow(val1 int64, val2 int64) (any, error) {
|
||||
result := val1 * val2
|
||||
if val2 != 0 && result/val2 != val1 {
|
||||
return nil, errors.Errorf("bigint out of range")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -0,0 +1,584 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
"github.com/dolthub/doltgresql/server/compare"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '<>' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryNotEqual registers the functions to the catalog.
|
||||
func initBinaryNotEqual() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, boolne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, bpcharne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, byteane)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, charne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, date_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, date_ne_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, date_ne_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, enum_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, float4ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, float48ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, float84ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, float8ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int2ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int24ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int28ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int42ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int4ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int48ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int82ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int84ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, int8ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, interval_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, jsonb_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, namene)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, namenetext)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, numeric_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, oidne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, oidvectorne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, textnename)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, text_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, time_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timestamp_ne_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timestamp_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timestamp_ne_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timestamptz_ne_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timestamptz_ne_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timestamptz_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, timetz_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, record_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, uuid_ne)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, xidneqint4)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryNotEqual, xidneq)
|
||||
}
|
||||
|
||||
// boolne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var boolne = framework.Function2{
|
||||
Name: "boolne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bool.Compare(ctx, val1.(bool), val2.(bool))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bpcharne = framework.Function2{
|
||||
Name: "bpcharne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.BpChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// byteane represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var byteane = framework.Function2{
|
||||
Name: "byteane",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Bytea.Compare(ctx, val1.([]byte), val2.([]byte))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// charne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var charne = framework.Function2{
|
||||
Name: "charne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.InternalChar.Compare(ctx, val1.(string), val2.(string))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_ne = framework.Function2{
|
||||
Name: "date_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Date.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_ne_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_ne_timestamp = framework.Function2{
|
||||
Name: "date_ne_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res != 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_ne_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_ne_timestamptz = framework.Function2{
|
||||
Name: "date_ne_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res != 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var enum_ne = framework.Function2{
|
||||
Name: "enum_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := t[0].Compare(ctx, val1, val2)
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float4ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4ne = framework.Function2{
|
||||
Name: "float4ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float32.Compare(ctx, val1.(float32), val2.(float32))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float48ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48ne = framework.Function2{
|
||||
Name: "float48ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, float64(val1.(float32)), val2.(float64))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float84ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84ne = framework.Function2{
|
||||
Name: "float84ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), float64(val2.(float32)))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// float8ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8ne = framework.Function2{
|
||||
Name: "float8ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Float64.Compare(ctx, val1.(float64), val2.(float64))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int2ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2ne = framework.Function2{
|
||||
Name: "int2ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int16.Compare(ctx, val1.(int16), val2.(int16))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int24ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24ne = framework.Function2{
|
||||
Name: "int24ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, int32(val1.(int16)), val2.(int32))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int28ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28ne = framework.Function2{
|
||||
Name: "int28ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int16)), val2.(int64))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int42ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42ne = framework.Function2{
|
||||
Name: "int42ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), int32(val2.(int16)))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int4ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4ne = framework.Function2{
|
||||
Name: "int4ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int32.Compare(ctx, val1.(int32), val2.(int32))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int48ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48ne = framework.Function2{
|
||||
Name: "int48ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(int32)), val2.(int64))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int82ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82ne = framework.Function2{
|
||||
Name: "int82ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int16)))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int84ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84ne = framework.Function2{
|
||||
Name: "int84ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), int64(val2.(int32)))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// int8ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8ne = framework.Function2{
|
||||
Name: "int8ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Int64.Compare(ctx, val1.(int64), val2.(int64))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// interval_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_ne = framework.Function2{
|
||||
Name: "interval_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Interval.Compare(ctx, val1.(duration.Duration), val2.(duration.Duration))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// jsonb_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var jsonb_ne = framework.Function2{
|
||||
Name: "jsonb_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.JsonB, pgtypes.JsonB},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.JsonB.Compare(ctx, val1, val2)
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// namene represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namene = framework.Function2{
|
||||
Name: "namene",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Name.Compare(ctx, val1.(string), val2.(string))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// namenetext represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var namenetext = framework.Function2{
|
||||
Name: "namenetext",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Name, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// numeric_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_ne = framework.Function2{
|
||||
Name: "numeric_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Numeric.Compare(ctx, val1.(*apd.Decimal), val2.(*apd.Decimal))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// oidne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidne = framework.Function2{
|
||||
Name: "oidne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oid.Compare(ctx, val1.(id.Id), val2.(id.Id))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// oidvectorne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var oidvectorne = framework.Function2{
|
||||
Name: "oidvectorne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oidvector, pgtypes.Oidvector},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Oidvector.Compare(ctx, val1.([]any), val2.([]any))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// textnename represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var textnename = framework.Function2{
|
||||
Name: "textnename",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Name},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// text_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var text_ne = framework.Function2{
|
||||
Name: "text_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Text.Compare(ctx, val1.(string), val2.(string))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// time_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_ne = framework.Function2{
|
||||
Name: "time_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Time.Compare(ctx, val1.(timeofday.TimeOfDay), val2.(timeofday.TimeOfDay))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_ne_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_ne_date = framework.Function2{
|
||||
Name: "timestamp_ne_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res != 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_ne = framework.Function2{
|
||||
Name: "timestamp_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Timestamp.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_ne_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_ne_timestamptz = framework.Function2{
|
||||
Name: "timestamp_ne_timestamptz",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_ne_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_ne_date = framework.Function2{
|
||||
Name: "timestamptz_ne_date",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res := val1.(time.Time).Compare(val2.(time.Time))
|
||||
return res != 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_ne_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_ne_timestamp = framework.Function2{
|
||||
Name: "timestamptz_ne_timestamp",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_ne = framework.Function2{
|
||||
Name: "timestamptz_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimestampTZ.Compare(ctx, val1.(time.Time), val2.(time.Time))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_ne = framework.Function2{
|
||||
Name: "timetz_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.TimeTZ.Compare(ctx, val1.(timetz.TimeTZ), val2.(timetz.TimeTZ))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// record_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var record_ne = framework.Function2{
|
||||
Name: "record_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Record, pgtypes.Record},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return compare.CompareRecords(ctx, framework.Operator_BinaryNotEqual, val1, val2)
|
||||
},
|
||||
}
|
||||
|
||||
// uuid_ne represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var uuid_ne = framework.Function2{
|
||||
Name: "uuid_ne",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Uuid, pgtypes.Uuid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Uuid.Compare(ctx, val1.(uuid.UUID), val2.(uuid.UUID))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// xidneqint4 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var xidneqint4 = framework.Function2{
|
||||
Name: "xidneqint4",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Xid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: investigate the edge cases
|
||||
res, err := pgtypes.Int64.Compare(ctx, int64(val1.(uint32)), int64(val2.(int32)))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
|
||||
// xidneq represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var xidneq = framework.Function2{
|
||||
Name: "xidneq",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Xid, pgtypes.Xid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
res, err := pgtypes.Xid.Compare(ctx, val1.(uint32), val2.(uint32))
|
||||
return res != 0, err
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,617 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/server/functions"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '+' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryPlus registers the functions to the catalog.
|
||||
func initBinaryPlus() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, date_pl_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, date_pli)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, datetime_pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, datetimetz_pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, float4pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, float48pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, float8pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, float84pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int2pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int24pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int28pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int4pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int42pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int48pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int8pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int82pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, int84pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, integer_pl_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, interval_pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, interval_pl_time)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, interval_pl_date)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, interval_pl_timetz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, interval_pl_timestamp)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, interval_pl_timestamptz)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, numeric_add)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, time_pl_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, timedate_pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, timetz_pl_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, timetzdate_pl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, timestamp_pl_interval)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryPlus, timestamptz_pl_interval)
|
||||
}
|
||||
|
||||
// float4pl_callable is the callable logic for the float4pl function.
|
||||
func float4pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float32) + val2.(float32), nil
|
||||
}
|
||||
|
||||
// float4pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float4pl = framework.Function2{
|
||||
Name: "float4pl",
|
||||
Return: pgtypes.Float32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: float4pl_callable,
|
||||
}
|
||||
|
||||
// float48pl_callable is the callable logic for the float48pl function.
|
||||
func float48pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return float64(val1.(float32)) + val2.(float64), nil
|
||||
}
|
||||
|
||||
// float48pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float48pl = framework.Function2{
|
||||
Name: "float48pl",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: float48pl_callable,
|
||||
}
|
||||
|
||||
// float8pl_callable is the callable logic for the float8pl function.
|
||||
func float8pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float64) + val2.(float64), nil
|
||||
}
|
||||
|
||||
// float8pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float8pl = framework.Function2{
|
||||
Name: "float8pl",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: float8pl_callable,
|
||||
}
|
||||
|
||||
// float84pl_callable is the callable logic for the float84pl function.
|
||||
func float84pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return val1.(float64) + float64(val2.(float32)), nil
|
||||
}
|
||||
|
||||
// float84pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var float84pl = framework.Function2{
|
||||
Name: "float84pl",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: float84pl_callable,
|
||||
}
|
||||
|
||||
// int2pl_callable is the callable logic for the int2pl function.
|
||||
func int2pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int16)) + int64(val2.(int16))
|
||||
if result > math.MaxInt16 || result < math.MinInt16 {
|
||||
return nil, errors.Errorf("smallint out of range")
|
||||
}
|
||||
return int16(result), nil
|
||||
}
|
||||
|
||||
// int2pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2pl = framework.Function2{
|
||||
Name: "int2pl",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int2pl_callable,
|
||||
}
|
||||
|
||||
// int24pl_callable is the callable logic for the int24pl function.
|
||||
func int24pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int16)) + int64(val2.(int32))
|
||||
if result > math.MaxInt16 || result < math.MinInt16 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
}
|
||||
|
||||
// int24pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int24pl = framework.Function2{
|
||||
Name: "int24pl",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int24pl_callable,
|
||||
}
|
||||
|
||||
// int28pl_callable is the callable logic for the int28pl function.
|
||||
func int28pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return plusOverflow(int64(val1.(int16)), val2.(int64))
|
||||
}
|
||||
|
||||
// int28pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int28pl = framework.Function2{
|
||||
Name: "int28pl",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int28pl_callable,
|
||||
}
|
||||
|
||||
// int4pl_callable is the callable logic for the int4pl function.
|
||||
func int4pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int32)) + int64(val2.(int32))
|
||||
if result > math.MaxInt32 || result < math.MinInt32 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
}
|
||||
|
||||
// int4pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4pl = framework.Function2{
|
||||
Name: "int4pl",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int4pl_callable,
|
||||
}
|
||||
|
||||
// int42pl_callable is the callable logic for the int42pl function.
|
||||
func int42pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
result := int64(val1.(int32)) + int64(val2.(int16))
|
||||
if result > math.MaxInt32 || result < math.MinInt32 {
|
||||
return nil, errors.Errorf("integer out of range")
|
||||
}
|
||||
return int32(result), nil
|
||||
}
|
||||
|
||||
// int42pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int42pl = framework.Function2{
|
||||
Name: "int42pl",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int42pl_callable,
|
||||
}
|
||||
|
||||
// int48pl_callable is the callable logic for the int48pl function.
|
||||
func int48pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return plusOverflow(int64(val1.(int32)), val2.(int64))
|
||||
}
|
||||
|
||||
// int48pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int48pl = framework.Function2{
|
||||
Name: "int48pl",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int48pl_callable,
|
||||
}
|
||||
|
||||
// int8pl_callable is the callable logic for the int8pl function.
|
||||
func int8pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return plusOverflow(val1.(int64), val2.(int64))
|
||||
}
|
||||
|
||||
// int8pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8pl = framework.Function2{
|
||||
Name: "int8pl",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: int8pl_callable,
|
||||
}
|
||||
|
||||
// int82pl_callable is the callable logic for the int82pl function.
|
||||
func int82pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return plusOverflow(val1.(int64), int64(val2.(int16)))
|
||||
}
|
||||
|
||||
// int82pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int82pl = framework.Function2{
|
||||
Name: "int82pl",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int16},
|
||||
Strict: true,
|
||||
Callable: int82pl_callable,
|
||||
}
|
||||
|
||||
// int84pl_callable is the callable logic for the int84pl function.
|
||||
func int84pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return plusOverflow(val1.(int64), int64(val2.(int32)))
|
||||
}
|
||||
|
||||
// int84pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int84pl = framework.Function2{
|
||||
Name: "int84pl",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: int84pl_callable,
|
||||
}
|
||||
|
||||
// integer_pl_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var integer_pl_date = framework.Function2{
|
||||
Name: "integer_pl_date",
|
||||
Return: pgtypes.Date,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
days := val1.(int32)
|
||||
date := val2.(time.Time)
|
||||
|
||||
// Add the specified number of days to the date (reverse of date_pli)
|
||||
result := date.AddDate(0, 0, int(days))
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// interval_pl_callable is the callable logic for the interval_pl function.
|
||||
func interval_pl_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
dur1 := val1.(duration.Duration)
|
||||
dur2 := val2.(duration.Duration)
|
||||
return dur1.Add(dur2), nil
|
||||
}
|
||||
|
||||
// interval_pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_pl = framework.Function2{
|
||||
Name: "interval_pl",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: interval_pl_callable,
|
||||
}
|
||||
|
||||
// interval_pl_time_callable is the callable logic for the interval_pl_time function.
|
||||
func interval_pl_time_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
interval := val1.(duration.Duration)
|
||||
timeVal := val2.(timeofday.TimeOfDay)
|
||||
return timeVal.Add(interval), nil
|
||||
}
|
||||
|
||||
// interval_pl_time represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_pl_time = framework.Function2{
|
||||
Name: "interval_pl_time",
|
||||
Return: pgtypes.Time,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: interval_pl_time_callable,
|
||||
}
|
||||
|
||||
// interval_pl_date_callable is the callable logic for the interval_pl_date function.
|
||||
func interval_pl_date_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return intervalPlusNonInterval(val1.(duration.Duration), val2.(time.Time))
|
||||
}
|
||||
|
||||
// interval_pl_date represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_pl_date = framework.Function2{
|
||||
Name: "interval_pl_date",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: interval_pl_date_callable,
|
||||
}
|
||||
|
||||
// interval_pl_timetz_callable is the callable logic for the interval_pl_timetz function.
|
||||
func interval_pl_timetz_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
interval := val1.(duration.Duration)
|
||||
timeVal := val2.(timetz.TimeTZ)
|
||||
timeVal.TimeOfDay = timeVal.TimeOfDay.Add(interval)
|
||||
return timeVal, nil
|
||||
}
|
||||
|
||||
// interval_pl_timetz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_pl_timetz = framework.Function2{
|
||||
Name: "interval_pl_timetz",
|
||||
Return: pgtypes.TimeTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: interval_pl_timetz_callable,
|
||||
}
|
||||
|
||||
// interval_pl_timestamp_callable is the callable logic for the interval_pl_timestamp function.
|
||||
func interval_pl_timestamp_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return intervalPlusNonInterval(val1.(duration.Duration), val2.(time.Time))
|
||||
}
|
||||
|
||||
// interval_pl_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_pl_timestamp = framework.Function2{
|
||||
Name: "interval_pl_timestamp",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: interval_pl_timestamp_callable,
|
||||
}
|
||||
|
||||
// interval_pl_timestamptz_callable is the callable logic for the interval_pl_timestamptz function.
|
||||
func interval_pl_timestamptz_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return intervalPlusNonInterval(val1.(duration.Duration), val2.(time.Time))
|
||||
}
|
||||
|
||||
// interval_pl_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var interval_pl_timestamptz = framework.Function2{
|
||||
Name: "interval_pl_timestamptz",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: interval_pl_timestamptz_callable,
|
||||
}
|
||||
|
||||
// numeric_add_callable is the callable logic for the numeric_add function.
|
||||
func numeric_add_callable(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
num1 := val1.(*apd.Decimal)
|
||||
num2 := val2.(*apd.Decimal)
|
||||
if num1.Form == apd.NaN || num2.Form == apd.NaN ||
|
||||
(num1.Form == apd.Infinite && num2.Form == apd.Infinite && num2.Negative) ||
|
||||
(num2.Form == apd.Infinite && num1.Form == apd.Infinite && num1.Negative) {
|
||||
return pgtypes.NumericNaN, nil
|
||||
}
|
||||
if num1.Form == apd.Infinite || num2.Form == apd.Infinite {
|
||||
if num1.Negative || num2.Negative {
|
||||
return pgtypes.NumericNegInf, nil
|
||||
}
|
||||
return pgtypes.NumericInf, nil
|
||||
}
|
||||
|
||||
res := new(apd.Decimal)
|
||||
p := uint32(math.Max(float64(num1.NumDigits()), float64(num2.NumDigits())+
|
||||
math.Max(math.Abs(float64(num1.Exponent)), math.Abs(float64(num2.Exponent)))))
|
||||
_, err := sql.DecimalCtx.WithPrecision(p).Add(res, num1, num2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// numeric_add represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var numeric_add = framework.Function2{
|
||||
Name: "numeric_add",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: numeric_add_callable,
|
||||
}
|
||||
|
||||
// plusOverflow is a convenience function that checks for overflow for int64 addition.
|
||||
func plusOverflow(val1 int64, val2 int64) (any, error) {
|
||||
if val2 > 0 {
|
||||
if val1 > math.MaxInt64-val2 {
|
||||
return nil, errors.Errorf("bigint out of range")
|
||||
}
|
||||
} else {
|
||||
if val1 < math.MinInt64-val2 {
|
||||
return nil, errors.Errorf("bigint out of range")
|
||||
}
|
||||
}
|
||||
return val1 + val2, nil
|
||||
}
|
||||
|
||||
// date_pl_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_pl_interval = framework.Function2{
|
||||
Name: "date_pl_interval",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date := val1.(time.Time)
|
||||
interval := val2.(duration.Duration)
|
||||
|
||||
// Add the interval to the date using the existing helper function
|
||||
return intervalPlusNonInterval(interval, date)
|
||||
},
|
||||
}
|
||||
|
||||
// date_pli represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_pli = framework.Function2{
|
||||
Name: "date_pli",
|
||||
Return: pgtypes.Date,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date := val1.(time.Time)
|
||||
days := val2.(int32)
|
||||
|
||||
// Add the specified number of days to the date
|
||||
result := date.AddDate(0, 0, int(days))
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// datetime_pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var datetime_pl = framework.Function2{
|
||||
Name: "datetime_pl",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date := val1.(time.Time)
|
||||
timeVal := val2.(timeofday.TimeOfDay).ToTime()
|
||||
|
||||
// Combine date from first parameter with time from second parameter
|
||||
// Extract hour, minute, second, nanosecond from time
|
||||
hour, min, sec := timeVal.Clock()
|
||||
nsec := timeVal.Nanosecond()
|
||||
|
||||
// Create new timestamp with date components from date and time components from time
|
||||
result := time.Date(date.Year(), date.Month(), date.Day(), hour, min, sec, nsec, date.Location())
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// datetimetz_pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var datetimetz_pl = framework.Function2{
|
||||
Name: "datetimetz_pl",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
date := val1.(time.Time)
|
||||
timetzVal := val2.(timetz.TimeTZ).ToTime()
|
||||
|
||||
// Combine date from first parameter with time+timezone from second parameter
|
||||
// Extract hour, minute, second, nanosecond, and timezone from timetz
|
||||
hour, min, sec := timetzVal.Clock()
|
||||
nsec := timetzVal.Nanosecond()
|
||||
location := timetzVal.Location()
|
||||
|
||||
// Create new timestamptz with date components from date and time+timezone components from timetz
|
||||
result := time.Date(date.Year(), date.Month(), date.Day(), hour, min, sec, nsec, location)
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timedate_pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timedate_pl = framework.Function2{
|
||||
Name: "timedate_pl",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timeVal := val1.(timeofday.TimeOfDay).ToTime()
|
||||
date := val2.(time.Time)
|
||||
|
||||
// Combine time from first parameter with date from second parameter
|
||||
// Extract hour, minute, second, nanosecond from time
|
||||
hour, min, sec := timeVal.Clock()
|
||||
nsec := timeVal.Nanosecond()
|
||||
|
||||
// Create new timestamp with time components from time and date components from date
|
||||
result := time.Date(date.Year(), date.Month(), date.Day(), hour, min, sec, nsec, date.Location())
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timetzdate_pl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetzdate_pl = framework.Function2{
|
||||
Name: "timetzdate_pl",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timetzVal := val1.(timetz.TimeTZ).ToTime()
|
||||
date := val2.(time.Time)
|
||||
|
||||
// Combine timetz from first parameter with date from second parameter
|
||||
// Extract hour, minute, second, nanosecond, and timezone from timetz
|
||||
hour, min, sec := timetzVal.Clock()
|
||||
nsec := timetzVal.Nanosecond()
|
||||
location := timetzVal.Location()
|
||||
|
||||
// Create new timestamptz with time+timezone components from timetz and date components from date
|
||||
result := time.Date(date.Year(), date.Month(), date.Day(), hour, min, sec, nsec, location)
|
||||
|
||||
return result, nil
|
||||
},
|
||||
}
|
||||
|
||||
// timestamp_pl_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamp_pl_interval = framework.Function2{
|
||||
Name: "timestamp_pl_interval",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Timestamp, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timestamp := val1.(time.Time)
|
||||
interval := val2.(duration.Duration)
|
||||
|
||||
// Add the interval to the timestamp using the existing helper function
|
||||
return intervalPlusNonInterval(interval, timestamp)
|
||||
},
|
||||
}
|
||||
|
||||
// timestamptz_pl_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timestamptz_pl_interval = framework.Function2{
|
||||
Name: "timestamptz_pl_interval",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimestampTZ, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timestamptz := val1.(time.Time)
|
||||
interval := val2.(duration.Duration)
|
||||
|
||||
// Add the interval to the timestamptz using the existing helper function
|
||||
return intervalPlusNonInterval(interval, timestamptz)
|
||||
},
|
||||
}
|
||||
|
||||
// time_pl_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var time_pl_interval = framework.Function2{
|
||||
Name: "time_pl_interval",
|
||||
Return: pgtypes.Time,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Time, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timeVal := val1.(timeofday.TimeOfDay)
|
||||
interval := val2.(duration.Duration)
|
||||
return timeVal.Add(interval), nil
|
||||
},
|
||||
}
|
||||
|
||||
// timetz_pl_interval represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var timetz_pl_interval = framework.Function2{
|
||||
Name: "timetz_pl_interval",
|
||||
Return: pgtypes.TimeTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.TimeTZ, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
timetzVal := val1.(timetz.TimeTZ)
|
||||
interval := val2.(duration.Duration)
|
||||
timetzVal.TimeOfDay = timetzVal.TimeOfDay.Add(interval)
|
||||
return timetzVal, nil
|
||||
},
|
||||
}
|
||||
|
||||
// intervalPlusNonInterval adds given interval duration to the given time.Time value.
|
||||
// During converting interval duration to time.Duration type, it can overflow.
|
||||
func intervalPlusNonInterval(d duration.Duration, t time.Time) (time.Time, error) {
|
||||
seconds, ok := d.AsInt64()
|
||||
if !ok {
|
||||
return time.Time{}, errors.Errorf("interval overflow")
|
||||
}
|
||||
nanos := float64(seconds) * functions.NanosPerSec
|
||||
if nanos > float64(math.MaxInt64) || nanos < float64(math.MinInt64) {
|
||||
return time.Time{}, errors.Errorf("interval overflow")
|
||||
}
|
||||
return t.Add(time.Duration(nanos)), nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '<<' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryShiftLeft registers the functions to the catalog.
|
||||
func initBinaryShiftLeft() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryShiftLeft, int2shl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryShiftLeft, int4shl)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryShiftLeft, int8shl)
|
||||
}
|
||||
|
||||
// int2shl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2shl = framework.Function2{
|
||||
Name: "int2shl",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int16(int32(val1.(int16)) << val2.(int32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int4shl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4shl = framework.Function2{
|
||||
Name: "int4shl",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int32(val1.(int32) << val2.(int32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int8shl represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8shl = framework.Function2{
|
||||
Name: "int8shl",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int64(val1.(int64) << int64(val2.(int32))), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// 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 binary
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// These functions can be gathered using the following query from a Postgres 15 instance:
|
||||
// SELECT * FROM pg_operator o WHERE o.oprname = '>>' ORDER BY o.oprcode::varchar;
|
||||
|
||||
// initBinaryShiftRight registers the functions to the catalog.
|
||||
func initBinaryShiftRight() {
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryShiftRight, int2shr)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryShiftRight, int4shr)
|
||||
framework.RegisterBinaryFunction(framework.Operator_BinaryShiftRight, int8shr)
|
||||
}
|
||||
|
||||
// int2shr represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int2shr = framework.Function2{
|
||||
Name: "int2shr",
|
||||
Return: pgtypes.Int16,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int16, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int16(int32(val1.(int16)) >> val2.(int32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int4shr represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int4shr = framework.Function2{
|
||||
Name: "int4shr",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int32, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int32(val1.(int32) >> val2.(int32)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// int8shr represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var int8shr = framework.Function2{
|
||||
Name: "int8shr",
|
||||
Return: pgtypes.Int64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Int64, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
return int64(val1.(int64) >> int64(val2.(int32))), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/jackc/pgtype"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sem/tree"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initBit registers the functions to the catalog.
|
||||
func initBit() {
|
||||
framework.RegisterFunction(bitin)
|
||||
framework.RegisterFunction(bitout)
|
||||
framework.RegisterFunction(bitrecv)
|
||||
framework.RegisterFunction(bitsend)
|
||||
framework.RegisterFunction(bittypmodin)
|
||||
framework.RegisterFunction(bittypmodout)
|
||||
}
|
||||
|
||||
// bitin represents the PostgreSQL function of bit type IO input.
|
||||
var bitin = framework.Function3{
|
||||
Name: "bit_in",
|
||||
Return: pgtypes.Bit,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Cstring, pgtypes.Oid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, _, val3 any) (any, error) {
|
||||
input := val1.(string)
|
||||
typmod := val3.(int32)
|
||||
|
||||
// validation and normalization
|
||||
array, err := tree.ParseDBitArray(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if array.BitLen() != uint(typmod) {
|
||||
return nil, pgtypes.ErrWrongLengthBit.New(len(input), typmod)
|
||||
}
|
||||
|
||||
return tree.AsStringWithFlags(array, tree.FmtPgwireText), nil
|
||||
},
|
||||
}
|
||||
|
||||
// bitout represents the PostgreSQL function of bit type IO output.
|
||||
var bitout = framework.Function1{
|
||||
Name: "bit_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bit},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
bitStr := val.(string)
|
||||
return bitStr, nil
|
||||
},
|
||||
}
|
||||
|
||||
// bitrecv represents the PostgreSQL function of bit type IO receive.
|
||||
var bitrecv = framework.Function3{
|
||||
Name: "bit_recv",
|
||||
Return: pgtypes.Bit,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Internal, pgtypes.Oid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
data := val1.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
typmod := val3.(int32)
|
||||
var out pgtype.Bit
|
||||
err := out.DecodeBinary(nil, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var str string
|
||||
for _, b := range out.Bytes {
|
||||
str += fmt.Sprintf("%08b", b)
|
||||
}
|
||||
str = str[:out.Len]
|
||||
if typmod != -1 {
|
||||
str = str[:typmod]
|
||||
}
|
||||
return str, nil
|
||||
},
|
||||
}
|
||||
|
||||
// bitsend represents the PostgreSQL function of bit type IO send.
|
||||
var bitsend = framework.Function1{
|
||||
Name: "bit_send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bit},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if wrapper, ok := val.(sql.AnyWrapper); ok {
|
||||
var err error
|
||||
val, err = wrapper.UnwrapAny(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
// We process bits in chunks of 8, so we append zeroes until our string is evenly divisible by 8
|
||||
bitString := val.(string)
|
||||
if len(bitString)%8 != 0 {
|
||||
bitString += strings.Repeat("0", 8-(len(bitString)%8))
|
||||
}
|
||||
writer := utils.NewWireWriter()
|
||||
writer.Reserve(uint64(4 + (len(bitString) / 8)))
|
||||
writer.WriteInt32(t[0].GetAttTypMod())
|
||||
for bufIdx := 0; bufIdx < len(bitString); bufIdx += 8 {
|
||||
parsedByte, err := strconv.ParseUint(bitString[bufIdx:bufIdx+8], 2, 8)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf(`error encountered while converting "BIT" to binary wire format:\n%s`, err.Error())
|
||||
}
|
||||
writer.WriteUint8(byte(parsedByte))
|
||||
}
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// bittypmodin represents the PostgreSQL function of bit type IO typmod input.
|
||||
var bittypmodin = framework.Function1{
|
||||
Name: "bittypmodin",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.CstringArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
typmod, err := getTypModFromStringArr("bit", val.([]any))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// getTypModFromStringArr always adds 4, so we remove 4 here since it doesn't apply to bit types
|
||||
return typmod - 4, nil
|
||||
},
|
||||
}
|
||||
|
||||
// bittypmodout represents the PostgreSQL function of bit type IO typmod output.
|
||||
var bittypmodout = framework.Function1{
|
||||
Name: "bittypmodout",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
typmod := val.(int32)
|
||||
if typmod < 1 {
|
||||
return "", nil
|
||||
}
|
||||
return fmt.Sprintf("(%v)", typmod), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initBitLength registers the functions to the catalog.
|
||||
func initBitLength() {
|
||||
framework.RegisterFunction(bit_length_text)
|
||||
}
|
||||
|
||||
// bit_length_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var bit_length_text = framework.Function1{
|
||||
Name: "bit_length",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
result, err := octet_length_text.Callable(ctx, t, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.(int32) * 8, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initBool registers the functions to the catalog.
|
||||
func initBool() {
|
||||
framework.RegisterFunction(boolin)
|
||||
framework.RegisterFunction(boolout)
|
||||
framework.RegisterFunction(boolrecv)
|
||||
framework.RegisterFunction(boolsend)
|
||||
framework.RegisterFunction(btboolcmp)
|
||||
}
|
||||
|
||||
// boolin represents the PostgreSQL function of boolean type IO input.
|
||||
var boolin = framework.Function1{
|
||||
Name: "boolin",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
val = strings.TrimSpace(strings.ToLower(val.(string)))
|
||||
if val == "true" || val == "t" || val == "yes" || val == "on" || val == "1" {
|
||||
return true, nil
|
||||
} else if val == "false" || val == "f" || val == "no" || val == "off" || val == "0" {
|
||||
return false, nil
|
||||
} else {
|
||||
return nil, pgtypes.ErrInvalidSyntaxForType.New("boolean", val)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// boolout represents the PostgreSQL function of boolean type IO output.
|
||||
var boolout = framework.Function1{
|
||||
Name: "boolout",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if val.(bool) {
|
||||
return "true", nil
|
||||
} else {
|
||||
return "false", nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// boolrecv represents the PostgreSQL function of boolean type IO receive.
|
||||
var boolrecv = framework.Function1{
|
||||
Name: "boolrecv",
|
||||
Return: pgtypes.Bool,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
data := val.([]byte)
|
||||
if len(data) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return data[0] != 0, nil
|
||||
},
|
||||
}
|
||||
|
||||
// boolsend represents the PostgreSQL function of boolean type IO send.
|
||||
var boolsend = framework.Function1{
|
||||
Name: "boolsend",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteBool(val.(bool))
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// btboolcmp represents the PostgreSQL function of boolean type byte compare.
|
||||
var btboolcmp = framework.Function2{
|
||||
Name: "btboolcmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bool, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := val1.(bool)
|
||||
bb := val2.(bool)
|
||||
if ab == bb {
|
||||
return int32(0), nil
|
||||
} else if !ab {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initBpChar registers the functions to the catalog.
|
||||
func initBpChar() {
|
||||
framework.RegisterFunction(bpcharin)
|
||||
framework.RegisterFunction(bpcharout)
|
||||
framework.RegisterFunction(bpcharrecv)
|
||||
framework.RegisterFunction(bpcharsend)
|
||||
framework.RegisterFunction(bpchartypmodin)
|
||||
framework.RegisterFunction(bpchartypmodout)
|
||||
framework.RegisterFunction(bpcharcmp)
|
||||
}
|
||||
|
||||
// bpcharin represents the PostgreSQL function of bpchar type IO input.
|
||||
var bpcharin = framework.Function3{
|
||||
Name: "bpcharin",
|
||||
Return: pgtypes.BpChar,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Cstring, pgtypes.Oid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
input := val1.(string)
|
||||
typmod := val3.(int32)
|
||||
maxChars := int32(pgtypes.StringMaxLength)
|
||||
if typmod != -1 {
|
||||
maxChars = pgtypes.GetCharLengthFromTypmod(typmod)
|
||||
if maxChars < pgtypes.StringUnbounded {
|
||||
maxChars = pgtypes.StringMaxLength
|
||||
}
|
||||
}
|
||||
str, runeLength := truncateString(input, maxChars)
|
||||
if runeLength > maxChars {
|
||||
return input, errors.Wrap(pgtypes.ErrCastOutOfRange, fmt.Sprintf("value too long for type varying(%v)", maxChars))
|
||||
} else {
|
||||
return str, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharout represents the PostgreSQL function of bpchar type IO output.
|
||||
var bpcharout = framework.Function1{
|
||||
Name: "bpcharout",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
typ := t[0]
|
||||
tm := typ.GetAttTypMod()
|
||||
if tm == -1 {
|
||||
return val.(string), nil
|
||||
}
|
||||
maxChars := pgtypes.GetCharLengthFromTypmod(tm)
|
||||
if maxChars < 1 {
|
||||
return val.(string), nil
|
||||
} else {
|
||||
str, runeCount := truncateString(val.(string), maxChars)
|
||||
if runeCount < maxChars {
|
||||
return str + strings.Repeat(" ", int(maxChars-runeCount)), nil
|
||||
}
|
||||
return str, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharrecv represents the PostgreSQL function of bpchar type IO receive.
|
||||
var bpcharrecv = framework.Function3{
|
||||
Name: "bpcharrecv",
|
||||
Return: pgtypes.BpChar,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Internal, pgtypes.Oid, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
data := val1.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return t[3].IoInput(ctx, string(data))
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharsend represents the PostgreSQL function of bpchar type IO send.
|
||||
var bpcharsend = framework.Function1{
|
||||
Name: "bpcharsend",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if wrapper, ok := val.(sql.AnyWrapper); ok {
|
||||
var err error
|
||||
val, err = wrapper.UnwrapAny(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
str, err := t[0].IoOutput(ctx, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteString(str)
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// bpchartypmodin represents the PostgreSQL function of bpchar type IO typmod input.
|
||||
var bpchartypmodin = framework.Function1{
|
||||
Name: "bpchartypmodin",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.CstringArray},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
return getTypModFromStringArr("char", val.([]any))
|
||||
},
|
||||
}
|
||||
|
||||
// bpchartypmodout represents the PostgreSQL function of bpchar type IO typmod output.
|
||||
var bpchartypmodout = framework.Function1{
|
||||
Name: "bpchartypmodout",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
typmod := val.(int32)
|
||||
if typmod < 5 {
|
||||
return "", nil
|
||||
}
|
||||
maxChars := pgtypes.GetCharLengthFromTypmod(typmod)
|
||||
return fmt.Sprintf("(%v)", maxChars), nil
|
||||
},
|
||||
}
|
||||
|
||||
// bpcharcmp represents the PostgreSQL function of bpchar type compare.
|
||||
var bpcharcmp = framework.Function2{
|
||||
Name: "bpcharcmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.BpChar, pgtypes.BpChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
return int32(bytes.Compare([]byte(val1.(string)), []byte(val2.(string)))), nil
|
||||
},
|
||||
}
|
||||
|
||||
// truncateString returns a string that has been truncated to the given length. Uses the rune count rather than the
|
||||
// byte count. Returns the input string if it's smaller than the length. Also returns the rune count of the string.
|
||||
func truncateString(val string, runeLimit int32) (string, int32) {
|
||||
var n int32
|
||||
for pos := range val {
|
||||
if n >= runeLimit {
|
||||
return val[:pos], n
|
||||
}
|
||||
n++
|
||||
}
|
||||
return val, n
|
||||
}
|
||||
|
||||
func getTypModFromStringArr(typName string, inputArr []any) (int32, error) {
|
||||
if len(inputArr) == 0 {
|
||||
return 0, pgtypes.ErrTypmodArrayMustBe1D.New()
|
||||
} else if len(inputArr) > 1 {
|
||||
return 0, errors.Errorf("invalid type modifier")
|
||||
}
|
||||
|
||||
l, err := strconv.ParseInt(inputArr[0].(string), 10, 32)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return pgtypes.GetTypModFromCharLength(typName, int32(l))
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2026 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 functions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTruncateString(t *testing.T) {
|
||||
tests := []struct {
|
||||
inStr string
|
||||
inLen int32
|
||||
expStr string
|
||||
expLen int32
|
||||
}{
|
||||
{
|
||||
// ascii string is not truncated
|
||||
inStr: "abc",
|
||||
inLen: 100,
|
||||
expStr: "abc",
|
||||
expLen: 3,
|
||||
},
|
||||
{
|
||||
// ascii string is truncated
|
||||
inStr: "abcdefgh",
|
||||
inLen: 5,
|
||||
expStr: "abcde",
|
||||
expLen: 5,
|
||||
},
|
||||
{
|
||||
// non ascii string is not truncated
|
||||
inStr: "こんにちは", // 5 characters 15 bytes
|
||||
inLen: 100,
|
||||
expStr: "こんにちは",
|
||||
expLen: 5,
|
||||
},
|
||||
{
|
||||
// non ascii string is truncated
|
||||
inStr: "こんにちは", // 5 characters 15 bytes
|
||||
inLen: 3,
|
||||
expStr: "こんに",
|
||||
expLen: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("truncateString(%s, %d)", test.inStr, test.inLen), func(t *testing.T) {
|
||||
outStr, outLen := truncateString(test.inStr, test.inLen)
|
||||
assert.Equal(t, test.expStr, outStr)
|
||||
assert.Equal(t, test.expLen, outLen)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initBtrim registers the functions to the catalog.
|
||||
func initBtrim() {
|
||||
framework.RegisterFunction(btrim_text)
|
||||
framework.RegisterFunction(btrim_text_text)
|
||||
}
|
||||
|
||||
// btrim_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var btrim_text = framework.Function1{
|
||||
Name: "btrim",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, str any) (any, error) {
|
||||
result, err := ltrim_text.Callable(ctx, t, str)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rtrim_text.Callable(ctx, t, result)
|
||||
},
|
||||
}
|
||||
|
||||
// btrim_text_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var btrim_text_text = framework.Function2{
|
||||
Name: "btrim",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, str any, characters any) (any, error) {
|
||||
result, err := ltrim_text_text.Callable(ctx, t, str, characters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rtrim_text_text.Callable(ctx, t, result, characters)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initBytea registers the functions to the catalog.
|
||||
func initBytea() {
|
||||
framework.RegisterFunction(byteain)
|
||||
framework.RegisterFunction(byteaout)
|
||||
framework.RegisterFunction(bytearecv)
|
||||
framework.RegisterFunction(byteasend)
|
||||
framework.RegisterFunction(byteacmp)
|
||||
}
|
||||
|
||||
// byteain represents the PostgreSQL function of bytea type IO input.
|
||||
var byteain = framework.Function1{
|
||||
Name: "byteain",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
input := val.(string)
|
||||
if strings.HasPrefix(input, `\x`) {
|
||||
return hex.DecodeString(input[2:])
|
||||
} else {
|
||||
return []byte(input), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// byteaout represents the PostgreSQL function of bytea type IO output.
|
||||
var byteaout = framework.Function1{
|
||||
Name: "byteaout",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
return `\x` + hex.EncodeToString(val.([]byte)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// bytearecv represents the PostgreSQL function of bytea type IO receive.
|
||||
var bytearecv = framework.Function1{
|
||||
Name: "bytearecv",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
return val, nil
|
||||
},
|
||||
}
|
||||
|
||||
// byteasend represents the PostgreSQL function of bytea type IO send.
|
||||
var byteasend = framework.Function1{
|
||||
Name: "byteasend",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if wrapper, ok := val.(sql.AnyWrapper); ok {
|
||||
var err error
|
||||
val, err = wrapper.UnwrapAny(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteBytes(val.([]byte))
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// byteacmp represents the PostgreSQL function of bytea type compare.
|
||||
var byteacmp = framework.Function2{
|
||||
Name: "byteacmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Bytea, pgtypes.Bytea},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
return int32(bytes.Compare(val1.([]byte), val2.([]byte))), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2023 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCbrt registers the functions to the catalog.
|
||||
func initCbrt() {
|
||||
framework.RegisterFunction(cbrt_float64)
|
||||
}
|
||||
|
||||
// cbrt_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var cbrt_float64 = framework.Function1{
|
||||
Name: "cbrt",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Cbrt(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCeil registers the functions to the catalog.
|
||||
func initCeil() {
|
||||
framework.RegisterFunction(ceil_float64)
|
||||
framework.RegisterFunction(ceil_numeric)
|
||||
// Register aliases
|
||||
ceiling_float64 := ceil_float64
|
||||
ceiling_numeric := ceil_numeric
|
||||
ceiling_float64.Name = "ceiling"
|
||||
ceiling_numeric.Name = "ceiling"
|
||||
framework.RegisterFunction(ceiling_float64)
|
||||
framework.RegisterFunction(ceiling_numeric)
|
||||
}
|
||||
|
||||
// ceil_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var ceil_float64 = framework.Function1{
|
||||
Name: "ceil",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Ceil(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// ceil_numeric represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var ceil_numeric = framework.Function1{
|
||||
Name: "ceil",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
dec := val.(*apd.Decimal)
|
||||
if dec.Form != apd.Finite {
|
||||
return dec, nil
|
||||
}
|
||||
res := new(apd.Decimal)
|
||||
_, err := sql.DecimalCtx.Ceil(res, dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initChar registers the functions to the catalog.
|
||||
func initChar() {
|
||||
framework.RegisterFunction(charin)
|
||||
framework.RegisterFunction(charout)
|
||||
framework.RegisterFunction(charrecv)
|
||||
framework.RegisterFunction(charsend)
|
||||
framework.RegisterFunction(btcharcmp)
|
||||
}
|
||||
|
||||
// charin represents the PostgreSQL function of "char" type IO input.
|
||||
var charin = framework.Function1{
|
||||
Name: "charin",
|
||||
Return: pgtypes.InternalChar,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
input := val.(string)
|
||||
c := []byte(input)
|
||||
if uint32(len(c)) > pgtypes.InternalCharLength {
|
||||
return input[:pgtypes.InternalCharLength], nil
|
||||
}
|
||||
return input, nil
|
||||
},
|
||||
}
|
||||
|
||||
// charout represents the PostgreSQL function of "char" type IO output.
|
||||
var charout = framework.Function1{
|
||||
Name: "charout",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
str := val.(string)
|
||||
if uint32(len(str)) > pgtypes.InternalCharLength {
|
||||
return str[:pgtypes.InternalCharLength], nil
|
||||
}
|
||||
return str, nil
|
||||
},
|
||||
}
|
||||
|
||||
// charrecv represents the PostgreSQL function of "char" type IO receive.
|
||||
var charrecv = framework.Function1{
|
||||
Name: "charrecv",
|
||||
Return: pgtypes.InternalChar,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
data := val.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return string(data[0]), nil
|
||||
},
|
||||
}
|
||||
|
||||
// charsend represents the PostgreSQL function of "char" type IO send.
|
||||
var charsend = framework.Function1{
|
||||
Name: "charsend",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if wrapper, ok := val.(sql.AnyWrapper); ok {
|
||||
var err error
|
||||
val, err = wrapper.UnwrapAny(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
str := val.(string)
|
||||
writer := utils.NewWireWriter()
|
||||
if len(str) == 1 {
|
||||
writer.WriteUint8(str[0])
|
||||
} else if len(str) == 0 {
|
||||
writer.WriteUint8(0)
|
||||
} else {
|
||||
return nil, errors.New(`"char" found multiple characters during binary wire formatting`)
|
||||
}
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// btcharcmp represents the PostgreSQL function of "char" type compare.
|
||||
var btcharcmp = framework.Function2{
|
||||
Name: "btcharcmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.InternalChar, pgtypes.InternalChar},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := strings.TrimRight(val1.(string), " ")
|
||||
bb := strings.TrimRight(val2.(string), " ")
|
||||
if ab == bb {
|
||||
return int32(0), nil
|
||||
} else if ab < bb {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCharLength registers the functions to the catalog.
|
||||
func initCharLength() {
|
||||
framework.RegisterFunction(char_length_text)
|
||||
// Register alias
|
||||
character_length_text := char_length_text
|
||||
character_length_text.Name = "character_length"
|
||||
framework.RegisterFunction(character_length_text)
|
||||
}
|
||||
|
||||
// char_length_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var char_length_text = framework.Function1{
|
||||
Name: "char_length",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
// We can't use Wrapper.MaxByteLength to avoid unwrapping because that gives byte length, not char length.
|
||||
valString, _, err := sql.Unwrap[string](ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return int32(len([]rune(valString))), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initChr registers the functions to the catalog.
|
||||
func initChr() {
|
||||
framework.RegisterFunction(chr_int32)
|
||||
}
|
||||
|
||||
// chr_int32 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var chr_int32 = framework.Function1{
|
||||
Name: "chr",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1Interface any) (any, error) {
|
||||
val1 := val1Interface.(int32)
|
||||
if val1 == 0 {
|
||||
return nil, errors.Errorf("null character not permitted")
|
||||
} else if val1 < 0 {
|
||||
return nil, errors.Errorf("character number must be positive")
|
||||
}
|
||||
r := rune(val1)
|
||||
if !utf8.ValidRune(r) {
|
||||
return nil, errors.Errorf("requested character too large for encoding")
|
||||
}
|
||||
return string(r), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initColDescription registers the functions to the catalog.
|
||||
func initColDescription() {
|
||||
framework.RegisterFunction(col_description)
|
||||
}
|
||||
|
||||
// col_description represents the PostgreSQL comment information function.
|
||||
var col_description = framework.Function2{
|
||||
Name: "col_description",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Int32},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
// TODO: When we support comments this should return the comment for a table
|
||||
// column, which is specified by the OID of its table and its column number
|
||||
return nil, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2026 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initConcat registers the functions to the catalog.
|
||||
func initConcat() {
|
||||
framework.RegisterFunction(concat_any)
|
||||
}
|
||||
|
||||
// concat_any represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var concat_any = framework.Function1N{
|
||||
Name: "concat",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Any},
|
||||
Strict: false,
|
||||
Callable: func(ctx *sql.Context, t []*pgtypes.DoltgresType, val1 any, vals []any) (any, error) {
|
||||
sb := strings.Builder{}
|
||||
if val1 != nil {
|
||||
output, err := t[0].IoOutput(ctx, val1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.WriteString(output)
|
||||
}
|
||||
for i, val := range vals {
|
||||
if val == nil {
|
||||
continue
|
||||
}
|
||||
valType := t[i+1]
|
||||
if valType.ID == pgtypes.Bool.ID {
|
||||
// Within this context, `bool` returns 't' rather than 'true'
|
||||
if val.(bool) {
|
||||
sb.WriteRune('t')
|
||||
} else {
|
||||
sb.WriteRune('f')
|
||||
}
|
||||
} else {
|
||||
output, err := valType.IoOutput(ctx, val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sb.WriteString(output)
|
||||
}
|
||||
}
|
||||
return sb.String(), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCos registers the functions to the catalog.
|
||||
func initCos() {
|
||||
framework.RegisterFunction(cos_float64)
|
||||
}
|
||||
|
||||
// cos_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var cos_float64 = framework.Function1{
|
||||
Name: "cos",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Cos(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCosd registers the functions to the catalog.
|
||||
func initCosd() {
|
||||
framework.RegisterFunction(cosd_float64)
|
||||
}
|
||||
|
||||
// cos_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var cosd_float64 = framework.Function1{
|
||||
Name: "cosd",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Cos(toRadians(val1.(float64))), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCosh registers the functions to the catalog.
|
||||
func initCosh() {
|
||||
framework.RegisterFunction(cosh_float64)
|
||||
}
|
||||
|
||||
// cos_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var cosh_float64 = framework.Function1{
|
||||
Name: "cosh",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Cosh(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCot registers the functions to the catalog.
|
||||
func initCot() {
|
||||
framework.RegisterFunction(cot_float64)
|
||||
}
|
||||
|
||||
// cot_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var cot_float64 = framework.Function1{
|
||||
Name: "cot",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1Interface any) (any, error) {
|
||||
val1 := val1Interface.(float64)
|
||||
if val1 == 0 {
|
||||
return math.Inf(1), nil
|
||||
}
|
||||
return math.Cos(val1) / math.Sin(val1), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCotd registers the functions to the catalog.
|
||||
func initCotd() {
|
||||
framework.RegisterFunction(cotd_float64)
|
||||
}
|
||||
|
||||
// cot_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var cotd_float64 = framework.Function1{
|
||||
Name: "cotd",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1Interface any) (any, error) {
|
||||
val1 := toRadians(val1Interface.(float64))
|
||||
if val1 == 0 {
|
||||
return math.Inf(1), nil
|
||||
}
|
||||
return math.Cos(val1) / math.Sin(val1), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCurrentDatabase registers the functions to the catalog.
|
||||
func initCurrentDatabase() {
|
||||
framework.RegisterFunction(current_database)
|
||||
}
|
||||
|
||||
// current_database represents the PostgreSQL system information function of the same name, taking no parameters.
|
||||
var current_database = framework.Function0{
|
||||
Name: "current_database",
|
||||
Return: pgtypes.Name,
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context) (any, error) {
|
||||
if ctx.GetCurrentDatabase() == "" {
|
||||
return nil, nil
|
||||
}
|
||||
return ctx.GetCurrentDatabase(), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
"github.com/dolthub/doltgresql/server/settings"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCurrentSchema registers the functions to the catalog.
|
||||
func initCurrentSchema() {
|
||||
framework.RegisterFunction(current_schema)
|
||||
}
|
||||
|
||||
// current_schema represents the PostgreSQL system information function of the same name, taking no parameters.
|
||||
var current_schema = framework.Function0{
|
||||
Name: "current_schema",
|
||||
Return: pgtypes.Name,
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context) (any, error) {
|
||||
schemas, err := settings.GetCurrentSchemas(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(schemas) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return schemas[0], nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/sessiondata"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
"github.com/dolthub/doltgresql/server/settings"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCurrentSchemas registers the functions to the catalog.
|
||||
func initCurrentSchemas() {
|
||||
framework.RegisterFunction(current_schemas)
|
||||
}
|
||||
|
||||
// current_schemas represents the PostgreSQL system information function taking a bool parameter.
|
||||
var current_schemas = framework.Function1{
|
||||
Name: "current_schemas",
|
||||
Return: pgtypes.NameArray,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Bool},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
schemas := make([]any, 0)
|
||||
if val.(bool) {
|
||||
schemas = append(schemas, sessiondata.PgCatalogName)
|
||||
}
|
||||
searchPaths, err := settings.GetCurrentSchemas(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, schema := range searchPaths {
|
||||
schemas = append(schemas, schema)
|
||||
}
|
||||
return schemas, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initCurrentSetting registers the functions to the catalog.
|
||||
func initCurrentSetting() {
|
||||
framework.RegisterFunction(current_setting_text)
|
||||
framework.RegisterFunction(current_setting_text_bool)
|
||||
}
|
||||
|
||||
// current_setting_text represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var current_setting_text = framework.Function1{
|
||||
Name: "current_setting",
|
||||
Return: pgtypes.Text, // TODO: it would be nice to support non-text values as well, but this is all postgres supports
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return getCurSetting(ctx, val1.(string), false)
|
||||
},
|
||||
}
|
||||
|
||||
// current_setting_text_bool represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var current_setting_text_bool = framework.Function2{
|
||||
Name: "current_setting",
|
||||
Return: pgtypes.Text, // TODO: it would be nice to support non-text values as well, but this is all postgres supports
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Bool},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
return getCurSetting(ctx, val1.(string), val2.(bool))
|
||||
},
|
||||
}
|
||||
|
||||
// getCurSetting returns value set for given user variable. It returns nil instead of an error
|
||||
// if it doesn't exist and missingOk is set to true.
|
||||
func getCurSetting(ctx *sql.Context, s string, missingOk bool) (any, error) {
|
||||
_, variable, err := ctx.GetUserVariable(ctx, s)
|
||||
if err != nil {
|
||||
if missingOk {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if variable != nil {
|
||||
return fmt.Sprintf("%v", variable), nil
|
||||
}
|
||||
|
||||
variable, err = ctx.GetSessionVariable(ctx, s)
|
||||
if err != nil {
|
||||
if missingOk {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Errorf(`unrecognized configuration parameter "%s"`, s)
|
||||
}
|
||||
|
||||
if variable != nil {
|
||||
return fmt.Sprintf("%v", variable), nil
|
||||
}
|
||||
|
||||
if missingOk {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.Errorf(`unrecognized configuration parameter "%s"`, s)
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/jackc/pgtype"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/pgdate"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initDate registers the functions to the catalog.
|
||||
func initDate() {
|
||||
framework.RegisterFunction(date_in)
|
||||
framework.RegisterFunction(date_out)
|
||||
framework.RegisterFunction(date_recv)
|
||||
framework.RegisterFunction(date_send)
|
||||
framework.RegisterFunction(date_cmp)
|
||||
}
|
||||
|
||||
// date_in represents the PostgreSQL function of date type IO input.
|
||||
var date_in = framework.Function1{
|
||||
Name: "date_in",
|
||||
Return: pgtypes.Date,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
input := val.(string)
|
||||
formatsInOrder := getDateStyleInputFormat(ctx)
|
||||
var date pgdate.Date
|
||||
var err error
|
||||
for _, format := range formatsInOrder {
|
||||
date, _, err = pgdate.ParseDate(time.Now(), format, input)
|
||||
if err == nil {
|
||||
return date.ToTime()
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
},
|
||||
}
|
||||
|
||||
// date_out represents the PostgreSQL function of date type IO output.
|
||||
var date_out = framework.Function1{
|
||||
Name: "date_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
return FormatDateTimeWithBC(val.(time.Time), getLayoutStringFormat(ctx, true), false), nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_recv represents the PostgreSQL function of date type IO receive.
|
||||
var date_recv = framework.Function1{
|
||||
Name: "date_recv",
|
||||
Return: pgtypes.Date,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
data := val.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var out pgtype.Date
|
||||
err := out.DecodeBinary(nil, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Time, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_send represents the PostgreSQL function of date type IO send.
|
||||
var date_send = framework.Function1{
|
||||
Name: "date_send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
postgresEpoch := time.UnixMilli(946684800000).UTC() // Jan 1, 2000 @ Midnight
|
||||
deltaInMilliseconds := val.(time.Time).UTC().UnixMilli() - postgresEpoch.UnixMilli()
|
||||
const millisecondsPerDay = 86400000
|
||||
days := uint32(deltaInMilliseconds / millisecondsPerDay)
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteUint32(days)
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_cmp represents the PostgreSQL function of date type compare.
|
||||
var date_cmp = framework.Function2{
|
||||
Name: "date_cmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Date, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := val1.(time.Time)
|
||||
bb := val2.(time.Time)
|
||||
return int32(ab.Compare(bb)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// getDateStyleInputFormat sets the defined format in DateStyle config as the first in the ordered list of date parsing modes.
|
||||
// TODO: this or something similar should be used in postgres/parser/sem/tree/datum.go when parsing timestamp/timestamptz/date values.
|
||||
func getDateStyleInputFormat(ctx *sql.Context) []pgdate.ParseMode {
|
||||
formatsInOrder := []pgdate.ParseMode{pgdate.ParseModeMDY, pgdate.ParseModeDMY, pgdate.ParseModeYMD} // default
|
||||
if ctx == nil {
|
||||
return formatsInOrder
|
||||
}
|
||||
val, err := ctx.GetSessionVariable(ctx, "datestyle")
|
||||
if err != nil {
|
||||
return formatsInOrder
|
||||
}
|
||||
|
||||
ds := strings.ReplaceAll(val.(string), " ", "")
|
||||
values := strings.Split(ds, ",")
|
||||
setFormat := pgdate.ParseModeYMD
|
||||
for _, value := range values {
|
||||
switch value {
|
||||
case "MDY":
|
||||
setFormat = pgdate.ParseModeMDY
|
||||
case "DMY":
|
||||
setFormat = pgdate.ParseModeDMY
|
||||
case "YMD":
|
||||
setFormat = pgdate.ParseModeYMD
|
||||
}
|
||||
}
|
||||
if setFormat == formatsInOrder[0] {
|
||||
return formatsInOrder
|
||||
}
|
||||
|
||||
curFirst := formatsInOrder[0]
|
||||
for i, f := range formatsInOrder {
|
||||
if setFormat == f {
|
||||
formatsInOrder[i] = curFirst
|
||||
}
|
||||
}
|
||||
formatsInOrder[0] = setFormat
|
||||
return formatsInOrder
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
cerrors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDateBin registers the functions to the catalog.
|
||||
func initDateBin() {
|
||||
framework.RegisterFunction(date_bin_interval_timestamp_timestamp)
|
||||
framework.RegisterFunction(date_bin_interval_timestamptz_timestamptz)
|
||||
}
|
||||
|
||||
// date_bin_interval_timestamp_timestamp represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_bin_interval_timestamp_timestamp = framework.Function3{
|
||||
Name: "date_bin",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.Timestamp, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
interval := val1.(duration.Duration)
|
||||
timestamp := val2.(time.Time)
|
||||
origin := val3.(time.Time)
|
||||
return binTimestamp(interval, timestamp, origin)
|
||||
},
|
||||
}
|
||||
|
||||
// date_bin_interval_timestamptz_timestamptz represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var date_bin_interval_timestamptz_timestamptz = framework.Function3{
|
||||
Name: "date_bin",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Interval, pgtypes.TimestampTZ, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
interval := val1.(duration.Duration)
|
||||
timestamp := val2.(time.Time)
|
||||
origin := val3.(time.Time)
|
||||
return binTimestamp(interval, timestamp, origin)
|
||||
},
|
||||
}
|
||||
|
||||
// binTimestamp implements the core logic for date_bin function.
|
||||
func binTimestamp(interval duration.Duration, timestamp time.Time, origin time.Time) (time.Time, error) {
|
||||
if interval.Months != 0 {
|
||||
return time.Time{}, cerrors.Errorf("timestamps cannot be binned into intervals containing months or years")
|
||||
}
|
||||
|
||||
// Calculate total nanoseconds in the interval
|
||||
intervalNanos := interval.Nanos() + int64(interval.Days)*24*3600*1000000000
|
||||
|
||||
// Check for zero or negative interval
|
||||
if intervalNanos <= 0 {
|
||||
return time.Time{}, cerrors.Errorf("stride must be greater than zero")
|
||||
}
|
||||
|
||||
originNanos := origin.UnixNano()
|
||||
diffNanos := timestamp.UnixNano() - originNanos
|
||||
|
||||
// Calculate how many complete intervals have passed
|
||||
binCount := diffNanos / intervalNanos
|
||||
if diffNanos < 0 {
|
||||
// For negative differences, we need to round down (towards negative infinity)
|
||||
binCount = int64(math.Floor(float64(diffNanos) / float64(intervalNanos)))
|
||||
}
|
||||
|
||||
// Calculate the bin start time
|
||||
binStartNanos := originNanos + binCount*intervalNanos
|
||||
|
||||
return time.Unix(0, binStartNanos).In(timestamp.Location()), nil
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
cerrors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDatePart registers the functions to the catalog.
|
||||
func initDatePart() {
|
||||
framework.RegisterFunction(date_part_text_date)
|
||||
framework.RegisterFunction(date_part_text_time)
|
||||
framework.RegisterFunction(date_part_text_timetz)
|
||||
framework.RegisterFunction(date_part_text_timestamp)
|
||||
framework.RegisterFunction(date_part_text_timestamptz)
|
||||
framework.RegisterFunction(date_part_text_interval)
|
||||
}
|
||||
|
||||
// date_part_text_date represents the PostgreSQL date_part function for date type.
|
||||
var date_part_text_date = framework.Function2{
|
||||
Name: "date_part",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Date},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
dateVal := val2.(time.Time)
|
||||
switch strings.ToLower(field) {
|
||||
case "hour", "hours", "microsecond", "microseconds", "millisecond", "milliseconds",
|
||||
"minute", "minutes", "second", "seconds", "timezone", "timezone_hour", "timezone_minute":
|
||||
return nil, ErrUnitNotSupported.New(field, "date")
|
||||
case "epoch":
|
||||
return float64(dateVal.UnixMicro()) / 1000000, nil
|
||||
default:
|
||||
result, err := getFieldFromTimeVal(field, dateVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := result.Float64()
|
||||
return f, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// date_part_text_time represents the PostgreSQL date_part function for time type.
|
||||
var date_part_text_time = framework.Function2{
|
||||
Name: "date_part",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Time},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
timeVal := val2.(timeofday.TimeOfDay).ToTime()
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries", "day", "days", "decade", "decades", "dow", "doy",
|
||||
"isodow", "isoyear", "julian", "millennium", "millenniums", "month", "months",
|
||||
"quarter", "timezone", "timezone_hour", "timezone_minute", "week", "year", "years":
|
||||
return nil, ErrUnitNotSupported.New(field, "time without time zone")
|
||||
default:
|
||||
result, err := getFieldFromTimeVal(field, timeVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := result.Float64()
|
||||
return f, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// date_part_text_timetz represents the PostgreSQL date_part function for time with time zone type.
|
||||
var date_part_text_timetz = framework.Function2{
|
||||
Name: "date_part",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.TimeTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
timetzVal := val2.(timetz.TimeTZ).ToTime()
|
||||
_, currentOffset := timetzVal.Zone()
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries", "day", "days", "decade", "decades", "dow", "doy",
|
||||
"isodow", "isoyear", "julian", "millennium", "millenniums", "month", "months",
|
||||
"quarter", "week", "year", "years":
|
||||
return nil, ErrUnitNotSupported.New(field, "time with time zone")
|
||||
case "timezone":
|
||||
return float64(-currentOffset), nil
|
||||
case "timezone_hour":
|
||||
return float64(-currentOffset / 3600), nil
|
||||
case "timezone_minute":
|
||||
return float64((-currentOffset % 3600) / 60), nil
|
||||
default:
|
||||
result, err := getFieldFromTimeVal(field, timetzVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := result.Float64()
|
||||
return f, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// date_part_text_timestamp represents the PostgreSQL date_part function for timestamp type.
|
||||
var date_part_text_timestamp = framework.Function2{
|
||||
Name: "date_part",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
tsVal := val2.(time.Time)
|
||||
switch strings.ToLower(field) {
|
||||
case "timezone", "timezone_hour", "timezone_minute":
|
||||
return nil, ErrUnitNotSupported.New(field, "timestamp without time zone")
|
||||
default:
|
||||
result, err := getFieldFromTimeVal(field, tsVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := result.Float64()
|
||||
return f, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// date_part_text_timestamptz represents the PostgreSQL date_part function for timestamp with time zone type.
|
||||
var date_part_text_timestamptz = framework.Function2{
|
||||
Name: "date_part",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
loc, err := GetServerLocation(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tstzVal := val2.(time.Time).In(loc)
|
||||
switch strings.ToLower(field) {
|
||||
case "timezone":
|
||||
// TODO: postgres seem to use server timezone regardless of input value
|
||||
return float64(-28800), nil
|
||||
case "timezone_hour":
|
||||
// TODO: postgres seem to use server timezone regardless of input value
|
||||
return float64(-8), nil
|
||||
case "timezone_minute":
|
||||
// TODO: postgres seem to use server timezone regardless of input value
|
||||
return float64(0), nil
|
||||
default:
|
||||
result, err := getFieldFromTimeVal(field, tstzVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := result.Float64()
|
||||
return f, nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// date_part_text_interval represents the PostgreSQL date_part function for interval type.
|
||||
var date_part_text_interval = framework.Function2{
|
||||
Name: "date_part",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
dur := val2.(duration.Duration)
|
||||
|
||||
// This mirrors the exact logic from extract_text_interval
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries":
|
||||
dec, err := numericFloor(float64(dur.Months) / 12 / 100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := dec.Float64()
|
||||
return f, nil
|
||||
case "day", "days":
|
||||
return float64(dur.Days), nil
|
||||
case "decade", "decades":
|
||||
dec, err := numericFloor(float64(dur.Months) / 12 / 10)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := dec.Float64()
|
||||
return f, nil
|
||||
case "epoch":
|
||||
epoch := float64(duration.SecsPerDay*duration.DaysPerMonth*dur.Months) + float64(duration.SecsPerDay*dur.Days) +
|
||||
(float64(dur.Nanos()) / float64(NanosPerSec))
|
||||
return epoch, nil
|
||||
case "hour", "hours":
|
||||
hours := float64(dur.Nanos()) / float64(NanosPerSec*duration.SecsPerHour)
|
||||
dec, err := numericFloor(hours)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := dec.Float64()
|
||||
return f, nil
|
||||
case "microsecond", "microseconds":
|
||||
secondsInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerMinute)
|
||||
microseconds := float64(secondsInNanos) / float64(NanosPerMicro)
|
||||
return microseconds, nil
|
||||
case "millennium", "millenniums":
|
||||
dec, err := numericFloor(float64(dur.Months) / 12 / 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := dec.Float64()
|
||||
return f, nil
|
||||
case "millisecond", "milliseconds":
|
||||
secondsInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerMinute)
|
||||
milliseconds := float64(secondsInNanos) / float64(NanosPerMilli)
|
||||
return milliseconds, nil
|
||||
case "minute", "minutes":
|
||||
minutesInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerHour)
|
||||
minutes := float64(minutesInNanos) / float64(NanosPerSec*duration.SecsPerMinute)
|
||||
dec, err := numericFloor(minutes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := dec.Float64()
|
||||
return f, nil
|
||||
case "month", "months":
|
||||
return float64(dur.Months % 12), nil
|
||||
case "quarter":
|
||||
return float64((dur.Months%12-1)/3 + 1), nil
|
||||
case "second", "seconds":
|
||||
secondsInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerMinute)
|
||||
seconds := float64(secondsInNanos) / float64(NanosPerSec)
|
||||
return seconds, nil
|
||||
case "year", "years":
|
||||
dec, err := numericFloor(float64(dur.Months) / 12)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, _ := dec.Float64()
|
||||
return f, nil
|
||||
case "dow", "doy", "isodow", "isoyear", "julian", "timezone", "timezone_hour", "timezone_minute", "week":
|
||||
return nil, ErrUnitNotSupported.New(field, "interval")
|
||||
default:
|
||||
return nil, ErrUnitNotSupported.New(field, "interval")
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func numericFloor(val any) (*apd.Decimal, error) {
|
||||
switch val.(type) {
|
||||
case int64, float64:
|
||||
// expects these types to Scan from
|
||||
default:
|
||||
return nil, cerrors.Errorf("invalid type for numeric convert: %T", val)
|
||||
}
|
||||
dec := new(apd.Decimal)
|
||||
err := dec.Scan(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = sql.DecimalCtx.Floor(dec, dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cerrors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDateTrunc registers the functions to the catalog.
|
||||
func initDateTrunc() {
|
||||
framework.RegisterFunction(date_trunc_text_timestamp)
|
||||
framework.RegisterFunction(date_trunc_text_timestamptz)
|
||||
framework.RegisterFunction(date_trunc_text_timestamptz_text)
|
||||
framework.RegisterFunction(date_trunc_text_interval)
|
||||
}
|
||||
|
||||
// date_trunc_text_timestamp represents the PostgreSQL date_trunc function for timestamp type.
|
||||
var date_trunc_text_timestamp = framework.Function2{
|
||||
Name: "date_trunc",
|
||||
Return: pgtypes.Timestamp,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Timestamp},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
unit := val1.(string)
|
||||
ts := val2.(time.Time)
|
||||
return truncateTime(unit, ts)
|
||||
},
|
||||
}
|
||||
|
||||
// date_trunc_text_timestamptz represents the PostgreSQL date_trunc function for timestamptz type.
|
||||
var date_trunc_text_timestamptz = framework.Function2{
|
||||
Name: "date_trunc",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.TimestampTZ},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
unit := val1.(string)
|
||||
ts := val2.(time.Time)
|
||||
// Convert to server location for consistent behavior
|
||||
loc, err := GetServerLocation(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localTs := ts.In(loc)
|
||||
truncated, err := truncateTime(unit, localTs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return truncated, nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_trunc_text_timestamptz_text represents the PostgreSQL date_trunc function with timezone parameter.
|
||||
var date_trunc_text_timestamptz_text = framework.Function3{
|
||||
Name: "date_trunc",
|
||||
Return: pgtypes.TimestampTZ,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.TimestampTZ, pgtypes.Text},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
unit := val1.(string)
|
||||
ts := val2.(time.Time)
|
||||
timezone := val3.(string)
|
||||
|
||||
// Convert timezone string to offset
|
||||
_, newOffset, _, err := convertTzToOffsetSecs(ts, timezone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a location with the specified offset
|
||||
loc := time.FixedZone("", int(newOffset))
|
||||
|
||||
// Convert timestamp to the specified timezone
|
||||
tsInTz := ts.In(loc)
|
||||
|
||||
// Truncate in the specified timezone
|
||||
truncated, err := truncateTime(unit, tsInTz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert back to server timezone
|
||||
serverLoc, err := GetServerLocation(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return truncated.In(serverLoc), nil
|
||||
},
|
||||
}
|
||||
|
||||
// date_trunc_text_interval represents the PostgreSQL date_trunc function for interval type.
|
||||
var date_trunc_text_interval = framework.Function2{
|
||||
Name: "date_trunc",
|
||||
Return: pgtypes.Interval,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Interval},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
unit := val1.(string)
|
||||
interval := val2.(duration.Duration)
|
||||
return truncateInterval(unit, interval)
|
||||
},
|
||||
}
|
||||
|
||||
// truncateTime truncates a time value to the specified unit.
|
||||
func truncateTime(unit string, t time.Time) (time.Time, error) {
|
||||
switch strings.ToLower(unit) {
|
||||
case "microsecond", "microseconds":
|
||||
// Truncate to microseconds - remove nanoseconds beyond microseconds
|
||||
return t.Truncate(time.Microsecond), nil
|
||||
case "millisecond", "milliseconds":
|
||||
// Truncate to milliseconds
|
||||
return t.Truncate(time.Millisecond), nil
|
||||
case "second", "seconds":
|
||||
// Truncate to seconds
|
||||
return t.Truncate(time.Second), nil
|
||||
case "minute", "minutes":
|
||||
// Truncate to minutes
|
||||
return t.Truncate(time.Minute), nil
|
||||
case "hour", "hours":
|
||||
// Truncate to hours
|
||||
return t.Truncate(time.Hour), nil
|
||||
case "day", "days":
|
||||
// Truncate to beginning of day
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()), nil
|
||||
case "week":
|
||||
// Truncate to beginning of week (Monday)
|
||||
// ISO week starts on Monday
|
||||
days := int(t.Weekday())
|
||||
if days == 0 {
|
||||
days = 7 // Sunday becomes 7
|
||||
}
|
||||
days-- // Make Monday = 0
|
||||
weekStart := t.AddDate(0, 0, -days)
|
||||
return time.Date(weekStart.Year(), weekStart.Month(), weekStart.Day(), 0, 0, 0, 0, t.Location()), nil
|
||||
case "month", "months":
|
||||
// Truncate to beginning of month
|
||||
return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()), nil
|
||||
case "quarter":
|
||||
// Truncate to beginning of quarter
|
||||
quarterMonth := ((int(t.Month())-1)/3)*3 + 1
|
||||
return time.Date(t.Year(), time.Month(quarterMonth), 1, 0, 0, 0, 0, t.Location()), nil
|
||||
case "year", "years":
|
||||
// Truncate to beginning of year
|
||||
return time.Date(t.Year(), 1, 1, 0, 0, 0, 0, t.Location()), nil
|
||||
case "decade", "decades":
|
||||
// Truncate to beginning of decade
|
||||
decade := (t.Year() / 10) * 10
|
||||
return time.Date(decade, 1, 1, 0, 0, 0, 0, t.Location()), nil
|
||||
case "century", "centuries":
|
||||
// Truncate to beginning of century
|
||||
// Century 1 is years 1-100, century 2 is years 101-200, etc.
|
||||
century := ((t.Year()-1)/100)*100 + 1
|
||||
return time.Date(century, 1, 1, 0, 0, 0, 0, t.Location()), nil
|
||||
case "millennium", "millenniums":
|
||||
// Truncate to beginning of millennium
|
||||
// Millennium 1 is years 1-1000, millennium 2 is years 1001-2000, etc.
|
||||
millennium := ((t.Year()-1)/1000)*1000 + 1
|
||||
return time.Date(millennium, 1, 1, 0, 0, 0, 0, t.Location()), nil
|
||||
default:
|
||||
return time.Time{}, cerrors.Errorf("date_trunc units \"%s\" not supported", unit)
|
||||
}
|
||||
}
|
||||
|
||||
// truncateInterval truncates an interval value to the specified unit.
|
||||
func truncateInterval(unit string, interval duration.Duration) (duration.Duration, error) {
|
||||
switch strings.ToLower(unit) {
|
||||
case "microsecond", "microseconds":
|
||||
// Keep only microseconds and larger units, zero out nanoseconds
|
||||
nanos := interval.Nanos()
|
||||
truncatedNanos := (nanos / 1000) * 1000 // Truncate to microseconds
|
||||
return duration.MakeDuration(truncatedNanos, interval.Days, interval.Months), nil
|
||||
case "millisecond", "milliseconds":
|
||||
// Keep only milliseconds and larger units
|
||||
nanos := interval.Nanos()
|
||||
truncatedNanos := (nanos / 1000000) * 1000000 // Truncate to milliseconds
|
||||
return duration.MakeDuration(truncatedNanos, interval.Days, interval.Months), nil
|
||||
case "second", "seconds":
|
||||
// Keep only seconds and larger units
|
||||
nanos := interval.Nanos()
|
||||
truncatedNanos := (nanos / 1000000000) * 1000000000 // Truncate to seconds
|
||||
return duration.MakeDuration(truncatedNanos, interval.Days, interval.Months), nil
|
||||
case "minute", "minutes":
|
||||
// Keep only minutes and larger units
|
||||
nanos := interval.Nanos()
|
||||
truncatedNanos := (nanos / (60 * 1000000000)) * (60 * 1000000000) // Truncate to minutes
|
||||
return duration.MakeDuration(truncatedNanos, interval.Days, interval.Months), nil
|
||||
case "hour", "hours":
|
||||
// Keep only hours and larger units
|
||||
nanos := interval.Nanos()
|
||||
truncatedNanos := (nanos / (3600 * 1000000000)) * (3600 * 1000000000) // Truncate to hours
|
||||
return duration.MakeDuration(truncatedNanos, interval.Days, interval.Months), nil
|
||||
case "day", "days":
|
||||
// Keep only days and larger units, zero out time portion
|
||||
return duration.MakeDuration(0, interval.Days, interval.Months), nil
|
||||
case "month", "months":
|
||||
// Keep only months and larger units, zero out days and time
|
||||
return duration.MakeDuration(0, 0, interval.Months), nil
|
||||
case "year", "years":
|
||||
// Keep only whole years, truncate months
|
||||
years := interval.Months / 12
|
||||
return duration.MakeDuration(0, 0, years*12), nil
|
||||
case "decade", "decades":
|
||||
// Keep only whole decades
|
||||
years := interval.Months / 12
|
||||
decades := years / 10
|
||||
return duration.MakeDuration(0, 0, decades*10*12), nil
|
||||
case "century", "centuries":
|
||||
// Keep only whole centuries
|
||||
years := interval.Months / 12
|
||||
centuries := years / 100
|
||||
return duration.MakeDuration(0, 0, centuries*100*12), nil
|
||||
case "millennium", "millenniums":
|
||||
// Keep only whole millenniums
|
||||
years := interval.Months / 12
|
||||
millenniums := years / 1000
|
||||
return duration.MakeDuration(0, 0, millenniums*1000*12), nil
|
||||
default:
|
||||
return duration.Duration{}, cerrors.Errorf("date_trunc units \"%s\" not supported for interval", unit)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDegrees registers the functions to the catalog.
|
||||
func initDegrees() {
|
||||
framework.RegisterFunction(degrees_float64)
|
||||
}
|
||||
|
||||
// degrees_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var degrees_float64 = framework.Function1{
|
||||
Name: "degrees",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return toDegrees(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// toDegrees converts the given radians to degrees.
|
||||
func toDegrees(radians float64) float64 {
|
||||
return (radians * 180.0) / math.Pi
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
errors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDiv registers the functions to the catalog.
|
||||
func initDiv() {
|
||||
framework.RegisterFunction(div_numeric)
|
||||
}
|
||||
|
||||
// div_numeric represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var div_numeric = framework.Function2{
|
||||
Name: "div",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Numeric, pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
num1 := val1.(*apd.Decimal)
|
||||
num2 := val2.(*apd.Decimal)
|
||||
if num1.Form == apd.NaN || num2.Form == apd.NaN ||
|
||||
(num1.Form == apd.Infinite && num2.Form == apd.Infinite) {
|
||||
return pgtypes.NumericNaN, nil
|
||||
}
|
||||
if num2.IsZero() {
|
||||
return nil, errors.Errorf("division by zero")
|
||||
}
|
||||
if num1.Form == apd.Infinite {
|
||||
return num1, nil
|
||||
}
|
||||
if num2.Form == apd.Infinite {
|
||||
return apd.New(0, 0), nil
|
||||
}
|
||||
|
||||
p := num1.NumDigits()
|
||||
if num1.Exponent > 0 {
|
||||
p += int64(num1.Exponent)
|
||||
}
|
||||
if num2.Exponent < 0 {
|
||||
p += int64(-num2.Exponent)
|
||||
}
|
||||
|
||||
res := new(apd.Decimal)
|
||||
_, err := sql.DecimalCtx.WithPrecision(uint32(p)).QuoInteger(res, num1, num2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
"github.com/dolthub/go-mysql-server/sql/plan"
|
||||
"github.com/dolthub/go-mysql-server/sql/types"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
"github.com/dolthub/doltgresql/server/auth"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDoltProcedurePermissionDenied = errors.New("permission denied for Dolt procedure")
|
||||
ErrDoltProcedureSelectOnly = errors.New("Dolt stored procedure may only be invoked using SELECT")
|
||||
)
|
||||
|
||||
func initDoltProcedures() {
|
||||
for _, procDef := range dprocedures.DoltProcedures {
|
||||
p, err := resolveExternalStoredProcedure(nil, procDef)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
funcVal := reflect.ValueOf(procDef.Function)
|
||||
varArgCallable := varArgCallableForDoltProcedure(p, funcVal)
|
||||
noArgCallable := noArgCallableForDoltProcedure(p, funcVal)
|
||||
framework.RegisterFunction(framework.Function1{
|
||||
Name: procDef.Name,
|
||||
Return: pgtypes.TextArray,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.TextArray},
|
||||
Variadic: true,
|
||||
Callable: varArgCallable,
|
||||
})
|
||||
framework.RegisterFunction(framework.Function0{
|
||||
Name: procDef.Name,
|
||||
Return: pgtypes.TextArray,
|
||||
Callable: noArgCallable,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// varArgCallableForDoltProcedure creates a callable function that takes in a variadic number of parameters. This is
|
||||
// equivalent to calling "DOLT_PROC_NAME('abc', ...)".
|
||||
func varArgCallableForDoltProcedure(p *plan.ExternalProcedure, funcVal reflect.Value) func(ctx *sql.Context, paramsAndReturn [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
funcType := funcVal.Type()
|
||||
|
||||
return func(ctx *sql.Context, paramsAndReturn [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
err := checkDoltProcedureAccess(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
values, ok := val1.([]any)
|
||||
if !ok {
|
||||
return nil, sql.ErrExternalProcedureInvalidParamType.New(reflect.TypeOf(val1).String())
|
||||
}
|
||||
|
||||
funcParams := make([]reflect.Value, len(values)+1)
|
||||
funcParams[0] = reflect.ValueOf(ctx)
|
||||
|
||||
for i := range values {
|
||||
paramDefinition := p.ParamDefinitions[0]
|
||||
var funcParamType reflect.Type
|
||||
if paramDefinition.Variadic {
|
||||
funcParamType = funcType.In(funcType.NumIn() - 1).Elem()
|
||||
} else {
|
||||
// TODO: support non-variadic procedures
|
||||
return nil, sql.ErrExternalProcedureInvalidParamType.New(funcType.String())
|
||||
}
|
||||
|
||||
// Grab the passed-in variable and convert it to the type we expect
|
||||
exprParamVal, _, err := paramDefinition.Type.Convert(ctx, values[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcParams[i+1], err = p.ProcessParam(ctx, funcParamType, exprParamVal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out := funcVal.Call(funcParams)
|
||||
if err, ok := out[1].Interface().(error); ok { // Only evaluates to true when error is not nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rowIter sql.RowIter
|
||||
if iter, ok := out[0].Interface().(sql.RowIter); ok {
|
||||
rowIter = iter
|
||||
} else {
|
||||
rowIter = sql.RowsToRowIter()
|
||||
}
|
||||
|
||||
return drainRowIter(ctx, rowIter)
|
||||
}
|
||||
}
|
||||
|
||||
// noArgCallableForDoltProcedure creates a callable function that does not take any parameters. This is equivalent to
|
||||
// calling "DOLT_PROC_NAME()".
|
||||
func noArgCallableForDoltProcedure(p *plan.ExternalProcedure, funcVal reflect.Value) func(ctx *sql.Context) (any, error) {
|
||||
return func(ctx *sql.Context) (any, error) {
|
||||
err := checkDoltProcedureAccess(ctx, p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
funcParams := []reflect.Value{reflect.ValueOf(ctx)}
|
||||
out := funcVal.Call(funcParams)
|
||||
if err, ok := out[1].Interface().(error); ok { // Only evaluates to true when error is not nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rowIter sql.RowIter
|
||||
if iter, ok := out[0].Interface().(sql.RowIter); ok {
|
||||
rowIter = iter
|
||||
} else {
|
||||
rowIter = sql.RowsToRowIter()
|
||||
}
|
||||
return drainRowIter(ctx, rowIter)
|
||||
}
|
||||
}
|
||||
|
||||
// checkDoltProcedureAccess ensures the current user is authorized as a SUPERUSER if the given |procedure| requires
|
||||
// admin.
|
||||
func checkDoltProcedureAccess(ctx *sql.Context, procedure *plan.ExternalProcedure) error {
|
||||
if !procedure.AdminOnly {
|
||||
return nil
|
||||
}
|
||||
|
||||
var userRole auth.Role
|
||||
auth.LockRead(func() {
|
||||
userRole = auth.GetRole(ctx.Client().User)
|
||||
})
|
||||
|
||||
if !userRole.IsValid() || !userRole.IsSuperUser {
|
||||
return ErrDoltProcedurePermissionDenied
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func drainRowIter(ctx *sql.Context, rowIter sql.RowIter) (any, error) {
|
||||
defer rowIter.Close(ctx)
|
||||
|
||||
row, err := rowIter.Next(ctx)
|
||||
if err == io.EOF {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
castsColl, err := core.GetCastsCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The conversion to []text needs []any, not sql.Row
|
||||
rowSlice := make([]any, len(row))
|
||||
for i := range row {
|
||||
sourceType, err := typeForElement(row[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cast, err := castsColl.GetExplicitCast(ctx, sourceType, pgtypes.Text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
textVal, err := cast.Eval(ctx, row[i], sourceType, pgtypes.Text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rowSlice[i] = textVal
|
||||
}
|
||||
return rowSlice, nil
|
||||
}
|
||||
|
||||
func typeForElement(v any) (*pgtypes.DoltgresType, error) {
|
||||
switch x := v.(type) {
|
||||
case int64:
|
||||
return pgtypes.Int64, nil
|
||||
case int32:
|
||||
return pgtypes.Int32, nil
|
||||
case int16, int8:
|
||||
return pgtypes.Int16, nil
|
||||
case string:
|
||||
return pgtypes.Text, nil
|
||||
default:
|
||||
return nil, errors.Errorf("dolt_procedures: unsupported type %T", x)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// ctxType is the reflect.Type of a *sql.Context.
|
||||
ctxType = reflect.TypeOf((*sql.Context)(nil))
|
||||
// ctxType is the reflect.Type of a sql.RowIter.
|
||||
rowIterType = reflect.TypeOf((*sql.RowIter)(nil)).Elem()
|
||||
// ctxType is the reflect.Type of an error.
|
||||
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
// externalStoredProcedurePointerTypes maps a non-pointer type to a sql.Type for external stored procedures.
|
||||
externalStoredProcedureTypes = map[reflect.Type]sql.Type{
|
||||
reflect.TypeOf(int(0)): types.Int64,
|
||||
reflect.TypeOf(int8(0)): types.Int8,
|
||||
reflect.TypeOf(int16(0)): types.Int16,
|
||||
reflect.TypeOf(int32(0)): types.Int32,
|
||||
reflect.TypeOf(int64(0)): types.Int64,
|
||||
reflect.TypeOf(uint(0)): types.Uint64,
|
||||
reflect.TypeOf(uint8(0)): types.Uint8,
|
||||
reflect.TypeOf(uint16(0)): types.Uint16,
|
||||
reflect.TypeOf(uint32(0)): types.Uint32,
|
||||
reflect.TypeOf(uint64(0)): types.Uint64,
|
||||
reflect.TypeOf(float32(0)): types.Float32,
|
||||
reflect.TypeOf(float64(0)): types.Float64,
|
||||
reflect.TypeOf(bool(false)): types.Int8,
|
||||
reflect.TypeOf(string("")): types.LongText,
|
||||
reflect.TypeOf([]byte{}): types.LongBlob,
|
||||
reflect.TypeOf(time.Time{}): types.DatetimeMaxPrecision,
|
||||
}
|
||||
// externalStoredProcedurePointerTypes maps a pointer type to a sql.Type for external stored procedures.
|
||||
externalStoredProcedurePointerTypes = map[reflect.Type]sql.Type{
|
||||
reflect.TypeOf((*int)(nil)): types.Int64,
|
||||
reflect.TypeOf((*int8)(nil)): types.Int8,
|
||||
reflect.TypeOf((*int16)(nil)): types.Int16,
|
||||
reflect.TypeOf((*int32)(nil)): types.Int32,
|
||||
reflect.TypeOf((*int64)(nil)): types.Int64,
|
||||
reflect.TypeOf((*uint)(nil)): types.Uint64,
|
||||
reflect.TypeOf((*uint8)(nil)): types.Uint8,
|
||||
reflect.TypeOf((*uint16)(nil)): types.Uint16,
|
||||
reflect.TypeOf((*uint32)(nil)): types.Uint32,
|
||||
reflect.TypeOf((*uint64)(nil)): types.Uint64,
|
||||
reflect.TypeOf((*float32)(nil)): types.Float32,
|
||||
reflect.TypeOf((*float64)(nil)): types.Float64,
|
||||
reflect.TypeOf((*bool)(nil)): types.Int8,
|
||||
reflect.TypeOf((*string)(nil)): types.LongText,
|
||||
reflect.TypeOf((*[]byte)(nil)): types.LongBlob,
|
||||
reflect.TypeOf((*time.Time)(nil)): types.DatetimeMaxPrecision,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
if strconv.IntSize == 32 {
|
||||
externalStoredProcedureTypes[reflect.TypeOf(int(0))] = types.Int32
|
||||
externalStoredProcedureTypes[reflect.TypeOf(uint(0))] = types.Uint32
|
||||
externalStoredProcedurePointerTypes[reflect.TypeOf((*int)(nil))] = types.Int32
|
||||
externalStoredProcedurePointerTypes[reflect.TypeOf((*uint)(nil))] = types.Uint32
|
||||
}
|
||||
}
|
||||
|
||||
func resolveExternalStoredProcedure(_ *sql.Context, externalProcedure sql.ExternalStoredProcedureDetails) (*plan.ExternalProcedure, error) {
|
||||
funcVal := reflect.ValueOf(externalProcedure.Function)
|
||||
funcType := funcVal.Type()
|
||||
if funcType.Kind() != reflect.Func {
|
||||
return nil, sql.ErrExternalProcedureNonFunction.New(externalProcedure.Function)
|
||||
}
|
||||
if funcType.NumIn() == 0 {
|
||||
return nil, sql.ErrExternalProcedureMissingContextParam.New()
|
||||
}
|
||||
if funcType.NumOut() != 2 {
|
||||
return nil, sql.ErrExternalProcedureReturnTypes.New()
|
||||
}
|
||||
if funcType.In(0) != ctxType {
|
||||
return nil, sql.ErrExternalProcedureMissingContextParam.New()
|
||||
}
|
||||
if funcType.Out(0) != rowIterType {
|
||||
return nil, sql.ErrExternalProcedureFirstReturn.New()
|
||||
}
|
||||
if funcType.Out(1) != errorType {
|
||||
return nil, sql.ErrExternalProcedureSecondReturn.New()
|
||||
}
|
||||
funcIsVariadic := funcType.IsVariadic()
|
||||
|
||||
paramDefinitions := make([]plan.ProcedureParam, funcType.NumIn()-1)
|
||||
paramReferences := make([]*expression.ProcedureParam, len(paramDefinitions))
|
||||
for i := 0; i < len(paramDefinitions); i++ {
|
||||
funcParamType := funcType.In(i + 1)
|
||||
paramName := "A" + strconv.FormatInt(int64(i), 10)
|
||||
paramIsVariadic := false
|
||||
if funcIsVariadic && i == len(paramDefinitions)-1 {
|
||||
paramIsVariadic = true
|
||||
funcParamType = funcParamType.Elem()
|
||||
if funcParamType.Kind() == reflect.Ptr {
|
||||
return nil, sql.ErrExternalProcedurePointerVariadic.New()
|
||||
}
|
||||
}
|
||||
|
||||
if sqlType, ok := externalStoredProcedureTypes[funcParamType]; ok {
|
||||
paramDefinitions[i] = plan.ProcedureParam{
|
||||
Direction: plan.ProcedureParamDirection_In,
|
||||
Name: paramName,
|
||||
Type: sqlType,
|
||||
Variadic: paramIsVariadic,
|
||||
}
|
||||
paramReferences[i] = expression.NewProcedureParam(paramName, sqlType)
|
||||
} else if sqlType, ok = externalStoredProcedurePointerTypes[funcParamType]; ok {
|
||||
paramDefinitions[i] = plan.ProcedureParam{
|
||||
Direction: plan.ProcedureParamDirection_Inout,
|
||||
Name: paramName,
|
||||
Type: sqlType,
|
||||
Variadic: paramIsVariadic,
|
||||
}
|
||||
paramReferences[i] = expression.NewProcedureParam(paramName, sqlType)
|
||||
} else {
|
||||
return nil, sql.ErrExternalProcedureInvalidParamType.New(funcParamType.String())
|
||||
}
|
||||
}
|
||||
|
||||
return &plan.ExternalProcedure{
|
||||
ExternalStoredProcedureDetails: externalProcedure,
|
||||
ParamDefinitions: paramDefinitions,
|
||||
Params: paramReferences,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDoltRecordTrim registers the functions to the catalog.
|
||||
func initDoltRecordTrim() {
|
||||
framework.RegisterFunction(dolt_recordTrim)
|
||||
}
|
||||
|
||||
// dolt_recordTrim is used to remove a specific column within a composite type. This will generally lead to an invalid
|
||||
// value for the composite type, however this is used within the DROP COLUMN table hook to fix data for all columns that
|
||||
// reference the type, as that is the only time when the data is invalid. This is why this is a "dolt_" function as
|
||||
// well, as it's not intended for general use.
|
||||
var dolt_recordTrim = framework.Function2{
|
||||
Name: "dolt_recordtrim",
|
||||
Return: pgtypes.AnyElement,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyElement, pgtypes.Int32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, types [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
|
||||
if !types[0].IsCompositeType() {
|
||||
return val1, nil
|
||||
}
|
||||
trimVal := val2.(int32)
|
||||
recordVals := val1.([]pgtypes.RecordValue)
|
||||
if trimVal < 0 || int(trimVal) >= len(recordVals) {
|
||||
return nil, errors.New("record trim index is out of bounds")
|
||||
}
|
||||
newRecordVals := make([]pgtypes.RecordValue, len(recordVals)-1)
|
||||
copy(newRecordVals, recordVals[:trimVal])
|
||||
copy(newRecordVals[trimVal:], recordVals[trimVal+1:])
|
||||
return newRecordVals, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initDomain registers the functions to the catalog.
|
||||
func initDomain() {
|
||||
framework.RegisterFunction(domain_in)
|
||||
framework.RegisterFunction(domain_recv)
|
||||
}
|
||||
|
||||
// domain_in represents the PostgreSQL function of domain type IO input.
|
||||
var domain_in = framework.Function3{
|
||||
Name: "domain_in",
|
||||
Return: pgtypes.Any,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Cstring, pgtypes.Oid, pgtypes.Int32},
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
str := val1.(string)
|
||||
baseTypeOid := val2.(id.Id)
|
||||
t := pgtypes.IDToBuiltInDoltgresType[id.Type(baseTypeOid)]
|
||||
typmod := val3.(int32)
|
||||
t = t.WithAttTypMod(typmod)
|
||||
return t.IoInput(ctx, str)
|
||||
},
|
||||
}
|
||||
|
||||
// domain_recv represents the PostgreSQL function of domain type IO receive.
|
||||
var domain_recv = framework.Function3{
|
||||
Name: "domain_recv",
|
||||
Return: pgtypes.Any,
|
||||
Parameters: [3]*pgtypes.DoltgresType{pgtypes.Internal, pgtypes.Oid, pgtypes.Int32},
|
||||
Callable: func(ctx *sql.Context, _ [4]*pgtypes.DoltgresType, val1, val2, val3 any) (any, error) {
|
||||
data := val1.([]byte)
|
||||
baseTypeOid := val2.(id.Id)
|
||||
t := pgtypes.IDToBuiltInDoltgresType[id.Type(baseTypeOid)]
|
||||
typmod := val3.(int32)
|
||||
t = t.WithAttTypMod(typmod)
|
||||
return t.DeserializeValue(ctx, data)
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/errors"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initEnum registers the functions to the catalog.
|
||||
func initEnum() {
|
||||
framework.RegisterFunction(enum_in)
|
||||
framework.RegisterFunction(enum_out)
|
||||
framework.RegisterFunction(enum_recv)
|
||||
framework.RegisterFunction(enum_send)
|
||||
framework.RegisterFunction(enum_cmp)
|
||||
}
|
||||
|
||||
// enum_in represents the PostgreSQL function of enum type IO input.
|
||||
var enum_in = framework.Function2{
|
||||
Name: "enum_in",
|
||||
Return: pgtypes.AnyEnum,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Cstring, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
typ, err := getDoltgresTypeFromId(ctx, val2.(id.Id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typ.TypCategory != pgtypes.TypeCategory_EnumTypes {
|
||||
return nil, errors.Errorf(`"%s" is not an enum type`, typ.Name())
|
||||
}
|
||||
|
||||
value := val1.(string)
|
||||
if _, exists := typ.EnumLabels[value]; !exists {
|
||||
return nil, pgtypes.ErrInvalidInputValueForEnum.New(typ.Name(), value)
|
||||
}
|
||||
// TODO: should return the index instead of label?
|
||||
return value, nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_out represents the PostgreSQL function of enum type IO output.
|
||||
var enum_out = framework.Function1{
|
||||
Name: "enum_out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
// TODO: should receive the index instead of label?
|
||||
return val.(string), nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_recv represents the PostgreSQL function of enum type IO receive.
|
||||
var enum_recv = framework.Function2{
|
||||
Name: "enum_recv",
|
||||
Return: pgtypes.AnyEnum,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Internal, pgtypes.Oid},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
data := val1.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return string(data), nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_send represents the PostgreSQL function of enum type IO send.
|
||||
var enum_send = framework.Function1{
|
||||
Name: "enum_send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
if wrapper, ok := val.(sql.AnyWrapper); ok {
|
||||
var err error
|
||||
val, err = wrapper.UnwrapAny(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteString(val.(string))
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// enum_cmp represents the PostgreSQL function of enum type compare.
|
||||
var enum_cmp = framework.Function2{
|
||||
Name: "enum_cmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.AnyEnum, pgtypes.AnyEnum},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, t [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := val1.(string)
|
||||
bb := val2.(string)
|
||||
enumType := t[0]
|
||||
if enumType.EnumLabels == nil {
|
||||
return nil, errors.Errorf(`enum label lookup failed for type %s`, enumType.Name())
|
||||
}
|
||||
abLabel, ok := enumType.EnumLabels[ab]
|
||||
if !ok {
|
||||
return nil, pgtypes.ErrInvalidInputValueForEnum.New(enumType.Name(), ab)
|
||||
}
|
||||
bbLabel, ok := enumType.EnumLabels[bb]
|
||||
if !ok {
|
||||
return nil, pgtypes.ErrInvalidInputValueForEnum.New(enumType.Name(), bb)
|
||||
}
|
||||
if abLabel.SortOrder == bbLabel.SortOrder {
|
||||
return int32(0), nil
|
||||
} else if abLabel.SortOrder < bbLabel.SortOrder {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// getDoltgresTypeFromId takes internal ID and returns the DoltgresType associated to it. It allows retrieving a
|
||||
// user-defined type, and it requires a valid sql.Context.
|
||||
func getDoltgresTypeFromId(ctx *sql.Context, rawId id.Id) (*pgtypes.DoltgresType, error) {
|
||||
typCol, err := core.GetTypesCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typID := id.Type(rawId)
|
||||
|
||||
schName := typID.SchemaName()
|
||||
if schName == "" {
|
||||
schName, err = core.GetCurrentSchema(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
typName := typID.TypeName()
|
||||
typ, err := typCol.GetType(ctx, id.NewType(schName, typName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if typ == nil {
|
||||
return nil, pgtypes.ErrTypeDoesNotExist.New(typName)
|
||||
}
|
||||
return typ, nil
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initExp registers the functions to the catalog.
|
||||
func initExp() {
|
||||
framework.RegisterFunction(exp_float64)
|
||||
framework.RegisterFunction(exp_numeric)
|
||||
}
|
||||
|
||||
// exp_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var exp_float64 = framework.Function1{
|
||||
Name: "exp",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Exp(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// exp_numeric represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var exp_numeric = framework.Function1{
|
||||
Name: "exp",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
dec := val.(*apd.Decimal)
|
||||
res := new(apd.Decimal)
|
||||
_, err := sql.DecimalCtx.WithPrecision(32).Exp(res, dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
cerrors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"gopkg.in/src-d/go-errors.v1"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/duration"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timeofday"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/timetz"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initExtract registers the functions to the catalog.
|
||||
func initExtract() {
|
||||
framework.RegisterFunction(extract_text_date)
|
||||
framework.RegisterFunction(extract_text_time)
|
||||
framework.RegisterFunction(extract_text_timetz)
|
||||
framework.RegisterFunction(extract_text_timestamp)
|
||||
framework.RegisterFunction(extract_text_timestamptz)
|
||||
framework.RegisterFunction(extract_text_interval)
|
||||
}
|
||||
|
||||
var ErrUnitNotSupported = errors.NewKind("unit \"%s\" not supported for type %s")
|
||||
|
||||
// extract_text_date represents the PostgreSQL date/time function, taking {text, date}
|
||||
var extract_text_date = framework.Function2{
|
||||
Name: "extract",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Date},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
dateVal := val2.(time.Time)
|
||||
switch strings.ToLower(field) {
|
||||
case "hour", "hours", "microsecond", "microseconds", "millisecond", "milliseconds",
|
||||
"minute", "minutes", "second", "seconds", "timezone", "timezone_hour", "timezone_minute":
|
||||
return nil, ErrUnitNotSupported.New(field, "date")
|
||||
case "epoch":
|
||||
return numeric(float64(dateVal.UnixMicro())/1000000, false, 0)
|
||||
default:
|
||||
return getFieldFromTimeVal(field, dateVal)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// extract_text_time represents the PostgreSQL date/time function, taking {text, time without time zone}
|
||||
var extract_text_time = framework.Function2{
|
||||
Name: "extract",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Time},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
timeVal := val2.(timeofday.TimeOfDay).ToTime()
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries", "day", "days", "decade", "decades", "dow", "doy",
|
||||
"isodow", "isoyear", "julian", "millennium", "millenniums", "month", "months",
|
||||
"quarter", "timezone", "timezone_hour", "timezone_minute", "week", "year", "years":
|
||||
return nil, ErrUnitNotSupported.New(field, "time without time zone")
|
||||
default:
|
||||
return getFieldFromTimeVal(field, timeVal)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// extract_text_timetz represents the PostgreSQL date/time function, taking {text, time with time zone}
|
||||
var extract_text_timetz = framework.Function2{
|
||||
Name: "extract",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.TimeTZ},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
timetzVal := val2.(timetz.TimeTZ).ToTime()
|
||||
_, currentOffset := timetzVal.Zone()
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries", "day", "days", "decade", "decades", "dow", "doy",
|
||||
"isodow", "isoyear", "julian", "millennium", "millenniums", "month", "months",
|
||||
"quarter", "week", "year", "years":
|
||||
return nil, ErrUnitNotSupported.New(field, "time with time zone")
|
||||
case "timezone":
|
||||
return numeric(-int64(-currentOffset), false, 0)
|
||||
case "timezone_hour":
|
||||
return numeric(-int64(-currentOffset/3600), false, 0)
|
||||
case "timezone_minute":
|
||||
return numeric(-int64((-currentOffset%3600)/60), false, 0)
|
||||
default:
|
||||
return getFieldFromTimeVal(field, timetzVal)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// extract_text_timestamp represents the PostgreSQL date/time function, taking {text, timestamp without time zone}
|
||||
var extract_text_timestamp = framework.Function2{
|
||||
Name: "extract",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Timestamp},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
tsVal := val2.(time.Time)
|
||||
switch strings.ToLower(field) {
|
||||
case "timezone", "timezone_hour", "timezone_minute":
|
||||
return nil, ErrUnitNotSupported.New(field, "timestamp without time zone")
|
||||
default:
|
||||
return getFieldFromTimeVal(field, tsVal)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// extract_text_timestamptz represents the PostgreSQL date/time function, taking {text, timestamp with time zone}
|
||||
var extract_text_timestamptz = framework.Function2{
|
||||
Name: "extract",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.TimestampTZ},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
loc, err := GetServerLocation(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tstzVal := val2.(time.Time).In(loc)
|
||||
switch strings.ToLower(field) {
|
||||
case "timezone":
|
||||
// TODO: postgres seem to use server timezone regardless of input value
|
||||
return numeric(int64(-28800), false, 0)
|
||||
case "timezone_hour":
|
||||
// TODO: postgres seem to use server timezone regardless of input value
|
||||
return numeric(int64(-8), false, 0)
|
||||
case "timezone_minute":
|
||||
// TODO: postgres seem to use server timezone regardless of input value
|
||||
return numeric(int64(0), false, 0)
|
||||
default:
|
||||
return getFieldFromTimeVal(field, tstzVal)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
NanosPerMicro = 1000
|
||||
NanosPerMilli = NanosPerMicro * duration.MicrosPerMilli
|
||||
NanosPerSec = NanosPerMicro * duration.MicrosPerMilli * duration.MillisPerSec
|
||||
)
|
||||
|
||||
// extract_text_interval represents the PostgreSQL date/time function, taking {text, interval}
|
||||
var extract_text_interval = framework.Function2{
|
||||
Name: "extract",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Text, pgtypes.Interval},
|
||||
IsNonDeterministic: true,
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
field := val1.(string)
|
||||
dur := val2.(duration.Duration)
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries":
|
||||
return numeric(math.Floor(float64(dur.Months)/12/100), false, 0)
|
||||
case "day", "days":
|
||||
return numeric(dur.Days, false, 0)
|
||||
case "decade", "decades":
|
||||
return numeric(math.Floor(float64(dur.Months)/12/10), false, 0)
|
||||
case "epoch":
|
||||
epoch := float64(duration.SecsPerDay*duration.DaysPerMonth*dur.Months) + float64(duration.SecsPerDay*dur.Days) +
|
||||
(float64(dur.Nanos()) / (NanosPerSec))
|
||||
return numeric(epoch, true, 6)
|
||||
case "hour", "hours":
|
||||
hours := math.Floor(float64(dur.Nanos()) / (NanosPerSec * duration.SecsPerHour))
|
||||
return numeric(hours, false, 0)
|
||||
case "microsecond", "microseconds":
|
||||
secondsInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerMinute)
|
||||
microseconds := float64(secondsInNanos) / NanosPerMicro
|
||||
return numeric(microseconds, false, 0)
|
||||
case "millennium", "millenniums":
|
||||
return numeric(math.Floor(float64(dur.Months)/12/1000), false, 0)
|
||||
case "millisecond", "milliseconds":
|
||||
secondsInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerMinute)
|
||||
milliseconds := float64(secondsInNanos) / NanosPerMilli
|
||||
return numeric(milliseconds, true, 3)
|
||||
case "minute", "minutes":
|
||||
minutesInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerHour)
|
||||
minutes := math.Floor(float64(minutesInNanos) / (NanosPerSec * duration.SecsPerMinute))
|
||||
return numeric(minutes, false, 0)
|
||||
case "month", "months":
|
||||
return numeric(dur.Months%12, false, 0)
|
||||
case "quarter":
|
||||
return numeric((dur.Months%12-1)/3+1, false, 0)
|
||||
case "second", "seconds":
|
||||
secondsInNanos := dur.Nanos() % (NanosPerSec * duration.SecsPerMinute)
|
||||
seconds := float64(secondsInNanos) / NanosPerSec
|
||||
return numeric(seconds, true, 6)
|
||||
case "year", "years":
|
||||
return numeric(math.Floor(float64(dur.Months)/12), false, 0)
|
||||
case "dow", "doy", "isodow", "isoyear", "julian", "timezone", "timezone_hour", "timezone_minute", "week":
|
||||
return nil, ErrUnitNotSupported.New(field, "interval")
|
||||
default:
|
||||
return nil, cerrors.Errorf("unknown field given: %s", field)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// getFieldFromTimeVal returns the value for given field extracted from non-interval values.
|
||||
func getFieldFromTimeVal(field string, tVal time.Time) (*apd.Decimal, error) {
|
||||
switch strings.ToLower(field) {
|
||||
case "century", "centuries":
|
||||
if year := tVal.Year(); year <= 0 {
|
||||
return numeric(math.Floor(float64(year-1)/100), false, 0)
|
||||
} else {
|
||||
return numeric(math.Ceil(float64(year)/100), false, 0)
|
||||
}
|
||||
case "day", "days":
|
||||
return numeric(int64(tVal.Day()), false, 0)
|
||||
case "decade", "decades":
|
||||
return numeric(math.Floor(float64(tVal.Year())/10), false, 0)
|
||||
case "dow":
|
||||
return numeric(int64(tVal.Weekday()), false, 0)
|
||||
case "doy":
|
||||
return numeric(int64(tVal.YearDay()), false, 0)
|
||||
case "epoch":
|
||||
return numeric(float64(tVal.UnixMicro())/1000000, true, 6)
|
||||
case "hour", "hours":
|
||||
return numeric(int64(tVal.Hour()), false, 0)
|
||||
case "isodow":
|
||||
wd := int64(tVal.Weekday())
|
||||
if wd == 0 {
|
||||
wd = 7
|
||||
}
|
||||
return numeric(wd, false, 0)
|
||||
case "isoyear":
|
||||
year, _ := tVal.ISOWeek()
|
||||
return numeric(int64(year), false, 0)
|
||||
case "julian":
|
||||
return numeric(int64(date2J(tVal.Year(), int(tVal.Month()), tVal.Day())), false, 0)
|
||||
case "microsecond", "microseconds", "usec", "usecs":
|
||||
w := float64(tVal.Second() * 1000000)
|
||||
f := float64(tVal.Nanosecond()) / float64(1000)
|
||||
return numeric(w+f, false, 0)
|
||||
case "millennium", "millenniums":
|
||||
return numeric(math.Ceil(float64(tVal.Year())/1000), false, 0)
|
||||
case "millisecond", "milliseconds", "msec", "msecs":
|
||||
w := float64(tVal.Second() * 1000)
|
||||
f := float64(tVal.Nanosecond()) / float64(1000000)
|
||||
return numeric(w+f, true, 3)
|
||||
case "minute", "minutes":
|
||||
return numeric(int64(tVal.Minute()), false, 0)
|
||||
case "month", "months":
|
||||
return numeric(int64(tVal.Month()), false, 0)
|
||||
case "quarter":
|
||||
q := (int(tVal.Month())-1)/3 + 1
|
||||
return numeric(int64(q), false, 0)
|
||||
case "second", "seconds":
|
||||
w := float64(tVal.Second())
|
||||
f := float64(tVal.Nanosecond()) / float64(1000000000)
|
||||
return numeric(w+f, true, 6)
|
||||
|
||||
case "week":
|
||||
_, week := tVal.ISOWeek()
|
||||
return numeric(int64(week), false, 0)
|
||||
case "year", "years":
|
||||
return numeric(int64(tVal.Year()), false, 0)
|
||||
default:
|
||||
return nil, cerrors.Errorf("unknown field given: %s", field)
|
||||
}
|
||||
}
|
||||
|
||||
func numeric(val any, setScale bool, scale int32) (*apd.Decimal, error) {
|
||||
switch val.(type) {
|
||||
case int64, float64:
|
||||
// expects these types to Scan from
|
||||
default:
|
||||
return nil, cerrors.Errorf("invalid type for numeric convert: %T", val)
|
||||
}
|
||||
dec := new(apd.Decimal)
|
||||
err := dec.Scan(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if setScale {
|
||||
dec, err = sql.DecimalRound(dec, scale)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return dec, nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/cockroachdb/errors"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initFactorial registers the functions to the catalog.
|
||||
func initFactorial() {
|
||||
framework.RegisterFunction(factorial_int64)
|
||||
}
|
||||
|
||||
// factorial_int64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var factorial_int64 = framework.Function1{
|
||||
Name: "factorial",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Int64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
n := val.(int64)
|
||||
if n < 0 {
|
||||
return nil, errors.Errorf("factorial of a negative number is undefined")
|
||||
}
|
||||
total := int64(1)
|
||||
for i := int64(2); i <= n; i++ {
|
||||
total *= i
|
||||
}
|
||||
return apd.New(total, 0), nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initFloat4 registers the functions to the catalog.
|
||||
func initFloat4() {
|
||||
framework.RegisterFunction(float4in)
|
||||
framework.RegisterFunction(float4out)
|
||||
framework.RegisterFunction(float4recv)
|
||||
framework.RegisterFunction(float4send)
|
||||
framework.RegisterFunction(btfloat4cmp)
|
||||
framework.RegisterFunction(btfloat48cmp)
|
||||
}
|
||||
|
||||
// float4in represents the PostgreSQL function of float4 type IO input.
|
||||
var float4in = framework.Function1{
|
||||
Name: "float4in",
|
||||
Return: pgtypes.Float32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
input := val.(string)
|
||||
fVal, err := strconv.ParseFloat(strings.TrimSpace(input), 32)
|
||||
if err != nil {
|
||||
return nil, pgtypes.ErrInvalidSyntaxForType.New("float4", input)
|
||||
}
|
||||
return float32(fVal), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float4out represents the PostgreSQL function of float4 type IO output.
|
||||
var float4out = framework.Function1{
|
||||
Name: "float4out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
return strconv.FormatFloat(float64(val.(float32)), 'f', -1, 32), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float4recv represents the PostgreSQL function of float4 type IO receive.
|
||||
var float4recv = framework.Function1{
|
||||
Name: "float4recv",
|
||||
Return: pgtypes.Float32,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
data := val.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
reader := utils.NewWireReader(data)
|
||||
return reader.ReadFloat32(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float4send represents the PostgreSQL function of float4 type IO send.
|
||||
var float4send = framework.Function1{
|
||||
Name: "float4send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteFloat32(val.(float32))
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// btfloat4cmp represents the PostgreSQL function of float4 type compare.
|
||||
var btfloat4cmp = framework.Function2{
|
||||
Name: "btfloat4cmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := val1.(float32)
|
||||
bb := val2.(float32)
|
||||
if ab == bb {
|
||||
return int32(0), nil
|
||||
} else if ab < bb {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// btfloat48cmp represents the PostgreSQL function of float4 type compare with float8.
|
||||
var btfloat48cmp = framework.Function2{
|
||||
Name: "btfloat48cmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float32, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := float64(val1.(float32))
|
||||
bb := val2.(float64)
|
||||
if ab == bb {
|
||||
return int32(0), nil
|
||||
} else if ab < bb {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
"github.com/dolthub/doltgresql/utils"
|
||||
)
|
||||
|
||||
// initFloat8 registers the functions to the catalog.
|
||||
func initFloat8() {
|
||||
framework.RegisterFunction(float8in)
|
||||
framework.RegisterFunction(float8out)
|
||||
framework.RegisterFunction(float8recv)
|
||||
framework.RegisterFunction(float8send)
|
||||
framework.RegisterFunction(btfloat8cmp)
|
||||
framework.RegisterFunction(btfloat84cmp)
|
||||
}
|
||||
|
||||
// float8in represents the PostgreSQL function of float8 type IO input.
|
||||
var float8in = framework.Function1{
|
||||
Name: "float8in",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Cstring},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
input := val.(string)
|
||||
fVal, err := strconv.ParseFloat(strings.TrimSpace(input), 64)
|
||||
if err != nil {
|
||||
return nil, pgtypes.ErrInvalidSyntaxForType.New("float8", input)
|
||||
}
|
||||
return fVal, nil
|
||||
},
|
||||
}
|
||||
|
||||
// float8out represents the PostgreSQL function of float8 type IO output.
|
||||
var float8out = framework.Function1{
|
||||
Name: "float8out",
|
||||
Return: pgtypes.Cstring,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
return strconv.FormatFloat(val.(float64), 'f', -1, 64), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float8recv represents the PostgreSQL function of float8 type IO receive.
|
||||
var float8recv = framework.Function1{
|
||||
Name: "float8recv",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Internal},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
data := val.([]byte)
|
||||
if data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
reader := utils.NewWireReader(data)
|
||||
return reader.ReadFloat64(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// float8send represents the PostgreSQL function of float8 type IO send.
|
||||
var float8send = framework.Function1{
|
||||
Name: "float8send",
|
||||
Return: pgtypes.Bytea,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
writer := utils.NewWireWriter()
|
||||
writer.WriteFloat64(val.(float64))
|
||||
return writer.BufferData(), nil
|
||||
},
|
||||
}
|
||||
|
||||
// btfloat8cmp represents the PostgreSQL function of float8 type compare.
|
||||
var btfloat8cmp = framework.Function2{
|
||||
Name: "btfloat8cmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := val1.(float64)
|
||||
bb := val2.(float64)
|
||||
if ab == bb {
|
||||
return int32(0), nil
|
||||
} else if ab < bb {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// btfloat84cmp represents the PostgreSQL function of float8 type compare with float4.
|
||||
var btfloat84cmp = framework.Function2{
|
||||
Name: "btfloat84cmp",
|
||||
Return: pgtypes.Int32,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Float64, pgtypes.Float32},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
ab := val1.(float64)
|
||||
bb := float64(val2.(float32))
|
||||
if ab == bb {
|
||||
return int32(0), nil
|
||||
} else if ab < bb {
|
||||
return int32(-1), nil
|
||||
} else {
|
||||
return int32(1), nil
|
||||
}
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/cockroachdb/apd/v3"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initFloor registers the functions to the catalog.
|
||||
func initFloor() {
|
||||
framework.RegisterFunction(floor_float64)
|
||||
framework.RegisterFunction(floor_numeric)
|
||||
}
|
||||
|
||||
// floor_float64 represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var floor_float64 = framework.Function1{
|
||||
Name: "floor",
|
||||
Return: pgtypes.Float64,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Float64},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val1 any) (any, error) {
|
||||
return math.Floor(val1.(float64)), nil
|
||||
},
|
||||
}
|
||||
|
||||
// floor_numeric represents the PostgreSQL function of the same name, taking the same parameters.
|
||||
var floor_numeric = framework.Function1{
|
||||
Name: "floor",
|
||||
Return: pgtypes.Numeric,
|
||||
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Numeric},
|
||||
Strict: true,
|
||||
Callable: func(ctx *sql.Context, _ [2]*pgtypes.DoltgresType, val any) (any, error) {
|
||||
dec := val.(*apd.Decimal)
|
||||
if dec.Form != apd.Finite {
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
res := new(apd.Decimal)
|
||||
newDecimalCtx := sql.DecimalCtx
|
||||
newDecimalCtx.Rounding = apd.RoundFloor
|
||||
_, err := newDecimalCtx.Floor(res, dec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// floor(-0.1) returns -0, which is -1 in postgres because postgres does not support -0
|
||||
if res.IsZero() && res.Negative {
|
||||
return apd.New(-1, 0), nil
|
||||
}
|
||||
return res, nil
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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 functions
|
||||
|
||||
import (
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/lib/pq/oid"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/types"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// initFormatType registers the functions to the catalog.
|
||||
func initFormatType() {
|
||||
framework.RegisterFunction(format_type)
|
||||
}
|
||||
|
||||
// format_type represents the PostgreSQL system information function.
|
||||
var format_type = framework.Function2{
|
||||
Name: "format_type",
|
||||
Return: pgtypes.Text,
|
||||
Parameters: [2]*pgtypes.DoltgresType{pgtypes.Oid, pgtypes.Int32},
|
||||
Callable: func(ctx *sql.Context, _ [3]*pgtypes.DoltgresType, val1, val2 any) (any, error) {
|
||||
if val1 == nil {
|
||||
return nil, nil
|
||||
}
|
||||
toid := id.Cache().ToOID(val1.(id.Id))
|
||||
if t, ok := types.OidToType[oid.Oid(toid)]; ok {
|
||||
if val2 == nil {
|
||||
return t.SQLStandardName(), nil
|
||||
} else {
|
||||
return t.SQLStandardNameWithTypmod(true, int(val2.(int32))), nil
|
||||
}
|
||||
}
|
||||
return "???", nil
|
||||
},
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,74 @@
|
||||
// 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 framework
|
||||
|
||||
import (
|
||||
"github.com/dolthub/doltgresql/core/extensions/pg_extension"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/postgres/parser/uuid"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
var cConversionToDatumMap = map[id.Type]func(val any) (pg_extension.NullableDatum, error){
|
||||
pgtypes.Text.ID: textToDatum,
|
||||
pgtypes.Uuid.ID: uuidToDatum,
|
||||
}
|
||||
var cConversionFromDatumMap = map[id.Type]func(datum pg_extension.Datum) (any, error){
|
||||
pgtypes.Text.ID: textFromDatum,
|
||||
pgtypes.Uuid.ID: uuidFromDatum,
|
||||
}
|
||||
|
||||
// textFromDatum converts from a Datum to a TEXT value.
|
||||
func textFromDatum(datum pg_extension.Datum) (any, error) {
|
||||
convertedVal := pg_extension.FromDatumGoString(datum)
|
||||
pg_extension.FreeDatum(datum)
|
||||
return convertedVal, nil
|
||||
}
|
||||
|
||||
// textToDatum converts from a TEXT value to a NullableDatum.
|
||||
func textToDatum(val any) (pg_extension.NullableDatum, error) {
|
||||
if val == nil {
|
||||
return pg_extension.NullableDatum{
|
||||
Value: 0,
|
||||
IsNull: true,
|
||||
}, nil
|
||||
}
|
||||
return pg_extension.NullableDatum{
|
||||
Value: pg_extension.ToDatumGoString(val.(string)),
|
||||
IsNull: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// uuidFromDatum converts from a Datum to a UUID value.
|
||||
func uuidFromDatum(datum pg_extension.Datum) (any, error) {
|
||||
convertedVal := pg_extension.FromDatumGoBytes(datum, 16)
|
||||
pg_extension.FreeDatum(datum)
|
||||
return uuid.FromBytes(convertedVal)
|
||||
}
|
||||
|
||||
// uuidToDatum converts from a UUID value to a NullableDatum.
|
||||
func uuidToDatum(val any) (pg_extension.NullableDatum, error) {
|
||||
if val == nil {
|
||||
return pg_extension.NullableDatum{
|
||||
Value: 0,
|
||||
IsNull: true,
|
||||
}, nil
|
||||
}
|
||||
return pg_extension.NullableDatum{
|
||||
Value: pg_extension.ToDatumGoBytes(val.(uuid.UUID).GetBytes()),
|
||||
IsNull: false,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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 framework
|
||||
|
||||
import (
|
||||
"github.com/dolthub/doltgresql/core/extensions"
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// CFunction is the implementation of functions that host their logic in a shared library.
|
||||
type CFunction struct {
|
||||
ID id.Function
|
||||
ReturnType *pgtypes.DoltgresType
|
||||
ParameterTypes []*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
ExtensionName extensions.LibraryIdentifier
|
||||
ExtensionSymbol string
|
||||
}
|
||||
|
||||
var _ FunctionInterface = CFunction{}
|
||||
|
||||
// GetExpectedParameterCount implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) GetExpectedParameterCount() int {
|
||||
return len(cFunc.ParameterTypes)
|
||||
}
|
||||
|
||||
// GetName implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) GetName() string {
|
||||
return cFunc.ID.FunctionName()
|
||||
}
|
||||
|
||||
// GetParameters implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) GetParameters() []*pgtypes.DoltgresType {
|
||||
return cFunc.ParameterTypes
|
||||
}
|
||||
|
||||
// GetReturn implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) GetReturn() *pgtypes.DoltgresType {
|
||||
return cFunc.ReturnType
|
||||
}
|
||||
|
||||
// InternalID implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) InternalID() id.Id {
|
||||
return cFunc.ID.AsId()
|
||||
}
|
||||
|
||||
// IsStrict implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) IsStrict() bool {
|
||||
return cFunc.Strict
|
||||
}
|
||||
|
||||
// NonDeterministic implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) NonDeterministic() bool {
|
||||
return cFunc.IsNonDeterministic
|
||||
}
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (cFunc CFunction) IsCVariadic() bool {
|
||||
// TODO: implement c-language variadic
|
||||
return false
|
||||
}
|
||||
|
||||
// VariadicIndex implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) VariadicIndex() int {
|
||||
// TODO: implement variadic
|
||||
return -1
|
||||
}
|
||||
|
||||
// IsSRF implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) IsSRF() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the interface FunctionInterface.
|
||||
func (cFunc CFunction) enforceInterfaceInheritance(error) {}
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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/dolthub/doltgresql/core/casts"
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// TODO: no need to use these functions, should instead add everything directly to built-in.
|
||||
// For now, this just makes the transition easier since it's less to rewrite
|
||||
|
||||
// TypeCast is used to cast from one type to another.
|
||||
type TypeCast struct {
|
||||
FromType *pgtypes.DoltgresType
|
||||
ToType *pgtypes.DoltgresType
|
||||
Function pgtypes.TypeCastFunction
|
||||
}
|
||||
|
||||
// MustAddExplicitTypeCast registers the given explicit type cast. Panics if an error occurs.
|
||||
func MustAddExplicitTypeCast(builtInCasts map[id.Cast]casts.Cast, cast TypeCast) {
|
||||
castID := id.NewCast(cast.FromType.ID, cast.ToType.ID)
|
||||
if _, ok := builtInCasts[castID]; ok {
|
||||
panic("duplicate built-in cast")
|
||||
}
|
||||
builtInCasts[castID] = casts.Cast{
|
||||
ID: castID,
|
||||
CastType: casts.CastType_Explicit,
|
||||
Function: id.NullFunction,
|
||||
BuiltIn: cast.Function,
|
||||
UseInOut: false,
|
||||
}
|
||||
}
|
||||
|
||||
// MustAddAssignmentTypeCast registers the given assignment type cast. Panics if an error occurs.
|
||||
func MustAddAssignmentTypeCast(builtInCasts map[id.Cast]casts.Cast, cast TypeCast) {
|
||||
castID := id.NewCast(cast.FromType.ID, cast.ToType.ID)
|
||||
if _, ok := builtInCasts[castID]; ok {
|
||||
panic("duplicate built-in cast")
|
||||
}
|
||||
builtInCasts[castID] = casts.Cast{
|
||||
ID: castID,
|
||||
CastType: casts.CastType_Assignment,
|
||||
Function: id.NullFunction,
|
||||
BuiltIn: cast.Function,
|
||||
UseInOut: false,
|
||||
}
|
||||
}
|
||||
|
||||
// MustAddImplicitTypeCast registers the given implicit type cast. Panics if an error occurs.
|
||||
func MustAddImplicitTypeCast(builtInCasts map[id.Cast]casts.Cast, cast TypeCast) {
|
||||
castID := id.NewCast(cast.FromType.ID, cast.ToType.ID)
|
||||
if _, ok := builtInCasts[castID]; ok {
|
||||
panic("duplicate built-in cast")
|
||||
}
|
||||
builtInCasts[castID] = casts.Cast{
|
||||
ID: castID,
|
||||
CastType: casts.CastType_Implicit,
|
||||
Function: id.NullFunction,
|
||||
BuiltIn: cast.Function,
|
||||
UseInOut: false,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/analyzer"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression/function"
|
||||
"github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
|
||||
"github.com/dolthub/doltgresql/postgres/parser/parser"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// Catalog contains all of the PostgreSQL functions.
|
||||
var Catalog = map[string][]FunctionInterface{}
|
||||
|
||||
// AggregateCatalog contains all of the PostgreSQL aggregate functions.
|
||||
var AggregateCatalog = map[string][]AggregateFunctionInterface{}
|
||||
|
||||
// initializedFunctions simply states whether Initialize has been called yet.
|
||||
var initializedFunctions = false
|
||||
|
||||
// RegisterFunction registers the given function, so that it will be usable from a running server. This should be called
|
||||
// from within an init().
|
||||
func RegisterFunction(f FunctionInterface) {
|
||||
if initializedFunctions {
|
||||
panic("attempted to register a function after the init() phase")
|
||||
}
|
||||
switch f := f.(type) {
|
||||
case Function0:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function1:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function1N:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function2:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function2N:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function3:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function4:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function5:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function6:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case Function7:
|
||||
name := strings.ToLower(f.Name)
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
case InterpretedFunction:
|
||||
name := strings.ToLower(f.ID.FunctionName())
|
||||
Catalog[name] = append(Catalog[name], f)
|
||||
default:
|
||||
panic("unhandled function type")
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterAggregateFunction registers the given function, so that it will be usable from a running server. This should be called
|
||||
// from within an init().
|
||||
func RegisterAggregateFunction(f AggregateFunctionInterface) {
|
||||
if initializedFunctions {
|
||||
panic("attempted to register a function after the init() phase")
|
||||
}
|
||||
switch f := f.(type) {
|
||||
case Func1Aggregate:
|
||||
name := strings.ToLower(f.Name)
|
||||
AggregateCatalog[name] = append(AggregateCatalog[name], f)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled function type %T", f))
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize handles the initialization of the catalog by overwriting the built-in GMS functions, since they do not
|
||||
// apply to PostgreSQL (and functions of the same name often have different behavior).
|
||||
func Initialize(astConvert func(parser.Statement) (sqlparser.Statement, error)) {
|
||||
// This should only be called once. We don't use sync.Once since we also want to panic if someone attempts to
|
||||
// register a function after initialization.
|
||||
if initializedFunctions {
|
||||
return
|
||||
}
|
||||
initializedFunctions = true
|
||||
|
||||
convertToVitess = astConvert
|
||||
pgtypes.LoadFunctionFromCatalog = getQuickFunctionForTypes
|
||||
analyzer.ExternalFunctionProvider = &FunctionProvider{}
|
||||
replaceGmsBuiltIns()
|
||||
validateFunctions()
|
||||
compileFunctions()
|
||||
compileAggs()
|
||||
}
|
||||
|
||||
// replaceGmsBuiltIns replaces all GMS built-ins that have conflicting names with PostgreSQL functions.
|
||||
func replaceGmsBuiltIns() {
|
||||
functionNames := make(map[string]struct{})
|
||||
for name := range Catalog {
|
||||
functionNames[strings.ToLower(name)] = struct{}{}
|
||||
}
|
||||
var newBuiltIns []sql.Function
|
||||
for _, f := range function.BuiltIns {
|
||||
if _, ok := functionNames[strings.ToLower(f.FunctionName())]; !ok {
|
||||
newBuiltIns = append(newBuiltIns, f)
|
||||
}
|
||||
}
|
||||
function.BuiltIns = newBuiltIns
|
||||
}
|
||||
|
||||
// validateFunctions panics if any functions are defined incorrectly or ambiguously.
|
||||
func validateFunctions() {
|
||||
for funcName, overloads := range Catalog {
|
||||
if err := validateFunction(funcName, overloads); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validateFunction validates whether functions are defined incorrectly or ambiguously.
|
||||
func validateFunction(funcName string, overloads []FunctionInterface) error {
|
||||
// Verify that each function uses the correct Function overload
|
||||
for _, functionOverload := range overloads {
|
||||
if functionOverload.GetExpectedParameterCount() >= 0 &&
|
||||
len(functionOverload.GetParameters()) != functionOverload.GetExpectedParameterCount() {
|
||||
return errors.Errorf("function `%s` should have %d arguments but has %d arguments",
|
||||
funcName, functionOverload.GetExpectedParameterCount(), len(functionOverload.GetParameters()))
|
||||
}
|
||||
}
|
||||
// Verify that all overloads are unique
|
||||
for functionIndex, f1 := range overloads {
|
||||
for _, f2 := range overloads[functionIndex+1:] {
|
||||
sameCount := 0
|
||||
if f1.GetExpectedParameterCount() == f2.GetExpectedParameterCount() {
|
||||
f2Parameters := f2.GetParameters()
|
||||
for parameterIndex, f1Parameter := range f1.GetParameters() {
|
||||
if f1Parameter.Equals(f2Parameters[parameterIndex]) {
|
||||
sameCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
if sameCount == f1.GetExpectedParameterCount() && f1.GetExpectedParameterCount() > 0 {
|
||||
return errors.Errorf("duplicate function overloads on `%s`", funcName)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// compileNonOperatorFunction creates a CompiledFunction for each overload of the given function.
|
||||
func compileNonOperatorFunction(funcName string, overloads []FunctionInterface) {
|
||||
overloadTree := NewOverloads()
|
||||
for _, functionOverload := range overloads {
|
||||
if err := overloadTree.Add(functionOverload); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Store the compiled function into the engine's built-in functions
|
||||
// TODO: don't do this, use an actual contract for communicating these functions to the engine catalog
|
||||
createFunc := func(ctx *sql.Context, params ...sql.Expression) (sql.Expression, error) {
|
||||
return NewCompiledFunction(ctx, funcName, params, overloadTree, false), nil
|
||||
}
|
||||
function.BuiltIns = append(function.BuiltIns, sql.FunctionN{
|
||||
Name: funcName,
|
||||
Fn: createFunc,
|
||||
})
|
||||
compiledCatalog[funcName] = createFunc
|
||||
}
|
||||
|
||||
// compileNonOperatorFunction creates a CompiledFunction for each overload of the given function.
|
||||
func compileAggFunction(funcName string, overloads []AggregateFunctionInterface) {
|
||||
var newBuffer NewBufferFn
|
||||
overloadTree := NewOverloads()
|
||||
for _, functionOverload := range overloads {
|
||||
newBuffer = functionOverload.NewBuffer
|
||||
if err := overloadTree.Add(functionOverload); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Store the compiled function into the engine's built-in functions
|
||||
// TODO: don't do this, use an actual contract for communicating these functions to the engine catalog
|
||||
createFunc := func(ctx *sql.Context, params ...sql.Expression) (sql.Expression, error) {
|
||||
return NewCompiledAggregateFunction(ctx, funcName, params, overloadTree, newBuffer), nil
|
||||
}
|
||||
function.BuiltIns = append(function.BuiltIns, sql.FunctionN{
|
||||
Name: funcName,
|
||||
Fn: createFunc,
|
||||
})
|
||||
compiledCatalog[funcName] = createFunc
|
||||
}
|
||||
|
||||
// compileFunctions creates a CompiledFunction for each overload of each function in the catalog.
|
||||
func compileFunctions() {
|
||||
for funcName, overloads := range Catalog {
|
||||
compileNonOperatorFunction(funcName, overloads)
|
||||
}
|
||||
|
||||
// Build the overload for all unary and binary functions based on their operator. This will be used for fallback if
|
||||
// an exact match is not found. Compiled functions (which wrap the overload deducer) handle upcasting and other
|
||||
// special rules, so it's far more efficient to reuse it for operators. Operators are also a special case since they
|
||||
// all have different names, while standard overload deducers work on a function-name basis.
|
||||
for signature, functionOverload := range unaryFunctions {
|
||||
overloads, ok := unaryOperatorOverloads[signature.Operator]
|
||||
if !ok {
|
||||
overloads = NewOverloads()
|
||||
unaryOperatorOverloads[signature.Operator] = overloads
|
||||
}
|
||||
if err := overloads.Add(functionOverload); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
for signature, functionOverload := range binaryFunctions {
|
||||
overloads, ok := binaryOperatorOverloads[signature.Operator]
|
||||
if !ok {
|
||||
overloads = NewOverloads()
|
||||
binaryOperatorOverloads[signature.Operator] = overloads
|
||||
}
|
||||
if err := overloads.Add(functionOverload); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add all permutations for the unary and binary operators
|
||||
for operator, overload := range unaryOperatorOverloads {
|
||||
unaryOperatorPermutations[operator] = overload.overloadsForParams(1)
|
||||
}
|
||||
for operator, overload := range binaryOperatorOverloads {
|
||||
binaryOperatorPermutations[operator] = overload.overloadsForParams(2)
|
||||
}
|
||||
}
|
||||
|
||||
func compileAggs() {
|
||||
for funcName, overloads := range AggregateCatalog {
|
||||
compileAggFunction(funcName, overloads)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// FindCommonType returns the common type that given types can convert to. Returns false if no implicit casts are needed
|
||||
// to resolve the given types as the returned common type.
|
||||
// https://www.postgresql.org/docs/15/typeconv-union-case.html
|
||||
func FindCommonType(ctx *sql.Context, types []*pgtypes.DoltgresType) (_ *pgtypes.DoltgresType, requiresCasts bool, err error) {
|
||||
candidateType := pgtypes.Unknown
|
||||
differentTypes := false
|
||||
for _, typ := range types {
|
||||
if typ.ID == candidateType.ID {
|
||||
continue
|
||||
} else if candidateType.ID == pgtypes.Unknown.ID {
|
||||
candidateType = typ
|
||||
} else {
|
||||
candidateType = pgtypes.Unknown
|
||||
differentTypes = true
|
||||
}
|
||||
}
|
||||
if !differentTypes {
|
||||
if candidateType.ID == pgtypes.Unknown.ID {
|
||||
// We require implicit casts from `unknown` to `text`
|
||||
return pgtypes.Text, true, nil
|
||||
}
|
||||
return candidateType, false, nil
|
||||
}
|
||||
// We have different types if we've made it this far, so we're guaranteed to require implicit casts
|
||||
requiresCasts = true
|
||||
for _, typ := range types {
|
||||
if candidateType.ID == pgtypes.Unknown.ID {
|
||||
candidateType = typ
|
||||
}
|
||||
if typ.ID != pgtypes.Unknown.ID && candidateType.TypCategory != typ.TypCategory {
|
||||
return nil, false, errors.Errorf("types %s and %s cannot be matched", candidateType.String(), typ.String())
|
||||
}
|
||||
}
|
||||
castsColl, err := core.GetCastsCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
// Attempt to find the most general type (or the preferred type in the type category)
|
||||
for _, typ := range types {
|
||||
if typ.ID == pgtypes.Unknown.ID || typ.ID == candidateType.ID {
|
||||
continue
|
||||
} else if cast, err := castsColl.GetImplicitCast(ctx, typ, candidateType); err != nil || cast.ID.IsValid() {
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
// typ can convert to the candidate type, so the candidate type is at least as general
|
||||
continue
|
||||
} else if cast, err = castsColl.GetImplicitCast(ctx, candidateType, typ); err != nil || cast.ID.IsValid() {
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
// the candidate type can convert to typ, but not vice versa, so typ is likely more general
|
||||
candidateType = typ
|
||||
if candidateType.IsPreferred {
|
||||
// We stop considering more types once we've found a preferred type
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// Verify that all types have an implicit conversion to the candidate type
|
||||
for _, typ := range types {
|
||||
if typ.ID == pgtypes.Unknown.ID || typ.ID == candidateType.ID {
|
||||
continue
|
||||
} else if cast, err := castsColl.GetImplicitCast(ctx, typ, candidateType); err != nil {
|
||||
return nil, false, err
|
||||
} else if !cast.ID.IsValid() {
|
||||
return nil, false, errors.Errorf("cannot find implicit cast function from %s to %s", candidateType.String(), typ.String())
|
||||
}
|
||||
}
|
||||
return candidateType, requiresCasts, nil
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
// 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"
|
||||
|
||||
cerrors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
)
|
||||
|
||||
// AggregateFunction is an expression that represents CompiledAggregateFunction
|
||||
type AggregateFunction interface {
|
||||
sql.FunctionExpression
|
||||
sql.Aggregation
|
||||
specificFuncImpl()
|
||||
}
|
||||
|
||||
type NewBufferFn func([]sql.Expression) (sql.AggregationBuffer, error)
|
||||
|
||||
// CompiledAggregateFunction is an expression that represents a fully-analyzed PostgreSQL aggregate function.
|
||||
type CompiledAggregateFunction struct {
|
||||
*CompiledFunction
|
||||
aggId sql.ColumnId
|
||||
newBuffer NewBufferFn
|
||||
}
|
||||
|
||||
var _ AggregateFunction = (*CompiledAggregateFunction)(nil)
|
||||
|
||||
// NewCompiledAggregateFunction returns a newly compiled function.
|
||||
// TODO: newBuffer probably needs to be parameterized in the overloads
|
||||
func NewCompiledAggregateFunction(ctx *sql.Context, name string, args []sql.Expression, functions *Overloads, newBuffer NewBufferFn) *CompiledAggregateFunction {
|
||||
return newCompiledAggregateFunctionInternal(ctx, name, args, functions, functions.overloadsForParams(len(args)), newBuffer)
|
||||
}
|
||||
|
||||
// newCompiledAggregateFunctionInternal is called internally, which skips steps that may have already been processed.
|
||||
func newCompiledAggregateFunctionInternal(ctx *sql.Context, name string, args []sql.Expression, overloads *Overloads, fnOverloads []Overload, newBuffer NewBufferFn) *CompiledAggregateFunction {
|
||||
cf := newCompiledFunctionInternal(ctx, name, args, overloads, fnOverloads, false, nil)
|
||||
c := &CompiledAggregateFunction{
|
||||
CompiledFunction: cf,
|
||||
newBuffer: newBuffer,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Eval implements the interface sql.Expression.
|
||||
func (c *CompiledAggregateFunction) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
|
||||
return nil, cerrors.New("Eval should not be called on CompiledAggregateFunction")
|
||||
}
|
||||
|
||||
// WithChildren implements the interface sql.Expression.
|
||||
func (c *CompiledAggregateFunction) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
|
||||
if len(children) != len(c.Arguments) {
|
||||
return nil, sql.ErrInvalidChildrenNumber.New(len(children), len(c.Arguments))
|
||||
}
|
||||
|
||||
// We have to re-resolve here, since the change in children may require it (e.g. we have more type info than we did)
|
||||
return newCompiledAggregateFunctionInternal(ctx, c.Name, children, c.overloads, c.fnOverloads, c.newBuffer), nil
|
||||
}
|
||||
|
||||
// SetStatementRunner implements the interface analyzer.Interpreter.
|
||||
func (c *CompiledAggregateFunction) SetStatementRunner(ctx *sql.Context, runner sql.StatementRunner) sql.Expression {
|
||||
nc := *c
|
||||
nc.runner = runner
|
||||
return &nc
|
||||
}
|
||||
|
||||
// specificFuncImpl implements the interface sql.Expression.
|
||||
func (*CompiledAggregateFunction) specificFuncImpl() {}
|
||||
|
||||
func (c *CompiledAggregateFunction) DebugString(ctx *sql.Context) string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString("CompiledAggregateFunction:")
|
||||
sb.WriteString(c.Name + "(")
|
||||
for i, param := range c.Arguments {
|
||||
// Aliases will output the string "x as x", which is an artifact of how we build the AST, so we'll bypass it
|
||||
if alias, ok := param.(*expression.Alias); ok {
|
||||
param = alias.Child
|
||||
}
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(sql.DebugString(ctx, param))
|
||||
}
|
||||
sb.WriteString(")")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// NewBuffer implements the interface sql.Aggregation.
|
||||
func (c *CompiledAggregateFunction) NewBuffer(ctx *sql.Context) (sql.AggregationBuffer, error) {
|
||||
return c.newBuffer(c.Arguments)
|
||||
}
|
||||
|
||||
// Id implements the interface sql.Aggregation.
|
||||
func (c *CompiledAggregateFunction) Id() sql.ColumnId {
|
||||
return c.aggId
|
||||
}
|
||||
|
||||
// WithId implements the interface sql.Aggregation.
|
||||
func (c *CompiledAggregateFunction) WithId(id sql.ColumnId) sql.IdExpression {
|
||||
nc := *c
|
||||
nc.aggId = id
|
||||
return &nc
|
||||
}
|
||||
|
||||
// NewWindowFunction implements the interface sql.WindowAdaptableExpression.
|
||||
func (c *CompiledAggregateFunction) NewWindowFunction(ctx *sql.Context) (sql.WindowFunction, error) {
|
||||
panic("windows are not implemented yet")
|
||||
}
|
||||
|
||||
// WithWindow implements the interface sql.WindowAdaptableExpression.
|
||||
func (c *CompiledAggregateFunction) WithWindow(ctx *sql.Context, window *sql.WindowDefinition) sql.WindowAdaptableExpression {
|
||||
panic("windows are not implemented yet")
|
||||
}
|
||||
|
||||
// Window implements the interface sql.WindowAdaptableExpression.
|
||||
func (c *CompiledAggregateFunction) Window() *sql.WindowDefinition {
|
||||
panic("windows are not implemented yet")
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// 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/dolthub/go-mysql-server/sql"
|
||||
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// compiledCatalog contains all of PostgreSQL functions in their compiled forms.
|
||||
var compiledCatalog = map[string]sql.CreateFuncNArgs{}
|
||||
|
||||
// GetFunction returns the compiled function with the given name and parameters. Returns false if the function could not
|
||||
// be found.
|
||||
func GetFunction(ctx *sql.Context, functionName string, params ...sql.Expression) (*CompiledFunction, bool, error) {
|
||||
if createFunc, ok := compiledCatalog[functionName]; ok {
|
||||
expr, err := createFunc(ctx, params...)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return expr.(*CompiledFunction), true, nil
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
// dummyExpression is a simple expression that exists solely to capture type information for a parameter. This is used
|
||||
// exclusively by the getQuickFunctionForTypes function.
|
||||
type dummyExpression struct {
|
||||
t *pgtypes.DoltgresType
|
||||
}
|
||||
|
||||
var _ sql.Expression = dummyExpression{}
|
||||
|
||||
func (d dummyExpression) Resolved() bool { return true }
|
||||
func (d dummyExpression) String() string { return d.t.String() }
|
||||
func (d dummyExpression) Type(ctx *sql.Context) sql.Type { return d.t }
|
||||
func (d dummyExpression) IsNullable(ctx *sql.Context) bool { return false }
|
||||
func (d dummyExpression) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
|
||||
panic("cannot Eval dummyExpression")
|
||||
}
|
||||
func (d dummyExpression) Children() []sql.Expression { return nil }
|
||||
func (d dummyExpression) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// getQuickFunctionForTypes is used by the types package to load quick functions. This is declared here to work around
|
||||
// import cycles. Returns nil if a QuickFunction could not be constructed.
|
||||
func getQuickFunctionForTypes(ctx *sql.Context, functionName string, params []*pgtypes.DoltgresType) any {
|
||||
exprs := make([]sql.Expression, len(params))
|
||||
for i := range params {
|
||||
exprs[i] = dummyExpression{t: params[i]}
|
||||
}
|
||||
cf, ok, err := GetFunction(ctx, functionName, exprs...)
|
||||
if err != nil || !ok {
|
||||
return nil
|
||||
}
|
||||
return cf.GetQuickFunction()
|
||||
}
|
||||
@@ -0,0 +1,945 @@
|
||||
// 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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
cerrors "github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
"github.com/dolthub/go-mysql-server/sql/procedures"
|
||||
"gopkg.in/src-d/go-errors.v1"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
"github.com/dolthub/doltgresql/core/casts"
|
||||
"github.com/dolthub/doltgresql/core/extensions"
|
||||
"github.com/dolthub/doltgresql/core/extensions/pg_extension"
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/server/plpgsql"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// ErrFunctionDoesNotExist is returned when the function in use cannot be found.
|
||||
var ErrFunctionDoesNotExist = errors.NewKind(`function %s does not exist`)
|
||||
|
||||
// Function is an expression that represents either a CompiledFunction or a QuickFunction.
|
||||
type Function interface {
|
||||
sql.FunctionExpression
|
||||
sql.NonDeterministicExpression
|
||||
specificFuncImpl()
|
||||
}
|
||||
|
||||
// CompiledFunction is an expression that represents a fully-analyzed PostgreSQL function.
|
||||
type CompiledFunction struct {
|
||||
Name string
|
||||
Arguments []sql.Expression
|
||||
IsOperator bool
|
||||
overloads *Overloads
|
||||
fnOverloads []Overload
|
||||
overload overloadMatch
|
||||
originalTypes []*pgtypes.DoltgresType
|
||||
callResolved []*pgtypes.DoltgresType
|
||||
runner sql.StatementRunner
|
||||
stashedErr error
|
||||
}
|
||||
|
||||
var _ sql.FunctionExpression = (*CompiledFunction)(nil)
|
||||
var _ sql.NonDeterministicExpression = (*CompiledFunction)(nil)
|
||||
var _ procedures.InterpreterExpr = (*CompiledFunction)(nil)
|
||||
var _ sql.RowIterExpression = (*CompiledFunction)(nil)
|
||||
|
||||
// NewCompiledFunction returns a newly compiled function.
|
||||
func NewCompiledFunction(ctx *sql.Context, name string, args []sql.Expression, functions *Overloads, isOperator bool) *CompiledFunction {
|
||||
return newCompiledFunctionInternal(ctx, name, args, functions, functions.overloadsForParams(len(args)), isOperator, nil)
|
||||
}
|
||||
|
||||
// newCompiledFunctionInternal is called internally, which skips steps that may have already been processed.
|
||||
func newCompiledFunctionInternal(
|
||||
ctx *sql.Context,
|
||||
name string,
|
||||
args []sql.Expression,
|
||||
overloads *Overloads,
|
||||
fnOverloads []Overload,
|
||||
isOperator bool,
|
||||
runner sql.StatementRunner,
|
||||
) *CompiledFunction {
|
||||
c := &CompiledFunction{
|
||||
Name: name,
|
||||
Arguments: args,
|
||||
IsOperator: isOperator,
|
||||
overloads: overloads,
|
||||
fnOverloads: fnOverloads,
|
||||
runner: runner,
|
||||
}
|
||||
// First we'll analyze all the parameters.
|
||||
originalTypes, err := c.analyzeParameters(ctx)
|
||||
if err != nil {
|
||||
// Errors should be returned from the call to Eval, so we'll stash it for now
|
||||
c.stashedErr = err
|
||||
return c
|
||||
}
|
||||
// Next we'll resolve the overload based on the parameters given.
|
||||
overload, err := c.resolve(ctx, overloads, fnOverloads, originalTypes)
|
||||
if err != nil {
|
||||
c.stashedErr = err
|
||||
return c
|
||||
}
|
||||
// If we do not receive an overload, then the parameters given did not result in a valid match
|
||||
if !overload.Valid() {
|
||||
if isOperator {
|
||||
if strings.HasPrefix(name, "internal_binary_operator_func_") {
|
||||
opStr := strings.TrimPrefix(name, "internal_binary_operator_func_")
|
||||
var leftType, rightType string
|
||||
if len(originalTypes) > 0 {
|
||||
leftType = originalTypes[0].String()
|
||||
}
|
||||
if len(originalTypes) > 1 {
|
||||
rightType = originalTypes[1].String()
|
||||
}
|
||||
c.stashedErr = cerrors.Errorf("operator does not exist: %s %s %s", leftType, opStr, rightType)
|
||||
return c
|
||||
} else if strings.HasPrefix(name, "internal_unary_operator_func_") {
|
||||
opStr := strings.TrimPrefix(name, "internal_unary_operator_func_")
|
||||
var childType string
|
||||
if len(originalTypes) > 0 {
|
||||
childType = originalTypes[0].String()
|
||||
}
|
||||
c.stashedErr = cerrors.Errorf("operator does not exist: %s%s", opStr, childType)
|
||||
return c
|
||||
}
|
||||
}
|
||||
c.stashedErr = ErrFunctionDoesNotExist.New(c.OverloadString(originalTypes))
|
||||
return c
|
||||
}
|
||||
|
||||
fn := overload.Function()
|
||||
|
||||
// Then we'll handle the polymorphic types
|
||||
// https://www.postgresql.org/docs/15/extend-type-system.html#EXTEND-TYPES-POLYMORPHIC
|
||||
c.callResolved = make([]*pgtypes.DoltgresType, len(overload.params.paramTypes)+1)
|
||||
hasPolymorphicParam := false
|
||||
for i, param := range overload.params.paramTypes {
|
||||
if param.IsPolymorphicType() {
|
||||
// resolve will ensure that the parameter types are valid, so we can just assign them here
|
||||
hasPolymorphicParam = true
|
||||
c.callResolved[i] = originalTypes[i]
|
||||
} else if param.ID == pgtypes.Any.ID {
|
||||
c.callResolved[i] = originalTypes[i]
|
||||
} else if i < len(args) {
|
||||
if d, ok := args[i].Type(ctx).(*pgtypes.DoltgresType); ok {
|
||||
if param.IsRecordType() && d.IsCompositeType() {
|
||||
// Preserve the composite type's field info (CompositeAttrs) so that
|
||||
// functions like row_to_json can access column names at call time.
|
||||
param = d
|
||||
} else {
|
||||
// `param` is a default type which does not have type modifier set
|
||||
param = param.WithAttTypMod(d.GetAttTypMod())
|
||||
}
|
||||
}
|
||||
c.callResolved[i] = param
|
||||
}
|
||||
}
|
||||
returnType := fn.GetReturn()
|
||||
c.callResolved[len(c.callResolved)-1] = returnType
|
||||
if returnType.IsPolymorphicType() {
|
||||
if hasPolymorphicParam {
|
||||
c.callResolved[len(c.callResolved)-1] = c.resolvePolymorphicReturnType(overload.params.paramTypes, originalTypes, returnType)
|
||||
} else if c.Name == "array_in" || c.Name == "array_recv" || c.Name == "enum_in" || c.Name == "enum_recv" || c.Name == "anyenum_in" || c.Name == "anyenum_recv" {
|
||||
// The return type should resolve to the type of OID value passed in as second argument.
|
||||
// TODO: Possible that the oid type has a special property with polymorphic return types,
|
||||
// in that perhaps their value will set the return type in the absence of another polymorphic type in the parameter list
|
||||
} else {
|
||||
c.stashedErr = cerrors.Errorf("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.", returnType.String())
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
// Lastly, we assign everything to the function struct
|
||||
c.overload = overload
|
||||
c.originalTypes = originalTypes
|
||||
return c
|
||||
}
|
||||
|
||||
// FunctionName implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) FunctionName() string {
|
||||
return c.Name
|
||||
}
|
||||
|
||||
// Description implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) Description() string {
|
||||
return fmt.Sprintf("The PostgreSQL function `%s`", c.Name)
|
||||
}
|
||||
|
||||
// Resolved implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) Resolved() bool {
|
||||
for _, param := range c.Arguments {
|
||||
if !param.Resolved() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
// We don't error until evaluation time, so we need to tell the engine we're resolved if there was a stashed error
|
||||
return c.stashedErr != nil || c.overload.Valid()
|
||||
}
|
||||
|
||||
// StashedError returns the stashed error if one exists. Otherwise, returns nil.
|
||||
func (c *CompiledFunction) StashedError() error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.stashedErr
|
||||
}
|
||||
|
||||
// String implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) String() string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(c.Name + "(")
|
||||
for i, param := range c.Arguments {
|
||||
// Aliases will output the string "x as x", which is an artifact of how we build the AST, so we'll bypass it
|
||||
if alias, ok := param.(*expression.Alias); ok {
|
||||
param = alias.Child
|
||||
}
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(param.String())
|
||||
}
|
||||
sb.WriteString(")")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// OverloadString returns the name of the function represented by the given overload.
|
||||
func (c *CompiledFunction) OverloadString(types []*pgtypes.DoltgresType) string {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(c.Name + "(")
|
||||
for i, t := range types {
|
||||
if i > 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
sb.WriteString(t.String())
|
||||
}
|
||||
sb.WriteString(")")
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Type implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) Type(ctx *sql.Context) sql.Type {
|
||||
if len(c.callResolved) > 0 {
|
||||
rt := c.callResolved[len(c.callResolved)-1]
|
||||
rt = getTypeIfRowType(c.IsSRF(), rt)
|
||||
// If the return type is polymorphic type, we need the underlying type to be able to
|
||||
// convert the result value using the base type.
|
||||
// TODO: need to add underlying to these types for IO input and output uses
|
||||
if rt.IsPolymorphicType() && len(c.originalTypes) > 0 {
|
||||
rt = c.originalTypes[0]
|
||||
if rt.IsArrayType() {
|
||||
return rt.ArrayBaseType()
|
||||
}
|
||||
}
|
||||
return rt
|
||||
}
|
||||
// Compilation must have errored, so we'll return the unknown type
|
||||
return pgtypes.Unknown
|
||||
}
|
||||
|
||||
// IsNullable implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) IsNullable(ctx *sql.Context) bool {
|
||||
// All functions seem to return NULL when given a NULL value
|
||||
return true
|
||||
}
|
||||
|
||||
// IsNonDeterministic implements the interface sql.NonDeterministicExpression.
|
||||
func (c *CompiledFunction) IsNonDeterministic() bool {
|
||||
if c.overload.Valid() {
|
||||
return c.overload.Function().NonDeterministic()
|
||||
}
|
||||
// Compilation must have errored, so we'll just return true
|
||||
return true
|
||||
}
|
||||
|
||||
// IsStrict returns whether this function has the STRICT property regarding nulls.
|
||||
func (c *CompiledFunction) IsStrict() bool {
|
||||
if c.overload.Valid() {
|
||||
return c.overload.Function().IsStrict()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSRF returns whether this function is a set returning function.
|
||||
func (c *CompiledFunction) IsSRF() bool {
|
||||
if c.overload.Valid() {
|
||||
return c.overload.Function().IsSRF()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsVariadic returns whether this function has any variadic parameters.
|
||||
func (c *CompiledFunction) IsVariadic() bool {
|
||||
if c.overload.Valid() {
|
||||
return c.overload.params.variadic != -1
|
||||
}
|
||||
// Compilation must have errored, so we'll just return true
|
||||
return true
|
||||
}
|
||||
|
||||
// Eval implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
|
||||
// If we have a stashed error, then we should return that now. Errors are stashed when they're supposed to be
|
||||
// returned during the call to Eval. This helps to ensure consistency with how errors are returned in Postgres.
|
||||
if c.stashedErr != nil {
|
||||
return nil, c.stashedErr
|
||||
}
|
||||
|
||||
// Evaluate all arguments, returning immediately if we encounter a null argument and the function is marked STRICT
|
||||
var err error
|
||||
isStrict := c.overload.Function().IsStrict()
|
||||
args := make([]any, len(c.Arguments))
|
||||
exprTypes := make([]*pgtypes.DoltgresType, len(args))
|
||||
for i, arg := range c.Arguments {
|
||||
args[i], err = arg.Eval(ctx, row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var ok bool
|
||||
if exprTypes[i], ok = arg.Type(ctx).(*pgtypes.DoltgresType); !ok {
|
||||
dt, err := pgtypes.FromGmsTypeToDoltgresType(arg.Type(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args[i], _, _ = dt.Convert(ctx, args[i])
|
||||
exprTypes[i] = dt
|
||||
}
|
||||
if args[i] == nil && isStrict {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(c.overload.casts) > 0 {
|
||||
targetParamTypes := c.overload.params.paramTypes
|
||||
for i, arg := range args {
|
||||
// For variadic params, we need to identify the corresponding target type
|
||||
var targetType *pgtypes.DoltgresType
|
||||
isVariadicArg := c.overload.params.variadic >= 0 && i >= len(c.overload.params.paramTypes)-1
|
||||
if isVariadicArg {
|
||||
targetType = targetParamTypes[c.overload.params.variadic]
|
||||
if !targetType.IsArrayType() {
|
||||
// should be impossible, we check this at function compile time
|
||||
return nil, cerrors.Errorf("variadic arguments must be array types, was %T", targetType)
|
||||
}
|
||||
targetType = targetType.ArrayBaseType()
|
||||
} else {
|
||||
targetType = targetParamTypes[i]
|
||||
// When the declared parameter type is anyarray, the implicit cast from an
|
||||
// unknown/text argument (via UseInOut) would target anyarray.IoInput which
|
||||
// cannot be loaded as a QuickFunction. Resolve to the concrete array type
|
||||
// (e.g. _aggtype) so the cast uses the real element-type I/O path instead.
|
||||
// TODO: If targetType.ID can be resolved to the concrete type earlier in
|
||||
// processing, then we don't need this check here anymore.
|
||||
if targetType.ID == pgtypes.AnyArray.ID {
|
||||
targetType = c.resolvePolymorphicReturnType(targetParamTypes, exprTypes, targetType)
|
||||
}
|
||||
}
|
||||
|
||||
if c.overload.casts[i].ID.IsValid() {
|
||||
args[i], err = c.overload.casts[i].Eval(ctx, arg, exprTypes[i], targetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, cerrors.Errorf("function %s is missing the appropriate implicit cast", c.OverloadString(c.originalTypes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args = c.overload.params.coalesceVariadicValues(args)
|
||||
|
||||
// Call the function
|
||||
switch f := c.overload.Function().(type) {
|
||||
case Function0:
|
||||
return f.Callable(ctx)
|
||||
case Function1:
|
||||
return f.Callable(ctx, ([2]*pgtypes.DoltgresType)(c.callResolved), args[0])
|
||||
case Function1N:
|
||||
return f.Callable(ctx, c.callResolved, args[0], args[1:])
|
||||
case Function2:
|
||||
return f.Callable(ctx, ([3]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1])
|
||||
case Function2N:
|
||||
return f.Callable(ctx, c.callResolved, args[0], args[1], args[2:])
|
||||
case Function3:
|
||||
return f.Callable(ctx, ([4]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1], args[2])
|
||||
case Function4:
|
||||
return f.Callable(ctx, ([5]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1], args[2], args[3])
|
||||
case Function5:
|
||||
return f.Callable(ctx, ([6]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1], args[2], args[3], args[4])
|
||||
case Function6:
|
||||
return f.Callable(ctx, ([7]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1], args[2], args[3], args[4], args[5])
|
||||
case Function7:
|
||||
return f.Callable(ctx, ([8]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1], args[2], args[3], args[4], args[5], args[6])
|
||||
case InterpretedFunction:
|
||||
return plpgsql.Call(ctx, f, c.runner, c.callResolved, args)
|
||||
case CFunction:
|
||||
cfunc, err := extensions.GetExtensionFunction(f.ExtensionName, f.ExtensionSymbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cargs := make([]pg_extension.NullableDatum, len(args))
|
||||
for i, argType := range f.ParameterTypes { // TODO: ParameterTypes does not account for variadic parameters
|
||||
cConvFunc, ok := cConversionToDatumMap[argType.ID]
|
||||
if !ok {
|
||||
return nil, cerrors.Errorf("no conversion function from Go to C for `%s`", argType.ID.TypeName())
|
||||
}
|
||||
cargs[i], err = cConvFunc(args[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
result, isNotNull := pg_extension.CallFmgrFunction(cfunc.Ptr, cargs...)
|
||||
if isNotNull {
|
||||
cConvFunc, ok := cConversionFromDatumMap[f.ReturnType.ID]
|
||||
if !ok {
|
||||
return nil, cerrors.Errorf("no conversion function from C to Go for `%s`", f.ReturnType.ID.TypeName())
|
||||
}
|
||||
retVal, err := cConvFunc(result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return retVal, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
case SQLFunction:
|
||||
return CallSqlFunction(ctx, f, c.runner, args)
|
||||
default:
|
||||
return nil, cerrors.Errorf("unknown function type in CompiledFunction::Eval %T", f)
|
||||
}
|
||||
}
|
||||
|
||||
// EvalRowIter implements sql.RowIterExpression
|
||||
func (c *CompiledFunction) EvalRowIter(ctx *sql.Context, r sql.Row) (sql.RowIter, error) {
|
||||
eval, err := c.Eval(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch v := eval.(type) {
|
||||
case sql.RowIter:
|
||||
return v, nil
|
||||
case nil:
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, cerrors.Errorf("function %s returned a value of type %T, which is not a RowIter", c.Name, eval)
|
||||
}
|
||||
}
|
||||
|
||||
// ReturnsRowIter implements the interface sql.RowIterExpression
|
||||
func (c *CompiledFunction) ReturnsRowIter() bool {
|
||||
return c.IsSRF()
|
||||
}
|
||||
|
||||
// Children implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) Children() []sql.Expression {
|
||||
return c.Arguments
|
||||
}
|
||||
|
||||
// WithChildren implements the interface sql.Expression.
|
||||
func (c *CompiledFunction) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
|
||||
if len(children) != len(c.Arguments) {
|
||||
return nil, sql.ErrInvalidChildrenNumber.New(len(children), len(c.Arguments))
|
||||
}
|
||||
|
||||
// We have to re-resolve here, since the change in children may require it (e.g. we have more type info than we did)
|
||||
return newCompiledFunctionInternal(ctx, c.Name, children, c.overloads, c.fnOverloads, c.IsOperator, c.runner), nil
|
||||
}
|
||||
|
||||
// SetStatementRunner implements the interface analyzer.Interpreter.
|
||||
func (c *CompiledFunction) SetStatementRunner(ctx *sql.Context, runner sql.StatementRunner) sql.Expression {
|
||||
nc := *c
|
||||
nc.runner = runner
|
||||
return &nc
|
||||
}
|
||||
|
||||
// GetQuickFunction returns the QuickFunction form of this function, if it exists. If one does not exist, then this
|
||||
// return nil.
|
||||
func (c *CompiledFunction) GetQuickFunction() QuickFunction {
|
||||
if c.stashedErr != nil || !c.Resolved() || !c.overload.Valid() || c.overload.params.variadic != -1 ||
|
||||
len(c.overload.casts) > 0 {
|
||||
return nil
|
||||
}
|
||||
switch f := c.overload.Function().(type) {
|
||||
case Function1:
|
||||
return &QuickFunction1{
|
||||
Name: c.Name,
|
||||
Argument: c.Arguments[0],
|
||||
IsStrict: c.overload.Function().IsStrict(),
|
||||
IsSRF: c.IsSRF(),
|
||||
callResolved: ([2]*pgtypes.DoltgresType)(c.callResolved),
|
||||
function: f,
|
||||
}
|
||||
case Function2:
|
||||
return &QuickFunction2{
|
||||
Name: c.Name,
|
||||
Arguments: ([2]sql.Expression)(c.Arguments),
|
||||
IsStrict: c.overload.Function().IsStrict(),
|
||||
IsSRF: c.IsSRF(),
|
||||
callResolved: ([3]*pgtypes.DoltgresType)(c.callResolved),
|
||||
function: f,
|
||||
}
|
||||
case Function3:
|
||||
return &QuickFunction3{
|
||||
Name: c.Name,
|
||||
Arguments: ([3]sql.Expression)(c.Arguments),
|
||||
IsStrict: c.overload.Function().IsStrict(),
|
||||
IsSRF: c.IsSRF(),
|
||||
callResolved: ([4]*pgtypes.DoltgresType)(c.callResolved),
|
||||
function: f,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// resolve returns an overloadMatch that either matches the given parameters exactly, or is a viable match after casting.
|
||||
// Returns an invalid overloadMatch if a viable match is not found.
|
||||
func (c *CompiledFunction) resolve(ctx *sql.Context, overloads *Overloads, fnOverloads []Overload, argTypes []*pgtypes.DoltgresType) (overloadMatch, error) {
|
||||
// First check for an exact match
|
||||
exactMatch, found := overloads.ExactMatchForTypes(argTypes...)
|
||||
if found {
|
||||
return overloadMatch{
|
||||
params: Overload{
|
||||
function: exactMatch,
|
||||
paramTypes: argTypes,
|
||||
argTypes: argTypes,
|
||||
variadic: -1,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
// There are no exact matches, so now we'll look through all overloads to determine the best match. This is
|
||||
// much more work, but there's a performance penalty for runtime overload resolution in Postgres as well.
|
||||
if c.IsOperator {
|
||||
return c.resolveOperator(ctx, argTypes, overloads, fnOverloads)
|
||||
} else {
|
||||
return c.resolveFunction(ctx, argTypes, fnOverloads)
|
||||
}
|
||||
}
|
||||
|
||||
// resolveOperator resolves an operator according to the rules defined by Postgres.
|
||||
// https://www.postgresql.org/docs/15/typeconv-oper.html
|
||||
func (c *CompiledFunction) resolveOperator(ctx *sql.Context, argTypes []*pgtypes.DoltgresType, overloads *Overloads, fnOverloads []Overload) (overloadMatch, error) {
|
||||
// Binary operators treat unknown literals as the other type, so we'll account for that here to see if we can find
|
||||
// an "exact" match.
|
||||
if len(argTypes) == 2 {
|
||||
leftUnknownType := argTypes[0].ID == pgtypes.Unknown.ID
|
||||
rightUnknownType := argTypes[1].ID == pgtypes.Unknown.ID
|
||||
if (leftUnknownType && !rightUnknownType) || (!leftUnknownType && rightUnknownType) {
|
||||
var typ *pgtypes.DoltgresType
|
||||
identity := casts.Cast{
|
||||
ID: id.NewCast(argTypes[0].ID, argTypes[1].ID),
|
||||
CastType: casts.CastType_Explicit,
|
||||
Function: id.NullFunction,
|
||||
UseInOut: false,
|
||||
}
|
||||
opCasts := []casts.Cast{identity, identity}
|
||||
if leftUnknownType {
|
||||
opCasts[0].UseInOut = true
|
||||
typ = argTypes[1]
|
||||
} else {
|
||||
opCasts[1].UseInOut = true
|
||||
typ = argTypes[0]
|
||||
}
|
||||
if exactMatch, ok := overloads.ExactMatchForTypes(typ, typ); ok {
|
||||
return overloadMatch{
|
||||
params: Overload{
|
||||
function: exactMatch,
|
||||
paramTypes: []*pgtypes.DoltgresType{typ, typ},
|
||||
argTypes: []*pgtypes.DoltgresType{typ, typ},
|
||||
variadic: -1,
|
||||
},
|
||||
casts: opCasts,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
// From this point, the steps appear to be the same for functions and operators
|
||||
return c.resolveFunction(ctx, argTypes, fnOverloads)
|
||||
}
|
||||
|
||||
// resolveFunction resolves a function according to the rules defined by Postgres.
|
||||
// https://www.postgresql.org/docs/15/typeconv-func.html
|
||||
func (c *CompiledFunction) resolveFunction(ctx *sql.Context, argTypes []*pgtypes.DoltgresType, overloads []Overload) (overloadMatch, error) {
|
||||
// First we'll discard all overloads that do not have implicitly-convertible param types
|
||||
compatibleOverloads, err := c.typeCompatibleOverloads(ctx, overloads, argTypes)
|
||||
if err != nil {
|
||||
return overloadMatch{}, err
|
||||
}
|
||||
|
||||
// No compatible overloads available, return early
|
||||
if len(compatibleOverloads) == 0 {
|
||||
return overloadMatch{}, nil
|
||||
}
|
||||
|
||||
// If we've found exactly one match then we'll return that one
|
||||
// TODO: we need to also prefer non-variadic functions here over variadic ones (no such conflict can exist for now)
|
||||
// https://www.postgresql.org/docs/15/typeconv-func.html
|
||||
if len(compatibleOverloads) == 1 {
|
||||
return compatibleOverloads[0], nil
|
||||
}
|
||||
|
||||
// Next rank the candidates by the number of params whose types match exactly
|
||||
closestMatches := c.closestTypeMatches(argTypes, compatibleOverloads)
|
||||
|
||||
// Now check again for exactly one match
|
||||
if len(closestMatches) == 1 {
|
||||
return closestMatches[0], nil
|
||||
}
|
||||
|
||||
// If there was more than a single match, try to find the one with the most preferred type conversions
|
||||
preferredOverloads := c.preferredTypeMatches(argTypes, closestMatches)
|
||||
|
||||
// Check once more for exactly one match
|
||||
if len(preferredOverloads) == 1 {
|
||||
return preferredOverloads[0], nil
|
||||
}
|
||||
|
||||
// Next we'll check the type categories for `unknown` types
|
||||
unknownOverloads, ok := c.unknownTypeCategoryMatches(argTypes, preferredOverloads)
|
||||
if !ok {
|
||||
return overloadMatch{}, nil
|
||||
}
|
||||
|
||||
// Check again for exactly one match
|
||||
if len(unknownOverloads) == 1 {
|
||||
return unknownOverloads[0], nil
|
||||
}
|
||||
|
||||
// No matching function overload found
|
||||
return overloadMatch{}, nil
|
||||
}
|
||||
|
||||
// typeCompatibleOverloads returns all overloads that have a matching number of params whose types can be
|
||||
// implicitly converted to the ones provided. This is the set of all possible overloads that could be used with the
|
||||
// param types provided.
|
||||
func (c *CompiledFunction) typeCompatibleOverloads(ctx *sql.Context, fnOverloads []Overload, argTypes []*pgtypes.DoltgresType) ([]overloadMatch, error) {
|
||||
castsColl, err := core.GetCastsCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var compatible []overloadMatch
|
||||
for _, overload := range fnOverloads {
|
||||
isConvertible := true
|
||||
overloadCasts := make([]casts.Cast, len(argTypes))
|
||||
// Polymorphic parameters must be gathered so that we can later verify that they all have matching base types
|
||||
var polymorphicParameters []*pgtypes.DoltgresType
|
||||
var polymorphicTargets []*pgtypes.DoltgresType
|
||||
for i := range argTypes {
|
||||
paramType := overload.argTypes[i]
|
||||
if paramType.IsValidForPolymorphicType(argTypes[i]) {
|
||||
overloadCasts[i] = casts.Cast{
|
||||
ID: id.NewCast(argTypes[i].ID, paramType.ID),
|
||||
CastType: casts.CastType_Explicit,
|
||||
Function: id.NullFunction,
|
||||
UseInOut: false,
|
||||
}
|
||||
polymorphicParameters = append(polymorphicParameters, paramType)
|
||||
polymorphicTargets = append(polymorphicTargets, argTypes[i])
|
||||
} else if paramType.IsRecordType() && argTypes[i].IsCompositeType() {
|
||||
// Composite types (e.g. table row types) are compatible with the generic Record parameter.
|
||||
overloadCasts[i] = casts.Cast{
|
||||
ID: id.NewCast(argTypes[i].ID, paramType.ID),
|
||||
CastType: casts.CastType_Explicit,
|
||||
Function: id.NullFunction,
|
||||
UseInOut: false,
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
overloadCasts[i], err = castsColl.GetImplicitCast(ctx, argTypes[i], paramType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !overloadCasts[i].ID.IsValid() {
|
||||
isConvertible = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if isConvertible && c.polymorphicTypesCompatible(polymorphicParameters, polymorphicTargets) {
|
||||
compatible = append(compatible, overloadMatch{params: overload, casts: overloadCasts})
|
||||
}
|
||||
}
|
||||
return compatible, nil
|
||||
}
|
||||
|
||||
// closestTypeMatches returns the set of overload candidates that have the most exact type matches for the arg types
|
||||
// provided.
|
||||
func (*CompiledFunction) closestTypeMatches(argTypes []*pgtypes.DoltgresType, candidates []overloadMatch) []overloadMatch {
|
||||
matchCount := 0
|
||||
var matches []overloadMatch
|
||||
for _, cand := range candidates {
|
||||
currentMatchCount := 0
|
||||
for argIdx := range argTypes {
|
||||
argType := cand.params.argTypes[argIdx]
|
||||
if argTypes[argIdx].ID == argType.ID || (argTypes[argIdx].ID == pgtypes.Unknown.ID && argType.ID == pgtypes.Text.ID) {
|
||||
currentMatchCount++
|
||||
}
|
||||
}
|
||||
if currentMatchCount > matchCount {
|
||||
matchCount = currentMatchCount
|
||||
matches = append([]overloadMatch{}, cand)
|
||||
} else if currentMatchCount == matchCount {
|
||||
matches = append(matches, cand)
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// preferredTypeMatches returns the overload candidates that have the most preferred types for args that require casts.
|
||||
func (*CompiledFunction) preferredTypeMatches(argTypes []*pgtypes.DoltgresType, candidates []overloadMatch) []overloadMatch {
|
||||
preferredCount := 0
|
||||
var preferredOverloads []overloadMatch
|
||||
for _, cand := range candidates {
|
||||
currentPreferredCount := 0
|
||||
for argIdx := range argTypes {
|
||||
argType := cand.params.argTypes[argIdx]
|
||||
if argTypes[argIdx].ID != argType.ID && argType.IsPreferred {
|
||||
currentPreferredCount++
|
||||
}
|
||||
}
|
||||
|
||||
if currentPreferredCount > preferredCount {
|
||||
preferredCount = currentPreferredCount
|
||||
preferredOverloads = append([]overloadMatch{}, cand)
|
||||
} else if currentPreferredCount == preferredCount {
|
||||
preferredOverloads = append(preferredOverloads, cand)
|
||||
}
|
||||
}
|
||||
return preferredOverloads
|
||||
}
|
||||
|
||||
// unknownTypeCategoryMatches checks the type categories of `unknown` types. These types have an inherent bias toward
|
||||
// the string category since an `unknown` literal resembles a string. Returns false if the resolution should fail.
|
||||
func (c *CompiledFunction) unknownTypeCategoryMatches(argTypes []*pgtypes.DoltgresType, candidates []overloadMatch) ([]overloadMatch, bool) {
|
||||
matches := make([]overloadMatch, len(candidates))
|
||||
copy(matches, candidates)
|
||||
// For our first loop, we'll filter matches based on whether they accept the string category
|
||||
for argIdx := range argTypes {
|
||||
// We're only concerned with `unknown` types
|
||||
if argTypes[argIdx].ID != pgtypes.Unknown.ID {
|
||||
continue
|
||||
}
|
||||
var newMatches []overloadMatch
|
||||
for _, match := range matches {
|
||||
if match.params.argTypes[argIdx].TypCategory == pgtypes.TypeCategory_StringTypes {
|
||||
newMatches = append(newMatches, match)
|
||||
}
|
||||
}
|
||||
// If we've found matches in this step, then we'll update our match set
|
||||
if len(newMatches) > 0 {
|
||||
matches = newMatches
|
||||
}
|
||||
}
|
||||
// Return early if we've filtered down to a single match
|
||||
if len(matches) == 1 {
|
||||
return matches, true
|
||||
}
|
||||
// TODO: implement the remainder of step 4.e. from the documentation (following code assumes it has been implemented)
|
||||
// ...
|
||||
|
||||
// If we've discarded every function, then we'll actually return all original candidates
|
||||
if len(matches) == 0 {
|
||||
return candidates, true
|
||||
}
|
||||
// In this case, we've trimmed at least one candidate, so we'll return our new matches
|
||||
return matches, true
|
||||
}
|
||||
|
||||
// polymorphicTypesCompatible returns whether any polymorphic types given are compatible with the expression types given
|
||||
func (*CompiledFunction) polymorphicTypesCompatible(paramTypes []*pgtypes.DoltgresType, exprTypes []*pgtypes.DoltgresType) bool {
|
||||
if len(paramTypes) != len(exprTypes) {
|
||||
return false
|
||||
}
|
||||
// If there are less than two parameters then we don't even need to check
|
||||
if len(paramTypes) < 2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// If one of the types is anyarray, then anyelement behaves as anynonarray, so we can convert them to anynonarray
|
||||
for _, paramType := range paramTypes {
|
||||
if paramType.ID == pgtypes.AnyArray.ID {
|
||||
// At least one parameter is anyarray, so copy all parameters to a new slice and replace anyelement with anynonarray
|
||||
newParamTypes := make([]*pgtypes.DoltgresType, len(paramTypes))
|
||||
copy(newParamTypes, paramTypes)
|
||||
for i := range newParamTypes {
|
||||
if paramTypes[i].ID == pgtypes.AnyElement.ID {
|
||||
newParamTypes[i] = pgtypes.AnyNonArray
|
||||
}
|
||||
}
|
||||
paramTypes = newParamTypes
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The base type is the type that must match between all polymorphic types.
|
||||
var baseType *pgtypes.DoltgresType
|
||||
for i, paramType := range paramTypes {
|
||||
if paramType.IsPolymorphicType() && exprTypes[i].ID != pgtypes.Unknown.ID {
|
||||
// Although we do this check before we ever reach this function, we do it again as we may convert anyelement
|
||||
// to anynonarray, which changes type validity
|
||||
if !paramType.IsValidForPolymorphicType(exprTypes[i]) {
|
||||
return false
|
||||
}
|
||||
// Get the base expression type that we'll compare against
|
||||
baseExprType := exprTypes[i]
|
||||
if baseExprType.IsArrayType() {
|
||||
baseExprType = baseExprType.ArrayBaseType()
|
||||
}
|
||||
// TODO: handle range types
|
||||
// Check that the base expression type matches the previously-found base type
|
||||
if baseType.IsEmptyType() {
|
||||
baseType = baseExprType
|
||||
} else if baseType.ID != baseExprType.ID {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// resolvePolymorphicReturnType returns the type that should be used for the return type. If the return type is not a
|
||||
// polymorphic type, then the return type is directly returned. However, if the return type is a polymorphic type, then
|
||||
// the type is determined using the expression types and parameter types. This makes the assumption that everything has
|
||||
// already been validated.
|
||||
func (c *CompiledFunction) resolvePolymorphicReturnType(functionInterfaceTypes []*pgtypes.DoltgresType, originalTypes []*pgtypes.DoltgresType, returnType *pgtypes.DoltgresType) *pgtypes.DoltgresType {
|
||||
if !returnType.IsPolymorphicType() {
|
||||
return returnType
|
||||
}
|
||||
// We can use the first polymorphic non-unknown type that we find, since we can morph it into any type that we need.
|
||||
// We've verified that all polymorphic types are compatible in a previous step, so this is safe to do.
|
||||
var firstPolymorphicType *pgtypes.DoltgresType
|
||||
for i, functionInterfaceType := range functionInterfaceTypes {
|
||||
if functionInterfaceType.IsPolymorphicType() && originalTypes[i].ID != pgtypes.Unknown.ID {
|
||||
firstPolymorphicType = originalTypes[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if all types are `unknown`, use `text` type
|
||||
if firstPolymorphicType.IsEmptyType() {
|
||||
firstPolymorphicType = pgtypes.Text
|
||||
}
|
||||
|
||||
switch returnType.ID {
|
||||
case pgtypes.AnyElement.ID, pgtypes.AnyNonArray.ID:
|
||||
// For return types, anyelement behaves the same as anynonarray.
|
||||
// This isn't explicitly in the documentation, however it does note that:
|
||||
// "...anynonarray and anyenum do not represent separate type variables; they are the same type as anyelement..."
|
||||
// The implication of this being that anyelement will always return the base type even for array types,
|
||||
// just like anynonarray would.
|
||||
if firstPolymorphicType.IsArrayType() {
|
||||
return firstPolymorphicType.ArrayBaseType()
|
||||
} else {
|
||||
return firstPolymorphicType
|
||||
}
|
||||
case pgtypes.AnyArray.ID:
|
||||
// Array types will return themselves, so this is safe
|
||||
if firstPolymorphicType.IsArrayType() {
|
||||
return firstPolymorphicType
|
||||
} else if firstPolymorphicType.ID == pgtypes.Internal.ID {
|
||||
return pgtypes.IDToBuiltInDoltgresType[firstPolymorphicType.BaseTypeForInternal]
|
||||
} else {
|
||||
return firstPolymorphicType.ToArrayType()
|
||||
}
|
||||
default:
|
||||
panic(cerrors.Errorf("`%s` is not yet handled during function compilation", returnType.String()))
|
||||
}
|
||||
}
|
||||
|
||||
// analyzeParameters analyzes the parameters within an Eval call.
|
||||
func (c *CompiledFunction) analyzeParameters(ctx *sql.Context) (originalTypes []*pgtypes.DoltgresType, err error) {
|
||||
originalTypes = make([]*pgtypes.DoltgresType, len(c.Arguments))
|
||||
for i, param := range c.Arguments {
|
||||
returnType := param.Type(ctx)
|
||||
if extendedType, ok := returnType.(*pgtypes.DoltgresType); ok && !extendedType.IsEmptyType() {
|
||||
if extendedType.TypType == pgtypes.TypeType_Domain {
|
||||
extendedType = extendedType.DomainUnderlyingBaseType()
|
||||
}
|
||||
originalTypes[i] = extendedType
|
||||
} else {
|
||||
// TODO: we need to remove GMS types from all of our expressions so that we can remove this
|
||||
dt, err := pgtypes.FromGmsTypeToDoltgresType(returnType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
originalTypes[i] = dt
|
||||
}
|
||||
}
|
||||
return originalTypes, nil
|
||||
}
|
||||
|
||||
// specificFuncImpl implements the interface sql.Expression.
|
||||
func (*CompiledFunction) specificFuncImpl() {}
|
||||
|
||||
// getTypeIfRowType returns the underlying type if it's Row Type;
|
||||
// otherwise, it returns the type that is passed.
|
||||
func getTypeIfRowType(isSRF bool, t *pgtypes.DoltgresType) *pgtypes.DoltgresType {
|
||||
if isSRF {
|
||||
// TODO: need support for used defined types
|
||||
if typ, ok := pgtypes.IDToBuiltInDoltgresType[t.Elem.ID]; ok {
|
||||
return typ
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
// ResolveDefaultValues adds missing arguments if there is any using the default value set on the parameter.
|
||||
// It checks if it's a valid SQL function that has fewer arguments than defined parameters.
|
||||
func (c *CompiledFunction) ResolveDefaultValues(ctx *sql.Context, getDefExpr func(defExpr string) (sql.Expression, error)) error {
|
||||
if !c.overload.Valid() {
|
||||
return nil
|
||||
}
|
||||
sqlFunc, ok := c.overload.params.function.(SQLFunction)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(c.Arguments) < len(sqlFunc.ParameterTypes) {
|
||||
castsColl, err := core.GetCastsCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i, param := range sqlFunc.ParameterTypes {
|
||||
if i < len(c.Arguments) {
|
||||
if exprTypeId := c.Arguments[i].Type(ctx).(*pgtypes.DoltgresType).ID; exprTypeId != pgtypes.Unknown.ID && param.ID != exprTypeId {
|
||||
// if non-matching type, then skip appending defaults
|
||||
break
|
||||
}
|
||||
} else if sqlFunc.ParameterDefaults[i] != "" {
|
||||
// only if there is default, then append
|
||||
cdv, err := getDefExpr(sqlFunc.ParameterDefaults[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Arguments = append(c.Arguments, cdv)
|
||||
implicitCast, err := castsColl.GetImplicitCast(ctx, cdv.Type(ctx).(*pgtypes.DoltgresType), sqlFunc.ParameterTypes[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.overload.casts = append(c.overload.casts, implicitCast)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,614 @@
|
||||
// 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/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// FunctionInterface is an interface for PostgreSQL functions.
|
||||
type FunctionInterface interface {
|
||||
// GetName returns the name of the function. The name is case-insensitive, so the casing does not matter.
|
||||
GetName() string
|
||||
// GetReturn returns the return type.
|
||||
GetReturn() *pgtypes.DoltgresType
|
||||
// GetParameters returns the parameter types for the function.
|
||||
GetParameters() []*pgtypes.DoltgresType
|
||||
// VariadicIndex returns the index of the variadic parameter, if it exists, or -1 otherwise
|
||||
VariadicIndex() int
|
||||
// GetExpectedParameterCount returns the number of parameters that are valid for this function.
|
||||
GetExpectedParameterCount() int
|
||||
// NonDeterministic returns whether the function is non-deterministic.
|
||||
NonDeterministic() bool
|
||||
// IsCVariadic returns whether this function uses a variadic form restricted to C-language functions.
|
||||
// https://www.postgresql.org/docs/15/xfunc-c.html#id-1.8.3.13.13
|
||||
IsCVariadic() bool
|
||||
// IsStrict returns whether the function is STRICT, which means if any parameter is NULL, then it returns NULL.
|
||||
// Otherwise, if it's not, the NULL input must be handled by user.
|
||||
IsStrict() bool
|
||||
// IsSRF returns whether the function is a set returning function, meaning whether the
|
||||
// function returns one or more rows as a result.
|
||||
IsSRF() bool
|
||||
// InternalID returns the ID associated with this function.
|
||||
InternalID() id.Id
|
||||
// enforceInterfaceInheritance is a special function that ensures only the expected types inherit this interface.
|
||||
enforceInterfaceInheritance(error)
|
||||
}
|
||||
|
||||
// AggregateFunctionInterface is an interface for PostgreSQL aggregate functions
|
||||
type AggregateFunctionInterface interface {
|
||||
FunctionInterface
|
||||
// TODO: this maybe needs to take the place of the Callable function
|
||||
NewBuffer([]sql.Expression) (sql.AggregationBuffer, error)
|
||||
}
|
||||
|
||||
// Function0 is a function that does not take any parameters.
|
||||
type Function0 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context) (any, error)
|
||||
}
|
||||
|
||||
// Function1 is a function that takes one parameter. The parameter and return type is passed into the Callable function
|
||||
// when the parameter (and possibly return type) is a polymorphic type. The return type is the last type in the array.
|
||||
type Function1 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [1]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [2]*pgtypes.DoltgresType, val1 any) (any, error)
|
||||
}
|
||||
|
||||
// Function1N is a function that takes at least one parameter. This is different from a SQL variadic function, as the
|
||||
// additional parameters may have different types (that do not contribute to type deduction like with `anyelement`), and
|
||||
// is only usable by C-language functions (notably built-in ones such as `concat`). The field `paramsAndReturn` works
|
||||
// similarly to standard functions, with the last type being the return type.
|
||||
type Function1N struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [1]*pgtypes.DoltgresType
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn []*pgtypes.DoltgresType, val1 any, vals []any) (any, error)
|
||||
}
|
||||
|
||||
// Function2 is a function that takes two parameters. The parameter and return types are passed into the Callable
|
||||
// function when the parameters (and possibly return type) have at least one polymorphic type. The return type is the
|
||||
// last type in the array.
|
||||
type Function2 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [2]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [3]*pgtypes.DoltgresType, val1 any, val2 any) (any, error)
|
||||
}
|
||||
|
||||
// Function2N is a function that takes at least two parameters. This is different from a SQL variadic function, as the
|
||||
// additional parameters may have different types (that do not contribute to type deduction like with `anyelement`), and
|
||||
// is only usable by C-language functions (notably built-in ones such as `concat_ws`). The field `paramsAndReturn` works
|
||||
// similarly to standard functions, with the last type being the return type.
|
||||
type Function2N struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [2]*pgtypes.DoltgresType
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn []*pgtypes.DoltgresType, val1 any, val2 any, vals []any) (any, error)
|
||||
}
|
||||
|
||||
// Function3 is a function that takes three parameters. The parameter and return types are passed into the Callable
|
||||
// function when the parameters (and possibly return type) have at least one polymorphic type. The return type is the
|
||||
// last type in the array.
|
||||
type Function3 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [3]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [4]*pgtypes.DoltgresType, val1 any, val2 any, val3 any) (any, error)
|
||||
}
|
||||
|
||||
// Function4 is a function that takes four parameters. The parameter and return types are passed into the Callable
|
||||
// function when the parameters (and possibly return type) have at least one polymorphic type. The return type is the
|
||||
// last type in the array.
|
||||
type Function4 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [4]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [5]*pgtypes.DoltgresType, val1 any, val2 any, val3 any, val4 any) (any, error)
|
||||
}
|
||||
|
||||
// Function5 is a function that takes five parameters. The parameter and return types are passed into the Callable
|
||||
// function when the parameters (and possibly return type) have at least one polymorphic type. The return type is the
|
||||
// last type in the array.
|
||||
type Function5 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [5]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [6]*pgtypes.DoltgresType, val1 any, val2 any, val3 any, val4 any, val5 any) (any, error)
|
||||
}
|
||||
|
||||
// Function6 is a function that takes six parameters. The parameter and return types are passed into the Callable
|
||||
// function when the parameters (and possibly return type) have at least one polymorphic type. The return type is the
|
||||
// last type in the array.
|
||||
type Function6 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [6]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [7]*pgtypes.DoltgresType, val1 any, val2 any, val3 any, val4 any, val5 any, val6 any) (any, error)
|
||||
}
|
||||
|
||||
// Function7 is a function that takes seven parameters. The parameter and return types are passed into the Callable
|
||||
// function when the parameters (and possibly return type) have at least one polymorphic type. The return type is the
|
||||
// last type in the array.
|
||||
type Function7 struct {
|
||||
Name string
|
||||
Return *pgtypes.DoltgresType
|
||||
Parameters [7]*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Callable func(ctx *sql.Context, paramsAndReturn [8]*pgtypes.DoltgresType, val1 any, val2 any, val3 any, val4 any, val5 any, val6 any, val7 any) (any, error)
|
||||
}
|
||||
|
||||
var _ FunctionInterface = Function0{}
|
||||
var _ FunctionInterface = Function1{}
|
||||
var _ FunctionInterface = Function1N{}
|
||||
var _ FunctionInterface = Function2{}
|
||||
var _ FunctionInterface = Function2N{}
|
||||
var _ FunctionInterface = Function3{}
|
||||
var _ FunctionInterface = Function4{}
|
||||
var _ FunctionInterface = Function5{}
|
||||
var _ FunctionInterface = Function6{}
|
||||
var _ FunctionInterface = Function7{}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function0) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function0) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function0) GetParameters() []*pgtypes.DoltgresType { return nil }
|
||||
|
||||
func (f Function0) VariadicIndex() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function0) GetExpectedParameterCount() int { return 0 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function0) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function0) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function0) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function0) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function0) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function0) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function1) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function1) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function1) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function1) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 0
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function1) GetExpectedParameterCount() int { return 1 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function1) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function1) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function1) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function1) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function1) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function1) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function1N) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function1N) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function1N) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function1N) VariadicIndex() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function1N) GetExpectedParameterCount() int { return 1 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function1N) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function1N) IsCVariadic() bool { return true }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function1N) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function1N) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function1N) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function1N) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function2) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function2) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function2) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function2) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 1
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function2) GetExpectedParameterCount() int { return 2 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function2) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function2) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function2) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function2) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function2) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function2) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function2N) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function2N) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function2N) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function2N) VariadicIndex() int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function2N) GetExpectedParameterCount() int { return 2 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function2N) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function2N) IsCVariadic() bool { return true }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function2N) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function2N) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function2N) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function2N) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function3) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function3) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function3) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function3) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 2
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function3) GetExpectedParameterCount() int { return 3 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function3) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function3) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function3) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function3) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function3) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID, f.Parameters[2].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function3) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function4) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function4) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function4) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function4) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 3
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function4) GetExpectedParameterCount() int { return 4 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function4) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function4) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function4) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function4) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function4) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID, f.Parameters[2].ID, f.Parameters[3].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function4) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function5) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function5) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function5) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function5) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 4
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function5) GetExpectedParameterCount() int { return 5 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function5) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function5) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function5) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function5) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function5) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID, f.Parameters[2].ID, f.Parameters[3].ID, f.Parameters[4].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function5) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function6) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function6) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function6) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function6) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 5
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function6) GetExpectedParameterCount() int { return 6 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function6) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function6) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function6) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function6) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function6) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID, f.Parameters[2].ID, f.Parameters[3].ID, f.Parameters[4].ID, f.Parameters[5].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function6) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// GetName implements the FunctionInterface interface.
|
||||
func (f Function7) GetName() string { return f.Name }
|
||||
|
||||
// GetReturn implements the FunctionInterface interface.
|
||||
func (f Function7) GetReturn() *pgtypes.DoltgresType { return getTypeIfRowType(f.IsSRF(), f.Return) }
|
||||
|
||||
// GetParameters implements the FunctionInterface interface.
|
||||
func (f Function7) GetParameters() []*pgtypes.DoltgresType { return f.Parameters[:] }
|
||||
|
||||
// VariadicIndex implements the FunctionInterface interface.
|
||||
func (f Function7) VariadicIndex() int {
|
||||
if f.Variadic {
|
||||
return 6
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// GetExpectedParameterCount implements the FunctionInterface interface.
|
||||
func (f Function7) GetExpectedParameterCount() int { return 7 }
|
||||
|
||||
// NonDeterministic implements the FunctionInterface interface.
|
||||
func (f Function7) NonDeterministic() bool { return f.IsNonDeterministic }
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (f Function7) IsCVariadic() bool { return false }
|
||||
|
||||
// IsStrict implements the FunctionInterface interface.
|
||||
func (f Function7) IsStrict() bool { return f.Strict }
|
||||
|
||||
// IsSRF implements the FunctionInterface interface.
|
||||
func (f Function7) IsSRF() bool { return f.SRF }
|
||||
|
||||
// InternalID implements the FunctionInterface interface.
|
||||
func (f Function7) InternalID() id.Id {
|
||||
return id.NewFunction("pg_catalog", f.Name, f.Parameters[0].ID, f.Parameters[1].ID, f.Parameters[2].ID, f.Parameters[3].ID, f.Parameters[4].ID, f.Parameters[5].ID, f.Parameters[6].ID).AsId()
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the FunctionInterface interface.
|
||||
func (f Function7) enforceInterfaceInheritance(error) {}
|
||||
|
||||
// Func1Aggregate is a function that takes one parameter and is an aggregate function.
|
||||
type Func1Aggregate struct {
|
||||
Function1
|
||||
NewAggBuffer func([]sql.Expression) (sql.AggregationBuffer, error)
|
||||
}
|
||||
|
||||
func (f Func1Aggregate) NewBuffer(exprs []sql.Expression) (sql.AggregationBuffer, error) {
|
||||
return f.NewAggBuffer(exprs)
|
||||
}
|
||||
|
||||
var _ AggregateFunctionInterface = Func1Aggregate{}
|
||||
@@ -0,0 +1,35 @@
|
||||
// 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/dolthub/go-mysql-server/sql"
|
||||
)
|
||||
|
||||
// IntermediateFunction is an expression that represents an incomplete PostgreSQL function.
|
||||
type IntermediateFunction struct {
|
||||
Functions *Overloads
|
||||
AllOverloads []Overload
|
||||
IsOperator bool
|
||||
}
|
||||
|
||||
// Compile returns a CompiledFunction created from the calling IntermediateFunction. Returns a nil function if it could
|
||||
// not be compiled.
|
||||
func (f IntermediateFunction) Compile(ctx *sql.Context, name string, parameters ...sql.Expression) *CompiledFunction {
|
||||
if f.Functions == nil {
|
||||
return nil
|
||||
}
|
||||
return newCompiledFunctionInternal(ctx, name, parameters, f.Functions, f.AllOverloads, f.IsOperator, nil)
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// 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 framework
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/dolthub/doltgresql/core"
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/server/plpgsql"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// InterpretedFunction is the implementation of functions created using PL/pgSQL. The created functions are converted to
|
||||
// a collection of operations, and an interpreter iterates over those operations to handle the logic.
|
||||
type InterpretedFunction struct {
|
||||
ID id.Function
|
||||
ReturnType *pgtypes.DoltgresType
|
||||
ParameterNames []string
|
||||
ParameterTypes []*pgtypes.DoltgresType
|
||||
Variadic bool
|
||||
IsNonDeterministic bool
|
||||
Strict bool
|
||||
SRF bool
|
||||
Statements []plpgsql.InterpreterOperation
|
||||
}
|
||||
|
||||
var _ FunctionInterface = InterpretedFunction{}
|
||||
var _ plpgsql.InterpretedFunction = InterpretedFunction{}
|
||||
|
||||
// GetExpectedParameterCount implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) GetExpectedParameterCount() int {
|
||||
return len(iFunc.ParameterTypes)
|
||||
}
|
||||
|
||||
// GetName implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) GetName() string {
|
||||
return iFunc.ID.FunctionName()
|
||||
}
|
||||
|
||||
// GetParameters implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) GetParameters() []*pgtypes.DoltgresType {
|
||||
return iFunc.ParameterTypes
|
||||
}
|
||||
|
||||
// GetParameterNames returns the names of all parameters.
|
||||
func (iFunc InterpretedFunction) GetParameterNames() []string {
|
||||
return iFunc.ParameterNames
|
||||
}
|
||||
|
||||
// GetReturn implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) GetReturn() *pgtypes.DoltgresType {
|
||||
return iFunc.ReturnType
|
||||
}
|
||||
|
||||
// GetStatements returns the contained statements.
|
||||
func (iFunc InterpretedFunction) GetStatements() []plpgsql.InterpreterOperation {
|
||||
return iFunc.Statements
|
||||
}
|
||||
|
||||
// InternalID implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) InternalID() id.Id {
|
||||
return iFunc.ID.AsId()
|
||||
}
|
||||
|
||||
// IsStrict implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) IsStrict() bool {
|
||||
return iFunc.Strict
|
||||
}
|
||||
|
||||
// IsSRF implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) IsSRF() bool {
|
||||
return iFunc.SRF
|
||||
}
|
||||
|
||||
// NonDeterministic implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) NonDeterministic() bool {
|
||||
return iFunc.IsNonDeterministic
|
||||
}
|
||||
|
||||
// IsCVariadic implements the FunctionInterface interface.
|
||||
func (iFunc InterpretedFunction) IsCVariadic() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// VariadicIndex implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) VariadicIndex() int {
|
||||
// TODO: implement variadic
|
||||
return -1
|
||||
}
|
||||
|
||||
// QuerySingleReturn handles queries that are supposed to return a single value.
|
||||
func (iFunc InterpretedFunction) QuerySingleReturn(ctx *sql.Context, stack plpgsql.InterpreterStack, stmt string, targetType *pgtypes.DoltgresType, bindings []string) (val any, err error) {
|
||||
stmt, _, err = iFunc.ApplyBindings(ctx, stack, stmt, bindings, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sql.RunInterpreted(ctx, func(subCtx *sql.Context) (any, error) {
|
||||
sch, rowIter, _, err := stack.Runner().QueryWithBindings(subCtx, stmt, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rows, err := sql.RowIterToRows(subCtx, rowIter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(sch) != 1 {
|
||||
return nil, errors.New("expression does not result in a single value")
|
||||
}
|
||||
if len(rows) != 1 {
|
||||
return nil, errors.New("expression returned multiple result sets")
|
||||
}
|
||||
if len(rows[0]) != 1 {
|
||||
return nil, errors.New("expression returned multiple results")
|
||||
}
|
||||
if targetType == nil {
|
||||
return rows[0][0], nil
|
||||
}
|
||||
if rows[0][0] == nil {
|
||||
return nil, nil
|
||||
}
|
||||
sourceType, ok := sch[0].Type.(*pgtypes.DoltgresType)
|
||||
if !ok {
|
||||
// TODO: We ensure we have a DoltgresType, but we should also convert the value to
|
||||
// ensure it's in the correct form for the DoltgresType. This logic lives in
|
||||
// pgexpressions.GMSCast, but need to be extracted to avoid a dependency cycle
|
||||
// so it can be used here and from server.plpgsql.
|
||||
sourceType, err = pgtypes.FromGmsTypeToDoltgresType(sch[0].Type)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
castsColl, err := core.GetCastsCollectionFromContext(ctx, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cast, err := castsColl.GetAssignmentCast(ctx, sourceType, targetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !cast.ID.IsValid() {
|
||||
// TODO: We're using assignment casting, but for some reason we have to use I/O casting here, which is incorrect?
|
||||
// We need to dig into this and figure out exactly what's happening, as this is "wrong" according to what
|
||||
// I understand. This lines up more with explicit casting, but it's supposed to be assignment.
|
||||
// Maybe there are specific rules for pgsql?
|
||||
if sourceType.TypCategory == pgtypes.TypeCategory_StringTypes {
|
||||
cast.ID = id.NewCast(sourceType.ID, targetType.ID)
|
||||
cast.UseInOut = true
|
||||
} else {
|
||||
return nil, errors.New("no valid cast for return value")
|
||||
}
|
||||
}
|
||||
return cast.Eval(subCtx, rows[0][0], sourceType, targetType)
|
||||
})
|
||||
}
|
||||
|
||||
// QueryMultiReturn handles queries that may return multiple values over multiple rows.
|
||||
func (iFunc InterpretedFunction) QueryMultiReturn(ctx *sql.Context, stack plpgsql.InterpreterStack, stmt string, bindings []string) (schema sql.Schema, rows []sql.Row, err error) {
|
||||
stmt, _, err = iFunc.ApplyBindings(ctx, stack, stmt, bindings, true)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rows, err = sql.RunInterpreted(ctx, func(subCtx *sql.Context) (rows []sql.Row, err error) {
|
||||
var rowIter sql.RowIter
|
||||
schema, rowIter, _, err = stack.Runner().QueryWithBindings(subCtx, stmt, nil, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// TODO: we should come up with a good way of carrying the RowIter out of the function without needing to wrap
|
||||
// each call to QueryMultiReturn with RunInterpreted. For now, we don't check the returned rows, so this is
|
||||
// fine.
|
||||
return sql.RowIterToRows(subCtx, rowIter)
|
||||
})
|
||||
return schema, rows, err
|
||||
}
|
||||
|
||||
// ApplyBindings applies the given bindings to the statement. If `varFound` is false, then the error will state that
|
||||
// the variable was not found (which means the error may be ignored if you're only concerned with finding a variable).
|
||||
// If `varFound` is true, then the error is related to formatting the variable. `enforceType` adds casting and quotes to
|
||||
// ensure that the value is correctly represented in the string.
|
||||
func (InterpretedFunction) ApplyBindings(ctx *sql.Context, stack plpgsql.InterpreterStack, stmt string, bindings []string, enforceType bool) (newStmt string, varFound bool, err error) {
|
||||
if len(bindings) == 0 {
|
||||
return stmt, false, nil
|
||||
}
|
||||
newStmt = stmt
|
||||
for i, bindingName := range bindings {
|
||||
variable := stack.GetVariable(bindingName)
|
||||
if variable.Type == nil {
|
||||
return newStmt, false, fmt.Errorf("variable `%s` could not be found", bindingName)
|
||||
}
|
||||
var formattedVar string
|
||||
if *variable.Value != nil {
|
||||
formattedVar, err = variable.Type.FormatValueWithContext(ctx, *variable.Value)
|
||||
if err != nil {
|
||||
return newStmt, true, err
|
||||
}
|
||||
if enforceType {
|
||||
switch variable.Type.TypCategory {
|
||||
case pgtypes.TypeCategory_ArrayTypes, pgtypes.TypeCategory_CompositeTypes, pgtypes.TypeCategory_DateTimeTypes, pgtypes.TypeCategory_StringTypes, pgtypes.TypeCategory_UserDefinedTypes:
|
||||
formattedVar = pq.QuoteLiteral(formattedVar)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
formattedVar = "NULL"
|
||||
}
|
||||
if enforceType {
|
||||
if variable.Type.TypCategory == pgtypes.TypeCategory_CompositeTypes {
|
||||
newStmt = strings.Replace(newStmt, "$"+strconv.Itoa(i+1), fmt.Sprintf(`(%s::%s)`, formattedVar, variable.Type.String()), 1)
|
||||
} else {
|
||||
newStmt = strings.Replace(newStmt, "$"+strconv.Itoa(i+1), fmt.Sprintf(`((%s)::%s)`, formattedVar, variable.Type.String()), 1)
|
||||
}
|
||||
} else {
|
||||
newStmt = strings.Replace(newStmt, "$"+strconv.Itoa(i+1), formattedVar, 1)
|
||||
}
|
||||
}
|
||||
return newStmt, true, nil
|
||||
}
|
||||
|
||||
// enforceInterfaceInheritance implements the interface FunctionInterface.
|
||||
func (iFunc InterpretedFunction) enforceInterfaceInheritance(error) {}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2026 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_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/dolthub/doltgresql/core/id"
|
||||
"github.com/dolthub/doltgresql/server/functions"
|
||||
"github.com/dolthub/doltgresql/server/functions/framework"
|
||||
"github.com/dolthub/doltgresql/server/plpgsql"
|
||||
pgtypes "github.com/dolthub/doltgresql/server/types"
|
||||
)
|
||||
|
||||
// TestApplyBindings_RendersRegclassVariableThroughSessionContext asserts
|
||||
// that ApplyBindings renders a regclass-typed variable using the
|
||||
// session context it is given.
|
||||
//
|
||||
// See https://github.com/dolthub/doltgresql/issues/1142.
|
||||
func TestApplyBindings_RendersRegclassVariableThroughSessionContext(t *testing.T) {
|
||||
t.Parallel()
|
||||
functions.Init()
|
||||
framework.Initialize(nil)
|
||||
ctx := sql.NewEmptyContext()
|
||||
stack := plpgsql.NewInterpreterStack(nil)
|
||||
stack.NewVariableWithValue("rel", pgtypes.Regclass, id.NewOID(1259).AsId())
|
||||
|
||||
require.NotPanics(t, func() {
|
||||
_, _, _ = framework.InterpretedFunction{}.ApplyBindings(ctx, stack, "SELECT $1", []string{"rel"}, false)
|
||||
})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user