@bytebase/dbhub 0.22.5 → 0.22.6

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  stripCommentsAndStrings
3
- } from "./chunk-C7WEAPX4.js";
3
+ } from "./chunk-JEZZN2YZ.js";
4
4
 
5
5
  // src/utils/sql-row-limiter.ts
6
6
  var SQLRowLimiter = class {
@@ -267,6 +267,20 @@ function scanSingleLineComment(sql, i) {
267
267
  }
268
268
  return { type: TokenType.Comment, end: j };
269
269
  }
270
+ function scanSingleLineCommentMySQL(sql, i) {
271
+ if (sql[i] !== "-" || sql[i + 1] !== "-") {
272
+ return null;
273
+ }
274
+ const next = sql[i + 2];
275
+ if (next !== void 0 && next.charCodeAt(0) > 32 && next.charCodeAt(0) !== 127) {
276
+ return null;
277
+ }
278
+ let j = i;
279
+ while (j < sql.length && sql[j] !== "\n") {
280
+ j++;
281
+ }
282
+ return { type: TokenType.Comment, end: j };
283
+ }
270
284
  function scanMultiLineComment(sql, i) {
271
285
  if (sql[i] !== "/" || sql[i + 1] !== "*") {
272
286
  return null;
@@ -405,7 +419,7 @@ function scanTokenPostgres(sql, i) {
405
419
  return scanSingleLineComment(sql, i) ?? scanNestedMultiLineComment(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? scanDollarQuotedBlock(sql, i) ?? plainToken(i);
406
420
  }
407
421
  function scanTokenMySQL(sql, i) {
408
- return scanSingleLineComment(sql, i) ?? scanMultiLineCommentMySQL(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? scanBacktickQuotedIdentifier(sql, i) ?? plainToken(i);
422
+ return scanSingleLineCommentMySQL(sql, i) ?? scanMultiLineCommentMySQL(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? scanBacktickQuotedIdentifier(sql, i) ?? plainToken(i);
409
423
  }
410
424
  function scanTokenSQLite(sql, i) {
411
425
  return scanSingleLineComment(sql, i) ?? scanMultiLineComment(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? scanBacktickQuotedIdentifier(sql, i) ?? scanBracketQuotedIdentifier(sql, i) ?? plainToken(i);
@@ -8,7 +8,7 @@ import {
8
8
  getDefaultPortForType,
9
9
  parseConnectionInfoFromDSN,
10
10
  stripCommentsAndStrings
11
- } from "./chunk-C7WEAPX4.js";
11
+ } from "./chunk-JEZZN2YZ.js";
12
12
 
13
13
  // src/tools/builtin-tools.ts
14
14
  var BUILTIN_TOOL_EXECUTE_SQL = "execute_sql";
@@ -1802,9 +1802,6 @@ var ConnectorManager = class {
1802
1802
  if (source.query_timeout !== void 0 && connector.id !== "sqlite") {
1803
1803
  config.queryTimeoutSeconds = source.query_timeout;
1804
1804
  }
1805
- if (source.readonly !== void 0) {
1806
- config.readonly = source.readonly;
1807
- }
1808
1805
  if (source.search_path) {
1809
1806
  config.searchPath = source.search_path;
1810
1807
  }
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  resolveSourceConfigs,
16
16
  resolveTomlConfigPath,
17
17
  resolveTransport
18
- } from "./chunk-IOKERPF2.js";
18
+ } from "./chunk-WPXPKRDU.js";
19
19
  import {
20
20
  loadConnectors
21
21
  } from "./chunk-WVVMH6FJ.js";
@@ -29,7 +29,7 @@ import {
29
29
  parseConnectionInfoFromDSN,
30
30
  splitSQLStatements,
31
31
  stripCommentsAndStrings
32
- } from "./chunk-C7WEAPX4.js";
32
+ } from "./chunk-JEZZN2YZ.js";
33
33
 
34
34
  // src/server.ts
35
35
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -134,6 +134,13 @@ var mutatingPatterns = {
134
134
  sqlserver: mutatingPattern
135
135
  };
136
136
  var selectIntoPattern = /\bselect\b[\s\S]+\binto\b/i;
137
+ var sqliteReadOnlyArgPragmas = /* @__PURE__ */ new Set([
138
+ "table_info",
139
+ "index_info",
140
+ "index_list",
141
+ "foreign_key_list"
142
+ ]);
143
+ var sqlitePragmaParenPattern = /^pragma\s+(?:[a-z0-9_]+\.)?([a-z0-9_]+)\s*\(/;
137
144
  var explainAnalyzePattern = /^explain\s+(?:\([^)]*\banalyze\b(?!\s*(?:=\s*)?(?:false|off|0)\b)[^)]*\)|\banalyze\b(?!\s*(?:=\s*)?(?:false|off|0)\b)(?:\s+verbose\b)?)/i;
138
145
  function isReadOnlySQL(sql, connectorType) {
139
146
  return checkReadOnly(
@@ -156,6 +163,15 @@ function checkReadOnly(cleanedSQL, connectorType) {
156
163
  return false;
157
164
  }
158
165
  }
166
+ if (firstWord === "pragma" && connectorType === "sqlite") {
167
+ if (cleanedSQL.includes("=")) {
168
+ return false;
169
+ }
170
+ const parenMatch = cleanedSQL.match(sqlitePragmaParenPattern);
171
+ if (parenMatch && !sqliteReadOnlyArgPragmas.has(parenMatch[1])) {
172
+ return false;
173
+ }
174
+ }
159
175
  if ((firstWord === "select" || firstWord === "with") && selectIntoPattern.test(cleanedSQL)) {
160
176
  return false;
161
177
  }
@@ -1656,7 +1672,7 @@ See documentation for more details on configuring database connections.
1656
1672
  const sources = sourceConfigsData.sources;
1657
1673
  console.error(`Configuration source: ${sourceConfigsData.source}`);
1658
1674
  await connectorManager.connectWithSources(sources);
1659
- const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-L3ZIOUF2.js");
1675
+ const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-IPRZ55YC.js");
1660
1676
  initializeToolRegistry2({
1661
1677
  sources: sourceConfigsData.sources,
1662
1678
  tools: sourceConfigsData.tools
@@ -1810,11 +1826,11 @@ See documentation for more details on configuring database connections.
1810
1826
 
1811
1827
  // src/index.ts
1812
1828
  var connectorModules = [
1813
- { load: () => import("./postgres-EKVOBAH6.js"), name: "PostgreSQL", driver: "pg" },
1814
- { load: () => import("./sqlserver-5TFVBCMA.js"), name: "SQL Server", driver: "mssql" },
1815
- { load: () => import("./sqlite-2GBIF3MQ.js"), name: "SQLite", driver: "node:sqlite" },
1816
- { load: () => import("./mysql-WG6UFALX.js"), name: "MySQL", driver: "mysql2" },
1817
- { load: () => import("./mariadb-ERB4D3RP.js"), name: "MariaDB", driver: "mariadb" }
1829
+ { load: () => import("./postgres-BKBCGDZS.js"), name: "PostgreSQL", driver: "pg" },
1830
+ { load: () => import("./sqlserver-47APXMSF.js"), name: "SQL Server", driver: "mssql" },
1831
+ { load: () => import("./sqlite-CQ4LCBB6.js"), name: "SQLite", driver: "node:sqlite" },
1832
+ { load: () => import("./mysql-EK6TCER5.js"), name: "MySQL", driver: "mysql2" },
1833
+ { load: () => import("./mariadb-JPEKVFEW.js"), name: "MariaDB", driver: "mariadb" }
1818
1834
  ];
1819
1835
  loadConnectors(connectorModules).then(() => main()).catch((error) => {
1820
1836
  console.error("Fatal error:", error);
@@ -7,13 +7,13 @@ import {
7
7
  } from "./chunk-JFWX35TB.js";
8
8
  import {
9
9
  SQLRowLimiter
10
- } from "./chunk-BRXZ5ZQB.js";
10
+ } from "./chunk-BXXHWEQS.js";
11
11
  import {
12
12
  ConnectorRegistry,
13
13
  SafeURL,
14
14
  obfuscateDSNPassword,
15
15
  splitSQLStatements
16
- } from "./chunk-C7WEAPX4.js";
16
+ } from "./chunk-JEZZN2YZ.js";
17
17
 
18
18
  // src/connectors/mariadb/index.ts
19
19
  import * as mariadb from "mariadb";
@@ -466,6 +466,9 @@ var MariaDBConnector = class _MariaDBConnector {
466
466
  }
467
467
  const conn = await this.pool.getConnection();
468
468
  try {
469
+ if (options.readonly) {
470
+ await conn.query("START TRANSACTION READ ONLY");
471
+ }
469
472
  let processedSQL = sql;
470
473
  if (options.maxRows) {
471
474
  const statements = splitSQLStatements(sql, "mariadb");
@@ -492,8 +495,17 @@ var MariaDBConnector = class _MariaDBConnector {
492
495
  }
493
496
  const rows = parseQueryResults(results);
494
497
  const rowCount = extractAffectedRows(results);
498
+ if (options.readonly) {
499
+ await conn.query("COMMIT");
500
+ }
495
501
  return { rows, rowCount };
496
502
  } catch (error) {
503
+ if (options.readonly) {
504
+ try {
505
+ await conn.query("ROLLBACK");
506
+ } catch {
507
+ }
508
+ }
497
509
  console.error("Error executing query:", error);
498
510
  throw error;
499
511
  } finally {
@@ -7,13 +7,13 @@ import {
7
7
  } from "./chunk-JFWX35TB.js";
8
8
  import {
9
9
  SQLRowLimiter
10
- } from "./chunk-BRXZ5ZQB.js";
10
+ } from "./chunk-BXXHWEQS.js";
11
11
  import {
12
12
  ConnectorRegistry,
13
13
  SafeURL,
14
14
  obfuscateDSNPassword,
15
15
  splitSQLStatements
16
- } from "./chunk-C7WEAPX4.js";
16
+ } from "./chunk-JEZZN2YZ.js";
17
17
 
18
18
  // src/connectors/mysql/index.ts
19
19
  import mysql from "mysql2/promise";
@@ -472,6 +472,9 @@ var MySQLConnector = class _MySQLConnector {
472
472
  }
473
473
  const conn = await this.pool.getConnection();
474
474
  try {
475
+ if (options.readonly) {
476
+ await conn.query("START TRANSACTION READ ONLY");
477
+ }
475
478
  let processedSQL = sql;
476
479
  if (options.maxRows) {
477
480
  const statements = splitSQLStatements(sql, "mysql");
@@ -499,8 +502,17 @@ var MySQLConnector = class _MySQLConnector {
499
502
  const [firstResult] = results;
500
503
  const rows = parseQueryResults(firstResult);
501
504
  const rowCount = extractAffectedRows(firstResult);
505
+ if (options.readonly) {
506
+ await conn.query("COMMIT");
507
+ }
502
508
  return { rows, rowCount };
503
509
  } catch (error) {
510
+ if (options.readonly) {
511
+ try {
512
+ await conn.query("ROLLBACK");
513
+ } catch {
514
+ }
515
+ }
504
516
  console.error("Error executing query:", error);
505
517
  throw error;
506
518
  } finally {
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-JFWX35TB.js";
4
4
  import {
5
5
  SQLRowLimiter
6
- } from "./chunk-BRXZ5ZQB.js";
6
+ } from "./chunk-BXXHWEQS.js";
7
7
  import {
8
8
  ConnectorRegistry,
9
9
  SafeURL,
10
10
  obfuscateDSNPassword,
11
11
  splitSQLStatements
12
- } from "./chunk-C7WEAPX4.js";
12
+ } from "./chunk-JEZZN2YZ.js";
13
13
 
14
14
  // src/connectors/postgres/index.ts
15
15
  import fs from "fs";
@@ -491,6 +491,25 @@ var PostgresConnector = class _PostgresConnector {
491
491
  const statements = splitSQLStatements(sql, "postgres");
492
492
  if (statements.length === 1) {
493
493
  const processedStatement = SQLRowLimiter.applyMaxRows(statements[0], options.maxRows);
494
+ if (options.readonly) {
495
+ await client.query("BEGIN READ ONLY");
496
+ try {
497
+ const result2 = parameters && parameters.length > 0 ? await client.query(processedStatement, parameters) : await client.query(processedStatement);
498
+ await client.query("COMMIT");
499
+ return { rows: result2.rows, rowCount: result2.rowCount ?? result2.rows.length };
500
+ } catch (error) {
501
+ try {
502
+ await client.query("ROLLBACK");
503
+ } catch {
504
+ }
505
+ console.error(`[PostgreSQL executeSQL] ERROR: ${error.message}`);
506
+ console.error(`[PostgreSQL executeSQL] SQL: ${processedStatement}`);
507
+ if (parameters && parameters.length > 0) {
508
+ console.error(`[PostgreSQL executeSQL] Parameters: ${JSON.stringify(parameters)}`);
509
+ }
510
+ throw error;
511
+ }
512
+ }
494
513
  let result;
495
514
  if (parameters && parameters.length > 0) {
496
515
  try {
@@ -511,7 +530,7 @@ var PostgresConnector = class _PostgresConnector {
511
530
  }
512
531
  let allRows = [];
513
532
  let totalRowCount = 0;
514
- await client.query("BEGIN");
533
+ await client.query(options.readonly ? "BEGIN READ ONLY" : "BEGIN");
515
534
  try {
516
535
  for (let statement of statements) {
517
536
  const processedStatement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
@@ -525,7 +544,10 @@ var PostgresConnector = class _PostgresConnector {
525
544
  }
526
545
  await client.query("COMMIT");
527
546
  } catch (error) {
528
- await client.query("ROLLBACK");
547
+ try {
548
+ await client.query("ROLLBACK");
549
+ } catch {
550
+ }
529
551
  throw error;
530
552
  }
531
553
  return { rows: allRows, rowCount: totalRowCount };
@@ -2,9 +2,9 @@ import {
2
2
  ToolRegistry,
3
3
  getToolRegistry,
4
4
  initializeToolRegistry
5
- } from "./chunk-IOKERPF2.js";
5
+ } from "./chunk-WPXPKRDU.js";
6
6
  import "./chunk-WVVMH6FJ.js";
7
- import "./chunk-C7WEAPX4.js";
7
+ import "./chunk-JEZZN2YZ.js";
8
8
  export {
9
9
  ToolRegistry,
10
10
  getToolRegistry,
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-JFWX35TB.js";
4
4
  import {
5
5
  SQLRowLimiter
6
- } from "./chunk-BRXZ5ZQB.js";
6
+ } from "./chunk-BXXHWEQS.js";
7
7
  import {
8
8
  ConnectorRegistry,
9
9
  SafeURL,
10
10
  obfuscateDSNPassword,
11
11
  splitSQLStatements
12
- } from "./chunk-C7WEAPX4.js";
12
+ } from "./chunk-JEZZN2YZ.js";
13
13
 
14
14
  // src/connectors/sqlite/suppress-experimental-warning.ts
15
15
  var installed = false;
@@ -319,6 +319,9 @@ var SQLiteConnector = class _SQLiteConnector {
319
319
  if (!this.db) {
320
320
  throw new Error("Not connected to SQLite database");
321
321
  }
322
+ if (options.readonly) {
323
+ this.db.exec("PRAGMA query_only = ON");
324
+ }
322
325
  try {
323
326
  const statements = splitSQLStatements(sql, "sqlite");
324
327
  if (statements.length === 1) {
@@ -375,19 +378,30 @@ var SQLiteConnector = class _SQLiteConnector {
375
378
  }
376
379
  let totalChanges = 0;
377
380
  for (const statement of writeStatements) {
381
+ if (options.readonly) {
382
+ this.db.exec("PRAGMA query_only = ON");
383
+ }
378
384
  const result = this.prepare(statement).run();
379
385
  totalChanges += Number(result.changes);
380
386
  }
381
387
  let allRows = [];
382
388
  for (let statement of readStatements) {
389
+ if (options.readonly) {
390
+ this.db.exec("PRAGMA query_only = ON");
391
+ }
383
392
  statement = SQLRowLimiter.applyMaxRows(statement, options.maxRows);
384
393
  const result = this.prepare(statement).all();
385
394
  allRows.push(...result);
386
395
  }
387
396
  return { rows: allRows, rowCount: totalChanges + allRows.length };
388
397
  }
389
- } catch (error) {
390
- throw error;
398
+ } finally {
399
+ if (options.readonly) {
400
+ try {
401
+ this.db.exec("PRAGMA query_only = OFF");
402
+ } catch {
403
+ }
404
+ }
391
405
  }
392
406
  }
393
407
  };
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-WVVMH6FJ.js";
4
4
  import {
5
5
  SQLRowLimiter
6
- } from "./chunk-BRXZ5ZQB.js";
6
+ } from "./chunk-BXXHWEQS.js";
7
7
  import {
8
8
  ConnectorRegistry,
9
9
  SafeURL,
10
10
  obfuscateDSNPassword,
11
11
  stripCommentsAndStrings
12
- } from "./chunk-C7WEAPX4.js";
12
+ } from "./chunk-JEZZN2YZ.js";
13
13
 
14
14
  // src/connectors/sqlserver/index.ts
15
15
  import sql from "mssql";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebase/dbhub",
3
- "version": "0.22.5",
3
+ "version": "0.22.6",
4
4
  "mcpName": "io.github.bytebase/dbhub",
5
5
  "description": "Minimal, token-efficient Database MCP Server for PostgreSQL, MySQL, SQL Server, SQLite, MariaDB",
6
6
  "repository": {