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  }