Files
2026-07-13 12:32:25 +08:00

618 lines
24 KiB
Go

// 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
}