Files
go-sqlite/all_test.go
Jan Mercl 1a3b0a731a Add bug reproduction test. Updates #11.
modified:   all_test.go
2017-05-22 14:58:44 +02:00

495 lines
9.7 KiB
Go

// 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"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
"sort"
"strings"
"testing"
"time"
"github.com/cznic/virtual"
)
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 profile(t testing.TB, d time.Duration, w io.Writer, format string, arg ...interface{}) {
rate := vm.ProfileRate
if rate == 0 {
rate = 1
}
if len(vm.ProfileFunctions)+len(vm.ProfileLines)+len(vm.ProfileInstructions) != 0 {
fmt.Fprintf(w, "# %v\n", os.Args[1:])
}
if len(vm.ProfileFunctions) != 0 {
fmt.Fprintf(w, format, arg...)
type u struct {
virtual.PCInfo
n int
}
var s int64
var a []u
var wi int
for k, v := range vm.ProfileFunctions {
a = append(a, u{k, v})
s += int64(v)
if n := len(k.Name.String()); n > wi {
wi = n
}
}
sort.Slice(a, func(i, j int) bool {
if a[i].n < a[j].n {
return true
}
if a[i].n > a[j].n {
return false
}
return a[i].Name < a[j].Name
})
fmt.Fprintf(w, "---- Profile functions, %v samples in %v, %.3f MIPS, %v/sample\n", s, d, float64(s)/1e6*float64(rate)*float64(time.Second)/float64(d), time.Duration(float64(d)/float64(s)))
if x, ok := t.(*testing.B); ok {
fmt.Fprintf(w, "\t%v samples/1 [samples/b.N]\n", s/int64(x.N))
}
var c int64
for i := len(a) - 1; i >= 0; i-- {
c += int64(a[i].n)
fmt.Fprintf(
w,
"%*v\t%10v%10.2f%%%10.2f%%\n",
-wi, a[i].Name, a[i].n,
100*float64(a[i].n)/float64(s),
100*float64(c)/float64(s),
)
}
}
if len(vm.ProfileLines) != 0 {
fmt.Fprintf(w, format, arg...)
type u struct {
virtual.PCInfo
n int
}
var s int64
var a []u
for k, v := range vm.ProfileLines {
a = append(a, u{k, v})
s += int64(v)
}
sort.Slice(a, func(i, j int) bool {
if a[i].n < a[j].n {
return true
}
if a[i].n > a[j].n {
return false
}
if a[i].Name < a[j].Name {
return true
}
if a[i].Name > a[j].Name {
return false
}
return a[i].Line < a[j].Line
})
fmt.Fprintf(w, "---- Profile lines, %v samples in %v, %.3f MIPS, %v/sample\n", s, d, float64(s)/1e6*float64(rate)*float64(time.Second)/float64(d), time.Duration(float64(d)/float64(s)))
if x, ok := t.(*testing.B); ok {
fmt.Fprintf(w, "\t%v samples/1 [samples/b.N]\n", s/int64(x.N))
}
var c int64
for i := len(a) - 1; i >= 0; i-- {
c += int64(a[i].n)
fmt.Fprintf(
w,
"%v:%v:\t%10v%10.2f%%%10.2f%%\n",
a[i].Name, a[i].Line, a[i].n,
100*float64(a[i].n)/float64(s),
100*float64(c)/float64(s),
)
}
}
if len(vm.ProfileInstructions) != 0 {
fmt.Fprintf(w, format, arg...)
type u struct {
virtual.Opcode
n int
}
var s int64
var a []u
var wi int
for k, v := range vm.ProfileInstructions {
a = append(a, u{k, v})
s += int64(v)
if n := len(k.String()); n > wi {
wi = n
}
}
sort.Slice(a, func(i, j int) bool {
if a[i].n < a[j].n {
return true
}
if a[i].n > a[j].n {
return false
}
return a[i].Opcode < a[j].Opcode
})
fmt.Fprintf(w, "---- Profile instructions, %v samples in %v, %.3f MIPS, %v/sample\n", s, d, float64(s)/1e6*float64(rate)*float64(time.Second)/float64(d), time.Duration(float64(d)/float64(s)))
if x, ok := t.(*testing.B); ok {
fmt.Fprintf(w, "\t%v samples/1 [samples/b.N]\n", s/int64(x.N))
}
var c int64
for i := len(a) - 1; i >= 0; i-- {
c += int64(a[i].n)
fmt.Fprintf(
w,
"%*s%10v%10.2f%%\t%10.2f%%\n",
-wi, a[i].Opcode, a[i].n,
100*float64(a[i].n)/float64(s),
100*float64(c)/float64(s),
)
}
}
}
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)
}
if *profileAll || *profileFunctions {
vm.ProfileFunctions = map[virtual.PCInfo]int{}
}
if *profileAll || *profileLines {
vm.ProfileLines = map[virtual.PCInfo]int{}
}
if *profileAll || *profileInstructions {
vm.ProfileInstructions = map[virtual.Opcode]int{}
}
vm.ProfileRate = int(*profileRate)
t0 := time.Now()
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)
}
d := time.Since(t0)
if _, err := db.Exec(`commit;`); err != nil {
b.Fatal(err)
}
profile(b, d, os.Stderr, "==== BenchmarkInsertMemory b.N %v\n", b.N)
}
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()
if *profileAll || *profileFunctions {
vm.ProfileFunctions = map[virtual.PCInfo]int{}
}
if *profileAll || *profileLines {
vm.ProfileLines = map[virtual.PCInfo]int{}
}
if *profileAll || *profileInstructions {
vm.ProfileInstructions = map[virtual.Opcode]int{}
}
vm.ProfileRate = int(*profileRate)
t0 := time.Now()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if !r.Next() {
b.Fatal(err)
}
}
b.StopTimer()
if *recsPerSec {
b.SetBytes(1e6)
}
d := time.Since(t0)
profile(b, d, os.Stderr, "==== BenchmarkNextMemory b.N %v\n", b.N)
}
// 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)
}
}