@zz1996/dbhub-dameng 0.1.1 → 0.1.3
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/dbhub.dameng.toml.example +1 -0
- package/dist/{dameng-DC7OP4VV.js → dameng-JYMTC4HE.js} +114 -11
- package/dist/index.js +21 -19
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
id = "dm8_dev"
|
|
6
6
|
description = "Dameng DM8 development database. Read-only access for agent schema discovery and SQL queries."
|
|
7
7
|
dsn = "dameng://${DM8_USER}:${DM8_PASSWORD}@${DM8_HOST}:5236/${DM8_SCHEMA}"
|
|
8
|
+
connection_timeout = 5
|
|
8
9
|
query_timeout = 30
|
|
9
10
|
lazy = true
|
|
10
11
|
|
|
@@ -23,22 +23,30 @@ Expected: ${expectedFormat}`
|
|
|
23
23
|
const url = new SafeURL(dsn);
|
|
24
24
|
const schema = url.pathname ? decodeURIComponent(url.pathname.substring(1)) : void 0;
|
|
25
25
|
const port = url.port ? parseInt(url.port, 10) : 5236;
|
|
26
|
-
const queryParams =
|
|
26
|
+
const queryParams = {};
|
|
27
|
+
const queryStringParts = [];
|
|
27
28
|
url.forEachSearchParam((value, key) => {
|
|
28
|
-
queryParams
|
|
29
|
+
queryParams[key] = value;
|
|
30
|
+
queryStringParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
29
31
|
});
|
|
30
|
-
const connectString = `dm://${encodeURIComponent(url.username)}:${encodeURIComponent(url.password)}@${url.hostname}:${port}${queryParams.length > 0 ? `?${queryParams.join("&")}` : ""}`;
|
|
31
32
|
const connectionConfig = {
|
|
32
|
-
connectString
|
|
33
|
+
connectString: `dm://${encodeURIComponent(url.username)}:${encodeURIComponent(url.password)}@${url.hostname}:${port}${queryStringParts.length > 0 ? `?${queryStringParts.join("&")}` : ""}`,
|
|
34
|
+
directConnectString: `${url.hostname}:${port}`,
|
|
33
35
|
user: url.username,
|
|
34
36
|
password: url.password,
|
|
35
37
|
schema: schema || void 0,
|
|
36
38
|
poolMin: 0,
|
|
37
|
-
poolMax: 4
|
|
39
|
+
poolMax: 4,
|
|
40
|
+
...queryParams
|
|
38
41
|
};
|
|
39
42
|
if (config?.connectionTimeoutSeconds !== void 0) {
|
|
43
|
+
connectionConfig.connectTimeout = config.connectionTimeoutSeconds * 1e3;
|
|
40
44
|
connectionConfig.queueTimeout = config.connectionTimeoutSeconds * 1e3;
|
|
41
45
|
}
|
|
46
|
+
if (config?.queryTimeoutSeconds !== void 0) {
|
|
47
|
+
connectionConfig.sessionTimeout = config.queryTimeoutSeconds;
|
|
48
|
+
connectionConfig.socketTimeout = config.queryTimeoutSeconds * 1e3 + 1e3;
|
|
49
|
+
}
|
|
42
50
|
return connectionConfig;
|
|
43
51
|
} catch (error) {
|
|
44
52
|
throw new Error(
|
|
@@ -65,6 +73,7 @@ var DamengConnector = class _DamengConnector {
|
|
|
65
73
|
this.pool = null;
|
|
66
74
|
this.sourceId = "default";
|
|
67
75
|
this.defaultSchema = null;
|
|
76
|
+
this.poolAlias = null;
|
|
68
77
|
}
|
|
69
78
|
getId() {
|
|
70
79
|
return this.sourceId;
|
|
@@ -73,13 +82,16 @@ var DamengConnector = class _DamengConnector {
|
|
|
73
82
|
return new _DamengConnector();
|
|
74
83
|
}
|
|
75
84
|
async connect(dsn, initScript, config) {
|
|
85
|
+
let createdPool = null;
|
|
76
86
|
try {
|
|
77
87
|
const connectionConfig = await this.dsnParser.parse(dsn, config);
|
|
88
|
+
connectionConfig.poolAlias = this.buildPoolAlias();
|
|
78
89
|
this.defaultSchema = connectionConfig.schema ?? null;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
90
|
+
this.poolAlias = connectionConfig.poolAlias;
|
|
91
|
+
await this.closeRegisteredPool(connectionConfig.poolAlias);
|
|
92
|
+
await this.validateDirectConnection(connectionConfig);
|
|
93
|
+
createdPool = await dmdb.createPool(connectionConfig);
|
|
94
|
+
this.pool = createdPool;
|
|
83
95
|
await this.withConnection(async (conn) => {
|
|
84
96
|
await conn.execute("SELECT 1 AS OK", [], this.executeOptions());
|
|
85
97
|
if (!this.defaultSchema) {
|
|
@@ -97,15 +109,26 @@ var DamengConnector = class _DamengConnector {
|
|
|
97
109
|
}
|
|
98
110
|
});
|
|
99
111
|
} catch (error) {
|
|
112
|
+
if (createdPool) {
|
|
113
|
+
await this.closePoolQuietly(createdPool);
|
|
114
|
+
} else if (this.poolAlias) {
|
|
115
|
+
await this.closeRegisteredPool(this.poolAlias);
|
|
116
|
+
}
|
|
117
|
+
this.pool = null;
|
|
118
|
+
this.poolAlias = null;
|
|
100
119
|
console.error("Failed to connect to Dameng database:", error);
|
|
101
120
|
throw error;
|
|
102
121
|
}
|
|
103
122
|
}
|
|
104
123
|
async disconnect() {
|
|
105
124
|
if (this.pool) {
|
|
106
|
-
await this.pool
|
|
125
|
+
await this.closePoolQuietly(this.pool);
|
|
107
126
|
this.pool = null;
|
|
108
127
|
}
|
|
128
|
+
if (this.poolAlias) {
|
|
129
|
+
await this.closeRegisteredPool(this.poolAlias);
|
|
130
|
+
this.poolAlias = null;
|
|
131
|
+
}
|
|
109
132
|
}
|
|
110
133
|
async getSchemas() {
|
|
111
134
|
const rows = await this.queryRows(`
|
|
@@ -116,6 +139,17 @@ var DamengConnector = class _DamengConnector {
|
|
|
116
139
|
`);
|
|
117
140
|
return rows.map((row) => this.rowValue(row, "SCHEMA_NAME")).filter(this.isPresent);
|
|
118
141
|
}
|
|
142
|
+
async schemaExists(schema) {
|
|
143
|
+
const rows = await this.queryRows(
|
|
144
|
+
`
|
|
145
|
+
SELECT COUNT(*) AS CNT
|
|
146
|
+
FROM ALL_USERS
|
|
147
|
+
WHERE USERNAME = :1
|
|
148
|
+
`,
|
|
149
|
+
[this.normalizeIdentifier(schema)]
|
|
150
|
+
);
|
|
151
|
+
return Number(this.rowValue(rows[0], "CNT") ?? 0) > 0;
|
|
152
|
+
}
|
|
119
153
|
async getDefaultSchema() {
|
|
120
154
|
return this.defaultSchema;
|
|
121
155
|
}
|
|
@@ -132,6 +166,25 @@ var DamengConnector = class _DamengConnector {
|
|
|
132
166
|
);
|
|
133
167
|
return rows.map((row) => this.rowValue(row, "TABLE_NAME")).filter(this.isPresent);
|
|
134
168
|
}
|
|
169
|
+
async searchTables(pattern, schema, limit = 100) {
|
|
170
|
+
const owner = await this.resolveSchema(schema);
|
|
171
|
+
const rowLimit = this.normalizeLimit(limit);
|
|
172
|
+
const rows = await this.queryRows(
|
|
173
|
+
`
|
|
174
|
+
SELECT TABLE_NAME
|
|
175
|
+
FROM (
|
|
176
|
+
SELECT TABLE_NAME
|
|
177
|
+
FROM ALL_TABLES
|
|
178
|
+
WHERE OWNER = :1
|
|
179
|
+
AND TABLE_NAME LIKE :2
|
|
180
|
+
ORDER BY TABLE_NAME
|
|
181
|
+
)
|
|
182
|
+
WHERE ROWNUM <= ${rowLimit}
|
|
183
|
+
`,
|
|
184
|
+
[owner, this.normalizeLikePattern(pattern)]
|
|
185
|
+
);
|
|
186
|
+
return rows.map((row) => this.rowValue(row, "TABLE_NAME")).filter(this.isPresent).map((name) => ({ name, schema: owner }));
|
|
187
|
+
}
|
|
135
188
|
async getViews(schema) {
|
|
136
189
|
const owner = await this.resolveSchema(schema);
|
|
137
190
|
const rows = await this.queryRows(
|
|
@@ -362,10 +415,54 @@ var DamengConnector = class _DamengConnector {
|
|
|
362
415
|
executeOptions(extra = {}) {
|
|
363
416
|
return {
|
|
364
417
|
outFormat: dmdb.OUT_FORMAT_OBJECT,
|
|
365
|
-
...this.queryTimeoutMs !== void 0 && { queryTimeout: this.queryTimeoutMs },
|
|
366
418
|
...extra
|
|
367
419
|
};
|
|
368
420
|
}
|
|
421
|
+
async validateDirectConnection(config) {
|
|
422
|
+
let conn = null;
|
|
423
|
+
try {
|
|
424
|
+
const {
|
|
425
|
+
directConnectString,
|
|
426
|
+
poolAlias,
|
|
427
|
+
poolMin,
|
|
428
|
+
poolMax,
|
|
429
|
+
queueRequests,
|
|
430
|
+
queueTimeout,
|
|
431
|
+
...directConfig
|
|
432
|
+
} = config;
|
|
433
|
+
const directConn = await dmdb.getConnection({
|
|
434
|
+
...directConfig,
|
|
435
|
+
connectString: directConnectString ?? config.connectString
|
|
436
|
+
});
|
|
437
|
+
conn = directConn;
|
|
438
|
+
await conn.execute("SELECT 1 AS OK", [], this.executeOptions());
|
|
439
|
+
} finally {
|
|
440
|
+
if (conn) {
|
|
441
|
+
await conn.close();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
buildPoolAlias() {
|
|
446
|
+
const safeSourceId = this.sourceId.replace(/[^a-zA-Z0-9_-]/g, "_") || "default";
|
|
447
|
+
return `dbhub_dameng_${safeSourceId}`;
|
|
448
|
+
}
|
|
449
|
+
async closeRegisteredPool(poolAlias) {
|
|
450
|
+
if (!dmdb.pools?.has?.(poolAlias)) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
const pool = dmdb.pools.get(poolAlias);
|
|
454
|
+
await this.closePoolQuietly(pool);
|
|
455
|
+
dmdb.pools?.delete?.(poolAlias);
|
|
456
|
+
}
|
|
457
|
+
async closePoolQuietly(pool) {
|
|
458
|
+
try {
|
|
459
|
+
await pool.close(0);
|
|
460
|
+
} catch {
|
|
461
|
+
if (pool.poolAlias) {
|
|
462
|
+
dmdb.pools?.delete?.(pool.poolAlias);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
369
466
|
async resolveSchema(schema) {
|
|
370
467
|
const resolved = schema || this.defaultSchema || await this.getDefaultSchema();
|
|
371
468
|
if (!resolved) {
|
|
@@ -376,6 +473,12 @@ var DamengConnector = class _DamengConnector {
|
|
|
376
473
|
normalizeIdentifier(identifier) {
|
|
377
474
|
return /[a-z]/.test(identifier) ? identifier.toUpperCase() : identifier;
|
|
378
475
|
}
|
|
476
|
+
normalizeLikePattern(pattern) {
|
|
477
|
+
return /[a-z]/.test(pattern) ? pattern.toUpperCase() : pattern;
|
|
478
|
+
}
|
|
479
|
+
normalizeLimit(limit) {
|
|
480
|
+
return Math.max(1, Math.min(1e3, Math.floor(limit)));
|
|
481
|
+
}
|
|
379
482
|
normalizeRows(rows) {
|
|
380
483
|
return rows.map((row) => {
|
|
381
484
|
if (!Array.isArray(row)) {
|
package/dist/index.js
CHANGED
|
@@ -431,23 +431,25 @@ async function searchTables(connector, pattern, schemaFilter, detailLevel, limit
|
|
|
431
431
|
for (const schemaName of schemasToSearch) {
|
|
432
432
|
if (results.length >= limit) break;
|
|
433
433
|
try {
|
|
434
|
-
const
|
|
435
|
-
const matched =
|
|
436
|
-
for (const
|
|
434
|
+
const searchableConnector = connector;
|
|
435
|
+
const matched = searchableConnector.searchTables ? await searchableConnector.searchTables(pattern, schemaName, limit - results.length) : (await connector.getTables(schemaName)).filter((table) => regex.test(table)).map((name) => ({ name, schema: schemaName }));
|
|
436
|
+
for (const tableMatch of matched) {
|
|
437
437
|
if (results.length >= limit) break;
|
|
438
|
+
const tableName = tableMatch.name;
|
|
439
|
+
const matchedSchema = tableMatch.schema;
|
|
438
440
|
if (detailLevel === "names") {
|
|
439
441
|
results.push({
|
|
440
442
|
name: tableName,
|
|
441
|
-
schema:
|
|
443
|
+
schema: matchedSchema
|
|
442
444
|
});
|
|
443
445
|
} else if (detailLevel === "summary") {
|
|
444
446
|
try {
|
|
445
|
-
const columns = await connector.getTableSchema(tableName,
|
|
446
|
-
const rowCount = await getTableRowCount(connector, tableName,
|
|
447
|
-
const comment = await getTableComment(connector, tableName,
|
|
447
|
+
const columns = await connector.getTableSchema(tableName, matchedSchema);
|
|
448
|
+
const rowCount = await getTableRowCount(connector, tableName, matchedSchema);
|
|
449
|
+
const comment = await getTableComment(connector, tableName, matchedSchema);
|
|
448
450
|
results.push({
|
|
449
451
|
name: tableName,
|
|
450
|
-
schema:
|
|
452
|
+
schema: matchedSchema,
|
|
451
453
|
column_count: columns.length,
|
|
452
454
|
row_count: rowCount,
|
|
453
455
|
...comment ? { comment } : {}
|
|
@@ -455,20 +457,20 @@ async function searchTables(connector, pattern, schemaFilter, detailLevel, limit
|
|
|
455
457
|
} catch (error) {
|
|
456
458
|
results.push({
|
|
457
459
|
name: tableName,
|
|
458
|
-
schema:
|
|
460
|
+
schema: matchedSchema,
|
|
459
461
|
column_count: null,
|
|
460
462
|
row_count: null
|
|
461
463
|
});
|
|
462
464
|
}
|
|
463
465
|
} else {
|
|
464
466
|
try {
|
|
465
|
-
const columns = await connector.getTableSchema(tableName,
|
|
466
|
-
const indexes = await connector.getTableIndexes(tableName,
|
|
467
|
-
const rowCount = await getTableRowCount(connector, tableName,
|
|
468
|
-
const comment = await getTableComment(connector, tableName,
|
|
467
|
+
const columns = await connector.getTableSchema(tableName, matchedSchema);
|
|
468
|
+
const indexes = await connector.getTableIndexes(tableName, matchedSchema);
|
|
469
|
+
const rowCount = await getTableRowCount(connector, tableName, matchedSchema);
|
|
470
|
+
const comment = await getTableComment(connector, tableName, matchedSchema);
|
|
469
471
|
results.push({
|
|
470
472
|
name: tableName,
|
|
471
|
-
schema:
|
|
473
|
+
schema: matchedSchema,
|
|
472
474
|
column_count: columns.length,
|
|
473
475
|
row_count: rowCount,
|
|
474
476
|
...comment ? { comment } : {},
|
|
@@ -489,7 +491,7 @@ async function searchTables(connector, pattern, schemaFilter, detailLevel, limit
|
|
|
489
491
|
} catch (error) {
|
|
490
492
|
results.push({
|
|
491
493
|
name: tableName,
|
|
492
|
-
schema:
|
|
494
|
+
schema: matchedSchema,
|
|
493
495
|
error: `Unable to fetch full details: ${error.message}`
|
|
494
496
|
});
|
|
495
497
|
}
|
|
@@ -759,10 +761,10 @@ function createSearchDatabaseObjectsToolHandler(sourceId) {
|
|
|
759
761
|
}
|
|
760
762
|
}
|
|
761
763
|
if (schema) {
|
|
762
|
-
const
|
|
763
|
-
if (!
|
|
764
|
+
const exists = connector.schemaExists ? await connector.schemaExists(schema) : (await connector.getSchemas()).includes(schema);
|
|
765
|
+
if (!exists) {
|
|
764
766
|
success = false;
|
|
765
|
-
errorMessage = `Schema '${schema}' does not exist
|
|
767
|
+
errorMessage = `Schema '${schema}' does not exist`;
|
|
766
768
|
return createToolErrorResponse(errorMessage, "SCHEMA_NOT_FOUND");
|
|
767
769
|
}
|
|
768
770
|
}
|
|
@@ -1833,7 +1835,7 @@ var connectorModules = [
|
|
|
1833
1835
|
{ load: () => import("./sqlite-IOUAYHGE.js"), name: "SQLite", driver: "node:sqlite" },
|
|
1834
1836
|
{ load: () => import("./mysql-A43SL7UM.js"), name: "MySQL", driver: "mysql2" },
|
|
1835
1837
|
{ load: () => import("./mariadb-7F72IRB4.js"), name: "MariaDB", driver: "mariadb" },
|
|
1836
|
-
{ load: () => import("./dameng-
|
|
1838
|
+
{ load: () => import("./dameng-JYMTC4HE.js"), name: "Dameng", driver: "dmdb" }
|
|
1837
1839
|
];
|
|
1838
1840
|
loadConnectors(connectorModules).then(() => main()).catch((error) => {
|
|
1839
1841
|
console.error("Fatal error:", error);
|