github.com/paweljw/pop@v4.13.1+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 if err := sm.beforeValidate(c); err != nil { 56 return nil, err 57 } 58 verrs, err := sm.validateSave(c) 59 if err != nil { 60 return verrs, err 61 } 62 if verrs.HasAny() { 63 return verrs, nil 64 } 65 return verrs, c.Save(model, excludeColumns...) 66 } 67 68 var emptyUUID = uuid.Nil.String() 69 70 // IsZeroOfUnderlyingType will check if the value of anything is the equal to the Zero value of that type. 71 func IsZeroOfUnderlyingType(x interface{}) bool { 72 return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface()) 73 } 74 75 // Save wraps the Create and Update methods. It executes a Create if no ID is provided with the entry; 76 // or issues an Update otherwise. 77 // 78 // If model is a slice, each item of the slice is saved in the database. 79 func (c *Connection) Save(model interface{}, excludeColumns ...string) error { 80 sm := &Model{Value: model} 81 return sm.iterate(func(m *Model) error { 82 id, err := m.fieldByName("ID") 83 if err != nil { 84 return err 85 } 86 if IsZeroOfUnderlyingType(id.Interface()) { 87 return c.Create(m.Value, excludeColumns...) 88 } 89 return c.Update(m.Value, excludeColumns...) 90 }) 91 } 92 93 // ValidateAndCreate applies validation rules on the given entry, then creates it 94 // if the validation succeed, excluding the given columns. 95 // 96 // If model is a slice, each item of the slice is validated then created in the database. 97 func (c *Connection) ValidateAndCreate(model interface{}, excludeColumns ...string) (*validate.Errors, error) { 98 sm := &Model{Value: model} 99 if err := sm.beforeValidate(c); err != nil { 100 return nil, err 101 } 102 verrs, err := sm.validateCreate(c) 103 if err != nil { 104 return verrs, err 105 } 106 if verrs.HasAny() { 107 return verrs, nil 108 } 109 110 if c.eager { 111 asos, err := associations.ForStruct(model, c.eagerFields...) 112 if err != nil { 113 return verrs, errors.Wrap(err, "could not retrieve associations") 114 } 115 116 if len(asos) == 0 { 117 log(logging.Debug, "no associations found for given struct, disable eager mode") 118 c.disableEager() 119 return verrs, c.Create(model, excludeColumns...) 120 } 121 122 before := asos.AssociationsBeforeCreatable() 123 for index := range before { 124 i := before[index].BeforeInterface() 125 if i == nil { 126 continue 127 } 128 129 sm := &Model{Value: i} 130 verrs, err := sm.validateAndOnlyCreate(c) 131 if err != nil || verrs.HasAny() { 132 return verrs, err 133 } 134 } 135 136 after := asos.AssociationsAfterCreatable() 137 for index := range after { 138 i := after[index].AfterInterface() 139 if i == nil { 140 continue 141 } 142 143 sm := &Model{Value: i} 144 verrs, err := sm.validateAndOnlyCreate(c) 145 if err != nil || verrs.HasAny() { 146 return verrs, err 147 } 148 } 149 150 sm := &Model{Value: model} 151 verrs, err = sm.validateCreate(c) 152 if err != nil || verrs.HasAny() { 153 return verrs, err 154 } 155 } 156 157 return verrs, c.Create(model, excludeColumns...) 158 } 159 160 // Create add a new given entry to the database, excluding the given columns. 161 // It updates `created_at` and `updated_at` columns automatically. 162 // 163 // If model is a slice, each item of the slice is created in the database. 164 // 165 // Create support two modes: 166 // * Flat (default): Associate existing nested objects only. NO creation or update of nested objects. 167 // * Eager: Associate existing nested objects and create non-existent objects. NO change to existing objects. 168 func (c *Connection) Create(model interface{}, excludeColumns ...string) error { 169 var isEager = c.eager 170 171 c.disableEager() 172 173 sm := &Model{Value: model} 174 return sm.iterate(func(m *Model) error { 175 return c.timeFunc("Create", func() error { 176 var localIsEager = isEager 177 asos, err := associations.ForStruct(m.Value, c.eagerFields...) 178 if err != nil { 179 return errors.Wrap(err, "could not retrieve associations") 180 } 181 182 if localIsEager && len(asos) == 0 { 183 // No association, fallback to non-eager mode. 184 localIsEager = false 185 } 186 187 if err = m.beforeSave(c); err != nil { 188 return err 189 } 190 191 if err = m.beforeCreate(c); err != nil { 192 return err 193 } 194 195 processAssoc := len(asos) > 0 196 197 if processAssoc { 198 before := asos.AssociationsBeforeCreatable() 199 for index := range before { 200 i := before[index].BeforeInterface() 201 if i == nil { 202 continue 203 } 204 205 if localIsEager { 206 sm := &Model{Value: i} 207 err = sm.iterate(func(m *Model) error { 208 id, err := m.fieldByName("ID") 209 if err != nil { 210 return err 211 } 212 if IsZeroOfUnderlyingType(id.Interface()) { 213 return c.Create(m.Value) 214 } 215 return nil 216 }) 217 218 if err != nil { 219 return err 220 } 221 } 222 223 err = before[index].BeforeSetup() 224 if err != nil { 225 return err 226 } 227 } 228 } 229 230 tn := m.TableName() 231 cols := columns.ForStructWithAlias(m.Value, tn, m.As) 232 233 if tn == sm.TableName() { 234 cols.Remove(excludeColumns...) 235 } 236 237 m.touchCreatedAt() 238 m.touchUpdatedAt() 239 240 if err = c.Dialect.Create(c.Store, m, cols); err != nil { 241 return err 242 } 243 244 if processAssoc { 245 after := asos.AssociationsAfterCreatable() 246 for index := range after { 247 if localIsEager { 248 err = after[index].AfterSetup() 249 if err != nil { 250 return err 251 } 252 253 i := after[index].AfterInterface() 254 if i == nil { 255 continue 256 } 257 258 sm := &Model{Value: i} 259 err = sm.iterate(func(m *Model) error { 260 fbn, err := m.fieldByName("ID") 261 if err != nil { 262 return err 263 } 264 id := fbn.Interface() 265 if IsZeroOfUnderlyingType(id) { 266 return c.Create(m.Value) 267 } 268 exists, errE := Q(c).Exists(i) 269 if errE != nil || !exists { 270 return c.Create(m.Value) 271 } 272 return nil 273 }) 274 275 if err != nil { 276 return err 277 } 278 } 279 stm := after[index].AfterProcess() 280 if c.TX != nil && !stm.Empty() { 281 _, err := c.TX.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...) 282 if err != nil { 283 return err 284 } 285 } 286 } 287 288 stms := asos.AssociationsCreatableStatement() 289 for index := range stms { 290 statements := stms[index].Statements() 291 for _, stm := range statements { 292 if c.TX != nil { 293 _, err := c.TX.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...) 294 if err != nil { 295 return err 296 } 297 continue 298 } 299 _, err = c.Store.Exec(c.Dialect.TranslateSQL(stm.Statement), stm.Args...) 300 if err != nil { 301 return err 302 } 303 } 304 } 305 } 306 307 if err = m.afterCreate(c); err != nil { 308 return err 309 } 310 311 return m.afterSave(c) 312 }) 313 }) 314 } 315 316 // ValidateAndUpdate applies validation rules on the given entry, then update it 317 // if the validation succeed, excluding the given columns. 318 // 319 // If model is a slice, each item of the slice is validated then updated in the database. 320 func (c *Connection) ValidateAndUpdate(model interface{}, excludeColumns ...string) (*validate.Errors, error) { 321 sm := &Model{Value: model} 322 if err := sm.beforeValidate(c); err != nil { 323 return nil, err 324 } 325 verrs, err := sm.validateUpdate(c) 326 if err != nil { 327 return verrs, err 328 } 329 if verrs.HasAny() { 330 return verrs, nil 331 } 332 return verrs, c.Update(model, excludeColumns...) 333 } 334 335 // Update writes changes from an entry to the database, excluding the given columns. 336 // It updates the `updated_at` column automatically. 337 // 338 // If model is a slice, each item of the slice is updated in the database. 339 func (c *Connection) Update(model interface{}, excludeColumns ...string) error { 340 sm := &Model{Value: model} 341 return sm.iterate(func(m *Model) error { 342 return c.timeFunc("Update", func() error { 343 var err error 344 345 if err = m.beforeSave(c); err != nil { 346 return err 347 } 348 if err = m.beforeUpdate(c); err != nil { 349 return err 350 } 351 352 tn := m.TableName() 353 cols := columns.ForStructWithAlias(model, tn, m.As) 354 cols.Remove("id", "created_at") 355 356 if tn == sm.TableName() { 357 cols.Remove(excludeColumns...) 358 } 359 360 m.touchUpdatedAt() 361 362 if err = c.Dialect.Update(c.Store, m, cols); err != nil { 363 return err 364 } 365 if err = m.afterUpdate(c); err != nil { 366 return err 367 } 368 369 return m.afterSave(c) 370 }) 371 }) 372 } 373 374 // UpdateColumns writes changes from an entry to the database, including only the given columns 375 // or all columns if no column names are provided. 376 // It updates the `updated_at` column automatically if you include `updated_at` in columnNames. 377 // 378 // If model is a slice, each item of the slice is updated in the database. 379 func (c *Connection) UpdateColumns(model interface{}, columnNames ...string) error { 380 sm := &Model{Value: model} 381 return sm.iterate(func(m *Model) error { 382 return c.timeFunc("Update", func() error { 383 var err error 384 385 if err = m.beforeSave(c); err != nil { 386 return err 387 } 388 if err = m.beforeUpdate(c); err != nil { 389 return err 390 } 391 392 tn := m.TableName() 393 394 cols := columns.Columns{} 395 if len(columnNames) > 0 && tn == sm.TableName() { 396 cols = columns.NewColumnsWithAlias(tn, m.As) 397 cols.Add(columnNames...) 398 399 } else { 400 cols = columns.ForStructWithAlias(model, tn, m.As) 401 } 402 cols.Remove("id", "created_at") 403 404 m.touchUpdatedAt() 405 406 if err = c.Dialect.Update(c.Store, m, cols); err != nil { 407 return err 408 } 409 if err = m.afterUpdate(c); err != nil { 410 return err 411 } 412 413 return m.afterSave(c) 414 }) 415 }) 416 } 417 418 // Destroy deletes a given entry from the database. 419 // 420 // If model is a slice, each item of the slice is deleted from the database. 421 func (c *Connection) Destroy(model interface{}) error { 422 sm := &Model{Value: model} 423 return sm.iterate(func(m *Model) error { 424 return c.timeFunc("Destroy", func() error { 425 var err error 426 427 if err = m.beforeDestroy(c); err != nil { 428 return err 429 } 430 if err = c.Dialect.Destroy(c.Store, m); err != nil { 431 return err 432 } 433 434 return m.afterDestroy(c) 435 }) 436 }) 437 }