Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix "char" and array types for system functions and tables #519

Merged
merged 11 commits into from
Jul 31, 2024
6 changes: 3 additions & 3 deletions server/ast/resolvable_type_reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ func nodeResolvableTypeReference(typ tree.ResolvableTypeReference) (*vitess.Conv
}
case oid.T_char:
width := uint32(columnType.Width())
if width > pgtypes.StringMaxLength {
return nil, nil, fmt.Errorf("length for type char cannot exceed %d", pgtypes.StringMaxLength)
if width > pgtypes.InternalCharLength {
return nil, nil, fmt.Errorf("length for type \"char\" cannot exceed %d", pgtypes.InternalCharLength)
}
if width == 0 {
width = 1
}
resolvedType = pgtypes.CharType{Length: width}
resolvedType = pgtypes.InternalChar
case oid.T_date:
resolvedType = pgtypes.Date
case oid.T_float4:
Expand Down
12 changes: 12 additions & 0 deletions server/cast/char.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,22 @@ import (

// initChar handles all casts that are built-in. This comprises only the "From" types.
func initChar() {
charAssignment()
charExplicit()
charImplicit()
}

// charAssignment registers all assignment casts. This comprises only the "From" types.
func charAssignment() {
framework.MustAddAssignmentTypeCast(framework.TypeCast{
FromType: pgtypes.BpChar,
ToType: pgtypes.InternalChar,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
return targetType.IoInput(ctx, val.(string))
},
})
}

// charExplicit registers all explicit casts. This comprises only the "From" types.
func charExplicit() {
framework.MustAddExplicitTypeCast(framework.TypeCast{
Expand Down
1 change: 1 addition & 0 deletions server/cast/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func Init() {
initInt16()
initInt32()
initInt64()
initInternalChar()
initJson()
initJsonB()
initName()
Expand Down
84 changes: 84 additions & 0 deletions server/cast/internal_char.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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 cast

import (
"strconv"
"unicode"

"github.com/dolthub/go-mysql-server/sql"

"github.com/dolthub/doltgresql/server/functions/framework"
pgtypes "github.com/dolthub/doltgresql/server/types"
)

// initInternalChar handles all casts that are built-in. This comprises only the "From" types.
func initInternalChar() {
internalCharAssignment()
internalCharExplicit()
internalCharImplicit()
}

// internalCharAssignment registers all assignment casts. This comprises only the "From" types.
func internalCharAssignment() {
framework.MustAddAssignmentTypeCast(framework.TypeCast{
FromType: pgtypes.InternalChar,
ToType: pgtypes.BpChar,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
return targetType.IoInput(ctx, val.(string))
},
})
framework.MustAddAssignmentTypeCast(framework.TypeCast{
FromType: pgtypes.InternalChar,
ToType: pgtypes.VarChar,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
return handleStringCast(val.(string), targetType)
},
})
}

// internalCharExplicit registers all explicit casts. This comprises only the "From" types.
func internalCharExplicit() {
framework.MustAddExplicitTypeCast(framework.TypeCast{
FromType: pgtypes.InternalChar,
ToType: pgtypes.Int32,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
s := val.(string)
if len(s) == 0 {
return int32(0), nil
}
if unicode.IsLetter(rune(s[0])) {
return int32(s[0]), nil
}
i, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return 0, err
}
return int32(i), nil
},
})
}

// internalCharImplicit registers all implicit casts. This comprises only the "From" types.
func internalCharImplicit() {
framework.MustAddImplicitTypeCast(framework.TypeCast{
FromType: pgtypes.InternalChar,
ToType: pgtypes.Text,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
return val, nil
},
})

}
7 changes: 7 additions & 0 deletions server/cast/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ func textAssignment() {
return handleStringCast(val.(string), targetType)
},
})
framework.MustAddAssignmentTypeCast(framework.TypeCast{
FromType: pgtypes.Text,
ToType: pgtypes.InternalChar,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
return handleStringCast(val.(string), targetType)
},
})
}

// textImplicit registers all implicit casts. This comprises only the "From" types.
Expand Down
3 changes: 3 additions & 0 deletions server/cast/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ func handleStringCast(str string, targetType pgtypes.DoltgresType) (string, erro
return str, nil
}
}
case pgtypes.InternalCharType:
str, _ := truncateString(str, pgtypes.InternalCharLength)
return str, nil
Hydrocharged marked this conversation as resolved.
Show resolved Hide resolved
case pgtypes.NameType:
// Name seems to never throw an error, regardless of the context or how long the input is
str, _ := truncateString(str, targetType.Length)
Expand Down
7 changes: 7 additions & 0 deletions server/cast/varchar.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ func varcharAssignment() {
return handleStringCast(val.(string), targetType)
},
})
framework.MustAddAssignmentTypeCast(framework.TypeCast{
FromType: pgtypes.VarChar,
ToType: pgtypes.InternalChar,
Function: func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
return handleStringCast(val.(string), targetType)
},
})
}

