Repository: go-gorm/gorm
Pull request #6540 (2023-08-23 00:08:25)

Fix handling of unknown column types
<!--
Make sure these boxes checked before submitting your pull request.

For significant changes, please open an issue to make an agreement on an implementation design/plan first before starting it.
-->

- [x] Do only one thing
- [x] Non breaking API changes
- [x] Tested

### What did this pull request do?

<!--
provide a general description of the code changes in your pull request
-->

When using the go clickhouse driver, scanning into sql.RawBytes is not supported for most columns. I think the database/sql standard only [guarantees](https://pkg.go.dev/database/sql#Rows.Scan) that all columns can be converted to *interface{}, not RawBytes, so gorm should not make this assumption.

This issue doesn't affect most drivers like sqlite, mysql, postgres, but it does affect clickhouse. The following test will pass once this change is merged: https://github.com/go-gorm/clickhouse/pull/128

### User Case Description

<!-- Your use case -->


jinzhu: This might broken query with maps? 

If it is possible to make the clickhouse driver support sql.RawBytes, I think this might be a better solution?
looi: Why should it break maps? I think any driver would be happy to scan a map into interface{}?
looi: I don't think changing the clickhouse driver is the right solution because the clickhouse driver isn't violating the spec

Patch:
diff --git a/README.md b/README.md
index 85ad305..745dad6 100644
--- a/README.md
+++ b/README.md
@@ -41,4 +41,4 @@ The fantastic ORM library for Golang, aims to be developer friendly.
 
 © Jinzhu, 2013~time.Now
 
-Released under the [MIT License](https://github.com/go-gorm/gorm/blob/master/License)
+Released under the [MIT License](https://github.com/go-gorm/gorm/blob/master/LICENSE)
diff --git a/callbacks.go b/callbacks.go
index 195d172..50b5b0e 100644
--- a/callbacks.go
+++ b/callbacks.go
@@ -187,10 +187,18 @@ func (p *processor) Replace(name string, fn func(*DB)) error {
 
 func (p *processor) compile() (err error) {
 	var callbacks []*callback
+	removedMap := map[string]bool{}
 	for _, callback := range p.callbacks {
 		if callback.match == nil || callback.match(p.db) {
 			callbacks = append(callbacks, callback)
 		}
+		if callback.remove {
+			removedMap[callback.name] = true
+		}
+	}
+
+	if len(removedMap) > 0 {
+		callbacks = removeCallbacks(callbacks, removedMap)
 	}
 	p.callbacks = callbacks
 
@@ -339,3 +347,14 @@ func sortCallbacks(cs []*callback) (fns []func(*DB), err error) {
 
 	return
 }
+
+func removeCallbacks(cs []*callback, nameMap map[string]bool) []*callback {
+	callbacks := make([]*callback, 0, len(cs))
+	for _, callback := range cs {
+		if nameMap[callback.name] {
+			continue
+		}
+		callbacks = append(callbacks, callback)
+	}
+	return callbacks
+}
diff --git a/callbacks/create.go b/callbacks/create.go
index f0b7813..8b7846b 100644
--- a/callbacks/create.go
+++ b/callbacks/create.go
@@ -103,13 +103,62 @@ func Create(config *Config) func(db *gorm.DB) {
 		}
 
 		db.RowsAffected, _ = result.RowsAffected()
-		if db.RowsAffected != 0 && db.Statement.Schema != nil &&
-			db.Statement.Schema.PrioritizedPrimaryField != nil &&
-			db.Statement.Schema.PrioritizedPrimaryField.HasDefaultValue {
-			insertID, err := result.LastInsertId()
-			insertOk := err == nil && insertID > 0
-			if !insertOk {
+		if db.RowsAffected == 0 {
+			return
+		}
+
+		var (
+			pkField     *schema.Field
+			pkFieldName = "@id"
+		)
+
+		insertID, err := result.LastInsertId()
+		insertOk := err == nil && insertID > 0
+
+		if !insertOk {
+			if !supportReturning {
 				db.AddError(err)
+			}
+			return
+		}
+
+		if db.Statement.Schema != nil {
+			if db.Statement.Schema.PrioritizedPrimaryField == nil || !db.Statement.Schema.PrioritizedPrimaryField.HasDefaultValue {
+				return
+			}
+			pkField = db.Statement.Schema.PrioritizedPrimaryField
+			pkFieldName = db.Statement.Schema.PrioritizedPrimaryField.DBName
+		}
+
+		// append @id column with value for auto-increment primary key
+		// the @id value is correct, when: 1. without setting auto-increment primary key, 2. database AutoIncrementIncrement = 1
+		switch values := db.Statement.Dest.(type) {
+		case map[string]interface{}:
+			values[pkFieldName] = insertID
+		case *map[string]interface{}:
+			(*values)[pkFieldName] = insertID
+		case []map[string]interface{}, *[]map[string]interface{}:
+			mapValues, ok := values.([]map[string]interface{})
+			if !ok {
+				if v, ok := values.(*[]map[string]interface{}); ok {
+					if *v != nil {
+						mapValues = *v
+					}
+				}
+			}
+
+			if config.LastInsertIDReversed {
+				insertID -= int64(len(mapValues)-1) * schema.DefaultAutoIncrementIncrement
+			}
+
+			for _, mapValue := range mapValues {
+				if mapValue != nil {
+					mapValue[pkFieldName] = insertID
+				}
+				insertID += schema.DefaultAutoIncrementIncrement
+			}
+		default:
+			if pkField == nil {
 				return
 			}
 
@@ -122,10 +171,10 @@ func Create(config *Config) func(db *gorm.DB) {
 							break
 						}
 
-						_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, rv)
+						_, isZero := pkField.ValueOf(db.Statement.Context, rv)
 						if isZero {
-							db.AddError(db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, rv, insertID))
-							insertID -= db.Statement.Schema.PrioritizedPrimaryField.AutoIncrementIncrement
+							db.AddError(pkField.Set(db.Statement.Context, rv, insertID))
+							insertID -= pkField.AutoIncrementIncrement
 						}
 					}
 				} else {
@@ -135,16 +184,16 @@ func Create(config *Config) func(db *gorm.DB) {
 							break
 						}
 
-						if _, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, rv); isZero {
-							db.AddError(db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, rv, insertID))
-							insertID += db.Statement.Schema.PrioritizedPrimaryField.AutoIncrementIncrement
+						if _, isZero := pkField.ValueOf(db.Statement.Context, rv); isZero {
+							db.AddError(pkField.Set(db.Statement.Context, rv, insertID))
+							insertID += pkField.AutoIncrementIncrement
 						}
 					}
 				}
 			case reflect.Struct:
-				_, isZero := db.Statement.Schema.PrioritizedPrimaryField.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
+				_, isZero := pkField.ValueOf(db.Statement.Context, db.Statement.ReflectValue)
 				if isZero {
-					db.AddError(db.Statement.Schema.PrioritizedPrimaryField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID))
+					db.AddError(pkField.Set(db.Statement.Context, db.Statement.ReflectValue, insertID))
 				}
 			}
 		}
@@ -253,13 +302,15 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {
 				}
 			}
 
