@mimersql/node-mimer 1.0.0 → 1.0.2

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/src/helpers.cc ADDED
@@ -0,0 +1,440 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #include "helpers.h"
24
+ #include <cstring>
25
+ #include <sstream>
26
+ #include <cmath>
27
+ #include <climits>
28
+ #include <vector>
29
+ #include <string>
30
+
31
+ static constexpr size_t LOB_READ_CHUNK = 65536;
32
+ static constexpr size_t LOB_WRITE_CHUNK = 2 * 1024 * 1024; // 2 MB, well under ~10 MB API limit
33
+
34
+ /**
35
+ * Count the number of UTF-8 characters (code points) in a byte string.
36
+ */
37
+ static size_t Utf8CharCount(const char* s, size_t byteLen) {
38
+ size_t count = 0;
39
+ for (size_t i = 0; i < byteLen; ) {
40
+ unsigned char c = static_cast<unsigned char>(s[i]);
41
+ if (c < 0x80) i += 1;
42
+ else if ((c & 0xE0) == 0xC0) i += 2;
43
+ else if ((c & 0xF0) == 0xE0) i += 3;
44
+ else i += 4;
45
+ count++;
46
+ }
47
+ return count;
48
+ }
49
+
50
+ /**
51
+ * Create and throw a structured Mimer error.
52
+ * Sets error.mimerCode and error.operation on the JS Error object.
53
+ */
54
+ void ThrowMimerError(Napi::Env env, int rc, const std::string& operation,
55
+ const std::string& detail) {
56
+ std::ostringstream oss;
57
+ if (detail.empty()) {
58
+ oss << operation << " failed (code: " << rc << ")";
59
+ } else {
60
+ oss << operation << " failed: " << detail << " (code: " << rc << ")";
61
+ }
62
+
63
+ Napi::Error error = Napi::Error::New(env, oss.str());
64
+ error.Set("mimerCode", Napi::Number::New(env, rc));
65
+ error.Set("operation", Napi::String::New(env, operation));
66
+ error.ThrowAsJavaScriptException();
67
+ }
68
+
69
+ /**
70
+ * Map a Mimer type code (absolute value) to a human-readable SQL type name.
71
+ */
72
+ static const char* MimerTypeName(int absType) {
73
+ switch (absType) {
74
+ case MIMER_CHARACTER: return "CHARACTER";
75
+ case MIMER_CHARACTER_VARYING: return "CHARACTER VARYING";
76
+ case MIMER_NCHAR: return "NCHAR";
77
+ case MIMER_NCHAR_VARYING: return "NCHAR VARYING";
78
+ case MIMER_UTF8: return "NVARCHAR";
79
+ case MIMER_DECIMAL: return "DECIMAL";
80
+ case MIMER_NUMERIC: return "NUMERIC";
81
+ case MIMER_INTEGER: return "INTEGER";
82
+ case MIMER_UNSIGNED_INTEGER: return "INTEGER";
83
+ case MIMER_T_INTEGER: return "INTEGER";
84
+ case MIMER_T_UNSIGNED_INTEGER: return "INTEGER";
85
+ case MIMER_T_SMALLINT: return "SMALLINT";
86
+ case MIMER_T_UNSIGNED_SMALLINT:return "SMALLINT";
87
+ case MIMER_T_BIGINT: return "BIGINT";
88
+ case MIMER_T_UNSIGNED_BIGINT: return "BIGINT";
89
+ case MIMER_FLOAT: return "FLOAT";
90
+ case MIMER_T_FLOAT: return "FLOAT";
91
+ case MIMER_T_REAL: return "REAL";
92
+ case MIMER_T_DOUBLE: return "DOUBLE PRECISION";
93
+ case MIMER_BOOLEAN: return "BOOLEAN";
94
+ case MIMER_DATE: return "DATE";
95
+ case MIMER_TIME: return "TIME";
96
+ case MIMER_TIMESTAMP: return "TIMESTAMP";
97
+ case MIMER_BINARY: return "BINARY";
98
+ case MIMER_BINARY_VARYING: return "BINARY VARYING";
99
+ case MIMER_BLOB: return "BLOB";
100
+ case MIMER_CLOB: return "CLOB";
101
+ case MIMER_NCLOB: return "NCLOB";
102
+ case MIMER_BLOB_LOCATOR: return "BLOB";
103
+ case MIMER_CLOB_LOCATOR: return "CLOB";
104
+ case MIMER_NCLOB_LOCATOR: return "NCLOB";
105
+ case MIMER_NATIVE_SMALLINT:
106
+ case MIMER_NATIVE_SMALLINT_NULLABLE: return "SMALLINT";
107
+ case MIMER_NATIVE_INTEGER:
108
+ case MIMER_NATIVE_INTEGER_NULLABLE: return "INTEGER";
109
+ case MIMER_NATIVE_BIGINT:
110
+ case MIMER_NATIVE_BIGINT_NULLABLE: return "BIGINT";
111
+ case MIMER_NATIVE_REAL:
112
+ case MIMER_NATIVE_REAL_NULLABLE: return "REAL";
113
+ case MIMER_NATIVE_DOUBLE:
114
+ case MIMER_NATIVE_DOUBLE_NULLABLE: return "DOUBLE PRECISION";
115
+ case MIMER_NATIVE_BLOB:
116
+ case MIMER_NATIVE_BLOB_LOCATOR: return "BLOB";
117
+ case MIMER_NATIVE_CLOB:
118
+ case MIMER_NATIVE_CLOB_LOCATOR: return "CLOB";
119
+ case MIMER_NATIVE_NCLOB:
120
+ case MIMER_NATIVE_NCLOB_LOCATOR: return "NCLOB";
121
+ case MIMER_UUID: return "UUID";
122
+ default: {
123
+ // Interval types
124
+ if (absType >= MIMER_INTERVAL_YEAR && absType <= MIMER_INTERVAL_MINUTE_TO_SECOND) {
125
+ return "INTERVAL";
126
+ }
127
+ return "UNKNOWN";
128
+ }
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Build an array of column metadata objects from a prepared statement.
134
+ * Each element is { name, dataTypeCode, dataTypeName, nullable }.
135
+ * In the Mimer C API, a negative type code indicates the column is nullable.
136
+ */
137
+ Napi::Array BuildFieldsArray(Napi::Env env, MimerStatement stmt, int columnCount) {
138
+ Napi::Array fields = Napi::Array::New(env, columnCount);
139
+
140
+ for (int col = 1; col <= columnCount; col++) {
141
+ Napi::Object field = Napi::Object::New(env);
142
+
143
+ // Column name
144
+ char nameBuf[256];
145
+ MimerColumnName8(stmt, static_cast<int16_t>(col), nameBuf, sizeof(nameBuf));
146
+ field.Set("name", Napi::String::New(env, nameBuf));
147
+
148
+ // Column type — raw code from Mimer
149
+ // Negative type code means nullable for non-native types.
150
+ // Native types use even codes for nullable variants:
151
+ // MIMER_NATIVE_xxx (odd) = NOT NULL, MIMER_NATIVE_xxx_NULLABLE (even) = nullable
152
+ int rawType = MimerColumnType(stmt, static_cast<int16_t>(col));
153
+ field.Set("dataTypeCode", Napi::Number::New(env, rawType));
154
+
155
+ // Human-readable type name (uses absolute value)
156
+ int absType = rawType < 0 ? -rawType : rawType;
157
+ field.Set("dataTypeName", Napi::String::New(env, MimerTypeName(absType)));
158
+
159
+ // Determine nullability
160
+ bool nullable;
161
+ if (rawType < 0) {
162
+ // Non-native types: negative code means nullable
163
+ nullable = true;
164
+ } else if (absType == MIMER_NATIVE_SMALLINT_NULLABLE
165
+ || absType == MIMER_NATIVE_INTEGER_NULLABLE
166
+ || absType == MIMER_NATIVE_BIGINT_NULLABLE
167
+ || absType == MIMER_NATIVE_REAL_NULLABLE
168
+ || absType == MIMER_NATIVE_DOUBLE_NULLABLE) {
169
+ // Native types with explicit _NULLABLE variants
170
+ nullable = true;
171
+ } else {
172
+ nullable = false;
173
+ }
174
+ field.Set("nullable", Napi::Boolean::New(env, nullable));
175
+
176
+ fields.Set(static_cast<uint32_t>(col - 1), field);
177
+ }
178
+
179
+ return fields;
180
+ }
181
+
182
+ /**
183
+ * Bind a JavaScript array of parameters to a prepared Mimer statement.
184
+ * JS array is 0-indexed, Mimer parameters are 1-indexed.
185
+ */
186
+ void BindParameters(Napi::Env env, MimerStatement stmt, Napi::Array params) {
187
+ int paramCount = MimerParameterCount(stmt);
188
+ int providedCount = static_cast<int>(params.Length());
189
+
190
+ if (providedCount != paramCount) {
191
+ std::ostringstream detail;
192
+ detail << "statement expects " << paramCount
193
+ << " but " << providedCount << " were provided";
194
+ ThrowMimerError(env, 0, "BindParameters", detail.str());
195
+ return;
196
+ }
197
+
198
+ for (int i = 0; i < providedCount; i++) {
199
+ int16_t paramIndex = static_cast<int16_t>(i + 1); // Mimer is 1-based
200
+ Napi::Value val = params[static_cast<uint32_t>(i)];
201
+ int rc;
202
+
203
+ if (val.IsNull() || val.IsUndefined()) {
204
+ rc = MimerSetNull(stmt, paramIndex);
205
+ } else if (val.IsBoolean()) {
206
+ rc = MimerSetBoolean(stmt, paramIndex, val.As<Napi::Boolean>().Value() ? 1 : 0);
207
+ } else if (val.IsNumber()) {
208
+ double num = val.As<Napi::Number>().DoubleValue();
209
+ // Check if it's an integer value
210
+ if (std::trunc(num) == num && std::isfinite(num)) {
211
+ if (num >= INT32_MIN && num <= INT32_MAX) {
212
+ rc = MimerSetInt32(stmt, paramIndex, static_cast<int32_t>(num));
213
+ } else {
214
+ rc = MimerSetInt64(stmt, paramIndex, static_cast<int64_t>(num));
215
+ }
216
+ } else {
217
+ rc = MimerSetDouble(stmt, paramIndex, num);
218
+ }
219
+ } else if (val.IsString()) {
220
+ std::string str = val.As<Napi::String>().Utf8Value();
221
+ int ptype = MimerParameterType(stmt, paramIndex);
222
+ if (MimerIsNclob(ptype)) {
223
+ MimerLob lobHandle;
224
+ size_t charCount = Utf8CharCount(str.c_str(), str.size());
225
+ rc = MimerSetLob(stmt, paramIndex, charCount, &lobHandle);
226
+ if (rc == 0) {
227
+ const char* data = str.c_str();
228
+ size_t remaining = str.size();
229
+ size_t offset = 0;
230
+ while (remaining > 0 && rc >= 0) {
231
+ size_t chunk = remaining < LOB_WRITE_CHUNK ? remaining : LOB_WRITE_CHUNK;
232
+ // Don't split multi-byte UTF-8 sequences at chunk boundary
233
+ while (chunk > 0 && chunk < remaining
234
+ && (data[offset + chunk] & 0xC0) == 0x80) {
235
+ chunk--;
236
+ }
237
+ rc = MimerSetNclobData8(&lobHandle, data + offset, chunk);
238
+ offset += chunk;
239
+ remaining -= chunk;
240
+ }
241
+ }
242
+ } else {
243
+ rc = MimerSetString8(stmt, paramIndex, str.c_str());
244
+ }
245
+ } else if (val.IsBuffer()) {
246
+ Napi::Buffer<uint8_t> buf = val.As<Napi::Buffer<uint8_t>>();
247
+ int ptype = MimerParameterType(stmt, paramIndex);
248
+ if (MimerIsBlob(ptype)) {
249
+ MimerLob lobHandle;
250
+ rc = MimerSetLob(stmt, paramIndex, buf.Length(), &lobHandle);
251
+ if (rc == 0) {
252
+ const uint8_t* data = buf.Data();
253
+ size_t remaining = buf.Length();
254
+ size_t offset = 0;
255
+ while (remaining > 0 && rc >= 0) {
256
+ size_t chunk = remaining < LOB_WRITE_CHUNK ? remaining : LOB_WRITE_CHUNK;
257
+ rc = MimerSetBlobData(&lobHandle, data + offset, chunk);
258
+ offset += chunk;
259
+ remaining -= chunk;
260
+ }
261
+ }
262
+ } else {
263
+ rc = MimerSetBinary(stmt, paramIndex, buf.Data(), buf.Length());
264
+ }
265
+ } else {
266
+ // Try to convert to string as fallback
267
+ std::string str = val.ToString().Utf8Value();
268
+ rc = MimerSetString8(stmt, paramIndex, str.c_str());
269
+ }
270
+
271
+ if (rc < 0) {
272
+ std::ostringstream detail;
273
+ detail << "failed to bind parameter " << (i + 1);
274
+ ThrowMimerError(env, rc, "BindParameters", detail.str());
275
+ return;
276
+ }
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Cache column names and type codes from a prepared statement.
282
+ */
283
+ void CacheColumnMetadata(MimerStatement stmt, int columnCount,
284
+ std::vector<std::string>& colNames,
285
+ std::vector<int>& colTypes) {
286
+ colNames.resize(columnCount);
287
+ colTypes.resize(columnCount);
288
+ for (int col = 1; col <= columnCount; col++) {
289
+ char colNameBuf[256];
290
+ MimerColumnName8(stmt, static_cast<int16_t>(col), colNameBuf, sizeof(colNameBuf));
291
+ colNames[col - 1] = colNameBuf;
292
+ colTypes[col - 1] = MimerColumnType(stmt, static_cast<int16_t>(col));
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Fetch a single row from an open cursor into a JS object.
298
+ * Assumes MimerFetch() has already returned MIMER_SUCCESS.
299
+ */
300
+ Napi::Object FetchSingleRow(Napi::Env env, MimerStatement stmt, int columnCount,
301
+ const std::vector<std::string>& colNames,
302
+ const std::vector<int>& colTypes) {
303
+ Napi::Object row = Napi::Object::New(env);
304
+ int rc;
305
+
306
+ for (int col = 1; col <= columnCount; col++) {
307
+ const char* colName = colNames[col - 1].c_str();
308
+ int colType = colTypes[col - 1];
309
+
310
+ // Check if NULL
311
+ if (MimerIsNull(stmt, static_cast<int16_t>(col)) > 0) {
312
+ row.Set(colName, env.Null());
313
+ continue;
314
+ }
315
+
316
+ // Get value based on type
317
+ if (MimerIsInt32(colType)) {
318
+ int32_t value;
319
+ rc = MimerGetInt32(stmt, static_cast<int16_t>(col), &value);
320
+ if (rc == 0) {
321
+ row.Set(colName, Napi::Number::New(env, value));
322
+ }
323
+ } else if (MimerIsInt64(colType)) {
324
+ int64_t value;
325
+ rc = MimerGetInt64(stmt, static_cast<int16_t>(col), &value);
326
+ if (rc == 0) {
327
+ row.Set(colName, Napi::Number::New(env, static_cast<double>(value)));
328
+ }
329
+ } else if (MimerIsDouble(colType)) {
330
+ double value;
331
+ rc = MimerGetDouble(stmt, static_cast<int16_t>(col), &value);
332
+ if (rc == 0) {
333
+ row.Set(colName, Napi::Number::New(env, value));
334
+ }
335
+ } else if (MimerIsFloat(colType)) {
336
+ float value;
337
+ rc = MimerGetFloat(stmt, static_cast<int16_t>(col), &value);
338
+ if (rc == 0) {
339
+ row.Set(colName, Napi::Number::New(env, value));
340
+ }
341
+ } else if (MimerIsBoolean(colType)) {
342
+ int32_t value = MimerGetBoolean(stmt, static_cast<int16_t>(col));
343
+ row.Set(colName, Napi::Boolean::New(env, value > 0));
344
+ } else if (MimerIsBlob(colType)) {
345
+ // BLOB → Buffer via LOB API, read in chunks
346
+ size_t lobSize;
347
+ MimerLob lobHandle;
348
+ rc = MimerGetLob(stmt, static_cast<int16_t>(col), &lobSize, &lobHandle);
349
+ if (rc == 0 && lobSize > 0) {
350
+ uint8_t* buf = new uint8_t[lobSize];
351
+ size_t offset = 0;
352
+ size_t remaining = lobSize;
353
+ while (remaining > 0) {
354
+ size_t chunk = remaining < LOB_READ_CHUNK ? remaining : LOB_READ_CHUNK;
355
+ rc = MimerGetBlobData(&lobHandle, buf + offset, chunk);
356
+ if (rc < 0) break;
357
+ offset += chunk;
358
+ remaining -= chunk;
359
+ }
360
+ if (rc >= 0) {
361
+ row.Set(colName, Napi::Buffer<uint8_t>::Copy(env, buf, lobSize));
362
+ }
363
+ delete[] buf;
364
+ } else if (rc == 0) {
365
+ row.Set(colName, Napi::Buffer<uint8_t>::New(env, 0));
366
+ }
367
+ } else if (MimerIsNclob(colType)) {
368
+ // CLOB/NCLOB → String via LOB API, read in chunks
369
+ size_t charCount;
370
+ MimerLob lobHandle;
371
+ rc = MimerGetLob(stmt, static_cast<int16_t>(col), &charCount, &lobHandle);
372
+ if (rc == 0 && charCount > 0) {
373
+ std::string result;
374
+ result.reserve(charCount); // at least charCount bytes
375
+ char chunkBuf[LOB_READ_CHUNK + 1];
376
+ do {
377
+ rc = MimerGetNclobData8(&lobHandle, chunkBuf, sizeof(chunkBuf));
378
+ if (rc < 0) break;
379
+ result.append(chunkBuf);
380
+ } while (rc > 0);
381
+ if (rc >= 0) {
382
+ row.Set(colName, Napi::String::New(env, result));
383
+ }
384
+ } else if (rc == 0) {
385
+ row.Set(colName, Napi::String::New(env, ""));
386
+ }
387
+ } else if (MimerIsBinary(colType)) {
388
+ int32_t size = MimerGetBinary(stmt, static_cast<int16_t>(col), nullptr, 0);
389
+ if (size > 0) {
390
+ uint8_t* buffer = new uint8_t[size];
391
+ rc = MimerGetBinary(stmt, static_cast<int16_t>(col), buffer, size);
392
+ if (rc >= 0) {
393
+ row.Set(colName, Napi::Buffer<uint8_t>::Copy(env, buffer, size));
394
+ }
395
+ delete[] buffer;
396
+ } else {
397
+ row.Set(colName, Napi::Buffer<uint8_t>::New(env, 0));
398
+ }
399
+ } else {
400
+ // Default: try as string (covers VARCHAR, DATE, TIME, TIMESTAMP, DECIMAL, UUID, etc.)
401
+ // Use a single buffer that fits most values on the first call.
402
+ // Only retry with the exact size if the value was truncated.
403
+ char buf[256];
404
+ int32_t size = MimerGetString8(stmt, static_cast<int16_t>(col), buf, sizeof(buf));
405
+ if (size > 0 && size < static_cast<int32_t>(sizeof(buf))) {
406
+ row.Set(colName, Napi::String::New(env, buf));
407
+ } else if (size >= static_cast<int32_t>(sizeof(buf))) {
408
+ char* buffer = new char[size + 1];
409
+ rc = MimerGetString8(stmt, static_cast<int16_t>(col), buffer, size + 1);
410
+ if (rc >= 0) {
411
+ row.Set(colName, Napi::String::New(env, buffer));
412
+ }
413
+ delete[] buffer;
414
+ } else {
415
+ row.Set(colName, Napi::String::New(env, ""));
416
+ }
417
+ }
418
+ }
419
+
420
+ return row;
421
+ }
422
+
423
+ /**
424
+ * Fetch all result rows from an open cursor into a JS array of objects.
425
+ */
426
+ Napi::Array FetchResults(Napi::Env env, MimerStatement stmt, int columnCount) {
427
+ std::vector<std::string> colNames;
428
+ std::vector<int> colTypes;
429
+ CacheColumnMetadata(stmt, columnCount, colNames, colTypes);
430
+
431
+ Napi::Array rows = Napi::Array::New(env);
432
+ int rowIndex = 0;
433
+
434
+ while (MimerFetch(stmt) == MIMER_SUCCESS) {
435
+ Napi::Object row = FetchSingleRow(env, stmt, columnCount, colNames, colTypes);
436
+ rows.Set(rowIndex++, row);
437
+ }
438
+
439
+ return rows;
440
+ }
package/src/helpers.h ADDED
@@ -0,0 +1,79 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #ifndef MIMER_HELPERS_H
24
+ #define MIMER_HELPERS_H
25
+
26
+ #include <napi.h>
27
+ #include <mimerapi.h>
28
+ #include <string>
29
+ #include <vector>
30
+
31
+ /**
32
+ * Create and throw a structured Mimer error as a JS exception.
33
+ * The error object gets:
34
+ * - message: human-readable description
35
+ * - mimerCode: the numeric Mimer return code (e.g. -24101)
36
+ * - operation: the Mimer C API function that failed
37
+ */
38
+ void ThrowMimerError(Napi::Env env, int rc, const std::string& operation,
39
+ const std::string& detail = "");
40
+
41
+ /**
42
+ * Build an array of column metadata objects from a prepared statement.
43
+ * Each element is { name, dataTypeCode, dataTypeName, nullable }.
44
+ * Can be called before or after opening a cursor — only uses column
45
+ * metadata, not row data.
46
+ */
47
+ Napi::Array BuildFieldsArray(Napi::Env env, MimerStatement stmt, int columnCount);
48
+
49
+ /**
50
+ * Bind a JavaScript array of parameters to a prepared Mimer statement.
51
+ * Parameter indices are 1-based in the Mimer API, 0-based in the JS array.
52
+ * Throws a JS exception on error.
53
+ */
54
+ void BindParameters(Napi::Env env, MimerStatement stmt, Napi::Array params);
55
+
56
+ /**
57
+ * Cache column names and type codes from a prepared statement.
58
+ * Populates colNames and colTypes vectors (0-indexed, columns are 1-based in Mimer).
59
+ */
60
+ void CacheColumnMetadata(MimerStatement stmt, int columnCount,
61
+ std::vector<std::string>& colNames,
62
+ std::vector<int>& colTypes);
63
+
64
+ /**
65
+ * Fetch a single row from an open cursor into a JS object.
66
+ * Assumes MimerFetch() has already returned MIMER_SUCCESS for this row.
67
+ * Column metadata must have been cached via CacheColumnMetadata().
68
+ */
69
+ Napi::Object FetchSingleRow(Napi::Env env, MimerStatement stmt, int columnCount,
70
+ const std::vector<std::string>& colNames,
71
+ const std::vector<int>& colTypes);
72
+
73
+ /**
74
+ * Fetch all result rows from an open cursor into a JS array of objects.
75
+ * Each row is a plain JS object with column names as keys.
76
+ */
77
+ Napi::Array FetchResults(Napi::Env env, MimerStatement stmt, int columnCount);
78
+
79
+ #endif // MIMER_HELPERS_H
@@ -0,0 +1,49 @@
1
+ // Copyright (c) 2026 Mimer Information Technology
2
+ //
3
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ // of this software and associated documentation files (the "Software"), to deal
5
+ // in the Software without restriction, including without limitation the rights
6
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ // copies of the Software, and to permit persons to whom the Software is
8
+ // furnished to do so, subject to the following conditions:
9
+ //
10
+ // The above copyright notice and this permission notice shall be included in all
11
+ // copies or substantial portions of the Software.
12
+ //
13
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ // SOFTWARE.
20
+ //
21
+ // See license for more details.
22
+
23
+ #include <napi.h>
24
+ #include "connection.h"
25
+ #include "statement.h"
26
+ #include "resultset.h"
27
+
28
+ /**
29
+ * Initialize the Mimer addon module
30
+ * This is the entry point when Node.js loads the module
31
+ */
32
+ Napi::Object Init(Napi::Env env, Napi::Object exports) {
33
+ // Export the Connection class
34
+ MimerConnection::Init(env, exports);
35
+
36
+ // Export the Statement class
37
+ MimerStmtWrapper::Init(env, exports);
38
+
39
+ // Export the ResultSet class
40
+ MimerResultSetWrapper::Init(env, exports);
41
+
42
+ // Export version information
43
+ exports.Set("version", Napi::String::New(env, "1.0.0"));
44
+
45
+ return exports;
46
+ }
47
+
48
+ // Register the module with Node.js
49
+ NODE_API_MODULE(mimer, Init)