Skip to content

Commit

Permalink
FR-72 - Allow to ignore unknown db colums (#85)
Browse files Browse the repository at this point in the history
this PR should resolve the issue mentioned here -
#72

allowing the user to pass in select * statements and ignoring the columns that doesnt exist in the destination struct
  • Loading branch information
talbSE authored Jul 27, 2022
1 parent faa6baa commit fe4b088
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 11 deletions.
16 changes: 13 additions & 3 deletions dbscan/dbscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type API struct {
fieldMapperFn NameMapperFunc
scannableTypesOption []interface{}
scannableTypesReflect []reflect.Type
allowUnknownColumns bool
}

// APIOption is a function type that changes API configuration.
Expand All @@ -61,9 +62,10 @@ type APIOption func(api *API)
// NewAPI creates a new API object with provided list of options.
func NewAPI(opts ...APIOption) (*API, error) {
api := &API{
structTagKey: "db",
columnSeparator: ".",
fieldMapperFn: SnakeCaseMapper,
structTagKey: "db",
columnSeparator: ".",
fieldMapperFn: SnakeCaseMapper,
allowUnknownColumns: false,
}
for _, o := range opts {
o(api)
Expand Down Expand Up @@ -129,6 +131,14 @@ func WithScannableTypes(scannableTypes ...interface{}) APIOption {
}
}

// WithAllowUnknownColumns allows the scanner to ignore db columns that doesn't exist at the destination.
// The default function is to throw an error when a db column ain't found at the destination.
func WithAllowUnknownColumns(allowUnknownColumns bool) APIOption {
return func(api *API) {
api.allowUnknownColumns = allowUnknownColumns
}
}

// ScanAll iterates all rows to the end. After iterating it closes the rows,
// and propagates any errors that could pop up.
// It expects that destination should be a slice. For each row it scans data and appends it to the destination slice.
Expand Down
17 changes: 17 additions & 0 deletions dbscan/dbscan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,23 @@ func TestNewAPI_WithScannableTypes_InvalidInput(t *testing.T) {
}
}

func TestScanRow_withAllowUnknownColumns_returnsRow(t *testing.T) {
t.Parallel()
rows := queryRows(t, singleRowsQuery)
defer rows.Close() // nolint: errcheck
rows.Next()

got := &struct{ Foo string }{}
testAPIWithUnknownColumns, err := getAPI(dbscan.WithAllowUnknownColumns(true))
require.NoError(t, err)
err = testAPIWithUnknownColumns.ScanRow(got, rows)
require.NoError(t, err)
requireNoRowsErrorsAndClose(t, rows)

expected := struct{ Foo string }{Foo: "foo val"}
assert.Equal(t, expected, *got)
}

func TestMain(m *testing.M) {
exitCode := func() int {
flag.Parse()
Expand Down
18 changes: 10 additions & 8 deletions dbscan/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ func queryRows(t *testing.T, query string) dbscan.Rows {
return rows
}

func getAPI() (*dbscan.API, error) {
return dbscan.NewAPI(
dbscan.WithScannableTypes(
(*sql.Scanner)(nil),
(*pgtype.TextDecoder)(nil),
(*pgtype.BinaryDecoder)(nil),
),
)
func getAPI(opts ...dbscan.APIOption) (*dbscan.API, error) {
if len(opts) < 1 {
opts = []dbscan.APIOption{}
}
opts = append(opts, dbscan.WithScannableTypes(
(*sql.Scanner)(nil),
(*pgtype.TextDecoder)(nil),
(*pgtype.BinaryDecoder)(nil),
))
return dbscan.NewAPI(opts...)
}

func scan(t *testing.T, dst interface{}, rows dbscan.Rows) error {
Expand Down
5 changes: 5 additions & 0 deletions dbscan/rowscanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ func (rs *RowScanner) scanStruct(structValue reflect.Value) error {
for i, column := range rs.columns {
fieldIndex, ok := rs.columnToFieldIndex[column]
if !ok {
if rs.api.allowUnknownColumns {
var tmp interface{}
scans[i] = &tmp
continue
}
return errors.Errorf(
"scany: column: '%s': no corresponding field found, or it's unexported in %v",
column, structValue.Type(),
Expand Down

0 comments on commit fe4b088

Please sign in to comment.