@zz1996/dbhub-dameng 1.2.5-dameng.621 → 1.3.0-dameng.631

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.
@@ -4,13 +4,14 @@ import {
4
4
  import {
5
5
  getDatabaseTypeFromDSN,
6
6
  getDefaultPortForType,
7
+ obfuscateDSNPassword,
7
8
  parseConnectionInfoFromDSN
8
- } from "./chunk-3RJRH3CN.js";
9
+ } from "./chunk-GDZT5PH3.js";
9
10
  import {
10
11
  ConnectorRegistry,
11
12
  SafeURL,
12
13
  stripCommentsAndStrings
13
- } from "./chunk-2JL4KCP5.js";
14
+ } from "./chunk-AQKEKC24.js";
14
15
 
15
16
  // src/tools/builtin-tools.ts
16
17
  var BUILTIN_TOOL_EXECUTE_SQL = "execute_sql";
@@ -662,7 +663,7 @@ function buildDSNFromEnvParams() {
662
663
  return null;
663
664
  }
664
665
  }
665
- const supportedTypes = ["postgres", "postgresql", "mysql", "mariadb", "sqlserver", "sqlite", "dameng"];
666
+ const supportedTypes = ["postgres", "postgresql", "mysql", "mariadb", "sqlserver", "sqlite", "oracle", "dameng"];
666
667
  if (!supportedTypes.includes(dbType.toLowerCase())) {
667
668
  throw new Error(`Unsupported DB_TYPE: ${dbType}. Supported types: ${supportedTypes.join(", ")}`);
668
669
  }
