diff options
Diffstat (limited to 'plan.go')
| -rw-r--r-- | plan.go | 32 |
1 files changed, 26 insertions, 6 deletions
@@ -148,7 +148,7 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) { } // discover addressable fields in this type, collect information from markers and tags - for _, field := range reflect.VisibleFields(t) { + for _, field := range assignableFields(t) { // recurse into struct fields (i.e. ignore the struct itself and consider its members instead) // unless the field itself has a `db:"..."` tag if field.Type.Kind() == reflect.Struct || (field.Type.Kind() == reflect.Pointer && field.Type.Elem().Kind() == reflect.Struct) { @@ -168,11 +168,6 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) { indexesOfOpaqueStructs = append(indexesOfOpaqueStructs, field.Index) } - // ignore unexported fields (otherwise reflect.Value.Interface() on the field would panic) - if field.PkgPath != "" { - continue - } - // ignore fields that are within a struct type that is mapped as a whole if slices.ContainsFunc(indexesOfOpaqueStructs, func(index []int) bool { return isWithin(field.Index, index) @@ -288,6 +283,31 @@ func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) { return p, nil } +// Like reflect.VisibleFields(), but considers all fields within the type that +// are assignable (i.e. `v.FieldByIndex(...).Set(...)` does not panic). +func assignableFields(t reflect.Type) (result []reflect.StructField) { + for field := range t.Fields() { + // assignment is allowed for exported or embedded fields only + if field.IsExported() || field.Anonymous { + result = append(result, field) + + // recurse into struct fields + ft := field.Type + if ft.Kind() == reflect.Pointer { + ft = ft.Elem() + } + if ft.Kind() == reflect.Struct { + for _, subfield := range assignableFields(ft) { + subfield.Index = append(slices.Clone(field.Index), subfield.Index...) + result = append(result, subfield) + } + } + } + } + + return result +} + func (p plan) getNonAutoColumnNames() []string { result := make([]string, 0, len(p.AllColumnNames)-len(p.AutoColumnNames)) for _, columnName := range p.AllColumnNames { |
