chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:32:25 +08:00
commit e014feafe1
2285 changed files with 1131979 additions and 0 deletions
+90
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+125
View File
@@ -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
}
+122
View File
@@ -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
View File
@@ -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
}
+19
View File
@@ -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()
}
+52
View File
@@ -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
},
}
+78
View File
@@ -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
},
}
+52
View File
@@ -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
},
}
+52
View File
@@ -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
},
}
+52
View File
@@ -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
},
}
+343
View File
@@ -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
},
}
+55
View File
@@ -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
},
}
+136
View File
@@ -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
},
}
+138
View File
@@ -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
}
+83
View File
@@ -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
}
+51
View File
@@ -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
},
}
+43
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+45
View File
@@ -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
},
}
+74
View File
@@ -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,
}
+74
View File
@@ -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,
}
+74
View File
@@ -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,
}
+225
View File
@@ -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,
}
+331
View File
@@ -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,
}
+739
View File
@@ -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,
}
+558
View File
@@ -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
},
}
+558
View File
@@ -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
},
}
+37
View File
@@ -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()
}
+849
View File
@@ -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")
},
}
+558
View File
@@ -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
},
}
+558
View File
@@ -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
},
}
+429
View File
@@ -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
}
+104
View File
@@ -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)
},
}
+250
View File
@@ -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
}
+584
View File
@@ -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
},
}
+617
View File
@@ -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
}
+65
View File
@@ -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
},
}
+65
View File
@@ -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
},
}
+172
View File
@@ -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
},
}
+42
View File
@@ -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
},
}
+114
View File
@@ -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
}
},
}
+197
View File
@@ -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))
}
+69
View File
@@ -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)
})
}
}
+58
View File
@@ -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)
},
}
+108
View File
@@ -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
},
}
+40
View File
@@ -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
},
}
+69
View File
@@ -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
},
}
+133
View File
@@ -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
}
},
}
+48
View File
@@ -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
},
}
+51
View File
@@ -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
},
}
+41
View File
@@ -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
},
}
+68
View File
@@ -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
},
}
+40
View File
@@ -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
},
}
+40
View File
@@ -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
},
}
+40
View File
@@ -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
},
}
+44
View File
@@ -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
},
}
+44
View File
@@ -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
},
}
+41
View File
@@ -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
},
}
+46
View File
@@ -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
},
}
+52
View File
@@ -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
},
}
+86
View File
@@ -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)
}
+158
View File
@@ -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
}
+91
View File
@@ -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
}
+286
View File
@@ -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
}
+240
View File
@@ -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)
}
}
+45
View File
@@ -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
}
+69
View File
@@ -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
},
}
+336
View File
@@ -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
}
+53
View File
@@ -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
},
}
+60
View File
@@ -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)
},
}
+169
View File
@@ -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
}
+59
View File
@@ -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
},
}
+312
View File
@@ -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
}
+49
View File
@@ -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
},
}
+130
View File
@@ -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
}
},
}
+130
View File
@@ -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
}
},
}
+69
View File
@@ -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
},
}
+52
View File
@@ -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
}
+90
View File
@@ -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) {}
+76
View File
@@ -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,
}
}
+262
View File
@@ -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)
}
}
+96
View File
@@ -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
}
+614
View File
@@ -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