@@ -680,6 +681,9 @@ function buildDSNFromEnvParams() {
680
681
  case "sqlserver":
681
682
  port = "1433";
682
683
  break;
684
+ case "oracle":
685
+ port = "1521";
686
+ break;
683
687
  case "dameng":
684
688
  port = "5236";
685
689
  break;
@@ -826,15 +830,7 @@ function splitTokenList(value) {
826
830
  return value.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
827
831
  }
828
832
  function redactDSN(dsn) {
829
- try {
830
- const url = new URL(dsn);
831
- if (url.password) {
832
- url.password = "*******";
833
- }
834
- return url.toString();
835
- } catch (error) {
836
- return dsn.replace(/\/\/([^:]+):([^@]+)@/, "//$1:***@");
837
- }
833
+ return obfuscateDSNPassword(dsn);
838
834
  }
839
835
  function resolveId() {
840
836
  const args = parseCommandLineArgs();
@@ -981,7 +977,7 @@ async function resolveSourceConfigs() {
981
977
  dsnUrl = new SafeURL(dsnResult.dsn);
982
978
  } catch (error) {
983
979
  throw new Error(
984
- `Invalid DSN format: ${dsnResult.dsn}. Expected format: protocol://[user[:password]@]host[:port]/database`
980
+ `Invalid DSN format: ${redactDSN(dsnResult.dsn)}. Expected format: protocol://[user[:password]@]host[:port]/database`
985
981
  );
986
982
  }
987
983
  const protocol = dsnUrl.protocol.replace(":", "");
@@ -998,6 +994,8 @@ async function resolveSourceConfigs() {
998
994
  dbType = "dameng";
999
995
  } else if (protocol === "sqlite") {
1000
996
  dbType = "sqlite";
997
+ } else if (protocol === "oracle") {
998
+ dbType = "oracle";
1001
999
  } else {
1002
1000
  throw new Error(`Unsupported database type in DSN: ${protocol}`);
1003
1001
  }
@@ -1295,7 +1293,7 @@ function validateSourceConfig(source, configPath) {
1295
1293
  );
1296
1294
  }
1297
1295
  if (source.type) {
1298
- const validTypes = ["postgres", "mysql", "mariadb", "sqlserver", "sqlite", "dameng"];
1296
+ const validTypes = ["postgres", "mysql", "mariadb", "sqlserver", "sqlite", "oracle", "dameng"];
1299
1297
  if (!validTypes.includes(source.type)) {
1300
1298
  throw new Error(
1301
1299
  `Configuration file ${configPath}: source '${source.id}' has invalid type '${source.type}'. Valid types: ${validTypes.join(", ")}`
@@ -1384,9 +1382,14 @@ function validateSourceConfig(source, configPath) {
1384
1382
  `Configuration file ${configPath}: source '${source.id}' has invalid sslmode '${source.sslmode}'. Valid values: ${validSslModes.join(", ")}`
1385
1383
  );
1386
1384
  }
1387
- if ((source.sslmode === "verify-ca" || source.sslmode === "verify-full") && source.type !== "postgres") {
1385
+ const verifyModesByType = {
1386
+ postgres: ["verify-ca", "verify-full"],
1387
+ oracle: ["verify-full"]
1388
+ };
1389
+ if ((source.sslmode === "verify-ca" || source.sslmode === "verify-full") && !(verifyModesByType[source.type] ?? []).includes(source.sslmode)) {
1390
+ const supported = ["disable", "require", ...verifyModesByType[source.type] ?? []];
1388
1391
  throw new Error(
1389
- `Configuration file ${configPath}: source '${source.id}' has sslmode '${source.sslmode}' which is only supported for PostgreSQL. Valid values for ${source.type}: disable, require`
1392
+ `Configuration file ${configPath}: source '${source.id}' has sslmode '${source.sslmode}' which is not supported for ${source.type}. Valid values for ${source.type}: ${supported.join(", ")}`
1390
1393
  );
1391
1394
  }
1392
1395
  }
@@ -1394,6 +1397,11 @@ function validateSourceConfig(source, configPath) {
1394
1397
  validateDSNFieldConflicts(source, configPath);
1395
1398
  }
1396
1399
  if (source.sslrootcert !== void 0) {
1400
+ if (source.type !== "postgres") {
1401
+ throw new Error(
1402
+ `Configuration file ${configPath}: source '${source.id}' has sslrootcert but it is only supported for PostgreSQL.`
1403
+ );
1404
+ }
1397
1405
  if (source.sslmode !== "verify-ca" && source.sslmode !== "verify-full") {
1398
1406
  throw new Error(
1399
1407
  `Configuration file ${configPath}: source '${source.id}' has sslrootcert but sslmode is '${source.sslmode ?? "not set"}'. sslrootcert requires sslmode 'verify-ca' or 'verify-full'`
@@ -1746,11 +1754,17 @@ var NETWORK_CODES = /* @__PURE__ */ new Set([
1746
1754
  "ENETUNREACH",
1747
1755
  "ECONNRESET"
1748
1756
  ]);
1757
+ var NETWORK_CODES_BY_TYPE = {
1758
+ // NJS-503: "connection to host ... could not be established" (Thin mode)
1759
+ oracle: /* @__PURE__ */ new Set(["NJS-503"])
1760
+ };
1749
1761
  var AUTH_CODES = {
1750
1762
  postgres: ["28P01", "28000"],
1751
1763
  mysql: ["ER_ACCESS_DENIED_ERROR", 1045, 1698],
1752
1764
  mariadb: ["ER_ACCESS_DENIED_ERROR", 1045, 1698],
1753
1765
  sqlserver: ["ELOGIN"],
1766
+ // ORA-01017: invalid username/password; ORA-28000: account locked
1767
+ oracle: ["ORA-01017", "ORA-28000"],
1754
1768
  dameng: [-2501],
1755
1769
  // Invalid username or password
1756
1770
  sqlite: []
@@ -1774,7 +1788,7 @@ function classifyConnectionError(error, connectorType, sourceId) {
1774
1788
  return { code: "TUNNEL_FAILED", message: tunnelMessage(sourceId) };
1775
1789
  }
1776
1790
  const code = err.code;
1777
- if (typeof code === "string" && NETWORK_CODES.has(code)) {
1791
+ if (typeof code === "string" && (NETWORK_CODES.has(code) || NETWORK_CODES_BY_TYPE[connectorType]?.has(code))) {
1778
1792
  return { code: "SOURCE_UNREACHABLE", message: unreachableMessage(sourceId) };
1779
1793
  }
1780
1794
  const authCodes = AUTH_CODES[connectorType];
@@ -2272,24 +2286,29 @@ var PARAMETER_STYLES = {
2272
2286
  // ?, ?, ?
2273
2287
  sqlserver: "named",
2274
2288
  // @p1, @p2, @p3
2275
- sqlite: "positional"
2289
+ sqlite: "positional",
2276
2290
  // ?, ?, ?
2291
+ oracle: "colon"
2292
+ // :1, :2, :3
2277
2293
  };
2278
- function detectParameterStyle(statement) {
2279
- const cleanedSQL = stripCommentsAndStrings(statement);
2294
+ function detectParameterStyle(statement, connectorType) {
2295
+ const cleanedSQL = stripCommentsAndStrings(statement, connectorType);
2280
2296
  if (/\$\d+/.test(cleanedSQL)) {
2281
2297
  return "numbered";
2282
2298
  }
2283
2299
  if (/@p\d+/.test(cleanedSQL)) {
2284
2300
  return "named";
2285
2301
  }
2302
+ if (/(?<!:):\d+/.test(cleanedSQL)) {
2303
+ return "colon";
2304
+ }
2286
2305
  if (/\?/.test(cleanedSQL)) {
2287
2306
  return "positional";
2288
2307
  }
2289
2308
  return "none";
2290
2309
  }
2291
2310
  function validateParameterStyle(statement, connectorType) {
2292
- const detectedStyle = detectParameterStyle(statement);
2311
+ const detectedStyle = detectParameterStyle(statement, connectorType);
2293
2312
  const expectedStyle = PARAMETER_STYLES[connectorType];
2294
2313
  if (detectedStyle === "none") {
2295
2314
  return;
@@ -2298,57 +2317,53 @@ function validateParameterStyle(statement, connectorType) {
2298
2317
  const examples = {
2299
2318
  numbered: "$1, $2, $3",
2300
2319
  positional: "?, ?, ?",
2301
- named: "@p1, @p2, @p3"
2320
+ named: "@p1, @p2, @p3",
2321
+ colon: ":1, :2, :3"
2302
2322
  };
2303
2323
  throw new Error(
2304
2324
  `Invalid parameter syntax for ${connectorType}. Expected ${expectedStyle} style (${examples[expectedStyle]}), but found ${detectedStyle} style in statement.`
2305
2325
  );
2306
2326
  }
2307
2327
  }
2308
- function countParameters(statement) {
2309
- const style = detectParameterStyle(statement);
2310
- const cleanedSQL = stripCommentsAndStrings(statement);
2328
+ function countParameters(statement, connectorType) {
2329
+ const style = detectParameterStyle(statement, connectorType);
2330
+ const cleanedSQL = stripCommentsAndStrings(statement, connectorType);
2311
2331
  switch (style) {
2312
- case "numbered": {
2313
- const matches = cleanedSQL.match(/\$\d+/g);
2314
- if (!matches) return 0;
2315
- const numbers = matches.map((m) => parseInt(m.slice(1), 10));
2316
- const uniqueIndices = Array.from(new Set(numbers)).sort((a, b) => a - b);
2317
- const maxIndex = Math.max(...uniqueIndices);
2318
- for (let i = 1; i <= maxIndex; i++) {
2319
- if (!uniqueIndices.includes(i)) {
2320
- throw new Error(
2321
- `Non-sequential numbered parameters detected. Found placeholders: ${uniqueIndices.map((n) => `$${n}`).join(", ")}. Parameters must be sequential starting from $1 (missing $${i}).`
2322
- );
2323
- }
2324
- }
2325
- return maxIndex;
2326
- }
2327
- case "named": {
2328
- const matches = cleanedSQL.match(/@p\d+/g);
2329
- if (!matches) return 0;
2330
- const numbers = matches.map((m) => parseInt(m.slice(2), 10));
2331
- const uniqueIndices = Array.from(new Set(numbers)).sort((a, b) => a - b);
2332
- const maxIndex = Math.max(...uniqueIndices);
2333
- for (let i = 1; i <= maxIndex; i++) {
2334
- if (!uniqueIndices.includes(i)) {
2335
- throw new Error(
2336
- `Non-sequential named parameters detected. Found placeholders: ${uniqueIndices.map((n) => `@p${n}`).join(", ")}. Parameters must be sequential starting from @p1 (missing @p${i}).`
2337
- );
2338
- }
2339
- }
2340
- return maxIndex;
2341
- }
2342
- case "positional": {
2332
+ case "numbered":
2333
+ return countIndexedParameters(cleanedSQL, /\$\d+/g, "$", "numbered");
2334
+ case "named":
2335
+ return countIndexedParameters(cleanedSQL, /@p\d+/g, "@p", "named");
2336
+ case "colon":
2337
+ return countIndexedParameters(cleanedSQL, /(?<!:):\d+/g, ":", "colon-numbered");
2338
+ case "positional":
2343
2339
  return (cleanedSQL.match(/\?/g) || []).length;
2344
- }
2345
2340
  default:
2346
2341
  return 0;
2347
2342
  }
2348
2343
  }
2344
+ function countIndexedParameters(cleanedSQL, pattern, sigil, styleName) {
2345
+ const matches = cleanedSQL.match(pattern);
2346
+ if (!matches) return 0;
2347
+ const indices = new Set(matches.map((m) => parseInt(m.slice(sigil.length), 10)));
2348
+ if (indices.has(0)) {
2349
+ throw new Error(
2350
+ `Invalid ${styleName} parameter ${sigil}0. Parameters must be sequential starting from ${sigil}1.`
2351
+ );
2352
+ }
2353
+ const maxIndex = Math.max(...indices);
2354
+ for (let i = 1; i <= maxIndex; i++) {
2355
+ if (!indices.has(i)) {
2356
+ const found = Array.from(indices).sort((a, b) => a - b).map((n) => `${sigil}${n}`).join(", ");
2357
+ throw new Error(
2358
+ `Non-sequential ${styleName} parameters detected. Found placeholders: ${found}. Parameters must be sequential starting from ${sigil}1 (missing ${sigil}${i}).`
2359
+ );
2360
+ }
2361
+ }
2362
+ return maxIndex;
2363
+ }
2349
2364
  function validateParameters(statement, parameters, connectorType) {
2350
2365
  validateParameterStyle(statement, connectorType);
2351
- const paramCount = countParameters(statement);
2366
+ const paramCount = countParameters(statement, connectorType);
2352
2367
  const definedCount = parameters?.length || 0;
2353
2368
  if (paramCount !== definedCount) {
2354
2369
  throw new Error(
@@ -320,13 +320,31 @@ function scanTokenSQLite(sql, i) {
320
320
  function scanTokenSQLServer(sql, i) {
321
321
  return scanSingleLineComment(sql, i) ?? scanMultiLineComment(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? scanBracketQuotedIdentifier(sql, i) ?? plainToken(i);
322
322
  }
323
+ function scanOracleAlternativeQuotedString(sql, i) {
324
+ if (sql[i] !== "q" && sql[i] !== "Q" || sql[i + 1] !== "'") {
325
+ return null;
326
+ }
327
+ const open = sql[i + 2];
328
+ if (open === void 0 || open === " " || open === " " || open === "\n") {
329
+ return null;
330
+ }
331
+ const mirrors = { "(": ")", "[": "]", "{": "}", "<": ">" };
332
+ const close = (mirrors[open] ?? open) + "'";
333
+ const closeIdx = sql.indexOf(close, i + 3);
334
+ const end = closeIdx !== -1 ? closeIdx + close.length : sql.length;
335
+ return { type: TokenType.QuotedBlock, end };
336
+ }
337
+ function scanTokenOracle(sql, i) {
338
+ return scanSingleLineComment(sql, i) ?? scanMultiLineComment(sql, i) ?? scanOracleAlternativeQuotedString(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? plainToken(i);
339
+ }
323
340
  var dialectScanners = {
324
341
  dameng: scanTokenAnsi,
325
342
  postgres: scanTokenPostgres,
326
343
  mysql: scanTokenMySQL,
327
344
  mariadb: scanTokenMySQL,
328
345
  sqlite: scanTokenSQLite,
329
- sqlserver: scanTokenSQLServer
346
+ sqlserver: scanTokenSQLServer,
347
+ oracle: scanTokenOracle
330
348
  };
331
349
  function getScanner(dialect) {
332
350
  return dialect ? dialectScanners[dialect] ?? scanTokenAnsi : scanTokenAnsi;
@@ -371,6 +389,7 @@ function blankCommentsAndStrings(sql, dialect) {
371
389
  }
372
390
  return result;
373
391
  }
392
+ var LEADING_SQL_NOISE = /^(?:\s+|--[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)*/;
374
393
  function splitSQLStatements(sql, dialect) {
375
394
  const scanToken = getScanner(dialect);
376
395
  const statements = [];
@@ -401,5 +420,6 @@ export {
401
420
  SafeURL,
402
421
  stripCommentsAndStrings,
403
422
  blankCommentsAndStrings,
423
+ LEADING_SQL_NOISE,
404
424
  splitSQLStatements
405
425
  };
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  hasMutatingKeyword
3
- } from "./chunk-HDPZAIDL.js";
3
+ } from "./chunk-JKYVOIJG.js";
4
4
  import {
5
5
  blankCommentsAndStrings
6
- } from "./chunk-2JL4KCP5.js";
6
+ } from "./chunk-AQKEKC24.js";
7
7
 
8
8
  // src/utils/sql-row-limiter.ts
9
9
  var SQLRowLimiter = class {
@@ -227,6 +227,47 @@ LIMIT ${maxRows}${semicolon}`;
227
227
  }
228
228
  return this.applyTopToQuery(sql, maxRows);
229
229
  }
230
+ /**
231
+ * Apply maxRows to a row-returning query using Oracle's row-limiting clause.
232
+ *
233
+ * The statement is always wrapped in an inline view and capped from the
234
+ * outside: `SELECT * FROM (<sql>) FETCH FIRST n ROWS ONLY`. Wrapping rather
235
+ * than splicing keeps every shape correct with one rule - a set operation
236
+ * (UNION & co.) is capped as a whole, a trailing ORDER BY stays inside the
237
+ * view where Oracle honours it, and a query that already has its own
238
+ * `FETCH FIRST` / `ROWNUM` cap simply returns the smaller of the two. Oracle
239
+ * also accepts a leading CTE (subquery factoring) inside an inline view, so
240
+ * `WITH ... SELECT` needs no special casing.
241
+ */
242
+ static applyMaxRowsForOracle(sql, maxRows) {
243
+ if (!maxRows || !this.isSelectQuery(sql, "oracle") || this.hasOracleForUpdate(sql)) {
244
+ return sql;
245
+ }
246
+ const { sql: sqlWithoutSemicolon } = trimSemicolon(sql);
247
+ return `SELECT * FROM (${sqlWithoutSemicolon}
248
+ ) FETCH FIRST ${maxRows} ROWS ONLY`;
249
+ }
250
+ /**
251
+ * A statement-level `FOR UPDATE` (locking read). Oracle allows neither
252
+ * `FOR UPDATE` inside an inline view nor a row-limiting clause alongside
253
+ * it, so such a statement cannot be capped and is left as written.
254
+ */
255
+ static hasOracleForUpdate(sql) {
256
+ return this.findTopLevelMatch(sql, /\(|\)|\bfor\s+update\b/gi, "first", "oracle") !== null;
257
+ }
258
+ /**
259
+ * Oracle variant of applyMaxRowsWithTruncationProbe. Because the cap always
260
+ * wraps (see applyMaxRowsForOracle), the probe is applied to every
261
+ * row-returning statement; a query whose own cap is tighter than maxRows
262
+ * returns fewer than maxRows + 1 rows and is therefore never flagged.
263
+ * A `FOR UPDATE` statement is never capped (see hasOracleForUpdate).
264
+ */
265
+ static applyMaxRowsForOracleWithTruncationProbe(sql, maxRows) {
266
+ if (!maxRows || !this.isSelectQuery(sql, "oracle") || this.hasOracleForUpdate(sql)) {
267
+ return { sql, probeApplied: false };
268
+ }
269
+ return { sql: this.applyMaxRowsForOracle(sql, maxRows + 1), probeApplied: true };
270
+ }
230
271
  /**
231
272
  * Like applyMaxRows, but requests one probe row (maxRows + 1) whenever the
232
273
  * cap is the binding constraint, so callers can detect truncation exactly
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SafeURL
3
- } from "./chunk-2JL4KCP5.js";
3
+ } from "./chunk-AQKEKC24.js";
4
4
 
5
5
  // src/utils/dsn-obfuscate.ts
6
6
  function parseConnectionInfoFromDSN(dsn) {
@@ -45,6 +45,7 @@ function parseConnectionInfoFromDSN(dsn) {
45
45
  return null;
46
46
  }
47
47
  }
48
+ var REDACTED_DSN = "<redacted DSN>";
48
49
  function obfuscateDSNPassword(dsn) {
49
50
  if (!dsn) {
50
51
  return dsn;
@@ -55,6 +56,9 @@ function obfuscateDSNPassword(dsn) {
55
56
  return dsn;
56
57
  }
57
58
  const url = new SafeURL(dsn);
59
+ if (url.port && !/^\d+$/.test(url.port)) {
60
+ return REDACTED_DSN;
61
+ }
58
62
  if (!url.password) {
59
63
  return dsn;
60
64
  }
@@ -79,7 +83,7 @@ function obfuscateDSNPassword(dsn) {
79
83
  }
80
84
  return result;
81
85
  } catch {
82
- return dsn;
86
+ return REDACTED_DSN;
83
87
  }
84
88
  }
85
89
  function getDatabaseTypeFromDSN(dsn) {
@@ -97,7 +101,8 @@ function protocolToConnectorType(protocol) {
97
101
  "mysql": "mysql",
98
102
  "mariadb": "mariadb",
99
103
  "sqlserver": "sqlserver",
100
- "sqlite": "sqlite"
104
+ "sqlite": "sqlite",
105
+ "oracle": "oracle"
101
106
  };
102
107
  return mapping[protocol];
103
108
  }
@@ -108,6 +113,7 @@ function getDefaultPortForType(type) {
108
113
  "mysql": 3306,
109
114
  "mariadb": 3306,
110
115
  "sqlserver": 1433,
116
+ "oracle": 1521,
111
117
  "sqlite": void 0
112
118
  };
113
119
  return ports[type];
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  stripCommentsAndStrings
3
- } from "./chunk-2JL4KCP5.js";
3
+ } from "./chunk-AQKEKC24.js";
4
4
 
5
5
  // src/utils/allowed-keywords.ts
6
6
  var allowedKeywords = {
@@ -11,7 +11,12 @@ var allowedKeywords = {
11
11
  sqlite: ["select", "with", "explain", "pragma"],
12
12
  // SQL Server has no native EXPLAIN statement; the connector translates a
13
13
  // leading `EXPLAIN` into a SET SHOWPLAN_XML request (see SQLServerConnector).
14
- sqlserver: ["select", "with", "explain"]
14
+ sqlserver: ["select", "with", "explain"],
15
+ // Oracle's native form is `EXPLAIN PLAN FOR <stmt>`; the connector accepts
16
+ // both that and a bare `EXPLAIN <stmt>` and reads the plan back through
17
+ // DBMS_XPLAN (see OracleConnector.explainQuery). EXPLAIN PLAN only parses
18
+ // the statement, it never executes it.
19
+ oracle: ["select", "with", "explain"]
15
20
  };
16
21
  var mutatingKeywords = [
17
22
  "insert",
@@ -57,12 +62,35 @@ var escapeHatchFunctionKeywords = {
57
62
  mysql: ["load_file", "get_lock", "release_lock", "release_all_locks"],
58
63
  mariadb: ["load_file", "get_lock", "release_lock", "release_all_locks"],
59
64
  postgres: ["pg_read_file", "pg_read_binary_file", "pg_ls_dir"],
60
- sqlserver: sqlServerPassThroughKeywords
65
+ sqlserver: sqlServerPassThroughKeywords,
66
+ // Oracle packages a SELECT can call to reach outside the database, matched
67
+ // as `package.member(` (see escapeHatchCallSuffix).
68
+ oracle: [
69
+ "utl_http",
70
+ "utl_tcp",
71
+ "utl_smtp",
72
+ "utl_inaddr",
73
+ "utl_file",
74
+ "dbms_ldap",
75
+ "dbms_sql",
76
+ "dbms_scheduler",
77
+ "dbms_java",
78
+ "dbms_lob",
79
+ "dbms_xslprocessor",
80
+ "dbms_advisor"
81
+ ]
61
82
  };
83
+ var escapeHatchCallSuffix = {
84
+ oracle: "\\s*\\.\\s*[a-z_][a-z0-9_$#]*\\s*\\("
85
+ };
86
+ var DEFAULT_CALL_SUFFIX = "\\s*\\(";
62
87
  var escapeHatchFunctionPatterns = Object.fromEntries(
63
88
  Object.entries(escapeHatchFunctionKeywords).map(([type, keywords]) => [
64
89
  type,
65
- new RegExp(`\\b(?:${keywords.join("|")})\\s*\\(`, "i")
90
+ new RegExp(
91
+ `\\b(?:${keywords.join("|")})${escapeHatchCallSuffix[type] ?? DEFAULT_CALL_SUFFIX}`,
92
+ "i"
93
+ )
66
94
  ])
67
95
  );
68
96
  function hasEscapeHatchFunction(strippedSQL, connectorType) {
@@ -79,7 +107,8 @@ var mutatingPatterns = {
79
107
  mysql: mutatingPatternWithReplace,
80
108
  mariadb: mutatingPatternWithReplace,
81
109
  sqlite: mutatingPatternWithReplace,
82
- sqlserver: mutatingPatternSqlServer
110
+ sqlserver: mutatingPatternSqlServer,
111
+ oracle: mutatingPattern
83
112
  };
84
113
  function hasMutatingKeyword(strippedSQL, connectorType) {
85
114
  return (mutatingPatterns[connectorType] ?? mutatingPattern).test(strippedSQL);
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-FU2ZJE4E.js";
5
5
  import {
6
6
  obfuscateDSNPassword
7
- } from "./chunk-3RJRH3CN.js";
7
+ } from "./chunk-GDZT5PH3.js";
8
8
 
9
9
  // src/connectors/mysql-family-health-check.ts
10
10
  async function getMySQLFamilyHealthCheck(query) {
@@ -9,6 +9,7 @@ function quoteIdentifier(identifier, dbType) {
9
9
  switch (dbType) {
10
10
  case "postgres":
11
11
  case "sqlite":
12
+ case "oracle":
12
13
  return `"${identifier.replace(/"/g, '""')}"`;
13
14
  case "mysql":
14
15
  case "mariadb":
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  SQLRowLimiter,
3
3
  closeQuietly
4
- } from "./chunk-O3PEWKBB.js";
5
- import "./chunk-HDPZAIDL.js";
4
+ } from "./chunk-CJ5QQT7D.js";
5
+ import "./chunk-JKYVOIJG.js";
6
6
  import {
7
7
  ConnectorRegistry,
8
8
  SafeURL,
9
9
  splitSQLStatements
10
- } from "./chunk-2JL4KCP5.js";
10
+ } from "./chunk-AQKEKC24.js";
11
11
 
12
12
  // src/connectors/dameng/index.ts
13
13
  import { randomUUID } from "node:crypto";
package/dist/index.js CHANGED
@@ -18,30 +18,30 @@ import {
18
18
  resolveSourceConfigs,
19
19
  resolveTomlConfigPath,
20
20
  resolveTransport
21
- } from "./chunk-HGU7ILEZ.js";
21
+ } from "./chunk-7OHPTFUJ.js";
22
22
  import {
23
23
  loadConnectors
24
24
  } from "./chunk-WVVMH6FJ.js";
25
25
  import {
26
26
  quoteQualifiedIdentifier
27
- } from "./chunk-JFWX35TB.js";
27
+ } from "./chunk-X2EC47W4.js";
28
28
  import {
29
29
  getDatabaseTypeFromDSN,
30
30
  getDefaultPortForType,
31
31
  parseConnectionInfoFromDSN
32
- } from "./chunk-3RJRH3CN.js";
32
+ } from "./chunk-GDZT5PH3.js";
33
33
  import {
34
34
  allowedKeywords,
35
35
  getFirstKeyword,
36
36
  hasEscapeHatchFunction,
37
37
  isReadOnlySQL,
38
38
  sqlServerDynamicSqlPattern
39
- } from "./chunk-HDPZAIDL.js";
39
+ } from "./chunk-JKYVOIJG.js";
40
40
  import {
41
41
  ConnectorRegistry,
42
42
  splitSQLStatements,
43
43
  stripCommentsAndStrings
44
- } from "./chunk-2JL4KCP5.js";
44
+ } from "./chunk-AQKEKC24.js";
45
45
 
46
46
  // src/server.ts
47
47
  import { McpServer, createMcpHandler } from "@modelcontextprotocol/server";
@@ -1915,7 +1915,7 @@ See documentation for more details on configuring database connections.
1915
1915
  const sources = sourceConfigsData.sources;
1916
1916
  console.error(`Configuration source: ${sourceConfigsData.source}`);
1917
1917
  await connectorManager.connectWithSources(sources);
1918
- const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-WVSFVS35.js");
1918
+ const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-OYHTCTSH.js");
1919
1919
  initializeToolRegistry2({
1920
1920
  sources: sourceConfigsData.sources,
1921
1921
  tools: sourceConfigsData.tools
@@ -2070,12 +2070,13 @@ See documentation for more details on configuring database connections.
2070
2070
 
2071
2071
  // src/index.ts
2072
2072
  var connectorModules = [
2073
- { load: () => import("./postgres-C24SBQ4I.js"), name: "PostgreSQL", driver: "pg" },
2074
- { load: () => import("./sqlserver-KZPPK67Q.js"), name: "SQL Server", driver: "mssql" },
2075
- { load: () => import("./dameng-NKF7NZKE.js"), name: "Dameng", driver: "dmdb" },
2076
- { load: () => import("./sqlite-7BNGDMBZ.js"), name: "SQLite", driver: "node:sqlite" },
2077
- { load: () => import("./mysql-TESH7KKV.js"), name: "MySQL", driver: "mysql2" },
2078
- { load: () => import("./mariadb-MMC6WMEG.js"), name: "MariaDB", driver: "mariadb" }
2073
+ { load: () => import("./postgres-KO5JB4XV.js"), name: "PostgreSQL", driver: "pg" },
2074
+ { load: () => import("./sqlserver-OQFUEISM.js"), name: "SQL Server", driver: "mssql" },
2075
+ { load: () => import("./dameng-DTZZQBDP.js"), name: "Dameng", driver: "dmdb" },
2076
+ { load: () => import("./sqlite-OSP72VCJ.js"), name: "SQLite", driver: "node:sqlite" },
2077
+ { load: () => import("./mysql-XL7JXXWL.js"), name: "MySQL", driver: "mysql2" },
2078
+ { load: () => import("./mariadb-3RX7DLHI.js"), name: "MariaDB", driver: "mariadb" },
2079
+ { load: () => import("./oracle-7KQQDGH7.js"), name: "Oracle", driver: "oracledb" }
2079
2080
  ];
2080
2081
  loadConnectors(connectorModules).then(() => main()).catch((error) => {
2081
2082
  console.error("Fatal error:", error);
@@ -5,24 +5,24 @@ import {
5
5
  parseQueryResultSets,
6
6
  requireDatabaseInDSN,
7
7
  withReadOnlyTransaction
8
- } from "./chunk-4276LPPU.js";
8
+ } from "./chunk-PVBTULUE.js";
9
9
  import "./chunk-FU2ZJE4E.js";
10
10
  import {
11
11
  quoteIdentifier
12
- } from "./chunk-JFWX35TB.js";
12
+ } from "./chunk-X2EC47W4.js";
13
13
  import {
14
14
  obfuscateDSNPassword
15
- } from "./chunk-3RJRH3CN.js";
15
+ } from "./chunk-GDZT5PH3.js";
16
16
  import {
17
17
  SQLRowLimiter,
18
18
  closeQuietly
19
- } from "./chunk-O3PEWKBB.js";
20
- import "./chunk-HDPZAIDL.js";
19
+ } from "./chunk-CJ5QQT7D.js";
20
+ import "./chunk-JKYVOIJG.js";
21
21
  import {
22
22
  ConnectorRegistry,
23
23
  SafeURL,
24
24
  splitSQLStatements
25
- } from "./chunk-2JL4KCP5.js";
25
+ } from "./chunk-AQKEKC24.js";
26
26
 
27
27
  // src/connectors/mariadb/index.ts
28
28
  import * as mariadb from "mariadb";
@@ -6,24 +6,24 @@ import {
6
6
  parseQueryResultSets,
7
7
  requireDatabaseInDSN,
8
8
  withReadOnlyTransaction
9
- } from "./chunk-4276LPPU.js";
9
+ } from "./chunk-PVBTULUE.js";
10
10
  import "./chunk-FU2ZJE4E.js";
11
11
  import {
12
12
  quoteIdentifier
13
- } from "./chunk-JFWX35TB.js";
13
+ } from "./chunk-X2EC47W4.js";
14
14
  import {
15
15
  obfuscateDSNPassword
16
- } from "./chunk-3RJRH3CN.js";
16
+ } from "./chunk-GDZT5PH3.js";
17
17
  import {
18
18
  SQLRowLimiter,
19
19
  closeQuietly
20
- } from "./chunk-O3PEWKBB.js";
21
- import "./chunk-HDPZAIDL.js";
20
+ } from "./chunk-CJ5QQT7D.js";
21
+ import "./chunk-JKYVOIJG.js";
22
22
  import {
23
23
  ConnectorRegistry,
24
24
  SafeURL,
25
25
  splitSQLStatements
26
- } from "./chunk-2JL4KCP5.js";
26
+ } from "./chunk-AQKEKC24.js";
27
27
 
28
28
  // src/connectors/mysql/index.ts
29
29
  import mysql from "mysql2/promise";