@mimersql/node-mimer 1.0.0
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/LICENSE +21 -0
- package/README.md +670 -0
- package/index.d.ts +153 -0
- package/index.js +48 -0
- package/lib/client.js +225 -0
- package/lib/find-mimer-library.js +154 -0
- package/lib/koffi-binding.js +970 -0
- package/lib/native.js +52 -0
- package/lib/pool.js +242 -0
- package/lib/prepared.js +73 -0
- package/lib/resultset.js +120 -0
- package/package.json +38 -0
|
@@ -0,0 +1,970 @@
|
|
|
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
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
const koffi = require('koffi');
|
|
26
|
+
const findMimerLibrary = require('./find-mimer-library');
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// Load the Mimer SQL shared library
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
const libPath = findMimerLibrary();
|
|
32
|
+
const lib = koffi.load(libPath);
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Opaque pointer types
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
const MimerSession = koffi.pointer('MimerSession', koffi.opaque());
|
|
38
|
+
const MimerStatement = koffi.pointer('MimerStatement', koffi.opaque());
|
|
39
|
+
const MimerLob = koffi.pointer('MimerLob', koffi.opaque());
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Constants
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
const MIMER_SUCCESS = 0;
|
|
45
|
+
const MIMER_NO_DATA = 100;
|
|
46
|
+
const MIMER_FORWARD_ONLY = 0;
|
|
47
|
+
const MIMER_COMMIT = 0;
|
|
48
|
+
const MIMER_ROLLBACK = 1;
|
|
49
|
+
const MIMER_TRANS_READWRITE = 0;
|
|
50
|
+
const MIMER_STATEMENT_CANNOT_BE_PREPARED = -24005;
|
|
51
|
+
|
|
52
|
+
// Type codes
|
|
53
|
+
const MIMER_CHARACTER = 1;
|
|
54
|
+
const MIMER_DECIMAL = 2;
|
|
55
|
+
const MIMER_INTEGER = 3;
|
|
56
|
+
const MIMER_FLOAT = 4;
|
|
57
|
+
const MIMER_LIKE_PATTERN = 5;
|
|
58
|
+
const MIMER_T_INTEGER = 6;
|
|
59
|
+
const MIMER_T_SMALLINT = 7;
|
|
60
|
+
const MIMER_T_FLOAT = 8;
|
|
61
|
+
const MIMER_T_REAL = 9;
|
|
62
|
+
const MIMER_T_DOUBLE = 10;
|
|
63
|
+
const MIMER_CHARACTER_VARYING = 11;
|
|
64
|
+
const MIMER_DATE = 12;
|
|
65
|
+
const MIMER_TIME = 13;
|
|
66
|
+
const MIMER_TIMESTAMP = 14;
|
|
67
|
+
const MIMER_INTERVAL_YEAR = 15;
|
|
68
|
+
const MIMER_INTERVAL_MINUTE_TO_SECOND = 27;
|
|
69
|
+
const MIMER_UNSIGNED_INTEGER = 28;
|
|
70
|
+
const MIMER_T_UNSIGNED_INTEGER = 29;
|
|
71
|
+
const MIMER_T_UNSIGNED_SMALLINT = 30;
|
|
72
|
+
const MIMER_NUMERIC = 31;
|
|
73
|
+
const MIMER_T_BIGINT = 32;
|
|
74
|
+
const MIMER_T_UNSIGNED_BIGINT = 33;
|
|
75
|
+
const MIMER_BINARY = 34;
|
|
76
|
+
const MIMER_BINARY_VARYING = 35;
|
|
77
|
+
const MIMER_BLOB = 37;
|
|
78
|
+
const MIMER_CLOB = 38;
|
|
79
|
+
const MIMER_NCHAR = 39;
|
|
80
|
+
const MIMER_NCHAR_VARYING = 40;
|
|
81
|
+
const MIMER_NCLOB = 41;
|
|
82
|
+
const MIMER_BOOLEAN = 42;
|
|
83
|
+
const MIMER_BLOB_LOCATOR = 43;
|
|
84
|
+
const MIMER_CLOB_LOCATOR = 44;
|
|
85
|
+
const MIMER_NCLOB_LOCATOR = 45;
|
|
86
|
+
const MIMER_NATIVE_SMALLINT = 47;
|
|
87
|
+
const MIMER_NATIVE_SMALLINT_NULLABLE = 48;
|
|
88
|
+
const MIMER_NATIVE_INTEGER = 49;
|
|
89
|
+
const MIMER_NATIVE_INTEGER_NULLABLE = 50;
|
|
90
|
+
const MIMER_NATIVE_BIGINT = 51;
|
|
91
|
+
const MIMER_NATIVE_BIGINT_NULLABLE = 52;
|
|
92
|
+
const MIMER_NATIVE_REAL = 53;
|
|
93
|
+
const MIMER_NATIVE_REAL_NULLABLE = 54;
|
|
94
|
+
const MIMER_NATIVE_DOUBLE = 55;
|
|
95
|
+
const MIMER_NATIVE_DOUBLE_NULLABLE = 56;
|
|
96
|
+
const MIMER_NATIVE_BLOB = 57;
|
|
97
|
+
const MIMER_NATIVE_CLOB = 58;
|
|
98
|
+
const MIMER_NATIVE_NCLOB = 59;
|
|
99
|
+
const MIMER_NATIVE_BLOB_LOCATOR = 60;
|
|
100
|
+
const MIMER_NATIVE_CLOB_LOCATOR = 61;
|
|
101
|
+
const MIMER_NATIVE_NCLOB_LOCATOR = 62;
|
|
102
|
+
const MIMER_UTF8 = 63;
|
|
103
|
+
const MIMER_UUID = 8104;
|
|
104
|
+
|
|
105
|
+
const LOB_READ_CHUNK = 65536;
|
|
106
|
+
const LOB_WRITE_CHUNK = 2 * 1024 * 1024;
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// FFI function declarations
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
const MimerBeginSession8 = lib.func('int32_t MimerBeginSession8(str, str, str, _Out_ MimerSession *)');
|
|
112
|
+
const MimerEndSession = lib.func('int32_t MimerEndSession(_Inout_ MimerSession *)');
|
|
113
|
+
const MimerBeginStatement8 = lib.func('int32_t MimerBeginStatement8(MimerSession, str, int32_t, _Out_ MimerStatement *)');
|
|
114
|
+
const MimerEndStatement = lib.func('int32_t MimerEndStatement(_Inout_ MimerStatement *)');
|
|
115
|
+
const MimerExecuteStatement8 = lib.func('int32_t MimerExecuteStatement8(MimerSession, str)');
|
|
116
|
+
const MimerExecute = lib.func('int32_t MimerExecute(MimerStatement)');
|
|
117
|
+
const MimerOpenCursor = lib.func('int32_t MimerOpenCursor(MimerStatement)');
|
|
118
|
+
const MimerFetch = lib.func('int32_t MimerFetch(MimerStatement)');
|
|
119
|
+
const MimerCloseCursor = lib.func('int32_t MimerCloseCursor(MimerStatement)');
|
|
120
|
+
const MimerColumnCount = lib.func('int32_t MimerColumnCount(MimerStatement)');
|
|
121
|
+
const MimerColumnName8 = lib.func('int32_t MimerColumnName8(MimerStatement, int16_t, _Out_ uint8_t *, size_t)');
|
|
122
|
+
const MimerColumnType = lib.func('int32_t MimerColumnType(MimerStatement, int16_t)');
|
|
123
|
+
const MimerParameterCount = lib.func('int32_t MimerParameterCount(MimerStatement)');
|
|
124
|
+
const MimerParameterType = lib.func('int32_t MimerParameterType(MimerStatement, int16_t)');
|
|
125
|
+
const MimerIsNull = lib.func('int32_t MimerIsNull(MimerStatement, int16_t)');
|
|
126
|
+
const MimerSetNull = lib.func('int32_t MimerSetNull(MimerStatement, int16_t)');
|
|
127
|
+
const MimerSetString8 = lib.func('int32_t MimerSetString8(MimerStatement, int16_t, str)');
|
|
128
|
+
const MimerSetInt32 = lib.func('int32_t MimerSetInt32(MimerStatement, int16_t, int32_t)');
|
|
129
|
+
const MimerSetInt64 = lib.func('int32_t MimerSetInt64(MimerStatement, int16_t, int64_t)');
|
|
130
|
+
const MimerSetDouble = lib.func('int32_t MimerSetDouble(MimerStatement, int16_t, double)');
|
|
131
|
+
const MimerSetBoolean = lib.func('int32_t MimerSetBoolean(MimerStatement, int16_t, int32_t)');
|
|
132
|
+
const MimerSetBinary = lib.func('int32_t MimerSetBinary(MimerStatement, int16_t, _In_ uint8_t *, size_t)');
|
|
133
|
+
const MimerGetString8 = lib.func('int32_t MimerGetString8(MimerStatement, int16_t, _Out_ uint8_t *, size_t)');
|
|
134
|
+
const MimerGetInt32 = lib.func('int32_t MimerGetInt32(MimerStatement, int16_t, _Out_ int32_t *)');
|
|
135
|
+
const MimerGetInt64 = lib.func('int32_t MimerGetInt64(MimerStatement, int16_t, _Out_ int64_t *)');
|
|
136
|
+
const MimerGetDouble = lib.func('int32_t MimerGetDouble(MimerStatement, int16_t, _Out_ double *)');
|
|
137
|
+
const MimerGetFloat = lib.func('int32_t MimerGetFloat(MimerStatement, int16_t, _Out_ float *)');
|
|
138
|
+
const MimerGetBoolean = lib.func('int32_t MimerGetBoolean(MimerStatement, int16_t)');
|
|
139
|
+
const MimerGetBinary = lib.func('int32_t MimerGetBinary(MimerStatement, int16_t, _Out_ uint8_t *, size_t)');
|
|
140
|
+
const MimerBeginTransaction = lib.func('int32_t MimerBeginTransaction(MimerSession, int32_t)');
|
|
141
|
+
const MimerEndTransaction = lib.func('int32_t MimerEndTransaction(MimerSession, int32_t)');
|
|
142
|
+
const MimerSetLob = lib.func('int32_t MimerSetLob(MimerStatement, int16_t, size_t, _Out_ MimerLob *)');
|
|
143
|
+
const MimerSetBlobData = lib.func('int32_t MimerSetBlobData(_Inout_ MimerLob *, _In_ uint8_t *, size_t)');
|
|
144
|
+
const MimerSetNclobData8 = lib.func('int32_t MimerSetNclobData8(_Inout_ MimerLob *, str, size_t)');
|
|
145
|
+
const MimerGetLob = lib.func('int32_t MimerGetLob(MimerStatement, int16_t, _Out_ size_t *, _Out_ MimerLob *)');
|
|
146
|
+
const MimerGetBlobData = lib.func('int32_t MimerGetBlobData(_Inout_ MimerLob *, _Out_ uint8_t *, size_t)');
|
|
147
|
+
const MimerGetNclobData8 = lib.func('int32_t MimerGetNclobData8(_Inout_ MimerLob *, _Out_ uint8_t *, size_t)');
|
|
148
|
+
const MimerGetError8 = lib.func('int32_t MimerGetError8(MimerSession, _Out_ int32_t *, _Out_ uint8_t *, size_t)');
|
|
149
|
+
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// Type detection functions (ported from mimerapi.h macros)
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
function mimerIsInt32(n) {
|
|
154
|
+
const a = Math.abs(n);
|
|
155
|
+
return a === MIMER_T_INTEGER || a === MIMER_T_SMALLINT
|
|
156
|
+
|| a === MIMER_T_UNSIGNED_INTEGER || a === MIMER_T_UNSIGNED_SMALLINT
|
|
157
|
+
|| (a >= MIMER_NATIVE_SMALLINT && a <= MIMER_NATIVE_BIGINT_NULLABLE);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function mimerIsInt64(n) {
|
|
161
|
+
const a = Math.abs(n);
|
|
162
|
+
return a === MIMER_T_BIGINT || a === MIMER_T_UNSIGNED_BIGINT
|
|
163
|
+
|| a === MIMER_NATIVE_BIGINT || a === MIMER_NATIVE_BIGINT_NULLABLE;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function mimerIsDouble(n) {
|
|
167
|
+
const a = Math.abs(n);
|
|
168
|
+
return a === MIMER_T_DOUBLE || a === MIMER_NATIVE_DOUBLE
|
|
169
|
+
|| a === MIMER_NATIVE_DOUBLE_NULLABLE;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function mimerIsFloat(n) {
|
|
173
|
+
const a = Math.abs(n);
|
|
174
|
+
return a === MIMER_T_FLOAT || a === MIMER_T_REAL
|
|
175
|
+
|| a === MIMER_NATIVE_REAL || a === MIMER_NATIVE_REAL_NULLABLE;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function mimerIsBoolean(n) {
|
|
179
|
+
return Math.abs(n) === MIMER_BOOLEAN;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function mimerIsBlob(n) {
|
|
183
|
+
const a = Math.abs(n);
|
|
184
|
+
return a === MIMER_BLOB || a === MIMER_BLOB_LOCATOR
|
|
185
|
+
|| a === MIMER_NATIVE_BLOB || a === MIMER_NATIVE_BLOB_LOCATOR;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function mimerIsClob(n) {
|
|
189
|
+
const a = Math.abs(n);
|
|
190
|
+
return a === MIMER_CLOB || a === MIMER_CLOB_LOCATOR
|
|
191
|
+
|| a === MIMER_NATIVE_CLOB || a === MIMER_NATIVE_CLOB_LOCATOR;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function mimerIsNclob(n) {
|
|
195
|
+
const a = Math.abs(n);
|
|
196
|
+
return a === MIMER_NCLOB || a === MIMER_NCLOB_LOCATOR
|
|
197
|
+
|| a === MIMER_NATIVE_NCLOB || a === MIMER_NATIVE_NCLOB_LOCATOR
|
|
198
|
+
|| mimerIsClob(n);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function mimerIsBinary(n) {
|
|
202
|
+
const a = Math.abs(n);
|
|
203
|
+
return a === MIMER_BINARY || a === MIMER_BINARY_VARYING;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Type name mapping (ported from helpers.cc MimerTypeName)
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
const TYPE_NAMES = {
|
|
210
|
+
[MIMER_CHARACTER]: 'CHARACTER',
|
|
211
|
+
[MIMER_CHARACTER_VARYING]: 'CHARACTER VARYING',
|
|
212
|
+
[MIMER_NCHAR]: 'NCHAR',
|
|
213
|
+
[MIMER_NCHAR_VARYING]: 'NCHAR VARYING',
|
|
214
|
+
[MIMER_UTF8]: 'NVARCHAR',
|
|
215
|
+
[MIMER_DECIMAL]: 'DECIMAL',
|
|
216
|
+
[MIMER_NUMERIC]: 'NUMERIC',
|
|
217
|
+
[MIMER_INTEGER]: 'INTEGER',
|
|
218
|
+
[MIMER_UNSIGNED_INTEGER]: 'INTEGER',
|
|
219
|
+
[MIMER_T_INTEGER]: 'INTEGER',
|
|
220
|
+
[MIMER_T_UNSIGNED_INTEGER]: 'INTEGER',
|
|
221
|
+
[MIMER_T_SMALLINT]: 'SMALLINT',
|
|
222
|
+
[MIMER_T_UNSIGNED_SMALLINT]: 'SMALLINT',
|
|
223
|
+
[MIMER_T_BIGINT]: 'BIGINT',
|
|
224
|
+
[MIMER_T_UNSIGNED_BIGINT]: 'BIGINT',
|
|
225
|
+
[MIMER_FLOAT]: 'FLOAT',
|
|
226
|
+
[MIMER_T_FLOAT]: 'FLOAT',
|
|
227
|
+
[MIMER_T_REAL]: 'REAL',
|
|
228
|
+
[MIMER_T_DOUBLE]: 'DOUBLE PRECISION',
|
|
229
|
+
[MIMER_BOOLEAN]: 'BOOLEAN',
|
|
230
|
+
[MIMER_DATE]: 'DATE',
|
|
231
|
+
[MIMER_TIME]: 'TIME',
|
|
232
|
+
[MIMER_TIMESTAMP]: 'TIMESTAMP',
|
|
233
|
+
[MIMER_BINARY]: 'BINARY',
|
|
234
|
+
[MIMER_BINARY_VARYING]: 'BINARY VARYING',
|
|
235
|
+
[MIMER_BLOB]: 'BLOB',
|
|
236
|
+
[MIMER_CLOB]: 'CLOB',
|
|
237
|
+
[MIMER_NCLOB]: 'NCLOB',
|
|
238
|
+
[MIMER_BLOB_LOCATOR]: 'BLOB',
|
|
239
|
+
[MIMER_CLOB_LOCATOR]: 'CLOB',
|
|
240
|
+
[MIMER_NCLOB_LOCATOR]: 'NCLOB',
|
|
241
|
+
[MIMER_NATIVE_SMALLINT]: 'SMALLINT',
|
|
242
|
+
[MIMER_NATIVE_SMALLINT_NULLABLE]: 'SMALLINT',
|
|
243
|
+
[MIMER_NATIVE_INTEGER]: 'INTEGER',
|
|
244
|
+
[MIMER_NATIVE_INTEGER_NULLABLE]: 'INTEGER',
|
|
245
|
+
[MIMER_NATIVE_BIGINT]: 'BIGINT',
|
|
246
|
+
[MIMER_NATIVE_BIGINT_NULLABLE]: 'BIGINT',
|
|
247
|
+
[MIMER_NATIVE_REAL]: 'REAL',
|
|
248
|
+
[MIMER_NATIVE_REAL_NULLABLE]: 'REAL',
|
|
249
|
+
[MIMER_NATIVE_DOUBLE]: 'DOUBLE PRECISION',
|
|
250
|
+
[MIMER_NATIVE_DOUBLE_NULLABLE]: 'DOUBLE PRECISION',
|
|
251
|
+
[MIMER_NATIVE_BLOB]: 'BLOB',
|
|
252
|
+
[MIMER_NATIVE_BLOB_LOCATOR]: 'BLOB',
|
|
253
|
+
[MIMER_NATIVE_CLOB]: 'CLOB',
|
|
254
|
+
[MIMER_NATIVE_CLOB_LOCATOR]: 'CLOB',
|
|
255
|
+
[MIMER_NATIVE_NCLOB]: 'NCLOB',
|
|
256
|
+
[MIMER_NATIVE_NCLOB_LOCATOR]: 'NCLOB',
|
|
257
|
+
[MIMER_UUID]: 'UUID',
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
function mimerTypeName(absType) {
|
|
261
|
+
if (TYPE_NAMES[absType]) return TYPE_NAMES[absType];
|
|
262
|
+
if (absType >= MIMER_INTERVAL_YEAR && absType <= MIMER_INTERVAL_MINUTE_TO_SECOND) {
|
|
263
|
+
return 'INTERVAL';
|
|
264
|
+
}
|
|
265
|
+
return 'UNKNOWN';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Error handling
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
function throwMimerError(rc, operation, detail) {
|
|
272
|
+
let msg;
|
|
273
|
+
if (detail) {
|
|
274
|
+
msg = `${operation} failed: ${detail} (code: ${rc})`;
|
|
275
|
+
} else {
|
|
276
|
+
msg = `${operation} failed (code: ${rc})`;
|
|
277
|
+
}
|
|
278
|
+
const err = new Error(msg);
|
|
279
|
+
err.mimerCode = rc;
|
|
280
|
+
err.operation = operation;
|
|
281
|
+
throw err;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function checkError(session, rc, operation) {
|
|
285
|
+
if (rc < 0) {
|
|
286
|
+
let detail = 'Unknown error';
|
|
287
|
+
if (session) {
|
|
288
|
+
const errCode = [0];
|
|
289
|
+
const errBuf = Buffer.alloc(1024);
|
|
290
|
+
const errRc = MimerGetError8(session, errCode, errBuf, errBuf.length);
|
|
291
|
+
if (errRc > 0) {
|
|
292
|
+
const nullIdx = errBuf.indexOf(0);
|
|
293
|
+
detail = errBuf.toString('utf8', 0, nullIdx >= 0 ? nullIdx : errBuf.length);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
throwMimerError(rc, operation, detail);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ---------------------------------------------------------------------------
|
|
301
|
+
// UTF-8 character counting
|
|
302
|
+
// ---------------------------------------------------------------------------
|
|
303
|
+
function utf8CharCount(buf) {
|
|
304
|
+
let count = 0;
|
|
305
|
+
for (let i = 0; i < buf.length; ) {
|
|
306
|
+
const c = buf[i];
|
|
307
|
+
if (c < 0x80) i += 1;
|
|
308
|
+
else if ((c & 0xe0) === 0xc0) i += 2;
|
|
309
|
+
else if ((c & 0xf0) === 0xe0) i += 3;
|
|
310
|
+
else i += 4;
|
|
311
|
+
count++;
|
|
312
|
+
}
|
|
313
|
+
return count;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ---------------------------------------------------------------------------
|
|
317
|
+
// Helper: build fields array
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
function buildFieldsArray(stmt, columnCount) {
|
|
320
|
+
const fields = [];
|
|
321
|
+
for (let col = 1; col <= columnCount; col++) {
|
|
322
|
+
const nameBuf = Buffer.alloc(256);
|
|
323
|
+
MimerColumnName8(stmt, col, nameBuf, nameBuf.length);
|
|
324
|
+
const nullIdx = nameBuf.indexOf(0);
|
|
325
|
+
const name = nameBuf.toString('utf8', 0, nullIdx >= 0 ? nullIdx : nameBuf.length);
|
|
326
|
+
|
|
327
|
+
const rawType = MimerColumnType(stmt, col);
|
|
328
|
+
const absType = rawType < 0 ? -rawType : rawType;
|
|
329
|
+
const dataTypeName = mimerTypeName(absType);
|
|
330
|
+
|
|
331
|
+
let nullable;
|
|
332
|
+
if (rawType < 0) {
|
|
333
|
+
nullable = true;
|
|
334
|
+
} else if (
|
|
335
|
+
absType === MIMER_NATIVE_SMALLINT_NULLABLE
|
|
336
|
+
|| absType === MIMER_NATIVE_INTEGER_NULLABLE
|
|
337
|
+
|| absType === MIMER_NATIVE_BIGINT_NULLABLE
|
|
338
|
+
|| absType === MIMER_NATIVE_REAL_NULLABLE
|
|
339
|
+
|| absType === MIMER_NATIVE_DOUBLE_NULLABLE
|
|
340
|
+
) {
|
|
341
|
+
nullable = true;
|
|
342
|
+
} else {
|
|
343
|
+
nullable = false;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
fields.push({
|
|
347
|
+
name,
|
|
348
|
+
dataTypeCode: rawType,
|
|
349
|
+
dataTypeName,
|
|
350
|
+
nullable,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
return fields;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// ---------------------------------------------------------------------------
|
|
357
|
+
// Helper: bind parameters
|
|
358
|
+
// ---------------------------------------------------------------------------
|
|
359
|
+
function bindParameters(stmt, params, session) {
|
|
360
|
+
const paramCount = MimerParameterCount(stmt);
|
|
361
|
+
if (params.length !== paramCount) {
|
|
362
|
+
throwMimerError(
|
|
363
|
+
0,
|
|
364
|
+
'BindParameters',
|
|
365
|
+
`statement expects ${paramCount} but ${params.length} were provided`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
for (let i = 0; i < params.length; i++) {
|
|
370
|
+
const paramIndex = i + 1; // Mimer is 1-based
|
|
371
|
+
const val = params[i];
|
|
372
|
+
let rc;
|
|
373
|
+
|
|
374
|
+
if (val === null || val === undefined) {
|
|
375
|
+
rc = MimerSetNull(stmt, paramIndex);
|
|
376
|
+
} else if (typeof val === 'boolean') {
|
|
377
|
+
rc = MimerSetBoolean(stmt, paramIndex, val ? 1 : 0);
|
|
378
|
+
} else if (typeof val === 'number') {
|
|
379
|
+
if (Number.isFinite(val) && Math.trunc(val) === val) {
|
|
380
|
+
if (val >= -2147483648 && val <= 2147483647) {
|
|
381
|
+
rc = MimerSetInt32(stmt, paramIndex, val);
|
|
382
|
+
} else {
|
|
383
|
+
rc = MimerSetInt64(stmt, paramIndex, val);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
rc = MimerSetDouble(stmt, paramIndex, val);
|
|
387
|
+
}
|
|
388
|
+
} else if (typeof val === 'string') {
|
|
389
|
+
const ptype = MimerParameterType(stmt, paramIndex);
|
|
390
|
+
if (mimerIsNclob(ptype)) {
|
|
391
|
+
const buf = Buffer.from(val, 'utf8');
|
|
392
|
+
const charCount = utf8CharCount(buf);
|
|
393
|
+
const lobHandle = [null];
|
|
394
|
+
rc = MimerSetLob(stmt, paramIndex, charCount, lobHandle);
|
|
395
|
+
if (rc === 0) {
|
|
396
|
+
let remaining = buf.length;
|
|
397
|
+
let offset = 0;
|
|
398
|
+
while (remaining > 0 && rc >= 0) {
|
|
399
|
+
let chunk = remaining < LOB_WRITE_CHUNK ? remaining : LOB_WRITE_CHUNK;
|
|
400
|
+
// Don't split multi-byte UTF-8 sequences
|
|
401
|
+
while (chunk > 0 && chunk < remaining && (buf[offset + chunk] & 0xc0) === 0x80) {
|
|
402
|
+
chunk--;
|
|
403
|
+
}
|
|
404
|
+
const chunkBuf = buf.subarray(offset, offset + chunk);
|
|
405
|
+
rc = MimerSetNclobData8(lobHandle, chunkBuf, chunk);
|
|
406
|
+
offset += chunk;
|
|
407
|
+
remaining -= chunk;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
rc = MimerSetString8(stmt, paramIndex, val);
|
|
412
|
+
}
|
|
413
|
+
} else if (Buffer.isBuffer(val)) {
|
|
414
|
+
const ptype = MimerParameterType(stmt, paramIndex);
|
|
415
|
+
if (mimerIsBlob(ptype)) {
|
|
416
|
+
const lobHandle = [null];
|
|
417
|
+
rc = MimerSetLob(stmt, paramIndex, val.length, lobHandle);
|
|
418
|
+
if (rc === 0) {
|
|
419
|
+
let remaining = val.length;
|
|
420
|
+
let offset = 0;
|
|
421
|
+
while (remaining > 0 && rc >= 0) {
|
|
422
|
+
const chunk = remaining < LOB_WRITE_CHUNK ? remaining : LOB_WRITE_CHUNK;
|
|
423
|
+
const chunkBuf = val.subarray(offset, offset + chunk);
|
|
424
|
+
rc = MimerSetBlobData(lobHandle, chunkBuf, chunk);
|
|
425
|
+
offset += chunk;
|
|
426
|
+
remaining -= chunk;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
} else {
|
|
430
|
+
rc = MimerSetBinary(stmt, paramIndex, val, val.length);
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
// Fallback: convert to string
|
|
434
|
+
rc = MimerSetString8(stmt, paramIndex, String(val));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (rc < 0) {
|
|
438
|
+
checkError(session, rc, 'BindParameters');
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ---------------------------------------------------------------------------
|
|
444
|
+
// Helper: fetch a single row
|
|
445
|
+
// ---------------------------------------------------------------------------
|
|
446
|
+
function fetchSingleRow(stmt, columnCount, colNames, colTypes) {
|
|
447
|
+
const row = {};
|
|
448
|
+
|
|
449
|
+
for (let col = 1; col <= columnCount; col++) {
|
|
450
|
+
const colName = colNames[col - 1];
|
|
451
|
+
const colType = colTypes[col - 1];
|
|
452
|
+
|
|
453
|
+
if (MimerIsNull(stmt, col) > 0) {
|
|
454
|
+
row[colName] = null;
|
|
455
|
+
continue;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (mimerIsInt32(colType)) {
|
|
459
|
+
const val = [0];
|
|
460
|
+
const rc = MimerGetInt32(stmt, col, val);
|
|
461
|
+
if (rc === 0) row[colName] = val[0];
|
|
462
|
+
} else if (mimerIsInt64(colType)) {
|
|
463
|
+
const val = [0n];
|
|
464
|
+
const rc = MimerGetInt64(stmt, col, val);
|
|
465
|
+
if (rc === 0) row[colName] = Number(val[0]);
|
|
466
|
+
} else if (mimerIsDouble(colType)) {
|
|
467
|
+
const val = [0];
|
|
468
|
+
const rc = MimerGetDouble(stmt, col, val);
|
|
469
|
+
if (rc === 0) row[colName] = val[0];
|
|
470
|
+
} else if (mimerIsFloat(colType)) {
|
|
471
|
+
const val = [0];
|
|
472
|
+
const rc = MimerGetFloat(stmt, col, val);
|
|
473
|
+
if (rc === 0) row[colName] = val[0];
|
|
474
|
+
} else if (mimerIsBoolean(colType)) {
|
|
475
|
+
const val = MimerGetBoolean(stmt, col);
|
|
476
|
+
row[colName] = val > 0;
|
|
477
|
+
} else if (mimerIsBlob(colType)) {
|
|
478
|
+
const lobSize = [0];
|
|
479
|
+
const lobHandle = [null];
|
|
480
|
+
const rc = MimerGetLob(stmt, col, lobSize, lobHandle);
|
|
481
|
+
if (rc === 0 && lobSize[0] > 0) {
|
|
482
|
+
const totalSize = lobSize[0];
|
|
483
|
+
const buf = Buffer.alloc(totalSize);
|
|
484
|
+
let offset = 0;
|
|
485
|
+
let remaining = totalSize;
|
|
486
|
+
let readRc = 0;
|
|
487
|
+
while (remaining > 0) {
|
|
488
|
+
const chunk = remaining < LOB_READ_CHUNK ? remaining : LOB_READ_CHUNK;
|
|
489
|
+
const chunkBuf = Buffer.alloc(chunk);
|
|
490
|
+
readRc = MimerGetBlobData(lobHandle, chunkBuf, chunk);
|
|
491
|
+
if (readRc < 0) break;
|
|
492
|
+
chunkBuf.copy(buf, offset);
|
|
493
|
+
offset += chunk;
|
|
494
|
+
remaining -= chunk;
|
|
495
|
+
}
|
|
496
|
+
if (readRc >= 0) row[colName] = buf;
|
|
497
|
+
} else if (rc === 0) {
|
|
498
|
+
row[colName] = Buffer.alloc(0);
|
|
499
|
+
}
|
|
500
|
+
} else if (mimerIsNclob(colType)) {
|
|
501
|
+
const charCount = [0];
|
|
502
|
+
const lobHandle = [null];
|
|
503
|
+
const rc = MimerGetLob(stmt, col, charCount, lobHandle);
|
|
504
|
+
if (rc === 0 && charCount[0] > 0) {
|
|
505
|
+
const parts = [];
|
|
506
|
+
let readRc;
|
|
507
|
+
do {
|
|
508
|
+
const chunkBuf = Buffer.alloc(LOB_READ_CHUNK + 1);
|
|
509
|
+
readRc = MimerGetNclobData8(lobHandle, chunkBuf, chunkBuf.length);
|
|
510
|
+
if (readRc < 0) break;
|
|
511
|
+
const nullIdx = chunkBuf.indexOf(0);
|
|
512
|
+
parts.push(chunkBuf.toString('utf8', 0, nullIdx >= 0 ? nullIdx : chunkBuf.length));
|
|
513
|
+
} while (readRc > 0);
|
|
514
|
+
if (readRc >= 0) row[colName] = parts.join('');
|
|
515
|
+
} else if (rc === 0) {
|
|
516
|
+
row[colName] = '';
|
|
517
|
+
}
|
|
518
|
+
} else if (mimerIsBinary(colType)) {
|
|
519
|
+
const size = MimerGetBinary(stmt, col, null, 0);
|
|
520
|
+
if (size > 0) {
|
|
521
|
+
const buf = Buffer.alloc(size);
|
|
522
|
+
const rc = MimerGetBinary(stmt, col, buf, size);
|
|
523
|
+
if (rc >= 0) row[colName] = buf;
|
|
524
|
+
} else {
|
|
525
|
+
row[colName] = Buffer.alloc(0);
|
|
526
|
+
}
|
|
527
|
+
} else {
|
|
528
|
+
// Default: string (VARCHAR, DATE, TIME, TIMESTAMP, DECIMAL, UUID, etc.)
|
|
529
|
+
// Use a single large buffer instead of a two-call probe pattern.
|
|
530
|
+
// The probe pattern (size-0 call with _Out_ buffer) causes Koffi heap corruption.
|
|
531
|
+
let bufSize = 256;
|
|
532
|
+
let buf = Buffer.alloc(bufSize);
|
|
533
|
+
let size = MimerGetString8(stmt, col, buf, bufSize);
|
|
534
|
+
if (size >= bufSize) {
|
|
535
|
+
// String was truncated — retry with exact size
|
|
536
|
+
bufSize = size + 1;
|
|
537
|
+
buf = Buffer.alloc(bufSize);
|
|
538
|
+
size = MimerGetString8(stmt, col, buf, bufSize);
|
|
539
|
+
}
|
|
540
|
+
if (size > 0) {
|
|
541
|
+
const nullIdx = buf.indexOf(0);
|
|
542
|
+
row[colName] = buf.toString('utf8', 0, nullIdx >= 0 ? nullIdx : buf.length);
|
|
543
|
+
} else {
|
|
544
|
+
row[colName] = '';
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return row;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ---------------------------------------------------------------------------
|
|
553
|
+
// Helper: cache column metadata
|
|
554
|
+
// ---------------------------------------------------------------------------
|
|
555
|
+
function cacheColumnMetadata(stmt, columnCount) {
|
|
556
|
+
const colNames = [];
|
|
557
|
+
const colTypes = [];
|
|
558
|
+
for (let col = 1; col <= columnCount; col++) {
|
|
559
|
+
const nameBuf = Buffer.alloc(256);
|
|
560
|
+
MimerColumnName8(stmt, col, nameBuf, nameBuf.length);
|
|
561
|
+
const nullIdx = nameBuf.indexOf(0);
|
|
562
|
+
colNames.push(nameBuf.toString('utf8', 0, nullIdx >= 0 ? nullIdx : nameBuf.length));
|
|
563
|
+
colTypes.push(MimerColumnType(stmt, col));
|
|
564
|
+
}
|
|
565
|
+
return { colNames, colTypes };
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// ---------------------------------------------------------------------------
|
|
569
|
+
// Helper: fetch all results
|
|
570
|
+
// ---------------------------------------------------------------------------
|
|
571
|
+
function fetchResults(stmt, columnCount) {
|
|
572
|
+
const { colNames, colTypes } = cacheColumnMetadata(stmt, columnCount);
|
|
573
|
+
const rows = [];
|
|
574
|
+
while (MimerFetch(stmt) === MIMER_SUCCESS) {
|
|
575
|
+
rows.push(fetchSingleRow(stmt, columnCount, colNames, colTypes));
|
|
576
|
+
}
|
|
577
|
+
return rows;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ---------------------------------------------------------------------------
|
|
581
|
+
// Connection class
|
|
582
|
+
// ---------------------------------------------------------------------------
|
|
583
|
+
class Connection {
|
|
584
|
+
constructor() {
|
|
585
|
+
this._session = null;
|
|
586
|
+
this._connected = false;
|
|
587
|
+
this._openStatements = new Set();
|
|
588
|
+
this._openResultSets = new Set();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
connect(dsn, user, password) {
|
|
592
|
+
if (arguments.length < 3) {
|
|
593
|
+
throw new TypeError('Expected 3 arguments: dsn, user, password');
|
|
594
|
+
}
|
|
595
|
+
if (typeof dsn !== 'string' || typeof user !== 'string' || typeof password !== 'string') {
|
|
596
|
+
throw new TypeError('All arguments must be strings');
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
const sessionOut = [null];
|
|
600
|
+
const rc = MimerBeginSession8(dsn, user, password, sessionOut);
|
|
601
|
+
if (rc < 0) {
|
|
602
|
+
checkError(null, rc, 'MimerBeginSession8');
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
this._session = sessionOut[0];
|
|
606
|
+
this._connected = true;
|
|
607
|
+
return true;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
close() {
|
|
611
|
+
if (!this._connected) return true;
|
|
612
|
+
|
|
613
|
+
// Invalidate all open result sets
|
|
614
|
+
for (const rs of this._openResultSets) {
|
|
615
|
+
rs._invalidate();
|
|
616
|
+
}
|
|
617
|
+
this._openResultSets.clear();
|
|
618
|
+
|
|
619
|
+
// Invalidate all open statements
|
|
620
|
+
for (const st of this._openStatements) {
|
|
621
|
+
st._invalidate();
|
|
622
|
+
}
|
|
623
|
+
this._openStatements.clear();
|
|
624
|
+
|
|
625
|
+
if (this._session !== null) {
|
|
626
|
+
const sessionRef = [this._session];
|
|
627
|
+
const rc = MimerEndSession(sessionRef);
|
|
628
|
+
if (rc < 0) {
|
|
629
|
+
checkError(this._session, rc, 'MimerEndSession');
|
|
630
|
+
}
|
|
631
|
+
this._session = null;
|
|
632
|
+
this._connected = false;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
execute(sql, params) {
|
|
639
|
+
if (!this._connected) {
|
|
640
|
+
throw new Error('Not connected to database');
|
|
641
|
+
}
|
|
642
|
+
if (typeof sql !== 'string') {
|
|
643
|
+
throw new TypeError('Expected SQL string as first argument');
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
const hasParams = Array.isArray(params) && params.length > 0;
|
|
647
|
+
|
|
648
|
+
// Try to prepare the statement
|
|
649
|
+
const stmtOut = [null];
|
|
650
|
+
let rc = MimerBeginStatement8(this._session, sql, MIMER_FORWARD_ONLY, stmtOut);
|
|
651
|
+
|
|
652
|
+
// DDL statements cannot be prepared — execute directly
|
|
653
|
+
if (rc === MIMER_STATEMENT_CANNOT_BE_PREPARED) {
|
|
654
|
+
rc = MimerExecuteStatement8(this._session, sql);
|
|
655
|
+
if (rc < 0) {
|
|
656
|
+
checkError(this._session, rc, 'MimerExecuteStatement8');
|
|
657
|
+
}
|
|
658
|
+
return { rowCount: 0 };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (rc < 0) {
|
|
662
|
+
checkError(this._session, rc, 'MimerBeginStatement8');
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
const stmt = stmtOut[0];
|
|
666
|
+
|
|
667
|
+
try {
|
|
668
|
+
// Bind parameters if provided
|
|
669
|
+
if (hasParams) {
|
|
670
|
+
bindParameters(stmt, params, this._session);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
const columnCount = MimerColumnCount(stmt);
|
|
674
|
+
const hasResultSet = columnCount > 0;
|
|
675
|
+
const result = {};
|
|
676
|
+
|
|
677
|
+
if (hasResultSet) {
|
|
678
|
+
result.fields = buildFieldsArray(stmt, columnCount);
|
|
679
|
+
|
|
680
|
+
rc = MimerOpenCursor(stmt);
|
|
681
|
+
if (rc < 0) {
|
|
682
|
+
checkError(this._session, rc, 'MimerOpenCursor');
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const rows = fetchResults(stmt, columnCount);
|
|
686
|
+
result.rows = rows;
|
|
687
|
+
result.rowCount = rows.length;
|
|
688
|
+
} else {
|
|
689
|
+
rc = MimerExecute(stmt);
|
|
690
|
+
if (rc < 0) {
|
|
691
|
+
checkError(this._session, rc, 'MimerExecute');
|
|
692
|
+
}
|
|
693
|
+
result.rowCount = rc;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
return result;
|
|
697
|
+
} finally {
|
|
698
|
+
const stmtRef = [stmt];
|
|
699
|
+
MimerEndStatement(stmtRef);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
beginTransaction() {
|
|
704
|
+
if (!this._connected) {
|
|
705
|
+
throw new Error('Not connected to database');
|
|
706
|
+
}
|
|
707
|
+
const rc = MimerBeginTransaction(this._session, MIMER_TRANS_READWRITE);
|
|
708
|
+
if (rc < 0) {
|
|
709
|
+
checkError(this._session, rc, 'MimerBeginTransaction');
|
|
710
|
+
}
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
commit() {
|
|
715
|
+
if (!this._connected) {
|
|
716
|
+
throw new Error('Not connected to database');
|
|
717
|
+
}
|
|
718
|
+
const rc = MimerEndTransaction(this._session, MIMER_COMMIT);
|
|
719
|
+
if (rc < 0) {
|
|
720
|
+
checkError(this._session, rc, 'MimerEndTransaction (commit)');
|
|
721
|
+
}
|
|
722
|
+
return true;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
rollback() {
|
|
726
|
+
if (!this._connected) {
|
|
727
|
+
throw new Error('Not connected to database');
|
|
728
|
+
}
|
|
729
|
+
const rc = MimerEndTransaction(this._session, MIMER_ROLLBACK);
|
|
730
|
+
if (rc < 0) {
|
|
731
|
+
checkError(this._session, rc, 'MimerEndTransaction (rollback)');
|
|
732
|
+
}
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
isConnected() {
|
|
737
|
+
return this._connected;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
prepare(sql) {
|
|
741
|
+
if (!this._connected) {
|
|
742
|
+
throw new Error('Not connected to database');
|
|
743
|
+
}
|
|
744
|
+
if (typeof sql !== 'string') {
|
|
745
|
+
throw new TypeError('Expected SQL string as first argument');
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const st = new Statement(this._session, sql, this);
|
|
749
|
+
this._openStatements.add(st);
|
|
750
|
+
return st;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
executeQuery(sql, params) {
|
|
754
|
+
if (!this._connected) {
|
|
755
|
+
throw new Error('Not connected to database');
|
|
756
|
+
}
|
|
757
|
+
if (typeof sql !== 'string') {
|
|
758
|
+
throw new TypeError('Expected SQL string as first argument');
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const hasParams = Array.isArray(params) && params.length > 0;
|
|
762
|
+
|
|
763
|
+
const stmtOut = [null];
|
|
764
|
+
let rc = MimerBeginStatement8(this._session, sql, MIMER_FORWARD_ONLY, stmtOut);
|
|
765
|
+
|
|
766
|
+
if (rc === MIMER_STATEMENT_CANNOT_BE_PREPARED) {
|
|
767
|
+
throw new Error('queryCursor only supports SELECT statements (DDL cannot be prepared)');
|
|
768
|
+
}
|
|
769
|
+
if (rc < 0) {
|
|
770
|
+
checkError(this._session, rc, 'MimerBeginStatement8');
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const stmt = stmtOut[0];
|
|
774
|
+
|
|
775
|
+
if (hasParams) {
|
|
776
|
+
try {
|
|
777
|
+
bindParameters(stmt, params, this._session);
|
|
778
|
+
} catch (e) {
|
|
779
|
+
const stmtRef = [stmt];
|
|
780
|
+
MimerEndStatement(stmtRef);
|
|
781
|
+
throw e;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
const columnCount = MimerColumnCount(stmt);
|
|
786
|
+
if (columnCount <= 0) {
|
|
787
|
+
const stmtRef = [stmt];
|
|
788
|
+
MimerEndStatement(stmtRef);
|
|
789
|
+
throw new Error('queryCursor only supports SELECT statements (DML has no result columns)');
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
rc = MimerOpenCursor(stmt);
|
|
793
|
+
if (rc < 0) {
|
|
794
|
+
const stmtRef = [stmt];
|
|
795
|
+
MimerEndStatement(stmtRef);
|
|
796
|
+
checkError(this._session, rc, 'MimerOpenCursor');
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
const rs = new ResultSet(stmt, columnCount, this);
|
|
800
|
+
this._openResultSets.add(rs);
|
|
801
|
+
return rs;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
_unregisterStatement(st) {
|
|
805
|
+
this._openStatements.delete(st);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
_unregisterResultSet(rs) {
|
|
809
|
+
this._openResultSets.delete(rs);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// ---------------------------------------------------------------------------
|
|
814
|
+
// Statement class
|
|
815
|
+
// ---------------------------------------------------------------------------
|
|
816
|
+
class Statement {
|
|
817
|
+
constructor(session, sql, parentConnection) {
|
|
818
|
+
this._session = session;
|
|
819
|
+
this._parentConnection = parentConnection;
|
|
820
|
+
this._closed = false;
|
|
821
|
+
|
|
822
|
+
const stmtOut = [null];
|
|
823
|
+
const rc = MimerBeginStatement8(session, sql, MIMER_FORWARD_ONLY, stmtOut);
|
|
824
|
+
if (rc < 0) {
|
|
825
|
+
throwMimerError(rc, 'MimerBeginStatement8');
|
|
826
|
+
}
|
|
827
|
+
this._stmt = stmtOut[0];
|
|
828
|
+
this._columnCount = MimerColumnCount(this._stmt);
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
execute(params) {
|
|
832
|
+
if (this._closed) {
|
|
833
|
+
throw new Error('Statement is closed');
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
if (Array.isArray(params) && params.length > 0) {
|
|
837
|
+
bindParameters(this._stmt, params, this._session);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
const hasResultSet = this._columnCount > 0;
|
|
841
|
+
const result = {};
|
|
842
|
+
|
|
843
|
+
if (hasResultSet) {
|
|
844
|
+
result.fields = buildFieldsArray(this._stmt, this._columnCount);
|
|
845
|
+
|
|
846
|
+
const rc = MimerOpenCursor(this._stmt);
|
|
847
|
+
if (rc < 0) {
|
|
848
|
+
throwMimerError(rc, 'MimerOpenCursor');
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const rows = fetchResults(this._stmt, this._columnCount);
|
|
852
|
+
MimerCloseCursor(this._stmt);
|
|
853
|
+
|
|
854
|
+
result.rows = rows;
|
|
855
|
+
result.rowCount = rows.length;
|
|
856
|
+
} else {
|
|
857
|
+
const rc = MimerExecute(this._stmt);
|
|
858
|
+
if (rc < 0) {
|
|
859
|
+
throwMimerError(rc, 'MimerExecute');
|
|
860
|
+
}
|
|
861
|
+
result.rowCount = rc;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
return result;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
close() {
|
|
868
|
+
this._closeInternal();
|
|
869
|
+
return true;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
_closeInternal() {
|
|
873
|
+
if (!this._closed && this._stmt) {
|
|
874
|
+
const stmtRef = [this._stmt];
|
|
875
|
+
MimerEndStatement(stmtRef);
|
|
876
|
+
}
|
|
877
|
+
this._closed = true;
|
|
878
|
+
if (this._parentConnection) {
|
|
879
|
+
this._parentConnection._unregisterStatement(this);
|
|
880
|
+
this._parentConnection = null;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
_invalidate() {
|
|
885
|
+
if (!this._closed && this._stmt) {
|
|
886
|
+
const stmtRef = [this._stmt];
|
|
887
|
+
MimerEndStatement(stmtRef);
|
|
888
|
+
}
|
|
889
|
+
this._closed = true;
|
|
890
|
+
this._parentConnection = null;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// ---------------------------------------------------------------------------
|
|
895
|
+
// ResultSet class
|
|
896
|
+
// ---------------------------------------------------------------------------
|
|
897
|
+
class ResultSet {
|
|
898
|
+
constructor(stmt, columnCount, parentConnection) {
|
|
899
|
+
this._stmt = stmt;
|
|
900
|
+
this._columnCount = columnCount;
|
|
901
|
+
this._parentConnection = parentConnection;
|
|
902
|
+
this._closed = false;
|
|
903
|
+
this._exhausted = false;
|
|
904
|
+
|
|
905
|
+
const meta = cacheColumnMetadata(stmt, columnCount);
|
|
906
|
+
this._colNames = meta.colNames;
|
|
907
|
+
this._colTypes = meta.colTypes;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
fetchNext() {
|
|
911
|
+
if (this._closed || this._exhausted) {
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const rc = MimerFetch(this._stmt);
|
|
916
|
+
if (rc === MIMER_SUCCESS) {
|
|
917
|
+
return fetchSingleRow(this._stmt, this._columnCount, this._colNames, this._colTypes);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
this._exhausted = true;
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
getFields() {
|
|
925
|
+
if (this._closed) return [];
|
|
926
|
+
return buildFieldsArray(this._stmt, this._columnCount);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
close() {
|
|
930
|
+
this._closeInternal();
|
|
931
|
+
return true;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
isClosed() {
|
|
935
|
+
return this._closed;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
_closeInternal() {
|
|
939
|
+
if (!this._closed && this._stmt) {
|
|
940
|
+
MimerCloseCursor(this._stmt);
|
|
941
|
+
const stmtRef = [this._stmt];
|
|
942
|
+
MimerEndStatement(stmtRef);
|
|
943
|
+
}
|
|
944
|
+
this._closed = true;
|
|
945
|
+
if (this._parentConnection) {
|
|
946
|
+
this._parentConnection._unregisterResultSet(this);
|
|
947
|
+
this._parentConnection = null;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
_invalidate() {
|
|
952
|
+
if (!this._closed && this._stmt) {
|
|
953
|
+
MimerCloseCursor(this._stmt);
|
|
954
|
+
const stmtRef = [this._stmt];
|
|
955
|
+
MimerEndStatement(stmtRef);
|
|
956
|
+
}
|
|
957
|
+
this._closed = true;
|
|
958
|
+
this._parentConnection = null;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// ---------------------------------------------------------------------------
|
|
963
|
+
// Module exports (same shape as the C++ addon)
|
|
964
|
+
// ---------------------------------------------------------------------------
|
|
965
|
+
module.exports = {
|
|
966
|
+
Connection,
|
|
967
|
+
Statement,
|
|
968
|
+
ResultSet,
|
|
969
|
+
version: '1.0.0',
|
|
970
|
+
};
|