Commit 64cf802e authored by gwenn's avatar gwenn

Add Conn.Views,Indexes and Stmt.Empty,Tail,ScanRawBytes methods.

parent d6af0c6f
...@@ -97,10 +97,10 @@ Conn.NewBlobReadWriter ...@@ -97,10 +97,10 @@ Conn.NewBlobReadWriter
Meta: Meta:
Conn.Databases Conn.Databases
Conn.Tables Conn.Tables/Views/Indexes
Conn.Columns Conn.Columns
Conn.ForeignKeys Conn.ForeignKeys
Conn.Indexes/IndexColumns Conn.TableIndexes/IndexColumns
Time: Time:
JulianDay JulianDay
......
...@@ -6,8 +6,9 @@ package sqlite_test ...@@ -6,8 +6,9 @@ package sqlite_test
import ( import (
"fmt" "fmt"
"github.com/gwenn/gosqlite"
"sync" "sync"
"github.com/gwenn/gosqlite"
) )
func check(err error) { func check(err error) {
...@@ -57,7 +58,7 @@ func ExampleConn_Exec() { ...@@ -57,7 +58,7 @@ func ExampleConn_Exec() {
err = db.Exec("CREATE TABLE test1 (content TEXT); CREATE TABLE test2 (content TEXT); INSERT INTO test1 VALUES ('DATA')") err = db.Exec("CREATE TABLE test1 (content TEXT); CREATE TABLE test2 (content TEXT); INSERT INTO test1 VALUES ('DATA')")
check(err) check(err)
tables, err := db.Tables("") tables, err := db.Tables("", false)
check(err) check(err)
fmt.Printf("%d tables\n", len(tables)) fmt.Printf("%d tables\n", len(tables))
// Output: 2 tables // Output: 2 tables
......
...@@ -56,14 +56,16 @@ func (c *Conn) Databases() (map[string]string, error) { ...@@ -56,14 +56,16 @@ func (c *Conn) Databases() (map[string]string, error) {
return databases, nil return databases, nil
} }
// Tables returns tables (no view) from 'sqlite_master' and filters system tables out. // Tables returns tables (no view) from 'sqlite_master'/'sqlite_temp_master' and filters system tables out.
// TODO create Views method to return views... func (c *Conn) Tables(dbName string, temp bool) ([]string, error) {
func (c *Conn) Tables(dbName string) ([]string, error) {
var sql string var sql string
if len(dbName) == 0 { if len(dbName) == 0 {
sql = "SELECT name FROM sqlite_master WHERE type IN ('table') AND name NOT LIKE 'sqlite_%' ORDER BY 1" sql = "SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%' ORDER BY 1"
} else { } else {
sql = Mprintf("SELECT name FROM %Q.sqlite_master WHERE type IN ('table') AND name NOT LIKE 'sqlite_%%' ORDER BY 1", dbName) sql = Mprintf("SELECT name FROM %Q.sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%%' ORDER BY 1", dbName)
}
if temp {
sql = strings.Replace(sql, "sqlite_master", "sqlite_temp_master", 1)
} }
s, err := c.prepare(sql) s, err := c.prepare(sql)
if err != nil { if err != nil {
...@@ -82,6 +84,63 @@ func (c *Conn) Tables(dbName string) ([]string, error) { ...@@ -82,6 +84,63 @@ func (c *Conn) Tables(dbName string) ([]string, error) {
return tables, nil return tables, nil
} }
// Views returns views from 'sqlite_master'/'sqlite_temp_master'.
func (c *Conn) Views(dbName string, temp bool) ([]string, error) {
var sql string
if len(dbName) == 0 {
sql = "SELECT name FROM sqlite_master WHERE type = 'view' ORDER BY 1"
} else {
sql = Mprintf("SELECT name FROM %Q.sqlite_master WHERE type = 'view' ORDER BY 1", dbName)
}
if temp {
sql = strings.Replace(sql, "sqlite_master", "sqlite_temp_master", 1)
}
s, err := c.prepare(sql)
if err != nil {
return nil, err
}
defer s.finalize()
var views = make([]string, 0, 20)
err = s.Select(func(s *Stmt) (err error) {
name, _ := s.ScanText(0)
views = append(views, name)
return
})
if err != nil {
return nil, err
}
return views, nil
}
// Indexes returns indexes from 'sqlite_master'/'sqlite_temp_master'.
func (c *Conn) Indexes(dbName string, temp bool) (map[string]string, error) {
var sql string
if len(dbName) == 0 {
sql = "SELECT name, tbl_name FROM sqlite_master WHERE type = 'index'"
} else {
sql = Mprintf("SELECT name, tbl_name FROM %Q.sqlite_master WHERE type = 'index'", dbName)
}
if temp {
sql = strings.Replace(sql, "sqlite_master", "sqlite_temp_master", 1)
}
s, err := c.prepare(sql)
if err != nil {
return nil, err
}
defer s.finalize()
var indexes = make(map[string]string)
var name, table string
err = s.Select(func(s *Stmt) (err error) {
s.Scan(&name, &table)
indexes[name] = table
return
})
if err != nil {
return nil, err
}
return indexes, nil
}
// Column is the description of one table's column // Column is the description of one table's column
// See Conn.Columns/IndexColumns // See Conn.Columns/IndexColumns
type Column struct { type Column struct {
...@@ -281,9 +340,9 @@ type Index struct { ...@@ -281,9 +340,9 @@ type Index struct {
Unique bool Unique bool
} }
// Indexes returns one description for each index associated with the given table. // TableIndexes returns one description for each index associated with the given table.
// (See http://www.sqlite.org/pragma.html#pragma_index_list) // (See http://www.sqlite.org/pragma.html#pragma_index_list)
func (c *Conn) Indexes(dbName, table string) ([]Index, error) { func (c *Conn) TableIndexes(dbName, table string) ([]Index, error) {
var pragma string var pragma string
if len(dbName) == 0 { if len(dbName) == 0 {
pragma = Mprintf("PRAGMA index_list(%Q)", table) pragma = Mprintf("PRAGMA index_list(%Q)", table)
......
...@@ -35,14 +35,47 @@ func TestTables(t *testing.T) { ...@@ -35,14 +35,47 @@ func TestTables(t *testing.T) {
db := open(t) db := open(t)
defer checkClose(db, t) defer checkClose(db, t)
tables, err := db.Tables("") tables, err := db.Tables("", false)
checkNoError(t, err, "error looking for tables: %s") checkNoError(t, err, "error looking for tables: %s")
assert.Equal(t, 0, len(tables), "table count") assert.Equal(t, 0, len(tables), "table count")
createTable(db, t) createTable(db, t)
tables, err = db.Tables("main") tables, err = db.Tables("main", false)
checkNoError(t, err, "error looking for tables: %s") checkNoError(t, err, "error looking for tables: %s")
assert.Equal(t, 1, len(tables), "table count") assert.Equal(t, 1, len(tables), "table count")
assert.Equal(t, "test", tables[0], "table name") assert.Equal(t, "test", tables[0], "table name")
tables, err = db.Tables("", true)
checkNoError(t, err, "error looking for tables: %s")
assert.Equal(t, 0, len(tables), "table count")
}
func TestViews(t *testing.T) {
db := open(t)
defer checkClose(db, t)
views, err := db.Views("", false)
checkNoError(t, err, "error looking for views: %s")
assert.Equal(t, 0, len(views), "table count")
err = db.FastExec("CREATE VIEW myview AS SELECT 1")
checkNoError(t, err, "error creating for views: %s")
views, err = db.Views("", false)
checkNoError(t, err, "error looking for views: %s")
assert.Equal(t, 1, len(views), "table count")
assert.Equal(t, "myview", views[0], "table name")
views, err = db.Views("", true)
checkNoError(t, err, "error looking for views: %s")
assert.Equal(t, 0, len(views), "table count")
}
func TestIndexes(t *testing.T) {
db := open(t)
defer checkClose(db, t)
createTable(db, t)
indexes, err := db.Indexes("", false)
checkNoError(t, err, "error looking for indexes: %s")
assert.Equal(t, 0, len(indexes), "table count")
} }
func TestColumns(t *testing.T) { func TestColumns(t *testing.T) {
...@@ -96,13 +129,13 @@ func TestForeignKeys(t *testing.T) { ...@@ -96,13 +129,13 @@ func TestForeignKeys(t *testing.T) {
} }
} }
func TestIndexes(t *testing.T) { func TestTableIndexes(t *testing.T) {
db := open(t) db := open(t)
defer checkClose(db, t) defer checkClose(db, t)
createTable(db, t) createTable(db, t)
createIndex(db, t) createIndex(db, t)
indexes, err := db.Indexes("", "test") indexes, err := db.TableIndexes("", "test")
checkNoError(t, err, "error listing indexes: %s") checkNoError(t, err, "error listing indexes: %s")
if len(indexes) != 1 { if len(indexes) != 1 {
t.Fatalf("Expected one index <> %d", len(indexes)) t.Fatalf("Expected one index <> %d", len(indexes))
......
...@@ -75,7 +75,7 @@ func (s *Stmt) specificError(msg string, a ...interface{}) error { ...@@ -75,7 +75,7 @@ func (s *Stmt) specificError(msg string, a ...interface{}) error {
} }
// CheckTypeMismatch enables type check in Scan methods (default true) // CheckTypeMismatch enables type check in Scan methods (default true)
var CheckTypeMismatch bool = true var CheckTypeMismatch = true
// SQL statement // SQL statement
// (See http://sqlite.org/c3ref/stmt.html) // (See http://sqlite.org/c3ref/stmt.html)
...@@ -600,6 +600,16 @@ func (s *Stmt) SQL() string { ...@@ -600,6 +600,16 @@ func (s *Stmt) SQL() string {
return s.sql return s.sql
} }
// Empty returns true when then input text contains no SQL (if the input is an empty string or a comment)
func (s *Stmt) Empty() bool {
return s.stmt == nil
}
// Tail returns the unused portion of the original SQL statement.
func (s *Stmt) Tail() string {
return s.tail
}
// ColumnIndex returns the column index in a result set for a given column name. // ColumnIndex returns the column index in a result set for a given column name.
// The leftmost column is number 0. // The leftmost column is number 0.
// Must scan all columns (but result is cached). // Must scan all columns (but result is cached).
...@@ -1011,6 +1021,21 @@ func (s *Stmt) ScanBlob(index int) (value []byte, isNull bool) { ...@@ -1011,6 +1021,21 @@ func (s *Stmt) ScanBlob(index int) (value []byte, isNull bool) {
return return
} }
// ScanRawBytes scans result value from a query without making any copy.
// The leftmost column/index is number 0.
// Returns true when column is null.
// (See sqlite3_column_blob: http://sqlite.org/c3ref/column_blob.html)
func (s *Stmt) ScanRawBytes(index int) (value sql.RawBytes, isNull bool) {
p := C.sqlite3_column_blob(s.stmt, C.int(index))
if p == nil {
isNull = true
} else {
n := C.sqlite3_column_bytes(s.stmt, C.int(index))
value = sql.RawBytes((*[1 << 30]byte)(unsafe.Pointer(p))[:n])
}
return
}
// ScanTime scans result value from a query. // ScanTime scans result value from a query.
// If time is persisted as string without timezone, UTC is used. // If time is persisted as string without timezone, UTC is used.
// If time is persisted as numeric, local is used. // If time is persisted as numeric, local is used.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment