jsql-neo 5.4.2 → 5.5.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.
Files changed (72) hide show
  1. package/LICENSE +202 -202
  2. package/README.md +8097 -7971
  3. package/bin/jsql +320 -320
  4. package/bin/jsql-server +90 -90
  5. package/bin/jsql-server-control.js +177 -177
  6. package/index.d.ts +385 -357
  7. package/index.js +124 -118
  8. package/lib/ast.js +214 -0
  9. package/lib/btree.js +413 -413
  10. package/lib/cache.js +328 -328
  11. package/lib/client.js +125 -125
  12. package/lib/database.js +1744 -1744
  13. package/lib/date-types.js +182 -182
  14. package/lib/errors.js +89 -89
  15. package/lib/jsql_format.js +497 -497
  16. package/lib/migrate.js +254 -254
  17. package/lib/mod.js +292 -292
  18. package/lib/mongo_server.js +607 -607
  19. package/lib/multiserver.js +140 -140
  20. package/lib/mysql_compat.js +413 -413
  21. package/lib/mysql_server.js +1237 -1237
  22. package/lib/native_client.js +732 -732
  23. package/lib/nedb_compat.js +506 -506
  24. package/lib/pg_server.js +864 -864
  25. package/lib/plugin.js +34 -34
  26. package/lib/query.js +815 -815
  27. package/lib/redis_server.js +571 -571
  28. package/lib/sql.js +3460 -2925
  29. package/lib/sqlite_compat.js +323 -323
  30. package/lib/sqlite_worker.js +264 -264
  31. package/lib/table.js +1304 -1304
  32. package/lib/tui.js +502 -502
  33. package/lib/wasm.js +1 -1
  34. package/lib/wasm_client.js +718 -718
  35. package/lib/web_ui.js +257 -257
  36. package/native/jsql-neo-native.node +0 -0
  37. package/nativesrc/jsql-neo-core/Cargo.lock +455 -455
  38. package/nativesrc/jsql-neo-core/Cargo.toml +24 -24
  39. package/nativesrc/jsql-neo-core/src/engine/hybrid.rs +455 -455
  40. package/nativesrc/jsql-neo-core/src/engine/memory.rs +168 -168
  41. package/nativesrc/jsql-neo-core/src/engine/mod.rs +62 -62
  42. package/nativesrc/jsql-neo-core/src/engine/table.rs +700 -700
  43. package/nativesrc/jsql-neo-core/src/lib.rs +2 -2
  44. package/nativesrc/jsql-neo-core/src/storage/mod.rs +2 -2
  45. package/nativesrc/jsql-neo-core/src/storage/persistent.rs +1 -1
  46. package/nativesrc/jsql-neo-core/src/storage/wal.rs +85 -85
  47. package/nativesrc/jsql-neo-core/src/types.rs +93 -93
  48. package/nativesrc/jsql-neo-native/Cargo.lock +606 -606
  49. package/nativesrc/jsql-neo-native/Cargo.toml +16 -16
  50. package/nativesrc/jsql-neo-native/build.rs +3 -3
  51. package/nativesrc/jsql-neo-native/package.json +7 -7
  52. package/nativesrc/jsql-neo-native/src/lib.rs +269 -269
  53. package/nativesrc/jsql-neo-wasm/Cargo.lock +465 -465
  54. package/nativesrc/jsql-neo-wasm/Cargo.toml +15 -15
  55. package/nativesrc/jsql-neo-wasm/src/lib.rs +447 -447
  56. package/package.json +87 -86
  57. package/postinstall.js +158 -158
  58. package/test/btree.test.js +128 -128
  59. package/test/coverage.js +187 -187
  60. package/test/join.test.js +110 -110
  61. package/test/native.test.js +158 -158
  62. package/test/regress-5.1.0.js +271 -271
  63. package/test/smoke.js +58 -58
  64. package/test/sql-parser.test.js +273 -0
  65. package/test/wasm.test.js +110 -110
  66. package/wasm/browser.d.ts +89 -89
  67. package/wasm/browser.mjs +569 -569
  68. package/wasm/browser_bg.mjs +597 -597
  69. package/wasm/jsql_neo_wasm.d.ts +38 -38
  70. package/wasm/jsql_neo_wasm.js +520 -520
  71. package/wasm/jsql_neo_wasm_bg.wasm.d.ts +26 -26
  72. package/wasm/package.json +11 -11
