Skip to content

Commit

Permalink
Allow nil pointers to structs
Browse files Browse the repository at this point in the history
  • Loading branch information
jfbus committed Dec 8, 2015
1 parent 011bfe1 commit 42baf69
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
14 changes: 12 additions & 2 deletions fieldmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,18 @@ func field(key string, v reflect.Value) (reflect.Value, bool) {
is = map[string][]int{}
buildIndexes(v.Type(), []int{}, is)
}
if i, found := is[key]; found {
return v.FieldByIndex(i), true
if idxs, found := is[key]; found {
for i, idx := range idxs {
v = v.FieldByIndex([]int{idx})
if i != len(idxs)-1 {
if v.Type().Kind() == reflect.Ptr && v.IsNil() {
return reflect.Value{}, false
} else {
v = reflect.Indirect(v)
}
}
}
return v, true
}
return reflect.Value{}, false
}
Expand Down
57 changes: 57 additions & 0 deletions named_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ func TestNamedDefaultArgs(t *testing.T) {
Bar: nil,
}, tc, "default2")
}

func TestNamedIn(t *testing.T) {
tc := []testCase{
{
Expand Down Expand Up @@ -483,6 +484,62 @@ func TestMissing(t *testing.T) {
}, "struct/missing/ptr")
}

func TestNullField(t *testing.T) {

type testStructEmbeded struct {
Foo string
}

type testStructEmbed struct {
FooEmbed *testStructEmbeded
Bar string
}
tt := testStructEmbed{
Bar: "bazbar",
}
doTest(t, tt, []testCase{
{
src: `INSERT INTO example (::names) VALUES(::values)`,
mySQL: `INSERT INTO example (Bar) VALUES(?)`,
pgSQL: `INSERT INTO example (Bar) VALUES($1)`,
args: []interface{}{"bazbar"},
},
{
src: `UPDATE example SET ::name=::value`,
mySQL: `UPDATE example SET Bar=?`,
pgSQL: `UPDATE example SET Bar=$1`,
args: []interface{}{"bazbar"},
},
{
src: `SELECT * FROM foo WHERE foo=:Foo`,
mySQL: `SELECT * FROM foo WHERE foo=?`,
pgSQL: `SELECT * FROM foo WHERE foo=$1`,
args: []interface{}{nil},
},
}, "struct/null/null")
tt.FooEmbed = &testStructEmbeded{Foo: "foobar"}
doTest(t, tt, []testCase{
{
src: `INSERT INTO example (::names) VALUES(::values)`,
mySQL: `INSERT INTO example (Bar, Foo) VALUES(?, ?)`,
pgSQL: `INSERT INTO example (Bar, Foo) VALUES($1, $2)`,
args: []interface{}{"bazbar", "foobar"},
},
{
src: `UPDATE example SET ::name=::value`,
mySQL: `UPDATE example SET Bar=?, Foo=?`,
pgSQL: `UPDATE example SET Bar=$1, Foo=$2`,
args: []interface{}{"bazbar", "foobar"},
},
{
src: `SELECT * FROM foo WHERE foo=:Foo AND bar=:Bar`,
mySQL: `SELECT * FROM foo WHERE foo=? AND bar=?`,
pgSQL: `SELECT * FROM foo WHERE foo=$1 AND bar=$2`,
args: []interface{}{"foobar", "bazbar"},
},
}, "struct/null/notnull")
}

func TestErrors(t *testing.T) {
_, _, err := Named("{var}", map[string]interface{}{}, Variables("var"))
if err == nil {
Expand Down

0 comments on commit 42baf69

Please sign in to comment.