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