// 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 ( 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) } } // https://github.com/cznic/sqlite/issues/12 func TestMemDB(t *testing.T) { // Verify we can create out-of-the heap memory DB instance. db, err := sql.Open(driverName, "file::memory:") if err != nil { t.Fatal(err) } defer func() { db.Close() }() v := strings.Repeat("a", 1024) if _, err := db.Exec(` create table t(s string); begin; `); err != nil { t.Fatal(err) } s, err := db.Prepare("insert into t values(?)") if err != nil { t.Fatal(err) } // Heap used to be fixed at 32MB. for i := 0; i < (64<<20)/len(v); i++ { if _, err := s.Exec(v); err != nil { t.Fatalf("%v * %v= %v: %v", i, len(v), i*len(v), err) } } if _, err := db.Exec(`commit;`); err != nil { t.Fatal(err) } }