|  | // Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>. | 
|  | // | 
|  | // Use of this source code is governed by an MIT-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package sqlite3 | 
|  |  | 
|  | import ( | 
|  | "database/sql" | 
|  | "database/sql/driver" | 
|  | "errors" | 
|  | "fmt" | 
|  | "io/ioutil" | 
|  | "net/url" | 
|  | "os" | 
|  | "reflect" | 
|  | "regexp" | 
|  | "strings" | 
|  | "sync" | 
|  | "testing" | 
|  | "time" | 
|  |  | 
|  | "github.com/mattn/go-sqlite3/sqlite3_test" | 
|  | ) | 
|  |  | 
|  | func TempFilename(t *testing.T) string { | 
|  | f, err := ioutil.TempFile("", "go-sqlite3-test-") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | f.Close() | 
|  | return f.Name() | 
|  | } | 
|  |  | 
|  | func doTestOpen(t *testing.T, option string) (string, error) { | 
|  | var url string | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | if option != "" { | 
|  | url = tempFilename + option | 
|  | } else { | 
|  | url = tempFilename | 
|  | } | 
|  | db, err := sql.Open("sqlite3", url) | 
|  | if err != nil { | 
|  | return "Failed to open database:", err | 
|  | } | 
|  | defer os.Remove(tempFilename) | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("drop table foo") | 
|  | _, err = db.Exec("create table foo (id integer)") | 
|  | if err != nil { | 
|  | return "Failed to create table:", err | 
|  | } | 
|  |  | 
|  | if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() { | 
|  | return "Failed to create ./foo.db", nil | 
|  | } | 
|  |  | 
|  | return "", nil | 
|  | } | 
|  |  | 
|  | func TestOpen(t *testing.T) { | 
|  | cases := map[string]bool{ | 
|  | "":                   true, | 
|  | "?_txlock=immediate": true, | 
|  | "?_txlock=deferred":  true, | 
|  | "?_txlock=exclusive": true, | 
|  | "?_txlock=bogus":     false, | 
|  | } | 
|  | for option, expectedPass := range cases { | 
|  | result, err := doTestOpen(t, option) | 
|  | if result == "" { | 
|  | if !expectedPass { | 
|  | errmsg := fmt.Sprintf("_txlock error not caught at dbOpen with option: %s", option) | 
|  | t.Fatal(errmsg) | 
|  | } | 
|  | } else if expectedPass { | 
|  | if err == nil { | 
|  | t.Fatal(result) | 
|  | } else { | 
|  | t.Fatal(result, err) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestReadonly(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  |  | 
|  | db1, err := sql.Open("sqlite3", "file:"+tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | db1.Exec("CREATE TABLE test (x int, y float)") | 
|  |  | 
|  | db2, err := sql.Open("sqlite3", "file:"+tempFilename+"?mode=ro") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | _ = db2 | 
|  | _, err = db2.Exec("INSERT INTO test VALUES (1, 3.14)") | 
|  | if err == nil { | 
|  | t.Fatal("didn't expect INSERT into read-only database to work") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestClose(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  |  | 
|  | _, err = db.Exec("drop table foo") | 
|  | _, err = db.Exec("create table foo (id integer)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | stmt, err := db.Prepare("select id from foo where id = ?") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to select records:", err) | 
|  | } | 
|  |  | 
|  | db.Close() | 
|  | _, err = stmt.Exec(1) | 
|  | if err == nil { | 
|  | t.Fatal("Failed to operate closed statement") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestInsert(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("drop table foo") | 
|  | _, err = db.Exec("create table foo (id integer)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | res, err := db.Exec("insert into foo(id) values(123)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert record:", err) | 
|  | } | 
|  | affected, _ := res.RowsAffected() | 
|  | if affected != 1 { | 
|  | t.Fatalf("Expected %d for affected rows, but %d:", 1, affected) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("select id from foo") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to select records:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  |  | 
|  | rows.Next() | 
|  |  | 
|  | var result int | 
|  | rows.Scan(&result) | 
|  | if result != 123 { | 
|  | t.Errorf("Expected %d for fetched result, but %d:", 123, result) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestUpdate(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("drop table foo") | 
|  | _, err = db.Exec("create table foo (id integer)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | res, err := db.Exec("insert into foo(id) values(123)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert record:", err) | 
|  | } | 
|  | expected, err := res.LastInsertId() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get LastInsertId:", err) | 
|  | } | 
|  | affected, _ := res.RowsAffected() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get RowsAffected:", err) | 
|  | } | 
|  | if affected != 1 { | 
|  | t.Fatalf("Expected %d for affected rows, but %d:", 1, affected) | 
|  | } | 
|  |  | 
|  | res, err = db.Exec("update foo set id = 234") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to update record:", err) | 
|  | } | 
|  | lastID, err := res.LastInsertId() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get LastInsertId:", err) | 
|  | } | 
|  | if expected != lastID { | 
|  | t.Errorf("Expected %q for last Id, but %q:", expected, lastID) | 
|  | } | 
|  | affected, _ = res.RowsAffected() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get RowsAffected:", err) | 
|  | } | 
|  | if affected != 1 { | 
|  | t.Fatalf("Expected %d for affected rows, but %d:", 1, affected) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("select id from foo") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to select records:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  |  | 
|  | rows.Next() | 
|  |  | 
|  | var result int | 
|  | rows.Scan(&result) | 
|  | if result != 234 { | 
|  | t.Errorf("Expected %d for fetched result, but %d:", 234, result) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestDelete(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("drop table foo") | 
|  | _, err = db.Exec("create table foo (id integer)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | res, err := db.Exec("insert into foo(id) values(123)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert record:", err) | 
|  | } | 
|  | expected, err := res.LastInsertId() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get LastInsertId:", err) | 
|  | } | 
|  | affected, err := res.RowsAffected() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get RowsAffected:", err) | 
|  | } | 
|  | if affected != 1 { | 
|  | t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected) | 
|  | } | 
|  |  | 
|  | res, err = db.Exec("delete from foo where id = 123") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to delete record:", err) | 
|  | } | 
|  | lastID, err := res.LastInsertId() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get LastInsertId:", err) | 
|  | } | 
|  | if expected != lastID { | 
|  | t.Errorf("Expected %q for last Id, but %q:", expected, lastID) | 
|  | } | 
|  | affected, err = res.RowsAffected() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to get RowsAffected:", err) | 
|  | } | 
|  | if affected != 1 { | 
|  | t.Errorf("Expected %d for cout of affected rows, but %q:", 1, affected) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("select id from foo") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to select records:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  |  | 
|  | if rows.Next() { | 
|  | t.Error("Fetched row but expected not rows") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestBooleanRoundtrip(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("DROP TABLE foo") | 
|  | _, err = db.Exec("CREATE TABLE foo(id INTEGER, value BOOL)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | _, err = db.Exec("INSERT INTO foo(id, value) VALUES(1, ?)", true) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert true value:", err) | 
|  | } | 
|  |  | 
|  | _, err = db.Exec("INSERT INTO foo(id, value) VALUES(2, ?)", false) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert false value:", err) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("SELECT id, value FROM foo") | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  |  | 
|  | for rows.Next() { | 
|  | var id int | 
|  | var value bool | 
|  |  | 
|  | if err := rows.Scan(&id, &value); err != nil { | 
|  | t.Error("Unable to scan results:", err) | 
|  | continue | 
|  | } | 
|  |  | 
|  | if id == 1 && !value { | 
|  | t.Error("Value for id 1 should be true, not false") | 
|  |  | 
|  | } else if id == 2 && value { | 
|  | t.Error("Value for id 2 should be false, not true") | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func timezone(t time.Time) string { return t.Format("-07:00") } | 
|  |  | 
|  | func TestTimestamp(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("DROP TABLE foo") | 
|  | _, err = db.Exec("CREATE TABLE foo(id INTEGER, ts timeSTAMP, dt DATETIME)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC) | 
|  | timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC) | 
|  | timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC) | 
|  | tzTest := time.FixedZone("TEST", -9*3600-13*60) | 
|  | tests := []struct { | 
|  | value    interface{} | 
|  | expected time.Time | 
|  | }{ | 
|  | {"nonsense", time.Time{}}, | 
|  | {"0000-00-00 00:00:00", time.Time{}}, | 
|  | {timestamp1, timestamp1}, | 
|  | {timestamp2.Unix(), timestamp2.Truncate(time.Second)}, | 
|  | {timestamp2.UnixNano() / int64(time.Millisecond), timestamp2.Truncate(time.Millisecond)}, | 
|  | {timestamp1.In(tzTest), timestamp1.In(tzTest)}, | 
|  | {timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1}, | 
|  | {timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1}, | 
|  | {timestamp1.Format("2006-01-02 15:04:05"), timestamp1}, | 
|  | {timestamp1.Format("2006-01-02T15:04:05"), timestamp1}, | 
|  | {timestamp2, timestamp2}, | 
|  | {"2006-01-02 15:04:05.123456789", timestamp2}, | 
|  | {"2006-01-02T15:04:05.123456789", timestamp2}, | 
|  | {"2006-01-02T05:51:05.123456789-09:13", timestamp2.In(tzTest)}, | 
|  | {"2012-11-04", timestamp3}, | 
|  | {"2012-11-04 00:00", timestamp3}, | 
|  | {"2012-11-04 00:00:00", timestamp3}, | 
|  | {"2012-11-04 00:00:00.000", timestamp3}, | 
|  | {"2012-11-04T00:00", timestamp3}, | 
|  | {"2012-11-04T00:00:00", timestamp3}, | 
|  | {"2012-11-04T00:00:00.000", timestamp3}, | 
|  | {"2006-01-02T15:04:05.123456789Z", timestamp2}, | 
|  | {"2012-11-04Z", timestamp3}, | 
|  | {"2012-11-04 00:00Z", timestamp3}, | 
|  | {"2012-11-04 00:00:00Z", timestamp3}, | 
|  | {"2012-11-04 00:00:00.000Z", timestamp3}, | 
|  | {"2012-11-04T00:00Z", timestamp3}, | 
|  | {"2012-11-04T00:00:00Z", timestamp3}, | 
|  | {"2012-11-04T00:00:00.000Z", timestamp3}, | 
|  | } | 
|  | for i := range tests { | 
|  | _, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert timestamp:", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC") | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  |  | 
|  | seen := 0 | 
|  | for rows.Next() { | 
|  | var id int | 
|  | var ts, dt time.Time | 
|  |  | 
|  | if err := rows.Scan(&id, &ts, &dt); err != nil { | 
|  | t.Error("Unable to scan results:", err) | 
|  | continue | 
|  | } | 
|  | if id < 0 || id >= len(tests) { | 
|  | t.Error("Bad row id: ", id) | 
|  | continue | 
|  | } | 
|  | seen++ | 
|  | if !tests[id].expected.Equal(ts) { | 
|  | t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) | 
|  | } | 
|  | if !tests[id].expected.Equal(dt) { | 
|  | t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) | 
|  | } | 
|  | if timezone(tests[id].expected) != timezone(ts) { | 
|  | t.Errorf("Timezone for id %v (%v) should be %v, not %v", id, tests[id].value, | 
|  | timezone(tests[id].expected), timezone(ts)) | 
|  | } | 
|  | if timezone(tests[id].expected) != timezone(dt) { | 
|  | t.Errorf("Timezone for id %v (%v) should be %v, not %v", id, tests[id].value, | 
|  | timezone(tests[id].expected), timezone(dt)) | 
|  | } | 
|  | } | 
|  |  | 
|  | if seen != len(tests) { | 
|  | t.Errorf("Expected to see %d rows", len(tests)) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestBoolean(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  |  | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("CREATE TABLE foo(id INTEGER, fbool BOOLEAN)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | bool1 := true | 
|  | _, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(1, ?)", bool1) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert boolean:", err) | 
|  | } | 
|  |  | 
|  | bool2 := false | 
|  | _, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(2, ?)", bool2) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert boolean:", err) | 
|  | } | 
|  |  | 
|  | bool3 := "nonsense" | 
|  | _, err = db.Exec("INSERT INTO foo(id, fbool) VALUES(3, ?)", bool3) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert nonsense:", err) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("SELECT id, fbool FROM foo where fbool = ?", bool1) | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  | counter := 0 | 
|  |  | 
|  | var id int | 
|  | var fbool bool | 
|  |  | 
|  | for rows.Next() { | 
|  | if err := rows.Scan(&id, &fbool); err != nil { | 
|  | t.Fatal("Unable to scan results:", err) | 
|  | } | 
|  | counter++ | 
|  | } | 
|  |  | 
|  | if counter != 1 { | 
|  | t.Fatalf("Expected 1 row but %v", counter) | 
|  | } | 
|  |  | 
|  | if id != 1 && fbool != true { | 
|  | t.Fatalf("Value for id 1 should be %v, not %v", bool1, fbool) | 
|  | } | 
|  |  | 
|  | rows, err = db.Query("SELECT id, fbool FROM foo where fbool = ?", bool2) | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  |  | 
|  | counter = 0 | 
|  |  | 
|  | for rows.Next() { | 
|  | if err := rows.Scan(&id, &fbool); err != nil { | 
|  | t.Fatal("Unable to scan results:", err) | 
|  | } | 
|  | counter++ | 
|  | } | 
|  |  | 
|  | if counter != 1 { | 
|  | t.Fatalf("Expected 1 row but %v", counter) | 
|  | } | 
|  |  | 
|  | if id != 2 && fbool != false { | 
|  | t.Fatalf("Value for id 2 should be %v, not %v", bool2, fbool) | 
|  | } | 
|  |  | 
|  | // make sure "nonsense" triggered an error | 
|  | rows, err = db.Query("SELECT id, fbool FROM foo where id=?;", 3) | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  |  | 
|  | rows.Next() | 
|  | err = rows.Scan(&id, &fbool) | 
|  | if err == nil { | 
|  | t.Error("Expected error from \"nonsense\" bool") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFloat32(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("CREATE TABLE foo(id INTEGER)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | _, err = db.Exec("INSERT INTO foo(id) VALUES(null)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert null:", err) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("SELECT id FROM foo") | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  |  | 
|  | if !rows.Next() { | 
|  | t.Fatal("Unable to query results:", err) | 
|  | } | 
|  |  | 
|  | var id interface{} | 
|  | if err := rows.Scan(&id); err != nil { | 
|  | t.Fatal("Unable to scan results:", err) | 
|  | } | 
|  | if id != nil { | 
|  | t.Error("Expected nil but not") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestNull(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | rows, err := db.Query("SELECT 3.141592") | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  |  | 
|  | if !rows.Next() { | 
|  | t.Fatal("Unable to query results:", err) | 
|  | } | 
|  |  | 
|  | var v interface{} | 
|  | if err := rows.Scan(&v); err != nil { | 
|  | t.Fatal("Unable to scan results:", err) | 
|  | } | 
|  | f, ok := v.(float64) | 
|  | if !ok { | 
|  | t.Error("Expected float but not") | 
|  | } | 
|  | if f != 3.141592 { | 
|  | t.Error("Expected 3.141592 but not") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestTransaction(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("CREATE TABLE foo(id INTEGER)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | tx, err := db.Begin() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to begin transaction:", err) | 
|  | } | 
|  |  | 
|  | _, err = tx.Exec("INSERT INTO foo(id) VALUES(1)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert null:", err) | 
|  | } | 
|  |  | 
|  | rows, err := tx.Query("SELECT id from foo") | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  |  | 
|  | err = tx.Rollback() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to rollback transaction:", err) | 
|  | } | 
|  |  | 
|  | if rows.Next() { | 
|  | t.Fatal("Unable to query results:", err) | 
|  | } | 
|  |  | 
|  | tx, err = db.Begin() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to begin transaction:", err) | 
|  | } | 
|  |  | 
|  | _, err = tx.Exec("INSERT INTO foo(id) VALUES(1)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert null:", err) | 
|  | } | 
|  |  | 
|  | err = tx.Commit() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to commit transaction:", err) | 
|  | } | 
|  |  | 
|  | rows, err = tx.Query("SELECT id from foo") | 
|  | if err == nil { | 
|  | t.Fatal("Expected failure to query") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestWAL(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | if _, err = db.Exec("PRAGMA journal_mode=WAL;"); err != nil { | 
|  | t.Fatal("Failed to Exec PRAGMA journal_mode:", err) | 
|  | } | 
|  | if _, err = db.Exec("PRAGMA locking_mode=EXCLUSIVE;"); err != nil { | 
|  | t.Fatal("Failed to Exec PRAGMA locking_mode:", err) | 
|  | } | 
|  | if _, err = db.Exec("CREATE TABLE test (id SERIAL, user TEXT NOT NULL, name TEXT NOT NULL);"); err != nil { | 
|  | t.Fatal("Failed to Exec CREATE TABLE:", err) | 
|  | } | 
|  | if _, err = db.Exec("INSERT INTO test (user, name) VALUES ('user','name');"); err != nil { | 
|  | t.Fatal("Failed to Exec INSERT:", err) | 
|  | } | 
|  |  | 
|  | trans, err := db.Begin() | 
|  | if err != nil { | 
|  | t.Fatal("Failed to Begin:", err) | 
|  | } | 
|  | s, err := trans.Prepare("INSERT INTO test (user, name) VALUES (?, ?);") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to Prepare:", err) | 
|  | } | 
|  |  | 
|  | var count int | 
|  | if err = trans.QueryRow("SELECT count(user) FROM test;").Scan(&count); err != nil { | 
|  | t.Fatal("Failed to QueryRow:", err) | 
|  | } | 
|  | if _, err = s.Exec("bbbb", "aaaa"); err != nil { | 
|  | t.Fatal("Failed to Exec prepared statement:", err) | 
|  | } | 
|  | if err = s.Close(); err != nil { | 
|  | t.Fatal("Failed to Close prepared statement:", err) | 
|  | } | 
|  | if err = trans.Commit(); err != nil { | 
|  | t.Fatal("Failed to Commit:", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestTimezoneConversion(t *testing.T) { | 
|  | zones := []string{"UTC", "US/Central", "US/Pacific", "Local"} | 
|  | for _, tz := range zones { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename+"?_loc="+url.QueryEscape(tz)) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("DROP TABLE foo") | 
|  | _, err = db.Exec("CREATE TABLE foo(id INTEGER, ts TIMESTAMP, dt DATETIME)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | loc, err := time.LoadLocation(tz) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to load location:", err) | 
|  | } | 
|  |  | 
|  | timestamp1 := time.Date(2012, time.April, 6, 22, 50, 0, 0, time.UTC) | 
|  | timestamp2 := time.Date(2006, time.January, 2, 15, 4, 5, 123456789, time.UTC) | 
|  | timestamp3 := time.Date(2012, time.November, 4, 0, 0, 0, 0, time.UTC) | 
|  | tests := []struct { | 
|  | value    interface{} | 
|  | expected time.Time | 
|  | }{ | 
|  | {"nonsense", time.Time{}.In(loc)}, | 
|  | {"0000-00-00 00:00:00", time.Time{}.In(loc)}, | 
|  | {timestamp1, timestamp1.In(loc)}, | 
|  | {timestamp1.Unix(), timestamp1.In(loc)}, | 
|  | {timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1.In(loc)}, | 
|  | {timestamp1.Format("2006-01-02 15:04:05.000"), timestamp1.In(loc)}, | 
|  | {timestamp1.Format("2006-01-02T15:04:05.000"), timestamp1.In(loc)}, | 
|  | {timestamp1.Format("2006-01-02 15:04:05"), timestamp1.In(loc)}, | 
|  | {timestamp1.Format("2006-01-02T15:04:05"), timestamp1.In(loc)}, | 
|  | {timestamp2, timestamp2.In(loc)}, | 
|  | {"2006-01-02 15:04:05.123456789", timestamp2.In(loc)}, | 
|  | {"2006-01-02T15:04:05.123456789", timestamp2.In(loc)}, | 
|  | {"2012-11-04", timestamp3.In(loc)}, | 
|  | {"2012-11-04 00:00", timestamp3.In(loc)}, | 
|  | {"2012-11-04 00:00:00", timestamp3.In(loc)}, | 
|  | {"2012-11-04 00:00:00.000", timestamp3.In(loc)}, | 
|  | {"2012-11-04T00:00", timestamp3.In(loc)}, | 
|  | {"2012-11-04T00:00:00", timestamp3.In(loc)}, | 
|  | {"2012-11-04T00:00:00.000", timestamp3.In(loc)}, | 
|  | } | 
|  | for i := range tests { | 
|  | _, err = db.Exec("INSERT INTO foo(id, ts, dt) VALUES(?, ?, ?)", i, tests[i].value, tests[i].value) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert timestamp:", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | rows, err := db.Query("SELECT id, ts, dt FROM foo ORDER BY id ASC") | 
|  | if err != nil { | 
|  | t.Fatal("Unable to query foo table:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  |  | 
|  | seen := 0 | 
|  | for rows.Next() { | 
|  | var id int | 
|  | var ts, dt time.Time | 
|  |  | 
|  | if err := rows.Scan(&id, &ts, &dt); err != nil { | 
|  | t.Error("Unable to scan results:", err) | 
|  | continue | 
|  | } | 
|  | if id < 0 || id >= len(tests) { | 
|  | t.Error("Bad row id: ", id) | 
|  | continue | 
|  | } | 
|  | seen++ | 
|  | if !tests[id].expected.Equal(ts) { | 
|  | t.Errorf("Timestamp value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, ts) | 
|  | } | 
|  | if !tests[id].expected.Equal(dt) { | 
|  | t.Errorf("Datetime value for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected, dt) | 
|  | } | 
|  | if tests[id].expected.Location().String() != ts.Location().String() { | 
|  | t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), ts.Location().String()) | 
|  | } | 
|  | if tests[id].expected.Location().String() != dt.Location().String() { | 
|  | t.Errorf("Location for id %v (%v) should be %v, not %v", id, tests[id].value, tests[id].expected.Location().String(), dt.Location().String()) | 
|  | } | 
|  | } | 
|  |  | 
|  | if seen != len(tests) { | 
|  | t.Errorf("Expected to see %d rows", len(tests)) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestSuite(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename+"?_busy_timeout=99999") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | sqlite3_test.RunTests(t, db, sqlite3_test.SQLITE) | 
|  | } | 
|  |  | 
|  | // TODO: Execer & Queryer currently disabled | 
|  | // https://github.com/mattn/go-sqlite3/issues/82 | 
|  | func TestExecer(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec(` | 
|  | create table foo (id integer); -- one comment | 
|  | insert into foo(id) values(?); | 
|  | insert into foo(id) values(?); | 
|  | insert into foo(id) values(?); -- another comment | 
|  | `, 1, 2, 3) | 
|  | if err != nil { | 
|  | t.Error("Failed to call db.Exec:", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestQueryer(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec(` | 
|  | create table foo (id integer); | 
|  | `) | 
|  | if err != nil { | 
|  | t.Error("Failed to call db.Query:", err) | 
|  | } | 
|  |  | 
|  | rows, err := db.Query(` | 
|  | insert into foo(id) values(?); | 
|  | insert into foo(id) values(?); | 
|  | insert into foo(id) values(?); | 
|  | select id from foo order by id; | 
|  | `, 3, 2, 1) | 
|  | if err != nil { | 
|  | t.Error("Failed to call db.Query:", err) | 
|  | } | 
|  | defer rows.Close() | 
|  | n := 1 | 
|  | if rows != nil { | 
|  | for rows.Next() { | 
|  | var id int | 
|  | err = rows.Scan(&id) | 
|  | if err != nil { | 
|  | t.Error("Failed to db.Query:", err) | 
|  | } | 
|  | if id != n { | 
|  | t.Error("Failed to db.Query: not matched results") | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestStress(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | db.Exec("CREATE TABLE foo (id int);") | 
|  | db.Exec("INSERT INTO foo VALUES(1);") | 
|  | db.Exec("INSERT INTO foo VALUES(2);") | 
|  | db.Close() | 
|  |  | 
|  | for i := 0; i < 10000; i++ { | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  |  | 
|  | for j := 0; j < 3; j++ { | 
|  | rows, err := db.Query("select * from foo where id=1;") | 
|  | if err != nil { | 
|  | t.Error("Failed to call db.Query:", err) | 
|  | } | 
|  | for rows.Next() { | 
|  | var i int | 
|  | if err := rows.Scan(&i); err != nil { | 
|  | t.Errorf("Scan failed: %v\n", err) | 
|  | } | 
|  | } | 
|  | if err := rows.Err(); err != nil { | 
|  | t.Errorf("Post-scan failed: %v\n", err) | 
|  | } | 
|  | rows.Close() | 
|  | } | 
|  | db.Close() | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestDateTimeLocal(t *testing.T) { | 
|  | zone := "Asia/Tokyo" | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename+"?_loc="+zone) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | db.Exec("CREATE TABLE foo (dt datetime);") | 
|  | db.Exec("INSERT INTO foo VALUES('2015-03-05 15:16:17');") | 
|  |  | 
|  | row := db.QueryRow("select * from foo") | 
|  | var d time.Time | 
|  | err = row.Scan(&d) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to scan datetime:", err) | 
|  | } | 
|  | if d.Hour() == 15 || !strings.Contains(d.String(), "JST") { | 
|  | t.Fatal("Result should have timezone", d) | 
|  | } | 
|  | db.Close() | 
|  |  | 
|  | db, err = sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  |  | 
|  | row = db.QueryRow("select * from foo") | 
|  | err = row.Scan(&d) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to scan datetime:", err) | 
|  | } | 
|  | if d.UTC().Hour() != 15 || !strings.Contains(d.String(), "UTC") { | 
|  | t.Fatalf("Result should not have timezone %v %v", zone, d.String()) | 
|  | } | 
|  |  | 
|  | _, err = db.Exec("DELETE FROM foo") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to delete table:", err) | 
|  | } | 
|  | dt, err := time.Parse("2006/1/2 15/4/5 -0700 MST", "2015/3/5 15/16/17 +0900 JST") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to parse datetime:", err) | 
|  | } | 
|  | db.Exec("INSERT INTO foo VALUES(?);", dt) | 
|  |  | 
|  | db.Close() | 
|  | db, err = sql.Open("sqlite3", tempFilename+"?_loc="+zone) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  |  | 
|  | row = db.QueryRow("select * from foo") | 
|  | err = row.Scan(&d) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to scan datetime:", err) | 
|  | } | 
|  | if d.Hour() != 15 || !strings.Contains(d.String(), "JST") { | 
|  | t.Fatalf("Result should have timezone %v %v", zone, d.String()) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestVersion(t *testing.T) { | 
|  | s, n, id := Version() | 
|  | if s == "" || n == 0 || id == "" { | 
|  | t.Errorf("Version failed %q, %d, %q\n", s, n, id) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestStringContainingZero(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec(` | 
|  | create table foo (id integer, name, extra text); | 
|  | `) | 
|  | if err != nil { | 
|  | t.Error("Failed to call db.Query:", err) | 
|  | } | 
|  |  | 
|  | const text = "foo\x00bar" | 
|  |  | 
|  | _, err = db.Exec(`insert into foo(id, name, extra) values($1, $2, $2)`, 1, text) | 
|  | if err != nil { | 
|  | t.Error("Failed to call db.Exec:", err) | 
|  | } | 
|  |  | 
|  | row := db.QueryRow(`select id, extra from foo where id = $1 and extra = $2`, 1, text) | 
|  | if row == nil { | 
|  | t.Error("Failed to call db.QueryRow") | 
|  | } | 
|  |  | 
|  | var id int | 
|  | var extra string | 
|  | err = row.Scan(&id, &extra) | 
|  | if err != nil { | 
|  | t.Error("Failed to db.Scan:", err) | 
|  | } | 
|  | if id != 1 || extra != text { | 
|  | t.Error("Failed to db.QueryRow: not matched results") | 
|  | } | 
|  | } | 
|  |  | 
|  | const CurrentTimeStamp = "2006-01-02 15:04:05" | 
|  |  | 
|  | type TimeStamp struct{ *time.Time } | 
|  |  | 
|  | func (t TimeStamp) Scan(value interface{}) error { | 
|  | var err error | 
|  | switch v := value.(type) { | 
|  | case string: | 
|  | *t.Time, err = time.Parse(CurrentTimeStamp, v) | 
|  | case []byte: | 
|  | *t.Time, err = time.Parse(CurrentTimeStamp, string(v)) | 
|  | default: | 
|  | err = errors.New("invalid type for current_timestamp") | 
|  | } | 
|  | return err | 
|  | } | 
|  |  | 
|  | func (t TimeStamp) Value() (driver.Value, error) { | 
|  | return t.Time.Format(CurrentTimeStamp), nil | 
|  | } | 
|  |  | 
|  | func TestDateTimeNow(t *testing.T) { | 
|  | tempFilename := TempFilename(t) | 
|  | defer os.Remove(tempFilename) | 
|  | db, err := sql.Open("sqlite3", tempFilename) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | var d time.Time | 
|  | err = db.QueryRow("SELECT datetime('now')").Scan(TimeStamp{&d}) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to scan datetime:", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestFunctionRegistration(t *testing.T) { | 
|  | addi8_16_32 := func(a int8, b int16) int32 { return int32(a) + int32(b) } | 
|  | addi64 := func(a, b int64) int64 { return a + b } | 
|  | addu8_16_32 := func(a uint8, b uint16) uint32 { return uint32(a) + uint32(b) } | 
|  | addu64 := func(a, b uint64) uint64 { return a + b } | 
|  | addiu := func(a int, b uint) int64 { return int64(a) + int64(b) } | 
|  | addf32_64 := func(a float32, b float64) float64 { return float64(a) + b } | 
|  | not := func(a bool) bool { return !a } | 
|  | regex := func(re, s string) (bool, error) { | 
|  | return regexp.MatchString(re, s) | 
|  | } | 
|  | generic := func(a interface{}) int64 { | 
|  | switch a.(type) { | 
|  | case int64: | 
|  | return 1 | 
|  | case float64: | 
|  | return 2 | 
|  | case []byte: | 
|  | return 3 | 
|  | case string: | 
|  | return 4 | 
|  | default: | 
|  | panic("unreachable") | 
|  | } | 
|  | } | 
|  | variadic := func(a, b int64, c ...int64) int64 { | 
|  | ret := a + b | 
|  | for _, d := range c { | 
|  | ret += d | 
|  | } | 
|  | return ret | 
|  | } | 
|  | variadicGeneric := func(a ...interface{}) int64 { | 
|  | return int64(len(a)) | 
|  | } | 
|  |  | 
|  | sql.Register("sqlite3_FunctionRegistration", &SQLiteDriver{ | 
|  | ConnectHook: func(conn *SQLiteConn) error { | 
|  | if err := conn.RegisterFunc("addi8_16_32", addi8_16_32, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("addi64", addi64, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("addu8_16_32", addu8_16_32, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("addu64", addu64, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("addiu", addiu, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("addf32_64", addf32_64, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("not", not, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("regex", regex, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("generic", generic, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("variadic", variadic, true); err != nil { | 
|  | return err | 
|  | } | 
|  | if err := conn.RegisterFunc("variadicGeneric", variadicGeneric, true); err != nil { | 
|  | return err | 
|  | } | 
|  | return nil | 
|  | }, | 
|  | }) | 
|  | db, err := sql.Open("sqlite3_FunctionRegistration", ":memory:") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | ops := []struct { | 
|  | query    string | 
|  | expected interface{} | 
|  | }{ | 
|  | {"SELECT addi8_16_32(1,2)", int32(3)}, | 
|  | {"SELECT addi64(1,2)", int64(3)}, | 
|  | {"SELECT addu8_16_32(1,2)", uint32(3)}, | 
|  | {"SELECT addu64(1,2)", uint64(3)}, | 
|  | {"SELECT addiu(1,2)", int64(3)}, | 
|  | {"SELECT addf32_64(1.5,1.5)", float64(3)}, | 
|  | {"SELECT not(1)", false}, | 
|  | {"SELECT not(0)", true}, | 
|  | {`SELECT regex("^foo.*", "foobar")`, true}, | 
|  | {`SELECT regex("^foo.*", "barfoobar")`, false}, | 
|  | {"SELECT generic(1)", int64(1)}, | 
|  | {"SELECT generic(1.1)", int64(2)}, | 
|  | {`SELECT generic(NULL)`, int64(3)}, | 
|  | {`SELECT generic("foo")`, int64(4)}, | 
|  | {"SELECT variadic(1,2)", int64(3)}, | 
|  | {"SELECT variadic(1,2,3,4)", int64(10)}, | 
|  | {"SELECT variadic(1,1,1,1,1,1,1,1,1,1)", int64(10)}, | 
|  | {`SELECT variadicGeneric(1,"foo",2.3, NULL)`, int64(4)}, | 
|  | } | 
|  |  | 
|  | for _, op := range ops { | 
|  | ret := reflect.New(reflect.TypeOf(op.expected)) | 
|  | err = db.QueryRow(op.query).Scan(ret.Interface()) | 
|  | if err != nil { | 
|  | t.Errorf("Query %q failed: %s", op.query, err) | 
|  | } else if !reflect.DeepEqual(ret.Elem().Interface(), op.expected) { | 
|  | t.Errorf("Query %q returned wrong value: got %v (%T), want %v (%T)", op.query, ret.Elem().Interface(), ret.Elem().Interface(), op.expected, op.expected) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | type sumAggregator int64 | 
|  |  | 
|  | func (s *sumAggregator) Step(x int64) { | 
|  | *s += sumAggregator(x) | 
|  | } | 
|  |  | 
|  | func (s *sumAggregator) Done() int64 { | 
|  | return int64(*s) | 
|  | } | 
|  |  | 
|  | func TestAggregatorRegistration(t *testing.T) { | 
|  | customSum := func() *sumAggregator { | 
|  | var ret sumAggregator | 
|  | return &ret | 
|  | } | 
|  |  | 
|  | sql.Register("sqlite3_AggregatorRegistration", &SQLiteDriver{ | 
|  | ConnectHook: func(conn *SQLiteConn) error { | 
|  | if err := conn.RegisterAggregator("customSum", customSum, true); err != nil { | 
|  | return err | 
|  | } | 
|  | return nil | 
|  | }, | 
|  | }) | 
|  | db, err := sql.Open("sqlite3_AggregatorRegistration", ":memory:") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | _, err = db.Exec("create table foo (department integer, profits integer)") | 
|  | if err != nil { | 
|  | // trace feature is not implemented | 
|  | t.Skip("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | _, err = db.Exec("insert into foo values (1, 10), (1, 20), (2, 42)") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert records:", err) | 
|  | } | 
|  |  | 
|  | tests := []struct { | 
|  | dept, sum int64 | 
|  | }{ | 
|  | {1, 30}, | 
|  | {2, 42}, | 
|  | } | 
|  |  | 
|  | for _, test := range tests { | 
|  | var ret int64 | 
|  | err = db.QueryRow("select customSum(profits) from foo where department = $1 group by department", test.dept).Scan(&ret) | 
|  | if err != nil { | 
|  | t.Fatal("Query failed:", err) | 
|  | } | 
|  | if ret != test.sum { | 
|  | t.Fatalf("Custom sum returned wrong value, got %d, want %d", ret, test.sum) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestDeclTypes(t *testing.T) { | 
|  |  | 
|  | d := SQLiteDriver{} | 
|  |  | 
|  | conn, err := d.Open(":memory:") | 
|  | if err != nil { | 
|  | t.Fatal("Failed to begin transaction:", err) | 
|  | } | 
|  | defer conn.Close() | 
|  |  | 
|  | sqlite3conn := conn.(*SQLiteConn) | 
|  |  | 
|  | _, err = sqlite3conn.Exec("create table foo (id integer not null primary key, name text)", nil) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to create table:", err) | 
|  | } | 
|  |  | 
|  | _, err = sqlite3conn.Exec("insert into foo(name) values(\"bar\")", nil) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to insert:", err) | 
|  | } | 
|  |  | 
|  | rs, err := sqlite3conn.Query("select * from foo", nil) | 
|  | if err != nil { | 
|  | t.Fatal("Failed to select:", err) | 
|  | } | 
|  | defer rs.Close() | 
|  |  | 
|  | declTypes := rs.(*SQLiteRows).DeclTypes() | 
|  |  | 
|  | if !reflect.DeepEqual(declTypes, []string{"integer", "text"}) { | 
|  | t.Fatal("Unexpected declTypes:", declTypes) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPinger(t *testing.T) { | 
|  | db, err := sql.Open("sqlite3", ":memory:") | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | err = db.Ping() | 
|  | if err != nil { | 
|  | t.Fatal(err) | 
|  | } | 
|  | db.Close() | 
|  | err = db.Ping() | 
|  | if err == nil { | 
|  | t.Fatal("Should be closed") | 
|  | } | 
|  | } | 
|  |  | 
|  | var customFunctionOnce sync.Once | 
|  |  | 
|  | func BenchmarkCustomFunctions(b *testing.B) { | 
|  | customFunctionOnce.Do(func() { | 
|  | custom_add := func(a, b int64) int64 { | 
|  | return a + b | 
|  | } | 
|  |  | 
|  | sql.Register("sqlite3_BenchmarkCustomFunctions", &SQLiteDriver{ | 
|  | ConnectHook: func(conn *SQLiteConn) error { | 
|  | // Impure function to force sqlite to reexecute it each time. | 
|  | if err := conn.RegisterFunc("custom_add", custom_add, false); err != nil { | 
|  | return err | 
|  | } | 
|  | return nil | 
|  | }, | 
|  | }) | 
|  | }) | 
|  |  | 
|  | db, err := sql.Open("sqlite3_BenchmarkCustomFunctions", ":memory:") | 
|  | if err != nil { | 
|  | b.Fatal("Failed to open database:", err) | 
|  | } | 
|  | defer db.Close() | 
|  |  | 
|  | b.ResetTimer() | 
|  | for i := 0; i < b.N; i++ { | 
|  | var i int64 | 
|  | err = db.QueryRow("SELECT custom_add(1,2)").Scan(&i) | 
|  | if err != nil { | 
|  | b.Fatal("Failed to run custom add:", err) | 
|  | } | 
|  | } | 
|  | } |