@stoolap/node 0.3.3 → 0.3.5
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 +75 -39
- package/binding.gyp +31 -0
- package/index.d.ts +141 -128
- package/index.js +14 -584
- package/lib/database.js +367 -0
- package/lib/ffi.js +183 -0
- package/lib/parse.js +246 -0
- package/lib/statement.js +371 -0
- package/lib/transaction.js +248 -0
- package/package.json +16 -24
- package/src/stoolap.c +3322 -0
package/lib/parse.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Optimized pure-JS buffer parser for stoolap_rows_fetch_all binary format.
|
|
4
|
+
// Works on Node, Bun, Deno. No native dependencies.
|
|
5
|
+
//
|
|
6
|
+
// Key optimizations vs naive approach:
|
|
7
|
+
// - Module-level _off eliminates [value, offset] tuple allocations (~70K/query)
|
|
8
|
+
// - Single Buffer.from(abuf) per query on Node for fast string decoding
|
|
9
|
+
// - Buffer.toString('utf8', start, end) avoids per-cell Uint8Array views
|
|
10
|
+
|
|
11
|
+
const _hasBuffer = typeof Buffer !== "undefined";
|
|
12
|
+
const td = new TextDecoder();
|
|
13
|
+
|
|
14
|
+
let _off = 0;
|
|
15
|
+
|
|
16
|
+
function readVal(bytes, dv, nb) {
|
|
17
|
+
const tag = bytes[_off++];
|
|
18
|
+
switch (tag) {
|
|
19
|
+
case 0:
|
|
20
|
+
return null;
|
|
21
|
+
case 1: {
|
|
22
|
+
// INTEGER: read as two u32 to avoid BigInt
|
|
23
|
+
const lo = dv.getUint32(_off, true);
|
|
24
|
+
const hi = dv.getInt32(_off + 4, true);
|
|
25
|
+
_off += 8;
|
|
26
|
+
return hi * 0x100000000 + lo;
|
|
27
|
+
}
|
|
28
|
+
case 2: {
|
|
29
|
+
// FLOAT
|
|
30
|
+
const v = dv.getFloat64(_off, true);
|
|
31
|
+
_off += 8;
|
|
32
|
+
return v;
|
|
33
|
+
}
|
|
34
|
+
case 3: // TEXT
|
|
35
|
+
case 6: {
|
|
36
|
+
// JSON
|
|
37
|
+
const len = dv.getUint32(_off, true);
|
|
38
|
+
_off += 4;
|
|
39
|
+
const start = _off;
|
|
40
|
+
_off += len;
|
|
41
|
+
return nb
|
|
42
|
+
? nb.toString("utf8", start, start + len)
|
|
43
|
+
: td.decode(
|
|
44
|
+
new Uint8Array(bytes.buffer, bytes.byteOffset + start, len),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
case 4: // BOOLEAN
|
|
48
|
+
return bytes[_off++] !== 0;
|
|
49
|
+
case 5: {
|
|
50
|
+
// TIMESTAMP
|
|
51
|
+
const lo = dv.getUint32(_off, true);
|
|
52
|
+
const hi = dv.getInt32(_off + 4, true);
|
|
53
|
+
_off += 8;
|
|
54
|
+
return new Date((hi * 0x100000000 + lo) / 1_000_000);
|
|
55
|
+
}
|
|
56
|
+
case 7: {
|
|
57
|
+
// BLOB (vector)
|
|
58
|
+
const len = dv.getUint32(_off, true);
|
|
59
|
+
_off += 4;
|
|
60
|
+
if (len === 0) return new Float32Array(0);
|
|
61
|
+
const ab = new ArrayBuffer(len);
|
|
62
|
+
new Uint8Array(ab).set(bytes.subarray(_off, _off + len));
|
|
63
|
+
_off += len;
|
|
64
|
+
return new Float32Array(ab);
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function parseRows(abuf) {
|
|
72
|
+
if (!abuf) return [];
|
|
73
|
+
const bytes = new Uint8Array(abuf);
|
|
74
|
+
const dv = new DataView(abuf);
|
|
75
|
+
const nb = _hasBuffer ? Buffer.from(abuf) : null;
|
|
76
|
+
|
|
77
|
+
_off = 0;
|
|
78
|
+
const colCount = dv.getUint32(0, true);
|
|
79
|
+
_off = 4;
|
|
80
|
+
const colNames = new Array(colCount);
|
|
81
|
+
for (let i = 0; i < colCount; i++) {
|
|
82
|
+
const len = dv.getUint16(_off, true);
|
|
83
|
+
_off += 2;
|
|
84
|
+
colNames[i] = nb
|
|
85
|
+
? nb.toString("utf8", _off, _off + len)
|
|
86
|
+
: td.decode(new Uint8Array(abuf, _off, len));
|
|
87
|
+
_off += len;
|
|
88
|
+
}
|
|
89
|
+
const rowCount = dv.getUint32(_off, true);
|
|
90
|
+
_off += 4;
|
|
91
|
+
|
|
92
|
+
const results = new Array(rowCount);
|
|
93
|
+
for (let r = 0; r < rowCount; r++) {
|
|
94
|
+
const row = {};
|
|
95
|
+
for (let c = 0; c < colCount; c++) {
|
|
96
|
+
row[colNames[c]] = readVal(bytes, dv, nb);
|
|
97
|
+
}
|
|
98
|
+
results[r] = row;
|
|
99
|
+
}
|
|
100
|
+
return results;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function parseRaw(abuf) {
|
|
104
|
+
if (!abuf) return { columns: [], rows: [] };
|
|
105
|
+
const bytes = new Uint8Array(abuf);
|
|
106
|
+
const dv = new DataView(abuf);
|
|
107
|
+
const nb = _hasBuffer ? Buffer.from(abuf) : null;
|
|
108
|
+
|
|
109
|
+
_off = 0;
|
|
110
|
+
const colCount = dv.getUint32(0, true);
|
|
111
|
+
_off = 4;
|
|
112
|
+
const colNames = new Array(colCount);
|
|
113
|
+
for (let i = 0; i < colCount; i++) {
|
|
114
|
+
const len = dv.getUint16(_off, true);
|
|
115
|
+
_off += 2;
|
|
116
|
+
colNames[i] = nb
|
|
117
|
+
? nb.toString("utf8", _off, _off + len)
|
|
118
|
+
: td.decode(new Uint8Array(abuf, _off, len));
|
|
119
|
+
_off += len;
|
|
120
|
+
}
|
|
121
|
+
const rowCount = dv.getUint32(_off, true);
|
|
122
|
+
_off += 4;
|
|
123
|
+
|
|
124
|
+
const rows = new Array(rowCount);
|
|
125
|
+
for (let r = 0; r < rowCount; r++) {
|
|
126
|
+
const row = new Array(colCount);
|
|
127
|
+
for (let c = 0; c < colCount; c++) {
|
|
128
|
+
row[c] = readVal(bytes, dv, nb);
|
|
129
|
+
}
|
|
130
|
+
rows[r] = row;
|
|
131
|
+
}
|
|
132
|
+
return { columns: colNames, rows };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/* ---- Cached prepared-statement fast path ---- */
|
|
136
|
+
|
|
137
|
+
/* Parse just the header (column names) and return metadata for caching. */
|
|
138
|
+
function parseHeader(abuf) {
|
|
139
|
+
const dv = new DataView(abuf);
|
|
140
|
+
const nb = _hasBuffer ? Buffer.from(abuf) : null;
|
|
141
|
+
let off = 0;
|
|
142
|
+
const colCount = dv.getUint32(0, true);
|
|
143
|
+
off = 4;
|
|
144
|
+
const colNames = new Array(colCount);
|
|
145
|
+
for (let i = 0; i < colCount; i++) {
|
|
146
|
+
const len = dv.getUint16(off, true);
|
|
147
|
+
off += 2;
|
|
148
|
+
colNames[i] = nb
|
|
149
|
+
? nb.toString("utf8", off, off + len)
|
|
150
|
+
: td.decode(new Uint8Array(abuf, off, len));
|
|
151
|
+
off += len;
|
|
152
|
+
}
|
|
153
|
+
return { colNames, headerOff: off, colCount };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Build a compiled row-constructor for fast hidden class creation. */
|
|
157
|
+
function makeRowFactory(colNames) {
|
|
158
|
+
const n = colNames.length;
|
|
159
|
+
if (n === 0 || n > 16) return null;
|
|
160
|
+
try {
|
|
161
|
+
const params = new Array(n);
|
|
162
|
+
const props = new Array(n);
|
|
163
|
+
for (let i = 0; i < n; i++) {
|
|
164
|
+
params[i] = "v" + i;
|
|
165
|
+
props[i] = JSON.stringify(colNames[i]) + ":v" + i;
|
|
166
|
+
}
|
|
167
|
+
return new Function(params.join(","), "return {" + props.join(",") + "}");
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/* Parse rows using cached column info + row factory. Skips header re-parsing. */
|
|
174
|
+
function parseRowsCached(abuf, headerOff, factory, colCount) {
|
|
175
|
+
const bytes = new Uint8Array(abuf);
|
|
176
|
+
const dv = new DataView(abuf);
|
|
177
|
+
const nb = _hasBuffer ? Buffer.from(abuf) : null;
|
|
178
|
+
_off = headerOff;
|
|
179
|
+
const rowCount = dv.getUint32(_off, true);
|
|
180
|
+
_off += 4;
|
|
181
|
+
const results = new Array(rowCount);
|
|
182
|
+
switch (colCount) {
|
|
183
|
+
case 1:
|
|
184
|
+
for (let r = 0; r < rowCount; r++)
|
|
185
|
+
results[r] = factory(readVal(bytes, dv, nb));
|
|
186
|
+
break;
|
|
187
|
+
case 2:
|
|
188
|
+
for (let r = 0; r < rowCount; r++)
|
|
189
|
+
results[r] = factory(readVal(bytes, dv, nb), readVal(bytes, dv, nb));
|
|
190
|
+
break;
|
|
191
|
+
case 3:
|
|
192
|
+
for (let r = 0; r < rowCount; r++)
|
|
193
|
+
results[r] = factory(
|
|
194
|
+
readVal(bytes, dv, nb),
|
|
195
|
+
readVal(bytes, dv, nb),
|
|
196
|
+
readVal(bytes, dv, nb),
|
|
197
|
+
);
|
|
198
|
+
break;
|
|
199
|
+
case 4:
|
|
200
|
+
for (let r = 0; r < rowCount; r++)
|
|
201
|
+
results[r] = factory(
|
|
202
|
+
readVal(bytes, dv, nb),
|
|
203
|
+
readVal(bytes, dv, nb),
|
|
204
|
+
readVal(bytes, dv, nb),
|
|
205
|
+
readVal(bytes, dv, nb),
|
|
206
|
+
);
|
|
207
|
+
break;
|
|
208
|
+
case 5:
|
|
209
|
+
for (let r = 0; r < rowCount; r++)
|
|
210
|
+
results[r] = factory(
|
|
211
|
+
readVal(bytes, dv, nb),
|
|
212
|
+
readVal(bytes, dv, nb),
|
|
213
|
+
readVal(bytes, dv, nb),
|
|
214
|
+
readVal(bytes, dv, nb),
|
|
215
|
+
readVal(bytes, dv, nb),
|
|
216
|
+
);
|
|
217
|
+
break;
|
|
218
|
+
case 6:
|
|
219
|
+
for (let r = 0; r < rowCount; r++)
|
|
220
|
+
results[r] = factory(
|
|
221
|
+
readVal(bytes, dv, nb),
|
|
222
|
+
readVal(bytes, dv, nb),
|
|
223
|
+
readVal(bytes, dv, nb),
|
|
224
|
+
readVal(bytes, dv, nb),
|
|
225
|
+
readVal(bytes, dv, nb),
|
|
226
|
+
readVal(bytes, dv, nb),
|
|
227
|
+
);
|
|
228
|
+
break;
|
|
229
|
+
default: {
|
|
230
|
+
const v = new Array(colCount);
|
|
231
|
+
for (let r = 0; r < rowCount; r++) {
|
|
232
|
+
for (let c = 0; c < colCount; c++) v[c] = readVal(bytes, dv, nb);
|
|
233
|
+
results[r] = factory.apply(null, v);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return results;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = {
|
|
241
|
+
parseRows,
|
|
242
|
+
parseRaw,
|
|
243
|
+
parseHeader,
|
|
244
|
+
makeRowFactory,
|
|
245
|
+
parseRowsCached,
|
|
246
|
+
};
|
package/lib/statement.js
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { processParams } = require("./ffi");
|
|
4
|
+
const {
|
|
5
|
+
parseRows,
|
|
6
|
+
parseRaw,
|
|
7
|
+
parseHeader,
|
|
8
|
+
makeRowFactory,
|
|
9
|
+
parseRowsCached,
|
|
10
|
+
} = require("./parse");
|
|
11
|
+
|
|
12
|
+
/* Call stmtExecNums with direct numeric args (no array wrapping).
|
|
13
|
+
* Manual unpack for 1-3 params avoids spread overhead. */
|
|
14
|
+
function callExecNums(stoolap, ptr, params) {
|
|
15
|
+
const n = params.length;
|
|
16
|
+
switch (n) {
|
|
17
|
+
case 1:
|
|
18
|
+
return { changes: stoolap.stmtExecNums(ptr, params[0]) };
|
|
19
|
+
case 2:
|
|
20
|
+
return { changes: stoolap.stmtExecNums(ptr, params[0], params[1]) };
|
|
21
|
+
case 3:
|
|
22
|
+
return {
|
|
23
|
+
changes: stoolap.stmtExecNums(ptr, params[0], params[1], params[2]),
|
|
24
|
+
};
|
|
25
|
+
default: {
|
|
26
|
+
const args = [ptr];
|
|
27
|
+
for (let i = 0; i < n; i++) args.push(params[i]);
|
|
28
|
+
return { changes: stoolap.stmtExecNums.apply(stoolap, args) };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* Call stmtQueryNums with direct numeric args. */
|
|
34
|
+
function callQueryNums(stoolap, ptr, params) {
|
|
35
|
+
const n = params.length;
|
|
36
|
+
switch (n) {
|
|
37
|
+
case 1:
|
|
38
|
+
return stoolap.stmtQueryNums(ptr, params[0]);
|
|
39
|
+
case 2:
|
|
40
|
+
return stoolap.stmtQueryNums(ptr, params[0], params[1]);
|
|
41
|
+
case 3:
|
|
42
|
+
return stoolap.stmtQueryNums(ptr, params[0], params[1], params[2]);
|
|
43
|
+
default: {
|
|
44
|
+
const args = [ptr];
|
|
45
|
+
for (let i = 0; i < n; i++) args.push(params[i]);
|
|
46
|
+
return stoolap.stmtQueryNums.apply(stoolap, args);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Call stmtQueryOneNums with direct numeric args. */
|
|
52
|
+
function callQueryOneNums(stoolap, ptr, params) {
|
|
53
|
+
const n = params.length;
|
|
54
|
+
switch (n) {
|
|
55
|
+
case 1:
|
|
56
|
+
return stoolap.stmtQueryOneNums(ptr, params[0]);
|
|
57
|
+
case 2:
|
|
58
|
+
return stoolap.stmtQueryOneNums(ptr, params[0], params[1]);
|
|
59
|
+
case 3:
|
|
60
|
+
return stoolap.stmtQueryOneNums(ptr, params[0], params[1], params[2]);
|
|
61
|
+
default: {
|
|
62
|
+
const args = [ptr];
|
|
63
|
+
for (let i = 0; i < n; i++) args.push(params[i]);
|
|
64
|
+
return stoolap.stmtQueryOneNums.apply(stoolap, args);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isAllNums(params) {
|
|
70
|
+
for (let i = 0; i < params.length; i++) {
|
|
71
|
+
if (typeof params[i] !== "number") return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* ---- Binary param buffer encoding ---- */
|
|
77
|
+
/* Tags: 0=NULL, 1=INT32(4B), 2=F64(8B), 3=TEXT(u32+data+\0), 4=BOOL(1B), 5=INT64(8B) */
|
|
78
|
+
|
|
79
|
+
let _pbuf = Buffer.allocUnsafe(16384);
|
|
80
|
+
|
|
81
|
+
/* Encode a batch of param arrays into a single buffer.
|
|
82
|
+
* Format: [u32 row_count][u8 params_per_row][row0 params][row1 params]...
|
|
83
|
+
* Returns Buffer subarray ready to pass to C. */
|
|
84
|
+
function encodeBatch(paramsArray) {
|
|
85
|
+
const rowCount = paramsArray.length;
|
|
86
|
+
const ppr = paramsArray[0].length;
|
|
87
|
+
let buf = _pbuf;
|
|
88
|
+
const estSize = 5 + rowCount * ppr * 14;
|
|
89
|
+
if (estSize > buf.length) {
|
|
90
|
+
buf = Buffer.allocUnsafe(estSize + 4096);
|
|
91
|
+
_pbuf = buf;
|
|
92
|
+
}
|
|
93
|
+
let off = 0;
|
|
94
|
+
buf.writeUInt32LE(rowCount, off);
|
|
95
|
+
off += 4;
|
|
96
|
+
buf[off++] = ppr;
|
|
97
|
+
for (let r = 0; r < rowCount; r++) {
|
|
98
|
+
const row = paramsArray[r];
|
|
99
|
+
for (let j = 0; j < ppr; j++) {
|
|
100
|
+
const v = row[j];
|
|
101
|
+
if (v === null || v === undefined) {
|
|
102
|
+
buf[off++] = 0;
|
|
103
|
+
} else if (typeof v === "number") {
|
|
104
|
+
const i32 = v | 0;
|
|
105
|
+
if (i32 === v) {
|
|
106
|
+
buf[off++] = 1;
|
|
107
|
+
buf.writeInt32LE(i32, off);
|
|
108
|
+
off += 4;
|
|
109
|
+
} else {
|
|
110
|
+
buf[off++] = 2;
|
|
111
|
+
buf.writeDoubleLE(v, off);
|
|
112
|
+
off += 8;
|
|
113
|
+
}
|
|
114
|
+
} else if (typeof v === "string") {
|
|
115
|
+
const maxBytes = v.length * 3 + 6;
|
|
116
|
+
if (off + maxBytes > buf.length) {
|
|
117
|
+
const nb = Buffer.allocUnsafe(
|
|
118
|
+
Math.max(buf.length * 2, off + maxBytes + 4096),
|
|
119
|
+
);
|
|
120
|
+
buf.copy(nb, 0, 0, off);
|
|
121
|
+
buf = nb;
|
|
122
|
+
_pbuf = nb;
|
|
123
|
+
}
|
|
124
|
+
buf[off++] = 3;
|
|
125
|
+
const written = buf.write(v, off + 4, "utf8");
|
|
126
|
+
buf.writeUInt32LE(written, off);
|
|
127
|
+
off += 4;
|
|
128
|
+
off += written;
|
|
129
|
+
buf[off++] = 0;
|
|
130
|
+
} else if (typeof v === "boolean") {
|
|
131
|
+
buf[off++] = 4;
|
|
132
|
+
buf[off++] = v ? 1 : 0;
|
|
133
|
+
} else if (typeof v === "bigint") {
|
|
134
|
+
buf[off++] = 5;
|
|
135
|
+
buf.writeBigInt64LE(v, off);
|
|
136
|
+
off += 8;
|
|
137
|
+
} else if (typeof v === "object") {
|
|
138
|
+
const s = v instanceof Date ? v.toISOString() : JSON.stringify(v);
|
|
139
|
+
const maxBytes = s.length * 3 + 6;
|
|
140
|
+
if (off + maxBytes > buf.length) {
|
|
141
|
+
const nb = Buffer.allocUnsafe(
|
|
142
|
+
Math.max(buf.length * 2, off + maxBytes + 4096),
|
|
143
|
+
);
|
|
144
|
+
buf.copy(nb, 0, 0, off);
|
|
145
|
+
buf = nb;
|
|
146
|
+
_pbuf = nb;
|
|
147
|
+
}
|
|
148
|
+
buf[off++] = 3;
|
|
149
|
+
const written = buf.write(s, off + 4, "utf8");
|
|
150
|
+
buf.writeUInt32LE(written, off);
|
|
151
|
+
off += 4;
|
|
152
|
+
off += written;
|
|
153
|
+
buf[off++] = 0;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return buf.subarray(0, off);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
class PreparedStatement {
|
|
161
|
+
#ptr = null;
|
|
162
|
+
#stoolap = null;
|
|
163
|
+
#q1i = null;
|
|
164
|
+
#factory = null;
|
|
165
|
+
#headerOff = 0;
|
|
166
|
+
#colCount = 0;
|
|
167
|
+
#owner = null;
|
|
168
|
+
#sql = "";
|
|
169
|
+
#preparedSql = "";
|
|
170
|
+
#paramNames = null;
|
|
171
|
+
|
|
172
|
+
constructor(
|
|
173
|
+
ptr,
|
|
174
|
+
stoolap,
|
|
175
|
+
queryOneIntBound,
|
|
176
|
+
owner,
|
|
177
|
+
sql,
|
|
178
|
+
preparedSql,
|
|
179
|
+
paramNames,
|
|
180
|
+
) {
|
|
181
|
+
this.#ptr = ptr;
|
|
182
|
+
this.#stoolap = stoolap;
|
|
183
|
+
this.#q1i = queryOneIntBound || null;
|
|
184
|
+
this.#owner = owner;
|
|
185
|
+
this.#sql = sql;
|
|
186
|
+
this.#preparedSql = preparedSql || sql;
|
|
187
|
+
this.#paramNames = paramNames || null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#checkOpen() {
|
|
191
|
+
if (!this.#ptr) throw new Error("PreparedStatement is finalized");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
#parseResult(abuf) {
|
|
195
|
+
if (this.#factory) {
|
|
196
|
+
return parseRowsCached(
|
|
197
|
+
abuf,
|
|
198
|
+
this.#headerOff,
|
|
199
|
+
this.#factory,
|
|
200
|
+
this.#colCount,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
const meta = parseHeader(abuf);
|
|
204
|
+
this.#headerOff = meta.headerOff;
|
|
205
|
+
this.#colCount = meta.colCount;
|
|
206
|
+
const f = makeRowFactory(meta.colNames);
|
|
207
|
+
if (f) {
|
|
208
|
+
this.#factory = f;
|
|
209
|
+
return parseRowsCached(abuf, meta.headerOff, f, meta.colCount);
|
|
210
|
+
}
|
|
211
|
+
return parseRows(abuf);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#normalizeParams(params) {
|
|
215
|
+
if (!params) {
|
|
216
|
+
return { sql: this.#preparedSql, values: null };
|
|
217
|
+
}
|
|
218
|
+
if (Array.isArray(params)) {
|
|
219
|
+
return {
|
|
220
|
+
sql: this.#preparedSql,
|
|
221
|
+
values: params.length === 0 ? null : params,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (this.#paramNames) {
|
|
225
|
+
const values = new Array(this.#paramNames.length);
|
|
226
|
+
for (let i = 0; i < this.#paramNames.length; i++) {
|
|
227
|
+
values[i] = params[this.#paramNames[i]];
|
|
228
|
+
}
|
|
229
|
+
return { sql: this.#preparedSql, values };
|
|
230
|
+
}
|
|
231
|
+
const rewritten = processParams(this.#sql, params);
|
|
232
|
+
return {
|
|
233
|
+
sql: rewritten.sql,
|
|
234
|
+
values: rewritten.values,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
get sql() {
|
|
239
|
+
this.#checkOpen();
|
|
240
|
+
return this.#sql;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// -- Execute --
|
|
244
|
+
|
|
245
|
+
execute(params) {
|
|
246
|
+
this.#checkOpen();
|
|
247
|
+
const normalized = this.#normalizeParams(params);
|
|
248
|
+
return this.#owner._stmtExecAsync(
|
|
249
|
+
this.#ptr,
|
|
250
|
+
normalized.values ? normalized.values : undefined,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
executeSync(params) {
|
|
255
|
+
this.#checkOpen();
|
|
256
|
+
const normalized = this.#normalizeParams(params);
|
|
257
|
+
if (!normalized.values) return this.#stoolap.stmtExec(this.#ptr);
|
|
258
|
+
if (normalized.values.length <= 16 && isAllNums(normalized.values)) {
|
|
259
|
+
return callExecNums(this.#stoolap, this.#ptr, normalized.values);
|
|
260
|
+
}
|
|
261
|
+
return this.#stoolap.stmtExec(this.#ptr, normalized.values);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// -- Query --
|
|
265
|
+
|
|
266
|
+
async query(params) {
|
|
267
|
+
this.#checkOpen();
|
|
268
|
+
const normalized = this.#normalizeParams(params);
|
|
269
|
+
const buf = await this.#owner._stmtQueryBufAsync(
|
|
270
|
+
this.#ptr,
|
|
271
|
+
normalized.values ? normalized.values : undefined,
|
|
272
|
+
);
|
|
273
|
+
return this.#parseResult(buf);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
querySync(params) {
|
|
277
|
+
this.#checkOpen();
|
|
278
|
+
const normalized = this.#normalizeParams(params);
|
|
279
|
+
if (!normalized.values) {
|
|
280
|
+
const result = this.#stoolap.stmtQuery(this.#ptr);
|
|
281
|
+
return Array.isArray(result) ? result : this.#parseResult(result);
|
|
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);
|
|
288
|
+
return Array.isArray(result) ? result : this.#parseResult(result);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// -- QueryOne --
|
|
292
|
+
|
|
293
|
+
async queryOne(params) {
|
|
294
|
+
const rows = await this.query(params);
|
|
295
|
+
return rows.length === 0 ? null : rows[0];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
queryOneSync(params) {
|
|
299
|
+
this.#checkOpen();
|
|
300
|
+
const normalized = this.#normalizeParams(params);
|
|
301
|
+
if (!normalized.values) return this.#stoolap.stmtQueryOne(this.#ptr);
|
|
302
|
+
if (
|
|
303
|
+
normalized.values.length === 1 &&
|
|
304
|
+
typeof normalized.values[0] === "number"
|
|
305
|
+
) {
|
|
306
|
+
return this.#q1i(normalized.values[0]);
|
|
307
|
+
}
|
|
308
|
+
if (normalized.values.length <= 16 && isAllNums(normalized.values)) {
|
|
309
|
+
return callQueryOneNums(this.#stoolap, this.#ptr, normalized.values);
|
|
310
|
+
}
|
|
311
|
+
return this.#stoolap.stmtQueryOne(this.#ptr, normalized.values);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// -- QueryRaw --
|
|
315
|
+
|
|
316
|
+
async queryRaw(params) {
|
|
317
|
+
this.#checkOpen();
|
|
318
|
+
const normalized = this.#normalizeParams(params);
|
|
319
|
+
const buf = await this.#owner._stmtQueryBufAsync(
|
|
320
|
+
this.#ptr,
|
|
321
|
+
normalized.values ? normalized.values : undefined,
|
|
322
|
+
);
|
|
323
|
+
return parseRaw(buf);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
queryRawSync(params) {
|
|
327
|
+
this.#checkOpen();
|
|
328
|
+
const normalized = this.#normalizeParams(params);
|
|
329
|
+
if (!normalized.values) {
|
|
330
|
+
return parseRaw(this.#stoolap.stmtQueryBuf(this.#ptr));
|
|
331
|
+
}
|
|
332
|
+
return parseRaw(this.#stoolap.stmtQueryBuf(this.#ptr, normalized.values));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// -- Batch --
|
|
336
|
+
|
|
337
|
+
executeBatchSync(paramsArray) {
|
|
338
|
+
this.#checkOpen();
|
|
339
|
+
if (paramsArray.length === 0) return { changes: 0 };
|
|
340
|
+
if (paramsArray.length > 0 && Array.isArray(paramsArray[0])) {
|
|
341
|
+
const batchBuf = encodeBatch(paramsArray);
|
|
342
|
+
return this.#stoolap.stmtExecBatchBuf(this.#ptr, batchBuf);
|
|
343
|
+
}
|
|
344
|
+
let totalChanges = 0;
|
|
345
|
+
for (const params of paramsArray) {
|
|
346
|
+
const normalized = this.#normalizeParams(params);
|
|
347
|
+
const result = normalized.values
|
|
348
|
+
? this.#stoolap.stmtExec(this.#ptr, normalized.values)
|
|
349
|
+
: this.#stoolap.stmtExec(this.#ptr);
|
|
350
|
+
totalChanges += result.changes;
|
|
351
|
+
}
|
|
352
|
+
return { changes: totalChanges };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
_invalidate() {
|
|
356
|
+
if (this.#ptr) {
|
|
357
|
+
this.#stoolap.stmtFinalize(this.#ptr);
|
|
358
|
+
this.#ptr = null;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
finalize() {
|
|
363
|
+
if (this.#ptr) {
|
|
364
|
+
this.#stoolap.stmtFinalize(this.#ptr);
|
|
365
|
+
this.#ptr = null;
|
|
366
|
+
if (this.#owner) this.#owner._untrackChild(this);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
module.exports = { PreparedStatement, encodeBatch };
|