@@ -1,413 +1,413 @@
1
- // © Vexify 2026 All Rights Reserved.
2
- /**
3
- * MySQL API 兼容层 — createConnection / createPool / query
4
- * 底层用 JSQL SQL 执行器,接口形状对齐 mysql / mysql2 包。
5
- */
6
-
7
- const { executeSQL, applyParams, escapeValue, escapeId } = require('./sql');
8
- const Database = require('./database');
9
- const EventEmitter = require('events');
10
-
11
- function isQueryResult(r) {
12
- return !!r && typeof r === 'object' && Array.isArray(r.rows) && Array.isArray(r.columns);
13
- }
14
-
15
- function toResultPacket(r, table, rowsAsArray) {
16
- if (isQueryResult(r)) {
17
- const columns = r.columns || [];
18
- const rawRows = r.rows || [];
19
- const fields = columns.map(name => ({
20
- name,
21
- table: table || '',
22
- type: 253,
23
- length: 1024,
24
- flags: 0,
25
- charsetNr: 45,
26
- }));
27
- const rows = rowsAsArray
28
- ? rawRows
29
- : rawRows.map(vals => {
30
- const obj = {};
31
- columns.forEach((c, j) => { obj[c] = vals[j]; });
32
- return obj;
33
- });
34
- return { rows, fields };
35
- }
36
- return {
37
- fieldCount: 0,
38
- affectedRows: r && r.affectedRows !== undefined ? r.affectedRows : 0,
39
- insertId: r && r.insertId !== undefined ? r.insertId : 0,
40
- serverStatus: 2,
41
- warningCount: 0,
42
- message: r ? r.type + (r.table ? ' ' + r.table : '') : '',
43
- protocol41: true,
44
- changedRows: r && r.affectedRows !== undefined ? r.affectedRows : 0,
45
- };
46
- }
47
-
48
- class Connection extends EventEmitter {
49
- constructor(options = {}) {
50
- super();
51
- this.config = {
52
- host: options.host || 'localhost',
53
- port: options.port || 3306,
54
- user: options.user || 'root',
55
- password: options.password || '',
56
- database: options.database || null,
57
- multipleStatements: options.multipleStatements === true,
58
- };
59
- this.safety = options.safety !== false;
60
- this.allowComments = options.allowComments === true;
61
- this.database = options.database && typeof options.database === 'object' && !options.filename && typeof options.database.start === 'function'
62
- ? options.database
63
- : (options.engine || null);
64
- this.filename = options.filename || null;
65
- this.engine = null;
66
- this._ownEngine = false;
67
- this.state = 'disconnected';
68
- this.stream = {
69
- destroyed: false,
70
- writable: true,
71
- destroy: () => { this.stream.destroyed = true; },
72
- };
73
- if (options.host !== undefined || options.database || options.engine || options.filename) {
74
- queueMicrotask(() => {
75
- this._getEngine().then(() => this.emit('connect'), err => {
76
- if (this.listenerCount('error') > 0) this.emit('error', err);
77
- });
78
- });
79
- }
80
- }
81
-
82
- async _getEngine() {
83
- if (this.engine) return this.engine;
84
- if (this.database && typeof this.database.start === 'function') {
85
- this.engine = this.database;
86
- await this.engine.start();
87
- } else if (this._pool) {
88
- this.engine = await this._pool._sharedEngineFor(this.config.database || 'default');
89
- } else if (this.filename) {
90
- this.engine = new Database(this.filename);
91
- await this.engine.start();
92
- this._ownEngine = true;
93
- } else {
94
- this.engine = new Database(':memory:');
95
- await this.engine.start();
96
- this._ownEngine = true;
97
- }
98
- this.state = 'connected';
99
- return this.engine;
100
- }
101
-
102
- connect(cb) {
103
- const p = this._getEngine().then(c => {
104
- this.emit('connect');
105
- return c;
106
- });
107
- if (cb) p.then(c => cb(null, c), err => cb(err));
108
- return p;
109
- }
110
-
111
- query(...args) {
112
- let sql, values, cb, rowsAsArray;
113
- if (typeof args[0] === 'object' && args[0] !== null && typeof args[0].sql === 'string') {
114
- sql = args[0].sql;
115
- rowsAsArray = args[0].rowsAsArray === true;
116
- values = args[0].values !== undefined ? args[0].values : (Array.isArray(args[1]) ? args[1] : null);
117
- cb = typeof args[1] === 'function' ? args[1] : (typeof args[2] === 'function' ? args[2] : null);
118
- } else {
119
- sql = args[0];
120
- values = Array.isArray(args[1]) ? args[1] : null;
121
- cb = typeof args[1] === 'function' ? args[1] : (typeof args[2] === 'function' ? args[2] : null);
122
- }
123
- const p = (async () => {
124
- const engine = await this._getEngine();
125
- const finalSql = applyParams(sql, values);
126
- const r = await executeSQL(engine, finalSql, {
127
- safety: this.safety,
128
- allowComments: this.allowComments,
129
- maxStatements: this.config.multipleStatements ? null : 1,
130
- });
131
- const results = Array.isArray(r) ? r : [r];
132
- const last = results[results.length - 1];
133
- const packet = toResultPacket(last, last ? last.table : null, rowsAsArray);
134
- if (isQueryResult(last)) {
135
- const arr = [packet.rows, packet.fields];
136
- arr.rows = packet.rows;
137
- arr.fields = packet.fields;
138
- return arr;
139
- }
140
- const arr = [packet];
141
- arr.rows = null;
142
- arr.fields = null;
143
- return arr;
144
- })();
145
- const q = new EventEmitter();
146
- q.setMaxListeners = EventEmitter.prototype.setMaxListeners;
147
- p.then(res => {
148
- q.emit('result', res.rows || res[0]);
149
- q.emit('fields', res.fields || []);
150
- }, err => {
151
- if (q.listenerCount('error') > 0) q.emit('error', err);
152
- });
153
- if (cb) {
154
- p.then(res => {
155
- if (res && res.rows && res.fields) cb(null, res.rows, res.fields);
156
- else if (res && res[0] && res[0].insertId !== undefined) cb(null, res[0]);
157
- else cb(null, res[0]);
158
- }, err => cb(err));
159
- return q;
160
- }
161
- p.setMaxListeners = q.setMaxListeners;
162
- return p;
163
- }
164
-
165
- execute(sql, values, cb) {
166
- if (typeof values === 'function') {
167
- cb = values;
168
- values = undefined;
169
- }
170
- return this.query(sql, values, cb);
171
- }
172
-
173
- beginTransaction(cb) {
174
- const p = this.query('BEGIN');
175
- if (cb) p.then(() => cb(null), err => cb(err));
176
- return p;
177
- }
178
-
179
- commit(cb) {
180
- const p = this.query('COMMIT');
181
- if (cb) p.then(() => cb(null), err => cb(err));
182
- return p;
183
- }
184
-
185
- rollback(cb) {
186
- const p = this.query('ROLLBACK');
187
- if (cb) p.then(() => cb(null), err => cb(err));
188
- return p;
189
- }
190
-
191
- ping(cb) {
192
- const p = this._getEngine().then(() => true);
193
- if (cb) p.then(ok => cb(null, ok), err => cb(err));
194
- return p;
195
- }
196
-
197
- release() {
198
- this.state = 'released';
199
- }
200
-
201
- destroy() {
202
- this.state = 'destroyed';
203
- this.stream.destroy();
204
- this.emit('close');
205
- }
206
-
207
- end(cb) {
208
- const p = (async () => {
209
- if (this._ownEngine && this.engine && typeof this.engine.stop === 'function') {
210
- await this.engine.stop();
211
- }
212
- this.state = 'closed';
213
- this.stream.destroy();
214
- this.emit('close');
215
- return undefined;
216
- })();
217
- if (cb) p.then(() => cb(null), err => cb(err));
218
- return p;
219
- }
220
-
221
- promise() {
222
- return this;
223
- }
224
- }
225
-
226
- class Pool {
227
- constructor(options = {}) {
228
- this.config = options;
229
- this.connectionLimit = options.connectionLimit || 10;
230
- this.queueLimit = options.queueLimit || 0;
231
- this.idleTimeout = options.idleTimeout != null ? options.idleTimeout : 300000;
232
- this._connections = [];
233
- this._waiters = [];
234
- this._closed = false;
235
- this._sharedEngines = new Map();
236
- this._reaper = setInterval(() => this._reapIdle(), Math.max(1000, Math.floor(this.idleTimeout / 10) || 10000));
237
- this._reaper.unref();
238
- }
239
-
240
- async _sharedEngineFor(database) {
241
- const key = database || 'default';
242
- let engine = this._sharedEngines.get(key);
243
- if (!engine) {
244
- const filename = (this.config && this.config.filename) || null;
245
- engine = filename ? new Database(filename) : new Database(':memory:');
246
- if (typeof engine.start === 'function') await engine.start();
247
- this._sharedEngines.set(key, engine);
248
- }
249
- return engine;
250
- }
251
-
252
- _createConnection() {
253
- const conn = new Connection(this.config);
254
- conn._pool = this;
255
- const origDestroy = conn.destroy.bind(conn);
256
- conn.destroy = () => {
257
- this._remove(conn);
258
- origDestroy();
259
- };
260
- return conn;
261
- }
262
-
263
- _remove(conn) {
264
- const idx = this._connections.indexOf(conn);
265
- if (idx !== -1) this._connections.splice(idx, 1);
266
- }
267
-
268
- _reapIdle() {
269
- if (this.idleTimeout <= 0) return;
270
- const now = Date.now();
271
- for (const conn of this._connections.slice()) {
272
- if (conn.state === 'released' && !conn._inUse && now - (conn._releasedAt || 0) > this.idleTimeout) {
273
- this._remove(conn);
274
- if (conn._ownEngine && conn.engine && typeof conn.engine.stop === 'function') {
275
- conn.engine.stop().catch(() => {});
276
- }
277
- conn.state = 'destroyed';
278
- }
279
- }
280
- }
281
-
282
- _acquire() {
283
- if (this._closed) return Promise.reject(new Error('Pool is closed'));
284
- const free = this._connections.find(c => c.state === 'released' && !c._inUse);
285
- if (free) {
286
- free._inUse = true;
287
- free.state = 'acquired';
288
- return Promise.resolve(free);
289
- }
290
- if (this._connections.length < this.connectionLimit) {
291
- return Promise.resolve().then(() => {
292
- if (this._connections.length >= this.connectionLimit) {
293
- return this._waitForConnection();
294
- }
295
- const conn = this._createConnection();
296
- this._connections.push(conn);
297
- conn._inUse = true;
298
- conn.state = 'acquired';
299
- return conn._getEngine().then(
300
- () => conn,
301
- err => {
302
- this._remove(conn);
303
- throw err;
304
- }
305
- );
306
- });
307
- }
308
- return this._waitForConnection();
309
- }
310
-
311
- _waitForConnection() {
312
- return new Promise((resolve, reject) => {
313
- this._waiters.push({ resolve, reject });
314
- if (this.queueLimit > 0 && this._waiters.length > this.queueLimit) {
315
- const w = this._waiters.shift();
316
- w.reject(new Error('Pool queue limit exceeded'));
317
- }
318
- });
319
- }
320
-
321
- _release(conn) {
322
- conn._inUse = false;
323
- conn.state = 'released';
324
- conn._releasedAt = Date.now();
325
- const w = this._waiters.shift();
326
- if (w) {
327
- conn._inUse = true;
328
- conn.state = 'acquired';
329
- w.resolve(conn);
330
- }
331
- }
332
-
333
- async _getConnection() {
334
- return this._acquire();
335
- }
336
-
337
- async query(...args) {
338
- const conn = await this._acquire();
339
- try {
340
- return await conn.query(...args);
341
- } finally {
342
- this._release(conn);
343
- }
344
- }
345
-
346
- async getConnection(cb) {
347
- const p = (async () => {
348
- const conn = await this._acquire();
349
- let released = false;
350
- const release = () => {
351
- if (released) return;
352
- released = true;
353
- if (conn.state === 'destroyed') {
354
- this._remove(conn);
355
- return;
356
- }
357
- this._release(conn);
358
- };
359
- return { connection: conn, release };
360
- })();
361
- if (cb) p.then(r => cb(null, r.connection, r.release), err => cb(err));
362
- return p;
363
- }
364
-
365
- async end(cb) {
366
- const p = (async () => {
367
- this._closed = true;
368
- if (this._reaper) { clearInterval(this._reaper); this._reaper = null; }
369
- const err = new Error('Pool is closed');
370
- for (const w of this._waiters.splice(0)) w.reject(err);
371
- const conns = this._connections.splice(0);
372
- await Promise.allSettled(conns.map(c => c.end()));
373
- const engines = Array.from(this._sharedEngines.values());
374
- this._sharedEngines.clear();
375
- await Promise.allSettled(engines.map(e => (typeof e.stop === 'function' ? e.stop() : null)));
376
- return undefined;
377
- })();
378
- if (cb) p.then(() => cb(null), err => cb(err));
379
- return p;
380
- }
381
-
382
- promise() {
383
- return this;
384
- }
385
- }
386
-
387
- function createConnection(options) {
388
- return new Connection(options || {});
389
- }
390
-
391
- function createPool(options) {
392
- return new Pool(options || {});
393
- }
394
-
395
- class RowDataPacket {}
396
- class OkPacket {}
397
- class ResultSetHeader {}
398
- class FieldPacket {}
399
-
400
- module.exports = {
401
- createConnection,
402
- createPool,
403
- createConnectionPromise: () => Promise.resolve(createConnection()),
404
- Connection,
405
- Pool,
406
- RowDataPacket,
407
- OkPacket,
408
- ResultSetHeader,
409
- FieldPacket,
410
- escape: escapeValue,
411
- escapeId,
412
- format: applyParams,
413
- };
1
+ // © Vexify 2026 All Rights Reserved.
2
+ /**
3
+ * MySQL API 兼容层 — createConnection / createPool / query
4
+ * 底层用 JSQL SQL 执行器,接口形状对齐 mysql / mysql2 包。
5
+ */
6
+
7
+ const { executeSQL, applyParams, escapeValue, escapeId } = require('./sql');
8
+ const Database = require('./database');
9
+ const EventEmitter = require('events');
10
+
11
+ function isQueryResult(r) {
12
+ return !!r && typeof r === 'object' && Array.isArray(r.rows) && Array.isArray(r.columns);
13
+ }
14
+
15
+ function toResultPacket(r, table, rowsAsArray) {
16
+ if (isQueryResult(r)) {
17
+ const columns = r.columns || [];
18
+ const rawRows = r.rows || [];
19
+ const fields = columns.map(name => ({
20
+ name,
21
+ table: table || '',
22
+ type: 253,
23
+ length: 1024,
24
+ flags: 0,
25
+ charsetNr: 45,
26
+ }));
27
+ const rows = rowsAsArray
28
+ ? rawRows
29
+ : rawRows.map(vals => {
30
+ const obj = {};
31
+ columns.forEach((c, j) => { obj[c] = vals[j]; });
32
+ return obj;
33
+ });
34
+ return { rows, fields };
35
+ }
36
+ return {
37
+ fieldCount: 0,
38
+ affectedRows: r && r.affectedRows !== undefined ? r.affectedRows : 0,
39
+ insertId: r && r.insertId !== undefined ? r.insertId : 0,
40
+ serverStatus: 2,
41
+ warningCount: 0,
42
+ message: r ? r.type + (r.table ? ' ' + r.table : '') : '',
43
+ protocol41: true,
44
+ changedRows: r && r.affectedRows !== undefined ? r.affectedRows : 0,
45
+ };
46
+ }
47
+
48
+ class Connection extends EventEmitter {
49
+ constructor(options = {}) {
50
+ super();
51
+ this.config = {
52
+ host: options.host || 'localhost',
53
+ port: options.port || 3306,
54
+ user: options.user || 'root',
55
+ password: options.password || '',
56
+ database: options.database || null,
57
+ multipleStatements: options.multipleStatements === true,
58
+ };
59
+ this.safety = options.safety !== false;
60
+ this.allowComments = options.allowComments === true;
61
+ this.database = options.database && typeof options.database === 'object' && !options.filename && typeof options.database.start === 'function'
62
+ ? options.database
63
+ : (options.engine || null);
64
+ this.filename = options.filename || null;
65
+ this.engine = null;
66
+ this._ownEngine = false;
67
+ this.state = 'disconnected';
68
+ this.stream = {
69
+ destroyed: false,
70
+ writable: true,
71
+ destroy: () => { this.stream.destroyed = true; },
72
+ };
73
+ if (options.host !== undefined || options.database || options.engine || options.filename) {
74
+ queueMicrotask(() => {
75
+ this._getEngine().then(() => this.emit('connect'), err => {
76
+ if (this.listenerCount('error') > 0) this.emit('error', err);
77
+ });
78
+ });
79
+ }
80
+ }
81
+
82
+ async _getEngine() {
83
+ if (this.engine) return this.engine;
84
+ if (this.database && typeof this.database.start === 'function') {
85
+ this.engine = this.database;
86
+ await this.engine.start();
87
+ } else if (this._pool) {
88
+ this.engine = await this._pool._sharedEngineFor(this.config.database || 'default');
89
+ } else if (this.filename) {
90
+ this.engine = new Database(this.filename);
91
+ await this.engine.start();
92
+ this._ownEngine = true;
93
+ } else {
94
+ this.engine = new Database(':memory:');
95
+ await this.engine.start();
96
+ this._ownEngine = true;
97
+ }
98
+ this.state = 'connected';
99
+ return this.engine;
100
+ }
101
+
102
+ connect(cb) {
103
+ const p = this._getEngine().then(c => {
104
+ this.emit('connect');
105
+ return c;
106
+ });
107
+ if (cb) p.then(c => cb(null, c), err => cb(err));
108
+ return p;
109
+ }
110
+
111
+ query(...args) {
112
+ let sql, values, cb, rowsAsArray;
113
+ if (typeof args[0] === 'object' && args[0] !== null && typeof args[0].sql === 'string') {
114
+ sql = args[0].sql;
115
+ rowsAsArray = args[0].rowsAsArray === true;
116
+ values = args[0].values !== undefined ? args[0].values : (Array.isArray(args[1]) ? args[1] : null);
117
+ cb = typeof args[1] === 'function' ? args[1] : (typeof args[2] === 'function' ? args[2] : null);
118
+ } else {
119
+ sql = args[0];
120
+ values = Array.isArray(args[1]) ? args[1] : null;
121
+ cb = typeof args[1] === 'function' ? args[1] : (typeof args[2] === 'function' ? args[2] : null);
122
+ }
123
+ const p = (async () => {
124
+ const engine = await this._getEngine();
125
+ const finalSql = applyParams(sql, values);
126
+ const r = await executeSQL(engine, finalSql, {
127
+ safety: this.safety,
128
+ allowComments: this.allowComments,
129
+ maxStatements: this.config.multipleStatements ? null : 1,
130
+ });
131
+ const results = Array.isArray(r) ? r : [r];
132
+ const last = results[results.length - 1];
133
+ const packet = toResultPacket(last, last ? last.table : null, rowsAsArray);
134
+ if (isQueryResult(last)) {
135
+ const arr = [packet.rows, packet.fields];
136
+ arr.rows = packet.rows;
137
+ arr.fields = packet.fields;
138
+ return arr;
139
+ }
140
+ const arr = [packet];
141
+ arr.rows = null;
142
+ arr.fields = null;
143
+ return arr;
144
+ })();
145
+ const q = new EventEmitter();
146
+ q.setMaxListeners = EventEmitter.prototype.setMaxListeners;
147
+ p.then(res => {
148
+ q.emit('result', res.rows || res[0]);
149
+ q.emit('fields', res.fields || []);
150
+ }, err => {
151
+ if (q.listenerCount('error') > 0) q.emit('error', err);
152
+ });
153
+ if (cb) {
154
+ p.then(res => {
155
+ if (res && res.rows && res.fields) cb(null, res.rows, res.fields);
156
+ else if (res && res[0] && res[0].insertId !== undefined) cb(null, res[0]);
157
+ else cb(null, res[0]);
158
+ }, err => cb(err));
159
+ return q;
160
+ }
161
+ p.setMaxListeners = q.setMaxListeners;
162
+ return p;
163
+ }
164
+
165
+ execute(sql, values, cb) {
166
+ if (typeof values === 'function') {
167
+ cb = values;
168
+ values = undefined;
169
+ }
170
+ return this.query(sql, values, cb);
171
+ }
172
+
173
+ beginTransaction(cb) {
174
+ const p = this.query('BEGIN');
175
+ if (cb) p.then(() => cb(null), err => cb(err));
176
+ return p;
177
+ }
178
+
179
+ commit(cb) {
180
+ const p = this.query('COMMIT');
181
+ if (cb) p.then(() => cb(null), err => cb(err));
182
+ return p;
183
+ }
184
+
185
+ rollback(cb) {
186
+ const p = this.query('ROLLBACK');
187
+ if (cb) p.then(() => cb(null), err => cb(err));
188
+ return p;
189
+ }
190
+
191
+ ping(cb) {
192
+ const p = this._getEngine().then(() => true);
193
+ if (cb) p.then(ok => cb(null, ok), err => cb(err));
194
+ return p;
195
+ }
196
+
197
+ release() {
198
+ this.state = 'released';
199
+ }
200
+
201
+ destroy() {
202
+ this.state = 'destroyed';
203
+ this.stream.destroy();
204
+ this.emit('close');
205
+ }
206
+
207
+ end(cb) {
208
+ const p = (async () => {
209
+ if (this._ownEngine && this.engine && typeof this.engine.stop === 'function') {
210
+ await this.engine.stop();
211
+ }
212
+ this.state = 'closed';
213
+ this.stream.destroy();
214
+ this.emit('close');
215
+ return undefined;
216
+ })();
217
+ if (cb) p.then(() => cb(null), err => cb(err));
218
+ return p;
219
+ }
220
+
221
+ promise() {
222
+ return this;
223
+ }
224
+ }
225
+
226
+ class Pool {
227
+ constructor(options = {}) {
228
+ this.config = options;
229
+ this.connectionLimit = options.connectionLimit || 10;
230
+ this.queueLimit = options.queueLimit || 0;
231
+ this.idleTimeout = options.idleTimeout != null ? options.idleTimeout : 300000;
232
+ this._connections = [];
233
+ this._waiters = [];
234
+ this._closed = false;
235
+ this._sharedEngines = new Map();
236
+ this._reaper = setInterval(() => this._reapIdle(), Math.max(1000, Math.floor(this.idleTimeout / 10) || 10000));
237
+ this._reaper.unref();
238
+ }
239
+
240
+ async _sharedEngineFor(database) {
241
+ const key = database || 'default';
242
+ let engine = this._sharedEngines.get(key);
243
+ if (!engine) {
244
+ const filename = (this.config && this.config.filename) || null;
245
+ engine = filename ? new Database(filename) : new Database(':memory:');
246
+ if (typeof engine.start === 'function') await engine.start();
247
+ this._sharedEngines.set(key, engine);
248
+ }
249
+ return engine;
250
+ }
251
+
252
+ _createConnection() {
253
+ const conn = new Connection(this.config);
254
+ conn._pool = this;
255
+ const origDestroy = conn.destroy.bind(conn);
256
+ conn.destroy = () => {
257
+ this._remove(conn);
258
+ origDestroy();
259
+ };
260
+ return conn;
261
+ }
262
+
263
+ _remove(conn) {
264
+ const idx = this._connections.indexOf(conn);
265
+ if (idx !== -1) this._connections.splice(idx, 1);
266
+ }
267
+
268
+ _reapIdle() {
269
+ if (this.idleTimeout <= 0) return;
270
+ const now = Date.now();
271
+ for (const conn of this._connections.slice()) {
272
+ if (conn.state === 'released' && !conn._inUse && now - (conn._releasedAt || 0) > this.idleTimeout) {
273
+ this._remove(conn);
274
+ if (conn._ownEngine && conn.engine && typeof conn.engine.stop === 'function') {
275
+ conn.engine.stop().catch(() => {});
276
+ }
277
+ conn.state = 'destroyed';
278
+ }
279
+ }
280
+ }
281
+
282
+ _acquire() {
283
+ if (this._closed) return Promise.reject(new Error('Pool is closed'));
284
+ const free = this._connections.find(c => c.state === 'released' && !c._inUse);
285
+ if (free) {
286
+ free._inUse = true;
287
+ free.state = 'acquired';
288
+ return Promise.resolve(free);
289
+ }
290
+ if (this._connections.length < this.connectionLimit) {
291
+ return Promise.resolve().then(() => {
292
+ if (this._connections.length >= this.connectionLimit) {
293
+ return this._waitForConnection();
294
+ }
295
+ const conn = this._createConnection();
296
+ this._connections.push(conn);
297
+ conn._inUse = true;
298
+ conn.state = 'acquired';
299
+ return conn._getEngine().then(
300
+ () => conn,
301
+ err => {
302
+ this._remove(conn);
303
+ throw err;
304
+ }
305
+ );
306
+ });
307
+ }
308
+ return this._waitForConnection();
309
+ }
310
+
311
+ _waitForConnection() {
312
+ return new Promise((resolve, reject) => {
313
+ this._waiters.push({ resolve, reject });
314
+ if (this.queueLimit > 0 && this._waiters.length > this.queueLimit) {
315
+ const w = this._waiters.shift();
316
+ w.reject(new Error('Pool queue limit exceeded'));
317
+ }
318
+ });
319
+ }
320
+
321
+ _release(conn) {
322
+ conn._inUse = false;
323
+ conn.state = 'released';
324
+ conn._releasedAt = Date.now();
325
+ const w = this._waiters.shift();
326
+ if (w) {
327
+ conn._inUse = true;
328
+ conn.state = 'acquired';
329
+ w.resolve(conn);
330
+ }
331
+ }
332
+
333
+ async _getConnection() {
334
+ return this._acquire();
335
+ }
336
+
337
+ async query(...args) {
338
+ const conn = await this._acquire();
339
+ try {
340
+ return await conn.query(...args);
341
+ } finally {
342
+ this._release(conn);
343
+ }
344
+ }
345
+
346
+ async getConnection(cb) {
347
+ const p = (async () => {
348
+ const conn = await this._acquire();
349
+ let released = false;
350
+ const release = () => {
351
+ if (released) return;
352
+ released = true;
353
+ if (conn.state === 'destroyed') {
354
+ this._remove(conn);
355
+ return;
356
+ }
357
+ this._release(conn);
358
+ };
359
+ return { connection: conn, release };
360
+ })();
361
+ if (cb) p.then(r => cb(null, r.connection, r.release), err => cb(err));
362
+ return p;
363
+ }
364
+
365
+ async end(cb) {
366
+ const p = (async () => {
367
+ this._closed = true;
368
+ if (this._reaper) { clearInterval(this._reaper); this._reaper = null; }
369
+ const err = new Error('Pool is closed');
370
+ for (const w of this._waiters.splice(0)) w.reject(err);
371
+ const conns = this._connections.splice(0);
372
+ await Promise.allSettled(conns.map(c => c.end()));
373
+ const engines = Array.from(this._sharedEngines.values());
374
+ this._sharedEngines.clear();
375
+ await Promise.allSettled(engines.map(e => (typeof e.stop === 'function' ? e.stop() : null)));
376
+ return undefined;
377
+ })();
378
+ if (cb) p.then(() => cb(null), err => cb(err));
379
+ return p;
380
+ }
381
+
382
+ promise() {
383
+ return this;
384
+ }
385
+ }
386
+
387
+ function createConnection(options) {
388
+ return new Connection(options || {});
389
+ }
390
+
391
+ function createPool(options) {
392
+ return new Pool(options || {});
393
+ }
394
+
395
+ class RowDataPacket {}
396
+ class OkPacket {}
397
+ class ResultSetHeader {}
398
+ class FieldPacket {}
399
+
400
+ module.exports = {
401
+ createConnection,
402
+ createPool,
403
+ createConnectionPromise: () => Promise.resolve(createConnection()),
404
+ Connection,
405
+ Pool,
406
+ RowDataPacket,
407
+ OkPacket,
408
+ ResultSetHeader,
409
+ FieldPacket,
410
+ escape: escapeValue,
411
+ escapeId,
412
+ format: applyParams,
413
+ };