@rocicorp/zero-sqlite3 1.0.9 → 1.0.10

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