Release the {cgo,VM}-free Linux/Intel version.

modified:   Makefile
	new file:   all_linux_test.go
	renamed:    all_test.go -> all_test_windows.go
	new file:   doc.go
	new file:   generate_linux.go
	new file:   generate_windows.go
	modified:   generator.go
	new file:   generator_windows.go
	modified:   internal/bin/bin_linux_386.go
	modified:   internal/bin/bin_linux_amd64.go
	modified:   main.c
	modified:   sqlite.go
	modified:   sqlite_go18.go
	new file:   sqlite_windows.go
This commit is contained in:
Jan Mercl
2017-06-05 20:13:10 +02:00
parent 1a3b0a731a
commit 8c183b526b
14 changed files with 343809 additions and 686 deletions

View File

@@ -29,13 +29,13 @@ cpu: clean
go tool pprof -lines *.test cpu.out
edit:
@ 1>/dev/null 2>/dev/null gvim -p Makefile main.c all_test.go sqlite.go
@ 1>/dev/null 2>/dev/null gvim -p Makefile main.c *.go
editor:
gofmt -l -s -w *.go
indent -linux *.c
go test -i
go test 2>&1 | tee log
go build
internalError:
egrep -ho '"internal error.*"' *.go | sort | cat -n

320
all_linux_test.go Normal file
View File

@@ -0,0 +1,320 @@
// Copyright 2017 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite
import (
"bytes"
"database/sql"
"flag"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"testing"
"time"
)
func caller(s string, va ...interface{}) {
if s == "" {
s = strings.Repeat("%v ", len(va))
}
_, fn, fl, _ := runtime.Caller(2)
fmt.Fprintf(os.Stderr, "# caller: %s:%d: ", path.Base(fn), fl)
fmt.Fprintf(os.Stderr, s, va...)
fmt.Fprintln(os.Stderr)
_, fn, fl, _ = runtime.Caller(1)
fmt.Fprintf(os.Stderr, "# \tcallee: %s:%d: ", path.Base(fn), fl)
fmt.Fprintln(os.Stderr)
os.Stderr.Sync()
}
func dbg(s string, va ...interface{}) {
if s == "" {
s = strings.Repeat("%v ", len(va))
}
_, fn, fl, _ := runtime.Caller(1)
fmt.Fprintf(os.Stderr, "# dbg %s:%d: ", path.Base(fn), fl)
fmt.Fprintf(os.Stderr, s, va...)
fmt.Fprintln(os.Stderr)
os.Stderr.Sync()
}
func TODO(...interface{}) string { //TODOOK
_, fn, fl, _ := runtime.Caller(1)
return fmt.Sprintf("# TODO: %s:%d:\n", path.Base(fn), fl) //TODOOK
}
func use(...interface{}) {}
func init() {
use(caller, dbg, TODO) //TODOOK
}
// ============================================================================
var (
// Add "-tags virtual.profile" to the command.
profileAll = flag.Bool("profile", false, "")
profileFunctions = flag.Bool("profile_functions", false, "")
profileInstructions = flag.Bool("profile_instructions", false, "")
profileLines = flag.Bool("profile_lines", false, "")
profileRate = flag.Int("profile_rate", 1000, "")
recsPerSec = flag.Bool("recs_per_sec_as_mbps", false, "Show records per second as MB/s.")
)
func tempDB(t testing.TB) (string, *sql.DB) {
dir, err := ioutil.TempDir("", "sqlite-test-")
if err != nil {
t.Fatal(err)
}
db, err := sql.Open(driverName, filepath.Join(dir, "tmp.db"))
if err != nil {
os.RemoveAll(dir)
t.Fatal(err)
}
return dir, db
}
func TestScalar(t *testing.T) {
dir, db := tempDB(t)
defer func() {
db.Close()
os.RemoveAll(dir)
}()
t1 := time.Date(2017, 4, 20, 1, 2, 3, 56789, time.UTC)
t2 := time.Date(2018, 5, 21, 2, 3, 4, 98765, time.UTC)
r, err := db.Exec(`
create table t(i int, f double, b bool, s text, t time);
insert into t values(12, 3.14, ?, "foo", ?), (34, 2.78, ?, "bar", ?);
`,
true, t1,
false, t2,
)
if err != nil {
t.Fatal(err)
}
n, err := r.RowsAffected()
if err != nil {
t.Fatal(err)
}
if g, e := n, int64(2); g != e {
t.Fatal(g, e)
}
rows, err := db.Query("select * from t")
if err != nil {
t.Fatal(err)
}
type rec struct {
i int
f float64
b bool
s string
t string
}
var a []rec
for rows.Next() {
var r rec
if err := rows.Scan(&r.i, &r.f, &r.b, &r.s, &r.t); err != nil {
t.Fatal(err)
}
a = append(a, r)
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
if g, e := len(a), 2; g != e {
t.Fatal(g, e)
}
if g, e := a[0], (rec{12, 3.14, true, "foo", t1.String()}); g != e {
t.Fatal(g, e)
}
if g, e := a[1], (rec{34, 2.78, false, "bar", t2.String()}); g != e {
t.Fatal(g, e)
}
}
func TestBlob(t *testing.T) {
dir, db := tempDB(t)
defer func() {
db.Close()
os.RemoveAll(dir)
}()
b1 := []byte(time.Now().String())
b2 := []byte("\x00foo\x00bar\x00")
if _, err := db.Exec(`
create table t(b blob);
insert into t values(?), (?);
`, b1, b2,
); err != nil {
t.Fatal(err)
}
rows, err := db.Query("select * from t")
if err != nil {
t.Fatal(err)
}
type rec struct {
b []byte
}
var a []rec
for rows.Next() {
var r rec
if err := rows.Scan(&r.b); err != nil {
t.Fatal(err)
}
a = append(a, r)
}
if err := rows.Err(); err != nil {
t.Fatal(err)
}
if g, e := len(a), 2; g != e {
t.Fatal(g, e)
}
if g, e := a[0].b, b1; !bytes.Equal(g, e) {
t.Fatal(g, e)
}
if g, e := a[1].b, b2; !bytes.Equal(g, e) {
t.Fatal(g, e)
}
}
func BenchmarkInsertMemory(b *testing.B) {
db, err := sql.Open(driverName, "file::memory:")
if err != nil {
b.Fatal(err)
}
defer func() {
db.Close()
}()
if _, err := db.Exec(`
create table t(i int);
begin;
`); err != nil {
b.Fatal(err)
}
s, err := db.Prepare("insert into t values(?)")
if err != nil {
b.Fatal(err)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
if _, err := s.Exec(int64(i)); err != nil {
b.Fatal(err)
}
}
b.StopTimer()
if *recsPerSec {
b.SetBytes(1e6)
}
if _, err := db.Exec(`commit;`); err != nil {
b.Fatal(err)
}
}
func BenchmarkNextMemory(b *testing.B) {
db, err := sql.Open(driverName, "file::memory:")
if err != nil {
b.Fatal(err)
}
defer func() {
db.Close()
}()
if _, err := db.Exec(`
create table t(i int);
begin;
`); err != nil {
b.Fatal(err)
}
s, err := db.Prepare("insert into t values(?)")
if err != nil {
b.Fatal(err)
}
defer s.Close()
for i := 0; i < b.N; i++ {
if _, err := s.Exec(int64(i)); err != nil {
b.Fatal(err)
}
}
if _, err := db.Exec("commit"); err != nil {
b.Fatal(err)
}
r, err := db.Query("select * from t")
if err != nil {
}
defer r.Close()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !r.Next() {
b.Fatal(err)
}
}
b.StopTimer()
if *recsPerSec {
b.SetBytes(1e6)
}
}
// https://github.com/cznic/sqlite/issues/11
func TestIssue11(t *testing.T) {
const N = 6570
dir, db := tempDB(t)
defer func() {
db.Close()
os.RemoveAll(dir)
}()
if _, err := db.Exec(`
CREATE TABLE t1 (t INT);
BEGIN;
`,
); err != nil {
panic(err)
}
for i := 0; i < N; i++ {
if _, err := db.Exec("INSERT INTO t1 (t) VALUES (?)", i); err != nil {
t.Fatalf("#%v: %v", i, err)
}
}
if _, err := db.Exec("COMMIT;"); err != nil {
t.Fatal(err)
}
}

