@stoolap/node 0.3.3 → 0.3.4
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 +73 -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 +246 -0
- package/package.json +15 -24
- package/src/stoolap.c +3286 -0
package/src/stoolap.c
ADDED
|
@@ -0,0 +1,3286 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Thin N-API module that wraps libstoolap via dlopen.
|
|
3
|
+
* Uses only stable N-API (no V8-specific APIs) — one binary works across
|
|
4
|
+
* all Node.js versions >= 18.
|
|
5
|
+
*
|
|
6
|
+
* Build: node-gyp rebuild
|
|
7
|
+
* Link: dlopen's libstoolap.{dylib,so,dll} at runtime
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
#define NAPI_VERSION 8
|
|
11
|
+
#include <node_api.h>
|
|
12
|
+
#include <stdlib.h>
|
|
13
|
+
#include <string.h>
|
|
14
|
+
#include <stdint.h>
|
|
15
|
+
|
|
16
|
+
#ifdef _WIN32
|
|
17
|
+
#include <windows.h>
|
|
18
|
+
#include <malloc.h>
|
|
19
|
+
#define DLOPEN(path) LoadLibraryA(path)
|
|
20
|
+
#define DLSYM(lib, name) GetProcAddress(lib, name)
|
|
21
|
+
#define DLERROR() "LoadLibrary failed"
|
|
22
|
+
#define alloca _alloca
|
|
23
|
+
typedef HMODULE LibHandle;
|
|
24
|
+
#else
|
|
25
|
+
#include <dlfcn.h>
|
|
26
|
+
#define DLOPEN(path) dlopen(path, RTLD_NOW | RTLD_LOCAL)
|
|
27
|
+
#define DLSYM(lib, name) dlsym(lib, name)
|
|
28
|
+
#define DLERROR() dlerror()
|
|
29
|
+
typedef void* LibHandle;
|
|
30
|
+
#endif
|
|
31
|
+
|
|
32
|
+
/* ---- Stoolap types (inlined from stoolap.h) ---- */
|
|
33
|
+
|
|
34
|
+
typedef struct StoolapDB StoolapDB;
|
|
35
|
+
typedef struct StoolapStmt StoolapStmt;
|
|
36
|
+
typedef struct StoolapTx StoolapTx;
|
|
37
|
+
typedef struct StoolapRows StoolapRows;
|
|
38
|
+
|
|
39
|
+
#define STOOLAP_OK 0
|
|
40
|
+
#define STOOLAP_ERR 1
|
|
41
|
+
#define STOOLAP_ROW 100
|
|
42
|
+
#define STOOLAP_DONE 101
|
|
43
|
+
|
|
44
|
+
#define T_NULL 0
|
|
45
|
+
#define T_INTEGER 1
|
|
46
|
+
#define T_FLOAT 2
|
|
47
|
+
#define T_TEXT 3
|
|
48
|
+
#define T_BOOLEAN 4
|
|
49
|
+
#define T_TIMESTAMP 5
|
|
50
|
+
#define T_JSON 6
|
|
51
|
+
#define T_BLOB 7
|
|
52
|
+
|
|
53
|
+
typedef struct {
|
|
54
|
+
int32_t value_type;
|
|
55
|
+
int32_t _padding;
|
|
56
|
+
union {
|
|
57
|
+
int64_t integer;
|
|
58
|
+
double float64;
|
|
59
|
+
int32_t boolean;
|
|
60
|
+
struct { const char* ptr; int64_t len; } text;
|
|
61
|
+
struct { const uint8_t* ptr; int64_t len; } blob;
|
|
62
|
+
int64_t timestamp_nanos;
|
|
63
|
+
} v;
|
|
64
|
+
} StoolapValue;
|
|
65
|
+
|
|
66
|
+
/* ---- Function pointer types ---- */
|
|
67
|
+
|
|
68
|
+
typedef const char* (*fn_version)(void);
|
|
69
|
+
typedef int32_t (*fn_open)(const char*, StoolapDB**);
|
|
70
|
+
typedef int32_t (*fn_open_mem)(StoolapDB**);
|
|
71
|
+
typedef int32_t (*fn_close)(StoolapDB*);
|
|
72
|
+
typedef const char* (*fn_errmsg)(const StoolapDB*);
|
|
73
|
+
typedef int32_t (*fn_exec)(StoolapDB*, const char*, int64_t*);
|
|
74
|
+
typedef int32_t (*fn_exec_p)(StoolapDB*, const char*, const StoolapValue*, int32_t, int64_t*);
|
|
75
|
+
typedef int32_t (*fn_query)(StoolapDB*, const char*, StoolapRows**);
|
|
76
|
+
typedef int32_t (*fn_query_p)(StoolapDB*, const char*, const StoolapValue*, int32_t, StoolapRows**);
|
|
77
|
+
typedef int32_t (*fn_prepare)(StoolapDB*, const char*, StoolapStmt**);
|
|
78
|
+
typedef int32_t (*fn_stmt_exec)(StoolapStmt*, const StoolapValue*, int32_t, int64_t*);
|
|
79
|
+
typedef int32_t (*fn_stmt_query)(StoolapStmt*, const StoolapValue*, int32_t, StoolapRows**);
|
|
80
|
+
typedef const char* (*fn_stmt_sql)(const StoolapStmt*);
|
|
81
|
+
typedef void (*fn_stmt_finalize)(StoolapStmt*);
|
|
82
|
+
typedef const char* (*fn_stmt_errmsg)(const StoolapStmt*);
|
|
83
|
+
typedef int32_t (*fn_begin)(StoolapDB*, StoolapTx**);
|
|
84
|
+
typedef int32_t (*fn_tx_exec)(StoolapTx*, const char*, int64_t*);
|
|
85
|
+
typedef int32_t (*fn_tx_exec_p)(StoolapTx*, const char*, const StoolapValue*, int32_t, int64_t*);
|
|
86
|
+
typedef int32_t (*fn_tx_query)(StoolapTx*, const char*, StoolapRows**);
|
|
87
|
+
typedef int32_t (*fn_tx_query_p)(StoolapTx*, const char*, const StoolapValue*, int32_t, StoolapRows**);
|
|
88
|
+
typedef int32_t (*fn_tx_commit)(StoolapTx*);
|
|
89
|
+
typedef int32_t (*fn_tx_rollback)(StoolapTx*);
|
|
90
|
+
typedef const char* (*fn_tx_errmsg)(const StoolapTx*);
|
|
91
|
+
typedef int32_t (*fn_rows_next)(StoolapRows*);
|
|
92
|
+
typedef int32_t (*fn_rows_col_count)(const StoolapRows*);
|
|
93
|
+
typedef const char* (*fn_rows_col_name)(const StoolapRows*, int32_t);
|
|
94
|
+
typedef int32_t (*fn_rows_col_type)(const StoolapRows*, int32_t);
|
|
95
|
+
typedef int64_t (*fn_rows_col_int)(const StoolapRows*, int32_t);
|
|
96
|
+
typedef double (*fn_rows_col_dbl)(const StoolapRows*, int32_t);
|
|
97
|
+
typedef const char* (*fn_rows_col_text)(StoolapRows*, int32_t, int64_t*);
|
|
98
|
+
typedef int32_t (*fn_rows_col_bool)(const StoolapRows*, int32_t);
|
|
99
|
+
typedef int64_t (*fn_rows_col_ts)(const StoolapRows*, int32_t);
|
|
100
|
+
typedef const uint8_t* (*fn_rows_col_blob)(const StoolapRows*, int32_t, int64_t*);
|
|
101
|
+
typedef int32_t (*fn_rows_col_null)(const StoolapRows*, int32_t);
|
|
102
|
+
typedef int64_t (*fn_rows_affected)(const StoolapRows*);
|
|
103
|
+
typedef void (*fn_rows_close)(StoolapRows*);
|
|
104
|
+
typedef int32_t (*fn_rows_fetch_all)(StoolapRows*, uint8_t**, int64_t*);
|
|
105
|
+
typedef void (*fn_buffer_free)(uint8_t*, int64_t);
|
|
106
|
+
|
|
107
|
+
/* ---- Loaded function pointers ---- */
|
|
108
|
+
|
|
109
|
+
static struct {
|
|
110
|
+
fn_version version;
|
|
111
|
+
fn_open open;
|
|
112
|
+
fn_open_mem open_mem;
|
|
113
|
+
fn_close close;
|
|
114
|
+
fn_errmsg errmsg;
|
|
115
|
+
fn_exec exec;
|
|
116
|
+
fn_exec_p exec_p;
|
|
117
|
+
fn_query query;
|
|
118
|
+
fn_query_p query_p;
|
|
119
|
+
fn_prepare prepare;
|
|
120
|
+
fn_stmt_exec stmt_exec;
|
|
121
|
+
fn_stmt_query stmt_query;
|
|
122
|
+
fn_stmt_sql stmt_sql;
|
|
123
|
+
fn_stmt_finalize stmt_finalize;
|
|
124
|
+
fn_stmt_errmsg stmt_errmsg;
|
|
125
|
+
fn_begin begin;
|
|
126
|
+
fn_tx_exec tx_exec;
|
|
127
|
+
fn_tx_exec_p tx_exec_p;
|
|
128
|
+
fn_tx_query tx_query;
|
|
129
|
+
fn_tx_query_p tx_query_p;
|
|
130
|
+
fn_tx_commit tx_commit;
|
|
131
|
+
fn_tx_rollback tx_rollback;
|
|
132
|
+
fn_tx_errmsg tx_errmsg;
|
|
133
|
+
fn_rows_next rows_next;
|
|
134
|
+
fn_rows_col_count rows_col_count;
|
|
135
|
+
fn_rows_col_name rows_col_name;
|
|
136
|
+
fn_rows_col_type rows_col_type;
|
|
137
|
+
fn_rows_col_int rows_col_int;
|
|
138
|
+
fn_rows_col_dbl rows_col_dbl;
|
|
139
|
+
fn_rows_col_text rows_col_text;
|
|
140
|
+
fn_rows_col_bool rows_col_bool;
|
|
141
|
+
fn_rows_col_ts rows_col_ts;
|
|
142
|
+
fn_rows_col_blob rows_col_blob;
|
|
143
|
+
fn_rows_col_null rows_col_null;
|
|
144
|
+
fn_rows_affected rows_affected;
|
|
145
|
+
fn_rows_close rows_close;
|
|
146
|
+
fn_rows_fetch_all rows_fetch_all;
|
|
147
|
+
fn_buffer_free buffer_free;
|
|
148
|
+
} S;
|
|
149
|
+
|
|
150
|
+
static LibHandle lib_handle = NULL;
|
|
151
|
+
|
|
152
|
+
/* ---- Helpers ---- */
|
|
153
|
+
|
|
154
|
+
#define THROW(env, msg) do { napi_throw_error(env, NULL, msg); return NULL; } while(0)
|
|
155
|
+
#define CHECK(env, status) if ((status) != napi_ok) return NULL
|
|
156
|
+
|
|
157
|
+
static napi_value make_changes(napi_env env, int64_t n) {
|
|
158
|
+
napi_value obj, val;
|
|
159
|
+
napi_create_object(env, &obj);
|
|
160
|
+
if (n >= -2147483648LL && n <= 2147483647LL)
|
|
161
|
+
napi_create_int32(env, (int32_t)n, &val);
|
|
162
|
+
else
|
|
163
|
+
napi_create_int64(env, n, &val);
|
|
164
|
+
napi_set_named_property(env, obj, "changes", val);
|
|
165
|
+
return obj;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Convert a JS value at index `i` in `args` array to StoolapValue. */
|
|
169
|
+
static int js_to_value(napi_env env, napi_value jsval, StoolapValue* out,
|
|
170
|
+
char** text_bufs, int* text_buf_count) {
|
|
171
|
+
napi_valuetype vt;
|
|
172
|
+
napi_typeof(env, jsval, &vt);
|
|
173
|
+
|
|
174
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
175
|
+
out->value_type = T_NULL;
|
|
176
|
+
out->_padding = 0;
|
|
177
|
+
out->v.integer = 0;
|
|
178
|
+
return 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (vt == napi_boolean) {
|
|
182
|
+
bool b;
|
|
183
|
+
napi_get_value_bool(env, jsval, &b);
|
|
184
|
+
out->value_type = T_BOOLEAN;
|
|
185
|
+
out->_padding = 0;
|
|
186
|
+
out->v.boolean = b ? 1 : 0;
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (vt == napi_number) {
|
|
191
|
+
double d;
|
|
192
|
+
napi_get_value_double(env, jsval, &d);
|
|
193
|
+
/* Check if integer */
|
|
194
|
+
int64_t i64 = (int64_t)d;
|
|
195
|
+
if ((double)i64 == d && d >= -9007199254740992.0 && d <= 9007199254740992.0) {
|
|
196
|
+
out->value_type = T_INTEGER;
|
|
197
|
+
out->_padding = 0;
|
|
198
|
+
out->v.integer = i64;
|
|
199
|
+
} else {
|
|
200
|
+
out->value_type = T_FLOAT;
|
|
201
|
+
out->_padding = 0;
|
|
202
|
+
out->v.float64 = d;
|
|
203
|
+
}
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (vt == napi_bigint) {
|
|
208
|
+
int64_t i64;
|
|
209
|
+
bool lossless;
|
|
210
|
+
napi_get_value_bigint_int64(env, jsval, &i64, &lossless);
|
|
211
|
+
out->value_type = T_INTEGER;
|
|
212
|
+
out->_padding = 0;
|
|
213
|
+
out->v.integer = i64;
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (vt == napi_string) {
|
|
218
|
+
/* Single-copy for short strings: try stack buffer first, avoid probe call */
|
|
219
|
+
char tmp[256];
|
|
220
|
+
size_t len;
|
|
221
|
+
napi_get_value_string_utf8(env, jsval, tmp, sizeof(tmp), &len);
|
|
222
|
+
char* buf;
|
|
223
|
+
if (len < sizeof(tmp) - 1) {
|
|
224
|
+
buf = malloc(len + 1);
|
|
225
|
+
if (!buf) return -1;
|
|
226
|
+
memcpy(buf, tmp, len + 1);
|
|
227
|
+
} else {
|
|
228
|
+
napi_get_value_string_utf8(env, jsval, NULL, 0, &len);
|
|
229
|
+
buf = malloc(len + 1);
|
|
230
|
+
if (!buf) return -1;
|
|
231
|
+
napi_get_value_string_utf8(env, jsval, buf, len + 1, &len);
|
|
232
|
+
}
|
|
233
|
+
text_bufs[*text_buf_count] = buf;
|
|
234
|
+
(*text_buf_count)++;
|
|
235
|
+
out->value_type = T_TEXT;
|
|
236
|
+
out->_padding = 0;
|
|
237
|
+
out->v.text.ptr = buf;
|
|
238
|
+
out->v.text.len = (int64_t)len;
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (vt == napi_object) {
|
|
243
|
+
/* Check Date */
|
|
244
|
+
bool is_date;
|
|
245
|
+
napi_is_date(env, jsval, &is_date);
|
|
246
|
+
if (is_date) {
|
|
247
|
+
double ms;
|
|
248
|
+
napi_get_date_value(env, jsval, &ms);
|
|
249
|
+
out->value_type = T_TIMESTAMP;
|
|
250
|
+
out->_padding = 0;
|
|
251
|
+
out->v.timestamp_nanos = (int64_t)(ms * 1000000.0);
|
|
252
|
+
return 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/* Check TypedArray (Float32Array for vectors) */
|
|
256
|
+
bool is_ta;
|
|
257
|
+
napi_is_typedarray(env, jsval, &is_ta);
|
|
258
|
+
if (is_ta) {
|
|
259
|
+
napi_typedarray_type ta_type;
|
|
260
|
+
size_t ta_len;
|
|
261
|
+
void* ta_data;
|
|
262
|
+
napi_get_typedarray_info(env, jsval, &ta_type, &ta_len, &ta_data, NULL, NULL);
|
|
263
|
+
if (ta_type == napi_float32_array) {
|
|
264
|
+
out->value_type = T_BLOB;
|
|
265
|
+
out->_padding = 0;
|
|
266
|
+
out->v.blob.ptr = (const uint8_t*)ta_data;
|
|
267
|
+
out->v.blob.len = (int64_t)(ta_len * 4);
|
|
268
|
+
return 0;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* Check Buffer */
|
|
273
|
+
bool is_buf;
|
|
274
|
+
napi_is_buffer(env, jsval, &is_buf);
|
|
275
|
+
if (is_buf) {
|
|
276
|
+
void* data;
|
|
277
|
+
size_t len;
|
|
278
|
+
napi_get_buffer_info(env, jsval, &data, &len);
|
|
279
|
+
char* buf = malloc(len + 1);
|
|
280
|
+
if (!buf) return -1;
|
|
281
|
+
memcpy(buf, data, len);
|
|
282
|
+
buf[len] = 0;
|
|
283
|
+
text_bufs[*text_buf_count] = buf;
|
|
284
|
+
(*text_buf_count)++;
|
|
285
|
+
out->value_type = T_TEXT;
|
|
286
|
+
out->_padding = 0;
|
|
287
|
+
out->v.text.ptr = buf;
|
|
288
|
+
out->v.text.len = (int64_t)len;
|
|
289
|
+
return 0;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Object/Array → JSON */
|
|
293
|
+
napi_value global, json_obj, stringify_fn, json_str;
|
|
294
|
+
napi_get_global(env, &global);
|
|
295
|
+
napi_get_named_property(env, global, "JSON", &json_obj);
|
|
296
|
+
napi_get_named_property(env, json_obj, "stringify", &stringify_fn);
|
|
297
|
+
napi_call_function(env, json_obj, stringify_fn, 1, &jsval, &json_str);
|
|
298
|
+
|
|
299
|
+
size_t len;
|
|
300
|
+
napi_get_value_string_utf8(env, json_str, NULL, 0, &len);
|
|
301
|
+
char* buf = malloc(len + 1);
|
|
302
|
+
if (!buf) return -1;
|
|
303
|
+
napi_get_value_string_utf8(env, json_str, buf, len + 1, &len);
|
|
304
|
+
text_bufs[*text_buf_count] = buf;
|
|
305
|
+
(*text_buf_count)++;
|
|
306
|
+
out->value_type = T_JSON;
|
|
307
|
+
out->_padding = 0;
|
|
308
|
+
out->v.text.ptr = buf;
|
|
309
|
+
out->v.text.len = (int64_t)len;
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
out->value_type = T_NULL;
|
|
314
|
+
out->_padding = 0;
|
|
315
|
+
out->v.integer = 0;
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* Stack-based fast param conversion for ≤16 params.
|
|
320
|
+
* Returns param count, or -1 on error. Caller must free text bufs only. */
|
|
321
|
+
#define MAX_STACK_PARAMS 16
|
|
322
|
+
|
|
323
|
+
/* Convert JS params (Array or null) to StoolapValue array. Caller frees. */
|
|
324
|
+
static int convert_params(napi_env env, napi_value js_params,
|
|
325
|
+
StoolapValue** out_values, int32_t* out_count,
|
|
326
|
+
char*** out_text_bufs, int* out_text_buf_count) {
|
|
327
|
+
*out_values = NULL;
|
|
328
|
+
*out_count = 0;
|
|
329
|
+
*out_text_bufs = NULL;
|
|
330
|
+
*out_text_buf_count = 0;
|
|
331
|
+
|
|
332
|
+
if (!js_params) return 0;
|
|
333
|
+
|
|
334
|
+
napi_valuetype vt;
|
|
335
|
+
napi_typeof(env, js_params, &vt);
|
|
336
|
+
if (vt == napi_undefined || vt == napi_null) return 0;
|
|
337
|
+
|
|
338
|
+
bool is_array;
|
|
339
|
+
napi_is_array(env, js_params, &is_array);
|
|
340
|
+
if (!is_array) return 0;
|
|
341
|
+
|
|
342
|
+
uint32_t len;
|
|
343
|
+
napi_get_array_length(env, js_params, &len);
|
|
344
|
+
if (len == 0) return 0;
|
|
345
|
+
|
|
346
|
+
StoolapValue* vals = calloc(len, sizeof(StoolapValue));
|
|
347
|
+
char** tbufs = calloc(len * 2, sizeof(char*)); /* generous allocation */
|
|
348
|
+
int tbuf_count = 0;
|
|
349
|
+
|
|
350
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
351
|
+
napi_value elem;
|
|
352
|
+
napi_get_element(env, js_params, i, &elem);
|
|
353
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_count) != 0) {
|
|
354
|
+
free(vals);
|
|
355
|
+
for (int j = 0; j < tbuf_count; j++) free(tbufs[j]);
|
|
356
|
+
free(tbufs);
|
|
357
|
+
return -1;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
*out_values = vals;
|
|
362
|
+
*out_count = (int32_t)len;
|
|
363
|
+
*out_text_bufs = tbufs;
|
|
364
|
+
*out_text_buf_count = tbuf_count;
|
|
365
|
+
return 0;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static int js_to_value_copy(napi_env env, napi_value jsval, StoolapValue* out,
|
|
369
|
+
char** heap_bufs, int* heap_buf_count) {
|
|
370
|
+
napi_valuetype vt;
|
|
371
|
+
napi_typeof(env, jsval, &vt);
|
|
372
|
+
|
|
373
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
374
|
+
out->value_type = T_NULL;
|
|
375
|
+
out->_padding = 0;
|
|
376
|
+
out->v.integer = 0;
|
|
377
|
+
return 0;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
if (vt == napi_boolean) {
|
|
381
|
+
bool b;
|
|
382
|
+
napi_get_value_bool(env, jsval, &b);
|
|
383
|
+
out->value_type = T_BOOLEAN;
|
|
384
|
+
out->_padding = 0;
|
|
385
|
+
out->v.boolean = b ? 1 : 0;
|
|
386
|
+
return 0;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (vt == napi_number) {
|
|
390
|
+
double d;
|
|
391
|
+
napi_get_value_double(env, jsval, &d);
|
|
392
|
+
int64_t i64 = (int64_t)d;
|
|
393
|
+
if ((double)i64 == d && d >= -9007199254740992.0 && d <= 9007199254740992.0) {
|
|
394
|
+
out->value_type = T_INTEGER;
|
|
395
|
+
out->_padding = 0;
|
|
396
|
+
out->v.integer = i64;
|
|
397
|
+
} else {
|
|
398
|
+
out->value_type = T_FLOAT;
|
|
399
|
+
out->_padding = 0;
|
|
400
|
+
out->v.float64 = d;
|
|
401
|
+
}
|
|
402
|
+
return 0;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (vt == napi_bigint) {
|
|
406
|
+
int64_t i64;
|
|
407
|
+
bool lossless;
|
|
408
|
+
napi_get_value_bigint_int64(env, jsval, &i64, &lossless);
|
|
409
|
+
out->value_type = T_INTEGER;
|
|
410
|
+
out->_padding = 0;
|
|
411
|
+
out->v.integer = i64;
|
|
412
|
+
return 0;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (vt == napi_string) {
|
|
416
|
+
size_t len;
|
|
417
|
+
napi_get_value_string_utf8(env, jsval, NULL, 0, &len);
|
|
418
|
+
char* buf = malloc(len + 1);
|
|
419
|
+
if (!buf) return -1;
|
|
420
|
+
napi_get_value_string_utf8(env, jsval, buf, len + 1, &len);
|
|
421
|
+
heap_bufs[*heap_buf_count] = buf;
|
|
422
|
+
(*heap_buf_count)++;
|
|
423
|
+
out->value_type = T_TEXT;
|
|
424
|
+
out->_padding = 0;
|
|
425
|
+
out->v.text.ptr = buf;
|
|
426
|
+
out->v.text.len = (int64_t)len;
|
|
427
|
+
return 0;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (vt == napi_object) {
|
|
431
|
+
bool is_date;
|
|
432
|
+
napi_is_date(env, jsval, &is_date);
|
|
433
|
+
if (is_date) {
|
|
434
|
+
double ms;
|
|
435
|
+
napi_get_date_value(env, jsval, &ms);
|
|
436
|
+
out->value_type = T_TIMESTAMP;
|
|
437
|
+
out->_padding = 0;
|
|
438
|
+
out->v.timestamp_nanos = (int64_t)(ms * 1000000.0);
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
bool is_ta;
|
|
443
|
+
napi_is_typedarray(env, jsval, &is_ta);
|
|
444
|
+
if (is_ta) {
|
|
445
|
+
napi_typedarray_type ta_type;
|
|
446
|
+
size_t ta_len;
|
|
447
|
+
void* ta_data;
|
|
448
|
+
napi_get_typedarray_info(env, jsval, &ta_type, &ta_len, &ta_data, NULL, NULL);
|
|
449
|
+
if (ta_type == napi_float32_array) {
|
|
450
|
+
size_t byte_len = ta_len * 4;
|
|
451
|
+
uint8_t* buf = malloc(byte_len);
|
|
452
|
+
if (!buf) return -1;
|
|
453
|
+
if (byte_len > 0) memcpy(buf, ta_data, byte_len);
|
|
454
|
+
heap_bufs[*heap_buf_count] = (char*)buf;
|
|
455
|
+
(*heap_buf_count)++;
|
|
456
|
+
out->value_type = T_BLOB;
|
|
457
|
+
out->_padding = 0;
|
|
458
|
+
out->v.blob.ptr = buf;
|
|
459
|
+
out->v.blob.len = (int64_t)byte_len;
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
bool is_buf;
|
|
465
|
+
napi_is_buffer(env, jsval, &is_buf);
|
|
466
|
+
if (is_buf) {
|
|
467
|
+
void* data;
|
|
468
|
+
size_t len;
|
|
469
|
+
napi_get_buffer_info(env, jsval, &data, &len);
|
|
470
|
+
char* buf = malloc(len + 1);
|
|
471
|
+
if (!buf) return -1;
|
|
472
|
+
memcpy(buf, data, len);
|
|
473
|
+
buf[len] = 0;
|
|
474
|
+
heap_bufs[*heap_buf_count] = buf;
|
|
475
|
+
(*heap_buf_count)++;
|
|
476
|
+
out->value_type = T_TEXT;
|
|
477
|
+
out->_padding = 0;
|
|
478
|
+
out->v.text.ptr = buf;
|
|
479
|
+
out->v.text.len = (int64_t)len;
|
|
480
|
+
return 0;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
napi_value global, json_obj, stringify_fn, json_str;
|
|
484
|
+
napi_get_global(env, &global);
|
|
485
|
+
napi_get_named_property(env, global, "JSON", &json_obj);
|
|
486
|
+
napi_get_named_property(env, json_obj, "stringify", &stringify_fn);
|
|
487
|
+
napi_call_function(env, json_obj, stringify_fn, 1, &jsval, &json_str);
|
|
488
|
+
|
|
489
|
+
size_t len;
|
|
490
|
+
napi_get_value_string_utf8(env, json_str, NULL, 0, &len);
|
|
491
|
+
char* buf = malloc(len + 1);
|
|
492
|
+
if (!buf) return -1;
|
|
493
|
+
napi_get_value_string_utf8(env, json_str, buf, len + 1, &len);
|
|
494
|
+
heap_bufs[*heap_buf_count] = buf;
|
|
495
|
+
(*heap_buf_count)++;
|
|
496
|
+
out->value_type = T_JSON;
|
|
497
|
+
out->_padding = 0;
|
|
498
|
+
out->v.text.ptr = buf;
|
|
499
|
+
out->v.text.len = (int64_t)len;
|
|
500
|
+
return 0;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
out->value_type = T_NULL;
|
|
504
|
+
out->_padding = 0;
|
|
505
|
+
out->v.integer = 0;
|
|
506
|
+
return 0;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
static int convert_params_copy(napi_env env, napi_value js_params,
|
|
510
|
+
StoolapValue** out_values, int32_t* out_count,
|
|
511
|
+
char*** out_heap_bufs, int* out_heap_buf_count) {
|
|
512
|
+
*out_values = NULL;
|
|
513
|
+
*out_count = 0;
|
|
514
|
+
*out_heap_bufs = NULL;
|
|
515
|
+
*out_heap_buf_count = 0;
|
|
516
|
+
|
|
517
|
+
if (!js_params) return 0;
|
|
518
|
+
|
|
519
|
+
napi_valuetype vt;
|
|
520
|
+
napi_typeof(env, js_params, &vt);
|
|
521
|
+
if (vt == napi_undefined || vt == napi_null) return 0;
|
|
522
|
+
|
|
523
|
+
bool is_array;
|
|
524
|
+
napi_is_array(env, js_params, &is_array);
|
|
525
|
+
if (!is_array) return 0;
|
|
526
|
+
|
|
527
|
+
uint32_t len;
|
|
528
|
+
napi_get_array_length(env, js_params, &len);
|
|
529
|
+
if (len == 0) return 0;
|
|
530
|
+
|
|
531
|
+
StoolapValue* vals = calloc(len, sizeof(StoolapValue));
|
|
532
|
+
char** heap_bufs = calloc(len * 2, sizeof(char*));
|
|
533
|
+
int heap_buf_count = 0;
|
|
534
|
+
|
|
535
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
536
|
+
napi_value elem;
|
|
537
|
+
napi_get_element(env, js_params, i, &elem);
|
|
538
|
+
if (js_to_value_copy(env, elem, &vals[i], heap_bufs, &heap_buf_count) != 0) {
|
|
539
|
+
free(vals);
|
|
540
|
+
for (int j = 0; j < heap_buf_count; j++) free(heap_bufs[j]);
|
|
541
|
+
free(heap_bufs);
|
|
542
|
+
return -1;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
*out_values = vals;
|
|
547
|
+
*out_count = (int32_t)len;
|
|
548
|
+
*out_heap_bufs = heap_bufs;
|
|
549
|
+
*out_heap_buf_count = heap_buf_count;
|
|
550
|
+
return 0;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
static void free_params(StoolapValue* vals, char** tbufs, int tbuf_count) {
|
|
554
|
+
if (vals) free(vals);
|
|
555
|
+
if (tbufs) {
|
|
556
|
+
for (int i = 0; i < tbuf_count; i++) free(tbufs[i]);
|
|
557
|
+
free(tbufs);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/* ---- Constants for direct C parsing ---- */
|
|
562
|
+
|
|
563
|
+
#define MAX_DIRECT_COLS 64
|
|
564
|
+
#define DIRECT_ROW_LIMIT 5
|
|
565
|
+
|
|
566
|
+
/* ---- Cached prepared statement wrapper ---- */
|
|
567
|
+
|
|
568
|
+
typedef struct {
|
|
569
|
+
StoolapStmt* stmt;
|
|
570
|
+
int32_t col_count;
|
|
571
|
+
uint32_t header_size; /* cached byte offset to first row data in binary buffer */
|
|
572
|
+
char col_names[MAX_DIRECT_COLS][128];
|
|
573
|
+
int has_cache;
|
|
574
|
+
} CachedStmt;
|
|
575
|
+
|
|
576
|
+
static void finalize_cached_stmt(napi_env env, void* data, void* hint) {
|
|
577
|
+
(void)env; (void)hint;
|
|
578
|
+
CachedStmt* cs = (CachedStmt*)data;
|
|
579
|
+
if (cs) {
|
|
580
|
+
if (cs->stmt) S.stmt_finalize(cs->stmt);
|
|
581
|
+
free(cs);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
#define GET_STMT(env, argv_0, cs_var) \
|
|
586
|
+
CachedStmt* cs_var; \
|
|
587
|
+
napi_get_value_external(env, argv_0, (void**)&cs_var)
|
|
588
|
+
|
|
589
|
+
/* ---- SQL string helper: single-copy for short strings ---- */
|
|
590
|
+
|
|
591
|
+
#define SQL_STACK_SIZE 512
|
|
592
|
+
|
|
593
|
+
#define GET_SQL_STRING(env, jsval, sql_var) \
|
|
594
|
+
char _stack_sql[SQL_STACK_SIZE]; \
|
|
595
|
+
size_t _sql_len; \
|
|
596
|
+
napi_get_value_string_utf8(env, jsval, _stack_sql, SQL_STACK_SIZE, &_sql_len); \
|
|
597
|
+
char* sql_var; \
|
|
598
|
+
if (_sql_len < SQL_STACK_SIZE - 1) { \
|
|
599
|
+
sql_var = _stack_sql; \
|
|
600
|
+
} else { \
|
|
601
|
+
napi_get_value_string_utf8(env, jsval, NULL, 0, &_sql_len); \
|
|
602
|
+
sql_var = alloca(_sql_len + 1); \
|
|
603
|
+
napi_get_value_string_utf8(env, jsval, sql_var, _sql_len + 1, &_sql_len); \
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/* ---- Result fetching ---- */
|
|
607
|
+
|
|
608
|
+
typedef struct {
|
|
609
|
+
uint8_t* ptr;
|
|
610
|
+
int64_t len;
|
|
611
|
+
} RustBuffer;
|
|
612
|
+
|
|
613
|
+
static char* dup_cstr(const char* src, const char* fallback) {
|
|
614
|
+
const char* use = src ? src : fallback;
|
|
615
|
+
size_t len = strlen(use);
|
|
616
|
+
char* copy = malloc(len + 1);
|
|
617
|
+
if (!copy) return NULL;
|
|
618
|
+
memcpy(copy, use, len + 1);
|
|
619
|
+
return copy;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
static void finalize_rust_buffer(napi_env env, void* data, void* hint) {
|
|
623
|
+
(void)env; (void)data;
|
|
624
|
+
RustBuffer* rb = (RustBuffer*)hint;
|
|
625
|
+
if (rb) {
|
|
626
|
+
S.buffer_free(rb->ptr, rb->len);
|
|
627
|
+
free(rb);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
static napi_value make_arraybuffer_from_owned_buffer(napi_env env, uint8_t* buf, int64_t buf_len) {
|
|
632
|
+
if (!buf || buf_len == 0) {
|
|
633
|
+
napi_value null_val;
|
|
634
|
+
napi_get_null(env, &null_val);
|
|
635
|
+
if (buf) S.buffer_free(buf, buf_len);
|
|
636
|
+
return null_val;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
RustBuffer* rb = malloc(sizeof(RustBuffer));
|
|
640
|
+
rb->ptr = buf;
|
|
641
|
+
rb->len = buf_len;
|
|
642
|
+
|
|
643
|
+
napi_value arraybuffer;
|
|
644
|
+
napi_status status = napi_create_external_arraybuffer(
|
|
645
|
+
env, buf, (size_t)buf_len, finalize_rust_buffer, rb, &arraybuffer);
|
|
646
|
+
|
|
647
|
+
if (status != napi_ok) {
|
|
648
|
+
free(rb);
|
|
649
|
+
void* ab_data;
|
|
650
|
+
napi_create_arraybuffer(env, (size_t)buf_len, &ab_data, &arraybuffer);
|
|
651
|
+
memcpy(ab_data, buf, (size_t)buf_len);
|
|
652
|
+
S.buffer_free(buf, buf_len);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
return arraybuffer;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/* Return raw ArrayBuffer — used for queryRaw only */
|
|
659
|
+
static napi_value fetch_as_arraybuffer(napi_env env, StoolapRows* rows) {
|
|
660
|
+
uint8_t* buf = NULL;
|
|
661
|
+
int64_t buf_len = 0;
|
|
662
|
+
int32_t rc = S.rows_fetch_all(rows, &buf, &buf_len);
|
|
663
|
+
S.rows_close(rows);
|
|
664
|
+
|
|
665
|
+
if (rc != STOOLAP_OK) {
|
|
666
|
+
if (buf) S.buffer_free(buf, buf_len);
|
|
667
|
+
napi_value null_val;
|
|
668
|
+
napi_get_null(env, &null_val);
|
|
669
|
+
return null_val;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
return make_arraybuffer_from_owned_buffer(env, buf, buf_len);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/* ---- Direct C parsing of binary buffer → JS objects ---- */
|
|
676
|
+
|
|
677
|
+
/* Read one typed value from the binary buffer, advance *poff */
|
|
678
|
+
static napi_value read_buf_val(napi_env env, const uint8_t* buf, uint32_t* poff) {
|
|
679
|
+
napi_value val;
|
|
680
|
+
uint8_t tag = buf[(*poff)++];
|
|
681
|
+
|
|
682
|
+
switch (tag) {
|
|
683
|
+
case T_NULL:
|
|
684
|
+
napi_get_null(env, &val);
|
|
685
|
+
break;
|
|
686
|
+
case T_INTEGER: {
|
|
687
|
+
int64_t v;
|
|
688
|
+
memcpy(&v, buf + *poff, 8); *poff += 8;
|
|
689
|
+
if (v >= -2147483648LL && v <= 2147483647LL)
|
|
690
|
+
napi_create_int32(env, (int32_t)v, &val);
|
|
691
|
+
else
|
|
692
|
+
napi_create_int64(env, v, &val);
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
case T_FLOAT: {
|
|
696
|
+
double d;
|
|
697
|
+
memcpy(&d, buf + *poff, 8); *poff += 8;
|
|
698
|
+
napi_create_double(env, d, &val);
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
case T_TEXT:
|
|
702
|
+
case T_JSON: {
|
|
703
|
+
uint32_t len;
|
|
704
|
+
memcpy(&len, buf + *poff, 4); *poff += 4;
|
|
705
|
+
napi_create_string_utf8(env, (const char*)(buf + *poff), len, &val);
|
|
706
|
+
*poff += len;
|
|
707
|
+
break;
|
|
708
|
+
}
|
|
709
|
+
case T_BOOLEAN:
|
|
710
|
+
napi_get_boolean(env, buf[(*poff)++] != 0, &val);
|
|
711
|
+
break;
|
|
712
|
+
case T_TIMESTAMP: {
|
|
713
|
+
int64_t nanos;
|
|
714
|
+
memcpy(&nanos, buf + *poff, 8); *poff += 8;
|
|
715
|
+
napi_create_date(env, (double)nanos / 1000000.0, &val);
|
|
716
|
+
break;
|
|
717
|
+
}
|
|
718
|
+
case T_BLOB: {
|
|
719
|
+
uint32_t blen;
|
|
720
|
+
memcpy(&blen, buf + *poff, 4); *poff += 4;
|
|
721
|
+
void* ab_data;
|
|
722
|
+
napi_value ab;
|
|
723
|
+
napi_create_arraybuffer(env, blen, &ab_data, &ab);
|
|
724
|
+
if (blen > 0) {
|
|
725
|
+
memcpy(ab_data, buf + *poff, blen);
|
|
726
|
+
*poff += blen;
|
|
727
|
+
}
|
|
728
|
+
napi_create_typedarray(env, napi_float32_array, blen / 4, ab, 0, &val);
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
default:
|
|
732
|
+
napi_get_null(env, &val);
|
|
733
|
+
}
|
|
734
|
+
return val;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/*
|
|
738
|
+
* Read a column value directly from StoolapRows (row-by-row API).
|
|
739
|
+
* No serialization — reads straight from the Rust iterator.
|
|
740
|
+
* Uses rows_col_type to detect NULL (saves separate rows_col_null FFI call).
|
|
741
|
+
*/
|
|
742
|
+
static napi_value read_col_val(napi_env env, StoolapRows* rows, int32_t c) {
|
|
743
|
+
napi_value val;
|
|
744
|
+
int32_t type = S.rows_col_type(rows, c);
|
|
745
|
+
switch (type) {
|
|
746
|
+
case T_NULL:
|
|
747
|
+
napi_get_null(env, &val);
|
|
748
|
+
return val;
|
|
749
|
+
case T_INTEGER: {
|
|
750
|
+
int64_t v = S.rows_col_int(rows, c);
|
|
751
|
+
if (v >= -2147483648LL && v <= 2147483647LL)
|
|
752
|
+
napi_create_int32(env, (int32_t)v, &val);
|
|
753
|
+
else
|
|
754
|
+
napi_create_int64(env, v, &val);
|
|
755
|
+
return val;
|
|
756
|
+
}
|
|
757
|
+
case T_FLOAT:
|
|
758
|
+
napi_create_double(env, S.rows_col_dbl(rows, c), &val);
|
|
759
|
+
return val;
|
|
760
|
+
case T_TEXT:
|
|
761
|
+
case T_JSON: {
|
|
762
|
+
int64_t len;
|
|
763
|
+
const char* txt = S.rows_col_text(rows, c, &len);
|
|
764
|
+
napi_create_string_utf8(env, txt, (size_t)len, &val);
|
|
765
|
+
return val;
|
|
766
|
+
}
|
|
767
|
+
case T_BOOLEAN:
|
|
768
|
+
napi_get_boolean(env, S.rows_col_bool(rows, c) != 0, &val);
|
|
769
|
+
return val;
|
|
770
|
+
case T_TIMESTAMP: {
|
|
771
|
+
double ms = (double)S.rows_col_ts(rows, c) / 1000000.0;
|
|
772
|
+
napi_create_date(env, ms, &val);
|
|
773
|
+
return val;
|
|
774
|
+
}
|
|
775
|
+
case T_BLOB: {
|
|
776
|
+
int64_t blen;
|
|
777
|
+
const uint8_t* bdata = S.rows_col_blob(rows, c, &blen);
|
|
778
|
+
void* ab_data;
|
|
779
|
+
napi_value ab;
|
|
780
|
+
napi_create_arraybuffer(env, (size_t)blen, &ab_data, &ab);
|
|
781
|
+
if (blen > 0) memcpy(ab_data, bdata, (size_t)blen);
|
|
782
|
+
napi_create_typedarray(env, napi_float32_array, (size_t)(blen / 4), ab, 0, &val);
|
|
783
|
+
return val;
|
|
784
|
+
}
|
|
785
|
+
default:
|
|
786
|
+
napi_get_null(env, &val);
|
|
787
|
+
return val;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/*
|
|
792
|
+
* Hybrid fetch: auto-selects based on result size.
|
|
793
|
+
* - Small results (≤ DIRECT_ROW_LIMIT): parse buffer in C with
|
|
794
|
+
* napi_set_named_property (no napi string key creation)
|
|
795
|
+
* - Large results: return ArrayBuffer for JS parsing
|
|
796
|
+
* Closes rows handle.
|
|
797
|
+
*/
|
|
798
|
+
static napi_value fetch_result(napi_env env, StoolapRows* rows) {
|
|
799
|
+
uint8_t* buf = NULL;
|
|
800
|
+
int64_t buf_len = 0;
|
|
801
|
+
int32_t rc = S.rows_fetch_all(rows, &buf, &buf_len);
|
|
802
|
+
S.rows_close(rows);
|
|
803
|
+
|
|
804
|
+
if (rc != STOOLAP_OK || !buf || buf_len == 0) {
|
|
805
|
+
if (buf) S.buffer_free(buf, buf_len);
|
|
806
|
+
napi_value empty;
|
|
807
|
+
napi_create_array_with_length(env, 0, &empty);
|
|
808
|
+
return empty;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/* Parse header */
|
|
812
|
+
uint32_t off = 0;
|
|
813
|
+
uint32_t col_count;
|
|
814
|
+
memcpy(&col_count, buf, 4); off = 4;
|
|
815
|
+
|
|
816
|
+
/* Store null-terminated column names on stack (avoids napi string creation) */
|
|
817
|
+
char col_names[MAX_DIRECT_COLS][128];
|
|
818
|
+
for (uint32_t i = 0; i < col_count; i++) {
|
|
819
|
+
uint16_t nlen;
|
|
820
|
+
memcpy(&nlen, buf + off, 2); off += 2;
|
|
821
|
+
if (i < MAX_DIRECT_COLS && nlen < 127) {
|
|
822
|
+
memcpy(col_names[i], buf + off, nlen);
|
|
823
|
+
col_names[i][nlen] = '\0';
|
|
824
|
+
}
|
|
825
|
+
off += nlen;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
uint32_t row_count;
|
|
829
|
+
memcpy(&row_count, buf + off, 4); off += 4;
|
|
830
|
+
|
|
831
|
+
if (row_count <= DIRECT_ROW_LIMIT && col_count <= MAX_DIRECT_COLS) {
|
|
832
|
+
/* Small result: parse in C, batch set via napi_define_properties */
|
|
833
|
+
napi_value result;
|
|
834
|
+
napi_create_array_with_length(env, row_count, &result);
|
|
835
|
+
|
|
836
|
+
napi_property_descriptor props[MAX_DIRECT_COLS];
|
|
837
|
+
for (uint32_t r = 0; r < row_count; r++) {
|
|
838
|
+
napi_value row_obj;
|
|
839
|
+
napi_create_object(env, &row_obj);
|
|
840
|
+
for (uint32_t c = 0; c < col_count; c++) {
|
|
841
|
+
props[c] = (napi_property_descriptor){
|
|
842
|
+
.utf8name = col_names[c],
|
|
843
|
+
.name = NULL, .method = NULL, .getter = NULL, .setter = NULL,
|
|
844
|
+
.value = read_buf_val(env, buf, &off),
|
|
845
|
+
.attributes = napi_default_jsproperty,
|
|
846
|
+
.data = NULL,
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
napi_define_properties(env, row_obj, col_count, props);
|
|
850
|
+
napi_set_element(env, result, r, row_obj);
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
S.buffer_free(buf, buf_len);
|
|
854
|
+
return result;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* Large result: return ArrayBuffer for JS parsing */
|
|
858
|
+
RustBuffer* rb = malloc(sizeof(RustBuffer));
|
|
859
|
+
rb->ptr = buf;
|
|
860
|
+
rb->len = buf_len;
|
|
861
|
+
|
|
862
|
+
napi_value arraybuffer;
|
|
863
|
+
napi_status status = napi_create_external_arraybuffer(
|
|
864
|
+
env, buf, (size_t)buf_len, finalize_rust_buffer, rb, &arraybuffer);
|
|
865
|
+
|
|
866
|
+
if (status != napi_ok) {
|
|
867
|
+
free(rb);
|
|
868
|
+
void* ab_data;
|
|
869
|
+
napi_create_arraybuffer(env, (size_t)buf_len, &ab_data, &arraybuffer);
|
|
870
|
+
memcpy(ab_data, buf, (size_t)buf_len);
|
|
871
|
+
S.buffer_free(buf, buf_len);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return arraybuffer;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/*
|
|
878
|
+
* Fetch first row as JS object, or null if empty.
|
|
879
|
+
* Uses DIRECT row iteration — skips rows_fetch_all serialization entirely.
|
|
880
|
+
* Uses napi_define_properties batch — 1 N-API call sets all properties.
|
|
881
|
+
* Optimal for point lookups.
|
|
882
|
+
* Closes rows handle.
|
|
883
|
+
*/
|
|
884
|
+
static napi_value fetch_one(napi_env env, StoolapRows* rows) {
|
|
885
|
+
int32_t rc = S.rows_next(rows);
|
|
886
|
+
if (rc != STOOLAP_ROW) {
|
|
887
|
+
S.rows_close(rows);
|
|
888
|
+
napi_value null_val;
|
|
889
|
+
napi_get_null(env, &null_val);
|
|
890
|
+
return null_val;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
int32_t col_count = S.rows_col_count(rows);
|
|
894
|
+
if (col_count > MAX_DIRECT_COLS) col_count = MAX_DIRECT_COLS;
|
|
895
|
+
|
|
896
|
+
napi_value row_obj;
|
|
897
|
+
napi_create_object(env, &row_obj);
|
|
898
|
+
|
|
899
|
+
/* Read all values and set in one batch via napi_define_properties */
|
|
900
|
+
napi_property_descriptor props[MAX_DIRECT_COLS];
|
|
901
|
+
for (int32_t c = 0; c < col_count; c++) {
|
|
902
|
+
props[c] = (napi_property_descriptor){
|
|
903
|
+
.utf8name = S.rows_col_name(rows, c),
|
|
904
|
+
.name = NULL,
|
|
905
|
+
.method = NULL,
|
|
906
|
+
.getter = NULL,
|
|
907
|
+
.setter = NULL,
|
|
908
|
+
.value = read_col_val(env, rows, c),
|
|
909
|
+
.attributes = napi_default_jsproperty,
|
|
910
|
+
.data = NULL,
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
napi_define_properties(env, row_obj, (size_t)col_count, props);
|
|
914
|
+
|
|
915
|
+
S.rows_close(rows);
|
|
916
|
+
return row_obj;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
/*
|
|
920
|
+
* Direct row iteration for prepared statement queryOne.
|
|
921
|
+
* Uses rows_next + read_col_val (direct column access) — no serialization.
|
|
922
|
+
* Caches column names in CachedStmt to avoid rows_col_name calls on repeat.
|
|
923
|
+
* Faster than fetch_one_cached (no rows_fetch_all serialize+deserialize roundtrip).
|
|
924
|
+
*/
|
|
925
|
+
static napi_value fetch_one_direct_cached(napi_env env, StoolapRows* rows, CachedStmt* cs) {
|
|
926
|
+
int32_t rc = S.rows_next(rows);
|
|
927
|
+
if (rc != STOOLAP_ROW) {
|
|
928
|
+
S.rows_close(rows);
|
|
929
|
+
napi_value null_val;
|
|
930
|
+
napi_get_null(env, &null_val);
|
|
931
|
+
return null_val;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
int32_t col_count;
|
|
935
|
+
|
|
936
|
+
if (!cs->has_cache) {
|
|
937
|
+
/* First call: cache column names from rows iterator */
|
|
938
|
+
int32_t cc = S.rows_col_count(rows);
|
|
939
|
+
col_count = (cc > MAX_DIRECT_COLS) ? MAX_DIRECT_COLS : cc;
|
|
940
|
+
cs->col_count = col_count;
|
|
941
|
+
|
|
942
|
+
for (int32_t i = 0; i < col_count; i++) {
|
|
943
|
+
const char* name = S.rows_col_name(rows, i);
|
|
944
|
+
size_t nlen = strlen(name);
|
|
945
|
+
if (nlen > 127) nlen = 127;
|
|
946
|
+
memcpy(cs->col_names[i], name, nlen);
|
|
947
|
+
cs->col_names[i][nlen] = '\0';
|
|
948
|
+
}
|
|
949
|
+
cs->has_cache = 1;
|
|
950
|
+
} else {
|
|
951
|
+
col_count = cs->col_count;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
napi_value row_obj;
|
|
955
|
+
napi_create_object(env, &row_obj);
|
|
956
|
+
|
|
957
|
+
napi_property_descriptor props[MAX_DIRECT_COLS];
|
|
958
|
+
for (int32_t c = 0; c < col_count; c++) {
|
|
959
|
+
props[c] = (napi_property_descriptor){
|
|
960
|
+
.utf8name = cs->col_names[c],
|
|
961
|
+
.name = NULL,
|
|
962
|
+
.method = NULL,
|
|
963
|
+
.getter = NULL,
|
|
964
|
+
.setter = NULL,
|
|
965
|
+
.value = read_col_val(env, rows, c),
|
|
966
|
+
.attributes = napi_default_jsproperty,
|
|
967
|
+
.data = NULL,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
napi_define_properties(env, row_obj, (size_t)col_count, props);
|
|
971
|
+
|
|
972
|
+
S.rows_close(rows);
|
|
973
|
+
return row_obj;
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
/*
|
|
977
|
+
* Hybrid fetch for prepared statements with column name caching.
|
|
978
|
+
* Same as fetch_result but uses CachedStmt to cache header info.
|
|
979
|
+
* On repeat calls: skip header parsing, use cached column names.
|
|
980
|
+
*/
|
|
981
|
+
static napi_value fetch_result_cached(napi_env env, StoolapRows* rows, CachedStmt* cs) {
|
|
982
|
+
uint8_t* buf = NULL;
|
|
983
|
+
int64_t buf_len = 0;
|
|
984
|
+
int32_t rc = S.rows_fetch_all(rows, &buf, &buf_len);
|
|
985
|
+
S.rows_close(rows);
|
|
986
|
+
|
|
987
|
+
if (rc != STOOLAP_OK || !buf || buf_len == 0) {
|
|
988
|
+
if (buf) S.buffer_free(buf, buf_len);
|
|
989
|
+
napi_value empty;
|
|
990
|
+
napi_create_array_with_length(env, 0, &empty);
|
|
991
|
+
return empty;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
uint32_t off = 0;
|
|
995
|
+
int32_t col_count;
|
|
996
|
+
|
|
997
|
+
if (!cs->has_cache || cs->header_size == 0) {
|
|
998
|
+
/* First call, or cache was set by direct path (no header_size) */
|
|
999
|
+
uint32_t cc;
|
|
1000
|
+
memcpy(&cc, buf, 4); off = 4;
|
|
1001
|
+
col_count = (cc > MAX_DIRECT_COLS) ? MAX_DIRECT_COLS : (int32_t)cc;
|
|
1002
|
+
cs->col_count = col_count;
|
|
1003
|
+
|
|
1004
|
+
for (int32_t i = 0; i < col_count; i++) {
|
|
1005
|
+
uint16_t nlen;
|
|
1006
|
+
memcpy(&nlen, buf + off, 2); off += 2;
|
|
1007
|
+
uint16_t cplen = (nlen > 127) ? 127 : nlen;
|
|
1008
|
+
memcpy(cs->col_names[i], buf + off, cplen);
|
|
1009
|
+
cs->col_names[i][cplen] = '\0';
|
|
1010
|
+
off += nlen;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
uint32_t row_count;
|
|
1014
|
+
memcpy(&row_count, buf + off, 4); off += 4;
|
|
1015
|
+
cs->header_size = off;
|
|
1016
|
+
cs->has_cache = 1;
|
|
1017
|
+
|
|
1018
|
+
if (row_count == 0) {
|
|
1019
|
+
S.buffer_free(buf, buf_len);
|
|
1020
|
+
napi_value empty;
|
|
1021
|
+
napi_create_array_with_length(env, 0, &empty);
|
|
1022
|
+
return empty;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (row_count <= DIRECT_ROW_LIMIT) {
|
|
1026
|
+
/* Small result: parse in C */
|
|
1027
|
+
napi_value result;
|
|
1028
|
+
napi_create_array_with_length(env, row_count, &result);
|
|
1029
|
+
napi_property_descriptor props[MAX_DIRECT_COLS];
|
|
1030
|
+
for (uint32_t r = 0; r < row_count; r++) {
|
|
1031
|
+
napi_value row_obj;
|
|
1032
|
+
napi_create_object(env, &row_obj);
|
|
1033
|
+
for (int32_t c = 0; c < col_count; c++) {
|
|
1034
|
+
props[c] = (napi_property_descriptor){
|
|
1035
|
+
.utf8name = cs->col_names[c],
|
|
1036
|
+
.name = NULL, .method = NULL, .getter = NULL, .setter = NULL,
|
|
1037
|
+
.value = read_buf_val(env, buf, &off),
|
|
1038
|
+
.attributes = napi_default_jsproperty, .data = NULL,
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
napi_define_properties(env, row_obj, (size_t)col_count, props);
|
|
1042
|
+
napi_set_element(env, result, r, row_obj);
|
|
1043
|
+
}
|
|
1044
|
+
S.buffer_free(buf, buf_len);
|
|
1045
|
+
return result;
|
|
1046
|
+
}
|
|
1047
|
+
} else {
|
|
1048
|
+
/* Cached: use cached column info */
|
|
1049
|
+
col_count = cs->col_count;
|
|
1050
|
+
off = cs->header_size;
|
|
1051
|
+
uint32_t row_count;
|
|
1052
|
+
memcpy(&row_count, buf + cs->header_size - 4, 4);
|
|
1053
|
+
|
|
1054
|
+
if (row_count == 0) {
|
|
1055
|
+
S.buffer_free(buf, buf_len);
|
|
1056
|
+
napi_value empty;
|
|
1057
|
+
napi_create_array_with_length(env, 0, &empty);
|
|
1058
|
+
return empty;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
if (row_count <= DIRECT_ROW_LIMIT) {
|
|
1062
|
+
napi_value result;
|
|
1063
|
+
napi_create_array_with_length(env, row_count, &result);
|
|
1064
|
+
napi_property_descriptor props[MAX_DIRECT_COLS];
|
|
1065
|
+
for (uint32_t r = 0; r < row_count; r++) {
|
|
1066
|
+
napi_value row_obj;
|
|
1067
|
+
napi_create_object(env, &row_obj);
|
|
1068
|
+
for (int32_t c = 0; c < col_count; c++) {
|
|
1069
|
+
props[c] = (napi_property_descriptor){
|
|
1070
|
+
.utf8name = cs->col_names[c],
|
|
1071
|
+
.name = NULL, .method = NULL, .getter = NULL, .setter = NULL,
|
|
1072
|
+
.value = read_buf_val(env, buf, &off),
|
|
1073
|
+
.attributes = napi_default_jsproperty, .data = NULL,
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
napi_define_properties(env, row_obj, (size_t)col_count, props);
|
|
1077
|
+
napi_set_element(env, result, r, row_obj);
|
|
1078
|
+
}
|
|
1079
|
+
S.buffer_free(buf, buf_len);
|
|
1080
|
+
return result;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/* Large result: return ArrayBuffer for JS parsing */
|
|
1085
|
+
RustBuffer* rb = malloc(sizeof(RustBuffer));
|
|
1086
|
+
rb->ptr = buf;
|
|
1087
|
+
rb->len = buf_len;
|
|
1088
|
+
|
|
1089
|
+
napi_value arraybuffer;
|
|
1090
|
+
napi_status status = napi_create_external_arraybuffer(
|
|
1091
|
+
env, buf, (size_t)buf_len, finalize_rust_buffer, rb, &arraybuffer);
|
|
1092
|
+
|
|
1093
|
+
if (status != napi_ok) {
|
|
1094
|
+
free(rb);
|
|
1095
|
+
void* ab_data;
|
|
1096
|
+
napi_create_arraybuffer(env, (size_t)buf_len, &ab_data, &arraybuffer);
|
|
1097
|
+
memcpy(ab_data, buf, (size_t)buf_len);
|
|
1098
|
+
S.buffer_free(buf, buf_len);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
return arraybuffer;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/* Forward declaration for bound function used in dbPrepare */
|
|
1105
|
+
static napi_value wrap_stmt_query_one_int_bound(napi_env env, napi_callback_info info);
|
|
1106
|
+
|
|
1107
|
+
/* ---- Async work helpers ---- */
|
|
1108
|
+
|
|
1109
|
+
typedef struct {
|
|
1110
|
+
napi_async_work work;
|
|
1111
|
+
napi_deferred deferred;
|
|
1112
|
+
char* error;
|
|
1113
|
+
} AsyncBase;
|
|
1114
|
+
|
|
1115
|
+
typedef struct {
|
|
1116
|
+
AsyncBase base;
|
|
1117
|
+
char* dsn;
|
|
1118
|
+
StoolapDB* db;
|
|
1119
|
+
} AsyncDbOpenTask;
|
|
1120
|
+
|
|
1121
|
+
typedef struct {
|
|
1122
|
+
AsyncBase base;
|
|
1123
|
+
StoolapDB* db;
|
|
1124
|
+
} AsyncDbCloseTask;
|
|
1125
|
+
|
|
1126
|
+
typedef enum {
|
|
1127
|
+
ASYNC_EXEC_DB,
|
|
1128
|
+
ASYNC_EXEC_DB_SIMPLE,
|
|
1129
|
+
ASYNC_EXEC_TX,
|
|
1130
|
+
ASYNC_EXEC_STMT,
|
|
1131
|
+
} AsyncExecKind;
|
|
1132
|
+
|
|
1133
|
+
typedef struct {
|
|
1134
|
+
AsyncBase base;
|
|
1135
|
+
AsyncExecKind kind;
|
|
1136
|
+
void* target;
|
|
1137
|
+
char* sql;
|
|
1138
|
+
StoolapValue* vals;
|
|
1139
|
+
int32_t val_count;
|
|
1140
|
+
char** heap_bufs;
|
|
1141
|
+
int heap_buf_count;
|
|
1142
|
+
int64_t affected;
|
|
1143
|
+
} AsyncExecTask;
|
|
1144
|
+
|
|
1145
|
+
typedef enum {
|
|
1146
|
+
ASYNC_QUERY_DB,
|
|
1147
|
+
ASYNC_QUERY_TX,
|
|
1148
|
+
ASYNC_QUERY_STMT,
|
|
1149
|
+
} AsyncQueryKind;
|
|
1150
|
+
|
|
1151
|
+
typedef struct {
|
|
1152
|
+
AsyncBase base;
|
|
1153
|
+
AsyncQueryKind kind;
|
|
1154
|
+
void* target;
|
|
1155
|
+
char* sql;
|
|
1156
|
+
StoolapValue* vals;
|
|
1157
|
+
int32_t val_count;
|
|
1158
|
+
char** heap_bufs;
|
|
1159
|
+
int heap_buf_count;
|
|
1160
|
+
uint8_t* buf;
|
|
1161
|
+
int64_t buf_len;
|
|
1162
|
+
} AsyncQueryTask;
|
|
1163
|
+
|
|
1164
|
+
typedef struct {
|
|
1165
|
+
AsyncBase base;
|
|
1166
|
+
StoolapDB* db;
|
|
1167
|
+
StoolapTx* tx;
|
|
1168
|
+
} AsyncTxBeginTask;
|
|
1169
|
+
|
|
1170
|
+
typedef enum {
|
|
1171
|
+
ASYNC_TX_COMMIT,
|
|
1172
|
+
ASYNC_TX_ROLLBACK,
|
|
1173
|
+
} AsyncTxFinishKind;
|
|
1174
|
+
|
|
1175
|
+
typedef struct {
|
|
1176
|
+
AsyncBase base;
|
|
1177
|
+
AsyncTxFinishKind kind;
|
|
1178
|
+
StoolapTx* tx;
|
|
1179
|
+
} AsyncTxFinishTask;
|
|
1180
|
+
|
|
1181
|
+
static int copy_napi_string(napi_env env, napi_value jsval, char** out) {
|
|
1182
|
+
size_t len = 0;
|
|
1183
|
+
*out = NULL;
|
|
1184
|
+
napi_get_value_string_utf8(env, jsval, NULL, 0, &len);
|
|
1185
|
+
char* buf = malloc(len + 1);
|
|
1186
|
+
if (!buf) return -1;
|
|
1187
|
+
napi_get_value_string_utf8(env, jsval, buf, len + 1, &len);
|
|
1188
|
+
*out = buf;
|
|
1189
|
+
return 0;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
static void reject_async_error(napi_env env, AsyncBase* base, const char* fallback) {
|
|
1193
|
+
napi_value msg, err;
|
|
1194
|
+
const char* text = base->error ? base->error : fallback;
|
|
1195
|
+
napi_create_string_utf8(env, text, NAPI_AUTO_LENGTH, &msg);
|
|
1196
|
+
napi_create_error(env, NULL, msg, &err);
|
|
1197
|
+
napi_reject_deferred(env, base->deferred, err);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
static napi_value queue_async_work(napi_env env, void* task, AsyncBase* base,
|
|
1201
|
+
const char* name,
|
|
1202
|
+
napi_async_execute_callback execute,
|
|
1203
|
+
napi_async_complete_callback complete) {
|
|
1204
|
+
napi_value promise, resource_name;
|
|
1205
|
+
if (napi_create_promise(env, &base->deferred, &promise) != napi_ok) return NULL;
|
|
1206
|
+
if (napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &resource_name) != napi_ok) return NULL;
|
|
1207
|
+
if (napi_create_async_work(env, NULL, resource_name, execute, complete, task, &base->work) != napi_ok) {
|
|
1208
|
+
return NULL;
|
|
1209
|
+
}
|
|
1210
|
+
if (napi_queue_async_work(env, base->work) != napi_ok) {
|
|
1211
|
+
napi_delete_async_work(env, base->work);
|
|
1212
|
+
return NULL;
|
|
1213
|
+
}
|
|
1214
|
+
return promise;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
static void execute_db_open_async(napi_env env, void* data) {
|
|
1218
|
+
(void)env;
|
|
1219
|
+
AsyncDbOpenTask* task = (AsyncDbOpenTask*)data;
|
|
1220
|
+
int32_t rc;
|
|
1221
|
+
|
|
1222
|
+
if (task->dsn[0] == '\0' ||
|
|
1223
|
+
strcmp(task->dsn, ":memory:") == 0 ||
|
|
1224
|
+
strcmp(task->dsn, "memory://") == 0) {
|
|
1225
|
+
rc = S.open_mem(&task->db);
|
|
1226
|
+
} else {
|
|
1227
|
+
rc = S.open(task->dsn, &task->db);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
if (rc != STOOLAP_OK) {
|
|
1231
|
+
task->base.error = dup_cstr(S.errmsg(NULL), "Failed to open database");
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
static void complete_db_open_async(napi_env env, napi_status status, void* data) {
|
|
1236
|
+
AsyncDbOpenTask* task = (AsyncDbOpenTask*)data;
|
|
1237
|
+
if (status != napi_ok && !task->base.error) {
|
|
1238
|
+
task->base.error = dup_cstr(NULL, "Database open cancelled");
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
if (task->base.error) {
|
|
1242
|
+
if (task->db) {
|
|
1243
|
+
S.close(task->db);
|
|
1244
|
+
task->db = NULL;
|
|
1245
|
+
}
|
|
1246
|
+
reject_async_error(env, &task->base, "Failed to open database");
|
|
1247
|
+
} else {
|
|
1248
|
+
napi_value ext;
|
|
1249
|
+
napi_create_external(env, task->db, NULL, NULL, &ext);
|
|
1250
|
+
napi_resolve_deferred(env, task->base.deferred, ext);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
napi_delete_async_work(env, task->base.work);
|
|
1254
|
+
free(task->dsn);
|
|
1255
|
+
free(task->base.error);
|
|
1256
|
+
free(task);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
static void execute_db_close_async(napi_env env, void* data) {
|
|
1260
|
+
(void)env;
|
|
1261
|
+
AsyncDbCloseTask* task = (AsyncDbCloseTask*)data;
|
|
1262
|
+
if (task->db) S.close(task->db);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
static void complete_db_close_async(napi_env env, napi_status status, void* data) {
|
|
1266
|
+
AsyncDbCloseTask* task = (AsyncDbCloseTask*)data;
|
|
1267
|
+
if (status != napi_ok && !task->base.error) {
|
|
1268
|
+
task->base.error = dup_cstr(NULL, "Database close cancelled");
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
if (task->base.error) {
|
|
1272
|
+
reject_async_error(env, &task->base, "Failed to close database");
|
|
1273
|
+
} else {
|
|
1274
|
+
napi_value undef;
|
|
1275
|
+
napi_get_undefined(env, &undef);
|
|
1276
|
+
napi_resolve_deferred(env, task->base.deferred, undef);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
napi_delete_async_work(env, task->base.work);
|
|
1280
|
+
free(task->base.error);
|
|
1281
|
+
free(task);
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
static void execute_async_exec(napi_env env, void* data) {
|
|
1285
|
+
(void)env;
|
|
1286
|
+
AsyncExecTask* task = (AsyncExecTask*)data;
|
|
1287
|
+
int32_t rc = STOOLAP_ERR;
|
|
1288
|
+
|
|
1289
|
+
switch (task->kind) {
|
|
1290
|
+
case ASYNC_EXEC_DB: {
|
|
1291
|
+
StoolapDB* db = (StoolapDB*)task->target;
|
|
1292
|
+
rc = task->val_count > 0
|
|
1293
|
+
? S.exec_p(db, task->sql, task->vals, task->val_count, &task->affected)
|
|
1294
|
+
: S.exec(db, task->sql, &task->affected);
|
|
1295
|
+
if (rc != STOOLAP_OK) task->base.error = dup_cstr(S.errmsg(db), "Exec error");
|
|
1296
|
+
break;
|
|
1297
|
+
}
|
|
1298
|
+
case ASYNC_EXEC_DB_SIMPLE: {
|
|
1299
|
+
StoolapDB* db = (StoolapDB*)task->target;
|
|
1300
|
+
rc = S.exec(db, task->sql, &task->affected);
|
|
1301
|
+
if (rc != STOOLAP_OK) task->base.error = dup_cstr(S.errmsg(db), "Exec error");
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
case ASYNC_EXEC_TX: {
|
|
1305
|
+
StoolapTx* tx = (StoolapTx*)task->target;
|
|
1306
|
+
rc = task->val_count > 0
|
|
1307
|
+
? S.tx_exec_p(tx, task->sql, task->vals, task->val_count, &task->affected)
|
|
1308
|
+
: S.tx_exec(tx, task->sql, &task->affected);
|
|
1309
|
+
if (rc != STOOLAP_OK) task->base.error = dup_cstr(S.tx_errmsg(tx), "Transaction exec error");
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1312
|
+
case ASYNC_EXEC_STMT: {
|
|
1313
|
+
StoolapStmt* stmt = (StoolapStmt*)task->target;
|
|
1314
|
+
rc = S.stmt_exec(stmt, task->vals, task->val_count, &task->affected);
|
|
1315
|
+
if (rc != STOOLAP_OK) task->base.error = dup_cstr(S.stmt_errmsg(stmt), "Statement exec error");
|
|
1316
|
+
break;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
static void complete_async_exec(napi_env env, napi_status status, void* data) {
|
|
1322
|
+
AsyncExecTask* task = (AsyncExecTask*)data;
|
|
1323
|
+
if (status != napi_ok && !task->base.error) {
|
|
1324
|
+
task->base.error = dup_cstr(NULL, "Async exec cancelled");
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
if (task->base.error) {
|
|
1328
|
+
reject_async_error(env, &task->base, "Exec error");
|
|
1329
|
+
} else if (task->kind == ASYNC_EXEC_DB_SIMPLE) {
|
|
1330
|
+
napi_value undef;
|
|
1331
|
+
napi_get_undefined(env, &undef);
|
|
1332
|
+
napi_resolve_deferred(env, task->base.deferred, undef);
|
|
1333
|
+
} else {
|
|
1334
|
+
napi_resolve_deferred(env, task->base.deferred, make_changes(env, task->affected));
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
napi_delete_async_work(env, task->base.work);
|
|
1338
|
+
free(task->sql);
|
|
1339
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
1340
|
+
free(task->base.error);
|
|
1341
|
+
free(task);
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
static void execute_async_query(napi_env env, void* data) {
|
|
1345
|
+
(void)env;
|
|
1346
|
+
AsyncQueryTask* task = (AsyncQueryTask*)data;
|
|
1347
|
+
StoolapRows* rows = NULL;
|
|
1348
|
+
int32_t rc = STOOLAP_ERR;
|
|
1349
|
+
|
|
1350
|
+
if (task->kind == ASYNC_QUERY_DB) {
|
|
1351
|
+
StoolapDB* db = (StoolapDB*)task->target;
|
|
1352
|
+
rc = task->val_count > 0
|
|
1353
|
+
? S.query_p(db, task->sql, task->vals, task->val_count, &rows)
|
|
1354
|
+
: S.query(db, task->sql, &rows);
|
|
1355
|
+
if (rc != STOOLAP_OK) {
|
|
1356
|
+
task->base.error = dup_cstr(S.errmsg(db), "Query error");
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
} else if (task->kind == ASYNC_QUERY_TX) {
|
|
1360
|
+
StoolapTx* tx = (StoolapTx*)task->target;
|
|
1361
|
+
rc = task->val_count > 0
|
|
1362
|
+
? S.tx_query_p(tx, task->sql, task->vals, task->val_count, &rows)
|
|
1363
|
+
: S.tx_query(tx, task->sql, &rows);
|
|
1364
|
+
if (rc != STOOLAP_OK) {
|
|
1365
|
+
task->base.error = dup_cstr(S.tx_errmsg(tx), "Transaction query error");
|
|
1366
|
+
return;
|
|
1367
|
+
}
|
|
1368
|
+
} else {
|
|
1369
|
+
StoolapStmt* stmt = (StoolapStmt*)task->target;
|
|
1370
|
+
rc = S.stmt_query(stmt, task->vals, task->val_count, &rows);
|
|
1371
|
+
if (rc != STOOLAP_OK) {
|
|
1372
|
+
task->base.error = dup_cstr(S.stmt_errmsg(stmt), "Statement query error");
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
rc = S.rows_fetch_all(rows, &task->buf, &task->buf_len);
|
|
1378
|
+
S.rows_close(rows);
|
|
1379
|
+
if (rc != STOOLAP_OK) {
|
|
1380
|
+
task->base.error = dup_cstr(NULL, "Failed to fetch query results");
|
|
1381
|
+
if (task->buf) {
|
|
1382
|
+
S.buffer_free(task->buf, task->buf_len);
|
|
1383
|
+
task->buf = NULL;
|
|
1384
|
+
task->buf_len = 0;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
static void complete_async_query(napi_env env, napi_status status, void* data) {
|
|
1390
|
+
AsyncQueryTask* task = (AsyncQueryTask*)data;
|
|
1391
|
+
if (status != napi_ok && !task->base.error) {
|
|
1392
|
+
task->base.error = dup_cstr(NULL, "Async query cancelled");
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
if (task->base.error) {
|
|
1396
|
+
if (task->buf) {
|
|
1397
|
+
S.buffer_free(task->buf, task->buf_len);
|
|
1398
|
+
task->buf = NULL;
|
|
1399
|
+
task->buf_len = 0;
|
|
1400
|
+
}
|
|
1401
|
+
reject_async_error(env, &task->base, "Query error");
|
|
1402
|
+
} else {
|
|
1403
|
+
napi_resolve_deferred(env, task->base.deferred,
|
|
1404
|
+
make_arraybuffer_from_owned_buffer(env, task->buf, task->buf_len));
|
|
1405
|
+
task->buf = NULL;
|
|
1406
|
+
task->buf_len = 0;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
napi_delete_async_work(env, task->base.work);
|
|
1410
|
+
free(task->sql);
|
|
1411
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
1412
|
+
free(task->base.error);
|
|
1413
|
+
free(task);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
static void execute_tx_begin_async(napi_env env, void* data) {
|
|
1417
|
+
(void)env;
|
|
1418
|
+
AsyncTxBeginTask* task = (AsyncTxBeginTask*)data;
|
|
1419
|
+
int32_t rc = S.begin(task->db, &task->tx);
|
|
1420
|
+
if (rc != STOOLAP_OK) {
|
|
1421
|
+
task->base.error = dup_cstr(S.errmsg(task->db), "Begin error");
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
static void complete_tx_begin_async(napi_env env, napi_status status, void* data) {
|
|
1426
|
+
AsyncTxBeginTask* task = (AsyncTxBeginTask*)data;
|
|
1427
|
+
if (status != napi_ok && !task->base.error) {
|
|
1428
|
+
task->base.error = dup_cstr(NULL, "Transaction begin cancelled");
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
if (task->base.error) {
|
|
1432
|
+
reject_async_error(env, &task->base, "Begin error");
|
|
1433
|
+
} else {
|
|
1434
|
+
napi_value ext;
|
|
1435
|
+
napi_create_external(env, task->tx, NULL, NULL, &ext);
|
|
1436
|
+
napi_resolve_deferred(env, task->base.deferred, ext);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
napi_delete_async_work(env, task->base.work);
|
|
1440
|
+
free(task->base.error);
|
|
1441
|
+
free(task);
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
static void execute_tx_finish_async(napi_env env, void* data) {
|
|
1445
|
+
(void)env;
|
|
1446
|
+
AsyncTxFinishTask* task = (AsyncTxFinishTask*)data;
|
|
1447
|
+
int32_t rc = task->kind == ASYNC_TX_COMMIT
|
|
1448
|
+
? S.tx_commit(task->tx)
|
|
1449
|
+
: S.tx_rollback(task->tx);
|
|
1450
|
+
|
|
1451
|
+
if (rc != STOOLAP_OK) {
|
|
1452
|
+
task->base.error = dup_cstr(S.tx_errmsg(task->tx),
|
|
1453
|
+
task->kind == ASYNC_TX_COMMIT
|
|
1454
|
+
? "Failed to commit transaction"
|
|
1455
|
+
: "Failed to rollback transaction");
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
static void complete_tx_finish_async(napi_env env, napi_status status, void* data) {
|
|
1460
|
+
AsyncTxFinishTask* task = (AsyncTxFinishTask*)data;
|
|
1461
|
+
if (status != napi_ok && !task->base.error) {
|
|
1462
|
+
task->base.error = dup_cstr(NULL,
|
|
1463
|
+
task->kind == ASYNC_TX_COMMIT
|
|
1464
|
+
? "Transaction commit cancelled"
|
|
1465
|
+
: "Transaction rollback cancelled");
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
if (task->base.error) {
|
|
1469
|
+
reject_async_error(env, &task->base,
|
|
1470
|
+
task->kind == ASYNC_TX_COMMIT
|
|
1471
|
+
? "Failed to commit transaction"
|
|
1472
|
+
: "Failed to rollback transaction");
|
|
1473
|
+
} else {
|
|
1474
|
+
napi_value undef;
|
|
1475
|
+
napi_get_undefined(env, &undef);
|
|
1476
|
+
napi_resolve_deferred(env, task->base.deferred, undef);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
napi_delete_async_work(env, task->base.work);
|
|
1480
|
+
free(task->base.error);
|
|
1481
|
+
free(task);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
/* ============================================================
|
|
1485
|
+
* Exported N-API functions
|
|
1486
|
+
* ============================================================ */
|
|
1487
|
+
|
|
1488
|
+
/* loadLibrary(path: string): void */
|
|
1489
|
+
static napi_value fn_load_library(napi_env env, napi_callback_info info) {
|
|
1490
|
+
size_t argc = 1;
|
|
1491
|
+
napi_value argv[1];
|
|
1492
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1493
|
+
|
|
1494
|
+
char path[4096];
|
|
1495
|
+
napi_get_value_string_utf8(env, argv[0], path, sizeof(path), NULL);
|
|
1496
|
+
|
|
1497
|
+
lib_handle = DLOPEN(path);
|
|
1498
|
+
if (!lib_handle) THROW(env, DLERROR());
|
|
1499
|
+
|
|
1500
|
+
#define LOAD(field, name) do { \
|
|
1501
|
+
void* _sym = DLSYM(lib_handle, name); \
|
|
1502
|
+
if (!_sym) THROW(env, "Missing symbol: " name); \
|
|
1503
|
+
*(void**)&S.field = _sym; \
|
|
1504
|
+
} while(0)
|
|
1505
|
+
|
|
1506
|
+
LOAD(version, "stoolap_version");
|
|
1507
|
+
LOAD(open, "stoolap_open");
|
|
1508
|
+
LOAD(open_mem, "stoolap_open_in_memory");
|
|
1509
|
+
LOAD(close, "stoolap_close");
|
|
1510
|
+
LOAD(errmsg, "stoolap_errmsg");
|
|
1511
|
+
LOAD(exec, "stoolap_exec");
|
|
1512
|
+
LOAD(exec_p, "stoolap_exec_params");
|
|
1513
|
+
LOAD(query, "stoolap_query");
|
|
1514
|
+
LOAD(query_p, "stoolap_query_params");
|
|
1515
|
+
LOAD(prepare, "stoolap_prepare");
|
|
1516
|
+
LOAD(stmt_exec, "stoolap_stmt_exec");
|
|
1517
|
+
LOAD(stmt_query, "stoolap_stmt_query");
|
|
1518
|
+
LOAD(stmt_sql, "stoolap_stmt_sql");
|
|
1519
|
+
LOAD(stmt_finalize, "stoolap_stmt_finalize");
|
|
1520
|
+
LOAD(stmt_errmsg, "stoolap_stmt_errmsg");
|
|
1521
|
+
LOAD(begin, "stoolap_begin");
|
|
1522
|
+
LOAD(tx_exec, "stoolap_tx_exec");
|
|
1523
|
+
LOAD(tx_exec_p, "stoolap_tx_exec_params");
|
|
1524
|
+
LOAD(tx_query, "stoolap_tx_query");
|
|
1525
|
+
LOAD(tx_query_p, "stoolap_tx_query_params");
|
|
1526
|
+
LOAD(tx_commit, "stoolap_tx_commit");
|
|
1527
|
+
LOAD(tx_rollback, "stoolap_tx_rollback");
|
|
1528
|
+
LOAD(tx_errmsg, "stoolap_tx_errmsg");
|
|
1529
|
+
LOAD(rows_next, "stoolap_rows_next");
|
|
1530
|
+
LOAD(rows_col_count,"stoolap_rows_column_count");
|
|
1531
|
+
LOAD(rows_col_name, "stoolap_rows_column_name");
|
|
1532
|
+
LOAD(rows_col_type, "stoolap_rows_column_type");
|
|
1533
|
+
LOAD(rows_col_int, "stoolap_rows_column_int64");
|
|
1534
|
+
LOAD(rows_col_dbl, "stoolap_rows_column_double");
|
|
1535
|
+
LOAD(rows_col_text, "stoolap_rows_column_text");
|
|
1536
|
+
LOAD(rows_col_bool, "stoolap_rows_column_bool");
|
|
1537
|
+
LOAD(rows_col_ts, "stoolap_rows_column_timestamp");
|
|
1538
|
+
LOAD(rows_col_blob, "stoolap_rows_column_blob");
|
|
1539
|
+
LOAD(rows_col_null, "stoolap_rows_column_is_null");
|
|
1540
|
+
LOAD(rows_affected, "stoolap_rows_affected");
|
|
1541
|
+
LOAD(rows_close, "stoolap_rows_close");
|
|
1542
|
+
LOAD(rows_fetch_all,"stoolap_rows_fetch_all");
|
|
1543
|
+
LOAD(buffer_free, "stoolap_buffer_free");
|
|
1544
|
+
|
|
1545
|
+
#undef LOAD
|
|
1546
|
+
|
|
1547
|
+
napi_value undef;
|
|
1548
|
+
napi_get_undefined(env, &undef);
|
|
1549
|
+
return undef;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
/* dbOpen(dsn: string): external */
|
|
1553
|
+
static napi_value fn_db_open(napi_env env, napi_callback_info info) {
|
|
1554
|
+
size_t argc = 1;
|
|
1555
|
+
napi_value argv[1];
|
|
1556
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1557
|
+
|
|
1558
|
+
char dsn[4096];
|
|
1559
|
+
size_t dsn_len;
|
|
1560
|
+
napi_get_value_string_utf8(env, argv[0], dsn, sizeof(dsn), &dsn_len);
|
|
1561
|
+
|
|
1562
|
+
StoolapDB* db = NULL;
|
|
1563
|
+
int32_t rc;
|
|
1564
|
+
|
|
1565
|
+
if (dsn_len == 0 || strcmp(dsn, ":memory:") == 0 || strcmp(dsn, "memory://") == 0) {
|
|
1566
|
+
rc = S.open_mem(&db);
|
|
1567
|
+
} else {
|
|
1568
|
+
rc = S.open(dsn, &db);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
if (rc != STOOLAP_OK) {
|
|
1572
|
+
const char* msg = S.errmsg(NULL);
|
|
1573
|
+
THROW(env, msg ? msg : "Failed to open database");
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
napi_value ext;
|
|
1577
|
+
napi_create_external(env, db, NULL, NULL, &ext);
|
|
1578
|
+
return ext;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/* dbClose(external): void */
|
|
1582
|
+
static napi_value fn_db_close(napi_env env, napi_callback_info info) {
|
|
1583
|
+
size_t argc = 1;
|
|
1584
|
+
napi_value argv[1];
|
|
1585
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1586
|
+
|
|
1587
|
+
StoolapDB* db;
|
|
1588
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
1589
|
+
if (db) S.close(db);
|
|
1590
|
+
|
|
1591
|
+
napi_value undef;
|
|
1592
|
+
napi_get_undefined(env, &undef);
|
|
1593
|
+
return undef;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
/* dbOpenAsync(dsn: string): Promise<external> */
|
|
1597
|
+
static napi_value fn_db_open_async(napi_env env, napi_callback_info info) {
|
|
1598
|
+
size_t argc = 1;
|
|
1599
|
+
napi_value argv[1];
|
|
1600
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1601
|
+
|
|
1602
|
+
AsyncDbOpenTask* task = calloc(1, sizeof(AsyncDbOpenTask));
|
|
1603
|
+
if (!task) THROW(env, "Out of memory");
|
|
1604
|
+
if (copy_napi_string(env, argv[0], &task->dsn) != 0) {
|
|
1605
|
+
free(task);
|
|
1606
|
+
THROW(env, "Out of memory");
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
napi_value promise = queue_async_work(
|
|
1610
|
+
env, task, &task->base, "dbOpenAsync", execute_db_open_async, complete_db_open_async);
|
|
1611
|
+
if (!promise) {
|
|
1612
|
+
free(task->dsn);
|
|
1613
|
+
free(task);
|
|
1614
|
+
THROW(env, "Failed to queue dbOpenAsync");
|
|
1615
|
+
}
|
|
1616
|
+
return promise;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/* dbCloseAsync(external): Promise<void> */
|
|
1620
|
+
static napi_value fn_db_close_async(napi_env env, napi_callback_info info) {
|
|
1621
|
+
size_t argc = 1;
|
|
1622
|
+
napi_value argv[1];
|
|
1623
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1624
|
+
|
|
1625
|
+
AsyncDbCloseTask* task = calloc(1, sizeof(AsyncDbCloseTask));
|
|
1626
|
+
if (!task) THROW(env, "Out of memory");
|
|
1627
|
+
napi_get_value_external(env, argv[0], (void**)&task->db);
|
|
1628
|
+
|
|
1629
|
+
napi_value promise = queue_async_work(
|
|
1630
|
+
env, task, &task->base, "dbCloseAsync", execute_db_close_async, complete_db_close_async);
|
|
1631
|
+
if (!promise) {
|
|
1632
|
+
free(task);
|
|
1633
|
+
THROW(env, "Failed to queue dbCloseAsync");
|
|
1634
|
+
}
|
|
1635
|
+
return promise;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
/* dbExec(external, sql, params?): { changes } */
|
|
1639
|
+
static napi_value fn_db_exec(napi_env env, napi_callback_info info) {
|
|
1640
|
+
size_t argc = 3;
|
|
1641
|
+
napi_value argv[3];
|
|
1642
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1643
|
+
|
|
1644
|
+
StoolapDB* db;
|
|
1645
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
1646
|
+
|
|
1647
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
1648
|
+
|
|
1649
|
+
int64_t affected = 0;
|
|
1650
|
+
int32_t rc;
|
|
1651
|
+
|
|
1652
|
+
if (argc > 2) {
|
|
1653
|
+
napi_valuetype vt;
|
|
1654
|
+
napi_typeof(env, argv[2], &vt);
|
|
1655
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
1656
|
+
rc = S.exec(db, sql, &affected);
|
|
1657
|
+
} else {
|
|
1658
|
+
uint32_t len;
|
|
1659
|
+
napi_get_array_length(env, argv[2], &len);
|
|
1660
|
+
if (len == 0) {
|
|
1661
|
+
rc = S.exec(db, sql, &affected);
|
|
1662
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
1663
|
+
/* Stack-allocated fast path */
|
|
1664
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
1665
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
1666
|
+
int tbuf_cnt = 0;
|
|
1667
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
1668
|
+
napi_value elem;
|
|
1669
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
1670
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
1671
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1672
|
+
THROW(env, "Failed to convert parameters");
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
rc = S.exec_p(db, sql, vals, (int32_t)len, &affected);
|
|
1676
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1677
|
+
} else {
|
|
1678
|
+
StoolapValue* vals; int32_t cnt;
|
|
1679
|
+
char** tbufs; int tbuf_cnt;
|
|
1680
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
1681
|
+
THROW(env, "Failed to convert parameters");
|
|
1682
|
+
rc = S.exec_p(db, sql, vals, cnt, &affected);
|
|
1683
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
} else {
|
|
1687
|
+
rc = S.exec(db, sql, &affected);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
if (rc != STOOLAP_OK) {
|
|
1691
|
+
const char* msg = S.errmsg(db);
|
|
1692
|
+
THROW(env, msg ? msg : "Exec error");
|
|
1693
|
+
}
|
|
1694
|
+
|
|
1695
|
+
return make_changes(env, affected);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
/* dbExecAsync(external, sql, params?): Promise<{ changes }> */
|
|
1699
|
+
static napi_value fn_db_exec_async(napi_env env, napi_callback_info info) {
|
|
1700
|
+
size_t argc = 3;
|
|
1701
|
+
napi_value argv[3];
|
|
1702
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1703
|
+
|
|
1704
|
+
AsyncExecTask* task = calloc(1, sizeof(AsyncExecTask));
|
|
1705
|
+
if (!task) THROW(env, "Out of memory");
|
|
1706
|
+
|
|
1707
|
+
task->kind = ASYNC_EXEC_DB;
|
|
1708
|
+
napi_get_value_external(env, argv[0], &task->target);
|
|
1709
|
+
if (copy_napi_string(env, argv[1], &task->sql) != 0) {
|
|
1710
|
+
free(task);
|
|
1711
|
+
THROW(env, "Out of memory");
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
if (argc > 2) {
|
|
1715
|
+
if (convert_params_copy(env, argv[2], &task->vals, &task->val_count,
|
|
1716
|
+
&task->heap_bufs, &task->heap_buf_count) != 0) {
|
|
1717
|
+
free(task->sql);
|
|
1718
|
+
free(task);
|
|
1719
|
+
THROW(env, "Failed to convert parameters");
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
napi_value promise = queue_async_work(
|
|
1724
|
+
env, task, &task->base, "dbExecAsync", execute_async_exec, complete_async_exec);
|
|
1725
|
+
if (!promise) {
|
|
1726
|
+
free(task->sql);
|
|
1727
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
1728
|
+
free(task);
|
|
1729
|
+
THROW(env, "Failed to queue dbExecAsync");
|
|
1730
|
+
}
|
|
1731
|
+
return promise;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
/* dbExecSimple(external, sql): void — no params, no return */
|
|
1735
|
+
static napi_value fn_db_exec_simple(napi_env env, napi_callback_info info) {
|
|
1736
|
+
size_t argc = 2;
|
|
1737
|
+
napi_value argv[2];
|
|
1738
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1739
|
+
|
|
1740
|
+
StoolapDB* db;
|
|
1741
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
1742
|
+
|
|
1743
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
1744
|
+
|
|
1745
|
+
int64_t affected = 0;
|
|
1746
|
+
int32_t rc = S.exec(db, sql, &affected);
|
|
1747
|
+
if (rc != STOOLAP_OK) {
|
|
1748
|
+
const char* msg = S.errmsg(db);
|
|
1749
|
+
THROW(env, msg ? msg : "Exec error");
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
napi_value undef;
|
|
1753
|
+
napi_get_undefined(env, &undef);
|
|
1754
|
+
return undef;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
/* dbExecSimpleAsync(external, sql): Promise<void> */
|
|
1758
|
+
static napi_value fn_db_exec_simple_async(napi_env env, napi_callback_info info) {
|
|
1759
|
+
size_t argc = 2;
|
|
1760
|
+
napi_value argv[2];
|
|
1761
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1762
|
+
|
|
1763
|
+
AsyncExecTask* task = calloc(1, sizeof(AsyncExecTask));
|
|
1764
|
+
if (!task) THROW(env, "Out of memory");
|
|
1765
|
+
|
|
1766
|
+
task->kind = ASYNC_EXEC_DB_SIMPLE;
|
|
1767
|
+
napi_get_value_external(env, argv[0], &task->target);
|
|
1768
|
+
if (copy_napi_string(env, argv[1], &task->sql) != 0) {
|
|
1769
|
+
free(task);
|
|
1770
|
+
THROW(env, "Out of memory");
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
napi_value promise = queue_async_work(
|
|
1774
|
+
env, task, &task->base, "dbExecSimpleAsync", execute_async_exec, complete_async_exec);
|
|
1775
|
+
if (!promise) {
|
|
1776
|
+
free(task->sql);
|
|
1777
|
+
free(task);
|
|
1778
|
+
THROW(env, "Failed to queue dbExecSimpleAsync");
|
|
1779
|
+
}
|
|
1780
|
+
return promise;
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
/* dbQueryBuf(external, sql, params?): ArrayBuffer | null */
|
|
1784
|
+
static napi_value fn_db_query_buf(napi_env env, napi_callback_info info) {
|
|
1785
|
+
size_t argc = 3;
|
|
1786
|
+
napi_value argv[3];
|
|
1787
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1788
|
+
|
|
1789
|
+
StoolapDB* db;
|
|
1790
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
1791
|
+
|
|
1792
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
1793
|
+
|
|
1794
|
+
StoolapRows* rows = NULL;
|
|
1795
|
+
int32_t rc;
|
|
1796
|
+
|
|
1797
|
+
if (argc > 2) {
|
|
1798
|
+
napi_valuetype vt;
|
|
1799
|
+
napi_typeof(env, argv[2], &vt);
|
|
1800
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
1801
|
+
rc = S.query(db, sql, &rows);
|
|
1802
|
+
} else {
|
|
1803
|
+
uint32_t len;
|
|
1804
|
+
napi_get_array_length(env, argv[2], &len);
|
|
1805
|
+
if (len == 0) {
|
|
1806
|
+
rc = S.query(db, sql, &rows);
|
|
1807
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
1808
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
1809
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
1810
|
+
int tbuf_cnt = 0;
|
|
1811
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
1812
|
+
napi_value elem;
|
|
1813
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
1814
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
1815
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1816
|
+
THROW(env, "Failed to convert parameters");
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
rc = S.query_p(db, sql, vals, (int32_t)len, &rows);
|
|
1820
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1821
|
+
} else {
|
|
1822
|
+
StoolapValue* vals; int32_t cnt;
|
|
1823
|
+
char** tbufs; int tbuf_cnt;
|
|
1824
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
1825
|
+
THROW(env, "Failed to convert parameters");
|
|
1826
|
+
rc = S.query_p(db, sql, vals, cnt, &rows);
|
|
1827
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
} else {
|
|
1831
|
+
rc = S.query(db, sql, &rows);
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
if (rc != STOOLAP_OK) {
|
|
1835
|
+
const char* msg = S.errmsg(db);
|
|
1836
|
+
THROW(env, msg ? msg : "Query error");
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
return fetch_as_arraybuffer(env, rows);
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/* dbQueryBufAsync(external, sql, params?): Promise<ArrayBuffer | null> */
|
|
1843
|
+
static napi_value fn_db_query_buf_async(napi_env env, napi_callback_info info) {
|
|
1844
|
+
size_t argc = 3;
|
|
1845
|
+
napi_value argv[3];
|
|
1846
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1847
|
+
|
|
1848
|
+
AsyncQueryTask* task = calloc(1, sizeof(AsyncQueryTask));
|
|
1849
|
+
if (!task) THROW(env, "Out of memory");
|
|
1850
|
+
|
|
1851
|
+
task->kind = ASYNC_QUERY_DB;
|
|
1852
|
+
napi_get_value_external(env, argv[0], &task->target);
|
|
1853
|
+
if (copy_napi_string(env, argv[1], &task->sql) != 0) {
|
|
1854
|
+
free(task);
|
|
1855
|
+
THROW(env, "Out of memory");
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
if (argc > 2) {
|
|
1859
|
+
if (convert_params_copy(env, argv[2], &task->vals, &task->val_count,
|
|
1860
|
+
&task->heap_bufs, &task->heap_buf_count) != 0) {
|
|
1861
|
+
free(task->sql);
|
|
1862
|
+
free(task);
|
|
1863
|
+
THROW(env, "Failed to convert parameters");
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
napi_value promise = queue_async_work(
|
|
1868
|
+
env, task, &task->base, "dbQueryBufAsync", execute_async_query, complete_async_query);
|
|
1869
|
+
if (!promise) {
|
|
1870
|
+
free(task->sql);
|
|
1871
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
1872
|
+
free(task);
|
|
1873
|
+
THROW(env, "Failed to queue dbQueryBufAsync");
|
|
1874
|
+
}
|
|
1875
|
+
return promise;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
/* dbQuery(external, sql, params?): Array | ArrayBuffer */
|
|
1879
|
+
static napi_value fn_db_query(napi_env env, napi_callback_info info) {
|
|
1880
|
+
size_t argc = 3;
|
|
1881
|
+
napi_value argv[3];
|
|
1882
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1883
|
+
|
|
1884
|
+
StoolapDB* db;
|
|
1885
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
1886
|
+
|
|
1887
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
1888
|
+
|
|
1889
|
+
StoolapRows* rows = NULL;
|
|
1890
|
+
int32_t rc;
|
|
1891
|
+
|
|
1892
|
+
if (argc > 2) {
|
|
1893
|
+
napi_valuetype vt;
|
|
1894
|
+
napi_typeof(env, argv[2], &vt);
|
|
1895
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
1896
|
+
rc = S.query(db, sql, &rows);
|
|
1897
|
+
} else {
|
|
1898
|
+
uint32_t len;
|
|
1899
|
+
napi_get_array_length(env, argv[2], &len);
|
|
1900
|
+
if (len == 0) {
|
|
1901
|
+
rc = S.query(db, sql, &rows);
|
|
1902
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
1903
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
1904
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
1905
|
+
int tbuf_cnt = 0;
|
|
1906
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
1907
|
+
napi_value elem;
|
|
1908
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
1909
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
1910
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1911
|
+
THROW(env, "Failed to convert parameters");
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
rc = S.query_p(db, sql, vals, (int32_t)len, &rows);
|
|
1915
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1916
|
+
} else {
|
|
1917
|
+
StoolapValue* vals; int32_t cnt;
|
|
1918
|
+
char** tbufs; int tbuf_cnt;
|
|
1919
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
1920
|
+
THROW(env, "Failed to convert parameters");
|
|
1921
|
+
rc = S.query_p(db, sql, vals, cnt, &rows);
|
|
1922
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
} else {
|
|
1926
|
+
rc = S.query(db, sql, &rows);
|
|
1927
|
+
}
|
|
1928
|
+
|
|
1929
|
+
if (rc != STOOLAP_OK) {
|
|
1930
|
+
const char* msg = S.errmsg(db);
|
|
1931
|
+
THROW(env, msg ? msg : "Query error");
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
return fetch_result(env, rows);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
/* dbQueryOne(external, sql, params?): Object | null */
|
|
1938
|
+
static napi_value fn_db_query_one(napi_env env, napi_callback_info info) {
|
|
1939
|
+
size_t argc = 3;
|
|
1940
|
+
napi_value argv[3];
|
|
1941
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
1942
|
+
|
|
1943
|
+
StoolapDB* db;
|
|
1944
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
1945
|
+
|
|
1946
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
1947
|
+
|
|
1948
|
+
StoolapRows* rows = NULL;
|
|
1949
|
+
int32_t rc;
|
|
1950
|
+
|
|
1951
|
+
if (argc > 2) {
|
|
1952
|
+
napi_valuetype vt;
|
|
1953
|
+
napi_typeof(env, argv[2], &vt);
|
|
1954
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
1955
|
+
rc = S.query(db, sql, &rows);
|
|
1956
|
+
} else {
|
|
1957
|
+
uint32_t len;
|
|
1958
|
+
napi_get_array_length(env, argv[2], &len);
|
|
1959
|
+
if (len == 0) {
|
|
1960
|
+
rc = S.query(db, sql, &rows);
|
|
1961
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
1962
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
1963
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
1964
|
+
int tbuf_cnt = 0;
|
|
1965
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
1966
|
+
napi_value elem;
|
|
1967
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
1968
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
1969
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1970
|
+
THROW(env, "Failed to convert parameters");
|
|
1971
|
+
}
|
|
1972
|
+
}
|
|
1973
|
+
rc = S.query_p(db, sql, vals, (int32_t)len, &rows);
|
|
1974
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
1975
|
+
} else {
|
|
1976
|
+
StoolapValue* vals; int32_t cnt;
|
|
1977
|
+
char** tbufs; int tbuf_cnt;
|
|
1978
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
1979
|
+
THROW(env, "Failed to convert parameters");
|
|
1980
|
+
rc = S.query_p(db, sql, vals, cnt, &rows);
|
|
1981
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
} else {
|
|
1985
|
+
rc = S.query(db, sql, &rows);
|
|
1986
|
+
}
|
|
1987
|
+
|
|
1988
|
+
if (rc != STOOLAP_OK) {
|
|
1989
|
+
const char* msg = S.errmsg(db);
|
|
1990
|
+
THROW(env, msg ? msg : "Query error");
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
return fetch_one(env, rows);
|
|
1994
|
+
}
|
|
1995
|
+
|
|
1996
|
+
/* dbPrepare(external, sql): external (returns CachedStmt wrapper) */
|
|
1997
|
+
static napi_value fn_db_prepare(napi_env env, napi_callback_info info) {
|
|
1998
|
+
size_t argc = 2;
|
|
1999
|
+
napi_value argv[2];
|
|
2000
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2001
|
+
|
|
2002
|
+
StoolapDB* db;
|
|
2003
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
2004
|
+
|
|
2005
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
2006
|
+
|
|
2007
|
+
StoolapStmt* stmt = NULL;
|
|
2008
|
+
int32_t rc = S.prepare(db, sql, &stmt);
|
|
2009
|
+
if (rc != STOOLAP_OK) {
|
|
2010
|
+
const char* msg = S.errmsg(db);
|
|
2011
|
+
THROW(env, msg ? msg : "Prepare error");
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
CachedStmt* cs = calloc(1, sizeof(CachedStmt));
|
|
2015
|
+
cs->stmt = stmt;
|
|
2016
|
+
cs->has_cache = 0;
|
|
2017
|
+
|
|
2018
|
+
napi_value ext;
|
|
2019
|
+
napi_create_external(env, cs, finalize_cached_stmt, NULL, &ext);
|
|
2020
|
+
|
|
2021
|
+
/* Create bound queryOneInt function with CachedStmt baked in */
|
|
2022
|
+
napi_value bound_fn;
|
|
2023
|
+
napi_create_function(env, "q1i", NAPI_AUTO_LENGTH,
|
|
2024
|
+
wrap_stmt_query_one_int_bound, cs, &bound_fn);
|
|
2025
|
+
|
|
2026
|
+
/* Return [external, boundFn] */
|
|
2027
|
+
napi_value result;
|
|
2028
|
+
napi_create_array_with_length(env, 2, &result);
|
|
2029
|
+
napi_set_element(env, result, 0, ext);
|
|
2030
|
+
napi_set_element(env, result, 1, bound_fn);
|
|
2031
|
+
return result;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
/* stmtExec(external, params?): { changes }
|
|
2035
|
+
* Uses stack allocation for ≤16 params (avoids calloc/free overhead). */
|
|
2036
|
+
static napi_value wrap_stmt_exec(napi_env env, napi_callback_info info) {
|
|
2037
|
+
size_t argc = 2;
|
|
2038
|
+
napi_value argv[2];
|
|
2039
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2040
|
+
|
|
2041
|
+
GET_STMT(env, argv[0], cs);
|
|
2042
|
+
StoolapStmt* stmt = cs->stmt;
|
|
2043
|
+
|
|
2044
|
+
if (argc <= 1) {
|
|
2045
|
+
int64_t affected = 0;
|
|
2046
|
+
int32_t rc = S.stmt_exec(stmt, NULL, 0, &affected);
|
|
2047
|
+
if (rc != STOOLAP_OK) {
|
|
2048
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2049
|
+
THROW(env, msg ? msg : "Statement exec error");
|
|
2050
|
+
}
|
|
2051
|
+
return make_changes(env, affected);
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/* Check if params is null/undefined */
|
|
2055
|
+
napi_valuetype vt;
|
|
2056
|
+
napi_typeof(env, argv[1], &vt);
|
|
2057
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2058
|
+
int64_t affected = 0;
|
|
2059
|
+
int32_t rc = S.stmt_exec(stmt, NULL, 0, &affected);
|
|
2060
|
+
if (rc != STOOLAP_OK) {
|
|
2061
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2062
|
+
THROW(env, msg ? msg : "Statement exec error");
|
|
2063
|
+
}
|
|
2064
|
+
return make_changes(env, affected);
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
uint32_t len;
|
|
2068
|
+
napi_get_array_length(env, argv[1], &len);
|
|
2069
|
+
|
|
2070
|
+
if (len == 0) {
|
|
2071
|
+
int64_t affected = 0;
|
|
2072
|
+
int32_t rc = S.stmt_exec(stmt, NULL, 0, &affected);
|
|
2073
|
+
if (rc != STOOLAP_OK) {
|
|
2074
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2075
|
+
THROW(env, msg ? msg : "Statement exec error");
|
|
2076
|
+
}
|
|
2077
|
+
return make_changes(env, affected);
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
if (len <= MAX_STACK_PARAMS) {
|
|
2081
|
+
/* Stack-allocated fast path — no calloc/free for vals and tbufs */
|
|
2082
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2083
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2084
|
+
int tbuf_cnt = 0;
|
|
2085
|
+
|
|
2086
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2087
|
+
napi_value elem;
|
|
2088
|
+
napi_get_element(env, argv[1], i, &elem);
|
|
2089
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2090
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2091
|
+
THROW(env, "Failed to convert parameters");
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
int64_t affected = 0;
|
|
2096
|
+
int32_t rc = S.stmt_exec(stmt, vals, (int32_t)len, &affected);
|
|
2097
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2098
|
+
|
|
2099
|
+
if (rc != STOOLAP_OK) {
|
|
2100
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2101
|
+
THROW(env, msg ? msg : "Statement exec error");
|
|
2102
|
+
}
|
|
2103
|
+
return make_changes(env, affected);
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
/* Large param count — heap allocated */
|
|
2107
|
+
StoolapValue* vals = NULL; int32_t cnt = 0;
|
|
2108
|
+
char** tbufs = NULL; int tbuf_cnt = 0;
|
|
2109
|
+
if (convert_params(env, argv[1], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2110
|
+
THROW(env, "Failed to convert parameters");
|
|
2111
|
+
|
|
2112
|
+
int64_t affected = 0;
|
|
2113
|
+
int32_t rc = S.stmt_exec(stmt, vals, cnt, &affected);
|
|
2114
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2115
|
+
|
|
2116
|
+
if (rc != STOOLAP_OK) {
|
|
2117
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2118
|
+
THROW(env, msg ? msg : "Statement exec error");
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
return make_changes(env, affected);
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
/* stmtQueryBuf(external, params?): ArrayBuffer | null */
|
|
2125
|
+
static napi_value wrap_stmt_query_buf(napi_env env, napi_callback_info info) {
|
|
2126
|
+
size_t argc = 2;
|
|
2127
|
+
napi_value argv[2];
|
|
2128
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2129
|
+
|
|
2130
|
+
GET_STMT(env, argv[0], cs);
|
|
2131
|
+
StoolapStmt* stmt = cs->stmt;
|
|
2132
|
+
|
|
2133
|
+
StoolapRows* rows = NULL;
|
|
2134
|
+
int32_t rc;
|
|
2135
|
+
|
|
2136
|
+
if (argc <= 1) {
|
|
2137
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2138
|
+
} else {
|
|
2139
|
+
napi_valuetype vt;
|
|
2140
|
+
napi_typeof(env, argv[1], &vt);
|
|
2141
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2142
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2143
|
+
} else {
|
|
2144
|
+
uint32_t len;
|
|
2145
|
+
napi_get_array_length(env, argv[1], &len);
|
|
2146
|
+
if (len == 0) {
|
|
2147
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2148
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2149
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2150
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2151
|
+
int tbuf_cnt = 0;
|
|
2152
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2153
|
+
napi_value elem;
|
|
2154
|
+
napi_get_element(env, argv[1], i, &elem);
|
|
2155
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2156
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2157
|
+
THROW(env, "Failed to convert parameters");
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
rc = S.stmt_query(stmt, vals, (int32_t)len, &rows);
|
|
2161
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2162
|
+
} else {
|
|
2163
|
+
StoolapValue* vals = NULL; int32_t cnt = 0;
|
|
2164
|
+
char** tbufs = NULL; int tbuf_cnt = 0;
|
|
2165
|
+
if (convert_params(env, argv[1], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2166
|
+
THROW(env, "Failed to convert parameters");
|
|
2167
|
+
rc = S.stmt_query(stmt, vals, cnt, &rows);
|
|
2168
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
if (rc != STOOLAP_OK) {
|
|
2174
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2175
|
+
THROW(env, msg ? msg : "Statement query error");
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
return fetch_as_arraybuffer(env, rows);
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/* stmtExecAsync(external, params?): Promise<{ changes }> */
|
|
2182
|
+
static napi_value wrap_stmt_exec_async(napi_env env, napi_callback_info info) {
|
|
2183
|
+
size_t argc = 2;
|
|
2184
|
+
napi_value argv[2];
|
|
2185
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2186
|
+
|
|
2187
|
+
GET_STMT(env, argv[0], cs);
|
|
2188
|
+
|
|
2189
|
+
AsyncExecTask* task = calloc(1, sizeof(AsyncExecTask));
|
|
2190
|
+
if (!task) THROW(env, "Out of memory");
|
|
2191
|
+
|
|
2192
|
+
task->kind = ASYNC_EXEC_STMT;
|
|
2193
|
+
task->target = cs->stmt;
|
|
2194
|
+
task->sql = NULL;
|
|
2195
|
+
|
|
2196
|
+
if (argc > 1) {
|
|
2197
|
+
napi_valuetype vt;
|
|
2198
|
+
napi_typeof(env, argv[1], &vt);
|
|
2199
|
+
if (vt != napi_undefined && vt != napi_null) {
|
|
2200
|
+
if (convert_params_copy(env, argv[1], &task->vals, &task->val_count,
|
|
2201
|
+
&task->heap_bufs, &task->heap_buf_count) != 0) {
|
|
2202
|
+
free(task);
|
|
2203
|
+
THROW(env, "Failed to convert parameters");
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
napi_value promise = queue_async_work(
|
|
2209
|
+
env, task, &task->base, "stmtExecAsync", execute_async_exec, complete_async_exec);
|
|
2210
|
+
if (!promise) {
|
|
2211
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
2212
|
+
free(task);
|
|
2213
|
+
THROW(env, "Failed to queue stmtExecAsync");
|
|
2214
|
+
}
|
|
2215
|
+
return promise;
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
/* stmtQueryBufAsync(external, params?): Promise<ArrayBuffer> */
|
|
2219
|
+
static napi_value wrap_stmt_query_buf_async(napi_env env, napi_callback_info info) {
|
|
2220
|
+
size_t argc = 2;
|
|
2221
|
+
napi_value argv[2];
|
|
2222
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2223
|
+
|
|
2224
|
+
GET_STMT(env, argv[0], cs);
|
|
2225
|
+
|
|
2226
|
+
AsyncQueryTask* task = calloc(1, sizeof(AsyncQueryTask));
|
|
2227
|
+
if (!task) THROW(env, "Out of memory");
|
|
2228
|
+
|
|
2229
|
+
task->kind = ASYNC_QUERY_STMT;
|
|
2230
|
+
task->target = cs->stmt;
|
|
2231
|
+
task->sql = NULL;
|
|
2232
|
+
|
|
2233
|
+
if (argc > 1) {
|
|
2234
|
+
napi_valuetype vt;
|
|
2235
|
+
napi_typeof(env, argv[1], &vt);
|
|
2236
|
+
if (vt != napi_undefined && vt != napi_null) {
|
|
2237
|
+
if (convert_params_copy(env, argv[1], &task->vals, &task->val_count,
|
|
2238
|
+
&task->heap_bufs, &task->heap_buf_count) != 0) {
|
|
2239
|
+
free(task);
|
|
2240
|
+
THROW(env, "Failed to convert parameters");
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
napi_value promise = queue_async_work(
|
|
2246
|
+
env, task, &task->base, "stmtQueryBufAsync", execute_async_query, complete_async_query);
|
|
2247
|
+
if (!promise) {
|
|
2248
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
2249
|
+
free(task);
|
|
2250
|
+
THROW(env, "Failed to queue stmtQueryBufAsync");
|
|
2251
|
+
}
|
|
2252
|
+
return promise;
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
/* stmtQuery(external, params?): Array | ArrayBuffer
|
|
2256
|
+
* Uses stack allocation for ≤16 params. */
|
|
2257
|
+
static napi_value wrap_stmt_query(napi_env env, napi_callback_info info) {
|
|
2258
|
+
size_t argc = 2;
|
|
2259
|
+
napi_value argv[2];
|
|
2260
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2261
|
+
|
|
2262
|
+
GET_STMT(env, argv[0], cs);
|
|
2263
|
+
StoolapStmt* stmt = cs->stmt;
|
|
2264
|
+
|
|
2265
|
+
StoolapRows* rows = NULL;
|
|
2266
|
+
int32_t rc;
|
|
2267
|
+
|
|
2268
|
+
if (argc <= 1) {
|
|
2269
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2270
|
+
} else {
|
|
2271
|
+
napi_valuetype vt;
|
|
2272
|
+
napi_typeof(env, argv[1], &vt);
|
|
2273
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2274
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2275
|
+
} else {
|
|
2276
|
+
uint32_t len;
|
|
2277
|
+
napi_get_array_length(env, argv[1], &len);
|
|
2278
|
+
if (len == 0) {
|
|
2279
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2280
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2281
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2282
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2283
|
+
int tbuf_cnt = 0;
|
|
2284
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2285
|
+
napi_value elem;
|
|
2286
|
+
napi_get_element(env, argv[1], i, &elem);
|
|
2287
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2288
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2289
|
+
THROW(env, "Failed to convert parameters");
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
rc = S.stmt_query(stmt, vals, (int32_t)len, &rows);
|
|
2293
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2294
|
+
} else {
|
|
2295
|
+
StoolapValue* vals = NULL; int32_t cnt = 0;
|
|
2296
|
+
char** tbufs = NULL; int tbuf_cnt = 0;
|
|
2297
|
+
if (convert_params(env, argv[1], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2298
|
+
THROW(env, "Failed to convert parameters");
|
|
2299
|
+
rc = S.stmt_query(stmt, vals, cnt, &rows);
|
|
2300
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
if (rc != STOOLAP_OK) {
|
|
2306
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2307
|
+
THROW(env, msg ? msg : "Statement query error");
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
return fetch_result_cached(env, rows, cs);
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
/* stmtQueryOne(external, params?): Object | null
|
|
2314
|
+
* Uses stack allocation for ≤16 params. */
|
|
2315
|
+
static napi_value wrap_stmt_query_one(napi_env env, napi_callback_info info) {
|
|
2316
|
+
size_t argc = 2;
|
|
2317
|
+
napi_value argv[2];
|
|
2318
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2319
|
+
|
|
2320
|
+
GET_STMT(env, argv[0], cs);
|
|
2321
|
+
StoolapStmt* stmt = cs->stmt;
|
|
2322
|
+
|
|
2323
|
+
StoolapRows* rows = NULL;
|
|
2324
|
+
int32_t rc;
|
|
2325
|
+
|
|
2326
|
+
if (argc <= 1) {
|
|
2327
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2328
|
+
} else {
|
|
2329
|
+
napi_valuetype vt;
|
|
2330
|
+
napi_typeof(env, argv[1], &vt);
|
|
2331
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2332
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2333
|
+
} else {
|
|
2334
|
+
uint32_t len;
|
|
2335
|
+
napi_get_array_length(env, argv[1], &len);
|
|
2336
|
+
if (len == 0) {
|
|
2337
|
+
rc = S.stmt_query(stmt, NULL, 0, &rows);
|
|
2338
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2339
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2340
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2341
|
+
int tbuf_cnt = 0;
|
|
2342
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2343
|
+
napi_value elem;
|
|
2344
|
+
napi_get_element(env, argv[1], i, &elem);
|
|
2345
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2346
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2347
|
+
THROW(env, "Failed to convert parameters");
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
rc = S.stmt_query(stmt, vals, (int32_t)len, &rows);
|
|
2351
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2352
|
+
} else {
|
|
2353
|
+
StoolapValue* vals = NULL; int32_t cnt = 0;
|
|
2354
|
+
char** tbufs = NULL; int tbuf_cnt = 0;
|
|
2355
|
+
if (convert_params(env, argv[1], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2356
|
+
THROW(env, "Failed to convert parameters");
|
|
2357
|
+
rc = S.stmt_query(stmt, vals, cnt, &rows);
|
|
2358
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
|
|
2363
|
+
if (rc != STOOLAP_OK) {
|
|
2364
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
2365
|
+
THROW(env, msg ? msg : "Statement query error");
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
return fetch_one_direct_cached(env, rows, cs);
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
/* stmtQueryOneIntBound(intValue): Object | null
|
|
2372
|
+
* Bound version — CachedStmt baked into function data.
|
|
2373
|
+
* Eliminates napi_get_value_external call (~35ns savings per call). */
|
|
2374
|
+
static napi_value wrap_stmt_query_one_int_bound(napi_env env, napi_callback_info info) {
|
|
2375
|
+
size_t argc = 1;
|
|
2376
|
+
napi_value argv[1];
|
|
2377
|
+
void* data;
|
|
2378
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, &data);
|
|
2379
|
+
CachedStmt* cs = (CachedStmt*)data;
|
|
2380
|
+
|
|
2381
|
+
double d;
|
|
2382
|
+
napi_get_value_double(env, argv[0], &d);
|
|
2383
|
+
StoolapValue val;
|
|
2384
|
+
val.value_type = T_INTEGER;
|
|
2385
|
+
val._padding = 0;
|
|
2386
|
+
val.v.integer = (int64_t)d;
|
|
2387
|
+
|
|
2388
|
+
StoolapRows* rows = NULL;
|
|
2389
|
+
int32_t rc = S.stmt_query(cs->stmt, &val, 1, &rows);
|
|
2390
|
+
|
|
2391
|
+
if (rc != STOOLAP_OK) {
|
|
2392
|
+
const char* msg = S.stmt_errmsg(cs->stmt);
|
|
2393
|
+
THROW(env, msg ? msg : "Statement query error");
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
return fetch_one_direct_cached(env, rows, cs);
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
/* Helper: read numeric args from argv[start..start+count] into StoolapValue array */
|
|
2400
|
+
static void read_num_args(napi_env env, napi_value* argv, uint32_t start, uint32_t count, StoolapValue* vals) {
|
|
2401
|
+
for (uint32_t i = 0; i < count; i++) {
|
|
2402
|
+
double d;
|
|
2403
|
+
napi_get_value_double(env, argv[start + i], &d);
|
|
2404
|
+
int64_t i64 = (int64_t)d;
|
|
2405
|
+
if ((double)i64 == d && d >= -9007199254740992.0 && d <= 9007199254740992.0) {
|
|
2406
|
+
vals[i].value_type = T_INTEGER;
|
|
2407
|
+
vals[i]._padding = 0;
|
|
2408
|
+
vals[i].v.integer = i64;
|
|
2409
|
+
} else {
|
|
2410
|
+
vals[i].value_type = T_FLOAT;
|
|
2411
|
+
vals[i]._padding = 0;
|
|
2412
|
+
vals[i].v.float64 = d;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
/* stmtExecNums(external, num1, num2, ...): int
|
|
2418
|
+
* Direct numeric args — no array unwrapping, returns raw int (JS wraps in {changes}).
|
|
2419
|
+
* Saves: napi_get_array_length + N×napi_get_element + napi_create_object + napi_set_named_property */
|
|
2420
|
+
static napi_value wrap_stmt_exec_nums(napi_env env, napi_callback_info info) {
|
|
2421
|
+
size_t argc = 17;
|
|
2422
|
+
napi_value argv[17];
|
|
2423
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2424
|
+
|
|
2425
|
+
GET_STMT(env, argv[0], cs);
|
|
2426
|
+
uint32_t len = (uint32_t)(argc - 1);
|
|
2427
|
+
if (len > 16) len = 16;
|
|
2428
|
+
|
|
2429
|
+
StoolapValue vals[16];
|
|
2430
|
+
read_num_args(env, argv, 1, len, vals);
|
|
2431
|
+
|
|
2432
|
+
int64_t affected = 0;
|
|
2433
|
+
int32_t rc = S.stmt_exec(cs->stmt, vals, (int32_t)len, &affected);
|
|
2434
|
+
|
|
2435
|
+
if (rc != STOOLAP_OK) {
|
|
2436
|
+
const char* msg = S.stmt_errmsg(cs->stmt);
|
|
2437
|
+
THROW(env, msg ? msg : "Statement exec error");
|
|
2438
|
+
}
|
|
2439
|
+
|
|
2440
|
+
napi_value result;
|
|
2441
|
+
napi_create_int32(env, (int32_t)affected, &result);
|
|
2442
|
+
return result;
|
|
2443
|
+
}
|
|
2444
|
+
|
|
2445
|
+
/* stmtQueryNums(external, num1, num2, ...): Array | ArrayBuffer
|
|
2446
|
+
* Direct numeric args — no array unwrapping overhead. */
|
|
2447
|
+
static napi_value wrap_stmt_query_nums(napi_env env, napi_callback_info info) {
|
|
2448
|
+
size_t argc = 17;
|
|
2449
|
+
napi_value argv[17];
|
|
2450
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2451
|
+
|
|
2452
|
+
GET_STMT(env, argv[0], cs);
|
|
2453
|
+
uint32_t len = (uint32_t)(argc - 1);
|
|
2454
|
+
if (len > 16) len = 16;
|
|
2455
|
+
|
|
2456
|
+
StoolapValue vals[16];
|
|
2457
|
+
read_num_args(env, argv, 1, len, vals);
|
|
2458
|
+
|
|
2459
|
+
StoolapRows* rows = NULL;
|
|
2460
|
+
int32_t rc = S.stmt_query(cs->stmt, vals, (int32_t)len, &rows);
|
|
2461
|
+
|
|
2462
|
+
if (rc != STOOLAP_OK) {
|
|
2463
|
+
const char* msg = S.stmt_errmsg(cs->stmt);
|
|
2464
|
+
THROW(env, msg ? msg : "Statement query error");
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
return fetch_result_cached(env, rows, cs);
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
/* stmtQueryOneNums(external, num1, num2, ...): Object | null
|
|
2471
|
+
* Direct numeric args — no array unwrapping overhead. */
|
|
2472
|
+
static napi_value wrap_stmt_query_one_nums(napi_env env, napi_callback_info info) {
|
|
2473
|
+
size_t argc = 17;
|
|
2474
|
+
napi_value argv[17];
|
|
2475
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2476
|
+
|
|
2477
|
+
GET_STMT(env, argv[0], cs);
|
|
2478
|
+
uint32_t len = (uint32_t)(argc - 1);
|
|
2479
|
+
if (len > 16) len = 16;
|
|
2480
|
+
|
|
2481
|
+
StoolapValue vals[16];
|
|
2482
|
+
read_num_args(env, argv, 1, len, vals);
|
|
2483
|
+
|
|
2484
|
+
StoolapRows* rows = NULL;
|
|
2485
|
+
int32_t rc = S.stmt_query(cs->stmt, vals, (int32_t)len, &rows);
|
|
2486
|
+
|
|
2487
|
+
if (rc != STOOLAP_OK) {
|
|
2488
|
+
const char* msg = S.stmt_errmsg(cs->stmt);
|
|
2489
|
+
THROW(env, msg ? msg : "Statement query error");
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
return fetch_one_direct_cached(env, rows, cs);
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
/* stmtSql(external): string */
|
|
2496
|
+
static napi_value wrap_stmt_sql(napi_env env, napi_callback_info info) {
|
|
2497
|
+
size_t argc = 1;
|
|
2498
|
+
napi_value argv[1];
|
|
2499
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2500
|
+
|
|
2501
|
+
GET_STMT(env, argv[0], cs);
|
|
2502
|
+
const char* sql = S.stmt_sql(cs->stmt);
|
|
2503
|
+
napi_value val;
|
|
2504
|
+
napi_create_string_utf8(env, sql ? sql : "", NAPI_AUTO_LENGTH, &val);
|
|
2505
|
+
return val;
|
|
2506
|
+
}
|
|
2507
|
+
|
|
2508
|
+
/* stmtFinalize(external): void */
|
|
2509
|
+
static napi_value wrap_stmt_finalize(napi_env env, napi_callback_info info) {
|
|
2510
|
+
size_t argc = 1;
|
|
2511
|
+
napi_value argv[1];
|
|
2512
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2513
|
+
|
|
2514
|
+
GET_STMT(env, argv[0], cs);
|
|
2515
|
+
if (cs->stmt) {
|
|
2516
|
+
S.stmt_finalize(cs->stmt);
|
|
2517
|
+
cs->stmt = NULL; /* prevent double-free from GC destructor */
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
napi_value undef;
|
|
2521
|
+
napi_get_undefined(env, &undef);
|
|
2522
|
+
return undef;
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
/* txBegin(external): external */
|
|
2526
|
+
static napi_value fn_tx_begin(napi_env env, napi_callback_info info) {
|
|
2527
|
+
size_t argc = 1;
|
|
2528
|
+
napi_value argv[1];
|
|
2529
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2530
|
+
|
|
2531
|
+
StoolapDB* db;
|
|
2532
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
2533
|
+
|
|
2534
|
+
StoolapTx* tx = NULL;
|
|
2535
|
+
int32_t rc = S.begin(db, &tx);
|
|
2536
|
+
if (rc != STOOLAP_OK) {
|
|
2537
|
+
const char* msg = S.errmsg(db);
|
|
2538
|
+
THROW(env, msg ? msg : "Begin error");
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
napi_value ext;
|
|
2542
|
+
napi_create_external(env, tx, NULL, NULL, &ext);
|
|
2543
|
+
return ext;
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
/* txBeginAsync(external): Promise<external> */
|
|
2547
|
+
static napi_value fn_tx_begin_async(napi_env env, napi_callback_info info) {
|
|
2548
|
+
size_t argc = 1;
|
|
2549
|
+
napi_value argv[1];
|
|
2550
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2551
|
+
|
|
2552
|
+
AsyncTxBeginTask* task = calloc(1, sizeof(AsyncTxBeginTask));
|
|
2553
|
+
if (!task) THROW(env, "Out of memory");
|
|
2554
|
+
napi_get_value_external(env, argv[0], (void**)&task->db);
|
|
2555
|
+
|
|
2556
|
+
napi_value promise = queue_async_work(
|
|
2557
|
+
env, task, &task->base, "txBeginAsync", execute_tx_begin_async, complete_tx_begin_async);
|
|
2558
|
+
if (!promise) {
|
|
2559
|
+
free(task);
|
|
2560
|
+
THROW(env, "Failed to queue txBeginAsync");
|
|
2561
|
+
}
|
|
2562
|
+
return promise;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
/* txExec(external, sql, params?): { changes } */
|
|
2566
|
+
static napi_value wrap_tx_exec(napi_env env, napi_callback_info info) {
|
|
2567
|
+
size_t argc = 3;
|
|
2568
|
+
napi_value argv[3];
|
|
2569
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2570
|
+
|
|
2571
|
+
StoolapTx* tx;
|
|
2572
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2573
|
+
|
|
2574
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
2575
|
+
|
|
2576
|
+
int64_t affected = 0;
|
|
2577
|
+
int32_t rc;
|
|
2578
|
+
|
|
2579
|
+
if (argc > 2) {
|
|
2580
|
+
napi_valuetype vt;
|
|
2581
|
+
napi_typeof(env, argv[2], &vt);
|
|
2582
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2583
|
+
rc = S.tx_exec(tx, sql, &affected);
|
|
2584
|
+
} else {
|
|
2585
|
+
uint32_t len;
|
|
2586
|
+
napi_get_array_length(env, argv[2], &len);
|
|
2587
|
+
if (len == 0) {
|
|
2588
|
+
rc = S.tx_exec(tx, sql, &affected);
|
|
2589
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2590
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2591
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2592
|
+
int tbuf_cnt = 0;
|
|
2593
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2594
|
+
napi_value elem;
|
|
2595
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
2596
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2597
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2598
|
+
THROW(env, "Failed to convert parameters");
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
rc = S.tx_exec_p(tx, sql, vals, (int32_t)len, &affected);
|
|
2602
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2603
|
+
} else {
|
|
2604
|
+
StoolapValue* vals; int32_t cnt;
|
|
2605
|
+
char** tbufs; int tbuf_cnt;
|
|
2606
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2607
|
+
THROW(env, "Failed to convert parameters");
|
|
2608
|
+
rc = S.tx_exec_p(tx, sql, vals, cnt, &affected);
|
|
2609
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
} else {
|
|
2613
|
+
rc = S.tx_exec(tx, sql, &affected);
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
if (rc != STOOLAP_OK) {
|
|
2617
|
+
const char* msg = S.tx_errmsg(tx);
|
|
2618
|
+
THROW(env, msg ? msg : "Transaction exec error");
|
|
2619
|
+
}
|
|
2620
|
+
|
|
2621
|
+
return make_changes(env, affected);
|
|
2622
|
+
}
|
|
2623
|
+
|
|
2624
|
+
/* txExecAsync(external, sql, params?): Promise<{ changes }> */
|
|
2625
|
+
static napi_value wrap_tx_exec_async(napi_env env, napi_callback_info info) {
|
|
2626
|
+
size_t argc = 3;
|
|
2627
|
+
napi_value argv[3];
|
|
2628
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2629
|
+
|
|
2630
|
+
AsyncExecTask* task = calloc(1, sizeof(AsyncExecTask));
|
|
2631
|
+
if (!task) THROW(env, "Out of memory");
|
|
2632
|
+
|
|
2633
|
+
task->kind = ASYNC_EXEC_TX;
|
|
2634
|
+
napi_get_value_external(env, argv[0], &task->target);
|
|
2635
|
+
if (copy_napi_string(env, argv[1], &task->sql) != 0) {
|
|
2636
|
+
free(task);
|
|
2637
|
+
THROW(env, "Out of memory");
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
if (argc > 2) {
|
|
2641
|
+
if (convert_params_copy(env, argv[2], &task->vals, &task->val_count,
|
|
2642
|
+
&task->heap_bufs, &task->heap_buf_count) != 0) {
|
|
2643
|
+
free(task->sql);
|
|
2644
|
+
free(task);
|
|
2645
|
+
THROW(env, "Failed to convert parameters");
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
napi_value promise = queue_async_work(
|
|
2650
|
+
env, task, &task->base, "txExecAsync", execute_async_exec, complete_async_exec);
|
|
2651
|
+
if (!promise) {
|
|
2652
|
+
free(task->sql);
|
|
2653
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
2654
|
+
free(task);
|
|
2655
|
+
THROW(env, "Failed to queue txExecAsync");
|
|
2656
|
+
}
|
|
2657
|
+
return promise;
|
|
2658
|
+
}
|
|
2659
|
+
|
|
2660
|
+
/* txQueryBuf(external, sql, params?): ArrayBuffer | null */
|
|
2661
|
+
static napi_value wrap_tx_query_buf(napi_env env, napi_callback_info info) {
|
|
2662
|
+
size_t argc = 3;
|
|
2663
|
+
napi_value argv[3];
|
|
2664
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2665
|
+
|
|
2666
|
+
StoolapTx* tx;
|
|
2667
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2668
|
+
|
|
2669
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
2670
|
+
|
|
2671
|
+
StoolapRows* rows = NULL;
|
|
2672
|
+
int32_t rc;
|
|
2673
|
+
|
|
2674
|
+
if (argc > 2) {
|
|
2675
|
+
napi_valuetype vt;
|
|
2676
|
+
napi_typeof(env, argv[2], &vt);
|
|
2677
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2678
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2679
|
+
} else {
|
|
2680
|
+
uint32_t len;
|
|
2681
|
+
napi_get_array_length(env, argv[2], &len);
|
|
2682
|
+
if (len == 0) {
|
|
2683
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2684
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2685
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2686
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2687
|
+
int tbuf_cnt = 0;
|
|
2688
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2689
|
+
napi_value elem;
|
|
2690
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
2691
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2692
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2693
|
+
THROW(env, "Failed to convert parameters");
|
|
2694
|
+
}
|
|
2695
|
+
}
|
|
2696
|
+
rc = S.tx_query_p(tx, sql, vals, (int32_t)len, &rows);
|
|
2697
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2698
|
+
} else {
|
|
2699
|
+
StoolapValue* vals; int32_t cnt;
|
|
2700
|
+
char** tbufs; int tbuf_cnt;
|
|
2701
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2702
|
+
THROW(env, "Failed to convert parameters");
|
|
2703
|
+
rc = S.tx_query_p(tx, sql, vals, cnt, &rows);
|
|
2704
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2707
|
+
} else {
|
|
2708
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
if (rc != STOOLAP_OK) {
|
|
2712
|
+
const char* msg = S.tx_errmsg(tx);
|
|
2713
|
+
THROW(env, msg ? msg : "Transaction query error");
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
return fetch_as_arraybuffer(env, rows);
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
/* txQueryBufAsync(external, sql, params?): Promise<ArrayBuffer | null> */
|
|
2720
|
+
static napi_value wrap_tx_query_buf_async(napi_env env, napi_callback_info info) {
|
|
2721
|
+
size_t argc = 3;
|
|
2722
|
+
napi_value argv[3];
|
|
2723
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2724
|
+
|
|
2725
|
+
AsyncQueryTask* task = calloc(1, sizeof(AsyncQueryTask));
|
|
2726
|
+
if (!task) THROW(env, "Out of memory");
|
|
2727
|
+
|
|
2728
|
+
task->kind = ASYNC_QUERY_TX;
|
|
2729
|
+
napi_get_value_external(env, argv[0], &task->target);
|
|
2730
|
+
if (copy_napi_string(env, argv[1], &task->sql) != 0) {
|
|
2731
|
+
free(task);
|
|
2732
|
+
THROW(env, "Out of memory");
|
|
2733
|
+
}
|
|
2734
|
+
|
|
2735
|
+
if (argc > 2) {
|
|
2736
|
+
if (convert_params_copy(env, argv[2], &task->vals, &task->val_count,
|
|
2737
|
+
&task->heap_bufs, &task->heap_buf_count) != 0) {
|
|
2738
|
+
free(task->sql);
|
|
2739
|
+
free(task);
|
|
2740
|
+
THROW(env, "Failed to convert parameters");
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
napi_value promise = queue_async_work(
|
|
2745
|
+
env, task, &task->base, "txQueryBufAsync", execute_async_query, complete_async_query);
|
|
2746
|
+
if (!promise) {
|
|
2747
|
+
free(task->sql);
|
|
2748
|
+
free_params(task->vals, task->heap_bufs, task->heap_buf_count);
|
|
2749
|
+
free(task);
|
|
2750
|
+
THROW(env, "Failed to queue txQueryBufAsync");
|
|
2751
|
+
}
|
|
2752
|
+
return promise;
|
|
2753
|
+
}
|
|
2754
|
+
|
|
2755
|
+
/* txQuery(external, sql, params?): Array | ArrayBuffer */
|
|
2756
|
+
static napi_value wrap_tx_query(napi_env env, napi_callback_info info) {
|
|
2757
|
+
size_t argc = 3;
|
|
2758
|
+
napi_value argv[3];
|
|
2759
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2760
|
+
|
|
2761
|
+
StoolapTx* tx;
|
|
2762
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2763
|
+
|
|
2764
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
2765
|
+
|
|
2766
|
+
StoolapRows* rows = NULL;
|
|
2767
|
+
int32_t rc;
|
|
2768
|
+
|
|
2769
|
+
if (argc > 2) {
|
|
2770
|
+
napi_valuetype vt;
|
|
2771
|
+
napi_typeof(env, argv[2], &vt);
|
|
2772
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2773
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2774
|
+
} else {
|
|
2775
|
+
uint32_t len;
|
|
2776
|
+
napi_get_array_length(env, argv[2], &len);
|
|
2777
|
+
if (len == 0) {
|
|
2778
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2779
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2780
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2781
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2782
|
+
int tbuf_cnt = 0;
|
|
2783
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2784
|
+
napi_value elem;
|
|
2785
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
2786
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2787
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2788
|
+
THROW(env, "Failed to convert parameters");
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
rc = S.tx_query_p(tx, sql, vals, (int32_t)len, &rows);
|
|
2792
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2793
|
+
} else {
|
|
2794
|
+
StoolapValue* vals; int32_t cnt;
|
|
2795
|
+
char** tbufs; int tbuf_cnt;
|
|
2796
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2797
|
+
THROW(env, "Failed to convert parameters");
|
|
2798
|
+
rc = S.tx_query_p(tx, sql, vals, cnt, &rows);
|
|
2799
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
} else {
|
|
2803
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
if (rc != STOOLAP_OK) {
|
|
2807
|
+
const char* msg = S.tx_errmsg(tx);
|
|
2808
|
+
THROW(env, msg ? msg : "Transaction query error");
|
|
2809
|
+
}
|
|
2810
|
+
|
|
2811
|
+
return fetch_result(env, rows);
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
/* txQueryOne(external, sql, params?): Object | null */
|
|
2815
|
+
static napi_value wrap_tx_query_one(napi_env env, napi_callback_info info) {
|
|
2816
|
+
size_t argc = 3;
|
|
2817
|
+
napi_value argv[3];
|
|
2818
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2819
|
+
|
|
2820
|
+
StoolapTx* tx;
|
|
2821
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2822
|
+
|
|
2823
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
2824
|
+
|
|
2825
|
+
StoolapRows* rows = NULL;
|
|
2826
|
+
int32_t rc;
|
|
2827
|
+
|
|
2828
|
+
if (argc > 2) {
|
|
2829
|
+
napi_valuetype vt;
|
|
2830
|
+
napi_typeof(env, argv[2], &vt);
|
|
2831
|
+
if (vt == napi_undefined || vt == napi_null) {
|
|
2832
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2833
|
+
} else {
|
|
2834
|
+
uint32_t len;
|
|
2835
|
+
napi_get_array_length(env, argv[2], &len);
|
|
2836
|
+
if (len == 0) {
|
|
2837
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2838
|
+
} else if (len <= MAX_STACK_PARAMS) {
|
|
2839
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
2840
|
+
char* tbufs[MAX_STACK_PARAMS * 2];
|
|
2841
|
+
int tbuf_cnt = 0;
|
|
2842
|
+
for (uint32_t i = 0; i < len; i++) {
|
|
2843
|
+
napi_value elem;
|
|
2844
|
+
napi_get_element(env, argv[2], i, &elem);
|
|
2845
|
+
if (js_to_value(env, elem, &vals[i], tbufs, &tbuf_cnt) != 0) {
|
|
2846
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2847
|
+
THROW(env, "Failed to convert parameters");
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
rc = S.tx_query_p(tx, sql, vals, (int32_t)len, &rows);
|
|
2851
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2852
|
+
} else {
|
|
2853
|
+
StoolapValue* vals; int32_t cnt;
|
|
2854
|
+
char** tbufs; int tbuf_cnt;
|
|
2855
|
+
if (convert_params(env, argv[2], &vals, &cnt, &tbufs, &tbuf_cnt) != 0)
|
|
2856
|
+
THROW(env, "Failed to convert parameters");
|
|
2857
|
+
rc = S.tx_query_p(tx, sql, vals, cnt, &rows);
|
|
2858
|
+
free_params(vals, tbufs, tbuf_cnt);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
} else {
|
|
2862
|
+
rc = S.tx_query(tx, sql, &rows);
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2865
|
+
if (rc != STOOLAP_OK) {
|
|
2866
|
+
const char* msg = S.tx_errmsg(tx);
|
|
2867
|
+
THROW(env, msg ? msg : "Transaction query error");
|
|
2868
|
+
}
|
|
2869
|
+
|
|
2870
|
+
return fetch_one(env, rows);
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
/* txCommit(external): void */
|
|
2874
|
+
static napi_value wrap_tx_commit(napi_env env, napi_callback_info info) {
|
|
2875
|
+
size_t argc = 1;
|
|
2876
|
+
napi_value argv[1];
|
|
2877
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2878
|
+
|
|
2879
|
+
StoolapTx* tx;
|
|
2880
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2881
|
+
|
|
2882
|
+
int32_t rc = S.tx_commit(tx);
|
|
2883
|
+
if (rc != STOOLAP_OK) THROW(env, "Failed to commit transaction");
|
|
2884
|
+
|
|
2885
|
+
napi_value undef;
|
|
2886
|
+
napi_get_undefined(env, &undef);
|
|
2887
|
+
return undef;
|
|
2888
|
+
}
|
|
2889
|
+
|
|
2890
|
+
/* txCommitAsync(external): Promise<void> */
|
|
2891
|
+
static napi_value wrap_tx_commit_async(napi_env env, napi_callback_info info) {
|
|
2892
|
+
size_t argc = 1;
|
|
2893
|
+
napi_value argv[1];
|
|
2894
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2895
|
+
|
|
2896
|
+
AsyncTxFinishTask* task = calloc(1, sizeof(AsyncTxFinishTask));
|
|
2897
|
+
if (!task) THROW(env, "Out of memory");
|
|
2898
|
+
task->kind = ASYNC_TX_COMMIT;
|
|
2899
|
+
napi_get_value_external(env, argv[0], (void**)&task->tx);
|
|
2900
|
+
|
|
2901
|
+
napi_value promise = queue_async_work(
|
|
2902
|
+
env, task, &task->base, "txCommitAsync", execute_tx_finish_async, complete_tx_finish_async);
|
|
2903
|
+
if (!promise) {
|
|
2904
|
+
free(task);
|
|
2905
|
+
THROW(env, "Failed to queue txCommitAsync");
|
|
2906
|
+
}
|
|
2907
|
+
return promise;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
/* txRollback(external): void */
|
|
2911
|
+
static napi_value wrap_tx_rollback(napi_env env, napi_callback_info info) {
|
|
2912
|
+
size_t argc = 1;
|
|
2913
|
+
napi_value argv[1];
|
|
2914
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2915
|
+
|
|
2916
|
+
StoolapTx* tx;
|
|
2917
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2918
|
+
|
|
2919
|
+
int32_t rc = S.tx_rollback(tx);
|
|
2920
|
+
if (rc != STOOLAP_OK) THROW(env, "Failed to rollback transaction");
|
|
2921
|
+
|
|
2922
|
+
napi_value undef;
|
|
2923
|
+
napi_get_undefined(env, &undef);
|
|
2924
|
+
return undef;
|
|
2925
|
+
}
|
|
2926
|
+
|
|
2927
|
+
/* txRollbackAsync(external): Promise<void> */
|
|
2928
|
+
static napi_value wrap_tx_rollback_async(napi_env env, napi_callback_info info) {
|
|
2929
|
+
size_t argc = 1;
|
|
2930
|
+
napi_value argv[1];
|
|
2931
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2932
|
+
|
|
2933
|
+
AsyncTxFinishTask* task = calloc(1, sizeof(AsyncTxFinishTask));
|
|
2934
|
+
if (!task) THROW(env, "Out of memory");
|
|
2935
|
+
task->kind = ASYNC_TX_ROLLBACK;
|
|
2936
|
+
napi_get_value_external(env, argv[0], (void**)&task->tx);
|
|
2937
|
+
|
|
2938
|
+
napi_value promise = queue_async_work(
|
|
2939
|
+
env, task, &task->base, "txRollbackAsync", execute_tx_finish_async, complete_tx_finish_async);
|
|
2940
|
+
if (!promise) {
|
|
2941
|
+
free(task);
|
|
2942
|
+
THROW(env, "Failed to queue txRollbackAsync");
|
|
2943
|
+
}
|
|
2944
|
+
return promise;
|
|
2945
|
+
}
|
|
2946
|
+
|
|
2947
|
+
/* txExecBatch(external, sql, paramsArrayOfArrays): { changes }
|
|
2948
|
+
* Batch exec in C — copies SQL once, pre-allocates param buffers,
|
|
2949
|
+
* loops param sets in C. Eliminates N × JS↔C crossings + N×malloc/free. */
|
|
2950
|
+
static napi_value wrap_tx_exec_batch(napi_env env, napi_callback_info info) {
|
|
2951
|
+
size_t argc = 3;
|
|
2952
|
+
napi_value argv[3];
|
|
2953
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
2954
|
+
|
|
2955
|
+
StoolapTx* tx;
|
|
2956
|
+
napi_get_value_external(env, argv[0], (void**)&tx);
|
|
2957
|
+
|
|
2958
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
2959
|
+
|
|
2960
|
+
uint32_t batch_len;
|
|
2961
|
+
napi_get_array_length(env, argv[2], &batch_len);
|
|
2962
|
+
if (batch_len == 0) return make_changes(env, 0);
|
|
2963
|
+
|
|
2964
|
+
/* Get param count from first element — pre-allocate ONCE */
|
|
2965
|
+
napi_value first_arr;
|
|
2966
|
+
napi_get_element(env, argv[2], 0, &first_arr);
|
|
2967
|
+
uint32_t param_count;
|
|
2968
|
+
napi_get_array_length(env, first_arr, ¶m_count);
|
|
2969
|
+
|
|
2970
|
+
StoolapValue* vals = calloc(param_count, sizeof(StoolapValue));
|
|
2971
|
+
char** tbufs = calloc(param_count * 2, sizeof(char*));
|
|
2972
|
+
|
|
2973
|
+
int64_t total_affected = 0;
|
|
2974
|
+
|
|
2975
|
+
for (uint32_t i = 0; i < batch_len; i++) {
|
|
2976
|
+
napi_value params_arr;
|
|
2977
|
+
napi_get_element(env, argv[2], i, ¶ms_arr);
|
|
2978
|
+
|
|
2979
|
+
/* Convert params into pre-allocated buffers */
|
|
2980
|
+
int tbuf_cnt = 0;
|
|
2981
|
+
for (uint32_t j = 0; j < param_count; j++) {
|
|
2982
|
+
napi_value elem;
|
|
2983
|
+
napi_get_element(env, params_arr, j, &elem);
|
|
2984
|
+
if (js_to_value(env, elem, &vals[j], tbufs, &tbuf_cnt) != 0) {
|
|
2985
|
+
/* Cleanup on error */
|
|
2986
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
2987
|
+
free(vals); free(tbufs);
|
|
2988
|
+
THROW(env, "Failed to convert parameters");
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
|
|
2992
|
+
int64_t affected = 0;
|
|
2993
|
+
int32_t rc = S.tx_exec_p(tx, sql, vals, (int32_t)param_count, &affected);
|
|
2994
|
+
|
|
2995
|
+
/* Free only text buffers, keep vals/tbufs arrays for reuse */
|
|
2996
|
+
for (int k = 0; k < tbuf_cnt; k++) { free(tbufs[k]); tbufs[k] = NULL; }
|
|
2997
|
+
|
|
2998
|
+
if (rc != STOOLAP_OK) {
|
|
2999
|
+
free(vals); free(tbufs);
|
|
3000
|
+
const char* msg = S.tx_errmsg(tx);
|
|
3001
|
+
THROW(env, msg ? msg : "Transaction batch exec error");
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
total_affected += affected;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
free(vals);
|
|
3008
|
+
free(tbufs);
|
|
3009
|
+
return make_changes(env, total_affected);
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
/* stmtExecBatch(external, paramsArrayOfArrays): { changes }
|
|
3013
|
+
* Batch exec via prepared statement — pre-allocated buffers, loops in C. */
|
|
3014
|
+
static napi_value wrap_stmt_exec_batch(napi_env env, napi_callback_info info) {
|
|
3015
|
+
size_t argc = 2;
|
|
3016
|
+
napi_value argv[2];
|
|
3017
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
3018
|
+
|
|
3019
|
+
GET_STMT(env, argv[0], cs);
|
|
3020
|
+
StoolapStmt* stmt = cs->stmt;
|
|
3021
|
+
|
|
3022
|
+
uint32_t batch_len;
|
|
3023
|
+
napi_get_array_length(env, argv[1], &batch_len);
|
|
3024
|
+
if (batch_len == 0) return make_changes(env, 0);
|
|
3025
|
+
|
|
3026
|
+
/* Get param count from first element — pre-allocate ONCE */
|
|
3027
|
+
napi_value first_arr;
|
|
3028
|
+
napi_get_element(env, argv[1], 0, &first_arr);
|
|
3029
|
+
uint32_t param_count;
|
|
3030
|
+
napi_get_array_length(env, first_arr, ¶m_count);
|
|
3031
|
+
|
|
3032
|
+
StoolapValue* vals = calloc(param_count, sizeof(StoolapValue));
|
|
3033
|
+
char** tbufs = calloc(param_count * 2, sizeof(char*));
|
|
3034
|
+
|
|
3035
|
+
int64_t total_affected = 0;
|
|
3036
|
+
|
|
3037
|
+
for (uint32_t i = 0; i < batch_len; i++) {
|
|
3038
|
+
napi_value params_arr;
|
|
3039
|
+
napi_get_element(env, argv[1], i, ¶ms_arr);
|
|
3040
|
+
|
|
3041
|
+
int tbuf_cnt = 0;
|
|
3042
|
+
for (uint32_t j = 0; j < param_count; j++) {
|
|
3043
|
+
napi_value elem;
|
|
3044
|
+
napi_get_element(env, params_arr, j, &elem);
|
|
3045
|
+
if (js_to_value(env, elem, &vals[j], tbufs, &tbuf_cnt) != 0) {
|
|
3046
|
+
for (int k = 0; k < tbuf_cnt; k++) free(tbufs[k]);
|
|
3047
|
+
free(vals); free(tbufs);
|
|
3048
|
+
THROW(env, "Failed to convert parameters");
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
|
|
3052
|
+
int64_t affected = 0;
|
|
3053
|
+
int32_t rc = S.stmt_exec(stmt, vals, (int32_t)param_count, &affected);
|
|
3054
|
+
|
|
3055
|
+
for (int k = 0; k < tbuf_cnt; k++) { free(tbufs[k]); tbufs[k] = NULL; }
|
|
3056
|
+
|
|
3057
|
+
if (rc != STOOLAP_OK) {
|
|
3058
|
+
free(vals); free(tbufs);
|
|
3059
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
3060
|
+
THROW(env, msg ? msg : "Statement batch exec error");
|
|
3061
|
+
}
|
|
3062
|
+
|
|
3063
|
+
total_affected += affected;
|
|
3064
|
+
}
|
|
3065
|
+
|
|
3066
|
+
free(vals);
|
|
3067
|
+
free(tbufs);
|
|
3068
|
+
return make_changes(env, total_affected);
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
/* ---- Binary param buffer protocol ---- */
|
|
3072
|
+
/* JS encodes params to a Buffer, C reads directly — eliminates per-param N-API calls.
|
|
3073
|
+
* Format per param: [uint8 tag][value bytes]
|
|
3074
|
+
* 0=NULL, 1=INT32(4B), 2=F64(8B), 3=TEXT(u32 len + data + \0), 4=BOOL(1B), 5=INT64(8B) */
|
|
3075
|
+
|
|
3076
|
+
#define PBUF_NULL 0
|
|
3077
|
+
#define PBUF_I32 1
|
|
3078
|
+
#define PBUF_F64 2
|
|
3079
|
+
#define PBUF_TEXT 3
|
|
3080
|
+
#define PBUF_BOOL 4
|
|
3081
|
+
#define PBUF_I64 5
|
|
3082
|
+
|
|
3083
|
+
static int decode_params_buf(const uint8_t* buf, size_t buf_len,
|
|
3084
|
+
StoolapValue* vals, int max_params,
|
|
3085
|
+
size_t* consumed) {
|
|
3086
|
+
const uint8_t* ptr = buf;
|
|
3087
|
+
const uint8_t* end = buf + buf_len;
|
|
3088
|
+
int count = 0;
|
|
3089
|
+
while (ptr < end && count < max_params) {
|
|
3090
|
+
uint8_t tag = *ptr++;
|
|
3091
|
+
switch (tag) {
|
|
3092
|
+
case PBUF_NULL:
|
|
3093
|
+
vals[count].value_type = T_NULL;
|
|
3094
|
+
vals[count]._padding = 0;
|
|
3095
|
+
break;
|
|
3096
|
+
case PBUF_I32: {
|
|
3097
|
+
if (ptr + 4 > end) return -1;
|
|
3098
|
+
int32_t i32; memcpy(&i32, ptr, 4); ptr += 4;
|
|
3099
|
+
vals[count].value_type = T_INTEGER;
|
|
3100
|
+
vals[count]._padding = 0;
|
|
3101
|
+
vals[count].v.integer = (int64_t)i32;
|
|
3102
|
+
break;
|
|
3103
|
+
}
|
|
3104
|
+
case PBUF_F64: {
|
|
3105
|
+
if (ptr + 8 > end) return -1;
|
|
3106
|
+
double d; memcpy(&d, ptr, 8); ptr += 8;
|
|
3107
|
+
int64_t i64 = (int64_t)d;
|
|
3108
|
+
if ((double)i64 == d && d >= -9007199254740992.0 && d <= 9007199254740992.0) {
|
|
3109
|
+
vals[count].value_type = T_INTEGER;
|
|
3110
|
+
vals[count]._padding = 0;
|
|
3111
|
+
vals[count].v.integer = i64;
|
|
3112
|
+
} else {
|
|
3113
|
+
vals[count].value_type = T_FLOAT;
|
|
3114
|
+
vals[count]._padding = 0;
|
|
3115
|
+
vals[count].v.float64 = d;
|
|
3116
|
+
}
|
|
3117
|
+
break;
|
|
3118
|
+
}
|
|
3119
|
+
case PBUF_TEXT: {
|
|
3120
|
+
if (ptr + 4 > end) return -1;
|
|
3121
|
+
uint32_t len; memcpy(&len, ptr, 4); ptr += 4;
|
|
3122
|
+
if (ptr + len + 1 > end) return -1;
|
|
3123
|
+
vals[count].value_type = T_TEXT;
|
|
3124
|
+
vals[count]._padding = 0;
|
|
3125
|
+
vals[count].v.text.ptr = (const char*)ptr;
|
|
3126
|
+
vals[count].v.text.len = (int64_t)len;
|
|
3127
|
+
ptr += len + 1; /* skip null terminator */
|
|
3128
|
+
break;
|
|
3129
|
+
}
|
|
3130
|
+
case PBUF_BOOL:
|
|
3131
|
+
if (ptr >= end) return -1;
|
|
3132
|
+
vals[count].value_type = T_BOOLEAN;
|
|
3133
|
+
vals[count]._padding = 0;
|
|
3134
|
+
vals[count].v.boolean = *ptr++;
|
|
3135
|
+
break;
|
|
3136
|
+
case PBUF_I64: {
|
|
3137
|
+
if (ptr + 8 > end) return -1;
|
|
3138
|
+
int64_t i64; memcpy(&i64, ptr, 8); ptr += 8;
|
|
3139
|
+
vals[count].value_type = T_INTEGER;
|
|
3140
|
+
vals[count]._padding = 0;
|
|
3141
|
+
vals[count].v.integer = i64;
|
|
3142
|
+
break;
|
|
3143
|
+
}
|
|
3144
|
+
default: return -1;
|
|
3145
|
+
}
|
|
3146
|
+
count++;
|
|
3147
|
+
}
|
|
3148
|
+
if (consumed) *consumed = (size_t)(ptr - buf);
|
|
3149
|
+
return count;
|
|
3150
|
+
}
|
|
3151
|
+
|
|
3152
|
+
/* dbExecBatchBuf(db, sql, buffer) → { changes }
|
|
3153
|
+
* Batch format: [u32 row_count][u8 params_per_row][row0..rowN params] */
|
|
3154
|
+
static napi_value wrap_db_exec_batch_buf(napi_env env, napi_callback_info info) {
|
|
3155
|
+
size_t argc = 3;
|
|
3156
|
+
napi_value argv[3];
|
|
3157
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
3158
|
+
StoolapDB* db;
|
|
3159
|
+
napi_get_value_external(env, argv[0], (void**)&db);
|
|
3160
|
+
GET_SQL_STRING(env, argv[1], sql);
|
|
3161
|
+
void* buf_data; size_t buf_len;
|
|
3162
|
+
napi_get_buffer_info(env, argv[2], &buf_data, &buf_len);
|
|
3163
|
+
const uint8_t* ptr = (const uint8_t*)buf_data;
|
|
3164
|
+
const uint8_t* end = ptr + buf_len;
|
|
3165
|
+
if (ptr + 5 > end) THROW(env, "Batch buffer too short");
|
|
3166
|
+
uint32_t row_count; memcpy(&row_count, ptr, 4); ptr += 4;
|
|
3167
|
+
uint8_t ppr = *ptr++;
|
|
3168
|
+
if (row_count == 0) return make_changes(env, 0);
|
|
3169
|
+
StoolapTx* tx = NULL;
|
|
3170
|
+
int32_t rc = S.begin(db, &tx);
|
|
3171
|
+
if (rc != STOOLAP_OK) { const char* msg = S.errmsg(db); THROW(env, msg ? msg : "Begin error"); }
|
|
3172
|
+
StoolapStmt* stmt = NULL;
|
|
3173
|
+
rc = S.prepare(db, sql, &stmt);
|
|
3174
|
+
if (rc != STOOLAP_OK) { S.tx_rollback(tx); const char* msg = S.errmsg(db); THROW(env, msg ? msg : "Prepare error"); }
|
|
3175
|
+
int64_t total = 0;
|
|
3176
|
+
for (uint32_t i = 0; i < row_count; i++) {
|
|
3177
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
3178
|
+
size_t consumed = 0;
|
|
3179
|
+
int decoded = decode_params_buf(ptr, (size_t)(end - ptr), vals, ppr, &consumed);
|
|
3180
|
+
if (decoded != ppr) { S.stmt_finalize(stmt); S.tx_rollback(tx); THROW(env, "Batch param decode error"); }
|
|
3181
|
+
ptr += consumed;
|
|
3182
|
+
int64_t affected = 0;
|
|
3183
|
+
rc = S.stmt_exec(stmt, vals, decoded, &affected);
|
|
3184
|
+
if (rc != STOOLAP_OK) { const char* msg = S.stmt_errmsg(stmt); S.stmt_finalize(stmt); S.tx_rollback(tx); THROW(env, msg ? msg : "Batch exec error"); }
|
|
3185
|
+
total += affected;
|
|
3186
|
+
}
|
|
3187
|
+
S.stmt_finalize(stmt);
|
|
3188
|
+
rc = S.tx_commit(tx);
|
|
3189
|
+
if (rc != STOOLAP_OK) { const char* msg = S.errmsg(db); THROW(env, msg ? msg : "Commit error"); }
|
|
3190
|
+
return make_changes(env, total);
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
/* stmtExecBatchBuf(external, buffer): { changes }
|
|
3194
|
+
* Binary batch protocol for prepared statements — eliminates per-param N-API calls. */
|
|
3195
|
+
static napi_value wrap_stmt_exec_batch_buf(napi_env env, napi_callback_info info) {
|
|
3196
|
+
size_t argc = 2;
|
|
3197
|
+
napi_value argv[2];
|
|
3198
|
+
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
|
|
3199
|
+
|
|
3200
|
+
GET_STMT(env, argv[0], cs);
|
|
3201
|
+
StoolapStmt* stmt = cs->stmt;
|
|
3202
|
+
|
|
3203
|
+
void* buf_data; size_t buf_len;
|
|
3204
|
+
napi_get_buffer_info(env, argv[1], &buf_data, &buf_len);
|
|
3205
|
+
const uint8_t* ptr = (const uint8_t*)buf_data;
|
|
3206
|
+
const uint8_t* end = ptr + buf_len;
|
|
3207
|
+
|
|
3208
|
+
if (ptr + 5 > end) THROW(env, "Batch buffer too short");
|
|
3209
|
+
uint32_t row_count; memcpy(&row_count, ptr, 4); ptr += 4;
|
|
3210
|
+
uint8_t ppr = *ptr++;
|
|
3211
|
+
if (row_count == 0) return make_changes(env, 0);
|
|
3212
|
+
|
|
3213
|
+
int64_t total = 0;
|
|
3214
|
+
for (uint32_t i = 0; i < row_count; i++) {
|
|
3215
|
+
StoolapValue vals[MAX_STACK_PARAMS];
|
|
3216
|
+
size_t consumed = 0;
|
|
3217
|
+
int decoded = decode_params_buf(ptr, (size_t)(end - ptr), vals, ppr, &consumed);
|
|
3218
|
+
if (decoded != ppr) THROW(env, "Batch param decode error");
|
|
3219
|
+
ptr += consumed;
|
|
3220
|
+
int64_t affected = 0;
|
|
3221
|
+
int32_t rc = S.stmt_exec(stmt, vals, decoded, &affected);
|
|
3222
|
+
if (rc != STOOLAP_OK) {
|
|
3223
|
+
const char* msg = S.stmt_errmsg(stmt);
|
|
3224
|
+
THROW(env, msg ? msg : "Statement batch exec error");
|
|
3225
|
+
}
|
|
3226
|
+
total += affected;
|
|
3227
|
+
}
|
|
3228
|
+
return make_changes(env, total);
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
/* ============================================================
|
|
3232
|
+
* Module initialization
|
|
3233
|
+
* ============================================================ */
|
|
3234
|
+
|
|
3235
|
+
#define EXPORT_FN(env, exports, name, fn) do { \
|
|
3236
|
+
napi_value _fn; \
|
|
3237
|
+
napi_create_function(env, name, NAPI_AUTO_LENGTH, fn, NULL, &_fn); \
|
|
3238
|
+
napi_set_named_property(env, exports, name, _fn); \
|
|
3239
|
+
} while(0)
|
|
3240
|
+
|
|
3241
|
+
static napi_value Init(napi_env env, napi_value exports) {
|
|
3242
|
+
EXPORT_FN(env, exports, "loadLibrary", fn_load_library);
|
|
3243
|
+
EXPORT_FN(env, exports, "dbOpen", fn_db_open);
|
|
3244
|
+
EXPORT_FN(env, exports, "dbOpenAsync", fn_db_open_async);
|
|
3245
|
+
EXPORT_FN(env, exports, "dbClose", fn_db_close);
|
|
3246
|
+
EXPORT_FN(env, exports, "dbCloseAsync", fn_db_close_async);
|
|
3247
|
+
EXPORT_FN(env, exports, "dbExec", fn_db_exec);
|
|
3248
|
+
EXPORT_FN(env, exports, "dbExecAsync", fn_db_exec_async);
|
|
3249
|
+
EXPORT_FN(env, exports, "dbExecSimple", fn_db_exec_simple);
|
|
3250
|
+
EXPORT_FN(env, exports, "dbExecSimpleAsync", fn_db_exec_simple_async);
|
|
3251
|
+
EXPORT_FN(env, exports, "dbQueryBuf", fn_db_query_buf);
|
|
3252
|
+
EXPORT_FN(env, exports, "dbQueryBufAsync", fn_db_query_buf_async);
|
|
3253
|
+
EXPORT_FN(env, exports, "dbQuery", fn_db_query);
|
|
3254
|
+
EXPORT_FN(env, exports, "dbQueryOne", fn_db_query_one);
|
|
3255
|
+
EXPORT_FN(env, exports, "dbPrepare", fn_db_prepare);
|
|
3256
|
+
EXPORT_FN(env, exports, "stmtExec", wrap_stmt_exec);
|
|
3257
|
+
EXPORT_FN(env, exports, "stmtExecAsync", wrap_stmt_exec_async);
|
|
3258
|
+
EXPORT_FN(env, exports, "stmtQueryBuf", wrap_stmt_query_buf);
|
|
3259
|
+
EXPORT_FN(env, exports, "stmtQueryBufAsync", wrap_stmt_query_buf_async);
|
|
3260
|
+
EXPORT_FN(env, exports, "stmtQuery", wrap_stmt_query);
|
|
3261
|
+
EXPORT_FN(env, exports, "stmtQueryOne", wrap_stmt_query_one);
|
|
3262
|
+
EXPORT_FN(env, exports, "stmtSql", wrap_stmt_sql);
|
|
3263
|
+
EXPORT_FN(env, exports, "stmtExecNums", wrap_stmt_exec_nums);
|
|
3264
|
+
EXPORT_FN(env, exports, "stmtQueryNums", wrap_stmt_query_nums);
|
|
3265
|
+
EXPORT_FN(env, exports, "stmtQueryOneNums", wrap_stmt_query_one_nums);
|
|
3266
|
+
EXPORT_FN(env, exports, "stmtExecBatch", wrap_stmt_exec_batch);
|
|
3267
|
+
EXPORT_FN(env, exports, "stmtExecBatchBuf", wrap_stmt_exec_batch_buf);
|
|
3268
|
+
EXPORT_FN(env, exports, "stmtFinalize", wrap_stmt_finalize);
|
|
3269
|
+
EXPORT_FN(env, exports, "dbExecBatchBuf", wrap_db_exec_batch_buf);
|
|
3270
|
+
EXPORT_FN(env, exports, "txBegin", fn_tx_begin);
|
|
3271
|
+
EXPORT_FN(env, exports, "txBeginAsync", fn_tx_begin_async);
|
|
3272
|
+
EXPORT_FN(env, exports, "txExec", wrap_tx_exec);
|
|
3273
|
+
EXPORT_FN(env, exports, "txExecAsync", wrap_tx_exec_async);
|
|
3274
|
+
EXPORT_FN(env, exports, "txExecBatch", wrap_tx_exec_batch);
|
|
3275
|
+
EXPORT_FN(env, exports, "txQueryBuf", wrap_tx_query_buf);
|
|
3276
|
+
EXPORT_FN(env, exports, "txQueryBufAsync", wrap_tx_query_buf_async);
|
|
3277
|
+
EXPORT_FN(env, exports, "txQuery", wrap_tx_query);
|
|
3278
|
+
EXPORT_FN(env, exports, "txQueryOne", wrap_tx_query_one);
|
|
3279
|
+
EXPORT_FN(env, exports, "txCommit", wrap_tx_commit);
|
|
3280
|
+
EXPORT_FN(env, exports, "txCommitAsync", wrap_tx_commit_async);
|
|
3281
|
+
EXPORT_FN(env, exports, "txRollback", wrap_tx_rollback);
|
|
3282
|
+
EXPORT_FN(env, exports, "txRollbackAsync", wrap_tx_rollback_async);
|
|
3283
|
+
return exports;
|
|
3284
|
+
}
|
|
3285
|
+
|
|
3286
|
+
NAPI_MODULE(stoolap, Init)
|