Commit 5b36b980 authored by gwenn's avatar gwenn

Improve CSV virtual table module: column affinity

parent 961e365a
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"strconv"
"strings" "strings"
"github.com/gwenn/yacr" "github.com/gwenn/yacr"
...@@ -18,9 +19,6 @@ import ( ...@@ -18,9 +19,6 @@ import (
type csvModule struct { type csvModule struct {
} }
// TODO http://www.ch-werner.de/sqliteodbc/html/csvtable_8c.html make possible to specify the column/type name
// TODO https://github.com/karbarcca/SQLite.jl & infer
// args[0] => module name // args[0] => module name
// args[1] => db name // args[1] => db name
// args[2] => table name // args[2] => table name
...@@ -32,6 +30,9 @@ type csvModule struct { ...@@ -32,6 +30,9 @@ type csvModule struct {
// - contains NAMES ignoring case => use args[i+1], ... as column names (until _TYPES_) // - contains NAMES ignoring case => use args[i+1], ... as column names (until _TYPES_)
// - contains TYPES ignoring case => use args[I+1], ... as column types // - contains TYPES ignoring case => use args[I+1], ... as column types
// Beware, empty args are skipped (..., ,...), use '' empty SQL string instead (..., '', ...). // Beware, empty args are skipped (..., ,...), use '' empty SQL string instead (..., '', ...).
// Adapted from:
// - https://github.com/gwenn/sqlite-csv-ext
// - http://www.ch-werner.de/sqliteodbc/html/csvtable_8c.html
func (m csvModule) Create(c *Conn, args []string) (VTab, error) { func (m csvModule) Create(c *Conn, args []string) (VTab, error) {
if len(args) < 4 { if len(args) < 4 {
return nil, errors.New("no CSV file specified") return nil, errors.New("no CSV file specified")
...@@ -138,6 +139,16 @@ func (m csvModule) Create(c *Conn, args []string) (VTab, error) { ...@@ -138,6 +139,16 @@ func (m csvModule) Create(c *Conn, args []string) (VTab, error) {
if err = c.DeclareVTab(sql); err != nil { if err = c.DeclareVTab(sql); err != nil {
return nil, err return nil, err
} }
vTab.affinities = make([]Affinity, len(vTab.cols))
if len(types) > 0 {
for i, typ := range types {
if i >= len(vTab.affinities) {
break
}
vTab.affinities[i] = typeAffinity(typ)
}
}
return vTab, nil return vTab, nil
} }
func (m csvModule) Connect(c *Conn, args []string) (VTab, error) { func (m csvModule) Connect(c *Conn, args []string) (VTab, error) {
...@@ -154,6 +165,7 @@ type csvTab struct { ...@@ -154,6 +165,7 @@ type csvTab struct {
eof bool eof bool
offsetFirstRow int64 offsetFirstRow int64
cols []string cols []string
affinities []Affinity
maxLength int maxLength int
maxColumn int maxColumn int
...@@ -260,7 +272,19 @@ func (vc *csvTabCursor) Column(c *Context, col int) error { ...@@ -260,7 +272,19 @@ func (vc *csvTabCursor) Column(c *Context, col int) error {
c.ResultNull() c.ResultNull()
return nil return nil
} }
// TODO dynamic typing c.ResultInt64() affinity := vc.vTab.affinities[col]
if affinity == Integral || affinity == Numerical {
if i, err := strconv.ParseInt(cols[col], 10, 64); err == nil {
c.ResultInt64(i)
return nil
}
}
if affinity == Real || affinity == Numerical {
if f, err := strconv.ParseFloat(cols[col], 64); err == nil {
c.ResultDouble(f)
return nil
}
}
c.ResultText(cols[col]) c.ResultText(cols[col])
return nil return nil
} }
......
...@@ -21,7 +21,7 @@ func TestCsvModule(t *testing.T) { ...@@ -21,7 +21,7 @@ func TestCsvModule(t *testing.T) {
defer checkClose(db, t) defer checkClose(db, t)
err := LoadCsvModule(db) err := LoadCsvModule(db)
checkNoError(t, err, "couldn't create CSV module: %s") checkNoError(t, err, "couldn't create CSV module: %s")
err = db.Exec("CREATE VIRTUAL TABLE vtab USING csv('test.csv', USE_HEADER_ROW)") err = db.Exec("CREATE VIRTUAL TABLE vtab USING csv('test.csv', USE_HEADER_ROW, TYPES, INT, TEXT, TEXT)")
checkNoError(t, err, "couldn't create CSV virtual table: %s") checkNoError(t, err, "couldn't create CSV virtual table: %s")
s, err := db.Prepare("SELECT rowid, * FROM vtab ORDER BY rowid LIMIT 3 OFFSET 2") s, err := db.Prepare("SELECT rowid, * FROM vtab ORDER BY rowid LIMIT 3 OFFSET 2")
...@@ -30,6 +30,8 @@ func TestCsvModule(t *testing.T) { ...@@ -30,6 +30,8 @@ func TestCsvModule(t *testing.T) {
w, err := os.Open(os.DevNull) w, err := os.Open(os.DevNull)
checkNoError(t, err, "couldn't open /dev/null: %s") checkNoError(t, err, "couldn't open /dev/null: %s")
defer w.Close()
//w := os.Stderr
var i int var i int
var col1, col2, col3 string var col1, col2, col3 string
err = s.Select(func(s *Stmt) (err error) { err = s.Select(func(s *Stmt) (err error) {
...@@ -41,6 +43,15 @@ func TestCsvModule(t *testing.T) { ...@@ -41,6 +43,15 @@ func TestCsvModule(t *testing.T) {
}) })
checkNoError(t, err, "couldn't select from CSV virtual table: %s") checkNoError(t, err, "couldn't select from CSV virtual table: %s")
err = db.Select("SELECT typeof(colA), typeof(colB), typeof(colC) FROM vtab", func(s *Stmt) (err error) {
if err = s.Scan(&col1, &col2, &col3); err != nil {
return
}
fmt.Fprintf(w, "%s|%s|%s\n", col1, col2, col3)
return
})
checkNoError(t, err, "couldn't select from CSV virtual table: %s")
err = db.Exec("DROP TABLE vtab") err = db.Exec("DROP TABLE vtab")
checkNoError(t, err, "couldn't drop CSV virtual table: %s") checkNoError(t, err, "couldn't drop CSV virtual table: %s")
} }
......
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