Commit 8c183b52 authored by cznic's avatar cznic

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
parent 1a3b0a73
......@@ -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
......
// 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)
}
}
// 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
// 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
// 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
......@@ -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 {
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)
}
build = append(build, ast)
}
bin, err := virtual.LoadMain(linked)
if err != nil {
var out buffer.Bytes
if err := ccgo.New(build, &out); 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