From 3f05115cd7452a192683cd862cdb4b3969de88db Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Wed, 29 Nov 2023 00:46:27 +0000 Subject: [PATCH] Virtual table API. --- ext/array/array.go | 2 +- ext/csv/csv.go | 16 +++++++++------- ext/csv/schema.go | 2 ++ ext/csv/schema_test.go | 10 ++++++---- ext/lines/lines.go | 4 ++-- vtab.go | 2 +- vtab_test.go | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/ext/array/array.go b/ext/array/array.go index 0e48fd9..928aa6b 100644 --- a/ext/array/array.go +++ b/ext/array/array.go @@ -15,7 +15,7 @@ import ( // https://sqlite.org/carray.html func Register(db *sqlite3.Conn) { sqlite3.CreateModule[array](db, "array", nil, - func(db *sqlite3.Conn, arg ...string) (array, error) { + func(db *sqlite3.Conn, _, _, _ string, _ ...string) (array, error) { err := db.DeclareVtab(`CREATE TABLE x(value, array HIDDEN)`) return array{}, err }) diff --git a/ext/csv/csv.go b/ext/csv/csv.go index 48c2234..87d173d 100644 --- a/ext/csv/csv.go +++ b/ext/csv/csv.go @@ -28,7 +28,7 @@ func Register(db *sqlite3.Conn) { // RegisterOpen registers the CSV virtual table. // If a filename is specified, open is used to open the file. func RegisterOpen(db *sqlite3.Conn, open func(name string) (io.ReaderAt, error)) { - declare := func(db *sqlite3.Conn, arg ...string) (_ *table, err error) { + declare := func(db *sqlite3.Conn, _, _, _ string, arg ...string) (_ *table, err error) { var ( filename string data string @@ -40,7 +40,7 @@ func RegisterOpen(db *sqlite3.Conn, open func(name string) (io.ReaderAt, error)) done = map[string]struct{}{} ) - for _, arg := range arg[3:] { + for _, arg := range arg { key, val := getParam(arg) if _, ok := done[key]; ok { return nil, fmt.Errorf("csv: more than one %q parameter", key) @@ -93,11 +93,13 @@ func RegisterOpen(db *sqlite3.Conn, open func(name string) (io.ReaderAt, error)) } }() - if schema == "" && (header || columns < 0) { - csv := table.newReader() - row, err := csv.Read() - if err != nil { - return nil, err + if schema == "" { + var row []string + if header || columns < 0 { + row, err = table.newReader().Read() + if err != nil { + return nil, err + } } schema = getSchema(header, columns, row) } diff --git a/ext/csv/schema.go b/ext/csv/schema.go index 0c20545..2ca807c 100644 --- a/ext/csv/schema.go +++ b/ext/csv/schema.go @@ -23,12 +23,14 @@ func getSchema(header bool, columns int, row []string) string { str.WriteByte('c') str.WriteString(strconv.Itoa(i + 1)) } + str.WriteString(" TEXT") sep = "," } for i := len(row); i < columns; i++ { str.WriteString(sep) str.WriteByte('c') str.WriteString(strconv.Itoa(i + 1)) + str.WriteString(" TEXT") sep = "," } str.WriteByte(')') diff --git a/ext/csv/schema_test.go b/ext/csv/schema_test.go index fa2cfdc..eb1aba9 100644 --- a/ext/csv/schema_test.go +++ b/ext/csv/schema_test.go @@ -9,10 +9,12 @@ func Test_getSchema(t *testing.T) { row []string want string }{ - {true, 2, nil, `CREATE TABLE x(c1,c2)`}, - {false, 2, nil, `CREATE TABLE x(c1,c2)`}, - {true, 3, []string{"abc", ""}, `CREATE TABLE x("abc",c2,c3)`}, - {true, 1, []string{"abc", "def"}, `CREATE TABLE x("abc")`}, + {true, 2, nil, `CREATE TABLE x(c1 TEXT,c2 TEXT)`}, + {false, 2, nil, `CREATE TABLE x(c1 TEXT,c2 TEXT)`}, + {false, -1, []string{"abc", ""}, `CREATE TABLE x(c1 TEXT,c2 TEXT)`}, + {true, 3, []string{"abc", ""}, `CREATE TABLE x("abc" TEXT,c2 TEXT,c3 TEXT)`}, + {true, -1, []string{"abc", "def"}, `CREATE TABLE x("abc" TEXT,"def" TEXT)`}, + {true, 1, []string{"abc", "def"}, `CREATE TABLE x("abc" TEXT)`}, } for _, tt := range tests { t.Run(tt.want, func(t *testing.T) { diff --git a/ext/lines/lines.go b/ext/lines/lines.go index b598cf7..638715e 100644 --- a/ext/lines/lines.go +++ b/ext/lines/lines.go @@ -17,13 +17,13 @@ import ( // The lines_read virtual table reads from a file or an [io.ReaderAt]. func Register(db *sqlite3.Conn) { sqlite3.CreateModule[lines](db, "lines", nil, - func(db *sqlite3.Conn, arg ...string) (lines, error) { + func(db *sqlite3.Conn, _, _, _ string, _ ...string) (lines, error) { err := db.DeclareVtab(`CREATE TABLE x(line TEXT, data HIDDEN)`) db.VtabConfig(sqlite3.VTAB_INNOCUOUS) return false, err }) sqlite3.CreateModule[lines](db, "lines_read", nil, - func(db *sqlite3.Conn, arg ...string) (lines, error) { + func(db *sqlite3.Conn, _, _, _ string, _ ...string) (lines, error) { err := db.DeclareVtab(`CREATE TABLE x(line TEXT, data HIDDEN)`) db.VtabConfig(sqlite3.VTAB_DIRECTONLY) return true, err diff --git a/vtab.go b/vtab.go index 576b14c..4d452ef 100644 --- a/vtab.go +++ b/vtab.go @@ -103,7 +103,7 @@ func (c *Conn) VtabConfig(op VtabConfigOption, args ...any) error { } // VTabConstructor is a virtual table constructor function. -type VTabConstructor[T VTab] func(db *Conn, arg ...string) (T, error) +type VTabConstructor[T VTab] func(db *Conn, module, schema, table string, arg ...string) (T, error) type module[T VTab] [2]VTabConstructor[T] diff --git a/vtab_test.go b/vtab_test.go index 6cf0b24..cf4f5c9 100644 --- a/vtab_test.go +++ b/vtab_test.go @@ -16,7 +16,7 @@ func ExampleCreateModule() { defer db.Close() err = sqlite3.CreateModule[seriesTable](db, "generate_series", nil, - func(db *sqlite3.Conn, arg ...string) (seriesTable, error) { + func(db *sqlite3.Conn, module, schema, table string, arg ...string) (seriesTable, error) { err := db.DeclareVtab(`CREATE TABLE x(value, start HIDDEN, stop HIDDEN, step HIDDEN)`) return seriesTable{}, err })