From 14bc8d39c28f35feab811f23aeabeca177190950 Mon Sep 17 00:00:00 2001 From: Chris Belsole Date: Fri, 6 Apr 2018 15:00:07 -0400 Subject: [PATCH 1/2] added support for setting null fields --- reflectx/reflectx.go | 4 ++++ sqlx-runner/fixtures_test.go | 4 +++- sqlx-runner/insert_exec_test.go | 11 +++++++++++ sqlx-runner/select_exec_test.go | 5 +++-- sqlx-runner/update_exec_test.go | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/reflectx/reflectx.go b/reflectx/reflectx.go index 8b28968..de87b7a 100644 --- a/reflectx/reflectx.go +++ b/reflectx/reflectx.go @@ -192,6 +192,10 @@ func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value { v = reflect.Indirect(v).Field(i) // if this is a pointer, it's possible it is nil if v.Kind() == reflect.Ptr && v.IsNil() { + if !v.CanSet() { + continue + } + alloc := reflect.New(Deref(v.Type())) v.Set(alloc) } diff --git a/sqlx-runner/fixtures_test.go b/sqlx-runner/fixtures_test.go index b08624a..d2e9465 100644 --- a/sqlx-runner/fixtures_test.go +++ b/sqlx-runner/fixtures_test.go @@ -18,6 +18,7 @@ type Person struct { Key dat.NullString `db:"key"` Name string `db:"name"` CreatedAt dat.NullTime `db:"created_at"` + Nullable *string `db:"nullable"` Posts []*Post `json:"posts"` } @@ -79,7 +80,8 @@ const createTables = ` image bytea, key text, name text NOT NULL, - created_at timestamptz default now() + created_at timestamptz default now(), + nullable text ); CREATE TABLE posts ( id SERIAL PRIMARY KEY, diff --git a/sqlx-runner/insert_exec_test.go b/sqlx-runner/insert_exec_test.go index f939033..3dcab55 100644 --- a/sqlx-runner/insert_exec_test.go +++ b/sqlx-runner/insert_exec_test.go @@ -124,6 +124,17 @@ func TestInsertReal(t *testing.T) { assert.NoError(t, err) assert.True(t, person2.ID > 0) assert.NotEqual(t, person.ID, person2.ID) + + person3 := Person{Name: "Barack", Nullable: nil} + err = s. + InsertInto("people"). + Columns("name", "nullable"). + Record(person3). + Returning("id", "nullable"). + QueryStruct(&person3) + assert.True(t, person3.ID > 0) + assert.NotEqual(t, person2.ID, person3.ID) + assert.Nil(t, person3.Nullable) } func TestInsertMultipleRecords(t *testing.T) { diff --git a/sqlx-runner/select_exec_test.go b/sqlx-runner/select_exec_test.go index 3d284f5..2c5316d 100644 --- a/sqlx-runner/select_exec_test.go +++ b/sqlx-runner/select_exec_test.go @@ -86,7 +86,7 @@ func TestSelectQueryStruct(t *testing.T) { // Found: var person Person err := s. - Select("id", "name", "email"). + Select("id", "name", "email", "nullable"). From("people"). Where("email = $1", "john@acme.com"). QueryStruct(&person) @@ -95,11 +95,12 @@ func TestSelectQueryStruct(t *testing.T) { assert.Equal(t, person.Name, "John") assert.True(t, person.Email.Valid) assert.Equal(t, person.Email.String, "john@acme.com") + assert.Nil(t, person.Nullable) // Not found: var person2 Person err = s. - Select("id", "name", "email"). + Select("id", "name", "email", "nullable"). From("people").Where("email = $1", "dontexist@acme.com"). QueryStruct(&person2) assert.Contains(t, err.Error(), "no rows") diff --git a/sqlx-runner/update_exec_test.go b/sqlx-runner/update_exec_test.go index 674f213..6d01a63 100644 --- a/sqlx-runner/update_exec_test.go +++ b/sqlx-runner/update_exec_test.go @@ -61,6 +61,34 @@ func TestUpdateReal(t *testing.T) { assert.Equal(t, person.Email.String, "barack@whitehouse.gov") } +func TestUpdateRealNullable(t *testing.T) { + s := beginTxWithFixtures() + defer s.AutoRollback() + + person := Person{Name: "Barack", Nullable: nil} + err := s. + InsertInto("people"). + Columns("name", "nullable"). + Record(person). + Returning("*"). + QueryStruct(&person) + person.Nullable = strToPtr("obama2@whitehouse.gov") + _, err = s. + Update("people"). + Set("nullable", person.Nullable). + Where("id = $1", person.ID). + Exec() + assert.NoError(t, err) + err = s. + Select("*"). + From("people"). + Where("id = $1", person.ID). + QueryStruct(&person) + assert.NoError(t, err) + assert.NotNil(t, person.Nullable) + assert.Equal(t, *person.Nullable, "obama2@whitehouse.gov") +} + func TestUpdateReturningStar(t *testing.T) { s := beginTxWithFixtures() defer s.AutoRollback() @@ -232,3 +260,7 @@ func TestUpdateScopeFunc(t *testing.T) { assert.Equal(t, person.Email.Valid, true) assert.Equal(t, person.Email.String, "barack@whitehouse.gov") } + +func strToPtr(s string) *string { + return &s +} From 9fe6ae72c4b4c1785f137df40207451afc0ffc59 Mon Sep 17 00:00:00 2001 From: Chris Belsole Date: Fri, 6 Apr 2018 15:07:05 -0400 Subject: [PATCH 2/2] better nullable tests --- sqlx-runner/update_exec_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/sqlx-runner/update_exec_test.go b/sqlx-runner/update_exec_test.go index 6d01a63..81d90c3 100644 --- a/sqlx-runner/update_exec_test.go +++ b/sqlx-runner/update_exec_test.go @@ -3,7 +3,7 @@ package runner import ( "testing" - "gopkg.in/mgutz/dat.v1" + dat "gopkg.in/mgutz/dat.v2" "gopkg.in/stretchr/testify.v1/assert" ) @@ -73,12 +73,14 @@ func TestUpdateRealNullable(t *testing.T) { Returning("*"). QueryStruct(&person) person.Nullable = strToPtr("obama2@whitehouse.gov") + _, err = s. Update("people"). Set("nullable", person.Nullable). Where("id = $1", person.ID). Exec() assert.NoError(t, err) + err = s. Select("*"). From("people"). @@ -87,6 +89,21 @@ func TestUpdateRealNullable(t *testing.T) { assert.NoError(t, err) assert.NotNil(t, person.Nullable) assert.Equal(t, *person.Nullable, "obama2@whitehouse.gov") + + _, err = s. + Update("people"). + Set("nullable", nil). + Where("id = $1", person.ID). + Exec() + assert.NoError(t, err) + + err = s. + Select("*"). + From("people"). + Where("id = $1", person.ID). + QueryStruct(&person) + assert.NoError(t, err) + assert.Nil(t, person.Nullable) } func TestUpdateReturningStar(t *testing.T) {