// varcharImplicit registers all implicit casts. This comprises only the "From" types.
Expand Down
20 changes: 18 additions & 2 deletions server/expression/assignment_cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,24 @@ func (ac *AssignmentCast) Eval(ctx *sql.Context, row sql.Row) (any, error) {
}
castFunc := framework.GetAssignmentCast(ac.fromType.BaseID(), ac.toType.BaseID())
if castFunc == nil {
return nil, fmt.Errorf("ASSIGNMENT_CAST: target is of type %s but expression is of type %s: %s",
ac.toType.String(), ac.fromType.String(), ac.expr.String())
// Technically, string literals have the type "unknown", but we currently implement them as having the "text"
// type. As a consequence, string literals should properly convert, but true "text" types should not.
// TODO: change string literals to the "unknown" type
if literal, ok := ac.expr.(*Literal); ok && literal.typ.BaseID() == pgtypes.DoltgresTypeBaseID_Text {
castFunc = func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
if val == nil {
return nil, nil
}
str, err := ac.fromType.IoOutput(ctx, val)
if err != nil {
return nil, err
}
return targetType.IoInput(ctx, str)
}
} else {
return nil, fmt.Errorf("ASSIGNMENT_CAST: target is of type %s but expression is of type %s: %s",
ac.toType.String(), ac.fromType.String(), ac.expr.String())
}
}
return castFunc(ctx, val, ac.toType)
}
Expand Down
13 changes: 13 additions & 0 deletions server/functions/binary/equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func initBinaryEqual() {
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)
Expand Down Expand Up @@ -105,6 +106,18 @@ var byteaeq = framework.Function2{
},
}

// 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: func(ctx *sql.Context, _ [3]pgtypes.DoltgresType, val1 any, val2 any) (any, error) {
res, err := pgtypes.InternalChar.Compare(val1.(string), val2.(string))
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",
Expand Down
13 changes: 13 additions & 0 deletions server/functions/binary/greater.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ 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)
Expand Down Expand Up @@ -103,6 +104,18 @@ var byteagt = framework.Function2{
},
}

// 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(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",
Expand Down
13 changes: 13 additions & 0 deletions server/functions/binary/greater_equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ 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)
Expand Down Expand Up @@ -103,6 +104,18 @@ var byteage = framework.Function2{
},
}

// 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(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",
Expand Down
13 changes: 13 additions & 0 deletions server/functions/binary/less.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ 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)
Expand Down Expand Up @@ -103,6 +104,18 @@ var bytealt = framework.Function2{
},
}

// 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(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",
Expand Down
13 changes: 13 additions & 0 deletions server/functions/binary/less_equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ 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)
Expand Down Expand Up @@ -103,6 +104,18 @@ var byteale = framework.Function2{
},
}

// 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(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",
Expand Down
13 changes: 13 additions & 0 deletions server/functions/binary/not_equal.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ 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)
Expand Down Expand Up @@ -105,6 +106,18 @@ var byteane = framework.Function2{
},
}

// 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(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",
Expand Down
12 changes: 6 additions & 6 deletions server/functions/framework/cast.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ func GetExplicitCast(fromType pgtypes.DoltgresTypeBaseID, toType pgtypes.Doltgre
if fromType == toType && toType.GetTypeCategory() != pgtypes.TypeCategory_StringTypes && fromType.GetTypeCategory() != pgtypes.TypeCategory_StringTypes {
return identityCast
}
// All types have a built-in explicit cast to string types: https://www.postgresql.org/docs/15/sql-createcast.html
if toType.GetTypeCategory() == pgtypes.TypeCategory_StringTypes {
// All types have a built-in explicit cast from string types: https://www.postgresql.org/docs/15/sql-createcast.html
if fromType.GetTypeCategory() == pgtypes.TypeCategory_StringTypes {
return func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
if val == nil {
return nil, nil
Expand All @@ -151,8 +151,8 @@ func GetExplicitCast(fromType pgtypes.DoltgresTypeBaseID, toType pgtypes.Doltgre
}
return targetType.IoInput(ctx, str)
}
} else if fromType.GetTypeCategory() == pgtypes.TypeCategory_StringTypes {
// All types have a built-in assignment cast from string types, which we can reference in an explicit cast
} else if toType.GetTypeCategory() == pgtypes.TypeCategory_StringTypes {
// All types have a built-in assignment cast to string types, which we can reference in an explicit cast
return func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
if val == nil {
return nil, nil
Expand Down Expand Up @@ -180,8 +180,8 @@ func GetAssignmentCast(fromType pgtypes.DoltgresTypeBaseID, toType pgtypes.Doltg
if fromType == toType && fromType.GetTypeCategory() != pgtypes.TypeCategory_StringTypes {
return identityCast
}
// All types have a built-in assignment cast from string types: https://www.postgresql.org/docs/15/sql-createcast.html
if fromType.GetTypeCategory() == pgtypes.TypeCategory_StringTypes {
// All types have a built-in assignment cast to string types: https://www.postgresql.org/docs/15/sql-createcast.html
if toType.GetTypeCategory() == pgtypes.TypeCategory_StringTypes {
return func(ctx *sql.Context, val any, targetType pgtypes.DoltgresType) (any, error) {
if val == nil {
return nil, nil
Expand Down
Loading
Loading