pi-para 0.7.7 → 0.7.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/node_modules/better-sqlite3/LICENSE +21 -0
- package/node_modules/better-sqlite3/README.md +99 -0
- package/node_modules/better-sqlite3/binding.gyp +60 -0
- package/node_modules/better-sqlite3/deps/common.gypi +68 -0
- package/node_modules/better-sqlite3/deps/copy.js +31 -0
- package/node_modules/better-sqlite3/deps/defines.gypi +42 -0
- package/node_modules/better-sqlite3/deps/download.sh +123 -0
- package/node_modules/better-sqlite3/deps/patches/1208.patch +15 -0
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +269668 -0
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +14349 -0
- package/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +739 -0
- package/node_modules/better-sqlite3/deps/sqlite3.gyp +80 -0
- package/node_modules/better-sqlite3/deps/test_extension.c +21 -0
- package/node_modules/better-sqlite3/lib/binding.js +62 -0
- package/node_modules/better-sqlite3/lib/darwin-arm64.js +3 -0
- package/node_modules/better-sqlite3/lib/darwin-x64.js +3 -0
- package/node_modules/better-sqlite3/lib/database.js +109 -0
- package/node_modules/better-sqlite3/lib/index.js +3 -0
- package/node_modules/better-sqlite3/lib/linux-arm64.js +3 -0
- package/node_modules/better-sqlite3/lib/linux-x64.js +3 -0
- package/node_modules/better-sqlite3/lib/linuxmusl-arm64.js +4 -0
- package/node_modules/better-sqlite3/lib/linuxmusl-x64.js +4 -0
- package/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
- package/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
- package/node_modules/better-sqlite3/lib/methods/explain.js +9 -0
- package/node_modules/better-sqlite3/lib/methods/function.js +31 -0
- package/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
- package/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
- package/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
- package/node_modules/better-sqlite3/lib/methods/table.js +195 -0
- package/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
- package/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
- package/node_modules/better-sqlite3/lib/sqlite-error.js +25 -0
- package/node_modules/better-sqlite3/lib/util.js +12 -0
- package/node_modules/better-sqlite3/lib/win32-arm64.js +3 -0
- package/node_modules/better-sqlite3/lib/win32-x64.js +3 -0
- package/node_modules/better-sqlite3/package.json +67 -0
- package/node_modules/better-sqlite3/prebuilds/darwin-arm64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/darwin-x64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/linux-arm64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/linux-x64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/linuxmusl-arm64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/linuxmusl-x64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/win32-arm64.node +0 -0
- package/node_modules/better-sqlite3/prebuilds/win32-x64.node +0 -0
- package/node_modules/better-sqlite3/src/addon.cpp +59 -0
- package/node_modules/better-sqlite3/src/better_sqlite3.cpp +73 -0
- package/node_modules/better-sqlite3/src/objects/backup.cpp +121 -0
- package/node_modules/better-sqlite3/src/objects/backup.hpp +32 -0
- package/node_modules/better-sqlite3/src/objects/database.cpp +432 -0
- package/node_modules/better-sqlite3/src/objects/database.hpp +100 -0
- package/node_modules/better-sqlite3/src/objects/statement-iterator.cpp +119 -0
- package/node_modules/better-sqlite3/src/objects/statement-iterator.hpp +71 -0
- package/node_modules/better-sqlite3/src/objects/statement.cpp +380 -0
- package/node_modules/better-sqlite3/src/objects/statement.hpp +65 -0
- package/node_modules/better-sqlite3/src/util/bind-map.cpp +73 -0
- package/node_modules/better-sqlite3/src/util/binder.cpp +210 -0
- package/node_modules/better-sqlite3/src/util/constants.cpp +172 -0
- package/node_modules/better-sqlite3/src/util/custom-aggregate.cpp +121 -0
- package/node_modules/better-sqlite3/src/util/custom-function.cpp +59 -0
- package/node_modules/better-sqlite3/src/util/custom-table.cpp +406 -0
- package/node_modules/better-sqlite3/src/util/data-converter.cpp +17 -0
- package/node_modules/better-sqlite3/src/util/data.cpp +134 -0
- package/node_modules/better-sqlite3/src/util/helpers.cpp +190 -0
- package/node_modules/better-sqlite3/src/util/macros.cpp +74 -0
- package/node_modules/better-sqlite3/src/util/query-macros.cpp +71 -0
- package/node_modules/better-sqlite3/src/util/row-builder.cpp +53 -0
- package/node_modules/better-sqlite3/src/util/row-builder.hpp +22 -0
- package/node_modules/node-addon-api/LICENSE.md +9 -0
- package/node_modules/node-addon-api/README.md +95 -0
- package/node_modules/node-addon-api/common.gypi +21 -0
- package/node_modules/node-addon-api/except.gypi +25 -0
- package/node_modules/node-addon-api/index.js +14 -0
- package/node_modules/node-addon-api/napi-inl.deprecated.h +186 -0
- package/node_modules/node-addon-api/napi-inl.h +7165 -0
- package/node_modules/node-addon-api/napi.h +3364 -0
- package/node_modules/node-addon-api/node_addon_api.gyp +42 -0
- package/node_modules/node-addon-api/node_api.gyp +9 -0
- package/node_modules/node-addon-api/noexcept.gypi +26 -0
- package/node_modules/node-addon-api/nothing.c +0 -0
- package/node_modules/node-addon-api/package-support.json +21 -0
- package/node_modules/node-addon-api/package.json +480 -0
- package/node_modules/node-addon-api/tools/README.md +73 -0
- package/node_modules/node-addon-api/tools/check-napi.js +99 -0
- package/node_modules/node-addon-api/tools/clang-format.js +71 -0
- package/node_modules/node-addon-api/tools/conversion.js +301 -0
- package/package.json +6 -3
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
class CustomTable {
|
|
2
|
+
public:
|
|
3
|
+
|
|
4
|
+
explicit CustomTable(
|
|
5
|
+
Napi::Env env,
|
|
6
|
+
Database* db,
|
|
7
|
+
const char* name,
|
|
8
|
+
Napi::Function factory
|
|
9
|
+
) :
|
|
10
|
+
addon(db->GetAddon()),
|
|
11
|
+
env(env),
|
|
12
|
+
db(db),
|
|
13
|
+
name(name),
|
|
14
|
+
factory(Napi::Persistent(factory)) {}
|
|
15
|
+
|
|
16
|
+
static void Destructor(void* self) {
|
|
17
|
+
delete static_cast<CustomTable*>(self);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static sqlite3_module MODULE;
|
|
21
|
+
static sqlite3_module EPONYMOUS_MODULE;
|
|
22
|
+
|
|
23
|
+
private:
|
|
24
|
+
|
|
25
|
+
// This nested class is instantiated on each CREATE VIRTUAL TABLE statement.
|
|
26
|
+
class VTab { friend class CustomTable;
|
|
27
|
+
explicit VTab(
|
|
28
|
+
CustomTable* parent,
|
|
29
|
+
Napi::Function generator,
|
|
30
|
+
std::vector<std::string> parameter_names,
|
|
31
|
+
bool safe_ints
|
|
32
|
+
) :
|
|
33
|
+
parent(parent),
|
|
34
|
+
parameter_count(parameter_names.size()),
|
|
35
|
+
safe_ints(safe_ints),
|
|
36
|
+
generator(Napi::Persistent(generator)),
|
|
37
|
+
parameter_names(parameter_names) {
|
|
38
|
+
((void)base);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static inline CustomTable::VTab* Upcast(sqlite3_vtab* vtab) {
|
|
42
|
+
return reinterpret_cast<VTab*>(vtab);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
inline sqlite3_vtab* Downcast() {
|
|
46
|
+
return reinterpret_cast<sqlite3_vtab*>(this);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
sqlite3_vtab base;
|
|
50
|
+
CustomTable * const parent;
|
|
51
|
+
const int parameter_count;
|
|
52
|
+
const bool safe_ints;
|
|
53
|
+
const Napi::FunctionReference generator;
|
|
54
|
+
const std::vector<std::string> parameter_names;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// This nested class is instantiated each time a virtual table is scanned.
|
|
58
|
+
class Cursor { friend class CustomTable;
|
|
59
|
+
static inline CustomTable::Cursor* Upcast(sqlite3_vtab_cursor* cursor) {
|
|
60
|
+
return reinterpret_cast<Cursor*>(cursor);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
inline sqlite3_vtab_cursor* Downcast() {
|
|
64
|
+
return reinterpret_cast<sqlite3_vtab_cursor*>(this);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
inline CustomTable::VTab* GetVTab() {
|
|
68
|
+
return VTab::Upcast(base.pVtab);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
sqlite3_vtab_cursor base;
|
|
72
|
+
Napi::ObjectReference iterator;
|
|
73
|
+
Napi::FunctionReference next;
|
|
74
|
+
Napi::Reference<Napi::Array> row;
|
|
75
|
+
bool done;
|
|
76
|
+
sqlite_int64 rowid;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// This nested class is used by Data::ResultValueFromJS to report errors.
|
|
80
|
+
class TempDataConverter : DataConverter { friend class CustomTable;
|
|
81
|
+
explicit TempDataConverter(CustomTable* parent) :
|
|
82
|
+
parent(parent),
|
|
83
|
+
status(SQLITE_OK) {}
|
|
84
|
+
|
|
85
|
+
void PropagateJSError(sqlite3_context* invocation) {
|
|
86
|
+
status = SQLITE_ERROR;
|
|
87
|
+
parent->PropagateJSError();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
std::string GetDataErrorPrefix() {
|
|
91
|
+
return std::string("Virtual table module \"") + parent->name + "\" yielded";
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
CustomTable * const parent;
|
|
95
|
+
int status;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Although this function does nothing, we cannot use xConnect directly,
|
|
99
|
+
// because that would cause SQLite to register an eponymous virtual table.
|
|
100
|
+
static int xCreate(sqlite3* db_handle, void* _self, int argc, const char* const * argv, sqlite3_vtab** output, char** errOutput) {
|
|
101
|
+
return xConnect(db_handle, _self, argc, argv, output, errOutput);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// This method uses the factory function to instantiate a new virtual table.
|
|
105
|
+
static int xConnect(sqlite3* db_handle, void* _self, int argc, const char* const * argv, sqlite3_vtab** output, char** errOutput) {
|
|
106
|
+
CustomTable* self = static_cast<CustomTable*>(_self);
|
|
107
|
+
Napi::Env env = self->env;
|
|
108
|
+
Napi::HandleScope scope(env);
|
|
109
|
+
|
|
110
|
+
napi_value* args = ALLOC_ARRAY<napi_value>(argc);
|
|
111
|
+
for (int i = 0; i < argc; ++i) {
|
|
112
|
+
args[i] = StringFromUtf8(env, argv[i], -1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Run the factory function to receive a new virtual table definition.
|
|
116
|
+
Napi::Value returnValue = SafeCall(env, self->factory.Value(), env.Undefined(), argc, args);
|
|
117
|
+
delete[] args;
|
|
118
|
+
|
|
119
|
+
if (env.IsExceptionPending()) {
|
|
120
|
+
self->PropagateJSError();
|
|
121
|
+
return SQLITE_ERROR;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Extract each part of the virtual table definition.
|
|
125
|
+
Napi::Array array = returnValue.As<Napi::Array>();
|
|
126
|
+
Napi::String sqlString = array.Get((uint32_t)0).As<Napi::String>();
|
|
127
|
+
Napi::Function generator = array.Get((uint32_t)1).As<Napi::Function>();
|
|
128
|
+
Napi::Array parameterNames = array.Get((uint32_t)2).As<Napi::Array>();
|
|
129
|
+
int safe_ints = array.Get((uint32_t)3).As<Napi::Number>().Int32Value();
|
|
130
|
+
bool direct_only = array.Get((uint32_t)4).As<Napi::Boolean>().Value();
|
|
131
|
+
|
|
132
|
+
std::string sql = sqlString.Utf8Value();
|
|
133
|
+
safe_ints = safe_ints < 2 ? safe_ints : static_cast<int>(self->db->GetState()->safe_ints);
|
|
134
|
+
|
|
135
|
+
// Copy the parameter names into a std::vector.
|
|
136
|
+
std::vector<std::string> parameter_names;
|
|
137
|
+
for (int i = 0, len = parameterNames.Length(); i < len; ++i) {
|
|
138
|
+
Napi::String parameterName = parameterNames.Get((uint32_t)i).As<Napi::String>();
|
|
139
|
+
parameter_names.emplace_back(parameterName.Utf8Value());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Pass our SQL table definition to SQLite (this should never fail).
|
|
143
|
+
if (sqlite3_declare_vtab(db_handle, sql.c_str()) != SQLITE_OK) {
|
|
144
|
+
*errOutput = sqlite3_mprintf("failed to declare virtual table \"%s\"", argv[2]);
|
|
145
|
+
return SQLITE_ERROR;
|
|
146
|
+
}
|
|
147
|
+
if (direct_only && sqlite3_vtab_config(db_handle, SQLITE_VTAB_DIRECTONLY) != SQLITE_OK) {
|
|
148
|
+
*errOutput = sqlite3_mprintf("failed to configure virtual table \"%s\"", argv[2]);
|
|
149
|
+
return SQLITE_ERROR;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Return the successfully created virtual table.
|
|
153
|
+
*output = (new VTab(self, generator, parameter_names, safe_ints))->Downcast();
|
|
154
|
+
return SQLITE_OK;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static int xDisconnect(sqlite3_vtab* vtab) {
|
|
158
|
+
delete VTab::Upcast(vtab);
|
|
159
|
+
return SQLITE_OK;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static int xOpen(sqlite3_vtab* vtab, sqlite3_vtab_cursor** output) {
|
|
163
|
+
*output = (new Cursor())->Downcast();
|
|
164
|
+
return SQLITE_OK;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
static int xClose(sqlite3_vtab_cursor* cursor) {
|
|
168
|
+
delete Cursor::Upcast(cursor);
|
|
169
|
+
return SQLITE_OK;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// This method uses a fresh cursor to start a new scan of a virtual table.
|
|
173
|
+
// The args and idxNum are provided by xBestIndex (idxStr is unused).
|
|
174
|
+
// idxNum is a bitmap that provides the proper indices of the received args.
|
|
175
|
+
static int xFilter(sqlite3_vtab_cursor* _cursor, int idxNum, const char* idxStr, int argc, sqlite3_value** argv) {
|
|
176
|
+
Cursor* cursor = Cursor::Upcast(_cursor);
|
|
177
|
+
VTab* vtab = cursor->GetVTab();
|
|
178
|
+
CustomTable* self = vtab->parent;
|
|
179
|
+
Addon* addon = self->addon;
|
|
180
|
+
Napi::Env env = self->env;
|
|
181
|
+
Napi::HandleScope scope(env);
|
|
182
|
+
|
|
183
|
+
// Convert the SQLite arguments into JavaScript arguments. Note that
|
|
184
|
+
// the values in argv may be in the wrong order, so we fix that here.
|
|
185
|
+
napi_value args_fast[4];
|
|
186
|
+
napi_value* args = NULL;
|
|
187
|
+
int parameter_count = vtab->parameter_count;
|
|
188
|
+
if (parameter_count != 0) {
|
|
189
|
+
args = parameter_count <= 4 ? args_fast : ALLOC_ARRAY<napi_value>(parameter_count);
|
|
190
|
+
int argn = 0;
|
|
191
|
+
bool safe_ints = vtab->safe_ints;
|
|
192
|
+
for (int i = 0; i < parameter_count; ++i) {
|
|
193
|
+
if (idxNum & 1 << i) {
|
|
194
|
+
Napi::Value arg = Data::GetValueJS(env, argv[argn++], safe_ints);
|
|
195
|
+
args[i] = arg;
|
|
196
|
+
// If any arguments are NULL, the result set is necessarily
|
|
197
|
+
// empty, so don't bother to run the generator function.
|
|
198
|
+
if (arg.IsNull()) {
|
|
199
|
+
if (args != args_fast) delete[] args;
|
|
200
|
+
cursor->done = true;
|
|
201
|
+
return SQLITE_OK;
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
args[i] = env.Undefined();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Invoke the generator function to create a new iterator.
|
|
210
|
+
Napi::Value maybeIterator = SafeCall(env, vtab->generator.Value(), env.Undefined(), parameter_count, args);
|
|
211
|
+
if (args != args_fast) delete[] args;
|
|
212
|
+
|
|
213
|
+
if (env.IsExceptionPending()) {
|
|
214
|
+
self->PropagateJSError();
|
|
215
|
+
return SQLITE_ERROR;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Store the iterator and its next() method; we'll be using it a lot.
|
|
219
|
+
Napi::Object iterator = maybeIterator.As<Napi::Object>();
|
|
220
|
+
Napi::Function next = iterator.Get(addon->cs.next.Value()).As<Napi::Function>();
|
|
221
|
+
cursor->iterator.Reset(iterator, 1);
|
|
222
|
+
cursor->next.Reset(next, 1);
|
|
223
|
+
cursor->rowid = 0;
|
|
224
|
+
|
|
225
|
+
// Advance the iterator/cursor to the first row.
|
|
226
|
+
return xNext(cursor->Downcast());
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// This method advances a virtual table's cursor to the next row.
|
|
230
|
+
// SQLite will call this method repeatedly, driving the generator function.
|
|
231
|
+
static int xNext(sqlite3_vtab_cursor* _cursor) {
|
|
232
|
+
Cursor* cursor = Cursor::Upcast(_cursor);
|
|
233
|
+
CustomTable* self = cursor->GetVTab()->parent;
|
|
234
|
+
Addon* addon = self->addon;
|
|
235
|
+
Napi::Env env = self->env;
|
|
236
|
+
Napi::HandleScope scope(env);
|
|
237
|
+
|
|
238
|
+
Napi::Object iterator = cursor->iterator.Value();
|
|
239
|
+
Napi::Function next = cursor->next.Value();
|
|
240
|
+
|
|
241
|
+
Napi::Value maybeRecord = SafeCall(env, next, iterator, 0, NULL);
|
|
242
|
+
if (env.IsExceptionPending()) {
|
|
243
|
+
self->PropagateJSError();
|
|
244
|
+
return SQLITE_ERROR;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Napi::Object record = maybeRecord.As<Napi::Object>();
|
|
248
|
+
bool done = record.Get(addon->cs.done.Value()).As<Napi::Boolean>().Value();
|
|
249
|
+
if (!done) {
|
|
250
|
+
cursor->row.Reset(record.Get(addon->cs.value.Value()).As<Napi::Array>(), 1);
|
|
251
|
+
}
|
|
252
|
+
cursor->done = done;
|
|
253
|
+
cursor->rowid += 1;
|
|
254
|
+
|
|
255
|
+
return SQLITE_OK;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// If this method returns 1, SQLite will stop scanning the virtual table.
|
|
259
|
+
static int xEof(sqlite3_vtab_cursor* cursor) {
|
|
260
|
+
return Cursor::Upcast(cursor)->done;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// This method extracts some column from the cursor's current row.
|
|
264
|
+
static int xColumn(sqlite3_vtab_cursor* _cursor, sqlite3_context* invocation, int column) {
|
|
265
|
+
Cursor* cursor = Cursor::Upcast(_cursor);
|
|
266
|
+
CustomTable* self = cursor->GetVTab()->parent;
|
|
267
|
+
TempDataConverter temp_data_converter(self);
|
|
268
|
+
Napi::Env env = self->env;
|
|
269
|
+
Napi::HandleScope scope(env);
|
|
270
|
+
|
|
271
|
+
Napi::Array row = cursor->row.Value();
|
|
272
|
+
Napi::Value maybeColumnValue = SafeGetElement(env, row, (uint32_t)column);
|
|
273
|
+
if (maybeColumnValue.IsEmpty()) {
|
|
274
|
+
temp_data_converter.PropagateJSError(NULL);
|
|
275
|
+
} else {
|
|
276
|
+
Data::ResultValueFromJS(env, invocation, maybeColumnValue, &temp_data_converter);
|
|
277
|
+
}
|
|
278
|
+
return temp_data_converter.status;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// This method outputs the rowid of the cursor's current row.
|
|
282
|
+
static int xRowid(sqlite3_vtab_cursor* cursor, sqlite_int64* output) {
|
|
283
|
+
*output = Cursor::Upcast(cursor)->rowid;
|
|
284
|
+
return SQLITE_OK;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// This method tells SQLite how to *plan* queries on our virtual table.
|
|
288
|
+
// It gets invoked (typically multiple times) during db.prepare().
|
|
289
|
+
static int xBestIndex(sqlite3_vtab* vtab, sqlite3_index_info* output) {
|
|
290
|
+
int parameter_count = VTab::Upcast(vtab)->parameter_count;
|
|
291
|
+
int argument_count = 0;
|
|
292
|
+
std::vector<std::pair<int, int>> forwarded;
|
|
293
|
+
|
|
294
|
+
for (int i = 0, len = output->nConstraint; i < len; ++i) {
|
|
295
|
+
auto item = output->aConstraint[i];
|
|
296
|
+
|
|
297
|
+
// The SQLITE_INDEX_CONSTRAINT_LIMIT and SQLITE_INDEX_CONSTRAINT_OFFSET
|
|
298
|
+
// operators have no left-hand operand, and so for those operators the
|
|
299
|
+
// corresponding item.iColumn is meaningless.
|
|
300
|
+
// We don't care those constraints.
|
|
301
|
+
if (item.op == SQLITE_INDEX_CONSTRAINT_LIMIT || item.op == SQLITE_INDEX_CONSTRAINT_OFFSET) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
// We only care about constraints on parameters, not regular columns.
|
|
305
|
+
if (item.iColumn >= 0 && item.iColumn < parameter_count) {
|
|
306
|
+
if (item.op != SQLITE_INDEX_CONSTRAINT_EQ) {
|
|
307
|
+
sqlite3_free(vtab->zErrMsg);
|
|
308
|
+
vtab->zErrMsg = sqlite3_mprintf(
|
|
309
|
+
"virtual table parameter \"%s\" can only be constrained by the '=' operator",
|
|
310
|
+
VTab::Upcast(vtab)->parameter_names.at(item.iColumn).c_str());
|
|
311
|
+
return SQLITE_ERROR;
|
|
312
|
+
}
|
|
313
|
+
if (!item.usable) {
|
|
314
|
+
// Don't allow SQLite to make plans that ignore arguments.
|
|
315
|
+
// Otherwise, a user could pass arguments, but then they
|
|
316
|
+
// could appear undefined in the generator function.
|
|
317
|
+
return SQLITE_CONSTRAINT;
|
|
318
|
+
}
|
|
319
|
+
forwarded.emplace_back(item.iColumn, i);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Tell SQLite to forward arguments to xFilter.
|
|
324
|
+
std::sort(forwarded.begin(), forwarded.end());
|
|
325
|
+
for (std::pair<int, int> pair : forwarded) {
|
|
326
|
+
int bit = 1 << pair.first;
|
|
327
|
+
if (!(output->idxNum & bit)) {
|
|
328
|
+
output->idxNum |= bit;
|
|
329
|
+
output->aConstraintUsage[pair.second].argvIndex = ++argument_count;
|
|
330
|
+
output->aConstraintUsage[pair.second].omit = 1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Use a very high estimated cost so SQLite is not tempted to invoke the
|
|
335
|
+
// generator function within a loop, if it can be avoided.
|
|
336
|
+
output->estimatedCost = output->estimatedRows = 1000000000 / (argument_count + 1);
|
|
337
|
+
return SQLITE_OK;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
void PropagateJSError() {
|
|
341
|
+
assert(db->GetState()->was_js_error == false);
|
|
342
|
+
db->GetState()->was_js_error = true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
Addon* const addon;
|
|
346
|
+
const Napi::Env env;
|
|
347
|
+
Database* const db;
|
|
348
|
+
const std::string name;
|
|
349
|
+
const Napi::FunctionReference factory;
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
sqlite3_module CustomTable::MODULE = {
|
|
353
|
+
0, /* iVersion */
|
|
354
|
+
xCreate, /* xCreate */
|
|
355
|
+
xConnect, /* xConnect */
|
|
356
|
+
xBestIndex, /* xBestIndex */
|
|
357
|
+
xDisconnect, /* xDisconnect */
|
|
358
|
+
xDisconnect, /* xDestroy */
|
|
359
|
+
xOpen, /* xOpen */
|
|
360
|
+
xClose, /* xClose */
|
|
361
|
+
xFilter, /* xFilter */
|
|
362
|
+
xNext, /* xNext */
|
|
363
|
+
xEof, /* xEof */
|
|
364
|
+
xColumn, /* xColumn */
|
|
365
|
+
xRowid, /* xRowid */
|
|
366
|
+
NULL, /* xUpdate */
|
|
367
|
+
NULL, /* xBegin */
|
|
368
|
+
NULL, /* xSync */
|
|
369
|
+
NULL, /* xCommit */
|
|
370
|
+
NULL, /* xRollback */
|
|
371
|
+
NULL, /* xFindMethod */
|
|
372
|
+
NULL, /* xRename */
|
|
373
|
+
NULL, /* xSavepoint */
|
|
374
|
+
NULL, /* xRelease */
|
|
375
|
+
NULL, /* xRollbackTo */
|
|
376
|
+
NULL, /* xShadowName */
|
|
377
|
+
NULL /* xIntegrity */
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
sqlite3_module CustomTable::EPONYMOUS_MODULE = {
|
|
381
|
+
0, /* iVersion */
|
|
382
|
+
NULL, /* xCreate */
|
|
383
|
+
xConnect, /* xConnect */
|
|
384
|
+
xBestIndex, /* xBestIndex */
|
|
385
|
+
xDisconnect, /* xDisconnect */
|
|
386
|
+
xDisconnect, /* xDestroy */
|
|
387
|
+
xOpen, /* xOpen */
|
|
388
|
+
xClose, /* xClose */
|
|
389
|
+
xFilter, /* xFilter */
|
|
390
|
+
xNext, /* xNext */
|
|
391
|
+
xEof, /* xEof */
|
|
392
|
+
xColumn, /* xColumn */
|
|
393
|
+
xRowid, /* xRowid */
|
|
394
|
+
NULL, /* xUpdate */
|
|
395
|
+
NULL, /* xBegin */
|
|
396
|
+
NULL, /* xSync */
|
|
397
|
+
NULL, /* xCommit */
|
|
398
|
+
NULL, /* xRollback */
|
|
399
|
+
NULL, /* xFindMethod */
|
|
400
|
+
NULL, /* xRename */
|
|
401
|
+
NULL, /* xSavepoint */
|
|
402
|
+
NULL, /* xRelease */
|
|
403
|
+
NULL, /* xRollbackTo */
|
|
404
|
+
NULL, /* xShadowName */
|
|
405
|
+
NULL /* xIntegrity */
|
|
406
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class DataConverter {
|
|
2
|
+
public:
|
|
3
|
+
|
|
4
|
+
void ThrowDataConversionError(Napi::Env env, sqlite3_context* invocation, bool isBigInt) {
|
|
5
|
+
if (isBigInt) {
|
|
6
|
+
ThrowRangeError(env, (GetDataErrorPrefix() + " a bigint that was too big").c_str());
|
|
7
|
+
} else {
|
|
8
|
+
ThrowTypeError(env, (GetDataErrorPrefix() + " an invalid value").c_str());
|
|
9
|
+
}
|
|
10
|
+
PropagateJSError(invocation);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
protected:
|
|
14
|
+
|
|
15
|
+
virtual void PropagateJSError(sqlite3_context* invocation) = 0;
|
|
16
|
+
virtual std::string GetDataErrorPrefix() = 0;
|
|
17
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#define JS_VALUE_TO_SQLITE(to, value, env, ...) \
|
|
2
|
+
if (value.IsNumber()) { \
|
|
3
|
+
return sqlite3_##to##_double( \
|
|
4
|
+
__VA_ARGS__, \
|
|
5
|
+
value.As<Napi::Number>().DoubleValue() \
|
|
6
|
+
); \
|
|
7
|
+
} else if (value.IsBigInt()) { \
|
|
8
|
+
bool lossless; \
|
|
9
|
+
int64_t v = value.As<Napi::BigInt>().Int64Value(&lossless); \
|
|
10
|
+
if (lossless) { \
|
|
11
|
+
return sqlite3_##to##_int64(__VA_ARGS__, v); \
|
|
12
|
+
} \
|
|
13
|
+
} else if (value.IsString()) { \
|
|
14
|
+
std::string utf8 = value.As<Napi::String>().Utf8Value(); \
|
|
15
|
+
return sqlite3_##to##_text( \
|
|
16
|
+
__VA_ARGS__, \
|
|
17
|
+
utf8.c_str(), \
|
|
18
|
+
utf8.length(), \
|
|
19
|
+
SQLITE_TRANSIENT \
|
|
20
|
+
); \
|
|
21
|
+
} else if (value.IsBuffer()) { \
|
|
22
|
+
Napi::Buffer<char> buffer = value.As<Napi::Buffer<char>>(); \
|
|
23
|
+
const char* data = buffer.Data(); \
|
|
24
|
+
return sqlite3_##to##_blob( \
|
|
25
|
+
__VA_ARGS__, \
|
|
26
|
+
data ? data : "", \
|
|
27
|
+
buffer.Length(), \
|
|
28
|
+
SQLITE_TRANSIENT \
|
|
29
|
+
); \
|
|
30
|
+
} else if (value.IsNull() || value.IsUndefined()) { \
|
|
31
|
+
return sqlite3_##to##_null(__VA_ARGS__); \
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#define SQLITE_VALUE_TO_JS(from, env, safe_ints, ...) \
|
|
35
|
+
switch (sqlite3_##from##_type(__VA_ARGS__)) { \
|
|
36
|
+
case SQLITE_INTEGER: \
|
|
37
|
+
if (safe_ints) { \
|
|
38
|
+
return Napi::BigInt::New( \
|
|
39
|
+
env, \
|
|
40
|
+
(int64_t)sqlite3_##from##_int64(__VA_ARGS__) \
|
|
41
|
+
); \
|
|
42
|
+
} \
|
|
43
|
+
case SQLITE_FLOAT: \
|
|
44
|
+
return Napi::Number::New( \
|
|
45
|
+
env, \
|
|
46
|
+
sqlite3_##from##_double(__VA_ARGS__) \
|
|
47
|
+
); \
|
|
48
|
+
case SQLITE_TEXT: \
|
|
49
|
+
return StringFromUtf8( \
|
|
50
|
+
env, \
|
|
51
|
+
reinterpret_cast<const char*>(sqlite3_##from##_text(__VA_ARGS__)), \
|
|
52
|
+
sqlite3_##from##_bytes(__VA_ARGS__) \
|
|
53
|
+
); \
|
|
54
|
+
case SQLITE_BLOB: \
|
|
55
|
+
return Napi::Buffer<char>::Copy( \
|
|
56
|
+
env, \
|
|
57
|
+
static_cast<const char*>(sqlite3_##from##_blob(__VA_ARGS__)), \
|
|
58
|
+
sqlite3_##from##_bytes(__VA_ARGS__) \
|
|
59
|
+
); \
|
|
60
|
+
default: \
|
|
61
|
+
assert(sqlite3_##from##_type(__VA_ARGS__) == SQLITE_NULL); \
|
|
62
|
+
return env.Null(); \
|
|
63
|
+
} \
|
|
64
|
+
assert(false);
|
|
65
|
+
|
|
66
|
+
namespace Data {
|
|
67
|
+
|
|
68
|
+
static const char FLAT = 0;
|
|
69
|
+
static const char PLUCK = 1;
|
|
70
|
+
static const char EXPAND = 2;
|
|
71
|
+
static const char RAW = 3;
|
|
72
|
+
|
|
73
|
+
Napi::Value GetValueJS(Napi::Env env, sqlite3_stmt* handle, int column, bool safe_ints) {
|
|
74
|
+
SQLITE_VALUE_TO_JS(column, env, safe_ints, handle, column);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Napi::Value GetValueJS(Napi::Env env, sqlite3_value* value, bool safe_ints) {
|
|
78
|
+
SQLITE_VALUE_TO_JS(value, env, safe_ints, value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Napi::Value GetExpandedRowJS(Napi::Env env, sqlite3_stmt* handle, bool safe_ints) {
|
|
82
|
+
Napi::Object row = Napi::Object::New(env);
|
|
83
|
+
int column_count = sqlite3_column_count(handle);
|
|
84
|
+
for (int i = 0; i < column_count; ++i) {
|
|
85
|
+
const char* table_raw = sqlite3_column_table_name(handle, i);
|
|
86
|
+
Napi::String table = InternalizedFromUtf8(env, table_raw == NULL ? "$" : table_raw, -1);
|
|
87
|
+
Napi::String column = InternalizedFromUtf8(env, sqlite3_column_name(handle, i), -1);
|
|
88
|
+
Napi::Value value = Data::GetValueJS(env, handle, i, safe_ints);
|
|
89
|
+
if (row.HasOwnProperty(table)) {
|
|
90
|
+
row.Get(table).As<Napi::Object>().Set(column, value);
|
|
91
|
+
} else {
|
|
92
|
+
Napi::Object nested = Napi::Object::New(env);
|
|
93
|
+
row.Set(table, nested);
|
|
94
|
+
nested.Set(column, value);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return row;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Napi::Value GetFlatRowJS(Napi::Env env, Statement* stmt, sqlite3_stmt* handle, bool safe_ints) {
|
|
101
|
+
return stmt->GetRowBuilder().GetRowJS(env, handle, safe_ints);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
Napi::Value GetRawRowJS(Napi::Env env, Statement* stmt, sqlite3_stmt* handle, bool safe_ints) {
|
|
105
|
+
return stmt->GetRowBuilder().GetRawRowJS(env, handle, safe_ints);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
Napi::Value GetRowJS(Napi::Env env, Statement* stmt, sqlite3_stmt* handle, bool safe_ints, char mode) {
|
|
109
|
+
if (mode == Data::FLAT) return GetFlatRowJS(env, stmt, handle, safe_ints);
|
|
110
|
+
if (mode == PLUCK) return GetValueJS(env, handle, 0, safe_ints);
|
|
111
|
+
if (mode == EXPAND) return GetExpandedRowJS(env, handle, safe_ints);
|
|
112
|
+
if (mode == RAW) return GetRawRowJS(env, stmt, handle, safe_ints);
|
|
113
|
+
assert(false);
|
|
114
|
+
return Napi::Value();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
void GetArgumentsJS(Napi::Env env, napi_value* out, sqlite3_value** values, int argument_count, bool safe_ints) {
|
|
118
|
+
assert(argument_count > 0);
|
|
119
|
+
for (int i = 0; i < argument_count; ++i) {
|
|
120
|
+
out[i] = Data::GetValueJS(env, values[i], safe_ints);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
int BindValueFromJS(Napi::Env env, sqlite3_stmt* handle, int index, Napi::Value value) {
|
|
125
|
+
JS_VALUE_TO_SQLITE(bind, value, env, handle, index);
|
|
126
|
+
return value.IsBigInt() ? SQLITE_TOOBIG : -1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
void ResultValueFromJS(Napi::Env env, sqlite3_context* invocation, Napi::Value value, DataConverter* converter) {
|
|
130
|
+
JS_VALUE_TO_SQLITE(result, value, env, invocation);
|
|
131
|
+
converter->ThrowDataConversionError(env, invocation, value.IsBigInt());
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
}
|