-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathinsert.go
More file actions
145 lines (124 loc) · 3.83 KB
/
Copy pathinsert.go
File metadata and controls
145 lines (124 loc) · 3.83 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
package goe
import (
"context"
"errors"
"reflect"
"github.com/go-goe/goe/enum"
"github.com/go-goe/goe/model"
)
type stateInsert[T any] struct {
conn model.Connection
table *T
builder builder
ctx context.Context
}
// Insert inserts a new record into the given table.
//
// Insert can return [ErrUniqueValue, ErrForeignKey and ErrBadRequest];
// use ErrBadRequest as a generic error for any user interaction.
//
// Insert uses [context.Background] internally;
// to specify the context, use [InsertContext].
//
// # Examples
//
// // insert one record
// err = goe.Insert(db.Person).One(&Person{Name: "John"})
// // insert a list of records
//
// persons := []Person{{Name: "John"}, {Name: "Mary"}}
// err = goe.Insert(db.Person).All(persons)
func Insert[T any](table *T) stateInsert[T] {
return InsertContext(context.Background(), table)
}
// InsertContext inserts a new record into the given table.
//
// See [Insert] for examples.
func InsertContext[T any](ctx context.Context, table *T) stateInsert[T] {
var state stateInsert[T] = createInsertState(ctx, table)
return state
}
// OnTransaction sets a transaction on the query.
//
// # Example
//
// tx, err = db.NewTransaction()
// if err != nil {
// // handler error
// }
// defer tx.Rollback()
//
// a := Animal{Name: "Cat"}
// err = goe.Insert(db.Animal).OnTransaction(tx).One(&a)
// if err != nil {
// // handler error
// }
//
// err = tx.Commit()
// if err != nil {
// // handler error
// }
func (s stateInsert[T]) OnTransaction(tx model.Transaction) stateInsert[T] {
s.conn = tx
return s
}
func (s stateInsert[T]) One(value *T) error {
if value == nil {
return errors.New("goe: invalid insert value. try sending a pointer to a struct as value")
}
valueOf := reflect.ValueOf(value).Elem()
s.builder.fields = getArgsTable(addrMap.mapField, s.table, valueOf)
pkFieldId := s.builder.buildSqlInsert(valueOf)
driver := s.builder.fields[0].getDb().driver
if s.conn == nil {
s.conn = driver.NewConnection()
}
if s.builder.query.ReturningID != nil {
return handlerValuesReturning(s.ctx, s.conn, s.builder.query, valueOf, pkFieldId, driver.GetDatabaseConfig())
}
return handlerValues(s.ctx, s.conn, s.builder.query, driver.GetDatabaseConfig())
}
func (s stateInsert[T]) All(value []T) error {
if len(value) == 0 {
return errors.New("goe: can't insert a empty batch value")
}
valueOf := reflect.ValueOf(value)
s.builder.fields = getArgsTable(addrMap.mapField, s.table, valueOf)
pkFieldId := s.builder.buildSqlInsertBatch(valueOf)
driver := s.builder.fields[0].getDb().driver
if s.conn == nil {
s.conn = driver.NewConnection()
}
return handlerValuesReturningBatch(s.ctx, s.conn, s.builder.query, valueOf, pkFieldId, driver.GetDatabaseConfig())
}
func createInsertState[T any](ctx context.Context, t *T) stateInsert[T] {
return stateInsert[T]{builder: createBuilder(enum.InsertQuery), ctx: ctx, table: t}
}
func getArgsTable(addrMap map[uintptr]field, table any, valueOf reflect.Value) []field {
if table == nil {
panic("goe: invalid argument. try sending a pointer to a database mapped struct as argument")
}
fields := make([]field, 0)
tableValueOf := reflect.ValueOf(table).Elem()
if tableValueOf.Kind() != reflect.Struct {
panic("goe: invalid argument. try sending a pointer to a database mapped struct as argument")
}
var fieldOf reflect.Value
for i := 0; i < tableValueOf.NumField(); i++ {
fieldOf = tableValueOf.Field(i)
if fieldOf.Kind() == reflect.Slice && fieldOf.Type().Elem().Kind() == reflect.Struct {
continue
}
field := addrMap[uintptr(fieldOf.Addr().UnsafePointer())]
if field != nil {
if field.getDefault() && valueOf.Field(field.getFieldId()).IsZero() {
continue
}
fields = append(fields, field)
}
}
if len(fields) == 0 {
panic("goe: invalid argument. try sending a pointer to a database mapped struct as argument")
}
return fields
}