@cmd233/mcp-database-server 1.1.7 → 1.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.
@@ -93,3 +93,40 @@ export function getDescribeTableQuery(tableName) {
93
93
  }
94
94
  return dbAdapter.getDescribeTableQuery(tableName);
95
95
  }
96
+ /**
97
+ * 获取列出视图的数据库特定查询
98
+ * 仅 SQL Server 支持,其他数据库返回空结果
99
+ */
100
+ export function getListViewsQuery() {
101
+ if (!dbAdapter) {
102
+ throw new Error("数据库未初始化");
103
+ }
104
+ if (dbAdapter.getListViewsQuery) {
105
+ return dbAdapter.getListViewsQuery();
106
+ }
107
+ // 不支持视图的数据库返回空结果查询
108
+ return "SELECT 1 as name WHERE 1=0";
109
+ }
110
+ /**
111
+ * 获取视图定义的数据库特定查询
112
+ * 仅 SQL Server 支持
113
+ * @param viewName 视图名
114
+ */
115
+ export function getViewDefinitionQuery(viewName) {
116
+ if (!dbAdapter) {
117
+ throw new Error("数据库未初始化");
118
+ }
119
+ if (dbAdapter.getViewDefinitionQuery) {
120
+ return dbAdapter.getViewDefinitionQuery(viewName);
121
+ }
122
+ throw new Error("当前数据库不支持视图功能");
123
+ }
124
+ /**
125
+ * 检查数据库是否支持视图功能
126
+ */
127
+ export function supportsViews() {
128
+ if (!dbAdapter) {
129
+ return false;
130
+ }
131
+ return dbAdapter.supportsViews ? dbAdapter.supportsViews() : false;
132
+ }
@@ -55,7 +55,7 @@ export class MysqlAdapter {
55
55
  throw new Error("AWS IAM 认证需要 AWS 用户名参数");
56
56
  }
