@zz1996/dbhub-dameng 0.1.7 → 1.2.5-dameng.621

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 (32) hide show
  1. package/README.md +126 -137
  2. package/dbhub.dameng.toml.example +4 -11
  3. package/dist/{chunk-SQA2ISDE.js → chunk-2JL4KCP5.js} +21 -119
  4. package/dist/chunk-3RJRH3CN.js +121 -0
  5. package/dist/chunk-4276LPPU.js +179 -0
  6. package/dist/chunk-FU2ZJE4E.js +16 -0
  7. package/dist/chunk-HDPZAIDL.js +155 -0
  8. package/dist/{chunk-2A2QF3CS.js → chunk-HGU7ILEZ.js} +215 -68
  9. package/dist/{chunk-IPK7BYBL.js → chunk-JFWX35TB.js} +0 -1
  10. package/dist/chunk-O3PEWKBB.js +302 -0
  11. package/dist/dameng-NKF7NZKE.js +250 -0
  12. package/dist/index.js +461 -251
  13. package/dist/{mariadb-7F72IRB4.js → mariadb-MMC6WMEG.js} +86 -53
  14. package/dist/{mysql-A43SL7UM.js → mysql-TESH7KKV.js} +126 -50
  15. package/dist/{postgres-VFNLBWMF.js → postgres-C24SBQ4I.js} +154 -36
  16. package/dist/public/assets/index-BJ-1UrcV.css +1 -0
  17. package/dist/public/assets/index-BdBaFSHd.js +148 -0
  18. package/dist/public/assets/postgres-BpcazhJg.svg +22 -0
  19. package/dist/public/assets/sqlserver-ByfFYYpV.svg +11 -0
  20. package/dist/public/favicon.svg +57 -0
  21. package/dist/public/index.html +14 -0
  22. package/dist/public/logo-full-light.svg +58 -0
  23. package/dist/{registry-XSX2VZTD.js → registry-WVSFVS35.js} +3 -2
  24. package/dist/{sqlite-IOUAYHGE.js → sqlite-7BNGDMBZ.js} +57 -42
  25. package/dist/sqlserver-KZPPK67Q.js +1042 -0
  26. package/package.json +22 -65
  27. package/dist/chunk-RTB262PR.js +0 -60
  28. package/dist/chunk-ZNQTMARG.js +0 -127
  29. package/dist/dameng-5KYI5JBK.js +0 -898
  30. package/dist/sqlserver-OE4SFH4B.js +0 -587
  31. package/docs/README.md +0 -14
  32. package/docs/dameng-connector-plan.md +0 -143
