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 go tool pprof -lines *.test cpu.out
edit: 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: editor:
gofmt -l -s -w *.go gofmt -l -s -w *.go
indent -linux *.c indent -linux *.c
go test -i
go test 2>&1 | tee log go test 2>&1 | tee log
go build
internalError: internalError:
egrep -ho '"internal error.*"' *.go | sort | cat -n 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 ( import (
"bytes" "bytes"
"compress/gzip"
"encoding/gob"
"flag" "flag"
"fmt" "fmt"
"go/format" "go/format"
"go/scanner" "go/scanner"
"go/token"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@@ -26,11 +23,10 @@ import (
"log" "log"
"github.com/cznic/cc" "github.com/cznic/cc"
"github.com/cznic/ccgo"
"github.com/cznic/ccir" "github.com/cznic/ccir"
"github.com/cznic/internal/buffer" "github.com/cznic/internal/buffer"
"github.com/cznic/ir"
"github.com/cznic/strutil" "github.com/cznic/strutil"
"github.com/cznic/virtual"
"github.com/cznic/xc" "github.com/cznic/xc"
) )
@@ -46,6 +42,55 @@ var (
yydebug = flag.Int("yydebug", 0, "") 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 { func findRepo(s string) string {
s = filepath.FromSlash(s) s = filepath.FromSlash(s)
for _, v := range strings.Split(strutil.Gopath(), string(os.PathListSeparator)) { 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) { func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit, []byte) {
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 := "" ndbg := ""
if *ndebug { if *ndebug {
ndbg = "#define NDEBUG 1" ndbg = "#define NDEBUG 1"
} }
var build [][]ir.Object var build []*cc.TranslationUnit
tus = append(tus, []string{ccir.CRT0Path}) tus = append(tus, []string{ccir.CRT0Path})
var asta []*cc.TranslationUnit
for _, src := range tus { for _, src := range tus {
model, err := ccir.NewModel() model, err := ccir.NewModel()
if err != nil { if err != nil {
@@ -129,6 +155,7 @@ func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit
ast, err := cc.Parse( ast, err := cc.Parse(
fmt.Sprintf(` fmt.Sprintf(`
%s %s
#define _CCGO 1
#define __arch__ %s #define __arch__ %s
#define __os__ %s #define __os__ %s
#include <builtin.h> #include <builtin.h>
@@ -138,79 +165,31 @@ func build(predef string, tus [][]string, opts ...cc.Opt) ([]*cc.TranslationUnit
model, model,
append([]cc.Opt{ append([]cc.Opt{
cc.AllowCompatibleTypedefRedefinitions(), cc.AllowCompatibleTypedefRedefinitions(),
cc.EnableEmptyStructs(),
cc.EnableImplicitFuncDef(), cc.EnableImplicitFuncDef(),
cc.EnableNonConstStaticInitExpressions(), cc.EnableNonConstStaticInitExpressions(),
cc.EnableWideBitFieldTypes(),
cc.ErrLimit(*errLimit), cc.ErrLimit(*errLimit),
cc.SysIncludePaths([]string{ccir.LibcIncludePath}), cc.SysIncludePaths([]string{ccir.LibcIncludePath}),
}, opts...)..., }, opts...)...,
) )
if s := b.Bytes(); len(s) != 0 {
log.Printf("\n%s", s)
b.Close()
}
if err != nil { if err != nil {
log.Fatal(errStr(err)) log.Fatal(errStr(err))
} }
asta = append(asta, ast) build = append(build, ast)
objs, err := ccir.New(ast) }
if err != nil {
var out buffer.Bytes
if err := ccgo.New(build, &out); err != nil {
log.Fatal(err) log.Fatal(err)
} }
if *oLog { return build, out.Bytes()
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) { func macros(buf io.Writer, ast *cc.TranslationUnit) {
fmt.Fprintf(buf, `const (
`)
var a []string var a []string
for k, v := range ast.Macros { for k, v := range ast.Macros {
if v.Value != nil && v.Type.Kind() != cc.Bool { 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) dd := ast.Declarations.Identifiers[dict.SID(v)].Node.(*cc.DirectDeclarator)
fmt.Fprintf(buf, "X%s = %v\n", v, dd.EnumVal) fmt.Fprintf(buf, "X%s = %v\n", v, dd.EnumVal)
} }
fmt.Fprintf(buf, ")\n")
} }
func main() { func main() {
@@ -282,10 +262,10 @@ func main() {
return return
} }
asta, bin := build( asta, src := build(
` `
//#define SQLITE_DEBUG 1 #define SQLITE_DEBUG 1
//#define SQLITE_ENABLE_API_ARMOR 1 #define SQLITE_ENABLE_API_ARMOR 1
#define SQLITE_ENABLE_MEMSYS5 1 #define SQLITE_ENABLE_MEMSYS5 1
#define SQLITE_USE_URI 1 #define SQLITE_USE_URI 1
`, `,
@@ -297,70 +277,24 @@ func main() {
cc.IncludePaths([]string{pth}), cc.IncludePaths([]string{pth}),
) )
var b0 bytes.Buffer var b 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") lic, err := ioutil.ReadFile("SQLITE-LICENSE")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
b2.WriteString(`// Code generated by running "go generate". DO NOT EDIT. fmt.Fprintf(&b, prologue, lic)
macros(&b, asta[0])
/* b.Write(src)
b2, err := format.Source(b.Bytes())
`)
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 { if err != nil {
b3 = b b2 = b.Bytes()
} }
os.MkdirAll("internal/bin", 0775) if err := os.MkdirAll("internal/bin", 0775); err != nil {
if err := ioutil.WriteFile(fmt.Sprintf("internal/bin/bin_%s_%s.go", runtime.GOOS, runtime.GOARCH), b3, 0664); err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Printf("code %#08x, text %#08x, data %#08x, bss %#08x, pc2func %v, pc2line %v, symbols %v, gz %v\n", if err := ioutil.WriteFile(fmt.Sprintf("internal/bin/bin_%s_%s.go", runtime.GOOS, runtime.GOARCH), b2, 0664); err != nil {
len(bin.Code), len(bin.Text), len(bin.Data), bin.BSS, len(bin.Functions), len(bin.Lines), len(bin.Sym), b1.Len(), 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 // SQLite Is Public Domain
//
// All of the code and documentation in SQLite has been dedicated to the public // +build ignore
// 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.
#define minAlloc (2<<5) #define minAlloc (2<<5)
@@ -33,16 +12,11 @@
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc != 2) { init(-1);
return 1; }
}
int heapSize = 0;
char *p = argv[1];
for (; *p; p++) {
heapSize = 10 * heapSize + *p - '0';
}
int init(int heapSize)
{
void *heap = malloc(heapSize); void *heap = malloc(heapSize);
if (heap == 0) { if (heap == 0) {
return 1; return 1;

627
sqlite.go
View File

@@ -2,67 +2,24 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // 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 package sqlite
import ( import (
"bytes"
"compress/gzip"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/gob"
"fmt" "fmt"
"io" "io"
"io/ioutil" "math"
"os" "os"
"runtime" "runtime"
"sync" "sync"
"time" "time"
"unsafe" "unsafe"
"github.com/cznic/crt"
"github.com/cznic/internal/buffer" "github.com/cznic/internal/buffer"
"github.com/cznic/ir"
"github.com/cznic/mathutil" "github.com/cznic/mathutil"
"github.com/cznic/sqlite/internal/bin" "github.com/cznic/sqlite/internal/bin"
"github.com/cznic/virtual"
"github.com/cznic/xc" "github.com/cznic/xc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@@ -76,121 +33,21 @@ var (
_ driver.Rows = (*rows)(nil) _ driver.Rows = (*rows)(nil)
_ driver.Stmt = (*stmt)(nil) _ driver.Stmt = (*stmt)(nil)
_ driver.Tx = (*tx)(nil) _ driver.Tx = (*tx)(nil)
_ io.Writer = debugWriter{}
) )
const ( const (
driverName = "sqlite" driverName = "sqlite"
ptrSize = mathutil.UintPtrBits / 8 ptrSize = mathutil.UintPtrBits / 8
vmHeapReserve = 1 << 20 heapReserve = 1 << 20
vmHeapSize = 32 << 20 heapSize = 32 << 20
vmMainStackSize = 1 << 16
vmStackSize = 1 << 18
) )
var ( var (
binary virtual.Binary
dict = xc.Dict 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() { func init() {
b0 := bytes.NewBufferString(bin.Data) bin.Init(heapSize, heapReserve)
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))
}
}
sql.Register(driverName, newDrv()) sql.Register(driverName, newDrv())
} }
@@ -203,13 +60,6 @@ func tracer(rx interface{}, format string, args ...interface{}) {
b.Close() 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 { type result struct {
*stmt *stmt
lastInsertId int64 lastInsertId int64
@@ -235,23 +85,13 @@ func newResult(s *stmt) (_ *result, err error) {
// sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); // sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
func (r *result) lastInsertRowID() (v int64, _ error) { func (r *result) lastInsertRowID() (v int64, _ error) {
_, err := r.FFI1( return bin.Xsqlite3_last_insert_rowid(r.tls, r.pdb()), nil
lastInsertRowID,
virtual.Int64Result{&v},
virtual.Ptr(r.pdb()),
)
return v, err
} }
// int sqlite3_changes(sqlite3*); // int sqlite3_changes(sqlite3*);
func (r *result) changes() (int, error) { func (r *result) changes() (int, error) {
var v int32 v := bin.Xsqlite3_changes(r.tls, r.pdb())
_, err := r.FFI1( return int(v), nil
changes,
virtual.Int32Result{&v},
virtual.Ptr(r.pdb()),
)
return int(v), err
} }
// LastInsertId returns the database's auto-generated ID after, for example, an // LastInsertId returns the database's auto-generated ID after, for example, an
@@ -277,7 +117,7 @@ type rows struct {
*stmt *stmt
columns []string columns []string
rc0 int rc0 int
pstmt uintptr pstmt unsafe.Pointer
doStep bool 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) 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{ r := &rows{
stmt: s, stmt: s,
pstmt: pstmt, pstmt: pstmt,
@@ -405,118 +245,68 @@ func (r *rows) Next(dest []driver.Value) (err error) {
// int sqlite3_column_bytes(sqlite3_stmt*, int iCol); // int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
func (r *rows) columnBytes(iCol int) (_ int, err error) { func (r *rows) columnBytes(iCol int) (_ int, err error) {
var v int32 v := bin.Xsqlite3_column_bytes(r.tls, r.pstmt, int32(iCol))
if _, err = r.FFI1( return int(v), nil
columnBytes,
virtual.Int32Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
); err != nil {
return 0, err
}
return int(v), err
} }
// const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); // const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
func (r *rows) columnBlob(iCol int) (v []byte, err error) { func (r *rows) columnBlob(iCol int) (v []byte, err error) {
var p uintptr p := bin.Xsqlite3_column_blob(r.tls, r.pstmt, int32(iCol))
if _, err = r.FFI1(
columnBlob,
virtual.PtrResult{&p},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
); err != nil {
return nil, err
}
len, err := r.columnBytes(iCol) len, err := r.columnBytes(iCol)
if err != nil { if err != nil {
return nil, err 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); // const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
func (r *rows) columnText(iCol int) (v string, err error) { func (r *rows) columnText(iCol int) (v string, err error) {
var p uintptr p := bin.Xsqlite3_column_text(r.tls, r.pstmt, int32(iCol))
if _, err = r.FFI1(
columnText,
virtual.PtrResult{&p},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
); err != nil {
return "", err
}
len, err := r.columnBytes(iCol) len, err := r.columnBytes(iCol)
if err != nil { if err != nil {
return "", err 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); // double sqlite3_column_double(sqlite3_stmt*, int iCol);
func (r *rows) columnDouble(iCol int) (v float64, err error) { func (r *rows) columnDouble(iCol int) (v float64, err error) {
_, err = r.FFI1( v = bin.Xsqlite3_column_double(r.tls, r.pstmt, int32(iCol))
columnDouble, return v, nil
virtual.Float64Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
)
return v, err
} }
// sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); // sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
func (r *rows) columnInt64(iCol int) (v int64, err error) { func (r *rows) columnInt64(iCol int) (v int64, err error) {
_, err = r.FFI1( v = bin.Xsqlite3_column_int64(r.tls, r.pstmt, int32(iCol))
columnInt64, return v, nil
virtual.Int64Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
)
return v, err
} }
// int sqlite3_column_type(sqlite3_stmt*, int iCol); // int sqlite3_column_type(sqlite3_stmt*, int iCol);
func (r *rows) columnType(iCol int) (_ int, err error) { func (r *rows) columnType(iCol int) (_ int, err error) {
var v int32 v := bin.Xsqlite3_column_type(r.tls, r.pstmt, int32(iCol))
_, err = r.FFI1( return int(v), nil
columnType,
virtual.Int32Result{&v},
virtual.Ptr(r.pstmt), virtual.Int32(iCol),
)
return int(v), err
} }
// int sqlite3_column_count(sqlite3_stmt *pStmt); // int sqlite3_column_count(sqlite3_stmt *pStmt);
func (r *rows) columnCount() (_ int, err error) { func (r *rows) columnCount() (_ int, err error) {
var v int32 v := bin.Xsqlite3_column_count(r.tls, r.pstmt)
_, err = r.FFI1( return int(v), nil
columnCount,
virtual.Int32Result{&v},
virtual.Ptr(r.pstmt),
)
return int(v), err
} }
// const char *sqlite3_column_name(sqlite3_stmt*, int N); // const char *sqlite3_column_name(sqlite3_stmt*, int N);
func (r *rows) columnName(n int) (string, error) { func (r *rows) columnName(n int) (string, error) {
var p uintptr p := bin.Xsqlite3_column_name(r.tls, r.pstmt, int32(n))
if _, err := r.FFI1( return crt.GoString(p), nil
columnName,
virtual.PtrResult{&p},
virtual.Ptr(r.pstmt), virtual.Int32(n),
); err != nil {
return "", err
}
return virtual.GoString(p), nil
} }
type stmt struct { type stmt struct {
*conn *conn
allocs []uintptr allocs []unsafe.Pointer
psql uintptr psql *int8
ppstmt uintptr ppstmt *unsafe.Pointer
pzTail uintptr pzTail **int8
} }
func (s *stmt) String() string { func (s *stmt) String() string {
@@ -533,19 +323,19 @@ func newStmt(c *conn, sql string) (*stmt, error) {
s.psql = psql s.psql = psql
ppstmt, err := s.malloc(ptrSize) ppstmt, err := s.malloc(ptrSize)
if err != nil { if err != nil {
s.free(psql) s.free(unsafe.Pointer(psql))
return nil, err return nil, err
} }
s.ppstmt = ppstmt s.ppstmt = (*unsafe.Pointer)(ppstmt)
pzTail, err := s.malloc(ptrSize) pzTail, err := s.malloc(ptrSize)
if err != nil { if err != nil {
s.free(psql) s.free(unsafe.Pointer(psql))
s.free(ppstmt) s.free(ppstmt)
return nil, err return nil, err
} }
s.pzTail = pzTail s.pzTail = (**int8)(pzTail)
return s, nil return s, nil
} }
@@ -558,21 +348,21 @@ func (s *stmt) Close() (err error) {
tracer(s, "Close(): %v", err) tracer(s, "Close(): %v", err)
}() }()
} }
if s.psql != 0 { if s.psql != nil {
err = s.free(s.psql) err = s.free(unsafe.Pointer(s.psql))
s.psql = 0 s.psql = nil
} }
if s.ppstmt != 0 { if s.ppstmt != nil {
if err2 := s.free(s.ppstmt); err2 != nil && err == nil { if err2 := s.free(unsafe.Pointer(s.ppstmt)); err2 != nil && err == nil {
err = err2 err = err2
} }
s.ppstmt = 0 s.ppstmt = nil
} }
if s.pzTail != 0 { if s.pzTail != nil {
if err2 := s.free(s.pzTail); err2 != nil && err == nil { if err2 := s.free(unsafe.Pointer(s.pzTail)); err2 != nil && err == nil {
err = err2 err = err2
} }
s.pzTail = 0 s.pzTail = nil
} }
for _, v := range s.allocs { for _, v := range s.allocs {
if err2 := s.free(v); err2 != nil && err == nil { 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) }(args)
} }
var pstmt uintptr var pstmt unsafe.Pointer
donech := make(chan struct{}) donech := make(chan struct{})
defer close(donech) defer close(donech)
go func() { go func() {
select { select {
case <-ctx.Done(): case <-ctx.Done():
if pstmt != 0 { if pstmt != nil {
s.interrupt(s.pdb()) s.interrupt(s.pdb())
} }
case <-donech: 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 { if err := s.prepareV2(psql); err != nil {
return nil, err return nil, err
} }
pstmt = readPtr(s.ppstmt) pstmt = *s.ppstmt
if pstmt == 0 { if pstmt == nil {
continue continue
} }
@@ -679,8 +469,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
}(args) }(args)
} }
var pstmt uintptr var pstmt, rowStmt unsafe.Pointer
var rowStmt uintptr
var rc0 int var rc0 int
donech := make(chan struct{}) donech := make(chan struct{})
@@ -688,20 +477,20 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
go func() { go func() {
select { select {
case <-ctx.Done(): case <-ctx.Done():
if pstmt != 0 { if pstmt != nil {
s.interrupt(s.pdb()) s.interrupt(s.pdb())
} }
case <-donech: 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 { if err := s.prepareV2(psql); err != nil {
return nil, err return nil, err
} }
pstmt = readPtr(s.ppstmt) pstmt = *s.ppstmt
if pstmt == 0 { if pstmt == nil {
continue continue
} }
@@ -724,7 +513,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
switch rc { switch rc {
case bin.XSQLITE_ROW: case bin.XSQLITE_ROW:
if rowStmt != 0 { if rowStmt != nil {
if err := s.finalize(pstmt); err != nil { if err := s.finalize(pstmt); err != nil {
return nil, err return nil, err
} }
@@ -735,7 +524,7 @@ func (s *stmt) query(ctx context.Context, args []namedValue) (r driver.Rows, err
rowStmt = pstmt rowStmt = pstmt
rc0 = rc rc0 = rc
case bin.XSQLITE_DONE: case bin.XSQLITE_DONE:
if rowStmt == 0 { if rowStmt == nil {
rc0 = rc rc0 = rc
} }
default: 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); // int sqlite3_bind_double(sqlite3_stmt*, int, double);
func (s *stmt) bindDouble(pstmt uintptr, idx1 int, value float64) (err error) { func (s *stmt) bindDouble(pstmt unsafe.Pointer, idx1 int, value float64) (err error) {
var rc int32 if rc := bin.Xsqlite3_bind_double(s.tls, pstmt, int32(idx1), value); rc != 0 {
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 {
return s.errstr(rc) 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); // int sqlite3_bind_int(sqlite3_stmt*, int, int);
func (s *stmt) bindInt(pstmt uintptr, idx1, value int) (err error) { func (s *stmt) bindInt(pstmt unsafe.Pointer, idx1, value int) (err error) {
var rc int32 if rc := bin.Xsqlite3_bind_int(s.tls, pstmt, int32(idx1), int32(value)); rc != bin.XSQLITE_OK {
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 {
return s.errstr(rc) 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); // int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
func (s *stmt) bindInt64(pstmt uintptr, idx1 int, value int64) (err error) { func (s *stmt) bindInt64(pstmt unsafe.Pointer, idx1 int, value int64) (err error) {
var rc int32 if rc := bin.Xsqlite3_bind_int64(s.tls, pstmt, int32(idx1), value); rc != bin.XSQLITE_OK {
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 {
return s.errstr(rc) 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*)); // 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)) p, err := s.malloc(len(value))
if err != nil { if err != nil {
return err return err
} }
s.allocs = append(s.allocs, p) s.allocs = append(s.allocs, p)
virtual.CopyBytes(p, value, false) crt.CopyBytes(p, value, false)
var rc int32 if rc := bin.Xsqlite3_bind_blob(s.tls, pstmt, int32(idx1), p, int32(len(value)), nil); rc != bin.XSQLITE_OK {
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 {
return s.errstr(rc) 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*)); // 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) p, err := s.cString(value)
if err != nil { if err != nil {
return err return err
} }
s.allocs = append(s.allocs, p) s.allocs = append(s.allocs, unsafe.Pointer(p))
var rc int32 if rc := bin.Xsqlite3_bind_text(s.tls, pstmt, int32(idx1), p, int32(len(value)), nil); rc != bin.XSQLITE_OK {
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 {
return s.errstr(rc) return s.errstr(rc)
} }
return nil 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++ { for i := 1; i <= n; i++ {
name, err := s.bindParameterName(pstmt, i) name, err := s.bindParameterName(pstmt, i)
if err != nil { if err != nil {
@@ -878,9 +622,9 @@ func (s *stmt) bind(pstmt uintptr, n int, args []namedValue) error {
if v.Ordinal == 0 { if v.Ordinal == 0 {
if name != "" { if name != "" {
return fmt.Errorf("missing named argument %q", name[1:]) 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) { 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*); // int sqlite3_bind_parameter_count(sqlite3_stmt*);
func (s *stmt) bindParameterCount(pstmt uintptr) (_ int, err error) { func (s *stmt) bindParameterCount(pstmt unsafe.Pointer) (_ int, err error) {
var r int32 r := bin.Xsqlite3_bind_parameter_count(s.tls, pstmt)
_, err = s.FFI1( return int(r), nil
bindParameterCount,
virtual.Int32Result{&r},
virtual.Ptr(pstmt),
)
return int(r), err
} }
// const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); // const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
func (s *stmt) bindParameterName(pstmt uintptr, i int) (string, error) { func (s *stmt) bindParameterName(pstmt unsafe.Pointer, i int) (string, error) {
var p uintptr p := bin.Xsqlite3_bind_parameter_name(s.tls, pstmt, int32(i))
_, err := s.FFI1( return crt.GoString(p), nil
bindParameterName,
virtual.PtrResult{&p},
virtual.Ptr(pstmt),
virtual.Int32(i),
)
return virtual.GoString(p), err
} }
// int sqlite3_finalize(sqlite3_stmt *pStmt); // int sqlite3_finalize(sqlite3_stmt *pStmt);
func (s *stmt) finalize(pstmt uintptr) error { func (s *stmt) finalize(pstmt unsafe.Pointer) error {
var rc int32 if rc := bin.Xsqlite3_finalize(s.tls, pstmt); rc != bin.XSQLITE_OK {
if _, err := s.FFI1(
finalize,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
return s.errstr(rc) return s.errstr(rc)
} }
@@ -961,14 +685,9 @@ func (s *stmt) finalize(pstmt uintptr) error {
} }
// int sqlite3_step(sqlite3_stmt*); // int sqlite3_step(sqlite3_stmt*);
func (s *stmt) step(pstmt uintptr) (int, error) { func (s *stmt) step(pstmt unsafe.Pointer) (int, error) {
var rc int32 r := bin.Xsqlite3_step(s.tls, pstmt)
_, err := s.FFI1( return int(r), nil
step,
virtual.Int32Result{&rc},
virtual.Ptr(pstmt),
)
return int(rc), err
} }
// int sqlite3_prepare_v2( // int sqlite3_prepare_v2(
@@ -978,17 +697,8 @@ func (s *stmt) step(pstmt uintptr) (int, error) {
// sqlite3_stmt **ppStmt, /* OUT: Statement handle */ // sqlite3_stmt **ppStmt, /* OUT: Statement handle */
// const char **pzTail /* OUT: Pointer to unused portion of zSql */ // const char **pzTail /* OUT: Pointer to unused portion of zSql */
// ); // );
func (s *stmt) prepareV2(zSql uintptr) error { func (s *stmt) prepareV2(zSql *int8) error {
var rc int32 if rc := bin.Xsqlite3_prepare_v2(s.tls, s.pdb(), zSql, -1, s.ppstmt, s.pzTail); rc != bin.XSQLITE_OK {
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 {
return s.errstr(rc) return s.errstr(rc)
} }
@@ -1043,7 +753,7 @@ func (t *tx) exec(ctx context.Context, sql string) (err error) {
return err return err
} }
defer t.free(psql) defer t.free(unsafe.Pointer(psql))
// TODO: use t.conn.ExecContext() instead // TODO: use t.conn.ExecContext() instead
donech := make(chan struct{}) donech := make(chan struct{})
@@ -1056,16 +766,7 @@ func (t *tx) exec(ctx context.Context, sql string) (err error) {
} }
}() }()
var rc int32 if rc := bin.Xsqlite3_exec(t.tls, t.pdb(), psql, nil, nil, nil); rc != bin.XSQLITE_OK {
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 {
return t.errstr(rc) return t.errstr(rc)
} }
@@ -1074,12 +775,12 @@ func (t *tx) exec(ctx context.Context, sql string) (err error) {
type conn struct { type conn struct {
*Driver *Driver
*virtual.Thread ppdb **bin.Xsqlite3
ppdb uintptr tls *crt.TLS
} }
func (c *conn) String() string { 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) { 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() defer c.Unlock()
c.conns++ c.tls = crt.NewTLS()
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
}
if err = c.openV2( if err = c.openV2(
name, name,
bin.XSQLITE_OPEN_READWRITE|bin.XSQLITE_OPEN_CREATE| 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) 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); // int sqlite3_extended_result_codes(sqlite3*, int onoff);
func (c *conn) extendedResultCodes(on bool) (err error) { func (c *conn) extendedResultCodes(on bool) (err error) {
var v, rc int32 var v int32
if on { if on {
v = 1 v = 1
} }
if _, err = c.FFI1( if rc := bin.Xsqlite3_extended_result_codes(c.tls, c.pdb(), v); rc != bin.XSQLITE_OK {
extendedResultCodes,
virtual.Int32Result{&rc},
virtual.Ptr(c.pdb()), virtual.Int32(v),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
return c.errstr(rc) return c.errstr(rc)
} }
@@ -1279,23 +955,28 @@ func (c *conn) extendedResultCodes(on bool) (err error) {
} }
// void *sqlite3_malloc(int); // void *sqlite3_malloc(int);
func (c *conn) malloc(n int) (r uintptr, err error) { func (c *conn) malloc(n int) (r unsafe.Pointer, err error) {
_, err = c.FFI1( if n > math.MaxInt32 {
maloc, panic("internal error")
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
} }
virtual.CopyString(p, s, true) r = bin.Xsqlite3_malloc(c.tls, int32(n))
return p, nil 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( // int sqlite3_open_v2(
@@ -1310,24 +991,15 @@ func (c *conn) openV2(name string, flags int32) error {
return err return err
} }
defer c.free(filename) defer c.free(unsafe.Pointer(filename))
ppdb, err := c.malloc(ptrSize) ppdb, err := c.malloc(ptrSize)
if err != nil { if err != nil {
return err return err
} }
c.ppdb = ppdb c.ppdb = (**bin.Xsqlite3)(ppdb)
var rc int32 if rc := bin.Xsqlite3_open_v2(c.tls, filename, c.ppdb, flags, nil); rc != bin.XSQLITE_OK {
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 {
return c.errstr(rc) return c.errstr(rc)
} }
@@ -1336,25 +1008,11 @@ func (c *conn) openV2(name string, flags int32) error {
// const char *sqlite3_errstr(int); // const char *sqlite3_errstr(int);
func (c *conn) errstr(rc int32) (err error) { func (c *conn) errstr(rc int32) (err error) {
var p uintptr p := bin.Xsqlite3_errstr(c.tls, rc)
if _, err = c.FFI1( str := crt.GoString(p)
errstr, p = bin.Xsqlite3_errmsg(c.tls, c.pdb())
virtual.PtrResult{&p},
virtual.Int32(rc),
); err != nil {
return err
}
str := virtual.GoString(p) switch msg := crt.GoString(p); {
if _, err = c.FFI1(
errmsg,
virtual.PtrResult{&p},
virtual.Ptr(c.pdb()),
); err != nil {
return err
}
switch msg := virtual.GoString(p); {
case msg == str: case msg == str:
return fmt.Errorf("%s (%v)", str, rc) return fmt.Errorf("%s (%v)", str, rc)
default: default:
@@ -1364,64 +1022,39 @@ func (c *conn) errstr(rc int32) (err error) {
// int sqlite3_close_v2(sqlite3*); // int sqlite3_close_v2(sqlite3*);
func (c *conn) closeV2() (err error) { func (c *conn) closeV2() (err error) {
var rc int32 if rc := bin.Xsqlite3_close_v2(c.tls, c.pdb()); rc != bin.XSQLITE_OK {
if _, err = c.FFI1(
closeV2,
virtual.Int32Result{&rc},
virtual.Ptr(c.pdb()),
); err != nil {
return err
}
if rc != bin.XSQLITE_OK {
return c.errstr(rc) return c.errstr(rc)
} }
err = c.free(c.ppdb) err = c.free(unsafe.Pointer(c.ppdb))
c.ppdb = 0 c.ppdb = nil
return err return err
} }
// void sqlite3_free(void*); // void sqlite3_free(void*);
func (c *conn) free(p uintptr) (err error) { func (c *conn) free(p unsafe.Pointer) (err error) {
_, err = c.FFI0( bin.Xsqlite3_free(c.tls, p)
free, return nil
virtual.Ptr(p),
)
return err
} }
// void sqlite3_interrupt(sqlite3*); // void sqlite3_interrupt(sqlite3*);
func (c *conn) interrupt(pdb uintptr) (err error) { func (c *conn) interrupt(pdb *bin.Xsqlite3) (err error) {
_, err = c.FFI0( bin.Xsqlite3_interrupt(c.tls, pdb)
interrupt, return nil
virtual.Ptr(pdb),
)
return err
} }
func (c *conn) close() (err error) { func (c *conn) close() (err error) {
c.Lock() c.Lock()
defer func() { defer c.Unlock()
c.conns--
if c.conns == 0 {
if err2 := vm.Close(); err2 != nil && err == nil {
err = err2
}
vm = nil
}
c.Unlock()
}()
if c.ppdb != 0 { if c.ppdb != nil {
err = c.closeV2() err = c.closeV2()
} }
return err return err
} }
type Driver struct { type Driver struct {
conns int
sync.Mutex sync.Mutex
} }

View File

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

1406
sqlite_windows.go Normal file

File diff suppressed because it is too large Load Diff