Commit 3940be47 authored by gwenn's avatar gwenn

Replace Go intarray by original C.

parent 220759b9
...@@ -4,16 +4,28 @@ ...@@ -4,16 +4,28 @@
package sqlite package sqlite
/*
#include <sqlite3.h>
#include <stdlib.h>
// An sqlite3_intarray is an abstract type to stores an instance of an integer array.
typedef struct sqlite3_intarray sqlite3_intarray;
int sqlite3_intarray_bind(sqlite3_intarray *pIntArray, int nElements, sqlite3_int64 *aElements, void (*xFree)(void*));
int sqlite3_intarray_create(sqlite3 *db, const char *zName, sqlite3_intarray **ppReturn);
*/
import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
"unsafe"
) )
// IntArray is the Go-language interface definition for the "intarray" or // IntArray is the Go-language interface definition for the "intarray" or
// integer array virtual table for SQLite. // integer array virtual table for SQLite.
// //
// The intarray virtual table is designed to facilitate using an // The intarray virtual table is designed to facilitate using an
// aray of integers as the right-hand side of an IN operator. So // array of integers as the right-hand side of an IN operator. So
// instead of doing a prepared statement like this: // instead of doing a prepared statement like this:
// //
// SELECT * FROM table WHERE x IN (?,?,?,...,?); // SELECT * FROM table WHERE x IN (?,?,?,...,?);
...@@ -75,67 +87,11 @@ type IntArray interface { ...@@ -75,67 +87,11 @@ type IntArray interface {
type intArray struct { type intArray struct {
c *Conn c *Conn
ia *C.sqlite3_intarray
name string name string
content []int64 content []int64
} }
func (m *intArray) Create(c *Conn, args []string) (VTab, error) {
err := c.DeclareVTab("CREATE TABLE x(value INTEGER PRIMARY KEY)")
if err != nil {
return nil, err
}
return m, nil
}
func (m *intArray) Connect(c *Conn, args []string) (VTab, error) {
return m.Create(c, args)
}
func (m *intArray) DestroyModule() {
}
func (m *intArray) BestIndex() error {
return nil
}
func (m *intArray) Disconnect() error {
return nil
}
func (m *intArray) Destroy() error {
return nil
}
func (m *intArray) Open() (VTabCursor, error) {
return &intArrayVTabCursor{m, 0}, nil
}
type intArrayVTabCursor struct {
vTab *intArray
i int /* Current cursor position */
}
func (vc *intArrayVTabCursor) Close() error {
return nil
}
func (vc *intArrayVTabCursor) Filter() error {
vc.i = 0
return nil
}
func (vc *intArrayVTabCursor) Next() error {
vc.i++
return nil
}
func (vc *intArrayVTabCursor) EOF() bool {
return vc.i >= len(vc.vTab.content)
}
func (vc *intArrayVTabCursor) Column(c *Context, col int) error {
if col != 0 {
return fmt.Errorf("column index out of bounds: %d", col)
}
c.ResultInt64(vc.vTab.content[vc.i])
return nil
}
func (vc *intArrayVTabCursor) Rowid() (int64, error) {
return int64(vc.i), nil
}
// CreateIntArray create a specific instance of an intarray object. // CreateIntArray create a specific instance of an intarray object.
// //
// Each intarray object corresponds to a virtual table in the TEMP table // Each intarray object corresponds to a virtual table in the TEMP table
...@@ -145,14 +101,17 @@ func (vc *intArrayVTabCursor) Rowid() (int64, error) { ...@@ -145,14 +101,17 @@ func (vc *intArrayVTabCursor) Rowid() (int64, error) {
// explicitly by the application, the virtual table will be dropped implicitly // explicitly by the application, the virtual table will be dropped implicitly
// by the system when the database connection is closed. // by the system when the database connection is closed.
func (c *Conn) CreateIntArray(name string) (IntArray, error) { func (c *Conn) CreateIntArray(name string) (IntArray, error) {
module := &intArray{c: c, name: name} var ia *C.sqlite3_intarray
if err := c.CreateModule(name, module); err != nil { cname := C.CString(name)
return nil, err defer C.free(unsafe.Pointer(cname))
rv := C.sqlite3_intarray_create(c.db, cname, &ia)
if rv != C.SQLITE_OK {
return nil, Errno(rv)
} }
name = escapeQuote(name) if ia == nil {
if err := c.FastExec(fmt.Sprintf(`CREATE VIRTUAL TABLE temp."%s" USING "%s"`, name, name)); err != nil { return nil, errors.New("sqlite succeeded without returning an intarray")
return nil, err
} }
module := &intArray{c: c, ia: ia, name: name}
return module, nil return module, nil
} }
...@@ -162,7 +121,15 @@ func (c *Conn) CreateIntArray(name string) (IntArray, error) { ...@@ -162,7 +121,15 @@ func (c *Conn) CreateIntArray(name string) (IntArray, error) {
// any query against the corresponding virtual table. If the integer // any query against the corresponding virtual table. If the integer
// array does change or is deallocated undefined behavior will result. // array does change or is deallocated undefined behavior will result.
func (m *intArray) Bind(elements []int64) { func (m *intArray) Bind(elements []int64) {
if m.ia == nil {
return
}
m.content = elements m.content = elements
var p *int64
if len(elements) > 0 {
p = &elements[0]
}
C.sqlite3_intarray_bind(m.ia, C.int(len(elements)), (*C.sqlite3_int64)(unsafe.Pointer(p)), nil)
} }
// Drop underlying virtual table. // Drop underlying virtual table.
...@@ -178,5 +145,6 @@ func (m *intArray) Drop() error { ...@@ -178,5 +145,6 @@ func (m *intArray) Drop() error {
return err return err
} }
m.c = nil m.c = nil
m.ia = nil
return nil return nil
} }
...@@ -78,6 +78,8 @@ func TestIntArrayModule(t *testing.T) { ...@@ -78,6 +78,8 @@ func TestIntArrayModule(t *testing.T) {
checkNoError(t, p3.Drop(), "%s") checkNoError(t, p3.Drop(), "%s")
} }
const INTARRAY_SIZE = 100
func BenchmarkNoIntArray(b *testing.B) { func BenchmarkNoIntArray(b *testing.B) {
b.StopTimer() b.StopTimer()
db, err := Open(":memory:") db, err := Open(":memory:")
...@@ -85,14 +87,14 @@ func BenchmarkNoIntArray(b *testing.B) { ...@@ -85,14 +87,14 @@ func BenchmarkNoIntArray(b *testing.B) {
defer db.Close() defer db.Close()
panicOnError(b, db.Exec("CREATE TABLE rand (r INT)")) panicOnError(b, db.Exec("CREATE TABLE rand (r INT)"))
values := rand.Perm(1000) values := rand.Perm(INTARRAY_SIZE)
for _, v := range values { for _, v := range values {
panicOnError(b, db.Exec("INSERT INTO rand (r) VALUES (?)", v)) panicOnError(b, db.Exec("INSERT INTO rand (r) VALUES (?)", v))
} }
b.StartTimer() b.StartTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
l := rand.Intn(999) + 1 // at least one value l := rand.Intn(INTARRAY_SIZE-1) + 1 // at least one value
sql := fmt.Sprintf("SELECT * FROM rand WHERE r IN (?%s)", strings.Repeat(", ?", l-1)) sql := fmt.Sprintf("SELECT * FROM rand WHERE r IN (?%s)", strings.Repeat(", ?", l-1))
s, err := db.Prepare(sql) s, err := db.Prepare(sql)
panicOnError(b, err) panicOnError(b, err)
...@@ -120,7 +122,7 @@ func BenchmarkIntArray(b *testing.B) { ...@@ -120,7 +122,7 @@ func BenchmarkIntArray(b *testing.B) {
defer db.Close() defer db.Close()
panicOnError(b, db.Exec("CREATE TABLE rand (r INT)")) panicOnError(b, db.Exec("CREATE TABLE rand (r INT)"))
perms := rand.Perm(1000) perms := rand.Perm(INTARRAY_SIZE)
values := make([]int64, len(perms)) values := make([]int64, len(perms))
for i, v := range perms { for i, v := range perms {
panicOnError(b, db.Exec("INSERT INTO rand (r) VALUES (?)", v)) panicOnError(b, db.Exec("INSERT INTO rand (r) VALUES (?)", v))
...@@ -137,7 +139,7 @@ func BenchmarkIntArray(b *testing.B) { ...@@ -137,7 +139,7 @@ func BenchmarkIntArray(b *testing.B) {
defer s.Finalize() defer s.Finalize()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
l := rand.Intn(999) + 1 // at least one value l := rand.Intn(INTARRAY_SIZE-1) + 1 // at least one value
p.Bind(values[0:l]) p.Bind(values[0:l])
nr := 0 nr := 0
err = s.Select(func(s *Stmt) error { err = s.Select(func(s *Stmt) error {
......
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