57
57
  try {
58
- console.info(`[INFO] Generating AWS auth token for region: ${this.awsRegion}, host: ${this.host}, user: ${this.config.user}`);
58
+ console.info(`[INFO] 正在为区域 ${this.awsRegion} 生成 AWS 认证令牌, 主机: ${this.host}, 用户: ${this.config.user}`);
59
59
  const signer = new Signer({
60
60
  region: this.awsRegion,
61
61
  hostname: this.host,
@@ -63,7 +63,7 @@ export class MysqlAdapter {
63
63
  username: this.config.user,
64
64
  });
65
65
  const token = await signer.getAuthToken();
66
- console.info(`[INFO] AWS auth token generated successfully`);
66
+ console.info(`[INFO] AWS 认证令牌生成成功`);
67
67
  return token;
68
68
  }
69
69
  catch (err) {
@@ -76,10 +76,10 @@ export class MysqlAdapter {
76
76
  */
77
77
  async init() {
78
78
  try {
79
- console.info(`[INFO] Connecting to MySQL: ${this.host}, Database: ${this.database}`);
79
+ console.info(`[INFO] 正在连接 MySQL: ${this.host}, 数据库: ${this.database}`);
80
80
  // 处理 AWS IAM 认证
81
81
  if (this.awsIamAuth) {
82
- console.info(`[INFO] Using AWS IAM authentication for user: ${this.config.user}`);
82
+ console.info(`[INFO] 正在为用户 ${this.config.user} 使用 AWS IAM 认证`);
83
83
  try {
84
84
  const authToken = await this.generateAwsAuthToken();
85
85
  // 使用生成的令牌作为密码创建新配置
@@ -97,10 +97,10 @@ export class MysqlAdapter {
97
97
  else {
98
98
  this.connection = await mysql.createConnection(this.config);
99
99
  }
100
- console.info(`[INFO] MySQL connection established successfully`);
100
+ console.info(`[INFO] MySQL 连接成功建立`);
101
101
  }
102
102
  catch (err) {
103
- console.error(`[ERROR] MySQL connection error: ${err.message}`);
103
+ console.error(`[ERROR] MySQL 连接错误: ${err.message}`);
104
104
  if (this.awsIamAuth) {
105
105
  throw new Error(`使用 AWS IAM 认证连接 MySQL 失败: ${err.message}。请验证您的 AWS 凭据、IAM 权限和 RDS 配置。`);
106
106
  }
@@ -24,7 +24,7 @@ export class PostgresqlAdapter {
24
24
  */
25
25
  async init() {
26
26
  try {
27
- console.error(`[INFO] Connecting to PostgreSQL: ${this.host}, Database: ${this.database}`);
27
+ console.error(`[INFO] 正在连接 PostgreSQL: ${this.host}, 数据库: ${this.database}`);
28
28
  console.error(`[DEBUG] Connection details:`, {
29
29
  host: this.host,
30
30
  database: this.database,
@@ -35,10 +35,10 @@ export class PostgresqlAdapter {
35
35
  });
36
36
  this.client = new pg.Client(this.config);
37
37
  await this.client.connect();
38
- console.error(`[INFO] PostgreSQL connection established successfully`);
38
+ console.error(`[INFO] PostgreSQL 连接成功建立`);
39
39
  }
40
40
  catch (err) {
41
- console.error(`[ERROR] PostgreSQL connection error: ${err.message}`);
41
+ console.error(`[ERROR] PostgreSQL 连接错误: ${err.message}`);
42
42
  throw new Error(`连接 PostgreSQL 失败: ${err.message}`);
43
43
  }
44
44
  }
@@ -54,7 +54,8 @@ export class PostgresqlAdapter {
54
54
  }
55
55
  try {
56
56
  // PostgreSQL 使用 $1, $2 等作为参数化查询的占位符
57
- const preparedQuery = query.replace(/\?/g, (_, i) => `$${i + 1}`);
57
+ let paramIndex = 0;
58
+ const preparedQuery = query.replace(/\?/g, () => `$${++paramIndex}`);
58
59
  const result = await this.client.query(preparedQuery, params);
59
60
  return result.rows;
60
61
  }
@@ -74,7 +75,8 @@ export class PostgresqlAdapter {
74
75
  }
75
76
  try {
76
77
  // 将 ? 替换为编号参数
77
- const preparedQuery = query.replace(/\?/g, (_, i) => `$${i + 1}`);
78
+ let paramIndex = 0;
79
+ const preparedQuery = query.replace(/\?/g, () => `$${++paramIndex}`);
78
80
  let lastID = 0;
79
81
  let changes = 0;
80
82
  // 对于 INSERT 查询,尝试获取插入的 ID
@@ -13,14 +13,14 @@ export class SqliteAdapter {
13
13
  async init() {
14
14
  return new Promise((resolve, reject) => {
15
15
  // 确保数据库路径可访问
16
- console.error(`[INFO] Opening SQLite database at: ${this.dbPath}`);
16
+ console.error(`[INFO] 正在打开 SQLite 数据库: ${this.dbPath}`);
17
17
  this.db = new sqlite3.Database(this.dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
18
18
  if (err) {
19
- console.error(`[ERROR] SQLite connection error: ${err.message}`);
19
+ console.error(`[ERROR] SQLite 连接错误: ${err.message}`);
20
20
  reject(err);
21
21
  }
22
22
  else {
23
- console.error("[INFO] SQLite database opened successfully");
23
+ console.error("[INFO] SQLite 数据库成功打开");
24
24
  resolve();
25
25
  }
26
26
  });
@@ -68,27 +68,27 @@ export class SqlServerAdapter {
68
68
  await this.pool.close();
69
69
  }
70
70
  catch (closeErr) {
71
- console.error(`[WARN] Error closing old connection pool: ${closeErr.message}`);
71
+ console.error(`[WARN] 关闭旧连接池时出错: ${closeErr.message}`);
72
72
  }
73
73
  this.pool = null;
74
74
  }
75
- console.error(`[INFO] Connecting to SQL Server: ${this.server}, Database: ${this.database}`);
75
+ console.error(`[INFO] 正在连接 SQL Server: ${this.server}, 数据库: ${this.database}`);
76
76
  const pool = new sql.ConnectionPool(this.config);
77
77
  // 使用单次监听器防止内存泄漏
78
78
  pool.once('error', (err) => {
79
- console.error(`[ERROR] SQL Server connection pool error: ${err.message}`);
79
+ console.error(`[ERROR] SQL Server 连接池错误: ${err.message}`);
80
80
  // 标记连接池为不可用,下次查询时会自动重连
81
81
  if (this.pool === pool) {
82
82
  this.pool = null;
83
83
  }
84
84
  });
85
85
  this.pool = await pool.connect();
86
- console.error(`[INFO] SQL Server connection established successfully`);
86
+ console.error(`[INFO] SQL Server 连接成功建立`);
87
87
  return this.pool;
88
88
  }
89
89
  catch (err) {
90
90
  this.pool = null;
91
- console.error(`[ERROR] SQL Server connection error: ${err.message}`);
91
+ console.error(`[ERROR] SQL Server 连接错误: ${err.message}`);
92
92
  throw new Error(`连接 SQL Server 失败: ${err.message}`);
93
93
  }
94
94
  finally {
@@ -139,7 +139,7 @@ export class SqlServerAdapter {
139
139
  // 只有在获取连接池后(即 poolAcquired = true)发生的连接错误才重试
140
140
  // ensureConnection 本身的错误(如认证失败、连接超时)不应重试
141
141
  if (isConnectionError && poolAcquired && attempt < retries && this.pool !== null) {
142
- console.error(`[WARN] Connection error detected, attempting reconnect (attempt ${attempt + 1}/${retries}): ${lastError.message}`);
142
+ console.error(`[WARN] 检测到连接错误,正在尝试重新连接 (尝试 ${attempt + 1}/${retries}): ${lastError.message}`);
143
143
  this.pool = null;
144
144
  poolAcquired = false; // 重置标记
145
145
  await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1)));
@@ -163,7 +163,7 @@ export class SqlServerAdapter {
163
163
  await this.pool.close();
164
164
  }
165
165
  catch (err) {
166
- console.error(`[WARN] Error closing connection pool: ${err.message}`);
166
+ console.error(`[WARN] 关闭连接池时出错: ${err.message}`);
167
167
  }
168
168
  this.pool = null;
169
169
  }
@@ -207,17 +207,22 @@ export class SqlServerAdapter {
207
207
  const preparedQuery = query.replace(/\?/g, () => `@param${paramIndex++}`);
208
208
  // 如果是 INSERT,添加标识值的输出参数
209
209
  let lastID = 0;
210
+ let changes = 0;
210
211
  if (query.trim().toUpperCase().startsWith('INSERT')) {
211
212
  request.output('insertedId', sql.Int, 0);
212
213
  const updatedQuery = `${preparedQuery}; SELECT @insertedId = SCOPE_IDENTITY();`;
213
214
  const result = await request.query(updatedQuery);
214
215
  lastID = result.output.insertedId || 0;
216
+ // 使用 rowsAffected 获取受影响行数
217
+ changes = result.rowsAffected?.[0] || (lastID > 0 ? 1 : 0);
215
218
  }
216
219
  else {
217
- await request.query(preparedQuery);
220
+ const result = await request.query(preparedQuery);
221
+ // 使用 rowsAffected 获取受影响行数
222
+ changes = result.rowsAffected?.[0] || 0;
218
223
  }
219
224
  return {
220
- changes: this.getAffectedRows(query, lastID),
225
+ changes: changes,
221
226
  lastID: lastID
222
227
  };
223
228
  });
@@ -251,8 +256,8 @@ export class SqlServerAdapter {
251
256
  return "SELECT TABLE_NAME as name FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' ORDER BY TABLE_NAME";
252
257
  }
253
258
  /**
254
- * 获取描述表的数据库特定查询
255
- * @param tableName 表名
259
+ * 获取描述表或视图的数据库特定查询
260
+ * @param tableName 表名或视图名
256
261
  */
257
262
  getDescribeTableQuery(tableName) {
258
263
  return `
@@ -272,10 +277,11 @@ export class SqlServerAdapter {
272
277
  LEFT JOIN
273
278
  sys.extended_properties ep
274
279
  ON ep.major_id = (
275
- SELECT t.object_id
276
- FROM sys.tables t
277
- INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
278
- WHERE t.name = '${tableName}' AND s.name = c.TABLE_SCHEMA
280
+ SELECT o.object_id
281
+ FROM sys.objects o
282
+ INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
283
+ WHERE o.name = '${tableName}' AND s.name = c.TABLE_SCHEMA
284
+ AND o.type IN ('U', 'V')
279
285
  )
280
286
  AND ep.minor_id = c.ORDINAL_POSITION
281
287
  AND ep.name = 'MS_Description'
@@ -286,13 +292,23 @@ export class SqlServerAdapter {
286
292
  `;
287
293
  }
288
294
  /**
289
- * 根据查询类型获取受影响行数的辅助方法
295
+ * 获取列出视图的数据库特定查询
290
296
  */
291
- getAffectedRows(query, lastID) {
292
- const queryType = query.trim().split(' ')[0].toUpperCase();
293
- if (queryType === 'INSERT' && lastID > 0) {
294
- return 1;
295
- }
296
- return 0; // 对于 SELECT 返回 0,对于 UPDATE/DELETE 在没有额外查询的情况下未知
297
+ getListViewsQuery() {
298
+ return "SELECT TABLE_NAME as name FROM INFORMATION_SCHEMA.VIEWS ORDER BY TABLE_NAME";
299
+ }
300
+ /**
301
+ * 获取视图定义的数据库特定查询
302
+ * @param viewName 视图名
303
+ * 注意: 使用 WITH ENCRYPTION 创建的视图无法获取定义
304
+ */
305
+ getViewDefinitionQuery(viewName) {
306
+ return `SELECT VIEW_DEFINITION as definition FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '${viewName}'`;
307
+ }
308
+ /**
309
+ * 检查数据库是否支持视图功能
310
+ */
311
+ supportsViews() {
312
+ return true;
297
313
  }
298
314
  }