@bytebase/dbhub 0.20.0 → 0.21.1
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/README.md +7 -1
- package/dist/{chunk-25VMLRAQ.js → chunk-6JFMPX62.js} +15 -2
- package/dist/chunk-GWPUVYLO.js +35 -0
- package/dist/chunk-RTB262PR.js +60 -0
- package/dist/{demo-loader-FM5OJVDA.js → demo-loader-PSMTLZ2T.js} +0 -2
- package/dist/index.js +73 -46
- package/dist/mariadb-VGTS4WXE.js +462 -0
- package/dist/mysql-I35IQ2GH.js +469 -0
- package/dist/postgres-4PCNQDGV.js +480 -0
- package/dist/{registry-FOASCI6Y.js → registry-I2JQWFDX.js} +2 -2
- package/dist/sqlite-FSCLCRIH.js +320 -0
- package/dist/sqlserver-JBR6X37Z.js +492 -0
- package/package.json +5 -4
- package/dist/chunk-B6JS6INF.js +0 -3644
- package/dist/chunk-OKXJNFBS.js +0 -380
- package/dist/chunk-WWAWV7DQ.js +0 -72
- package/dist/mariadb-L3YMONWJ.js +0 -17727
- package/dist/mysql-FOCVUTPX.js +0 -15872
- package/dist/postgres-JB3LPXGR.js +0 -5559
- package/dist/sqlite-5LT56F5B.js +0 -1099
- package/dist/sqlserver-LGFLHJHL.js +0 -38500
package/README.md
CHANGED
|
@@ -117,4 +117,10 @@ See [Testing](.claude/skills/testing/SKILL.md) and [Debug](https://dbhub.ai/conf
|
|
|
117
117
|
|
|
118
118
|
## Star History
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
<a href="https://www.star-history.com/?repos=bytebase%2Fdbhub&type=date&legend=top-left">
|
|
121
|
+
<picture>
|
|
122
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=bytebase/dbhub&type=date&theme=dark&legend=top-left" />
|
|
123
|
+
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=bytebase/dbhub&type=date&legend=top-left" />
|
|
124
|
+
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=bytebase/dbhub&type=date&legend=top-left" />
|
|
125
|
+
</picture>
|
|
126
|
+
</a>
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isDriverNotInstalled
|
|
3
|
+
} from "./chunk-GWPUVYLO.js";
|
|
1
4
|
import {
|
|
2
5
|
ConnectorRegistry,
|
|
3
6
|
SafeURL,
|
|
@@ -898,7 +901,7 @@ async function resolveSourceConfigs() {
|
|
|
898
901
|
source.ssh_keepalive_count_max = sshResult.config.keepaliveCountMax;
|
|
899
902
|
}
|
|
900
903
|
if (dsnResult.isDemo) {
|
|
901
|
-
const { getSqliteInMemorySetupSql } = await import("./demo-loader-
|
|
904
|
+
const { getSqliteInMemorySetupSql } = await import("./demo-loader-PSMTLZ2T.js");
|
|
902
905
|
source.init_script = getSqliteInMemorySetupSql();
|
|
903
906
|
}
|
|
904
907
|
return {
|
|
@@ -1314,8 +1317,18 @@ function buildDSNFromSource(source) {
|
|
|
1314
1317
|
}
|
|
1315
1318
|
|
|
1316
1319
|
// src/utils/aws-rds-signer.ts
|
|
1317
|
-
import { Signer } from "@aws-sdk/rds-signer";
|
|
1318
1320
|
async function generateRdsAuthToken(params) {
|
|
1321
|
+
let Signer;
|
|
1322
|
+
try {
|
|
1323
|
+
({ Signer } = await import("@aws-sdk/rds-signer"));
|
|
1324
|
+
} catch (error) {
|
|
1325
|
+
if (isDriverNotInstalled(error, "@aws-sdk/rds-signer")) {
|
|
1326
|
+
throw new Error(
|
|
1327
|
+
'AWS IAM authentication requires the "@aws-sdk/rds-signer" package. Install it with: pnpm add @aws-sdk/rds-signer'
|
|
1328
|
+
);
|
|
1329
|
+
}
|
|
1330
|
+
throw error;
|
|
1331
|
+
}
|
|
1319
1332
|
const signer = new Signer({
|
|
1320
1333
|
hostname: params.hostname,
|
|
1321
1334
|
port: params.port,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/utils/module-loader.ts
|
|
2
|
+
var MISSING_MODULE_RE = /Cannot find (?:package|module) '([^']+)'/;
|
|
3
|
+
function isDriverNotInstalled(err, driver) {
|
|
4
|
+
if (!(err instanceof Error) || !("code" in err) || err.code !== "ERR_MODULE_NOT_FOUND") {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
const match = err.message.match(MISSING_MODULE_RE);
|
|
8
|
+
if (!match) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const missingSpecifier = match[1];
|
|
12
|
+
return missingSpecifier === driver || missingSpecifier.startsWith(`${driver}/`);
|
|
13
|
+
}
|
|
14
|
+
async function loadConnectors(connectorModules) {
|
|
15
|
+
await Promise.all(
|
|
16
|
+
connectorModules.map(async ({ load, name, driver }) => {
|
|
17
|
+
try {
|
|
18
|
+
await load();
|
|
19
|
+
} catch (err) {
|
|
20
|
+
if (isDriverNotInstalled(err, driver)) {
|
|
21
|
+
console.error(
|
|
22
|
+
`Skipping ${name} connector: driver package "${driver}" not installed.`
|
|
23
|
+
);
|
|
24
|
+
} else {
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
isDriverNotInstalled,
|
|
34
|
+
loadConnectors
|
|
35
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// src/utils/multi-statement-result-parser.ts
|
|
2
|
+
function isMetadataObject(element) {
|
|
3
|
+
if (!element || typeof element !== "object" || Array.isArray(element)) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
return "affectedRows" in element || "insertId" in element || "fieldCount" in element || "warningStatus" in element;
|
|
7
|
+
}
|
|
8
|
+
function isMultiStatementResult(results) {
|
|
9
|
+
if (!Array.isArray(results) || results.length === 0) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const firstElement = results[0];
|
|
13
|
+
return isMetadataObject(firstElement) || Array.isArray(firstElement);
|
|
14
|
+
}
|
|
15
|
+
function extractRowsFromMultiStatement(results) {
|
|
16
|
+
if (!Array.isArray(results)) {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const allRows = [];
|
|
20
|
+
for (const result of results) {
|
|
21
|
+
if (Array.isArray(result)) {
|
|
22
|
+
allRows.push(...result);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return allRows;
|
|
26
|
+
}
|
|
27
|
+
function extractAffectedRows(results) {
|
|
28
|
+
if (isMetadataObject(results)) {
|
|
29
|
+
return results.affectedRows || 0;
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(results)) {
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
if (isMultiStatementResult(results)) {
|
|
35
|
+
let totalAffected = 0;
|
|
36
|
+
for (const result of results) {
|
|
37
|
+
if (isMetadataObject(result)) {
|
|
38
|
+
totalAffected += result.affectedRows || 0;
|
|
39
|
+
} else if (Array.isArray(result)) {
|
|
40
|
+
totalAffected += result.length;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return totalAffected;
|
|
44
|
+
}
|
|
45
|
+
return results.length;
|
|
46
|
+
}
|
|
47
|
+
function parseQueryResults(results) {
|
|
48
|
+
if (!Array.isArray(results)) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
if (isMultiStatementResult(results)) {
|
|
52
|
+
return extractRowsFromMultiStatement(results);
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export {
|
|
58
|
+
extractAffectedRows,
|
|
59
|
+
parseQueryResults
|
|
60
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,10 @@ import {
|
|
|
12
12
|
resolveSourceConfigs,
|
|
13
13
|
resolveTomlConfigPath,
|
|
14
14
|
resolveTransport
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6JFMPX62.js";
|
|
16
|
+
import {
|
|
17
|
+
loadConnectors
|
|
18
|
+
} from "./chunk-GWPUVYLO.js";
|
|
16
19
|
import {
|
|
17
20
|
quoteQualifiedIdentifier
|
|
18
21
|
} from "./chunk-JFWX35TB.js";
|
|
@@ -24,7 +27,6 @@ import {
|
|
|
24
27
|
splitSQLStatements,
|
|
25
28
|
stripCommentsAndStrings
|
|
26
29
|
} from "./chunk-C7WEAPX4.js";
|
|
27
|
-
import "./chunk-WWAWV7DQ.js";
|
|
28
30
|
|
|
29
31
|
// src/server.ts
|
|
30
32
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -91,20 +93,76 @@ function createToolSuccessResponse(data, meta = {}) {
|
|
|
91
93
|
|
|
92
94
|
// src/utils/allowed-keywords.ts
|
|
93
95
|
var allowedKeywords = {
|
|
94
|
-
postgres: ["select", "with", "explain", "
|
|
95
|
-
mysql: ["select", "with", "explain", "
|
|
96
|
-
mariadb: ["select", "with", "explain", "
|
|
97
|
-
sqlite: ["select", "with", "explain", "
|
|
96
|
+
postgres: ["select", "with", "explain", "show"],
|
|
97
|
+
mysql: ["select", "with", "explain", "show", "describe", "desc"],
|
|
98
|
+
mariadb: ["select", "with", "explain", "show", "describe", "desc"],
|
|
99
|
+
sqlite: ["select", "with", "explain", "pragma"],
|
|
98
100
|
sqlserver: ["select", "with", "explain", "showplan"]
|
|
99
101
|
};
|
|
102
|
+
var mutatingKeywords = [
|
|
103
|
+
"insert",
|
|
104
|
+
"update",
|
|
105
|
+
"delete",
|
|
106
|
+
"drop",
|
|
107
|
+
"alter",
|
|
108
|
+
"create",
|
|
109
|
+
"truncate",
|
|
110
|
+
"merge",
|
|
111
|
+
"grant",
|
|
112
|
+
"revoke",
|
|
113
|
+
"rename"
|
|
114
|
+
];
|
|
115
|
+
var mutatingPattern = new RegExp(
|
|
116
|
+
`\\b(?:${mutatingKeywords.join("|")})\\b`,
|
|
117
|
+
"i"
|
|
118
|
+
);
|
|
119
|
+
var mutatingPatternWithReplace = new RegExp(
|
|
120
|
+
`\\b(?:${mutatingKeywords.join("|")}|replace\\s+(?:(?:low_priority|delayed)\\s+)?into)\\b`,
|
|
121
|
+
"i"
|
|
122
|
+
);
|
|
123
|
+
var mutatingPatterns = {
|
|
124
|
+
postgres: mutatingPattern,
|
|
125
|
+
mysql: mutatingPatternWithReplace,
|
|
126
|
+
mariadb: mutatingPatternWithReplace,
|
|
127
|
+
sqlite: mutatingPatternWithReplace,
|
|
128
|
+
sqlserver: mutatingPattern
|
|
129
|
+
};
|
|
130
|
+
var selectIntoPattern = /\bselect\b[\s\S]+\binto\b/i;
|
|
131
|
+
var explainAnalyzePattern = /^explain\s+(?:\([^)]*\banalyze\b(?!\s*(?:=\s*)?(?:false|off|0)\b)[^)]*\)|\banalyze\b(?!\s*(?:=\s*)?(?:false|off|0)\b)(?:\s+verbose\b)?)/i;
|
|
100
132
|
function isReadOnlySQL(sql, connectorType) {
|
|
101
|
-
|
|
133
|
+
return checkReadOnly(
|
|
134
|
+
stripCommentsAndStrings(sql, connectorType).trim().toLowerCase(),
|
|
135
|
+
connectorType
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
function checkReadOnly(cleanedSQL, connectorType) {
|
|
102
139
|
if (!cleanedSQL) {
|
|
103
140
|
return false;
|
|
104
141
|
}
|
|
105
|
-
const firstWord = cleanedSQL.
|
|
142
|
+
const firstWord = cleanedSQL.match(/\S+/)?.[0] ?? "";
|
|
106
143
|
const keywordList = allowedKeywords[connectorType] || [];
|
|
107
|
-
|
|
144
|
+
if (!keywordList.includes(firstWord)) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
if (firstWord === "with") {
|
|
148
|
+
const pattern = mutatingPatterns[connectorType] ?? mutatingPattern;
|
|
149
|
+
if (pattern.test(cleanedSQL)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if ((firstWord === "select" || firstWord === "with") && selectIntoPattern.test(cleanedSQL)) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
if (firstWord === "explain") {
|
|
157
|
+
const m = explainAnalyzePattern.exec(cleanedSQL);
|
|
158
|
+
if (m) {
|
|
159
|
+
const afterExplain = cleanedSQL.slice(m[0].length).trim();
|
|
160
|
+
if (afterExplain && !checkReadOnly(afterExplain, connectorType)) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
108
166
|
}
|
|
109
167
|
|
|
110
168
|
// src/requests/store.ts
|
|
@@ -1363,7 +1421,7 @@ See documentation for more details on configuring database connections.
|
|
|
1363
1421
|
const sources = sourceConfigsData.sources;
|
|
1364
1422
|
console.error(`Configuration source: ${sourceConfigsData.source}`);
|
|
1365
1423
|
await connectorManager.connectWithSources(sources);
|
|
1366
|
-
const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-
|
|
1424
|
+
const { initializeToolRegistry: initializeToolRegistry2 } = await import("./registry-I2JQWFDX.js");
|
|
1367
1425
|
initializeToolRegistry2({
|
|
1368
1426
|
sources: sourceConfigsData.sources,
|
|
1369
1427
|
tools: sourceConfigsData.tools
|
|
@@ -1489,44 +1547,13 @@ See documentation for more details on configuring database connections.
|
|
|
1489
1547
|
}
|
|
1490
1548
|
}
|
|
1491
1549
|
|
|
1492
|
-
// src/utils/module-loader.ts
|
|
1493
|
-
var MISSING_MODULE_RE = /Cannot find (?:package|module) '([^']+)'/;
|
|
1494
|
-
function isDriverNotInstalled(err, driver) {
|
|
1495
|
-
if (!(err instanceof Error) || !("code" in err) || err.code !== "ERR_MODULE_NOT_FOUND") {
|
|
1496
|
-
return false;
|
|
1497
|
-
}
|
|
1498
|
-
const match = err.message.match(MISSING_MODULE_RE);
|
|
1499
|
-
if (!match) {
|
|
1500
|
-
return false;
|
|
1501
|
-
}
|
|
1502
|
-
const missingSpecifier = match[1];
|
|
1503
|
-
return missingSpecifier === driver || missingSpecifier.startsWith(`${driver}/`);
|
|
1504
|
-
}
|
|
1505
|
-
async function loadConnectors(connectorModules2) {
|
|
1506
|
-
await Promise.all(
|
|
1507
|
-
connectorModules2.map(async ({ load, name, driver }) => {
|
|
1508
|
-
try {
|
|
1509
|
-
await load();
|
|
1510
|
-
} catch (err) {
|
|
1511
|
-
if (isDriverNotInstalled(err, driver)) {
|
|
1512
|
-
console.error(
|
|
1513
|
-
`Skipping ${name} connector: driver package "${driver}" not installed.`
|
|
1514
|
-
);
|
|
1515
|
-
} else {
|
|
1516
|
-
throw err;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
})
|
|
1520
|
-
);
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
1550
|
// src/index.ts
|
|
1524
1551
|
var connectorModules = [
|
|
1525
|
-
{ load: () => import("./postgres-
|
|
1526
|
-
{ load: () => import("./sqlserver-
|
|
1527
|
-
{ load: () => import("./sqlite-
|
|
1528
|
-
{ load: () => import("./mysql-
|
|
1529
|
-
{ load: () => import("./mariadb-
|
|
1552
|
+
{ load: () => import("./postgres-4PCNQDGV.js"), name: "PostgreSQL", driver: "pg" },
|
|
1553
|
+
{ load: () => import("./sqlserver-JBR6X37Z.js"), name: "SQL Server", driver: "mssql" },
|
|
1554
|
+
{ load: () => import("./sqlite-FSCLCRIH.js"), name: "SQLite", driver: "better-sqlite3" },
|
|
1555
|
+
{ load: () => import("./mysql-I35IQ2GH.js"), name: "MySQL", driver: "mysql2" },
|
|
1556
|
+
{ load: () => import("./mariadb-VGTS4WXE.js"), name: "MariaDB", driver: "mariadb" }
|
|
1530
1557
|
];
|
|
1531
1558
|
loadConnectors(connectorModules).then(() => main()).catch((error) => {
|
|
1532
1559
|
console.error("Fatal error:", error);
|