github.com/pjdufour-truss/pop@v4.11.2-0.20190705085848-4c90b0ff4d5a+incompatible/executors.go (about) 1 package pop 2 3 import ( 4 "reflect" 5 6 "github.com/gobuffalo/pop/associations" 7 "github.com/gobuffalo/pop/columns" 8 "github.com/gobuffalo/pop/logging" 9 "github.com/gobuffalo/validate" 10 "github.com/gofrs/uuid" 11 "github.com/pkg/errors" 12 ) 13 14 // Reload fetch fresh data for a given model, using its ID. 15 func (c *Connection) Reload(model interface{}) error { 16 sm := Model{Value: model} 17 return sm.iterate(func(m *Model) error { 18 return c.Find(m.Value, m.ID()) 19 }) 20 } 21 22 // Exec runs the given query. 23 func (q *Query) Exec() error { 24 return q.Connection.timeFunc("Exec", func() error { 25 sql, args := q.ToSQL(nil) 26 log(logging.SQL, sql, args...) 27 _, err := q.Connection.Store.Exec(sql, args...) 28 return err 29 }) 30 } 31 32 // ExecWithCount runs the given query, and returns the amount of 33 // affected rows. 34 func (q *Query) ExecWithCount() (int, error) { 35 count := int64(0) 36 return int(count), q.Connection.timeFunc("Exec", func() error { 37 sql, args := q.ToSQL(nil) 38 log(logging.SQL, sql, args...) 39 result, err := q.Connection.Store.Exec(sql, args...) 40 if err != nil { 41 return err 42 } 43 44 count, err = result.RowsAffected() 45 return err 46 }) 47 } 48 49 // ValidateAndSave applies validation rules on the given entry, then save it 50 // if the validation succeed, excluding the given columns. 51 // 52 // If model is a slice, each item of the slice is validated then saved in the database. 53 func (c *Connection) ValidateAndSave(model interface{}, excludeColumns ...string) (*validate.Errors, error) { 54 sm := &Model{Value: model} 55 verrs, err := sm.validateSave(c) 56 if err != nil { 57 return verrs, err 58 } 59 if verrs.HasAny() { 60 return verrs, nil 61 } 62 return verrs, c.Save(model, excludeColumns...) 63 } 64 65 var emptyUUID = uuid.Nil.String() 66 67 // IsZeroOfUnderlyingType will check if the value of anything is the equal to the Zero value of that type. 68 func IsZeroOfUnderlyingType(x interface{}) bool { 69 return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface()) 70 } 71 72 // Save wraps the Create and Update methods. It executes a Create if no ID is provided with the entry; 73 // or issues an Update otherwise. 74 // 75 // If model is a slice, each item of the slice is saved in the database. 76 func (c *Connection) Save(model interface{}, excludeColumns ...string) error { 77 sm := &Model{Value: model} 78 return sm.iterate(func(m *Model) error { 79 id, err := m.fieldByName("ID") 80 if err != nil { 81 return err 82 } 83 if IsZeroOfUnderlyingType(id.Interface()) { 84 return c.Create(m.Value, excludeColumns...) 85 } 86 return c.Update(m.Value, excludeColumns...) 87 }) 88 } 89 90 // ValidateAndCreate applies validation rules on the given entry, then creates it 91 // if the validation succeed, excluding the given columns. 92 // 93 // If model is a slice, each item of the slice is validated then created in the database. 94 func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...string) (*validate.Errors, error) { 95 sm := &Model{Value: model} 96 verrs, err := sm.validateCreate(c) 97 if err != nil { 98 return verrs, err 99 } 100 if verrs.HasAny() { 101 return verrs, nil 102 } 103 104 if c.eager { 105 asos, err := associations.ForStruct(model, c.eagerFields...) 106 if err != nil { 107 return verrs, errors.Wrap(err, "could not retrieve associations") 108 } 109 110 if len(asos) == 0 { 111 log(logging.Debug, "no associations found for given struct, disable eager mode") 112 c.disableEager() 113 return verrs, c.Create(model, excludeColumns...) 114 } 115 116 before := asos.AssociationsBeforeCreatable() 117 for index := range before { 118 i := before[index].BeforeInterface() 119 if i == nil { 120 continue 121 } 122 123 sm := &Model{Value: i} 124 verrs, err := sm.validateAndOnlyCreate(c) 125 if err != nil || verrs.HasAny() { 126 return verrs, err 127 } 128 } 129 130 after := asos.AssociationsAfterCreatable() 131 for index := range after { 132 i := after[index].AfterInterface() 133 if i == nil { 134 continue 135 } 136 137 sm := &Model{Value: i} 138 verrs, err := sm.validateAndOnlyCreate(c) 139 if err != nil || verrs.HasAny() { 140 return verrs, err 141 } 142 } 143 144 sm := &Model{Value: model} 145 verrs, err = sm.validateCreate(c) 146 if err != nil || verrs.HasAny() { 147 return verrs, err 148 } 149 } 150 151 return verrs, c.Create(model, excludeColumns...) 152 } 153 154 // Create add a new given entry to the database, excluding the given columns. 155 // It updates `created_at` and `updated_at` columns automatically. 156 // 157 // If model is a slice, each item of the slice is created in the database. 158 // 159 // Create support two modes: 160 // * Flat (default): Associate existing nested objects only. NO creation or update of nested objects. 161 // * Eager: Associate existing nested objects and create non-existent objects. NO change to existing objects. 162 func (c *Connection) Create(model interface{}, excludeColumns ...string) error { 163 var isEager = c.eager 164 165 c.disableEager() 166 167 sm := &Model{Value: model} 168 return sm.iterate(func(m *Model) error { 169 return c.timeFunc("Create", func() error { 170 var localIsEager = isEager 171 asos, err := associations.ForStruct(m.Value, c.eagerFields...) 172 if err != nil { 173 return errors.Wrap(err, "could not retrieve associations") 174 } 175 176 if localIsEager && len(asos) == 0 { 177 // No association, fallback to non-eager mode. 178 localIsEager = false 179 } 180 181 if err = m.beforeSave(c); err != nil { 182 return err 183 } 184 185 if err = m.beforeCreate(c); err != nil { 186 return err 187 } 188 189 processAssoc := len(asos) > 0 190 191 if processAssoc { 192 before := asos.AssociationsBeforeCreatable() 193 for index := range before { 194 i := before[index].BeforeInterface() 195 if i == nil { 196 continue 197 } 198 199 if localIsEager { 200 sm := &Model{Value: i} 201 err = sm.iterate(func(m *Model) error { 202 id, err := m.fieldByName("ID") 203 if err != nil { 204 return err 205 } 206 if IsZeroOfUnderlyingType(id.Interface()) { 207 return c.Create(m.Value) 208 } 209 return nil 210 }) 211 212 if err != nil { 213 return err 214 } 215 } 216 217 err = before[index].BeforeSetup() 218 if err != nil { 219 return err 220 } 221 } 222 } 223 224 tn := m.TableName() 225 cols := columns.ForStructWithAlias(m.Value, tn, m.As) 226 227 if tn == sm.TableName() { 228 cols.Remove(excludeColumns...) 229 } 230 231 m.touchCreatedAt() 232 m.touchUpdatedAt() 233 234 if err = c.Dialect.Create(c.Store, m, cols); err != nil { 235 return err 236 } 237 238 if processAssoc { 239 after := asos.AssociationsAfterCreatable() 240 for index := range after { 241 if localIsEager { 242 err = after[index].AfterSetup() 243 if err != nil { 244 return err 245 } 246 247 i := after[index].AfterInterface() 248 if i == nil { 249 continue 250 } 251 252 sm := &Model{Value: i} 253 err = sm.iterate(func(m *Model) error { 254 fbn, err := m.fieldByName("ID") 255 if err != nil { 256 return err 257 } 258 id := fbn.Interface() 259 if IsZeroOfUnderlyingType(id) { 260 return c.Create(m.Value) 261 } 262 exists, errE := Q(c).Exists(i) 263 if errE != nil || !exists { 264 return c.Create(m.Value) 265 } 266 return nil 267 }) 268 269 if err != nil { 270 return err 271 } 272 } 273 stm := after[index].AfterProcess() 274 if c.TX != nil && !stm.Empty() { 275 _, err := c.TX.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...) 276 if err != nil { 277 return err 278 } 279 } 280 } 281 282 stms := asos.AssociationsCreatableStatement() 283 for index := range stms { 284 statements := stms[index].Statements() 285 for _, stm := range statements { 286 if c.TX != nil { 287 _, err := c.TX.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...) 288 if err != nil { 289 return err 290 } 291 continue 292 } 293 _, err = c.Store.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...) 294 if err != nil { 295 return err 296 } 297 } 298 } 299 } 300 301 if err = m.afterCreate(c); err != nil { 302 return err 303 } 304 305 return m.afterSave(c) 306 }) 307 }) 308 } 309 310 // ValidateAndUpdate applies validation rules on the given entry, then update it 311 // if the validation succeed, excluding the given columns. 312 // 313 // If model is a slice, each item of the slice is validated then updated in the database. 314 func (c *Connection) ValidateAndUpdate(model interface{}, excludeColumns ...string) (*validate.Errors, error) { 315 sm := &Model{Value: model} 316 verrs, err := sm.validateUpdate(c) 317 if err != nil { 318 return verrs, err 319 } 320 if verrs.HasAny() { 321 return verrs, nil 322 } 323 return verrs, c.Update(model, excludeColumns...) 324 } 325 326 // Update writes changes from an entry to the database, excluding the given columns. 327 // It updates the `updated_at` column automatically. 328 // 329 // If model is a slice, each item of the slice is updated in the database. 330 func (c *Connection) Update(model interface{}, excludeColumns ...string) error { 331 sm := &Model{Value: model} 332 return sm.iterate(func(m *Model) error { 333 return c.timeFunc("Update", func() error { 334 var err error 335 336 if err = m.beforeSave(c); err != nil { 337 return err 338 } 339 if err = m.beforeUpdate(c); err != nil { 340 return err 341 } 342 343 tn := m.TableName() 344 cols := columns.ForStructWithAlias(model, tn, m.As) 345 cols.Remove("id", "created_at") 346 347 if tn == sm.TableName() { 348 cols.Remove(excludeColumns...) 349 } 350 351 m.touchUpdatedAt() 352 353 if err = c.Dialect.Update(c.Store, m, cols); err != nil { 354 return err 355 } 356 if err = m.afterUpdate(c); err != nil { 357 return err 358 } 359 360 return m.afterSave(c) 361 }) 362 }) 363 } 364 365 // Destroy deletes a given entry from the database. 366 // 367 // If model is a slice, each item of the slice is deleted from the database. 368 func (c *Connection) Destroy(model interface{}) error { 369 sm := &Model{Value: model} 370 return sm.iterate(func(m *Model) error { 371 return c.timeFunc("Destroy", func() error { 372 var err error 373 374 if err = m.beforeDestroy(c); err != nil { 375 return err 376 } 377 if err = c.Dialect.Destroy(c.Store, m); err != nil { 378 return err 379 } 380 381 return m.afterDestroy(c) 382 }) 383 }) 384 }