Skip to content

Commit 1515464

Browse files
committed
Fixed issues 2547 and 2557
1 parent af5637f commit 1515464

File tree

15 files changed

+454
-45
lines changed

15 files changed

+454
-45
lines changed

server/functions/concat.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright 2026 Dolthub, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package functions
16+
17+
import (
18+
"strings"
19+
20+
"github.com/dolthub/go-mysql-server/sql"
21+
22+
"github.com/dolthub/doltgresql/server/functions/framework"
23+
pgtypes "github.com/dolthub/doltgresql/server/types"
24+
)
25+
26+
// initConcat registers the functions to the catalog.
27+
func initConcat() {
28+
framework.RegisterFunction(concat_any)
29+
}
30+
31+
// concat_any represents the PostgreSQL function of the same name, taking the same parameters.
32+
var concat_any = framework.Function1N{
33+
Name: "concat",
34+
Return: pgtypes.Text,
35+
Parameters: [1]*pgtypes.DoltgresType{pgtypes.Any},
36+
Strict: false,
37+
Callable: func(ctx *sql.Context, t []*pgtypes.DoltgresType, val1 any, vals []any) (any, error) {
38+
sb := strings.Builder{}
39+
if val1 != nil {
40+
output, err := t[0].IoOutput(ctx, val1)
41+
if err != nil {
42+
return nil, err
43+
}
44+
sb.WriteString(output)
45+
}
46+
for i, val := range vals {
47+
if val == nil {
48+
continue
49+
}
50+
valType := t[i+1]
51+
if valType.ID == pgtypes.Bool.ID {
52+
// Within this context, `bool` returns 't' rather than 'true'
53+
if val.(bool) {
54+
sb.WriteRune('t')
55+
} else {
56+
sb.WriteRune('f')
57+
}
58+
} else {
59+
output, err := valType.IoOutput(ctx, val)
60+
if err != nil {
61+
return nil, err
62+
}
63+
sb.WriteString(output)
64+
}
65+
}
66+
return sb.String(), nil
67+
},
68+
}

server/functions/framework/c_function.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ func (cFunc CFunction) NonDeterministic() bool {
6969
return cFunc.IsNonDeterministic
7070
}
7171

72+
// IsCVariadic implements the FunctionInterface interface.
73+
func (cFunc CFunction) IsCVariadic() bool {
74+
// TODO: implement c-language variadic
75+
return false
76+
}
77+
7278
// VariadicIndex implements the interface FunctionInterface.
7379
func (cFunc CFunction) VariadicIndex() int {
7480
// TODO: implement variadic

server/functions/framework/catalog.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,15 @@ func RegisterFunction(f FunctionInterface) {
4848
case Function1:
4949
name := strings.ToLower(f.Name)
5050
Catalog[name] = append(Catalog[name], f)
51+
case Function1N:
52+
name := strings.ToLower(f.Name)
53+
Catalog[name] = append(Catalog[name], f)
5154
case Function2:
5255
name := strings.ToLower(f.Name)
5356
Catalog[name] = append(Catalog[name], f)
57+
case Function2N:
58+
name := strings.ToLower(f.Name)
59+
Catalog[name] = append(Catalog[name], f)
5460
case Function3:
5561
name := strings.ToLower(f.Name)
5662
Catalog[name] = append(Catalog[name], f)

server/functions/framework/compiled_function.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,15 @@ func newCompiledFunctionInternal(
104104

105105
// Then we'll handle the polymorphic types
106106
// https://www.postgresql.org/docs/15/extend-type-system.html#EXTEND-TYPES-POLYMORPHIC
107-
functionParameterTypes := fn.GetParameters()
108-
c.callResolved = make([]*pgtypes.DoltgresType, len(functionParameterTypes)+1)
107+
c.callResolved = make([]*pgtypes.DoltgresType, len(overload.params.paramTypes)+1)
109108
hasPolymorphicParam := false
110-
for i, param := range functionParameterTypes {
109+
for i, param := range overload.params.paramTypes {
111110
if param.IsPolymorphicType() {
112111
// resolve will ensure that the parameter types are valid, so we can just assign them here
113112
hasPolymorphicParam = true
114113
c.callResolved[i] = originalTypes[i]
114+
} else if param.ID == pgtypes.Any.ID {
115+
c.callResolved[i] = originalTypes[i]
115116
} else if i < len(args) {
116117
if d, ok := args[i].Type().(*pgtypes.DoltgresType); ok {
117118
// `param` is a default type which does not have type modifier set
@@ -124,7 +125,7 @@ func newCompiledFunctionInternal(
124125
c.callResolved[len(c.callResolved)-1] = returnType
125126
if returnType.IsPolymorphicType() {
126127
if hasPolymorphicParam {
127-
c.callResolved[len(c.callResolved)-1] = c.resolvePolymorphicReturnType(functionParameterTypes, originalTypes, returnType)
128+
c.callResolved[len(c.callResolved)-1] = c.resolvePolymorphicReturnType(overload.params.paramTypes, originalTypes, returnType)
128129
} 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" {
129130
// The return type should resolve to the type of OID value passed in as second argument.
130131
// TODO: Possible that the oid type has a special property with polymorphic return types,
@@ -266,26 +267,28 @@ func (c *CompiledFunction) Eval(ctx *sql.Context, row sql.Row) (interface{}, err
266267
var err error
267268
isStrict := c.overload.Function().IsStrict()
268269
args := make([]any, len(c.Arguments))
270+
exprTypes := make([]*pgtypes.DoltgresType, len(args))
269271
for i, arg := range c.Arguments {
270272
args[i], err = arg.Eval(ctx, row)
271273
if err != nil {
272274
return nil, err
273275
}
274-
// TODO: once we remove GMS types from all of our expressions, we can remove this step which ensures the correct type
275-
if _, ok := arg.Type().(*pgtypes.DoltgresType); !ok {
276+
var ok bool
277+
if exprTypes[i], ok = arg.Type().(*pgtypes.DoltgresType); !ok {
276278
dt, err := pgtypes.FromGmsTypeToDoltgresType(arg.Type())
277279
if err != nil {
278280
return nil, err
279281
}
280282
args[i], _, _ = dt.Convert(ctx, args[i])
283+
exprTypes[i] = dt
281284
}
282285
if args[i] == nil && isStrict {
283286
return nil, nil
284287
}
285288
}
286289

287290
if len(c.overload.casts) > 0 {
288-
targetParamTypes := c.overload.Function().GetParameters()
291+
targetParamTypes := c.overload.params.paramTypes
289292
for i, arg := range args {
290293
// For variadic params, we need to identify the corresponding target type
291294
var targetType *pgtypes.DoltgresType
@@ -320,8 +323,12 @@ func (c *CompiledFunction) Eval(ctx *sql.Context, row sql.Row) (interface{}, err
320323
return f.Callable(ctx)
321324
case Function1:
322325
return f.Callable(ctx, ([2]*pgtypes.DoltgresType)(c.callResolved), args[0])
326+
case Function1N:
327+
return f.Callable(ctx, c.callResolved, args[0], args[1:])
323328
case Function2:
324329
return f.Callable(ctx, ([3]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1])
330+
case Function2N:
331+
return f.Callable(ctx, c.callResolved, args[0], args[1], args[2:])
325332
case Function3:
326333
return f.Callable(ctx, ([4]*pgtypes.DoltgresType)(c.callResolved), args[0], args[1], args[2])
327334
case Function4:

0 commit comments

Comments
 (0)