Skip to content

Commit ecb1fa3

Browse files
committed
Merge remote-tracking branch 'origin/main' into tinglwan-fix-miscellaneous-tests
2 parents c02274d + 97118aa commit ecb1fa3

File tree

5 files changed

+117
-25
lines changed

5 files changed

+117
-25
lines changed

oracle/delete.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ func Delete(db *gorm.DB) {
9393
addPrimaryKeyWhereClause(db)
9494
}
9595

96+
// redirect soft-delete to update clause with bulk returning
97+
if stmt.Schema != nil {
98+
if deletedAtField := stmt.Schema.LookUpField("deleted_at"); deletedAtField != nil && !stmt.Unscoped {
99+
for _, c := range stmt.Schema.DeleteClauses {
100+
stmt.AddClause(c)
101+
}
102+
delete(stmt.Clauses, "DELETE")
103+
delete(stmt.Clauses, "FROM")
104+
stmt.SQL.Reset()
105+
stmt.Vars = stmt.Vars[:0]
106+
stmt.AddClauseIfNotExists(clause.Update{})
107+
Update(db)
108+
return
109+
}
110+
}
111+
96112
// This prevents soft deletes from bypassing the safety check
97113
checkMissingWhereConditions(db)
98114
if db.Error != nil {
@@ -432,25 +448,7 @@ func executeDelete(db *gorm.DB) {
432448
_, hasReturning := stmt.Clauses["RETURNING"]
433449

434450
if hasReturning {
435-
// For RETURNING, we need to check if it's a soft delete or hard delete
436-
if stmt.Schema != nil {
437-
if deletedAtField := stmt.Schema.LookUpField("deleted_at"); deletedAtField != nil && !stmt.Unscoped {
438-
// Soft delete with RETURNING - use QueryContext
439-
if rows, err := stmt.ConnPool.QueryContext(stmt.Context, stmt.SQL.String(), stmt.Vars...); err == nil {
440-
defer rows.Close()
441-
gorm.Scan(rows, db, gorm.ScanInitialized)
442-
443-
if stmt.Result != nil {
444-
stmt.Result.RowsAffected = db.RowsAffected
445-
}
446-
} else {
447-
db.AddError(err)
448-
}
449-
return
450-
}
451-
}
452-
453-
// Hard delete with RETURNING - use ExecContext (for PL/SQL blocks)
451+
// Hard delete & soft delete with RETURNING - use ExecContext (for PL/SQL blocks)
454452
result, err := stmt.ConnPool.ExecContext(stmt.Context, stmt.SQL.String(), stmt.Vars...)
455453
if err == nil {
456454
db.RowsAffected, _ = result.RowsAffected()

tests/connection_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ package tests
4040

4141
import (
4242
"fmt"
43+
"log"
44+
"os"
45+
"sync"
4346
"testing"
4447

4548
"gorm.io/gorm"
@@ -65,3 +68,91 @@ func TestWithSingleConnection(t *testing.T) {
6568
t.Errorf("WithSingleConnection() method should get correct value, expect: %v, got %v", expectedString, actualString)
6669
}
6770
}
71+
72+
func TestConnectionWithInvalidQuery(t *testing.T) {
73+
err := DB.Connection(func(tx *gorm.DB) error {
74+
return tx.Exec("SELECT * FROM non_existent_table").Error
75+
})
76+
if err == nil {
77+
t.Fatalf("Expected error for invalid query in Connection, got nil")
78+
}
79+
}
80+
81+
func TestMultipleSequentialConnections(t *testing.T) {
82+
for i := 0; i < 20; i++ {
83+
var val int
84+
err := DB.Connection(func(tx *gorm.DB) error {
85+
return tx.Raw("SELECT 1 FROM dual").Scan(&val).Error
86+
})
87+
if err != nil {
88+
t.Fatalf("Sequential Connection #%d failed: %v", i+1, err)
89+
}
90+
if val != 1 {
91+
t.Fatalf("Sequential Connection #%d got wrong result: %v", i+1, val)
92+
}
93+
}
94+
}
95+
96+
func TestConnectionAfterDBClose(t *testing.T) {
97+
sqlDB, err := DB.DB()
98+
if err != nil {
99+
t.Fatalf("DB.DB() should not fail, got: %v", err)
100+
}
101+
err = sqlDB.Close()
102+
if err != nil {
103+
t.Fatalf("sqlDB.Close() failed: %v", err)
104+
}
105+
cerr := DB.Connection(func(tx *gorm.DB) error {
106+
var v int
107+
return tx.Raw("SELECT 1 FROM dual").Scan(&v).Error
108+
})
109+
if cerr == nil {
110+
t.Fatalf("Expected error when calling Connection after DB closed, got nil")
111+
}
112+
if DB, err = OpenTestConnection(&gorm.Config{Logger: newLogger}); err != nil {
113+
log.Printf("failed to connect database, got error %v", err)
114+
os.Exit(1)
115+
}
116+
}
117+
118+
func TestConnectionHandlesPanic(t *testing.T) {
119+
defer func() {
120+
if r := recover(); r == nil {
121+
t.Fatalf("Expected panic inside Connection, but none occurred")
122+
}
123+
}()
124+
DB.Connection(func(tx *gorm.DB) error {
125+
panic("panic in connection callback")
126+
})
127+
t.Fatalf("Should have panicked inside connection callback")
128+
}
129+
130+
func TestConcurrentConnections(t *testing.T) {
131+
const goroutines = 10
132+
var wg sync.WaitGroup
133+
wg.Add(goroutines)
134+
errChan := make(chan error, goroutines)
135+
136+
for i := 0; i < goroutines; i++ {
137+
go func(i int) {
138+
defer wg.Done()
139+
var val int
140+
err := DB.Connection(func(tx *gorm.DB) error {
141+
return tx.Raw("SELECT ? FROM dual", i).Scan(&val).Error
142+
})
143+
if err != nil {
144+
errChan <- fmt.Errorf("goroutine #%d: connection err: %v", i, err)
145+
return
146+
}
147+
if val != i {
148+
errChan <- fmt.Errorf("goroutine #%d: got wrong result: %v", i, val)
149+
}
150+
}(i)
151+
}
152+
153+
wg.Wait()
154+
close(errChan)
155+
for err := range errChan {
156+
t.Error(err)
157+
}
158+
}

tests/delete_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@ func TestDeleteSliceWithAssociations(t *testing.T) {
248248

249249
// only sqlite, postgres, sqlserver support returning
250250
func TestSoftDeleteReturning(t *testing.T) {
251-
t.Skip()
252251
users := []*User{
253252
GetUser("delete-returning-1", Config{}),
254253
GetUser("delete-returning-2", Config{}),
@@ -257,13 +256,13 @@ func TestSoftDeleteReturning(t *testing.T) {
257256
DB.Create(&users)
258257

259258
var results []User
260-
DB.Where("name IN ?", []string{users[0].Name, users[1].Name}).Clauses(clause.Returning{}).Delete(&results)
259+
DB.Where("\"name\" IN ?", []string{users[0].Name, users[1].Name}).Clauses(clause.Returning{}).Delete(&results)
261260
if len(results) != 2 {
262261
t.Errorf("failed to return delete data, got %v", results)
263262
}
264263

265264
var count int64
266-
DB.Model(&User{}).Where("name IN ?", []string{users[0].Name, users[1].Name, users[2].Name}).Count(&count)
265+
DB.Model(&User{}).Where("\"name\" IN ?", []string{users[0].Name, users[1].Name, users[2].Name}).Count(&count)
267266
if count != 1 {
268267
t.Errorf("failed to delete data, current count %v", count)
269268
}

tests/joins_test.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,6 @@ func TestJoinCount(t *testing.T) {
252252
}
253253

254254
func TestJoinWithSoftDeleted(t *testing.T) {
255-
t.Skip()
256-
257255
user := GetUser("TestJoinWithSoftDeletedUser", Config{Account: true, NamedPet: true})
258256
DB.Create(&user)
259257

tests/passed-tests.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ TestPluginCallbacks
3838
TestCallbacksGet
3939
TestCallbacksRemove
4040
TestWithSingleConnection
41+
TestConnectionWithInvalidQuery
42+
TestNestedConnection
43+
TestMultipleSequentialConnections
44+
TestConnectionAfterDBClose
45+
TestConnectionHandlesPanic
46+
TestConcurrentConnections
4147
TestCountWithGroup
4248
TestCount
4349
TestCountOnEmptyTable
@@ -79,7 +85,7 @@ TestBlockGlobalDelete
7985
TestDeleteWithAssociations
8086
TestDeleteAssociationsWithUnscoped
8187
TestDeleteSliceWithAssociations
82-
#TestSoftDeleteReturning
88+
TestSoftDeleteReturning
8389
TestDeleteReturning
8490
TestDeleteByPrimaryKeyOnly
8591
TestHardDeleteAfterSoftDelete

0 commit comments

Comments
 (0)