summaryrefslogtreecommitdiff
path: root/plan.go
diff options
context:
space:
mode:
Diffstat (limited to 'plan.go')
-rw-r--r--plan.go32
1 files changed, 26 insertions, 6 deletions
diff --git a/plan.go b/plan.go
index ed0f381..423e339 100644
--- a/plan.go
+++ b/plan.go
@@ -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 {