@zz1996/dbhub-dameng 0.1.7 → 1.2.5-dameng.620
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 +56 -164
- package/dbhub.dameng.toml.example +4 -11
- package/dist/{chunk-SQA2ISDE.js → chunk-2JL4KCP5.js} +21 -119
- package/dist/chunk-3RJRH3CN.js +121 -0
- package/dist/chunk-4276LPPU.js +179 -0
- package/dist/chunk-FU2ZJE4E.js +16 -0
- package/dist/chunk-HDPZAIDL.js +155 -0
- package/dist/{chunk-2A2QF3CS.js → chunk-HGU7ILEZ.js} +215 -68
- package/dist/{chunk-IPK7BYBL.js → chunk-JFWX35TB.js} +0 -1
- package/dist/chunk-O3PEWKBB.js +302 -0
- package/dist/dameng-NKF7NZKE.js +250 -0
- package/dist/index.js +461 -251
- package/dist/{mariadb-7F72IRB4.js → mariadb-MMC6WMEG.js} +86 -53
- package/dist/{mysql-A43SL7UM.js → mysql-TESH7KKV.js} +126 -50
- package/dist/{postgres-VFNLBWMF.js → postgres-C24SBQ4I.js} +154 -36
- package/dist/public/assets/index-BJ-1UrcV.css +1 -0
- package/dist/public/assets/index-BdBaFSHd.js +148 -0
- package/dist/public/assets/postgres-BpcazhJg.svg +22 -0
- package/dist/public/assets/sqlserver-ByfFYYpV.svg +11 -0
- package/dist/public/favicon.svg +57 -0
- package/dist/public/index.html +14 -0
- package/dist/public/logo-full-light.svg +58 -0
- package/dist/{registry-XSX2VZTD.js → registry-WVSFVS35.js} +3 -2
- package/dist/{sqlite-IOUAYHGE.js → sqlite-7BNGDMBZ.js} +57 -42
- package/dist/sqlserver-KZPPK67Q.js +1042 -0
- package/package.json +22 -65
- package/dist/chunk-RTB262PR.js +0 -60
- package/dist/chunk-ZNQTMARG.js +0 -127
- package/dist/dameng-5KYI5JBK.js +0 -898
- package/dist/sqlserver-OE4SFH4B.js +0 -587
- package/docs/README.md +0 -14
- package/docs/dameng-connector-plan.md +0 -143
package/README.md
CHANGED
|
@@ -1,183 +1,75 @@
|
|
|
1
|
-
|
|
2
|
-
> Brought to you by [Bytebase](https://www.bytebase.com/), open-source database DevSecOps platform.
|
|
3
|
-
|
|
4
|
-
> [!NOTE]
|
|
5
|
-
> This package is a local Dameng/DM8 connector fork of Bytebase DBHub. Use
|
|
6
|
-
> `npx -y @zz1996/dbhub-dameng --transport stdio --config ./dbhub.dameng.toml`
|
|
7
|
-
> for Dameng MCP usage.
|
|
8
|
-
>
|
|
9
|
-
> Dameng DSN format: `dameng://user:password@host:5236/schema`. Prefer a
|
|
10
|
-
> least-privilege read-only Dameng account for production or shared databases,
|
|
11
|
-
> even when the `execute_sql` tool is configured with `readonly = true`.
|
|
12
|
-
|
|
13
|
-
<p align="center">
|
|
14
|
-
<a href="https://dbhub.ai/" target="_blank">
|
|
15
|
-
<picture>
|
|
16
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/bytebase/dbhub/main/docs/images/logo/full-dark.svg" width="75%">
|
|
17
|
-
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/bytebase/dbhub/main/docs/images/logo/full-light.svg" width="75%">
|
|
18
|
-
<img src="https://raw.githubusercontent.com/bytebase/dbhub/main/docs/images/logo/full-light.svg" width="75%" alt="DBHub Logo">
|
|
19
|
-
</picture>
|
|
20
|
-
</a>
|
|
21
|
-
</p>
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
+------------------+ +--------------+ +------------------+
|
|
25
|
-
| | | | | |
|
|
26
|
-
| | | | | |
|
|
27
|
-
| Claude Desktop +--->+ +--->+ PostgreSQL |
|
|
28
|
-
| | | | | |
|
|
29
|
-
| Claude Code +--->+ +--->+ SQL Server |
|
|
30
|
-
| | | | | |
|
|
31
|
-
| Cursor +--->+ DBHub +--->+ SQLite |
|
|
32
|
-
| | | | | |
|
|
33
|
-
| VS Code +--->+ +--->+ MySQL |
|
|
34
|
-
| | | | | |
|
|
35
|
-
| Copilot CLI +--->+ +--->+ MariaDB |
|
|
36
|
-
| | | | | |
|
|
37
|
-
| +--->+ +--->+ Dameng / DM8 |
|
|
38
|
-
| | | | | |
|
|
39
|
-
+------------------+ +--------------+ +------------------+
|
|
40
|
-
MCP Clients MCP Server Databases
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
DBHub is a zero-dependency, token efficient MCP server implementing the Model Context Protocol (MCP) server interface. This lightweight gateway allows MCP-compatible clients to connect to and explore different databases:
|
|
44
|
-
|
|
45
|
-
- **Local Development First**: Zero dependency, token efficient with just two MCP tools to maximize context window
|
|
46
|
-
- **Multi-Database**: PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, and Dameng/DM8 through a single interface
|
|
47
|
-
- **Multi-Connection**: Connect to multiple databases simultaneously with TOML configuration
|
|
48
|
-
- **Guardrails**: Read-only mode, row limiting, and query timeout to prevent runaway operations
|
|
49
|
-
- **Secure Access**: SSH tunneling and SSL/TLS encryption
|
|
50
|
-
|
|
51
|
-
## Supported Databases
|
|
52
|
-
|
|
53
|
-
PostgreSQL, MySQL, SQL Server, MariaDB, SQLite, and Dameng/DM8.
|
|
54
|
-
|
|
55
|
-
## MCP Tools
|
|
56
|
-
|
|
57
|
-
DBHub implements MCP tools for database operations:
|
|
58
|
-
|
|
59
|
-
- **[execute_sql](https://dbhub.ai/tools/execute-sql)**: Execute SQL queries with transaction support and safety controls
|
|
60
|
-
- **[search_objects](https://dbhub.ai/tools/search-objects)**: Search and explore database schemas, tables, columns, indexes, and procedures with progressive disclosure
|
|
61
|
-
- **[Custom Tools](https://dbhub.ai/tools/custom-tools)**: Define reusable, parameterized SQL operations in your `dbhub.toml` configuration file
|
|
1
|
+
# DBHub:最小达梦适配
|
|
62
2
|
|
|
63
|
-
|
|
3
|
+
基于 [Bytebase DBHub](https://github.com/bytebase/dbhub) 的薄层 fork。
|
|
4
|
+
默认仍只有 `search_objects` 和 `execute_sql`,保留上游搜索流程和结果格式。
|
|
64
5
|
|
|
65
|
-
|
|
6
|
+
## 使用
|
|
66
7
|
|
|
67
|
-
|
|
8
|
+
Node.js >= 22.5。将 `dbhub.dameng.toml.example` 复制为本地 `dbhub.toml`,
|
|
9
|
+
通过环境变量 `DAMENG_DSN` 提供连接串:
|
|
68
10
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
See the full [Installation Guide](https://dbhub.ai/installation) for detailed instructions.
|
|
72
|
-
|
|
73
|
-
### Quick Start
|
|
74
|
-
|
|
75
|
-
**Docker:**
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
docker run --rm --init \
|
|
79
|
-
--name dbhub \
|
|
80
|
-
--publish 8080:8080 \
|
|
81
|
-
bytebase/dbhub \
|
|
82
|
-
--transport http \
|
|
83
|
-
--port 8080 \
|
|
84
|
-
--dsn "postgres://user:password@localhost:5432/dbname?sslmode=disable"
|
|
11
|
+
```text
|
|
12
|
+
dameng://user:password@host:5236/APP
|
|
85
13
|
```
|
|
86
14
|
|
|
87
|
-
|
|
15
|
+
账号、密码中的特殊字符需要 URL 编码。路径是 schema,名称与数据库保持一致,
|
|
16
|
+
包括大小写。使用最小权限只读账号连接共享或生产库。
|
|
88
17
|
|
|
89
|
-
```
|
|
90
|
-
npx @
|
|
18
|
+
```sh
|
|
19
|
+
npx @zz1996/dbhub-dameng@latest --transport stdio --config ./dbhub.toml
|
|
91
20
|
```
|
|
92
21
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
[[tools]]
|
|
114
|
-
name = "execute_sql"
|
|
115
|
-
source = "dm8_readonly"
|
|
116
|
-
readonly = true
|
|
117
|
-
max_rows = 100
|
|
22
|
+
- 驱动为固定版本 `dmdb`,采用原生连接池、参数绑定和行数限制。
|
|
23
|
+
- 不增加连接重试、超时重建、后台恢复或元数据搜索加速。
|
|
24
|
+
- `connection_timeout`、`query_timeout` 分别传给驱动的 `connectTimeout`、`sessionTimeout`。
|
|
25
|
+
- SQL 结果按语句返回,超过 `max_rows` 时附带 `truncated`。
|
|
26
|
+
- 自定义工具使用 `?` 占位符;参数仅支持单语句。
|
|
27
|
+
- 只读判断复用上游策略,不能代替数据库账号权限;函数副作用不由连接器保证隔离。
|
|
28
|
+
- 本实验覆盖普通 SQL 查询和元数据浏览;不提供达梦 `explain_sql`、`health_check`,
|
|
29
|
+
不支持包含内部分号的匿名 PL/SQL 块。初始化脚本按上游惯例在启动时执行,
|
|
30
|
+
不应依赖它设置连接池每条连接的会话状态,schema 应在 DSN 中指定。
|
|
31
|
+
|
|
32
|
+
## 验证
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pnpm install --frozen-lockfile
|
|
36
|
+
pnpm test:unit
|
|
37
|
+
pnpm build
|
|
38
|
+
pnpm test:build
|
|
39
|
+
# 可选:明确指定本机配置,只读验证真实 DM8(不会执行 init_script)
|
|
40
|
+
node scripts/verify-dameng.mjs /absolute/path/to/dbhub.toml [source-id]
|
|
118
41
|
```
|
|
119
42
|
|
|
120
|
-
|
|
121
|
-
committing credentials, set `DAMENG_DSN` or point to a local TOML file:
|
|
43
|
+
GitHub 检查无需真实库凭据;真实 DM8 验证在本机显式运行。
|
|
122
44
|
|
|
123
|
-
|
|
124
|
-
DAMENG_DSN="dameng://user:password@host:5236/schema" pnpm verify:dameng
|
|
125
|
-
pnpm verify:dameng -- --config ./dbhub.dameng.toml --source dm8_readonly
|
|
126
|
-
```
|
|
45
|
+
## 自动同步和发布
|
|
127
46
|
|
|
128
|
-
|
|
47
|
+
`.github/workflows/dameng-sync-publish.yml` 每天北京时间 10:23 执行,
|
|
48
|
+
也会在 `main` 更新时执行,支持手动运行。
|
|
129
49
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
**Restrict to loopback (recommended for production):**
|
|
135
|
-
|
|
136
|
-
```bash
|
|
137
|
-
npx @bytebase/dbhub@latest --transport http --host 127.0.0.1 --port 8080 --demo
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
> The HTTP transport defaults to `--host 0.0.0.0`, exposing DBHub on every network interface. For production, bind to `127.0.0.1` and front DBHub with a reverse proxy (nginx/Caddy) or firewall — DBHub does not authenticate HTTP clients.
|
|
141
|
-
>
|
|
142
|
-
> The HTTP transport also has built-in DNS-rebinding protection: it only accepts requests whose `Host` is loopback, this machine's own hostname/IPs, or a name you allow via [`--allowed-hosts`](https://dbhub.ai/config/command-line#allowed-hosts). If a client behind a reverse proxy or custom DNS name gets a `403`, add that hostname with `--allowed-hosts`.
|
|
143
|
-
|
|
144
|
-
See [Command-Line Options](https://dbhub.ai/config/command-line) for all available parameters.
|
|
145
|
-
|
|
146
|
-
### Multi-Database Setup
|
|
147
|
-
|
|
148
|
-
Connect to multiple databases simultaneously using TOML configuration files. Perfect for managing production, staging, and development databases from a single DBHub instance.
|
|
149
|
-
|
|
150
|
-
See [Multi-Database Configuration](https://dbhub.ai/config/toml) for complete setup instructions.
|
|
151
|
-
|
|
152
|
-
## Development
|
|
153
|
-
|
|
154
|
-
Requires Node.js >= 22.5.0 (DBHub uses the built-in `node:sqlite` module).
|
|
155
|
-
|
|
156
|
-
```bash
|
|
157
|
-
# Install dependencies
|
|
158
|
-
pnpm install
|
|
159
|
-
|
|
160
|
-
# Run in development mode
|
|
161
|
-
pnpm dev
|
|
162
|
-
|
|
163
|
-
# Build and run for production
|
|
164
|
-
pnpm build && pnpm start --transport stdio --dsn "postgres://user:password@localhost:5432/dbname"
|
|
165
|
-
```
|
|
50
|
+
1. 将最新 `bytebase/dbhub:main` 合并到临时工作区。
|
|
51
|
+
2. 安装锁定依赖,运行单测、完整构建与打包导入检查。
|
|
52
|
+
3. 全部通过后才将合并提交推送到本仓库 `main`,不强推。
|
|
53
|
+
4. 准备发布目录,使用 npm OIDC 发布 `@zz1996/dbhub-dameng`。
|
|
166
54
|
|
|
167
|
-
|
|
55
|
+
冲突、测试或构建失败时停止,不更新远端 `main`,通过 GitHub Actions 失败通知处理。
|
|
56
|
+
发布失败时保留已验证的合并结果,可重新运行;已发布版本会跳过。
|
|
57
|
+
机器人推送不会触发新的 workflow;产生合并提交后显式再次触发本工作流,
|
|
58
|
+
由新提交对应的运行发布,确保 npm provenance 指向实际构建的提交。
|
|
59
|
+
这是检查后直接合并的最小流程,不创建自动审批 PR,也不需要长期 PAT。
|
|
168
60
|
|
|
169
|
-
|
|
61
|
+
源码的 `package.json` 版本号、包名、上游 README 和上游发布工作流保持原样。
|
|
62
|
+
仅打包时设置 fork 身份,版本为 `<上游版本>-dameng.<提交总数>`,发布到 `latest` 标签。
|
|
63
|
+
例如 `1.2.5-dameng.620`;它是独立 fork 版本,不是 Bytebase 官方发行版。
|
|
64
|
+
每次代码合并都会得到新版本,同一提交重跑使用同一个版本号。
|
|
170
65
|
|
|
171
|
-
|
|
172
|
-
<img src="https://contrib.rocks/image?repo=bytebase/dbhub" />
|
|
173
|
-
</a>
|
|
66
|
+
首次配置:
|
|
174
67
|
|
|
175
|
-
|
|
68
|
+
- 在本仓库 Actions 设置中禁用继承的 `npm-publish.yml`、`docker-publish.yml`、
|
|
69
|
+
`mcpb-release.yml`,避免执行 Bytebase 的发布流程;保持文件原样以降低同步冲突。
|
|
70
|
+
- 在 npm 包设置中添加 GitHub Actions Trusted Publisher:用户 `zuozh11`,
|
|
71
|
+
仓库 `dbhub-dameng`,工作流 `dameng-sync-publish.yml`,环境名留空,允许 `npm publish`。
|
|
72
|
+
- workflow 显式申请 `contents: write`、`actions: write` 和 `id-token: write`;无需 npm token。
|
|
73
|
+
- 手动运行时可取消 `publish`,只验证同步和打包。
|
|
176
74
|
|
|
177
|
-
|
|
178
|
-
<picture>
|
|
179
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=bytebase/dbhub&type=date&theme=dark&legend=top-left" />
|
|
180
|
-
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=bytebase/dbhub&type=date&legend=top-left" />
|
|
181
|
-
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=bytebase/dbhub&type=date&legend=top-left" />
|
|
182
|
-
</picture>
|
|
183
|
-
</a>
|
|
75
|
+
旧实现保留在 `dameng-connector` 分支中;新实现不继承其恢复机制。
|
|
@@ -1,20 +1,13 @@
|
|
|
1
|
-
# Example DBHub configuration for the local Dameng/DM8 fork.
|
|
2
|
-
# Do not commit real credentials. Prefer environment variables.
|
|
3
|
-
|
|
4
1
|
[[sources]]
|
|
5
|
-
id = "
|
|
6
|
-
|
|
7
|
-
dsn = "dameng://${DM8_USER}:${DM8_PASSWORD}@${DM8_HOST}:5236/${DM8_SCHEMA}"
|
|
8
|
-
connection_timeout = 5
|
|
9
|
-
query_timeout = 30
|
|
10
|
-
lazy = true
|
|
2
|
+
id = "default"
|
|
3
|
+
dsn = "${DAMENG_DSN}"
|
|
11
4
|
|
|
12
5
|
[[tools]]
|
|
13
6
|
name = "execute_sql"
|
|
14
|
-
source = "
|
|
7
|
+
source = "default"
|
|
15
8
|
readonly = true
|
|
16
9
|
max_rows = 100
|
|
17
10
|
|
|
18
11
|
[[tools]]
|
|
19
12
|
name = "search_objects"
|
|
20
|
-
source = "
|
|
13
|
+
source = "default"
|
|
@@ -91,7 +91,9 @@ var SafeURL = class {
|
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
|
-
const
|
|
94
|
+
const pathStart = urlString.indexOf("/");
|
|
95
|
+
const authorityEnd = pathStart === -1 ? urlString.length : pathStart;
|
|
96
|
+
const atIndex = urlString.lastIndexOf("@", authorityEnd - 1);
|
|
95
97
|
if (atIndex !== -1) {
|
|
96
98
|
const auth = urlString.substring(0, atIndex);
|
|
97
99
|
urlString = urlString.substring(atIndex + 1);
|
|
@@ -143,118 +145,6 @@ var SafeURL = class {
|
|
|
143
145
|
}
|
|
144
146
|
};
|
|
145
147
|
|
|
146
|
-
// src/utils/dsn-obfuscate.ts
|
|
147
|
-
function parseConnectionInfoFromDSN(dsn) {
|
|
148
|
-
if (!dsn) {
|
|
149
|
-
return null;
|
|
150
|
-
}
|
|
151
|
-
try {
|
|
152
|
-
const type = getDatabaseTypeFromDSN(dsn);
|
|
153
|
-
if (typeof type === "undefined") {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
if (type === "sqlite") {
|
|
157
|
-
const prefix = "sqlite:///";
|
|
158
|
-
if (dsn.length > prefix.length) {
|
|
159
|
-
const rawPath = dsn.substring(prefix.length);
|
|
160
|
-
const firstChar = rawPath[0];
|
|
161
|
-
const isWindowsDrive = rawPath.length > 1 && rawPath[1] === ":";
|
|
162
|
-
const isSpecialPath = firstChar === ":" || firstChar === "." || firstChar === "~" || isWindowsDrive;
|
|
163
|
-
return {
|
|
164
|
-
type,
|
|
165
|
-
database: isSpecialPath ? rawPath : "/" + rawPath
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
return { type };
|
|
169
|
-
}
|
|
170
|
-
const url = new SafeURL(dsn);
|
|
171
|
-
const info = { type };
|
|
172
|
-
if (url.hostname) {
|
|
173
|
-
info.host = url.hostname;
|
|
174
|
-
}
|
|
175
|
-
if (url.port) {
|
|
176
|
-
info.port = parseInt(url.port, 10);
|
|
177
|
-
}
|
|
178
|
-
if (url.pathname && url.pathname.length > 1) {
|
|
179
|
-
info.database = url.pathname.substring(1);
|
|
180
|
-
}
|
|
181
|
-
if (url.username) {
|
|
182
|
-
info.user = url.username;
|
|
183
|
-
}
|
|
184
|
-
return info;
|
|
185
|
-
} catch {
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
function obfuscateDSNPassword(dsn) {
|
|
190
|
-
if (!dsn) {
|
|
191
|
-
return dsn;
|
|
192
|
-
}
|
|
193
|
-
try {
|
|
194
|
-
const type = getDatabaseTypeFromDSN(dsn);
|
|
195
|
-
if (type === "sqlite") {
|
|
196
|
-
return dsn;
|
|
197
|
-
}
|
|
198
|
-
const url = new SafeURL(dsn);
|
|
199
|
-
if (!url.password) {
|
|
200
|
-
return dsn;
|
|
201
|
-
}
|
|
202
|
-
const obfuscatedPassword = "*".repeat(Math.min(url.password.length, 8));
|
|
203
|
-
const protocol = dsn.split(":")[0];
|
|
204
|
-
let result;
|
|
205
|
-
if (url.username) {
|
|
206
|
-
result = `${protocol}://${url.username}:${obfuscatedPassword}@${url.hostname}`;
|
|
207
|
-
} else {
|
|
208
|
-
result = `${protocol}://${obfuscatedPassword}@${url.hostname}`;
|
|
209
|
-
}
|
|
210
|
-
if (url.port) {
|
|
211
|
-
result += `:${url.port}`;
|
|
212
|
-
}
|
|
213
|
-
result += url.pathname;
|
|
214
|
-
if (url.searchParams.size > 0) {
|
|
215
|
-
const params = [];
|
|
216
|
-
url.forEachSearchParam((value, key) => {
|
|
217
|
-
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
218
|
-
});
|
|
219
|
-
result += `?${params.join("&")}`;
|
|
220
|
-
}
|
|
221
|
-
return result;
|
|
222
|
-
} catch {
|
|
223
|
-
return dsn;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
function getDatabaseTypeFromDSN(dsn) {
|
|
227
|
-
if (!dsn) {
|
|
228
|
-
return void 0;
|
|
229
|
-
}
|
|
230
|
-
const protocol = dsn.split(":")[0];
|
|
231
|
-
return protocolToConnectorType(protocol);
|
|
232
|
-
}
|
|
233
|
-
function protocolToConnectorType(protocol) {
|
|
234
|
-
const mapping = {
|
|
235
|
-
"postgres": "postgres",
|
|
236
|
-
"postgresql": "postgres",
|
|
237
|
-
"mysql": "mysql",
|
|
238
|
-
"mariadb": "mariadb",
|
|
239
|
-
"sqlserver": "sqlserver",
|
|
240
|
-
"sqlite": "sqlite",
|
|
241
|
-
"dameng": "dameng",
|
|
242
|
-
"dm": "dameng"
|
|
243
|
-
};
|
|
244
|
-
return mapping[protocol];
|
|
245
|
-
}
|
|
246
|
-
function getDefaultPortForType(type) {
|
|
247
|
-
const ports = {
|
|
248
|
-
"postgres": 5432,
|
|
249
|
-
"mysql": 3306,
|
|
250
|
-
"mariadb": 3306,
|
|
251
|
-
"sqlserver": 1433,
|
|
252
|
-
"sqlite": void 0,
|
|
253
|
-
"dameng": 5236
|
|
254
|
-
};
|
|
255
|
-
return ports[type];
|
|
256
|
-
}
|
|
257
|
-
|
|
258
148
|
// src/utils/sql-parser.ts
|
|
259
149
|
var TokenType = { Plain: 0, Comment: 1, QuotedBlock: 2 };
|
|
260
150
|
function plainToken(i) {
|
|
@@ -431,12 +321,12 @@ function scanTokenSQLServer(sql, i) {
|
|
|
431
321
|
return scanSingleLineComment(sql, i) ?? scanMultiLineComment(sql, i) ?? scanSingleQuotedString(sql, i) ?? scanDoubleQuotedString(sql, i) ?? scanBracketQuotedIdentifier(sql, i) ?? plainToken(i);
|
|
432
322
|
}
|
|
433
323
|
var dialectScanners = {
|
|
324
|
+
dameng: scanTokenAnsi,
|
|
434
325
|
postgres: scanTokenPostgres,
|
|
435
326
|
mysql: scanTokenMySQL,
|
|
436
327
|
mariadb: scanTokenMySQL,
|
|
437
328
|
sqlite: scanTokenSQLite,
|
|
438
|
-
sqlserver: scanTokenSQLServer
|
|
439
|
-
dameng: scanTokenAnsi
|
|
329
|
+
sqlserver: scanTokenSQLServer
|
|
440
330
|
};
|
|
441
331
|
function getScanner(dialect) {
|
|
442
332
|
return dialect ? dialectScanners[dialect] ?? scanTokenAnsi : scanTokenAnsi;
|
|
@@ -466,6 +356,21 @@ function stripCommentsAndStrings(sql, dialect) {
|
|
|
466
356
|
}
|
|
467
357
|
return parts.join("");
|
|
468
358
|
}
|
|
359
|
+
function blankCommentsAndStrings(sql, dialect) {
|
|
360
|
+
const scanToken = getScanner(dialect);
|
|
361
|
+
let result = "";
|
|
362
|
+
let i = 0;
|
|
363
|
+
while (i < sql.length) {
|
|
364
|
+
const token = scanToken(sql, i);
|
|
365
|
+
if (token.type === TokenType.Plain) {
|
|
366
|
+
result += sql[i];
|
|
367
|
+
} else {
|
|
368
|
+
result += " ".repeat(token.end - i);
|
|
369
|
+
}
|
|
370
|
+
i = token.end;
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
469
374
|
function splitSQLStatements(sql, dialect) {
|
|
470
375
|
const scanToken = getScanner(dialect);
|
|
471
376
|
const statements = [];
|
|
@@ -494,10 +399,7 @@ function splitSQLStatements(sql, dialect) {
|
|
|
494
399
|
export {
|
|
495
400
|
ConnectorRegistry,
|
|
496
401
|
SafeURL,
|
|
497
|
-
parseConnectionInfoFromDSN,
|
|
498
|
-
obfuscateDSNPassword,
|
|
499
|
-
getDatabaseTypeFromDSN,
|
|
500
|
-
getDefaultPortForType,
|
|
501
402
|
stripCommentsAndStrings,
|
|
403
|
+
blankCommentsAndStrings,
|
|
502
404
|
splitSQLStatements
|
|
503
405
|
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SafeURL
|
|
3
|
+
} from "./chunk-2JL4KCP5.js";
|
|
4
|
+
|
|
5
|
+
// src/utils/dsn-obfuscate.ts
|
|
6
|
+
function parseConnectionInfoFromDSN(dsn) {
|
|
7
|
+
if (!dsn) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const type = getDatabaseTypeFromDSN(dsn);
|
|
12
|
+
if (typeof type === "undefined") {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
if (type === "sqlite") {
|
|
16
|
+
const prefix = "sqlite:///";
|
|
17
|
+
if (dsn.length > prefix.length) {
|
|
18
|
+
const rawPath = dsn.substring(prefix.length);
|
|
19
|
+
const firstChar = rawPath[0];
|
|
20
|
+
const isWindowsDrive = rawPath.length > 1 && rawPath[1] === ":";
|
|
21
|
+
const isSpecialPath = firstChar === ":" || firstChar === "." || firstChar === "~" || isWindowsDrive;
|
|
22
|
+
return {
|
|
23
|
+
type,
|
|
24
|
+
database: isSpecialPath ? rawPath : "/" + rawPath
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { type };
|
|
28
|
+
}
|
|
29
|
+
const url = new SafeURL(dsn);
|
|
30
|
+
const info = { type };
|
|
31
|
+
if (url.hostname) {
|
|
32
|
+
info.host = url.hostname;
|
|
33
|
+
}
|
|
34
|
+
if (url.port) {
|
|
35
|
+
info.port = parseInt(url.port, 10);
|
|
36
|
+
}
|
|
37
|
+
if (url.pathname && url.pathname.length > 1) {
|
|
38
|
+
info.database = url.pathname.substring(1);
|
|
39
|
+
}
|
|
40
|
+
if (url.username) {
|
|
41
|
+
info.user = url.username;
|
|
42
|
+
}
|
|
43
|
+
return info;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function obfuscateDSNPassword(dsn) {
|
|
49
|
+
if (!dsn) {
|
|
50
|
+
return dsn;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const type = getDatabaseTypeFromDSN(dsn);
|
|
54
|
+
if (type === "sqlite") {
|
|
55
|
+
return dsn;
|
|
56
|
+
}
|
|
57
|
+
const url = new SafeURL(dsn);
|
|
58
|
+
if (!url.password) {
|
|
59
|
+
return dsn;
|
|
60
|
+
}
|
|
61
|
+
const obfuscatedPassword = "*".repeat(Math.min(url.password.length, 8));
|
|
62
|
+
const protocol = dsn.split(":")[0];
|
|
63
|
+
let result;
|
|
64
|
+
if (url.username) {
|
|
65
|
+
result = `${protocol}://${url.username}:${obfuscatedPassword}@${url.hostname}`;
|
|
66
|
+
} else {
|
|
67
|
+
result = `${protocol}://${obfuscatedPassword}@${url.hostname}`;
|
|
68
|
+
}
|
|
69
|
+
if (url.port) {
|
|
70
|
+
result += `:${url.port}`;
|
|
71
|
+
}
|
|
72
|
+
result += url.pathname;
|
|
73
|
+
if (url.searchParams.size > 0) {
|
|
74
|
+
const params = [];
|
|
75
|
+
url.forEachSearchParam((value, key) => {
|
|
76
|
+
params.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
77
|
+
});
|
|
78
|
+
result += `?${params.join("&")}`;
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
} catch {
|
|
82
|
+
return dsn;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function getDatabaseTypeFromDSN(dsn) {
|
|
86
|
+
if (!dsn) {
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
const protocol = dsn.split(":")[0];
|
|
90
|
+
return protocolToConnectorType(protocol);
|
|
91
|
+
}
|
|
92
|
+
function protocolToConnectorType(protocol) {
|
|
93
|
+
const mapping = {
|
|
94
|
+
"dameng": "dameng",
|
|
95
|
+
"postgres": "postgres",
|
|
96
|
+
"postgresql": "postgres",
|
|
97
|
+
"mysql": "mysql",
|
|
98
|
+
"mariadb": "mariadb",
|
|
99
|
+
"sqlserver": "sqlserver",
|
|
100
|
+
"sqlite": "sqlite"
|
|
101
|
+
};
|
|
102
|
+
return mapping[protocol];
|
|
103
|
+
}
|
|
104
|
+
function getDefaultPortForType(type) {
|
|
105
|
+
const ports = {
|
|
106
|
+
"dameng": 5236,
|
|
107
|
+
"postgres": 5432,
|
|
108
|
+
"mysql": 3306,
|
|
109
|
+
"mariadb": 3306,
|
|
110
|
+
"sqlserver": 1433,
|
|
111
|
+
"sqlite": void 0
|
|
112
|
+
};
|
|
113
|
+
return ports[type];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
parseConnectionInfoFromDSN,
|
|
118
|
+
obfuscateDSNPassword,
|
|
119
|
+
getDatabaseTypeFromDSN,
|
|
120
|
+
getDefaultPortForType
|
|
121
|
+
};
|