model.go
3.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package orm
import (
"database/sql"
"errors"
"fmt"
"reflect"
"github.com/go-pg/pg/v10/types"
)
var errModelNil = errors.New("pg: Model(nil)")
type useQueryOne interface {
useQueryOne() bool
}
type HooklessModel interface {
// Init is responsible to initialize/reset model state.
// It is called only once no matter how many rows were returned.
Init() error
// NextColumnScanner returns a ColumnScanner that is used to scan columns
// from the current row. It is called once for every row.
NextColumnScanner() ColumnScanner
// AddColumnScanner adds the ColumnScanner to the model.
AddColumnScanner(ColumnScanner) error
}
type Model interface {
HooklessModel
AfterScanHook
AfterSelectHook
BeforeInsertHook
AfterInsertHook
BeforeUpdateHook
AfterUpdateHook
BeforeDeleteHook
AfterDeleteHook
}
func NewModel(value interface{}) (Model, error) {
return newModel(value, false)
}
func newScanModel(values []interface{}) (Model, error) {
if len(values) > 1 {
return Scan(values...), nil
}
return newModel(values[0], true)
}
func newModel(value interface{}, scan bool) (Model, error) {
switch value := value.(type) {
case Model:
return value, nil
case HooklessModel:
return newModelWithHookStubs(value), nil
case types.ValueScanner, sql.Scanner:
if !scan {
return nil, fmt.Errorf("pg: Model(unsupported %T)", value)
}
return Scan(value), nil
}
v := reflect.ValueOf(value)
if !v.IsValid() {
return nil, errModelNil
}
if v.Kind() != reflect.Ptr {
return nil, fmt.Errorf("pg: Model(non-pointer %T)", value)
}
if v.IsNil() {
typ := v.Type().Elem()
if typ.Kind() == reflect.Struct {
return newStructTableModel(GetTable(typ)), nil
}
return nil, errModelNil
}
v = v.Elem()
if v.Kind() == reflect.Interface {
if !v.IsNil() {
v = v.Elem()
if v.Kind() != reflect.Ptr {
return nil, fmt.Errorf("pg: Model(non-pointer %s)", v.Type().String())
}
}
}
switch v.Kind() {
case reflect.Struct:
if v.Type() != timeType {
return newStructTableModelValue(v), nil
}
case reflect.Slice:
elemType := sliceElemType(v)
switch elemType.Kind() {
case reflect.Struct:
if elemType != timeType {
return newSliceTableModel(v, elemType), nil
}
case reflect.Map:
if err := validMap(elemType); err != nil {
return nil, err
}
slicePtr := v.Addr().Interface().(*[]map[string]interface{})
return newMapSliceModel(slicePtr), nil
}
return newSliceModel(v, elemType), nil
case reflect.Map:
typ := v.Type()
if err := validMap(typ); err != nil {
return nil, err
}
mapPtr := v.Addr().Interface().(*map[string]interface{})
return newMapModel(mapPtr), nil
}
if !scan {
return nil, fmt.Errorf("pg: Model(unsupported %T)", value)
}
return Scan(value), nil
}
type modelWithHookStubs struct {
hookStubs
HooklessModel
}
func newModelWithHookStubs(m HooklessModel) Model {
return modelWithHookStubs{
HooklessModel: m,
}
}
func validMap(typ reflect.Type) error {
if typ.Key().Kind() != reflect.String || typ.Elem().Kind() != reflect.Interface {
return fmt.Errorf("pg: Model(unsupported %s, expected *map[string]interface{})",
typ.String())
}
return nil
}