2014-08-18 11:56:31 +04:00
|
|
|
// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by an MIT-style
|
|
|
|
// license that can be found in the LICENSE file.
|
2014-08-18 12:00:59 +04:00
|
|
|
|
2013-06-21 00:52:38 +04:00
|
|
|
package sqlite3
|
2011-11-12 22:52:02 +04:00
|
|
|
|
|
|
|
import (
|
2017-08-21 23:22:09 +03:00
|
|
|
"bytes"
|
2012-01-25 04:23:44 +04:00
|
|
|
"database/sql"
|
2015-04-15 10:26:27 +03:00
|
|
|
"database/sql/driver"
|
|
|
|
"errors"
|
2015-04-10 19:32:18 +03:00
|
|
|
"fmt"
|
2015-06-05 17:02:14 +03:00
|
|
|
"io/ioutil"
|
2017-08-01 19:26:57 +03:00
|
|
|
"math/rand"
|
2015-03-05 04:34:31 +03:00
|
|
|
"net/url"
|
2011-11-12 22:52:02 +04:00
|
|
|
"os"
|
2015-08-21 23:38:22 +03:00
|
|
|
"reflect"
|
2015-08-21 09:08:48 +03:00
|
|
|
"regexp"
|
2017-08-01 19:26:57 +03:00
|
|
|
"strconv"
|
2015-03-04 19:17:38 +03:00
|
|
|
"strings"
|
2015-08-21 09:08:48 +03:00
|
|
|
"sync"
|
2011-12-07 15:40:21 +04:00
|
|
|
"testing"
|
2012-04-07 08:17:54 +04:00
|
|
|
"time"
|
2011-11-12 22:52:02 +04:00
|
|
|
)
|
|
|
|
|
2015-06-05 17:02:14 +03:00
|
|
|
func TempFilename(t *testing.T) string {
|
|
|
|
f, err := ioutil.TempFile("", "go-sqlite3-test-")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
return f.Name()
|
2013-05-09 21:37:39 +04:00
|
|
|
}
|
|
|
|
|
2015-04-10 19:32:18 +03:00
|
|
|
func doTestOpen(t *testing.T, option string) (string, error) {
|
|
|
|
var url string
|
2015-06-05 17:02:14 +03:00
|
|
|
tempFilename := TempFilename(t)
|
2015-06-05 17:32:51 +03:00
|
|
|
defer os.Remove(tempFilename)
|
2015-04-10 19:32:18 +03:00
|
|
|
if option != "" {
|
|
|
|
url = tempFilename + option
|
|
|
|
} else {
|
|
|
|
url = tempFilename
|
|
|
|
}
|
|
|
|
db, err := sql.Open("sqlite3", url)
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2015-04-10 19:32:18 +03:00
|
|
|
return "Failed to open database:", err
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2013-05-09 21:37:39 +04:00
|
|
|
defer os.Remove(tempFilename)
|
2012-09-11 18:23:21 +04:00
|
|
|
defer db.Close()
|
2011-11-12 22:52:02 +04:00
|
|
|
|
2011-11-15 05:41:43 +04:00
|
|
|
_, err = db.Exec("drop table foo")
|
2011-11-12 22:52:02 +04:00
|
|
|
_, err = db.Exec("create table foo (id integer)")
|
|
|
|
if err != nil {
|
2015-04-10 19:32:18 +03:00
|
|
|
return "Failed to create table:", err
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
|
2013-05-09 21:37:39 +04:00
|
|
|
if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() {
|
2015-04-10 19:32:18 +03:00
|
|
|
return "Failed to create ./foo.db", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestOpen(t *testing.T) {
|
|
|
|
cases := map[string]bool{
|
|
|
|
"": true,
|
|
|
|
"?_txlock=immediate": true,
|
|
|
|
"?_txlock=deferred": true,
|
|
|
|
"?_txlock=exclusive": true,
|
|
|
|
"?_txlock=bogus": false,
|
|
|
|
}
|
|
|
|
for option, expectedPass := range cases {
|
|
|
|
result, err := doTestOpen(t, option)
|
|
|
|
if result == "" {
|
2015-04-15 10:26:27 +03:00
|
|
|
if !expectedPass {
|
2015-04-10 19:32:18 +03:00
|
|
|
errmsg := fmt.Sprintf("_txlock error not caught at dbOpen with option: %s", option)
|
|
|
|
t.Fatal(errmsg)
|
|
|
|
}
|
|
|
|
} else if expectedPass {
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal(result)
|
|
|
|
} else {
|
|
|
|
t.Fatal(result, err)
|
|
|
|
}
|
|
|
|
}
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-05 17:33:25 +03:00
|
|
|
func TestReadonly(t *testing.T) {
|
|
|
|
tempFilename := TempFilename(t)
|
|
|
|
defer os.Remove(tempFilename)
|
|
|
|
|
2015-11-02 05:53:42 +03:00
|
|
|
db1, err := sql.Open("sqlite3", "file:"+tempFilename)
|
2015-06-05 17:33:25 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
db1.Exec("CREATE TABLE test (x int, y float)")
|
|
|
|
|
2015-11-02 05:53:42 +03:00
|
|
|
db2, err := sql.Open("sqlite3", "file:"+tempFilename+"?mode=ro")
|
2015-06-05 17:33:25 +03:00
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
_ = db2
|
|
|
|
_, err = db2.Exec("INSERT INTO test VALUES (1, 3.14)")
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("didn't expect INSERT into read-only database to work")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-01 19:12:21 +03:00
|
|
|
func TestForeignKeys(t *testing.T) {
|
|
|
|
cases := map[string]bool{
|
|
|
|
"?_foreign_keys=1": true,
|
|
|
|
"?_foreign_keys=0": false,
|
|
|
|
}
|
|
|
|
for option, want := range cases {
|
|
|
|
fname := TempFilename(t)
|
|
|
|
uri := "file:" + fname + option
|
|
|
|
db, err := sql.Open("sqlite3", uri)
|
|
|
|
if err != nil {
|
|
|
|
os.Remove(fname)
|
|
|
|
t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var enabled bool
|
|
|
|
err = db.QueryRow("PRAGMA foreign_keys;").Scan(&enabled)
|
|
|
|
db.Close()
|
|
|
|
os.Remove(fname)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("query foreign_keys for %s: %v", uri, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if enabled != want {
|
|
|
|
t.Errorf("\"PRAGMA foreign_keys;\" for %q = %t; want %t", uri, enabled, want)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-09 17:32:14 +03:00
|
|
|
func TestRecursiveTriggers(t *testing.T) {
|
|
|
|
cases := map[string]bool{
|
|
|
|
"?_recursive_triggers=1": true,
|
|
|
|
"?_recursive_triggers=0": false,
|
|
|
|
}
|
|
|
|
for option, want := range cases {
|
|
|
|
fname := TempFilename(t)
|
|
|
|
uri := "file:" + fname + option
|
|
|
|
db, err := sql.Open("sqlite3", uri)
|
|
|
|
if err != nil {
|
|
|
|
os.Remove(fname)
|
|
|
|
t.Errorf("sql.Open(\"sqlite3\", %q): %v", uri, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var enabled bool
|
|
|
|
err = db.QueryRow("PRAGMA recursive_triggers;").Scan(&enabled)
|
|
|
|
db.Close()
|
|
|
|
os.Remove(fname)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("query recursive_triggers for %s: %v", uri, err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if enabled != want {
|
|
|
|
t.Errorf("\"PRAGMA recursive_triggers;\" for %q = %t; want %t", uri, enabled, want)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-03 14:36:33 +04:00
|
|
|
func TestClose(t *testing.T) {
|
2015-06-05 17:02:14 +03:00
|
|
|
tempFilename := TempFilename(t)
|
2015-11-03 15:52:28 +03:00
|
|
|
defer os.Remove(tempFilename)
|
2013-09-03 14:36:33 +04:00
|
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to open database:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = db.Exec("drop table foo")
|
|
|
|
_, err = db.Exec("create table foo (id integer)")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to create table:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
stmt, err := db.Prepare("select id from foo where id = ?")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to select records:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
db.Close()
|
|
|
|
_, err = stmt.Exec(1)
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("Failed to operate closed statement")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-12 22:52:02 +04:00
|
|
|
func TestInsert(t *testing.T) {
|
2015-06-05 17:02:14 +03:00
|
|
|
tempFilename := TempFilename(t)
|
2015-11-03 15:52:28 +03:00
|
|
|
defer os.Remove(tempFilename)
|
2013-05-09 21:37:39 +04:00
|
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to open database:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2012-09-11 18:23:21 +04:00
|
|
|
defer db.Close()
|
2011-11-12 22:52:02 +04:00
|
|
|
|
2011-11-15 05:41:43 +04:00
|
|
|
_, err = db.Exec("drop table foo")
|
2011-11-12 22:52:02 +04:00
|
|
|
_, err = db.Exec("create table foo (id integer)")
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to create table:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
|
2011-11-15 06:03:31 +04:00
|
|
|
res, err := db.Exec("insert into foo(id) values(123)")
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to insert record:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2011-11-15 06:03:31 +04:00
|
|
|
affected, _ := res.RowsAffected()
|
|
|
|
if affected != 1 {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
2011-11-12 22:52:02 +04:00
|
|
|
|
|
|
|
rows, err := db.Query("select id from foo")
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to select records:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
rows.Next()
|
|
|
|
|
|
|
|
var result int
|
|
|
|
rows.Scan(&result)
|
|
|
|
if result != 123 {
|
2016-03-14 14:54:10 +03:00
|
|
|
t.Errorf("Expected %d for fetched result, but %d:", 123, result)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-09 07:42:16 +03:00
|
|
|
func TestUpsert(t *testing.T) {
|
|
|
|
_, n, _ := Version()
|
|
|
|
if !(n >= 3024000) {
|
2018-06-12 12:05:28 +03:00
|
|
|
t.Skip("UPSERT requires sqlite3 => 3.24.0")
|
2018-06-09 07:42:16 +03:00
|
|
|
}
|
|
|
|
tempFilename := TempFilename(t)
|
|
|
|
defer os.Remove(tempFilename)
|
|
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to open database:", err)
|
|
|
|
}
|
|
|
|
defer db.Close()
|
|
|
|
|
|
|
|
_, err = db.Exec("drop table foo")
|
|
|
|
_, err = db.Exec("create table foo (name string primary key, counter integer)")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to create table:", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
res, err := db.Exec("insert into foo(name, counter) values('key', 1) on conflict (name) do update set counter=counter+1")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to upsert record:", err)
|
|
|
|
}
|
|
|
|
affected, _ := res.RowsAffected()
|
|
|
|
if affected != 1 {
|
|
|
|
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rows, err := db.Query("select name, counter from foo")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("Failed to select records:", err)
|
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
rows.Next()
|
|
|
|
|
|
|
|
var resultName string
|
|
|
|
var resultCounter int
|
|
|
|
rows.Scan(&resultName, &resultCounter)
|
|
|
|
if resultName != "key" {
|
|
|
|
t.Errorf("Expected %s for fetched result, but %s:", "key", resultName)
|
|
|
|
}
|
|
|
|
if resultCounter != 10 {
|
|
|
|
t.Errorf("Expected %d for fetched result, but %d:", 10, resultCounter)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-11-12 22:52:02 +04:00
|
|
|
func TestUpdate(t *testing.T) {
|
2015-06-05 17:02:14 +03:00
|
|
|
tempFilename := TempFilename(t)
|
2015-11-03 15:52:28 +03:00
|
|
|
defer os.Remove(tempFilename)
|
2013-05-09 21:37:39 +04:00
|
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to open database:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2012-09-11 18:23:21 +04:00
|
|
|
defer db.Close()
|
2011-11-12 22:52:02 +04:00
|
|
|
|
2011-11-15 05:41:43 +04:00
|
|
|
_, err = db.Exec("drop table foo")
|
2011-11-12 22:52:02 +04:00
|
|
|
_, err = db.Exec("create table foo (id integer)")
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to create table:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
|
2011-11-15 06:03:31 +04:00
|
|
|
res, err := db.Exec("insert into foo(id) values(123)")
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to insert record:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2011-11-15 06:03:31 +04:00
|
|
|
expected, err := res.LastInsertId()
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to get LastInsertId:", err)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
|
|
|
affected, _ := res.RowsAffected()
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to get RowsAffected:", err)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
|
|
|
if affected != 1 {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
2011-11-12 22:52:02 +04:00
|
|
|
|
2011-11-15 06:03:31 +04:00
|
|
|
res, err = db.Exec("update foo set id = 234")
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to update record:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2016-11-06 07:16:38 +03:00
|
|
|
lastID, err := res.LastInsertId()
|
2011-11-15 06:03:31 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to get LastInsertId:", err)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
2016-11-06 07:16:38 +03:00
|
|
|
if expected != lastID {
|
|
|
|
t.Errorf("Expected %q for last Id, but %q:", expected, lastID)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
|
|
|
affected, _ = res.RowsAffected()
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to get RowsAffected:", err)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
|
|
|
if affected != 1 {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
2011-11-12 22:52:02 +04:00
|
|
|
|
|
|
|
rows, err := db.Query("select id from foo")
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to select records:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
defer rows.Close()
|
|
|
|
|
|
|
|
rows.Next()
|
|
|
|
|
|
|
|
var result int
|
|
|
|
rows.Scan(&result)
|
|
|
|
if result != 234 {
|
2016-03-14 14:54:10 +03:00
|
|
|
t.Errorf("Expected %d for fetched result, but %d:", 234, result)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDelete(t *testing.T) {
|
2015-06-05 17:02:14 +03:00
|
|
|
tempFilename := TempFilename(t)
|
2015-11-03 15:52:28 +03:00
|
|
|
defer os.Remove(tempFilename)
|
2013-05-09 21:37:39 +04:00
|
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to open database:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2012-09-11 18:23:21 +04:00
|
|
|
defer db.Close()
|
2011-11-12 22:52:02 +04:00
|
|
|
|
2011-11-15 05:41:43 +04:00
|
|
|
_, err = db.Exec("drop table foo")
|
2011-11-12 22:52:02 +04:00
|
|
|
_, err = db.Exec("create table foo (id integer)")
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to create table:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
|
|
|
|
2011-11-15 06:03:31 +04:00
|
|
|
res, err := db.Exec("insert into foo(id) values(123)")
|
2011-11-12 22:52:02 +04:00
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to insert record:", err)
|
2011-11-12 22:52:02 +04:00
|
|
|
}
|
2011-11-15 06:03:31 +04:00
|
|
|
expected, err := res.LastInsertId()
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to get LastInsertId:", err)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
|
|
|
affected, err := res.RowsAffected()
|
|
|
|
if err != nil {
|
2012-09-11 19:13:11 +04:00
|
|
|
t.Fatal("Failed to get RowsAffected:", err)
|
2011-11-15 06:03:31 +04:00
|
|
|
}
|
|
|
|
if affected != 1 {
|
|
|
|
t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
|
|