Files
sqlite3/tests/txn_test.go

612 lines
10 KiB
Go
Raw Permalink Normal View History

2023-02-24 14:31:41 +00:00
package tests
import (
"context"
"errors"
"testing"
"github.com/ncruces/go-sqlite3"
2023-11-23 03:28:56 +00:00
_ "github.com/ncruces/go-sqlite3/embed"
2024-06-02 10:33:20 +01:00
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
2024-08-05 21:25:47 +01:00
"github.com/ncruces/go-sqlite3/vfs/memdb"
2023-02-24 14:31:41 +00:00
)
2023-02-26 03:22:08 +00:00
func TestConn_Transaction_exec(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-26 03:22:08 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-01-27 10:05:31 +00:00
db.RollbackHook(func() {})
2024-01-27 10:57:46 +00:00
db.CommitHook(func() bool { return true })
db.UpdateHook(func(sqlite3.AuthorizerActionCode, string, string, int64) {})
2024-01-27 10:05:31 +00:00
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
errFailed := errors.New("failed")
count := func() int {
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
2023-02-26 03:22:08 +00:00
if stmt.Step() {
return stmt.ColumnInt(0)
}
t.Fatal(stmt.Err())
return 0
}
insert := func(succeed bool) (err error) {
tx := db.Begin()
defer tx.End(&err)
err = db.Exec(`INSERT INTO test VALUES ('hello')`)
if err != nil {
t.Fatal(err)
}
2024-01-17 15:39:13 +00:00
if s := db.TxnState("main"); s != sqlite3.TXN_WRITE {
t.Errorf("got %d", s)
}
2023-02-26 03:22:08 +00:00
if succeed {
return nil
}
return errFailed
}
err = insert(true)
if err != nil {
t.Fatal(err)
}
if got := count(); got != 1 {
t.Errorf("got %d, want 1", got)
}
err = insert(true)
if err != nil {
t.Fatal(err)
}
if got := count(); got != 2 {
t.Errorf("got %d, want 2", got)
}
err = insert(false)
if err != errFailed {
t.Errorf("got %v, want errFailed", err)
}
if got := count(); got != 2 {
t.Errorf("got %d, want 2", got)
}
}
func TestConn_Transaction_panic(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-26 03:22:08 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
err = db.Exec(`INSERT INTO test VALUES ('one');`)
if err != nil {
t.Fatal(err)
}
panics := func() (err error) {
tx := db.Begin()
defer tx.End(&err)
err = db.Exec(`INSERT INTO test VALUES ('hello')`)
if err != nil {
return err
}
panic("omg!")
}
defer func() {
p := recover()
if p != "omg!" {
t.Errorf("got %v, want panic", p)
}
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
2023-02-26 03:22:08 +00:00
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
return
}
t.Fatal(stmt.Err())
}()
err = panics()
if err != nil {
t.Error(err)
}
}
func TestConn_Transaction_interrupt(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-26 03:22:08 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
tx, err := db.BeginImmediate()
if err != nil {
t.Fatal(err)
}
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (1)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
tx.End(&err)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
db.SetInterrupt(ctx)
tx, err = db.BeginExclusive()
if err != nil {
t.Fatal(err)
}
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (2)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
err = db.CacheFlush()
if err != nil {
t.Fatal(err)
}
2023-02-26 03:22:08 +00:00
cancel()
_, err = db.BeginImmediate()
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
}
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (3)`)
2023-02-26 03:22:08 +00:00
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
}
2023-03-08 16:29:29 +00:00
err = nil
tx.End(&err)
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
2023-02-26 03:22:08 +00:00
}
db.SetInterrupt(context.Background())
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
}
}
2023-03-08 16:29:29 +00:00
func TestConn_Transaction_interrupted(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
db.SetInterrupt(ctx)
cancel()
tx := db.Begin()
err = tx.Commit()
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
}
err = nil
tx.End(&err)
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
}
}
2024-05-19 01:04:56 +01:00
func TestConn_Transaction_busy(t *testing.T) {
t.Parallel()
2024-08-05 21:25:47 +01:00
tmp := memdb.TestDB(t)
2024-05-19 01:04:56 +01:00
2024-08-05 21:25:47 +01:00
db1, err := sqlite3.Open(tmp)
2024-05-19 01:04:56 +01:00
if err != nil {
t.Fatal(err)
}
defer db1.Close()
2024-08-05 21:25:47 +01:00
db2, err := sqlite3.Open(tmp + "&_pragma=busy_timeout(10000)")
2024-05-19 01:04:56 +01:00
if err != nil {
t.Fatal(err)
}
defer db2.Close()
err = db1.Exec(`CREATE TABLE test (col)`)
if err != nil {
t.Fatal(err)
}
tx, err := db1.BeginImmediate()
if err != nil {
t.Fatal(err)
}
err = db1.Exec(`INSERT INTO test VALUES (1)`)
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
db2.SetInterrupt(ctx)
go cancel()
_, err = db2.BeginExclusive()
2024-05-20 01:10:13 +01:00
if !errors.Is(err, sqlite3.BUSY) && !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.BUSY or sqlite3.INTERRUPT", err)
2024-05-19 01:04:56 +01:00
}
err = nil
tx.End(&err)
if err != nil {
t.Fatal(err)
}
}
2023-02-26 03:22:08 +00:00
func TestConn_Transaction_rollback(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-26 03:22:08 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
tx := db.Begin()
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (1)`)
2023-02-26 03:22:08 +00:00
if err != nil {
t.Fatal(err)
}
err = db.Exec(`COMMIT`)
if err != nil {
t.Fatal(err)
}
tx.End(&err)
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
}
}
2024-07-24 20:03:23 +01:00
func TestConn_Transaction_concurrent(t *testing.T) {
t.Parallel()
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
_, err = db.BeginConcurrent()
if !errors.Is(err, sqlite3.ERROR) {
t.Errorf("got %v, want sqlite3.ERROR", err)
}
}
2023-02-24 14:31:41 +00:00
func TestConn_Savepoint_exec(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-24 14:31:41 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
errFailed := errors.New("failed")
count := func() int {
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
2023-02-24 14:31:41 +00:00
if stmt.Step() {
return stmt.ColumnInt(0)
}
t.Fatal(stmt.Err())
return 0
}
insert := func(succeed bool) (err error) {
2023-03-08 16:29:29 +00:00
defer db.Savepoint().Release(&err)
2023-02-24 14:31:41 +00:00
err = db.Exec(`INSERT INTO test VALUES ('hello')`)
if err != nil {
t.Fatal(err)
}
if succeed {
return nil
}
return errFailed
}
err = insert(true)
if err != nil {
t.Fatal(err)
}
if got := count(); got != 1 {
t.Errorf("got %d, want 1", got)
}
err = insert(true)
if err != nil {
t.Fatal(err)
}
if got := count(); got != 2 {
t.Errorf("got %d, want 2", got)
}
err = insert(false)
if err != errFailed {
t.Errorf("got %v, want errFailed", err)
}
if got := count(); got != 2 {
t.Errorf("got %d, want 2", got)
}
}
func TestConn_Savepoint_panic(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-24 14:31:41 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
err = db.Exec(`INSERT INTO test VALUES ('one');`)
if err != nil {
t.Fatal(err)
}
panics := func() (err error) {
2023-03-08 16:29:29 +00:00
defer db.Savepoint().Release(&err)
2023-02-24 14:31:41 +00:00
err = db.Exec(`INSERT INTO test VALUES ('hello')`)
if err != nil {
return err
}
panic("omg!")
}
defer func() {
p := recover()
if p != "omg!" {
t.Errorf("got %v, want panic", p)
}
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
2023-02-24 14:31:41 +00:00
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
return
}
t.Fatal(stmt.Err())
}()
err = panics()
if err != nil {
t.Error(err)
}
}
func TestConn_Savepoint_interrupt(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-24 14:31:41 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
2023-03-08 16:29:29 +00:00
savept := db.Savepoint()
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (1)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
2023-03-08 16:29:29 +00:00
savept.Release(&err)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
2023-02-24 14:56:49 +00:00
db.SetInterrupt(ctx)
2023-02-24 14:31:41 +00:00
2023-03-08 16:29:29 +00:00
savept1 := db.Savepoint()
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (2)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
2023-03-08 16:29:29 +00:00
savept2 := db.Savepoint()
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (3)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
cancel()
2023-03-08 16:29:29 +00:00
db.Savepoint().Release(&err)
2023-02-25 15:11:07 +00:00
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
}
2023-02-24 14:31:41 +00:00
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (4)`)
2023-02-25 15:11:07 +00:00
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
}
2023-02-24 14:31:41 +00:00
err = context.Canceled
2023-03-08 16:29:29 +00:00
savept2.Release(&err)
2023-02-24 14:31:41 +00:00
if err != context.Canceled {
t.Fatal(err)
}
2023-03-08 16:29:29 +00:00
err = nil
savept1.Release(&err)
if !errors.Is(err, sqlite3.INTERRUPT) {
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
2023-02-25 15:11:07 +00:00
}
2023-02-24 14:31:41 +00:00
2023-02-25 15:11:07 +00:00
db.SetInterrupt(context.Background())
2023-02-24 14:31:41 +00:00
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
}
}
func TestConn_Savepoint_rollback(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-24 14:31:41 +00:00
db, err := sqlite3.Open(":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
2024-04-04 01:25:52 +01:00
err = db.Exec(`CREATE TABLE test (col)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
2023-03-08 16:29:29 +00:00
savept := db.Savepoint()
2023-02-27 12:07:48 +00:00
err = db.Exec(`INSERT INTO test VALUES (1)`)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
err = db.Exec(`COMMIT`)
if err != nil {
t.Fatal(err)
}
2023-03-08 16:29:29 +00:00
savept.Release(&err)
2023-02-24 14:31:41 +00:00
if err != nil {
t.Fatal(err)
}
stmt, _, err := db.Prepare(`SELECT count(*) FROM test`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
if stmt.Step() {
got := stmt.ColumnInt(0)
if got != 1 {
t.Errorf("got %d, want 1", got)
}
}
err = stmt.Err()
if err != nil {
t.Error(err)
}
}