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..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" ) @@ -61,6 +61,51 @@ 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") + + _, 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) { s := beginTxWithFixtures() defer s.AutoRollback() @@ -232,3 +277,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 +}