2023-02-22 14:19:56 +00:00
|
|
|
package tests
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
2024-01-18 15:53:00 +00:00
|
|
|
"math"
|
2024-08-05 21:25:47 +01:00
|
|
|
"net/url"
|
2023-06-02 02:31:15 +01:00
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
2023-02-22 14:19:56 +00:00
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/ncruces/go-sqlite3"
|
|
|
|
|
_ "github.com/ncruces/go-sqlite3/embed"
|
2024-06-02 10:33:20 +01:00
|
|
|
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
2024-07-24 12:37:35 +01:00
|
|
|
"github.com/ncruces/go-sqlite3/vfs"
|
2024-08-05 21:25:47 +01:00
|
|
|
"github.com/ncruces/go-sqlite3/vfs/memdb"
|
2024-04-12 14:57:13 +01:00
|
|
|
_ "github.com/ncruces/go-sqlite3/vfs/memdb"
|
2023-02-22 14:19:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestConn_Open_dir(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
2023-03-16 12:27:44 +00:00
|
|
|
_, err := sqlite3.OpenFlags(".", 0)
|
2023-02-22 14:19:56 +00:00
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("want error")
|
|
|
|
|
}
|
2023-02-25 15:11:07 +00:00
|
|
|
if !errors.Is(err, sqlite3.CANTOPEN) {
|
|
|
|
|
t.Errorf("got %v, want sqlite3.CANTOPEN", err)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-05-23 16:34:09 +01:00
|
|
|
func TestConn_Open_notfound(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
_, err := sqlite3.OpenFlags("test.db", sqlite3.OPEN_READONLY)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("want error")
|
|
|
|
|
}
|
|
|
|
|
if !errors.Is(err, sqlite3.CANTOPEN) {
|
|
|
|
|
t.Errorf("got %v, want sqlite3.CANTOPEN", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-02 02:31:15 +01:00
|
|
|
func TestConn_Open_modeof(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
file := filepath.Join(dir, "test.db")
|
|
|
|
|
mode := filepath.Join(dir, "modeof.txt")
|
|
|
|
|
|
|
|
|
|
fd, err := os.OpenFile(mode, os.O_CREATE, 0624)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
fi, err := fd.Stat()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
fd.Close()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open("file:" + file + "?modeof=" + mode)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
di, err := os.Stat(file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
db.Close()
|
|
|
|
|
|
|
|
|
|
if di.Mode() != fi.Mode() {
|
|
|
|
|
t.Errorf("got %v, want %v", di.Mode(), fi.Mode())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = sqlite3.Open("file:" + file + "?modeof=" + mode + "2")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("want error")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-22 14:19:56 +00:00
|
|
|
func TestConn_Close(t *testing.T) {
|
2023-11-22 13:11:23 +00:00
|
|
|
var db *sqlite3.Conn
|
|
|
|
|
db.Close()
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_Close_BUSY(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
stmt, _, err := db.Prepare(`BEGIN`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer stmt.Close()
|
|
|
|
|
|
|
|
|
|
err = db.Close()
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("want error")
|
|
|
|
|
}
|
2023-02-25 15:11:07 +00:00
|
|
|
if !errors.Is(err, sqlite3.BUSY) {
|
|
|
|
|
t.Errorf("got %v, want sqlite3.BUSY", err)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
var terr interface{ Temporary() bool }
|
|
|
|
|
if !errors.As(err, &terr) || !terr.Temporary() {
|
|
|
|
|
t.Error("not temporary", err)
|
|
|
|
|
}
|
|
|
|
|
if got := err.Error(); got != `sqlite3: database is locked: unable to close due to unfinalized statements or unfinished backups` {
|
2023-02-28 14:50:15 +00:00
|
|
|
t.Error("got message:", got)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_SetInterrupt(t *testing.T) {
|
2023-02-27 12:07:48 +00:00
|
|
|
t.Parallel()
|
|
|
|
|
|
2023-02-22 14:19:56 +00:00
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2023-02-24 14:56:49 +00:00
|
|
|
db.SetInterrupt(ctx)
|
2023-02-22 14:19:56 +00:00
|
|
|
|
|
|
|
|
// Interrupt doesn't interrupt this.
|
|
|
|
|
err = db.Exec(`SELECT 1`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 15:11:07 +00:00
|
|
|
db.SetInterrupt(context.Background())
|
2023-02-22 14:19:56 +00:00
|
|
|
|
|
|
|
|
stmt, _, err := db.Prepare(`
|
|
|
|
|
WITH RECURSIVE
|
|
|
|
|
fibonacci (curr, next)
|
|
|
|
|
AS (
|
|
|
|
|
SELECT 0, 1
|
|
|
|
|
UNION ALL
|
|
|
|
|
SELECT next, curr + next FROM fibonacci
|
2024-04-28 10:33:39 +01:00
|
|
|
LIMIT 1e7
|
2023-02-22 14:19:56 +00:00
|
|
|
)
|
|
|
|
|
SELECT min(curr) FROM fibonacci
|
|
|
|
|
`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer stmt.Close()
|
|
|
|
|
|
2023-02-24 14:56:49 +00:00
|
|
|
db.SetInterrupt(ctx)
|
2023-10-25 12:56:52 +01:00
|
|
|
go cancel()
|
2023-02-22 14:19:56 +00:00
|
|
|
|
|
|
|
|
// Interrupting works.
|
|
|
|
|
err = stmt.Exec()
|
2023-02-25 15:11:07 +00:00
|
|
|
if !errors.Is(err, sqlite3.INTERRUPT) {
|
|
|
|
|
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Interrupting sticks.
|
|
|
|
|
err = db.Exec(`SELECT 1`)
|
2023-02-25 15:11:07 +00:00
|
|
|
if !errors.Is(err, sqlite3.INTERRUPT) {
|
|
|
|
|
t.Errorf("got %v, want sqlite3.INTERRUPT", err)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
2023-02-24 14:31:41 +00:00
|
|
|
ctx, cancel = context.WithCancel(context.Background())
|
|
|
|
|
defer cancel()
|
2023-02-24 14:56:49 +00:00
|
|
|
db.SetInterrupt(ctx)
|
2023-02-22 14:19:56 +00:00
|
|
|
|
|
|
|
|
// Interrupting can be cleared.
|
|
|
|
|
err = db.Exec(`SELECT 1`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2024-07-10 15:41:28 +01:00
|
|
|
|
|
|
|
|
db.SetInterrupt(ctx)
|
|
|
|
|
if got := db.GetInterrupt(); got != ctx {
|
|
|
|
|
t.Errorf("got %v, want %v", got, ctx)
|
|
|
|
|
}
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_Prepare_empty(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
stmt, _, err := db.Prepare(``)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer stmt.Close()
|
|
|
|
|
|
|
|
|
|
if stmt != nil {
|
|
|
|
|
t.Error("want nil")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_Prepare_tail(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
stmt, tail, err := db.Prepare(`SELECT 1; -- HERE`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer stmt.Close()
|
|
|
|
|
|
|
|
|
|
if !strings.Contains(tail, "-- HERE") {
|
|
|
|
|
t.Errorf("got %q", tail)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_Prepare_invalid(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
var serr *sqlite3.Error
|
|
|
|
|
|
|
|
|
|
_, _, err = db.Prepare(`SELECT`)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("want error")
|
|
|
|
|
}
|
|
|
|
|
if !errors.As(err, &serr) {
|
|
|
|
|
t.Fatalf("got %T, want sqlite3.Error", err)
|
|
|
|
|
}
|
|
|
|
|
if rc := serr.Code(); rc != sqlite3.ERROR {
|
|
|
|
|
t.Errorf("got %d, want sqlite3.ERROR", rc)
|
|
|
|
|
}
|
|
|
|
|
if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` {
|
2023-02-28 14:50:15 +00:00
|
|
|
t.Error("got message:", got)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, err = db.Prepare(`SELECT * FRM sqlite_schema`)
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("want error")
|
|
|
|
|
}
|
|
|
|
|
if !errors.As(err, &serr) {
|
|
|
|
|
t.Fatalf("got %T, want sqlite3.ERROR", err)
|
|
|
|
|
}
|
|
|
|
|
if rc := serr.Code(); rc != sqlite3.ERROR {
|
|
|
|
|
t.Errorf("got %d, want sqlite3.ERROR", rc)
|
|
|
|
|
}
|
|
|
|
|
if got := serr.SQL(); got != `FRM sqlite_schema` {
|
2023-02-28 14:50:15 +00:00
|
|
|
t.Error("got SQL:", got)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
if got := serr.Error(); got != `sqlite3: SQL logic error: near "FRM": syntax error` {
|
2023-02-28 14:50:15 +00:00
|
|
|
t.Error("got message:", got)
|
2023-02-22 14:19:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-23 14:53:15 +00:00
|
|
|
|
|
|
|
|
func TestConn_Config(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
o, err := db.Config(sqlite3.DBCONFIG_DEFENSIVE)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != false {
|
|
|
|
|
t.Error("want false")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != true {
|
|
|
|
|
t.Error("want true")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != true {
|
|
|
|
|
t.Error("want true")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE, false)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != false {
|
|
|
|
|
t.Error("want false")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != false {
|
|
|
|
|
t.Error("want false")
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-12-27 14:06:44 +00:00
|
|
|
|
|
|
|
|
func TestConn_ConfigLog(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
var code sqlite3.ExtendedErrorCode
|
|
|
|
|
err = db.ConfigLog(func(c sqlite3.ExtendedErrorCode, msg string) {
|
|
|
|
|
t.Log(msg)
|
|
|
|
|
code = c
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.Prepare(`SELECT * FRM sqlite_schema`)
|
|
|
|
|
|
|
|
|
|
if code != sqlite3.ExtendedErrorCode(sqlite3.ERROR) {
|
|
|
|
|
t.Error("want sqlite3.ERROR")
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-17 15:39:13 +00:00
|
|
|
|
2024-07-24 12:37:35 +01:00
|
|
|
func TestConn_FileControl(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
file := filepath.Join(t.TempDir(), "test.db")
|
|
|
|
|
db, err := sqlite3.Open(file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
t.Run("MISUSE", func(t *testing.T) {
|
|
|
|
|
_, err := db.FileControl("main", 0)
|
|
|
|
|
if !errors.Is(err, sqlite3.MISUSE) {
|
|
|
|
|
t.Errorf("got %v, want MISUSE", err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
t.Run("FCNTL_RESET_CACHE", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_RESET_CACHE)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != nil {
|
|
|
|
|
t.Errorf("got %v, want nil", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_PERSIST_WAL", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_PERSIST_WAL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != false {
|
|
|
|
|
t.Errorf("got %v, want false", o)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.FileControl("", sqlite3.FCNTL_PERSIST_WAL, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != true {
|
|
|
|
|
t.Errorf("got %v, want true", o)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.FileControl("", sqlite3.FCNTL_PERSIST_WAL)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != true {
|
|
|
|
|
t.Errorf("got %v, want true", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_CHUNK_SIZE", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_CHUNK_SIZE, 1024*1024)
|
|
|
|
|
if !errors.Is(err, sqlite3.NOTFOUND) {
|
|
|
|
|
t.Errorf("got %v, want NOTFOUND", err)
|
|
|
|
|
}
|
|
|
|
|
if o != nil {
|
|
|
|
|
t.Errorf("got %v, want nil", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_RESERVE_BYTES", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_RESERVE_BYTES, 4)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != 0 {
|
|
|
|
|
t.Errorf("got %v, want 0", o)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
o, err = db.FileControl("", sqlite3.FCNTL_RESERVE_BYTES)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != 4 {
|
|
|
|
|
t.Errorf("got %v, want 4", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_DATA_VERSION", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_DATA_VERSION)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != uint32(2) {
|
|
|
|
|
t.Errorf("got %v, want 2", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_VFS_POINTER", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_VFS_POINTER)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != vfs.Find("os") {
|
|
|
|
|
t.Errorf("got %v, want os", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_FILE_POINTER", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_FILE_POINTER)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if _, ok := o.(vfs.File); !ok {
|
|
|
|
|
t.Errorf("got %v, want File", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_JOURNAL_POINTER", func(t *testing.T) {
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_JOURNAL_POINTER)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != nil {
|
|
|
|
|
t.Errorf("got %v, want nil", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
t.Run("FCNTL_LOCKSTATE", func(t *testing.T) {
|
|
|
|
|
if !vfs.SupportsFileLocking {
|
|
|
|
|
t.Skip("skipping without locks")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
txn, err := db.BeginExclusive()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer txn.End(&err)
|
|
|
|
|
|
|
|
|
|
o, err := db.FileControl("", sqlite3.FCNTL_LOCKSTATE)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if o != vfs.LOCK_EXCLUSIVE {
|
|
|
|
|
t.Errorf("got %v, want LOCK_EXCLUSIVE", o)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-18 15:53:00 +00:00
|
|
|
func TestConn_Limit(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
l := db.Limit(sqlite3.LIMIT_COLUMN, -1)
|
|
|
|
|
if l != 2000 {
|
|
|
|
|
t.Errorf("got %d, want 2000", l)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l = db.Limit(sqlite3.LIMIT_COLUMN, 100)
|
|
|
|
|
if l != 2000 {
|
|
|
|
|
t.Errorf("got %d, want 2000", l)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l = db.Limit(sqlite3.LIMIT_COLUMN, -1)
|
|
|
|
|
if l != 100 {
|
|
|
|
|
t.Errorf("got %d, want 100", l)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l = db.Limit(math.MaxUint32, -1)
|
|
|
|
|
if l != -1 {
|
|
|
|
|
t.Errorf("got %d, want -1", l)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-27 10:57:46 +00:00
|
|
|
func TestConn_SetAuthorizer(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
err = db.SetAuthorizer(func(action sqlite3.AuthorizerActionCode, name3rd, name4th, schema, nameInner string) sqlite3.AuthorizerReturnCode {
|
2024-07-10 15:41:28 +01:00
|
|
|
if action != sqlite3.AUTH_PRAGMA {
|
|
|
|
|
t.Errorf("got %v, want PRAGMA", action)
|
|
|
|
|
}
|
|
|
|
|
if name3rd != "busy_timeout" {
|
|
|
|
|
t.Errorf("got %q, want busy_timeout", name3rd)
|
|
|
|
|
}
|
|
|
|
|
if name4th != "5000" {
|
|
|
|
|
t.Errorf("got %q, want 5000", name4th)
|
|
|
|
|
}
|
|
|
|
|
if schema != "main" {
|
|
|
|
|
t.Errorf("got %q, want main", schema)
|
|
|
|
|
}
|
2024-01-27 10:57:46 +00:00
|
|
|
return sqlite3.AUTH_DENY
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 15:41:28 +01:00
|
|
|
err = db.Exec(`PRAGMA main.busy_timeout=5000`)
|
2024-01-27 10:57:46 +00:00
|
|
|
if !errors.Is(err, sqlite3.AUTH) {
|
|
|
|
|
t.Errorf("got %v, want sqlite3.AUTH", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-31 12:15:08 +01:00
|
|
|
func TestConn_Trace(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
rows := 0
|
|
|
|
|
closed := false
|
|
|
|
|
err = db.Trace(math.MaxUint32, func(evt sqlite3.TraceEvent, a1 any, a2 any) error {
|
|
|
|
|
switch evt {
|
|
|
|
|
case sqlite3.TRACE_CLOSE:
|
|
|
|
|
closed = true
|
|
|
|
|
_ = a1.(*sqlite3.Conn)
|
|
|
|
|
return db.Exec(`PRAGMA optimize`)
|
|
|
|
|
case sqlite3.TRACE_STMT:
|
|
|
|
|
stmt := a1.(*sqlite3.Stmt)
|
|
|
|
|
if sql := a2.(string); sql != stmt.SQL() {
|
|
|
|
|
t.Errorf("got %q, want %q", sql, stmt.SQL())
|
|
|
|
|
}
|
|
|
|
|
if sql := stmt.ExpandedSQL(); sql != `SELECT 1` {
|
|
|
|
|
t.Errorf("got %q", sql)
|
|
|
|
|
}
|
|
|
|
|
case sqlite3.TRACE_PROFILE:
|
|
|
|
|
_ = a1.(*sqlite3.Stmt)
|
|
|
|
|
if ns := a2.(int64); ns < 0 {
|
|
|
|
|
t.Errorf("got %d", ns)
|
|
|
|
|
}
|
|
|
|
|
case sqlite3.TRACE_ROW:
|
|
|
|
|
_ = a1.(*sqlite3.Stmt)
|
|
|
|
|
if a2 != nil {
|
|
|
|
|
t.Errorf("got %v", a2)
|
|
|
|
|
}
|
|
|
|
|
rows++
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stmt, _, err := db.Prepare(`SELECT ?`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
err = stmt.BindInt(1, 1)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
err = stmt.Exec()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
err = stmt.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if rows != 1 {
|
|
|
|
|
t.Error("want 1")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = db.Close()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !closed {
|
|
|
|
|
t.Error("want closed")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 15:39:13 +00:00
|
|
|
func TestConn_ReleaseMemory(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
err = db.ReleaseMemory()
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_SetLastInsertRowID(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
db.SetLastInsertRowID(42)
|
|
|
|
|
|
|
|
|
|
got := db.LastInsertRowID()
|
|
|
|
|
if got != 42 {
|
|
|
|
|
t.Errorf("got %d, want 42", got)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-23 11:43:14 +01:00
|
|
|
func TestConn_Filename(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
file := filepath.Join(t.TempDir(), "test.db")
|
|
|
|
|
db, err := sqlite3.Open(file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
n := db.Filename("")
|
|
|
|
|
if n.String() != file {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.Database() != file {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.DatabaseFile() == nil {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = db.Filename("xpto")
|
|
|
|
|
if n != nil {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.String() != "" {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.Database() != "" {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.Journal() != "" {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.WAL() != "" {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
if n.DatabaseFile() != nil {
|
|
|
|
|
t.Errorf("got %v", n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-17 15:39:13 +00:00
|
|
|
func TestConn_ReadOnly(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
if ro, ok := db.ReadOnly(""); ro != false || ok != false {
|
|
|
|
|
t.Errorf("got %v,%v", ro, ok)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ro, ok := db.ReadOnly("xpto"); ro != false || ok != true {
|
|
|
|
|
t.Errorf("got %v,%v", ro, ok)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_DBName(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
if name := db.DBName(0); name != "main" {
|
|
|
|
|
t.Errorf("got %s", name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if name := db.DBName(5); name != "" {
|
|
|
|
|
t.Errorf("got %s", name)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-12 14:57:13 +01:00
|
|
|
|
|
|
|
|
func TestConn_AutoVacuumPages(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
2024-08-05 21:25:47 +01:00
|
|
|
tmp := memdb.TestDB(t, url.Values{
|
|
|
|
|
"_pragma": {"auto_vacuum(full)"},
|
|
|
|
|
})
|
2024-04-12 14:57:13 +01:00
|
|
|
|
2024-08-05 21:25:47 +01:00
|
|
|
db, err := sqlite3.Open(tmp)
|
2024-04-12 14:57:13 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
err = db.AutoVacuumPages(func(schema string, dbPages, freePages, bytesPerPage uint) uint {
|
|
|
|
|
return freePages
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = db.Exec(`CREATE TABLE test (col)`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = db.Exec(`INSERT INTO test VALUES (zeroblob(1024*1024))`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = db.Exec(`DROP TABLE test`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-31 12:15:08 +01:00
|
|
|
|
|
|
|
|
func TestConn_Status(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
err = db.Exec(`CREATE TABLE test (col)`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cr, hi, err := db.Status(sqlite3.DBSTATUS_SCHEMA_USED, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error("want nil")
|
|
|
|
|
}
|
|
|
|
|
if cr == 0 {
|
|
|
|
|
t.Error("want something")
|
|
|
|
|
}
|
|
|
|
|
if hi != 0 {
|
|
|
|
|
t.Error("want zero")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cr, hi, err = db.Status(sqlite3.DBSTATUS_LOOKASIDE_HIT, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error("want nil")
|
|
|
|
|
}
|
|
|
|
|
if cr != 0 {
|
|
|
|
|
t.Error("want zero")
|
|
|
|
|
}
|
|
|
|
|
if hi == 0 {
|
|
|
|
|
t.Error("want something")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cr, hi, err = db.Status(sqlite3.DBSTATUS_LOOKASIDE_HIT, true)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error("want nil")
|
|
|
|
|
}
|
|
|
|
|
if cr != 0 {
|
|
|
|
|
t.Error("want zero")
|
|
|
|
|
}
|
|
|
|
|
if hi != 0 {
|
|
|
|
|
t.Error("want zero")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestConn_TableColumnMetadata(t *testing.T) {
|
|
|
|
|
t.Parallel()
|
|
|
|
|
|
|
|
|
|
db, err := sqlite3.Open(":memory:")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
|
|
err = db.Exec(`CREATE TABLE test (col)`)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, _, _, _, err = db.TableColumnMetadata("", "table", "")
|
|
|
|
|
if err == nil {
|
|
|
|
|
t.Error("want error")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, _, _, _, _, err = db.TableColumnMetadata("", "test", "")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Error("want nil")
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-31 13:18:52 +01:00
|
|
|
typ, ord, nn, pk, ai, err := db.TableColumnMetadata("main", "test", "rowid")
|
2024-07-31 12:15:08 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Error("want nil")
|
|
|
|
|
}
|
|
|
|
|
if typ != "INTEGER" {
|
|
|
|
|
t.Error("want INTEGER")
|
|
|
|
|
}
|
|
|
|
|
if ord != "BINARY" {
|
|
|
|
|
t.Error("want BINARY")
|
|
|
|
|
}
|
|
|
|
|
if nn != false {
|
|
|
|
|
t.Error("want false")
|
|
|
|
|
}
|
|
|
|
|
if pk != true {
|
|
|
|
|
t.Error("want true")
|
|
|
|
|
}
|
|
|
|
|
if ai != false {
|
|
|
|
|
t.Error("want false")
|
|
|
|
|
}
|
|
|
|
|
}
|