51
doc.go Normal file
View File

@@ -0,0 +1,51 @@
// Copyright 2017 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sqlite is an in-process implementation of a self-contained,
// serverless, zero-configuration, transactional SQL database engine. (Work In Progress)
//
// Changelog
//
// 2017-06-05 Linux/Intel no more uses the VM (cznic/virtual).
//
// Connecting to a database
//
// To access a Sqlite database do something like
//
// import (
// "database/sql"
//
// _ "github.com/cznic/sqlite"
// )
//
// ...
//
//
// db, err := sql.Open("sqlite", dsnURI)
//
// ...
//
//
// Do not use in production
//
// This is an experimental, pre-alpha, technology preview package. Performance
// is not (yet) a priority. When this virtual machine approach, hopefully,
// reaches a reasonable level of completeness and correctness, the plan is to
// eventually mechanically translate the IR form, produced by
// http://github.com/cznic/ccir, to Go. Unreadable Go, presumably.
//
// Even though the translation to Go is now done for Linux/Intel, the package
// status is still as described above, it's just closer to the alpha release in
// this respect. The alpha release is due when the C runtime support of SQLite
// in cznic/crt will be complete.
//
// Supported platforms and architectures
//
// See http://github.com/cznic/ccir. To add a newly supported os/arch
// combination to this package try running 'go generate'.
//
// Sqlite documentation
//
// See https://sqlite.org/docs.html
package sqlite

7
generate_linux.go Normal file
View File

@@ -0,0 +1,7 @@
// Copyright 2017 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run generator.go
package sqlite

7
generate_windows.go Normal file
View File

@@ -0,0 +1,7 @@
// Copyright 2017 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run generator_windows.go
package sqlite

View File

