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
Meta:
Conn.Databases
Conn.Tables
Conn.Tables/Views/Indexes
Conn.Columns
Conn.ForeignKeys
Conn.Indexes/IndexColumns
Conn.TableIndexes/IndexColumns
Time:
JulianDay
......
......@@ -6,8 +6,9 @@ package sqlite_test
import (
"fmt"
"github.com/gwenn/gosqlite"
"sync"
"github.com/gwenn/gosqlite"
)
func check(err error) {
......@@ -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')")
check(err)
tables, err := db.Tables("")
tables, err := db.Tables("", false)
check(err)
fmt.Printf("%d tables\n", len(tables))
// Output: 2 tables
......
......@@ -56,14 +56,16 @@ func (c *Conn) Databases() (map[string]string, error) {
return databases, nil
}
// Tables returns tables (no view) from 'sqlite_master' and filters system tables out.
// TODO create Views method to return views...
func (c *Conn) Tables(dbName string) ([]string, error) {
// Tables returns tables (no view) from 'sqlite_master'/'sqlite_temp_master' and filters system tables out.
func (c *Conn) Tables(dbName string, temp bool) ([]string, error) {
var sql string
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 {
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)
if err != nil {
......@@ -82,6 +84,63 @@ func (c *Conn) Tables(dbName string) ([]string, error) {
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
// See Conn.Columns/IndexColumns
type Column struct {
......@@ -281,9 +340,9 @@ type Index struct {
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)
func (c *Conn) Indexes(dbName, table string) ([]Index, error) {
func (c *Conn) TableIndexes(dbName, table string) ([]Index, error) {
var pragma string
if len(dbName) == 0 {
pragma = Mprintf("PRAGMA index_list(%Q)", table)
......
......@@ -35,14 +35,47 @@ func TestTables(t *testing.T) {
db := open(t)
defer checkClose(db, t)
tables, err := db.Tables("")
tables, err := db.Tables("", false)
checkNoError(t, err, "error looking for tables: %s")
assert.Equal(t, 0, len(tables), "table count")
createTable(db, t)
tables, err = db.Tables("main")
tables, err = db.Tables("main", false)
checkNoError(t, err, "error looking for tables: %s")
assert.Equal(t, 1, len(tables), "table count")
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) {
......@@ -96,13 +129,13 @@ func TestForeignKeys(t *testing.T) {
}
}
func TestIndexes(t *testing.T) {
func TestTableIndexes(t *testing.T) {
db := open(t)
defer checkClose(db, t)
createTable(db, t)
createIndex(db, t)
indexes, err := db.Indexes("", "test")
indexes, err := db.TableIndexes("", "test")
checkNoError(t, err, "error listing indexes: %s")
if len(indexes) != 1 {
t.Fatalf("Expected one index <> %d", len(indexes))
......
......@@ -75,7 +75,7 @@ func (s *Stmt) specificError(msg string, a ...interface{}) error {
}
// CheckTypeMismatch enables type check in Scan methods (default true)
var CheckTypeMismatch bool = true
var CheckTypeMismatch = true
// SQL statement
// (See http://sqlite.org/c3ref/stmt.html)
......@@ -600,6 +600,16 @@ func (s *Stmt) SQL() string {
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.
// The leftmost column is number 0.
// Must scan all columns (but result is cached).
......@@ -1011,6 +1021,21 @@ func (s *Stmt) ScanBlob(index int) (value []byte, isNull bool) {
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.
// If time is persisted as string without timezone, UTC 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