aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Hull <joshbuddy@gmail.com>2022-09-02 02:45:11 +0000
committerGitHub <noreply@github.com>2022-09-01 22:45:11 -0400
commit4ef63c9c0db77925ab91b95237f9e3802c4710a4 (patch)
tree68be44c04811559551b31a13f0c17e3bb6e5ebf0
parentFix TestQueryer test to use exec for multistatement insertion (diff)
downloadgolite-4ef63c9c0db77925ab91b95237f9e3802c4710a4.tar.gz
golite-4ef63c9c0db77925ab91b95237f9e3802c4710a4.tar.xz
Rollback on constraint failure (#1071)
Always rollback on a commit error
-rw-r--r--sqlite3.go6
-rw-r--r--sqlite3_test.go37
2 files changed, 41 insertions, 2 deletions
diff --git a/sqlite3.go b/sqlite3.go
index e037857..9c0f4d8 100644
--- a/sqlite3.go
+++ b/sqlite3.go
@@ -494,10 +494,12 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
// Commit transaction.
func (tx *SQLiteTx) Commit() error {
_, err := tx.c.exec(context.Background(), "COMMIT", nil)
- if err != nil && err.(Error).Code == C.SQLITE_BUSY {
- // sqlite3 will leave the transaction open in this scenario.
+ if err != nil {
+ // sqlite3 may leave the transaction open in this scenario.
// However, database/sql considers the transaction complete once we
// return from Commit() - we must clean up to honour its semantics.
+ // We don't know if the ROLLBACK is strictly necessary, but according
+ // to sqlite's docs, there is no harm in calling ROLLBACK unnecessarily.
tx.c.exec(context.Background(), "ROLLBACK", nil)
}
return err
diff --git a/sqlite3_test.go b/sqlite3_test.go
index 33d03fd..326361e 100644
--- a/sqlite3_test.go
+++ b/sqlite3_test.go
@@ -248,6 +248,43 @@ func TestForeignKeys(t *testing.T) {
}
}
+func TestDeferredForeignKey(t *testing.T) {
+ fname := TempFilename(t)
+ uri := "file:" + fname + "?_foreign_keys=1"
+ db, err := sql.Open("sqlite3", uri)
+ if err != nil {
+ os.Remove(fname)
+ t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err)
+ }
+ _, err = db.Exec("CREATE TABLE bar (id INTEGER PRIMARY KEY)")
+ if err != nil {
+ t.Errorf("failed creating tables: %v", err)
+ }
+ _, err = db.Exec("CREATE TABLE foo (bar_id INTEGER, FOREIGN KEY(bar_id) REFERENCES bar(id) DEFERRABLE INITIALLY DEFERRED)")
+ if err != nil {
+ t.Errorf("failed creating tables: %v", err)
+ }
+ tx, err := db.Begin()
+ if err != nil {
+ t.Errorf("Failed to begin transaction: %v", err)
+ }
+ _, err = tx.Exec("INSERT INTO foo (bar_id) VALUES (123)")
+ if err != nil {
+ t.Errorf("Failed to insert row: %v", err)
+ }
+ err = tx.Commit()
+ if err == nil {
+ t.Errorf("Expected an error: %v", err)
+ }
+ _, err = db.Begin()
+ if err != nil {
+ t.Errorf("Failed to begin transaction: %v", err)
+ }
+
+ db.Close()
+ os.Remove(fname)
+}
+
func TestRecursiveTriggers(t *testing.T) {
cases := map[string]bool{
"?_recursive_triggers=1": true,