From c88df8eb4499df6ffed9114a834cfa2df88952e3 Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Wed, 27 Jan 2016 17:44:58 -0800 Subject: [PATCH 1/8] Lots of progress --- association_test.go | 6 +- callback_create.go | 41 +++++++++++-- callback_query.go | 3 + callback_shared.go | 9 ++- cockroach.go | 141 ++++++++++++++++++++++++++++++++++++++++++++ common_dialect.go | 8 +++ dialect.go | 4 ++ main_test.go | 4 ++ model_struct.go | 13 ++-- scope.go | 11 +++- scope_private.go | 8 ++- structs_test.go | 4 +- test_all.sh | 2 +- 13 files changed, 234 insertions(+), 20 deletions(-) create mode 100644 cockroach.go diff --git a/association_test.go b/association_test.go index f02d4620..58746572 100644 --- a/association_test.go +++ b/association_test.go @@ -16,7 +16,7 @@ func TestBelongsTo(t *testing.T) { } if err := DB.Save(&post).Error; err != nil { - t.Errorf("Got errors when save post", err.Error()) + t.Errorf("Got errors when save post %s", err) } if post.Category.ID == 0 || post.MainCategory.ID == 0 { @@ -184,7 +184,7 @@ func TestHasOne(t *testing.T) { } if err := DB.Save(&user).Error; err != nil { - t.Errorf("Got errors when save user", err.Error()) + t.Errorf("Got errors when save user %s", err.Error()) } if user.CreditCard.UserId.Int64 == 0 { @@ -331,7 +331,7 @@ func TestHasMany(t *testing.T) { } if err := DB.Save(&post).Error; err != nil { - t.Errorf("Got errors when save post", err.Error()) + t.Errorf("Got errors when save post %s", err.Error()) } for _, comment := range post.Comments { diff --git a/callback_create.go b/callback_create.go index d13a71be..6c418662 100644 --- a/callback_create.go +++ b/callback_create.go @@ -1,7 +1,10 @@ package gorm import ( + "database/sql" "fmt" + "log" + "reflect" "strings" ) @@ -28,7 +31,16 @@ func Create(scope *Scope) { for _, field := range fields { if scope.changeableField(field) { if field.IsNormal { - if !field.IsPrimaryKey || (field.IsPrimaryKey && !field.IsBlank) { + supportPrimary := scope.Dialect().SupportUniquePrimaryKey() + if !field.IsPrimaryKey || (field.IsPrimaryKey && (!field.IsBlank || !supportPrimary)) { + if field.IsPrimaryKey && !supportPrimary && field.IsBlank { + id := scope.Dialect().NewUniqueKey(scope) + if scope.HasError() { + return + } + log.Printf("ID %+v %+v", id, field.Field.Type().String()) + field.Field.Set(reflect.ValueOf(id).Convert(field.Field.Type())) + } if !field.IsBlank || !field.HasDefaultValue { columns = append(columns, scope.Quote(field.DBName)) sqls = append(sqls, scope.AddToVars(field.Field.Interface())) @@ -86,18 +98,37 @@ func Create(scope *Scope) { } } else { if primaryField == nil { - if results, err := scope.SqlDB().Exec(scope.Sql, scope.SqlVars...); err == nil { + if results, err := scope.SqlDB().Exec(scope.Sql, scope.SqlVars...); err == sql.ErrNoRows { + } else if err == nil { scope.db.RowsAffected, _ = results.RowsAffected() } else { + log.Printf("create err no primary %#v eql %#v", err, err == sql.ErrNoRows) scope.Err(err) } - } else { - if err := scope.Err(scope.SqlDB().QueryRow(scope.Sql, scope.SqlVars...).Scan(primaryField.Field.Addr().Interface())); err == nil { + } else { // if scope.Dialect().SupportUniquePrimaryKey() { + if err := scope.SqlDB().QueryRow(scope.Sql, scope.SqlVars...).Scan(primaryField.Field.Addr().Interface()); err == nil || err == sql.ErrNoRows { scope.db.RowsAffected = 1 } else { + log.Printf("create err %#v eql %#v", err, err == sql.ErrNoRows) scope.Err(err) } - } + } /* else { + // Create a new primary key if one is required, not set, and the server doesn't support unique primary keys. + log.Printf("key type %T %#v", val.Interface(), val.Interface()) + if key, ok := val.Interface().(*uint); ok && (key == nil || *key == 0) { + val := primaryField.Field.Addr() + id := scope.Dialect().NewUniqueKey(scope) + v := reflect.Indirect(val) + v.SetUint(id) + } + if results, err := scope.SqlDB().Exec(scope.Sql, scope.SqlVars...); err == sql.ErrNoRows { + } else if err == nil { + scope.db.RowsAffected, _ = results.RowsAffected() + } else { + log.Printf("create err no primary %#v eql %#v", err, err == sql.ErrNoRows) + scope.Err(err) + } + }*/ } } } diff --git a/callback_query.go b/callback_query.go index 5473f232..0db197ff 100644 --- a/callback_query.go +++ b/callback_query.go @@ -3,6 +3,7 @@ package gorm import ( "errors" "fmt" + "log" "reflect" ) @@ -83,6 +84,8 @@ func Query(scope *Scope) { scope.Err(rows.Scan(values...)) + log.Println("result values", values) + for index, column := range columns { value := values[index] if field, ok := fields[column]; ok { diff --git a/callback_shared.go b/callback_shared.go index 547059e3..da45b18c 100644 --- a/callback_shared.go +++ b/callback_shared.go @@ -1,6 +1,9 @@ package gorm -import "reflect" +import ( + "database/sql" + "reflect" +) func BeginTransaction(scope *Scope) { scope.Begin() @@ -18,7 +21,9 @@ func SaveBeforeAssociations(scope *Scope) { if scope.changeableField(field) && !field.IsBlank && !field.IsIgnored { if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { value := field.Field - scope.Err(scope.NewDB().Save(value.Addr().Interface()).Error) + if err := scope.NewDB().Save(value.Addr().Interface()).Error; err != nil && err != sql.ErrNoRows { + scope.Err(err) + } if len(relationship.ForeignFieldNames) != 0 { for idx, fieldName := range relationship.ForeignFieldNames { associationForeignName := relationship.AssociationForeignDBNames[idx] diff --git a/cockroach.go b/cockroach.go new file mode 100644 index 00000000..f4d572d0 --- /dev/null +++ b/cockroach.go @@ -0,0 +1,141 @@ +package gorm + +import ( + "fmt" + "log" + "reflect" + "time" +) + +type cockroach struct { + commonDialect +} + +func (cockroach) BinVar(i int) string { + return fmt.Sprintf("$%v", i) +} + +func (cockroach) SupportLastInsertId() bool { + return false +} + +func (cockroach) SupportUniquePrimaryKey() bool { + return false +} + +func (cockroach) NewUniqueKey(scope *Scope) uint64 { + rows, err := scope.NewDB().Raw(`SELECT experimental_unique_int()`).Rows() + if err != nil { + scope.Err(err) + return 0 + } + var id int64 + for rows.Next() { + if err := rows.Scan(&id); err != nil { + log.Fatal("ERR UNIQUE ID", id, err) + scope.Err(err) + return 0 + } + } + log.Printf("UNIQUE ID %#v", id) + return uint64(id) +} + +func (cockroach) SqlTag(value reflect.Value, size int, autoIncrease bool) string { + switch value.Kind() { + case reflect.Bool: + return "BOOLEAN" + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uintptr: + if autoIncrease { + return "INTEGER PRIMARY KEY" + } + return "INTEGER" + case reflect.Int64, reflect.Uint64: + if autoIncrease { + return "BIGINT PRIMARY KEY" + } + return "BIGINT" + case reflect.Float32, reflect.Float64: + return "FLOAT" + case reflect.String: + if size > 0 && size < 65532 { + return "VARCHAR" + } + return "TEXT" + case reflect.Struct: + if _, ok := value.Interface().(time.Time); ok { + return "TIMESTAMP" + } + default: + if _, ok := value.Interface().([]byte); ok { + return "BYTES" + } + } + panic(fmt.Sprintf("invalid sql type %s (%s) for cockroach", value.Type().Name(), value.Kind().String())) +} + +func (s cockroach) HasTable(scope *Scope, tableName string) bool { + rows, err := scope.NewDB().Raw("show tables").Rows() + if err != nil { + scope.Err(err) + return false + } + defer rows.Close() + var name string + for rows.Next() { + rows.Scan(&name) + if name == tableName { + return true + } + } + return false +} + +func (s cockroach) HasColumn(scope *Scope, tableName string, columnName string) bool { + rows, err := scope.NewDB().Raw(fmt.Sprintf("show columns from %s", tableName)).Rows() + if err != nil { + scope.Err(err) + return false + } + defer rows.Close() + var column string + for rows.Next() { + rows.Scan(&column) + if column == columnName { + return true + } + } + return false +} + +func (s cockroach) HasIndex(scope *Scope, tableName string, indexName string) bool { + /* + var count int + s.RawScanInt(scope, &count, fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND sql LIKE '%%INDEX %v ON%%'", indexName), tableName) + return count > 0 + */ + rows, err := scope.NewDB().Raw(fmt.Sprintf("show index from %s", tableName)).Rows() + if err != nil { + scope.Err(err) + return false + } + defer rows.Close() + var name string + for rows.Next() { + rows.Scan(nil, &name) + if name == indexName { + return true + } + } + return false +} + +func (cockroach) RemoveIndex(scope *Scope, indexName string) { + scope.Err(scope.NewDB().Raw(fmt.Sprintf("DROP INDEX %v@%v", scope.QuotedTableName(), indexName)).Error) +} + +func (s cockroach) CurrentDatabase(scope *Scope) string { + var name string + s.RawScanString(scope, &name, "SHOW DATABASE") + return name +} diff --git a/common_dialect.go b/common_dialect.go index 7f08b04f..4d5d00a5 100644 --- a/common_dialect.go +++ b/common_dialect.go @@ -16,6 +16,14 @@ func (commonDialect) SupportLastInsertId() bool { return true } +func (commonDialect) SupportUniquePrimaryKey() bool { + return true +} + +func (commonDialect) NewUniqueKey(scope *Scope) uint64 { + panic("NewUniqueKey not supported by commonDialect") +} + func (commonDialect) HasTop() bool { return false } diff --git a/dialect.go b/dialect.go index 926f8a11..f2074d13 100644 --- a/dialect.go +++ b/dialect.go @@ -8,6 +8,8 @@ import ( type Dialect interface { BinVar(i int) string SupportLastInsertId() bool + SupportUniquePrimaryKey() bool + NewUniqueKey(scope *Scope) uint64 HasTop() bool SqlTag(value reflect.Value, size int, autoIncrease bool) string ReturningStr(tableName, key string) string @@ -23,6 +25,8 @@ type Dialect interface { func NewDialect(driver string) Dialect { var d Dialect switch driver { + case "cockroach": + d = &cockroach{} case "postgres": d = &postgres{} case "foundation": diff --git a/main_test.go b/main_test.go index 65467d73..a1505126 100644 --- a/main_test.go +++ b/main_test.go @@ -6,6 +6,7 @@ import ( "fmt" "strconv" + _ "github.com/cockroachdb/cockroach/sql/driver" _ "github.com/denisenkom/go-mssqldb" testdb "github.com/erikstmartin/go-testdb" _ "github.com/go-sql-driver/mysql" @@ -53,6 +54,9 @@ func OpenTestConnection() (db gorm.DB, err error) { case "postgres": fmt.Println("testing postgres...") db, err = gorm.Open("postgres", "user=gorm DB.name=gorm sslmode=disable") + case "cockroach": + fmt.Println("testing cockroach...") + db, err = gorm.Open("cockroach", "http://localhost:26257?database=gorm&user=root") case "foundation": fmt.Println("testing foundation...") db, err = gorm.Open("foundation", "dbname=gorm port=15432 sslmode=disable") diff --git a/model_struct.go b/model_struct.go index d80165c8..866d276f 100644 --- a/model_struct.go +++ b/model_struct.go @@ -524,11 +524,6 @@ func (scope *Scope) generateSqlTag(field *StructField) string { sqlType = value } - additionalType := field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"] - if value, ok := field.TagSettings["DEFAULT"]; ok { - additionalType = additionalType + " DEFAULT " + value - } - if field.IsScanner { var getScannerValue func(reflect.Value) getScannerValue = func(value reflect.Value) { @@ -558,6 +553,14 @@ func (scope *Scope) generateSqlTag(field *StructField) string { sqlType = scope.Dialect().SqlTag(reflectValue, size, autoIncrease) } + additionalType := field.TagSettings["NOT NULL"] + " " + field.TagSettings["UNIQUE"] + if value, ok := field.TagSettings["DEFAULT"]; ok { + if _, ok := scope.Dialect().(*cockroach); ok && strings.TrimSpace(strings.ToLower(value)) == "null" { + value = value + "::" + strings.Split(sqlType, " ")[0] + } + additionalType = additionalType + " DEFAULT " + value + } + if strings.TrimSpace(additionalType) == "" { return sqlType } else { diff --git a/scope.go b/scope.go index a11d4ec4..0871842d 100644 --- a/scope.go +++ b/scope.go @@ -1,9 +1,12 @@ package gorm import ( + "database/sql/driver" "errors" "fmt" + "log" "regexp" + "runtime/debug" "strings" "time" @@ -103,6 +106,8 @@ func (scope *Scope) Dialect() Dialect { // Err write error func (scope *Scope) Err(err error) error { if err != nil { + log.Println("ERR", err) + debug.PrintStack() scope.db.AddError(err) } return err @@ -314,13 +319,15 @@ func (scope *Scope) Raw(sql string) *Scope { return scope } +var _, driverResultNoRows = driver.ResultNoRows.RowsAffected() + // Exec invoke sql func (scope *Scope) Exec() *Scope { defer scope.Trace(NowFunc()) if !scope.HasError() { if result, err := scope.SqlDB().Exec(scope.Sql, scope.SqlVars...); scope.Err(err) == nil { - if count, err := result.RowsAffected(); scope.Err(err) == nil { + if count, err := result.RowsAffected(); err != nil && err.Error() == driverResultNoRows.Error() || scope.Err(err) == nil { scope.db.RowsAffected = count } } @@ -358,6 +365,8 @@ func (scope *Scope) InstanceGet(name string) (interface{}, bool) { // Trace print sql log func (scope *Scope) Trace(t time.Time) { if len(scope.Sql) > 0 { + // TODO(d4l3k): Remove this line + log.Println("sql", scope.Sql, scope.SqlVars) scope.db.slog(scope.Sql, t, scope.SqlVars...) } } diff --git a/scope_private.go b/scope_private.go index fa5d5f44..269b2a7d 100644 --- a/scope_private.go +++ b/scope_private.go @@ -8,6 +8,7 @@ import ( "regexp" "strconv" "strings" + "time" ) func (scope *Scope) primaryCondition(value interface{}) string { @@ -170,7 +171,12 @@ func (scope *Scope) whereSql() (sql string) { var primaryConditions, andConditions, orConditions []string if !scope.Search.Unscoped && scope.Fields()["deleted_at"] != nil { - sql := fmt.Sprintf("(%v.deleted_at IS NULL OR %v.deleted_at <= '0001-01-02')", scope.QuotedTableName(), scope.QuotedTableName()) + time, err := time.Parse("2006-01-02", "0001-01-02") + if err != nil { + scope.Err(err) + return + } + sql := fmt.Sprintf("(%v.deleted_at IS NULL OR %v.deleted_at <= %v)", scope.QuotedTableName(), scope.QuotedTableName(), scope.AddToVars(time)) primaryConditions = append(primaryConditions, sql) } diff --git a/structs_test.go b/structs_test.go index cb9c9260..da3159af 100644 --- a/structs_test.go +++ b/structs_test.go @@ -39,7 +39,7 @@ type User struct { } type CreditCard struct { - ID int8 + ID uint64 Number string UserId sql.NullInt64 CreatedAt time.Time @@ -48,7 +48,7 @@ type CreditCard struct { } type Email struct { - Id int16 + Id int64 UserId int Email string `sql:"type:varchar(100);"` CreatedAt time.Time diff --git a/test_all.sh b/test_all.sh index 6c5593b3..701018e3 100755 --- a/test_all.sh +++ b/test_all.sh @@ -1,4 +1,4 @@ -dialects=("postgres" "mysql" "sqlite") +dialects=("postgres" "mysql" "sqlite" "cockroach") for dialect in "${dialects[@]}" ; do GORM_DIALECT=${dialect} go test From f179cddafbf19f6d34b8d483af8940ebf385f4b0 Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Wed, 27 Jan 2016 18:08:28 -0800 Subject: [PATCH 2/8] Removed a number of logging statements --- callback_create.go | 24 ++---------------------- callback_query.go | 3 +-- cockroach.go | 3 --- 3 files changed, 3 insertions(+), 27 deletions(-) diff --git a/callback_create.go b/callback_create.go index 6c418662..c0e9c59d 100644 --- a/callback_create.go +++ b/callback_create.go @@ -3,7 +3,6 @@ package gorm import ( "database/sql" "fmt" - "log" "reflect" "strings" ) @@ -38,7 +37,6 @@ func Create(scope *Scope) { if scope.HasError() { return } - log.Printf("ID %+v %+v", id, field.Field.Type().String()) field.Field.Set(reflect.ValueOf(id).Convert(field.Field.Type())) } if !field.IsBlank || !field.HasDefaultValue { @@ -102,33 +100,15 @@ func Create(scope *Scope) { } else if err == nil { scope.db.RowsAffected, _ = results.RowsAffected() } else { - log.Printf("create err no primary %#v eql %#v", err, err == sql.ErrNoRows) scope.Err(err) } - } else { // if scope.Dialect().SupportUniquePrimaryKey() { + } else { if err := scope.SqlDB().QueryRow(scope.Sql, scope.SqlVars...).Scan(primaryField.Field.Addr().Interface()); err == nil || err == sql.ErrNoRows { scope.db.RowsAffected = 1 } else { - log.Printf("create err %#v eql %#v", err, err == sql.ErrNoRows) scope.Err(err) } - } /* else { - // Create a new primary key if one is required, not set, and the server doesn't support unique primary keys. - log.Printf("key type %T %#v", val.Interface(), val.Interface()) - if key, ok := val.Interface().(*uint); ok && (key == nil || *key == 0) { - val := primaryField.Field.Addr() - id := scope.Dialect().NewUniqueKey(scope) - v := reflect.Indirect(val) - v.SetUint(id) - } - if results, err := scope.SqlDB().Exec(scope.Sql, scope.SqlVars...); err == sql.ErrNoRows { - } else if err == nil { - scope.db.RowsAffected, _ = results.RowsAffected() - } else { - log.Printf("create err no primary %#v eql %#v", err, err == sql.ErrNoRows) - scope.Err(err) - } - }*/ + } } } } diff --git a/callback_query.go b/callback_query.go index 0db197ff..453634b9 100644 --- a/callback_query.go +++ b/callback_query.go @@ -3,7 +3,6 @@ package gorm import ( "errors" "fmt" - "log" "reflect" ) @@ -84,7 +83,7 @@ func Query(scope *Scope) { scope.Err(rows.Scan(values...)) - log.Println("result values", values) + //log.Println("result values", values) for index, column := range columns { value := values[index] diff --git a/cockroach.go b/cockroach.go index f4d572d0..80008dee 100644 --- a/cockroach.go +++ b/cockroach.go @@ -2,7 +2,6 @@ package gorm import ( "fmt" - "log" "reflect" "time" ) @@ -32,12 +31,10 @@ func (cockroach) NewUniqueKey(scope *Scope) uint64 { var id int64 for rows.Next() { if err := rows.Scan(&id); err != nil { - log.Fatal("ERR UNIQUE ID", id, err) scope.Err(err) return 0 } } - log.Printf("UNIQUE ID %#v", id) return uint64(id) } From a80bfb8bc5b3a1edf59853b04bde539b37d6b43c Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Fri, 29 Jan 2016 13:38:47 -0800 Subject: [PATCH 3/8] Removed debug statments --- callback_query.go | 2 -- query_test.go | 4 ++-- scope.go | 6 ------ 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/callback_query.go b/callback_query.go index 453634b9..5473f232 100644 --- a/callback_query.go +++ b/callback_query.go @@ -83,8 +83,6 @@ func Query(scope *Scope) { scope.Err(rows.Scan(values...)) - //log.Println("result values", values) - for index, column := range columns { value := values[index] if field, ok := fields[column]; ok { diff --git a/query_test.go b/query_test.go index a7d5bc0e..51c1c5e2 100644 --- a/query_test.go +++ b/query_test.go @@ -31,8 +31,8 @@ func TestFirstAndLast(t *testing.T) { t.Errorf("Find first record as slice") } - if DB.Joins("left join emails on emails.user_id = users.id").First(&User{}).Error != nil { - t.Errorf("Should not raise any error when order with Join table") + if err := DB.Joins("left join emails on emails.user_id = users.id").First(&User{}).Error; err != nil { + t.Errorf("Should not raise any error when order with Join table: %s", err) } } diff --git a/scope.go b/scope.go index 0871842d..29354133 100644 --- a/scope.go +++ b/scope.go @@ -4,9 +4,7 @@ import ( "database/sql/driver" "errors" "fmt" - "log" "regexp" - "runtime/debug" "strings" "time" @@ -106,8 +104,6 @@ func (scope *Scope) Dialect() Dialect { // Err write error func (scope *Scope) Err(err error) error { if err != nil { - log.Println("ERR", err) - debug.PrintStack() scope.db.AddError(err) } return err @@ -365,8 +361,6 @@ func (scope *Scope) InstanceGet(name string) (interface{}, bool) { // Trace print sql log func (scope *Scope) Trace(t time.Time) { if len(scope.Sql) > 0 { - // TODO(d4l3k): Remove this line - log.Println("sql", scope.Sql, scope.SqlVars) scope.db.slog(scope.Sql, t, scope.SqlVars...) } } From 039a759a53a822c4736b3755a7f542fdb0612d8f Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Fri, 29 Jan 2016 14:11:04 -0800 Subject: [PATCH 4/8] Disabled updates to the primary key using cockroachdb --- callback_update.go | 8 ++++++++ cockroach.go | 4 ++++ common_dialect.go | 4 ++++ dialect.go | 1 + 4 files changed, 17 insertions(+) diff --git a/callback_update.go b/callback_update.go index 4c9952d2..64d71d7c 100644 --- a/callback_update.go +++ b/callback_update.go @@ -43,6 +43,11 @@ func Update(scope *Scope) { if updateAttrs, ok := scope.InstanceGet("gorm:update_attrs"); ok { for key, value := range updateAttrs.(map[string]interface{}) { + if !scope.Dialect().SupportUpdatePrimaryKey() { + if field, ok := scope.Fields()[key]; ok && field.IsPrimaryKey { + continue + } + } if scope.changeableDBColumn(key) { sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(key), scope.AddToVars(value))) } @@ -50,6 +55,9 @@ func Update(scope *Scope) { } else { fields := scope.Fields() for _, field := range fields { + if field.IsPrimaryKey && !scope.Dialect().SupportUpdatePrimaryKey() { + continue + } if scope.changeableField(field) && !field.IsPrimaryKey && field.IsNormal { sqls = append(sqls, fmt.Sprintf("%v = %v", scope.Quote(field.DBName), scope.AddToVars(field.Field.Interface()))) } else if relationship := field.Relationship; relationship != nil && relationship.Kind == "belongs_to" { diff --git a/cockroach.go b/cockroach.go index 80008dee..828d7b0c 100644 --- a/cockroach.go +++ b/cockroach.go @@ -22,6 +22,10 @@ func (cockroach) SupportUniquePrimaryKey() bool { return false } +func (cockroach) SupportUpdatePrimaryKey() bool { + return false +} + func (cockroach) NewUniqueKey(scope *Scope) uint64 { rows, err := scope.NewDB().Raw(`SELECT experimental_unique_int()`).Rows() if err != nil { diff --git a/common_dialect.go b/common_dialect.go index 4d5d00a5..874523b2 100644 --- a/common_dialect.go +++ b/common_dialect.go @@ -20,6 +20,10 @@ func (commonDialect) SupportUniquePrimaryKey() bool { return true } +func (commonDialect) SupportUpdatePrimaryKey() bool { + return true +} + func (commonDialect) NewUniqueKey(scope *Scope) uint64 { panic("NewUniqueKey not supported by commonDialect") } diff --git a/dialect.go b/dialect.go index f2074d13..94294717 100644 --- a/dialect.go +++ b/dialect.go @@ -9,6 +9,7 @@ type Dialect interface { BinVar(i int) string SupportLastInsertId() bool SupportUniquePrimaryKey() bool + SupportUpdatePrimaryKey() bool NewUniqueKey(scope *Scope) uint64 HasTop() bool SqlTag(value reflect.Value, size int, autoIncrease bool) string From 597b31e969c468213f70fa4eeb857ef0a95664cb Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Fri, 29 Jan 2016 14:16:15 -0800 Subject: [PATCH 5/8] Updated TestUIntPrimaryKey to no longer require incrementing primary keys --- query_test.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/query_test.go b/query_test.go index 51c1c5e2..148c395c 100644 --- a/query_test.go +++ b/query_test.go @@ -52,15 +52,19 @@ func TestFirstAndLastWithNoStdPrimaryKey(t *testing.T) { } func TestUIntPrimaryKey(t *testing.T) { + insertedAnimal := &Animal{Name: "animalUint1"} + insertedAnimal2 := &Animal{Name: "animalUint2"} + DB.Save(insertedAnimal) + DB.Save(insertedAnimal2) var animal Animal - DB.First(&animal, uint64(1)) - if animal.Counter != 1 { - t.Errorf("Fetch a record from with a non-int primary key should work, but failed") + DB.First(&animal, insertedAnimal.Counter) + if animal.Counter != insertedAnimal.Counter || animal.Counter <= 0 { + t.Errorf("Fetch a record from with a non-int primary key should work, but failed; got %d", animal.Counter) } - DB.Model(Animal{}).Where(Animal{Counter: uint64(2)}).Scan(&animal) - if animal.Counter != 2 { - t.Errorf("Fetch a record from with a non-int primary key should work, but failed") + DB.Model(Animal{}).Where(Animal{Counter: insertedAnimal2.Counter}).Scan(&animal) + if animal.Counter != insertedAnimal2.Counter || animal.Counter <= 0 { + t.Errorf("Fetch a record from with a non-int primary key should work, but failed; got %d", animal.Counter) } } From d334c5ec52ad8c8794cfbbf3a82a993bfcc190b9 Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Fri, 29 Jan 2016 15:01:46 -0800 Subject: [PATCH 6/8] Fixed TestIndexes --- cockroach.go | 30 ++++++++++++++++++++---------- main_test.go | 4 +++- scope.go | 3 +++ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/cockroach.go b/cockroach.go index 828d7b0c..def2fcd7 100644 --- a/cockroach.go +++ b/cockroach.go @@ -2,6 +2,7 @@ package gorm import ( "fmt" + "log" "reflect" "time" ) @@ -32,6 +33,7 @@ func (cockroach) NewUniqueKey(scope *Scope) uint64 { scope.Err(err) return 0 } + defer rows.Close() var id int64 for rows.Next() { if err := rows.Scan(&id); err != nil { @@ -84,7 +86,10 @@ func (s cockroach) HasTable(scope *Scope, tableName string) bool { defer rows.Close() var name string for rows.Next() { - rows.Scan(&name) + if err := rows.Scan(&name); err != nil { + scope.Err(err) + return false + } if name == tableName { return true } @@ -101,7 +106,10 @@ func (s cockroach) HasColumn(scope *Scope, tableName string, columnName string) defer rows.Close() var column string for rows.Next() { - rows.Scan(&column) + if err := rows.Scan(&column); err != nil { + scope.Err(err) + return false + } if column == columnName { return true } @@ -110,20 +118,22 @@ func (s cockroach) HasColumn(scope *Scope, tableName string, columnName string) } func (s cockroach) HasIndex(scope *Scope, tableName string, indexName string) bool { - /* - var count int - s.RawScanInt(scope, &count, fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND sql LIKE '%%INDEX %v ON%%'", indexName), tableName) - return count > 0 - */ rows, err := scope.NewDB().Raw(fmt.Sprintf("show index from %s", tableName)).Rows() if err != nil { scope.Err(err) return false } defer rows.Close() - var name string + + var table, name, column, direction string + var unique, storing bool + var seq int for rows.Next() { - rows.Scan(nil, &name) + if err := rows.Scan(&table, &name, &unique, &seq, &column, &direction, &storing); err != nil { + scope.Err(err) + return false + } + log.Printf("HasIndex %#v %#v %#v ", table, name, indexName) if name == indexName { return true } @@ -132,7 +142,7 @@ func (s cockroach) HasIndex(scope *Scope, tableName string, indexName string) bo } func (cockroach) RemoveIndex(scope *Scope, indexName string) { - scope.Err(scope.NewDB().Raw(fmt.Sprintf("DROP INDEX %v@%v", scope.QuotedTableName(), indexName)).Error) + scope.Err(scope.NewDB().Exec(fmt.Sprintf("DROP INDEX %v@%v", scope.TableName(), indexName)).Error) } func (s cockroach) CurrentDatabase(scope *Scope) string { diff --git a/main_test.go b/main_test.go index a1505126..28b110d1 100644 --- a/main_test.go +++ b/main_test.go @@ -467,7 +467,9 @@ func TestJoins(t *testing.T) { DB.Save(&user) var result User - DB.Joins("left join emails on emails.user_id = users.id").Where("name = ?", "joins").First(&result) + if err := DB.Joins("left join emails on emails.user_id = users.id").Where("name = ?", "joins").First(&result).Error; err != nil { + t.Errorf("Error while joining: %s", err) + } if result.Name != "joins" || result.Id != user.Id { t.Errorf("Should find all two emails with Join") } diff --git a/scope.go b/scope.go index 29354133..9a4b263e 100644 --- a/scope.go +++ b/scope.go @@ -4,6 +4,7 @@ import ( "database/sql/driver" "errors" "fmt" + "log" "regexp" "strings" "time" @@ -361,6 +362,8 @@ func (scope *Scope) InstanceGet(name string) (interface{}, bool) { // Trace print sql log func (scope *Scope) Trace(t time.Time) { if len(scope.Sql) > 0 { + // TODO(d4l3k): Remove this line + log.Println("sql", scope.Sql, scope.SqlVars) scope.db.slog(scope.Sql, t, scope.SqlVars...) } } From bb04a8b212a326e4f0fd283d179c6b3eed328628 Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Fri, 29 Jan 2016 15:01:46 -0800 Subject: [PATCH 7/8] Fixed TestIndexes --- cockroach.go | 30 ++++++++++++++++++++---------- main_test.go | 4 +++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/cockroach.go b/cockroach.go index 828d7b0c..def2fcd7 100644 --- a/cockroach.go +++ b/cockroach.go @@ -2,6 +2,7 @@ package gorm import ( "fmt" + "log" "reflect" "time" ) @@ -32,6 +33,7 @@ func (cockroach) NewUniqueKey(scope *Scope) uint64 { scope.Err(err) return 0 } + defer rows.Close() var id int64 for rows.Next() { if err := rows.Scan(&id); err != nil { @@ -84,7 +86,10 @@ func (s cockroach) HasTable(scope *Scope, tableName string) bool { defer rows.Close() var name string for rows.Next() { - rows.Scan(&name) + if err := rows.Scan(&name); err != nil { + scope.Err(err) + return false + } if name == tableName { return true } @@ -101,7 +106,10 @@ func (s cockroach) HasColumn(scope *Scope, tableName string, columnName string) defer rows.Close() var column string for rows.Next() { - rows.Scan(&column) + if err := rows.Scan(&column); err != nil { + scope.Err(err) + return false + } if column == columnName { return true } @@ -110,20 +118,22 @@ func (s cockroach) HasColumn(scope *Scope, tableName string, columnName string) } func (s cockroach) HasIndex(scope *Scope, tableName string, indexName string) bool { - /* - var count int - s.RawScanInt(scope, &count, fmt.Sprintf("SELECT count(*) FROM sqlite_master WHERE tbl_name = ? AND sql LIKE '%%INDEX %v ON%%'", indexName), tableName) - return count > 0 - */ rows, err := scope.NewDB().Raw(fmt.Sprintf("show index from %s", tableName)).Rows() if err != nil { scope.Err(err) return false } defer rows.Close() - var name string + + var table, name, column, direction string + var unique, storing bool + var seq int for rows.Next() { - rows.Scan(nil, &name) + if err := rows.Scan(&table, &name, &unique, &seq, &column, &direction, &storing); err != nil { + scope.Err(err) + return false + } + log.Printf("HasIndex %#v %#v %#v ", table, name, indexName) if name == indexName { return true } @@ -132,7 +142,7 @@ func (s cockroach) HasIndex(scope *Scope, tableName string, indexName string) bo } func (cockroach) RemoveIndex(scope *Scope, indexName string) { - scope.Err(scope.NewDB().Raw(fmt.Sprintf("DROP INDEX %v@%v", scope.QuotedTableName(), indexName)).Error) + scope.Err(scope.NewDB().Exec(fmt.Sprintf("DROP INDEX %v@%v", scope.TableName(), indexName)).Error) } func (s cockroach) CurrentDatabase(scope *Scope) string { diff --git a/main_test.go b/main_test.go index a1505126..28b110d1 100644 --- a/main_test.go +++ b/main_test.go @@ -467,7 +467,9 @@ func TestJoins(t *testing.T) { DB.Save(&user) var result User - DB.Joins("left join emails on emails.user_id = users.id").Where("name = ?", "joins").First(&result) + if err := DB.Joins("left join emails on emails.user_id = users.id").Where("name = ?", "joins").First(&result).Error; err != nil { + t.Errorf("Error while joining: %s", err) + } if result.Name != "joins" || result.Id != user.Id { t.Errorf("Should find all two emails with Join") } From 6508b88f620d33933fdd74c9925039b2fb6466db Mon Sep 17 00:00:00 2001 From: Tristan Rice Date: Fri, 29 Jan 2016 15:23:15 -0800 Subject: [PATCH 8/8] Fixed HasColumn, removed debug statements, improved test error messages --- cockroach.go | 5 ++--- scope.go | 3 --- slice_test.go | 4 ++-- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cockroach.go b/cockroach.go index def2fcd7..7de61b8d 100644 --- a/cockroach.go +++ b/cockroach.go @@ -2,7 +2,6 @@ package gorm import ( "fmt" - "log" "reflect" "time" ) @@ -105,8 +104,9 @@ func (s cockroach) HasColumn(scope *Scope, tableName string, columnName string) } defer rows.Close() var column string + var typ, null, defaultVal interface{} for rows.Next() { - if err := rows.Scan(&column); err != nil { + if err := rows.Scan(&column, &typ, &null, &defaultVal); err != nil { scope.Err(err) return false } @@ -133,7 +133,6 @@ func (s cockroach) HasIndex(scope *Scope, tableName string, indexName string) bo scope.Err(err) return false } - log.Printf("HasIndex %#v %#v %#v ", table, name, indexName) if name == indexName { return true } diff --git a/scope.go b/scope.go index 9a4b263e..29354133 100644 --- a/scope.go +++ b/scope.go @@ -4,7 +4,6 @@ import ( "database/sql/driver" "errors" "fmt" - "log" "regexp" "strings" "time" @@ -362,8 +361,6 @@ func (scope *Scope) InstanceGet(name string) (interface{}, bool) { // Trace print sql log func (scope *Scope) Trace(t time.Time) { if len(scope.Sql) > 0 { - // TODO(d4l3k): Remove this line - log.Println("sql", scope.Sql, scope.SqlVars) scope.db.slog(scope.Sql, t, scope.SqlVars...) } } diff --git a/slice_test.go b/slice_test.go index 21410548..fa1c6c76 100644 --- a/slice_test.go +++ b/slice_test.go @@ -20,13 +20,13 @@ func TestScannableSlices(t *testing.T) { } if err := DB.Save(&r1).Error; err != nil { - t.Errorf("Should save record with slice values") + t.Errorf("Should save record with slice values; err %s", err) } var r2 RecordWithSlice if err := DB.Find(&r2).Error; err != nil { - t.Errorf("Should fetch record with slice values") + t.Errorf("Should fetch record with slice values; err %s", err) } if len(r2.Strings) != 3 || r2.Strings[0] != "a" || r2.Strings[1] != "b" || r2.Strings[2] != "c" {