// Copyright (C) 2018 The Go-SQLite3 Authors. // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // +build cgo package sqlite3 import ( "bytes" "database/sql" "fmt" "os" "reflect" "testing" ) 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 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 { 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 { 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 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") } } 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 TestNilAndEmptyBytes(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatal(err) } defer db.Close() actualNil := []byte("use this to use an actual nil not a reference to nil") emptyBytes := []byte{} for tsti, tst := range []struct { name string columnType string insertBytes []byte expectedBytes []byte }{ {"actual nil blob", "blob", actualNil, nil}, {"referenced nil blob", "blob", nil, nil}, {"empty blob", "blob", emptyBytes, emptyBytes}, {"actual nil text", "text", actualNil, nil}, {"referenced nil text", "text", nil, nil}, {"empty text", "text", emptyBytes, emptyBytes}, } { if _, err = db.Exec(fmt.Sprintf("create table tbl%d (txt %s)", tsti, tst.columnType)); err != nil { t.Fatal(tst.name, err) } if bytes.Equal(tst.insertBytes, actualNil) { if _, err = db.Exec(fmt.Sprintf("insert into tbl%d (txt) values (?)", tsti), nil); err != nil { t.Fatal(tst.name, err) } } else { if _, err = db.Exec(fmt.Sprintf("insert into tbl%d (txt) values (?)", tsti), &tst.insertBytes); err != nil { t.Fatal(tst.name, err) } } rows, err := db.Query(fmt.Sprintf("select txt from tbl%d", tsti)) if err != nil { t.Fatal(tst.name, err) } if !rows.Next() { t.Fatal(tst.name, "no rows") } var scanBytes []byte if err = rows.Scan(&scanBytes); err != nil { t.Fatal(tst.name, err) } if err = rows.Err(); err != nil { t.Fatal(tst.name, err) } if tst.expectedBytes == nil && scanBytes != nil { t.Errorf("%s: %#v != %#v", tst.name, scanBytes, tst.expectedBytes) } else if !bytes.Equal(scanBytes, tst.expectedBytes) { t.Errorf("%s: %#v != %#v", tst.name, scanBytes, tst.expectedBytes) } } } func TestInsertNilByteSlice(t *testing.T) { db, err := sql.Open("sqlite3", ":memory:") if err != nil { t.Fatal(err) } defer db.Close() if _, err := db.Exec("create table blob_not_null (b blob not null)"); err != nil { t.Fatal(err) } var nilSlice []byte if _, err := db.Exec("insert into blob_not_null (b) values (?)", nilSlice); err == nil { t.Fatal("didn't expect INSERT to 'not null' column with a nil []byte slice to work") } zeroLenSlice := []byte{} if _, err := db.Exec("insert into blob_not_null (b) values (?)", zeroLenSlice); err != nil { t.Fatal("failed to insert zero-length slice") } }