forked from patrickxb/fgosqlite
-
Notifications
You must be signed in to change notification settings - Fork 12
/
intarray.go
150 lines (141 loc) · 4.56 KB
/
intarray.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
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;
extern int sqlite3_intarray_bind(sqlite3_intarray *pIntArray, int nElements, sqlite3_int64 *aElements, void (*xFree)(void*));
extern int sqlite3_intarray_create(sqlite3 *db, const char *zName, sqlite3_intarray **ppReturn);
*/
import "C"
import (
"errors"
"fmt"
"unsafe"
)
// IntArray is the Go-language interface definition for the "intarray" or
// integer array virtual table for SQLite.
//
// The intarray virtual table is designed to facilitate using an
// array of integers as the right-hand side of an IN operator. So
// instead of doing a prepared statement like this:
//
// SELECT * FROM table WHERE x IN (?,?,?,...,?);
//
// And then binding indivdual integers to each of ? slots, a Go-language
// application can create an intarray object (named "ex1" in the following
// example), prepare a statement like this:
//
// SELECT * FROM table WHERE x IN ex1;
//
// Then bind an ordinary Go slice of integer values to the ex1 object
// to run the statement.
//
// USAGE:
//
// One or more intarray objects can be created as follows:
//
// var p1, p2, p3 IntArray
// p1, err = db.CreateIntArray("ex1")
// p2, err = db.CreateIntArray("ex2")
// p3, err = db.CreateIntArray("ex3")
//
// Each call to CreateIntArray() generates a new virtual table
// module and a singleton of that virtual table module in the TEMP
// database. Both the module and the virtual table instance use the
// name given by the second parameter. The virtual tables can then be
// used in prepared statements:
//
// SELECT * FROM t1, t2, t3
// WHERE t1.x IN ex1
// AND t2.y IN ex2
// AND t3.z IN ex3;
//
// Each integer array is initially empty. New arrays can be bound to
// an integer array as follows:
//
// p1.Bind([]int64{ 1, 2, 3, 4 })
// p2.Bind([]int64{ 5, 6, 7, 8, 9, 10, 11 })
// a3 := make([]int64, 100)
// // Fill in content of a3
// p3.Bind(a3)
//
// A single intarray object can be rebound multiple times. But do not
// attempt to change the bindings of an intarray while it is in the middle
// of a query.
//
// The application must not change the intarray values while an intarray is in
// the middle of a query.
//
// The intarray object is automatically destroyed when its corresponding
// virtual table is dropped. Since the virtual tables are created in the
// TEMP database, they are automatically dropped when the database connection
// closes so the application does not normally need to take any special
// action to free the intarray objects (except if connections are pooled...).
type IntArray interface {
Bind(elements []int64)
Drop() error
}
type intArray struct {
c *Conn
ia *C.sqlite3_intarray
name string
content []int64
}
// CreateIntArray create a specific instance of an intarray object.
//
// Each intarray object corresponds to a virtual table in the TEMP database
// with the specified name.
//
// Destroy the intarray object by dropping the virtual table. If not done
// explicitly by the application, the virtual table will be dropped implicitly
// by the system when the database connection is closed.
func (c *Conn) CreateIntArray(name string) (IntArray, error) {
var ia *C.sqlite3_intarray
cname := C.CString(name)
rv := C.sqlite3_intarray_create(c.db, cname, &ia)
C.free(unsafe.Pointer(cname))
if rv != C.SQLITE_OK {
return nil, Errno(rv)
}
if ia == nil {
return nil, errors.New("sqlite succeeded without returning an intarray")
}
module := &intArray{c: c, ia: ia, name: name}
return module, nil
}
// Bind a new array of integers to a specific intarray object.
//
// The array of integers bound must be unchanged for the duration of
// any query against the corresponding virtual table. If the integer
// array does change or is deallocated undefined behavior will result.
func (m *intArray) Bind(elements []int64) {
if m.ia == nil {
return
}
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.
func (m *intArray) Drop() error {
if m == nil {
return errors.New("nil sqlite intarray")
}
if m.c == nil {
return nil
}
err := m.c.FastExec(fmt.Sprintf(`DROP TABLE temp."%s"`, escapeQuote(m.name)))
if err != nil {
return err
}
m.c = nil
m.ia = nil
return nil
}