Skip to content

Commit a2ef31c

Browse files
committed
Move ObjectContext out of reflectx where it doesn't belong
1 parent 3373418 commit a2ef31c

3 files changed

Lines changed: 59 additions & 55 deletions

File tree

convert.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package sqlx
2+
3+
import (
4+
_ "unsafe"
5+
)
6+
7+
//go:linkname convertAssign database/sql.convertAssign
8+
func convertAssign(dest, src interface{}) error

reflectx/reflect.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"runtime"
1212
"strings"
1313
"sync"
14-
_ "unsafe"
1514
)
1615

1716
// A FieldInfo is metadata for a struct field.
@@ -202,56 +201,6 @@ func (m *Mapper) TraversalsByNameFunc(t reflect.Type, names []string, fn func(in
202201
return nil
203202
}
204203

205-
// ObjectContext provides a single layer to abstract away
206-
// nested struct scanning functionality
207-
type ObjectContext struct {
208-
value reflect.Value
209-
}
210-
211-
func NewObjectContext() *ObjectContext {
212-
return &ObjectContext{}
213-
}
214-
215-
// NewRow updates the object reference.
216-
// This ensures all columns point to the same object
217-
func (o *ObjectContext) NewRow(value reflect.Value) {
218-
o.value = value
219-
}
220-
221-
// FieldForIndexes returns the value for address. If the address is a nested struct,
222-
// a nestedFieldScanner is returned instead of the standard value reference
223-
func (o *ObjectContext) FieldForIndexes(indexes []int) reflect.Value {
224-
if len(indexes) == 1 {
225-
return FieldByIndexes(o.value, indexes)
226-
}
227-
228-
obj := &nestedFieldScanner{
229-
parent: o,
230-
indexes: indexes,
231-
}
232-
233-
return reflect.ValueOf(obj).Elem()
234-
}
235-
236-
// nestedFieldScanner will only forward the Scan to the nested value if
237-
// the database value is not nil.
238-
type nestedFieldScanner struct {
239-
parent *ObjectContext
240-
indexes []int
241-
}
242-
243-
// Scan implements sql.Scanner.
244-
func (o *nestedFieldScanner) Scan(src interface{}) error {
245-
if src == nil {
246-
return nil
247-
}
248-
dest := FieldByIndexes(o.parent.value, o.indexes)
249-
return convertAssign(dest.Addr().Interface(), src)
250-
}
251-
252-
//go:linkname convertAssign database/sql.convertAssign
253-
func convertAssign(dest, src interface{}) error
254-
255204
// FieldByIndexes returns a value for the field given by the struct traversal
256205
// for the given value.
257206
func FieldByIndexes(v reflect.Value, indexes []int) reflect.Value {

sqlx.go

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ func (r *Rows) StructScan(dest interface{}) error {
621621
r.started = true
622622
}
623623

624-
octx := reflectx.NewObjectContext()
624+
octx := newObjectContext()
625625
err := fieldsByTraversal(octx, v, r.fields, r.values, true)
626626
if err != nil {
627627
return err
@@ -782,7 +782,7 @@ func (r *Row) scanAny(dest interface{}, structOnly bool) error {
782782
}
783783
values := make([]interface{}, len(columns))
784784

785-
octx := reflectx.NewObjectContext()
785+
octx := newObjectContext()
786786

787787
err = fieldsByTraversal(octx, v, fields, values, true)
788788
if err != nil {
@@ -951,7 +951,7 @@ func scanAll(rows rowsi, dest interface{}, structOnly bool) error {
951951
return fmt.Errorf("missing destination name %s in %T", columns[f], dest)
952952
}
953953
values = make([]interface{}, len(columns))
954-
octx := reflectx.NewObjectContext()
954+
octx := newObjectContext()
955955

956956
for rows.Next() {
957957
// create a new struct type (which returns PtrTo) and indirect it
@@ -1024,7 +1024,7 @@ func baseType(t reflect.Type, expected reflect.Kind) (reflect.Type, error) {
10241024
// when iterating over many rows. Empty traversals will get an interface pointer.
10251025
// Because of the necessity of requesting ptrs or values, it's considered a bit too
10261026
// specialized for inclusion in reflectx itself.
1027-
func fieldsByTraversal(octx *reflectx.ObjectContext, v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error {
1027+
func fieldsByTraversal(octx *objectContext, v reflect.Value, traversals [][]int, values []interface{}, ptrs bool) error {
10281028
v = reflect.Indirect(v)
10291029
if v.Kind() != reflect.Struct {
10301030
return errors.New("argument not a struct")
@@ -1055,3 +1055,50 @@ func missingFields(transversals [][]int) (field int, err error) {
10551055
}
10561056
return 0, nil
10571057
}
1058+
1059+
// objectContext provides a single layer to abstract away
1060+
// nested struct scanning functionality
1061+
type objectContext struct {
1062+
value reflect.Value
1063+
}
1064+
1065+
func newObjectContext() *objectContext {
1066+
return &objectContext{}
1067+
}
1068+
1069+
// NewRow updates the object reference.
1070+
// This ensures all columns point to the same object
1071+
func (o *objectContext) NewRow(value reflect.Value) {
1072+
o.value = value
1073+
}
1074+
1075+
// FieldForIndexes returns the value for address. If the address is a nested struct,
1076+
// a nestedFieldScanner is returned instead of the standard value reference
1077+
func (o *objectContext) FieldForIndexes(indexes []int) reflect.Value {
1078+
if len(indexes) == 1 {
1079+
return reflectx.FieldByIndexes(o.value, indexes)
1080+
}
1081+
1082+
obj := &nestedFieldScanner{
1083+
parent: o,
1084+
indexes: indexes,
1085+
}
1086+
1087+
return reflect.ValueOf(obj).Elem()
1088+
}
1089+
1090+
// nestedFieldScanner will only forward the Scan to the nested value if
1091+
// the database value is not nil.
1092+
type nestedFieldScanner struct {
1093+
parent *objectContext
1094+
indexes []int
1095+
}
1096+
1097+
// Scan implements sql.Scanner.
1098+
func (o *nestedFieldScanner) Scan(src interface{}) error {
1099+
if src == nil {
1100+
return nil
1101+
}
1102+
dest := reflectx.FieldByIndexes(o.parent.value, o.indexes)
1103+
return convertAssign(dest.Addr().Interface(), src)
1104+
}

0 commit comments

Comments
 (0)