github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/driver/driver_test.go (about)

     1  package driver
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"database/sql"
     7  	"errors"
     8  	"math"
     9  	"net/url"
    10  	"path/filepath"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/ncruces/go-sqlite3"
    15  	_ "github.com/ncruces/go-sqlite3/embed"
    16  	"github.com/ncruces/go-sqlite3/internal/util"
    17  	_ "github.com/ncruces/go-sqlite3/tests/testcfg"
    18  	"github.com/ncruces/go-sqlite3/vfs"
    19  )
    20  
    21  func Test_Open_dir(t *testing.T) {
    22  	t.Parallel()
    23  
    24  	db, err := sql.Open("sqlite3", ".")
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	defer db.Close()
    29  
    30  	_, err = db.Conn(context.TODO())
    31  	if err == nil {
    32  		t.Fatal("want error")
    33  	}
    34  	if !errors.Is(err, sqlite3.CANTOPEN) {
    35  		t.Errorf("got %v, want sqlite3.CANTOPEN", err)
    36  	}
    37  }
    38  
    39  func Test_Open_pragma(t *testing.T) {
    40  	t.Parallel()
    41  
    42  	db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout(1000)")
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  	defer db.Close()
    47  
    48  	var timeout int
    49  	err = db.QueryRow(`PRAGMA busy_timeout`).Scan(&timeout)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	if timeout != 1000 {
    54  		t.Errorf("got %v, want 1000", timeout)
    55  	}
    56  }
    57  
    58  func Test_Open_pragma_invalid(t *testing.T) {
    59  	t.Parallel()
    60  
    61  	db, err := sql.Open("sqlite3", "file::memory:?_pragma=busy_timeout+1000")
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	defer db.Close()
    66  
    67  	_, err = db.Conn(context.TODO())
    68  	if err == nil {
    69  		t.Fatal("want error")
    70  	}
    71  	var serr *sqlite3.Error
    72  	if !errors.As(err, &serr) {
    73  		t.Fatalf("got %T, want sqlite3.Error", err)
    74  	}
    75  	if rc := serr.Code(); rc != sqlite3.ERROR {
    76  		t.Errorf("got %d, want sqlite3.ERROR", rc)
    77  	}
    78  	if got := err.Error(); got != `sqlite3: invalid _pragma: sqlite3: SQL logic error: near "1000": syntax error` {
    79  		t.Error("got message:", got)
    80  	}
    81  }
    82  
    83  func Test_Open_txLock(t *testing.T) {
    84  	if !vfs.SupportsFileLocking {
    85  		t.Skip("skipping without locks")
    86  	}
    87  	t.Parallel()
    88  
    89  	db, err := sql.Open("sqlite3", "file:"+
    90  		filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+
    91  		"?_txlock=exclusive&_pragma=busy_timeout(0)")
    92  	if err != nil {
    93  		t.Fatal(err)
    94  	}
    95  	defer db.Close()
    96  
    97  	tx1, err := db.Begin()
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	_, err = db.Begin()
   103  	if err == nil {
   104  		t.Error("want error")
   105  	}
   106  	if !errors.Is(err, sqlite3.BUSY) {
   107  		t.Errorf("got %v, want sqlite3.BUSY", err)
   108  	}
   109  	var terr interface{ Temporary() bool }
   110  	if !errors.As(err, &terr) || !terr.Temporary() {
   111  		t.Error("not temporary", err)
   112  	}
   113  
   114  	err = tx1.Commit()
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  }
   119  
   120  func Test_Open_txLock_invalid(t *testing.T) {
   121  	t.Parallel()
   122  
   123  	_, err := sql.Open("sqlite3", "file::memory:?_txlock=xclusive")
   124  	if err == nil {
   125  		t.Fatal("want error")
   126  	}
   127  	if got := err.Error(); got != `sqlite3: invalid _txlock: xclusive` {
   128  		t.Error("got message:", got)
   129  	}
   130  }
   131  
   132  func Test_BeginTx(t *testing.T) {
   133  	if !vfs.SupportsFileLocking {
   134  		t.Skip("skipping without locks")
   135  	}
   136  	t.Parallel()
   137  
   138  	ctx, cancel := context.WithCancel(context.Background())
   139  	defer cancel()
   140  
   141  	db, err := sql.Open("sqlite3", "file:"+
   142  		filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))+
   143  		"?_txlock=exclusive&_pragma=busy_timeout(0)")
   144  	if err != nil {
   145  		t.Fatal(err)
   146  	}
   147  	defer db.Close()
   148  
   149  	_, err = db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelReadCommitted})
   150  	if err.Error() != string(util.IsolationErr) {
   151  		t.Error("want isolationErr")
   152  	}
   153  
   154  	tx1, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
   155  	if err != nil {
   156  		t.Fatal(err)
   157  	}
   158  
   159  	tx2, err := db.BeginTx(ctx, &sql.TxOptions{ReadOnly: true})
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	_, err = tx1.Exec(`CREATE TABLE test (col)`)
   165  	if err == nil {
   166  		t.Error("want error")
   167  	}
   168  	if !errors.Is(err, sqlite3.READONLY) {
   169  		t.Errorf("got %v, want sqlite3.READONLY", err)
   170  	}
   171  
   172  	err = tx2.Commit()
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  
   177  	err = tx1.Commit()
   178  	if err != nil {
   179  		t.Fatal(err)
   180  	}
   181  }
   182  
   183  func Test_Prepare(t *testing.T) {
   184  	t.Parallel()
   185  
   186  	db, err := sql.Open("sqlite3", ":memory:")
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	defer db.Close()
   191  
   192  	var serr *sqlite3.Error
   193  	_, err = db.Prepare(`SELECT`)
   194  	if err == nil {
   195  		t.Error("want error")
   196  	}
   197  	if !errors.As(err, &serr) {
   198  		t.Fatalf("got %T, want sqlite3.Error", err)
   199  	}
   200  	if rc := serr.Code(); rc != sqlite3.ERROR {
   201  		t.Errorf("got %d, want sqlite3.ERROR", rc)
   202  	}
   203  	if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` {
   204  		t.Error("got message:", got)
   205  	}
   206  
   207  	_, err = db.Prepare(`SELECT 1; `)
   208  	if err.Error() != string(util.TailErr) {
   209  		t.Error("want tailErr")
   210  	}
   211  
   212  	_, err = db.Prepare(`SELECT 1; SELECT`)
   213  	if err.Error() != string(util.TailErr) {
   214  		t.Error("want tailErr")
   215  	}
   216  
   217  	_, err = db.Prepare(`SELECT 1; SELECT 2`)
   218  	if err.Error() != string(util.TailErr) {
   219  		t.Error("want tailErr")
   220  	}
   221  }
   222  
   223  func Test_QueryRow_named(t *testing.T) {
   224  	t.Parallel()
   225  
   226  	ctx, cancel := context.WithCancel(context.Background())
   227  	defer cancel()
   228  
   229  	db, err := sql.Open("sqlite3", ":memory:")
   230  	if err != nil {
   231  		t.Fatal(err)
   232  	}
   233  	defer db.Close()
   234  
   235  	conn, err := db.Conn(ctx)
   236  	if err != nil {
   237  		t.Fatal(err)
   238  	}
   239  	defer conn.Close()
   240  
   241  	stmt, err := conn.PrepareContext(ctx, `SELECT ?, ?5, :AAA, @AAA, $AAA`)
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	defer stmt.Close()
   246  
   247  	date := time.Now()
   248  	row := stmt.QueryRow(true, sql.Named("AAA", math.Pi), nil /*3*/, nil /*4*/, date /*5*/)
   249  
   250  	var first bool
   251  	var fifth time.Time
   252  	var colon, at, dollar float32
   253  	err = row.Scan(&first, &fifth, &colon, &at, &dollar)
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  
   258  	if first != true {
   259  		t.Errorf("want true, got %v", first)
   260  	}
   261  	if colon != math.Pi {
   262  		t.Errorf("want π, got %v", colon)
   263  	}
   264  	if at != math.Pi {
   265  		t.Errorf("want π, got %v", at)
   266  	}
   267  	if dollar != math.Pi {
   268  		t.Errorf("want π, got %v", dollar)
   269  	}
   270  	if !fifth.Equal(date) {
   271  		t.Errorf("want %v, got %v", date, fifth)
   272  	}
   273  }
   274  
   275  func Test_QueryRow_blob_null(t *testing.T) {
   276  	t.Parallel()
   277  
   278  	db, err := sql.Open("sqlite3", ":memory:")
   279  	if err != nil {
   280  		t.Fatal(err)
   281  	}
   282  	defer db.Close()
   283  
   284  	rows, err := db.Query(`
   285  		SELECT NULL    UNION ALL
   286  		SELECT x'cafe' UNION ALL
   287  		SELECT x'babe' UNION ALL
   288  		SELECT NULL
   289  	`)
   290  	if err != nil {
   291  		t.Fatal(err)
   292  	}
   293  	defer rows.Close()
   294  
   295  	want := [][]byte{nil, {0xca, 0xfe}, {0xba, 0xbe}, nil}
   296  	for i := 0; rows.Next(); i++ {
   297  		var buf sql.RawBytes
   298  		err = rows.Scan(&buf)
   299  		if err != nil {
   300  			t.Fatal(err)
   301  		}
   302  		if !bytes.Equal(buf, want[i]) {
   303  			t.Errorf("got %q, want %q", buf, want[i])
   304  		}
   305  	}
   306  }
   307  
   308  func Test_time(t *testing.T) {
   309  	t.Parallel()
   310  
   311  	for _, fmt := range []string{"auto", "sqlite", "rfc3339", time.ANSIC} {
   312  		t.Run(fmt, func(t *testing.T) {
   313  			db, err := sql.Open("sqlite3", "file::memory:?_timefmt="+url.QueryEscape(fmt))
   314  			if err != nil {
   315  				t.Fatal(err)
   316  			}
   317  			defer db.Close()
   318  
   319  			twosday := time.Date(2022, 2, 22, 22, 22, 22, 0, time.UTC)
   320  
   321  			_, err = db.Exec(`CREATE TABLE test (at DATETIME)`)
   322  			if err != nil {
   323  				t.Fatal(err)
   324  			}
   325  
   326  			_, err = db.Exec(`INSERT INTO test VALUES (?)`, twosday)
   327  			if err != nil {
   328  				t.Fatal(err)
   329  			}
   330  
   331  			var got time.Time
   332  			err = db.QueryRow(`SELECT * FROM test`).Scan(&got)
   333  			if err != nil {
   334  				t.Fatal(err)
   335  			}
   336  
   337  			if !got.Equal(twosday) {
   338  				t.Errorf("got: %v", got)
   339  			}
   340  		})
   341  	}
   342  }