github.com/royge/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  }