scanStruct returns the configuration for scanning a sql.Row into a struct.
(typ reflect.Type, columns []string)
| 193 | |
| 194 | // scanStruct returns the configuration for scanning a sql.Row into a struct. |
| 195 | func scanStruct(typ reflect.Type, columns []string) (*rowScan, error) { |
| 196 | var ( |
| 197 | scan = &rowScan{} |
| 198 | idxs = make([][]int, 0, typ.NumField()) |
| 199 | names = make(map[string][]int, typ.NumField()) |
| 200 | ) |
| 201 | for i := 0; i < typ.NumField(); i++ { |
| 202 | f := typ.Field(i) |
| 203 | // Skip unexported fields. |
| 204 | if f.PkgPath != "" { |
| 205 | continue |
| 206 | } |
| 207 | // Support 1-level embedding to accept types as `type T struct {ent.T; V int}`. |
| 208 | if typ := f.Type; f.Anonymous && typ.Kind() == reflect.Struct { |
| 209 | for j := 0; j < typ.NumField(); j++ { |
| 210 | names[columnName(typ.Field(j))] = []int{i, j} |
| 211 | } |
| 212 | continue |
| 213 | } |
| 214 | names[columnName(f)] = []int{i} |
| 215 | } |
| 216 | for _, c := range columns { |
| 217 | var idx []int |
| 218 | // Normalize columns if necessary, |
| 219 | // for example: COUNT(*) => count. |
| 220 | switch name := strings.Split(c, "(")[0]; { |
| 221 | case names[name] != nil: |
| 222 | idx = names[name] |
| 223 | case names[strings.ToLower(name)] != nil: |
| 224 | idx = names[strings.ToLower(name)] |
| 225 | default: |
| 226 | return nil, fmt.Errorf("sql/scan: missing struct field for column: %s (%s)", c, name) |
| 227 | } |
| 228 | idxs = append(idxs, idx) |
| 229 | rtype := typ.Field(idx[0]).Type |
| 230 | if len(idx) > 1 { |
| 231 | rtype = rtype.Field(idx[1]).Type |
| 232 | } |
| 233 | switch { |
| 234 | // If the field is not support by the standard |
| 235 | // convertAssign, assume it is a JSON field. |
| 236 | case !supportsScan(rtype): |
| 237 | rtype = nullJSONType |
| 238 | // Create a pointer to the actual reflect |
| 239 | // types to accept optional struct fields. |
| 240 | case !nillable(rtype): |
| 241 | rtype = reflect.PtrTo(rtype) |
| 242 | } |
| 243 | scan.columns = append(scan.columns, rtype) |
| 244 | } |
| 245 | scan.value = func(vs ...any) (reflect.Value, error) { |
| 246 | st := reflect.New(typ).Elem() |
| 247 | for i, v := range vs { |
| 248 | rv := reflect.Indirect(reflect.ValueOf(v)) |
| 249 | if rv.IsNil() { |
| 250 | continue |
| 251 | } |
| 252 | idx := idxs[i] |
no test coverage detected
searching dependent graphs…