# Reported issues for gaussdb-ro-mcp Desktop Extension

Pod holds 15 of 15 GitHub reports that passed its relevance review. This can include external user reports, maintainer-confirmed bugs, and concrete feature gaps. Treat them as evidence to inspect, not a count of distinct defects.

Back to [gaussdb-ro-mcp Desktop Extension](/mcp/gaussdb-ro-mcp-desktop-extension).

## Most discussed

### tools: describe_table 各段错误处理不一致，constraints/indexes 失败被静默吞掉

## 问题描述

`handleDescribeTable` 对各段"尽力而为"查询的错误处理**不一致**：

- `DescribeColumns` / `ViewDefinition` 失败 → 输出 `columns_error` / `view_definition_error` 键 ✅
- `TableComment`、`DescribeConstraints`、`DescribeIndexes`、`ListPartitions` 失败 → **键直接缺失**，错误被静默吞掉 ❌

## 影响

`pg_constraint` / `pg_index` 查询失败（权限被回收、ctx 取消）时工具返回"成功"，`constraints`/`indexes` 键缺失——LLM 无法区分"无约束/索引"与"查询失败"，会把表当成无主键分析下去，没有任何错误信号可供纠正。

## 位置

`internal/tools/tools.go:150-175`

## 建议修复

统一为 `[]struct{name string; fn func(...)}`…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/15) · 2026-09-10 · closed · 1 comment

### db: detectPartitionSupport 出错也消耗 sync.Once，分区检测被永久错判

## 问题描述

`detectPartitionSupport` 用 `sync.Once` 缓存探测结果，但**出错也会消耗 Once**：瞬时错误（ctx 取消、连接抖动——agent 重试场景下很常见）把 `partitionMode` 永久钉死为 false，无重试、无日志。

## 影响

进程余生中所有分区表被错标为 `table`（GaussDB `parttype='p'` 检测失效），`describe_table` 静默缺失分区清单，且无从发现。

## 位置

`internal/db/meta.go:38-49`

## 建议修复

仅缓存成功结果：出错时不清 Once 的完成位（例如把 Once 换成 `atomic.Bool` + "已确定"标志，或 Once 内部 err 非 nil 时重置可重入的状态）。

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/14) · 2026-09-10 · closed · 1 comment

### db: 亚毫秒 statement_timeout 截断为 0，反而禁用服务端超时

## 问题描述

`enforceReadOnly` 用 `stmtTimeout.Milliseconds()`（整数毫秒）格式化 `SET statement_timeout`。亚毫秒的配置时长（如 yaml `statement_timeout: 100us`，合法解析为 100µs 且 >0 通过 SetDefaults）被截断为 **0**——在 PG/GaussDB 中语义是**禁用超时**，与配置意图完全相反（已实证）。

## 位置

`internal/db/manager.go:103`

## 建议修复

向下取整后为 0 时钳制为最小 1ms（`max(1, ms)`），或在 config 层校验 statement_timeout 不得小于 1ms 并报配置错误。

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/13) · 2026-09-10 · closed · 1 comment

### config: 未知 YAML 键被静默忽略，拼错的限制项实际在用默认值

## 问题描述

`Load` 使用不带 `KnownFields` 的 `yaml.Unmarshal`，未知/拼写错误的 YAML 键被静默忽略。

## 影响

```yaml
server:
  max_row: 5000      # max_rows 拼错
  stmt_timeout: 10s  # 拼错
```

加载零错误零日志，服务器以 `max_rows=500`、`statement_timeout=30s` 运行。运维以为设了紧约束，实际每个查询都在用默认语义——对一个以"限制"为核心卖点的工具尤其危险。

## 位置

`internal/config/config.go:240`

## 建议修复

改用 `yaml.Decoder` + `dec.KnownFields(true)` 拒绝未知键（注意 Duration 自定义 Unmarshal 兼容性）；若担心破坏既有配置，至少对常见拼写在加载日志中告警。

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/12) · 2026-09-10 · closed · 1 comment

