This commit introduces a new type 'ErrNo', implementing the error interface. Constants for all sqlite3 error codes are provided in the new source file "error.go".
426 lines
10 KiB
Go
426 lines
10 KiB
Go
package sqlite3
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"database/sql"
|
|
"encoding/hex"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TempFilename() string {
|
|
randBytes := make([]byte, 16)
|
|
rand.Read(randBytes)
|
|
return filepath.Join(os.TempDir(), "foo"+hex.EncodeToString(randBytes)+".db")
|
|
}
|
|
|
|
func TestOpen(t *testing.T) {
|
|
tempFilename := TempFilename()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("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 {
|
|
t.Fatal("Failed to create table:", err)
|
|
}
|
|
|
|
if stat, err := os.Stat(tempFilename); err != nil || stat.IsDir() {
|
|
t.Error("Failed to create ./foo.db")
|
|
}
|
|
}
|
|
|
|
func TestInsert(t *testing.T) {
|
|
tempFilename := TempFilename()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("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 {
|
|
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("Fetched %q; expected %q", 123, result)
|
|
}
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
tempFilename := TempFilename()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("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 {
|
|
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("Fetched %q; expected %q", 234, result)
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
tempFilename := TempFilename()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("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 {
|
|
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()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("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, 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 TestTimestamp(t *testing.T) {
|
|
tempFilename := TempFilename()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("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, 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)
|
|
tests := []struct {
|
|
value interface{}
|
|
expected time.Time
|
|
}{
|
|
{"nonsense", time.Time{}},
|
|
{"0000-00-00 00:00:00", time.Time{}},
|
|
{timestamp1, timestamp1},
|
|
{timestamp1.Unix(), timestamp1},
|
|
{timestamp1.In(time.FixedZone("TEST", -7*3600)), timestamp1},
|
|
{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},
|
|
{"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},
|
|
}
|
|
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 seen != len(tests) {
|
|
t.Errorf("Expected to see %d rows", len(tests))
|
|
}
|
|
}
|
|
|
|
func TestBoolean(t *testing.T) {
|
|
tempFilename := TempFilename()
|
|
db, err := sql.Open("sqlite3", tempFilename)
|
|
if err != nil {
|
|
t.Fatal("Failed to open database:", err)
|
|
}
|
|
|
|
defer os.Remove(tempFilename)
|
|
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")
|
|
}
|
|
}
|