From 66a730893f002a37e33a22cc0107d15eb511ab89 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Wed, 8 Mar 2023 18:05:18 +0000 Subject: [PATCH] Fix readonly transaction rollback. --- driver/driver.go | 44 ++++++++++++++++++++++++++++--------------- driver/driver_test.go | 14 +++++++++++++- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/driver/driver.go b/driver/driver.go index a3ee072..0638056 100644 --- a/driver/driver.go +++ b/driver/driver.go @@ -86,9 +86,10 @@ func (sqlite) Open(name string) (_ driver.Conn, err error) { } type conn struct { - conn *sqlite3.Conn - txBegin string - txCommit string + conn *sqlite3.Conn + txBegin string + txCommit string + txRollback string } var ( @@ -107,26 +108,39 @@ func (c conn) Begin() (driver.Tx, error) { } func (c conn) BeginTx(_ context.Context, opts driver.TxOptions) (driver.Tx, error) { - switch opts.Isolation { - default: - return nil, isolationErr - case driver.IsolationLevel(sql.LevelDefault): - case driver.IsolationLevel(sql.LevelSerializable): - } - txBegin := c.txBegin c.txCommit = `COMMIT` + c.txRollback = `ROLLBACK` + if opts.ReadOnly { query_only, err := c.conn.Pragma("query_only") if err != nil { return nil, err } - c.txCommit = ` - ROLLBACK; - PRAGMA query_only=` + query_only[0] txBegin = ` BEGIN deferred; PRAGMA query_only=on` + c.txCommit = ` + ROLLBACK; + PRAGMA query_only=` + query_only[0] + c.txRollback = c.txCommit + } + + switch opts.Isolation { + default: + return nil, isolationErr + case + driver.IsolationLevel(sql.LevelDefault), + driver.IsolationLevel(sql.LevelSerializable): + break + case driver.IsolationLevel(sql.LevelReadUncommitted): + read_uncommitted, err := c.conn.Pragma("read_uncommitted") + if err != nil { + return nil, err + } + txBegin += `; PRAGMA read_uncommitted=on` + c.txCommit += `; PRAGMA read_uncommitted=` + read_uncommitted[0] + c.txRollback += `; PRAGMA read_uncommitted=` + read_uncommitted[0] } err := c.conn.Exec(txBegin) @@ -138,14 +152,14 @@ func (c conn) BeginTx(_ context.Context, opts driver.TxOptions) (driver.Tx, erro func (c conn) Commit() error { err := c.conn.Exec(c.txCommit) - if err != nil { + if err != nil && !c.conn.GetAutocommit() { c.Rollback() } return err } func (c conn) Rollback() error { - return c.conn.Exec(`ROLLBACK`) + return c.conn.Exec(c.txRollback) } func (c conn) Prepare(query string) (driver.Stmt, error) { diff --git a/driver/driver_test.go b/driver/driver_test.go index 4ab9c1d..b1fed7b 100644 --- a/driver/driver_test.go +++ b/driver/driver_test.go @@ -134,7 +134,9 @@ func Test_BeginTx(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - db, err := sql.Open("sqlite3", filepath.Join(t.TempDir(), "test.db")) + db, err := sql.Open("sqlite3", "file:"+ + filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+ + "?_txlock=exclusive&_pragma=busy_timeout(0)") if err != nil { t.Fatal(err) } @@ -145,6 +147,16 @@ func Test_BeginTx(t *testing.T) { t.Error("want isolationErr") } + tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted}) + if err != nil { + t.Fatal(err) + } + + err = tx.Rollback() + if err != nil { + t.Fatal(err) + } + tx1, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true}) if err != nil { t.Fatal(err)