diff --git a/column.go b/column.go index 5c8b88b..64aa52f 100644 --- a/column.go +++ b/column.go @@ -15,6 +15,21 @@ type column struct { i fieldIndex } +// allocateColumns maps result set columns into the given Mapper's fields and its sub-mappers. +// It populates m.PresentColumns and m.SortedColumnIndexes, sets AncestorNames on sub-maps, +// and removes claimed entries from the provided columns map. +// +// For a mapper marked IsBasic, the function requires exactly one remaining column in +// columns and binds that single column to the mapper; otherwise it returns an error. +// For non-basic mappers, it matches basic fields by name using getColumnNameCandidates +// (honoring the mapper/sub-map delimiter and ancestor names) and records each matched +// column (including the field index). After collecting direct-field mappings it sorts +// the resulting column indexes for m.SortedColumnIndexes and then recursively allocates +// columns for each sub-map. +// +// The function mutates the Mapper structures and the input columns map. It returns any +// error returned by recursive allocation or an error when the IsBasic column constraint +// is violated. func allocateColumns(m *Mapper, columns map[string]column) error { presentColumns := map[string]column{} if m.IsBasic { diff --git a/load.go b/load.go index e33680c..74cc663 100644 --- a/load.go +++ b/load.go @@ -57,7 +57,18 @@ func (m *Mapper) loadRows(rows *sql.Rows, colTyps []*sql.ColumnType) (*resolver, // // for example, if a blog has many Authors // -// rows are actually []*Cell, theu are passed here as interface since sql scan requires []interface{} +// loadRow maps a single scanned SQL row into the resolver using the provided Mapper. +// +// It creates or reuses an element in rsv based on a computed unique id: +// - For basic mappers (m.IsBasic) the id is "row-" (ensures per-row identity). +// - For non-basic mappers the id is derived from the row values via getUniqueId. +// +// The function expects row to contain the scanned values as []*value.Cell (passed as []interface{} because sql.Scan requires that shape). +// For each present column it converts the corresponding Cell into the destination field (handling pointers, nullable types, basic primitives, and known struct wrappers such as Time, NullBool, NullString, etc.). +// If a column is NULL, loadRow enforces that the destination is either a pointer or a type listed in value.NullableTypes; otherwise it returns an error. +// After populating the element it initializes per-submap resolvers (if any) and recursively calls loadRow for each non-nil subMap, passing the same rowCount. +// +// Returns an error on conversion failures, attempts to load null into non-nullable destinations, or on any recursive loadRow error. func loadRow(m *Mapper, row []interface{}, rsv *resolver, rowCount int) error { var ( err error