From 87b19988ffcad015ee1a6922435ced1f3247bf0b Mon Sep 17 00:00:00 2001 From: Mark Bates Date: Fri, 15 Feb 2019 16:43:44 -0500 Subject: [PATCH] ngerous serialization in slices.Strings, broken behavior fixes #342 (#343) --- packrd/packed-packr.go | 18 ++++++++-------- slices/string.go | 47 ++++++++++++++++++++++++------------------ slices/string_test.go | 29 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/packrd/packed-packr.go b/packrd/packed-packr.go index e352d302..35a7d254 100644 --- a/packrd/packed-packr.go +++ b/packrd/packed-packr.go @@ -11,13 +11,13 @@ import ( ) var _ = func() error { - const gk = "78aa01be528be8fc333fc794bc4a3c1b" + const gk = "251d576975f593cab8f44e1ed7243ea6" g := packr.New(gk, "") hgr, err := resolver.NewHexGzip(map[string]string{ - "4526cfd1d77bab7843d30700c2868275": "1f8b08000000000000ffac94516bdb400cc7dffb29340f460b251d648c61285bb61656284d4952c69ee28bad24c7ce2757a74b1742befb909d3849b70c56f69060cb27e92ffda42b70818eaa12bda4270085350e7349212997e1d1256a32622626600ac96ad5a14a42e79e716a7faed7e33d6f3d3aa7a0ae8e72e3f4596d15b1dabaddb7eff53506e41412266abe9a109e888bd67422188e2979ad3ff84e1172e335121808d6cf1cc2c3e01682b0f533981283cc1182291172f2533b8b6cc4924fb73122bb6ddcf4e24213a7faf7e9b4559eaadeb38bdf0a56751f2bc30147b6c44be1886fcae8c40ec5086a1f42636434851ea12897ddb0537f478220732360a7b0a45897914576d93990cc910f2507308c10ac432f6e0976e689b1e8d4a16e3cc8dc06c84dc0f3ba64ed421983680c31d683710e181fa3652cd4e831d7a8501936250a72e86c94fd8db216fdbff0564c45cc0f68b4448d0b54f703fdc232796da771b0306ccdc4610021a00532db0261615cc40076db84ba6b9d03c2ab55b25a25eb35fa459f21b9ea8d7a9f7bc3ebf1c3e0367919fe9dfa970cc17abd6df6bf6dd92eeb8ec2b1dabef687a3649f519db4a174cce7be3f509f9a617dbca178b47dc3eb41b201da446f291fcdd01b0ebff50757076e0dfc9e2fea3d6807a025fcc77985c912028ae8a65b81e80b64a0aade9596fee6bd9e3095b721958252698ccf78ed7fdaa39642373446f43915d6cf5248a24c3f949377e3197a64e3c6b9ddadf797dec3e8a67ff7aa7182accd9d81f145f63c6dd6aceb04b5261def4c6564faf444fc032aa60ad92d3b9b7837cd9da1778227811019cfa1a0fa259f1b3f433825d660677a2184ed9a744e7e050000ffff79f0e5cfe8050000", - "53687fd7c4aa5237e9bf81249c4bd931": "1f8b08000000000000ffecd1416b32311006e0fbfe8a21df7d51c14fd89b6c05c1426463293d494ca6b8189d25136dcb92ff5e62517b28dbe24df03ae4c99bc96bf1808e9a2dee429101d85a3b34a1004366e3499b751aeaa0579ab180b6cda9099ccf3dbed6ef312ebff10c604d1cd221d1b62246c0dd417a10a52c67951c97d3e554aa8500d11f8cf25edecbfb0220c60ca021dfe9e6b24a6ef07f301c9dcc9ed177992735a904084f14ce319af98dbced8c1a2bf52cab0701e2f23a72050c33006a424d3b4e1f05c0ecb664b1005bb35e393c0eff31bb1469b07b23a51e9795948b7272dcec14f575c15ff08f70831fbfb9d9e4e5ccb2807c55ebc9ddebbeb9ba1b4f766fd24ed7947ed1f7ea6fadfacf000000ffff1757d2dbe6050000", - "793e1955dae01f2888949d22a97bc7af": "1f8b08000000000000ffac8ec14a03311086ef798a210f906dab450888aed89ba0d8f5bca4cda80be94ec8cc5621e4dd2542ab45bd79fbf9997fbecfe31e03c51d8e6215801f5cc0ad5888c4f292906be7c46d1ca3859c0d4561f390f079782fa5ffb6560013633a5946c7fc46c99f94afc46261beb830333333f37a46142c2c9512e44f8b29850ad339eb5270dcdf27d0dd6addf5b76dd7deb4eb55fff478a7411fbedaa639c643b83e12ecf2fc6cd1fc70afb02be6b0238f977e60b709a84b512a26f2d356061aff72f94f8d2fda6f321f010000ffffb44a8cd79f010000", - "d6126d474cf9b7af331e89fba0c6f146": "1f8b08000000000000ff4a492d4bcdc92fc84dcd2bb1e2525048c94ccc494d2eb152502a2eccc92c4935560209269624262516a75a295457ebe5179414eb05e5e797d4d6c6c3b80145a9699915b5b5f148a6e9410ce0e22a492da686d12063e0661614e5a794269764e6e751c164846130f301010000ffffd5f1ef6a15010000", + "50f60207eb74d1b7f2b9b07fa815c278": "1f8b08000000000000ffecd1416b32311006e0fbfe8a21df7d51c14fd89b6c05c1426463293d494ca6b8189d25136dcb92ff5e62517b28dbe24df03ae4c99bc96bf1808e9a2dee429101d85a3b34a1004366e3499b751aeaa0579ab180b6cda9099ccf3dbed6ef312ebff10c604d1cd221d1b62246c0dd417a10a52c67951c97d3e554aa8500d11f8cf25edecbfb0220c60ca021dfe9e6b24a6ef07f301c9dcc9ed177992735a904084f14ce319af98dbced8c1a2bf52cab0701e2f23a72050c33006a424d3b4e1f05c0ecb664b1005bb35e393c0eff31bb1469b07b23a51e9795948b7272dcec14f575c15ff08f70831fbfb9d9e4e5ccb2807c55ebc9ddebbeb9ba1b4f766fd24ed7947ed1f7ea6fadfacf000000ffff1757d2dbe6050000", + "5326a1f49e88f03a1e5f2c3a40e54348": "1f8b08000000000000ffac94516bdb400cc7dffb29340f460b251d648c61285bb61656284d4952c69ee28bad24c7ce2757a74b1742befb909d3849b70c56f69060cb27e92ffda42b70818eaa12bda4270085350e7349212997e1d1256a32622626600ac96ad5a14a42e79e716a7faed7e33d6f3d3aa7a0ae8e72e3f4596d15b1dabaddb7eff53506e41412266abe9a109e888bd67422188e2979ad3ff84e1172e335121808d6cf1cc2c3e01682b0f533981283cc1182291172f2533b8b6cc4924fb73122bb6ddcf4e24213a7faf7e9b4559eaadeb38bdf0a56751f2bc30147b6c44be1886fcae8c40ec5086a1f42636434851ea12897ddb0537f478220732360a7b0a45897914576d93990cc910f2507308c10ac432f6e0976e689b1e8d4a16e3cc8dc06c84dc0f3ba64ed421983680c31d683710e181fa3652cd4e831d7a8501936250a72e86c94fd8db216fdbff0564c45cc0f68b4448d0b54f703fdc232796da771b0306ccdc4610021a00532db0261615cc40076db84ba6b9d03c2ab55b25a25eb35fa459f21b9ea8d7a9f7bc3ebf1c3e0367919fe9dfa970cc17abd6df6bf6dd92eeb8ec2b1dabef687a3649f519db4a174cce7be3f509f9a617dbca178b47dc3eb41b201da446f291fcdd01b0ebff50757076e0dfc9e2fea3d6807a025fcc77985c912028ae8a65b81e80b64a0aade9596fee6bd9e3095b721958252698ccf78ed7fdaa39642373446f43915d6cf5248a24c3f949377e3197a64e3c6b9ddadf797dec3e8a67ff7aa7182accd9d81f145f63c6dd6aceb04b5261def4c6564faf444fc032aa60ad92d3b9b7837cd9da1778227811019cfa1a0fa259f1b3f433825d660677a2184ed9a744e7e050000ffff79f0e5cfe8050000", + "e2475e65981b98fa738efc3c4b639c69": "1f8b08000000000000ffac8ec14a03311086ef798a210f906dab450888aed89ba0d8f5bca4cda80be94ec8cc5621e4dd2542ab45bd79fbf9997fbecfe31e03c51d8e6215801f5cc0ad5888c4f292906be7c46d1ca3859c0d4561f390f079782fa5ffb6560013633a5946c7fc46c99f94afc46261beb830333333f37a46142c2c9512e44f8b29850ad339eb5270dcdf27d0dd6addf5b76dd7deb4eb55fff478a7411fbedaa639c643b83e12ecf2fc6cd1fc70afb02be6b0238f977e60b709a84b512a26f2d356061aff72f94f8d2fda6f321f010000ffffb44a8cd79f010000", + "fb34aa975abbcb40d48be167668d4187": "1f8b08000000000000ff4a492d4bcdc92fc84dcd2bb1e2525048c94ccc494d2eb152502a2eccc92c4935560209269624262516a75a295457ebe5179414eb05e5e797d4d6c6c3b80145a9699915b5b5f148a6e9410ce0e22a492da686d12063e0661614e5a794269764e6e751c164846130f301010000ffffd5f1ef6a15010000", }) if err != nil { panic(err) @@ -26,10 +26,10 @@ var _ = func() error { func() { b := packr.New("pop:genny:config", "../config/templates") - b.SetResolver("cockroach.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "53687fd7c4aa5237e9bf81249c4bd931"}) - b.SetResolver("mysql.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "4526cfd1d77bab7843d30700c2868275"}) - b.SetResolver("postgres.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "793e1955dae01f2888949d22a97bc7af"}) - b.SetResolver("sqlite3.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "d6126d474cf9b7af331e89fba0c6f146"}) + b.SetResolver("cockroach.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "50f60207eb74d1b7f2b9b07fa815c278"}) + b.SetResolver("mysql.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "5326a1f49e88f03a1e5f2c3a40e54348"}) + b.SetResolver("postgres.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "e2475e65981b98fa738efc3c4b639c69"}) + b.SetResolver("sqlite3.yml.tmpl", packr.Pointer{ForwardBox: gk, ForwardPath: "fb34aa975abbcb40d48be167668d4187"}) }() return nil diff --git a/slices/string.go b/slices/string.go index 3ad1b659..6b2b1d72 100644 --- a/slices/string.go +++ b/slices/string.go @@ -1,18 +1,20 @@ package slices import ( + "bytes" "database/sql/driver" + "encoding/csv" "encoding/json" - "fmt" + "io" "strings" - "github.com/pkg/errors" + "github.com/lib/pq" ) // For reading in arrays from postgres // String is a slice of strings. -type String []string +type String pq.StringArray // Interface implements the nulls.nullable interface. func (s String) Interface() interface{} { @@ -22,24 +24,23 @@ func (s String) Interface() interface{} { // Scan implements the sql.Scanner interface. // It allows to read the string slice from the database value. func (s *String) Scan(src interface{}) error { - b, ok := src.([]byte) - if !ok { - return errors.New("Scan source was not []byte") - } - *s = strToString(string(b)) - return nil + ss := pq.StringArray(*s) + err := ss.Scan(src) + *s = String(ss) + return err } // Value implements the driver.Valuer interface. // It allows to convert the string slice to a driver.value. func (s String) Value() (driver.Value, error) { - return fmt.Sprintf("{%s}", strings.Join(s, ",")), nil + ss := pq.StringArray(s) + return ss.Value() } // UnmarshalJSON will unmarshall JSON value into // the string slice representation of this value. func (s *String) UnmarshalJSON(data []byte) error { - var ss []string + var ss pq.StringArray if err := json.Unmarshal(data, &ss); err != nil { return err } @@ -50,11 +51,22 @@ func (s *String) UnmarshalJSON(data []byte) error { // UnmarshalText will unmarshall text value into // the string slice representation of this value. func (s *String) UnmarshalText(text []byte) error { - var ss []string - for _, x := range strings.Split(string(text), ",") { - ss = append(ss, strings.TrimSpace(x)) + r := csv.NewReader(bytes.NewReader(text)) + + var words []string + for { + record, err := r.Read() + if err == io.EOF { + break + } + if err != nil { + return err + } + + words = append(words, record...) } - *s = ss + + *s = String(words) return nil } @@ -67,8 +79,3 @@ func (s String) TagValue() string { func (s String) Format(sep string) string { return strings.Join([]string(s), sep) } - -func strToString(s string) []string { - r := strings.Trim(s, "{}") - return strings.Split(r, ",") -} diff --git a/slices/string_test.go b/slices/string_test.go index 0dfb28ec..a2ba2519 100644 --- a/slices/string_test.go +++ b/slices/string_test.go @@ -7,6 +7,35 @@ import ( "github.com/stretchr/testify/require" ) +func Test_String_Scan(t *testing.T) { + r := require.New(t) + in := `{"This has a comma,","This has a double quote\"","Also a single'"}` + s := &String{} + r.NoError(s.Scan(in)) + ss := []string(*s) + r.Len(ss, 3) + r.Equal([]string{"This has a comma,", "This has a double quote\"", "Also a single'"}, ss) +} + +func Test_String_Value(t *testing.T) { + r := require.New(t) + s := String{"This has a comma,", "This has a double quote\"", "Also a single'"} + v, err := s.Value() + r.NoError(err) + r.Equal(`{"This has a comma,","This has a double quote\"","Also a single'"}`, v) +} + +func Test_String_UnmarshalText(t *testing.T) { + r := require.New(t) + in := "foo,bar,\"baz,bax\"" + s := &String{} + r.NoError(s.UnmarshalText([]byte(in))) + + ss := []string(*s) + r.Len(ss, 3) + r.Equal([]string{"foo", "bar", "baz,bax"}, ss) +} + func Test_String_JSON_Unmarshal(t *testing.T) { r := require.New(t)