### config: blocked_functions 文档称前缀匹配、实现为精确匹配；非空配置会整体替换默认黑名单

## 问题描述

`blocked_functions` 的**文档与实现不符**，且替换语义未声明：

1. `config.go:46` 注释与 `gaussdb-ro-mcp.example.yaml` 称"前缀匹配"，但 `isBlockedFunction` 只对**以 `*` 结尾**的条目做前缀匹配，其余是剥通配后的**精确匹配**。按文档写 `pg_advisory` 期望前缀覆盖，实际只有精确名 `pg_advisory` 被拦，`pg_try_advisory_lock` 等全部通过第 1 层。
2. schema 限定条目（如 `pg_catalog.dblink`）永远不匹配——比对只取尾段。
3. 配置**任何**非空 `blocked_functions` 会**整体替换**默认黑名单（`dblink*`、`set_config`、`pg_read_file*`…），追加一个 `pg_sleep` 会把其余全部放行，且无任何提示。

## 位置…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/11) · 2026-09-10 · closed · 1 comment

### config: BuildDSN 不转义密码（含空格即损坏）；ensureSSLMode 子串匹配可被密码抑制

## 问题描述

两个字面量拼接缺陷：

1. `BuildDSN` 发出**不加引号**的 `keyword=value` 对。密码含空格（或引入歧义的字符）时产生损坏的 DSN：`pass word@2026` → `password=pass word@2026 sslmode=disable`，驱动解析器在空格处截断值、把 `word@2026` 当非法关键字 → "初始化实例失败"或密码错误（已实证）。
2. `ensureSSLMode` 用子串 `Contains("sslmode=")` 判断整个 DSN：密码中恰好含 `sslmode=`（如 `xK3sslmode=9q`）时，DSN 路径上预期的默认 sslmode 被抑制，驱动静默回退 `prefer`。

## 位置

`internal/config/config.go:163-180`（BuildDSN）、`config.go:207-224`（ensureSSLMode）

## 建议修复

- 拆分字段路径：改用 `url.URL` + `url.UserPassword` 构造 URL 形式…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/10) · 2026-09-10 · closed · 1 comment

### tools: test_connection 在 transaction_read_only=off 时仍报 ok:true

## 问题描述

`test_connection` 在 `SHOW transaction_read_only` 查询**成功**时即报 `"ok": true`，即使查询返回的是 `off`。一个以"只读状态检查"为卖点的工具，对恰好是 `enforceReadOnly` 要抓的故障形态（启动参数被代理剥离、会话可写）亮绿灯。

## 复现

对启动参数被剥离、会话实际可写的池调用 `test_connection`：

```json
{"ok": true, "transaction_read_only": "off"}
```

## 位置

`internal/tools/tools.go:92`（`"ok": roErr == nil`）

## 建议修复

`ok` 应要求 `strings.EqualFold(ro, "on")`，与 `enforceReadOnly` 的判定一致；`off` 时可附 `read_only_check_error: "transaction_read_only=off"` 之类的显式错误信息。

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/9) · 2026-09-10 · closed · 1 comment

### db: Select/Query 行 map 按列名建键，重复列名静默丢数据

## 问题描述

`Instance.Select`（及 `Query`）的行映射以**输出列名**为 map 键：重复列名时第 N 列的值静默覆盖第 1 列的值，`Columns`（长度 N）与行 map（键更少）不一致。

## 复现

```sql
SELECT u.id, o.id FROM users u JOIN orders o ON u.id = o.user_id
```

返回 `columns: ["id","id"]`，但每行 map 只剩一个 `id` 键（持有第二个 id）——第一列数据静默丢失，且 `truncated: false`、`row_count` 正常，消费结果的 agent **无法察觉丢失**。

## 位置

`internal/db/manager.go:204-215`（Select 行循环）、`manager.go:236-248`（Query 同样问题）

