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

     1  package tests
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"math"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/ncruces/go-sqlite3"
    13  	_ "github.com/ncruces/go-sqlite3/embed"
    14  	_ "github.com/ncruces/go-sqlite3/tests/testcfg"
    15  	_ "github.com/ncruces/go-sqlite3/vfs/memdb"
    16  )
    17  
    18  func TestConn_Open_dir(t *testing.T) {
    19  	t.Parallel()
    20  
    21  	_, err := sqlite3.OpenFlags(".", 0)
    22  	if err == nil {
    23  		t.Fatal("want error")
    24  	}
    25  	if !errors.Is(err, sqlite3.CANTOPEN) {
    26  		t.Errorf("got %v, want sqlite3.CANTOPEN", err)
    27  	}
    28  }
    29  
    30  func TestConn_Open_notfound(t *testing.T) {
    31  	t.Parallel()
    32  
    33  	_, err := sqlite3.OpenFlags("test.db", sqlite3.OPEN_READONLY)
    34  	if err == nil {
    35  		t.Fatal("want error")
    36  	}
    37  	if !errors.Is(err, sqlite3.CANTOPEN) {
    38  		t.Errorf("got %v, want sqlite3.CANTOPEN", err)
    39  	}
    40  }
    41  
    42  func TestConn_Open_modeof(t *testing.T) {
    43  	t.Parallel()
    44  
    45  	dir := t.TempDir()
    46  	file := filepath.Join(dir, "test.db")
    47  	mode := filepath.Join(dir, "modeof.txt")
    48  
    49  	fd, err := os.OpenFile(mode, os.O_CREATE, 0624)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	fi, err := fd.Stat()
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	fd.Close()
    58  
    59  	db, err := sqlite3.Open("file:" + file + "?modeof=" + mode)
    60  	if err != nil {
    61  		t.Fatal(err)
    62  	}
    63  	di, err := os.Stat(file)
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  	db.Close()
    68  
    69  	if di.Mode() != fi.Mode() {
    70  		t.Errorf("got %v, want %v", di.Mode(), fi.Mode())
    71  	}
    72  
    73  	_, err = sqlite3.Open("file:" + file + "?modeof=" + mode + "2")
    74  	if err == nil {
    75  		t.Fatal("want error")
    76  	}
    77  }
    78  
    79  func TestConn_Close(t *testing.T) {
    80  	var db *sqlite3.Conn
    81  	db.Close()
    82  }
    83  
    84  func TestConn_Close_BUSY(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	db, err := sqlite3.Open(":memory:")
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	defer db.Close()
    92  
    93  	stmt, _, err := db.Prepare(`BEGIN`)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  	defer stmt.Close()
    98  
    99  	err = db.Close()
   100  	if err == nil {
   101  		t.Fatal("want error")
   102  	}
   103  	if !errors.Is(err, sqlite3.BUSY) {
   104  		t.Errorf("got %v, want sqlite3.BUSY", err)
   105  	}
   106  	var terr interface{ Temporary() bool }
   107  	if !errors.As(err, &terr) || !terr.Temporary() {
   108  		t.Error("not temporary", err)
   109  	}
   110  	if got := err.Error(); got != `sqlite3: database is locked: unable to close due to unfinalized statements or unfinished backups` {
   111  		t.Error("got message:", got)
   112  	}
   113  }
   114  
   115  func TestConn_SetInterrupt(t *testing.T) {
   116  	t.Parallel()
   117  
   118  	db, err := sqlite3.Open(":memory:")
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	defer db.Close()
   123  
   124  	ctx, cancel := context.WithCancel(context.Background())
   125  	db.SetInterrupt(ctx)
   126  
   127  	// Interrupt doesn't interrupt this.
   128  	err = db.Exec(`SELECT 1`)
   129  	if err != nil {
   130  		t.Fatal(err)
   131  	}
   132  
   133  	db.SetInterrupt(context.Background())
   134  
   135  	stmt, _, err := db.Prepare(`
   136  		WITH RECURSIVE
   137  		  fibonacci (curr, next)
   138  		AS (
   139  		  SELECT 0, 1
   140  		  UNION ALL
   141  		  SELECT next, curr + next FROM fibonacci
   142  		  LIMIT 1e7
   143  		)
   144  		SELECT min(curr) FROM fibonacci
   145  	`)
   146  	if err != nil {
   147  		t.Fatal(err)
   148  	}
   149  	defer stmt.Close()
   150  
   151  	db.SetInterrupt(ctx)
   152  	go cancel()
   153  
   154  	// Interrupting works.
   155  	err = stmt.Exec()
   156  	if !errors.Is(err, sqlite3.INTERRUPT) {
   157  		t.Errorf("got %v, want sqlite3.INTERRUPT", err)
   158  	}
   159  
   160  	// Interrupting sticks.
   161  	err = db.Exec(`SELECT 1`)
   162  	if !errors.Is(err, sqlite3.INTERRUPT) {
   163  		t.Errorf("got %v, want sqlite3.INTERRUPT", err)
   164  	}
   165  
   166  	ctx, cancel = context.WithCancel(context.Background())
   167  	defer cancel()
   168  	db.SetInterrupt(ctx)
   169  
   170  	// Interrupting can be cleared.
   171  	err = db.Exec(`SELECT 1`)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  }
   176  
   177  func TestConn_Prepare_empty(t *testing.T) {
   178  	t.Parallel()
   179  
   180  	db, err := sqlite3.Open(":memory:")
   181  	if err != nil {
   182  		t.Fatal(err)
   183  	}
   184  	defer db.Close()
   185  
   186  	stmt, _, err := db.Prepare(``)
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	defer stmt.Close()
   191  
   192  	if stmt != nil {
   193  		t.Error("want nil")
   194  	}
   195  }
   196  
   197  func TestConn_Prepare_tail(t *testing.T) {
   198  	t.Parallel()
   199  
   200  	db, err := sqlite3.Open(":memory:")
   201  	if err != nil {
   202  		t.Fatal(err)
   203  	}
   204  	defer db.Close()
   205  
   206  	stmt, tail, err := db.Prepare(`SELECT 1; -- HERE`)
   207  	if err != nil {
   208  		t.Fatal(err)
   209  	}
   210  	defer stmt.Close()
   211  
   212  	if !strings.Contains(tail, "-- HERE") {
   213  		t.Errorf("got %q", tail)
   214  	}
   215  }
   216  
   217  func TestConn_Prepare_invalid(t *testing.T) {
   218  	t.Parallel()
   219  
   220  	db, err := sqlite3.Open(":memory:")
   221  	if err != nil {
   222  		t.Fatal(err)
   223  	}
   224  	defer db.Close()
   225  
   226  	var serr *sqlite3.Error
   227  
   228  	_, _, err = db.Prepare(`SELECT`)
   229  	if err == nil {
   230  		t.Fatal("want error")
   231  	}
   232  	if !errors.As(err, &serr) {
   233  		t.Fatalf("got %T, want sqlite3.Error", err)
   234  	}
   235  	if rc := serr.Code(); rc != sqlite3.ERROR {
   236  		t.Errorf("got %d, want sqlite3.ERROR", rc)
   237  	}
   238  	if got := err.Error(); got != `sqlite3: SQL logic error: incomplete input` {
   239  		t.Error("got message:", got)
   240  	}
   241  
   242  	_, _, err = db.Prepare(`SELECT * FRM sqlite_schema`)
   243  	if err == nil {
   244  		t.Fatal("want error")
   245  	}
   246  	if !errors.As(err, &serr) {
   247  		t.Fatalf("got %T, want sqlite3.ERROR", err)
   248  	}
   249  	if rc := serr.Code(); rc != sqlite3.ERROR {
   250  		t.Errorf("got %d, want sqlite3.ERROR", rc)
   251  	}
   252  	if got := serr.SQL(); got != `FRM sqlite_schema` {
   253  		t.Error("got SQL:", got)
   254  	}
   255  	if got := serr.Error(); got != `sqlite3: SQL logic error: near "FRM": syntax error` {
   256  		t.Error("got message:", got)
   257  	}
   258  }
   259  
   260  func TestConn_Config(t *testing.T) {
   261  	t.Parallel()
   262  
   263  	db, err := sqlite3.Open(":memory:")
   264  	if err != nil {
   265  		t.Fatal(err)
   266  	}
   267  	defer db.Close()
   268  
   269  	o, err := db.Config(sqlite3.DBCONFIG_DEFENSIVE)
   270  	if err != nil {
   271  		t.Fatal(err)
   272  	}
   273  	if o != false {
   274  		t.Error("want false")
   275  	}
   276  
   277  	o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE, true)
   278  	if err != nil {
   279  		t.Fatal(err)
   280  	}
   281  	if o != true {
   282  		t.Error("want true")
   283  	}
   284  
   285  	o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE)
   286  	if err != nil {
   287  		t.Fatal(err)
   288  	}
   289  	if o != true {
   290  		t.Error("want true")
   291  	}
   292  
   293  	o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE, false)
   294  	if err != nil {
   295  		t.Fatal(err)
   296  	}
   297  	if o != false {
   298  		t.Error("want false")
   299  	}
   300  
   301  	o, err = db.Config(sqlite3.DBCONFIG_DEFENSIVE)
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  	if o != false {
   306  		t.Error("want false")
   307  	}
   308  }
   309  
   310  func TestConn_ConfigLog(t *testing.T) {
   311  	t.Parallel()
   312  
   313  	db, err := sqlite3.Open(":memory:")
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  	defer db.Close()
   318  
   319  	var code sqlite3.ExtendedErrorCode
   320  	err = db.ConfigLog(func(c sqlite3.ExtendedErrorCode, msg string) {
   321  		t.Log(msg)
   322  		code = c
   323  	})
   324  	if err != nil {
   325  		t.Fatal(err)
   326  	}
   327  
   328  	db.Prepare(`SELECT * FRM sqlite_schema`)
   329  
   330  	if code != sqlite3.ExtendedErrorCode(sqlite3.ERROR) {
   331  		t.Error("want sqlite3.ERROR")
   332  	}
   333  }
   334  
   335  func TestConn_Limit(t *testing.T) {
   336  	t.Parallel()
   337  
   338  	db, err := sqlite3.Open(":memory:")
   339  	if err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	defer db.Close()
   343  
   344  	l := db.Limit(sqlite3.LIMIT_COLUMN, -1)
   345  	if l != 2000 {
   346  		t.Errorf("got %d, want 2000", l)
   347  	}
   348  
   349  	l = db.Limit(sqlite3.LIMIT_COLUMN, 100)
   350  	if l != 2000 {
   351  		t.Errorf("got %d, want 2000", l)
   352  	}
   353  
   354  	l = db.Limit(sqlite3.LIMIT_COLUMN, -1)
   355  	if l != 100 {
   356  		t.Errorf("got %d, want 100", l)
   357  	}
   358  
   359  	l = db.Limit(math.MaxUint32, -1)
   360  	if l != -1 {
   361  		t.Errorf("got %d, want -1", l)
   362  	}
   363  }
   364  
   365  func TestConn_SetAuthorizer(t *testing.T) {
   366  	t.Parallel()
   367  
   368  	db, err := sqlite3.Open(":memory:")
   369  	if err != nil {
   370  		t.Fatal(err)
   371  	}
   372  	defer db.Close()
   373  
   374  	err = db.SetAuthorizer(func(action sqlite3.AuthorizerActionCode, name3rd, name4th, schema, nameInner string) sqlite3.AuthorizerReturnCode {
   375  		return sqlite3.AUTH_DENY
   376  	})
   377  	if err != nil {
   378  		t.Fatal(err)
   379  	}
   380  
   381  	err = db.Exec(`SELECT * FROM sqlite_schema`)
   382  	if !errors.Is(err, sqlite3.AUTH) {
   383  		t.Errorf("got %v, want sqlite3.AUTH", err)
   384  	}
   385  }
   386  
   387  func TestConn_ReleaseMemory(t *testing.T) {
   388  	t.Parallel()
   389  
   390  	db, err := sqlite3.Open(":memory:")
   391  	if err != nil {
   392  		t.Fatal(err)
   393  	}
   394  	defer db.Close()
   395  
   396  	err = db.ReleaseMemory()
   397  	if err != nil {
   398  		t.Fatal(err)
   399  	}
   400  }
   401  
   402  func TestConn_SetLastInsertRowID(t *testing.T) {
   403  	t.Parallel()
   404  
   405  	db, err := sqlite3.Open(":memory:")
   406  	if err != nil {
   407  		t.Fatal(err)
   408  	}
   409  	defer db.Close()
   410  
   411  	db.SetLastInsertRowID(42)
   412  
   413  	got := db.LastInsertRowID()
   414  	if got != 42 {
   415  		t.Errorf("got %d, want 42", got)
   416  	}
   417  }
   418  
   419  func TestConn_Filename(t *testing.T) {
   420  	t.Parallel()
   421  
   422  	file := filepath.Join(t.TempDir(), "test.db")
   423  	db, err := sqlite3.Open(file)
   424  	if err != nil {
   425  		t.Fatal(err)
   426  	}
   427  	defer db.Close()
   428  
   429  	n := db.Filename("")
   430  	if n.String() != file {
   431  		t.Errorf("got %v", n)
   432  	}
   433  	if n.Database() != file {
   434  		t.Errorf("got %v", n)
   435  	}
   436  	if n.DatabaseFile() == nil {
   437  		t.Errorf("got %v", n)
   438  	}
   439  
   440  	n = db.Filename("xpto")
   441  	if n != nil {
   442  		t.Errorf("got %v", n)
   443  	}
   444  	if n.String() != "" {
   445  		t.Errorf("got %v", n)
   446  	}
   447  	if n.Database() != "" {
   448  		t.Errorf("got %v", n)
   449  	}
   450  	if n.Journal() != "" {
   451  		t.Errorf("got %v", n)
   452  	}
   453  	if n.WAL() != "" {
   454  		t.Errorf("got %v", n)
   455  	}
   456  	if n.DatabaseFile() != nil {
   457  		t.Errorf("got %v", n)
   458  	}
   459  }
   460  
   461  func TestConn_ReadOnly(t *testing.T) {
   462  	t.Parallel()
   463  
   464  	db, err := sqlite3.Open(":memory:")
   465  	if err != nil {
   466  		t.Fatal(err)
   467  	}
   468  	defer db.Close()
   469  
   470  	if ro, ok := db.ReadOnly(""); ro != false || ok != false {
   471  		t.Errorf("got %v,%v", ro, ok)
   472  	}
   473  
   474  	if ro, ok := db.ReadOnly("xpto"); ro != false || ok != true {
   475  		t.Errorf("got %v,%v", ro, ok)
   476  	}
   477  }
   478  
   479  func TestConn_DBName(t *testing.T) {
   480  	t.Parallel()
   481  
   482  	db, err := sqlite3.Open(":memory:")
   483  	if err != nil {
   484  		t.Fatal(err)
   485  	}
   486  	defer db.Close()
   487  
   488  	if name := db.DBName(0); name != "main" {
   489  		t.Errorf("got %s", name)
   490  	}
   491  
   492  	if name := db.DBName(5); name != "" {
   493  		t.Errorf("got %s", name)
   494  	}
   495  }
   496  
   497  func TestConn_AutoVacuumPages(t *testing.T) {
   498  	t.Parallel()
   499  
   500  	db, err := sqlite3.Open("file:test.db?vfs=memdb&_pragma=auto_vacuum(full)")
   501  	if err != nil {
   502  		t.Fatal(err)
   503  	}
   504  	defer db.Close()
   505  
   506  	err = db.AutoVacuumPages(func(schema string, dbPages, freePages, bytesPerPage uint) uint {
   507  		return freePages
   508  	})
   509  	if err != nil {
   510  		t.Fatal(err)
   511  	}
   512  
   513  	err = db.Exec(`CREATE TABLE test (col)`)
   514  	if err != nil {
   515  		t.Fatal(err)
   516  	}
   517  
   518  	err = db.Exec(`INSERT INTO test VALUES (zeroblob(1024*1024))`)
   519  	if err != nil {
   520  		t.Fatal(err)
   521  	}
   522  
   523  	err = db.Exec(`DROP TABLE test`)
   524  	if err != nil {
   525  		t.Fatal(err)
   526  	}
   527  }