From 5fb76627ce38ff334df69e0a89667d1fa6b075ff Mon Sep 17 00:00:00 2001 From: "Ryan P. Brewster" Date: Sat, 28 May 2022 13:26:55 -0400 Subject: [PATCH 1/6] Add barebones support for converting mysql rowexprs --- internal/engine/dolphin/convert.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/engine/dolphin/convert.go b/internal/engine/dolphin/convert.go index 632b38ea1f..1f9b51a7fb 100644 --- a/internal/engine/dolphin/convert.go +++ b/internal/engine/dolphin/convert.go @@ -1117,7 +1117,13 @@ func (c *cc) convertRollbackStmt(n *pcast.RollbackStmt) ast.Node { } func (c *cc) convertRowExpr(n *pcast.RowExpr) ast.Node { - return todo(n) + var items []ast.Node + for _, v := range n.Values { + items = append(items, c.convert(v)) + } + return &ast.RowExpr{ + Args: &ast.List{Items: items}, + } } func (c *cc) convertSetCollationExpr(n *pcast.SetCollationExpr) ast.Node { From 685279c736600a956d6bb39c5011ec482c0d75fa Mon Sep 17 00:00:00 2001 From: "Ryan P. Brewster" Date: Sat, 28 May 2022 22:18:43 -0400 Subject: [PATCH 2/6] add a convert test --- internal/engine/dolphin/convert_test.go | 42 +++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 internal/engine/dolphin/convert_test.go diff --git a/internal/engine/dolphin/convert_test.go b/internal/engine/dolphin/convert_test.go new file mode 100644 index 0000000000..b487ee6858 --- /dev/null +++ b/internal/engine/dolphin/convert_test.go @@ -0,0 +1,42 @@ +package dolphin_test + +import ( + "strings" + "testing" + + "github.com/kyleconroy/sqlc/internal/engine/dolphin" + "github.com/kyleconroy/sqlc/internal/sql/ast" + "github.com/kyleconroy/sqlc/internal/sql/astutils" +) + +func Test_TupleComparison(t *testing.T) { + p := dolphin.NewParser() + stmts, err := p.Parse(strings.NewReader("SELECT * WHERE (a, b) > (?, ?)")) + if err != nil { + t.Fatal(err) + } + + if l := len(stmts); l != 1 { + t.Fatalf("expected 1 statement, got %d", l) + } + + // Right now all this test does is make sure we noticed the two ParamRefs. + // This ensures that the Go code is generated correctly. + e := &refExtractor{} + astutils.Walk(e, stmts[0].Raw.Stmt) + if l := len(e.params); l != 2 { + t.Fatalf("expected to extract 2 params, got %d", l) + } +} + +type refExtractor struct { + params []*ast.ParamRef +} + +func (e *refExtractor) Visit(n ast.Node) astutils.Visitor { + switch t := n.(type) { + case *ast.ParamRef: + e.params = append(e.params, t) + } + return e +} From 9d54c165dbb9e7d6ad27ed33585a6847dda681f9 Mon Sep 17 00:00:00 2001 From: "Ryan P. Brewster" Date: Sat, 28 May 2022 22:20:14 -0400 Subject: [PATCH 3/6] add a comment --- internal/engine/dolphin/convert_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/engine/dolphin/convert_test.go b/internal/engine/dolphin/convert_test.go index b487ee6858..f15b0fdfe4 100644 --- a/internal/engine/dolphin/convert_test.go +++ b/internal/engine/dolphin/convert_test.go @@ -29,6 +29,8 @@ func Test_TupleComparison(t *testing.T) { } } +// refExtractor is an astutils.Visitor instance that will extract all of the +// ParamRef instances it encounters while walking an AST. type refExtractor struct { params []*ast.ParamRef } From 66521ef228e70df45e5a3c9b95d165ba231dff16 Mon Sep 17 00:00:00 2001 From: "Ryan P. Brewster" Date: Sat, 28 May 2022 23:04:25 -0400 Subject: [PATCH 4/6] Add a few more paramref dolphin tests --- internal/engine/dolphin/convert.go | 5 ++- internal/engine/dolphin/convert_test.go | 41 +++++++++++++++---------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/internal/engine/dolphin/convert.go b/internal/engine/dolphin/convert.go index 1f9b51a7fb..a9ab2bc3a5 100644 --- a/internal/engine/dolphin/convert.go +++ b/internal/engine/dolphin/convert.go @@ -873,7 +873,10 @@ func (c *cc) convertFrameClause(n *pcast.FrameClause) ast.Node { } func (c *cc) convertFuncCastExpr(n *pcast.FuncCastExpr) ast.Node { - return todo(n) + return &ast.TypeCast{ + Arg: c.convert(n.Expr), + TypeName: &ast.TypeName{Name: types.TypeStr(n.Tp.Tp)}, + } } func (c *cc) convertGetFormatSelectorExpr(n *pcast.GetFormatSelectorExpr) ast.Node { diff --git a/internal/engine/dolphin/convert_test.go b/internal/engine/dolphin/convert_test.go index f15b0fdfe4..a2a6da7c52 100644 --- a/internal/engine/dolphin/convert_test.go +++ b/internal/engine/dolphin/convert_test.go @@ -9,23 +9,32 @@ import ( "github.com/kyleconroy/sqlc/internal/sql/astutils" ) -func Test_TupleComparison(t *testing.T) { - p := dolphin.NewParser() - stmts, err := p.Parse(strings.NewReader("SELECT * WHERE (a, b) > (?, ?)")) - if err != nil { - t.Fatal(err) +func Test_ParamRefParsing(t *testing.T) { + cases := []struct { + name string + input string + paramRefCount int + }{ + {name: "tuple comparison", paramRefCount: 2, input: "SELECT * WHERE (a, b) > (?, ?)"}, + {name: "cast", paramRefCount: 1, input: "SELECT CAST(? AS JSON)"}, } - - if l := len(stmts); l != 1 { - t.Fatalf("expected 1 statement, got %d", l) - } - - // Right now all this test does is make sure we noticed the two ParamRefs. - // This ensures that the Go code is generated correctly. - e := &refExtractor{} - astutils.Walk(e, stmts[0].Raw.Stmt) - if l := len(e.params); l != 2 { - t.Fatalf("expected to extract 2 params, got %d", l) + for _, test := range cases { + test := test + t.Run(test.name, func(t *testing.T) { + p := dolphin.NewParser() + stmts, err := p.Parse(strings.NewReader(test.input)) + if err != nil { + t.Fatal(err) + } + if l := len(stmts); l != 1 { + t.Fatalf("expected 1 statement, got %d", l) + } + e := &refExtractor{} + astutils.Walk(e, stmts[0].Raw.Stmt) + if got, want := len(e.params), test.paramRefCount; got != want { + t.Fatalf("extracted params: want %d, got %d", want, got) + } + }) } } From 187b9592e649df373268a7e0bac471f9e1f8bbcb Mon Sep 17 00:00:00 2001 From: "Ryan P. Brewster" Date: Sun, 29 May 2022 00:55:48 -0400 Subject: [PATCH 5/6] be a bit more explicit --- internal/engine/dolphin/convert_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/engine/dolphin/convert_test.go b/internal/engine/dolphin/convert_test.go index a2a6da7c52..27d7aef29a 100644 --- a/internal/engine/dolphin/convert_test.go +++ b/internal/engine/dolphin/convert_test.go @@ -17,6 +17,8 @@ func Test_ParamRefParsing(t *testing.T) { }{ {name: "tuple comparison", paramRefCount: 2, input: "SELECT * WHERE (a, b) > (?, ?)"}, {name: "cast", paramRefCount: 1, input: "SELECT CAST(? AS JSON)"}, + {name: "convert", paramRefCount: 1, input: "SELECT CONVERT(? USING UTF8)"}, + {name: "issues/1622", paramRefCount: 1, input: "INSERT INTO foo (x) VALUES (CAST(CONVERT(? USING UTF8) AS JSON))"}, } for _, test := range cases { test := test From 6497945204067b4b4c8d608e2c0f1ee400def33a Mon Sep 17 00:00:00 2001 From: "Ryan P. Brewster" Date: Mon, 30 May 2022 12:19:31 -0400 Subject: [PATCH 6/6] slightly simpler --- internal/engine/dolphin/convert_test.go | 27 +++++++++++-------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/internal/engine/dolphin/convert_test.go b/internal/engine/dolphin/convert_test.go index 27d7aef29a..1ff2daa38e 100644 --- a/internal/engine/dolphin/convert_test.go +++ b/internal/engine/dolphin/convert_test.go @@ -31,25 +31,22 @@ func Test_ParamRefParsing(t *testing.T) { if l := len(stmts); l != 1 { t.Fatalf("expected 1 statement, got %d", l) } - e := &refExtractor{} - astutils.Walk(e, stmts[0].Raw.Stmt) - if got, want := len(e.params), test.paramRefCount; got != want { + paramRefs := extractParamRefs(stmts[0].Raw.Stmt) + if got, want := len(paramRefs), test.paramRefCount; got != want { t.Fatalf("extracted params: want %d, got %d", want, got) } }) } } -// refExtractor is an astutils.Visitor instance that will extract all of the -// ParamRef instances it encounters while walking an AST. -type refExtractor struct { - params []*ast.ParamRef -} - -func (e *refExtractor) Visit(n ast.Node) astutils.Visitor { - switch t := n.(type) { - case *ast.ParamRef: - e.params = append(e.params, t) - } - return e +// extractParamRefs extracts all of the ParamRef instances by walking the provided AST. +func extractParamRefs(n ast.Node) []*ast.ParamRef { + var params []*ast.ParamRef + astutils.Walk(astutils.VisitorFunc(func(n ast.Node) { + switch t := n.(type) { + case *ast.ParamRef: + params = append(params, t) + } + }), n) + return params }