From 6706f4b228f51cf810129f57ba90bbdb60b85fe7 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sat, 14 May 2022 11:17:39 +0200 Subject: [PATCH] [Bug] DebugSQL panics with libraries that do not implemente Stringer interface. --- go.mod | 1 + go.sum | 12 ++++++++++++ internal/jet/sql_builder.go | 20 ++++++++++++++----- tests/postgres/scan_test.go | 39 +++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 701d49d..f15e634 100644 --- a/go.mod +++ b/go.mod @@ -17,4 +17,5 @@ require ( github.com/pkg/profile v1.6.0 github.com/shopspring/decimal v1.3.1 github.com/stretchr/testify v1.7.0 + github.com/volatiletech/null/v8 v8.1.2 ) diff --git a/go.sum b/go.sum index 349cd29..cb1e8e5 100644 --- a/go.sum +++ b/go.sum @@ -9,11 +9,14 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk= +github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= @@ -118,6 +121,14 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/volatiletech/inflect v0.0.1 h1:2a6FcMQyhmPZcLa+uet3VJ8gLn/9svWhJxJYwvE8KsU= +github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= +github.com/volatiletech/null/v8 v8.1.2 h1:kiTiX1PpwvuugKwfvUNX/SU/5A2KGZMXfGD0DUHdKEI= +github.com/volatiletech/null/v8 v8.1.2/go.mod h1:98DbwNoKEpRrYtGjWFctievIfm4n4MxG0A6EBUcoS5g= +github.com/volatiletech/randomize v0.0.1 h1:eE5yajattWqTB2/eN8df4dw+8jwAzBtbdo5sbWC4nMk= +github.com/volatiletech/randomize v0.0.1/go.mod h1:GN3U0QYqfZ9FOJ67bzax1cqZ5q2xuj2mXrXBjWaRTlY= +github.com/volatiletech/strmangle v0.0.1 h1:UKQoHmY6be/R3tSvD2nQYrH41k43OJkidwEiC74KIzk= +github.com/volatiletech/strmangle v0.0.1/go.mod h1:F6RA6IkB5vq0yTG4GQ0UsbbRcl3ni9P76i+JrTBKFFg= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -182,6 +193,7 @@ golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= diff --git a/internal/jet/sql_builder.go b/internal/jet/sql_builder.go index e3fb61b..96ef2e6 100644 --- a/internal/jet/sql_builder.go +++ b/internal/jet/sql_builder.go @@ -2,6 +2,7 @@ package jet import ( "bytes" + "database/sql/driver" "fmt" "github.com/go-jet/jet/v2/internal/3rdparty/pq" "github.com/go-jet/jet/v2/internal/utils" @@ -232,17 +233,26 @@ func argToString(value interface{}) string { case time.Time: return stringQuote(string(pq.FormatTimestamp(bindVal))) default: - if strBindValue, ok := bindVal.(toStringInterface); ok { + if strBindValue, ok := bindVal.(fmt.Stringer); ok { return stringQuote(strBindValue.String()) } + + if valuer, ok := bindVal.(driver.Valuer); ok { + val, err := valuer.Value() + + if err != nil { + // If valuer for some reason returns an error, we return error string representation. + // This is fine because argToString is called only from DebugSQL, and DebugSQL shouldn't be used in production. + return err.Error() + } + + return argToString(val) + } + panic(fmt.Sprintf("jet: %s type can not be used as SQL query parameter", reflect.TypeOf(value).String())) } } -type toStringInterface interface { - String() string -} - func integerTypesToString(value interface{}) string { switch bindVal := value.(type) { case int: diff --git a/tests/postgres/scan_test.go b/tests/postgres/scan_test.go index 3078709..24b5949 100644 --- a/tests/postgres/scan_test.go +++ b/tests/postgres/scan_test.go @@ -2,6 +2,7 @@ package postgres import ( "context" + "github.com/volatiletech/null/v8" "testing" "time" @@ -1034,6 +1035,44 @@ func TestScanToPrimitiveElementsSlice(t *testing.T) { require.Len(t, dest[1].Title, 20) } +// https://github.com/go-jet/jet/issues/127 +func TestValuerTypeDebugSQL(t *testing.T) { + type customer struct { + CustomerID null.Int32 `sql:"primary_key"` + StoreID null.Int16 + FirstName null.String + LastName string + Email null.String + AddressID int16 + Activebool null.Bool + CreateDate null.Time + LastUpdate null.Time + Active null.Int8 + } + + stmt := Customer.INSERT(). + MODEL( + customer{ + CustomerID: null.Int32From(1234), + StoreID: null.Int16From(0), + FirstName: null.StringFrom("Joe"), + LastName: "", + Email: null.StringFromPtr(nil), + AddressID: 1, + Activebool: null.BoolFrom(true), + CreateDate: null.TimeFrom(time.Date(2020, 2, 2, 10, 0, 0, 0, time.UTC)), + LastUpdate: null.TimeFromPtr(nil), + Active: null.Int8From(1), + }, + ) + + testutils.AssertDebugStatementSql(t, stmt, ` +INSERT INTO dvds.customer +VALUES (1234, 0, 'Joe', '', NULL, 1, TRUE, '2020-02-02 10:00:00Z', NULL, 1); +`) + testutils.AssertExecAndRollback(t, stmt, db) +} + var address256 = model.Address{ AddressID: 256, Address: "1497 Yuzhou Drive",