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