@xcr1234/dbhub-fork 1.0.0 → 1.2.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.
@@ -901,7 +901,7 @@ async function resolveSourceConfigs() {
901
901
  source.ssh_keepalive_count_max = sshResult.config.keepaliveCountMax;
902
902
  }
903
903
  if (dsnResult.isDemo) {
904
- const { getSqliteInMemorySetupSql } = await import("./demo-loader-WKQAEFSX.js");
904
+ const { getSqliteInMemorySetupSql } = await import("./demo-loader-PSMTLZ2T.js");
905
905
  source.init_script = getSqliteInMemorySetupSql();
906
906
  }
907
907
  return {
@@ -1073,7 +1073,7 @@ function validateSourceConfig(source, configPath) {
1073
1073
  );
1074
1074
  }
1075
1075
  if (source.type) {
1076
- const validTypes = ["postgres", "mysql", "mariadb", "sqlserver", "sqlite"];
1076
+ const validTypes = ["postgres", "mysql", "mariadb", "sqlserver", "sqlite", "oracle"];
1077
1077
  if (!validTypes.includes(source.type)) {
1078
1078
  throw new Error(
1079
1079
  `Configuration file ${configPath}: source '${source.id}' has invalid type '${source.type}'. Valid types: ${validTypes.join(", ")}`
@@ -1,5 +1,3 @@
1
- import "./chunk-FAIJPBT5.js";
2
-
3
1
  // src/config/demo-loader.ts
4
2
  import fs from "fs";
5
3
  import path from "path";
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  resolveSourceConfigs,
13
13
  resolveTomlConfigPath,
14
14
  resolveTransport
15
- } from "./chunk-6O5I6UAW.js";
15
+ } from "./chunk-XVCBZIOJ.js";
16
16
  import {
17
17
  loadConnectors
18
18
  } from "./chunk-WVVMH6FJ.js";
@@ -27,7 +27,6 @@ import {
27
27
  splitSQLStatements,
28
28
  stripCommentsAndStrings
29
29
  } from "./chunk-C7WEAPX4.js";
30
- import "./chunk-FAIJPBT5.js";
31
30
 
32
31
  // src/server.ts
33
32
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -1429,7 +1428,7 @@ See documentation for more details on configuring database connections.
1429
1428
  const sources = sourceConfigsData.sources;
1430
1429
  console.error(`Configuration source: ${sourceConfigsData.source}`);
1431
1430
  await connectorManager.connectWithSources(sources);
1432
- const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-KI3KJMRY.js");
1431
+ const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-LAQHLQWA.js");
1433
1432
  initializeToolRegistry2({
1434
1433
  sources: sourceConfigsData.sources,
1435
1434
  tools: sourceConfigsData.tools
@@ -1571,12 +1570,12 @@ See documentation for more details on configuring database connections.
1571
1570
 
1572
1571
  // src/index.ts
1573
1572
  var connectorModules = [
1574
- { load: () => import("./postgres-CK6N5BXI.js"), name: "PostgreSQL", driver: "pg" },
1575
- { load: () => import("./sqlserver-5RM44GWI.js"), name: "SQL Server", driver: "mssql" },
1576
- { load: () => import("./sqlite-P6NXTMYE.js"), name: "SQLite", driver: "better-sqlite3" },
1577
- { load: () => import("./mysql-GOLPG2Q6.js"), name: "MySQL", driver: "mysql2" },
1578
- { load: () => import("./mariadb-QHRMVK47.js"), name: "MariaDB", driver: "mariadb" },
1579
- { load: () => import("./oracle-3V2T7ZWF.js"), name: "Oracle", driver: "oracle" }
1573
+ { load: () => import("./postgres-SN3BESYN.js"), name: "PostgreSQL", driver: "pg" },
1574
+ { load: () => import("./sqlserver-YV3KVJ3B.js"), name: "SQL Server", driver: "mssql" },
1575
+ { load: () => import("./sqlite-SJAXGYH3.js"), name: "SQLite", driver: "better-sqlite3" },
1576
+ { load: () => import("./mysql-M4INQQ46.js"), name: "MySQL", driver: "mysql2" },
1577
+ { load: () => import("./mariadb-4IBCPIEV.js"), name: "MariaDB", driver: "mariadb" },
1578
+ { load: () => import("./oracle-N4YKSM4F.js"), name: "Oracle", driver: "oracle" }
1580
1579
  ];
1581
1580
  loadConnectors(connectorModules).then(() => main()).catch((error) => {
1582
1581
  console.error("Fatal error:", error);
@@ -14,7 +14,6 @@ import {
14
14
  obfuscateDSNPassword,
15
15
  splitSQLStatements
16
16
  } from "./chunk-C7WEAPX4.js";
17
- import "./chunk-FAIJPBT5.js";
18
17
 
19
18
  // src/connectors/mariadb/index.ts
20
19
  import * as mariadb from "mariadb";
@@ -14,7 +14,6 @@ import {
14
14
  obfuscateDSNPassword,
15
15
  splitSQLStatements
16
16
  } from "./chunk-C7WEAPX4.js";
17
- import "./chunk-FAIJPBT5.js";
18
17
 
19
18
  // src/connectors/mysql/index.ts
20
19
  import mysql from "mysql2/promise";
@@ -0,0 +1,384 @@
1
+ import {
2
+ SQLRowLimiter
3
+ } from "./chunk-BRXZ5ZQB.js";
4
+ import {
5
+ ConnectorRegistry,
6
+ SafeURL,
7
+ obfuscateDSNPassword,
8
+ splitSQLStatements
9
+ } from "./chunk-C7WEAPX4.js";
10
+
11
+ // src/connectors/oracle/index.ts
12
+ import oracledb from "oracledb";
13
+ var OracleDSNParser = class {
14
+ async parse(dsn, config) {
15
+ if (!this.isValidDSN(dsn)) {
16
+ const obfuscatedDSN = obfuscateDSNPassword(dsn);
17
+ const expectedFormat = this.getSampleDSN();
18
+ throw new Error(
19
+ `Invalid Oracle DSN format.
20
+ Provided: ${obfuscatedDSN}
21
+ Expected: ${expectedFormat}`
22
+ );
23
+ }
24
+ try {
25
+ const url = new SafeURL(dsn);
26
+ const serviceName = url.pathname ? url.pathname.substring(1) : "";
27
+ const port = url.port || "1521";
28
+ const connectString = `${url.hostname}:${port}/${serviceName}`;
29
+ const poolConfig = {
30
+ user: url.username,
31
+ password: url.password,
32
+ connectString,
33
+ // Optional tuning for connection pools
34
+ poolMin: 1,
35
+ poolMax: 10,
36
+ poolIncrement: 1
37
+ };
38
+ if (config?.connectionTimeoutSeconds !== void 0) {
39
+ poolConfig.poolTimeout = config.connectionTimeoutSeconds;
40
+ }
41
+ return poolConfig;
42
+ } catch (error) {
43
+ throw new Error(
44
+ `Failed to parse Oracle DSN: ${error instanceof Error ? error.message : String(error)}`
45
+ );
46
+ }
47
+ }
48
+ getSampleDSN() {
49
+ return "oracle://scott:tiger@localhost:1521/ORCLCDB";
50
+ }
51
+ isValidDSN(dsn) {
52
+ try {
53
+ return dsn.startsWith("oracle://");
54
+ } catch (error) {
55
+ return false;
56
+ }
57
+ }
58
+ };
59
+ var OracleConnector = class _OracleConnector {
60
+ constructor() {
61
+ // Assuming "oracle" will be added to ConnectorType in interface.ts
62
+ this.id = "oracle";
63
+ this.name = "Oracle";
64
+ this.dsnParser = new OracleDSNParser();
65
+ this.pool = null;
66
+ this.sourceId = "default";
67
+ }
68
+ getId() {
69
+ return this.sourceId;
70
+ }
71
+ clone() {
72
+ return new _OracleConnector();
73
+ }
74
+ async connect(dsn, initScript, config) {
75
+ try {
76
+ oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT;
77
+ const poolOptions = await this.dsnParser.parse(dsn, config);
78
+ this.pool = await oracledb.createPool(poolOptions);
79
+ if (config?.queryTimeoutSeconds !== void 0) {
80
+ this.queryTimeoutMs = config.queryTimeoutSeconds * 1e3;
81
+ }
82
+ const conn = await this.pool.getConnection();
83
+ try {
84
+ await conn.execute("SELECT 1 FROM DUAL");
85
+ if (initScript) {
86
+ await conn.execute(initScript);
87
+ }
88
+ } finally {
89
+ await conn.close();
90
+ }
91
+ } catch (err) {
92
+ console.error("Failed to connect to Oracle database:", err);
93
+ throw err;
94
+ }
95
+ }
96
+ async disconnect() {
97
+ if (this.pool) {
98
+ await this.pool.close(0);
99
+ this.pool = null;
100
+ }
101
+ }
102
+ async getSchemas() {
103
+ if (!this.pool) throw new Error("Not connected to database");
104
+ const conn = await this.pool.getConnection();
105
+ try {
106
+ const result = await conn.execute(`
107
+ SELECT USERNAME
108
+ FROM ALL_USERS
109
+ WHERE ORACLE_MAINTAINED = 'N'
110
+ ORDER BY USERNAME
111
+ `);
112
+ return result.rows.map((row) => row.USERNAME);
113
+ } catch (error) {
114
+ console.error("Error getting schemas:", error);
115
+ throw error;
116
+ } finally {
117
+ await conn.close();
118
+ }
119
+ }
120
+ async getTables(schema) {
121
+ if (!this.pool) throw new Error("Not connected to database");
122
+ const conn = await this.pool.getConnection();
123
+ try {
124
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
125
+ const result = await conn.execute(
126
+ `
127
+ SELECT TABLE_NAME
128
+ FROM ALL_TABLES
129
+ WHERE OWNER = :schema
130
+ ORDER BY TABLE_NAME
131
+ `,
132
+ { schema: targetSchema }
133
+ );
134
+ return result.rows.map((row) => row.TABLE_NAME);
135
+ } finally {
136
+ await conn.close();
137
+ }
138
+ }
139
+ async tableExists(tableName, schema) {
140
+ if (!this.pool) throw new Error("Not connected to database");
141
+ const conn = await this.pool.getConnection();
142
+ try {
143
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
144
+ const result = await conn.execute(
145
+ `
146
+ SELECT COUNT(*) AS CNT
147
+ FROM ALL_TABLES
148
+ WHERE OWNER = :schema
149
+ AND TABLE_NAME = :tableName
150
+ `,
151
+ { schema: targetSchema, tableName: tableName.toUpperCase() }
152
+ );
153
+ return result.rows[0].CNT > 0;
154
+ } finally {
155
+ await conn.close();
156
+ }
157
+ }
158
+ async getTableIndexes(tableName, schema) {
159
+ if (!this.pool) throw new Error("Not connected to database");
160
+ const conn = await this.pool.getConnection();
161
+ try {
162
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
163
+ const result = await conn.execute(
164
+ `
165
+ SELECT
166
+ i.INDEX_NAME,
167
+ ic.COLUMN_NAME,
168
+ i.UNIQUENESS,
169
+ c.CONSTRAINT_TYPE
170
+ FROM ALL_INDEXES i
171
+ JOIN ALL_IND_COLUMNS ic
172
+ ON i.INDEX_NAME = ic.INDEX_NAME AND i.OWNER = ic.INDEX_OWNER
173
+ LEFT JOIN ALL_CONSTRAINTS c
174
+ ON c.INDEX_NAME = i.INDEX_NAME AND c.OWNER = i.OWNER AND c.CONSTRAINT_TYPE = 'P'
175
+ WHERE i.TABLE_OWNER = :schema
176
+ AND i.TABLE_NAME = :tableName
177
+ ORDER BY i.INDEX_NAME, ic.COLUMN_POSITION
178
+ `,
179
+ { schema: targetSchema, tableName: tableName.toUpperCase() }
180
+ );
181
+ const indexMap = /* @__PURE__ */ new Map();
182
+ for (const row of result.rows) {
183
+ const indexName = row.INDEX_NAME;
184
+ const columnName = row.COLUMN_NAME;
185
+ const isUnique = row.UNIQUENESS === "UNIQUE";
186
+ const isPrimary = row.CONSTRAINT_TYPE === "P";
187
+ if (!indexMap.has(indexName)) {
188
+ indexMap.set(indexName, {
189
+ columns: [],
190
+ is_unique: isUnique,
191
+ is_primary: isPrimary
192
+ });
193
+ }
194
+ indexMap.get(indexName).columns.push(columnName);
195
+ }
196
+ return Array.from(indexMap.entries()).map(([indexName, indexInfo]) => ({
197
+ index_name: indexName,
198
+ column_names: indexInfo.columns,
199
+ is_unique: indexInfo.is_unique,
200
+ is_primary: indexInfo.is_primary
201
+ }));
202
+ } finally {
203
+ await conn.close();
204
+ }
205
+ }
206
+ async getTableSchema(tableName, schema) {
207
+ if (!this.pool) throw new Error("Not connected to database");
208
+ const conn = await this.pool.getConnection();
209
+ try {
210
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
211
+ const result = await conn.execute(
212
+ `
213
+ SELECT
214
+ c.COLUMN_NAME as "column_name",
215
+ c.DATA_TYPE as "data_type",
216
+ c.NULLABLE as "is_nullable",
217
+ c.DATA_DEFAULT as "column_default",
218
+ cc.COMMENTS as "description"
219
+ FROM ALL_TAB_COLS c
220
+ LEFT JOIN ALL_COL_COMMENTS cc
221
+ ON c.OWNER = cc.OWNER AND c.TABLE_NAME = cc.TABLE_NAME AND c.COLUMN_NAME = cc.COLUMN_NAME
222
+ WHERE c.OWNER = :schema
223
+ AND c.TABLE_NAME = :tableName
224
+ AND c.USER_GENERATED = 'YES' -- Exclude hidden/system columns
225
+ ORDER BY c.COLUMN_ID
226
+ `,
227
+ { schema: targetSchema, tableName: tableName.toUpperCase() }
228
+ );
229
+ return result.rows.map((row) => ({
230
+ ...row,
231
+ // Oracle returns 'Y' or 'N' for NULLABLE; map it to standard format if needed, or leave as string
232
+ is_nullable: row.is_nullable === "Y" ? "YES" : "NO",
233
+ description: row.description || null
234
+ }));
235
+ } finally {
236
+ await conn.close();
237
+ }
238
+ }
239
+ async getTableComment(tableName, schema) {
240
+ if (!this.pool) throw new Error("Not connected to database");
241
+ const conn = await this.pool.getConnection();
242
+ try {
243
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
244
+ const result = await conn.execute(
245
+ `
246
+ SELECT COMMENTS
247
+ FROM ALL_TAB_COMMENTS
248
+ WHERE OWNER = :schema
249
+ AND TABLE_NAME = :tableName
250
+ `,
251
+ { schema: targetSchema, tableName: tableName.toUpperCase() }
252
+ );
253
+ const rows = result.rows;
254
+ return rows.length > 0 ? rows[0].COMMENTS || null : null;
255
+ } finally {
256
+ await conn.close();
257
+ }
258
+ }
259
+ async getStoredProcedures(schema, routineType) {
260
+ if (!this.pool) throw new Error("Not connected to database");
261
+ const conn = await this.pool.getConnection();
262
+ try {
263
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
264
+ let typeFilter = "OBJECT_TYPE IN ('PROCEDURE', 'FUNCTION')";
265
+ if (routineType === "function") typeFilter = "OBJECT_TYPE = 'FUNCTION'";
266
+ if (routineType === "procedure") typeFilter = "OBJECT_TYPE = 'PROCEDURE'";
267
+ const result = await conn.execute(
268
+ `
269
+ SELECT OBJECT_NAME
270
+ FROM ALL_OBJECTS
271
+ WHERE OWNER = :schema
272
+ AND ${typeFilter}
273
+ ORDER BY OBJECT_NAME
274
+ `,
275
+ { schema: targetSchema }
276
+ );
277
+ return result.rows.map((row) => row.OBJECT_NAME);
278
+ } finally {
279
+ await conn.close();
280
+ }
281
+ }
282
+ async getStoredProcedureDetail(procedureName, schema) {
283
+ if (!this.pool) throw new Error("Not connected to database");
284
+ const conn = await this.pool.getConnection();
285
+ try {
286
+ const targetSchema = schema ? schema.toUpperCase() : await this.getCurrentSchema(conn);
287
+ const targetProcedure = procedureName.toUpperCase();
288
+ const objResult = await conn.execute(
289
+ `SELECT OBJECT_TYPE FROM ALL_OBJECTS WHERE OWNER = :schema AND OBJECT_NAME = :procName AND OBJECT_TYPE IN ('PROCEDURE', 'FUNCTION')`,
290
+ { schema: targetSchema, procName: targetProcedure }
291
+ );
292
+ const objRows = objResult.rows;
293
+ if (objRows.length === 0) {
294
+ throw new Error(`Stored procedure '${procedureName}' not found in ${targetSchema}`);
295
+ }
296
+ const procType = objRows[0].OBJECT_TYPE.toLowerCase();
297
+ const argResult = await conn.execute(
298
+ `
299
+ SELECT ARGUMENT_NAME, IN_OUT, DATA_TYPE
300
+ FROM ALL_ARGUMENTS
301
+ WHERE OWNER = :schema
302
+ AND OBJECT_NAME = :procName
303
+ AND ARGUMENT_NAME IS NOT NULL
304
+ ORDER BY POSITION
305
+ `,
306
+ { schema: targetSchema, procName: targetProcedure }
307
+ );
308
+ const parameters = argResult.rows.map((p) => `${p.ARGUMENT_NAME} ${p.IN_OUT} ${p.DATA_TYPE}`).join(", ");
309
+ let definition = "";
310
+ try {
311
+ const srcResult = await conn.execute(
312
+ `
313
+ SELECT TEXT
314
+ FROM ALL_SOURCE
315
+ WHERE OWNER = :schema AND NAME = :procName
316
+ ORDER BY LINE
317
+ `,
318
+ { schema: targetSchema, procName: targetProcedure }
319
+ );
320
+ definition = srcResult.rows.map((r) => r.TEXT).join("");
321
+ } catch (err) {
322
+ console.error(`Error fetching source for ${targetProcedure}: ${err}`);
323
+ }
324
+ return {
325
+ procedure_name: targetProcedure,
326
+ procedure_type: procType,
327
+ language: "plsql",
328
+ parameter_list: parameters,
329
+ definition: definition || void 0
330
+ };
331
+ } finally {
332
+ await conn.close();
333
+ }
334
+ }
335
+ async getCurrentSchema(conn) {
336
+ const result = await conn.execute(`SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') as SCHEMA FROM DUAL`);
337
+ return result.rows[0].SCHEMA;
338
+ }
339
+ async executeSQL(sql, options, parameters) {
340
+ if (!this.pool) throw new Error("Not connected to database");
341
+ const conn = await this.pool.getConnection();
342
+ try {
343
+ const execOptions = {
344
+ outFormat: oracledb.OUT_FORMAT_OBJECT,
345
+ autoCommit: true
346
+ // Default true for executing read/write commands
347
+ };
348
+ if (this.queryTimeoutMs) {
349
+ execOptions.timeout = this.queryTimeoutMs;
350
+ }
351
+ let processedSQL = sql;
352
+ if (options.maxRows) {
353
+ const statements = splitSQLStatements(sql, "oracle");
354
+ const processedStatements = statements.map((statement) => {
355
+ if (statement.trim().toUpperCase().startsWith("SELECT") && !statement.toUpperCase().includes("FETCH FIRST")) {
356
+ return `${statement} FETCH FIRST ${options.maxRows} ROWS ONLY`;
357
+ }
358
+ return SQLRowLimiter.applyMaxRows(statement, options.maxRows);
359
+ });
360
+ processedSQL = processedStatements.join(";\n");
361
+ if (processedStatements.length > 1) {
362
+ processedSQL = `BEGIN
363
+ ${processedSQL};
364
+ END;`;
365
+ }
366
+ }
367
+ const result = await conn.execute(processedSQL, parameters || [], execOptions);
368
+ return {
369
+ rows: result.rows || [],
370
+ rowCount: result.rowsAffected || 0
371
+ };
372
+ } catch (error) {
373
+ console.error("Error executing query:", error);
374
+ throw error;
375
+ } finally {
376
+ await conn.close();
377
+ }
378
+ }
379
+ };
380
+ var oracleConnector = new OracleConnector();
381
+ ConnectorRegistry.register(oracleConnector);
382
+ export {
383
+ OracleConnector
384
+ };
@@ -10,7 +10,6 @@ import {
10
10
  obfuscateDSNPassword,
11
11
  splitSQLStatements
12
12
  } from "./chunk-C7WEAPX4.js";
13
- import "./chunk-FAIJPBT5.js";
14
13
 
15
14
  // src/connectors/postgres/index.ts
16
15
  import fs from "fs";
@@ -2,10 +2,9 @@ import {
2
2
  ToolRegistry,
3
3
  getToolRegistry,
4
4
  initializeToolRegistry
5
- } from "./chunk-6O5I6UAW.js";
5
+ } from "./chunk-XVCBZIOJ.js";
6
6
  import "./chunk-WVVMH6FJ.js";
7
7
  import "./chunk-C7WEAPX4.js";
8
- import "./chunk-FAIJPBT5.js";
9
8
  export {
10
9
  ToolRegistry,
11
10
  getToolRegistry,