Skip to content

Commit

Permalink
Merge pull request #2085 from nolandseigler/rows-snake-case
Browse files Browse the repository at this point in the history
RowToStructByName Snake Case Collision
  • Loading branch information
jackc authored Jul 12, 2024
2 parents 96791c8 + 71a8e53 commit 67aa0e5
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 8 deletions.
21 changes: 13 additions & 8 deletions rows.go
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,7 @@ func computeNamedStructFields(
if !dbTagPresent {
colName = sf.Name
}
fpos := fieldPosByName(fldDescs, colName)
fpos := fieldPosByName(fldDescs, colName, !dbTagPresent)
if fpos == -1 {
if missingField == "" {
missingField = colName
Expand All @@ -816,16 +816,21 @@ func computeNamedStructFields(

const structTagKey = "db"

func fieldPosByName(fldDescs []pgconn.FieldDescription, field string) (i int) {
func fieldPosByName(fldDescs []pgconn.FieldDescription, field string, normalize bool) (i int) {
i = -1
for i, desc := range fldDescs {

// Snake case support.
if normalize {
field = strings.ReplaceAll(field, "_", "")
descName := strings.ReplaceAll(desc.Name, "_", "")

if strings.EqualFold(descName, field) {
return i
}
for i, desc := range fldDescs {
if normalize {
if strings.EqualFold(strings.ReplaceAll(desc.Name, "_", ""), field) {
return i
}
} else {
if desc.Name == field {
return i
}
}
}
return
Expand Down
35 changes: 35 additions & 0 deletions rows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,41 @@ func TestRowToStructByName(t *testing.T) {
})
}

func TestRowToStructByNameDbTags(t *testing.T) {
type person struct {
Last string `db:"last_name"`
First string `db:"first_name"`
Age int32 `db:"age"`
AccountID string `db:"account_id"`
AnotherAccountID string `db:"account__id"`
}

defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
rows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, 'd5e49d3f' as account_id, '5e49d321' as account__id from generate_series(0, 9) n`)
slice, err := pgx.CollectRows(rows, pgx.RowToStructByName[person])
assert.NoError(t, err)

assert.Len(t, slice, 10)
for i := range slice {
assert.Equal(t, "Smith", slice[i].Last)
assert.Equal(t, "John", slice[i].First)
assert.EqualValues(t, i, slice[i].Age)
assert.Equal(t, "d5e49d3f", slice[i].AccountID)
assert.Equal(t, "5e49d321", slice[i].AnotherAccountID)
}

// check missing fields in a returned row
rows, _ = conn.Query(ctx, `select 'Smith' as last_name, n as age from generate_series(0, 9) n`)
_, err = pgx.CollectRows(rows, pgx.RowToStructByName[person])
assert.ErrorContains(t, err, "cannot find field first_name in returned row")

// check missing field in a destination struct
rows, _ = conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, 'd5e49d3f' as account_id, '5e49d321' as account__id, null as ignore from generate_series(0, 9) n`)
_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[person])
assert.ErrorContains(t, err, "struct doesn't have corresponding row field ignore")
})
}

func TestRowToStructByNameEmbeddedStruct(t *testing.T) {
type Name struct {
Last string `db:"last_name"`
Expand Down

0 comments on commit 67aa0e5

Please sign in to comment.