Skip to content

Commit 39f16cc

Browse files
feat(mysql): Implement cast function parser (#2473)
What is this As the title said, this PR wants to add support for CAST function in MySQL. This PR is based from PR by @ryanpbrewster here (which unfortunately he didn't send here, and only exist in his repository). Why is this PR created Currently sqlc unable to infer the correct type from SQL function like MAX, MIN, SUM, etc. For those function, sqlc will return its value as interface{}. This behavior can be seen in this playground. As workaround, it advised to use CAST function to explicitly tell what is the type for that column, as mentioned in #1574. Unfortunately, currently sqlc only support CAST function in PostgreSQL and not in MySQL. Thanks to this, right now MySQL users have to parse the interface{} manually, which is not really desirable. What does this PR do? Implement convertFuncCast function for MySQL. Add better nil pointer check in some functions that related to convertFuncCast. I haven't write any test because I'm not sure how and where to put it. However, as far as I know the code that handle ast.TypeCast for PostgreSQL also don't have any test, so I guess it's fine 🤷‍♂️ Related issues Support CAST ... AS #687, which currently is the oldest MySQL issue that still opened. Using MYSQL functions ( CONVERT and CAST) result in removing column from struct #1622 Unable to Type Alias #1866 sum in select result in model field type interface{} #1901 MIN() returns an interface{} #1965
1 parent d354c0e commit 39f16cc

File tree

14 files changed

+197
-9
lines changed

14 files changed

+197
-9
lines changed

internal/compiler/compat.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,16 @@ type Relation struct {
3232

3333
func parseRelation(node ast.Node) (*Relation, error) {
3434
switch n := node.(type) {
35-
3635
case *ast.Boolean:
37-
return &Relation{
38-
Name: "bool",
39-
}, nil
36+
if n == nil {
37+
return nil, fmt.Errorf("unexpected nil in %T node", n)
38+
}
39+
return &Relation{Name: "bool"}, nil
4040

4141
case *ast.List:
42+
if n == nil {
43+
return nil, fmt.Errorf("unexpected nil in %T node", n)
44+
}
4245
parts := stringSlice(n)
4346
switch len(parts) {
4447
case 1:
@@ -61,6 +64,9 @@ func parseRelation(node ast.Node) (*Relation, error) {
6164
}
6265

6366
case *ast.RangeVar:
67+
if n == nil {
68+
return nil, fmt.Errorf("unexpected nil in %T node", n)
69+
}
6470
name := Relation{}
6571
if n.Catalogname != nil {
6672
name.Catalog = *n.Catalogname
@@ -74,10 +80,17 @@ func parseRelation(node ast.Node) (*Relation, error) {
7480
return &name, nil
7581

7682
case *ast.TypeName:
77-
return parseRelation(n.Names)
83+
if n == nil {
84+
return nil, fmt.Errorf("unexpected nil in %T node", n)
85+
}
86+
if n.Names != nil {
87+
return parseRelation(n.Names)
88+
} else {
89+
return &Relation{Name: n.Name}, nil
90+
}
7891

7992
default:
80-
return nil, fmt.Errorf("unexpected node type: %T", n)
93+
return nil, fmt.Errorf("unexpected node type: %T", node)
8194
}
8295
}
8396

internal/compiler/to_column.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
func isArray(n *ast.TypeName) bool {
11-
if n == nil {
11+
if n == nil || n.ArrayBounds == nil {
1212
return false
1313
}
1414
return len(n.ArrayBounds.Items) > 0

internal/endtoend/testdata/func_call_cast/mysql/go/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/func_call_cast/mysql/go/models.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/func_call_cast/mysql/go/query.sql.go

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- name: Demo :one
2+
SELECT CAST(GREATEST(1,2,3,4,5) AS UNSIGNED) as col1
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "1",
3+
"packages": [
4+
{
5+
"path": "go",
6+
"engine": "mysql",
7+
"name": "querytest",
8+
"schema": "query.sql",
9+
"queries": "query.sql"
10+
}
11+
]
12+
}

internal/endtoend/testdata/select_column_cast/mysql/go/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/select_column_cast/mysql/go/models.go

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/select_column_cast/mysql/go/query.sql.go

Lines changed: 37 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
CREATE TABLE foo (bar BOOLEAN NOT NULL);
2+
3+
-- name: SelectColumnCast :many
4+
SELECT CAST(bar AS UNSIGNED) FROM foo;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "1",
3+
"packages": [
4+
{
5+
"path": "go",
6+
"engine": "mysql",
7+
"name": "querytest",
8+
"schema": "query.sql",
9+
"queries": "query.sql"
10+
}
11+
]
12+
}

internal/engine/dolphin/convert.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -900,7 +900,10 @@ func (c *cc) convertFrameClause(n *pcast.FrameClause) ast.Node {
900900
}
901901

902902
func (c *cc) convertFuncCastExpr(n *pcast.FuncCastExpr) ast.Node {
903-
return todo(n)
903+
return &ast.TypeCast{
904+
Arg: c.convert(n.Expr),
905+
TypeName: &ast.TypeName{Name: types.TypeStr(n.Tp.GetType())},
906+
}
904907
}
905908

906909
func (c *cc) convertGetFormatSelectorExpr(n *pcast.GetFormatSelectorExpr) ast.Node {

internal/sql/astutils/join.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import (
77
)
88

99
func Join(list *ast.List, sep string) string {
10-
items := []string{}
10+
if list == nil {
11+
return ""
12+
}
13+
14+
var items []string
1115
for _, item := range list.Items {
1216
if n, ok := item.(*ast.String); ok {
1317
items = append(items, n.Str)

0 commit comments

Comments
 (0)