universal-db-mcp 2.0.0 → 2.2.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 +95 -0
- package/dist/adapters/gaussdb.d.ts +5 -3
- package/dist/adapters/gaussdb.d.ts.map +1 -1
- package/dist/adapters/gaussdb.js +121 -91
- package/dist/adapters/gaussdb.js.map +1 -1
- package/dist/adapters/goldendb.d.ts +5 -3
- package/dist/adapters/goldendb.d.ts.map +1 -1
- package/dist/adapters/goldendb.js +104 -55
- package/dist/adapters/goldendb.js.map +1 -1
- package/dist/adapters/highgo.d.ts +5 -3
- package/dist/adapters/highgo.d.ts.map +1 -1
- package/dist/adapters/highgo.js +123 -90
- package/dist/adapters/highgo.js.map +1 -1
- package/dist/adapters/kingbase.d.ts +5 -3
- package/dist/adapters/kingbase.d.ts.map +1 -1
- package/dist/adapters/kingbase.js +121 -91
- package/dist/adapters/kingbase.js.map +1 -1
- package/dist/adapters/mysql.d.ts +8 -3
- package/dist/adapters/mysql.d.ts.map +1 -1
- package/dist/adapters/mysql.js +112 -54
- package/dist/adapters/mysql.js.map +1 -1
- package/dist/adapters/oceanbase.d.ts +5 -3
- package/dist/adapters/oceanbase.d.ts.map +1 -1
- package/dist/adapters/oceanbase.js +104 -55
- package/dist/adapters/oceanbase.js.map +1 -1
- package/dist/adapters/polardb.d.ts +5 -3
- package/dist/adapters/polardb.d.ts.map +1 -1
- package/dist/adapters/polardb.js +104 -55
- package/dist/adapters/polardb.js.map +1 -1
- package/dist/adapters/postgres.d.ts +8 -3
- package/dist/adapters/postgres.d.ts.map +1 -1
- package/dist/adapters/postgres.js +131 -90
- package/dist/adapters/postgres.js.map +1 -1
- package/dist/adapters/sqlserver.d.ts +5 -3
- package/dist/adapters/sqlserver.d.ts.map +1 -1
- package/dist/adapters/sqlserver.js +113 -102
- package/dist/adapters/sqlserver.js.map +1 -1
- package/dist/adapters/tidb.d.ts +5 -3
- package/dist/adapters/tidb.d.ts.map +1 -1
- package/dist/adapters/tidb.js +109 -55
- package/dist/adapters/tidb.js.map +1 -1
- package/dist/adapters/vastbase.d.ts +5 -3
- package/dist/adapters/vastbase.d.ts.map +1 -1
- package/dist/adapters/vastbase.js +123 -90
- package/dist/adapters/vastbase.js.map +1 -1
- package/dist/core/connection-manager.d.ts +12 -2
- package/dist/core/connection-manager.d.ts.map +1 -1
- package/dist/core/connection-manager.js +39 -8
- package/dist/core/connection-manager.js.map +1 -1
- package/dist/core/database-service.d.ts +53 -4
- package/dist/core/database-service.d.ts.map +1 -1
- package/dist/core/database-service.js +86 -7
- package/dist/core/database-service.js.map +1 -1
- package/dist/http/routes/schema.d.ts.map +1 -1
- package/dist/http/routes/schema.js +118 -7
- package/dist/http/routes/schema.js.map +1 -1
- package/dist/mcp/mcp-server.d.ts +3 -1
- package/dist/mcp/mcp-server.d.ts.map +1 -1
- package/dist/mcp/mcp-server.js +58 -8
- package/dist/mcp/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
|
|
28
28
|
✅ **开箱即用** - 无需复杂配置,一行命令启动
|
|
29
29
|
|
|
30
|
+
✅ **智能缓存** - Schema 信息自动缓存,大幅提升大型数据库的响应速度
|
|
31
|
+
|
|
30
32
|
## 🌐 双模式支持
|
|
31
33
|
|
|
32
34
|
本项目支持两种运行模式:
|
|
@@ -719,6 +721,99 @@ Claude 会自动调用数据库工具完成查询!
|
|
|
719
721
|
|
|
720
722
|
⚠️ **警告**:启用写入模式后,Claude 可以修改你的数据库。请仅在开发环境使用,或确保你完全理解操作的后果。
|
|
721
723
|
|
|
724
|
+
## 📦 Schema 缓存
|
|
725
|
+
|
|
726
|
+
为了提升大型数据库的性能,本项目实现了智能 Schema 缓存机制。
|
|
727
|
+
|
|
728
|
+
### 缓存特性
|
|
729
|
+
|
|
730
|
+
- **自动缓存**: 首次获取 Schema 后自动缓存,后续请求直接返回缓存数据
|
|
731
|
+
- **默认 TTL**: 缓存有效期为 5 分钟,过期后自动刷新
|
|
732
|
+
- **强制刷新**: 支持手动强制刷新缓存,获取最新的数据库结构
|
|
733
|
+
- **缓存统计**: 提供缓存命中率等统计信息,便于监控和调优
|
|
734
|
+
|
|
735
|
+
### MCP 模式
|
|
736
|
+
|
|
737
|
+
在 MCP 模式下,新增了以下工具:
|
|
738
|
+
|
|
739
|
+
| 工具名 | 描述 |
|
|
740
|
+
|--------|------|
|
|
741
|
+
| `get_schema` | 获取数据库结构(支持 `forceRefresh` 参数强制刷新) |
|
|
742
|
+
| `get_table_info` | 获取表信息(支持 `forceRefresh` 参数强制刷新) |
|
|
743
|
+
| `clear_cache` | 清除 Schema 缓存 |
|
|
744
|
+
|
|
745
|
+
**使用示例**(在 Claude Desktop 中):
|
|
746
|
+
- "获取数据库结构" - 使用缓存
|
|
747
|
+
- "强制刷新数据库结构" - 忽略缓存,重新获取
|
|
748
|
+
- "清除 Schema 缓存" - 手动清除缓存
|
|
749
|
+
|
|
750
|
+
### HTTP API 模式
|
|
751
|
+
|
|
752
|
+
在 HTTP API 模式下,Schema 相关端点支持 `forceRefresh` 参数:
|
|
753
|
+
|
|
754
|
+
```bash
|
|
755
|
+
# 使用缓存(默认,推荐)
|
|
756
|
+
curl "http://localhost:3000/api/schema?sessionId=xxx"
|
|
757
|
+
|
|
758
|
+
# 强制刷新缓存
|
|
759
|
+
curl "http://localhost:3000/api/schema?sessionId=xxx&forceRefresh=true"
|
|
760
|
+
|
|
761
|
+
# 清除缓存
|
|
762
|
+
curl -X DELETE "http://localhost:3000/api/cache?sessionId=xxx"
|
|
763
|
+
|
|
764
|
+
# 查看缓存状态
|
|
765
|
+
curl "http://localhost:3000/api/cache/status?sessionId=xxx"
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
### 性能提升
|
|
769
|
+
|
|
770
|
+
对于表数量较多的数据库,Schema 缓存可以显著提升性能:
|
|
771
|
+
|
|
772
|
+
| 场景 | 无缓存 | 有缓存 | 提升 |
|
|
773
|
+
|------|--------|--------|------|
|
|
774
|
+
| 100 张表 | ~2-5 秒 | <10 毫秒 | 200-500x |
|
|
775
|
+
| 500 张表 | ~10-30 秒 | <10 毫秒 | 1000-3000x |
|
|
776
|
+
| 1000+ 张表 | 可能超时 | <10 毫秒 | ∞ |
|
|
777
|
+
|
|
778
|
+
### 批量查询优化
|
|
779
|
+
|
|
780
|
+
除了缓存机制,本项目还对 Schema 获取进行了批量查询优化:
|
|
781
|
+
|
|
782
|
+
**优化前(N+1 查询问题)**:
|
|
783
|
+
```
|
|
784
|
+
100 张表 = 1次获取表列表 + 100次获取列信息 + 100次获取主键 + 100次获取索引 + 100次获取行数
|
|
785
|
+
= 401 次数据库查询
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
**优化后(批量查询)**:
|
|
789
|
+
```
|
|
790
|
+
100 张表 = 1次获取所有列 + 1次获取所有主键 + 1次获取所有索引 + 1次获取所有行数
|
|
791
|
+
= 4 次数据库查询
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
**首次加载性能提升**:
|
|
795
|
+
|
|
796
|
+
| 表数量 | 优化前 | 优化后 | 提升 |
|
|
797
|
+
|--------|--------|--------|------|
|
|
798
|
+
| 50 张表 | ~5 秒 | ~200 毫秒 | 25x |
|
|
799
|
+
| 100 张表 | ~10 秒 | ~300 毫秒 | 33x |
|
|
800
|
+
| 500 张表 | ~50 秒 | ~500 毫秒 | 100x |
|
|
801
|
+
|
|
802
|
+
已优化的数据库适配器:
|
|
803
|
+
- MySQL、TiDB、OceanBase、PolarDB、GoldenDB(MySQL 兼容)
|
|
804
|
+
- PostgreSQL、KingbaseES、GaussDB、Vastbase、HighGo(PostgreSQL 兼容)
|
|
805
|
+
- SQL Server
|
|
806
|
+
|
|
807
|
+
共 **11 个**适配器已完成批量查询优化。
|
|
808
|
+
|
|
809
|
+
### 何时需要刷新缓存
|
|
810
|
+
|
|
811
|
+
以下情况建议强制刷新或清除缓存:
|
|
812
|
+
- 新增或删除了表
|
|
813
|
+
- 修改了表结构(新增/删除/修改列)
|
|
814
|
+
- 新增或删除了索引
|
|
815
|
+
- 数据库版本升级后
|
|
816
|
+
|
|
722
817
|
## 🏗️ 架构设计
|
|
723
818
|
|
|
724
819
|
### 双模式架构
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* GaussDB / OpenGauss 数据库适配器
|
|
3
3
|
* 使用 pg 驱动实现 DbAdapter 接口
|
|
4
4
|
* GaussDB 和 OpenGauss 基于 PostgreSQL 开发,兼容 PostgreSQL 协议
|
|
5
|
+
*
|
|
6
|
+
* 性能优化:使用批量查询获取 Schema 信息,避免 N+1 查询问题
|
|
5
7
|
*/
|
|
6
8
|
import type { DbAdapter, QueryResult, SchemaInfo } from '../types/adapter.js';
|
|
7
9
|
export declare class GaussDBAdapter implements DbAdapter {
|
|
@@ -27,13 +29,13 @@ export declare class GaussDBAdapter implements DbAdapter {
|
|
|
27
29
|
*/
|
|
28
30
|
executeQuery(query: string, params?: unknown[]): Promise<QueryResult>;
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
32
|
+
* 获取数据库结构信息(批量查询优化版本)
|
|
31
33
|
*/
|
|
32
34
|
getSchema(): Promise<SchemaInfo>;
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
36
|
+
* 组装 Schema 信息
|
|
35
37
|
*/
|
|
36
|
-
private
|
|
38
|
+
private assembleSchema;
|
|
37
39
|
/**
|
|
38
40
|
* 检查是否为写操作
|
|
39
41
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gaussdb.d.ts","sourceRoot":"","sources":["../../src/adapters/gaussdb.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gaussdb.d.ts","sourceRoot":"","sources":["../../src/adapters/gaussdb.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,UAAU,EAIX,MAAM,qBAAqB,CAAC;AAK7B,qBAAa,cAAe,YAAW,SAAS;IAC9C,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,MAAM,CAMZ;gBAEU,MAAM,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAID;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IA8B3E;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IA4FtC;;OAEG;IACH,OAAO,CAAC,cAAc;IAuGtB;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAGzC"}
|
package/dist/adapters/gaussdb.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* GaussDB / OpenGauss 数据库适配器
|
|
3
3
|
* 使用 pg 驱动实现 DbAdapter 接口
|
|
4
4
|
* GaussDB 和 OpenGauss 基于 PostgreSQL 开发,兼容 PostgreSQL 协议
|
|
5
|
+
*
|
|
6
|
+
* 性能优化:使用批量查询获取 Schema 信息,避免 N+1 查询问题
|
|
5
7
|
*/
|
|
6
8
|
import pg from 'pg';
|
|
7
9
|
import { isWriteOperation as checkWriteOperation } from '../utils/safety.js';
|
|
@@ -70,7 +72,7 @@ export class GaussDBAdapter {
|
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
/**
|
|
73
|
-
*
|
|
75
|
+
* 获取数据库结构信息(批量查询优化版本)
|
|
74
76
|
*/
|
|
75
77
|
async getSchema() {
|
|
76
78
|
if (!this.client) {
|
|
@@ -83,122 +85,150 @@ export class GaussDBAdapter {
|
|
|
83
85
|
// 获取当前数据库名
|
|
84
86
|
const dbResult = await this.client.query('SELECT current_database()');
|
|
85
87
|
const databaseName = dbResult.rows[0]?.current_database || this.config.database || 'unknown';
|
|
86
|
-
//
|
|
87
|
-
const
|
|
88
|
-
SELECT
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
// 批量获取所有表的列信息
|
|
89
|
+
const allColumnsResult = await this.client.query(`
|
|
90
|
+
SELECT
|
|
91
|
+
c.table_name,
|
|
92
|
+
c.column_name,
|
|
93
|
+
c.data_type,
|
|
94
|
+
c.is_nullable,
|
|
95
|
+
c.column_default,
|
|
96
|
+
c.character_maximum_length,
|
|
97
|
+
c.numeric_precision,
|
|
98
|
+
c.numeric_scale,
|
|
99
|
+
c.ordinal_position
|
|
100
|
+
FROM information_schema.columns c
|
|
101
|
+
JOIN information_schema.tables t
|
|
102
|
+
ON c.table_schema = t.table_schema AND c.table_name = t.table_name
|
|
103
|
+
WHERE c.table_schema = 'public'
|
|
104
|
+
AND t.table_type = 'BASE TABLE'
|
|
105
|
+
ORDER BY c.table_name, c.ordinal_position
|
|
93
106
|
`);
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
// 批量获取所有表的主键信息
|
|
108
|
+
const allPrimaryKeysResult = await this.client.query(`
|
|
109
|
+
SELECT
|
|
110
|
+
t.relname as table_name,
|
|
111
|
+
a.attname as column_name
|
|
112
|
+
FROM pg_index i
|
|
113
|
+
JOIN pg_class t ON t.oid = i.indrelid
|
|
114
|
+
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(i.indkey)
|
|
115
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
116
|
+
WHERE i.indisprimary
|
|
117
|
+
AND n.nspname = 'public'
|
|
118
|
+
ORDER BY t.relname, a.attnum
|
|
119
|
+
`);
|
|
120
|
+
// 批量获取所有表的索引信息
|
|
121
|
+
const allIndexesResult = await this.client.query(`
|
|
122
|
+
SELECT
|
|
123
|
+
t.relname as table_name,
|
|
124
|
+
i.relname as index_name,
|
|
125
|
+
a.attname as column_name,
|
|
126
|
+
ix.indisunique as is_unique
|
|
127
|
+
FROM pg_class t
|
|
128
|
+
JOIN pg_index ix ON t.oid = ix.indrelid
|
|
129
|
+
JOIN pg_class i ON i.oid = ix.indexrelid
|
|
130
|
+
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
|
|
131
|
+
JOIN pg_namespace n ON n.oid = t.relnamespace
|
|
132
|
+
WHERE t.relkind = 'r'
|
|
133
|
+
AND n.nspname = 'public'
|
|
134
|
+
AND NOT ix.indisprimary
|
|
135
|
+
ORDER BY t.relname, i.relname, a.attnum
|
|
136
|
+
`);
|
|
137
|
+
// 批量获取所有表的行数估算
|
|
138
|
+
const allStatsResult = await this.client.query(`
|
|
139
|
+
SELECT
|
|
140
|
+
c.relname as table_name,
|
|
141
|
+
c.reltuples::bigint as estimated_rows
|
|
142
|
+
FROM pg_class c
|
|
143
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
144
|
+
WHERE c.relkind = 'r'
|
|
145
|
+
AND n.nspname = 'public'
|
|
146
|
+
`);
|
|
147
|
+
return this.assembleSchema(databaseName, version, allColumnsResult.rows, allPrimaryKeysResult.rows, allIndexesResult.rows, allStatsResult.rows);
|
|
106
148
|
}
|
|
107
149
|
catch (error) {
|
|
108
150
|
throw new Error(`获取数据库结构失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
109
151
|
}
|
|
110
152
|
}
|
|
111
153
|
/**
|
|
112
|
-
*
|
|
154
|
+
* 组装 Schema 信息
|
|
113
155
|
*/
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
column_name,
|
|
122
|
-
data_type,
|
|
123
|
-
is_nullable,
|
|
124
|
-
column_default,
|
|
125
|
-
character_maximum_length,
|
|
126
|
-
numeric_precision,
|
|
127
|
-
numeric_scale
|
|
128
|
-
FROM information_schema.columns
|
|
129
|
-
WHERE table_schema = 'public'
|
|
130
|
-
AND table_name = $1
|
|
131
|
-
ORDER BY ordinal_position
|
|
132
|
-
`, [tableName]);
|
|
133
|
-
const columnInfos = columnsResult.rows.map((col) => {
|
|
156
|
+
assembleSchema(databaseName, version, allColumns, allPrimaryKeys, allIndexes, allStats) {
|
|
157
|
+
const columnsByTable = new Map();
|
|
158
|
+
for (const col of allColumns) {
|
|
159
|
+
const tableName = col.table_name;
|
|
160
|
+
if (!columnsByTable.has(tableName)) {
|
|
161
|
+
columnsByTable.set(tableName, []);
|
|
162
|
+
}
|
|
134
163
|
let dataType = col.data_type;
|
|
135
|
-
// 添加长度/精度信息
|
|
136
164
|
if (col.character_maximum_length) {
|
|
137
165
|
dataType += `(${col.character_maximum_length})`;
|
|
138
166
|
}
|
|
139
167
|
else if (col.numeric_precision) {
|
|
140
168
|
dataType += `(${col.numeric_precision}${col.numeric_scale ? `,${col.numeric_scale}` : ''})`;
|
|
141
169
|
}
|
|
142
|
-
|
|
170
|
+
columnsByTable.get(tableName).push({
|
|
143
171
|
name: col.column_name,
|
|
144
172
|
type: dataType,
|
|
145
173
|
nullable: col.is_nullable === 'YES',
|
|
146
174
|
defaultValue: col.column_default || undefined,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
SELECT
|
|
161
|
-
i.relname as index_name,
|
|
162
|
-
a.attname as column_name,
|
|
163
|
-
ix.indisunique as is_unique
|
|
164
|
-
FROM pg_class t
|
|
165
|
-
JOIN pg_index ix ON t.oid = ix.indrelid
|
|
166
|
-
JOIN pg_class i ON i.oid = ix.indexrelid
|
|
167
|
-
JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
|
|
168
|
-
WHERE t.relname = $1
|
|
169
|
-
AND t.relkind = 'r'
|
|
170
|
-
AND NOT ix.indisprimary
|
|
171
|
-
ORDER BY i.relname, a.attnum
|
|
172
|
-
`, [tableName]);
|
|
173
|
-
const indexMap = new Map();
|
|
174
|
-
for (const idx of indexResult.rows) {
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const primaryKeysByTable = new Map();
|
|
178
|
+
for (const pk of allPrimaryKeys) {
|
|
179
|
+
const tableName = pk.table_name;
|
|
180
|
+
if (!primaryKeysByTable.has(tableName)) {
|
|
181
|
+
primaryKeysByTable.set(tableName, []);
|
|
182
|
+
}
|
|
183
|
+
primaryKeysByTable.get(tableName).push(pk.column_name);
|
|
184
|
+
}
|
|
185
|
+
const indexesByTable = new Map();
|
|
186
|
+
for (const idx of allIndexes) {
|
|
187
|
+
const tableName = idx.table_name;
|
|
175
188
|
const indexName = idx.index_name;
|
|
176
|
-
if (!
|
|
177
|
-
|
|
189
|
+
if (!indexesByTable.has(tableName)) {
|
|
190
|
+
indexesByTable.set(tableName, new Map());
|
|
191
|
+
}
|
|
192
|
+
const tableIndexes = indexesByTable.get(tableName);
|
|
193
|
+
if (!tableIndexes.has(indexName)) {
|
|
194
|
+
tableIndexes.set(indexName, {
|
|
178
195
|
columns: [],
|
|
179
196
|
unique: idx.is_unique,
|
|
180
197
|
});
|
|
181
198
|
}
|
|
182
|
-
|
|
199
|
+
tableIndexes.get(indexName).columns.push(idx.column_name);
|
|
200
|
+
}
|
|
201
|
+
const rowsByTable = new Map();
|
|
202
|
+
for (const stat of allStats) {
|
|
203
|
+
rowsByTable.set(stat.table_name, Number(stat.estimated_rows) || 0);
|
|
204
|
+
}
|
|
205
|
+
const tableInfos = [];
|
|
206
|
+
for (const [tableName, columns] of columnsByTable.entries()) {
|
|
207
|
+
const tableIndexes = indexesByTable.get(tableName);
|
|
208
|
+
const indexInfos = [];
|
|
209
|
+
if (tableIndexes) {
|
|
210
|
+
for (const [indexName, indexData] of tableIndexes.entries()) {
|
|
211
|
+
indexInfos.push({
|
|
212
|
+
name: indexName,
|
|
213
|
+
columns: indexData.columns,
|
|
214
|
+
unique: indexData.unique,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
tableInfos.push({
|
|
219
|
+
name: tableName,
|
|
220
|
+
columns,
|
|
221
|
+
primaryKeys: primaryKeysByTable.get(tableName) || [],
|
|
222
|
+
indexes: indexInfos,
|
|
223
|
+
estimatedRows: rowsByTable.get(tableName) || 0,
|
|
224
|
+
});
|
|
183
225
|
}
|
|
184
|
-
|
|
185
|
-
name,
|
|
186
|
-
columns: info.columns,
|
|
187
|
-
unique: info.unique,
|
|
188
|
-
}));
|
|
189
|
-
// 获取表行数估算
|
|
190
|
-
const statsResult = await this.client.query(`
|
|
191
|
-
SELECT reltuples::bigint as estimate
|
|
192
|
-
FROM pg_class
|
|
193
|
-
WHERE relname = $1
|
|
194
|
-
`, [tableName]);
|
|
195
|
-
const estimatedRows = Number(statsResult.rows[0]?.estimate || 0);
|
|
226
|
+
tableInfos.sort((a, b) => a.name.localeCompare(b.name));
|
|
196
227
|
return {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
estimatedRows,
|
|
228
|
+
databaseType: 'gaussdb',
|
|
229
|
+
databaseName,
|
|
230
|
+
tables: tableInfos,
|
|
231
|
+
version,
|
|
202
232
|
};
|
|
203
233
|
}
|
|
204
234
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gaussdb.js","sourceRoot":"","sources":["../../src/adapters/gaussdb.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gaussdb.js","sourceRoot":"","sources":["../../src/adapters/gaussdb.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* GoldenDB 数据库适配器
|
|
3
3
|
* 使用 mysql2 驱动实现 DbAdapter 接口
|
|
4
4
|
* GoldenDB 兼容 MySQL 协议
|
|
5
|
+
*
|
|
6
|
+
* 性能优化:使用批量查询获取 Schema 信息,避免 N+1 查询问题
|
|
5
7
|
*/
|
|
6
8
|
import type { DbAdapter, QueryResult, SchemaInfo } from '../types/adapter.js';
|
|
7
9
|
export declare class GoldenDBAdapter implements DbAdapter {
|
|
@@ -27,13 +29,13 @@ export declare class GoldenDBAdapter implements DbAdapter {
|
|
|
27
29
|
*/
|
|
28
30
|
executeQuery(query: string, params?: unknown[]): Promise<QueryResult>;
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
32
|
+
* 获取数据库结构信息(批量查询优化版本)
|
|
31
33
|
*/
|
|
32
34
|
getSchema(): Promise<SchemaInfo>;
|
|
33
35
|
/**
|
|
34
|
-
*
|
|
36
|
+
* 组装 Schema 信息
|
|
35
37
|
*/
|
|
36
|
-
private
|
|
38
|
+
private assembleSchema;
|
|
37
39
|
/**
|
|
38
40
|
* 检查是否为写操作
|
|
39
41
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"goldendb.d.ts","sourceRoot":"","sources":["../../src/adapters/goldendb.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"goldendb.d.ts","sourceRoot":"","sources":["../../src/adapters/goldendb.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,UAAU,EAIX,MAAM,qBAAqB,CAAC;AAG7B,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,UAAU,CAAiC;IACnD,OAAO,CAAC,MAAM,CAMZ;gBAEU,MAAM,EAAE;QAClB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB;IAID;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;OAEG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAwC3E;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,UAAU,CAAC;IA8DtC;;OAEG;IACH,OAAO,CAAC,cAAc;IA+FtB;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAGzC"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* GoldenDB 数据库适配器
|
|
3
3
|
* 使用 mysql2 驱动实现 DbAdapter 接口
|
|
4
4
|
* GoldenDB 兼容 MySQL 协议
|
|
5
|
+
*
|
|
6
|
+
* 性能优化:使用批量查询获取 Schema 信息,避免 N+1 查询问题
|
|
5
7
|
*/
|
|
6
8
|
import mysql from 'mysql2/promise';
|
|
7
9
|
import { isWriteOperation as checkWriteOperation } from '../utils/safety.js';
|
|
@@ -80,7 +82,7 @@ export class GoldenDBAdapter {
|
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
/**
|
|
83
|
-
*
|
|
85
|
+
* 获取数据库结构信息(批量查询优化版本)
|
|
84
86
|
*/
|
|
85
87
|
async getSchema() {
|
|
86
88
|
if (!this.connection) {
|
|
@@ -93,74 +95,121 @@ export class GoldenDBAdapter {
|
|
|
93
95
|
// 获取当前数据库名
|
|
94
96
|
const [dbRows] = await this.connection.query('SELECT DATABASE() as db');
|
|
95
97
|
const databaseName = dbRows[0]?.db || this.config.database || 'unknown';
|
|
96
|
-
//
|
|
97
|
-
const [
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
// 批量获取所有表的列信息
|
|
99
|
+
const [allColumns] = await this.connection.query(`
|
|
100
|
+
SELECT
|
|
101
|
+
TABLE_NAME,
|
|
102
|
+
COLUMN_NAME,
|
|
103
|
+
COLUMN_TYPE,
|
|
104
|
+
IS_NULLABLE,
|
|
105
|
+
COLUMN_DEFAULT,
|
|
106
|
+
COLUMN_KEY,
|
|
107
|
+
COLUMN_COMMENT,
|
|
108
|
+
ORDINAL_POSITION
|
|
109
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
110
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
111
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION
|
|
112
|
+
`);
|
|
113
|
+
// 批量获取所有表的索引信息
|
|
114
|
+
const [allIndexes] = await this.connection.query(`
|
|
115
|
+
SELECT
|
|
116
|
+
TABLE_NAME,
|
|
117
|
+
INDEX_NAME,
|
|
118
|
+
COLUMN_NAME,
|
|
119
|
+
NON_UNIQUE,
|
|
120
|
+
SEQ_IN_INDEX
|
|
121
|
+
FROM INFORMATION_SCHEMA.STATISTICS
|
|
122
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
123
|
+
ORDER BY TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX
|
|
124
|
+
`);
|
|
125
|
+
// 批量获取所有表的行数估算
|
|
126
|
+
const [allStats] = await this.connection.query(`
|
|
127
|
+
SELECT
|
|
128
|
+
TABLE_NAME,
|
|
129
|
+
TABLE_ROWS
|
|
130
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
131
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
132
|
+
AND TABLE_TYPE = 'BASE TABLE'
|
|
133
|
+
`);
|
|
134
|
+
// 在内存中组装数据
|
|
135
|
+
return this.assembleSchema(databaseName, version, allColumns, allIndexes, allStats);
|
|
110
136
|
}
|
|
111
137
|
catch (error) {
|
|
112
138
|
throw new Error(`获取数据库结构失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
113
139
|
}
|
|
114
140
|
}
|
|
115
141
|
/**
|
|
116
|
-
*
|
|
142
|
+
* 组装 Schema 信息
|
|
117
143
|
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
144
|
+
assembleSchema(databaseName, version, allColumns, allIndexes, allStats) {
|
|
145
|
+
const columnsByTable = new Map();
|
|
146
|
+
const primaryKeysByTable = new Map();
|
|
147
|
+
for (const col of allColumns) {
|
|
148
|
+
const tableName = col.TABLE_NAME;
|
|
149
|
+
if (!columnsByTable.has(tableName)) {
|
|
150
|
+
columnsByTable.set(tableName, []);
|
|
151
|
+
primaryKeysByTable.set(tableName, []);
|
|
152
|
+
}
|
|
153
|
+
columnsByTable.get(tableName).push({
|
|
154
|
+
name: col.COLUMN_NAME,
|
|
155
|
+
type: col.COLUMN_TYPE,
|
|
156
|
+
nullable: col.IS_NULLABLE === 'YES',
|
|
157
|
+
defaultValue: col.COLUMN_DEFAULT,
|
|
158
|
+
comment: col.COLUMN_COMMENT || undefined,
|
|
159
|
+
});
|
|
160
|
+
if (col.COLUMN_KEY === 'PRI') {
|
|
161
|
+
primaryKeysByTable.get(tableName).push(col.COLUMN_NAME);
|
|
162
|
+
}
|
|
121
163
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
type: col.Type,
|
|
127
|
-
nullable: col.Null === 'YES',
|
|
128
|
-
defaultValue: col.Default,
|
|
129
|
-
comment: col.Comment || undefined,
|
|
130
|
-
}));
|
|
131
|
-
// 获取主键
|
|
132
|
-
const primaryKeys = columns
|
|
133
|
-
.filter((col) => col.Key === 'PRI')
|
|
134
|
-
.map((col) => col.Field);
|
|
135
|
-
// 获取索引信息
|
|
136
|
-
const [indexes] = await this.connection.query('SHOW INDEX FROM ??', [tableName]);
|
|
137
|
-
const indexMap = new Map();
|
|
138
|
-
for (const idx of indexes) {
|
|
139
|
-
const indexName = idx.Key_name;
|
|
164
|
+
const indexesByTable = new Map();
|
|
165
|
+
for (const idx of allIndexes) {
|
|
166
|
+
const tableName = idx.TABLE_NAME;
|
|
167
|
+
const indexName = idx.INDEX_NAME;
|
|
140
168
|
if (indexName === 'PRIMARY')
|
|
141
|
-
continue;
|
|
142
|
-
if (!
|
|
143
|
-
|
|
169
|
+
continue;
|
|
170
|
+
if (!indexesByTable.has(tableName)) {
|
|
171
|
+
indexesByTable.set(tableName, new Map());
|
|
172
|
+
}
|
|
173
|
+
const tableIndexes = indexesByTable.get(tableName);
|
|
174
|
+
if (!tableIndexes.has(indexName)) {
|
|
175
|
+
tableIndexes.set(indexName, {
|
|
144
176
|
columns: [],
|
|
145
|
-
unique: idx.
|
|
177
|
+
unique: idx.NON_UNIQUE === 0,
|
|
146
178
|
});
|
|
147
179
|
}
|
|
148
|
-
|
|
180
|
+
tableIndexes.get(indexName).columns.push(idx.COLUMN_NAME);
|
|
181
|
+
}
|
|
182
|
+
const rowsByTable = new Map();
|
|
183
|
+
for (const stat of allStats) {
|
|
184
|
+
rowsByTable.set(stat.TABLE_NAME, stat.TABLE_ROWS || 0);
|
|
185
|
+
}
|
|
186
|
+
const tableInfos = [];
|
|
187
|
+
for (const [tableName, columns] of columnsByTable.entries()) {
|
|
188
|
+
const tableIndexes = indexesByTable.get(tableName);
|
|
189
|
+
const indexInfos = [];
|
|
190
|
+
if (tableIndexes) {
|
|
191
|
+
for (const [indexName, indexData] of tableIndexes.entries()) {
|
|
192
|
+
indexInfos.push({
|
|
193
|
+
name: indexName,
|
|
194
|
+
columns: indexData.columns,
|
|
195
|
+
unique: indexData.unique,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
tableInfos.push({
|
|
200
|
+
name: tableName,
|
|
201
|
+
columns,
|
|
202
|
+
primaryKeys: primaryKeysByTable.get(tableName) || [],
|
|
203
|
+
indexes: indexInfos,
|
|
204
|
+
estimatedRows: rowsByTable.get(tableName) || 0,
|
|
205
|
+
});
|
|
149
206
|
}
|
|
150
|
-
|
|
151
|
-
name,
|
|
152
|
-
columns: info.columns,
|
|
153
|
-
unique: info.unique,
|
|
154
|
-
}));
|
|
155
|
-
// 获取表行数估算
|
|
156
|
-
const [statusRows] = await this.connection.query('SHOW TABLE STATUS WHERE Name = ?', [tableName]);
|
|
157
|
-
const estimatedRows = statusRows[0]?.Rows || 0;
|
|
207
|
+
tableInfos.sort((a, b) => a.name.localeCompare(b.name));
|
|
158
208
|
return {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
estimatedRows,
|
|
209
|
+
databaseType: 'goldendb',
|
|
210
|
+
databaseName,
|
|
211
|
+
tables: tableInfos,
|
|
212
|
+
version,
|
|
164
213
|
};
|
|
165
214
|
}
|
|
166
215
|
/**
|