@stoolap/node 0.4.1 → 0.5.0-beta.1
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/README.md +33 -6
- package/index.d.ts +38 -3
- package/index.js +3 -1
- package/lib/database.js +88 -63
- package/lib/ffi.js +13 -1
- package/lib/iterator.js +111 -0
- package/lib/statement.js +155 -107
- package/lib/transaction.js +95 -64
- package/package.json +6 -6
- package/src/stoolap.c +993 -1441
package/lib/statement.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const { processParams } = require("./ffi");
|
|
4
3
|
const {
|
|
5
4
|
parseRows,
|
|
6
5
|
parseRaw,
|
|
@@ -8,6 +7,15 @@ const {
|
|
|
8
7
|
makeRowFactory,
|
|
9
8
|
parseRowsCached,
|
|
10
9
|
} = require("./parse");
|
|
10
|
+
const { RowCursor } = require("./iterator");
|
|
11
|
+
|
|
12
|
+
function sameHeader(bytes, header) {
|
|
13
|
+
if (!header || bytes.length < header.length) return false;
|
|
14
|
+
for (let i = 0; i < header.length; i++) {
|
|
15
|
+
if (bytes[i] !== header[i]) return false;
|
|
16
|
+
}
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
11
19
|
|
|
12
20
|
/* Call stmtExecNums with direct numeric args (no array wrapping).
|
|
13
21
|
* Manual unpack for 1-3 params avoids spread overhead. */
|
|
@@ -84,8 +92,9 @@ let _pbuf = Buffer.allocUnsafe(16384);
|
|
|
84
92
|
function encodeBatch(paramsArray) {
|
|
85
93
|
const rowCount = paramsArray.length;
|
|
86
94
|
const ppr = paramsArray[0].length;
|
|
95
|
+
if (ppr > 65535) throw new RangeError("Too many parameters per row");
|
|
87
96
|
let buf = _pbuf;
|
|
88
|
-
const estSize =
|
|
97
|
+
const estSize = 6 + rowCount * ppr * 14;
|
|
89
98
|
if (estSize > buf.length) {
|
|
90
99
|
buf = Buffer.allocUnsafe(estSize + 4096);
|
|
91
100
|
_pbuf = buf;
|
|
@@ -93,7 +102,8 @@ function encodeBatch(paramsArray) {
|
|
|
93
102
|
let off = 0;
|
|
94
103
|
buf.writeUInt32LE(rowCount, off);
|
|
95
104
|
off += 4;
|
|
96
|
-
buf
|
|
105
|
+
buf.writeUInt16LE(ppr, off);
|
|
106
|
+
off += 2;
|
|
97
107
|
for (let r = 0; r < rowCount; r++) {
|
|
98
108
|
const row = paramsArray[r];
|
|
99
109
|
for (let j = 0; j < ppr; j++) {
|
|
@@ -134,8 +144,44 @@ function encodeBatch(paramsArray) {
|
|
|
134
144
|
buf[off++] = 5;
|
|
135
145
|
buf.writeBigInt64LE(v, off);
|
|
136
146
|
off += 8;
|
|
147
|
+
} else if (v instanceof Date) {
|
|
148
|
+
const ms = v.getTime();
|
|
149
|
+
if (ms !== ms) {
|
|
150
|
+
buf[off++] = 0;
|
|
151
|
+
} else {
|
|
152
|
+
buf[off++] = 6;
|
|
153
|
+
buf.writeBigInt64LE(BigInt(Math.trunc(ms)) * 1000000n, off);
|
|
154
|
+
off += 8;
|
|
155
|
+
}
|
|
156
|
+
} else if (v instanceof Float32Array || v instanceof Uint8Array) {
|
|
157
|
+
/* Float32Array binds as a vector; Buffer and other Uint8Array as text bytes */
|
|
158
|
+
const bytes =
|
|
159
|
+
v instanceof Float32Array
|
|
160
|
+
? new Uint8Array(v.buffer, v.byteOffset, v.byteLength)
|
|
161
|
+
: v;
|
|
162
|
+
const need = bytes.length + 6;
|
|
163
|
+
if (off + need > buf.length) {
|
|
164
|
+
const nb = Buffer.allocUnsafe(Math.max(buf.length * 2, off + need + 4096));
|
|
165
|
+
buf.copy(nb, 0, 0, off);
|
|
166
|
+
buf = nb;
|
|
167
|
+
_pbuf = nb;
|
|
168
|
+
}
|
|
169
|
+
if (v instanceof Float32Array) {
|
|
170
|
+
buf[off++] = 7;
|
|
171
|
+
buf.writeUInt32LE(bytes.length, off);
|
|
172
|
+
off += 4;
|
|
173
|
+
buf.set(bytes, off);
|
|
174
|
+
off += bytes.length;
|
|
175
|
+
} else {
|
|
176
|
+
buf[off++] = 3;
|
|
177
|
+
buf.writeUInt32LE(bytes.length, off);
|
|
178
|
+
off += 4;
|
|
179
|
+
buf.set(bytes, off);
|
|
180
|
+
off += bytes.length;
|
|
181
|
+
buf[off++] = 0;
|
|
182
|
+
}
|
|
137
183
|
} else if (typeof v === "object") {
|
|
138
|
-
const s =
|
|
184
|
+
const s = JSON.stringify(v);
|
|
139
185
|
const maxBytes = s.length * 3 + 6;
|
|
140
186
|
if (off + maxBytes > buf.length) {
|
|
141
187
|
const nb = Buffer.allocUnsafe(
|
|
@@ -158,7 +204,8 @@ function encodeBatch(paramsArray) {
|
|
|
158
204
|
}
|
|
159
205
|
|
|
160
206
|
class PreparedStatement {
|
|
161
|
-
#ptr = null;
|
|
207
|
+
#ptr = null; /* null once finalized or finalize is pending */
|
|
208
|
+
#native = null; /* the handle, until it is actually released */
|
|
162
209
|
#stoolap = null;
|
|
163
210
|
#q1i = null;
|
|
164
211
|
#factory = null;
|
|
@@ -166,24 +213,18 @@ class PreparedStatement {
|
|
|
166
213
|
#colCount = 0;
|
|
167
214
|
#owner = null;
|
|
168
215
|
#sql = "";
|
|
169
|
-
#preparedSql = "";
|
|
170
216
|
#paramNames = null;
|
|
217
|
+
#header = null;
|
|
218
|
+
#pending = 0;
|
|
219
|
+
#finalizeWanted = false;
|
|
171
220
|
|
|
172
|
-
constructor(
|
|
173
|
-
ptr,
|
|
174
|
-
stoolap,
|
|
175
|
-
queryOneIntBound,
|
|
176
|
-
owner,
|
|
177
|
-
sql,
|
|
178
|
-
preparedSql,
|
|
179
|
-
paramNames,
|
|
180
|
-
) {
|
|
221
|
+
constructor(ptr, stoolap, queryOneIntBound, owner, sql, paramNames) {
|
|
181
222
|
this.#ptr = ptr;
|
|
223
|
+
this.#native = ptr;
|
|
182
224
|
this.#stoolap = stoolap;
|
|
183
225
|
this.#q1i = queryOneIntBound || null;
|
|
184
226
|
this.#owner = owner;
|
|
185
227
|
this.#sql = sql;
|
|
186
|
-
this.#preparedSql = preparedSql || sql;
|
|
187
228
|
this.#paramNames = paramNames || null;
|
|
188
229
|
}
|
|
189
230
|
|
|
@@ -191,48 +232,49 @@ class PreparedStatement {
|
|
|
191
232
|
if (!this.#ptr) throw new Error("PreparedStatement is finalized");
|
|
192
233
|
}
|
|
193
234
|
|
|
235
|
+
/* Parse a result buffer with the row factory cached from the first result,
|
|
236
|
+
* as long as the packed header still matches (a schema change after
|
|
237
|
+
* prepare gives the statement new columns). */
|
|
194
238
|
#parseResult(abuf) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
this.#headerOff,
|
|
199
|
-
this.#factory,
|
|
200
|
-
this.#colCount,
|
|
201
|
-
);
|
|
239
|
+
const bytes = new Uint8Array(abuf);
|
|
240
|
+
if (this.#factory && sameHeader(bytes, this.#header)) {
|
|
241
|
+
return parseRowsCached(abuf, this.#headerOff, this.#factory, this.#colCount);
|
|
202
242
|
}
|
|
203
243
|
const meta = parseHeader(abuf);
|
|
204
244
|
this.#headerOff = meta.headerOff;
|
|
205
245
|
this.#colCount = meta.colCount;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return parseRows(abuf);
|
|
246
|
+
this.#header = bytes.slice(0, meta.headerOff);
|
|
247
|
+
this.#factory = makeRowFactory(meta.colNames);
|
|
248
|
+
return this.#factory
|
|
249
|
+
? parseRowsCached(abuf, meta.headerOff, this.#factory, meta.colCount)
|
|
250
|
+
: parseRows(abuf);
|
|
212
251
|
}
|
|
213
252
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
};
|
|
253
|
+
/* Run an async native call; finalize() waits for the last one to land. */
|
|
254
|
+
async #inflight(promise) {
|
|
255
|
+
this.#pending += 1;
|
|
256
|
+
try {
|
|
257
|
+
return await promise;
|
|
258
|
+
} finally {
|
|
259
|
+
this.#pending -= 1;
|
|
260
|
+
if (this.#finalizeWanted && this.#pending === 0) this.#release();
|
|
223
261
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_hasPending() {
|
|
265
|
+
return this.#pending > 0;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Bind params positionally: an object's values are taken in the order of
|
|
269
|
+
* the :name placeholders the statement was prepared with. */
|
|
270
|
+
#positional(params) {
|
|
271
|
+
if (!params) return null;
|
|
272
|
+
if (Array.isArray(params)) return params.length === 0 ? null : params;
|
|
273
|
+
const names = this.#paramNames;
|
|
274
|
+
if (!names) return null;
|
|
275
|
+
const values = new Array(names.length);
|
|
276
|
+
for (let i = 0; i < names.length; i++) values[i] = params[names[i]];
|
|
277
|
+
return values;
|
|
236
278
|
}
|
|
237
279
|
|
|
238
280
|
get sql() {
|
|
@@ -244,47 +286,38 @@ class PreparedStatement {
|
|
|
244
286
|
|
|
245
287
|
execute(params) {
|
|
246
288
|
this.#checkOpen();
|
|
247
|
-
const
|
|
248
|
-
return this.#owner._stmtExecAsync(
|
|
249
|
-
this.#ptr,
|
|
250
|
-
normalized.values ? normalized.values : undefined,
|
|
251
|
-
);
|
|
289
|
+
const values = this.#positional(params);
|
|
290
|
+
return this.#inflight(this.#owner._stmtExecAsync(this.#ptr, values || undefined));
|
|
252
291
|
}
|
|
253
292
|
|
|
254
293
|
executeSync(params) {
|
|
255
294
|
this.#checkOpen();
|
|
256
|
-
const
|
|
257
|
-
if (!
|
|
258
|
-
if (
|
|
259
|
-
return callExecNums(this.#stoolap, this.#ptr,
|
|
295
|
+
const values = this.#positional(params);
|
|
296
|
+
if (!values) return this.#stoolap.stmtExec(this.#ptr);
|
|
297
|
+
if (values.length <= 16 && isAllNums(values)) {
|
|
298
|
+
return callExecNums(this.#stoolap, this.#ptr, values);
|
|
260
299
|
}
|
|
261
|
-
return this.#stoolap.stmtExec(this.#ptr,
|
|
300
|
+
return this.#stoolap.stmtExec(this.#ptr, values);
|
|
262
301
|
}
|
|
263
302
|
|
|
264
303
|
// -- Query --
|
|
265
304
|
|
|
266
305
|
async query(params) {
|
|
267
306
|
this.#checkOpen();
|
|
268
|
-
const
|
|
269
|
-
const buf = await this.#
|
|
270
|
-
this.#ptr,
|
|
271
|
-
normalized.values ? normalized.values : undefined,
|
|
307
|
+
const values = this.#positional(params);
|
|
308
|
+
const buf = await this.#inflight(
|
|
309
|
+
this.#owner._stmtQueryBufAsync(this.#ptr, values || undefined),
|
|
272
310
|
);
|
|
273
311
|
return this.#parseResult(buf);
|
|
274
312
|
}
|
|
275
313
|
|
|
276
314
|
querySync(params) {
|
|
277
315
|
this.#checkOpen();
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if (normalized.values.length <= 16 && isAllNums(normalized.values)) {
|
|
284
|
-
const result = callQueryNums(this.#stoolap, this.#ptr, normalized.values);
|
|
285
|
-
return Array.isArray(result) ? result : this.#parseResult(result);
|
|
286
|
-
}
|
|
287
|
-
const result = this.#stoolap.stmtQuery(this.#ptr, normalized.values);
|
|
316
|
+
const values = this.#positional(params);
|
|
317
|
+
const result =
|
|
318
|
+
values && values.length <= 16 && isAllNums(values)
|
|
319
|
+
? callQueryNums(this.#stoolap, this.#ptr, values)
|
|
320
|
+
: this.#stoolap.stmtQueryAll(this.#ptr, values, 0);
|
|
288
321
|
return Array.isArray(result) ? result : this.#parseResult(result);
|
|
289
322
|
}
|
|
290
323
|
|
|
@@ -297,39 +330,47 @@ class PreparedStatement {
|
|
|
297
330
|
|
|
298
331
|
queryOneSync(params) {
|
|
299
332
|
this.#checkOpen();
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
if (
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
333
|
+
const values = this.#positional(params);
|
|
334
|
+
let result;
|
|
335
|
+
if (!values) {
|
|
336
|
+
result = this.#stoolap.stmtQueryAll(this.#ptr, undefined, 1);
|
|
337
|
+
} else if (values.length === 1 && typeof values[0] === "number") {
|
|
338
|
+
result = this.#q1i(values[0]);
|
|
339
|
+
} else if (values.length <= 16 && isAllNums(values)) {
|
|
340
|
+
result = callQueryOneNums(this.#stoolap, this.#ptr, values);
|
|
341
|
+
} else {
|
|
342
|
+
result = this.#stoolap.stmtQueryAll(this.#ptr, values, 1);
|
|
307
343
|
}
|
|
308
|
-
if (
|
|
309
|
-
|
|
344
|
+
if (result instanceof ArrayBuffer) {
|
|
345
|
+
const rows = this.#parseResult(result);
|
|
346
|
+
return rows.length === 0 ? null : rows[0];
|
|
310
347
|
}
|
|
311
|
-
return
|
|
348
|
+
return result;
|
|
312
349
|
}
|
|
313
350
|
|
|
314
351
|
// -- QueryRaw --
|
|
315
352
|
|
|
316
353
|
async queryRaw(params) {
|
|
317
354
|
this.#checkOpen();
|
|
318
|
-
const
|
|
319
|
-
const buf = await this.#
|
|
320
|
-
this.#ptr,
|
|
321
|
-
normalized.values ? normalized.values : undefined,
|
|
355
|
+
const values = this.#positional(params);
|
|
356
|
+
const buf = await this.#inflight(
|
|
357
|
+
this.#owner._stmtQueryBufAsync(this.#ptr, values || undefined),
|
|
322
358
|
);
|
|
323
359
|
return parseRaw(buf);
|
|
324
360
|
}
|
|
325
361
|
|
|
326
362
|
queryRawSync(params) {
|
|
327
363
|
this.#checkOpen();
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
364
|
+
return parseRaw(this.#stoolap.stmtQueryAll(this.#ptr, this.#positional(params), 2));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// -- Iterate --
|
|
368
|
+
|
|
369
|
+
/** Open a cursor over the statement's rows; iterate with `for...of` or `for await...of`. */
|
|
370
|
+
iterate(params) {
|
|
371
|
+
this.#checkOpen();
|
|
372
|
+
const ptr = this.#stoolap.rowsOpenStmt(this.#ptr, this.#positional(params));
|
|
373
|
+
return new RowCursor(this.#stoolap, ptr, this.#owner);
|
|
333
374
|
}
|
|
334
375
|
|
|
335
376
|
// -- Batch --
|
|
@@ -337,34 +378,41 @@ class PreparedStatement {
|
|
|
337
378
|
executeBatchSync(paramsArray) {
|
|
338
379
|
this.#checkOpen();
|
|
339
380
|
if (paramsArray.length === 0) return { changes: 0 };
|
|
340
|
-
if (
|
|
381
|
+
if (Array.isArray(paramsArray[0])) {
|
|
341
382
|
const batchBuf = encodeBatch(paramsArray);
|
|
342
383
|
return this.#stoolap.stmtExecBatchBuf(this.#ptr, batchBuf);
|
|
343
384
|
}
|
|
344
385
|
let totalChanges = 0;
|
|
345
386
|
for (const params of paramsArray) {
|
|
346
|
-
|
|
347
|
-
const result = normalized.values
|
|
348
|
-
? this.#stoolap.stmtExec(this.#ptr, normalized.values)
|
|
349
|
-
: this.#stoolap.stmtExec(this.#ptr);
|
|
350
|
-
totalChanges += result.changes;
|
|
387
|
+
totalChanges += this.executeSync(params).changes;
|
|
351
388
|
}
|
|
352
389
|
return { changes: totalChanges };
|
|
353
390
|
}
|
|
354
391
|
|
|
355
|
-
|
|
356
|
-
if (this.#
|
|
357
|
-
this.#stoolap.stmtFinalize(this.#
|
|
358
|
-
this.#
|
|
392
|
+
#release() {
|
|
393
|
+
if (this.#native) {
|
|
394
|
+
this.#stoolap.stmtFinalize(this.#native);
|
|
395
|
+
this.#native = null;
|
|
359
396
|
}
|
|
397
|
+
this.#ptr = null;
|
|
398
|
+
this.#finalizeWanted = false;
|
|
399
|
+
if (this.#owner) this.#owner._untrackChild(this);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/* Called by the owner when it closes; it has already waited for async work. */
|
|
403
|
+
_invalidate() {
|
|
404
|
+
if (this.#pending > 0) this.#finalizeWanted = true;
|
|
405
|
+
else this.#release();
|
|
360
406
|
}
|
|
361
407
|
|
|
408
|
+
/** Release the statement. Async calls still in flight finish first. */
|
|
362
409
|
finalize() {
|
|
363
|
-
if (this.#
|
|
364
|
-
this.#
|
|
365
|
-
this.#ptr = null;
|
|
366
|
-
|
|
410
|
+
if (this.#pending > 0) {
|
|
411
|
+
this.#finalizeWanted = true;
|
|
412
|
+
this.#ptr = null; /* no new calls; the handle is freed when the last one lands */
|
|
413
|
+
return;
|
|
367
414
|
}
|
|
415
|
+
this.#release();
|
|
368
416
|
}
|
|
369
417
|
}
|
|
370
418
|
|
package/lib/transaction.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const { processParams } = require("./ffi");
|
|
3
|
+
const { processParams, splitNamed } = require("./ffi");
|
|
4
4
|
const { parseRows, parseRaw } = require("./parse");
|
|
5
|
+
const { RowCursor } = require("./iterator");
|
|
5
6
|
|
|
6
7
|
class Transaction {
|
|
7
8
|
#ptr = null;
|
|
@@ -11,6 +12,7 @@ class Transaction {
|
|
|
11
12
|
#tail = Promise.resolve();
|
|
12
13
|
#closingPromise = null;
|
|
13
14
|
#owner = null;
|
|
15
|
+
#cursors = new Set(); /* WeakRef<RowCursor> */
|
|
14
16
|
|
|
15
17
|
constructor(ptr, stoolap, owner, dbPtr) {
|
|
16
18
|
this.#ptr = ptr;
|
|
@@ -19,8 +21,66 @@ class Transaction {
|
|
|
19
21
|
this.#dbPtr = dbPtr || null;
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
/* Called by the owner when it closes, after async work has settled:
|
|
25
|
+
* an open transaction is rolled back so its handle is released. */
|
|
22
26
|
_invalidate() {
|
|
23
|
-
this.#
|
|
27
|
+
this.#closeCursors();
|
|
28
|
+
if (this.#ptr) {
|
|
29
|
+
const ptr = this.#ptr;
|
|
30
|
+
this.#ptr = null;
|
|
31
|
+
try {
|
|
32
|
+
this.#stoolap.txRollback(ptr);
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
_settle() {
|
|
38
|
+
return this.#closingPromise || this.#tail;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
_hasPending() {
|
|
42
|
+
return this.#pendingAsync > 0 || this.#closingPromise !== null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
_trackCursor(ref) {
|
|
46
|
+
this.#cursors.add(ref);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
_untrackCursor(ref) {
|
|
50
|
+
this.#cursors.delete(ref);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#closeCursors() {
|
|
54
|
+
for (const ref of this.#cursors) {
|
|
55
|
+
const cursor = ref.deref();
|
|
56
|
+
if (cursor) cursor._invalidate();
|
|
57
|
+
}
|
|
58
|
+
this.#cursors.clear();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_rowsChunk(rowsPtr, maxRows, buf) {
|
|
62
|
+
this.#checkSyncReady();
|
|
63
|
+
return this.#stoolap.rowsChunk(rowsPtr, maxRows, buf);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_rowsChunkAsync(rowsPtr, maxRows) {
|
|
67
|
+
return this.#enqueueAsync(() => this.#stoolap.rowsChunkAsync(rowsPtr, maxRows));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Open a cursor over the query's rows inside the transaction. Open
|
|
72
|
+
* cursors are closed by commit and rollback.
|
|
73
|
+
*/
|
|
74
|
+
iterate(sql, params) {
|
|
75
|
+
this.#checkSyncReady();
|
|
76
|
+
let ptr;
|
|
77
|
+
if (params && !Array.isArray(params)) {
|
|
78
|
+
const [names, values] = splitNamed(params);
|
|
79
|
+
ptr = this.#stoolap.rowsOpenNamed(this.#dbPtr, this.#ptr, sql, names, values);
|
|
80
|
+
} else {
|
|
81
|
+
ptr = this.#stoolap.rowsOpen(this.#dbPtr, this.#ptr, sql, params);
|
|
82
|
+
}
|
|
83
|
+
return new RowCursor(this.#stoolap, ptr, this);
|
|
24
84
|
}
|
|
25
85
|
|
|
26
86
|
#checkOpen() {
|
|
@@ -71,19 +131,13 @@ class Transaction {
|
|
|
71
131
|
|
|
72
132
|
executeSync(sql, params) {
|
|
73
133
|
this.#checkSyncReady();
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return this.#stoolap.txExec(this.#ptr, sql, params);
|
|
134
|
+
if (params && !Array.isArray(params)) {
|
|
135
|
+
const [names, values] = splitNamed(params);
|
|
136
|
+
return this.#stoolap.txExecNamed(this.#ptr, sql, names, values);
|
|
78
137
|
}
|
|
79
|
-
|
|
80
|
-
return p.values
|
|
81
|
-
? this.#stoolap.txExec(this.#ptr, p.sql, p.values)
|
|
82
|
-
: this.#stoolap.txExec(this.#ptr, sql);
|
|
138
|
+
return this.#stoolap.txExec(this.#ptr, sql, params);
|
|
83
139
|
}
|
|
84
140
|
|
|
85
|
-
// -- Query --
|
|
86
|
-
|
|
87
141
|
async query(sql, params) {
|
|
88
142
|
const buf = await this.#enqueueAsync((ptr) => {
|
|
89
143
|
if (!params) return this.#stoolap.txQueryBufAsync(ptr, sql);
|
|
@@ -99,27 +153,21 @@ class Transaction {
|
|
|
99
153
|
return parseRows(buf);
|
|
100
154
|
}
|
|
101
155
|
|
|
156
|
+
/* One FFI call inside the transaction. mode: 0 = rows, 1 = first row, 2 = raw buffer */
|
|
157
|
+
#queryAll(sql, params, mode) {
|
|
158
|
+
if (params && !Array.isArray(params)) {
|
|
159
|
+
const [names, values] = splitNamed(params);
|
|
160
|
+
return this.#stoolap.dbQueryNamedAll(this.#dbPtr, this.#ptr, sql, names, values, mode);
|
|
161
|
+
}
|
|
162
|
+
return this.#stoolap.dbQueryAll(this.#dbPtr, this.#ptr, sql, params, mode);
|
|
163
|
+
}
|
|
164
|
+
|
|
102
165
|
querySync(sql, params) {
|
|
103
166
|
this.#checkSyncReady();
|
|
104
|
-
|
|
105
|
-
if (!params) {
|
|
106
|
-
result = this.#stoolap.txQuery(this.#ptr, sql);
|
|
107
|
-
} else if (Array.isArray(params)) {
|
|
108
|
-
result =
|
|
109
|
-
params.length === 0
|
|
110
|
-
? this.#stoolap.txQuery(this.#ptr, sql)
|
|
111
|
-
: this.#stoolap.txQuery(this.#ptr, sql, params);
|
|
112
|
-
} else {
|
|
113
|
-
const p = processParams(sql, params);
|
|
114
|
-
result = p.values
|
|
115
|
-
? this.#stoolap.txQuery(this.#ptr, p.sql, p.values)
|
|
116
|
-
: this.#stoolap.txQuery(this.#ptr, sql);
|
|
117
|
-
}
|
|
167
|
+
const result = this.#queryAll(sql, params, 0);
|
|
118
168
|
return Array.isArray(result) ? result : parseRows(result);
|
|
119
169
|
}
|
|
120
170
|
|
|
121
|
-
// -- QueryOne --
|
|
122
|
-
|
|
123
171
|
async queryOne(sql, params) {
|
|
124
172
|
const rows = await this.query(sql, params);
|
|
125
173
|
return rows.length === 0 ? null : rows[0];
|
|
@@ -127,19 +175,14 @@ class Transaction {
|
|
|
127
175
|
|
|
128
176
|
queryOneSync(sql, params) {
|
|
129
177
|
this.#checkSyncReady();
|
|
130
|
-
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
return
|
|
178
|
+
const result = this.#queryAll(sql, params, 1);
|
|
179
|
+
if (result instanceof ArrayBuffer) {
|
|
180
|
+
const rows = parseRows(result);
|
|
181
|
+
return rows.length === 0 ? null : rows[0];
|
|
134
182
|
}
|
|
135
|
-
|
|
136
|
-
return p.values
|
|
137
|
-
? this.#stoolap.txQueryOne(this.#ptr, p.sql, p.values)
|
|
138
|
-
: this.#stoolap.txQueryOne(this.#ptr, sql);
|
|
183
|
+
return result;
|
|
139
184
|
}
|
|
140
185
|
|
|
141
|
-
// -- QueryRaw --
|
|
142
|
-
|
|
143
186
|
async queryRaw(sql, params) {
|
|
144
187
|
const buf = await this.#enqueueAsync((ptr) => {
|
|
145
188
|
if (!params) return this.#stoolap.txQueryBufAsync(ptr, sql);
|
|
@@ -157,25 +200,9 @@ class Transaction {
|
|
|
157
200
|
|
|
158
201
|
queryRawSync(sql, params) {
|
|
159
202
|
this.#checkSyncReady();
|
|
160
|
-
|
|
161
|
-
if (!params) {
|
|
162
|
-
buf = this.#stoolap.txQueryBuf(this.#ptr, sql);
|
|
163
|
-
} else if (Array.isArray(params)) {
|
|
164
|
-
buf =
|
|
165
|
-
params.length === 0
|
|
166
|
-
? this.#stoolap.txQueryBuf(this.#ptr, sql)
|
|
167
|
-
: this.#stoolap.txQueryBuf(this.#ptr, sql, params);
|
|
168
|
-
} else {
|
|
169
|
-
const p = processParams(sql, params);
|
|
170
|
-
buf = p.values
|
|
171
|
-
? this.#stoolap.txQueryBuf(this.#ptr, p.sql, p.values)
|
|
172
|
-
: this.#stoolap.txQueryBuf(this.#ptr, sql);
|
|
173
|
-
}
|
|
174
|
-
return parseRaw(buf);
|
|
203
|
+
return parseRaw(this.#queryAll(sql, params, 2));
|
|
175
204
|
}
|
|
176
205
|
|
|
177
|
-
// -- Batch --
|
|
178
|
-
|
|
179
206
|
executeBatchSync(sql, paramsArray) {
|
|
180
207
|
this.#checkSyncReady();
|
|
181
208
|
if (paramsArray.length > 0 && Array.isArray(paramsArray[0])) {
|
|
@@ -188,11 +215,7 @@ class Transaction {
|
|
|
188
215
|
}
|
|
189
216
|
let totalChanges = 0;
|
|
190
217
|
for (const params of paramsArray) {
|
|
191
|
-
|
|
192
|
-
const result = p.values
|
|
193
|
-
? this.#stoolap.txExec(this.#ptr, p.sql, p.values)
|
|
194
|
-
: this.#stoolap.txExec(this.#ptr, sql);
|
|
195
|
-
totalChanges += result.changes;
|
|
218
|
+
totalChanges += this.executeSync(sql, params).changes;
|
|
196
219
|
}
|
|
197
220
|
return { changes: totalChanges };
|
|
198
221
|
}
|
|
@@ -205,7 +228,10 @@ class Transaction {
|
|
|
205
228
|
|
|
206
229
|
const ptr = this.#ptr;
|
|
207
230
|
this.#closingPromise = this.#tail
|
|
208
|
-
.then(() =>
|
|
231
|
+
.then(() => {
|
|
232
|
+
this.#closeCursors();
|
|
233
|
+
return this.#stoolap.txCommitAsync(ptr);
|
|
234
|
+
})
|
|
209
235
|
.finally(() => {
|
|
210
236
|
this.#ptr = null;
|
|
211
237
|
this.#closingPromise = null;
|
|
@@ -218,10 +244,11 @@ class Transaction {
|
|
|
218
244
|
|
|
219
245
|
commitSync() {
|
|
220
246
|
this.#checkSyncReady();
|
|
247
|
+
this.#closeCursors();
|
|
221
248
|
const ptr = this.#ptr;
|
|
222
249
|
this.#ptr = null;
|
|
223
|
-
this.#stoolap.txCommit(ptr);
|
|
224
250
|
if (this.#owner) this.#owner._untrackChild(this);
|
|
251
|
+
this.#stoolap.txCommit(ptr);
|
|
225
252
|
}
|
|
226
253
|
|
|
227
254
|
async rollback() {
|
|
@@ -230,7 +257,10 @@ class Transaction {
|
|
|
230
257
|
|
|
231
258
|
const ptr = this.#ptr;
|
|
232
259
|
this.#closingPromise = this.#tail
|
|
233
|
-
.then(() =>
|
|
260
|
+
.then(() => {
|
|
261
|
+
this.#closeCursors();
|
|
262
|
+
return this.#stoolap.txRollbackAsync(ptr);
|
|
263
|
+
})
|
|
234
264
|
.finally(() => {
|
|
235
265
|
this.#ptr = null;
|
|
236
266
|
this.#closingPromise = null;
|
|
@@ -243,10 +273,11 @@ class Transaction {
|
|
|
243
273
|
|
|
244
274
|
rollbackSync() {
|
|
245
275
|
this.#checkSyncReady();
|
|
276
|
+
this.#closeCursors();
|
|
246
277
|
const ptr = this.#ptr;
|
|
247
278
|
this.#ptr = null;
|
|
248
|
-
this.#stoolap.txRollback(ptr);
|
|
249
279
|
if (this.#owner) this.#owner._untrackChild(this);
|
|
280
|
+
this.#stoolap.txRollback(ptr);
|
|
250
281
|
}
|
|
251
282
|
}
|
|
252
283
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stoolap/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0-beta.1",
|
|
4
4
|
"description": "High-performance Node.js driver for Stoolap embedded SQL database",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"bench": "node benchmark.mjs"
|
|
19
19
|
},
|
|
20
20
|
"optionalDependencies": {
|
|
21
|
-
"@stoolap/lib-darwin-arm64": "0.
|
|
22
|
-
"@stoolap/lib-darwin-x64": "0.
|
|
23
|
-
"@stoolap/lib-linux-arm64-gnu": "0.
|
|
24
|
-
"@stoolap/lib-linux-x64-gnu": "0.
|
|
25
|
-
"@stoolap/lib-win32-x64-msvc": "0.
|
|
21
|
+
"@stoolap/lib-darwin-arm64": "0.5.0-beta.1",
|
|
22
|
+
"@stoolap/lib-darwin-x64": "0.5.0-beta.1",
|
|
23
|
+
"@stoolap/lib-linux-arm64-gnu": "0.5.0-beta.1",
|
|
24
|
+
"@stoolap/lib-linux-x64-gnu": "0.5.0-beta.1",
|
|
25
|
+
"@stoolap/lib-win32-x64-msvc": "0.5.0-beta.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"better-sqlite3": "^12.6.2",
|