diff --git a/clause/expression.go b/clause/expression.go index dde00b1d..92ac7f22 100644 --- a/clause/expression.go +++ b/clause/expression.go @@ -127,7 +127,7 @@ func (expr NamedExpr) Build(builder Builder) { if v == '@' && !inName { inName = true name = []byte{} - } else if v == ' ' || v == ',' || v == ')' || v == '"' || v == '\'' || v == '`' || v == '\n' || v == ';' { + } else if v == ' ' || v == ',' || v == ')' || v == '"' || v == '\'' || v == '`' || v == '\r' || v == '\n' || v == ';' { if inName { if nv, ok := namedMap[string(name)]; ok { builder.AddVar(builder, nv) diff --git a/clause/expression_test.go b/clause/expression_test.go index 4826db38..aaede61c 100644 --- a/clause/expression_test.go +++ b/clause/expression_test.go @@ -94,6 +94,16 @@ func TestNamedExpr(t *testing.T) { Vars: []interface{}{sql.Named("name", "jinzhu")}, Result: "name1 = ? AND name2 = ?;", ExpectedVars: []interface{}{"jinzhu", "jinzhu"}, + }, { + SQL: "name1 = @name1\r\n AND name2 = @name2", + Vars: []interface{}{map[string]interface{}{"name1": "jinzhu", "name2": "jinzhu"}}, + Result: "name1 = ?\r\n AND name2 = ?", + ExpectedVars: []interface{}{"jinzhu", "jinzhu"}, + }, { + SQL: "name1 = @name1\r AND name2 = @name2", + Vars: []interface{}{map[string]interface{}{"name1": "jinzhu", "name2": "jinzhu"}}, + Result: "name1 = ?\r AND name2 = ?", + ExpectedVars: []interface{}{"jinzhu", "jinzhu"}, }, { SQL: "?", Vars: []interface{}{clause.Column{Table: "table", Name: "col"}}, diff --git a/finisher_api.go b/finisher_api.go index da4ef8f7..7a3f27ba 100644 --- a/finisher_api.go +++ b/finisher_api.go @@ -351,9 +351,9 @@ func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) { } return tx.Model(dest).Updates(assigns) - } else { - tx.Error = result.Error } + } else { + tx.Error = result.Error } return tx } diff --git a/scan.go b/scan.go index ad3734d8..a611a9ce 100644 --- a/scan.go +++ b/scan.go @@ -193,14 +193,21 @@ func Scan(rows Rows, db *DB, mode ScanMode) { // Not Pluck if sch != nil { + schFieldsCount := len(sch.Fields) for idx, column := range columns { if field := sch.LookUpField(column); field != nil && field.Readable { if curIndex, ok := selectedColumnsMap[column]; ok { - for fieldIndex, selectField := range sch.Fields[curIndex+1:] { - if selectField.DBName == column && selectField.Readable { - selectedColumnsMap[column] = curIndex + fieldIndex + 1 - fields[idx] = selectField - break + fields[idx] = field // handle duplicate fields + offset := curIndex + 1 + // handle sch inconsistent with database + // like Raw(`...`).Scan + if schFieldsCount > offset { + for fieldIndex, selectField := range sch.Fields[offset:] { + if selectField.DBName == column && selectField.Readable { + selectedColumnsMap[column] = curIndex + fieldIndex + 1 + fields[idx] = selectField + break + } } } } else { diff --git a/tests/create_test.go b/tests/create_test.go index 3730172f..274a7f48 100644 --- a/tests/create_test.go +++ b/tests/create_test.go @@ -476,6 +476,13 @@ func TestOmitWithCreate(t *testing.T) { CheckUser(t, result2, user2) } +func TestFirstOrCreateNotExistsTable(t *testing.T) { + company := Company{Name: "first_or_create_if_not_exists_table"} + if err := DB.Table("not_exists").FirstOrCreate(&company).Error; err == nil { + t.Errorf("not exists table, but err is nil") + } +} + func TestFirstOrCreateWithPrimaryKey(t *testing.T) { company := Company{ID: 100, Name: "company100_with_primarykey"} DB.FirstOrCreate(&company) diff --git a/tests/migrate_test.go b/tests/migrate_test.go index f5b3e551..9e7caec9 100644 --- a/tests/migrate_test.go +++ b/tests/migrate_test.go @@ -8,6 +8,7 @@ import ( "testing" "time" + "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/schema" . "gorm.io/gorm/utils/tests" @@ -848,3 +849,38 @@ func findColumnType(dest interface{}, columnName string) ( } return } + +func TestInvalidCachedPlan(t *testing.T) { + if DB.Dialector.Name() != "postgres" { + return + } + + db, err := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{}) + if err != nil { + t.Errorf("Open err:%v", err) + } + + type Object1 struct{} + type Object2 struct { + Field1 string + } + type Object3 struct { + Field2 string + } + db.Migrator().DropTable("objects") + + err = db.Table("objects").AutoMigrate(&Object1{}) + if err != nil { + t.Errorf("AutoMigrate err:%v", err) + } + + err = db.Table("objects").AutoMigrate(&Object2{}) + if err != nil { + t.Errorf("AutoMigrate err:%v", err) + } + + err = db.Table("objects").AutoMigrate(&Object3{}) + if err != nil { + t.Errorf("AutoMigrate err:%v", err) + } +} diff --git a/tests/scan_test.go b/tests/scan_test.go index 425c0a29..6f2e9f54 100644 --- a/tests/scan_test.go +++ b/tests/scan_test.go @@ -214,4 +214,29 @@ func TestScanToEmbedded(t *testing.T) { if !addressMatched { t.Errorf("Failed, no address matched") } + + personDupField := Person{ID: person1.ID} + if err := DB.Select("people.id, people.*"). + First(&personDupField).Error; err != nil { + t.Errorf("Failed to run join query, got error: %v", err) + } + AssertEqual(t, person1, personDupField) + + user := User{ + Name: "TestScanToEmbedded_1", + Manager: &User{ + Name: "TestScanToEmbedded_1_m1", + Manager: &User{Name: "TestScanToEmbedded_1_m1_m1"}, + }, + } + DB.Create(&user) + + type UserScan struct { + ID uint + Name string + ManagerID *uint + } + var user2 UserScan + err := DB.Raw("SELECT * FROM users INNER JOIN users Manager ON users.manager_id = Manager.id WHERE users.id = ?", user.ID).Scan(&user2).Error + AssertEqual(t, err, nil) } diff --git a/tests/tests_test.go b/tests/tests_test.go index 08f4f193..dcba3cbf 100644 --- a/tests/tests_test.go +++ b/tests/tests_test.go @@ -17,6 +17,11 @@ import ( ) var DB *gorm.DB +var ( + mysqlDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local" + postgresDSN = "user=gorm password=gorm dbname=gorm host=localhost port=9920 sslmode=disable TimeZone=Asia/Shanghai" + sqlserverDSN = "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm" +) func init() { var err error @@ -49,13 +54,13 @@ func OpenTestConnection() (db *gorm.DB, err error) { case "mysql": log.Println("testing mysql...") if dbDSN == "" { - dbDSN = "gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True&loc=Local" + dbDSN = mysqlDSN } db, err = gorm.Open(mysql.Open(dbDSN), &gorm.Config{}) case "postgres": log.Println("testing postgres...") if dbDSN == "" { - dbDSN = "user=gorm password=gorm dbname=gorm host=localhost port=9920 sslmode=disable TimeZone=Asia/Shanghai" + dbDSN = postgresDSN } db, err = gorm.Open(postgres.New(postgres.Config{ DSN: dbDSN, @@ -72,7 +77,7 @@ func OpenTestConnection() (db *gorm.DB, err error) { // GO log.Println("testing sqlserver...") if dbDSN == "" { - dbDSN = "sqlserver://gorm:LoremIpsum86@localhost:9930?database=gorm" + dbDSN = sqlserverDSN } db, err = gorm.Open(sqlserver.Open(dbDSN), &gorm.Config{}) default: