fix(go16): fixed hang with go 1.6 by updating sqlite3, fixes #4367

This commit is contained in:
Torkel Ödegaard 2016-05-10 11:21:26 +02:00
parent 0c5abc491d
commit 72b324c95b
22 changed files with 56464 additions and 17401 deletions

4
Godeps/Godeps.json generated
View File

@ -1,7 +1,6 @@
{
"ImportPath": "github.com/grafana/grafana",
"GoVersion": "go1.5.1",
"GodepVersion": "v60",
"Packages": [
"./pkg/..."
],
@ -276,7 +275,8 @@
},
{
"ImportPath": "github.com/mattn/go-sqlite3",
"Rev": "e28cd440fabdd39b9520344bc26829f61db40ece"
"Comment": "v1.1.0-67-g7204887",
"Rev": "7204887cf3a42df1cfaa5505dc3a3427f6dded8b"
},
{
"ImportPath": "github.com/rainycape/unidecode",

View File

@ -1,9 +1,13 @@
language: go
sudo: required
dist: trusty
go:
- 1.5
- 1.6
- tip
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
- $HOME/gopath/bin/goveralls -repotoken 3qJVUE0iQwqnCbmNcDsjYu1nh4J4KIFXx
- go test -v . -tags "libsqlite3"

View File

@ -1,8 +1,9 @@
go-sqlite3
==========
[![Build Status](https://travis-ci.org/mattn/go-sqlite3.png?branch=master)](https://travis-ci.org/mattn/go-sqlite3)
[![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.png?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
[![Build Status](https://travis-ci.org/mattn/go-sqlite3.svg?branch=master)](https://travis-ci.org/mattn/go-sqlite3)
[![Coverage Status](https://coveralls.io/repos/mattn/go-sqlite3/badge.svg?branch=master)](https://coveralls.io/r/mattn/go-sqlite3?branch=master)
[![GoDoc](https://godoc.org/github.com/mattn/go-sqlite3?status.svg)](http://godoc.org/github.com/mattn/go-sqlite3)
Description
-----------
@ -16,6 +17,10 @@ This package can be installed with the go get command:
go get github.com/mattn/go-sqlite3
_go-sqlite3_ is *cgo* package.
If you want to build your app using go-sqlite3, you need gcc.
However, if you install _go-sqlite3_ with `go install github.com/mattn/go-sqlite3`, you don't need gcc to build your app anymore.
Documentation
-------------
@ -26,6 +31,20 @@ Examples can be found under the `./_example` directory
FAQ
---
* Want to build go-sqlite3 with libsqlite3 on my linux.
Use `go build --tags "libsqlite3 linux"`
* Want to build go-sqlite3 with libsqlite3 on OS X.
Install sqlite3 from homebrew: `brew install sqlite3`
Use `go build --tags "libsqlite3 darwin"`
* Want to build go-sqlite3 with icu extension.
Use `go build --tags "icu"`
* Can't build go-sqlite3 on windows 64bit.
> Probably, you are using go 1.0, go1.0 has a problem when it comes to compiling/linking on windows 64bit.
@ -36,7 +55,7 @@ FAQ
> You can pass some arguments into the connection string, for example, a URI.
> See: https://github.com/mattn/go-sqlite3/issues/39
* Do you want cross compiling? mingw on Linux or Mac?
* Do you want to cross compile? mingw on Linux or Mac?
> See: https://github.com/mattn/go-sqlite3/issues/106
> See also: http://www.limitlessfx.com/cross-compile-golang-app-for-windows-from-linux.html
@ -54,7 +73,7 @@ sqlite3-binding.c, sqlite3-binding.h, sqlite3ext.h
The -binding suffix was added to avoid build failures under gccgo.
In this repository, those files are amalgamation code that copied from SQLite3. The license of those codes are depend on the license of SQLite3.
In this repository, those files are an amalgamation of code that was copied from SQLite3. The license of that code is the same as the license of SQLite3.
Author
------

View File

@ -6,7 +6,11 @@
package sqlite3
/*
#ifndef USE_LIBSQLITE3
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
#include <stdlib.h>
*/
import "C"

View File

@ -0,0 +1,336 @@
// 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.
package sqlite3
// You can't export a Go function to C and have definitions in the C
// preamble in the same file, so we have to have callbackTrampoline in
// its own file. Because we need a separate file anyway, the support
// code for SQLite custom functions is in here.
/*
#include <sqlite3-binding.h>
#include <stdlib.h>
void _sqlite3_result_text(sqlite3_context* ctx, const char* s);
void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l);
*/
import "C"
import (
"errors"
"fmt"
"math"
"reflect"
"sync"
"unsafe"
)
//export callbackTrampoline
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo)
fi.Call(ctx, args)
}
//export stepTrampoline
func stepTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo)
ai.Step(ctx, args)
}
//export doneTrampoline
func doneTrampoline(ctx *C.sqlite3_context) {
handle := uintptr(C.sqlite3_user_data(ctx))
ai := lookupHandle(handle).(*aggInfo)
ai.Done(ctx)
}
// Use handles to avoid passing Go pointers to C.
type handleVal struct {
db *SQLiteConn
val interface{}
}
var handleLock sync.Mutex
var handleVals = make(map[uintptr]handleVal)
var handleIndex uintptr = 100
func newHandle(db *SQLiteConn, v interface{}) uintptr {
handleLock.Lock()
defer handleLock.Unlock()
i := handleIndex
handleIndex++
handleVals[i] = handleVal{db, v}
return i
}
func lookupHandle(handle uintptr) interface{} {
handleLock.Lock()
defer handleLock.Unlock()
r, ok := handleVals[handle]
if !ok {
if handle >= 100 && handle < handleIndex {
panic("deleted handle")
} else {
panic("invalid handle")
}
}
return r.val
}
func deleteHandles(db *SQLiteConn) {
handleLock.Lock()
defer handleLock.Unlock()
for handle, val := range handleVals {
if val.db == db {
delete(handleVals, handle)
}
}
}
// This is only here so that tests can refer to it.
type callbackArgRaw C.sqlite3_value
type callbackArgConverter func(*C.sqlite3_value) (reflect.Value, error)
type callbackArgCast struct {
f callbackArgConverter
typ reflect.Type
}
func (c callbackArgCast) Run(v *C.sqlite3_value) (reflect.Value, error) {
val, err := c.f(v)
if err != nil {
return reflect.Value{}, err
}
if !val.Type().ConvertibleTo(c.typ) {
return reflect.Value{}, fmt.Errorf("cannot convert %s to %s", val.Type(), c.typ)
}
return val.Convert(c.typ), nil
}
func callbackArgInt64(v *C.sqlite3_value) (reflect.Value, error) {
if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
}
return reflect.ValueOf(int64(C.sqlite3_value_int64(v))), nil
}
func callbackArgBool(v *C.sqlite3_value) (reflect.Value, error) {
if C.sqlite3_value_type(v) != C.SQLITE_INTEGER {
return reflect.Value{}, fmt.Errorf("argument must be an INTEGER")
}
i := int64(C.sqlite3_value_int64(v))
val := false
if i != 0 {
val = true
}
return reflect.ValueOf(val), nil
}
func callbackArgFloat64(v *C.sqlite3_value) (reflect.Value, error) {
if C.sqlite3_value_type(v) != C.SQLITE_FLOAT {
return reflect.Value{}, fmt.Errorf("argument must be a FLOAT")
}
return reflect.ValueOf(float64(C.sqlite3_value_double(v))), nil
}
func callbackArgBytes(v *C.sqlite3_value) (reflect.Value, error) {
switch C.sqlite3_value_type(v) {
case C.SQLITE_BLOB:
l := C.sqlite3_value_bytes(v)
p := C.sqlite3_value_blob(v)
return reflect.ValueOf(C.GoBytes(p, l)), nil
case C.SQLITE_TEXT:
l := C.sqlite3_value_bytes(v)
c := unsafe.Pointer(C.sqlite3_value_text(v))
return reflect.ValueOf(C.GoBytes(c, l)), nil
default:
return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
}
}
func callbackArgString(v *C.sqlite3_value) (reflect.Value, error) {
switch C.sqlite3_value_type(v) {
case C.SQLITE_BLOB:
l := C.sqlite3_value_bytes(v)
p := (*C.char)(C.sqlite3_value_blob(v))
return reflect.ValueOf(C.GoStringN(p, l)), nil
case C.SQLITE_TEXT:
c := (*C.char)(unsafe.Pointer(C.sqlite3_value_text(v)))
return reflect.ValueOf(C.GoString(c)), nil
default:
return reflect.Value{}, fmt.Errorf("argument must be BLOB or TEXT")
}
}
func callbackArgGeneric(v *C.sqlite3_value) (reflect.Value, error) {
switch C.sqlite3_value_type(v) {
case C.SQLITE_INTEGER:
return callbackArgInt64(v)
case C.SQLITE_FLOAT:
return callbackArgFloat64(v)
case C.SQLITE_TEXT:
return callbackArgString(v)
case C.SQLITE_BLOB:
return callbackArgBytes(v)
case C.SQLITE_NULL:
// Interpret NULL as a nil byte slice.
var ret []byte
return reflect.ValueOf(ret), nil
default:
panic("unreachable")
}
}
func callbackArg(typ reflect.Type) (callbackArgConverter, error) {
switch typ.Kind() {
case reflect.Interface:
if typ.NumMethod() != 0 {
return nil, errors.New("the only supported interface type is interface{}")
}
return callbackArgGeneric, nil
case reflect.Slice:
if typ.Elem().Kind() != reflect.Uint8 {
return nil, errors.New("the only supported slice type is []byte")
}
return callbackArgBytes, nil
case reflect.String:
return callbackArgString, nil
case reflect.Bool:
return callbackArgBool, nil
case reflect.Int64:
return callbackArgInt64, nil
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
c := callbackArgCast{callbackArgInt64, typ}
return c.Run, nil
case reflect.Float64:
return callbackArgFloat64, nil
case reflect.Float32:
c := callbackArgCast{callbackArgFloat64, typ}
return c.Run, nil
default:
return nil, fmt.Errorf("don't know how to convert to %s", typ)
}
}
func callbackConvertArgs(argv []*C.sqlite3_value, converters []callbackArgConverter, variadic callbackArgConverter) ([]reflect.Value, error) {
var args []reflect.Value
if len(argv) < len(converters) {
return nil, fmt.Errorf("function requires at least %d arguments", len(converters))
}
for i, arg := range argv[:len(converters)] {
v, err := converters[i](arg)
if err != nil {
return nil, err
}
args = append(args, v)
}
if variadic != nil {
for _, arg := range argv[len(converters):] {
v, err := variadic(arg)
if err != nil {
return nil, err
}
args = append(args, v)
}
}
return args, nil
}
type callbackRetConverter func(*C.sqlite3_context, reflect.Value) error
func callbackRetInteger(ctx *C.sqlite3_context, v reflect.Value) error {
switch v.Type().Kind() {
case reflect.Int64:
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
v = v.Convert(reflect.TypeOf(int64(0)))
case reflect.Bool:
b := v.Interface().(bool)
if b {
v = reflect.ValueOf(int64(1))
} else {
v = reflect.ValueOf(int64(0))
}
default:
return fmt.Errorf("cannot convert %s to INTEGER", v.Type())
}
C.sqlite3_result_int64(ctx, C.sqlite3_int64(v.Interface().(int64)))
return nil
}
func callbackRetFloat(ctx *C.sqlite3_context, v reflect.Value) error {
switch v.Type().Kind() {
case reflect.Float64:
case reflect.Float32:
v = v.Convert(reflect.TypeOf(float64(0)))
default:
return fmt.Errorf("cannot convert %s to FLOAT", v.Type())
}
C.sqlite3_result_double(ctx, C.double(v.Interface().(float64)))
return nil
}
func callbackRetBlob(ctx *C.sqlite3_context, v reflect.Value) error {
if v.Type().Kind() != reflect.Slice || v.Type().Elem().Kind() != reflect.Uint8 {
return fmt.Errorf("cannot convert %s to BLOB", v.Type())
}
i := v.Interface()
if i == nil || len(i.([]byte)) == 0 {
C.sqlite3_result_null(ctx)
} else {
bs := i.([]byte)
C._sqlite3_result_blob(ctx, unsafe.Pointer(&bs[0]), C.int(len(bs)))
}
return nil
}
func callbackRetText(ctx *C.sqlite3_context, v reflect.Value) error {
if v.Type().Kind() != reflect.String {
return fmt.Errorf("cannot convert %s to TEXT", v.Type())
}
C._sqlite3_result_text(ctx, C.CString(v.Interface().(string)))
return nil
}
func callbackRet(typ reflect.Type) (callbackRetConverter, error) {
switch typ.Kind() {
case reflect.Slice:
if typ.Elem().Kind() != reflect.Uint8 {
return nil, errors.New("the only supported slice type is []byte")
}
return callbackRetBlob, nil
case reflect.String:
return callbackRetText, nil
case reflect.Bool, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Uint:
return callbackRetInteger, nil
case reflect.Float32, reflect.Float64:
return callbackRetFloat, nil
default:
return nil, fmt.Errorf("don't know how to convert to %s", typ)
}
}
func callbackError(ctx *C.sqlite3_context, err error) {
cstr := C.CString(err.Error())
defer C.free(unsafe.Pointer(cstr))
C.sqlite3_result_error(ctx, cstr, -1)
}
// Test support code. Tests are not allowed to import "C", so we can't
// declare any functions that use C.sqlite3_value.
func callbackSyntheticForTests(v reflect.Value, err error) callbackArgConverter {
return func(*C.sqlite3_value) (reflect.Value, error) {
return v, err
}
}

View File

@ -1,7 +1,7 @@
/*
Package sqlite3 provides interface to SQLite3 databases.
This works as driver for database/sql.
This works as a driver for database/sql.
Installation
@ -9,7 +9,7 @@ Installation
Supported Types
Currently, go-sqlite3 support following data types.
Currently, go-sqlite3 supports the following data types.
+------------------------------+
|go | sqlite3 |
@ -26,14 +26,14 @@ Currently, go-sqlite3 support following data types.
SQLite3 Extension
You can write your own extension module for sqlite3. For example, below is a
extension for Regexp matcher operation.
You can write your own extension module for sqlite3. For example, below is an
extension for a Regexp matcher operation.
#include <pcre.h>
#include <string.h>
#include <stdio.h>
#include <sqlite3ext.h>
SQLITE_EXTENSION_INIT1
static void regexp_func(sqlite3_context *context, int argc, sqlite3_value **argv) {
if (argc >= 2) {
@ -44,7 +44,7 @@ extension for Regexp matcher operation.
int vec[500];
int n, rc;
pcre* re = pcre_compile(pattern, 0, &errstr, &erroff, NULL);
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
rc = pcre_exec(re, NULL, target, strlen(target), 0, 0, vec, 500);
if (rc <= 0) {
sqlite3_result_error(context, errstr, 0);
return;
@ -52,7 +52,7 @@ extension for Regexp matcher operation.
sqlite3_result_int(context, 1);
}
}
#ifdef _WIN32
__declspec(dllexport)
#endif
@ -63,8 +63,8 @@ extension for Regexp matcher operation.
(void*)db, regexp_func, NULL, NULL);
}
It need to build as so/dll shared library. And you need to register
extension module like below.
It needs to be built as a so/dll shared library. And you need to register
the extension module like below.
sql.Register("sqlite3_with_extensions",
&sqlite3.SQLiteDriver{
@ -79,9 +79,9 @@ Then, you can use this extension.
Connection Hook
You can hook and inject your codes when connection established. database/sql
doesn't provide the way to get native go-sqlite3 interfaces. So if you want,
you need to hook ConnectHook and get the SQLiteConn.
You can hook and inject your code when the connection is established. database/sql
doesn't provide a way to get native go-sqlite3 interfaces. So if you want,
you need to set ConnectHook and get the SQLiteConn.
sql.Register("sqlite3_with_hook_example",
&sqlite3.SQLiteDriver{
@ -91,5 +91,22 @@ you need to hook ConnectHook and get the SQLiteConn.
},
})
Go SQlite3 Extensions
If you want to register Go functions as SQLite extension functions,
call RegisterFunction from ConnectHook.
regex = func(re, s string) (bool, error) {
return regexp.MatchString(re, s)
}
sql.Register("sqlite3_with_go_func",
&sqlite3.SQLiteDriver{
ConnectHook: func(conn *sqlite3.SQLiteConn) error {
return conn.RegisterFunc("regexp", regex, true)
},
})
See the documentation of RegisterFunc for more details.
*/
package sqlite3

View File

@ -1,242 +0,0 @@
// 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.
package sqlite3
import (
"database/sql"
"io/ioutil"
"os"
"path"
"testing"
)
func TestSimpleError(t *testing.T) {
e := ErrError.Error()
if e != "SQL logic error or missing database" {
t.Error("wrong error code:" + e)
}
}
func TestCorruptDbErrors(t *testing.T) {
dirName, err := ioutil.TempDir("", "sqlite3")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dirName)
dbFileName := path.Join(dirName, "test.db")
f, err := os.Create(dbFileName)
if err != nil {
t.Error(err)
}
f.Write([]byte{1, 2, 3, 4, 5})
f.Close()
db, err := sql.Open("sqlite3", dbFileName)
if err == nil {
_, err = db.Exec("drop table foo")
}
sqliteErr := err.(Error)
if sqliteErr.Code != ErrNotADB {
t.Error("wrong error code for corrupted DB")
}
if err.Error() == "" {
t.Error("wrong error string for corrupted DB")
}
db.Close()
}
func TestSqlLogicErrors(t *testing.T) {
dirName, err := ioutil.TempDir("", "sqlite3")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dirName)
dbFileName := path.Join(dirName, "test.db")
db, err := sql.Open("sqlite3", dbFileName)
if err != nil {
t.Error(err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
if err != nil {
t.Error(err)
}
const expectedErr = "table Foo already exists"
_, err = db.Exec("CREATE TABLE Foo (id INTEGER PRIMARY KEY)")
if err.Error() != expectedErr {
t.Errorf("Unexpected error: %s, expected %s", err.Error(), expectedErr)
}
}
func TestExtendedErrorCodes_ForeignKey(t *testing.T) {
dirName, err := ioutil.TempDir("", "sqlite3-err")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dirName)
dbFileName := path.Join(dirName, "test.db")
db, err := sql.Open("sqlite3", dbFileName)
if err != nil {
t.Error(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys=ON;")
if err != nil {
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
}
_, err = db.Exec(`CREATE TABLE Foo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value INTEGER NOT NULL,
ref INTEGER NULL REFERENCES Foo (id),
UNIQUE(value)
);`)
if err != nil {
t.Error(err)
}
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (100, 100);")
if err == nil {
t.Error("No error!")
} else {
sqliteErr := err.(Error)
if sqliteErr.Code != ErrConstraint {
t.Errorf("Wrong basic error code: %d != %d",
sqliteErr.Code, ErrConstraint)
}
if sqliteErr.ExtendedCode != ErrConstraintForeignKey {
t.Errorf("Wrong extended error code: %d != %d",
sqliteErr.ExtendedCode, ErrConstraintForeignKey)
}
}
}
func TestExtendedErrorCodes_NotNull(t *testing.T) {
dirName, err := ioutil.TempDir("", "sqlite3-err")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dirName)
dbFileName := path.Join(dirName, "test.db")
db, err := sql.Open("sqlite3", dbFileName)
if err != nil {
t.Error(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys=ON;")
if err != nil {
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
}
_, err = db.Exec(`CREATE TABLE Foo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value INTEGER NOT NULL,
ref INTEGER NULL REFERENCES Foo (id),
UNIQUE(value)
);`)
if err != nil {
t.Error(err)
}
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
if err != nil {
t.Fatalf("Creating first row: %v", err)
}
id, err := res.LastInsertId()
if err != nil {
t.Fatalf("Retrieving last insert id: %v", err)
}
_, err = db.Exec("INSERT INTO Foo (ref) VALUES (?);", id)
if err == nil {
t.Error("No error!")
} else {
sqliteErr := err.(Error)
if sqliteErr.Code != ErrConstraint {
t.Errorf("Wrong basic error code: %d != %d",
sqliteErr.Code, ErrConstraint)
}
if sqliteErr.ExtendedCode != ErrConstraintNotNull {
t.Errorf("Wrong extended error code: %d != %d",
sqliteErr.ExtendedCode, ErrConstraintNotNull)
}
}
}
func TestExtendedErrorCodes_Unique(t *testing.T) {
dirName, err := ioutil.TempDir("", "sqlite3-err")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dirName)
dbFileName := path.Join(dirName, "test.db")
db, err := sql.Open("sqlite3", dbFileName)
if err != nil {
t.Error(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys=ON;")
if err != nil {
t.Errorf("PRAGMA foreign_keys=ON: %v", err)
}
_, err = db.Exec(`CREATE TABLE Foo (
id INTEGER PRIMARY KEY AUTOINCREMENT,
value INTEGER NOT NULL,
ref INTEGER NULL REFERENCES Foo (id),
UNIQUE(value)
);`)
if err != nil {
t.Error(err)
}
res, err := db.Exec("INSERT INTO Foo (value) VALUES (100);")
if err != nil {
t.Fatalf("Creating first row: %v", err)
}
id, err := res.LastInsertId()
if err != nil {
t.Fatalf("Retrieving last insert id: %v", err)
}
_, err = db.Exec("INSERT INTO Foo (ref, value) VALUES (?, 100);", id)
if err == nil {
t.Error("No error!")
} else {
sqliteErr := err.(Error)
if sqliteErr.Code != ErrConstraint {
t.Errorf("Wrong basic error code: %d != %d",
sqliteErr.Code, ErrConstraint)
}
if sqliteErr.ExtendedCode != ErrConstraintUnique {
t.Errorf("Wrong extended error code: %d != %d",
sqliteErr.ExtendedCode, ErrConstraintUnique)
}
extended := sqliteErr.Code.Extend(3).Error()
expected := "constraint failed"
if extended != expected {
t.Errorf("Wrong basic error code: %q != %q",
extended, expected)
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,12 @@ package sqlite3
/*
#cgo CFLAGS: -std=gnu99
#cgo CFLAGS: -DSQLITE_ENABLE_RTREE -DSQLITE_THREADSAFE
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS
#cgo CFLAGS: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS -DSQLITE_ENABLE_FTS4_UNICODE61
#ifndef USE_LIBSQLITE3
#include <sqlite3-binding.h>
#else
#include <sqlite3.h>
#endif
#include <stdlib.h>
#include <string.h>
@ -25,6 +29,10 @@ package sqlite3
# define SQLITE_OPEN_FULLMUTEX 0
#endif
#ifndef SQLITE_DETERMINISTIC
# define SQLITE_DETERMINISTIC 0
#endif
static int
_sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs) {
#ifdef SQLITE_OPEN_URI
@ -48,24 +56,49 @@ _sqlite3_bind_blob(sqlite3_stmt *stmt, int n, void *p, int np) {
#include <stdint.h>
static int
_sqlite3_exec(sqlite3* db, const char* pcmd, long* rowid, long* changes)
_sqlite3_exec(sqlite3* db, const char* pcmd, long long* rowid, long long* changes)
{
int rv = sqlite3_exec(db, pcmd, 0, 0, 0);
*rowid = (long) sqlite3_last_insert_rowid(db);
*changes = (long) sqlite3_changes(db);
*rowid = (long long) sqlite3_last_insert_rowid(db);
*changes = (long long) sqlite3_changes(db);
return rv;
}
static int
_sqlite3_step(sqlite3_stmt* stmt, long* rowid, long* changes)
_sqlite3_step(sqlite3_stmt* stmt, long long* rowid, long long* changes)
{
int rv = sqlite3_step(stmt);
sqlite3* db = sqlite3_db_handle(stmt);
*rowid = (long) sqlite3_last_insert_rowid(db);
*changes = (long) sqlite3_changes(db);
*rowid = (long long) sqlite3_last_insert_rowid(db);
*changes = (long long) sqlite3_changes(db);
return rv;
}
void _sqlite3_result_text(sqlite3_context* ctx, const char* s) {
sqlite3_result_text(ctx, s, -1, &free);
}
void _sqlite3_result_blob(sqlite3_context* ctx, const void* b, int l) {
sqlite3_result_blob(ctx, b, l, SQLITE_TRANSIENT);
}
int _sqlite3_create_function(
sqlite3 *db,
const char *zFunctionName,
int nArg,
int eTextRep,
uintptr_t pApp,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
) {
return sqlite3_create_function(db, zFunctionName, nArg, eTextRep, (void*) pApp, xFunc, xStep, xFinal);
}
void callbackTrampoline(sqlite3_context*, int, sqlite3_value**);
void stepTrampoline(sqlite3_context*, int, sqlite3_value**);
void doneTrampoline(sqlite3_context*);
*/
import "C"
import (
@ -75,6 +108,7 @@ import (
"fmt"
"io"
"net/url"
"reflect"
"runtime"
"strconv"
"strings"
@ -87,6 +121,10 @@ import (
// into the database. When parsing a string from a timestamp or
// datetime column, the formats are tried in order.
var SQLiteTimestampFormats = []string{
// By default, store timestamps with whatever timezone they come with.
// When parsed, they will be returned with the same timezone.
"2006-01-02 15:04:05.999999999-07:00",
"2006-01-02T15:04:05.999999999-07:00",
"2006-01-02 15:04:05.999999999",
"2006-01-02T15:04:05.999999999",
"2006-01-02 15:04:05",
@ -94,14 +132,13 @@ var SQLiteTimestampFormats = []string{
"2006-01-02 15:04",
"2006-01-02T15:04",
"2006-01-02",
"2006-01-02 15:04:05-07:00",
}
func init() {
sql.Register("sqlite3", &SQLiteDriver{})
}
// Return SQLite library Version information.
// Version returns SQLite library version information.
func Version() (libVersion string, libVersionNumber int, sourceId string) {
libVersion = C.GoString(C.sqlite3_libversion())
libVersionNumber = int(C.sqlite3_libversion_number())
@ -117,8 +154,11 @@ type SQLiteDriver struct {
// Conn struct.
type SQLiteConn struct {
db *C.sqlite3
loc *time.Location
db *C.sqlite3
loc *time.Location
txlock string
funcs []*functionInfo
aggregators []*aggInfo
}
// Tx struct.
@ -152,6 +192,107 @@ type SQLiteRows struct {
cls bool
}
type functionInfo struct {
f reflect.Value
argConverters []callbackArgConverter
variadicConverter callbackArgConverter
retConverter callbackRetConverter
}
func (fi *functionInfo) Call(ctx *C.sqlite3_context, argv []*C.sqlite3_value) {
args, err := callbackConvertArgs(argv, fi.argConverters, fi.variadicConverter)
if err != nil {
callbackError(ctx, err)
return
}
ret := fi.f.Call(args)
if len(ret) == 2 && ret[1].Interface() != nil {
callbackError(ctx, ret[1].Interface().(error))
return
}
err = fi.retConverter(ctx, ret[0])
if err != nil {
callbackError(ctx, err)
return
}
}
type aggInfo struct {
constructor reflect.Value
// Active aggregator objects for aggregations in flight. The
// aggregators are indexed by a counter stored in the aggregation
// user data space provided by sqlite.
active map[int64]reflect.Value
next int64
stepArgConverters []callbackArgConverter
stepVariadicConverter callbackArgConverter
doneRetConverter callbackRetConverter
}
func (ai *aggInfo) agg(ctx *C.sqlite3_context) (int64, reflect.Value, error) {
aggIdx := (*int64)(C.sqlite3_aggregate_context(ctx, C.int(8)))
if *aggIdx == 0 {
*aggIdx = ai.next
ret := ai.constructor.Call(nil)
if len(ret) == 2 && ret[1].Interface() != nil {
return 0, reflect.Value{}, ret[1].Interface().(error)
}
if ret[0].IsNil() {
return 0, reflect.Value{}, errors.New("aggregator constructor returned nil state")
}
ai.next++
ai.active[*aggIdx] = ret[0]
}
return *aggIdx, ai.active[*aggIdx], nil
}
func (ai *aggInfo) Step(ctx *C.sqlite3_context, argv []*C.sqlite3_value) {
_, agg, err := ai.agg(ctx)
if err != nil {
callbackError(ctx, err)
return
}
args, err := callbackConvertArgs(argv, ai.stepArgConverters, ai.stepVariadicConverter)
if err != nil {
callbackError(ctx, err)
return
}
ret := agg.MethodByName("Step").Call(args)
if len(ret) == 1 && ret[0].Interface() != nil {
callbackError(ctx, ret[0].Interface().(error))
return
}
}
func (ai *aggInfo) Done(ctx *C.sqlite3_context) {
idx, agg, err := ai.agg(ctx)
if err != nil {
callbackError(ctx, err)
return
}
defer func() { delete(ai.active, idx) }()
ret := agg.MethodByName("Done").Call(nil)
if len(ret) == 2 && ret[1].Interface() != nil {
callbackError(ctx, ret[1].Interface().(error))
return
}
err = ai.doneRetConverter(ctx, ret[0])
if err != nil {
callbackError(ctx, err)
return
}
}
// Commit transaction.
func (tx *SQLiteTx) Commit() error {
_, err := tx.c.exec("COMMIT")
@ -164,6 +305,208 @@ func (tx *SQLiteTx) Rollback() error {
return err
}
// RegisterFunc makes a Go function available as a SQLite function.
//
// The Go function can have arguments of the following types: any
// numeric type except complex, bool, []byte, string and
// interface{}. interface{} arguments are given the direct translation
// of the SQLite data type: int64 for INTEGER, float64 for FLOAT,
// []byte for BLOB, string for TEXT.
//
// The function can additionally be variadic, as long as the type of
// the variadic argument is one of the above.
//
// If pure is true. SQLite will assume that the function's return
// value depends only on its inputs, and make more aggressive
// optimizations in its queries.
//
// See _example/go_custom_funcs for a detailed example.
func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) error {
var fi functionInfo
fi.f = reflect.ValueOf(impl)
t := fi.f.Type()
if t.Kind() != reflect.Func {
return errors.New("Non-function passed to RegisterFunc")
}
if t.NumOut() != 1 && t.NumOut() != 2 {
return errors.New("SQLite functions must return 1 or 2 values")
}
if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
return errors.New("Second return value of SQLite function must be error")
}
numArgs := t.NumIn()
if t.IsVariadic() {
numArgs--
}
for i := 0; i < numArgs; i++ {
conv, err := callbackArg(t.In(i))
if err != nil {
return err
}
fi.argConverters = append(fi.argConverters, conv)
}
if t.IsVariadic() {
conv, err := callbackArg(t.In(numArgs).Elem())
if err != nil {
return err
}
fi.variadicConverter = conv
// Pass -1 to sqlite so that it allows any number of
// arguments. The call helper verifies that the minimum number
// of arguments is present for variadic functions.
numArgs = -1
}
conv, err := callbackRet(t.Out(0))
if err != nil {
return err
}
fi.retConverter = conv
// fi must outlast the database connection, or we'll have dangling pointers.
c.funcs = append(c.funcs, &fi)
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
opts := C.SQLITE_UTF8
if pure {
opts |= C.SQLITE_DETERMINISTIC
}
rv := C._sqlite3_create_function(c.db, cname, C.int(numArgs), C.int(opts), C.uintptr_t(newHandle(c, &fi)), (*[0]byte)(unsafe.Pointer(C.callbackTrampoline)), nil, nil)
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
}
// RegisterAggregator makes a Go type available as a SQLite aggregation function.
//
// Because aggregation is incremental, it's implemented in Go with a
// type that has 2 methods: func Step(values) accumulates one row of
// data into the accumulator, and func Done() ret finalizes and
// returns the aggregate value. "values" and "ret" may be any type
// supported by RegisterFunc.
//
// RegisterAggregator takes as implementation a constructor function
// that constructs an instance of the aggregator type each time an
// aggregation begins. The constructor must return a pointer to a
// type, or an interface that implements Step() and Done().
//
// The constructor function and the Step/Done methods may optionally
// return an error in addition to their other return values.
//
// See _example/go_custom_funcs for a detailed example.
func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool) error {
var ai aggInfo
ai.constructor = reflect.ValueOf(impl)
t := ai.constructor.Type()
if t.Kind() != reflect.Func {
return errors.New("non-function passed to RegisterAggregator")
}
if t.NumOut() != 1 && t.NumOut() != 2 {
return errors.New("SQLite aggregator constructors must return 1 or 2 values")
}
if t.NumOut() == 2 && !t.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
return errors.New("Second return value of SQLite function must be error")
}
if t.NumIn() != 0 {
return errors.New("SQLite aggregator constructors must not have arguments")
}
agg := t.Out(0)
switch agg.Kind() {
case reflect.Ptr, reflect.Interface:
default:
return errors.New("SQlite aggregator constructor must return a pointer object")
}
stepFn, found := agg.MethodByName("Step")
if !found {
return errors.New("SQlite aggregator doesn't have a Step() function")
}
step := stepFn.Type
if step.NumOut() != 0 && step.NumOut() != 1 {
return errors.New("SQlite aggregator Step() function must return 0 or 1 values")
}
if step.NumOut() == 1 && !step.Out(0).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
return errors.New("type of SQlite aggregator Step() return value must be error")
}
stepNArgs := step.NumIn()
start := 0
if agg.Kind() == reflect.Ptr {
// Skip over the method receiver
stepNArgs--
start++
}
if step.IsVariadic() {
stepNArgs--
}
for i := start; i < start+stepNArgs; i++ {
conv, err := callbackArg(step.In(i))
if err != nil {
return err
}
ai.stepArgConverters = append(ai.stepArgConverters, conv)
}
if step.IsVariadic() {
conv, err := callbackArg(t.In(start + stepNArgs).Elem())
if err != nil {
return err
}
ai.stepVariadicConverter = conv
// Pass -1 to sqlite so that it allows any number of
// arguments. The call helper verifies that the minimum number
// of arguments is present for variadic functions.
stepNArgs = -1
}
doneFn, found := agg.MethodByName("Done")
if !found {
return errors.New("SQlite aggregator doesn't have a Done() function")
}
done := doneFn.Type
doneNArgs := done.NumIn()
if agg.Kind() == reflect.Ptr {
// Skip over the method receiver
doneNArgs--
}
if doneNArgs != 0 {
return errors.New("SQlite aggregator Done() function must have no arguments")
}
if done.NumOut() != 1 && done.NumOut() != 2 {
return errors.New("SQLite aggregator Done() function must return 1 or 2 values")
}
if done.NumOut() == 2 && !done.Out(1).Implements(reflect.TypeOf((*error)(nil)).Elem()) {
return errors.New("second return value of SQLite aggregator Done() function must be error")
}
conv, err := callbackRet(done.Out(0))
if err != nil {
return err
}
ai.doneRetConverter = conv
ai.active = make(map[int64]reflect.Value)
ai.next = 1
// ai must outlast the database connection, or we'll have dangling pointers.
c.aggregators = append(c.aggregators, &ai)
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
opts := C.SQLITE_UTF8
if pure {
opts |= C.SQLITE_DETERMINISTIC
}
rv := C._sqlite3_create_function(c.db, cname, C.int(stepNArgs), C.int(opts), C.uintptr_t(newHandle(c, &ai)), nil, (*[0]byte)(unsafe.Pointer(C.stepTrampoline)), (*[0]byte)(unsafe.Pointer(C.doneTrampoline)))
if rv != C.SQLITE_OK {
return c.lastError()
}
return nil
}
// AutoCommit return which currently auto commit or not.
func (c *SQLiteConn) AutoCommit() bool {
return int(C.sqlite3_get_autocommit(c.db)) != 0
@ -242,7 +585,7 @@ func (c *SQLiteConn) exec(cmd string) (driver.Result, error) {
pcmd := C.CString(cmd)
defer C.free(unsafe.Pointer(pcmd))
var rowid, changes C.long
var rowid, changes C.longlong
rv := C._sqlite3_exec(c.db, pcmd, &rowid, &changes)
if rv != C.SQLITE_OK {
return nil, c.lastError()
@ -252,7 +595,7 @@ func (c *SQLiteConn) exec(cmd string) (driver.Result, error) {
// Begin transaction.
func (c *SQLiteConn) Begin() (driver.Tx, error) {
if _, err := c.exec("BEGIN"); err != nil {
if _, err := c.exec(c.txlock); err != nil {
return nil, err
}
return &SQLiteTx{c}, nil
@ -263,22 +606,26 @@ func errorString(err Error) string {
}
// Open database and return a new connection.
// You can specify DSN string with URI filename.
// You can specify a DSN string using a URI as the filename.
// test.db
// file:test.db?cache=shared&mode=memory
// :memory:
// file::memory:
// go-sqlite handle especially query parameters.
// go-sqlite3 adds the following query parameters to those used by SQLite:
// _loc=XXX
// Specify location of time format. It's possible to specify "auto".
// _busy_timeout=XXX
// Specify value for sqlite3_busy_timeout.
// _txlock=XXX
// Specify locking behavior for transactions. XXX can be "immediate",
// "deferred", "exclusive".
func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
if C.sqlite3_threadsafe() == 0 {
return nil, errors.New("sqlite library was not compiled for thread-safe operation")
}
var loc *time.Location
txlock := "BEGIN"
busy_timeout := 5000
pos := strings.IndexRune(dsn, '?')
if pos >= 1 {
@ -308,6 +655,20 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
busy_timeout = int(iv)
}
// _txlock
if val := params.Get("_txlock"); val != "" {
switch val {
case "immediate":
txlock = "BEGIN IMMEDIATE"
case "exclusive":
txlock = "BEGIN EXCLUSIVE"
case "deferred":
txlock = "BEGIN"
default:
return nil, fmt.Errorf("Invalid _txlock: %v", val)
}
}
if !strings.HasPrefix(dsn, "file:") {
dsn = dsn[:pos]
}
@ -333,26 +694,11 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
return nil, Error{Code: ErrNo(rv)}
}
conn := &SQLiteConn{db: db, loc: loc}
conn := &SQLiteConn{db: db, loc: loc, txlock: txlock}
if len(d.Extensions) > 0 {
rv = C.sqlite3_enable_load_extension(db, 1)
if rv != C.SQLITE_OK {
return nil, errors.New(C.GoString(C.sqlite3_errmsg(db)))
}
for _, extension := range d.Extensions {
cext := C.CString(extension)
defer C.free(unsafe.Pointer(cext))
rv = C.sqlite3_load_extension(db, cext, nil, nil)
if rv != C.SQLITE_OK {
return nil, errors.New(C.GoString(C.sqlite3_errmsg(db)))
}
}
rv = C.sqlite3_enable_load_extension(db, 0)
if rv != C.SQLITE_OK {
return nil, errors.New(C.GoString(C.sqlite3_errmsg(db)))
if err := conn.loadExtensions(d.Extensions); err != nil {
return nil, err
}
}
@ -367,6 +713,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
// Close the connection.
func (c *SQLiteConn) Close() error {
deleteHandles(c)
rv := C.sqlite3_close_v2(c.db)
if rv != C.SQLITE_OK {
return c.lastError()
@ -376,7 +723,7 @@ func (c *SQLiteConn) Close() error {
return nil
}
// Prepare query string. Return a new statement.
// Prepare the query string. Return a new statement.
func (c *SQLiteConn) Prepare(query string) (driver.Stmt, error) {
pquery := C.CString(query)
defer C.free(unsafe.Pointer(pquery))
@ -476,13 +823,13 @@ func (s *SQLiteStmt) bind(args []driver.Value) error {
case float64:
rv = C.sqlite3_bind_double(s.s, n, C.double(v))
case []byte:
var p *byte
if len(v) > 0 {
p = &v[0]
if len(v) == 0 {
rv = C._sqlite3_bind_blob(s.s, n, nil, 0)
} else {
rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(&v[0]), C.int(len(v)))
}
rv = C._sqlite3_bind_blob(s.s, n, unsafe.Pointer(p), C.int(len(v)))
case time.Time:
b := []byte(v.UTC().Format(SQLiteTimestampFormats[0]))
b := []byte(v.Format(SQLiteTimestampFormats[0]))
rv = C._sqlite3_bind_text(s.s, n, (*C.char)(unsafe.Pointer(&b[0])), C.int(len(b)))
}
if rv != C.SQLITE_OK {
@ -517,7 +864,7 @@ func (s *SQLiteStmt) Exec(args []driver.Value) (driver.Result, error) {
C.sqlite3_clear_bindings(s.s)
return nil, err
}
var rowid, changes C.long
var rowid, changes C.longlong
rv := C._sqlite3_step(s.s, &rowid, &changes)
if rv != C.SQLITE_ROW && rv != C.SQLITE_OK && rv != C.SQLITE_DONE {
err := s.c.lastError()
@ -554,6 +901,17 @@ func (rc *SQLiteRows) Columns() []string {
return rc.cols
}
// Return column types.
func (rc *SQLiteRows) DeclTypes() []string {
if rc.decltype == nil {
rc.decltype = make([]string, rc.nc)
for i := 0; i < rc.nc; i++ {
rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
}
}
return rc.decltype
}
// Move cursor to next.
func (rc *SQLiteRows) Next(dest []driver.Value) error {
rv := C.sqlite3_step(rc.s.s)
@ -568,12 +926,7 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
return nil
}
if rc.decltype == nil {
rc.decltype = make([]string, rc.nc)
for i := 0; i < rc.nc; i++ {
rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i))))
}
}
rc.DeclTypes()
for i := range dest {
switch C.sqlite3_column_type(rc.s.s, C.int(i)) {
@ -581,18 +934,15 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
val := int64(C.sqlite3_column_int64(rc.s.s, C.int(i)))
switch rc.decltype[i] {
case "timestamp", "datetime", "date":
unixTimestamp := strconv.FormatInt(val, 10)
var t time.Time
if len(unixTimestamp) == 13 {
duration, err := time.ParseDuration(unixTimestamp + "ms")
if err != nil {
return fmt.Errorf("error parsing %s value %d, %s", rc.decltype[i], val, err)
}
epoch := time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC)
t = epoch.Add(duration)
// Assume a millisecond unix timestamp if it's 13 digits -- too
// large to be a reasonable timestamp in seconds.
if val > 1e12 || val < -1e12 {
val *= int64(time.Millisecond) // convert ms to nsec
} else {
t = time.Unix(val, 0)
val *= int64(time.Second) // convert sec to nsec
}
t = time.Unix(0, val).UTC()
if rc.s.c.loc != nil {
t = t.In(rc.s.c.loc)
}
@ -624,11 +974,14 @@ func (rc *SQLiteRows) Next(dest []driver.Value) error {
case C.SQLITE_TEXT:
var err error
var timeVal time.Time
s := C.GoString((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))))
n := int(C.sqlite3_column_bytes(rc.s.s, C.int(i)))
s := C.GoStringN((*C.char)(unsafe.Pointer(C.sqlite3_column_text(rc.s.s, C.int(i)))), C.int(n))
switch rc.decltype[i] {
case "timestamp", "datetime", "date":
var t time.Time
s = strings.TrimSuffix(s, "Z")
for _, format := range SQLiteTimestampFormats {
if timeVal, err = time.ParseInLocation(format, s, time.UTC); err == nil {
t = timeVal

View File

@ -1,83 +0,0 @@
// Copyright (C) 2015 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.
package sqlite3
import (
"database/sql"
"os"
"testing"
)
func TestFTS3(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("DROP TABLE foo")
_, err = db.Exec("CREATE VIRTUAL TABLE foo USING fts3(id INTEGER PRIMARY KEY, value TEXT)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 1, `今日の 晩御飯は 天麩羅よ`)
if err != nil {
t.Fatal("Failed to insert value:", err)
}
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(?, ?)", 2, `今日は いい 天気だ`)
if err != nil {
t.Fatal("Failed to insert value:", err)
}
rows, err := db.Query("SELECT id, value FROM foo WHERE value MATCH '今日* 天*'")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
defer rows.Close()
for rows.Next() {
var id int
var value string
if err := rows.Scan(&id, &value); err != nil {
t.Error("Unable to scan results:", err)
continue
}
if id == 1 && value != `今日の 晩御飯は 天麩羅よ` {
t.Error("Value for id 1 should be `今日の 晩御飯は 天麩羅よ`, but:", value)
} else if id == 2 && value != `今日は いい 天気だ` {
t.Error("Value for id 2 should be `今日は いい 天気だ`, but:", value)
}
}
rows, err = db.Query("SELECT value FROM foo WHERE value MATCH '今日* 天麩羅*'")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
defer rows.Close()
var value string
if !rows.Next() {
t.Fatal("Result should be only one")
}
if err := rows.Scan(&value); err != nil {
t.Fatal("Unable to scan results:", err)
}
if value != `今日の 晩御飯は 天麩羅よ` {
t.Fatal("Value should be `今日の 晩御飯は 天麩羅よ`, but:", value)
}
if rows.Next() {
t.Fatal("Result should be only one")
}
}

View File

@ -0,0 +1,13 @@
// 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.
// +build fts5
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_ENABLE_FTS5
#cgo LDFLAGS: -lm
*/
import "C"

View File

@ -0,0 +1,13 @@
// 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.
// +build icu
package sqlite3
/*
#cgo LDFLAGS: -licuuc -licui18n
#cgo CFLAGS: -DSQLITE_ENABLE_ICU
*/
import "C"

View File

@ -0,0 +1,12 @@
// 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.
// +build json1
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_ENABLE_JSON1
*/
import "C"

View File

@ -0,0 +1,14 @@
// 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.
// +build libsqlite3
package sqlite3
/*
#cgo CFLAGS: -DUSE_LIBSQLITE3
#cgo linux LDFLAGS: -lsqlite3
#cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3
*/
import "C"

View File

@ -0,0 +1,63 @@
// 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.
// +build !sqlite_omit_load_extension
package sqlite3
/*
#include <sqlite3-binding.h>
#include <stdlib.h>
*/
import "C"
import (
"errors"
"unsafe"
)
func (c *SQLiteConn) loadExtensions(extensions []string) error {
rv := C.sqlite3_enable_load_extension(c.db, 1)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
}
for _, extension := range extensions {
cext := C.CString(extension)
defer C.free(unsafe.Pointer(cext))
rv = C.sqlite3_load_extension(c.db, cext, nil, nil)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
}
}
rv = C.sqlite3_enable_load_extension(c.db, 0)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
}
return nil
}
func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
rv := C.sqlite3_enable_load_extension(c.db, 1)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
}
clib := C.CString(lib)
defer C.free(unsafe.Pointer(clib))
centry := C.CString(entry)
defer C.free(unsafe.Pointer(centry))
rv = C.sqlite3_load_extension(c.db, clib, centry, nil)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
}
rv = C.sqlite3_enable_load_extension(c.db, 0)
if rv != C.SQLITE_OK {
return errors.New(C.GoString(C.sqlite3_errmsg(c.db)))
}
return nil
}

View File

@ -0,0 +1,23 @@
// 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.
// +build sqlite_omit_load_extension
package sqlite3
/*
#cgo CFLAGS: -DSQLITE_OMIT_LOAD_EXTENSION
*/
import "C"
import (
"errors"
)
func (c *SQLiteConn) loadExtensions(extensions []string) error {
return errors.New("Extensions have been disabled for static builds")
}
func (c *SQLiteConn) LoadExtension(lib string, entry string) error {
return errors.New("Extensions have been disabled for static builds")
}

View File

@ -9,6 +9,5 @@ package sqlite3
/*
#cgo CFLAGS: -I.
#cgo linux LDFLAGS: -ldl
#cgo LDFLAGS: -lpthread
*/
import "C"

View File

@ -1,947 +0,0 @@
// 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.
package sqlite3
import (
"crypto/rand"
"database/sql"
"encoding/hex"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/mattn/go-sqlite3/sqlite3_test"
)
func TempFilename() string {
randBytes := make([]byte, 16)
rand.Read(randBytes)
return filepath.Join(os.TempDir(), "foo"+hex.EncodeToString(randBytes)+".db")
}
func TestOpen(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("drop table foo")
_, err = db.Exec("create table foo (id integer)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() {
t.Error("Failed to create ./foo.db")
}
}
func TestClose(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
_, 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")
}
}
func TestInsert(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("drop table foo")
_, err = db.Exec("create table foo (id integer)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
res, err := db.Exec("insert into foo(id) values(123)")
if err != nil {
t.Fatal("Failed to insert record:", err)
}
affected, _ := res.RowsAffected()
if affected != 1 {
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
}
rows, err := db.Query("select id from foo")
if err != nil {
t.Fatal("Failed to select records:", err)
}
defer rows.Close()
rows.Next()
var result int
rows.Scan(&result)
if result != 123 {
t.Errorf("Fetched %q; expected %q", 123, result)
}
}
func TestUpdate(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("drop table foo")
_, err = db.Exec("create table foo (id integer)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
res, err := db.Exec("insert into foo(id) values(123)")
if err != nil {
t.Fatal("Failed to insert record:", err)
}
expected, err := res.LastInsertId()
if err != nil {
t.Fatal("Failed to get LastInsertId:", err)
}
affected, _ := res.RowsAffected()
if err != nil {
t.Fatal("Failed to get RowsAffected:", err)
}
if affected != 1 {
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
}
res, err = db.Exec("update foo set id = 234")
if err != nil {
t.Fatal("Failed to update record:", err)
}
lastId, err := res.LastInsertId()
if err != nil {
t.Fatal("Failed to get LastInsertId:", err)
}
if expected != lastId {
t.Errorf("Expected %q for last Id, but %q:", expected, lastId)
}
affected, _ = res.RowsAffected()
if err != nil {
t.Fatal("Failed to get RowsAffected:", err)
}
if affected != 1 {
t.Fatalf("Expected %d for affected rows, but %d:", 1, affected)
}
rows, err := db.Query("select id from foo")
if err != nil {
t.Fatal("Failed to select records:", err)
}
defer rows.Close()
rows.Next()
var result int
rows.Scan(&result)
if result != 234 {
t.Errorf("Fetched %q; expected %q", 234, result)
}
}
func TestDelete(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("drop table foo")
_, err = db.Exec("create table foo (id integer)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
res, err := db.Exec("insert into foo(id) values(123)")
if err != nil {
t.Fatal("Failed to insert record:", err)
}
expected, err := res.LastInsertId()
if err != nil {
t.Fatal("Failed to get LastInsertId:", err)
}
affected, err := res.RowsAffected()
if err != nil {
t.Fatal("Failed to get RowsAffected:", err)
}
if affected != 1 {
t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
}
res, err = db.Exec("delete from foo where id = 123")
if err != nil {
t.Fatal("Failed to delete record:", err)
}
lastId, err := res.LastInsertId()
if err != nil {
t.Fatal("Failed to get LastInsertId:", err)
}
if expected != lastId {
t.Errorf("Expected %q for last Id, but %q:", expected, lastId)
}
affected, err = res.RowsAffected()
if err != nil {
t.Fatal("Failed to get RowsAffected:", err)
}
if affected != 1 {
t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected)
}
rows, err := db.Query("select id from foo")
if err != nil {
t.Fatal("Failed to select records:", err)
}
defer rows.Close()
if rows.Next() {
t.Error("Fetched row but expected not rows")
}
}
func TestBooleanRoundtrip(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("DROP TABLE foo")
_, err = db.Exec("CREATE TABLE foo(id INTEGER, value BOOL)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(1, ?)", true)
if err != nil {
t.Fatal("Failed to insert true value:", err)
}
_, err = db.Exec("INSERT INTO foo(id, value) VALUES(2, ?)", false)
if err != nil {
t.Fatal("Failed to insert false value:", err)
}
rows, err := db.Query("SELECT id, value FROM foo")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
defer rows.Close()
for rows.Next() {
var id int
var value bool
if err := rows.Scan(&id, &value); err != nil {
t.Error("Unable to scan results:", err)
continue
}
if id == 1 && !value {
t.Error("Value for id 1 should be true, not false")
} else if id == 2 && value {
t.Error("Value for id 2 should be false, not true")
}
}
}
func TestTimestamp(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("DROP TABLE foo")
_, err = db.Exec("CREATE TABLE foo(id INTEGER, ts timeSTAMP, dt DATETIME)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC)
timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC)
timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC)
tests := []struct {
value interface{}
expected time.Time
}{
{"nonsense", time.Time{}},
{"0000-00-00 00:00:00", time.Time{}},
{timestamp1, timestamp1},
{timestamp1.Unix(), timestamp1},
{timestamp1.UnixNano() / int64(time.Millisecond), timestamp1},
{timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1},
{timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1},
{timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1},
{timestamp1.Format("2006-01-02 15:04:05"), timestamp1},
{timestamp1.Format("2006-01-02T15:04:05"), timestamp1},
{timestamp2, timestamp2},
{"2006-01-02 15:04:05.123456789", timestamp2},
{"2006-01-02T15:04:05.123456789", timestamp2},
{"2012-11-04", timestamp3},
{"2012-11-04 00:00", timestamp3},
{"2012-11-04 00:00:00", timestamp3},
{"2012-11-04 00:00:00.000", timestamp3},
{"2012-11-04T00:00", timestamp3},
{"2012-11-04T00:00:00", timestamp3},
{"2012-11-04T00:00:00.000", timestamp3},
}
for i := range tests {
_, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value)
if err != nil {
t.Fatal("Failed to insert timestamp:", err)
}
}
rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
defer rows.Close()
seen := 0
for rows.Next() {
var id int
var ts, dt time.Time
if err := rows.Scan(&id, &ts, &dt); err != nil {
t.Error("Unable to scan results:", err)
continue
}
if id < 0 || id >= len(tests) {
t.Error("Bad row id: ", id)
continue
}
seen++
if !tests[id].expected.Equal(ts) {
t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt)
}
if !tests[id].expected.Equal(dt) {
t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt)
}
}
if seen != len(tests) {
t.Errorf("Expected to see %d rows", len(tests))
}
}
func TestBoolean(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("CREATE TABLE foo(id INTEGER, fbool BOOLEAN)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
bool1 := true
_, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(1, ?)", bool1)
if err != nil {
t.Fatal("Failed to insert boolean:", err)
}
bool2 := false
_, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(2, ?)", bool2)
if err != nil {
t.Fatal("Failed to insert boolean:", err)
}
bool3 := "nonsense"
_, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(3, ?)", bool3)
if err != nil {
t.Fatal("Failed to insert nonsense:", err)
}
rows, err := db.Query("SELECT id, fbool FROM foo where fbool = ?", bool1)
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
counter := 0
var id int
var fbool bool
for rows.Next() {
if err := rows.Scan(&id, &fbool); err != nil {
t.Fatal("Unable to scan results:", err)
}
counter++
}
if counter != 1 {
t.Fatalf("Expected 1 row but %v", counter)
}
if id != 1 && fbool != true {
t.Fatalf("Value for id 1 should be %v, not %v", bool1, fbool)
}
rows, err = db.Query("SELECT id, fbool FROM foo where fbool = ?", bool2)
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
counter = 0
for rows.Next() {
if err := rows.Scan(&id, &fbool); err != nil {
t.Fatal("Unable to scan results:", err)
}
counter++
}
if counter != 1 {
t.Fatalf("Expected 1 row but %v", counter)
}
if id != 2 && fbool != false {
t.Fatalf("Value for id 2 should be %v, not %v", bool2, fbool)
}
// make sure "nonsense" triggered an error
rows, err = db.Query("SELECT id, fbool FROM foo where id=?;", 3)
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
rows.Next()
err = rows.Scan(&id, &fbool)
if err == nil {
t.Error("Expected error from \"nonsense\" bool")
}
}
func TestFloat32(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("CREATE TABLE foo(id INTEGER)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
_, err = db.Exec("INSERT INTO foo(id) VALUES(null)")
if err != nil {
t.Fatal("Failed to insert null:", err)
}
rows, err := db.Query("SELECT id FROM foo")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
if !rows.Next() {
t.Fatal("Unable to query results:", err)
}
var id interface{}
if err := rows.Scan(&id); err != nil {
t.Fatal("Unable to scan results:", err)
}
if id != nil {
t.Error("Expected nil but not")
}
}
func TestNull(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
rows, err := db.Query("SELECT 3.141592")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
if !rows.Next() {
t.Fatal("Unable to query results:", err)
}
var v interface{}
if err := rows.Scan(&v); err != nil {
t.Fatal("Unable to scan results:", err)
}
f, ok := v.(float64)
if !ok {
t.Error("Expected float but not")
}
if f != 3.141592 {
t.Error("Expected 3.141592 but not")
}
}
func TestTransaction(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("CREATE TABLE foo(id INTEGER)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
tx, err := db.Begin()
if err != nil {
t.Fatal("Failed to begin transaction:", err)
}
_, err = tx.Exec("INSERT INTO foo(id) VALUES(1)")
if err != nil {
t.Fatal("Failed to insert null:", err)
}
rows, err := tx.Query("SELECT id from foo")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
err = tx.Rollback()
if err != nil {
t.Fatal("Failed to rollback transaction:", err)
}
if rows.Next() {
t.Fatal("Unable to query results:", err)
}
tx, err = db.Begin()
if err != nil {
t.Fatal("Failed to begin transaction:", err)
}
_, err = tx.Exec("INSERT INTO foo(id) VALUES(1)")
if err != nil {
t.Fatal("Failed to insert null:", err)
}
err = tx.Commit()
if err != nil {
t.Fatal("Failed to commit transaction:", err)
}
rows, err = tx.Query("SELECT id from foo")
if err == nil {
t.Fatal("Expected failure to query")
}
}
func TestWAL(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
if _, err = db.Exec("PRAGMA journal_mode=WAL;"); err != nil {
t.Fatal("Failed to Exec PRAGMA journal_mode:", err)
}
if _, err = db.Exec("PRAGMA locking_mode=EXCLUSIVE;"); err != nil {
t.Fatal("Failed to Exec PRAGMA locking_mode:", err)
}
if _, err = db.Exec("CREATE TABLE test (id SERIAL, user TEXT NOT NULL, name TEXT NOT NULL);"); err != nil {
t.Fatal("Failed to Exec CREATE TABLE:", err)
}
if _, err = db.Exec("INSERT INTO test (user, name) VALUES ('user','name');"); err != nil {
t.Fatal("Failed to Exec INSERT:", err)
}
trans, err := db.Begin()
if err != nil {
t.Fatal("Failed to Begin:", err)
}
s, err := trans.Prepare("INSERT INTO test (user, name) VALUES (?, ?);")
if err != nil {
t.Fatal("Failed to Prepare:", err)
}
var count int
if err = trans.QueryRow("SELECT count(user) FROM test;").Scan(&count); err != nil {
t.Fatal("Failed to QueryRow:", err)
}
if _, err = s.Exec("bbbb", "aaaa"); err != nil {
t.Fatal("Failed to Exec prepared statement:", err)
}
if err = s.Close(); err != nil {
t.Fatal("Failed to Close prepared statement:", err)
}
if err = trans.Commit(); err != nil {
t.Fatal("Failed to Commit:", err)
}
}
func TestTimezoneConversion(t *testing.T) {
zones := []string{"UTC", "US/Central", "US/Pacific", "Local"}
for _, tz := range zones {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename+"?_loc="+url.QueryEscape(tz))
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec("DROP TABLE foo")
_, err = db.Exec("CREATE TABLE foo(id INTEGER, ts TIMESTAMP, dt DATETIME)")
if err != nil {
t.Fatal("Failed to create table:", err)
}
loc, err := time.LoadLocation(tz)
if err != nil {
t.Fatal("Failed to load location:", err)
}
timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC)
timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC)
timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC)
tests := []struct {
value interface{}
expected time.Time
}{
{"nonsense", time.Time{}.In(loc)},
{"0000-00-00 00:00:00", time.Time{}.In(loc)},
{timestamp1, timestamp1.In(loc)},
{timestamp1.Unix(), timestamp1.In(loc)},
{timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1.In(loc)},
{timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1.In(loc)},
{timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1.In(loc)},
{timestamp1.Format("2006-01-02 15:04:05"), timestamp1.In(loc)},
{timestamp1.Format("2006-01-02T15:04:05"), timestamp1.In(loc)},
{timestamp2, timestamp2.In(loc)},
{"2006-01-02 15:04:05.123456789", timestamp2.In(loc)},
{"2006-01-02T15:04:05.123456789", timestamp2.In(loc)},
{"2012-11-04", timestamp3.In(loc)},
{"2012-11-04 00:00", timestamp3.In(loc)},
{"2012-11-04 00:00:00", timestamp3.In(loc)},
{"2012-11-04 00:00:00.000", timestamp3.In(loc)},
{"2012-11-04T00:00", timestamp3.In(loc)},
{"2012-11-04T00:00:00", timestamp3.In(loc)},
{"2012-11-04T00:00:00.000", timestamp3.In(loc)},
}
for i := range tests {
_, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value)
if err != nil {
t.Fatal("Failed to insert timestamp:", err)
}
}
rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC")
if err != nil {
t.Fatal("Unable to query foo table:", err)
}
defer rows.Close()
seen := 0
for rows.Next() {
var id int
var ts, dt time.Time
if err := rows.Scan(&id, &ts, &dt); err != nil {
t.Error("Unable to scan results:", err)
continue
}
if id < 0 || id >= len(tests) {
t.Error("Bad row id: ", id)
continue
}
seen++
if !tests[id].expected.Equal(ts) {
t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, ts)
}
if !tests[id].expected.Equal(dt) {
t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt)
}
if tests[id].expected.Location().String() != ts.Location().String() {
t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), ts.Location().String())
}
if tests[id].expected.Location().String() != dt.Location().String() {
t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), dt.Location().String())
}
}
if seen != len(tests) {
t.Errorf("Expected to see %d rows", len(tests))
}
}
}
func TestSuite(t *testing.T) {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
defer db.Close()
sqlite3_test.RunTests(t, db, sqlite3_test.SQLITE)
}
// TODO: Execer & Queryer currently disabled
// https://github.com/mattn/go-sqlite3/issues/82
func TestExecer(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec(`
create table foo (id integer); -- one comment
insert into foo(id) values(?);
insert into foo(id) values(?);
insert into foo(id) values(?); -- another comment
`, 1, 2, 3)
if err != nil {
t.Error("Failed to call db.Exec:", err)
}
}
func TestQueryer(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec(`
create table foo (id integer);
`)
if err != nil {
t.Error("Failed to call db.Query:", err)
}
rows, err := db.Query(`
insert into foo(id) values(?);
insert into foo(id) values(?);
insert into foo(id) values(?);
select id from foo order by id;
`, 3, 2, 1)
if err != nil {
t.Error("Failed to call db.Query:", err)
}
defer rows.Close()
n := 1
if rows != nil {
for rows.Next() {
var id int
err = rows.Scan(&id)
if err != nil {
t.Error("Failed to db.Query:", err)
}
if id != n {
t.Error("Failed to db.Query: not matched results")
}
}
}
}
func TestStress(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
db.Exec("CREATE TABLE foo (id int);")
db.Exec("INSERT INTO foo VALUES(1);")
db.Exec("INSERT INTO foo VALUES(2);")
db.Close()
for i := 0; i < 10000; i++ {
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
for j := 0; j < 3; j++ {
rows, err := db.Query("select * from foo where id=1;")
if err != nil {
t.Error("Failed to call db.Query:", err)
}
for rows.Next() {
var i int
if err := rows.Scan(&i); err != nil {
t.Errorf("Scan failed: %v\n", err)
}
}
if err := rows.Err(); err != nil {
t.Errorf("Post-scan failed: %v\n", err)
}
rows.Close()
}
db.Close()
}
}
func TestDateTimeLocal(t *testing.T) {
zone := "Asia/Tokyo"
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename+"?_loc="+zone)
if err != nil {
t.Fatal("Failed to open database:", err)
}
db.Exec("CREATE TABLE foo (dt datetime);")
db.Exec("INSERT INTO foo VALUES('2015-03-05 15:16:17');")
row := db.QueryRow("select * from foo")
var d time.Time
err = row.Scan(&d)
if err != nil {
t.Fatal("Failed to scan datetime:", err)
}
if d.Hour() == 15 || !strings.Contains(d.String(), "JST") {
t.Fatal("Result should have timezone", d)
}
db.Close()
db, err = sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
row = db.QueryRow("select * from foo")
err = row.Scan(&d)
if err != nil {
t.Fatal("Failed to scan datetime:", err)
}
if d.UTC().Hour() != 15 || !strings.Contains(d.String(), "UTC") {
t.Fatalf("Result should not have timezone %v %v", zone, d.String())
}
_, err = db.Exec("DELETE FROM foo")
if err != nil {
t.Fatal("Failed to delete table:", err)
}
dt, err := time.Parse("2006/1/2 15/4/5 -0700 MST", "2015/3/5 15/16/17 +0900 JST")
if err != nil {
t.Fatal("Failed to parse datetime:", err)
}
db.Exec("INSERT INTO foo VALUES(?);", dt)
db.Close()
db, err = sql.Open("sqlite3", tempFilename+"?_loc="+zone)
if err != nil {
t.Fatal("Failed to open database:", err)
}
row = db.QueryRow("select * from foo")
err = row.Scan(&d)
if err != nil {
t.Fatal("Failed to scan datetime:", err)
}
if d.Hour() != 15 || !strings.Contains(d.String(), "JST") {
t.Fatalf("Result should have timezone %v %v", zone, d.String())
}
}
func TestVersion(t *testing.T) {
s, n, id := Version()
if s == "" || n == 0 || id == "" {
t.Errorf("Version failed %q, %d, %q\n", s, n, id)
}
}
func TestNumberNamedParams(t *testing.T) {
tempFilename := TempFilename()
db, err := sql.Open("sqlite3", tempFilename)
if err != nil {
t.Fatal("Failed to open database:", err)
}
defer os.Remove(tempFilename)
defer db.Close()
_, err = db.Exec(`
create table foo (id integer, name text, extra text);
`)
if err != nil {
t.Error("Failed to call db.Query:", err)
}
_, err = db.Exec(`insert into foo(id, name, extra) values($1, $2, $2)`, 1, "foo")
if err != nil {
t.Error("Failed to call db.Exec:", err)
}
row := db.QueryRow(`select id, extra from foo where id = $1 and extra = $2`, 1, "foo")
if row == nil {
t.Error("Failed to call db.QueryRow")
}
var id int
var extra string
err = row.Scan(&id, &extra)
if err != nil {
t.Error("Failed to db.Scan:", err)
}
if id != 1 || extra != "foo" {
t.Error("Failed to db.QueryRow: not matched results")
}
}

View File

@ -275,12 +275,11 @@ func TestPreparedStmt(t *testing.T) {
}
const nRuns = 10
ch := make(chan bool)
var wg sync.WaitGroup
for i := 0; i < nRuns; i++ {
wg.Add(1)
go func() {
defer func() {
ch <- true
}()
defer wg.Done()
for j := 0; j < 10; j++ {
count := 0
if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
@ -294,9 +293,7 @@ func TestPreparedStmt(t *testing.T) {
}
}()
}
for i := 0; i < nRuns; i++ {
<-ch
}
wg.Wait()
}
// Benchmarks need to use panic() since b.Error errors are lost when
@ -318,7 +315,7 @@ func BenchmarkQuery(b *testing.B) {
var i int
var f float64
var s string
// var t time.Time
// var t time.Time
if err := db.QueryRow("select null, 1, 1.1, 'foo'").Scan(&n, &i, &f, &s); err != nil {
panic(err)
}
@ -331,7 +328,7 @@ func BenchmarkParams(b *testing.B) {
var i int
var f float64
var s string
// var t time.Time
// var t time.Time
if err := db.QueryRow("select ?, ?, ?, ?", nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil {
panic(err)
}
@ -350,7 +347,7 @@ func BenchmarkStmt(b *testing.B) {
var i int
var f float64
var s string
// var t time.Time
// var t time.Time
if err := st.QueryRow(nil, 1, 1.1, "foo").Scan(&n, &i, &f, &s); err != nil {
panic(err)
}

View File

@ -8,7 +8,7 @@ package sqlite3
/*
#cgo CFLAGS: -I. -fno-stack-check -fno-stack-protector -mno-stack-arg-probe
#cgo windows,386 CFLAGS: -D_localtime32=localtime
#cgo windows,386 CFLAGS: -D_USE_32BIT_TIME_T
#cgo LDFLAGS: -lmingwex -lmingw32
*/
import "C"

View File

@ -28,7 +28,7 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
** versions of SQLite will not be able to load each others' shared
** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
@ -250,11 +250,40 @@ struct sqlite3_api_routines {
const char *(*uri_parameter)(const char*,const char*);
char *(*vsnprintf)(int,char*,const char*,va_list);
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
/* Version 3.8.7 and later */
int (*auto_extension)(void(*)(void));
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
void(*)(void*));
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
void(*)(void*),unsigned char);
int (*cancel_auto_extension)(void(*)(void));
int (*load_extension)(sqlite3*,const char*,const char*,char**);
void *(*malloc64)(sqlite3_uint64);
sqlite3_uint64 (*msize)(void*);
void *(*realloc64)(void*,sqlite3_uint64);
void (*reset_auto_extension)(void);
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
void(*)(void*));
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
void(*)(void*), unsigned char);
int (*strglob)(const char*,const char*);
/* Version 3.8.11 and later */
sqlite3_value *(*value_dup)(const sqlite3_value*);
void (*value_free)(sqlite3_value*);
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
/* Version 3.9.0 and later */
unsigned int (*value_subtype)(sqlite3_value*);
void (*result_subtype)(sqlite3_context*,unsigned int);
/* Version 3.10.0 and later */
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
int (*strlike)(const char*,const char*,unsigned int);
int (*db_cacheflush)(sqlite3*);
};
/*
** The following macros redefine the API routines so that they are
** redirected throught the global sqlite3_api structure.
** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
@ -263,7 +292,7 @@ struct sqlite3_api_routines {
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
#ifndef SQLITE_CORE
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
@ -390,6 +419,7 @@ struct sqlite3_api_routines {
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
@ -467,9 +497,34 @@ struct sqlite3_api_routines {
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
#endif /* SQLITE_CORE */
/* Version 3.8.7 and later */
#define sqlite3_auto_extension sqlite3_api->auto_extension
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
#define sqlite3_bind_text64 sqlite3_api->bind_text64
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
#define sqlite3_load_extension sqlite3_api->load_extension
#define sqlite3_malloc64 sqlite3_api->malloc64
#define sqlite3_msize sqlite3_api->msize
#define sqlite3_realloc64 sqlite3_api->realloc64
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
#define sqlite3_result_blob64 sqlite3_api->result_blob64
#define sqlite3_result_text64 sqlite3_api->result_text64
#define sqlite3_strglob sqlite3_api->strglob
/* Version 3.8.11 and later */
#define sqlite3_value_dup sqlite3_api->value_dup
#define sqlite3_value_free sqlite3_api->value_free
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
/* Version 3.9.0 and later */
#define sqlite3_value_subtype sqlite3_api->value_subtype
#define sqlite3_result_subtype sqlite3_api->result_subtype
/* Version 3.10.0 and later */
#define sqlite3_status64 sqlite3_api->status64
#define sqlite3_strlike sqlite3_api->strlike
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#ifndef SQLITE_CORE
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
/* This case when the file really is being compiled as a loadable
** extension */
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;