## 建议修复

任选其一：
1. 行结构改为 `[]any` 按位置对应 `columns`（破坏现有 map 形态，需评估客户端兼容）；
2. 保留 map…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/8) · 2026-09-10 · closed · 1 comment

## Most recent

### db: newInstance 无条件覆盖 ConnectTimeout，实例级 connect_timeout 配置失效

## 问题描述

`newInstance` 在 `gaussdbxpool.ParseConfig` 之后**无条件**用服务级值覆盖 `poolCfg.ConnConfig.ConnectTimeout`，静默丢弃用户通过 DSN 或 `options` 传入的实例级 `connect_timeout`——而 `config.go:65` 的注释明确把 `connect_timeout=5` 列为受支持的 Options 条目（驱动确实会解析它，见 vendored `gaussdbconn/config.go:281-287`）。

## 影响

```yaml
instances:
  - name: slow-endpoint
    options: ["connect_timeout=1"]   # 期望 1s
# server.connect_timeout 未设置 → 默认 10s
```

连接黑洞时尝试会停滞约 10s 而非配置的 1s，且无任何提示。

## 位置

`internal/db/manager.go:74`

## 建议修复

仅当实例/DSN…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/7) · 2026-09-10 · closed · 1 comment

### config: instances 列表含空项时 SetDefaults 解引用 nil 而 panic

## 问题描述

YAML 中 `instances:` 列表的空列表项解码为 **nil `*Instance` 指针**，而 `Load` 的调用顺序是先 `SetDefaults()` 再 `validate()`——`SetDefaults` 在 `inst.SSLMode == ""`（config.go:110）处对 nil 解引用，直接 panic。

## 复现（已实证）

```yaml
instances:
  -
  - name: ok
```

`config.Load` 以 `runtime error: invalid memory address or nil pointer dereference` panic（stderr 带堆栈），而不是走文档化的"配置加载失败"错误路径。

## 位置

`internal/config/config.go:108-124`（SetDefaults 的实例循环）与 `config.go:241-245`（Load 中 SetDefaults 先于 validate）

## 建议修复

任选其一：
1.…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/6) · 2026-09-10 · closed · 1 comment

### main: SIGINT/SIGTERM 优雅退出被当作崩溃（退出码 1）

## 问题描述

SIGINT/SIGTERM 触发 `signal.NotifyContext` 取消后，MCP SDK 的 `Server.Run` 返回 `ctx.Err()`（`context.Canceled`，见 vendor 内 `mcp/server.go:1295-1303`）。而 `main` 中 `start` 返回的任何非空错误都走 `logger.Fatalf` → `os.Exit(1)`。

结果：**每次正常关闭都被记录为崩溃**——日志打出 `gaussdb-ro-mcp 退出: context canceled`，退出码 1。与第 55 行"优雅退出"注释矛盾；`systemd Restart=on-failure` 会重启循环，MCP 客户端会把服务器记为异常退出。

## 复现

启动 stdio 服务器 → 发送 SIGTERM（或客户端关闭 agent 时的常规信号）→ 观察退出码 1 与 fatal 日志。

## 位置

`cmd/gaussdb-ro-mcp/main.go:70`（`start` 返回 `server.Run`…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/5) · 2026-09-10 · closed · 1 comment

### [安全] db: enforceReadOnly 中 SET statement_timeout 先于 ROLLBACK，破坏只读回退路径且可致连接无超时入池

## 问题描述

`enforceReadOnly` 的执行顺序是：先 `SET statement_timeout`，再回读校验只读，失败才 `ROLLBACK` + 会话级 `SET READ ONLY` 回退。这个顺序破坏了注释（manager.go:113-118）所描述的恢复路径：