@@ -1,898 +0,0 @@
1
- import {
2
- ConnectorRegistry,
3
- SafeURL,
4
- obfuscateDSNPassword,
5
- splitSQLStatements,
6
- stripCommentsAndStrings
7
- } from "./chunk-SQA2ISDE.js";
8
-
9
- // src/connectors/dameng/index.ts
10
- import dmdb from "dmdb";
11
- var DEFAULT_OPERATION_TIMEOUT_MS = 11e4;
12
- var RESOURCE_CLEANUP_TIMEOUT_MS = 2e3;
13
- var CONNECT_RETRY_ATTEMPTS = 3;
14
- var CONNECT_RETRY_DELAY_MS = 500;
15
- var DamengOperationTimeoutError = class extends Error {
16
- constructor(message, label) {
17
- super(message);
18
- this.label = label;
19
- this.name = "DamengOperationTimeoutError";
20
- }
21
- };
22
- var DamengDSNParser = class {
23
- async parse(dsn, config) {
24
- if (!this.isValidDSN(dsn)) {
25
- const obfuscatedDSN = obfuscateDSNPassword(dsn);
26
- const expectedFormat = this.getSampleDSN();
27
- throw new Error(
28
- `Invalid Dameng DSN format.
29
- Provided: ${obfuscatedDSN}
30
- Expected: ${expectedFormat}`
31
- );
32
- }
33
- try {
34
- const url = new SafeURL(dsn);
35
- const schema = url.pathname ? decodeURIComponent(url.pathname.substring(1)) : void 0;
36
- const port = url.port ? parseInt(url.port, 10) : 5236;
37
- const queryParams = {};
38
- const queryStringParts = [];
39
- url.forEachSearchParam((value, key) => {
40
- queryParams[key] = value;
41
- queryStringParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
42
- });
43
- const connectionConfig = {
44
- connectString: `dm://${encodeURIComponent(url.username)}:${encodeURIComponent(url.password)}@${url.hostname}:${port}${queryStringParts.length > 0 ? `?${queryStringParts.join("&")}` : ""}`,
45
- directConnectString: `${url.hostname}:${port}`,
46
- user: url.username,
47
- password: url.password,
48
- schema: schema || void 0,
49
- poolMin: 0,
50
- poolMax: 4,
51
- ...queryParams
52
- };
53
- if (config?.connectionTimeoutSeconds !== void 0) {
54
- connectionConfig.connectTimeout = config.connectionTimeoutSeconds * 1e3;
55
- connectionConfig.queueTimeout = config.connectionTimeoutSeconds * 1e3;
56
- }
57
- if (config?.queryTimeoutSeconds !== void 0) {
58
- connectionConfig.sessionTimeout = config.queryTimeoutSeconds;
59
- connectionConfig.socketTimeout = config.queryTimeoutSeconds * 1e3 + 1e3;
60
- }
61
- return connectionConfig;
62
- } catch (error) {
63
- throw new Error(
64
- `Failed to parse Dameng DSN: ${error instanceof Error ? error.message : String(error)}`
65
- );
66
- }
67
- }
68
- getSampleDSN() {
69
- return "dameng://SYSDBA:password@localhost:5236/SYSDBA";
70
- }
71
- isValidDSN(dsn) {
72
- try {
73
- return dsn.startsWith("dameng://") || dsn.startsWith("dm://");
74
- } catch {
75
- return false;
76
- }
77
- }
78
- };
79
- var DamengConnector = class _DamengConnector {
80
- constructor() {
81
- this.id = "dameng";
82
- this.name = "Dameng";
83
- this.dsnParser = new DamengDSNParser();
84
- this.pool = null;
85
- this.sourceId = "default";
86
- this.defaultSchema = null;
87
- this.poolAlias = null;
88
- this.connectionConfig = null;
89
- this.connectionTimeoutMs = 5e3;
90
- this.operationTimeoutMs = DEFAULT_OPERATION_TIMEOUT_MS;
91
- this.reconnectingPool = null;
92
- }
93
- getId() {
94
- return this.sourceId;
95
- }
96
- clone() {
97
- return new _DamengConnector();
98
- }
99
- async connect(dsn, initScript, config) {
100
- for (let attempt = 1; attempt <= CONNECT_RETRY_ATTEMPTS; attempt += 1) {
101
- let createdPool = null;
102
- try {
103
- const connectionConfig = await this.dsnParser.parse(dsn, config);
104
- connectionConfig.poolAlias = this.buildPoolAlias();
105
- this.defaultSchema = connectionConfig.schema ?? null;
106
- this.poolAlias = connectionConfig.poolAlias;
107
- this.connectionTimeoutMs = config?.connectionTimeoutSeconds !== void 0 ? config.connectionTimeoutSeconds * 1e3 : 5e3;
108
- this.operationTimeoutMs = config?.queryTimeoutSeconds !== void 0 ? config.queryTimeoutSeconds * 1e3 : DEFAULT_OPERATION_TIMEOUT_MS;
109
- connectionConfig.connectTimeout ?? (connectionConfig.connectTimeout = this.connectionTimeoutMs);
110
- connectionConfig.queueTimeout ?? (connectionConfig.queueTimeout = this.connectionTimeoutMs);
111
- await this.closeRegisteredPool(connectionConfig.poolAlias);
112
- await this.validateDirectConnection(connectionConfig);
113
- createdPool = await dmdb.createPool(connectionConfig);
114
- this.pool = createdPool;
115
- await this.withConnection((conn) => this.initializeSession(conn, initScript));
116
- this.connectionConfig = connectionConfig;
117
- this.initScript = initScript;
118
- return;
119
- } catch (error) {
120
- if (createdPool) {
121
- await this.closePoolQuietly(createdPool);
122
- } else if (this.poolAlias) {
123
- await this.closeRegisteredPool(this.poolAlias);
124
- }
125
- this.pool = null;
126
- this.poolAlias = null;
127
- this.connectionConfig = null;
128
- this.initScript = void 0;
129
- if (attempt < CONNECT_RETRY_ATTEMPTS && this.isRetryableConnectFailure(error)) {
130
- console.error(
131
- `Retrying Dameng source '${this.sourceId}' connection after transient failure (${attempt}/${CONNECT_RETRY_ATTEMPTS}): ${error instanceof Error ? error.message : String(error)}`
132
- );
133
- await this.delay(CONNECT_RETRY_DELAY_MS * attempt);
134
- continue;
135
- }
136
- console.error("Failed to connect to Dameng database:", error);
137
- throw error;
138
- }
139
- }
140
- throw new Error("Failed to connect to Dameng database");
141
- }
142
- async initializeSession(conn, initScript) {
143
- await this.executeWithTimeout(
144
- conn,
145
- "SELECT 1 AS OK",
146
- [],
147
- this.executeOptions()
148
- );
149
- if (!this.defaultSchema) {
150
- const result = await this.executeWithTimeout(
151
- conn,
152
- "SELECT USER AS SCHEMA_NAME FROM DUAL",
153
- [],
154
- this.executeOptions()
155
- );
156
- this.defaultSchema = this.rowValue(result.rows?.[0], "SCHEMA_NAME") ?? null;
157
- }
158
- if (initScript) {
159
- for (const statement of splitSQLStatements(initScript, "dameng")) {
160
- await this.executeWithTimeout(
161
- conn,
162
- statement,
163
- [],
164
- this.executeOptions({ autoCommit: true })
165
- );
166
- }
167
- }
168
- }
169
- async disconnect() {
170
- if (this.pool) {
171
- await this.closePoolQuietly(this.pool);
172
- this.pool = null;
173
- }
174
- if (this.poolAlias) {
175
- await this.closeRegisteredPool(this.poolAlias);
176
- this.poolAlias = null;
177
- }
178
- this.connectionConfig = null;
179
- this.initScript = void 0;
180
- this.reconnectingPool = null;
181
- }
182
- async getSchemas() {
183
- const rows = await this.queryRows(`
184
- SELECT USERNAME AS SCHEMA_NAME
185
- FROM ALL_USERS
186
- WHERE USERNAME NOT IN ('SYS', 'SYSTEM', 'SYSAUDITOR', 'SYSSSO', 'CTISYS')
187
- ORDER BY USERNAME
188
- `);
189
- return rows.map((row) => this.rowValue(row, "SCHEMA_NAME")).filter(this.isPresent);
190
- }
191
- async schemaExists(schema) {
192
- const rows = await this.queryRows(
193
- `
194
- SELECT COUNT(*) AS CNT
195
- FROM ALL_USERS
196
- WHERE USERNAME = :1
197
- `,
198
- [this.normalizeIdentifier(schema)]
199
- );
200
- return Number(this.rowValue(rows[0], "CNT") ?? 0) > 0;
201
- }
202
- async getDefaultSchema() {
203
- return this.defaultSchema;
204
- }
205
- async getTables(schema) {
206
- const owner = await this.resolveSchema(schema);
207
- const rows = await this.queryRows(
208
- `
209
- SELECT TABLE_NAME
210
- FROM ALL_TABLES
211
- WHERE OWNER = :1
212
- ORDER BY TABLE_NAME
213
- `,
214
- [owner]
215
- );
216
- return rows.map((row) => this.rowValue(row, "TABLE_NAME")).filter(this.isPresent);
217
- }
218
- async searchTables(pattern, schema, limit = 100) {
219
- const owner = await this.resolveSchema(schema);
220
- const rowLimit = this.normalizeLimit(limit);
221
- const rows = await this.queryRows(
222
- `
223
- SELECT TABLE_NAME
224
- FROM (
225
- SELECT TABLE_NAME
226
- FROM ALL_TABLES
227
- WHERE OWNER = :1
228
- AND TABLE_NAME LIKE :2
229
- ORDER BY TABLE_NAME
230
- )
231
- WHERE ROWNUM <= ${rowLimit}
232
- `,
233
- [owner, this.normalizeLikePattern(pattern)]
234
- );
235
- return rows.map((row) => this.rowValue(row, "TABLE_NAME")).filter(this.isPresent).map((name) => ({ name, schema: owner }));
236
- }
237
- async searchColumns(pattern, schema, table, limit = 100) {
238
- const owner = await this.resolveSchema(schema);
239
- const rowLimit = this.normalizeLimit(limit);
240
- const bindValues = [
241
- owner,
242
- this.normalizeLikePattern(pattern),
243
- ...table ? [this.normalizeIdentifier(table)] : []
244
- ];
245
- const tablePredicate = table ? "AND c.TABLE_NAME = :3" : "";
246
- const rows = await this.queryRows(
247
- `
248
- SELECT COLUMN_NAME,
249
- TABLE_NAME,
250
- DATA_TYPE,
251
- DATA_LENGTH,
252
- DATA_PRECISION,
253
- DATA_SCALE,
254
- NULLABLE,
255
- DATA_DEFAULT,
256
- COMMENTS
257
- FROM (
258
- SELECT c.COLUMN_NAME,
259
- c.TABLE_NAME,
260
- c.DATA_TYPE,
261
- c.DATA_LENGTH,
262
- c.DATA_PRECISION,
263
- c.DATA_SCALE,
264
- c.NULLABLE,
265
- c.DATA_DEFAULT,
266
- cc.COMMENTS,
267
- c.COLUMN_ID
268
- FROM ALL_TAB_COLUMNS c
269
- LEFT JOIN ALL_COL_COMMENTS cc
270
- ON cc.OWNER = c.OWNER
271
- AND cc.TABLE_NAME = c.TABLE_NAME
272
- AND cc.COLUMN_NAME = c.COLUMN_NAME
273
- WHERE c.OWNER = :1
274
- AND c.COLUMN_NAME LIKE :2
275
- ${tablePredicate}
276
- ORDER BY c.TABLE_NAME, c.COLUMN_ID
277
- )
278
- WHERE ROWNUM <= ${rowLimit}
279
- `,
280
- bindValues
281
- );
282
- return rows.map((row) => {
283
- const description = this.rowValue(row, "COMMENTS");
284
- return {
285
- name: this.rowValue(row, "COLUMN_NAME") ?? "",
286
- table: this.rowValue(row, "TABLE_NAME") ?? "",
287
- schema: owner,
288
- type: this.formatDataType(row),
289
- nullable: this.rowValue(row, "NULLABLE") === "Y",
290
- default: this.rowValue(row, "DATA_DEFAULT") ?? null,
291
- ...description ? { description } : {}
292
- };
293
- });
294
- }
295
- async getViews(schema) {
296
- const owner = await this.resolveSchema(schema);
297
- const rows = await this.queryRows(
298
- `
299
- SELECT VIEW_NAME
300
- FROM ALL_VIEWS
301
- WHERE OWNER = :1
302
- ORDER BY VIEW_NAME
303
- `,
304
- [owner]
305
- );
306
- return rows.map((row) => this.rowValue(row, "VIEW_NAME")).filter(this.isPresent);
307
- }
308
- async tableExists(tableName, schema) {
309
- const owner = await this.resolveSchema(schema);
310
- const rows = await this.queryRows(
311
- `
312
- SELECT COUNT(*) AS CNT
313
- FROM ALL_OBJECTS
314
- WHERE OWNER = :1
315
- AND OBJECT_NAME = :2
316
- AND OBJECT_TYPE IN ('TABLE', 'VIEW')
317
- `,
318
- [owner, this.normalizeIdentifier(tableName)]
319
- );
320
- return Number(this.rowValue(rows[0], "CNT") ?? 0) > 0;
321
- }
322
- async getTableSchema(tableName, schema) {
323
- const owner = await this.resolveSchema(schema);
324
- const rows = await this.queryRows(
325
- `
326
- SELECT c.COLUMN_NAME,
327
- c.DATA_TYPE,
328
- c.DATA_LENGTH,
329
- c.DATA_PRECISION,
330
- c.DATA_SCALE,
331
- c.NULLABLE,
332
- c.DATA_DEFAULT,
333
- cc.COMMENTS
334
- FROM ALL_TAB_COLUMNS c
335
- LEFT JOIN ALL_COL_COMMENTS cc
336
- ON cc.OWNER = c.OWNER
337
- AND cc.TABLE_NAME = c.TABLE_NAME
338
- AND cc.COLUMN_NAME = c.COLUMN_NAME
339
- WHERE c.OWNER = :1
340
- AND c.TABLE_NAME = :2
341
- ORDER BY c.COLUMN_ID
342
- `,
343
- [owner, this.normalizeIdentifier(tableName)]
344
- );
345
- return rows.map((row) => {
346
- const dataType = this.formatDataType(row);
347
- return {
348
- column_name: this.rowValue(row, "COLUMN_NAME") ?? "",
349
- data_type: dataType,
350
- is_nullable: this.rowValue(row, "NULLABLE") === "Y" ? "YES" : "NO",
351
- column_default: this.rowValue(row, "DATA_DEFAULT") ?? null,
352
- description: this.rowValue(row, "COMMENTS") ?? null
353
- };
354
- });
355
- }
356
- async getTableIndexes(tableName, schema) {
357
- const owner = await this.resolveSchema(schema);
358
- const rows = await this.queryRows(
359
- `
360
- SELECT i.INDEX_NAME,
361
- i.UNIQUENESS,
362
- ic.COLUMN_NAME,
363
- ic.COLUMN_POSITION,
364
- CASE WHEN c.CONSTRAINT_TYPE = 'P' THEN 1 ELSE 0 END AS IS_PRIMARY
365
- FROM ALL_INDEXES i
366
- JOIN ALL_IND_COLUMNS ic
367
- ON i.OWNER = ic.INDEX_OWNER
368
- AND i.INDEX_NAME = ic.INDEX_NAME
369
- LEFT JOIN ALL_CONSTRAINTS c
370
- ON c.OWNER = i.TABLE_OWNER
371
- AND c.TABLE_NAME = i.TABLE_NAME
372
- AND c.INDEX_NAME = i.INDEX_NAME
373
- AND c.CONSTRAINT_TYPE = 'P'
374
- WHERE i.TABLE_OWNER = :1
375
- AND i.TABLE_NAME = :2
376
- ORDER BY i.INDEX_NAME, ic.COLUMN_POSITION
377
- `,
378
- [owner, this.normalizeIdentifier(tableName)]
379
- );
380
- const indexMap = /* @__PURE__ */ new Map();
381
- for (const row of rows) {
382
- const indexName = this.rowValue(row, "INDEX_NAME") ?? "";
383
- if (!indexMap.has(indexName)) {
384
- indexMap.set(indexName, {
385
- index_name: indexName,
386
- column_names: [],
387
- is_unique: this.rowValue(row, "UNIQUENESS") === "UNIQUE",
388
- is_primary: Number(this.rowValue(row, "IS_PRIMARY") ?? 0) === 1
389
- });
390
- }
391
- const columnName = this.rowValue(row, "COLUMN_NAME");
392
- if (columnName) {
393
- indexMap.get(indexName).column_names.push(columnName);
394
- }
395
- }
396
- return Array.from(indexMap.values());
397
- }
398
- async getStoredProcedures(schema, routineType) {
399
- const owner = await this.resolveSchema(schema);
400
- const types = routineType ? [routineType === "procedure" ? "PROCEDURE" : "FUNCTION"] : ["PROCEDURE", "FUNCTION"];
401
- const rows = await this.queryRows(
402
- `
403
- SELECT OBJECT_NAME
404
- FROM ALL_OBJECTS
405
- WHERE OWNER = :1
406
- AND OBJECT_TYPE IN (${types.map((_, i) => `:${i + 2}`).join(", ")})
407
- ORDER BY OBJECT_NAME
408
- `,
409
- [owner, ...types]
410
- );
411
- return rows.map((row) => this.rowValue(row, "OBJECT_NAME")).filter(this.isPresent);
412
- }
413
- async getStoredProcedureDetail(procedureName, schema) {
414
- const owner = await this.resolveSchema(schema);
415
- const name = this.normalizeIdentifier(procedureName);
416
- const rows = await this.queryRows(
417
- `
418
- SELECT OBJECT_NAME, OBJECT_TYPE
419
- FROM ALL_OBJECTS
420
- WHERE OWNER = :1
421
- AND OBJECT_NAME = :2
422
- AND OBJECT_TYPE IN ('PROCEDURE', 'FUNCTION')
423
- `,
424
- [owner, name]
425
- );
426
- if (rows.length === 0) {
427
- throw new Error(`Stored procedure '${procedureName}' not found in ${owner}`);
428
- }
429
- const sourceRows = await this.queryRows(
430
- `
431
- SELECT TEXT
432
- FROM ALL_SOURCE
433
- WHERE OWNER = :1
434
- AND NAME = :2
435
- ORDER BY LINE
436
- `,
437
- [owner, name]
438
- );
439
- const objectType = this.rowValue(rows[0], "OBJECT_TYPE") ?? "PROCEDURE";
440
- return {
441
- procedure_name: this.rowValue(rows[0], "OBJECT_NAME") ?? procedureName,
442
- procedure_type: objectType === "FUNCTION" ? "function" : "procedure",
443
- language: "sql",
444
- parameter_list: "",
445
- definition: sourceRows.map((row) => this.rowValue(row, "TEXT") ?? "").join("")
446
- };
447
- }
448
- async getTableRowCount(tableName, schema) {
449
- const owner = await this.resolveSchema(schema);
450
- const rows = await this.queryRows(
451
- `
452
- SELECT NUM_ROWS
453
- FROM ALL_TABLES
454
- WHERE OWNER = :1
455
- AND TABLE_NAME = :2
456
- `,
457
- [owner, this.normalizeIdentifier(tableName)]
458
- );
459
- const value = this.rowValue(rows[0], "NUM_ROWS");
460
- return value === null || value === void 0 ? null : Number(value);
461
- }
462
- async getTableComment(tableName, schema) {
463
- const owner = await this.resolveSchema(schema);
464
- const rows = await this.queryRows(
465
- `
466
- SELECT COMMENTS
467
- FROM ALL_TAB_COMMENTS
468
- WHERE OWNER = :1
469
- AND TABLE_NAME = :2
470
- `,
471
- [owner, this.normalizeIdentifier(tableName)]
472
- );
473
- return this.rowValue(rows[0], "COMMENTS") ?? null;
474
- }
475
- async executeSQL(sql, options, parameters) {
476
- if (!this.pool) {
477
- throw new Error("Not connected to Dameng database");
478
- }
479
- return this.withConnection(async (conn) => {
480
- const statements = splitSQLStatements(sql, "dameng");
481
- const allRows = [];
482
- let rowCount = 0;
483
- for (const [index, statement] of statements.entries()) {
484
- const processedSQL = this.applyMaxRows(statement, options.maxRows);
485
- const { sql: boundSQL, bindValues } = this.replacePositionalParameters(
486
- processedSQL,
487
- index === 0 ? parameters ?? [] : []
488
- );
489
- const result = await this.executeWithTimeout(
490
- conn,
491
- boundSQL,
492
- this.toBindParams(bindValues),
493
- this.executeOptions({ autoCommit: true, maxRows: options.maxRows })
494
- );
495
- const rows = this.normalizeRows(result.rows ?? []);
496
- allRows.push(...rows);
497
- rowCount += Number(result.rowsAffected ?? rows.length ?? 0);
498
- }
499
- return { rows: allRows, rowCount };
500
- });
501
- }
502
- async queryRows(sql, bindValues = []) {
503
- return this.withConnection(async (conn) => {
504
- const result = await this.executeWithTimeout(
505
- conn,
506
- sql,
507
- this.toBindParams(bindValues),
508
- this.executeOptions()
509
- );
510
- return this.normalizeRows(result.rows ?? []);
511
- });
512
- }
513
- async withConnection(fn) {
514
- try {
515
- return await this.withConnectionAttempt(fn);
516
- } catch (error) {
517
- if (!this.isConnectionAcquisitionFailure(error)) {
518
- throw error;
519
- }
520
- console.error(
521
- `Retrying Dameng source '${this.sourceId}' after connection acquisition failure: ${error instanceof Error ? error.message : String(error)}`
522
- );
523
- await this.ensurePool();
524
- return this.withConnectionAttempt(fn);
525
- }
526
- }
527
- async withConnectionAttempt(fn) {
528
- await this.ensurePool();
529
- const pool = this.pool;
530
- if (!pool) {
531
- throw new Error("Not connected to Dameng database");
532
- }
533
- let conn = null;
534
- let shouldRelease = true;
535
- try {
536
- conn = await this.acquireConnectionWithTimeout(pool);
537
- return await fn(conn);
538
- } catch (error) {
539
- if (error instanceof DamengOperationTimeoutError) {
540
- shouldRelease = false;
541
- this.markPoolUnhealthy(error.message);
542
- } else if (!conn && this.isConnectionAcquisitionFailure(error)) {
543
- shouldRelease = false;
544
- this.markPoolUnhealthy(error instanceof Error ? error.message : String(error));
545
- }
546
- throw error;
547
- } finally {
548
- if (conn && shouldRelease) {
549
- await this.releaseConnectionQuietly(conn);
550
- }
551
- }
552
- }
553
- async acquireConnectionWithTimeout(pool) {
554
- const acquisition = pool.getConnection();
555
- try {
556
- return await this.withTimeout(
557
- acquisition,
558
- this.connectionTimeoutMs,
559
- "Dameng connection acquisition"
560
- );
561
- } catch (error) {
562
- if (error instanceof DamengOperationTimeoutError) {
563
- void acquisition.then((conn) => this.releaseConnectionQuietly(conn)).catch(() => void 0);
564
- }
565
- throw error;
566
- }
567
- }
568
- async ensurePool() {
569
- if (this.pool) {
570
- return;
571
- }
572
- if (!this.connectionConfig) {
573
- throw new Error("Not connected to Dameng database");
574
- }
575
- if (!this.reconnectingPool) {
576
- this.reconnectingPool = this.reconnectPool();
577
- }
578
- try {
579
- await this.reconnectingPool;
580
- } finally {
581
- this.reconnectingPool = null;
582
- }
583
- }
584
- async reconnectPool() {
585
- const config = this.connectionConfig;
586
- if (!config) {
587
- throw new Error("Not connected to Dameng database");
588
- }
589
- console.error(`Reconnecting Dameng source '${this.sourceId}' after pool reset...`);
590
- await this.closeRegisteredPool(config.poolAlias ?? this.buildPoolAlias());
591
- await this.validateDirectConnection(config);
592
- const createdPool = await dmdb.createPool(config);
593
- this.pool = createdPool;
594
- this.poolAlias = config.poolAlias ?? null;
595
- try {
596
- await this.withConnection(async (conn) => {
597
- await this.executeWithTimeout(conn, "SELECT 1 AS OK", [], this.executeOptions());
598
- if (this.initScript) {
599
- for (const statement of splitSQLStatements(this.initScript, "dameng")) {
600
- await this.executeWithTimeout(
601
- conn,
602
- statement,
603
- [],
604
- this.executeOptions({ autoCommit: true })
605
- );
606
- }
607
- }
608
- });
609
- } catch (error) {
610
- await this.closePoolQuietly(createdPool);
611
- this.pool = null;
612
- throw error;
613
- }
614
- }
615
- executeWithTimeout(conn, sql, bindParams, options) {
616
- return this.withTimeout(
617
- conn.execute(sql, bindParams, options),
618
- this.operationTimeoutMs,
619
- "Dameng SQL execution"
620
- );
621
- }
622
- async withTimeout(promise, timeoutMs, label) {
623
- if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
624
- return promise;
625
- }
626
- let timer;
627
- const timeout = new Promise((_, reject) => {
628
- timer = setTimeout(() => {
629
- reject(new DamengOperationTimeoutError(`${label} timed out after ${timeoutMs}ms`, label));
630
- }, timeoutMs);
631
- });
632
- try {
633
- return await Promise.race([promise, timeout]);
634
- } finally {
635
- if (timer) {
636
- clearTimeout(timer);
637
- }
638
- }
639
- }
640
- markPoolUnhealthy(reason) {
641
- const pool = this.pool;
642
- this.pool = null;
643
- console.error(`Resetting Dameng source '${this.sourceId}' pool: ${reason}`);
644
- if (pool) {
645
- void this.closePoolQuietly(pool);
646
- }
647
- }
648
- async releaseConnectionQuietly(conn) {
649
- try {
650
- const release = conn.release ? conn.release() : conn.close();
651
- await this.withTimeout(
652
- release,
653
- RESOURCE_CLEANUP_TIMEOUT_MS,
654
- "Dameng connection release"
655
- );
656
- } catch (error) {
657
- this.markPoolUnhealthy(
658
- `failed to release connection: ${error instanceof Error ? error.message : String(error)}`
659
- );
660
- }
661
- }
662
- isConnectionAcquisitionFailure(error) {
663
- if (error instanceof DamengOperationTimeoutError) {
664
- return error.label === "Dameng connection acquisition";
665
- }
666
- const message = error instanceof Error ? error.message : String(error);
667
- return [
668
- "Connection request timeout in queue",
669
- "Pool cannot open more connections",
670
- "\u83B7\u53D6\u8FDE\u63A5\u8BF7\u6C42\u7B49\u5F85\u8D85\u65F6",
671
- "\u8FDE\u63A5\u6C60\u5DF2\u8FBE\u5230\u6700\u5927\u8FDE\u63A5\u6570"
672
- ].some((item) => message.includes(item));
673
- }
674
- isRetryableConnectFailure(error) {
675
- if (error instanceof DamengOperationTimeoutError) {
676
- return [
677
- "Dameng direct connection",
678
- "Dameng connection acquisition",
679
- "Dameng SQL execution"
680
- ].includes(error.label);
681
- }
682
- const message = error instanceof Error ? error.message : String(error);
683
- return [
684
- "ECONNRESET",
685
- "ETIMEDOUT",
686
- "ECONNREFUSED",
687
- "socket hang up",
688
- "Socket timeout",
689
- "connect timeout",
690
- "Connection request timeout in queue",
691
- "Pool cannot open more connections",
692
- "\u7F51\u7EDC\u901A\u8BAF\u8D85\u65F6",
693
- "\u7F51\u7EDC\u901A\u4FE1\u5F02\u5E38",
694
- "\u8FDE\u63A5\u8D85\u65F6",
695
- "\u83B7\u53D6\u8FDE\u63A5\u8BF7\u6C42\u7B49\u5F85\u8D85\u65F6",
696
- "\u8FDE\u63A5\u6C60\u5DF2\u8FBE\u5230\u6700\u5927\u8FDE\u63A5\u6570"
697
- ].some((item) => message.includes(item));
698
- }
699
- delay(ms) {
700
- return new Promise((resolve) => setTimeout(resolve, ms));
701
- }
702
- executeOptions(extra = {}) {
703
- return {
704
- outFormat: dmdb.OUT_FORMAT_OBJECT,
705
- ...extra
706
- };
707
- }
708
- async validateDirectConnection(config) {
709
- let conn = null;
710
- try {
711
- const {
712
- directConnectString,
713
- poolAlias,
714
- poolMin,
715
- poolMax,
716
- queueRequests,
717
- queueTimeout,
718
- ...directConfig
719
- } = config;
720
- const directConn = await this.withTimeout(
721
- dmdb.getConnection({
722
- ...directConfig,
723
- connectString: directConnectString ?? config.connectString
724
- }),
725
- this.connectionTimeoutMs,
726
- "Dameng direct connection"
727
- );
728
- conn = directConn;
729
- await this.executeWithTimeout(conn, "SELECT 1 AS OK", [], this.executeOptions());
730
- } finally {
731
- if (conn) {
732
- await this.releaseConnectionQuietly(conn);
733
- }
734
- }
735
- }
736
- buildPoolAlias() {
737
- const safeSourceId = this.sourceId.replace(/[^a-zA-Z0-9_-]/g, "_") || "default";
738
- return `dbhub_dameng_${safeSourceId}`;
739
- }
740
- async closeRegisteredPool(poolAlias) {
741
- if (!dmdb.pools?.has?.(poolAlias)) {
742
- return;
743
- }
744
- const pool = dmdb.pools.get(poolAlias);
745
- await this.closePoolQuietly(pool);
746
- dmdb.pools?.delete?.(poolAlias);
747
- }
748
- async closePoolQuietly(pool) {
749
- try {
750
- await this.withTimeout(pool.close(0), RESOURCE_CLEANUP_TIMEOUT_MS, "Dameng pool close");
751
- } catch {
752
- if (pool.poolAlias) {
753
- dmdb.pools?.delete?.(pool.poolAlias);
754
- }
755
- }
756
- }
757
- async resolveSchema(schema) {
758
- const resolved = schema || this.defaultSchema || await this.getDefaultSchema();
759
- if (!resolved) {
760
- throw new Error("No Dameng schema specified and current schema could not be resolved");
761
- }
762
- return this.normalizeIdentifier(resolved);
763
- }
764
- normalizeIdentifier(identifier) {
765
- return /[a-z]/.test(identifier) ? identifier.toUpperCase() : identifier;
766
- }
767
- normalizeLikePattern(pattern) {
768
- return /[a-z]/.test(pattern) ? pattern.toUpperCase() : pattern;
769
- }
770
- normalizeLimit(limit) {
771
- return Math.max(1, Math.min(1e3, Math.floor(limit)));
772
- }
773
- normalizeRows(rows) {
774
- return rows.map((row) => {
775
- if (!Array.isArray(row)) {
776
- return row;
777
- }
778
- return row;
779
- });
780
- }
781
- rowValue(row, key) {
782
- if (!row) {
783
- return null;
784
- }
785
- return row[key] ?? row[key.toLowerCase()] ?? row[key.toUpperCase()] ?? null;
786
- }
787
- isPresent(value) {
788
- return value !== null && value !== "";
789
- }
790
- formatDataType(row) {
791
- const dataType = this.rowValue(row, "DATA_TYPE") ?? "";
792
- const precision = this.rowValue(row, "DATA_PRECISION");
793
- const scale = this.rowValue(row, "DATA_SCALE");
794
- const length = this.rowValue(row, "DATA_LENGTH");
795
- if (precision !== null && precision !== void 0) {
796
- return scale !== null && scale !== void 0 ? `${dataType}(${precision},${scale})` : `${dataType}(${precision})`;
797
- }
798
- if (length !== null && length !== void 0 && /CHAR|VARCHAR|BINARY/i.test(dataType)) {
799
- return `${dataType}(${length})`;
800
- }
801
- return dataType;
802
- }
803
- toBindParams(values) {
804
- return values.map((value) => ({ val: value }));
805
- }
806
- applyMaxRows(sql, maxRows) {
807
- if (!maxRows) {
808
- return sql;
809
- }
810
- const cleaned = stripCommentsAndStrings(sql, "dameng").trim().toLowerCase();
811
- if (!cleaned.startsWith("select")) {
812
- return sql;
813
- }
814
- const trimmed = sql.trim();
815
- const withoutSemicolon = trimmed.endsWith(";") ? trimmed.slice(0, -1) : trimmed;
816
- return `SELECT * FROM (${withoutSemicolon}) WHERE ROWNUM <= ${maxRows}`;
817
- }
818
- replacePositionalParameters(sql, parameters) {
819
- if (!parameters.length) {
820
- return { sql, bindValues: [] };
821
- }
822
- let index = 0;
823
- let result = "";
824
- let i = 0;
825
- while (i < sql.length) {
826
- const char = sql[i];
827
- const next = sql[i + 1];
828
- if (char === "'") {
829
- const start = i++;
830
- while (i < sql.length) {
831
- if (sql[i] === "'" && sql[i + 1] === "'") {
832
- i += 2;
833
- } else if (sql[i] === "'") {
834
- i++;
835
- break;
836
- } else {
837
- i++;
838
- }
839
- }
840
- result += sql.slice(start, i);
841
- continue;
842
- }
843
- if (char === '"') {
844
- const start = i++;
845
- while (i < sql.length) {
846
- if (sql[i] === '"' && sql[i + 1] === '"') {
847
- i += 2;
848
- } else if (sql[i] === '"') {
849
- i++;
850
- break;
851
- } else {
852
- i++;
853
- }
854
- }
855
- result += sql.slice(start, i);
856
- continue;
857
- }
858
- if (char === "-" && next === "-") {
859
- const start = i;
860
- i += 2;
861
- while (i < sql.length && sql[i] !== "\n") {
862
- i++;
863
- }
864
- result += sql.slice(start, i);
865
- continue;
866
- }
867
- if (char === "/" && next === "*") {
868
- const start = i;
869
- i += 2;
870
- while (i < sql.length && !(sql[i] === "*" && sql[i + 1] === "/")) {
871
- i++;
872
- }
873
- i = i < sql.length ? i + 2 : i;
874
- result += sql.slice(start, i);
875
- continue;
876
- }
877
- if (char === "?") {
878
- index += 1;
879
- result += `:${index}`;
880
- i++;
881
- continue;
882
- }
883
- result += char;
884
- i++;
885
- }
886
- if (index !== parameters.length) {
887
- throw new Error(
888
- `Parameter count mismatch: SQL statement has ${index} parameter(s), but ${parameters.length} value(s) were provided.`
889
- );
890
- }
891
- return { sql: result, bindValues: parameters };
892
- }
893
- };
894
- var damengConnector = new DamengConnector();
895
- ConnectorRegistry.register(damengConnector);
896
- export {
897
- DamengConnector
898
- };