Skip to content

Commit 4ef63c9

Browse files
authored
Rollback on constraint failure (#1071)
Always rollback on a commit error
1 parent f92b6bb commit 4ef63c9

File tree

2 files changed

+41
-2
lines changed

2 files changed

+41
-2
lines changed

Diff for: sqlite3.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -494,10 +494,12 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
494494
// Commit transaction.
495495
func (tx *SQLiteTx) Commit() error {
496496
_, err := tx.c.exec(context.Background(), "COMMIT", nil)
497-
if err != nil && err.(Error).Code == C.SQLITE_BUSY {
498-
// sqlite3 will leave the transaction open in this scenario.
497+
if err != nil {
498+
// sqlite3 may leave the transaction open in this scenario.
499499
// However, database/sql considers the transaction complete once we
500500
// return from Commit() - we must clean up to honour its semantics.
501+
// We don't know if the ROLLBACK is strictly necessary, but according
502+
// to sqlite's docs, there is no harm in calling ROLLBACK unnecessarily.
501503
tx.c.exec(context.Background(), "ROLLBACK", nil)
502504
}
503505
return err

Diff for: sqlite3_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,43 @@ func TestForeignKeys(t *testing.T) {
248248
}
249249
}
250250

251+
func TestDeferredForeignKey(t *testing.T) {
252+
fname := TempFilename(t)
253+
uri := "file:" + fname + "?_foreign_keys=1"
254+
db, err := sql.Open("sqlite3", uri)
255+
if err != nil {
256+
os.Remove(fname)
257+
t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err)
258+
}
259+
_, err = db.Exec("CREATE TABLE bar (id INTEGER PRIMARY KEY)")
260+
if err != nil {
261+
t.Errorf("failed creating tables: %v", err)
262+
}
263+
_, err = db.Exec("CREATE TABLE foo (bar_id INTEGER, FOREIGN KEY(bar_id) REFERENCES bar(id) DEFERRABLE INITIALLY DEFERRED)")
264+
if err != nil {
265+
t.Errorf("failed creating tables: %v", err)
266+
}
267+
tx, err := db.Begin()
268+
if err != nil {
269+
t.Errorf("Failed to begin transaction: %v", err)
270+
}
271+
_, err = tx.Exec("INSERT INTO foo (bar_id) VALUES (123)")
272+
if err != nil {
273+
t.Errorf("Failed to insert row: %v", err)
274+
}
275+
err = tx.Commit()
276+
if err == nil {
277+
t.Errorf("Expected an error: %v", err)
278+
}
279+
_, err = db.Begin()
280+
if err != nil {
281+
t.Errorf("Failed to begin transaction: %v", err)
282+
}
283+
284+
db.Close()
285+
os.Remove(fname)
286+
}
287+
251288
func TestRecursiveTriggers(t *testing.T) {
252289
cases := map[string]bool{
253290
"?_recursive_triggers=1": true,

0 commit comments

Comments
 (0)