Skip to content

DPI透明重连后 SET SCHEMA 丢失,导致偶发"无效的表或视图名"错误 #3

Description

@VoidBird

问题现象

使用 dmPython 连接达梦数据库时,偶尔报错:

[CODE:-2106] 无效的表或视图名

但实际上数据库中该表/视图确实存在。将客户端断开重连后,同样的 SQL 可以正常执行,问题消失。该问题是偶发性的,无法稳定复现。


根因分析

核心问题:SET SCHEMA 仅在 cursor 创建时执行一次

Cursor_SetSchema_And_Parsetype()Cursor.c 第 170-236 行)负责通过 dpi_exec_direct 执行 SET SCHEMA "xxx",但该函数仅在 Cursor_New() 中调用Cursor.c 第 260 行)。

完整的 SQL 执行路径中,没有任何地方重新设置 schema

Python: cursor.execute(sql)
  → Cursor_Execute()                          [Cursor.c:2285]
    → Cursor_Execute_inner()                  [Cursor.c:2149]
      → Cursor_InternalPrepare()              [Cursor.c:650]   ← 没有调用 SetSchema
        → dpi_prepare(handle, sql)
      → Cursor_InternalExecDirect()           [Cursor.c:706]   ← 没有调用 SetSchema
        → dpi_exec_direct(handle, sql)

触发条件:DPI 层透明重连

当底层网络出现超时或闪断时,达梦 DPI 层会在驱动无感知的情况下透明重连:

  1. TCP 连接断开
  2. DPI 内部自动 logoutlogin,建立新会话
  3. 驱动层的 isConnected 仍为 1,认为连接正常
  4. 但新会话的当前 schema 已回退为用户的默认 schema,而非业务 SET SCHEMA 指定的 schema
  5. 后续查询在错误的 schema 下执行 → 报"无效的表或视图名"

次要问题:Ping 重连后也未恢复 schema

Connection_Ping_inner()Connection.c 第 1041-1116 行)在检测到断连并重连(dpi_logoutdpi_login)后,也没有重新执行 SET SCHEMA。即使显式调用 conn.ping(reconnect=True),schema 仍会丢失。


建议修复

Cursor_InternalPrepare()Cursor_InternalExecDirect() 中,于 Cursor_clearDescExecInfo() 之后、dpi_prepare()/dpi_exec_direct() 之前,增加 Cursor_SetSchema_And_Parsetype() 调用:

Cursor.c 第 666 行附近 — Cursor_InternalPrepare()

    /* 清理上次的描述和执行信息 */
    Cursor_clearDescExecInfo(self, 1);

+   /* 确保 schema 设置正确(DPI 透明重连后可能丢失) */
+   if (Cursor_SetSchema_And_Parsetype(self) < 0)
+       return -1;

    // prepare statement
    Py_BEGIN_ALLOW_THREADS
        status = dpi_unbind_params(self->handle);
        status = dpi_prepare(self->handle, (sdbyte*)statementBuffer.ptr);
    Py_END_ALLOW_THREADS

Cursor.c 第 720 行附近 — Cursor_InternalExecDirect()

    /* 清理上次的描述和执行信息 */
    Cursor_clearDescExecInfo(self, 1);

+   /* 确保 schema 设置正确(DPI 透明重连后可能丢失) */
+   if (Cursor_SetSchema_And_Parsetype(self) < 0)
+       return -1;

    // execute directly
    Py_BEGIN_ALLOW_THREADS
        status = dpi_exec_direct(self->handle, (sdbyte*)statementBuffer.ptr);
    Py_END_ALLOW_THREADS

为什么只改这两处就够了:

  • prepareexec_direct所有 SQL 操作的必经路径(查询/插入/更新/DDL 都走这里)
  • Connection_Ping_inner 重连后,下一次 execute 自然会触发 schema 恢复
  • DPI 透明重连后,同理
  • Cursor_SetSchema_And_Parsetype() 内部已判断:若 connection->schemaNone 则直接返回 0,零额外开销

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions