From 231aba53c58fcb9ca0e3a70375eba88b337ad4cc Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Thu, 22 Oct 2020 11:28:43 +0800 Subject: [PATCH 1/6] Fix count with order by --- finisher_api.go | 9 +++++++++ tests/count_test.go | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/finisher_api.go b/finisher_api.go index 2951fdef..30616284 100644 --- a/finisher_api.go +++ b/finisher_api.go @@ -326,6 +326,15 @@ func (db *DB) Count(count *int64) (tx *DB) { defer tx.Statement.AddClause(clause.Select{}) } + if orderByClause, ok := db.Statement.Clauses["ORDER BY"]; ok { + if _, ok := db.Statement.Clauses["GROUP BY"]; !ok { + delete(db.Statement.Clauses, "ORDER BY") + defer func() { + db.Statement.Clauses["ORDER BY"] = orderByClause + }() + } + } + tx.Statement.Dest = count tx.callbacks.Query().Execute(tx) if tx.RowsAffected != 1 { diff --git a/tests/count_test.go b/tests/count_test.go index 0d348227..41bad71d 100644 --- a/tests/count_test.go +++ b/tests/count_test.go @@ -70,6 +70,11 @@ func TestCount(t *testing.T) { var count4 int64 if err := DB.Table("users").Joins("LEFT JOIN companies on companies.name = users.name").Where("users.name = ?", user1.Name).Count(&count4).Error; err != nil || count4 != 1 { + t.Errorf("count with join, got error: %v, count %v", err, count4) + } + + var count5 int64 + if err := DB.Table("users").Where("users.name = ?", user1.Name).Order("name").Count(&count5).Error; err != nil || count5 != 1 { t.Errorf("count with join, got error: %v, count %v", err, count) } } From 6d90d09cb86f5e57aacaee925d510cedaf839cae Mon Sep 17 00:00:00 2001 From: qifengzhang007 <15087404+qifengzhang007@users.noreply.github.com> Date: Thu, 22 Oct 2020 14:09:09 +0800 Subject: [PATCH 2/6] =?UTF-8?q?Recorder=E8=BF=BD=E8=B8=AA=E5=87=BD?= =?UTF-8?q?=E6=95=B0trace=E5=9C=A8finish=5Fapi=E6=96=87=E4=BB=B6358?= =?UTF-8?q?=E8=A1=8Cscan=E5=87=BD=E6=95=B0=E6=89=80=E5=9C=A8=E7=9A=84371?= =?UTF-8?q?=E8=A1=8C=E8=A2=AB=E8=B0=83=E7=94=A8=E6=97=B6=EF=BC=8CBeginAt?= =?UTF-8?q?=20=E6=B2=A1=E6=9C=89=E8=B5=8B=E5=80=BC=EF=BC=8C=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=80=BC0001-0:0:0=E5=AF=BC=E8=87=B4=E8=BF=BD?= =?UTF-8?q?=E8=B8=AA=E6=97=A5=E5=BF=97=E6=98=BE=E7=A4=BA=E7=9A=84sql?= =?UTF-8?q?=E8=80=97=E6=97=B6=E6=97=A0=E9=99=90=E5=A4=A7.=20(#3657)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 张奇峰 <10515935zwj> --- logger/logger.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logger/logger.go b/logger/logger.go index 6782736c..11619c92 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -64,7 +64,7 @@ var ( LogLevel: Warn, Colorful: true, }) - Recorder = traceRecorder{Interface: Default} + Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()} ) func New(writer Writer, config Config) Interface { @@ -173,7 +173,7 @@ type traceRecorder struct { } func (l traceRecorder) New() *traceRecorder { - return &traceRecorder{Interface: l.Interface} + return &traceRecorder{Interface: l.Interface, BeginAt: time.Now()} } func (l *traceRecorder) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { From 0aef8acc11c783808d1986e03b5e665f0c60fda4 Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Thu, 22 Oct 2020 14:00:10 +0800 Subject: [PATCH 3/6] Add smart auto migrate tests --- migrator/migrator.go | 6 +++--- tests/go.mod | 6 +++--- tests/migrate_test.go | 16 +++++++++------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/migrator/migrator.go b/migrator/migrator.go index c564cb67..c455a294 100644 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -370,9 +370,9 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy alterColumn = true } else { // has size in data type and not equal - matches := regexp.MustCompile(`[^\d](\d+)[^\d]`).FindAllStringSubmatch(realDataType, -1) - matches2 := regexp.MustCompile(`[^\d]*(\d+)[^\d]`).FindAllStringSubmatch(fullDataType, -1) - if (len(matches) == 1 && matches[0][1] != fmt.Sprint(field.Size)) && (len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length)) { + matches := regexp.MustCompile(`[^\d](\d+)[^\d]?`).FindAllStringSubmatch(realDataType, -1) + matches2 := regexp.MustCompile(`[^\d]*(\d+)[^\d]?`).FindAllStringSubmatch(fullDataType, -1) + if (len(matches) == 1 && matches[0][1] != fmt.Sprint(field.Size) || !field.PrimaryKey) && (len(matches2) == 1 && matches2[0][1] != fmt.Sprint(length)) { alterColumn = true } } diff --git a/tests/go.mod b/tests/go.mod index ddb1773b..3fa011f1 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -6,11 +6,11 @@ require ( github.com/google/uuid v1.1.1 github.com/jinzhu/now v1.1.1 github.com/lib/pq v1.6.0 - gorm.io/driver/mysql v1.0.2 - gorm.io/driver/postgres v1.0.4 + gorm.io/driver/mysql v1.0.3 + gorm.io/driver/postgres v1.0.5 gorm.io/driver/sqlite v1.1.3 gorm.io/driver/sqlserver v1.0.5 - gorm.io/gorm v1.20.2 + gorm.io/gorm v1.20.4 ) replace gorm.io/gorm => ../ diff --git a/tests/migrate_test.go b/tests/migrate_test.go index 4cc8a7c3..275fe634 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -48,11 +48,13 @@ func TestMigrate(t *testing.T) { } func TestSmartMigrateColumn(t *testing.T) { + fullSupported := map[string]bool{"mysql": true, "postgres": true}[DB.Dialector.Name()] + type UserMigrateColumn struct { ID uint Name string Salary float64 - Birthday time.Time + Birthday time.Time `gorm:"precision:4"` } DB.Migrator().DropTable(&UserMigrateColumn{}) @@ -78,15 +80,15 @@ func TestSmartMigrateColumn(t *testing.T) { for _, columnType := range columnTypes { switch columnType.Name() { case "name": - if length, _ := columnType.Length(); length != 0 && length != 128 { + if length, _ := columnType.Length(); (fullSupported || length != 0) && length != 128 { t.Fatalf("name's length should be 128, but got %v", length) } case "salary": - if precision, o, _ := columnType.DecimalSize(); precision != 0 && precision != 2 { + if precision, o, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 { t.Fatalf("salary's precision should be 2, but got %v %v", precision, o) } case "birthday": - if precision, _, _ := columnType.DecimalSize(); precision != 0 && precision != 2 { + if precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 2 { t.Fatalf("birthday's precision should be 2, but got %v", precision) } } @@ -111,15 +113,15 @@ func TestSmartMigrateColumn(t *testing.T) { for _, columnType := range columnTypes { switch columnType.Name() { case "name": - if length, _ := columnType.Length(); length != 0 && length != 256 { + if length, _ := columnType.Length(); (fullSupported || length != 0) && length != 256 { t.Fatalf("name's length should be 128, but got %v", length) } case "salary": - if precision, _, _ := columnType.DecimalSize(); precision != 0 && precision != 3 { + if precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 { t.Fatalf("salary's precision should be 2, but got %v", precision) } case "birthday": - if precision, _, _ := columnType.DecimalSize(); precision != 0 && precision != 3 { + if precision, _, _ := columnType.DecimalSize(); (fullSupported || precision != 0) && precision != 3 { t.Fatalf("birthday's precision should be 2, but got %v", precision) } } From db2630cb3a02edcc92678ed78e49d1e85d268224 Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Thu, 22 Oct 2020 17:32:39 +0800 Subject: [PATCH 4/6] Fix data race problem when using Scan, close #3662 --- finisher_api.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/finisher_api.go b/finisher_api.go index 30616284..857f9419 100644 --- a/finisher_api.go +++ b/finisher_api.go @@ -365,9 +365,13 @@ func (db *DB) Rows() (*sql.Rows, error) { // Scan scan value to a struct func (db *DB) Scan(dest interface{}) (tx *DB) { - currentLogger, newLogger := db.Logger, logger.Recorder.New() + config := *db.Config + currentLogger, newLogger := config.Logger, logger.Recorder.New() + config.Logger = newLogger + tx = db.getInstance() - tx.Logger = newLogger + tx.Config = &config + if rows, err := tx.Rows(); err != nil { tx.AddError(err) } else { From dd92f8bdc0ba926a538dce7a84fd3b630d45c168 Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Fri, 23 Oct 2020 11:01:45 +0800 Subject: [PATCH 5/6] Allow create table for other database/schema #3640 --- migrator/migrator.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/migrator/migrator.go b/migrator/migrator.go index c455a294..9493a00c 100644 --- a/migrator/migrator.go +++ b/migrator/migrator.go @@ -32,6 +32,7 @@ func (m Migrator) RunWithValue(value interface{}, fc func(*gorm.Statement) error stmt := &gorm.Statement{DB: m.DB} if m.DB.Statement != nil { stmt.Table = m.DB.Statement.Table + stmt.TableExpr = m.DB.Statement.TableExpr } if table, ok := value.(string); ok { @@ -161,6 +162,10 @@ func (m Migrator) CreateTable(values ...interface{}) error { hasPrimaryKeyInDataType bool ) + if stmt.TableExpr != nil { + values[0] = *stmt.TableExpr + } + for _, dbName := range stmt.Schema.DBNames { field := stmt.Schema.FieldsByDBName[dbName] createTableSQL += "? ?" From cb591a71299532f881a104cdb0abf7ae5b794a6f Mon Sep 17 00:00:00 2001 From: Jinzhu Date: Fri, 23 Oct 2020 18:40:05 +0800 Subject: [PATCH 6/6] Fix panic when using FirstOrCreate with soft delete, close #3671 --- schema/field.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/schema/field.go b/schema/field.go index e7f5b708..b303fb30 100644 --- a/schema/field.go +++ b/schema/field.go @@ -762,13 +762,15 @@ func (field *Field) setupValuerAndSetter() { // pointer scanner field.Set = func(value reflect.Value, v interface{}) (err error) { reflectV := reflect.ValueOf(v) - if reflectV.Type().AssignableTo(field.FieldType) { + if !reflectV.IsValid() { + field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) + } else if reflectV.Type().AssignableTo(field.FieldType) { field.ReflectValueOf(value).Set(reflectV) } else if reflectV.Kind() == reflect.Ptr { - if reflectV.IsNil() { + if reflectV.IsNil() || !reflectV.IsValid() { field.ReflectValueOf(value).Set(reflect.New(field.FieldType).Elem()) } else { - err = field.Set(value, reflectV.Elem().Interface()) + return field.Set(value, reflectV.Elem().Interface()) } } else { fieldValue := field.ReflectValueOf(value)