From b97fe9d6f468d4663f142db7c3997289559d95bc Mon Sep 17 00:00:00 2001 From: TheDorkKnight Date: Sun, 8 Mar 2015 13:16:50 -0700 Subject: [PATCH 1/2] Expose column decltypes from SQLiteRows iterator Many applications that are built upon or extended from SQLite are interested in knowing the declared type of columns. By exposing access to the decltypes of a query result (the SQLiteRows struct), we can add useful capabilities for those who wish to use the SQLite driver directly. These types are already being collected and stored in the SQLiteRows struct, but they are current inaccessible to callers outside of the go-sqlite3 package. Furthermore, decltypes are part of the public sqlite3 API and are not going away anytime soon, so exposing them adds very little risk for maintainability of go-sqlite3. For those who instead use the database/sql abstraction, the DeclTypes() method would remain unexposed, which is ideal. --- sqlite3.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sqlite3.go b/sqlite3.go index 5f94a96a..077a3ac7 100644 --- a/sqlite3.go +++ b/sqlite3.go @@ -505,6 +505,17 @@ func (rc *SQLiteRows) Columns() []string { return rc.cols } +// Return column decltypes. +func (rc *SQLiteRows) DeclTypes() []string { + if rc.nc != len(rc.decltype) { + rc.decltype = make([]string, rc.nc) + for i := 0; i < rc.nc; i++ { + rc.decltype[i] = strings.ToLower(C.GoString(C.sqlite3_column_decltype(rc.s.s, C.int(i)))) + } + } + return rc.decltype +} + // Move cursor to next. func (rc *SQLiteRows) Next(dest []driver.Value) error { rv := C.sqlite3_step(rc.s.s) From 35cee30124b3e2c645478371ae6f1dbc388fbd50 Mon Sep 17 00:00:00 2001 From: Jake Molnar Date: Sun, 8 Mar 2015 20:06:53 -0700 Subject: [PATCH 2/2] Add test for rows.DeclTypes(), using ConnectHook Adds new file for tests using ConnectHook pattern (ie. Get pointer to SQLiteConn with ConnectHook on sql.Register, open with sql.Open, use methods from sqlite3 package that are not exposed in database/sql or database/sql/driver) --- sqlite_hook_test.go | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 sqlite_hook_test.go diff --git a/sqlite_hook_test.go b/sqlite_hook_test.go new file mode 100644 index 00000000..3111be30 --- /dev/null +++ b/sqlite_hook_test.go @@ -0,0 +1,58 @@ +package sqlite3 + +import ( + "database/sql" + "database/sql/driver" + "os" + "strings" + "testing" +) + +// Register ConnectHook, get *SQLiteRows from driver.Rows, +// test DeclTypes() method. +func TestDeclTypes(t *testing.T) { + var sqlite3conn *SQLiteConn = nil + sql.Register("sqlite3_with_hook_for_decltypes", + &SQLiteDriver{ + ConnectHook: func(conn *SQLiteConn) error { + sqlite3conn = conn + return nil + }, + }) + + tempFilename := TempFilename() + db, err := sql.Open("sqlite3_with_hook_for_decltypes", tempFilename) + if err != nil { + t.Fatal("Failed to open database:", err) + } + defer os.Remove(tempFilename) + defer db.Close() + + db.Exec("CREATE TABLE foo (id INTEGER, d DOUBLE, dt DATETIME, b BLOB);") + db.Exec("INSERT INTO foo VALUES(1, 0.5, '2015-03-05 15:16:17', x'900df00d');") + + if sqlite3conn == nil { + t.Fatal("Failed to hook into SQLiteConn") + } + rows, err := sqlite3conn.Query("SELECT * FROM foo", []driver.Value{}) + if err != nil { + t.Fatal("Unable to query foo table:", err) + } + defer rows.Close() + + var decltypes []string + switch rows.(type) { + case *SQLiteRows: decltypes = rows.(*SQLiteRows).DeclTypes() + default: t.Fatal("Failed to convert driver.Rows to *SQLiteRows") + } + + expectedDeclTypes := []string{"INTEGER", "DOUBLE", "DATETIME", "BLOB"} + if len(decltypes) != len(expectedDeclTypes) { + t.Errorf("Number of decltypes should be %d, not %d", len(expectedDeclTypes), len(decltypes)) + } + for i := 0; i < len(expectedDeclTypes); i++ { + if !strings.EqualFold(decltypes[i], expectedDeclTypes[i]) { + t.Errorf("decltype of column %d should be %s, not %s", i, expectedDeclTypes[i], decltypes[i]) + } + } +}