@@ -8,13 +8,10 @@ package main
import (
"bytes"
"compress/gzip"
"encoding/gob"
"flag"
"fmt"
"go/format"
"go/scanner"
"go/token"
"io"
"io/ioutil"
"os"
@@ -26,11 +23,10 @@ import (
"log"
"github.com/cznic/cc"
"github.com/cznic/ccgo"
"github.com/cznic/ccir"
"github.com/cznic/internal/buffer"
"github.com/cznic/ir"
"github.com/cznic/strutil"
"github.com/cznic/virtual"
"github.com/cznic/xc"
)
@@ -46,6 +42,55 @@ var (
yydebug = flag.Int("yydebug", 0, "")
)
const (
prologue = `/*
%s
*/
// Code generated by ccgo DO NOT EDIT.
package bin
import (
"fmt"
"math"
"os"
"path"
"runtime"
"unsafe"
"github.com/cznic/crt"
"github.com/edsrzf/mmap-go"
)
const minAlloc = 2<<5
var (
inf = math.Inf(1)
)
func ftrace(s string, args ...interface{}) {
_, fn, fl, _ := runtime.Caller(1)
fmt.Fprintf(os.Stderr, "# %%s:%%d: %%v\n", path.Base(fn), fl, fmt.Sprintf(s, args...))
os.Stderr.Sync()
}
func Init(heapSize, heapReserve int) int {
heap, err := mmap.MapRegion(nil, heapSize+heapReserve, mmap.RDWR, mmap.ANON, 0)
if err != nil {
panic(err)
}
tls := crt.NewTLS()
crt.RegisterHeap(unsafe.Pointer(&heap[0]), int64(heapSize+heapReserve))
crt.X__register_stdfiles(tls, Xstdin, Xstdout, Xstderr)
return int(Xinit(tls, int32(heapSize)))
}
`
)
func findRepo(s string) string {
s = filepath.FromSlash(s)
for _, v := range strings.Split(strutil.Gopath(), string(os.PathListSeparator)) {
@@ -94,32 +139,13 @@ func errStr(err error) string {
}
}
func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit, *virtual.Binary) {
var b buffer.Bytes
var lpos token.Position
if *cpp {
opts = append(opts, cc.Cpp(func(toks []xc.Token) {
if len(toks) != 0 {
p := toks[0].Position()
if p.Filename != lpos.Filename {
fmt.Fprintf(&b, "# %d %q\n", p.Line, p.Filename)
}
lpos = p
}
for _, v := range toks {
b.WriteString(cc.TokSrc(v))
}
b.WriteByte('\n')
}))
}
func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit, []byte) {
ndbg := ""
if *ndebug {
ndbg = "#define NDEBUG 1"
}
var build [][]ir.Object
var build []*cc.TranslationUnit
tus = append(tus, []string{ccir.CRT0Path})
var asta []*cc.TranslationUnit
for _, src := range tus {
model, err := ccir.NewModel()
if err != nil {
@@ -129,6 +155,7 @@ func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit
ast, err := cc.Parse(
fmt.Sprintf(`
%s
#define _CCGO 1
#define __arch__ %s
#define __os__ %s
#include <builtin.h>
@@ -138,79 +165,31 @@ func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit
model,
append([]cc.Opt{
cc.AllowCompatibleTypedefRedefinitions(),
cc.EnableEmptyStructs(),
cc.EnableImplicitFuncDef(),
cc.EnableNonConstStaticInitExpressions(),
cc.EnableWideBitFieldTypes(),
cc.ErrLimit(*errLimit),
cc.SysIncludePaths([]string{ccir.LibcIncludePath}),
}, opts...)...,
)
if s := b.Bytes(); len(s) != 0 {
log.Printf("\n%s", s)
b.Close()
}
if err != nil {
log.Fatal(errStr(err))
}
asta = append(asta, ast)
objs, err := ccir.New(ast)
if err != nil {
build = append(build, ast)
}
var out buffer.Bytes
if err := ccgo.New(build, &out); err != nil {
log.Fatal(err)
}
if *oLog {
for i, v := range objs {
switch x := v.(type) {
case *ir.DataDefinition:
fmt.Fprintf(&b, "# [%v]: %T %v %v\n", i, x, x.ObjectBase, x.Value)
case *ir.FunctionDefinition:
fmt.Fprintf(&b, "# [%v]: %T %v %v\n", i, x, x.ObjectBase, x.Arguments)
for i, v := range x.Body {
fmt.Fprintf(&b, "%#05x\t%v\n", i, v)
}
default:
log.Fatalf("[%v]: %T %v: %v", i, x, x, err)
}
}
}
for i, v := range objs {
if err := v.Verify(); err != nil {
switch x := v.(type) {
case *ir.FunctionDefinition:
fmt.Fprintf(&b, "# [%v, err]: %T %v %v\n", i, x, x.ObjectBase, x.Arguments)
for i, v := range x.Body {
fmt.Fprintf(&b, "%#05x\t%v\n", i, v)
}
log.Fatalf("# [%v]: Verify (A): %v\n%s", i, err, b.Bytes())
default:
log.Fatalf("[%v]: %T %v: %v", i, x, x, err)
}
}
}
build = append(build, objs)
}
linked, err := ir.LinkMain(build...)
if err != nil {
log.Fatalf("ir.LinkMain: %s\n%s", err, b.Bytes())
}
for _, v := range linked {
if err := v.Verify(); err != nil {
log.Fatal(err)
}
}
bin, err := virtual.LoadMain(linked)
if err != nil {
log.Fatal(err)
}
return asta, bin
return build, out.Bytes()
}
func macros(buf io.Writer, ast *cc.TranslationUnit) {
fmt.Fprintf(buf, `const (
`)
var a []string
for k, v := range ast.Macros {
if v.Value != nil && v.Type.Kind() != cc.Bool {
@@ -269,6 +248,7 @@ func macros(buf io.Writer, ast *cc.TranslationUnit) {
dd := ast.Declarations.Identifiers[dict.SID(v)].Node.(*cc.DirectDeclarator)
fmt.Fprintf(buf, "X%s = %v\n", v, dd.EnumVal)
}
fmt.Fprintf(buf, ")\n")
}
func main() {
@@ -282,10 +262,10 @@ func main() {
return
}
asta, bin := build(
asta, src := build(
`
//#define SQLITE_DEBUG 1
//#define SQLITE_ENABLE_API_ARMOR 1
#define SQLITE_DEBUG 1
#define SQLITE_ENABLE_API_ARMOR 1
#define SQLITE_ENABLE_MEMSYS5 1
#define SQLITE_USE_URI 1
`,
@@ -297,70 +277,24 @@ func main() {
cc.IncludePaths([]string{pth}),
)
var b0 bytes.Buffer
enc := gob.NewEncoder(&b0)
if err := enc.Encode(&bin); err != nil {
log.Fatal(err)
}
var b1 bytes.Buffer
comp := gzip.NewWriter(&b1)
if _, err := comp.Write(b0.Bytes()); err != nil {
log.Fatal(err)
}
if err := comp.Close(); err != nil {
log.Fatal(err)
}
var b2 buffer.Bytes
var b bytes.Buffer
lic, err := ioutil.ReadFile("SQLITE-LICENSE")
if err != nil {
log.Fatal(err)
}
b2.WriteString(`// Code generated by running "go generate". DO NOT EDIT.
/*
`)
b2.Write(lic)
b2.WriteString(`
*/
package bin
const (
`)
macros(&b2, asta[0])
b2.WriteString(`
Data = "`)
b := b1.Bytes()
for _, v := range b {
switch {
case v == '\\':
b2.WriteString(`\\`)
case v == '"':
b2.WriteString(`\"`)
case v < ' ', v >= 0x7f:
fmt.Fprintf(&b2, `\x%02x`, v)
default:
b2.WriteByte(v)
}
}
b2.WriteString(`"
)
`)
b3, err := format.Source(b2.Bytes())
fmt.Fprintf(&b, prologue, lic)
macros(&b, asta[0])
b.Write(src)
b2, err := format.Source(b.Bytes())
if err != nil {
b3 = b
b2 = b.Bytes()
}
os.MkdirAll("internal/bin", 0775)
if err := ioutil.WriteFile(fmt.Sprintf("internal/bin/bin_%s_%s.go", runtime.GOOS, runtime.GOARCH), b3, 0664); err != nil {
if err := os.MkdirAll("internal/bin", 0775); err != nil {
log.Fatal(err)
}
log.Printf("code %#08x, text %#08x, data %#08x, bss %#08x, pc2func %v, pc2line %v, symbols %v, gz %v\n",
len(bin.Code), len(bin.Text), len(bin.Data), bin.BSS, len(bin.Functions), len(bin.Lines), len(bin.Sym), b1.Len(),
)
if err := ioutil.WriteFile(fmt.Sprintf("internal/bin/bin_%s_%s.go", runtime.GOOS, runtime.GOARCH), b2, 0664); err != nil {
log.Fatal(err)
}
}

366
generator_windows.go Normal file
View File

@@ -0,0 +1,366 @@
// Copyright 2017 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package main
import (
"bytes"
"compress/gzip"
"encoding/gob"
"flag"
"fmt"
"go/format"
"go/scanner"
"go/token"
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"log"
"github.com/cznic/cc"
"github.com/cznic/ccir"
"github.com/cznic/internal/buffer"
"github.com/cznic/ir"
"github.com/cznic/strutil"
"github.com/cznic/virtual"
"github.com/cznic/xc"
)
var (
cpp = flag.Bool("cpp", false, "")
dict = xc.Dict
errLimit = flag.Int("errlimit", 10, "")
filter = flag.String("re", "", "")
ndebug = flag.Bool("ndebug", false, "")
noexec = flag.Bool("noexec", false, "")
oLog = flag.Bool("log", false, "")
trace = flag.Bool("trc", false, "")
yydebug = flag.Int("yydebug", 0, "")
)
func findRepo(s string) string {
s = filepath.FromSlash(s)
for _, v := range strings.Split(strutil.Gopath(), string(os.PathListSeparator)) {
p := filepath.Join(v, "src", s)
fi, err := os.Lstat(p)
if err != nil {
continue
}
if fi.IsDir() {
wd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
if p, err = filepath.Rel(wd, p); err != nil {
log.Fatal(err)
}
return p
}
}
return ""
}
func errStr(err error) string {
switch x := err.(type) {
case scanner.ErrorList:
if len(x) != 1 {
x.RemoveMultiples()
}
var b bytes.Buffer
for i, v := range x {
if i != 0 {
b.WriteByte('\n')
}
b.WriteString(v.Error())
if i == 9 {
fmt.Fprintf(&b, "\n\t... and %v more errors", len(x)-10)
break
}
}
return b.String()
default:
return err.Error()
}
}
func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit, *virtual.Binary) {
var b buffer.Bytes
var lpos token.Position
if *cpp {
opts = append(opts, cc.Cpp(func(toks []xc.Token) {
if len(toks) != 0 {
p := toks[0].Position()
if p.Filename != lpos.Filename {
fmt.Fprintf(&b, "# %d %q\n", p.Line, p.Filename)
}
lpos = p
}
for _, v := range toks {
b.WriteString(cc.TokSrc(v))
}
b.WriteByte('\n')
}))
}
ndbg := ""
if *ndebug {
ndbg = "#define NDEBUG 1"
}
var build [][]ir.Object
tus = append(tus, []string{ccir.CRT0Path})
var asta []*cc.TranslationUnit
for _, src := range tus {
model, err := ccir.NewModel()
if err != nil {
log.Fatal(err)
}
ast, err := cc.Parse(
fmt.Sprintf(`
%s
#define __arch__ %s
#define __os__ %s
#include <builtin.h>
%s
`, ndbg, runtime.GOARCH, runtime.GOOS, predef),
src,
model,
append([]cc.Opt{
cc.AllowCompatibleTypedefRedefinitions(),
cc.EnableImplicitFuncDef(),
cc.EnableNonConstStaticInitExpressions(),
cc.EnableWideBitFieldTypes(),
cc.ErrLimit(*errLimit),
cc.SysIncludePaths([]string{ccir.LibcIncludePath}),
}, opts...)...,
)
if s := b.Bytes(); len(s) != 0 {
log.Printf("\n%s", s)
b.Close()
}
if err != nil {
log.Fatal(errStr(err))
}
asta = append(asta, ast)
objs, err := ccir.New(ast)
if err != nil {
log.Fatal(err)
}
if *oLog {
for i, v := range objs {
switch x := v.(type) {
case *ir.DataDefinition:
fmt.Fprintf(&b, "# [%v]: %T %v %v\n", i, x, x.ObjectBase, x.Value)
case *ir.FunctionDefinition:
fmt.Fprintf(&b, "# [%v]: %T %v %v\n", i, x, x.ObjectBase, x.Arguments)
for i, v := range x.Body {
fmt.Fprintf(&b, "%#05x\t%v\n", i, v)
}
default:
log.Fatalf("[%v]: %T %v: %v", i, x, x, err)
}
}
}
for i, v := range objs {
if err := v.Verify(); err != nil {
switch x := v.(type) {
case *ir.FunctionDefinition:
fmt.Fprintf(&b, "# [%v, err]: %T %v %v\n", i, x, x.ObjectBase, x.Arguments)
for i, v := range x.Body {
fmt.Fprintf(&b, "%#05x\t%v\n", i, v)
}
log.Fatalf("# [%v]: Verify (A): %v\n%s", i, err, b.Bytes())
default:
log.Fatalf("[%v]: %T %v: %v", i, x, x, err)
}
}
}
build = append(build, objs)
}
linked, err := ir.LinkMain(build...)
if err != nil {
log.Fatalf("ir.LinkMain: %s\n%s", err, b.Bytes())
}
for _, v := range linked {
if err := v.Verify(); err != nil {
log.Fatal(err)
}
}
bin, err := virtual.LoadMain(linked)
if err != nil {
log.Fatal(err)
}
return asta, bin
}
func macros(buf io.Writer, ast *cc.TranslationUnit) {
var a []string
for k, v := range ast.Macros {
if v.Value != nil && v.Type.Kind() != cc.Bool {
switch fn := v.DefTok.Position().Filename; {
case
fn == "builtin.h",
fn == "<predefine>",
strings.HasPrefix(fn, "predefined_"):
// ignore
default:
a = append(a, string(dict.S(k)))
}
}
}
sort.Strings(a)
for _, v := range a {
m := ast.Macros[dict.SID(v)]
if m.Value == nil {
log.Fatal("TODO")
}
switch t := m.Type; t.Kind() {
case
cc.Int, cc.UInt, cc.Long, cc.ULong, cc.LongLong, cc.ULongLong,
cc.Float, cc.LongDouble, cc.Bool:
fmt.Fprintf(buf, "X%s = %v\n", v, m.Value)
case cc.Ptr:
switch t := t.Element(); t.Kind() {
case cc.Char:
fmt.Fprintf(buf, "X%s = %q\n", v, dict.S(int(m.Value.(cc.StringLitID))))
default:
log.Fatalf("%v", t.Kind())
}
default:
log.Fatalf("%v", t.Kind())
}
}
a = a[:0]
for _, v := range ast.Declarations.Identifiers {
switch x := v.Node.(type) {
case *cc.DirectDeclarator:
d := x.TopDeclarator()
id, _ := d.Identifier()
if x.EnumVal == nil {
break
}
a = append(a, string(dict.S(id)))
default:
log.Fatalf("%T", x)
}
}
sort.Strings(a)
for _, v := range a {
dd := ast.Declarations.Identifiers[dict.SID(v)].Node.(*cc.DirectDeclarator)
fmt.Fprintf(buf, "X%s = %v\n", v, dd.EnumVal)
}
}
func main() {
const repo = "sqlite.org/sqlite-amalgamation-3180000/"
log.SetFlags(log.Lshortfile | log.Lmicroseconds)
flag.Parse()
pth := findRepo(repo)
if pth == "" {
log.Fatalf("repository not found: %v", repo)
return
}
asta, bin := build(
`
//#define SQLITE_DEBUG 1
//#define SQLITE_ENABLE_API_ARMOR 1
#define SQLITE_ENABLE_MEMSYS5 1
#define SQLITE_USE_URI 1
`,
[][]string{
{"main.c"},
{filepath.Join(pth, "sqlite3.c")},
},
cc.EnableAnonymousStructFields(),
cc.IncludePaths([]string{pth}),
)
var b0 bytes.Buffer
enc := gob.NewEncoder(&b0)
if err := enc.Encode(&bin); err != nil {
log.Fatal(err)
}
var b1 bytes.Buffer
comp := gzip.NewWriter(&b1)
if _, err := comp.Write(b0.Bytes()); err != nil {
log.Fatal(err)
}
if err := comp.Close(); err != nil {
log.Fatal(err)
}
var b2 buffer.Bytes
lic, err := ioutil.ReadFile("SQLITE-LICENSE")
if err != nil {
log.Fatal(err)
}
b2.WriteString(`// Code generated by running "go generate". DO NOT EDIT.
/*
`)
b2.Write(lic)
b2.WriteString(`
*/
package bin
const (
`)
macros(&b2, asta[0])
b2.WriteString(`
Data = "`)
b := b1.Bytes()
for _, v := range b {
switch {
case v == '\\':
b2.WriteString(`\\`)
case v == '"':
b2.WriteString(`\"`)
case v < ' ', v >= 0x7f:
fmt.Fprintf(&b2, `\x%02x`, v)
default:
b2.WriteByte(v)
}
}
b2.WriteString(`"
)
`)
b3, err := format.Source(b2.Bytes())
if err != nil {
b3 = b
}
os.MkdirAll("internal/bin", 0775)
if err := ioutil.WriteFile(fmt.Sprintf("internal/bin/bin_%s_%s.go", runtime.GOOS, runtime.GOARCH), b3, 0664); err != nil {
log.Fatal(err)
}
log.Printf("code %#08x, text %#08x, data %#08x, bss %#08x, pc2func %v, pc2line %v, symbols %v, gz %v\n",
len(bin.Code), len(bin.Text), len(bin.Data), bin.BSS, len(bin.Functions), len(bin.Lines), len(bin.Sym), b1.Len(),
)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

44
main.c
View File

@@ -1,30 +1,9 @@
// +build ignore
// Copyright 2017 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// SQLite Is Public Domain
//
// All of the code and documentation in SQLite has been dedicated to the public
// domain by the authors. All code authors, and representatives of the
// companies they work for, have signed affidavits dedicating their
// contributions to the public domain and originals of those signed affidavits
// are stored in a firesafe at the main offices of Hwaci. Anyone is free to
// copy, modify, publish, use, compile, sell, or distribute the original SQLite
// code, either in source code form or as a compiled binary, for any purpose,
// commercial or non-commercial, and by any means.
//
// The previous paragraph applies to the deliverable code and documentation in
// SQLite - those parts of the SQLite library that you actually bundle and ship
// with a larger application. Some scripts used as part of the build process
// (for example the "configure" scripts generated by autoconf) might fall under
// other open-source licenses. Nothing from these build scripts ever reaches
// the final deliverable SQLite library, however, and so the licenses
// associated with those scripts should not be a factor in assessing your
// rights to copy and use the SQLite library.
//
// All of the deliverable code in SQLite has been written from scratch. No code
// has been taken from other projects or from the open internet. Every line of
// code can be traced back to its original author, and all of those authors
// have public domain dedications on file. So the SQLite code base is clean and
// is uncontaminated with licensed code from other projects.
// +build ignore
#define minAlloc (2<<5)
@@ -33,16 +12,11 @@
int main(int argc, char **argv)
{
if (argc != 2) {
return 1;
}
int heapSize = 0;
char *p = argv[1];
for (; *p; p++) {
heapSize = 10 * heapSize + *p - '0';
}
init(-1);
}
int init(int heapSize)
{
void *heap = malloc(heapSize);
if (heap == 0) {
return 1;

627
sqlite.go
View File

@@ -2,67 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:generate go run generator.go
// Package sqlite is an in-process implementation of a self-contained,
// serverless, zero-configuration, transactional SQL database engine. (Work In Progress)
//
// Connecting to a database
//
// To access a Sqlite database do something like
//
// import (
// "database/sql"
//
// _ "github.com/cznic/sqlite"
// )
//
// ...
//
//
// db, err := sql.Open("sqlite", dsnURI)
//
// ...
//
//
// Do not use in production
//
// This is an experimental, pre-alpha, technology preview package. Performance
// is not (yet) a priority. When this virtual machine approach, hopefully,
// reaches a reasonable level of completeness and correctness, the plan is to
// eventually mechanically translate the IR form, produced by
// http://github.com/cznic/ccir, to Go. Unreadable Go, presumably.
//
// Supported platforms and architectures
//
// See http://github.com/cznic/ccir. To add a newly supported os/arch
// combination to this package try running 'go generate'.
//
// Sqlite documentation
//
// See https://sqlite.org/docs.html
package sqlite
import (
"bytes"
"compress/gzip"
"database/sql"
"database/sql/driver"
"encoding/gob"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"runtime"
"sync"
"time"
"unsafe"
"github.com/cznic/crt"
"github.com/cznic/internal/buffer"
"github.com/cznic/ir"
"github.com/cznic/mathutil"
"github.com/cznic/sqlite/internal/bin"
"github.com/cznic/virtual"
"github.com/cznic/xc"
"golang.org/x/net/context"
)
@@ -76,121 +33,21 @@ var (
_ driver.Rows = (*rows)(nil)
_ driver.Stmt = (*stmt)(nil)
_ driver.Tx = (*tx)(nil)
_ io.Writer = debugWriter{}
)
const (
driverName = "sqlite"
ptrSize = mathutil.UintPtrBits / 8
vmHeapReserve = 1 << 20
vmHeapSize = 32 << 20
vmMainStackSize = 1 << 16
vmStackSize = 1 << 18
heapReserve = 1 << 20
heapSize = 32 << 20
)
var (
binary virtual.Binary
dict = xc.Dict
null = virtual.Ptr(0)
vm *virtual.Machine
// FFI
bindBlob int
bindDouble int
bindInt int
bindInt64 int
bindParameterCount int
bindParameterName int
bindText int
changes int
closeV2 int
columnBlob int
columnBytes int
columnCount int
columnDouble int
columnInt64 int
columnName int
columnText int
columnType int
errmsg int
errstr int
exec int
extendedResultCodes int
finalize int
free int
interrupt int
lastInsertRowID int
maloc int
openV2 int
prepareV2 int
step int
)
func init() {
b0 := bytes.NewBufferString(bin.Data)
decomp, err := gzip.NewReader(b0)
if err != nil {
panic(err)
}
var b1 bytes.Buffer
chunk := make([]byte, 1<<15)
for {
n, err := decomp.Read(chunk)
b1.Write(chunk[:n])
if err != nil {
if err != io.EOF {
panic(err)
}
break
}
}
dec := gob.NewDecoder(&b1)
if err := dec.Decode(&binary); err != nil {
panic(err)
}
for _, v := range []struct {
*int
string
}{
{&bindBlob, "sqlite3_bind_blob"},
{&bindDouble, "sqlite3_bind_double"},
{&bindInt, "sqlite3_bind_int"},
{&bindInt64, "sqlite3_bind_int64"},
{&bindParameterCount, "sqlite3_bind_parameter_count"},
{&bindParameterName, "sqlite3_bind_parameter_name"},
{&bindText, "sqlite3_bind_text"},
{&changes, "sqlite3_changes"},
{&closeV2, "sqlite3_close_v2"},
{&columnBlob, "sqlite3_column_blob"},
{&columnBytes, "sqlite3_column_bytes"},
{&columnCount, "sqlite3_column_count"},
{&columnDouble, "sqlite3_column_double"},
{&columnInt64, "sqlite3_column_int64"},
{&columnName, "sqlite3_column_name"},
{&columnText, "sqlite3_column_text"},
{&columnType, "sqlite3_column_type"},
{&errmsg, "sqlite3_errmsg"},
{&errstr, "sqlite3_errstr"},
{&exec, "sqlite3_exec"},
{&extendedResultCodes, "sqlite3_extended_result_codes"},
{&finalize, "sqlite3_finalize"},
{&free, "sqlite3_free"},
{&interrupt, "sqlite3_interrupt"},
{&lastInsertRowID, "sqlite3_last_insert_rowid"},
{&maloc, "sqlite3_malloc"},
{&openV2, "sqlite3_open_v2"},
{&prepareV2, "sqlite3_prepare_v2"},
{&step, "sqlite3_step"},
} {
var ok bool
if *v.int, ok = binary.Sym[ir.NameID(dict.SID(v.string))]; !ok {
panic(fmt.Errorf("missing symbol: %v", v.string))
}
}
bin.Init(heapSize, heapReserve)
sql.Register(driverName, newDrv())
}
@@ -203,13 +60,6 @@ func tracer(rx interface{}, format string, args ...interface{}) {
b.Close()
}
func readI8(p uintptr) int8 { return *(*int8)(unsafe.Pointer(p)) }
func readPtr(p uintptr) uintptr { return *(*uintptr)(unsafe.Pointer(p)) }
type debugWriter struct{}
func (debugWriter) Write(b []byte) (int, error) { return os.Stderr.Write(b) }
type result struct {
*stmt
lastInsertId int64
@@ -235,23 +85,13 @@ func newResult(s *stmt) (_ *result, err error) {
// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
func (r *result) lastInsertRowID() (v int64, _ error) {
_, err := r.FFI1(
lastInsertRowID,
virtual.Int64Result{&v},
virtual.Ptr(r.pdb()),
)
return v, err
return bin.Xsqlite3_last_insert_rowid(r.tls, r.pdb()), nil
}
// int sqlite3_changes(sqlite3*);
func (r *result) changes() (int, error) {
var v int32
_, err := r.FFI1(
changes,
virtual.Int32Result{&v},
virtual.Ptr(r.pdb()),
)
return int(v), err
v := bin.Xsqlite3_changes(r.tls, r.pdb())
return int(v), nil
}
// LastInsertId returns the database's auto-generated ID after, for example, an
@@ -277,7 +117,7 @@ type rows struct {
*stmt
columns []string
rc0 int
pstmt uintptr
pstmt unsafe.Pointer
doStep bool
}
@@ -285,7 +125,7 @@ func (r *rows) String() string {
return fmt.Sprintf("&%T@%p{stmt: %p, columns: %v, rc0: %v, pstmt: %#x, doStep: %v}", *r, r, r.stmt, r.columns, r.rc0, r.pstmt, r.doStep)
}
func newRows(s *stmt, pstmt uintptr, rc0 int) (*rows, error) {
func newRows(s *stmt, pstmt unsafe.Pointer, rc0 int) (*rows, error) {
r := &rows{
stmt: s,
pstmt: pstmt,
@@ -405,118 +245,68 @@ func (r *rows) Next(dest []driver.Value) (err error) {
// int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
func (r *rows) columnBytes(iCol int) (_ int, err error) {
var v int32
if _, err = r.FFI1(
columnBytes,
virtual.Int32Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
); err != nil {
return 0, err
}
return int(v), err
v := bin.Xsqlite3_column_bytes(r.tls, r.pstmt, int32(iCol))
return int(v), nil
}
// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
func (r *rows) columnBlob(iCol int) (v []byte, err error) {
var p uintptr
if _, err = r.FFI1(
columnBlob,
virtual.PtrResult{&p},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
); err != nil {
return nil, err
}
p := bin.Xsqlite3_column_blob(r.tls, r.pstmt, int32(iCol))
len, err := r.columnBytes(iCol)
if err != nil {
return nil, err
}
return virtual.GoBytesLen(p, len), nil
return crt.GoBytesLen((*int8)(p), len), nil
}
// const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
func (r *rows) columnText(iCol int) (v string, err error) {
var p uintptr
if _, err = r.FFI1(
columnText,
virtual.PtrResult{&p},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
); err != nil {
return "", err
}
p := bin.Xsqlite3_column_text(r.tls, r.pstmt, int32(iCol))
len, err := r.columnBytes(iCol)
if err != nil {
return "", err
}
return virtual.GoStringLen(p, len), nil
return crt.GoStringLen((*int8)(unsafe.Pointer(p)), len), nil
}
// double sqlite3_column_double(sqlite3_stmt*, int iCol);
func (r *rows) columnDouble(iCol int) (v float64, err error) {
_, err = r.FFI1(
columnDouble,
virtual.Float64Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
)
return v, err
v = bin.Xsqlite3_column_double(r.tls, r.pstmt, int32(iCol))
return v, nil
}
// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
func (r *rows) columnInt64(iCol int) (v int64, err error) {
_, err = r.FFI1(
columnInt64,
virtual.Int64Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
)
return v, err
v = bin.Xsqlite3_column_int64(r.tls, r.pstmt, int32(iCol))
return v, nil
}
// int sqlite3_column_type(sqlite3_stmt*, int iCol);
func (r *rows) columnType(iCol int) (_ int, err error) {
var v int32
_, err = r.FFI1(
columnType,
virtual.Int32Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
)
return int(v), err
v := bin.Xsqlite3_column_type(r.tls, r.pstmt, int32(iCol))
return int(v), nil
}
// int sqlite3_column_count(sqlite3_stmt *pStmt);
func (r *rows) columnCount() (_ int, err error) {
var v int32
_, err = r.FFI1(
columnCount,
virtual.Int32Result{&v},
virtual.Ptr(r.pstmt),
)
return int(v), err
v := bin.Xsqlite3_column_count(r.tls, r.pstmt)
return int(v), nil
}
// const char *sqlite3_column_name(sqlite3_stmt*, int N);
func (r *rows) columnName(n int) (string, error) {
var p uintptr
if _, err := r.FFI1(
columnName,
virtual.PtrResult{&p},
virtual.Ptr(r.pstmt), virtual.Int32(n),
); err != nil {
return "", err
}
return virtual.GoString(p), nil
p := bin.Xsqlite3_column_name(r.tls, r.pstmt, int32(n))
return crt.GoString(p), nil
}
type stmt struct {
*conn
allocs []uintptr
psql uintptr
ppstmt uintptr
pzTail uintptr
allocs []unsafe.Pointer
psql *int8
ppstmt *unsafe.Pointer
pzTail **int8
}
func (s *stmt) String() string {
@@ -533,19 +323,19 @@ func newStmt(c *conn, sql string) (*stmt, error) {
s.psql = psql
ppstmt, err := s.malloc(ptrSize)
if err != nil {
s.free(psql)
s.free(unsafe.Pointer(psql))
return nil, err
}
s.ppstmt = ppstmt
s.ppstmt = (*unsafe.Pointer)(ppstmt)
pzTail, err := s.malloc(ptrSize)
if err != nil {
s.free(psql)
s.free(unsafe.Pointer(psql))
s.free(ppstmt)
return nil, err
}
s.pzTail = pzTail
s.pzTail = (**int8)(pzTail)
return s, nil
}
@@ -558,21 +348,21 @@ func (s *stmt) Close() (err error) {
tracer(s, "Close(): %v", err)
}()
}
if s.psql != 0 {
err = s.free(s.psql)
s.psql = 0
if s.psql != nil {
err = s.free(unsafe.Pointer(s.psql))
s.psql = nil
}
if s.ppstmt != 0 {
if err2 := s.free(s.ppstmt); err2 != nil && err == nil {
if s.ppstmt != nil {
if err2 := s.free(unsafe.Pointer(s.ppstmt)); err2 != nil && err == nil {
err = err2
}
s.ppstmt = 0
s.ppstmt = nil
}
if s.pzTail != 0 {
if err2 := s.free(s.pzTail); err2 != nil && err == nil {
if s.pzTail != nil {
if err2 := s.free(unsafe.Pointer(s.pzTail)); err2 != nil && err == nil {
err = err2
}
s.pzTail = 0
s.pzTail = nil
}
for _, v := range s.allocs {
if err2 := s.free(v); err2 != nil && err == nil {
@@ -613,27 +403,27 @@ func (s *stmt) exec(ctx context.Context, args []namedValue) (r driver.Result, er
}(args)
}
var pstmt uintptr
var pstmt unsafe.Pointer
donech := make(chan struct{})
defer close(donech)
go func() {
select {
case <-ctx.Done():
if pstmt != 0 {
if pstmt != nil {
s.interrupt(s.pdb())
}
case <-donech:
}
}()
for psql := s.psql; readI8(psql) != 0; psql = readPtr(s.pzTail) {
for psql := s.psql; *psql != 0; psql = *s.pzTail {
if err := s.prepareV2(psql); err != nil {
return nil, err
}
pstmt = readPtr(s.ppstmt)
if pstmt == 0 {
pstmt = *s.ppstmt
if pstmt == nil {
continue
}
@@ -679,8 +469,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
}(args)
}
var pstmt uintptr
var rowStmt uintptr
var pstmt, rowStmt unsafe.Pointer
var rc0 int
donech := make(chan struct{})
@@ -688,20 +477,20 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
go func() {
select {
case <-ctx.Done():
if pstmt != 0 {
if pstmt != nil {
s.interrupt(s.pdb())
}
case <-donech:
}
}()
for psql := s.psql; readI8(psql) != 0; psql = readPtr(s.pzTail) {
for psql := s.psql; *psql != 0; psql = *s.pzTail {
if err := s.prepareV2(psql); err != nil {
return nil, err
}
pstmt = readPtr(s.ppstmt)
if pstmt == 0 {
pstmt = *s.ppstmt
if pstmt == nil {
continue
}
@@ -724,7 +513,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
switch rc {
case bin.XSQLITE_ROW:
if rowStmt != 0 {
if rowStmt != nil {
if err := s.finalize(pstmt); err != nil {
return nil, err
}
@@ -735,7 +524,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
rowStmt = pstmt
rc0 = rc
case bin.XSQLITE_DONE:
if rowStmt == 0 {
if rowStmt == nil {
rc0 = rc
}
default:
@@ -748,17 +537,8 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
}
// int sqlite3_bind_double(sqlite3_stmt*, int, double);
func (s *stmt) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) {
var rc int32
if _, err = s.FFI1(
bindDouble,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Float64(value),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
func (s *stmt) bindDouble(pstmt unsafe.Pointer, idx1 int, value float64) (err error) {
if rc := bin.Xsqlite3_bind_double(s.tls, pstmt, int32(idx1), value); rc != 0 {
return s.errstr(rc)
}
@@ -766,17 +546,8 @@ func (s *stmt) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) {
}
// int sqlite3_bind_int(sqlite3_stmt*, int, int);
func (s *stmt) bindInt(pstmt uintptr, idx1, value int) (err error) {
var rc int32
if _, err = s.FFI1(
bindInt,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Int32(int32(value)),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
func (s *stmt) bindInt(pstmt unsafe.Pointer, idx1, value int) (err error) {
if rc := bin.Xsqlite3_bind_int(s.tls, pstmt, int32(idx1), int32(value)); rc != bin.XSQLITE_OK {
return s.errstr(rc)
}
@@ -784,17 +555,8 @@ func (s *stmt) bindInt(pstmt uintptr, idx1, value int) (err error) {
}
// int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
func (s *stmt) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) {
var rc int32
if _, err = s.FFI1(
bindInt64,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Int64(value),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
func (s *stmt) bindInt64(pstmt unsafe.Pointer, idx1 int, value int64) (err error) {
if rc := bin.Xsqlite3_bind_int64(s.tls, pstmt, int32(idx1), value); rc != bin.XSQLITE_OK {
return s.errstr(rc)
}
@@ -802,24 +564,15 @@ func (s *stmt) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) {
}
// int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
func (s *stmt) bindBlob(pstmt uintptr, idx1 int, value []byte) (err error) {
func (s *stmt) bindBlob(pstmt unsafe.Pointer, idx1 int, value []byte) (err error) {
p, err := s.malloc(len(value))
if err != nil {
return err
}
s.allocs = append(s.allocs, p)
virtual.CopyBytes(p, value, false)
var rc int32
if _, err = s.FFI1(
bindBlob,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Ptr(p), virtual.Int32(int32(len(value))), null,
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
crt.CopyBytes(p, value, false)
if rc := bin.Xsqlite3_bind_blob(s.tls, pstmt, int32(idx1), p, int32(len(value)), nil); rc != bin.XSQLITE_OK {
return s.errstr(rc)
}
@@ -827,30 +580,21 @@ func (s *stmt) bindBlob(pstmt uintptr, idx1 int, value []byte) (err error) {
}
// int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
func (s *stmt) bindText(pstmt uintptr, idx1 int, value string) (err error) {
func (s *stmt) bindText(pstmt unsafe.Pointer, idx1 int, value string) (err error) {
p, err := s.cString(value)
if err != nil {
return err
}
s.allocs = append(s.allocs, p)
var rc int32
if _, err = s.FFI1(
bindText,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt), virtual.Int32(int32(idx1)), virtual.Ptr(p), virtual.Int32(int32(len(value))), null,
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
s.allocs = append(s.allocs, unsafe.Pointer(p))
if rc := bin.Xsqlite3_bind_text(s.tls, pstmt, int32(idx1), p, int32(len(value)), nil); rc != bin.XSQLITE_OK {
return s.errstr(rc)
}
return nil
}
func (s *stmt) bind(pstmt uintptr, n int, args []namedValue) error {
func (s *stmt) bind(pstmt unsafe.Pointer, n int, args []namedValue) error {
for i := 1; i <= n; i++ {
name, err := s.bindParameterName(pstmt, i)
if err != nil {
@@ -878,9 +622,9 @@ func (s *stmt) bind(pstmt uintptr, n int, args []namedValue) error {
if v.Ordinal == 0 {
if name != "" {
return fmt.Errorf("missing named argument %q", name[1:])
} else {
return fmt.Errorf("missing argument with %d index", i)
}
return fmt.Errorf("missing argument with %d index", i)
}
switch x := v.Value.(type) {
@@ -920,40 +664,20 @@ func (s *stmt) bind(pstmt uintptr, n int, args []namedValue) error {
}
// int sqlite3_bind_parameter_count(sqlite3_stmt*);
func (s *stmt) bindParameterCount(pstmt uintptr) (_ int, err error) {
var r int32
_, err = s.FFI1(
bindParameterCount,
virtual.Int32Result{&r},
virtual.Ptr(pstmt),
)
return int(r), err
func (s *stmt) bindParameterCount(pstmt unsafe.Pointer) (_ int, err error) {
r := bin.Xsqlite3_bind_parameter_count(s.tls, pstmt)
return int(r), nil
}
// const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
func (s *stmt) bindParameterName(pstmt uintptr, i int) (string, error) {
var p uintptr
_, err := s.FFI1(
bindParameterName,
virtual.PtrResult{&p},
virtual.Ptr(pstmt),
virtual.Int32(i),
)
return virtual.GoString(p), err
func (s *stmt) bindParameterName(pstmt unsafe.Pointer, i int) (string, error) {
p := bin.Xsqlite3_bind_parameter_name(s.tls, pstmt, int32(i))
return crt.GoString(p), nil
}
// int sqlite3_finalize(sqlite3_stmt *pStmt);
func (s *stmt) finalize(pstmt uintptr) error {
var rc int32
if _, err := s.FFI1(
finalize,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
func (s *stmt) finalize(pstmt unsafe.Pointer) error {
if rc := bin.Xsqlite3_finalize(s.tls, pstmt); rc != bin.XSQLITE_OK {
return s.errstr(rc)
}
@@ -961,14 +685,9 @@ func (s *stmt) finalize(pstmt uintptr) error {
}
// int sqlite3_step(sqlite3_stmt*);
func (s *stmt) step(pstmt uintptr) (int, error) {
var rc int32
_, err := s.FFI1(
step,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt),
)
return int(rc), err
func (s *stmt) step(pstmt unsafe.Pointer) (int, error) {
r := bin.Xsqlite3_step(s.tls, pstmt)
return int(r), nil
}
// int sqlite3_prepare_v2(
@@ -978,17 +697,8 @@ func (s *stmt) step(pstmt uintptr) (int, error) {
// sqlite3_stmt **ppStmt, /* OUT: Statement handle */
// const char **pzTail /* OUT: Pointer to unused portion of zSql */
// );
func (s *stmt) prepareV2(zSql uintptr) error {
var rc int32
if _, err := s.FFI1(
prepareV2,
virtual.Int32Result{&rc},
virtual.Ptr(s.pdb()), virtual.Ptr(zSql), virtual.Int32(-1), virtual.Ptr(s.ppstmt), virtual.Ptr(s.pzTail),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
func (s *stmt) prepareV2(zSql *int8) error {
if rc := bin.Xsqlite3_prepare_v2(s.tls, s.pdb(), zSql, -1, s.ppstmt, s.pzTail); rc != bin.XSQLITE_OK {
return s.errstr(rc)
}
@@ -1043,7 +753,7 @@ func (t *tx) exec(ctx context.Context, sql string) (err error) {
return err
}
defer t.free(psql)
defer t.free(unsafe.Pointer(psql))
// TODO: use t.conn.ExecContext() instead
donech := make(chan struct{})
@@ -1056,16 +766,7 @@ func (t *tx) exec(ctx context.Context, sql string) (err error) {
}
}()
var rc int32
if _, err = t.FFI1(
exec,
virtual.Int32Result{&rc},
virtual.Ptr(t.pdb()), virtual.Ptr(psql), null, null, null,
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
if rc := bin.Xsqlite3_exec(t.tls, t.pdb(), psql, nil, nil, nil); rc != bin.XSQLITE_OK {
return t.errstr(rc)
}
@@ -1074,12 +775,12 @@ func (t *tx) exec(ctx context.Context, sql string) (err error) {
type conn struct {
*Driver
*virtual.Thread
ppdb uintptr
ppdb **bin.Xsqlite3
tls *crt.TLS
}
func (c *conn) String() string {
return fmt.Sprintf("&%T@%p{sqlite: %p, Thread: %p, ppdb: %#x}", *c, c, c.Driver, c.Thread, c.ppdb)
return fmt.Sprintf("&%T@%p{sqlite: %p, Thread: %p, ppdb: %#x}", *c, c, c.Driver, c.tls, c.ppdb)
}
func newConn(s *Driver, name string) (_ *conn, err error) {
@@ -1095,24 +796,7 @@ func newConn(s *Driver, name string) (_ *conn, err error) {
defer c.Unlock()
c.conns++
if c.conns == 1 {
stderr := ioutil.Discard
if trace {
stderr = debugWriter{}
}
m, status, err2 := virtual.New(&binary, []string{"", fmt.Sprint(vmHeapSize - vmHeapReserve)}, nil, nil, stderr, vmHeapSize, vmMainStackSize, "")
if status != 0 || err2 != nil {
return nil, fmt.Errorf("virtual.New: %v, %v", status, err2)
}
vm = m
}
c.Thread, err = vm.NewThread(vmStackSize)
if err != nil {
return nil, err
}
c.tls = crt.NewTLS()
if err = c.openV2(
name,
bin.XSQLITE_OPEN_READWRITE|bin.XSQLITE_OPEN_CREATE|
@@ -1255,23 +939,15 @@ func (c *conn) query(ctx context.Context, query string, args []namedValue) (r dr
return s.(*stmt).query(ctx, args)
}
func (c *conn) pdb() uintptr { return readPtr(c.ppdb) }
func (c *conn) pdb() *bin.Xsqlite3 { return *c.ppdb }
// int sqlite3_extended_result_codes(sqlite3*, int onoff);
func (c *conn) extendedResultCodes(on bool) (err error) {
var v, rc int32
var v int32
if on {
v = 1
}
if _, err = c.FFI1(
extendedResultCodes,
virtual.Int32Result{&rc},
virtual.Ptr(c.pdb()), virtual.Int32(v),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
if rc := bin.Xsqlite3_extended_result_codes(c.tls, c.pdb(), v); rc != bin.XSQLITE_OK {
return c.errstr(rc)
}
@@ -1279,23 +955,28 @@ func (c *conn) extendedResultCodes(on bool) (err error) {
}
// void *sqlite3_malloc(int);
func (c *conn) malloc(n int) (r uintptr, err error) {
_, err = c.FFI1(
maloc,
virtual.PtrResult{&r},
virtual.Int32(int32(n)),
)
return r, err
}
func (c *conn) cString(s string) (p uintptr, err error) {
n := len(s)
if p, err = c.malloc(n + 1); err != nil {
return 0, err
func (c *conn) malloc(n int) (r unsafe.Pointer, err error) {
if n > math.MaxInt32 {
panic("internal error")
}
virtual.CopyString(p, s, true)
return p, nil
r = bin.Xsqlite3_malloc(c.tls, int32(n))
if r == nil {
return nil, fmt.Errorf("malloc(%v) failed", n)
}
return r, nil
}
func (c *conn) cString(s string) (*int8, error) {
n := len(s)
p, err := c.malloc(n + 1)
if err != nil {
return nil, err
}
crt.CopyString(p, s, true)
return (*int8)(p), nil
}
// int sqlite3_open_v2(
@@ -1310,24 +991,15 @@ func (c *conn) openV2(name string, flags int32) error {
return err
}
defer c.free(filename)
defer c.free(unsafe.Pointer(filename))
ppdb, err := c.malloc(ptrSize)
if err != nil {
return err
}
c.ppdb = ppdb
var rc int32
if _, err = c.FFI1(
openV2,
virtual.Int32Result{&rc},
virtual.Ptr(filename), virtual.Ptr(ppdb), virtual.Int32(flags), null,
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
c.ppdb = (**bin.Xsqlite3)(ppdb)
if rc := bin.Xsqlite3_open_v2(c.tls, filename, c.ppdb, flags, nil); rc != bin.XSQLITE_OK {
return c.errstr(rc)
}
@@ -1336,25 +1008,11 @@ func (c *conn) openV2(name string, flags int32) error {
// const char *sqlite3_errstr(int);
func (c *conn) errstr(rc int32) (err error) {
var p uintptr
if _, err = c.FFI1(
errstr,
virtual.PtrResult{&p},
virtual.Int32(rc),
); err != nil {
return err
}
p := bin.Xsqlite3_errstr(c.tls, rc)
str := crt.GoString(p)
p = bin.Xsqlite3_errmsg(c.tls, c.pdb())
str := virtual.GoString(p)
if _, err = c.FFI1(
errmsg,
virtual.PtrResult{&p},
virtual.Ptr(c.pdb()),
); err != nil {
return err
}
switch msg := virtual.GoString(p); {
switch msg := crt.GoString(p); {
case msg == str:
return fmt.Errorf("%s (%v)", str, rc)
default:
@@ -1364,64 +1022,39 @@ func (c *conn) errstr(rc int32) (err error) {
// int sqlite3_close_v2(sqlite3*);
func (c *conn) closeV2() (err error) {
var rc int32
if _, err = c.FFI1(
closeV2,
virtual.Int32Result{&rc},
virtual.Ptr(c.pdb()),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
if rc := bin.Xsqlite3_close_v2(c.tls, c.pdb()); rc != bin.XSQLITE_OK {
return c.errstr(rc)
}
err = c.free(c.ppdb)
c.ppdb = 0
err = c.free(unsafe.Pointer(c.ppdb))
c.ppdb = nil
return err
}
// void sqlite3_free(void*);
func (c *conn) free(p uintptr) (err error) {
_, err = c.FFI0(
free,
virtual.Ptr(p),
)
return err
func (c *conn) free(p unsafe.Pointer) (err error) {
bin.Xsqlite3_free(c.tls, p)
return nil
}
// void sqlite3_interrupt(sqlite3*);
func (c *conn) interrupt(pdb uintptr) (err error) {
_, err = c.FFI0(
interrupt,
virtual.Ptr(pdb),
)
return err
func (c *conn) interrupt(pdb *bin.Xsqlite3) (err error) {
bin.Xsqlite3_interrupt(c.tls, pdb)
return nil
}
func (c *conn) close() (err error) {
c.Lock()
defer func() {
c.conns--
if c.conns == 0 {
if err2 := vm.Close(); err2 != nil && err == nil {
err = err2
}
vm = nil
}
c.Unlock()
}()
defer c.Unlock()
if c.ppdb != 0 {
if c.ppdb != nil {
err = c.closeV2()
}
return err
}
type Driver struct {
conns int
sync.Mutex
}

View File

@@ -10,6 +10,7 @@ import (
"context"
"database/sql/driver"
"errors"
"unsafe"
)
// Ping implements driver.Pinger
@@ -17,7 +18,7 @@ func (c *conn) Ping(ctx context.Context) error {
c.Lock()
defer c.Unlock()
if c.ppdb == 0 {
if uintptr(unsafe.Pointer(c.ppdb)) == 0 {
return errors.New("db is closed")
}

1406
sqlite_windows.go Normal file

File diff suppressed because it is too large Load Diff