-			for field, vs := range defaultValueFieldsHavingValue {
-				values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
-				for idx := range values.Values {
-					if vs[idx] == nil {
-						values.Values[idx] = append(values.Values[idx], stmt.Dialector.DefaultValueOf(field))
-					} else {
-						values.Values[idx] = append(values.Values[idx], vs[idx])
+			for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
+				if vs, ok := defaultValueFieldsHavingValue[field]; ok {
+					values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
+					for idx := range values.Values {
+						if vs[idx] == nil {
+							values.Values[idx] = append(values.Values[idx], stmt.DefaultValueOf(field))
+						} else {
+							values.Values[idx] = append(values.Values[idx], vs[idx])
+						}
 					}
 				}
 			}
@@ -282,7 +333,7 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {
 			}
 
 			for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
-				if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
+				if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) && field.DefaultValueInterface == nil {
 					if rvOfvalue, isZero := field.ValueOf(stmt.Context, stmt.ReflectValue); !isZero {
 						values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
 						values.Values[0] = append(values.Values[0], rvOfvalue)
@@ -311,7 +362,7 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {
 									case schema.UnixNanosecond:
 										assignment.Value = curTime.UnixNano()
 									case schema.UnixMillisecond:
-										assignment.Value = curTime.UnixNano() / 1e6
+										assignment.Value = curTime.UnixMilli()
 									case schema.UnixSecond:
 										assignment.Value = curTime.Unix()
 									}
diff --git a/callbacks/preload.go b/callbacks/preload.go
index 15669c8..112343f 100644
--- a/callbacks/preload.go
+++ b/callbacks/preload.go
@@ -3,6 +3,7 @@ package callbacks
 import (
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 
 	"gorm.io/gorm"
@@ -74,7 +75,7 @@ func embeddedValues(embeddedRelations *schema.Relationships) []string {
 	names := make([]string, 0, len(embeddedRelations.Relations)+len(embeddedRelations.EmbeddedRelations))
 	for _, relation := range embeddedRelations.Relations {
 		// skip first struct name
-		names = append(names, strings.Join(relation.Field.BindNames[1:], "."))
+		names = append(names, strings.Join(relation.Field.EmbeddedBindNames[1:], "."))
 	}
 	for _, relations := range embeddedRelations.EmbeddedRelations {
 		names = append(names, embeddedValues(relations)...)
@@ -82,27 +83,103 @@ func embeddedValues(embeddedRelations *schema.Relationships) []string {
 	return names
 }
 
-func preloadEmbedded(tx *gorm.DB, relationships *schema.Relationships, s *schema.Schema, preloads map[string][]interface{}, as []interface{}) error {
-	if relationships == nil {
-		return nil
+// preloadEntryPoint enters layer by layer. It will call real preload if it finds the right entry point.
+// If the current relationship is embedded or joined, current query will be ignored.
+//
+//nolint:cyclop
+func preloadEntryPoint(db *gorm.DB, joins []string, relationships *schema.Relationships, preloads map[string][]interface{}, associationsConds []interface{}) error {
+	preloadMap := parsePreloadMap(db.Statement.Schema, preloads)
+
+	// avoid random traversal of the map
+	preloadNames := make([]string, 0, len(preloadMap))
+	for key := range preloadMap {
+		preloadNames = append(preloadNames, key)
+	}
+	sort.Strings(preloadNames)
+
+	isJoined := func(name string) (joined bool, nestedJoins []string) {
+		for _, join := range joins {
+			if _, ok := relationships.Relations[join]; ok && name == join {
+				joined = true
+				continue
+			}
+			joinNames := strings.SplitN(join, ".", 2)
+			if len(joinNames) == 2 {
+				if _, ok := relationships.Relations[joinNames[0]]; ok && name == joinNames[0] {
+					joined = true
+					nestedJoins = append(nestedJoins, joinNames[1])
+				}
+			}
+		}
+		return joined, nestedJoins
 	}
-	preloadMap := parsePreloadMap(s, preloads)
-	for name := range preloadMap {
-		if embeddedRelations := relationships.EmbeddedRelations[name]; embeddedRelations != nil {
-			if err := preloadEmbedded(tx, embeddedRelations, s, preloadMap[name], as); err != nil {
+
+	for _, name := range preloadNames {
+		if relations := relationships.EmbeddedRelations[name]; relations != nil {
+			if err := preloadEntryPoint(db, joins, relations, preloadMap[name], associationsConds); err != nil {
 				return err
 			}
 		} else if rel := relationships.Relations[name]; rel != nil {
-			if err := preload(tx, rel, append(preloads[name], as), preloadMap[name]); err != nil {
-				return err
+			if joined, nestedJoins := isJoined(name); joined {
+				switch rv := db.Statement.ReflectValue; rv.Kind() {
+				case reflect.Slice, reflect.Array:
+					if rv.Len() > 0 {
+						reflectValue := rel.FieldSchema.MakeSlice().Elem()
+						reflectValue.SetLen(rv.Len())
+						for i := 0; i < rv.Len(); i++ {
+							frv := rel.Field.ReflectValueOf(db.Statement.Context, rv.Index(i))
+							if frv.Kind() != reflect.Ptr {
+								reflectValue.Index(i).Set(frv.Addr())
+							} else {
+								reflectValue.Index(i).Set(frv)
+							}
+						}
+
+						tx := preloadDB(db, reflectValue, reflectValue.Interface())
+						if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {
+							return err
+						}
+					}
+				case reflect.Struct:
+					reflectValue := rel.Field.ReflectValueOf(db.Statement.Context, rv)
+					tx := preloadDB(db, reflectValue, reflectValue.Interface())
+					if err := preloadEntryPoint(tx, nestedJoins, &tx.Statement.Schema.Relationships, preloadMap[name], associationsConds); err != nil {
+						return err
+					}
+				default:
+					return gorm.ErrInvalidData
+				}
+			} else {
+				tx := db.Table("").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks})
+				tx.Statement.ReflectValue = db.Statement.ReflectValue
+				tx.Statement.Unscoped = db.Statement.Unscoped
+				if err := preload(tx, rel, append(preloads[name], associationsConds...), preloadMap[name]); err != nil {
+					return err
+				}
 			}
 		} else {
-			return fmt.Errorf("%s: %w (embedded) for schema %s", name, gorm.ErrUnsupportedRelation, s.Name)
+			return fmt.Errorf("%s: %w for schema %s", name, gorm.ErrUnsupportedRelation, db.Statement.Schema.Name)
 		}
 	}
 	return nil
 }
 
+func preloadDB(db *gorm.DB, reflectValue reflect.Value, dest interface{}) *gorm.DB {
+	tx := db.Session(&gorm.Session{Context: db.Statement.Context, NewDB: true, SkipHooks: db.Statement.SkipHooks, Initialized: true})
+	db.Statement.Settings.Range(func(k, v interface{}) bool {
+		tx.Statement.Settings.Store(k, v)
+		return true
+	})
+
+	if err := tx.Statement.Parse(dest); err != nil {
+		tx.AddError(err)
+		return tx
+	}
+	tx.Statement.ReflectValue = reflectValue
+	tx.Statement.Unscoped = db.Statement.Unscoped
+	return tx
+}
+
 func preload(tx *gorm.DB, rel *schema.Relationship, conds []interface{}, preloads map[string][]interface{}) error {
 	var (
 		reflectValue     = tx.Statement.ReflectValue
diff --git a/callbacks/query.go b/callbacks/query.go
index e89dd19..2a82eab 100644
--- a/callbacks/query.go
+++ b/callbacks/query.go
@@ -3,7 +3,6 @@ package callbacks
 import (
 	"fmt"
 	"reflect"
-	"sort"
 	"strings"
 
 	"gorm.io/gorm"
@@ -254,7 +253,6 @@ func BuildQuerySQL(db *gorm.DB) {
 			}
 
 			db.Statement.AddClause(fromClause)
-			db.Statement.Joins = nil
 		} else {
 			db.Statement.AddClauseIfNotExists(clause.From{})
 		}
@@ -272,38 +270,23 @@ func Preload(db *gorm.DB) {
 			return
 		}
 
-		preloadMap := parsePreloadMap(db.Statement.Schema, db.Statement.Preloads)
-		preloadNames := make([]string, 0, len(preloadMap))
-		for key := range preloadMap {
-			preloadNames = append(preloadNames, key)
+		joins := make([]string, 0, len(db.Statement.Joins))
+		for _, join := range db.Statement.Joins {
+			joins = append(joins, join.Name)
 		}
-		sort.Strings(preloadNames)
 
-		preloadDB := db.Session(&gorm.Session{Context: db.Statement.Context, NewDB: true, SkipHooks: db.Statement.SkipHooks, Initialized: true})
-		db.Statement.Settings.Range(func(k, v interface{}) bool {
-			preloadDB.Statement.Settings.Store(k, v)
-			return true
-		})
-
-		if err := preloadDB.Statement.Parse(db.Statement.Dest); err != nil {
+		tx := preloadDB(db, db.Statement.ReflectValue, db.Statement.Dest)
+		if tx.Error != nil {
 			return
 		}
-		preloadDB.Statement.ReflectValue = db.Statement.ReflectValue
-		preloadDB.Statement.Unscoped = db.Statement.Unscoped
-
-		for _, name := range preloadNames {
-			if relations := preloadDB.Statement.Schema.Relationships.EmbeddedRelations[name]; relations != nil {
-				db.AddError(preloadEmbedded(preloadDB.Table("").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks}), relations, db.Statement.Schema, preloadMap[name], db.Statement.Preloads[clause.Associations]))
-			} else if rel := preloadDB.Statement.Schema.Relationships.Relations[name]; rel != nil {
-				db.AddError(preload(preloadDB.Table("").Session(&gorm.Session{Context: db.Statement.Context, SkipHooks: db.Statement.SkipHooks}), rel, append(db.Statement.Preloads[name], db.Statement.Preloads[clause.Associations]...), preloadMap[name]))
-			} else {
-				db.AddError(fmt.Errorf("%s: %w for schema %s", name, gorm.ErrUnsupportedRelation, db.Statement.Schema.Name))
-			}
-		}
+
+		db.AddError(preloadEntryPoint(tx, joins, &tx.Statement.Schema.Relationships, db.Statement.Preloads, db.Statement.Preloads[clause.Associations]))
 	}
 }
 
 func AfterQuery(db *gorm.DB) {
+	// clear the joins after query because preload need it
+	db.Statement.Joins = nil
 	if db.Error == nil && db.Statement.Schema != nil && !db.Statement.SkipHooks && db.Statement.Schema.AfterFind && db.RowsAffected > 0 {
 		callMethod(db, func(value interface{}, tx *gorm.DB) bool {
 			if i, ok := value.(AfterFindInterface); ok {
diff --git a/callbacks/update.go b/callbacks/update.go
index ff075dc..7cde7f6 100644
--- a/callbacks/update.go
+++ b/callbacks/update.go
@@ -234,7 +234,7 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
 						if field.AutoUpdateTime == schema.UnixNanosecond {
 							set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixNano()})
 						} else if field.AutoUpdateTime == schema.UnixMillisecond {
-							set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixNano() / 1e6})
+							set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.UnixMilli()})
 						} else if field.AutoUpdateTime == schema.UnixSecond {
 							set = append(set, clause.Assignment{Column: clause.Column{Name: field.DBName}, Value: now.Unix()})
 						} else {
@@ -268,7 +268,7 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
 								if field.AutoUpdateTime == schema.UnixNanosecond {
 									value = stmt.DB.NowFunc().UnixNano()
 								} else if field.AutoUpdateTime == schema.UnixMillisecond {
-									value = stmt.DB.NowFunc().UnixNano() / 1e6
+									value = stmt.DB.NowFunc().UnixMilli()
 								} else if field.AutoUpdateTime == schema.UnixSecond {
 									value = stmt.DB.NowFunc().Unix()
 								} else {
diff --git a/chainable_api.go b/chainable_api.go
index 3dc7256..3337060 100644
--- a/chainable_api.go
+++ b/chainable_api.go
@@ -367,33 +367,12 @@ func (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {
 }
 
 func (db *DB) executeScopes() (tx *DB) {
-	tx = db.getInstance()
 	scopes := db.Statement.scopes
-	if len(scopes) == 0 {
-		return tx
-	}
-	tx.Statement.scopes = nil
-
-	conditions := make([]clause.Interface, 0, 4)
-	if cs, ok := tx.Statement.Clauses["WHERE"]; ok && cs.Expression != nil {
-		conditions = append(conditions, cs.Expression.(clause.Interface))
-		cs.Expression = nil
-		tx.Statement.Clauses["WHERE"] = cs
-	}
-
+	db.Statement.scopes = nil
 	for _, scope := range scopes {
-		tx = scope(tx)
-		if cs, ok := tx.Statement.Clauses["WHERE"]; ok && cs.Expression != nil {
-			conditions = append(conditions, cs.Expression.(clause.Interface))
-			cs.Expression = nil
-			tx.Statement.Clauses["WHERE"] = cs
-		}
-	}
-
-	for _, condition := range conditions {
-		tx.Statement.AddClause(condition)
+		db = scope(db)
 	}
-	return tx
+	return db
 }
 
 // Preload preload associations with given conditions
@@ -450,6 +429,15 @@ func (db *DB) Assign(attrs ...interface{}) (tx *DB) {
 	return
 }
 
+// Unscoped disables the global scope of soft deletion in a query.
+// By default, GORM uses soft deletion, marking records as "deleted"
+// by setting a timestamp on a specific field (e.g., `deleted_at`).
+// Unscoped allows queries to include records marked as deleted,
+// overriding the soft deletion behavior.
+// Example:
+//    var users []User
+//    db.Unscoped().Find(&users)
+//    // Retrieves all users, including deleted ones.
 func (db *DB) Unscoped() (tx *DB) {
 	tx = db.getInstance()
 	tx.Statement.Unscoped = true
diff --git a/clause/limit.go b/clause/limit.go
index abda005..3edde43 100644
--- a/clause/limit.go
+++ b/clause/limit.go
@@ -1,7 +1,5 @@
 package clause
 
-import "strconv"
-
 // Limit limit clause
 type Limit struct {
 	Limit  *int
@@ -17,14 +15,14 @@ func (limit Limit) Name() string {
 func (limit Limit) Build(builder Builder) {
 	if limit.Limit != nil && *limit.Limit >= 0 {
 		builder.WriteString("LIMIT ")
-		builder.WriteString(strconv.Itoa(*limit.Limit))
+		builder.AddVar(builder, *limit.Limit)
 	}
 	if limit.Offset > 0 {
 		if limit.Limit != nil && *limit.Limit >= 0 {
 			builder.WriteByte(' ')
 		}
 		builder.WriteString("OFFSET ")
-		builder.WriteString(strconv.Itoa(limit.Offset))
+		builder.AddVar(builder, limit.Offset)
 	}
 }
 
diff --git a/clause/locking.go b/clause/locking.go
index 290aac9..2bc48ce 100644
--- a/clause/locking.go
+++ b/clause/locking.go
@@ -1,5 +1,12 @@
 package clause
 
+const (
+	LockingStrengthUpdate    = "UPDATE"
+	LockingStrengthShare     = "SHARE"
+	LockingOptionsSkipLocked = "SKIP LOCKED"
+	LockingOptionsNoWait     = "NOWAIT"
+)
+
 type Locking struct {
 	Strength string
 	Table    Table
diff --git a/clause/where.go b/clause/where.go
index a29401c..2c3c90f 100644
--- a/clause/where.go
+++ b/clause/where.go
@@ -21,6 +21,12 @@ func (where Where) Name() string {
 
 // Build build where clause
 func (where Where) Build(builder Builder) {
+	if len(where.Exprs) == 1 {
+		if andCondition, ok := where.Exprs[0].(AndConditions); ok {
+			where.Exprs = andCondition.Exprs
+		}
+	}
+
 	// Switch position if the first query expression is a single Or condition
 	for idx, expr := range where.Exprs {
 		if v, ok := expr.(OrConditions); !ok || len(v.Exprs) > 1 {
@@ -147,6 +153,11 @@ func Not(exprs ...Expression) Expression {
 	if len(exprs) == 0 {
 		return nil
 	}
+	if len(exprs) == 1 {
+		if andCondition, ok := exprs[0].(AndConditions); ok {
+			exprs = andCondition.Exprs
+		}
+	}
 	return NotConditions{Exprs: exprs}
 }
 
@@ -155,19 +166,63 @@ type NotConditions struct {
 }
 
 func (not NotConditions) Build(builder Builder) {
-	if len(not.Exprs) > 1 {
-		builder.WriteByte('(')
+	anyNegationBuilder := false
+	for _, c := range not.Exprs {
+		if _, ok := c.(NegationExpressionBuilder); ok {
+			anyNegationBuilder = true
+			break
+		}
 	}
 
-	for idx, c := range not.Exprs {
-		if idx > 0 {
-			builder.WriteString(AndWithSpace)
+	if anyNegationBuilder {
+		if len(not.Exprs) > 1 {
+			builder.WriteByte('(')
 		}
 
-		if negationBuilder, ok := c.(NegationExpressionBuilder); ok {
-			negationBuilder.NegationBuild(builder)
-		} else {
-			builder.WriteString("NOT ")
+		for idx, c := range not.Exprs {
+			if idx > 0 {
+				builder.WriteString(AndWithSpace)
+			}
+
+			if negationBuilder, ok := c.(NegationExpressionBuilder); ok {
+				negationBuilder.NegationBuild(builder)
+			} else {
+				builder.WriteString("NOT ")
+				e, wrapInParentheses := c.(Expr)
+				if wrapInParentheses {
+					sql := strings.ToUpper(e.SQL)
+					if wrapInParentheses = strings.Contains(sql, AndWithSpace) || strings.Contains(sql, OrWithSpace); wrapInParentheses {
+						builder.WriteByte('(')
+					}
+				}
+
+				c.Build(builder)
+
+				if wrapInParentheses {
+					builder.WriteByte(')')
+				}
+			}
+		}
+
+		if len(not.Exprs) > 1 {
+			builder.WriteByte(')')
+		}
+	} else {
+		builder.WriteString("NOT ")
+		if len(not.Exprs) > 1 {
+			builder.WriteByte('(')
+		}
+
+		for idx, c := range not.Exprs {
+			if idx > 0 {
+				switch c.(type) {
+				case OrConditions:
+					builder.WriteString(OrWithSpace)
+				default:
+					builder.WriteString(AndWithSpace)
+				}
+			}
+
 			e, wrapInParentheses := c.(Expr)
 			if wrapInParentheses {
 				sql := strings.ToUpper(e.SQL)
@@ -182,9 +237,9 @@ func (not NotConditions) Build(builder Builder) {
 				builder.WriteByte(')')
 			}
 		}
-	}
 
-	if len(not.Exprs) > 1 {
-		builder.WriteByte(')')
+		if len(not.Exprs) > 1 {
+			builder.WriteByte(')')
+		}
 	}
 }
diff --git a/errors.go b/errors.go
index cd76f1f..025f5d6 100644
--- a/errors.go
+++ b/errors.go
@@ -49,4 +49,6 @@ var (
 	ErrDuplicatedKey = errors.New("duplicated key not allowed")
 	// ErrForeignKeyViolated occurs when there is a foreign key constraint violation
 	ErrForeignKeyViolated = errors.New("violates foreign key constraint")
+	// ErrCheckConstraintViolated occurs when there is a check constraint violation
+	ErrCheckConstraintViolated = errors.New("violates check constraint")
 )
diff --git a/finisher_api.go b/finisher_api.go
index f80aa6c..f97571e 100644
--- a/finisher_api.go
+++ b/finisher_api.go
@@ -376,8 +376,12 @@ func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {
 	} else if len(db.Statement.assigns) > 0 {
 		exprs := tx.Statement.BuildCondition(db.Statement.assigns[0], db.Statement.assigns[1:]...)
 		assigns := map[string]interface{}{}
-		for _, expr := range exprs {
-			if eq, ok := expr.(clause.Eq); ok {
+		for i := 0; i < len(exprs); i++ {
+			expr := exprs[i]
+
+			if eq, ok := expr.(clause.AndConditions); ok {
+				exprs = append(exprs, eq.Exprs...)
+			} else if eq, ok := expr.(clause.Eq); ok {
 				switch column := eq.Column.(type) {
 				case string:
 					assigns[column] = eq.Value
diff --git a/logger/logger.go b/logger/logger.go
index aa0060b..253f032 100644
--- a/logger/logger.go
+++ b/logger/logger.go
@@ -69,7 +69,7 @@ type Interface interface {
 }
 
 var (
-	// Discard Discard logger will print any log to io.Discard
+	// Discard logger will print any log to io.Discard
 	Discard = New(log.New(io.Discard, "", log.LstdFlags), Config{})
 	// Default Default logger
 	Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{
@@ -78,7 +78,7 @@ var (
 		IgnoreRecordNotFoundError: false,
 		Colorful:                  true,
 	})
-	// Recorder Recorder logger records running SQL into a recorder instance
+	// Recorder logger records running SQL into a recorder instance
 	Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()}
 )
 
@@ -129,28 +129,30 @@ func (l *logger) LogMode(level LogLevel) Interface {
 }
 
 // Info print info
-func (l logger) Info(ctx context.Context, msg string, data ...interface{}) {
+func (l *logger) Info(ctx context.Context, msg string, data ...interface{}) {
 	if l.LogLevel >= Info {
 		l.Printf(l.infoStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 	}
 }
 
 // Warn print warn messages
-func (l logger) Warn(ctx context.Context, msg string, data ...interface{}) {
+func (l *logger) Warn(ctx context.Context, msg string, data ...interface{}) {
 	if l.LogLevel >= Warn {
 		l.Printf(l.warnStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 	}
 }
 
 // Error print error messages
-func (l logger) Error(ctx context.Context, msg string, data ...interface{}) {
+func (l *logger) Error(ctx context.Context, msg string, data ...interface{}) {
 	if l.LogLevel >= Error {
 		l.Printf(l.errStr+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
 	}
 }
 
 // Trace print sql message
-func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
+//
+//nolint:cyclop
+func (l *logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
 	if l.LogLevel <= Silent {
 		return
 	}
@@ -182,8 +184,8 @@ func (l logger) Trace(ctx context.Context, begin time.Time, fc func() (string, i
 	}
 }
 
-// Trace print sql message
-func (l logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {
+// ParamsFilter filter params
+func (l *logger) ParamsFilter(ctx context.Context, sql string, params ...interface{}) (string, []interface{}) {
 	if l.Config.ParameterizedQueries {
 		return sql, nil
 	}
@@ -198,8 +200,8 @@ type traceRecorder struct {
 	Err          error
 }
 
-// New new trace recorder
-func (l traceRecorder) New() *traceRecorder {
+// New trace recorder
+func (l *traceRecorder) New() *traceRecorder {
 	return &traceRecorder{Interface: l.Interface, BeginAt: time.Now()}
 }
 
diff --git a/logger/sql.go b/logger/sql.go
index 13e5d95..ad47879 100644
--- a/logger/sql.go
+++ b/logger/sql.go
@@ -34,6 +34,19 @@ var convertibleTypes = []reflect.Type{reflect.TypeOf(time.Time{}), reflect.TypeO
 // RegEx matches only numeric values
 var numericPlaceholderRe = regexp.MustCompile(`\$\d+\$`)
 
+func isNumeric(k reflect.Kind) bool {
+	switch k {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return true
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		return true
+	case reflect.Float32, reflect.Float64:
+		return true
+	default:
+		return false
+	}
+}
+
 // ExplainSQL generate SQL string with given parameters, the generated SQL is expected to be used in logger, execute it might introduce a SQL injection vulnerability
 func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, avars ...interface{}) string {
 	var (
@@ -79,17 +92,17 @@ func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, a
 			case reflect.Bool:
 				vars[idx] = fmt.Sprintf("%t", reflectValue.Interface())
 			case reflect.String:
-				vars[idx] = escaper + strings.ReplaceAll(fmt.Sprintf("%v", v), escaper, "\\"+escaper) + escaper
+				vars[idx] = escaper + strings.ReplaceAll(fmt.Sprintf("%v", v), escaper, escaper+escaper) + escaper
 			default:
 				if v != nil && reflectValue.IsValid() && ((reflectValue.Kind() == reflect.Ptr && !reflectValue.IsNil()) || reflectValue.Kind() != reflect.Ptr) {
-					vars[idx] = escaper + strings.ReplaceAll(fmt.Sprintf("%v", v), escaper, "\\"+escaper) + escaper
+					vars[idx] = escaper + strings.ReplaceAll(fmt.Sprintf("%v", v), escaper, escaper+escaper) + escaper
 				} else {
 					vars[idx] = nullStr
 				}
 			}
 		case []byte:
 			if s := string(v); isPrintable(s) {
-				vars[idx] = escaper + strings.ReplaceAll(s, escaper, "\\"+escaper) + escaper
+				vars[idx] = escaper + strings.ReplaceAll(s, escaper, escaper+escaper) + escaper
 			} else {
 				vars[idx] = escaper + "<binary>" + escaper
 			}
@@ -100,7 +113,7 @@ func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, a
 		case float64:
 			vars[idx] = strconv.FormatFloat(v, 'f', -1, 64)
 		case string:
-			vars[idx] = escaper + strings.ReplaceAll(v, escaper, "\\"+escaper) + escaper
+			vars[idx] = escaper + strings.ReplaceAll(v, escaper, escaper+escaper) + escaper
 		default:
 			rv := reflect.ValueOf(v)
 			if v == nil || !rv.IsValid() || rv.Kind() == reflect.Ptr && rv.IsNil() {
@@ -110,6 +123,12 @@ func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, a
 				convertParams(v, idx)
 			} else if rv.Kind() == reflect.Ptr && !rv.IsZero() {
 				convertParams(reflect.Indirect(rv).Interface(), idx)
+			} else if isNumeric(rv.Kind()) {
+				if rv.CanInt() || rv.CanUint() {
+					vars[idx] = fmt.Sprintf("%d", rv.Interface())
+				} else {
+					vars[idx] = fmt.Sprintf("%.6f", rv.Interface())
+				}
 			} else {
 				for _, t := range convertibleTypes {
 					if rv.Type().ConvertibleTo(t) {
@@ -117,7 +136,7 @@ func ExplainSQL(sql string, numericPlaceholder *regexp.Regexp, escaper string, a
 						return
 					}
 				}
-				vars[idx] = escaper + strings.ReplaceAll(fmt.Sprint(v), escaper, "\\"+escaper) + escaper
+				vars[idx] = escaper + strings.ReplaceAll(fmt.Sprint(v), escaper, escaper+escaper) + escaper
 			}
 		}
 	}
diff --git a/migrator/migrator.go b/migrator/migrator.go
index 64a5a4b..189a141 100644
--- a/migrator/migrator.go
+++ b/migrator/migrator.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"reflect"
 	"regexp"
+	"strconv"
 	"strings"
 	"time"
 
@@ -93,10 +94,6 @@ func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {
 		expr.SQL += " NOT NULL"
 	}
 
-	if field.Unique {
-		expr.SQL += " UNIQUE"
-	}
-
 	if field.HasDefaultValue && (field.DefaultValueInterface != nil || field.DefaultValue != "") {
 		if field.DefaultValueInterface != nil {
 			defaultStmt := &gorm.Statement{Vars: []interface{}{field.DefaultValueInterface}}
@@ -110,21 +107,31 @@ func (m Migrator) FullDataTypeOf(field *schema.Field) (expr clause.Expr) {
 	return
 }
 
+func (m Migrator) GetQueryAndExecTx() (queryTx, execTx *gorm.DB) {
+	queryTx = m.DB.Session(&gorm.Session{})
+	execTx = queryTx
+	if m.DB.DryRun {
+		queryTx.DryRun = false
+		execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
+	}
+	return queryTx, execTx
+}
+
 // AutoMigrate auto migrate values
 func (m Migrator) AutoMigrate(values ...interface{}) error {
 	for _, value := range m.ReorderModels(values, true) {
-		queryTx := m.DB.Session(&gorm.Session{})
-		execTx := queryTx
-		if m.DB.DryRun {
-			queryTx.DryRun = false
-			execTx = m.DB.Session(&gorm.Session{Logger: &printSQLLogger{Interface: m.DB.Logger}})
-		}
+		queryTx, execTx := m.GetQueryAndExecTx()
 		if !queryTx.Migrator().HasTable(value) {
 			if err := execTx.Migrator().CreateTable(value); err != nil {
 				return err
 			}
 		} else {
 			if err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
+
+				if stmt.Schema == nil {
+					return errors.New("failed to get schema")
+				}
+
 				columnTypes, err := queryTx.Migrator().ColumnTypes(value)
 				if err != nil {
 					return err
@@ -209,6 +216,11 @@ func (m Migrator) CreateTable(values ...interface{}) error {
 	for _, value := range m.ReorderModels(values, false) {
 		tx := m.DB.Session(&gorm.Session{})
 		if err := m.RunWithValue(value, func(stmt *gorm.Statement) (err error) {
+
+			if stmt.Schema == nil {
+				return errors.New("failed to get schema")
+			}
+
 			var (
 				createTableSQL          = "CREATE TABLE ? ("
 				values                  = []interface{}{m.CurrentTable(stmt)}
@@ -268,7 +280,7 @@ func (m Migrator) CreateTable(values ...interface{}) error {
 					}
 					if constraint := rel.ParseConstraint(); constraint != nil {
 						if constraint.Schema == stmt.Schema {
-							sql, vars := buildConstraint(constraint)
+							sql, vars := constraint.Build()
 							createTableSQL += sql + ","
 							values = append(values, vars...)
 						}
@@ -276,6 +288,11 @@ func (m Migrator) CreateTable(values ...interface{}) error {
 				}
 			}
 
+			for _, uni := range stmt.Schema.ParseUniqueConstraints() {
+				createTableSQL += "CONSTRAINT ? UNIQUE (?),"
+				values = append(values, clause.Column{Name: uni.Name}, clause.Expr{SQL: stmt.Quote(uni.Field.DBName)})
+			}
+
 			for _, chk := range stmt.Schema.ParseCheckConstraints() {
 				createTableSQL += "CONSTRAINT ? CHECK (?),"
 				values = append(values, clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint})
@@ -356,6 +373,9 @@ func (m Migrator) RenameTable(oldName, newName interface{}) error {
 func (m Migrator) AddColumn(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
 		// avoid using the same name field
+		if stmt.Schema == nil {
+			return errors.New("failed to get schema")
+		}
 		f := stmt.Schema.LookUpField(name)
 		if f == nil {
 			return fmt.Errorf("failed to look up field with name: %s", name)
@@ -375,8 +395,10 @@ func (m Migrator) AddColumn(value interface{}, name string) error {
 // DropColumn drop value's `name` column
 func (m Migrator) DropColumn(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if field := stmt.Schema.LookUpField(name); field != nil {
-			name = field.DBName
+		if stmt.Schema != nil {
+			if field := stmt.Schema.LookUpField(name); field != nil {
+				name = field.DBName
+			}
 		}
 
 		return m.DB.Exec(
@@ -388,13 +410,15 @@ func (m Migrator) DropColumn(value interface{}, name string) error {
 // AlterColumn alter value's `field` column' type based on schema definition
 func (m Migrator) AlterColumn(value interface{}, field string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if field := stmt.Schema.LookUpField(field); field != nil {
-			fileType := m.FullDataTypeOf(field)
-			return m.DB.Exec(
-				"ALTER TABLE ? ALTER COLUMN ? TYPE ?",
-				m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,
-			).Error
+		if stmt.Schema != nil {
+			if field := stmt.Schema.LookUpField(field); field != nil {
+				fileType := m.FullDataTypeOf(field)
+				return m.DB.Exec(
+					"ALTER TABLE ? ALTER COLUMN ? TYPE ?",
+					m.CurrentTable(stmt), clause.Column{Name: field.DBName}, fileType,
+				).Error
 
+			}
 		}
 		return fmt.Errorf("failed to look up field with name: %s", field)
 	})
@@ -406,8 +430,10 @@ func (m Migrator) HasColumn(value interface{}, field string) bool {
 	m.RunWithValue(value, func(stmt *gorm.Statement) error {
 		currentDatabase := m.DB.Migrator().CurrentDatabase()
 		name := field
-		if field := stmt.Schema.LookUpField(field); field != nil {
-			name = field.DBName
+		if stmt.Schema != nil {
+			if field := stmt.Schema.LookUpField(field); field != nil {
+				name = field.DBName
+			}
 		}
 
 		return m.DB.Raw(
@@ -422,12 +448,14 @@ func (m Migrator) HasColumn(value interface{}, field string) bool {
 // RenameColumn rename value's field name from oldName to newName
 func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if field := stmt.Schema.LookUpField(oldName); field != nil {
-			oldName = field.DBName
-		}
+		if stmt.Schema != nil {
+			if field := stmt.Schema.LookUpField(oldName); field != nil {
+				oldName = field.DBName
+			}
 
-		if field := stmt.Schema.LookUpField(newName); field != nil {
-			newName = field.DBName
+			if field := stmt.Schema.LookUpField(newName); field != nil {
+				newName = field.DBName
+			}
 		}
 
 		return m.DB.Exec(
@@ -439,6 +467,10 @@ func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error
 
 // MigrateColumn migrate column
 func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnType gorm.ColumnType) error {
+	if field.IgnoreMigration {
+		return nil
+	}
+
 	// found, smart migrate
 	fullDataType := strings.TrimSpace(strings.ToLower(m.DB.Migrator().FullDataTypeOf(field).SQL))
 	realDataType := strings.ToLower(columnType.DatabaseTypeName())
@@ -498,14 +530,6 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
 		}
 	}
 
-	// check unique
-	if unique, ok := columnType.Unique(); ok && unique != field.Unique {
-		// not primary key
-		if !field.PrimaryKey {
-			alterColumn = true
-		}
-	}
-
 	// check default value
 	if !field.PrimaryKey {
 		currentDefaultNotNull := field.HasDefaultValue && (field.DefaultValueInterface != nil || !strings.EqualFold(field.DefaultValue, "NULL"))
@@ -516,12 +540,18 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
 		} else if !dvNotNull && currentDefaultNotNull {
 			// null -> default value
 			alterColumn = true
-		} else if (field.GORMDataType != schema.Time && dv != field.DefaultValue) ||
-			(field.GORMDataType == schema.Time && !strings.EqualFold(strings.TrimSuffix(dv, "()"), strings.TrimSuffix(field.DefaultValue, "()"))) {
-			// default value not equal
-			// not both null
-			if currentDefaultNotNull || dvNotNull {
-				alterColumn = true
+		} else if currentDefaultNotNull || dvNotNull {
+			switch field.GORMDataType {
+			case schema.Time:
+				if !strings.EqualFold(strings.TrimSuffix(dv, "()"), strings.TrimSuffix(field.DefaultValue, "()")) {
+					alterColumn = true
+				}
+			case schema.Bool:
+				v1, _ := strconv.ParseBool(dv)
+				v2, _ := strconv.ParseBool(field.DefaultValue)
+				alterColumn = v1 != v2
+			default:
+				alterColumn = dv != field.DefaultValue
 			}
 		}
 	}
@@ -534,8 +564,14 @@ func (m Migrator) MigrateColumn(value interface{}, field *schema.Field, columnTy
 		}
 	}
 
-	if alterColumn && !field.IgnoreMigration {
-		return m.DB.Migrator().AlterColumn(value, field.DBName)
+	if alterColumn {
+		if err := m.DB.Migrator().AlterColumn(value, field.DBName); err != nil {
+			return err
+		}
+	}
+
+	if err := m.DB.Migrator().MigrateColumnUnique(value, field, columnType); err != nil {
+		return err
 	}
 
 	return nil
@@ -630,37 +666,36 @@ func (m Migrator) DropView(name string) error {
 	return m.DB.Exec("DROP VIEW IF EXISTS ?", clause.Table{Name: name}).Error
 }
 
-func buildConstraint(constraint *schema.Constraint) (sql string, results []interface{}) {
-	sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
-	if constraint.OnDelete != "" {
-		sql += " ON DELETE " + constraint.OnDelete
-	}
-
-	if constraint.OnUpdate != "" {
-		sql += " ON UPDATE " + constraint.OnUpdate
-	}
-
-	var foreignKeys, references []interface{}
-	for _, field := range constraint.ForeignKeys {
-		foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
-	}
-
-	for _, field := range constraint.References {
-		references = append(references, clause.Column{Name: field.DBName})
+// GuessConstraintAndTable guess statement's constraint and it's table based on name
+//
+// Deprecated: use GuessConstraintInterfaceAndTable instead.
+func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (*schema.Constraint, *schema.CheckConstraint, string) {
+	constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
+	switch c := constraint.(type) {
+	case *schema.Constraint:
+		return c, nil, table
+	case *schema.CheckConstraint:
+		return nil, c, table
+	default:
+		return nil, nil, table
 	}
-	results = append(results, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
-	return
 }
 
-// GuessConstraintAndTable guess statement's constraint and it's table based on name
-func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_ *schema.Constraint, _ *schema.Check, table string) {
+// GuessConstraintInterfaceAndTable guess statement's constraint and it's table based on name
+// nolint:cyclop
+func (m Migrator) GuessConstraintInterfaceAndTable(stmt *gorm.Statement, name string) (_ schema.ConstraintInterface, table string) {
 	if stmt.Schema == nil {
-		return nil, nil, stmt.Table
+		return nil, stmt.Table
 	}
 
 	checkConstraints := stmt.Schema.ParseCheckConstraints()
 	if chk, ok := checkConstraints[name]; ok {
-		return nil, &chk, stmt.Table
+		return &chk, stmt.Table
+	}
+
+	uniqueConstraints := stmt.Schema.ParseUniqueConstraints()
+	if uni, ok := uniqueConstraints[name]; ok {
+		return &uni, stmt.Table
 	}
 
 	getTable := func(rel *schema.Relationship) string {
@@ -675,7 +710,7 @@ func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_
 
 	for _, rel := range stmt.Schema.Relationships.Relations {
 		if constraint := rel.ParseConstraint(); constraint != nil && constraint.Name == name {
-			return constraint, nil, getTable(rel)
+			return constraint, getTable(rel)
 		}
 	}
 
@@ -683,40 +718,39 @@ func (m Migrator) GuessConstraintAndTable(stmt *gorm.Statement, name string) (_
 		for k := range checkConstraints {
 			if checkConstraints[k].Field == field {
 				v := checkConstraints[k]
-				return nil, &v, stmt.Table
+				return &v, stmt.Table
+			}
+		}
+
+		for k := range uniqueConstraints {
+			if uniqueConstraints[k].Field == field {
+				v := uniqueConstraints[k]
+				return &v, stmt.Table
 			}
 		}
 
 		for _, rel := range stmt.Schema.Relationships.Relations {
 			if constraint := rel.ParseConstraint(); constraint != nil && rel.Field == field {
-				return constraint, nil, getTable(rel)
+				return constraint, getTable(rel)
 			}
 		}
 	}
 
-	return nil, nil, stmt.Schema.Table
+	return nil, stmt.Schema.Table
 }
 
 // CreateConstraint create constraint
 func (m Migrator) CreateConstraint(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
-		if chk != nil {
-			return m.DB.Exec(
-				"ALTER TABLE ? ADD CONSTRAINT ? CHECK (?)",
-				m.CurrentTable(stmt), clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint},
-			).Error
-		}
-
+		constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
 		if constraint != nil {
 			vars := []interface{}{clause.Table{Name: table}}
 			if stmt.TableExpr != nil {
 				vars[0] = stmt.TableExpr
 			}
-			sql, values := buildConstraint(constraint)
+			sql, values := constraint.Build()
 			return m.DB.Exec("ALTER TABLE ? ADD "+sql, append(vars, values...)...).Error
 		}
-
 		return nil
 	})
 }
@@ -724,11 +758,9 @@ func (m Migrator) CreateConstraint(value interface{}, name string) error {
 // DropConstraint drop constraint
 func (m Migrator) DropConstraint(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
+		constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
 		if constraint != nil {
-			name = constraint.Name
-		} else if chk != nil {
-			name = chk.Name
+			name = constraint.GetName()
 		}
 		return m.DB.Exec("ALTER TABLE ? DROP CONSTRAINT ?", clause.Table{Name: table}, clause.Column{Name: name}).Error
 	})
@@ -739,11 +771,9 @@ func (m Migrator) HasConstraint(value interface{}, name string) bool {
 	var count int64
 	m.RunWithValue(value, func(stmt *gorm.Statement) error {
 		currentDatabase := m.DB.Migrator().CurrentDatabase()
-		constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
+		constraint, table := m.GuessConstraintInterfaceAndTable(stmt, name)
 		if constraint != nil {
-			name = constraint.Name
-		} else if chk != nil {
-			name = chk.Name
+			name = constraint.GetName()
 		}
 
 		return m.DB.Raw(
@@ -785,6 +815,9 @@ type BuildIndexOptionsInterface interface {
 // CreateIndex create index `name`
 func (m Migrator) CreateIndex(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
+		if stmt.Schema == nil {
+			return errors.New("failed to get schema")
+		}
 		if idx := stmt.Schema.LookIndex(name); idx != nil {
 			opts := m.DB.Migrator().(BuildIndexOptionsInterface).BuildIndexOptions(idx.Fields, stmt)
 			values := []interface{}{clause.Column{Name: idx.Name}, m.CurrentTable(stmt), opts}
@@ -817,8 +850,10 @@ func (m Migrator) CreateIndex(value interface{}, name string) error {
 // DropIndex drop index `name`
 func (m Migrator) DropIndex(value interface{}, name string) error {
 	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
-		if idx := stmt.Schema.LookIndex(name); idx != nil {
-			name = idx.Name
+		if stmt.Schema != nil {
+			if idx := stmt.Schema.LookIndex(name); idx != nil {
+				name = idx.Name
+			}
 		}
 
 		return m.DB.Exec("DROP INDEX ? ON ?", clause.Column{Name: name}, m.CurrentTable(stmt)).Error
@@ -830,8 +865,10 @@ func (m Migrator) HasIndex(value interface{}, name string) bool {
 	var count int64
 	m.RunWithValue(value, func(stmt *gorm.Statement) error {
 		currentDatabase := m.DB.Migrator().CurrentDatabase()
-		if idx := stmt.Schema.LookIndex(name); idx != nil {
-			name = idx.Name
+		if stmt.Schema != nil {
+			if idx := stmt.Schema.LookIndex(name); idx != nil {
+				name = idx.Name
+			}
 		}
 
 		return m.DB.Raw(
diff --git a/prepare_stmt.go b/prepare_stmt.go
index aa94462..4d53388 100644
--- a/prepare_stmt.go
+++ b/prepare_stmt.go
@@ -3,6 +3,8 @@ package gorm
 import (
 	"context"
 	"database/sql"
+	"database/sql/driver"
+	"errors"
 	"reflect"
 	"sync"
 )
@@ -147,7 +149,7 @@ func (db *PreparedStmtDB) ExecContext(ctx context.Context, query string, args ..
 	stmt, err := db.prepare(ctx, db.ConnPool, false, query)
 	if err == nil {
 		result, err = stmt.ExecContext(ctx, args...)
-		if err != nil {
+		if errors.Is(err, driver.ErrBadConn) {
 			db.Mux.Lock()
 			defer db.Mux.Unlock()
 			go stmt.Close()
@@ -161,7 +163,7 @@ func (db *PreparedStmtDB) QueryContext(ctx context.Context, query string, args .
 	stmt, err := db.prepare(ctx, db.ConnPool, false, query)
 	if err == nil {
 		rows, err = stmt.QueryContext(ctx, args...)
-		if err != nil {
+		if errors.Is(err, driver.ErrBadConn) {
 			db.Mux.Lock()
 			defer db.Mux.Unlock()
 
@@ -180,6 +182,14 @@ func (db *PreparedStmtDB) QueryRowContext(ctx context.Context, query string, arg
 	return &sql.Row{}
 }
 
+func (db *PreparedStmtDB) Ping() error {
+	conn, err := db.GetDBConn()
+	if err != nil {
+		return err
+	}
+	return conn.Ping()
+}
+
 type PreparedStmtTX struct {
 	Tx
 	PreparedStmtDB *PreparedStmtDB
@@ -207,7 +217,7 @@ func (tx *PreparedStmtTX) ExecContext(ctx context.Context, query string, args ..
 	stmt, err := tx.PreparedStmtDB.prepare(ctx, tx.Tx, true, query)
 	if err == nil {
 		result, err = tx.Tx.StmtContext(ctx, stmt.Stmt).ExecContext(ctx, args...)
-		if err != nil {
+		if errors.Is(err, driver.ErrBadConn) {
 			tx.PreparedStmtDB.Mux.Lock()
 			defer tx.PreparedStmtDB.Mux.Unlock()
 
@@ -222,7 +232,7 @@ func (tx *PreparedStmtTX) QueryContext(ctx context.Context, query string, args .
 	stmt, err := tx.PreparedStmtDB.prepare(ctx, tx.Tx, true, query)
 	if err == nil {
 		rows, err = tx.Tx.StmtContext(ctx, stmt.Stmt).QueryContext(ctx, args...)
-		if err != nil {
+		if errors.Is(err, driver.ErrBadConn) {
 			tx.PreparedStmtDB.Mux.Lock()
 			defer tx.PreparedStmtDB.Mux.Unlock()
 
@@ -240,3 +250,11 @@ func (tx *PreparedStmtTX) QueryRowContext(ctx context.Context, query string, arg
 	}
 	return &sql.Row{}
 }
+
+func (tx *PreparedStmtTX) Ping() error {
+	conn, err := tx.GetDBConn()
+	if err != nil {
+		return err
+	}
+	return conn.Ping()
+}
diff --git a/scan.go b/scan.go
index 736db4d..e95e6d3 100644
--- a/scan.go
+++ b/scan.go
@@ -257,9 +257,11 @@ func Scan(rows Rows, db *DB, mode ScanMode) {
 								continue
 							}
 						}
-						values[idx] = &sql.RawBytes{}
+						var val interface{}
+						values[idx] = &val
 					} else {
-						values[idx] = &sql.RawBytes{}
+						var val interface{}
+						values[idx] = &val
 					}
 				}
 			}
@@ -274,12 +276,16 @@ func Scan(rows Rows, db *DB, mode ScanMode) {
 
 			if !update || reflectValue.Len() == 0 {
 				update = false
-				// if the slice cap is externally initialized, the externally initialized slice is directly used here
-				if reflectValue.Cap() == 0 {
-					db.Statement.ReflectValue.Set(reflect.MakeSlice(reflectValue.Type(), 0, 20))
-				} else if !isArrayKind {
-					reflectValue.SetLen(0)
-					db.Statement.ReflectValue.Set(reflectValue)
+				if isArrayKind {
+					db.Statement.ReflectValue.Set(reflect.Zero(reflectValue.Type()))
+				} else {
+					// if the slice cap is externally initialized, the externally initialized slice is directly used here
+					if reflectValue.Cap() == 0 {
+						db.Statement.ReflectValue.Set(reflect.MakeSlice(reflectValue.Type(), 0, 20))
+					} else {
+						reflectValue.SetLen(0)
+						db.Statement.ReflectValue.Set(reflectValue)
+					}
 				}
 			}
 
diff --git a/schema/check.go b/schema/check.go
deleted file mode 100644
index 89e732d..0000000
--- a/schema/check.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package schema
-
-import (
-	"regexp"
-	"strings"
-)
-
-// reg match english letters and midline
-var regEnLetterAndMidline = regexp.MustCompile("^[A-Za-z-_]+$")
-
-type Check struct {
-	Name       string
-	Constraint string // length(phone) >= 10
-	*Field
-}
-
-// ParseCheckConstraints parse schema check constraints
-func (schema *Schema) ParseCheckConstraints() map[string]Check {
-	checks := map[string]Check{}
-	for _, field := range schema.FieldsByDBName {
-		if chk := field.TagSettings["CHECK"]; chk != "" {
-			names := strings.Split(chk, ",")
-			if len(names) > 1 && regEnLetterAndMidline.MatchString(names[0]) {
-				checks[names[0]] = Check{Name: names[0], Constraint: strings.Join(names[1:], ","), Field: field}
-			} else {
-				if names[0] == "" {
-					chk = strings.Join(names[1:], ",")
-				}
-				name := schema.namer.CheckerName(schema.Table, field.DBName)
-				checks[name] = Check{Name: name, Constraint: chk, Field: field}
-			}
-		}
-	}
-	return checks
-}
diff --git a/schema/constraint.go b/schema/constraint.go
new file mode 100644
index 0000000..80a743a
--- /dev/null
+++ b/schema/constraint.go
@@ -0,0 +1,66 @@
+package schema
+
+import (
+	"regexp"
+	"strings"
+
+	"gorm.io/gorm/clause"
+)
+
+// reg match english letters and midline
+var regEnLetterAndMidline = regexp.MustCompile(`^[\w-]+$`)
+
+type CheckConstraint struct {
+	Name       string
+	Constraint string // length(phone) >= 10
+	*Field
+}
+
+func (chk *CheckConstraint) GetName() string { return chk.Name }
+
+func (chk *CheckConstraint) Build() (sql string, vars []interface{}) {
+	return "CONSTRAINT ? CHECK (?)", []interface{}{clause.Column{Name: chk.Name}, clause.Expr{SQL: chk.Constraint}}
+}
+
+// ParseCheckConstraints parse schema check constraints
+func (schema *Schema) ParseCheckConstraints() map[string]CheckConstraint {
+	checks := map[string]CheckConstraint{}
+	for _, field := range schema.FieldsByDBName {
+		if chk := field.TagSettings["CHECK"]; chk != "" {
+			names := strings.Split(chk, ",")
+			if len(names) > 1 && regEnLetterAndMidline.MatchString(names[0]) {
+				checks[names[0]] = CheckConstraint{Name: names[0], Constraint: strings.Join(names[1:], ","), Field: field}
+			} else {
+				if names[0] == "" {
+					chk = strings.Join(names[1:], ",")
+				}
+				name := schema.namer.CheckerName(schema.Table, field.DBName)
+				checks[name] = CheckConstraint{Name: name, Constraint: chk, Field: field}
+			}
+		}
+	}
+	return checks
+}
+
+type UniqueConstraint struct {
+	Name  string
+	Field *Field
+}
+
+func (uni *UniqueConstraint) GetName() string { return uni.Name }
+
+func (uni *UniqueConstraint) Build() (sql string, vars []interface{}) {
+	return "CONSTRAINT ? UNIQUE (?)", []interface{}{clause.Column{Name: uni.Name}, clause.Column{Name: uni.Field.DBName}}
+}
+
+// ParseUniqueConstraints parse schema unique constraints
+func (schema *Schema) ParseUniqueConstraints() map[string]UniqueConstraint {
+	uniques := make(map[string]UniqueConstraint)
+	for _, field := range schema.Fields {
+		if field.Unique {
+			name := schema.namer.UniqueName(schema.Table, field.DBName)
+			uniques[name] = UniqueConstraint{Name: name, Field: field}
+		}
+	}
+	return uniques
+}
diff --git a/schema/field.go b/schema/field.go
index dd08e05..a16c98a 100644
--- a/schema/field.go
+++ b/schema/field.go
@@ -49,11 +49,14 @@ const (
 	Bytes  DataType = "bytes"
 )
 
+const DefaultAutoIncrementIncrement int64 = 1
+
 // Field is the representation of model schema's field
 type Field struct {
 	Name                   string
 	DBName                 string
 	BindNames              []string
+	EmbeddedBindNames      []string
 	DataType               DataType
 	GORMDataType           DataType
 	PrimaryKey             bool
@@ -87,6 +90,12 @@ type Field struct {
 	Set                    func(context.Context, reflect.Value, interface{}) error
 	Serializer             SerializerInterface
 	NewValuePool           FieldNewValuePool
+
+	// In some db (e.g. MySQL), Unique and UniqueIndex are indistinguishable.
+	// When a column has a (not Mul) UniqueIndex, Migrator always reports its gorm.ColumnType is Unique.
+	// It causes field unnecessarily migration.
+	// Therefore, we need to record the UniqueIndex on this column (exclude Mul UniqueIndex) for MigrateColumnUnique.
+	UniqueIndex string
 }
 
 func (field *Field) BindName() string {
@@ -104,6 +113,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 		Name:                   fieldStruct.Name,
 		DBName:                 tagSetting["COLUMN"],
 		BindNames:              []string{fieldStruct.Name},
+		EmbeddedBindNames:      []string{fieldStruct.Name},
 		FieldType:              fieldStruct.Type,
 		IndirectFieldType:      fieldStruct.Type,
 		StructField:            fieldStruct,
@@ -119,7 +129,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 		NotNull:                utils.CheckTruth(tagSetting["NOT NULL"], tagSetting["NOTNULL"]),
 		Unique:                 utils.CheckTruth(tagSetting["UNIQUE"]),
 		Comment:                tagSetting["COMMENT"],
-		AutoIncrementIncrement: 1,
+		AutoIncrementIncrement: DefaultAutoIncrementIncrement,
 	}
 
 	for field.IndirectFieldType.Kind() == reflect.Ptr {
@@ -395,6 +405,9 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 				ef.Schema = schema
 				ef.OwnerSchema = field.EmbeddedSchema
 				ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)
+				if _, ok := field.TagSettings["EMBEDDED"]; ok || !fieldStruct.Anonymous {
+					ef.EmbeddedBindNames = append([]string{fieldStruct.Name}, ef.EmbeddedBindNames...)
+				}
 				// index is negative means is pointer
 				if field.FieldType.Kind() == reflect.Struct {
 					ef.StructField.Index = append([]int{fieldStruct.Index[0]}, ef.StructField.Index...)
@@ -656,7 +669,7 @@ func (field *Field) setupValuerAndSetter() {
 				if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond {
 					field.ReflectValueOf(ctx, value).SetInt(data.UnixNano())
 				} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond {
-					field.ReflectValueOf(ctx, value).SetInt(data.UnixNano() / 1e6)
+					field.ReflectValueOf(ctx, value).SetInt(data.UnixMilli())
 				} else {
 					field.ReflectValueOf(ctx, value).SetInt(data.Unix())
 				}
@@ -665,7 +678,7 @@ func (field *Field) setupValuerAndSetter() {
 					if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond {
 						field.ReflectValueOf(ctx, value).SetInt(data.UnixNano())
 					} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond {
-						field.ReflectValueOf(ctx, value).SetInt(data.UnixNano() / 1e6)
+						field.ReflectValueOf(ctx, value).SetInt(data.UnixMilli())
 					} else {
 						field.ReflectValueOf(ctx, value).SetInt(data.Unix())
 					}
@@ -730,7 +743,7 @@ func (field *Field) setupValuerAndSetter() {
 				if field.AutoCreateTime == UnixNanosecond || field.AutoUpdateTime == UnixNanosecond {
 					field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano()))
 				} else if field.AutoCreateTime == UnixMillisecond || field.AutoUpdateTime == UnixMillisecond {
-					field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixNano() / 1e6))
+					field.ReflectValueOf(ctx, value).SetUint(uint64(data.UnixMilli()))
 				} else {
 					field.ReflectValueOf(ctx, value).SetUint(uint64(data.Unix()))
 				}
diff --git a/schema/index.go b/schema/index.go
index f5ac5dd..f4f3675 100644
--- a/schema/index.go
+++ b/schema/index.go
@@ -13,8 +13,8 @@ type Index struct {
 	Type    string // btree, hash, gist, spgist, gin, and brin
 	Where   string
 	Comment string
-	Option  string // WITH PARSER parser_name
-	Fields  []IndexOption
+	Option  string        // WITH PARSER parser_name
+	Fields  []IndexOption // Note: IndexOption's Field maybe the same
 }
 
 type IndexOption struct {
@@ -67,7 +67,7 @@ func (schema *Schema) ParseIndexes() map[string]Index {
 	}
 	for _, index := range indexes {
 		if index.Class == "UNIQUE" && len(index.Fields) == 1 {
-			index.Fields[0].Field.Unique = true
+			index.Fields[0].Field.UniqueIndex = index.Name
 		}
 	}
 	return indexes
diff --git a/schema/interfaces.go b/schema/interfaces.go
index a75a33c..306d4f4 100644
--- a/schema/interfaces.go
+++ b/schema/interfaces.go
@@ -4,6 +4,12 @@ import (
 	"gorm.io/gorm/clause"
 )
 
+// ConstraintInterface database constraint interface
+type ConstraintInterface interface {
+	GetName() string
+	Build() (sql string, vars []interface{})
+}
+
 // GormDataTypeInterface gorm data type interface
 type GormDataTypeInterface interface {
 	GormDataType() string
diff --git a/schema/relationship.go b/schema/relationship.go
index e03dcc5..c11918a 100644
--- a/schema/relationship.go
+++ b/schema/relationship.go
@@ -76,8 +76,8 @@ func (schema *Schema) parseRelation(field *Field) *Relationship {
 		return nil
 	}
 
-	if polymorphic := field.TagSettings["POLYMORPHIC"]; polymorphic != "" {
-		schema.buildPolymorphicRelation(relation, field, polymorphic)
+	if hasPolymorphicRelation(field.TagSettings) {
+		schema.buildPolymorphicRelation(relation, field)
 	} else if many2many := field.TagSettings["MANY2MANY"]; many2many != "" {
 		schema.buildMany2ManyRelation(relation, field, many2many)
 	} else if belongsTo := field.TagSettings["BELONGSTO"]; belongsTo != "" {
@@ -89,7 +89,8 @@ func (schema *Schema) parseRelation(field *Field) *Relationship {
 		case reflect.Slice:
 			schema.guessRelation(relation, field, guessHas)
 		default:
-			schema.err = fmt.Errorf("unsupported data type %v for %v on field %s", relation.FieldSchema, schema, field.Name)
+			schema.err = fmt.Errorf("unsupported data type %v for %v on field %s", relation.FieldSchema, schema,
+				field.Name)
 		}
 	}
 
@@ -124,6 +125,20 @@ func (schema *Schema) parseRelation(field *Field) *Relationship {
 	return relation
 }
 
+// hasPolymorphicRelation check if has polymorphic relation
+// 1. `POLYMORPHIC` tag
+// 2. `POLYMORPHICTYPE` and `POLYMORPHICID` tag
+func hasPolymorphicRelation(tagSettings map[string]string) bool {
+	if _, ok := tagSettings["POLYMORPHIC"]; ok {
+		return true
+	}
+
+	_, hasType := tagSettings["POLYMORPHICTYPE"]
+	_, hasId := tagSettings["POLYMORPHICID"]
+
+	return hasType && hasId
+}
+
 func (schema *Schema) setRelation(relation *Relationship) {
 	// set non-embedded relation
 	if rel := schema.Relationships.Relations[relation.Name]; rel != nil {
@@ -135,12 +150,12 @@ func (schema *Schema) setRelation(relation *Relationship) {
 	}
 
 	// set embedded relation
-	if len(relation.Field.BindNames) <= 1 {
+	if len(relation.Field.EmbeddedBindNames) <= 1 {
 		return
 	}
 	relationships := &schema.Relationships
-	for i, name := range relation.Field.BindNames {
-		if i < len(relation.Field.BindNames)-1 {
+	for i, name := range relation.Field.EmbeddedBindNames {
+		if i < len(relation.Field.EmbeddedBindNames)-1 {
 			if relationships.EmbeddedRelations == nil {
 				relationships.EmbeddedRelations = map[string]*Relationships{}
 			}
@@ -169,23 +184,41 @@ func (schema *Schema) setRelation(relation *Relationship) {
 //	  OwnerID   int
 //	  OwnerType string
 //	}
-func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field, polymorphic string) {
+func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Field) {
+	polymorphic := field.TagSettings["POLYMORPHIC"]
+
 	relation.Polymorphic = &Polymorphic{
-		Value:           schema.Table,
-		PolymorphicType: relation.FieldSchema.FieldsByName[polymorphic+"Type"],
-		PolymorphicID:   relation.FieldSchema.FieldsByName[polymorphic+"ID"],
+		Value: schema.Table,
+	}
+
+	var (
+		typeName = polymorphic + "Type"
+		typeId   = polymorphic + "ID"
+	)
+
+	if value, ok := field.TagSettings["POLYMORPHICTYPE"]; ok {
+		typeName = strings.TrimSpace(value)
+	}
+
+	if value, ok := field.TagSettings["POLYMORPHICID"]; ok {
+		typeId = strings.TrimSpace(value)
 	}
 
+	relation.Polymorphic.PolymorphicType = relation.FieldSchema.FieldsByName[typeName]
+	relation.Polymorphic.PolymorphicID = relation.FieldSchema.FieldsByName[typeId]
+
 	if value, ok := field.TagSettings["POLYMORPHICVALUE"]; ok {
 		relation.Polymorphic.Value = strings.TrimSpace(value)
 	}
 
 	if relation.Polymorphic.PolymorphicType == nil {
-		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", relation.FieldSchema, schema, field.Name, polymorphic+"Type")
+		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s",
+			relation.FieldSchema, schema, field.Name, polymorphic+"Type")
 	}
 
 	if relation.Polymorphic.PolymorphicID == nil {
-		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s", relation.FieldSchema, schema, field.Name, polymorphic+"ID")
+		schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing field %s",
+			relation.FieldSchema, schema, field.Name, polymorphic+"ID")
 	}
 
 	if schema.err == nil {
@@ -197,12 +230,14 @@ func (schema *Schema) buildPolymorphicRelation(relation *Relationship, field *Fi
 		primaryKeyField := schema.PrioritizedPrimaryField
 		if len(relation.foreignKeys) > 0 {
 			if primaryKeyField = schema.LookUpField(relation.foreignKeys[0]); primaryKeyField == nil || len(relation.foreignKeys) > 1 {
-				schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %s", relation.foreignKeys, schema, field.Name)
+				schema.err = fmt.Errorf("invalid polymorphic foreign keys %+v for %v on field %s", relation.foreignKeys,
+					schema, field.Name)
 			}
 		}
 
 		if primaryKeyField == nil {
-			schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing primaryKey field", relation.FieldSchema, schema, field.Name)
+			schema.err = fmt.Errorf("invalid polymorphic type %v for %v on field %s, missing primaryKey field",
+				relation.FieldSchema, schema, field.Name)
 			return
 		}
 
@@ -317,7 +352,8 @@ func (schema *Schema) buildMany2ManyRelation(relation *Relationship, field *Fiel
 		Tag:  `gorm:"-"`,
 	})
 
-	if relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore, schema.namer); err != nil {
+	if relation.JoinTable, err = Parse(reflect.New(reflect.StructOf(joinTableFields)).Interface(), schema.cacheStore,
+		schema.namer); err != nil {
 		schema.err = err
 	}
 	relation.JoinTable.Name = many2many
@@ -436,7 +472,8 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu
 			schema.guessRelation(relation, field, guessEmbeddedHas)
 		// case guessEmbeddedHas:
 		default:
-			schema.err = fmt.Errorf("invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface", schema, field.Name)
+			schema.err = fmt.Errorf("invalid field found for struct %v's field %s: define a valid foreign key for relations or implement the Valuer/Scanner interface",
+				schema, field.Name)
 		}
 	}
 
@@ -492,7 +529,9 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu
 
 			lookUpNames := []string{lookUpName}
 			if len(primaryFields) == 1 {
-				lookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID", strings.TrimSuffix(lookUpName, primaryField.Name)+"Id", schema.namer.ColumnName(foreignSchema.Table, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID"))
+				lookUpNames = append(lookUpNames, strings.TrimSuffix(lookUpName, primaryField.Name)+"ID",
+					strings.TrimSuffix(lookUpName, primaryField.Name)+"Id", schema.namer.ColumnName(foreignSchema.Table,
+						strings.TrimSuffix(lookUpName, primaryField.Name)+"ID"))
 			}
 
 			for _, name := range lookUpNames {
@@ -566,6 +605,7 @@ func (schema *Schema) guessRelation(relation *Relationship, field *Field, cgl gu
 	}
 }
 
+// Constraint is ForeignKey Constraint
 type Constraint struct {
 	Name            string
 	Field           *Field
@@ -577,6 +617,31 @@ type Constraint struct {
 	OnUpdate        string
 }
 
+func (constraint *Constraint) GetName() string { return constraint.Name }
+
+func (constraint *Constraint) Build() (sql string, vars []interface{}) {
+	sql = "CONSTRAINT ? FOREIGN KEY ? REFERENCES ??"
+	if constraint.OnDelete != "" {
+		sql += " ON DELETE " + constraint.OnDelete
+	}
+
+	if constraint.OnUpdate != "" {
+		sql += " ON UPDATE " + constraint.OnUpdate
+	}
+
+	foreignKeys := make([]interface{}, 0, len(constraint.ForeignKeys))
+	for _, field := range constraint.ForeignKeys {
+		foreignKeys = append(foreignKeys, clause.Column{Name: field.DBName})
+	}
+
+	references := make([]interface{}, 0, len(constraint.References))
+	for _, field := range constraint.References {
+		references = append(references, clause.Column{Name: field.DBName})
+	}
+	vars = append(vars, clause.Table{Name: constraint.Name}, foreignKeys, clause.Table{Name: constraint.ReferenceSchema.Table}, references)
+	return
+}
+
 func (rel *Relationship) ParseConstraint() *Constraint {
 	str := rel.Field.TagSettings["CONSTRAINT"]
 	if str == "-" {
diff --git a/schema/serializer.go b/schema/serializer.go
index 397edff..f500521 100644
--- a/schema/serializer.go
+++ b/schema/serializer.go
@@ -126,12 +126,12 @@ func (UnixSecondSerializer) Value(ctx context.Context, field *Field, dst reflect
 	rv := reflect.ValueOf(fieldValue)
 	switch v := fieldValue.(type) {
 	case int64, int, uint, uint64, int32, uint32, int16, uint16:
-		result = time.Unix(reflect.Indirect(rv).Int(), 0)
+		result = time.Unix(reflect.Indirect(rv).Int(), 0).UTC()
 	case *int64, *int, *uint, *uint64, *int32, *uint32, *int16, *uint16:
 		if rv.IsZero() {
 			return nil, nil
 		}
-		result = time.Unix(reflect.Indirect(rv).Int(), 0)
+		result = time.Unix(reflect.Indirect(rv).Int(), 0).UTC()
 	default:
 		err = fmt.Errorf("invalid field type %#v for UnixSecondSerializer, only int, uint supported", v)
 	}
diff --git a/statement.go b/statement.go
index 59c0b77..ae79aa3 100644
--- a/statement.go
+++ b/statement.go
@@ -326,7 +326,7 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []
 		case *DB:
 			v.executeScopes()
 
-			if cs, ok := v.Statement.Clauses["WHERE"]; ok && cs.Expression != nil {
+			if cs, ok := v.Statement.Clauses["WHERE"]; ok {
 				if where, ok := cs.Expression.(clause.Where); ok {
 					if len(where.Exprs) == 1 {
 						if orConds, ok := where.Exprs[0].(clause.OrConditions); ok {
@@ -334,13 +334,9 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []
 						}
 					}
 					conds = append(conds, clause.And(where.Exprs...))
-				} else {
+				} else if cs.Expression != nil {
 					conds = append(conds, cs.Expression)
 				}
-				if v.Statement == stmt {
-					cs.Expression = nil
-					stmt.Statement.Clauses["WHERE"] = cs
-				}
 			}
 		case map[interface{}]interface{}:
 			for i, j := range v {
@@ -451,8 +447,9 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []
 
 						if len(values) > 0 {
 							conds = append(conds, clause.IN{Column: clause.PrimaryColumn, Values: values})
+							return []clause.Expression{clause.And(conds...)}
 						}
-						return conds
+						return nil
 					}
 				}
 
@@ -461,7 +458,10 @@ func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []
 		}
 	}
 
-	return conds
+	if len(conds) > 0 {
+		return []clause.Expression{clause.And(conds...)}
+	}
+	return nil
 }
 
 // Build build sql with clauses names
@@ -665,7 +665,21 @@ func (stmt *Statement) Changed(fields ...string) bool {
 	return false
 }
 
-var nameMatcher = regexp.MustCompile(`^(?:\W?(\w+?)\W?\.)?\W?(\w+?)\W?$`)
+var matchName = func() func(tableColumn string) (table, column string) {
+	nameMatcher := regexp.MustCompile(`^(?:\W?(\w+?)\W?\.)?(?:(\*)|\W?(\w+?)\W?)$`)
+	return func(tableColumn string) (table, column string) {
+		if matches := nameMatcher.FindStringSubmatch(tableColumn); len(matches) == 4 {
+			table = matches[1]
+			star := matches[2]
+			columnName := matches[3]
+			if star != "" {
+				return table, star
+			}
+			return table, columnName
+		}
+		return "", ""
+	}
+}()
 
 // SelectAndOmitColumns get select and omit columns, select -> true, omit -> false
 func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (map[string]bool, bool) {
@@ -686,13 +700,13 @@ func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (
 			}
 		} else if field := stmt.Schema.LookUpField(column); field != nil && field.DBName != "" {
 			results[field.DBName] = result
-		} else if matches := nameMatcher.FindStringSubmatch(column); len(matches) == 3 && (matches[1] == stmt.Table || matches[1] == "") {
-			if matches[2] == "*" {
+		} else if table, col := matchName(column); col != "" && (table == stmt.Table || table == "") {
+			if col == "*" {
 				for _, dbName := range stmt.Schema.DBNames {
 					results[dbName] = result
 				}
 			} else {
-				results[matches[2]] = result
+				results[col] = result
 			}
 		} else {
 			results[column] = result
diff --git a/utils/utils.go b/utils/utils.go
index c8fec5b..b8d30b3 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -32,11 +32,16 @@ func sourceDir(file string) string {
 
 // FileWithLineNum return the file name and line number of the current file
 func FileWithLineNum() string {
-	// the second caller usually from gorm internal, so set i start from 2
-	for i := 2; i < 15; i++ {
-		_, file, line, ok := runtime.Caller(i)
-		if ok && (!strings.HasPrefix(file, gormSourceDir) || strings.HasSuffix(file, "_test.go")) {
-			return file + ":" + strconv.FormatInt(int64(line), 10)
+	pcs := [13]uintptr{}
+	// the third caller usually from gorm internal
+	len := runtime.Callers(3, pcs[:])
+	frames := runtime.CallersFrames(pcs[:len])
+	for i := 0; i < len; i++ {
+		// second return value is "more", not "ok"
+		frame, _ := frames.Next()
+		if (!strings.HasPrefix(frame.File, gormSourceDir) ||
+			strings.HasSuffix(frame.File, "_test.go")) && !strings.HasSuffix(frame.File, ".gen.go") {
+			return string(strconv.AppendInt(append([]byte(frame.File), ':'), int64(frame.Line), 10))
 		}
 	}
 
@@ -73,7 +78,11 @@ func ToStringKey(values ...interface{}) string {
 		case uint:
 			results[idx] = strconv.FormatUint(uint64(v), 10)
 		default:
-			results[idx] = fmt.Sprint(reflect.Indirect(reflect.ValueOf(v)).Interface())
+			results[idx] = "nil"
+			vv := reflect.ValueOf(v)
+			if vv.IsValid() && !vv.IsZero() {
+				results[idx] = fmt.Sprint(reflect.Indirect(vv).Interface())
+			}
 		}
 	}
 


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 380231b..af471d2 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -30,7 +30,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: go mod package cache
-      uses: actions/cache@v3
+      uses: actions/cache@v4
       with:
         path: ~/go/pkg/mod
         key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
@@ -73,7 +73,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: go mod package cache
-      uses: actions/cache@v3
+      uses: actions/cache@v4
       with:
         path: ~/go/pkg/mod
         key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
@@ -116,7 +116,7 @@ jobs:
         uses: actions/checkout@v4
 
       - name: go mod package cache
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: ~/go/pkg/mod
           key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
@@ -159,7 +159,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: go mod package cache
-      uses: actions/cache@v3
+      uses: actions/cache@v4
       with:
         path: ~/go/pkg/mod
         key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
@@ -202,7 +202,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: go mod package cache
-      uses: actions/cache@v3
+      uses: actions/cache@v4
       with:
         path: ~/go/pkg/mod
         key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
@@ -235,7 +235,7 @@ jobs:
 
 
       - name: go mod package cache
-        uses: actions/cache@v3
+        uses: actions/cache@v4
         with:
           path: ~/go/pkg/mod
           key: ${{ runner.os }}-go-${{ matrix.go }}-${{ hashFiles('tests/go.mod') }}
diff --git a/callbacks/create_test.go b/callbacks/create_test.go
new file mode 100644
index 0000000..da6b172
--- /dev/null
+++ b/callbacks/create_test.go
@@ -0,0 +1,71 @@
+package callbacks
+
+import (
+	"reflect"
+	"sync"
+	"testing"
+	"time"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"gorm.io/gorm/schema"
+)
+
+var schemaCache = &sync.Map{}
+
+func TestConvertToCreateValues_DestType_Slice(t *testing.T) {
+	type user struct {
+		ID    int `gorm:"primaryKey"`
+		Name  string
+		Email string `gorm:"default:(-)"`
+		Age   int    `gorm:"default:(-)"`
+	}
+
+	s, err := schema.Parse(&user{}, schemaCache, schema.NamingStrategy{})
+	if err != nil {
+		t.Errorf("parse schema error: %v, is not expected", err)
+		return
+	}
+	dest := []*user{
+		{
+			ID:    1,
+			Name:  "alice",
+			Email: "email",
+			Age:   18,
+		},
+		{
+			ID:    2,
+			Name:  "bob",
+			Email: "email",
+			Age:   19,
+		},
+	}
+	stmt := &gorm.Statement{
+		DB: &gorm.DB{
+			Config: &gorm.Config{
+				NowFunc: func() time.Time { return time.Time{} },
+			},
+			Statement: &gorm.Statement{
+				Settings: sync.Map{},
+				Schema:   s,
+			},
+		},
+		ReflectValue: reflect.ValueOf(dest),
+		Dest:         dest,
+	}
+
+	stmt.Schema = s
+
+	values := ConvertToCreateValues(stmt)
+	expected := clause.Values{
+		// column has value + defaultValue column has value (which should have a stable order)
+		Columns: []clause.Column{{Name: "name"}, {Name: "email"}, {Name: "age"}, {Name: "id"}},
+		Values: [][]interface{}{
+			{"alice", "email", 18, 1},
+			{"bob", "email", 19, 2},
+		},
+	}
+	if !reflect.DeepEqual(expected, values) {
+		t.Errorf("expected: %v got %v", expected, values)
+	}
+}
diff --git a/callbacks/helper_test.go b/callbacks/helper_test.go
new file mode 100644
index 0000000..08f94e2
--- /dev/null
+++ b/callbacks/helper_test.go
@@ -0,0 +1,157 @@
+package callbacks
+
+import (
+	"reflect"
+	"testing"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+)
+
+func TestLoadOrStoreVisitMap(t *testing.T) {
+	var vm visitMap
+	var loaded bool
+	type testM struct {
+		Name string
+	}
+
+	t1 := testM{Name: "t1"}
+	t2 := testM{Name: "t2"}
+	t3 := testM{Name: "t3"}
+
+	vm = make(visitMap)
+	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); loaded {
+		t.Fatalf("loaded should be false")
+	}
+
+	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); !loaded {
+		t.Fatalf("loaded should be true")
+	}
+
+	// t1 already exist but t2 not
+	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t1, &t2, &t3})); loaded {
+		t.Fatalf("loaded should be false")
+	}
+
+	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t2, &t3})); !loaded {
+		t.Fatalf("loaded should be true")
+	}
+}
+
+func TestConvertMapToValuesForCreate(t *testing.T) {
+	testCase := []struct {
+		name   string
+		input  map[string]interface{}
+		expect clause.Values
+	}{
+		{
+			name: "Test convert string value",
+			input: map[string]interface{}{
+				"name": "my name",
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "name"}},
+				Values:  [][]interface{}{{"my name"}},
+			},
+		},
+		{
+			name: "Test convert int value",
+			input: map[string]interface{}{
+				"age": 18,
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "age"}},
+				Values:  [][]interface{}{{18}},
+			},
+		},
+		{
+			name: "Test convert float value",
+			input: map[string]interface{}{
+				"score": 99.5,
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "score"}},
+				Values:  [][]interface{}{{99.5}},
+			},
+		},
+		{
+			name: "Test convert bool value",
+			input: map[string]interface{}{
+				"active": true,
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "active"}},
+				Values:  [][]interface{}{{true}},
+			},
+		},
+	}
+
+	for _, tc := range testCase {
+		t.Run(tc.name, func(t *testing.T) {
+			actual := ConvertMapToValuesForCreate(&gorm.Statement{}, tc.input)
+			if !reflect.DeepEqual(actual, tc.expect) {
+				t.Errorf("expect %v got %v", tc.expect, actual)
+			}
+		})
+	}
+}
+
+func TestConvertSliceOfMapToValuesForCreate(t *testing.T) {
+	testCase := []struct {
+		name   string
+		input  []map[string]interface{}
+		expect clause.Values
+	}{
+		{
+			name: "Test convert slice of string value",
+			input: []map[string]interface{}{
+				{"name": "my name"},
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "name"}},
+				Values:  [][]interface{}{{"my name"}},
+			},
+		},
+		{
+			name: "Test convert slice of int value",
+			input: []map[string]interface{}{
+				{"age": 18},
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "age"}},
+				Values:  [][]interface{}{{18}},
+			},
+		},
+		{
+			name: "Test convert slice of float value",
+			input: []map[string]interface{}{
+				{"score": 99.5},
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "score"}},
+				Values:  [][]interface{}{{99.5}},
+			},
+		},
+		{
+			name: "Test convert slice of bool value",
+			input: []map[string]interface{}{
+				{"active": true},
+			},
+			expect: clause.Values{
+				Columns: []clause.Column{{Name: "active"}},
+				Values:  [][]interface{}{{true}},
+			},
+		},
+	}
+
+	for _, tc := range testCase {
+		t.Run(tc.name, func(t *testing.T) {
+			actual := ConvertSliceOfMapToValuesForCreate(&gorm.Statement{}, tc.input)
+
+			if !reflect.DeepEqual(actual, tc.expect) {
+				t.Errorf("expected %v but got %v", tc.expect, actual)
+			}
+		})
+	}
+
+}
diff --git a/callbacks/visit_map_test.go b/callbacks/visit_map_test.go
deleted file mode 100644
index b1fb86d..0000000
--- a/callbacks/visit_map_test.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package callbacks
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestLoadOrStoreVisitMap(t *testing.T) {
-	var vm visitMap
-	var loaded bool
-	type testM struct {
-		Name string
-	}
-
-	t1 := testM{Name: "t1"}
-	t2 := testM{Name: "t2"}
-	t3 := testM{Name: "t3"}
-
-	vm = make(visitMap)
-	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); loaded {
-		t.Fatalf("loaded should be false")
-	}
-
-	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf(&t1)); !loaded {
-		t.Fatalf("loaded should be true")
-	}
-
-	// t1 already exist but t2 not
-	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t1, &t2, &t3})); loaded {
-		t.Fatalf("loaded should be false")
-	}
-
-	if loaded = loadOrStoreVisitMap(&vm, reflect.ValueOf([]*testM{&t2, &t3})); !loaded {
-		t.Fatalf("loaded should be true")
-	}
-}
diff --git a/clause/limit_test.go b/clause/limit_test.go
index a9fd4e2..96a7e7e 100644
--- a/clause/limit_test.go
+++ b/clause/limit_test.go
@@ -22,43 +22,53 @@ func TestLimit(t *testing.T) {
 				Limit:  &limit10,
 				Offset: 20,
 			}},
-			"SELECT * FROM `users` LIMIT 10 OFFSET 20", nil,
+			"SELECT * FROM `users` LIMIT ? OFFSET ?",
+			[]interface{}{limit10, 20},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}},
-			"SELECT * FROM `users` LIMIT 0", nil,
+			"SELECT * FROM `users` LIMIT ?",
+			[]interface{}{limit0},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit0}, clause.Limit{Offset: 0}},
-			"SELECT * FROM `users` LIMIT 0", nil,
+			"SELECT * FROM `users` LIMIT ?",
+			[]interface{}{limit0},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}},
-			"SELECT * FROM `users` OFFSET 20", nil,
+			"SELECT * FROM `users` OFFSET ?",
+			[]interface{}{20},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}, clause.Limit{Offset: 30}},
-			"SELECT * FROM `users` OFFSET 30", nil,
+			"SELECT * FROM `users` OFFSET ?",
+			[]interface{}{30},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Offset: 20}, clause.Limit{Limit: &limit10}},
-			"SELECT * FROM `users` LIMIT 10 OFFSET 20", nil,
+			"SELECT * FROM `users` LIMIT ? OFFSET ?",
+			[]interface{}{limit10, 20},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}},
-			"SELECT * FROM `users` LIMIT 10 OFFSET 30", nil,
+			"SELECT * FROM `users` LIMIT ? OFFSET ?",
+			[]interface{}{limit10, 30},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Offset: -10}},
-			"SELECT * FROM `users` LIMIT 10", nil,
+			"SELECT * FROM `users` LIMIT ?",
+			[]interface{}{limit10},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Limit: &limitNeg10}},
-			"SELECT * FROM `users` OFFSET 30", nil,
+			"SELECT * FROM `users` OFFSET ?",
+			[]interface{}{30},
 		},
 		{
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Limit{Limit: &limit10, Offset: 20}, clause.Limit{Offset: 30}, clause.Limit{Limit: &limit50}},
-			"SELECT * FROM `users` LIMIT 50 OFFSET 30", nil,
+			"SELECT * FROM `users` LIMIT ? OFFSET ?",
+			[]interface{}{limit50, 30},
 		},
 	}
 
diff --git a/clause/locking_test.go b/clause/locking_test.go
index 0e60731..e45c8e7 100644
--- a/clause/locking_test.go
+++ b/clause/locking_test.go
@@ -14,17 +14,21 @@ func TestLocking(t *testing.T) {
 		Vars    []interface{}
 	}{
 		{
-			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: "UPDATE"}},
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthUpdate}},
 			"SELECT * FROM `users` FOR UPDATE", nil,
 		},
 		{
-			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: "SHARE", Table: clause.Table{Name: clause.CurrentTable}}},
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthShare, Table: clause.Table{Name: clause.CurrentTable}}},
 			"SELECT * FROM `users` FOR SHARE OF `users`", nil,
 		},
 		{
-			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: "UPDATE"}, clause.Locking{Strength: "UPDATE", Options: "NOWAIT"}},
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthUpdate, Options: clause.LockingOptionsNoWait}},
 			"SELECT * FROM `users` FOR UPDATE NOWAIT", nil,
 		},
+		{
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Locking{Strength: clause.LockingStrengthUpdate, Options: clause.LockingOptionsSkipLocked}},
+			"SELECT * FROM `users` FOR UPDATE SKIP LOCKED", nil,
+		},
 	}
 
 	for idx, result := range results {
diff --git a/clause/where_test.go b/clause/where_test.go
index 35e3dbe..ad23a4e 100644
--- a/clause/where_test.go
+++ b/clause/where_test.go
@@ -63,7 +63,7 @@ func TestWhere(t *testing.T) {
 			[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{
 				Exprs: []clause.Expression{clause.And(clause.Eq{Column: "age", Value: 18}, clause.Or(clause.Neq{Column: "name", Value: "jinzhu"}))},
 			}},
-			"SELECT * FROM `users` WHERE (`age` = ? OR `name` <> ?)",
+			"SELECT * FROM `users` WHERE `age` = ? OR `name` <> ?",
 			[]interface{}{18, "jinzhu"},
 		},
 		{
@@ -94,7 +94,7 @@ func TestWhere(t *testing.T) {
 						clause.And(clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}, WithoutParentheses: false})),
 				},
 			}},
-			"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND `score` <= ?)",
+			"SELECT * FROM `users` WHERE `users`.`id` <> ? AND `score` <= ?",
 			[]interface{}{"1", 100},
 		},
 		{
@@ -105,6 +105,30 @@ func TestWhere(t *testing.T) {
 			"SELECT * FROM `users` WHERE (`users`.`id` <> ? AND NOT `score` <= ?)",
 			[]interface{}{"1", 100},
 		},
+		{
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{
+				Exprs: []clause.Expression{clause.Not(clause.Expr{SQL: "`score` <= ?", Vars: []interface{}{100}},
+					clause.Expr{SQL: "`age` <= ?", Vars: []interface{}{60}})},
+			}},
+			"SELECT * FROM `users` WHERE NOT (`score` <= ? AND `age` <= ?)",
+			[]interface{}{100, 60},
+		},
+		{
+			[]clause.Interface{clause.Select{}, clause.From{}, clause.Where{
+				Exprs: []clause.Expression{
+					clause.Not(clause.AndConditions{
+						Exprs: []clause.Expression{
+							clause.Eq{Column: clause.PrimaryColumn, Value: "1"},
+							clause.Gt{Column: "age", Value: 18},
+						}}, clause.OrConditions{
+						Exprs: []clause.Expression{
+							clause.Lt{Column: "score", Value: 100},
+						},
+					}),
+				}}},
+			"SELECT * FROM `users` WHERE NOT ((`users`.`id` = ? AND `age` > ?) OR `score` < ?)",
+			[]interface{}{"1", 18, 100},
+		},
 	}
 
 	for idx, result := range results {
diff --git a/logger/sql_test.go b/logger/sql_test.go
index a82fa54..9002a7e 100644
--- a/logger/sql_test.go
+++ b/logger/sql_test.go
@@ -31,20 +31,24 @@ func (s ExampleStruct) Value() (driver.Value, error) {
 }
 
 func format(v []byte, escaper string) string {
-	return escaper + strings.ReplaceAll(string(v), escaper, "\\"+escaper) + escaper
+	return escaper + strings.ReplaceAll(string(v), escaper, escaper+escaper) + escaper
 }
 
 func TestExplainSQL(t *testing.T) {
 	type role string
 	type password []byte
+	type intType int
+	type floatType float64
 	var (
-		tt     = now.MustParse("2020-02-23 11:10:10")
-		myrole = role("admin")
-		pwd    = password([]byte("pass"))
-		jsVal  = []byte(`{"Name":"test","Val":"test"}`)
-		js     = JSON(jsVal)
-		esVal  = []byte(`{"Name":"test","Val":"test"}`)
-		es     = ExampleStruct{Name: "test", Val: "test"}
+		tt                 = now.MustParse("2020-02-23 11:10:10")
+		myrole             = role("admin")
+		pwd                = password("pass")
+		jsVal              = []byte(`{"Name":"test","Val":"test"}`)
+		js                 = JSON(jsVal)
+		esVal              = []byte(`{"Name":"test","Val":"test"}`)
+		es                 = ExampleStruct{Name: "test", Val: "test"}
+		intVal   intType   = 1
+		floatVal floatType = 1.23
 	)
 
 	results := []struct {
@@ -57,13 +61,13 @@ func TestExplainSQL(t *testing.T) {
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			NumericRegexp: nil,
 			Vars:          []interface{}{"jinzhu", 1, 999.99, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd},
-			Result:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.\"com", "admin", "pass")`,
+			Result:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass")`,
 		},
 		{
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			NumericRegexp: nil,
 			Vars:          []interface{}{"jinzhu?", 1, 999.99, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd},
-			Result:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values ("jinzhu?", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.\"com", "admin", "pass")`,
+			Result:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values ("jinzhu?", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass")`,
 		},
 		{
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass) values (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9, @p10, @p11)",
@@ -87,25 +91,37 @@ func TestExplainSQL(t *testing.T) {
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			NumericRegexp: nil,
 			Vars:          []interface{}{"jinzhu", 1, 999.99, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd, js, es},
-			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.\"com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
+			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
 		},
 		{
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			NumericRegexp: nil,
 			Vars:          []interface{}{"jinzhu", 1, 999.99, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd, &js, &es},
-			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.\"com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
+			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
 		},
 		{
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			NumericRegexp: nil,
 			Vars:          []interface{}{"jinzhu", 1, 0.1753607109, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd, &js, &es},
-			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 0.1753607109, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.\"com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
+			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 0.1753607109, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
 		},
 		{
 			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
 			NumericRegexp: nil,
 			Vars:          []interface{}{"jinzhu", 1, float32(999.99), true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd, &js, &es},
-			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.\"com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
+			Result:        fmt.Sprintf(`create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, json_struct, example_struct) values ("jinzhu", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass", %v, %v)`, format(jsVal, `"`), format(esVal, `"`)),
+		},
+		{
+			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, int_val) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+			NumericRegexp: nil,
+			Vars:          []interface{}{"jinzhu?", 1, 999.99, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd, intVal},
+			Result:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, int_val) values ("jinzhu?", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass", 1)`,
+		},
+		{
+			SQL:           "create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, float_val) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+			NumericRegexp: nil,
+			Vars:          []interface{}{"jinzhu?", 1, 999.99, true, []byte("12345"), tt, &tt, nil, "w@g.\"com", myrole, pwd, floatVal},
+			Result:        `create table users (name, age, height, actived, bytes, create_at, update_at, deleted_at, email, role, pass, float_val) values ("jinzhu?", 1, 999.99, true, "12345", "2020-02-23 11:10:10", "2020-02-23 11:10:10", NULL, "w@g.""com", "admin", "pass", 1.230000)`,
 		},
 	}
 
diff --git a/schema/check_test.go b/schema/constraint_test.go
similarity index 59%
rename from schema/check_test.go
rename to schema/constraint_test.go
index eda043b..6fcb1b8 100644
--- a/schema/check_test.go
+++ b/schema/constraint_test.go
@@ -6,6 +6,7 @@ import (
 	"testing"
 
 	"gorm.io/gorm/schema"
+	"gorm.io/gorm/utils/tests"
 )
 
 type UserCheck struct {
@@ -20,7 +21,7 @@ func TestParseCheck(t *testing.T) {
 		t.Fatalf("failed to parse user check, got error %v", err)
 	}
 
-	results := map[string]schema.Check{
+	results := map[string]schema.CheckConstraint{
 		"name_checker": {
 			Name:       "name_checker",
 			Constraint: "name <> 'jinzhu'",
@@ -53,3 +54,31 @@ func TestParseCheck(t *testing.T) {
 		}
 	}
 }
+
+func TestParseUniqueConstraints(t *testing.T) {
+	type UserUnique struct {
+		Name1 string `gorm:"unique"`
+		Name2 string `gorm:"uniqueIndex"`
+	}
+
+	user, err := schema.Parse(&UserUnique{}, &sync.Map{}, schema.NamingStrategy{})
+	if err != nil {
+		t.Fatalf("failed to parse user unique, got error %v", err)
+	}
+	constraints := user.ParseUniqueConstraints()
+
+	results := map[string]schema.UniqueConstraint{
+		"uni_user_uniques_name1": {
+			Name:  "uni_user_uniques_name1",
+			Field: &schema.Field{Name: "Name1", Unique: true},
+		},
+	}
+	for k, result := range results {
+		v, ok := constraints[k]
+		if !ok {
+			t.Errorf("Failed to found unique constraint %v from parsed constraints %+v", k, constraints)
+		}
+		tests.AssertObjEqual(t, result, v, "Name")
+		tests.AssertObjEqual(t, result.Field, v.Field, "Name", "Unique", "UniqueIndex")
+	}
+}
diff --git a/schema/index_test.go b/schema/index_test.go
index 890327d..2f1e36a 100644
--- a/schema/index_test.go
+++ b/schema/index_test.go
@@ -1,11 +1,11 @@
 package schema_test
 
 import (
-	"reflect"
 	"sync"
 	"testing"
 
 	"gorm.io/gorm/schema"
+	"gorm.io/gorm/utils/tests"
 )
 
 type UserIndex struct {
@@ -19,6 +19,7 @@ type UserIndex struct {
 	OID          int64  `gorm:"index:idx_id;index:idx_oid,unique"`
 	MemberNumber string `gorm:"index:idx_id,priority:1"`
 	Name7        string `gorm:"index:type"`
+	Name8        string `gorm:"index:,length:10;index:,collate:utf8"`
 
 	// Composite Index: Flattened structure.
 	Data0A string `gorm:"index:,composite:comp_id0"`
@@ -65,7 +66,7 @@ func TestParseIndex(t *testing.T) {
 		"idx_name": {
 			Name:   "idx_name",
 			Class:  "UNIQUE",
-			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name2", Unique: true}}},
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name2", UniqueIndex: "idx_name"}}},
 		},
 		"idx_user_indices_name3": {
 			Name:  "idx_user_indices_name3",
@@ -81,7 +82,7 @@ func TestParseIndex(t *testing.T) {
 		"idx_user_indices_name4": {
 			Name:   "idx_user_indices_name4",
 			Class:  "UNIQUE",
-			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name4", Unique: true}}},
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name4", UniqueIndex: "idx_user_indices_name4"}}},
 		},
 		"idx_user_indices_name5": {
 			Name:    "idx_user_indices_name5",
@@ -102,18 +103,27 @@ func TestParseIndex(t *testing.T) {
 		},
 		"idx_id": {
 			Name:   "idx_id",
-			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "MemberNumber"}}, {Field: &schema.Field{Name: "OID", Unique: true}}},
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "MemberNumber"}}, {Field: &schema.Field{Name: "OID", UniqueIndex: "idx_oid"}}},
 		},
 		"idx_oid": {
 			Name:   "idx_oid",
 			Class:  "UNIQUE",
-			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "OID", Unique: true}}},
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "OID", UniqueIndex: "idx_oid"}}},
 		},
 		"type": {
 			Name:   "type",
 			Type:   "",
 			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "Name7"}}},
 		},
+		"idx_user_indices_name8": {
+			Name: "idx_user_indices_name8",
+			Type: "",
+			Fields: []schema.IndexOption{
+				{Field: &schema.Field{Name: "Name8"}, Length: 10},
+				// Note: Duplicate Columns
+				{Field: &schema.Field{Name: "Name8"}, Collate: "utf8"},
+			},
+		},
 		"idx_user_indices_comp_id0": {
 			Name: "idx_user_indices_comp_id0",
 			Type: "",
@@ -146,40 +156,109 @@ func TestParseIndex(t *testing.T) {
 		},
 	}
 
-	indices := user.ParseIndexes()
+	CheckIndices(t, results, user.ParseIndexes())
+}
 
-	for k, result := range results {
-		v, ok := indices[k]
-		if !ok {
-			t.Fatalf("Failed to found index %v from parsed indices %+v", k, indices)
-		}
+func TestParseIndexWithUniqueIndexAndUnique(t *testing.T) {
+	type IndexTest struct {
+		FieldA string `gorm:"unique;index"` // unique and index
+		FieldB string `gorm:"unique"`       // unique
 
-		for _, name := range []string{"Name", "Class", "Type", "Where", "Comment", "Option"} {
-			if reflect.ValueOf(result).FieldByName(name).Interface() != reflect.ValueOf(v).FieldByName(name).Interface() {
-				t.Errorf(
-					"index %v %v should equal, expects %v, got %v",
-					k, name, reflect.ValueOf(result).FieldByName(name).Interface(), reflect.ValueOf(v).FieldByName(name).Interface(),
-				)
-			}
-		}
+		FieldC string `gorm:"index:,unique"`     // uniqueIndex
+		FieldD string `gorm:"uniqueIndex;index"` // uniqueIndex and index
 
-		for idx, ef := range result.Fields {
-			rf := v.Fields[idx]
-			if rf.Field.Name != ef.Field.Name {
-				t.Fatalf("index field should equal, expects %v, got %v", rf.Field.Name, ef.Field.Name)
+		FieldE1 string `gorm:"uniqueIndex:uniq_field_e1_e2"` // mul uniqueIndex
+		FieldE2 string `gorm:"uniqueIndex:uniq_field_e1_e2"`
+
+		FieldF1 string `gorm:"uniqueIndex:uniq_field_f1_f2;index"` // mul uniqueIndex and index
+		FieldF2 string `gorm:"uniqueIndex:uniq_field_f1_f2;"`
+
+		FieldG string `gorm:"unique;uniqueIndex"` // unique and uniqueIndex
+
+		FieldH1 string `gorm:"unique;uniqueIndex:uniq_field_h1_h2"` // unique and mul uniqueIndex
+		FieldH2 string `gorm:"uniqueIndex:uniq_field_h1_h2"`        // unique and mul uniqueIndex
+	}
+	indexSchema, err := schema.Parse(&IndexTest{}, &sync.Map{}, schema.NamingStrategy{})
+	if err != nil {
+		t.Fatalf("failed to parse user index, got error %v", err)
+	}
+	indices := indexSchema.ParseIndexes()
+	CheckIndices(t, map[string]schema.Index{
+		"idx_index_tests_field_a": {
+			Name:   "idx_index_tests_field_a",
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldA", Unique: true}}},
+		},
+		"idx_index_tests_field_c": {
+			Name:   "idx_index_tests_field_c",
+			Class:  "UNIQUE",
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldC", UniqueIndex: "idx_index_tests_field_c"}}},
+		},
+		"idx_index_tests_field_d": {
+			Name:  "idx_index_tests_field_d",
+			Class: "UNIQUE",
+			Fields: []schema.IndexOption{
+				{Field: &schema.Field{Name: "FieldD"}},
+				// Note: Duplicate Columns
+				{Field: &schema.Field{Name: "FieldD"}},
+			},
+		},
+		"uniq_field_e1_e2": {
+			Name:  "uniq_field_e1_e2",
+			Class: "UNIQUE",
+			Fields: []schema.IndexOption{
+				{Field: &schema.Field{Name: "FieldE1"}},
+				{Field: &schema.Field{Name: "FieldE2"}},
+			},
+		},
+		"idx_index_tests_field_f1": {
+			Name:   "idx_index_tests_field_f1",
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldF1"}}},
+		},
+		"uniq_field_f1_f2": {
+			Name:  "uniq_field_f1_f2",
+			Class: "UNIQUE",
+			Fields: []schema.IndexOption{
+				{Field: &schema.Field{Name: "FieldF1"}},
+				{Field: &schema.Field{Name: "FieldF2"}},
+			},
+		},
+		"idx_index_tests_field_g": {
+			Name:   "idx_index_tests_field_g",
+			Class:  "UNIQUE",
+			Fields: []schema.IndexOption{{Field: &schema.Field{Name: "FieldG", Unique: true, UniqueIndex: "idx_index_tests_field_g"}}},
+		},
+		"uniq_field_h1_h2": {
+			Name:  "uniq_field_h1_h2",
+			Class: "UNIQUE",
+			Fields: []schema.IndexOption{
+				{Field: &schema.Field{Name: "FieldH1", Unique: true}},
+				{Field: &schema.Field{Name: "FieldH2"}},
+			},
+		},
+	}, indices)
+}
+
+func CheckIndices(t *testing.T, expected, actual map[string]schema.Index) {
+	for k, ei := range expected {
+		t.Run(k, func(t *testing.T) {
+			ai, ok := actual[k]
+			if !ok {
+				t.Errorf("expected index %q but actual missing", k)
+				return
 			}
-			if rf.Field.Unique != ef.Field.Unique {
-				t.Fatalf("index field '%s' should equal, expects %v, got %v", rf.Field.Name, rf.Field.Unique, ef.Field.Unique)
+			tests.AssertObjEqual(t, ai, ei, "Name", "Class", "Type", "Where", "Comment", "Option")
+			if len(ei.Fields) != len(ai.Fields) {
+				t.Errorf("expected index %q field length is %d but actual %d", k, len(ei.Fields), len(ai.Fields))
+				return
 			}
-
-			for _, name := range []string{"Expression", "Sort", "Collate", "Length"} {
-				if reflect.ValueOf(ef).FieldByName(name).Interface() != reflect.ValueOf(rf).FieldByName(name).Interface() {
-					t.Errorf(
-						"index %v field #%v's %v should equal, expects %v, got %v", k, idx+1, name,
-						reflect.ValueOf(ef).FieldByName(name).Interface(), reflect.ValueOf(rf).FieldByName(name).Interface(),
-					)
-				}
+			for i, ef := range ei.Fields {
+				af := ai.Fields[i]
+				tests.AssertObjEqual(t, af, ef, "Name", "Unique", "UniqueIndex", "Expression", "Sort", "Collate", "Length")
 			}
-		}
+		})
+		delete(actual, k)
+	}
+	for k := range actual {
+		t.Errorf("unexpected index %q", k)
 	}
 }
diff --git a/schema/relationship_test.go b/schema/relationship_test.go
index 1eb66bb..f1acf2d 100644
--- a/schema/relationship_test.go
+++ b/schema/relationship_test.go
@@ -121,6 +121,29 @@ func TestSelfReferentialBelongsToOverrideReferences(t *testing.T) {
 	})
 }
 
+func TestBelongsToWithMixin(t *testing.T) {
+	type Profile struct {
+		gorm.Model
+		Refer string
+		Name  string
+	}
+
+	type ProfileMixin struct {
+		Profile      Profile `gorm:"References:Refer"`
+		ProfileRefer int
+	}
+
+	type User struct {
+		gorm.Model
+		ProfileMixin
+	}
+
+	checkStructRelation(t, &User{}, Relation{
+		Name: "Profile", Type: schema.BelongsTo, Schema: "User", FieldSchema: "Profile",
+		References: []Reference{{"Refer", "Profile", "ProfileRefer", "User", "", false}},
+	})
+}
+
 func TestHasOneOverrideForeignKey(t *testing.T) {
 	type Profile struct {
 		gorm.Model
@@ -577,6 +600,193 @@ func TestEmbeddedHas(t *testing.T) {
 	})
 }
 
+func TestPolymorphic(t *testing.T) {
+	t.Run("has one", func(t *testing.T) {
+		type Toy struct {
+			ID        int
+			Name      string
+			OwnerID   int
+			OwnerType string
+		}
+
+		type Cat struct {
+			ID   int
+			Name string
+			Toy  Toy `gorm:"polymorphic:Owner;"`
+		}
+
+		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})
+		if err != nil {
+			t.Fatalf("Failed to parse schema, got error %v", err)
+		}
+
+		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+			"Cat": {
+				Relations: map[string]Relation{
+					"Toy": {
+						Name:        "Toy",
+						Type:        schema.HasOne,
+						Schema:      "User",
+						FieldSchema: "Toy",
+						Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"},
+						References: []Reference{
+							{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"},
+						},
+					},
+				},
+			},
+		})
+	})
+
+	t.Run("has one with custom polymorphic type and id", func(t *testing.T) {
+		type Toy struct {
+			ID    int
+			Name  string
+			RefId int
+			Type  string
+		}
+
+		type Cat struct {
+			ID   int
+			Name string
+			Toy  Toy `gorm:"polymorphic:Owner;polymorphicType:Type;polymorphicId:RefId"`
+		}
+
+		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})
+		if err != nil {
+			t.Fatalf("Failed to parse schema, got error %v", err)
+		}
+
+		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+			"Cat": {
+				Relations: map[string]Relation{
+					"Toy": {
+						Name:        "Toy",
+						Type:        schema.HasOne,
+						Schema:      "User",
+						FieldSchema: "Toy",
+						Polymorphic: Polymorphic{ID: "ref_id", Type: "Type", Value: "users"},
+						References: []Reference{
+							{ForeignKey: "Type", ForeignSchema: "Toy", PrimaryValue: "users"},
+						},
+					},
+				},
+			},
+		})
+	})
+
+	t.Run("has one with only polymorphic type", func(t *testing.T) {
+		type Toy struct {
+			ID      int
+			Name    string
+			OwnerID int
+			Type    string
+		}
+
+		type Cat struct {
+			ID   int
+			Name string
+			Toy  Toy `gorm:"polymorphic:Owner;polymorphicType:Type"`
+		}
+
+		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})
+		if err != nil {
+			t.Fatalf("Failed to parse schema, got error %v", err)
+		}
+
+		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+			"Cat": {
+				Relations: map[string]Relation{
+					"Toy": {
+						Name:        "Toy",
+						Type:        schema.HasOne,
+						Schema:      "User",
+						FieldSchema: "Toy",
+						Polymorphic: Polymorphic{ID: "owner_id", Type: "Type", Value: "users"},
+						References: []Reference{
+							{ForeignKey: "Type", ForeignSchema: "Toy", PrimaryValue: "users"},
+						},
+					},
+				},
+			},
+		})
+	})
+
+	t.Run("has many", func(t *testing.T) {
+		type Toy struct {
+			ID        int
+			Name      string
+			OwnerID   int
+			OwnerType string
+		}
+
+		type Cat struct {
+			ID   int
+			Name string
+			Toys []Toy `gorm:"polymorphic:Owner;"`
+		}
+
+		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})
+		if err != nil {
+			t.Fatalf("Failed to parse schema, got error %v", err)
+		}
+
+		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+			"Cat": {
+				Relations: map[string]Relation{
+					"Toys": {
+						Name:        "Toys",
+						Type:        schema.HasMany,
+						Schema:      "User",
+						FieldSchema: "Toy",
+						Polymorphic: Polymorphic{ID: "OwnerID", Type: "OwnerType", Value: "users"},
+						References: []Reference{
+							{ForeignKey: "OwnerType", ForeignSchema: "Toy", PrimaryValue: "users"},
+						},
+					},
+				},
+			},
+		})
+	})
+
+	t.Run("has many with custom polymorphic type and id", func(t *testing.T) {
+		type Toy struct {
+			ID    int
+			Name  string
+			RefId int
+			Type  string
+		}
+
+		type Cat struct {
+			ID   int
+			Name string
+			Toys []Toy `gorm:"polymorphicType:Type;polymorphicId:RefId"`
+		}
+
+		s, err := schema.Parse(&Cat{}, &sync.Map{}, schema.NamingStrategy{})
+		if err != nil {
+			t.Fatalf("Failed to parse schema, got error %v", err)
+		}
+
+		checkEmbeddedRelations(t, s.Relationships.EmbeddedRelations, map[string]EmbeddedRelations{
+			"Cat": {
+				Relations: map[string]Relation{
+					"Toys": {
+						Name:        "Toys",
+						Type:        schema.HasMany,
+						Schema:      "User",
+						FieldSchema: "Toy",
+						Polymorphic: Polymorphic{ID: "ref_id", Type: "Type", Value: "users"},
+						References: []Reference{
+							{ForeignKey: "Type", ForeignSchema: "Toy", PrimaryValue: "users"},
+						},
+					},
+				},
+			},
+		})
+	})
+}
+
 func TestEmbeddedBelongsTo(t *testing.T) {
 	type Country struct {
 		ID   int `gorm:"primaryKey"`
@@ -589,6 +799,10 @@ func TestEmbeddedBelongsTo(t *testing.T) {
 	type NestedAddress struct {
 		Address
 	}
+	type CountryMixin struct {
+		CountryID int
+		Country   Country
+	}
 	type Org struct {
 		ID              int
 		PostalAddress   Address `gorm:"embedded;embeddedPrefix:postal_address_"`
@@ -599,6 +813,7 @@ func TestEmbeddedBelongsTo(t *testing.T) {
 			Address
 		}
 		NestedAddress *NestedAddress `gorm:"embedded;embeddedPrefix:nested_address_"`
+		CountryMixin
 	}
 
 	s, err := schema.Parse(&Org{}, &sync.Map{}, schema.NamingStrategy{})
@@ -628,15 +843,11 @@ func TestEmbeddedBelongsTo(t *testing.T) {
 			},
 		},
 		"NestedAddress": {
-			EmbeddedRelations: map[string]EmbeddedRelations{
-				"Address": {
-					Relations: map[string]Relation{
-						"Country": {
-							Name: "Country", Type: schema.BelongsTo, Schema: "Org", FieldSchema: "Country",
-							References: []Reference{
-								{PrimaryKey: "ID", PrimarySchema: "Country", ForeignKey: "CountryID", ForeignSchema: "Org"},
-							},
-						},
+			Relations: map[string]Relation{
+				"Country": {
+					Name: "Country", Type: schema.BelongsTo, Schema: "Org", FieldSchema: "Country",
+					References: []Reference{
+						{PrimaryKey: "ID", PrimarySchema: "Country", ForeignKey: "CountryID", ForeignSchema: "Org"},
 					},
 				},
 			},
diff --git a/schema/schema_helper_test.go b/schema/schema_helper_test.go
index 605aa03..bc32668 100644
--- a/schema/schema_helper_test.go
+++ b/schema/schema_helper_test.go
@@ -163,8 +163,8 @@ func checkSchemaRelation(t *testing.T, s *schema.Schema, relation Relation) {
 					t.Errorf("schema %v relation's join table tablename expects %v, but got %v", s, relation.JoinTable.Table, r.JoinTable.Table)
 				}
 
-				for _, f := range relation.JoinTable.Fields {
-					checkSchemaField(t, r.JoinTable, &f, nil)
+				for i := range relation.JoinTable.Fields {
+					checkSchemaField(t, r.JoinTable, &relation.JoinTable.Fields[i], nil)
 				}
 			}
 
diff --git a/schema/schema_test.go b/schema/schema_test.go
index 5bc0fb8..45e152e 100644
--- a/schema/schema_test.go
+++ b/schema/schema_test.go
@@ -46,8 +46,8 @@ func checkUserSchema(t *testing.T, user *schema.Schema) {
 		{Name: "Active", DBName: "active", BindNames: []string{"Active"}, DataType: schema.Bool},
 	}
 
-	for _, f := range fields {
-		checkSchemaField(t, user, &f, func(f *schema.Field) {
+	for i := range fields {
+		checkSchemaField(t, user, &fields[i], func(f *schema.Field) {
 			f.Creatable = true
 			f.Updatable = true
 			f.Readable = true
@@ -136,8 +136,8 @@ func TestParseSchemaWithAdvancedDataType(t *testing.T) {
 		{Name: "Admin", DBName: "admin", BindNames: []string{"Admin"}, DataType: schema.Bool},
 	}
 
-	for _, f := range fields {
-		checkSchemaField(t, user, &f, func(f *schema.Field) {
+	for i := range fields {
+		checkSchemaField(t, user, &fields[i], func(f *schema.Field) {
 			f.Creatable = true
 			f.Updatable = true
 			f.Readable = true
diff --git a/statement_test.go b/statement_test.go
index 648bc87..0995d54 100644
--- a/statement_test.go
+++ b/statement_test.go
@@ -56,9 +56,15 @@ func TestNameMatcher(t *testing.T) {
 		"`name_1`":           {"", "name_1"},
 		"`Name_1`":           {"", "Name_1"},
 		"`Table`.`nAme`":     {"Table", "nAme"},
+		"my_table.*":         {"my_table", "*"},
+		"`my_table`.*":       {"my_table", "*"},
+		"User__Company.*":    {"User__Company", "*"},
+		"`User__Company`.*":  {"User__Company", "*"},
+		`"User__Company".*`:  {"User__Company", "*"},
+		`"table"."*"`:        {"", ""},
 	} {
-		if matches := nameMatcher.FindStringSubmatch(k); len(matches) < 3 || matches[1] != v[0] || matches[2] != v[1] {
-			t.Errorf("failed to match value: %v, got %v, expect: %v", k, matches, v)
+		if table, column := matchName(k); table != v[0] || column != v[1] {
+			t.Errorf("failed to match value: %v, got %v, expect: %v", k, []string{table, column}, v)
 		}
 	}
 }
diff --git a/tests/associations_has_many_test.go b/tests/associations_has_many_test.go
index c31c4b4..b8e8ff5 100644
--- a/tests/associations_has_many_test.go
+++ b/tests/associations_has_many_test.go
@@ -422,7 +422,7 @@ func TestPolymorphicHasManyAssociation(t *testing.T) {
 func TestPolymorphicHasManyAssociationForSlice(t *testing.T) {
 	users := []User{
 		*GetUser("slice-hasmany-1", Config{Toys: 2}),
-		*GetUser("slice-hasmany-2", Config{Toys: 0}),
+		*GetUser("slice-hasmany-2", Config{Toys: 0, Tools: 2}),
 		*GetUser("slice-hasmany-3", Config{Toys: 4}),
 	}
 
@@ -430,6 +430,7 @@ func TestPolymorphicHasManyAssociationForSlice(t *testing.T) {
 
 	// Count
 	AssertAssociationCount(t, users, "Toys", 6, "")
+	AssertAssociationCount(t, users, "Tools", 2, "")
 
 	// Find
 	var toys []Toy
@@ -437,6 +438,14 @@ func TestPolymorphicHasManyAssociationForSlice(t *testing.T) {
 		t.Errorf("toys count should be %v, but got %v", 6, len(toys))
 	}
 
+	// Find Tools (polymorphic with custom type and id)
+	var tools []Tools
+	DB.Model(&users).Association("Tools").Find(&tools)
+
+	if len(tools) != 2 {
+		t.Errorf("tools count should be %v, but got %v", 2, len(tools))
+	}
+
 	// Append
 	DB.Model(&users).Association("Toys").Append(
 		&Toy{Name: "toy-slice-append-1"},
diff --git a/tests/callbacks_test.go b/tests/callbacks_test.go
index 4479da4..f77209f 100644
--- a/tests/callbacks_test.go
+++ b/tests/callbacks_test.go
@@ -91,7 +91,7 @@ func TestCallbacks(t *testing.T) {
 		},
 		{
 			callbacks: []callback{{h: c1}, {h: c2, before: "c4", after: "c5"}, {h: c3}, {h: c4}, {h: c5}, {h: c2, remove: true}},
-			results:   []string{"c1", "c5", "c3", "c4"},
+			results:   []string{"c1", "c3", "c4", "c5"},
 		},
 		{
 			callbacks: []callback{{h: c1}, {name: "c", h: c2}, {h: c3}, {name: "c", h: c4, replace: true}},
@@ -206,3 +206,49 @@ func TestPluginCallbacks(t *testing.T) {
 		t.Errorf("callbacks tests failed, got %v", msg)
 	}
 }
+
+func TestCallbacksGet(t *testing.T) {
+	db, _ := gorm.Open(nil, nil)
+	createCallback := db.Callback().Create()
+
+	createCallback.Before("*").Register("c1", c1)
+	if cb := createCallback.Get("c1"); reflect.DeepEqual(cb, c1) {
+		t.Errorf("callbacks tests failed, got: %p, want: %p", cb, c1)
+	}
+
+	createCallback.Remove("c1")
+	if cb := createCallback.Get("c2"); cb != nil {
+		t.Errorf("callbacks test failed. got: %p, want: nil", cb)
+	}
+}
+
+func TestCallbacksRemove(t *testing.T) {
+	db, _ := gorm.Open(nil, nil)
+	createCallback := db.Callback().Create()
+
+	createCallback.Before("*").Register("c1", c1)
+	createCallback.After("*").Register("c2", c2)
+	createCallback.Before("c4").Register("c3", c3)
+	createCallback.After("c2").Register("c4", c4)
+
+	// callbacks: []string{"c1", "c3", "c4", "c2"}
+	createCallback.Remove("c1")
+	if ok, msg := assertCallbacks(createCallback, []string{"c3", "c4", "c2"}); !ok {
+		t.Errorf("callbacks tests failed, got %v", msg)
+	}
+
+	createCallback.Remove("c4")
+	if ok, msg := assertCallbacks(createCallback, []string{"c3", "c2"}); !ok {
+		t.Errorf("callbacks tests failed, got %v", msg)
+	}
+
+	createCallback.Remove("c2")
+	if ok, msg := assertCallbacks(createCallback, []string{"c3"}); !ok {
+		t.Errorf("callbacks tests failed, got %v", msg)
+	}
+
+	createCallback.Remove("c3")
+	if ok, msg := assertCallbacks(createCallback, []string{}); !ok {
+		t.Errorf("callbacks tests failed, got %v", msg)
+	}
+}
diff --git a/tests/connpool_test.go b/tests/connpool_test.go
index e0e1c77..21a2bad 100644
--- a/tests/connpool_test.go
+++ b/tests/connpool_test.go
@@ -102,13 +102,13 @@ func TestConnPoolWrapper(t *testing.T) {
 		expect: []string{
 			"SELECT VERSION()",
 			"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)",
-			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1",
+			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
 			"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)",
-			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1",
-			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1",
+			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
+			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
 			"INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`,`age`,`birthday`,`company_id`,`manager_id`,`active`) VALUES (?,?,?,?,?,?,?,?,?)",
-			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1",
-			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT 1",
+			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
+			"SELECT * FROM `users` WHERE name = ? AND `users`.`deleted_at` IS NULL ORDER BY `users`.`id` LIMIT ?",
 		},
 	}
 
diff --git a/tests/create_test.go b/tests/create_test.go
index 02613b7..abb8247 100644
--- a/tests/create_test.go
+++ b/tests/create_test.go
@@ -2,6 +2,7 @@ package tests_test
 
 import (
 	"errors"
+	"fmt"
 	"regexp"
 	"testing"
 	"time"
@@ -580,38 +581,213 @@ func TestCreateWithAutoIncrementCompositeKey(t *testing.T) {
 	}
 }
 
-func TestCreateOnConfilctWithDefalutNull(t *testing.T) {
-	type OnConfilctUser struct {
+func TestCreateOnConflictWithDefaultNull(t *testing.T) {
+	type OnConflictUser struct {
 		ID     string
 		Name   string `gorm:"default:null"`
 		Email  string
 		Mobile string `gorm:"default:'133xxxx'"`
 	}
 
-	err := DB.Migrator().DropTable(&OnConfilctUser{})
+	err := DB.Migrator().DropTable(&OnConflictUser{})
 	AssertEqual(t, err, nil)
-	err = DB.AutoMigrate(&OnConfilctUser{})
+	err = DB.AutoMigrate(&OnConflictUser{})
 	AssertEqual(t, err, nil)
 
-	u := OnConfilctUser{
-		ID:     "on-confilct-user-id",
-		Name:   "on-confilct-user-name",
-		Email:  "on-confilct-user-email",
-		Mobile: "on-confilct-user-mobile",
+	u := OnConflictUser{
+		ID:     "on-conflict-user-id",
+		Name:   "on-conflict-user-name",
+		Email:  "on-conflict-user-email",
+		Mobile: "on-conflict-user-mobile",
 	}
 	err = DB.Create(&u).Error
 	AssertEqual(t, err, nil)
 
-	u.Name = "on-confilct-user-name-2"
-	u.Email = "on-confilct-user-email-2"
+	u.Name = "on-conflict-user-name-2"
+	u.Email = "on-conflict-user-email-2"
 	u.Mobile = ""
 	err = DB.Clauses(clause.OnConflict{UpdateAll: true}).Create(&u).Error
 	AssertEqual(t, err, nil)
 
-	var u2 OnConfilctUser
+	var u2 OnConflictUser
 	err = DB.Where("id = ?", u.ID).First(&u2).Error
 	AssertEqual(t, err, nil)
-	AssertEqual(t, u2.Name, "on-confilct-user-name-2")
-	AssertEqual(t, u2.Email, "on-confilct-user-email-2")
+	AssertEqual(t, u2.Name, "on-conflict-user-name-2")
+	AssertEqual(t, u2.Email, "on-conflict-user-email-2")
 	AssertEqual(t, u2.Mobile, "133xxxx")
 }
+
+func TestCreateFromMapWithoutPK(t *testing.T) {
+	if !isMysql() {
+		t.Skipf("This test case skipped, because of only supporting for mysql")
+	}
+
+	// case 1: one record, create from map[string]interface{}
+	mapValue1 := map[string]interface{}{"name": "create_from_map_with_schema1", "age": 1}
+	if err := DB.Model(&User{}).Create(mapValue1).Error; err != nil {
+		t.Fatalf("failed to create data from map, got error: %v", err)
+	}
+
+	if _, ok := mapValue1["id"]; !ok {
+		t.Fatal("failed to create data from map with table, returning map has no primary key")
+	}
+
+	var result1 User
+	if err := DB.Where("name = ?", "create_from_map_with_schema1").First(&result1).Error; err != nil || result1.Age != 1 {
+		t.Fatalf("failed to create from map, got error %v", err)
+	}
+
+	var idVal int64
+	_, ok := mapValue1["id"].(uint)
+	if ok {
+		t.Skipf("This test case skipped, because the db supports returning")
+	}
+
+	idVal, ok = mapValue1["id"].(int64)
+	if !ok {
+		t.Fatal("ret result missing id")
+	}
+
+	if int64(result1.ID) != idVal {
+		t.Fatal("failed to create data from map with table, @id != id")
+	}
+
+	// case2: one record, create from *map[string]interface{}
+	mapValue2 := map[string]interface{}{"name": "create_from_map_with_schema2", "age": 1}
+	if err := DB.Model(&User{}).Create(&mapValue2).Error; err != nil {
+		t.Fatalf("failed to create data from map, got error: %v", err)
+	}
+
+	if _, ok := mapValue2["id"]; !ok {
+		t.Fatal("failed to create data from map with table, returning map has no primary key")
+	}
+
+	var result2 User
+	if err := DB.Where("name = ?", "create_from_map_with_schema2").First(&result2).Error; err != nil || result2.Age != 1 {
+		t.Fatalf("failed to create from map, got error %v", err)
+	}
+
+	_, ok = mapValue2["id"].(uint)
+	if ok {
+		t.Skipf("This test case skipped, because the db supports returning")
+	}
+
+	idVal, ok = mapValue2["id"].(int64)
+	if !ok {
+		t.Fatal("ret result missing id")
+	}
+
+	if int64(result2.ID) != idVal {
+		t.Fatal("failed to create data from map with table, @id != id")
+	}
+
+	// case 3: records
+	values := []map[string]interface{}{
+		{"name": "create_from_map_with_schema11", "age": 1}, {"name": "create_from_map_with_schema12", "age": 1},
+	}
+
+	beforeLen := len(values)
+	if err := DB.Model(&User{}).Create(&values).Error; err != nil {
+		t.Fatalf("failed to create data from map, got error: %v", err)
+	}
+
+	// mariadb with returning, values will be appended with id map
+	if len(values) == beforeLen*2 {
+		t.Skipf("This test case skipped, because the db supports returning")
+	}
+
+	for i := range values {
+		v, ok := values[i]["id"]
+		if !ok {
+			t.Fatal("failed to create data from map with table, returning map has no primary key")
+		}
+
+		var result User
+		if err := DB.Where("name = ?", fmt.Sprintf("create_from_map_with_schema1%d", i+1)).First(&result).Error; err != nil || result.Age != 1 {
+			t.Fatalf("failed to create from map, got error %v", err)
+		}
+		if int64(result.ID) != v.(int64) {
+			t.Fatal("failed to create data from map with table, @id != id")
+		}
+	}
+}
+
+func TestCreateFromMapWithTable(t *testing.T) {
+	tableDB := DB.Table("users")
+	supportLastInsertID := isMysql() || isSqlite()
+
+	// case 1: create from map[string]interface{}
+	record := map[string]interface{}{"name": "create_from_map_with_table", "age": 18}
+	if err := tableDB.Create(record).Error; err != nil {
+		t.Fatalf("failed to create data from map with table, got error: %v", err)
+	}
+
+	if _, ok := record["@id"]; !ok && supportLastInsertID {
+		t.Fatal("failed to create data from map with table, returning map has no key '@id'")
+	}
+
+	var res map[string]interface{}
+	if err := tableDB.Select([]string{"id", "name", "age"}).Where("name = ?", "create_from_map_with_table").Find(&res).Error; err != nil || res["age"] != int64(18) {
+		t.Fatalf("failed to create from map, got error %v", err)
+	}
+
+	if _, ok := record["@id"]; ok && fmt.Sprint(res["id"]) != fmt.Sprint(record["@id"]) {
+		t.Fatalf("failed to create data from map with table, @id != id, got %v, expect %v", res["id"], record["@id"])
+	}
+
+	// case 2: create from *map[string]interface{}
+	record1 := map[string]interface{}{"name": "create_from_map_with_table_1", "age": 18}
+	tableDB2 := DB.Table("users")
+	if err := tableDB2.Create(&record1).Error; err != nil {
+		t.Fatalf("failed to create data from map, got error: %v", err)
+	}
+	if _, ok := record1["@id"]; !ok && supportLastInsertID {
+		t.Fatal("failed to create data from map with table, returning map has no key '@id'")
+	}
+
+	var res1 map[string]interface{}
+	if err := tableDB2.Select([]string{"id", "name", "age"}).Where("name = ?", "create_from_map_with_table_1").Find(&res1).Error; err != nil || res1["age"] != int64(18) {
+		t.Fatalf("failed to create from map, got error %v", err)
+	}
+
+	if _, ok := record1["@id"]; ok && fmt.Sprint(res1["id"]) != fmt.Sprint(record1["@id"]) {
+		t.Fatal("failed to create data from map with table, @id != id")
+	}
+
+	// case 3: create from []map[string]interface{}
+	records := []map[string]interface{}{
+		{"name": "create_from_map_with_table_2", "age": 19},
+		{"name": "create_from_map_with_table_3", "age": 20},
+	}
+
+	tableDB = DB.Table("users")
+	if err := tableDB.Create(&records).Error; err != nil {
+		t.Fatalf("failed to create data from slice of map, got error: %v", err)
+	}
+
+	if _, ok := records[0]["@id"]; !ok && supportLastInsertID {
+		t.Fatal("failed to create data from map with table, returning map has no key '@id'")
+	}
+
+	if _, ok := records[1]["@id"]; !ok && supportLastInsertID {
+		t.Fatal("failed to create data from map with table, returning map has no key '@id'")
+	}
+
+	var res2 map[string]interface{}
+	if err := tableDB.Select([]string{"id", "name", "age"}).Where("name = ?", "create_from_map_with_table_2").Find(&res2).Error; err != nil || res2["age"] != int64(19) {
+		t.Fatalf("failed to query data after create from slice of map, got error %v", err)
+	}
+
+	var res3 map[string]interface{}
+	if err := DB.Table("users").Select([]string{"id", "name", "age"}).Where("name = ?", "create_from_map_with_table_3").Find(&res3).Error; err != nil || res3["age"] != int64(20) {
+		t.Fatalf("failed to query data after create from slice of map, got error %v", err)
+	}
+
+	if _, ok := records[0]["@id"]; ok && fmt.Sprint(res2["id"]) != fmt.Sprint(records[0]["@id"]) {
+		t.Errorf("failed to create data from map with table, @id != id, got %v, expect %v", res2["id"], records[0]["@id"])
+	}
+
+	if _, ok := records[1]["id"]; ok && fmt.Sprint(res3["id"]) != fmt.Sprint(records[1]["@id"]) {
+		t.Errorf("failed to create data from map with table, @id != id")
+	}
+}
diff --git a/tests/default_value_test.go b/tests/default_value_test.go
index 918f079..71d6deb 100644
--- a/tests/default_value_test.go
+++ b/tests/default_value_test.go
@@ -38,4 +38,22 @@ func TestDefaultValue(t *testing.T) {
 	} else if result.Name != "foo" || result.Name2 != "foo" || result.Name3 != "" || result.Age != 18 || !result.Enabled || result.Created.Format("20060102") != "20000102" {
 		t.Fatalf("Failed to find created data with default data, got %+v", result)
 	}
+
+	type Harumph2 struct {
+		ID      int       `gorm:"default:0"`
+		Email   string    `gorm:"not null;index:,unique"`
+		Name    string    `gorm:"notNull;default:foo"`
+		Name2   string    `gorm:"size:233;not null;default:'foo'"`
+		Name3   string    `gorm:"size:233;notNull;default:''"`
+		Age     int       `gorm:"default:18"`
+		Created time.Time `gorm:"default:2000-01-02"`
+		Enabled bool      `gorm:"default:true"`
+	}
+
+	harumph2 := Harumph2{ID: 2, Email: "hello2@gorm.io"}
+	if err := DB.Table("harumphs").Create(&harumph2).Error; err != nil {
+		t.Fatalf("Failed to create data with default value, got error: %v", err)
+	} else if harumph2.ID != 2 || harumph2.Name != "foo" || harumph2.Name2 != "foo" || harumph2.Name3 != "" || harumph2.Age != 18 || !harumph2.Enabled || harumph2.Created.Format("20060102") != "20000102" {
+		t.Fatalf("Failed to create data with default value, got: %+v", harumph2)
+	}
 }
diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml
index 866a4d6..8abd4d0 100644
--- a/tests/docker-compose.yml
+++ b/tests/docker-compose.yml
@@ -24,6 +24,7 @@ services:
     ports:
       - "9930:1433"
     environment:
+      - TZ=Asia/Shanghai
       - ACCEPT_EULA=Y
       - SA_PASSWORD=LoremIpsum86
       - MSSQL_DB=gorm
diff --git a/tests/go.mod b/tests/go.mod
index 5a0aedd..10fa7ec 100644
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -3,28 +3,39 @@ module gorm.io/gorm/tests
 go 1.18
 
 require (
-	github.com/google/uuid v1.3.1
+	github.com/google/uuid v1.6.0
 	github.com/jinzhu/now v1.1.5
 	github.com/lib/pq v1.10.9
-	gorm.io/driver/mysql v1.5.2-0.20230612053416-48b6526a21f0
-	gorm.io/driver/postgres v1.5.3-0.20230607070428-18bc84b75196
-	gorm.io/driver/sqlite v1.5.4
-	gorm.io/driver/sqlserver v1.5.2-0.20230613072041-6e2cde390b0a
-	gorm.io/gorm v1.25.4
+	github.com/stretchr/testify v1.9.0
+	gorm.io/driver/mysql v1.5.6
+	gorm.io/driver/postgres v1.5.7
+	gorm.io/driver/sqlite v1.5.5
+	gorm.io/driver/sqlserver v1.5.3
+	gorm.io/gorm v1.25.9
 )
 
 require (
-	github.com/go-sql-driver/mysql v1.7.1 // indirect
+	filippo.io/edwards25519 v1.1.0 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/go-sql-driver/mysql v1.8.1 // indirect
 	github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
 	github.com/golang-sql/sqlexp v0.1.0 // indirect
 	github.com/jackc/pgpassfile v1.0.0 // indirect
-	github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
-	github.com/jackc/pgx/v5 v5.4.3 // indirect
+	github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect
+	github.com/jackc/pgx/v5 v5.5.5 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
-	github.com/mattn/go-sqlite3 v1.14.17 // indirect
-	github.com/microsoft/go-mssqldb v1.6.0 // indirect
-	golang.org/x/crypto v0.14.0 // indirect
-	golang.org/x/text v0.13.0 // indirect
+	github.com/kr/text v0.2.0 // indirect
+	github.com/mattn/go-sqlite3 v1.14.22 // indirect
+	github.com/microsoft/go-mssqldb v1.7.1 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/rogpeppe/go-internal v1.12.0 // indirect
+	golang.org/x/crypto v0.22.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
 replace gorm.io/gorm => ../
+
+replace github.com/jackc/pgx/v5 => github.com/jackc/pgx/v5 v5.4.3
+
+replace github.com/microsoft/go-mssqldb => github.com/microsoft/go-mssqldb v1.7.0
diff --git a/tests/helper_test.go b/tests/helper_test.go
index 1a4874e..dc250b7 100644
--- a/tests/helper_test.go
+++ b/tests/helper_test.go
@@ -23,6 +23,7 @@ type Config struct {
 	Languages int
 	Friends   int
 	NamedPet  bool
+	Tools     int
 }
 
 func GetUser(name string, config Config) *User {
@@ -47,6 +48,10 @@ func GetUser(name string, config Config) *User {
 		user.Toys = append(user.Toys, Toy{Name: name + "_toy_" + strconv.Itoa(i+1)})
 	}
 
+	for i := 0; i < config.Tools; i++ {
+		user.Tools = append(user.Tools, Tools{Name: name + "_tool_" + strconv.Itoa(i+1)})
+	}
+
 	if config.Company {
 		user.Company = Company{Name: "company-" + name}
 	}
@@ -118,11 +123,13 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) {
 		if err := db(unscoped).Where("id = ?", user.ID).First(&newUser).Error; err != nil {
 			t.Fatalf("errors happened when query: %v", err)
 		} else {
-			AssertObjEqual(t, newUser, user, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active")
+			AssertObjEqual(t, newUser, user, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday",
+				"CompanyID", "ManagerID", "Active")
 		}
 	}
 
-	AssertObjEqual(t, user, expect, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active")
+	AssertObjEqual(t, user, expect, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID",
+		"ManagerID", "Active")
 
 	t.Run("Account", func(t *testing.T) {
 		AssertObjEqual(t, user.Account, expect.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID", "Number")
@@ -133,7 +140,8 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) {
 			} else {
 				var account Account
 				db(unscoped).First(&account, "user_id = ?", user.ID)
-				AssertObjEqual(t, account, user.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID", "Number")
+				AssertObjEqual(t, account, user.Account, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "UserID",
+					"Number")
 			}
 		}
 	})
@@ -193,8 +201,10 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) {
 			} else {
 				var manager User
 				db(unscoped).First(&manager, "id = ?", *user.ManagerID)
-				AssertObjEqual(t, manager, user.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active")
-				AssertObjEqual(t, manager, expect.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active")
+				AssertObjEqual(t, manager, user.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age",
+					"Birthday", "CompanyID", "ManagerID", "Active")
+				AssertObjEqual(t, manager, expect.Manager, "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age",
+					"Birthday", "CompanyID", "ManagerID", "Active")
 			}
 		} else if user.ManagerID != nil {
 			t.Errorf("Manager should not be created for zero value, got: %+v", user.ManagerID)
@@ -215,7 +225,8 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) {
 		})
 
 		for idx, team := range user.Team {
-			AssertObjEqual(t, team, expect.Team[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active")
+			AssertObjEqual(t, team, expect.Team[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age",
+				"Birthday", "CompanyID", "ManagerID", "Active")
 		}
 	})
 
@@ -250,7 +261,8 @@ func doCheckUser(t *testing.T, user User, expect User, unscoped bool) {
 		})
 
 		for idx, friend := range user.Friends {
-			AssertObjEqual(t, friend, expect.Friends[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age", "Birthday", "CompanyID", "ManagerID", "Active")
+			AssertObjEqual(t, friend, expect.Friends[idx], "ID", "CreatedAt", "UpdatedAt", "DeletedAt", "Name", "Age",
+				"Birthday", "CompanyID", "ManagerID", "Active")
 		}
 	})
 }
@@ -269,6 +281,10 @@ func isMysql() bool {
 	return os.Getenv("GORM_DIALECT") == "mysql"
 }
 
+func isSqlite() bool {
+	return os.Getenv("GORM_DIALECT") == "sqlite"
+}
+
 func db(unscoped bool) *gorm.DB {
 	if unscoped {
 		return DB.Unscoped()
diff --git a/tests/migrate_test.go b/tests/migrate_test.go
index cfd3e0a..d955c8d 100644
--- a/tests/migrate_test.go
+++ b/tests/migrate_test.go
@@ -2,23 +2,30 @@ package tests_test
 
 import (
 	"context"
+	"database/sql"
 	"fmt"
 	"math/rand"
 	"os"
 	"reflect"
+	"strconv"
 	"strings"
 	"testing"
 	"time"
 
+	"github.com/stretchr/testify/assert"
 	"gorm.io/driver/postgres"
+
 	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
 	"gorm.io/gorm/logger"
+	"gorm.io/gorm/migrator"
 	"gorm.io/gorm/schema"
+	"gorm.io/gorm/utils"
 	. "gorm.io/gorm/utils/tests"
 )
 
 func TestMigrate(t *testing.T) {
-	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}}
+	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Tools{}}
 	rand.Seed(time.Now().UnixNano())
 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] })
 	DB.Migrator().DropTable("user_speaks", "user_friends", "ccc")
@@ -34,7 +41,7 @@ func TestMigrate(t *testing.T) {
 	if tables, err := DB.Migrator().GetTables(); err != nil {
 		t.Fatalf("Failed to get database all tables, but got error %v", err)
 	} else {
-		for _, t1 := range []string{"users", "accounts", "pets", "companies", "toys", "languages"} {
+		for _, t1 := range []string{"users", "accounts", "pets", "companies", "toys", "languages", "tools"} {
 			hasTable := false
 			for _, t2 := range tables {
 				if t2 == t1 {
@@ -93,7 +100,8 @@ func TestAutoMigrateInt8PG(t *testing.T) {
 		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
 			sql, _ := fc()
 			if strings.HasPrefix(sql, "ALTER TABLE \"migrate_ints\" ALTER COLUMN \"int8\" TYPE smallint") {
-				t.Fatalf("shouldn't execute ALTER COLUMN TYPE if such type is already existed in DB schema: sql: %s", sql)
+				t.Fatalf("shouldn't execute ALTER COLUMN TYPE if such type is already existed in DB schema: sql: %s",
+					sql)
 			}
 		},
 	}
@@ -432,40 +440,50 @@ func TestTiDBMigrateColumns(t *testing.T) {
 			switch columnType.Name() {
 			case "id":
 				if v, ok := columnType.PrimaryKey(); !ok || !v {
-					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "name":
 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))
 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {
-					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
+					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v",
+						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
 				}
 				if length, ok := columnType.Length(); !ok || length != 100 {
-					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), length, 100, columnType)
+					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v",
+						columnType.Name(), length, 100, columnType)
 				}
 			case "age":
 				if v, ok := columnType.DefaultValue(); !ok || v != "18" {
-					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 				if v, ok := columnType.Comment(); !ok || v != "my age" {
-					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "code":
 				if v, ok := columnType.Unique(); !ok || !v {
-					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 				if v, ok := columnType.DefaultValue(); !ok || v != "hello" {
-					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v)
+					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v",
+						columnType.Name(), columnType, v)
 				}
 				if v, ok := columnType.Comment(); !ok || v != "my code2" {
-					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "code2":
 				// Code2 string `gorm:"comment:my code2;default:hello"`
 				if v, ok := columnType.DefaultValue(); !ok || v != "hello" {
-					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v)
+					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v",
+						columnType.Name(), columnType, v)
 				}
 				if v, ok := columnType.Comment(); !ok || v != "my code2" {
-					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			}
 		}
@@ -497,7 +515,8 @@ func TestTiDBMigrateColumns(t *testing.T) {
 		t.Fatalf("Failed to add column, got %v", err)
 	}
 
-	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", "new_new_name"); err != nil {
+	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName",
+		"new_new_name"); err != nil {
 		t.Fatalf("Failed to add column, got %v", err)
 	}
 
@@ -561,36 +580,45 @@ func TestMigrateColumns(t *testing.T) {
 			switch columnType.Name() {
 			case "id":
 				if v, ok := columnType.PrimaryKey(); !ok || !v {
-					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "name":
 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))
 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {
-					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
+					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v",
+						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
 				}
 				if length, ok := columnType.Length(); !sqlite && (!ok || length != 100) {
-					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), length, 100, columnType)
+					t.Fatalf("column name length should be correct, name: %v, length: %v, expects: %v, column: %#v",
+						columnType.Name(), length, 100, columnType)
 				}
 			case "age":
 				if v, ok := columnType.DefaultValue(); !ok || v != "18" {
-					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column age default value should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 				if v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != "my age") {
-					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column age comment should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "code":
 				if v, ok := columnType.Unique(); !ok || !v {
-					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column code unique should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 				if v, ok := columnType.DefaultValue(); !sqlserver && (!ok || v != "hello") {
-					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v", columnType.Name(), columnType, v)
+					t.Fatalf("column code default value should be correct, name: %v, column: %#v, default value: %v",
+						columnType.Name(), columnType, v)
 				}
 				if v, ok := columnType.Comment(); !sqlite && !sqlserver && (!ok || v != "my code2") {
-					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column code comment should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "code2":
 				if v, ok := columnType.Unique(); !sqlserver && (!ok || !v) {
-					t.Fatalf("column code2 unique should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column code2 unique should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "code3":
 				// TODO
@@ -627,7 +655,8 @@ func TestMigrateColumns(t *testing.T) {
 		t.Fatalf("Failed to add column, got %v", err)
 	}
 
-	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName", "new_new_name"); err != nil {
+	if err := DB.Table("column_structs").Migrator().RenameColumn(&NewColumnStruct{}, "NewName",
+		"new_new_name"); err != nil {
 		t.Fatalf("Failed to add column, got %v", err)
 	}
 
@@ -962,7 +991,8 @@ func TestCurrentTimestamp(t *testing.T) {
 	if err != nil {
 		t.Fatalf("AutoMigrate err:%v", err)
 	}
-	AssertEqual(t, true, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at"))
+	AssertEqual(t, true, DB.Migrator().HasConstraint(&CurrentTimestampTest{}, "uni_current_timestamp_tests_time_at"))
+	AssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at"))
 	AssertEqual(t, false, DB.Migrator().HasIndex(&CurrentTimestampTest{}, "time_at_2"))
 }
 
@@ -1024,7 +1054,8 @@ func TestUniqueColumn(t *testing.T) {
 	}
 
 	// not trigger alert column
-	AssertEqual(t, true, DB.Migrator().HasIndex(&UniqueTest{}, "name"))
+	AssertEqual(t, true, DB.Migrator().HasConstraint(&UniqueTest{}, "uni_unique_tests_name"))
+	AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name"))
 	AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name_1"))
 	AssertEqual(t, false, DB.Migrator().HasIndex(&UniqueTest{}, "name_2"))
 
@@ -1383,14 +1414,14 @@ func TestMigrateSameEmbeddedFieldName(t *testing.T) {
 	err = DB.Table("game_users").AutoMigrate(&GameUser1{})
 	AssertEqual(t, nil, err)
 
-	_, err = findColumnType(&GameUser{}, "stat_ab_ground_destory_count")
+	_, err = findColumnType(&GameUser{}, "stat_ab_ground_destroy_count")
 	AssertEqual(t, nil, err)
 
-	_, err = findColumnType(&GameUser{}, "rate_ground_rb_ground_destory_count")
+	_, err = findColumnType(&GameUser{}, "rate_ground_rb_ground_destroy_count")
 	AssertEqual(t, nil, err)
 }
 
-func TestMigrateDefaultNullString(t *testing.T) {
+func TestMigrateWithDefaultValue(t *testing.T) {
 	if DB.Dialector.Name() == "sqlserver" {
 		// sqlserver driver treats NULL and 'NULL' the same
 		t.Skip("skip sqlserver")
@@ -1404,6 +1435,7 @@ func TestMigrateDefaultNullString(t *testing.T) {
 	type NullStringModel struct {
 		ID      uint
 		Content string `gorm:"default:'null'"`
+		Active  bool   `gorm:"default:false"`
 	}
 
 	tableName := "null_string_model"
@@ -1424,6 +1456,14 @@ func TestMigrateDefaultNullString(t *testing.T) {
 	AssertEqual(t, defVal, "null")
 	AssertEqual(t, ok, true)
 
+	columnType2, err := findColumnType(tableName, "active")
+	AssertEqual(t, err, nil)
+
+	defVal, ok = columnType2.DefaultValue()
+	bv, _ := strconv.ParseBool(defVal)
+	AssertEqual(t, bv, false)
+	AssertEqual(t, ok, true)
+
 	// default 'null' -> 'null'
 	session := DB.Session(&gorm.Session{Logger: Tracer{
 		Logger: DB.Config.Logger,
@@ -1555,7 +1595,8 @@ func TestMigrateIgnoreRelations(t *testing.T) {
 func TestMigrateView(t *testing.T) {
 	DB.Save(GetUser("joins-args-db", Config{Pets: 2}))
 
-	if err := DB.Migrator().CreateView("invalid_users_pets", gorm.ViewOption{Query: nil}); err != gorm.ErrSubQueryRequired {
+	if err := DB.Migrator().CreateView("invalid_users_pets",
+		gorm.ViewOption{Query: nil}); err != gorm.ErrSubQueryRequired {
 		t.Fatalf("no view should be created, got %v", err)
 	}
 
@@ -1624,17 +1665,20 @@ func TestMigrateExistingBoolColumnPG(t *testing.T) {
 			switch columnType.Name() {
 			case "id":
 				if v, ok := columnType.PrimaryKey(); !ok || !v {
-					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(), columnType)
+					t.Fatalf("column id primary key should be correct, name: %v, column: %#v", columnType.Name(),
+						columnType)
 				}
 			case "string_bool":
 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))
 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {
-					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
+					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v",
+						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
 				}
 			case "smallint_bool":
 				dataType := DB.Dialector.DataTypeOf(stmt.Schema.LookUpField(columnType.Name()))
 				if !strings.Contains(strings.ToUpper(dataType), strings.ToUpper(columnType.DatabaseTypeName())) {
-					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v", columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
+					t.Fatalf("column name type should be correct, name: %v, length: %v, expects: %v, column: %#v",
+						columnType.Name(), columnType.DatabaseTypeName(), dataType, columnType)
 				}
 			}
 		}
@@ -1659,7 +1703,8 @@ func TestTableType(t *testing.T) {
 
 	DB.Migrator().DropTable(&City{})
 
-	if err := DB.Set("gorm:table_options", fmt.Sprintf("ENGINE InnoDB COMMENT '%s'", tblComment)).AutoMigrate(&City{}); err != nil {
+	if err := DB.Set("gorm:table_options",
+		fmt.Sprintf("ENGINE InnoDB COMMENT '%s'", tblComment)).AutoMigrate(&City{}); err != nil {
 		t.Fatalf("failed to migrate cities tables, got error: %v", err)
 	}
 
@@ -1685,3 +1730,218 @@ func TestTableType(t *testing.T) {
 		t.Fatalf("expected comment %s got %s", tblComment, comment)
 	}
 }
+
+func TestMigrateWithUniqueIndexAndUnique(t *testing.T) {
+	const table = "unique_struct"
+
+	checkField := func(model interface{}, fieldName string, unique bool, uniqueIndex string) {
+		stmt := &gorm.Statement{DB: DB}
+		err := stmt.Parse(model)
+		if err != nil {
+			t.Fatalf("%v: failed to parse schema, got error: %v", utils.FileWithLineNum(), err)
+		}
+		_ = stmt.Schema.ParseIndexes()
+		field := stmt.Schema.LookUpField(fieldName)
+		if field == nil {
+			t.Fatalf("%v: failed to find column %q", utils.FileWithLineNum(), fieldName)
+		}
+		if field.Unique != unique {
+			t.Fatalf("%v: %q column %q unique should be %v but got %v", utils.FileWithLineNum(), stmt.Schema.Table, fieldName, unique, field.Unique)
+		}
+		if field.UniqueIndex != uniqueIndex {
+			t.Fatalf("%v: %q column %q uniqueIndex should be %v but got %v", utils.FileWithLineNum(), stmt.Schema, fieldName, uniqueIndex, field.UniqueIndex)
+		}
+	}
+
+	type ( // not unique
+		UniqueStruct1 struct {
+			Name string `gorm:"size:10"`
+		}
+		UniqueStruct2 struct {
+			Name string `gorm:"size:20"`
+		}
+	)
+	checkField(&UniqueStruct1{}, "name", false, "")
+	checkField(&UniqueStruct2{}, "name", false, "")
+
+	type ( // unique
+		UniqueStruct3 struct {
+			Name string `gorm:"size:30;unique"`
+		}
+		UniqueStruct4 struct {
+			Name string `gorm:"size:40;unique"`
+		}
+	)
+	checkField(&UniqueStruct3{}, "name", true, "")
+	checkField(&UniqueStruct4{}, "name", true, "")
+
+	type ( // uniqueIndex
+		UniqueStruct5 struct {
+			Name string `gorm:"size:50;uniqueIndex"`
+		}
+		UniqueStruct6 struct {
+			Name string `gorm:"size:60;uniqueIndex"`
+		}
+		UniqueStruct7 struct {
+			Name     string `gorm:"size:70;uniqueIndex:idx_us6_all_names"`
+			NickName string `gorm:"size:70;uniqueIndex:idx_us6_all_names"`
+		}
+	)
+	checkField(&UniqueStruct5{}, "name", false, "idx_unique_struct5_name")
+	checkField(&UniqueStruct6{}, "name", false, "idx_unique_struct6_name")
+
+	checkField(&UniqueStruct7{}, "name", false, "")
+	checkField(&UniqueStruct7{}, "nick_name", false, "")
+	checkField(&UniqueStruct7{}, "nick_name", false, "")
+
+	type UniqueStruct8 struct { // unique and uniqueIndex
+		Name string `gorm:"size:60;unique;index:my_us8_index,unique;"`
+	}
+	checkField(&UniqueStruct8{}, "name", true, "my_us8_index")
+
+	type TestCase struct {
+		name      string
+		from, to  interface{}
+		checkFunc func(t *testing.T)
+	}
+
+	checkColumnType := func(t *testing.T, fieldName string, unique bool) {
+		columnTypes, err := DB.Migrator().ColumnTypes(table)
+		if err != nil {
+			t.Fatalf("%v: failed to get column types, got error: %v", utils.FileWithLineNum(), err)
+		}
+		var found gorm.ColumnType
+		for _, columnType := range columnTypes {
+			if columnType.Name() == fieldName {
+				found = columnType
+			}
+		}
+		if found == nil {
+			t.Fatalf("%v: failed to find column type %q", utils.FileWithLineNum(), fieldName)
+		}
+		if actualUnique, ok := found.Unique(); !ok || actualUnique != unique {
+			t.Fatalf("%v: column %q unique should be %v but got %v", utils.FileWithLineNum(), fieldName, unique, actualUnique)
+		}
+	}
+
+	checkIndex := func(t *testing.T, expected []gorm.Index) {
+		indexes, err := DB.Migrator().GetIndexes(table)
+		if err != nil {
+			t.Fatalf("%v: failed to get indexes, got error: %v", utils.FileWithLineNum(), err)
+		}
+		assert.ElementsMatch(t, expected, indexes)
+	}
+
+	uniqueIndex := &migrator.Index{TableName: table, NameValue: DB.Config.NamingStrategy.IndexName(table, "name"), ColumnList: []string{"name"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}
+	myIndex := &migrator.Index{TableName: table, NameValue: "my_us8_index", ColumnList: []string{"name"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}
+	mulIndex := &migrator.Index{TableName: table, NameValue: "idx_us6_all_names", ColumnList: []string{"name", "nick_name"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}
+
+	var checkNotUnique, checkUnique, checkUniqueIndex, checkMyIndex, checkMulIndex func(t *testing.T)
+	// UniqueAffectedByUniqueIndex is true
+	if DB.Dialector.Name() == "mysql" {
+		uniqueConstraintIndex := &migrator.Index{TableName: table, NameValue: DB.Config.NamingStrategy.UniqueName(table, "name"), ColumnList: []string{"name"}, PrimaryKeyValue: sql.NullBool{Bool: false, Valid: true}, UniqueValue: sql.NullBool{Bool: true, Valid: true}}
+		checkNotUnique = func(t *testing.T) {
+			checkColumnType(t, "name", false)
+			checkIndex(t, nil)
+		}
+		checkUnique = func(t *testing.T) {
+			checkColumnType(t, "name", true)
+			checkIndex(t, []gorm.Index{uniqueConstraintIndex})
+		}
+		checkUniqueIndex = func(t *testing.T) {
+			checkColumnType(t, "name", true)
+			checkIndex(t, []gorm.Index{uniqueIndex})
+		}
+		checkMyIndex = func(t *testing.T) {
+			checkColumnType(t, "name", true)
+			checkIndex(t, []gorm.Index{uniqueConstraintIndex, myIndex})
+		}
+		checkMulIndex = func(t *testing.T) {
+			checkColumnType(t, "name", false)
+			checkColumnType(t, "nick_name", false)
+			checkIndex(t, []gorm.Index{mulIndex})
+		}
+	} else {
+		checkNotUnique = func(t *testing.T) { checkColumnType(t, "name", false) }
+		checkUnique = func(t *testing.T) { checkColumnType(t, "name", true) }
+		checkUniqueIndex = func(t *testing.T) {
+			checkColumnType(t, "name", false)
+			checkIndex(t, []gorm.Index{uniqueIndex})
+		}
+		checkMyIndex = func(t *testing.T) {
+			checkColumnType(t, "name", true)
+			if !DB.Migrator().HasIndex(table, myIndex.Name()) {
+				t.Errorf("%v: should has index %s but not", utils.FileWithLineNum(), myIndex.Name())
+			}
+		}
+		checkMulIndex = func(t *testing.T) {
+			checkColumnType(t, "name", false)
+			checkColumnType(t, "nick_name", false)
+			if !DB.Migrator().HasIndex(table, mulIndex.Name()) {
+				t.Errorf("%v: should has index %s but not", utils.FileWithLineNum(), mulIndex.Name())
+			}
+		}
+	}
+
+	tests := []TestCase{
+		{name: "notUnique to notUnique", from: &UniqueStruct1{}, to: &UniqueStruct2{}, checkFunc: checkNotUnique},
+		{name: "notUnique to unique", from: &UniqueStruct1{}, to: &UniqueStruct3{}, checkFunc: checkUnique},
+		{name: "notUnique to uniqueIndex", from: &UniqueStruct1{}, to: &UniqueStruct5{}, checkFunc: checkUniqueIndex},
+		{name: "notUnique to uniqueAndUniqueIndex", from: &UniqueStruct1{}, to: &UniqueStruct8{}, checkFunc: checkMyIndex},
+		{name: "unique to unique", from: &UniqueStruct3{}, to: &UniqueStruct4{}, checkFunc: checkUnique},
+		{name: "unique to uniqueIndex", from: &UniqueStruct3{}, to: &UniqueStruct5{}, checkFunc: checkUniqueIndex},
+		{name: "unique to uniqueAndUniqueIndex", from: &UniqueStruct3{}, to: &UniqueStruct8{}, checkFunc: checkMyIndex},
+		{name: "uniqueIndex to uniqueIndex", from: &UniqueStruct5{}, to: &UniqueStruct6{}, checkFunc: checkUniqueIndex},
+		{name: "uniqueIndex to uniqueAndUniqueIndex", from: &UniqueStruct5{}, to: &UniqueStruct8{}, checkFunc: checkMyIndex},
+		{name: "uniqueIndex to multi uniqueIndex", from: &UniqueStruct5{}, to: &UniqueStruct7{}, checkFunc: checkMulIndex},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			if err := DB.Migrator().DropTable(table); err != nil {
+				t.Fatalf("failed to drop table, got error: %v", err)
+			}
+			if err := DB.Table(table).AutoMigrate(test.from); err != nil {
+				t.Fatalf("failed to migrate table, got error: %v", err)
+			}
+			if err := DB.Table(table).AutoMigrate(test.to); err != nil {
+				t.Fatalf("failed to migrate table, got error: %v", err)
+			}
+			test.checkFunc(t)
+		})
+	}
+
+	if DB.Dialector.Name() != "sqlserver" {
+		// In SQLServer, If an index or constraint depends on the column,
+		// this column will not be able to run ALTER
+		// see https://stackoverflow.com/questions/19460912/the-object-df-is-dependent-on-column-changing-int-to-double/19461205#19461205
+		// may we need to create another PR to fix it, see https://github.com/go-gorm/sqlserver/pull/106
+		tests = []TestCase{
+			{name: "unique to notUnique", from: &UniqueStruct3{}, to: &UniqueStruct1{}, checkFunc: checkNotUnique},
+			{name: "uniqueIndex to notUnique", from: &UniqueStruct5{}, to: &UniqueStruct2{}, checkFunc: checkNotUnique},
+			{name: "uniqueIndex to unique", from: &UniqueStruct5{}, to: &UniqueStruct3{}, checkFunc: checkUnique},
+		}
+	}
+
+	if DB.Dialector.Name() == "mysql" {
+		compatibilityTests := []TestCase{
+			{name: "oldUnique to notUnique", to: UniqueStruct1{}, checkFunc: checkNotUnique},
+			{name: "oldUnique to unique", to: UniqueStruct3{}, checkFunc: checkUnique},
+			{name: "oldUnique to uniqueIndex", to: UniqueStruct5{}, checkFunc: checkUniqueIndex},
+			{name: "oldUnique to uniqueAndUniqueIndex", to: UniqueStruct8{}, checkFunc: checkMyIndex},
+		}
+		for _, test := range compatibilityTests {
+			t.Run(test.name, func(t *testing.T) {
+				if err := DB.Migrator().DropTable(table); err != nil {
+					t.Fatalf("failed to drop table, got error: %v", err)
+				}
+				if err := DB.Exec("CREATE TABLE ? (`name` varchar(10) UNIQUE)", clause.Table{Name: table}).Error; err != nil {
+					t.Fatalf("failed to create table, got error: %v", err)
+				}
+				if err := DB.Table(table).AutoMigrate(test.to); err != nil {
+					t.Fatalf("failed to migrate table, got error: %v", err)
+				}
+				test.checkFunc(t)
+			})
+		}
+	}
+}
diff --git a/tests/preload_test.go b/tests/preload_test.go
index 3ff8649..6e0e91b 100644
--- a/tests/preload_test.go
+++ b/tests/preload_test.go
@@ -1,12 +1,14 @@
 package tests_test
 
 import (
+	"context"
 	"encoding/json"
 	"regexp"
 	"sort"
 	"strconv"
 	"sync"
 	"testing"
+	"time"
 
 	"gorm.io/gorm"
 	"gorm.io/gorm/clause"
@@ -307,6 +309,137 @@ func TestNestedPreloadWithUnscoped(t *testing.T) {
 	CheckUserUnscoped(t, *user6, user)
 }
 
+func TestNestedPreloadWithNestedJoin(t *testing.T) {
+	type (
+		Preload struct {
+			ID       uint
+			Value    string
+			NestedID uint
+		}
+		Join struct {
+			ID       uint
+			Value    string
+			NestedID uint
+		}
+		Nested struct {
+			ID       uint
+			Preloads []*Preload
+			Join     Join
+			ValueID  uint
+		}
+		Value struct {
+			ID     uint
+			Name   string
+			Nested Nested
+		}
+	)
+
+	DB.Migrator().DropTable(&Preload{}, &Join{}, &Nested{}, &Value{})
+	DB.Migrator().AutoMigrate(&Preload{}, &Join{}, &Nested{}, &Value{})
+
+	value1 := Value{
+		Name: "value",
+		Nested: Nested{
+			Preloads: []*Preload{
+				{Value: "p1"}, {Value: "p2"},
+			},
+			Join: Join{Value: "j1"},
+		},
+	}
+	value2 := Value{
+		Name: "value2",
+		Nested: Nested{
+			Preloads: []*Preload{
+				{Value: "p3"}, {Value: "p4"}, {Value: "p5"},
+			},
+			Join: Join{Value: "j2"},
+		},
+	}
+
+	values := []*Value{&value1, &value2}
+	if err := DB.Create(&values).Error; err != nil {
+		t.Errorf("failed to create value, got err: %v", err)
+	}
+
+	var find1 Value
+	err := DB.Joins("Nested").Joins("Nested.Join").Preload("Nested.Preloads").First(&find1, value1.ID).Error
+	if err != nil {
+		t.Errorf("failed to find value, got err: %v", err)
+	}
+	AssertEqual(t, find1, value1)
+
+	var find2 Value
+	// Joins will automatically add Nested queries.
+	err = DB.Joins("Nested.Join").Preload("Nested.Preloads").First(&find2, value2.ID).Error
+	if err != nil {
+		t.Errorf("failed to find value, got err: %v", err)
+	}
+	AssertEqual(t, find2, value2)
+
+	var finds []Value
+	err = DB.Joins("Nested.Join").Joins("Nested").Preload("Nested.Preloads").Find(&finds).Error
+	if err != nil {
+		t.Errorf("failed to find value, got err: %v", err)
+	}
+	AssertEqual(t, len(finds), 2)
+	AssertEqual(t, finds[0], value1)
+	AssertEqual(t, finds[1], value2)
+}
+
+func TestMergeNestedPreloadWithNestedJoin(t *testing.T) {
+	users := []User{
+		{
+			Name: "TestMergeNestedPreloadWithNestedJoin-1",
+			Manager: &User{
+				Name: "Alexis Manager",
+				Tools: []Tools{
+					{Name: "Alexis Tool 1"},
+					{Name: "Alexis Tool 2"},
+				},
+			},
+		},
+		{
+			Name: "TestMergeNestedPreloadWithNestedJoin-2",
+			Manager: &User{
+				Name: "Jinzhu Manager",
+				Tools: []Tools{
+					{Name: "Jinzhu Tool 1"},
+					{Name: "Jinzhu Tool 2"},
+				},
+			},
+		},
+	}
+
+	DB.Create(&users)
+
+	query := make([]string, 0)
+	sess := DB.Session(&gorm.Session{Logger: Tracer{
+		Logger: DB.Config.Logger,
+		Test: func(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
+			sql, _ := fc()
+			query = append(query, sql)
+		},
+	}})
+
+	var result []User
+	err := sess.
+		Joins("Manager").
+		Preload("Manager.Tools").
+		Where("users.name Like ?", "TestMergeNestedPreloadWithNestedJoin%").
+		Find(&result).Error
+
+	if err != nil {
+		t.Fatalf("failed to preload and find users: %v", err)
+	}
+
+	AssertEqual(t, result, users)
+	AssertEqual(t, len(query), 2) // Check preload queries are merged
+
+	if !regexp.MustCompile(`SELECT \* FROM .*tools.* WHERE .*IN.*`).MatchString(query[0]) {
+		t.Fatalf("Expected first query to preload manager tools, got: %s", query[0])
+	}
+}
+
 func TestEmbedPreload(t *testing.T) {
 	type Country struct {
 		ID   int `gorm:"primaryKey"`
@@ -399,7 +532,7 @@ func TestEmbedPreload(t *testing.T) {
 			},
 		}, {
 			name:     "nested address country",
-			preloads: map[string][]interface{}{"NestedAddress.EmbeddedAddress.Country": {}},
+			preloads: map[string][]interface{}{"NestedAddress.Country": {}},
 			expect: Org{
 				ID: org.ID,
 				PostalAddress: EmbeddedAddress{
diff --git a/tests/prepared_stmt_test.go b/tests/prepared_stmt_test.go
index b234c8b..b86bc3d 100644
--- a/tests/prepared_stmt_test.go
+++ b/tests/prepared_stmt_test.go
@@ -126,33 +126,6 @@ func TestPreparedStmtDeadlock(t *testing.T) {
 	AssertEqual(t, sqlDB.Stats().InUse, 0)
 }
 
-func TestPreparedStmtError(t *testing.T) {
-	tx, err := OpenTestConnection(&gorm.Config{})
-	AssertEqual(t, err, nil)
-
-	sqlDB, _ := tx.DB()
-	sqlDB.SetMaxOpenConns(1)
-
-	tx = tx.Session(&gorm.Session{PrepareStmt: true})
-
-	wg := sync.WaitGroup{}
-	for i := 0; i < 10; i++ {
-		wg.Add(1)
-		go func() {
-			// err prepare
-			tag := Tag{Locale: "zh"}
-			tx.Table("users").Find(&tag)
-			wg.Done()
-		}()
-	}
-	wg.Wait()
-
-	conn, ok := tx.ConnPool.(*gorm.PreparedStmtDB)
-	AssertEqual(t, ok, true)
-	AssertEqual(t, len(conn.Stmts), 0)
-	AssertEqual(t, sqlDB.Stats().InUse, 0)
-}
-
 func TestPreparedStmtInTransaction(t *testing.T) {
 	user := User{Name: "jinzhu"}
 
diff --git a/tests/query_test.go b/tests/query_test.go
index 5728378..d0ed675 100644
--- a/tests/query_test.go
+++ b/tests/query_test.go
@@ -554,6 +554,16 @@ func TestNot(t *testing.T) {
 	if !regexp.MustCompile("SELECT \\* FROM .*users.* WHERE .*users.*..*name.* <> .+ AND .*users.*..*age.* <> .+").MatchString(result.Statement.SQL.String()) {
 		t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String())
 	}
+
+	result = dryDB.Not(DB.Where("manager IS NULL").Where("age >= ?", 20)).Find(&User{})
+	if !regexp.MustCompile("SELECT \\* FROM .*users.* WHERE NOT \\(manager IS NULL AND age >= .+\\) AND .users.\\..deleted_at. IS NULL").MatchString(result.Statement.SQL.String()) {
+		t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String())
+	}
+
+	result = dryDB.Not(DB.Where("manager IS NULL").Or("age >= ?", 20)).Find(&User{})
+	if !regexp.MustCompile(`SELECT \* FROM .*users.* WHERE NOT \(manager IS NULL OR age >= .+\) AND .users.\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {
+		t.Fatalf("Build NOT condition, but got %v", result.Statement.SQL.String())
+	}
 }
 
 func TestNotWithAllFields(t *testing.T) {
@@ -1118,12 +1128,12 @@ func TestSearchWithStruct(t *testing.T) {
 	}
 
 	result = dryRunDB.Where(User{Name: "jinzhu", Age: 18}).Find(&User{})
-	if !regexp.MustCompile(`WHERE .users.\..name. = .{1,3} AND .users.\..age. = .{1,3} AND .users.\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {
+	if !regexp.MustCompile(`WHERE \(.users.\..name. = .{1,3} AND .users.\..age. = .{1,3}\) AND .users.\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {
 		t.Errorf("invalid query SQL, got %v", result.Statement.SQL.String())
 	}
 
 	result = dryRunDB.Where(User{Name: "jinzhu"}, "name", "Age").Find(&User{})
-	if !regexp.MustCompile(`WHERE .users.\..name. = .{1,3} AND .users.\..age. = .{1,3} AND .users.\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {
+	if !regexp.MustCompile(`WHERE \(.users.\..name. = .{1,3} AND .users.\..age. = .{1,3}\) AND .users.\..deleted_at. IS NULL`).MatchString(result.Statement.SQL.String()) {
 		t.Errorf("invalid query SQL, got %v", result.Statement.SQL.String())
 	}
 
@@ -1404,3 +1414,22 @@ func TestQueryError(t *testing.T) {
 	}, Value: 1}).Scan(&p2).Error
 	AssertEqual(t, err, gorm.ErrModelValueRequired)
 }
+
+func TestQueryScanToArray(t *testing.T) {
+	err := DB.Create(&User{Name: "testname1", Age: 10}).Error
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	users := [2]*User{{Name: "1"}, {Name: "2"}}
+	err = DB.Model(&User{}).Where("name = ?", "testname1").Find(&users).Error
+	if err != nil {
+		t.Fatal(err)
+	}
+	if users[0] == nil || users[0].Name != "testname1" {
+		t.Error("users[0] not covere")
+	}
+	if users[1] != nil {
+		t.Error("users[1] should be empty")
+	}
+}
diff --git a/tests/scopes_test.go b/tests/scopes_test.go
index 52c6b37..84aeb99 100644
--- a/tests/scopes_test.go
+++ b/tests/scopes_test.go
@@ -84,7 +84,9 @@ func TestComplexScopes(t *testing.T) {
 			queryFn: func(tx *gorm.DB) *gorm.DB {
 				return tx.Scopes(
 					func(d *gorm.DB) *gorm.DB { return d.Where("a = 1") },
-					func(d *gorm.DB) *gorm.DB { return d.Where(d.Or("b = 2").Or("c = 3")) },
+					func(d *gorm.DB) *gorm.DB {
+						return d.Where(DB.Or("b = 2").Or("c = 3"))
+					},
 				).Find(&Language{})
 			},
 			expected: `SELECT * FROM "languages" WHERE a = 1 AND (b = 2 OR c = 3)`,
@@ -93,7 +95,9 @@ func TestComplexScopes(t *testing.T) {
 			queryFn: func(tx *gorm.DB) *gorm.DB {
 				return tx.Where("z = 0").Scopes(
 					func(d *gorm.DB) *gorm.DB { return d.Where("a = 1") },
-					func(d *gorm.DB) *gorm.DB { return d.Or(d.Where("b = 2").Or("c = 3")) },
+					func(d *gorm.DB) *gorm.DB {
+						return d.Or(DB.Where("b = 2").Or("c = 3"))
+					},
 				).Find(&Language{})
 			},
 			expected: `SELECT * FROM "languages" WHERE z = 0 AND a = 1 OR (b = 2 OR c = 3)`,
@@ -104,7 +108,7 @@ func TestComplexScopes(t *testing.T) {
 					func(d *gorm.DB) *gorm.DB { return d.Model(&Language{}) },
 					func(d *gorm.DB) *gorm.DB {
 						return d.
-							Or(d.Scopes(
+							Or(DB.Scopes(
 								func(d *gorm.DB) *gorm.DB { return d.Where("a = 1") },
 								func(d *gorm.DB) *gorm.DB { return d.Where("b = 2") },
 							)).
diff --git a/tests/sql_builder_test.go b/tests/sql_builder_test.go
index 022e049..0c204db 100644
--- a/tests/sql_builder_test.go
+++ b/tests/sql_builder_test.go
@@ -388,7 +388,7 @@ func TestToSQL(t *testing.T) {
 	sql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
 		return tx.Model(&User{}).Where(&User{Name: "foo", Age: 20}).Limit(10).Offset(5).Order("name ASC").First(&User{})
 	})
-	assertEqualSQL(t, `SELECT * FROM "users" WHERE "users"."name" = 'foo' AND "users"."age" = 20 AND "users"."deleted_at" IS NULL ORDER BY name ASC,"users"."id" LIMIT 1 OFFSET 5`, sql)
+	assertEqualSQL(t, `SELECT * FROM "users" WHERE ("users"."name" = 'foo' AND "users"."age" = 20) AND "users"."deleted_at" IS NULL ORDER BY name ASC,"users"."id" LIMIT 1 OFFSET 5`, sql)
 
 	// last and unscoped
 	sql = DB.ToSQL(func(tx *gorm.DB) *gorm.DB {
diff --git a/tests/table_test.go b/tests/table_test.go
index fa569d3..0d44a15 100644
--- a/tests/table_test.go
+++ b/tests/table_test.go
@@ -2,8 +2,10 @@ package tests_test
 
 import (
 	"regexp"
+	"sync"
 	"testing"
 
+	"gorm.io/driver/postgres"
 	"gorm.io/gorm"
 	"gorm.io/gorm/schema"
 	"gorm.io/gorm/utils/tests"
@@ -172,3 +174,88 @@ func TestTableWithNamer(t *testing.T) {
 		t.Errorf("Table with namer, got %v", sql)
 	}
 }
+
+func TestPostgresTableWithIdentifierLength(t *testing.T) {
+	if DB.Dialector.Name() != "postgres" {
+		return
+	}
+
+	type LongString struct {
+		ThisIsAVeryVeryVeryVeryVeryVeryVeryVeryVeryLongString string `gorm:"unique"`
+	}
+
+	t.Run("default", func(t *testing.T) {
+		db, _ := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{})
+		user, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)
+		if err != nil {
+			t.Fatalf("failed to parse user unique, got error %v", err)
+		}
+
+		constraints := user.ParseUniqueConstraints()
+		if len(constraints) != 1 {
+			t.Fatalf("failed to find unique constraint, got %v", constraints)
+		}
+
+		for key := range constraints {
+			if len(key) != 63 {
+				t.Errorf("failed to find unique constraint, got %v", constraints)
+			}
+		}
+	})
+
+	t.Run("naming strategy", func(t *testing.T) {
+		db, _ := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{
+			NamingStrategy: schema.NamingStrategy{},
+		})
+
+		user, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)
+		if err != nil {
+			t.Fatalf("failed to parse user unique, got error %v", err)
+		}
+
+		constraints := user.ParseUniqueConstraints()
+		if len(constraints) != 1 {
+			t.Fatalf("failed to find unique constraint, got %v", constraints)
+		}
+
+		for key := range constraints {
+			if len(key) != 63 {
+				t.Errorf("failed to find unique constraint, got %v", constraints)
+			}
+		}
+	})
+
+	t.Run("namer", func(t *testing.T) {
+		uname := "custom_unique_name"
+		db, _ := gorm.Open(postgres.Open(postgresDSN), &gorm.Config{
+			NamingStrategy: mockUniqueNamingStrategy{
+				UName: uname,
+			},
+		})
+
+		user, err := schema.Parse(&LongString{}, &sync.Map{}, db.Config.NamingStrategy)
+		if err != nil {
+			t.Fatalf("failed to parse user unique, got error %v", err)
+		}
+
+		constraints := user.ParseUniqueConstraints()
+		if len(constraints) != 1 {
+			t.Fatalf("failed to find unique constraint, got %v", constraints)
+		}
+
+		for key := range constraints {
+			if key != uname {
+				t.Errorf("failed to find unique constraint, got %v", constraints)
+			}
+		}
+	})
+}
+
+type mockUniqueNamingStrategy struct {
+	UName string
+	schema.NamingStrategy
+}
+
+func (a mockUniqueNamingStrategy) UniqueName(table, column string) string {
+	return a.UName
+}
diff --git a/tests/tests_test.go b/tests/tests_test.go
index f9c6cab..a127734 100644
--- a/tests/tests_test.go
+++ b/tests/tests_test.go
@@ -107,7 +107,7 @@ func OpenTestConnection(cfg *gorm.Config) (db *gorm.DB, err error) {
 
 func RunMigrations() {
 	var err error
-	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}, &Parent{}, &Child{}}
+	allModels := []interface{}{&User{}, &Account{}, &Pet{}, &Company{}, &Toy{}, &Language{}, &Coupon{}, &CouponProduct{}, &Order{}, &Parent{}, &Child{}, &Tools{}}
 	rand.Seed(time.Now().UnixNano())
 	rand.Shuffle(len(allModels), func(i, j int) { allModels[i], allModels[j] = allModels[j], allModels[i] })
 
diff --git a/tests/update_test.go b/tests/update_test.go
index b719cc4..9eb9dbf 100644
--- a/tests/update_test.go
+++ b/tests/update_test.go
@@ -882,3 +882,52 @@ func TestSaveWithHooks(t *testing.T) {
 		t.Errorf(`token content should be "token2_encrypted", but got: "%s"`, o2.Token.Content)
 	}
 }
+
+// only postgres, sqlserver, sqlite support update from
+func TestUpdateFrom(t *testing.T) {
+	if DB.Dialector.Name() != "postgres" && DB.Dialector.Name() != "sqlite" && DB.Dialector.Name() != "sqlserver" {
+		return
+	}
+
+	users := []*User{
+		GetUser("update-from-1", Config{Account: true}),
+		GetUser("update-from-2", Config{Account: true}),
+		GetUser("update-from-3", Config{}),
+	}
+
+	if err := DB.Create(&users).Error; err != nil {
+		t.Fatalf("errors happened when create: %v", err)
+	} else if users[0].ID == 0 {
+		t.Fatalf("user's primary value should not zero, %v", users[0].ID)
+	} else if users[0].UpdatedAt.IsZero() {
+		t.Fatalf("user's updated at should not zero, %v", users[0].UpdatedAt)
+	}
+
+	if rowsAffected := DB.Model(&User{}).Clauses(clause.From{Tables: []clause.Table{{Name: "accounts"}}}).Where("accounts.user_id = users.id AND accounts.number = ? AND accounts.deleted_at IS NULL", users[0].Account.Number).Update("name", "franco").RowsAffected; rowsAffected != 1 {
+		t.Errorf("should only update one record, but got %v", rowsAffected)
+	}
+
+	var result User
+	if err := DB.Where("id = ?", users[0].ID).First(&result).Error; err != nil {
+		t.Errorf("errors happened when query before user: %v", err)
+	} else if result.UpdatedAt.UnixNano() == users[0].UpdatedAt.UnixNano() {
+		t.Errorf("user's updated at should be changed, but got %v, was %v", result.UpdatedAt, users[0].UpdatedAt)
+	} else if result.Name != "franco" {
+		t.Errorf("user's name should be updated")
+	}
+
+	if rowsAffected := DB.Model(&User{}).Clauses(clause.From{Tables: []clause.Table{{Name: "accounts"}}}).Where("accounts.user_id = users.id AND accounts.number IN ? AND accounts.deleted_at IS NULL", []string{users[0].Account.Number, users[1].Account.Number}).Update("name", gorm.Expr("accounts.number")).RowsAffected; rowsAffected != 2 {
+		t.Errorf("should update two records, but got %v", rowsAffected)
+	}
+
+	var results []User
+	if err := DB.Preload("Account").Find(&results, []uint{users[0].ID, users[1].ID}).Error; err != nil {
+		t.Errorf("Not error should happen when finding users, but got %v", err)
+	}
+
+	for _, user := range results {
+		if user.Name != user.Account.Number {
+			t.Errorf("user's name should be equal to the account's number %v, but got %v", user.Account.Number, user.Name)
+		}
+	}
+}
diff --git a/utils/tests/models.go b/utils/tests/models.go
index a4bad2f..f9f4f50 100644
--- a/utils/tests/models.go
+++ b/utils/tests/models.go
@@ -20,7 +20,8 @@ type User struct {
 	Account   Account
 	Pets      []*Pet
 	NamedPet  *Pet
-	Toys      []Toy `gorm:"polymorphic:Owner"`
+	Toys      []Toy   `gorm:"polymorphic:Owner"`
+	Tools     []Tools `gorm:"polymorphicType:Type;polymorphicId:CustomID"`
 	CompanyID *int
 	Company   Company
 	ManagerID *uint
@@ -51,6 +52,13 @@ type Toy struct {
 	OwnerType string
 }
 
+type Tools struct {
+	gorm.Model
+	Name     string
+	CustomID string
+	Type     string
+}
+
 type Company struct {
 	ID   int
 	Name string
diff --git a/utils/utils_test.go b/utils/utils_test.go
index d048682..8ff42af 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -48,8 +48,10 @@ func TestToStringKey(t *testing.T) {
 	}{
 		{[]interface{}{"a"}, "a"},
 		{[]interface{}{1, 2, 3}, "1_2_3"},
+		{[]interface{}{1, nil, 3}, "1_nil_3"},
 		{[]interface{}{[]interface{}{1, 2, 3}}, "[1 2 3]"},
 		{[]interface{}{[]interface{}{"1", "2", "3"}}, "[1 2 3]"},
+		{[]interface{}{[]interface{}{"1", nil, "3"}}, "[1 <nil> 3]"},
 	}
 	for _, c := range cases {
 		if key := ToStringKey(c.values...); key != c.key {