Repository: go-gorm/gorm
Pull request #5048 (2022-02-03 15:06:01)

fix: replace empty table name result in panic
<!--
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.
-->

- [] Do only one thing
- [] Non breaking API changes
- [] Tested

### What did this pull request do?

-  en: fix replace empty table name result in panic.  if replaced name equal to `""` , should return name.
-  zh: 修复替换 table name 为空时会导致 panic.    如果替换后的 name 等于 `""` 就将 name 返回.

<!--
provide a general description of the code changes in your pull request
-->

### User Case Description


<!-- Your use case -->

```
func TestReplaceEmptyTableName(t *testing.T) {
	ns := NamingStrategy{
		SingularTable: true,
		NameReplacer:  strings.NewReplacer("Model", ""),
	}
	tableName := ns.TableName("Model")
	if tableName != "Model" {
		t.Errorf("invalid table name generated, got %v", tableName)
	}
}
```



Patch:
diff --git a/finisher_api.go b/finisher_api.go
index cbbd48c..3a17997 100644
--- a/finisher_api.go
+++ b/finisher_api.go
@@ -255,7 +255,7 @@ func (tx *DB) assignInterfacesToValue(values ...interface{}) {
 		}
 	}
 }
-
+// FirstOrInit gets the first matched record or initialize a new instance with given conditions (only works with struct or map conditions)
 func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
 	queryTx := db.Limit(1).Order(clause.OrderByColumn{
 		Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
@@ -281,6 +281,7 @@ func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
 	return
 }
 
+// FirstOrCreate gets the first matched record or create a new one with given conditions (only works with struct, map conditions)
 func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {
 	queryTx := db.Limit(1).Order(clause.OrderByColumn{
 		Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
diff --git a/schema/naming.go b/schema/naming.go
index 8407bff..a4e3a75 100644
--- a/schema/naming.go
+++ b/schema/naming.go
@@ -120,7 +120,13 @@ func (ns NamingStrategy) toDBName(name string) string {
 	}
 
 	if ns.NameReplacer != nil {
-		name = ns.NameReplacer.Replace(name)
+		tmpName := ns.NameReplacer.Replace(name)
+
+		if tmpName == "" {
+			return name
+		}
+
+		name = tmpName
 	}
 
 	if ns.NoLowerCase {


Test patch:
diff --git a/schema/naming_test.go b/schema/naming_test.go
index c3e6bf9..1fdab9a 100644
--- a/schema/naming_test.go
+++ b/schema/naming_test.go
@@ -197,3 +197,14 @@ func TestFormatNameWithStringLongerThan64Characters(t *testing.T) {
 		t.Errorf("invalid formatted name generated, got %v", formattedName)
 	}
 }
+
+func TestReplaceEmptyTableName(t *testing.T) {
+	ns := NamingStrategy{
+		SingularTable: true,
+		NameReplacer:  strings.NewReplacer("Model", ""),
+	}
+	tableName := ns.TableName("Model")
+	if tableName != "Model" {
+		t.Errorf("invalid table name generated, got %v", tableName)
+	}
+}
diff --git a/tests/go.mod b/tests/go.mod
index f2addaa..5488c17 100644
--- a/tests/go.mod
+++ b/tests/go.mod
@@ -5,11 +5,11 @@ go 1.14
 require (
 	github.com/denisenkom/go-mssqldb v0.12.0 // indirect
 	github.com/google/uuid v1.3.0
-	github.com/jackc/pgx/v4 v4.14.1 // indirect
+	github.com/jackc/pgx/v4 v4.15.0 // indirect
 	github.com/jinzhu/now v1.1.4
 	github.com/lib/pq v1.10.4
 	github.com/mattn/go-sqlite3 v1.14.11 // indirect
-	golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed // indirect
+	golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab // indirect
 	gorm.io/driver/mysql v1.2.3
 	gorm.io/driver/postgres v1.2.3
 	gorm.io/driver/sqlite v1.2.6
diff --git a/tests/query_test.go b/tests/query_test.go
index c99214b..d10df18 100644
--- a/tests/query_test.go
+++ b/tests/query_test.go
@@ -512,7 +512,13 @@ func TestNotWithAllFields(t *testing.T) {
 func TestOr(t *testing.T) {
 	dryDB := DB.Session(&gorm.Session{DryRun: true})
 
-	result := dryDB.Where("role = ?", "admin").Where(DB.Or("role = ?", "super_admin")).Find(&User{})
+	var count int64
+	result := dryDB.Model(&User{}).Or("role = ?", "admin").Count(&count)
+	if !regexp.MustCompile("SELECT count\\(\\*\\) FROM .*users.* WHERE role = .+ AND .*users.*\\..*deleted_at.* IS NULL").MatchString(result.Statement.SQL.String()) {
+		t.Fatalf("Build OR condition, but got %v", result.Statement.SQL.String())
+	}
+
+	result = dryDB.Where("role = ?", "admin").Where(DB.Or("role = ?", "super_admin")).Find(&User{})
 	if !regexp.MustCompile("SELECT \\* FROM .*users.* WHERE .*role.* = .+ AND .*role.* = .+").MatchString(result.Statement.SQL.String()) {
 		t.Fatalf("Build OR condition, but got %v", result.Statement.SQL.String())
 	}