dsh-db-tool 1.3.1 → 1.4.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.
- package/README.md +19 -41
- package/README_EN.md +86 -0
- package/dist/adapters/gaussdb/index.js +17 -15
- package/dist/adapters/index.js +2 -2
- package/dist/adapters/sqlite/index.js +54 -9
- package/package.json +7 -8
- package/scripts/build-gaussdb.ps1 +0 -72
- package/scripts/build-gaussdb.sh +0 -53
package/README.md
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
# dsh-db-tool
|
|
2
2
|
|
|
3
|
+
[English](README_EN.md) | 简体中文
|
|
4
|
+
|
|
3
5
|
DSH 社区插件:在聊天中安全操作数据库,配套侧边栏管理台与 `db-admin` skill。架构与交互模式对齐 [dsh-ssh-tunnel](https://github.com/thirsty5034/dsh-ssh-tunnel)。
|
|
4
6
|
|
|
5
7
|
## 功能
|
|
6
8
|
|
|
7
|
-
- **8 种数据库**:MySQL、PostgreSQL、GaussDB
|
|
8
|
-
- **DatabaseManager 单工具多 action**:`list_connections / query / execute / schema / preview / run_script`
|
|
9
|
+
- **8 种数据库**:MySQL、PostgreSQL、GaussDB、SQLite、Redis、MongoDB、Oracle、达梦(DM)
|
|
10
|
+
- **DatabaseManager 单工具多 action**:`list_connections / query / execute / schema / preview / run_script`(`run_script` 在 node:vm 沙箱中执行,60s 超时,仅注入受限 `db.{query,execute}` 句柄)
|
|
9
11
|
- **分级权限**:连接级只读(ro)/读写(rw)+ 项目级授权(`grants.json`:projectPathKey → 连接 → 模式);未授权项目一律拒绝
|
|
10
12
|
- **危险操作确认**:DDL / FLUSHALL / dropDatabase 等先返回 `NEEDS_CONFIRMATION`,对话内(模型经 ask)或 SQL 控制台(弹窗)确认后携一次性 `challengeId`(绑定语句 SHA256、5 分钟过期)重试
|
|
11
13
|
- **审计**:全部执行落 `audit.jsonl`(语句、危险级、是否确认、结果)
|
|
@@ -25,6 +27,10 @@ DSH 社区插件:在聊天中安全操作数据库,配套侧边栏管理台
|
|
|
25
27
|
|
|
26
28
|
## 安装
|
|
27
29
|
|
|
30
|
+
8 种数据库驱动全部随 npm 包分发(SQLite 优先用 Node 内置 `node:sqlite`,可选 `better-sqlite3`;GaussDB 驱动为华为云官方 npm 包 `gaussdb-node`),无需任何构建步骤。GaussDB 认证支持 sha256 与 md5,md5-sha256 混合与 SM3 暂不支持。
|
|
31
|
+
|
|
32
|
+
唯一的一次性交互来自 oracledb:其 install 脚本会被 pnpm 默认拦截并使首次安装报告失败——在 DSH 插件安装界面点 **"Allow these scripts and retry"** 即可完成安装(该脚本仅做 Node 版本检查与横幅打印,批准无风险;批准持久化到 profile,后续升级不再提示)。
|
|
33
|
+
|
|
28
34
|
```bash
|
|
29
35
|
# npm(推荐)
|
|
30
36
|
dsh plugin --profile web add dsh-db-tool
|
|
@@ -36,8 +42,6 @@ dsh plugin --profile web add "dsh-db-tool@github:mengqi1436/dsh-db-tool"
|
|
|
36
42
|
dsh plugin --profile web add "link:E:\path\to\dsh-db-tool"
|
|
37
43
|
```
|
|
38
44
|
|
|
39
|
-
GaussDB 官方驱动未发布 npm,需先构建 vendor:`npm run build:gaussdb`(PowerShell)或 `bash scripts/build-gaussdb.sh`;运行要求 Node ≥ 22.12(产物依赖 p-limit@7 纯 ESM)。oracledb 安装脚本需 `npm approve-scripts oracledb`。mongodb 驱动已 bundle 进发布包(`vendor/mongodb-driver.cjs`,esbuild 构建,`npm run build:mongodb` 可重建),依赖树中不含 mongodb/punycode,详见 [docs/install.md](docs/install.md)。
|
|
40
|
-
|
|
41
45
|
### 桌面端安装
|
|
42
46
|
|
|
43
47
|
DSH 桌面端 0.2.0-rc.2+ 捆绑了 `dsh` 命令,全程无需另装 Node 或 pnpm:
|
|
@@ -46,44 +50,13 @@ DSH 桌面端 0.2.0-rc.2+ 捆绑了 `dsh` 命令,全程无需另装 Node 或 p
|
|
|
46
50
|
2. **安装插件**(必须钉精确版本:`@latest` 会因 release-age 校验回落到旧版):
|
|
47
51
|
|
|
48
52
|
```bash
|
|
49
|
-
dsh plugin --profile desktop add dsh-db-tool@1.
|
|
53
|
+
dsh plugin --profile desktop add dsh-db-tool@1.4.0 --registry=https://registry.npmjs.org/
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
已有 npm 镜像源偏好的用户,可把 `--registry` 替换为自己的镜像地址。
|
|
53
57
|
3. **重启桌面端**生效。
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
- **免补丁路径**:桌面端宿主代码打包在 `app.asar` 内,无法应用 `patch:dsh` 热补丁;但本插件 0.1.9+ 走免补丁路径(mongodb 驱动已 bundle 为 `vendor/mongodb-driver.cjs` 随包发布,依赖树不含 punycode),桌面端可直接安装加载,无需任何补丁。
|
|
58
|
-
- **GaussDB**:npm 发布包不含 GaussDB vendor 驱动。需要 GaussDB 的用户请改从源码安装(`dsh plugin --profile desktop add "dsh-db-tool@github:mengqi1436/dsh-db-tool"`),并在插件目录自行 `npm run build:gaussdb`(要求 Node ≥ 22.12)。
|
|
59
|
-
- **Oracle**:安装后需 `npm approve-scripts oracledb` 放行 oracledb 安装脚本(或按 DSH 插件安装界面的脚本审批提示放行)。
|
|
60
|
-
|
|
61
|
-
## 安全模型
|
|
62
|
-
|
|
63
|
-
- HTTP API 仅同源(`ctx.webServer` prefix `/dsh-db-tool/api`)+ 宿主 Connection 栅栏(`ctx.connection.requestRejection`:Host/Origin + 浏览器 token 鉴权。0.2.0-rc.1 起插件前缀路由不经宿主栅栏,故在 handler 顶层显式应用;宿主无 connection 服务时回退 loopback/Origin trust 校验,等价 ssh-tunnel `http-trust`),body 限 2MB
|
|
64
|
-
- ro 双保险:服务层拦截 + 驱动会话级 `SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLY`(pg/gaussdb)、readonly 打开(SQLite)
|
|
65
|
-
- SQL 参数绑定 + 标识符白名单(`[A-Za-z0-9_$]+` + 引用包裹);MySQL `multipleStatements:false`;Redis 元数据走 `SCAN`(禁 `KEYS`);Mongo 递归拒 `$where`
|
|
66
|
-
- `run_script`:node:vm 独立 context、60s 超时、无 require/process/网络/文件系统,仅注入受限 `db.{query,execute}` 句柄
|
|
67
|
-
- 已知边界:对话内确认为提示级强制 + 审计兜底;DSH 无硬中断通道前,恶意对话仍可能诱导用户确认,请配合最小权限数据库账号使用
|
|
68
|
-
|
|
69
|
-
## Troubleshooting
|
|
70
|
-
|
|
71
|
-
### npm 安装插件导入失败(punycode / resolve.paths)
|
|
72
|
-
|
|
73
|
-
症状:DSH 启动后插件加载报 `failed to import`,伴随 `TypeError: Cannot read properties of null (reading 'Symbol(Symbol.iterator)')`,栈指向 `dsh-app-boot` 的 `routeScoped`。
|
|
74
|
-
|
|
75
|
-
根因:上游 `@deepseek-ai/dsh-app-boot` 对 `createRequire(parent).resolve.paths(name)` 直接做 `for..of`,而 Node 对 core-module 同名包(`punycode` 等)返回 `null`,hoisted profile 下凡依赖树含此类 npm 包的插件都会炸。`0.1.7-rc.2`、`0.2.0-rc.1` 与 `0.2.0-rc.2` 中同型缺陷两处(`0.2.0-rc.2` 的 `lib/index.js` 与 `0.2.0-rc.1` 逐字节相同,尚未修复;锚点行均与 `0.1.7-rc.2` 一致,仅整体行号偏移 1 行):`routeScoped()` 与 `nativePackageDir()`,本补丁一并修复(两处分别独立判定,已打过旧版单处补丁的机器只会补上缺的那处)。
|
|
76
|
-
|
|
77
|
-
一键修复(幂等,应用前自动备份为 `index.js.bak-hotfix`;`--revert` 可还原):
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
npm run patch:dsh # Windows(PowerShell)
|
|
81
|
-
npm run patch:dsh:sh # macOS / Linux
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
脚本自动探测 DSH 安装根(`--dsh-root` 可显式指定);补丁文件按安装版本自动选择:`patches/dsh-app-boot-route-scoped-hotfix.patch`(`0.1.7-rc.2`)、`patches/dsh-app-boot-route-scoped-hotfix-0.2.0-rc.1.patch`(`0.2.0-rc.1`)、`patches/dsh-app-boot-route-scoped-hotfix-0.2.0-rc.2.patch`(`0.2.0-rc.2`,其 `lib/index.js` 与 `0.2.0-rc.1` 逐字节相同,故 hunk 完全一致;均与内建替换路径逐字节等价已验证);不支持的版本会警告(`--force` 覆盖)。上游 issue:<https://github.com/mengqi1436/dsh-db-tool/issues>(占位,待上游仓库开放后替换)。
|
|
85
|
-
|
|
86
|
-
**本插件的免补丁路径(0.1.9+)**:该 bug 的触发条件是依赖树中出现 core-module 同名包,mongodb 链(`mongodb-connection-string-url → whatwg-url → tr46 → punycode`)正是元凶。0.1.9 起 mongodb 驱动经 esbuild bundle 为 `vendor/mongodb-driver.cjs` 随包发布,生产依赖树不再含 mongodb 与 punycode——在**未修复宿主**(含桌面版 app.asar,无法打补丁)上也能正常安装加载。守卫测试 `tests/guard/deps-core-collision.spec.ts` 断言依赖树永不回退(dependencies 白名单 + lockfile 生产树 core 同名包零交集)。`patch:dsh` 仍保留,用于修复**其他**含同类依赖的插件或宿主自身报错。
|
|
59
|
+
mongodb 驱动已 bundle 进发布包(`vendor/mongodb-driver.cjs`),依赖树不含 mongodb/punycode——桌面端宿主打包在 `app.asar` 内、无法应用宿主补丁,本插件免补丁可直接安装加载。
|
|
87
60
|
|
|
88
61
|
## 测试
|
|
89
62
|
|
|
@@ -95,14 +68,19 @@ npx stryker run # 变异测试(范围 lib/guard + lib/manager + lib/store,
|
|
|
95
68
|
|
|
96
69
|
真机冒烟(设了才跑):`DBT_TEST_MYSQL_URL / DBT_TEST_PG_URL / DBT_TEST_REDIS_URL / DBT_TEST_DM_CONNECT / DBT_TEST_MONGO_URL / DBT_TEST_ORACLE_CONNECT`。GaussDB 与 Oracle/Mongo 官方要求均按官方文档实现,未真机验证处以代码内标注为准。
|
|
97
70
|
|
|
98
|
-
##
|
|
71
|
+
## 目录结构
|
|
99
72
|
|
|
100
73
|
```
|
|
101
74
|
lib/ host 插件(store / adapters×8 / guard / manager / http / index)
|
|
102
75
|
client/ 侧边栏单文件产物(client.js,即源码)
|
|
103
76
|
skills/ db-admin skill
|
|
104
|
-
scripts/
|
|
105
|
-
|
|
77
|
+
scripts/ 构建与工具脚本(mongodb 驱动 bundle、DSH 宿主热修复 patch:dsh 等)
|
|
78
|
+
patches/ DSH 宿主 dsh-app-boot 热修复补丁(patch:dsh 按宿主版本选用)
|
|
79
|
+
docs/ 预留(当前为空)
|
|
106
80
|
tests/ vitest(离线 mock + DBT_TEST_* 门控真机)
|
|
107
|
-
vendor/
|
|
81
|
+
vendor/ mongodb-driver.cjs bundle(gitignore,发布经 files 白名单收录)
|
|
108
82
|
```
|
|
83
|
+
|
|
84
|
+
## 许可证
|
|
85
|
+
|
|
86
|
+
MIT
|
package/README_EN.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# dsh-db-tool
|
|
2
|
+
|
|
3
|
+
[简体中文](README.md) | English
|
|
4
|
+
|
|
5
|
+
A DSH community plugin for operating databases safely from chat, with a sidebar management console and the `db-admin` skill. Architecture and interaction patterns follow [dsh-ssh-tunnel](https://github.com/thirsty5034/dsh-ssh-tunnel).
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **8 databases**: MySQL, PostgreSQL, GaussDB, SQLite, Redis, MongoDB, Oracle, and DM (Dameng)
|
|
10
|
+
- **One tool, six actions**: `DatabaseManager` exposes `list_connections / query / execute / schema / preview / run_script` (`run_script` executes in a `node:vm` sandbox with a 60s timeout and only restricted `db.{query,execute}` handles)
|
|
11
|
+
- **Tiered permissions**: per-connection read-only (ro) / read-write (rw) plus project-scoped grants (`grants.json`: projectPathKey → connection → mode); unauthorized projects are always rejected
|
|
12
|
+
- **Dangerous-operation confirmation**: DDL / FLUSHALL / dropDatabase and the like first return `NEEDS_CONFIRMATION`; after confirmation in chat (model via ask) or in the SQL console (dialog), the retry carries a one-time `challengeId` (bound to the statement SHA256, 5-minute TTL)
|
|
13
|
+
- **Auditing**: every execution is appended to `audit.jsonl` (statement, danger level, confirmed or not, outcome)
|
|
14
|
+
- **Sidebar with 4 panels** (dsh-better-sidebar, zh/en): connections, project grants, data browser, SQL console
|
|
15
|
+
- **db-admin skill**: shipped with the plugin, covering dialect cheat sheets for all 8 databases, safety rules, and the confirmation flow
|
|
16
|
+
|
|
17
|
+
## Data Layout
|
|
18
|
+
|
|
19
|
+
`$DSH_HOME/db-tool/` (0700):
|
|
20
|
+
|
|
21
|
+
| File | Contents |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `connections.json` | connection definitions (passwords masked as `***` in `urlSafe`) |
|
|
24
|
+
| `secrets.json` | 0600; password/URL credentials, never returned to the model |
|
|
25
|
+
| `grants.json` | project grants (normalized path → connId → ro/rw) |
|
|
26
|
+
| `audit.jsonl` | append-only audit log |
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
Drivers for all 8 databases ship with the npm package (SQLite prefers the built-in `node:sqlite`, optionally `better-sqlite3`; the GaussDB driver is the official Huawei Cloud npm package `gaussdb-node`) — no build steps of any kind. GaussDB authentication supports sha256 and md5; md5-sha256 hybrid and SM3 are not supported yet.
|
|
31
|
+
|
|
32
|
+
The only one-time interaction comes from oracledb: pnpm blocks its install script by default, which makes the first install report a failure — click **"Allow these scripts and retry"** in the DSH plugin installation UI to finish (the script only checks the Node version and prints a banner, so approving is risk-free; the approval is persisted to the profile and upgrades won't prompt again).
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# npm (recommended)
|
|
36
|
+
dsh plugin --profile web add dsh-db-tool
|
|
37
|
+
|
|
38
|
+
# From GitHub source
|
|
39
|
+
dsh plugin --profile web add "dsh-db-tool@github:mengqi1436/dsh-db-tool"
|
|
40
|
+
|
|
41
|
+
# Local development
|
|
42
|
+
dsh plugin --profile web add "link:E:\path\to\dsh-db-tool"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Desktop installation
|
|
46
|
+
|
|
47
|
+
DSH Desktop 0.2.0-rc.2+ bundles the `dsh` command — no separate Node or pnpm required:
|
|
48
|
+
|
|
49
|
+
1. **First use**: open the desktop menu bar, click **"Manage dsh command"**, and install the bundled CLI, which registers `dsh` on the system PATH.
|
|
50
|
+
2. **Install the plugin** (always pin an exact version: `@latest` falls back to an older release due to release-age checks):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
dsh plugin --profile desktop add dsh-db-tool@1.4.0 --registry=https://registry.npmjs.org/
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If you prefer an npm mirror, replace `--registry` with your mirror URL.
|
|
57
|
+
3. **Restart** the desktop app.
|
|
58
|
+
|
|
59
|
+
The MongoDB driver is bundled into the published package (`vendor/mongodb-driver.cjs`), so the dependency tree contains no mongodb/punycode — the desktop host is packed inside `app.asar` and cannot be host-patched, yet this plugin installs and loads there without any patch.
|
|
60
|
+
|
|
61
|
+
## Testing
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm test # full offline mock suite (incl. e2e-mock end-to-end and adversarial cases)
|
|
65
|
+
npx tsc --noEmit
|
|
66
|
+
npx stryker run # mutation testing (lib/guard + lib/manager + lib/store, reports in reports/mutation/)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Live-database smoke tests (run only when set): `DBT_TEST_MYSQL_URL / DBT_TEST_PG_URL / DBT_TEST_REDIS_URL / DBT_TEST_DM_CONNECT / DBT_TEST_MONGO_URL / DBT_TEST_ORACLE_CONNECT`. GaussDB, Oracle, and Mongo official requirements are implemented per their official docs; anything not verified against live databases is annotated in code.
|
|
70
|
+
|
|
71
|
+
## Project Structure
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
lib/ host plugin (store / adapters×8 / guard / manager / http / index)
|
|
75
|
+
client/ sidebar single-file artifact (client.js — this is the source)
|
|
76
|
+
skills/ db-admin skill
|
|
77
|
+
scripts/ build & tooling scripts (MongoDB driver bundle, DSH host hotfix patch:dsh, etc.)
|
|
78
|
+
patches/ DSH host dsh-app-boot hotfix patches (selected by host version for patch:dsh)
|
|
79
|
+
docs/ reserved (currently empty)
|
|
80
|
+
tests/ vitest (offline mocks + DBT_TEST_*-gated live runs)
|
|
81
|
+
vendor/ mongodb-driver.cjs bundle (gitignored; shipped via the files whitelist)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
MIT
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GaussDB(openGauss)适配器。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* 驱动:npm 包 gaussdb-node(华为云官方组织 HuaweiCloudDeveloper/gaussdb-node,
|
|
5
|
+
* pg 兼容 fork,导出结构同 pg:Pool/Client)。npm 安装即含 GaussDB 支持,无需
|
|
6
|
+
* 额外构建;依赖树(gaussdb-connection-string/gaussdb-pool/gaussdb-protocol/
|
|
7
|
+
* pg-types/pgpass/gaussdb-cloudflare)无 p-limit 等高版本要求,Node ≥ 20 即可。
|
|
8
|
+
*
|
|
9
|
+
* 认证支持(依包内实现核对,未真机验证):
|
|
10
|
+
* - sha256(RFC5802,password_encryption_type=2 默认):已实现——
|
|
11
|
+
* gaussdb-node/lib/crypto/rfc5802.js + client.js _handleAuthSHA256Password、
|
|
12
|
+
* gaussdb-protocol parser AuthenticationSHA256Password(case 10)分支;
|
|
13
|
+
* - md5(标准 PG 形态 md5(md5(password+user)+salt)):已实现(gaussdbMd5PasswordHash);
|
|
14
|
+
* - md5-sha256 混合(type=1)与 SM3(type=3):包内未提供(rfc5802 仅支持 sha256
|
|
15
|
+
* method、无 gm-crypto 依赖)——sha256/md5 已证,md5-sha256 混合与 SM3 未证,
|
|
16
|
+
* 服务端要求这两种认证方式时会握手失败。
|
|
8
17
|
*
|
|
9
18
|
* 已按官方文档对齐(证据见 skills/db-admin/SKILL.md GaussDB 节):
|
|
10
|
-
* - 认证:驱动实现 openGauss SHA256 / MD5-SHA256 / SM3 握手(vendor pg-protocol parser.ts:335-357、
|
|
11
|
-
* rfc5802.js:44),对应服务端 password_encryption_type 默认 2 (sha256);
|
|
12
19
|
* - 默认端口 8000(集中式 DN / 分布式 CN)、ro 会话 `SET SESSION CHARACTERISTICS AS
|
|
13
20
|
* TRANSACTION READ ONLY` 两形态官方支持(rf-dist/gaussdb-08-0400、rf-cent/gaussdb-38-0416);
|
|
14
21
|
* - 已知限制:SERIALIZABLE 功能不支持(等价 REPEATABLE READ,与 PG 的 SSI 行为不同);
|
|
@@ -16,25 +23,20 @@
|
|
|
16
23
|
* information_schema.tables/columns 集中式有官方视图文档、分布式参考无原生视图文档
|
|
17
24
|
* (仅 M-Compatibility 模式),跨库浏览在分布式实例上以真机行为为准。
|
|
18
25
|
*
|
|
19
|
-
* 构建方式:bash scripts/build-gaussdb.sh(或 npm run build:gaussdb)
|
|
20
26
|
* 核心逻辑与 postgresql 适配器共享(sql-shared/pg-like.ts)。
|
|
21
27
|
*/
|
|
22
28
|
import { createRequire } from 'node:module';
|
|
23
29
|
import { createPgLikeAdapter, } from '../sql-shared/pg-like.js';
|
|
24
30
|
const require = createRequire(import.meta.url);
|
|
25
|
-
// packages 布局:pg lib 内 require('../../pg-protocol'/'../../pg-pool') 按 monorepo 假设向上两级解析
|
|
26
|
-
// 相对 createRequire base(lib/adapters/gaussdb/)需三级到项目根:gaussdb/ → adapters/ → lib/ → root
|
|
27
|
-
const VENDOR_PATH = '../../../vendor/gaussdb-pg/packages/pg';
|
|
28
31
|
function loadGaussDriver() {
|
|
29
32
|
try {
|
|
30
|
-
//
|
|
31
|
-
return require(
|
|
33
|
+
// gaussdb-node 为 CJS 包,结构同 pg:module.exports = { Pool, Client, ... }
|
|
34
|
+
return require('gaussdb-node');
|
|
32
35
|
}
|
|
33
36
|
catch (e) {
|
|
34
|
-
//
|
|
37
|
+
// 附原始错误:区分「依赖未安装」与「安装产物损坏」两类故障
|
|
35
38
|
const msg = e instanceof Error ? e.message : String(e);
|
|
36
|
-
throw new Error('GaussDB
|
|
37
|
-
'请先执行 bash scripts/build-gaussdb.sh(或 npm run build:gaussdb)构建 vendor/gaussdb-pg。' +
|
|
39
|
+
throw new Error('GaussDB 驱动未找到:请重新安装插件(npm install gaussdb-node 或重装 dsh-db-tool)。' +
|
|
38
40
|
`原始错误: ${msg}`);
|
|
39
41
|
}
|
|
40
42
|
}
|
package/dist/adapters/index.js
CHANGED
|
@@ -12,8 +12,8 @@ const LOADERS = {
|
|
|
12
12
|
const INSTALL_HINTS = {
|
|
13
13
|
mysql: 'npm install mysql2',
|
|
14
14
|
postgresql: 'npm install pg',
|
|
15
|
-
gaussdb: 'npm
|
|
16
|
-
sqlite: 'npm install better-sqlite3',
|
|
15
|
+
gaussdb: 'npm install gaussdb-node(或重装插件恢复依赖)',
|
|
16
|
+
sqlite: '升级 Node ≥ 22.5(内置 node:sqlite),或 npm install better-sqlite3',
|
|
17
17
|
redis: 'npm install redis',
|
|
18
18
|
mongodb: 'npm run build:mongodb(构建 vendor/mongodb-driver.cjs;发布包已内置产物,仅在缺失时需要)',
|
|
19
19
|
oracle: 'npm install oracledb',
|
|
@@ -1,9 +1,50 @@
|
|
|
1
|
+
import { assertIdent, clampLimit, clampOffset, humanize, normalizeCell, quoteIdent } from '../sql-shared/common.js';
|
|
2
|
+
let cached = null;
|
|
3
|
+
let cachedFor = '';
|
|
1
4
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
5
|
+
* 惰性加载驱动(缓存结果);失败时抛带指引的错误。
|
|
6
|
+
* 可用 DBT_SQLITE_DRIVER=node|better 强制单一路径(不回退),便于测试与显式选型。
|
|
4
7
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
async function loadDriver() {
|
|
9
|
+
const forced = process.env.DBT_SQLITE_DRIVER;
|
|
10
|
+
if (cached && cachedFor === forced)
|
|
11
|
+
return cached;
|
|
12
|
+
const result = await (async () => {
|
|
13
|
+
// 1) node:sqlite:Node 22.5+ 内置(22.5–23.3 需 --experimental-sqlite)。
|
|
14
|
+
if (forced !== 'better') {
|
|
15
|
+
try {
|
|
16
|
+
const mod = await import('node:sqlite');
|
|
17
|
+
const ctor = mod.DatabaseSync;
|
|
18
|
+
if (typeof ctor === 'function') {
|
|
19
|
+
return { ctor: ctor, driver: 'node:sqlite' };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
if (forced === 'node')
|
|
24
|
+
throw new Error(`强制使用 node:sqlite 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// 2) better-sqlite3(optionalDependency,可能被离线环境排除或构建失败)。
|
|
28
|
+
if (forced !== 'node') {
|
|
29
|
+
try {
|
|
30
|
+
const mod = (await import('better-sqlite3'));
|
|
31
|
+
if (mod && typeof mod.default === 'function') {
|
|
32
|
+
return { ctor: mod.default, driver: 'better-sqlite3' };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
if (forced === 'better')
|
|
37
|
+
throw new Error(`强制使用 better-sqlite3 失败:${e instanceof Error ? e.message : String(e)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
throw new Error('SQLite 驱动不可用:当前 Node 无内置 node:sqlite(需 Node ≥ 22.5),且可选依赖 better-sqlite3 未安装或构建失败。' +
|
|
41
|
+
'请升级 Node 到 ≥ 22.5,或在具备编译工具链/预构建的环境执行 `npm install better-sqlite3`;' +
|
|
42
|
+
'其他 7 种数据库不受影响。');
|
|
43
|
+
})();
|
|
44
|
+
cached = result;
|
|
45
|
+
cachedFor = forced;
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
7
48
|
function resolveFile(conn) {
|
|
8
49
|
if (conn.fields) {
|
|
9
50
|
const f = conn.fields;
|
|
@@ -26,22 +67,26 @@ function toQueryResult(raw, columns) {
|
|
|
26
67
|
export async function createSqliteAdapter(conn, opts) {
|
|
27
68
|
const file = resolveFile(conn);
|
|
28
69
|
const readOnly = opts?.mode === 'ro';
|
|
70
|
+
const { ctor, driver } = await loadDriver();
|
|
29
71
|
let db;
|
|
30
72
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
73
|
+
// 选项名按驱动区分:node:sqlite 用 readOnly(打开不存在文件即报错);
|
|
74
|
+
// better-sqlite3 用 readonly + fileMustExist。node:sqlite 不接受显式
|
|
75
|
+
// undefined 作 options,rw 模式统一传空对象。
|
|
76
|
+
db = driver === 'node:sqlite'
|
|
77
|
+
? new ctor(file, readOnly ? { readOnly: true } : {})
|
|
78
|
+
: new ctor(file, readOnly ? { readonly: true, fileMustExist: true } : {});
|
|
34
79
|
}
|
|
35
80
|
catch (e) {
|
|
36
81
|
const msg = e instanceof Error ? e.message : String(e);
|
|
37
|
-
throw new Error(`sqlite 打开失败 (${file}): ${msg}`);
|
|
82
|
+
throw new Error(`sqlite 打开失败 (${file}, 驱动 ${driver}): ${msg}`);
|
|
38
83
|
}
|
|
39
84
|
return {
|
|
40
85
|
kind: 'sqlite',
|
|
41
86
|
connId: conn.meta.id,
|
|
42
87
|
testConnect: () => humanize('sqlite 连接测试', async () => {
|
|
43
88
|
const row = db.prepare('SELECT sqlite_version() AS v').get();
|
|
44
|
-
return { ok: true, serverInfo: `SQLite ${String(row?.v ?? '')}` };
|
|
89
|
+
return { ok: true, serverInfo: `SQLite ${String(row?.v ?? '')} (${driver})` };
|
|
45
90
|
}),
|
|
46
91
|
query: (sql, params) => humanize('sqlite 查询失败', async () => {
|
|
47
92
|
const stmt = db.prepare(sql);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-db-tool",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "DSH community plugin: chat-operated multi-database admin tool with sidebar management, project-scoped grants, ro/rw modes and dangerous-operation confirmation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,8 +29,6 @@
|
|
|
29
29
|
"build": "tsc -p tsconfig.build.json && node -e \"const fs=require('fs'); fs.mkdirSync('dist/script',{recursive:true}); fs.copyFileSync('lib/script/worker.cjs','dist/script/worker.cjs'); console.log('build OK')\" && node scripts/build-mongodb.mjs",
|
|
30
30
|
"build:mongodb": "node scripts/build-mongodb.mjs",
|
|
31
31
|
"prepack": "npm run build",
|
|
32
|
-
"build:gaussdb": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/build-gaussdb.ps1",
|
|
33
|
-
"build:gaussdb:sh": "bash scripts/build-gaussdb.sh",
|
|
34
32
|
"patch:dsh": "powershell -NoProfile -ExecutionPolicy Bypass -File scripts/apply-dsh-hotfix.ps1",
|
|
35
33
|
"patch:dsh:sh": "bash scripts/apply-dsh-hotfix.sh"
|
|
36
34
|
},
|
|
@@ -46,14 +44,17 @@
|
|
|
46
44
|
}
|
|
47
45
|
},
|
|
48
46
|
"dependencies": {
|
|
49
|
-
"better-sqlite3": "^13.0.3",
|
|
50
47
|
"bson": "^7.2.0",
|
|
51
48
|
"dmdb": "^1.0.52452",
|
|
49
|
+
"gaussdb-node": "^0.2.2",
|
|
52
50
|
"mysql2": "^3.24.4",
|
|
53
51
|
"oracledb": "^7.0.1",
|
|
54
52
|
"pg": "^8.23.0",
|
|
55
53
|
"redis": "^6.2.1"
|
|
56
54
|
},
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"better-sqlite3": "^13.0.3"
|
|
57
|
+
},
|
|
57
58
|
"devDependencies": {
|
|
58
59
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
59
60
|
"@stryker-mutator/core": "^10.0.0",
|
|
@@ -71,9 +72,6 @@
|
|
|
71
72
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
72
73
|
"dsh-better-sidebar": ">=0.12.0"
|
|
73
74
|
},
|
|
74
|
-
"allowScripts": {
|
|
75
|
-
"oracledb@7.0.1": true
|
|
76
|
-
},
|
|
77
75
|
"files": [
|
|
78
76
|
"cordis.patch.yml",
|
|
79
77
|
"dist",
|
|
@@ -83,6 +81,7 @@
|
|
|
83
81
|
"patches",
|
|
84
82
|
"scripts",
|
|
85
83
|
"vendor/mongodb-driver.cjs",
|
|
86
|
-
"README.md"
|
|
84
|
+
"README.md",
|
|
85
|
+
"README_EN.md"
|
|
87
86
|
]
|
|
88
87
|
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# Build the official GaussDB (openGauss) Node driver into vendor/.
|
|
2
|
-
# PowerShell version (use when bash/WSL is unavailable on Windows).
|
|
3
|
-
#
|
|
4
|
-
# powershell -ExecutionPolicy Bypass -File scripts/build-gaussdb.ps1 # skip if built
|
|
5
|
-
# powershell -ExecutionPolicy Bypass -File scripts/build-gaussdb.ps1 -Force # rebuild
|
|
6
|
-
#
|
|
7
|
-
# Equivalent to scripts/build-gaussdb.sh. Output: vendor/gaussdb-pg/packages/pg
|
|
8
|
-
# (CJS, pg-like: { Pool, Client, ... }; pg lib requires '../../pg-protocol'/'../../pg-pool',
|
|
9
|
-
# so the monorepo packages/ layout must be preserved)
|
|
10
|
-
# Requires Node >= 16, npm >= 7. Source: gitcode.com/opengauss/openGauss-connector-nodejs (master latest).
|
|
11
|
-
# NOTE: keep this file ASCII-only so Windows PowerShell 5.1 (GBK codepage) can parse it.
|
|
12
|
-
param(
|
|
13
|
-
[switch]$Force
|
|
14
|
-
)
|
|
15
|
-
$ErrorActionPreference = 'Stop'
|
|
16
|
-
|
|
17
|
-
$RepoRoot = Split-Path -Parent $PSScriptRoot
|
|
18
|
-
$Src = Join-Path $RepoRoot 'vendor\gaussdb-src'
|
|
19
|
-
$Dst = Join-Path $RepoRoot 'vendor\gaussdb-pg'
|
|
20
|
-
|
|
21
|
-
if (-not $Force -and (Test-Path (Join-Path $Dst 'packages\pg\package.json'))) {
|
|
22
|
-
Write-Host "[build-gaussdb] $Dst already built, skip (use -Force to rebuild)"
|
|
23
|
-
exit 0
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (Test-Path (Join-Path $Src '.git')) {
|
|
27
|
-
Write-Host "[build-gaussdb] source exists, updating..."
|
|
28
|
-
git -C $Src pull --ff-only
|
|
29
|
-
if ($LASTEXITCODE -ne 0) { throw "git pull failed (exit $LASTEXITCODE)" }
|
|
30
|
-
} else {
|
|
31
|
-
Write-Host "[build-gaussdb] cloning openGauss-connector-nodejs..."
|
|
32
|
-
if (Test-Path $Src) { Remove-Item -Recurse -Force $Src }
|
|
33
|
-
New-Item -ItemType Directory -Force -Path (Join-Path $RepoRoot 'vendor') | Out-Null
|
|
34
|
-
git clone https://gitcode.com/opengauss/openGauss-connector-nodejs $Src
|
|
35
|
-
if ($LASTEXITCODE -ne 0) { throw "git clone failed (exit $LASTEXITCODE)" }
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
Write-Host "[build-gaussdb] npm install && npm run build..."
|
|
39
|
-
Push-Location $Src
|
|
40
|
-
try {
|
|
41
|
-
npm install
|
|
42
|
-
if ($LASTEXITCODE -ne 0) { throw "npm install failed (exit $LASTEXITCODE)" }
|
|
43
|
-
npm run build
|
|
44
|
-
if ($LASTEXITCODE -ne 0) { throw "npm run build failed (exit $LASTEXITCODE)" }
|
|
45
|
-
} finally {
|
|
46
|
-
Pop-Location
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
Write-Host "[build-gaussdb] copying packages layout -> vendor/gaussdb-pg/packages ..."
|
|
50
|
-
if (Test-Path $Dst) { Remove-Item -Recurse -Force $Dst }
|
|
51
|
-
New-Item -ItemType Directory -Force (Join-Path $Dst 'packages') | Out-Null
|
|
52
|
-
Copy-Item -Recurse (Join-Path $Src 'packages\pg') (Join-Path $Dst 'packages\pg')
|
|
53
|
-
Copy-Item -Recurse (Join-Path $Src 'packages\pg-protocol') (Join-Path $Dst 'packages\pg-protocol')
|
|
54
|
-
Copy-Item -Recurse (Join-Path $Src 'packages\pg-pool') (Join-Path $Dst 'packages\pg-pool')
|
|
55
|
-
|
|
56
|
-
$PgDir = Join-Path $Dst 'packages\pg'
|
|
57
|
-
Write-Host "[build-gaussdb] installing runtime deps (incl. undeclared p-limit used by fork)..."
|
|
58
|
-
Push-Location $PgDir
|
|
59
|
-
try {
|
|
60
|
-
npm install --omit=dev --no-audit --no-fund
|
|
61
|
-
if ($LASTEXITCODE -ne 0) { throw "npm install failed (exit $LASTEXITCODE)" }
|
|
62
|
-
npm install p-limit --omit=dev --no-audit --no-fund
|
|
63
|
-
if ($LASTEXITCODE -ne 0) { throw "npm install p-limit failed (exit $LASTEXITCODE)" }
|
|
64
|
-
} finally {
|
|
65
|
-
Pop-Location
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (-not (Test-Path (Join-Path $PgDir 'package.json'))) {
|
|
69
|
-
throw "build output missing package.json, check $Src\packages\pg layout"
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
Write-Host "[build-gaussdb] done: $PgDir"
|
package/scripts/build-gaussdb.sh
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# 构建 GaussDB(openGauss)官方 Node 驱动 vendor 产物。
|
|
3
|
-
#
|
|
4
|
-
# 官方 openGauss-connector-nodejs 未发布 npm,需从源码构建:
|
|
5
|
-
# bash scripts/build-gaussdb.sh # 已有产物则跳过(幂等)
|
|
6
|
-
# bash scripts/build-gaussdb.sh --force # 强制重新拉取并构建
|
|
7
|
-
# npm run build:gaussdb # 等价入口
|
|
8
|
-
#
|
|
9
|
-
# 手动步骤(脚本因网络等失败时可手动执行):
|
|
10
|
-
# git clone https://gitcode.com/opengauss/openGauss-connector-nodejs vendor/gaussdb-src
|
|
11
|
-
# cd vendor/gaussdb-src && npm install && npm run build
|
|
12
|
-
# rm -rf vendor/gaussdb-pg && cp -r packages/pg vendor/gaussdb-pg
|
|
13
|
-
# # 注意保留 vendor/gaussdb-pg/package.json 与 dist/
|
|
14
|
-
#
|
|
15
|
-
# 产物:vendor/gaussdb-pg/packages/pg(CJS,结构同 pg:{ Pool, Client, ... };
|
|
16
|
-
# pg lib 内 require('../../pg-protocol'/'../../pg-pool') 需 packages/ 布局)
|
|
17
|
-
set -euo pipefail
|
|
18
|
-
cd "$(dirname "$0")/.."
|
|
19
|
-
|
|
20
|
-
SRC=vendor/gaussdb-src
|
|
21
|
-
DST=vendor/gaussdb-pg
|
|
22
|
-
FORCE=0
|
|
23
|
-
if [ "${1:-}" = "--force" ]; then FORCE=1; fi
|
|
24
|
-
|
|
25
|
-
if [ "$FORCE" != "1" ] && [ -f "$DST/packages/pg/package.json" ]; then
|
|
26
|
-
echo "[build-gaussdb] 已存在 $DST/packages/pg/package.json,跳过(--force 可强制重建)"
|
|
27
|
-
exit 0
|
|
28
|
-
fi
|
|
29
|
-
|
|
30
|
-
if [ -d "$SRC/.git" ]; then
|
|
31
|
-
echo "[build-gaussdb] 源码已存在,更新..."
|
|
32
|
-
git -C "$SRC" pull --ff-only
|
|
33
|
-
else
|
|
34
|
-
echo "[build-gaussdb] 克隆 openGauss-connector-nodejs..."
|
|
35
|
-
rm -rf "$SRC"
|
|
36
|
-
mkdir -p vendor
|
|
37
|
-
git clone https://gitcode.com/opengauss/openGauss-connector-nodejs "$SRC"
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
echo "[build-gaussdb] 安装依赖并构建..."
|
|
41
|
-
(cd "$SRC" && npm install && npm run build)
|
|
42
|
-
|
|
43
|
-
echo "[build-gaussdb] 拷贝 packages 布局 -> $DST"
|
|
44
|
-
rm -rf "$DST"
|
|
45
|
-
mkdir -p "$DST/packages"
|
|
46
|
-
cp -r "$SRC/packages/pg" "$DST/packages/pg"
|
|
47
|
-
# pg lib 内 require('../../pg-protocol'/'../../pg-pool') 按 monorepo packages/ 布局解析
|
|
48
|
-
cp -r "$SRC/packages/pg-protocol" "$DST/packages/pg-protocol"
|
|
49
|
-
cp -r "$SRC/packages/pg-pool" "$DST/packages/pg-pool"
|
|
50
|
-
# 运行依赖(含 fork 源码已用但 package.json 未声明的 p-limit)
|
|
51
|
-
(cd "$DST/packages/pg" && npm install --omit=dev --no-audit --no-fund && npm install p-limit --omit=dev --no-audit --no-fund)
|
|
52
|
-
|
|
53
|
-
echo "[build-gaussdb] 构建完成 -> $DST/packages/pg"
|