Rollback on constraint failure (#1071)
Always rollback on a commit errorsqlite-amalgamation-3390400
parent
f92b6bb2a1
commit
4ef63c9c0d
|
@ -494,10 +494,12 @@ func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
|
||||||
// Commit transaction.
|
// Commit transaction.
|
||||||
func (tx *SQLiteTx) Commit() error {
|
func (tx *SQLiteTx) Commit() error {
|
||||||
_, err := tx.c.exec(context.Background(), "COMMIT", nil)
|
_, err := tx.c.exec(context.Background(), "COMMIT", nil)
|
||||||
if err != nil && err.(Error).Code == C.SQLITE_BUSY {
|
if err != nil {
|
||||||
// sqlite3 will leave the transaction open in this scenario.
|
// sqlite3 may leave the transaction open in this scenario.
|
||||||
// However, database/sql considers the transaction complete once we
|
// However, database/sql considers the transaction complete once we
|
||||||
// return from Commit() - we must clean up to honour its semantics.
|
// 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)
|
tx.c.exec(context.Background(), "ROLLBACK", nil)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -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) {
|
func TestRecursiveTriggers(t *testing.T) {
|
||||||
cases := map[string]bool{
|
cases := map[string]bool{
|
||||||
"?_recursive_triggers=1": true,
|
"?_recursive_triggers=1": true,
|
||||||
|
|
Loading…
Reference in New Issue