diff --git a/callback.go b/callback.go index 9704e584..84526236 100644 --- a/callback.go +++ b/callback.go @@ -7,13 +7,13 @@ import ( // DefaultCallback default callbacks defined by gorm var DefaultCallback = &Callback{} -// Callback contains callbacks that used when CURD objects -// Field `creates` hold callbacks will be call when creating object -// Field `updates` hold callbacks will be call when updating object -// Field `deletes` hold callbacks will be call when deleting object -// Field `queries` hold callbacks will be call when querying object with query methods like Find, First, Related, Association... -// Field `rowQueries` hold callbacks will be call when querying object with Row, Rows... -// Field `processors` hold all callback processors, will be used to generate above callbacks in order +// Callback is a struct that contains all CURD callbacks +// Field `creates` contains callbacks will be call when creating object +// Field `updates` contains callbacks will be call when updating object +// Field `deletes` contains callbacks will be call when deleting object +// Field `queries` contains callbacks will be call when querying object with query methods like Find, First, Related, Association... +// Field `rowQueries` contains callbacks will be call when querying object with Row, Rows... +// Field `processors` contains all callback processors, will be used to generate above callbacks in order type Callback struct { creates []*func(scope *Scope) updates []*func(scope *Scope) @@ -23,7 +23,7 @@ type Callback struct { processors []*CallbackProcessor } -// CallbackProcessor contains all informations for a callback +// CallbackProcessor contains callback informations type CallbackProcessor struct { name string // current callback's name before string // register current callback before a callback @@ -68,7 +68,7 @@ func (c *Callback) Delete() *CallbackProcessor { } // Query could be used to register callbacks for querying objects with query methods like `Find`, `First`, `Related`, `Association`... -// refer `Create` for usage +// Refer `Create` for usage func (c *Callback) Query() *CallbackProcessor { return &CallbackProcessor{kind: "query", parent: c} } diff --git a/dialect.go b/dialect.go index 1d757078..6c9405da 100644 --- a/dialect.go +++ b/dialect.go @@ -34,7 +34,7 @@ type Dialect interface { // HasColumn check has column or not HasColumn(tableName string, columnName string) bool - // LimitAndOffsetSQL return generate SQL with limit and offset, as mssql has special case + // LimitAndOffsetSQL return generated SQL with Limit and Offset, as mssql has special case LimitAndOffsetSQL(limit, offset int) string // SelectFromDummyTable return select values, for most dbs, `SELECT values` just works, mysql needs `SELECT value FROM DUAL` SelectFromDummyTable() string diff --git a/errors.go b/errors.go index 32b7f220..cc66567d 100644 --- a/errors.go +++ b/errors.go @@ -6,9 +6,9 @@ import ( ) var ( - // ErrRecordNotFound record not found, happens when you are looking up with a struct, and haven't find any matched data + // ErrRecordNotFound record not found error, happens when haven't find any matched data when looking up with a struct ErrRecordNotFound = errors.New("record not found") - // ErrInvalidSQL invalid SQL, happens when you passed invalid SQL + // ErrInvalidSQL invalid SQL error, happens when you passed invalid SQL ErrInvalidSQL = errors.New("invalid SQL") // ErrInvalidTransaction invalid transaction when you are trying to `Commit` or `Rollback` ErrInvalidTransaction = errors.New("no valid transaction") diff --git a/main.go b/main.go index 2dbfb411..8baf1deb 100644 --- a/main.go +++ b/main.go @@ -26,14 +26,17 @@ type DB struct { joinTableHandlers map[string]JoinTableHandler } -// Open open a new db connection, need to import driver first, for example: +// Open initialize a new db connection, need to import driver first, e.g: // // import _ "github.com/go-sql-driver/mysql" // func main() { // db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local") // } -// GORM has wrapped some drivers, for easier to remember its name, so you could import the mysql driver with +// GORM has wrapped some drivers, for easier to remember driver's import path, so you could import the mysql driver with // import _ "github.com/jinzhu/gorm/dialects/mysql" +// // import _ "github.com/jinzhu/gorm/dialects/postgres" +// // import _ "github.com/jinzhu/gorm/dialects/sqlite" +// // import _ "github.com/jinzhu/gorm/dialects/mssql" func Open(dialect string, args ...interface{}) (*DB, error) { var db DB var err error @@ -87,7 +90,7 @@ func (s *DB) DB() *sql.DB { return s.db.(*sql.DB) } -// New initialize a new db connection without any search conditions +// New clone a new db connection without search conditions func (s *DB) New() *DB { clone := s.clone() clone.search = nil @@ -102,16 +105,14 @@ func (s *DB) NewScope(value interface{}) *Scope { return &Scope{db: dbClone, Search: dbClone.search.clone(), Value: value} } -// CommonDB return the underlying sql.DB or sql.Tx instance. -// Use of this method is discouraged. It's mainly intended to allow -// coexistence with legacy non-GORM code. +// CommonDB return the underlying `*sql.DB` or `*sql.Tx` instance, mainly intended to allow coexistence with legacy non-GORM code. func (s *DB) CommonDB() sqlCommon { return s.db } -// Callback return Callbacks container, you could add/remove/change callbacks with it +// Callback return `Callbacks` container, you could add/change/delete callbacks with it // db.Callback().Create().Register("update_created_at", updateCreated) -// Refer: https://jinzhu.github.io/gorm/development.html#callbacks for more +// Refer https://jinzhu.github.io/gorm/development.html#callbacks func (s *DB) Callback() *Callback { s.parent.callbacks = s.parent.callbacks.clone() return s.parent.callbacks @@ -138,17 +139,17 @@ func (s *DB) SingularTable(enable bool) { s.parent.singularTable = enable } -// Where return a new relation, accepts use `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/curd.html#query +// Where return a new relation, filter records with given conditions, accepts `map`, `struct` or `string` as conditions, refer http://jinzhu.github.io/gorm/curd.html#query func (s *DB) Where(query interface{}, args ...interface{}) *DB { return s.clone().search.Where(query, args...).db } -// Or match before conditions or this one, similar to `Where` +// Or filter records that match before conditions or this one, similar to `Where` func (s *DB) Or(query interface{}, args ...interface{}) *DB { return s.clone().search.Or(query, args...).db } -// Not don't match current conditions, similar to `Where` +// Not filter records that don't match current conditions, similar to `Where` func (s *DB) Not(query interface{}, args ...interface{}) *DB { return s.clone().search.Not(query, args...).db } @@ -163,18 +164,18 @@ func (s *DB) Offset(offset int) *DB { return s.clone().search.Offset(offset).db } -// Order specify order when retrieve records from database, pass `true` as the second argument to overwrite `Order` conditions +// Order specify order when retrieve records from database, set reorder to `true` to overwrite defined conditions func (s *DB) Order(value string, reorder ...bool) *DB { return s.clone().search.Order(value, reorder...).db } -// Select When querying, specify fields that you want to retrieve from database, by default, will select all fields; +// Select specify fields that you want to retrieve from database when querying, by default, will select all fields; // When creating/updating, specify fields that you want to save to database func (s *DB) Select(query interface{}, args ...interface{}) *DB { return s.clone().search.Select(query, args...).db } -// Omit specify fields that you want to ignore when save to database when creating/updating +// Omit specify fields that you want to ignore when saving to database for creating, updating func (s *DB) Omit(columns ...string) *DB { return s.clone().search.Omit(columns...).db } @@ -196,7 +197,18 @@ func (s *DB) Joins(query string, args ...interface{}) *DB { } // Scopes pass current database connection to arguments `func(*DB) *DB`, which could be used to add conditions dynamically -// Refer https://jinzhu.github.io/gorm/curd.html#scopes for more +// func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { +// return db.Where("amount > ?", 1000) +// } +// +// func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB { +// return func (db *gorm.DB) *gorm.DB { +// return db.Scopes(AmountGreaterThan1000).Where("status in (?)", status) +// } +// } +// +// db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders) +// Refer https://jinzhu.github.io/gorm/curd.html#scopes func (s *DB) Scopes(funcs ...func(*DB) *DB) *DB { for _, f := range funcs { s = f(s) @@ -204,7 +216,7 @@ func (s *DB) Scopes(funcs ...func(*DB) *DB) *DB { return s } -// Unscoped return all record including deleted record, refer Soft Delete https://jinzhu.github.io/gorm/curd.html#scopes +// Unscoped return all record including deleted record, refer Soft Delete https://jinzhu.github.io/gorm/curd.html#soft-delete func (s *DB) Unscoped() *DB { return s.clone().search.unscoped().db } @@ -245,12 +257,12 @@ func (s *DB) Scan(dest interface{}) *DB { return s.clone().NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db } -// Row return `*sql.Row` with given condtions +// Row return `*sql.Row` with given conditions func (s *DB) Row() *sql.Row { return s.NewScope(s.Value).row() } -// Rows return `*sql.Rows` with given condtions +// Rows return `*sql.Rows` with given conditions func (s *DB) Rows() (*sql.Rows, error) { return s.NewScope(s.Value).rows() } @@ -271,6 +283,8 @@ func (s *DB) ScanRows(rows *sql.Rows, result interface{}) error { } // Pluck used to query single column from a model as a map +// var ages []int64 +// db.Find(&users).Pluck("age", &ages) func (s *DB) Pluck(column string, value interface{}) *DB { return s.NewScope(s.Value).pluck(column, value).db } @@ -286,6 +300,7 @@ func (s *DB) Related(value interface{}, foreignKeys ...string) *DB { } // FirstOrInit find first matched record or initalize a new one with given conditions (only works with struct, map conditions) +// https://jinzhu.github.io/gorm/curd.html#firstorinit func (s *DB) FirstOrInit(out interface{}, where ...interface{}) *DB { c := s.clone() if result := c.First(out, where...); result.Error != nil { @@ -300,6 +315,7 @@ func (s *DB) FirstOrInit(out interface{}, where ...interface{}) *DB { } // FirstOrCreate find first matched record or create a new one with given conditions (only works with struct, map conditions) +// https://jinzhu.github.io/gorm/curd.html#firstorcreate func (s *DB) FirstOrCreate(out interface{}, where ...interface{}) *DB { c := s.clone() if result := c.First(out, where...); result.Error != nil { @@ -340,7 +356,7 @@ func (s *DB) UpdateColumns(values interface{}) *DB { callCallbacks(s.parent.callbacks.updates).db } -// Save update the value in database, if the value doesn't have primary key, will insert it +// Save update value in database, if the value doesn't have primary key, will insert it func (s *DB) Save(value interface{}) *DB { scope := s.clone().NewScope(value) if scope.PrimaryKeyZero() { @@ -355,7 +371,7 @@ func (s *DB) Create(value interface{}) *DB { return scope.callCallbacks(s.parent.callbacks.creates).db } -// Delete delete value that match given conditions, if the value has primary key, then will including the primary key as condition +// Delete delete value match given conditions, if the value has primary key, then will including the primary key as condition func (s *DB) Delete(value interface{}, where ...interface{}) *DB { return s.clone().NewScope(value).inlineCondition(where...).callCallbacks(s.parent.callbacks.deletes).db } @@ -432,14 +448,19 @@ func (s *DB) Rollback() *DB { return s } -// NewRecord check if value's primary key is blank or not +// NewRecord check if value's primary key is blank func (s *DB) NewRecord(value interface{}) bool { return s.clone().NewScope(value).PrimaryKeyZero() } -// RecordNotFound check if returning record not found error +// RecordNotFound check if returning ErrRecordNotFound error func (s *DB) RecordNotFound() bool { - return s.Error == ErrRecordNotFound + for _, err := range s.GetErrors() { + if err == ErrRecordNotFound { + return true + } + } + return false } // CreateTable create table for models @@ -464,19 +485,6 @@ func (s *DB) DropTable(values ...interface{}) *DB { return db } -// DropTableIfExists drop table for models only when it exists -func (s *DB) DropTableIfExists(values ...interface{}) *DB { - db := s.clone() - for _, value := range values { - if tableName, ok := value.(string); ok { - db = db.Table(tableName) - } - - db = db.NewScope(value).dropTableIfExists().db - } - return db -} - // HasTable check has table or not func (s *DB) HasTable(value interface{}) bool { var ( @@ -539,8 +547,8 @@ func (s *DB) RemoveIndex(indexName string) *DB { return scope.db } -// AddForeignKey Add foreign key to the given scope -// Example: db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT") +// AddForeignKey Add foreign key to the given scope, e.g: +// db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT") func (s *DB) AddForeignKey(field string, dest string, onDelete string, onUpdate string) *DB { scope := s.clone().NewScope(s.Value) scope.addForeignKey(field, dest, onDelete, onUpdate) @@ -569,7 +577,8 @@ func (s *DB) Association(column string) *Association { return &Association{Error: err} } -// Preload preload column with given conditions +// Preload preload associations with given conditions +// db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users) func (s *DB) Preload(column string, conditions ...interface{}) *DB { return s.clone().search.Preload(column, conditions...).db } @@ -631,7 +640,7 @@ func (s *DB) AddError(err error) error { return err } -// GetErrors get happened errors for the db +// GetErrors get happened errors from the db func (s *DB) GetErrors() (errors []error) { if errs, ok := s.Error.(errorsInterface); ok { return errs.GetErrors() diff --git a/model.go b/model.go index 1ffdf2ef..a6650877 100644 --- a/model.go +++ b/model.go @@ -2,7 +2,10 @@ package gorm import "time" -// Model base model definition, including `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`, which could be embeded in your models +// Model base model definition, including fields `ID`, `CreatedAt`, `UpdatedAt`, `DeletedAt`, which could be embeded in your models +// type User struct { +// gorm.Model +// } type Model struct { ID uint `gorm:"primary_key"` CreatedAt time.Time diff --git a/model_struct.go b/model_struct.go index 7773a1bf..b0dccf3b 100644 --- a/model_struct.go +++ b/model_struct.go @@ -109,7 +109,7 @@ func getForeignField(column string, fields []*StructField) *StructField { return nil } -// GetModelStruct generate model struct & relationships based on struct and tag definition +// GetModelStruct get value's model struct, relationships based on struct and tag definition func (scope *Scope) GetModelStruct() *ModelStruct { var modelStruct ModelStruct // Scope value can't be nil diff --git a/scope.go b/scope.go index 8d76489a..6239db7a 100644 --- a/scope.go +++ b/scope.go @@ -10,7 +10,7 @@ import ( "reflect" ) -// Scope contain any information of current operation when you perform any operation on the database +// Scope contain current operation's information when you perform any operation on the database type Scope struct { Search *search Value interface{} @@ -60,7 +60,7 @@ func (scope *Scope) SkipLeft() { scope.skipLeft = true } -// Quote used to quote database column name according to database dialect +// Quote used to quote string to escape them for database func (scope *Scope) Quote(str string) string { if strings.Index(str, ".") != -1 { newStrs := []string{} @@ -85,7 +85,7 @@ func (scope *Scope) Dialect() Dialect { return scope.db.parent.dialect } -// Err write error +// Err add error to Scope func (scope *Scope) Err(err error) error { if err != nil { scope.db.AddError(err) @@ -126,7 +126,7 @@ func (scope *Scope) PrimaryField() *Field { return nil } -// PrimaryKey get the primary key's column name +// PrimaryKey get main primary field's db name func (scope *Scope) PrimaryKey() string { if field := scope.PrimaryField(); field != nil { return field.DBName @@ -134,7 +134,7 @@ func (scope *Scope) PrimaryKey() string { return "" } -// PrimaryKeyZero check the primary key is blank or not +// PrimaryKeyZero check main primary field's value is blank or not func (scope *Scope) PrimaryKeyZero() bool { field := scope.PrimaryField() return field == nil || field.IsBlank @@ -158,7 +158,7 @@ func (scope *Scope) HasColumn(column string) bool { return false } -// SetColumn to set the column's value +// SetColumn to set the column's value, column could be field or field's name/dbname func (scope *Scope) SetColumn(column interface{}, value interface{}) error { var updateAttrs = map[string]interface{}{} if attrs, ok := scope.InstanceGet("gorm:update_attrs"); ok { @@ -221,7 +221,7 @@ func (scope *Scope) callMethod(methodName string, reflectValue reflect.Value) { } } -// CallMethod call scope value's method, if it is a slice, will call value's method one by one +// CallMethod call scope value's method, if it is a slice, will call its element's method one by one func (scope *Scope) CallMethod(methodName string) { if scope.Value == nil { return @@ -236,7 +236,7 @@ func (scope *Scope) CallMethod(methodName string) { } } -// AddToVars add value as sql's vars, gorm will escape them +// AddToVars add value as sql's vars, used to prevent SQL injection func (scope *Scope) AddToVars(value interface{}) string { if expr, ok := value.(*expr); ok { exp := expr.expr @@ -293,7 +293,7 @@ func (scope *Scope) CombinedConditionSql() string { scope.havingSQL() + scope.orderSQL() + scope.limitAndOffsetSQL() } -// FieldByName find gorm.Field with name and db name +// FieldByName find `gorm.Field` with field name or db name func (scope *Scope) FieldByName(name string) (field *Field, ok bool) { var ( dbName = ToDBName(name) @@ -311,13 +311,13 @@ func (scope *Scope) FieldByName(name string) (field *Field, ok bool) { return mostMatchedField, mostMatchedField != nil } -// Raw set sql +// Raw set raw sql func (scope *Scope) Raw(sql string) *Scope { scope.SQL = strings.Replace(sql, "$$", "?", -1) return scope } -// Exec invoke sql +// Exec perform generated SQL func (scope *Scope) Exec() *Scope { defer scope.trace(NowFunc()) @@ -337,7 +337,7 @@ func (scope *Scope) Set(name string, value interface{}) *Scope { return scope } -// Get get value by name +// Get get setting by name func (scope *Scope) Get(name string) (interface{}, bool) { return scope.db.Get(name) } @@ -350,12 +350,12 @@ func (scope *Scope) InstanceID() string { return scope.instanceID } -// InstanceSet set value for current instance, but not for associations +// InstanceSet set instance setting for current operation, but not for operations in callbacks, like saving associations callback func (scope *Scope) InstanceSet(name string, value interface{}) *Scope { return scope.Set(name+scope.InstanceID(), value) } -// InstanceGet get setting from current instance +// InstanceGet get instance setting from current operation func (scope *Scope) InstanceGet(name string) (interface{}, bool) { return scope.Get(name + scope.InstanceID()) } @@ -371,7 +371,7 @@ func (scope *Scope) Begin() *Scope { return scope } -// CommitOrRollback commit current transaction if there is no error, otherwise rollback it +// CommitOrRollback commit current transaction if no error happened, otherwise will rollback it func (scope *Scope) CommitOrRollback() *Scope { if _, ok := scope.InstanceGet("gorm:started_transaction"); ok { if db, ok := scope.db.db.(sqlTx); ok { @@ -386,7 +386,7 @@ func (scope *Scope) CommitOrRollback() *Scope { return scope } -// SelectAttrs retur nselected attributes +// SelectAttrs return selected attributes func (scope *Scope) SelectAttrs() []string { if scope.selectAttrs == nil { attrs := []string{} diff --git a/scope_private.go b/scope_private.go index 9309b6f4..c491cc7a 100644 --- a/scope_private.go +++ b/scope_private.go @@ -569,13 +569,6 @@ func (scope *Scope) dropTable() *Scope { return scope } -func (scope *Scope) dropTableIfExists() *Scope { - if scope.Dialect().HasTable(scope.TableName()) { - scope.dropTable() - } - return scope -} - func (scope *Scope) modifyColumn(column string, typ string) { scope.Raw(fmt.Sprintf("ALTER TABLE %v MODIFY %v %v", scope.QuotedTableName(), scope.Quote(column), typ)).Exec() } diff --git a/utils.go b/utils.go index 4ac2ab10..af11f5d2 100644 --- a/utils.go +++ b/utils.go @@ -14,10 +14,10 @@ import ( // NowFunc returns current time, this function is exported in order to be able // to give the flexibility to the developer to customize it according to their -// needs -// -// e.g: return time.Now().UTC() -// +// needs, e.g: +// gorm.NowFunc = func() time.Time { +// return time.Now().UTC() +// } var NowFunc = func() time.Time { return time.Now() } @@ -116,7 +116,7 @@ type expr struct { args []interface{} } -// Expr generate raw SQL expression for SQL, for example: +// Expr generate raw SQL expression, for example: // DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100)) func Expr(expression string, args ...interface{}) *expr { return &expr{expr: expression, args: args}