- **场景 a（中止的残留事务）**：`SET` 本身失败（SQLSTATE 25P02），函数提前返回错误——注释里的 `ROLLBACK` 回退**永远执行不到**，连接被拒绝，且报错信息误导性地显示"设置 statement_timeout 失败"。
- **场景 b（打开的残留事务）**：`SET` 成功但属事务作用域，随后的 `ROLLBACK`（回退路径的一部分）**把它回滚掉了且从未重设**——连接带着**无服务端超时**的状态入池，长查询只剩客户端 ctx 一条边界。

## 位置

`internal/db/manager.go:101-129`（第 103 行 SET 与第 117 行 ROLLBACK 的顺序）

## 建议修复

调整顺序：回读校验失败 → 先…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/4) · 2026-09-10 · closed · 1 comment

### [安全] guard: 咨询锁黑名单枚举了不存在的函数名，遗漏 pg_try_advisory_* 系列（可致持久 DoS）

## 问题描述

`defaultBlockedFunctions` 中咨询锁一节枚举了三个**不存在的函数名**：

- `pg_advisory_share_lock`（不存在）
- `pg_advisory_share_lock_shared`（不存在）
- `pg_advisory_xact_share_lock`（不存在）

同时**遗漏了真实的**：

- `pg_try_advisory_lock`、`pg_try_advisory_xact_lock`
- `pg_advisory_lock_shared`、`pg_advisory_xact_lock_shared`、`pg_try_advisory_xact_lock_shared`
- `pg_try_advisory_lock_shared`

而 README 和 `gaussdb-ro-mcp.example.yaml` 均声称 `pg_advisory_*` 默认被拦截。

## 影响

咨询锁在只读事务中是允许的，**第 2 层会话只读无法兜底**：

```sql
SELECT…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/3) · 2026-09-10 · closed · 1 comment

### [安全] guard: 引号标识符函数名（"dblink"(...) 等）完全绕过危险函数黑名单

## 问题描述

`tokenize` 给引号标识符 token 加了 `\x00` 前缀（`guard.go:241`），使其不参与关键字匹配；但危险函数黑名单检查以 `isWordToken(t)` 为门槛（`guard.go:86`），而 `isWordToken` 拒绝以 `\x00` 开头的 token——于是**用引号包裹函数名即可完全绕过黑名单**：

```sql
SELECT "dblink"('host=10.0.0.1 dbname=x', 'insert into t values(1)')   -- 通过
SELECT "set_config"('statement_timeout','0',false)                     -- 通过
SELECT pg_catalog."pg_read_file"('postgresql.conf',0,100)              -- 通过
SELECT "nextval"('s')                                                  -- 通过…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/2) · 2026-09-10 · closed · 1 comment

### [安全] guard: E''/U&'' 反斜杠判定忽略邻接关系，字符串边界反转可隐藏 set_config/dblink 调用

## 问题描述

`internal/guard/guard.go` 的 `tokenize` 在处理字符串字面量时，通过检查**前一个 token 是否为 `e`/`u`** 来决定是否启用反斜杠转义（E''/U&'' 语法），但忽略了邻接关系：即使 `e`/`u` 只是一个普通的列名/别名 token（与后面的字符串之间隔着逗号等标点），也会让后续字符串按转义语法 lex。

这会**反转 guard 与服务端之间的字符串边界认定**：

- **绕过方向**：guard 认为 `'\'` 是转义字符串的一部分，把紧随其后的 `set_config(...)`、`dblink(...)` 等吞进字符串；而服务端（`standard_conforming_strings=on`）认为 `'\'` 是完整字符串，危险函数真实执行。
- **误拒方向**：合法 SQL `SELECT e, '\' FROM t`（`e` 是列名）被误报"SQL 存在未闭合的字符串字面量"。

## 复现（已实证）

```sql
SELECT e, '\',…

[Read the thread](https://github.com/gxc/gaussdb-ro-mcp/issues/1) · 2026-09-10 · closed · 1 comment

The remaining reports are on [the project's issue tracker](https://github.com/gxc/gaussdb-ro-mcp/issues).
