Files
sqlite3/driver/driver_test.go

343 lines
6.7 KiB
Go
Raw Normal View History

2023-02-20 13:30:01 +00:00
package driver
import (
"bytes"
"context"
"database/sql"
"errors"
"math"
2023-12-12 16:55:17 +00:00
"net/url"
2023-02-20 13:30:01 +00:00
"path/filepath"
"testing"
"time"
"github.com/ncruces/go-sqlite3"
2023-11-23 03:28:56 +00:00
_ "github.com/ncruces/go-sqlite3/embed"
2023-03-29 15:01:25 +01:00
"github.com/ncruces/go-sqlite3/internal/util"
2024-04-16 17:33:48 +01:00
_ "github.com/ncruces/go-sqlite3/tests/testcfg"
2024-05-02 23:22:43 +01:00
"github.com/ncruces/go-sqlite3/vfs"
2023-02-20 13:30:01 +00:00
)
func Test_Open_dir(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
db, err := sql.Open("sqlite3", ".")
if err != nil {
t.Fatal(err)
}
defer db.Close()
_, err = db.Conn(context.TODO())
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-20 13:30:01 +00:00
}
}
func Test_Open_pragma(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout(1000)")
if err != nil {
t.Fatal(err)
}
defer db.Close()
var timeout int
err = db.QueryRow(`PRAGMA busy_timeout`).Scan(&timeout)
if err != nil {
t.Fatal(err)
}
if timeout != 1000 {
t.Errorf("got %v, want 1000", timeout)
}
}
func Test_Open_pragma_invalid(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout+1000")
if err != nil {
t.Fatal(err)
}
defer db.Close()
_, err = db.Conn(context.TODO())
if err == nil {
t.Fatal("want error")
}
var serr *sqlite3.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: invalid _pragma: sqlite3: SQL logic error: near "1000": syntax error` {
2023-02-28 14:50:15 +00:00
t.Error("got message:", got)
2023-02-20 13:30:01 +00:00
}
}
func Test_Open_txLock(t *testing.T) {
2024-05-02 23:22:43 +01:00
if !vfs.SupportsFileLocking {
t.Skip("skipping without locks")
}
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:38:03 +00:00
db, err := sql.Open("sqlite3", "file:"+
2023-02-27 12:07:48 +00:00
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+
2023-02-20 13:30:01 +00:00
"?_txlock=exclusive&_pragma=busy_timeout(0)")
if err != nil {
t.Fatal(err)
}
defer db.Close()
tx1, err := db.Begin()
if err != nil {
t.Fatal(err)
}
_, err = db.Begin()
if err == nil {
t.Error("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-20 13:30:01 +00:00
}
2023-02-20 13:38:03 +00:00
var terr interface{ Temporary() bool }
if !errors.As(err, &terr) || !terr.Temporary() {
t.Error("not temporary", err)
}
2023-02-20 13:30:01 +00:00
err = tx1.Commit()
if err != nil {
t.Fatal(err)
}
}
2023-02-20 13:38:03 +00:00
func Test_Open_txLock_invalid(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-12-07 03:09:37 -08:00
_, err := sql.Open("sqlite3", "file::memory:?_txlock=xclusive")
2023-02-20 13:38:03 +00:00
if err == nil {
t.Fatal("want error")
}
if got := err.Error(); got != `sqlite3: invalid _txlock: xclusive` {
2023-02-28 14:50:15 +00:00
t.Error("got message:", got)
2023-02-20 13:38:03 +00:00
}
}
2023-02-20 13:30:01 +00:00
func Test_BeginTx(t *testing.T) {
2024-05-02 23:22:43 +01:00
if !vfs.SupportsFileLocking {
t.Skip("skipping without locks")
}
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
2023-03-08 18:05:18 +00:00
db, err := sql.Open("sqlite3", "file:"+
filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+
"?_txlock=exclusive&_pragma=busy_timeout(0)")
2023-02-20 13:30:01 +00:00
if err != nil {
t.Fatal(err)
}
defer db.Close()
_, err = db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
2023-03-29 15:01:25 +01:00
if err.Error() != string(util.IsolationErr) {
2023-02-20 13:30:01 +00:00
t.Error("want isolationErr")
}
tx1, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
t.Fatal(err)
}
tx2, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
if err != nil {
t.Fatal(err)
}
2024-04-04 01:25:52 +01:00
_, err = tx1.Exec(`CREATE TABLE test (col)`)
2023-02-20 13:30:01 +00:00
if err == nil {
t.Error("want error")
}
2023-02-25 15:11:07 +00:00
if !errors.Is(err, sqlite3.READONLY) {
t.Errorf("got %v, want sqlite3.READONLY", err)
2023-02-20 13:30:01 +00:00
}
err = tx2.Commit()
if err != nil {
t.Fatal(err)
}
err = tx1.Commit()
if err != nil {
t.Fatal(err)
}
}
func Test_Prepare(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
var serr *sqlite3.Error
_, err = db.Prepare(`SELECT`)
if err == nil {
t.Error("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-20 13:30:01 +00:00
}
2023-11-30 12:26:15 +00:00
_, err = db.Prepare(`SELECT 1; `)
if err.Error() != string(util.TailErr) {
t.Error("want tailErr")
2023-02-20 13:30:01 +00:00
}
2023-11-30 12:26:15 +00:00
_, err = db.Prepare(`SELECT 1; SELECT`)
if err.Error() != string(util.TailErr) {
t.Error("want tailErr")
2023-02-20 13:30:01 +00:00
}
_, err = db.Prepare(`SELECT 1; SELECT 2`)
2023-03-29 15:01:25 +01:00
if err.Error() != string(util.TailErr) {
2023-02-20 13:30:01 +00:00
t.Error("want tailErr")
}
}
func Test_QueryRow_named(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
conn, err := db.Conn(ctx)
if err != nil {
t.Fatal(err)
}
defer conn.Close()
stmt, err := conn.PrepareContext(ctx, `SELECT ?, ?5, :AAA, @AAA, $AAA`)
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
date := time.Now()
row := stmt.QueryRow(true, sql.Named("AAA", math.Pi), nil /*3*/, nil /*4*/, date /*5*/)
var first bool
var fifth time.Time
var colon, at, dollar float32
err = row.Scan(&first, &fifth, &colon, &at, &dollar)
if err != nil {
t.Fatal(err)
}
if first != true {
t.Errorf("want true, got %v", first)
}
if colon != math.Pi {
t.Errorf("want π, got %v", colon)
}
if at != math.Pi {
t.Errorf("want π, got %v", at)
}
if dollar != math.Pi {
t.Errorf("want π, got %v", dollar)
}
if !fifth.Equal(date) {
t.Errorf("want %v, got %v", date, fifth)
}
}
func Test_QueryRow_blob_null(t *testing.T) {
2023-02-27 12:07:48 +00:00
t.Parallel()
2023-02-20 13:30:01 +00:00
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
rows, err := db.Query(`
SELECT NULL UNION ALL
SELECT x'cafe' UNION ALL
SELECT x'babe' UNION ALL
SELECT NULL
`)
if err != nil {
t.Fatal(err)
}
2023-11-23 15:32:28 +00:00
defer rows.Close()
2023-02-20 13:30:01 +00:00
want := [][]byte{nil, {0xca, 0xfe}, {0xba, 0xbe}, nil}
for i := 0; rows.Next(); i++ {
2023-03-20 02:16:42 +00:00
var buf sql.RawBytes
2023-02-20 13:30:01 +00:00
err = rows.Scan(&buf)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(buf, want[i]) {
t.Errorf("got %q, want %q", buf, want[i])
}
}
}
2023-12-12 16:55:17 +00:00
func Test_time(t *testing.T) {
t.Parallel()
for _, fmt := range []string{"auto", "sqlite", "rfc3339", time.ANSIC} {
t.Run(fmt, func(t *testing.T) {
db, err := sql.Open("sqlite3", "file::memory:?_timefmt="+url.QueryEscape(fmt))
if err != nil {
t.Fatal(err)
}
defer db.Close()
twosday := time.Date(2022, 2, 22, 22, 22, 22, 0, time.UTC)
2024-04-04 01:25:52 +01:00
_, err = db.Exec(`CREATE TABLE test (at DATETIME)`)
2023-12-12 16:55:17 +00:00
if err != nil {
t.Fatal(err)
}
_, err = db.Exec(`INSERT INTO test VALUES (?)`, twosday)
if err != nil {
t.Fatal(err)
}
var got time.Time
err = db.QueryRow(`SELECT * FROM test`).Scan(&got)
if err != nil {
t.Fatal(err)
}
if !got.Equal(twosday) {
t.Errorf("got: %v", got)
}
})
}
}