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

194 lines
7.7 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 expression
import (
"strconv"
"time"
"github.com/cockroachdb/apd/v3"
"github.com/dolthub/go-mysql-server/sql/expression"
vitess "github.com/dolthub/vitess/go/vt/sqlparser"
"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"
pgtypes "github.com/dolthub/doltgresql/server/types"
)
// NewNumericLiteral returns a new *expression.Literal containing a NUMERIC value.
func NewNumericLiteral(numericValue string) (*expression.Literal, error) {
d, err := pgtypes.GetNumericValueFromStringWithTypmod(numericValue, -1)
if err != nil {
return nil, err
}
return expression.NewLiteral(d, pgtypes.Numeric), err
}
// NewIntegerLiteral returns a new *expression.Literal containing an integer (INT2/4/8 or NUMERIC) value.
func NewIntegerLiteral(integerValue string) (*expression.Literal, error) {
i, err := strconv.ParseInt(integerValue, 10, 64)
// If we don't get an error, then we know the value is either an INT32 or INT64
if err == nil {
if i >= -2147483648 && i <= 2147483647 {
return expression.NewLiteral(int32(i), pgtypes.Int32), err
} else {
return expression.NewLiteral(i, pgtypes.Int64), err
}
} else {
// If we errored the first time, then we'll assume it's a NUMERIC value
return NewNumericLiteral(integerValue)
}
}
// NewNullLiteral returns a new *expression.Literal containing a null value.
func NewNullLiteral() *expression.Literal {
return expression.NewLiteral(nil, pgtypes.Unknown)
}
// NewUnknownLiteral returns a new *expression.Literal containing a UNKNOWN type value.
func NewUnknownLiteral(stringValue string) *expression.Literal {
return expression.NewLiteral(stringValue, pgtypes.Unknown)
}
// NewTextLiteral returns a new *expression.Literal containing a TEXT type value.
// This should be used for internal uses when the type of the value is certain.
func NewTextLiteral(stringValue string) *expression.Literal {
return expression.NewLiteral(stringValue, pgtypes.Text)
}
// NewIntervalLiteral returns a new *expression.Literal containing a INTERVAL value.
func NewIntervalLiteral(duration duration.Duration) *expression.Literal {
return expression.NewLiteral(duration, pgtypes.Interval)
}
// NewJSONLiteral returns a new *expression.Literal containing a JSON value. This is different from JSONB.
func NewJSONLiteral(jsonValue string) *expression.Literal {
return expression.NewLiteral(jsonValue, pgtypes.Json)
}
// NewRawLiteralBool returns a new *expression.Literal containing a boolean value.
func NewRawLiteralBool(val bool) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Bool)
}
// NewRawLiteralInt32 returns a new *expression.Literal containing an int32 value.
func NewRawLiteralInt32(val int32) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Int32)
}
// NewRawLiteralInt64 returns a new *expression.Literal containing an int64 value.
func NewRawLiteralInt64(val int64) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Int64)
}
// NewRawLiteralFloat32 returns a new *expression.Literal containing a float32 value.
func NewRawLiteralFloat32(val float32) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Float32)
}
// NewRawLiteralFloat64 returns a new *expression.Literal containing a float64 value.
func NewRawLiteralFloat64(val float64) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Float64)
}
// NewRawLiteralNumeric returns a new *expression.Literal containing an *apd.Decimal value.
func NewRawLiteralNumeric(val *apd.Decimal) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Numeric)
}
// NewRawLiteralDate returns a new *expression.Literal containing a DATE value.
func NewRawLiteralDate(date time.Time) *expression.Literal {
return expression.NewLiteral(date, pgtypes.Date)
}
// NewRawLiteralTime returns a new *expression.Literal containing a TIME value.
func NewRawLiteralTime(t timeofday.TimeOfDay) *expression.Literal {
return expression.NewLiteral(t, pgtypes.Time)
}
// NewRawLiteralTimeTZ returns a new *expression.Literal containing a TIMETZ value.
func NewRawLiteralTimeTZ(ttz timetz.TimeTZ) *expression.Literal {
return expression.NewLiteral(ttz, pgtypes.TimeTZ)
}
// NewRawLiteralTimestamp returns a new *expression.Literal containing a TIMESTAMP value. This is the variant without a time zone.
func NewRawLiteralTimestamp(val time.Time) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Timestamp)
}
// NewRawLiteralTimestampTZ returns a new *expression.Literal containing a TIMESTAMPTZ value. This is the variant with a time zone.
func NewRawLiteralTimestampTZ(val time.Time) *expression.Literal {
return expression.NewLiteral(val, pgtypes.TimestampTZ)
}
// NewRawLiteralJSON returns a new *expression.Literal containing a JSON value.
func NewRawLiteralJSON(val string) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Json)
}
// NewRawLiteralOid returns a new *expression.Literal containing a OID value.
func NewRawLiteralOid(val id.Id) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Oid)
}
// NewRawLiteralUuid returns a new *expression.Literal containing a UUID value.
func NewRawLiteralUuid(val uuid.UUID) *expression.Literal {
return expression.NewLiteral(val, pgtypes.Uuid)
}
// NewUnsafeLiteral returns a new *expression.Literal containing the given value and type. This should almost never be used, as
// it does not perform any checking and circumvents type safety, which may lead to hard-to-debug errors. This is
// currently only used within the analyzer, and will likely be removed in the future.
func NewUnsafeLiteral(val any, t *pgtypes.DoltgresType) *expression.Literal {
return expression.NewLiteral(val, t)
}
// ToVitessLiteral returns the literal as a Vitess literal. This is strictly for situations where GMS is hardcoded to
// expect a Vitess literal. This should only be used as a temporary measure, as the GMS code needs to be updated, or the
// equivalent functionality should be built into Doltgres (recommend the second approach).
func ToVitessLiteral(l *expression.Literal) *vitess.SQLVal {
// It's fine to use a nil context here as we know that the Literal expression doesn't need it
switch l.Type(nil).(*pgtypes.DoltgresType).ID {
case pgtypes.Bool.ID:
if l.Value().(bool) {
return vitess.NewIntVal([]byte("1"))
} else {
return vitess.NewIntVal([]byte("0"))
}
case pgtypes.Int32.ID:
return vitess.NewIntVal([]byte(strconv.FormatInt(int64(l.Value().(int32)), 10)))
case pgtypes.Int64.ID:
return vitess.NewIntVal([]byte(strconv.FormatInt(l.Value().(int64), 10)))
case pgtypes.Numeric.ID:
d := l.Value().(*apd.Decimal)
return vitess.NewFloatVal([]byte(d.String()))
case pgtypes.Text.ID:
return vitess.NewStrVal([]byte(l.Value().(string)))
case pgtypes.Unknown.ID:
if l.Value() == nil {
return nil
} else if str, ok := l.Value().(string); ok {
return vitess.NewStrVal([]byte(str))
} else {
panic("unhandled value of 'unknown' type in temporary literal conversion: " + l.Type(nil).String())
}
default:
panic("unhandled type in temporary literal conversion: " + l.Type(nil).String())
}
}