@cmd233/mcp-database-server 1.1.1 → 1.1.3
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.en.bak +328 -0
- package/dist/src/db/adapter.js +3 -3
- package/dist/src/db/index.js +23 -23
- package/dist/src/db/mysql-adapter.js +17 -17
- package/dist/src/db/postgresql-adapter.js +24 -24
- package/dist/src/db/sqlite-adapter.js +19 -19
- package/dist/src/db/sqlserver-adapter.js +187 -86
- package/dist/src/handlers/resourceHandlers.js +8 -8
- package/dist/src/handlers/toolHandlers.js +7 -7
- package/dist/src/index.js +25 -25
- package/dist/src/tools/insightTools.js +9 -9
- package/dist/src/tools/queryTools.js +10 -10
- package/dist/src/tools/schemaTools.js +15 -15
- package/dist/src/utils/formatUtils.js +14 -14
- package/package.json +3 -2
- package/readme.md +100 -100
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import pg from 'pg';
|
|
2
2
|
/**
|
|
3
|
-
* PostgreSQL
|
|
3
|
+
* PostgreSQL 数据库适配器实现
|
|
4
4
|
*/
|
|
5
5
|
export class PostgresqlAdapter {
|
|
6
6
|
constructor(connectionInfo) {
|
|
7
7
|
this.client = null;
|
|
8
8
|
this.host = connectionInfo.host;
|
|
9
9
|
this.database = connectionInfo.database;
|
|
10
|
-
//
|
|
10
|
+
// 创建 PostgreSQL 连接配置
|
|
11
11
|
this.config = {
|
|
12
12
|
host: connectionInfo.host,
|
|
13
13
|
database: connectionInfo.database,
|
|
@@ -15,12 +15,12 @@ export class PostgresqlAdapter {
|
|
|
15
15
|
user: connectionInfo.user,
|
|
16
16
|
password: connectionInfo.password,
|
|
17
17
|
ssl: connectionInfo.ssl,
|
|
18
|
-
//
|
|
18
|
+
// 如果提供了连接超时,则添加(以毫秒为单位)
|
|
19
19
|
connectionTimeoutMillis: connectionInfo.connectionTimeout || 30000,
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* 初始化 PostgreSQL 连接
|
|
24
24
|
*/
|
|
25
25
|
async init() {
|
|
26
26
|
try {
|
|
@@ -43,17 +43,17 @@ export class PostgresqlAdapter {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param query SQL
|
|
48
|
-
* @param params
|
|
49
|
-
* @returns Promise
|
|
46
|
+
* 执行 SQL 查询并获取所有结果
|
|
47
|
+
* @param query 要执行的 SQL 查询
|
|
48
|
+
* @param params 查询参数
|
|
49
|
+
* @returns 包含查询结果的 Promise
|
|
50
50
|
*/
|
|
51
51
|
async all(query, params = []) {
|
|
52
52
|
if (!this.client) {
|
|
53
53
|
throw new Error("Database not initialized");
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
56
|
-
// PostgreSQL
|
|
56
|
+
// PostgreSQL 使用 $1, $2 等作为参数化查询的占位符
|
|
57
57
|
const preparedQuery = query.replace(/\?/g, (_, i) => `$${i + 1}`);
|
|
58
58
|
const result = await this.client.query(preparedQuery, params);
|
|
59
59
|
return result.rows;
|
|
@@ -63,23 +63,23 @@ export class PostgresqlAdapter {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
67
|
-
* @param query SQL
|
|
68
|
-
* @param params
|
|
69
|
-
* @returns Promise
|
|
66
|
+
* 执行修改数据的 SQL 查询
|
|
67
|
+
* @param query 要执行的 SQL 查询
|
|
68
|
+
* @param params 查询参数
|
|
69
|
+
* @returns 包含结果信息的 Promise
|
|
70
70
|
*/
|
|
71
71
|
async run(query, params = []) {
|
|
72
72
|
if (!this.client) {
|
|
73
73
|
throw new Error("Database not initialized");
|
|
74
74
|
}
|
|
75
75
|
try {
|
|
76
|
-
//
|
|
76
|
+
// 将 ? 替换为编号参数
|
|
77
77
|
const preparedQuery = query.replace(/\?/g, (_, i) => `$${i + 1}`);
|
|
78
78
|
let lastID = 0;
|
|
79
79
|
let changes = 0;
|
|
80
|
-
//
|
|
80
|
+
// 对于 INSERT 查询,尝试获取插入的 ID
|
|
81
81
|
if (query.trim().toUpperCase().startsWith('INSERT')) {
|
|
82
|
-
//
|
|
82
|
+
// 如果查询中没有 RETURNING 子句,则添加以获取插入的 ID
|
|
83
83
|
const returningQuery = preparedQuery.includes('RETURNING')
|
|
84
84
|
? preparedQuery
|
|
85
85
|
: `${preparedQuery} RETURNING id`;
|
|
@@ -98,9 +98,9 @@ export class PostgresqlAdapter {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
|
-
*
|
|
102
|
-
* @param query SQL
|
|
103
|
-
* @returns Promise
|
|
101
|
+
* 执行多条 SQL 语句
|
|
102
|
+
* @param query 要执行的 SQL 语句
|
|
103
|
+
* @returns 执行完成后解析的 Promise
|
|
104
104
|
*/
|
|
105
105
|
async exec(query) {
|
|
106
106
|
if (!this.client) {
|
|
@@ -114,7 +114,7 @@ export class PostgresqlAdapter {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
/**
|
|
117
|
-
*
|
|
117
|
+
* 关闭数据库连接
|
|
118
118
|
*/
|
|
119
119
|
async close() {
|
|
120
120
|
if (this.client) {
|
|
@@ -123,7 +123,7 @@ export class PostgresqlAdapter {
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
/**
|
|
126
|
-
*
|
|
126
|
+
* 获取数据库元数据
|
|
127
127
|
*/
|
|
128
128
|
getMetadata() {
|
|
129
129
|
return {
|
|
@@ -134,14 +134,14 @@ export class PostgresqlAdapter {
|
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
|
-
*
|
|
137
|
+
* 获取用于列出表的数据库特定查询
|
|
138
138
|
*/
|
|
139
139
|
getListTablesQuery() {
|
|
140
140
|
return "SELECT table_name as name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name";
|
|
141
141
|
}
|
|
142
142
|
/**
|
|
143
|
-
*
|
|
144
|
-
* @param tableName
|
|
143
|
+
* 获取用于描述表结构的数据库特定查询
|
|
144
|
+
* @param tableName 表名
|
|
145
145
|
*/
|
|
146
146
|
getDescribeTableQuery(tableName) {
|
|
147
147
|
return `
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import sqlite3 from "sqlite3";
|
|
2
2
|
/**
|
|
3
|
-
* SQLite
|
|
3
|
+
* SQLite 数据库适配器实现
|
|
4
4
|
*/
|
|
5
5
|
export class SqliteAdapter {
|
|
6
6
|
constructor(dbPath) {
|
|
@@ -8,11 +8,11 @@ export class SqliteAdapter {
|
|
|
8
8
|
this.dbPath = dbPath;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* 初始化 SQLite 数据库连接
|
|
12
12
|
*/
|
|
13
13
|
async init() {
|
|
14
14
|
return new Promise((resolve, reject) => {
|
|
15
|
-
//
|
|
15
|
+
// 确保数据库路径可访问
|
|
16
16
|
console.error(`[INFO] Opening SQLite database at: ${this.dbPath}`);
|
|
17
17
|
this.db = new sqlite3.Database(this.dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
|
|
18
18
|
if (err) {
|
|
@@ -27,10 +27,10 @@ export class SqliteAdapter {
|
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
31
|
-
* @param query SQL
|
|
32
|
-
* @param params
|
|
33
|
-
* @returns Promise
|
|
30
|
+
* 执行 SQL 查询并返回所有结果
|
|
31
|
+
* @param query 要执行的 SQL 查询
|
|
32
|
+
* @param params 查询参数
|
|
33
|
+
* @returns 包含查询结果的 Promise
|
|
34
34
|
*/
|
|
35
35
|
async all(query, params = []) {
|
|
36
36
|
if (!this.db) {
|
|
@@ -48,10 +48,10 @@ export class SqliteAdapter {
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
52
|
-
* @param query SQL
|
|
53
|
-
* @param params
|
|
54
|
-
* @returns Promise
|
|
51
|
+
* 执行修改数据的 SQL 查询
|
|
52
|
+
* @param query 要执行的 SQL 查询
|
|
53
|
+
* @param params 查询参数
|
|
54
|
+
* @returns 包含结果信息的 Promise
|
|
55
55
|
*/
|
|
56
56
|
async run(query, params = []) {
|
|
57
57
|
if (!this.db) {
|
|
@@ -69,9 +69,9 @@ export class SqliteAdapter {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
|
-
*
|
|
73
|
-
* @param query SQL
|
|
74
|
-
* @returns Promise
|
|
72
|
+
* 执行多条 SQL 语句
|
|
73
|
+
* @param query 要执行的 SQL 语句
|
|
74
|
+
* @returns 执行完成后完成的 Promise
|
|
75
75
|
*/
|
|
76
76
|
async exec(query) {
|
|
77
77
|
if (!this.db) {
|
|
@@ -89,7 +89,7 @@ export class SqliteAdapter {
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
|
-
*
|
|
92
|
+
* 关闭数据库连接
|
|
93
93
|
*/
|
|
94
94
|
async close() {
|
|
95
95
|
return new Promise((resolve, reject) => {
|
|
@@ -109,7 +109,7 @@ export class SqliteAdapter {
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
112
|
+
* 获取数据库元数据
|
|
113
113
|
*/
|
|
114
114
|
getMetadata() {
|
|
115
115
|
return {
|
|
@@ -119,14 +119,14 @@ export class SqliteAdapter {
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
/**
|
|
122
|
-
*
|
|
122
|
+
* 获取列出所有表的数据库特定查询
|
|
123
123
|
*/
|
|
124
124
|
getListTablesQuery() {
|
|
125
125
|
return "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'";
|
|
126
126
|
}
|
|
127
127
|
/**
|
|
128
|
-
*
|
|
129
|
-
* @param tableName
|
|
128
|
+
* 获取描述表结构的数据库特定查询
|
|
129
|
+
* @param tableName 表名
|
|
130
130
|
*/
|
|
131
131
|
getDescribeTableQuery(tableName) {
|
|
132
132
|
return `PRAGMA table_info(${tableName})`;
|
|
@@ -1,91 +1,211 @@
|
|
|
1
1
|
import sql from 'mssql';
|
|
2
2
|
/**
|
|
3
|
-
* SQL Server
|
|
3
|
+
* SQL Server 数据库适配器实现
|
|
4
4
|
*/
|
|
5
5
|
export class SqlServerAdapter {
|
|
6
6
|
constructor(connectionInfo) {
|
|
7
7
|
this.pool = null;
|
|
8
|
+
this.isConnecting = false;
|
|
8
9
|
this.server = connectionInfo.server;
|
|
9
10
|
this.database = connectionInfo.database;
|
|
10
|
-
//
|
|
11
|
+
// 创建 SQL Server 连接配置
|
|
11
12
|
this.config = {
|
|
12
13
|
server: connectionInfo.server,
|
|
13
14
|
database: connectionInfo.database,
|
|
14
15
|
port: connectionInfo.port || 1433,
|
|
16
|
+
// 连接池配置
|
|
17
|
+
pool: {
|
|
18
|
+
max: 10, // 最大连接数
|
|
19
|
+
min: 1, // 最小连接数
|
|
20
|
+
idleTimeoutMillis: 30000, // 空闲连接超时时间 (30秒)
|
|
21
|
+
},
|
|
22
|
+
// 连接超时和请求超时配置
|
|
23
|
+
connectionTimeout: 30000, // 连接超时 (30秒)
|
|
24
|
+
requestTimeout: 30000, // 请求超时 (30秒)
|
|
15
25
|
options: {
|
|
16
26
|
trustServerCertificate: connectionInfo.trustServerCertificate ?? true,
|
|
27
|
+
enableArithAbort: true,
|
|
17
28
|
...connectionInfo.options
|
|
18
29
|
}
|
|
19
30
|
};
|
|
20
|
-
//
|
|
31
|
+
// 添加认证选项
|
|
21
32
|
if (connectionInfo.user && connectionInfo.password) {
|
|
22
33
|
this.config.user = connectionInfo.user;
|
|
23
34
|
this.config.password = connectionInfo.password;
|
|
24
35
|
}
|
|
25
36
|
else {
|
|
26
|
-
//
|
|
37
|
+
// 如果未提供用户名/密码,则使用 Windows 身份验证
|
|
27
38
|
this.config.options.trustedConnection = true;
|
|
28
|
-
this.config.options.enableArithAbort = true;
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
/**
|
|
32
|
-
*
|
|
42
|
+
* 初始化 SQL Server 连接
|
|
33
43
|
*/
|
|
34
44
|
async init() {
|
|
45
|
+
await this.ensureConnection();
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 确保连接可用,如果连接断开则自动重连
|
|
49
|
+
*/
|
|
50
|
+
async ensureConnection() {
|
|
51
|
+
// 如果正在连接中,等待连接完成
|
|
52
|
+
if (this.isConnecting) {
|
|
53
|
+
await this.waitForConnection();
|
|
54
|
+
if (this.pool && this.pool.connected) {
|
|
55
|
+
return this.pool;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// 检查现有连接是否可用
|
|
59
|
+
if (this.pool && this.pool.connected) {
|
|
60
|
+
return this.pool;
|
|
61
|
+
}
|
|
62
|
+
// 需要建立新连接
|
|
63
|
+
this.isConnecting = true;
|
|
35
64
|
try {
|
|
65
|
+
// 如果存在旧的连接池,先关闭它
|
|
66
|
+
if (this.pool) {
|
|
67
|
+
try {
|
|
68
|
+
await this.pool.close();
|
|
69
|
+
}
|
|
70
|
+
catch (closeErr) {
|
|
71
|
+
console.error(`[WARN] Error closing old connection pool: ${closeErr.message}`);
|
|
72
|
+
}
|
|
73
|
+
this.pool = null;
|
|
74
|
+
}
|
|
36
75
|
console.error(`[INFO] Connecting to SQL Server: ${this.server}, Database: ${this.database}`);
|
|
37
|
-
|
|
76
|
+
const pool = new sql.ConnectionPool(this.config);
|
|
77
|
+
// 使用单次监听器防止内存泄漏
|
|
78
|
+
pool.once('error', (err) => {
|
|
79
|
+
console.error(`[ERROR] SQL Server connection pool error: ${err.message}`);
|
|
80
|
+
// 标记连接池为不可用,下次查询时会自动重连
|
|
81
|
+
if (this.pool === pool) {
|
|
82
|
+
this.pool = null;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
this.pool = await pool.connect();
|
|
38
86
|
console.error(`[INFO] SQL Server connection established successfully`);
|
|
87
|
+
return this.pool;
|
|
39
88
|
}
|
|
40
89
|
catch (err) {
|
|
90
|
+
this.pool = null;
|
|
41
91
|
console.error(`[ERROR] SQL Server connection error: ${err.message}`);
|
|
42
92
|
throw new Error(`Failed to connect to SQL Server: ${err.message}`);
|
|
43
93
|
}
|
|
94
|
+
finally {
|
|
95
|
+
this.isConnecting = false;
|
|
96
|
+
}
|
|
44
97
|
}
|
|
45
98
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param query SQL query to execute
|
|
48
|
-
* @param params Query parameters
|
|
49
|
-
* @returns Promise with query results
|
|
99
|
+
* 等待正在进行的连接完成
|
|
50
100
|
*/
|
|
51
|
-
async
|
|
52
|
-
|
|
53
|
-
|
|
101
|
+
async waitForConnection() {
|
|
102
|
+
const maxWait = 30000; // 最大等待30秒
|
|
103
|
+
const interval = 100; // 每100ms检查一次
|
|
104
|
+
let waited = 0;
|
|
105
|
+
while (this.isConnecting && waited < maxWait) {
|
|
106
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
107
|
+
waited += interval;
|
|
54
108
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
109
|
+
// 如果超时,重置状态并抛出错误
|
|
110
|
+
if (this.isConnecting) {
|
|
111
|
+
this.isConnecting = false;
|
|
112
|
+
throw new Error('Connection timeout');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 执行带重试的操作
|
|
117
|
+
*/
|
|
118
|
+
async executeWithRetry(operation, retries = 2) {
|
|
119
|
+
let lastError = null;
|
|
120
|
+
let poolAcquired = false; // 标记是否已成功获取连接池
|
|
121
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
122
|
+
try {
|
|
123
|
+
const pool = await this.ensureConnection();
|
|
124
|
+
poolAcquired = true; // 成功获取连接池
|
|
125
|
+
return await operation(pool);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
lastError = err;
|
|
129
|
+
const errorMessage = lastError.message.toLowerCase();
|
|
130
|
+
// 检查是否是连接相关的错误
|
|
131
|
+
const isConnectionError = errorMessage.includes('connection') ||
|
|
132
|
+
errorMessage.includes('socket') ||
|
|
133
|
+
errorMessage.includes('timeout') ||
|
|
134
|
+
errorMessage.includes('closed') ||
|
|
135
|
+
errorMessage.includes('econnreset') ||
|
|
136
|
+
errorMessage.includes('econnrefused') ||
|
|
137
|
+
errorMessage.includes('network') ||
|
|
138
|
+
errorMessage.includes('failed to connect');
|
|
139
|
+
// 只有在获取连接池后(即 poolAcquired = true)发生的连接错误才重试
|
|
140
|
+
// ensureConnection 本身的错误(如认证失败、连接超时)不应重试
|
|
141
|
+
if (isConnectionError && poolAcquired && attempt < retries && this.pool !== null) {
|
|
142
|
+
console.error(`[WARN] Connection error detected, attempting reconnect (attempt ${attempt + 1}/${retries}): ${lastError.message}`);
|
|
143
|
+
this.pool = null;
|
|
144
|
+
poolAcquired = false; // 重置标记
|
|
145
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
throw lastError;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
throw lastError;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* 关闭数据库连接
|
|
155
|
+
*/
|
|
156
|
+
async close() {
|
|
157
|
+
// 等待正在进行的连接完成
|
|
158
|
+
if (this.isConnecting) {
|
|
159
|
+
await this.waitForConnection();
|
|
160
|
+
}
|
|
161
|
+
if (this.pool) {
|
|
162
|
+
try {
|
|
163
|
+
await this.pool.close();
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
console.error(`[WARN] Error closing connection pool: ${err.message}`);
|
|
167
|
+
}
|
|
168
|
+
this.pool = null;
|
|
169
|
+
}
|
|
170
|
+
this.isConnecting = false;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 执行 SQL 查询并获取所有结果
|
|
174
|
+
* @param query 要执行的 SQL 查询
|
|
175
|
+
* @param params 查询参数
|
|
176
|
+
* @returns 包含查询结果的 Promise
|
|
177
|
+
*/
|
|
178
|
+
async all(query, params = []) {
|
|
179
|
+
return this.executeWithRetry(async (pool) => {
|
|
180
|
+
const request = pool.request();
|
|
181
|
+
// 向请求添加参数
|
|
58
182
|
params.forEach((param, index) => {
|
|
59
183
|
request.input(`param${index}`, param);
|
|
60
184
|
});
|
|
61
|
-
//
|
|
62
|
-
|
|
185
|
+
// 将 ? 替换为命名参数
|
|
186
|
+
let paramIndex = 0;
|
|
187
|
+
const preparedQuery = query.replace(/\?/g, () => `@param${paramIndex++}`);
|
|
63
188
|
const result = await request.query(preparedQuery);
|
|
64
189
|
return result.recordset;
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
throw new Error(`SQL Server query error: ${err.message}`);
|
|
68
|
-
}
|
|
190
|
+
});
|
|
69
191
|
}
|
|
70
192
|
/**
|
|
71
|
-
*
|
|
72
|
-
* @param query SQL
|
|
73
|
-
* @param params
|
|
74
|
-
* @returns Promise
|
|
193
|
+
* 执行修改数据的 SQL 查询
|
|
194
|
+
* @param query 要执行的 SQL 查询
|
|
195
|
+
* @param params 查询参数
|
|
196
|
+
* @returns 包含结果信息的 Promise
|
|
75
197
|
*/
|
|
76
198
|
async run(query, params = []) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
try {
|
|
81
|
-
const request = this.pool.request();
|
|
82
|
-
// Add parameters to the request
|
|
199
|
+
return this.executeWithRetry(async (pool) => {
|
|
200
|
+
const request = pool.request();
|
|
201
|
+
// 向请求添加参数
|
|
83
202
|
params.forEach((param, index) => {
|
|
84
203
|
request.input(`param${index}`, param);
|
|
85
204
|
});
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
205
|
+
// 将 ? 替换为命名参数
|
|
206
|
+
let paramIndex = 0;
|
|
207
|
+
const preparedQuery = query.replace(/\?/g, () => `@param${paramIndex++}`);
|
|
208
|
+
// 如果是 INSERT,添加标识值的输出参数
|
|
89
209
|
let lastID = 0;
|
|
90
210
|
if (query.trim().toUpperCase().startsWith('INSERT')) {
|
|
91
211
|
request.output('insertedId', sql.Int, 0);
|
|
@@ -94,46 +214,27 @@ export class SqlServerAdapter {
|
|
|
94
214
|
lastID = result.output.insertedId || 0;
|
|
95
215
|
}
|
|
96
216
|
else {
|
|
97
|
-
|
|
98
|
-
lastID = 0;
|
|
217
|
+
await request.query(preparedQuery);
|
|
99
218
|
}
|
|
100
219
|
return {
|
|
101
220
|
changes: this.getAffectedRows(query, lastID),
|
|
102
221
|
lastID: lastID
|
|
103
222
|
};
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
throw new Error(`SQL Server query error: ${err.message}`);
|
|
107
|
-
}
|
|
223
|
+
});
|
|
108
224
|
}
|
|
109
225
|
/**
|
|
110
|
-
*
|
|
111
|
-
* @param query SQL
|
|
112
|
-
* @returns Promise
|
|
226
|
+
* 执行多条 SQL 语句
|
|
227
|
+
* @param query 要执行的 SQL 语句
|
|
228
|
+
* @returns 执行完成后解析的 Promise
|
|
113
229
|
*/
|
|
114
230
|
async exec(query) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
try {
|
|
119
|
-
const request = this.pool.request();
|
|
231
|
+
return this.executeWithRetry(async (pool) => {
|
|
232
|
+
const request = pool.request();
|
|
120
233
|
await request.batch(query);
|
|
121
|
-
}
|
|
122
|
-
catch (err) {
|
|
123
|
-
throw new Error(`SQL Server batch error: ${err.message}`);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* Close the database connection
|
|
128
|
-
*/
|
|
129
|
-
async close() {
|
|
130
|
-
if (this.pool) {
|
|
131
|
-
await this.pool.close();
|
|
132
|
-
this.pool = null;
|
|
133
|
-
}
|
|
234
|
+
});
|
|
134
235
|
}
|
|
135
236
|
/**
|
|
136
|
-
*
|
|
237
|
+
* 获取数据库元数据
|
|
137
238
|
*/
|
|
138
239
|
getMetadata() {
|
|
139
240
|
return {
|
|
@@ -144,43 +245,43 @@ export class SqlServerAdapter {
|
|
|
144
245
|
};
|
|
145
246
|
}
|
|
146
247
|
/**
|
|
147
|
-
*
|
|
248
|
+
* 获取列出表的数据库特定查询
|
|
148
249
|
*/
|
|
149
250
|
getListTablesQuery() {
|
|
150
251
|
return "SELECT TABLE_NAME as name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME";
|
|
151
252
|
}
|
|
152
253
|
/**
|
|
153
|
-
*
|
|
154
|
-
* @param tableName
|
|
254
|
+
* 获取描述表的数据库特定查询
|
|
255
|
+
* @param tableName 表名
|
|
155
256
|
*/
|
|
156
257
|
getDescribeTableQuery(tableName) {
|
|
157
|
-
return `
|
|
158
|
-
SELECT
|
|
159
|
-
c.COLUMN_NAME as name,
|
|
160
|
-
c.DATA_TYPE as type,
|
|
161
|
-
CASE WHEN c.IS_NULLABLE = 'NO' THEN 1 ELSE 0 END as notnull,
|
|
162
|
-
CASE WHEN pk.CONSTRAINT_TYPE = 'PRIMARY KEY' THEN 1 ELSE 0 END as pk,
|
|
163
|
-
c.COLUMN_DEFAULT as dflt_value
|
|
164
|
-
FROM
|
|
165
|
-
INFORMATION_SCHEMA.COLUMNS c
|
|
166
|
-
LEFT JOIN
|
|
167
|
-
INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON c.TABLE_NAME = kcu.TABLE_NAME AND c.COLUMN_NAME = kcu.COLUMN_NAME
|
|
168
|
-
LEFT JOIN
|
|
169
|
-
INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ON kcu.CONSTRAINT_NAME = pk.CONSTRAINT_NAME AND pk.CONSTRAINT_TYPE = 'PRIMARY KEY'
|
|
170
|
-
WHERE
|
|
171
|
-
c.TABLE_NAME = '${tableName}'
|
|
172
|
-
ORDER BY
|
|
173
|
-
c.ORDINAL_POSITION
|
|
258
|
+
return `
|
|
259
|
+
SELECT
|
|
260
|
+
c.COLUMN_NAME as name,
|
|
261
|
+
c.DATA_TYPE as type,
|
|
262
|
+
CASE WHEN c.IS_NULLABLE = 'NO' THEN 1 ELSE 0 END as notnull,
|
|
263
|
+
CASE WHEN pk.CONSTRAINT_TYPE = 'PRIMARY KEY' THEN 1 ELSE 0 END as pk,
|
|
264
|
+
c.COLUMN_DEFAULT as dflt_value
|
|
265
|
+
FROM
|
|
266
|
+
INFORMATION_SCHEMA.COLUMNS c
|
|
267
|
+
LEFT JOIN
|
|
268
|
+
INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON c.TABLE_NAME = kcu.TABLE_NAME AND c.COLUMN_NAME = kcu.COLUMN_NAME
|
|
269
|
+
LEFT JOIN
|
|
270
|
+
INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk ON kcu.CONSTRAINT_NAME = pk.CONSTRAINT_NAME AND pk.CONSTRAINT_TYPE = 'PRIMARY KEY'
|
|
271
|
+
WHERE
|
|
272
|
+
c.TABLE_NAME = '${tableName}'
|
|
273
|
+
ORDER BY
|
|
274
|
+
c.ORDINAL_POSITION
|
|
174
275
|
`;
|
|
175
276
|
}
|
|
176
277
|
/**
|
|
177
|
-
*
|
|
278
|
+
* 根据查询类型获取受影响行数的辅助方法
|
|
178
279
|
*/
|
|
179
280
|
getAffectedRows(query, lastID) {
|
|
180
281
|
const queryType = query.trim().split(' ')[0].toUpperCase();
|
|
181
282
|
if (queryType === 'INSERT' && lastID > 0) {
|
|
182
283
|
return 1;
|
|
183
284
|
}
|
|
184
|
-
return 0; //
|
|
285
|
+
return 0; // 对于 SELECT 返回 0,对于 UPDATE/DELETE 在没有额外查询的情况下未知
|
|
185
286
|
}
|
|
186
287
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { dbAll, getListTablesQuery, getDescribeTableQuery, getDatabaseMetadata } from '../db/index.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* @returns
|
|
3
|
+
* 处理列出资源的请求
|
|
4
|
+
* @returns 可用资源列表
|
|
5
5
|
*/
|
|
6
6
|
export async function handleListResources() {
|
|
7
7
|
try {
|
|
8
8
|
const dbInfo = getDatabaseMetadata();
|
|
9
9
|
const dbType = dbInfo.type;
|
|
10
10
|
let resourceBaseUrl;
|
|
11
|
-
//
|
|
11
|
+
// 根据数据库类型创建适当的 URL
|
|
12
12
|
if (dbType === 'sqlite' && dbInfo.path) {
|
|
13
13
|
resourceBaseUrl = new URL(`sqlite:///${dbInfo.path}`);
|
|
14
14
|
}
|
|
@@ -19,7 +19,7 @@ export async function handleListResources() {
|
|
|
19
19
|
resourceBaseUrl = new URL(`db:///database`);
|
|
20
20
|
}
|
|
21
21
|
const SCHEMA_PATH = "schema";
|
|
22
|
-
//
|
|
22
|
+
// 使用适配器特定的查询来列出表
|
|
23
23
|
const query = getListTablesQuery();
|
|
24
24
|
const result = await dbAll(query);
|
|
25
25
|
return {
|
|
@@ -35,9 +35,9 @@ export async function handleListResources() {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @param uri URI
|
|
40
|
-
* @returns
|
|
38
|
+
* 处理读取特定资源的请求
|
|
39
|
+
* @param uri 要读取的资源 URI
|
|
40
|
+
* @returns 资源内容
|
|
41
41
|
*/
|
|
42
42
|
export async function handleReadResource(uri) {
|
|
43
43
|
try {
|
|
@@ -49,7 +49,7 @@ export async function handleReadResource(uri) {
|
|
|
49
49
|
if (schema !== SCHEMA_PATH) {
|
|
50
50
|
throw new Error("Invalid resource URI");
|
|
51
51
|
}
|
|
52
|
-
//
|
|
52
|
+
// 使用适配器特定的查询来描述表结构
|
|
53
53
|
const query = getDescribeTableQuery(tableName);
|
|
54
54
|
const result = await dbAll(query);
|
|
55
55
|
return {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { formatErrorResponse } from '../utils/formatUtils.js';
|
|
2
|
-
//
|
|
2
|
+
// 导入所有工具实现
|
|
3
3
|
import { readQuery, writeQuery, exportQuery } from '../tools/queryTools.js';
|
|
4
4
|
import { createTable, alterTable, dropTable, listTables, describeTable } from '../tools/schemaTools.js';
|
|
5
5
|
import { appendInsight, listInsights } from '../tools/insightTools.js';
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @returns
|
|
7
|
+
* 处理列出可用工具的请求
|
|
8
|
+
* @returns 可用工具列表
|
|
9
9
|
*/
|
|
10
10
|
export function handleListTools() {
|
|
11
11
|
return {
|
|
@@ -120,10 +120,10 @@ export function handleListTools() {
|
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
123
|
-
*
|
|
124
|
-
* @param name
|
|
125
|
-
* @param args
|
|
126
|
-
* @returns
|
|
123
|
+
* 处理工具调用请求
|
|
124
|
+
* @param name 要调用的工具名称
|
|
125
|
+
* @param args 工具参数
|
|
126
|
+
* @returns 工具执行结果
|
|
127
127
|
*/
|
|
128
128
|
export async function handleToolCall(name, args) {
|
|
129
129
|
try {
|