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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions