@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mimer Information Technology AB
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,670 @@
|
|
|
1
|
+
# node-mimer
|
|
2
|
+
|
|
3
|
+
Node.js driver for Mimer SQL using the Mimer SQL C API.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Direct bindings to the Mimer SQL C API via [Koffi](https://koffi.dev/) FFI - no ODBC required
|
|
8
|
+
- Pure JavaScript - no C++ compiler needed at install time
|
|
9
|
+
- Promise-based API
|
|
10
|
+
- Parameterized queries with `?` placeholders (SQL injection safe)
|
|
11
|
+
- Prepared statements for repeated execution
|
|
12
|
+
- Result metadata with column names, types, and nullability
|
|
13
|
+
- Support for all major Mimer SQL data types (including BLOB, NCLOB, UUID)
|
|
14
|
+
- Transaction support (commit/rollback)
|
|
15
|
+
- Structured error objects with Mimer error codes
|
|
16
|
+
- Cursor support for streaming large result sets (`for await`)
|
|
17
|
+
- Connection pooling with automatic acquire/release
|
|
18
|
+
|
|
19
|
+
## Prerequisites
|
|
20
|
+
|
|
21
|
+
- Node.js 18.0 or later
|
|
22
|
+
- Mimer SQL 11.0 or later installed (the `libmimerapi` shared library must be available)
|
|
23
|
+
|
|
24
|
+
No C++ compiler is required. The package uses Koffi FFI to call the Mimer SQL C API
|
|
25
|
+
directly from JavaScript.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @mimersql/node-mimer
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That's it. No native compilation, no prebuilt binaries to download.
|
|
34
|
+
|
|
35
|
+
### Verifying Mimer SQL is found
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm run check-mimer
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Platform-specific library locations
|
|
42
|
+
|
|
43
|
+
**Linux:** `libmimerapi.so` (found via the standard library search path)
|
|
44
|
+
|
|
45
|
+
**macOS:** `/usr/local/lib/libmimerapi.dylib`
|
|
46
|
+
|
|
47
|
+
**Windows:** Automatically detected via `MIMER_HOME` environment variable,
|
|
48
|
+
Windows Registry, or Program Files scan (highest version wins).
|
|
49
|
+
|
|
50
|
+
## Usage
|
|
51
|
+
|
|
52
|
+
### Basic Example
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
const { connect } = require('@mimersql/node-mimer');
|
|
56
|
+
|
|
57
|
+
async function example() {
|
|
58
|
+
// Connect to database
|
|
59
|
+
const client = await connect({
|
|
60
|
+
dsn: 'mydb',
|
|
61
|
+
user: 'SYSADM',
|
|
62
|
+
password: 'password'
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Execute a query
|
|
66
|
+
const result = await client.query('SELECT * FROM my_table');
|
|
67
|
+
console.log(result.rows);
|
|
68
|
+
|
|
69
|
+
// Close connection
|
|
70
|
+
await client.close();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
example();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Using Transactions
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
const { MimerClient } = require('@mimersql/node-mimer');
|
|
80
|
+
|
|
81
|
+
async function transactionExample() {
|
|
82
|
+
const client = new MimerClient();
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await client.connect({
|
|
86
|
+
dsn: 'mydb',
|
|
87
|
+
user: 'SYSADM',
|
|
88
|
+
password: 'password'
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Start transaction
|
|
92
|
+
await client.beginTransaction();
|
|
93
|
+
|
|
94
|
+
// Execute multiple statements
|
|
95
|
+
await client.query("INSERT INTO accounts VALUES (1, 1000)");
|
|
96
|
+
await client.query("INSERT INTO accounts VALUES (2, 500)");
|
|
97
|
+
|
|
98
|
+
// Commit transaction
|
|
99
|
+
await client.commit();
|
|
100
|
+
|
|
101
|
+
} catch (error) {
|
|
102
|
+
// Rollback on error
|
|
103
|
+
await client.rollback();
|
|
104
|
+
throw error;
|
|
105
|
+
} finally {
|
|
106
|
+
await client.close();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Parameterized Queries
|
|
112
|
+
|
|
113
|
+
Use `?` placeholders to pass parameters safely. This prevents SQL injection and
|
|
114
|
+
lets the database optimize execution.
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
// INSERT with parameters
|
|
118
|
+
await client.query('INSERT INTO users VALUES (?, ?)', [1, 'Anna']);
|
|
119
|
+
|
|
120
|
+
// SELECT with a WHERE parameter
|
|
121
|
+
const result = await client.query(
|
|
122
|
+
'SELECT * FROM users WHERE id = ?', [1]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Multiple types are supported
|
|
126
|
+
await client.query(
|
|
127
|
+
'INSERT INTO readings VALUES (?, ?, ?, ?)',
|
|
128
|
+
[42, 'sensor-1', 3.14, true]
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// NULL values
|
|
132
|
+
await client.query(
|
|
133
|
+
'INSERT INTO users VALUES (?, ?)', [2, null]
|
|
134
|
+
);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Parameter types are mapped automatically:
|
|
138
|
+
|
|
139
|
+
| JavaScript type | Mimer SQL binding |
|
|
140
|
+
|-----------------|-------------------|
|
|
141
|
+
| `number` (integer) | `INTEGER` or `BIGINT` |
|
|
142
|
+
| `number` (decimal) | `DOUBLE PRECISION` |
|
|
143
|
+
| `string` | `VARCHAR` / `NVARCHAR` |
|
|
144
|
+
| `boolean` | `BOOLEAN` |
|
|
145
|
+
| `null` / `undefined` | `NULL` |
|
|
146
|
+
| `Buffer` | `BINARY` / `BLOB` |
|
|
147
|
+
|
|
148
|
+
### Prepared Statements
|
|
149
|
+
|
|
150
|
+
For statements executed multiple times with different parameters, prepared
|
|
151
|
+
statements avoid re-parsing the SQL on each call.
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
// Prepare once
|
|
155
|
+
const stmt = await client.prepare('INSERT INTO users VALUES (?, ?)');
|
|
156
|
+
|
|
157
|
+
// Execute many times with different parameters
|
|
158
|
+
await stmt.execute([1, 'Anna']);
|
|
159
|
+
await stmt.execute([2, 'Fredrik']);
|
|
160
|
+
await stmt.execute([3, 'Charlie']);
|
|
161
|
+
|
|
162
|
+
// Always close when done to release database resources
|
|
163
|
+
await stmt.close();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Prepared SELECT statements work the same way:
|
|
167
|
+
|
|
168
|
+
```javascript
|
|
169
|
+
const stmt = await client.prepare(
|
|
170
|
+
'SELECT * FROM users WHERE id = ?'
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const r1 = await stmt.execute([1]);
|
|
174
|
+
console.log(r1.rows); // [{ id: 1, name: 'Anna' }]
|
|
175
|
+
|
|
176
|
+
const r2 = await stmt.execute([2]);
|
|
177
|
+
console.log(r2.rows); // [{ id: 2, name: 'Fredrik' }]
|
|
178
|
+
|
|
179
|
+
await stmt.close();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Cursors (Streaming Large Result Sets)
|
|
183
|
+
|
|
184
|
+
For large result sets, `queryCursor()` returns a cursor that fetches rows one
|
|
185
|
+
at a time instead of loading everything into memory. The cursor implements the
|
|
186
|
+
async iterator protocol, so you can use `for await...of`.
|
|
187
|
+
|
|
188
|
+
```javascript
|
|
189
|
+
const cursor = await client.queryCursor('SELECT * FROM big_table');
|
|
190
|
+
|
|
191
|
+
for await (const row of cursor) {
|
|
192
|
+
console.log(row);
|
|
193
|
+
if (someDoneCondition) break; // early exit closes the cursor automatically
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
You can also iterate manually with `next()` and `close()`:
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
const cursor = await client.queryCursor(
|
|
201
|
+
'SELECT * FROM orders WHERE status = ?', ['pending']
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Column metadata is available immediately
|
|
205
|
+
console.log(cursor.fields);
|
|
206
|
+
// [{ name: 'id', dataTypeCode: 50, dataTypeName: 'INTEGER', nullable: true }, ...]
|
|
207
|
+
|
|
208
|
+
let row;
|
|
209
|
+
while ((row = await cursor.next()) !== null) {
|
|
210
|
+
process(row);
|
|
211
|
+
}
|
|
212
|
+
// cursor closes automatically when exhausted, or call cursor.close() to stop early
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`queryCursor()` only accepts SELECT statements. DDL and DML statements are
|
|
216
|
+
rejected with an error.
|
|
217
|
+
|
|
218
|
+
### Connection Pool
|
|
219
|
+
|
|
220
|
+
For applications that need concurrent database access, the connection pool
|
|
221
|
+
manages a set of reusable connections — lending them out on demand and returning
|
|
222
|
+
them when done.
|
|
223
|
+
|
|
224
|
+
```javascript
|
|
225
|
+
const { createPool } = require('@mimersql/node-mimer');
|
|
226
|
+
|
|
227
|
+
const pool = createPool({
|
|
228
|
+
dsn: 'mydb',
|
|
229
|
+
user: 'SYSADM',
|
|
230
|
+
password: 'password',
|
|
231
|
+
max: 10, // maximum connections (default 10)
|
|
232
|
+
idleTimeout: 30000, // ms before idle connection is closed (default 30s)
|
|
233
|
+
acquireTimeout: 5000, // ms to wait for a connection (default 5s)
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Convenience — auto-acquires and releases a connection
|
|
237
|
+
const result = await pool.query('SELECT * FROM t WHERE id = ?', [1]);
|
|
238
|
+
|
|
239
|
+
// Cursor — connection held until cursor closes
|
|
240
|
+
const cursor = await pool.queryCursor('SELECT * FROM big_table');
|
|
241
|
+
for await (const row of cursor) { ... }
|
|
242
|
+
// connection auto-released when cursor closes or exhausts
|
|
243
|
+
|
|
244
|
+
// Explicit checkout — for transactions or multiple operations
|
|
245
|
+
const client = await pool.connect();
|
|
246
|
+
try {
|
|
247
|
+
await client.beginTransaction();
|
|
248
|
+
await client.query('INSERT INTO t VALUES (?, ?)', [1, 'a']);
|
|
249
|
+
await client.commit();
|
|
250
|
+
} finally {
|
|
251
|
+
client.release(); // return to pool (NOT client.close())
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Shutdown — closes all connections
|
|
255
|
+
await pool.end();
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Result Metadata
|
|
259
|
+
|
|
260
|
+
SELECT queries return a `fields` array with column metadata alongside `rows`.
|
|
261
|
+
This is available even when the query returns zero rows, letting you discover
|
|
262
|
+
column names and types without needing actual data.
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
const result = await client.query('SELECT id, name FROM users');
|
|
266
|
+
|
|
267
|
+
console.log(result.fields);
|
|
268
|
+
// [
|
|
269
|
+
// { name: 'id', dataTypeCode: 50, dataTypeName: 'INTEGER', nullable: true },
|
|
270
|
+
// { name: 'name', dataTypeCode: 63, dataTypeName: 'NVARCHAR', nullable: false }
|
|
271
|
+
// ]
|
|
272
|
+
|
|
273
|
+
console.log(result.rows);
|
|
274
|
+
// [{ id: 1, name: 'Anna' }, ...]
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Each field object contains:
|
|
278
|
+
|
|
279
|
+
| Property | Type | Description |
|
|
280
|
+
|----------|------|-------------|
|
|
281
|
+
| `name` | string | Column name |
|
|
282
|
+
| `dataTypeCode` | number | Numeric Mimer type code |
|
|
283
|
+
| `dataTypeName` | string | Human-readable SQL type (`INTEGER`, `DOUBLE PRECISION`, `BOOLEAN`, etc.) |
|
|
284
|
+
| `nullable` | boolean | Whether the column allows NULL values |
|
|
285
|
+
|
|
286
|
+
Zero-row results still include full metadata:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
const result = await client.query('SELECT * FROM users WHERE 1 = 0');
|
|
290
|
+
console.log(result.rowCount); // 0
|
|
291
|
+
console.log(result.fields.length); // 2 — column info is still available
|
|
292
|
+
console.log(result.fields[0].name); // 'id'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
DML statements (INSERT, UPDATE, DELETE) do not return `fields` — only
|
|
296
|
+
`rowCount`.
|
|
297
|
+
|
|
298
|
+
Prepared statements also return `fields` on each `execute()`:
|
|
299
|
+
|
|
300
|
+
```javascript
|
|
301
|
+
const stmt = await client.prepare('SELECT * FROM users WHERE id = ?');
|
|
302
|
+
const result = await stmt.execute([1]);
|
|
303
|
+
console.log(result.fields[0].dataTypeName); // 'INTEGER'
|
|
304
|
+
await stmt.close();
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Error Handling
|
|
308
|
+
|
|
309
|
+
All errors from the Mimer SQL C API are thrown as JavaScript `Error` objects
|
|
310
|
+
with structured properties:
|
|
311
|
+
|
|
312
|
+
```javascript
|
|
313
|
+
try {
|
|
314
|
+
await client.query('INSERT INTO nonexistent VALUES (1)');
|
|
315
|
+
} catch (err) {
|
|
316
|
+
console.log(err.message); // Human-readable description
|
|
317
|
+
console.log(err.mimerCode); // Numeric Mimer error code (e.g. -12200)
|
|
318
|
+
console.log(err.operation); // C API function that failed (e.g. "MimerExecute")
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
This lets you handle specific error conditions programmatically:
|
|
323
|
+
|
|
324
|
+
```javascript
|
|
325
|
+
try {
|
|
326
|
+
await client.query('CREATE TABLE t1 (id INTEGER)');
|
|
327
|
+
} catch (err) {
|
|
328
|
+
if (err.mimerCode === -12517) {
|
|
329
|
+
// Table already exists — ignore
|
|
330
|
+
} else {
|
|
331
|
+
throw err;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
Parameter binding errors also include structured properties:
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
try {
|
|
340
|
+
// Wrong number of parameters
|
|
341
|
+
await client.query('INSERT INTO users VALUES (?, ?)', [1]);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
console.log(err.mimerCode); // 0 (validation error, not from Mimer API)
|
|
344
|
+
console.log(err.operation); // "BindParameters"
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Data Type Mapping
|
|
349
|
+
|
|
350
|
+
| Mimer SQL Type | JavaScript Type |
|
|
351
|
+
|----------------|-----------------|
|
|
352
|
+
| INTEGER, SMALLINT | Number |
|
|
353
|
+
| BIGINT | Number |
|
|
354
|
+
| FLOAT, REAL, DOUBLE | Number |
|
|
355
|
+
| VARCHAR, NVARCHAR | String |
|
|
356
|
+
| CHARACTER, NCHAR | String |
|
|
357
|
+
| DATE, TIME, TIMESTAMP | String |
|
|
358
|
+
| DECIMAL, NUMERIC | String |
|
|
359
|
+
| UUID | String |
|
|
360
|
+
| BINARY, VARBINARY | Buffer |
|
|
361
|
+
| BLOB | Buffer |
|
|
362
|
+
| CLOB, NCLOB | String |
|
|
363
|
+
| BOOLEAN | Boolean |
|
|
364
|
+
| NULL | null |
|
|
365
|
+
|
|
366
|
+
## Backend Selection
|
|
367
|
+
|
|
368
|
+
By default, `node-mimer` uses the Koffi FFI backend (pure JavaScript).
|
|
369
|
+
|
|
370
|
+
If the optional [`node-mimer-native`](https://github.com/user/node-mimer-native)
|
|
371
|
+
package is installed, it will be used instead. This can be useful as a fallback
|
|
372
|
+
if Koffi is ever unavailable for a given platform.
|
|
373
|
+
|
|
374
|
+
You can force a specific backend with the `NODE_MIMER_BACKEND` environment variable:
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
# Force Koffi FFI (default)
|
|
378
|
+
NODE_MIMER_BACKEND=koffi npm test
|
|
379
|
+
|
|
380
|
+
# Force native C++ addon (requires node-mimer-native)
|
|
381
|
+
NODE_MIMER_BACKEND=native npm test
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## API Reference
|
|
385
|
+
|
|
386
|
+
### MimerClient
|
|
387
|
+
|
|
388
|
+
#### `async connect(options)`
|
|
389
|
+
|
|
390
|
+
Connect to a Mimer SQL database.
|
|
391
|
+
|
|
392
|
+
**Parameters:**
|
|
393
|
+
- `options.dsn` (string): Database name
|
|
394
|
+
- `options.user` (string): Username
|
|
395
|
+
- `options.password` (string): Password
|
|
396
|
+
|
|
397
|
+
#### `async query(sql, params)`
|
|
398
|
+
|
|
399
|
+
Execute a SQL statement with optional parameter binding.
|
|
400
|
+
|
|
401
|
+
**Parameters:**
|
|
402
|
+
- `sql` (string): SQL statement, may contain `?` placeholders
|
|
403
|
+
- `params` (array, optional): Values to bind to `?` placeholders
|
|
404
|
+
|
|
405
|
+
**Returns:** Result object:
|
|
406
|
+
- For SELECT: `{ rows, rowCount, fields }` — `fields` is an array of column metadata
|
|
407
|
+
- For DML (INSERT/UPDATE/DELETE): `{ rowCount }`
|
|
408
|
+
- For DDL (CREATE/DROP/ALTER): `{ rowCount: 0 }`
|
|
409
|
+
|
|
410
|
+
#### `async prepare(sql)`
|
|
411
|
+
|
|
412
|
+
Prepare a SQL statement for repeated execution.
|
|
413
|
+
|
|
414
|
+
**Parameters:**
|
|
415
|
+
- `sql` (string): SQL statement with `?` placeholders
|
|
416
|
+
|
|
417
|
+
**Returns:** `PreparedStatement` instance
|
|
418
|
+
|
|
419
|
+
### PreparedStatement
|
|
420
|
+
|
|
421
|
+
#### `async execute(params)`
|
|
422
|
+
|
|
423
|
+
Execute the prepared statement with parameter values.
|
|
424
|
+
|
|
425
|
+
**Parameters:**
|
|
426
|
+
- `params` (array, optional): Values to bind to `?` placeholders
|
|
427
|
+
|
|
428
|
+
**Returns:** Result object:
|
|
429
|
+
- For SELECT statements: `{ rows, rowCount, fields }`
|
|
430
|
+
- For DML statements: `{ rowCount }`
|
|
431
|
+
|
|
432
|
+
#### `async close()`
|
|
433
|
+
|
|
434
|
+
Close the prepared statement and release its database resources. The statement
|
|
435
|
+
cannot be used after calling `close()`.
|
|
436
|
+
|
|
437
|
+
#### `async queryCursor(sql, params)`
|
|
438
|
+
|
|
439
|
+
Execute a SELECT query and return a cursor for row-at-a-time streaming.
|
|
440
|
+
|
|
441
|
+
**Parameters:**
|
|
442
|
+
- `sql` (string): SELECT statement, may contain `?` placeholders
|
|
443
|
+
- `params` (array, optional): Values to bind to `?` placeholders
|
|
444
|
+
|
|
445
|
+
**Returns:** `ResultSet` instance
|
|
446
|
+
|
|
447
|
+
**Throws:** Error if `sql` is a DDL or DML statement.
|
|
448
|
+
|
|
449
|
+
### ResultSet
|
|
450
|
+
|
|
451
|
+
Returned by `queryCursor()`. Fetches rows one at a time from an open
|
|
452
|
+
database cursor. Implements `Symbol.asyncIterator` for `for await...of`.
|
|
453
|
+
|
|
454
|
+
#### `fields`
|
|
455
|
+
|
|
456
|
+
Column metadata array (same format as `query()` results). Available
|
|
457
|
+
immediately after creation.
|
|
458
|
+
|
|
459
|
+
#### `async next()`
|
|
460
|
+
|
|
461
|
+
Fetch the next row as a plain object, or `null` when all rows have been read.
|
|
462
|
+
The cursor closes automatically when exhausted.
|
|
463
|
+
|
|
464
|
+
#### `async close()`
|
|
465
|
+
|
|
466
|
+
Close the cursor and release database resources. Safe to call multiple times.
|
|
467
|
+
Called automatically when `for await...of` ends or `break`s.
|
|
468
|
+
|
|
469
|
+
#### `async beginTransaction()`
|
|
470
|
+
|
|
471
|
+
Begin a new transaction.
|
|
472
|
+
|
|
473
|
+
#### `async commit()`
|
|
474
|
+
|
|
475
|
+
Commit the current transaction.
|
|
476
|
+
|
|
477
|
+
#### `async rollback()`
|
|
478
|
+
|
|
479
|
+
Rollback the current transaction.
|
|
480
|
+
|
|
481
|
+
#### `async close()`
|
|
482
|
+
|
|
483
|
+
Close the database connection.
|
|
484
|
+
|
|
485
|
+
#### `isConnected()`
|
|
486
|
+
|
|
487
|
+
Check if connected to database.
|
|
488
|
+
|
|
489
|
+
**Returns:** boolean
|
|
490
|
+
|
|
491
|
+
### Pool
|
|
492
|
+
|
|
493
|
+
#### `createPool(options)`
|
|
494
|
+
|
|
495
|
+
Create a new connection pool.
|
|
496
|
+
|
|
497
|
+
**Parameters:**
|
|
498
|
+
- `options.dsn` (string): Database name
|
|
499
|
+
- `options.user` (string): Username
|
|
500
|
+
- `options.password` (string): Password
|
|
501
|
+
- `options.max` (number, optional): Maximum connections (default 10)
|
|
502
|
+
- `options.idleTimeout` (number, optional): Ms before idle connection is closed (default 30000)
|
|
503
|
+
- `options.acquireTimeout` (number, optional): Ms to wait for a connection (default 5000)
|
|
504
|
+
|
|
505
|
+
**Returns:** `Pool` instance
|
|
506
|
+
|
|
507
|
+
#### `async pool.query(sql, params)`
|
|
508
|
+
|
|
509
|
+
Acquire a connection, execute the query, and release the connection.
|
|
510
|
+
|
|
511
|
+
**Returns:** Result object (same as `MimerClient.query()`)
|
|
512
|
+
|
|
513
|
+
#### `async pool.queryCursor(sql, params)`
|
|
514
|
+
|
|
515
|
+
Acquire a connection and open a cursor. The connection is automatically
|
|
516
|
+
released when the cursor closes or is exhausted.
|
|
517
|
+
|
|
518
|
+
**Returns:** `ResultSet` instance
|
|
519
|
+
|
|
520
|
+
#### `async pool.connect()`
|
|
521
|
+
|
|
522
|
+
Check out a connection for multiple operations (e.g. transactions).
|
|
523
|
+
|
|
524
|
+
**Returns:** `PoolClient` instance
|
|
525
|
+
|
|
526
|
+
#### `async pool.end()`
|
|
527
|
+
|
|
528
|
+
Close all idle connections and reject pending waiters. Operations after
|
|
529
|
+
`end()` will reject with an error.
|
|
530
|
+
|
|
531
|
+
### PoolClient
|
|
532
|
+
|
|
533
|
+
Returned by `pool.connect()`. Delegates `query()`, `queryCursor()`,
|
|
534
|
+
`prepare()`, `beginTransaction()`, `commit()`, and `rollback()` to the
|
|
535
|
+
underlying `MimerClient`.
|
|
536
|
+
|
|
537
|
+
#### `release()`
|
|
538
|
+
|
|
539
|
+
Return the connection to the pool. Always call this instead of `close()`.
|
|
540
|
+
Safe to call multiple times.
|
|
541
|
+
|
|
542
|
+
### Helper Functions
|
|
543
|
+
|
|
544
|
+
#### `async connect(options)`
|
|
545
|
+
|
|
546
|
+
Create and connect a new MimerClient instance.
|
|
547
|
+
|
|
548
|
+
**Returns:** Connected MimerClient instance
|
|
549
|
+
|
|
550
|
+
## Testing
|
|
551
|
+
|
|
552
|
+
Tests use the Node.js built-in test runner (`node:test`) and are split into
|
|
553
|
+
focused files by category:
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
test/
|
|
557
|
+
helper.js # Shared utilities (createClient, dropTable)
|
|
558
|
+
connection.test.js # Connect, isConnected, close
|
|
559
|
+
basic-queries.test.js # DDL, DML, SELECT
|
|
560
|
+
transactions.test.js # beginTransaction, commit, rollback
|
|
561
|
+
result-metadata.test.js # fields array, properties, edge cases
|
|
562
|
+
unicode.test.js # NVARCHAR round-trip, Unicode WHERE
|
|
563
|
+
parameterized-queries.test.js # ? params, types, NULL, mismatch error
|
|
564
|
+
prepared-statements.test.js # prepare/execute/close lifecycle
|
|
565
|
+
cursor.test.js # queryCursor, for-await-of, early break
|
|
566
|
+
error-handling.test.js # Structured errors (mimerCode, operation)
|
|
567
|
+
pool.test.js # Connection pool, PoolClient, auto-release
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
# Run all tests (requires a running Mimer SQL instance)
|
|
572
|
+
npm test
|
|
573
|
+
|
|
574
|
+
# Run a single test file
|
|
575
|
+
node --test test/unicode.test.js
|
|
576
|
+
|
|
577
|
+
# Test with a specific backend
|
|
578
|
+
NODE_MIMER_BACKEND=koffi npm test
|
|
579
|
+
NODE_MIMER_BACKEND=native npm test # requires node-mimer-native
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
## Architecture
|
|
583
|
+
|
|
584
|
+
The library has two layers:
|
|
585
|
+
|
|
586
|
+
```
|
|
587
|
+
Application (JavaScript)
|
|
588
|
+
|
|
|
589
|
+
| require('@mimersql/node-mimer')
|
|
590
|
+
v
|
|
591
|
+
JavaScript Wrapper (Promise-based)
|
|
592
|
+
index.js, lib/client.js, lib/prepared.js,
|
|
593
|
+
lib/resultset.js, lib/pool.js
|
|
594
|
+
|
|
|
595
|
+
| lib/native.js (backend selection)
|
|
596
|
+
v
|
|
597
|
+
Koffi FFI Backend (lib/koffi-binding.js)
|
|
598
|
+
— or —
|
|
599
|
+
node-mimer-native (C++ addon, optional)
|
|
600
|
+
|
|
|
601
|
+
| FFI / Node-API calls
|
|
602
|
+
v
|
|
603
|
+
Mimer SQL C API (libmimerapi.so)
|
|
604
|
+
|
|
|
605
|
+
v
|
|
606
|
+
Mimer SQL Database
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
The Koffi FFI backend (`lib/koffi-binding.js`) calls the Mimer SQL C API
|
|
610
|
+
functions directly using [Koffi](https://koffi.dev/), an FFI library that ships
|
|
611
|
+
its own prebuilt binaries. This means `node-mimer` is a pure JavaScript package
|
|
612
|
+
with no native compilation step.
|
|
613
|
+
|
|
614
|
+
The optional `node-mimer-native` package provides the same interface using a
|
|
615
|
+
C++ Node-API addon. It can be installed alongside `node-mimer` as a drop-in
|
|
616
|
+
replacement if needed.
|
|
617
|
+
|
|
618
|
+
### Mimer SQL C API Functions Used
|
|
619
|
+
|
|
620
|
+
- `MimerBeginSession8()` - Establish database connection
|
|
621
|
+
- `MimerEndSession()` - Close connection
|
|
622
|
+
- `MimerBeginStatement8()` - Prepare SQL statements
|
|
623
|
+
- `MimerExecuteStatement8()` - Execute DDL statements directly
|
|
624
|
+
- `MimerOpenCursor()` / `MimerFetch()` - Fetch result rows
|
|
625
|
+
- `MimerGetString8()`, `MimerGetInt32()`, etc. - Get column values
|
|
626
|
+
- `MimerSetString8()`, `MimerSetInt32()`, etc. - Bind parameter values
|
|
627
|
+
- `MimerSetLob()` / `MimerSetBlobData()` / `MimerSetNclobData8()` - Write LOBs
|
|
628
|
+
- `MimerGetLob()` / `MimerGetBlobData()` / `MimerGetNclobData8()` - Read LOBs
|
|
629
|
+
- `MimerBeginTransaction()` / `MimerEndTransaction()` - Transaction control
|
|
630
|
+
- `MimerGetError8()` - Error handling
|
|
631
|
+
|
|
632
|
+
## File Structure
|
|
633
|
+
|
|
634
|
+
```
|
|
635
|
+
node-mimer/
|
|
636
|
+
├── lib/ # JavaScript modules
|
|
637
|
+
│ ├── native.js # Backend selection (Koffi or native)
|
|
638
|
+
│ ├── koffi-binding.js # Koffi FFI backend
|
|
639
|
+
│ ├── find-mimer-library.js # Platform-specific library detection
|
|
640
|
+
│ ├── client.js # MimerClient, connect()
|
|
641
|
+
│ ├── prepared.js # PreparedStatement
|
|
642
|
+
│ ├── resultset.js # ResultSet (cursor wrapper)
|
|
643
|
+
│ └── pool.js # Pool, PoolClient
|
|
644
|
+
│
|
|
645
|
+
├── scripts/
|
|
646
|
+
│ └── check-mimer.js # Verify Mimer installation
|
|
647
|
+
│
|
|
648
|
+
├── index.js # Re-exports from lib/
|
|
649
|
+
├── index.d.ts # TypeScript type definitions
|
|
650
|
+
├── package.json
|
|
651
|
+
├── README.md
|
|
652
|
+
│
|
|
653
|
+
└── test/
|
|
654
|
+
├── helper.js # Shared test utilities
|
|
655
|
+
└── *.test.js # Test suites (node:test)
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
## License
|
|
659
|
+
|
|
660
|
+
MIT
|
|
661
|
+
|
|
662
|
+
## Contributing
|
|
663
|
+
|
|
664
|
+
Contributions are welcome! Please submit pull requests or open issues on GitHub.
|
|
665
|
+
|
|
666
|
+
## References
|
|
667
|
+
|
|
668
|
+
- [Mimer SQL Documentation](https://developer.mimer.com/documentation)
|
|
669
|
+
- [Mimer SQL C API Reference](https://developer.mimer.com/mimerapi)
|
|
670
|
+
- [Koffi FFI Library](https://koffi.dev/)
|