Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ dmPython.connect(
- `packet_size`
- `port`

`compress_msg` 仅在建连时接受 `DSQL_FALSE` 或 `DSQL_TRUE`,属性读回为协商后的布尔压缩状态。语句句柄缓存尚未实现:`use_stmt_pool=0` 可用,`use_stmt_pool=1` 会报错。

只读:

- `server_code`
Expand Down
6 changes: 5 additions & 1 deletion docs/test-results/2026-09-26-type-connection-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
| 空值 | 整数、十进制、文本、二进制、日期、时间戳列的 `NULL` 往返 |
| 连接地址 | `server`、`host`、`dsn=host:port` 均可连接;`host` 与 `server` 同时传入会报错 |
| 连接行为 | 默认元组与 `DictCursor` 行形状、`schema`、`autoCommit` 开关及开启后无需显式提交的持久化、`txn_isolation` 读回 |
| 选项基础检查 | 非数字 `port` 报错;`connection_timeout`、`login_timeout`、`compress_msg`、`use_stmt_pool` 可建连并执行查询 |
| 选项基础检查 | 非数字 `port` 报错;`connection_timeout`、`login_timeout` 可建连并执行查询;消息压缩和语句池行为见下文 |

`DECIMAL`、`FLOAT`、`DOUBLE`、`TIME`、`TIMESTAMP` 在当前默认读取路径通常返回字符串。矩阵断言数值或时间内容,同时保留未来改善 Python 返回类型的空间。

Expand Down Expand Up @@ -65,3 +65,7 @@
## 后续修复:区间子类型

桥接层原先只识别 `DAY TO SECOND` 和 `YEAR TO MONTH` 两种区间列;真实 DM8 中其余 11 种区间限定形式均被读成普通字符串。现在按 DPI 类型码识别全部 13 种形式。`DAY`、`HOUR`、`MINUTE`、`SECOND` 及其组合读为 `datetime.timedelta`,`YEAR`、`MONTH` 和 `YEAR TO MONTH` 保留 `dmPython.YEAR_MONTH_INTERVAL` 描述类型;部分子类型的正负 `timedelta` 写入也验证通过。Python 3.10 在 UTF-8 和 GB18030 官方 ARM DM8 实例上的完整真实库回归各为 **183 passed、0 skipped**,另各有 2 个非真实库用例未选入。

## 后续修复:连接消息压缩

`compress_msg` 原先被静默忽略;现在只接受 `DSQL_FALSE`/`DSQL_TRUE`,将选择传给 Go 驱动并从协商后的连接状态读回。两种取值均已在真实 DM8 上建连和查询成功。底层 Go 驱动只解析 `StmtPoolSize`,没有使用对应缓存大小;因此显式请求 `use_stmt_pool=1` 现在会报不支持,`use_stmt_pool=0` 可用。语句句柄缓存仍待实现。
42 changes: 40 additions & 2 deletions dpi_bridge/dpi_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type connHandle struct {
loginTimeout int
connTimeout int
appName string
compressMsg int
svcPath string
mppLogin int
rwSeparate int
Expand All @@ -82,6 +83,7 @@ func newConnHandle(env *envHandle) *connHandle {
port: DSQL_DEAFAULT_TCPIP_PORT,
autocommit: false,
loginTimeout: 5000,
compressMsg: -1,
mppLogin: -1,
rwSeparate: -1,
rwPercent: -1,
Expand Down Expand Up @@ -227,6 +229,21 @@ func dpi_set_con_attr(hcon C.dhcon, attrID C.sdint4, val C.dpointer, valLen C.sd
return DSQL_ERROR
}
conn.rwPercent = intVal
case DSQL_ATTR_COMPRESS_MSG:
if conn.conn != nil {
conn.lastErr = &diagInfo{errorCode: -1, message: "compress_msg can only be set before login"}
return DSQL_ERROR
}
if intVal != 0 && intVal != 1 {
conn.lastErr = &diagInfo{errorCode: -1, message: "compress_msg must be DSQL_FALSE (0) or DSQL_TRUE (1)"}
return DSQL_ERROR
}
conn.compressMsg = intVal
case DSQL_ATTR_USE_STMT_POOL:
if intVal != 0 {
conn.lastErr = &diagInfo{errorCode: -1, message: "use_stmt_pool is not supported by this bridge"}
return DSQL_ERROR
}
case DSQL_ATTR_SSL_PATH, DSQL_ATTR_SSL_PWD, DSQL_ATTR_UKEY_NAME, DSQL_ATTR_UKEY_PIN:
if val != nil && C.GoString((*C.char)(val)) != "" {
name := map[int32]string{
Expand All @@ -236,8 +253,7 @@ func dpi_set_con_attr(hcon C.dhcon, attrID C.sdint4, val C.dpointer, valLen C.sd
conn.lastErr = &diagInfo{errorCode: -1, message: name + " is not supported by this bridge"}
return DSQL_ERROR
}
case DSQL_ATTR_COMPRESS_MSG, DSQL_ATTR_USE_STMT_POOL,
DSQL_ATTR_CURSOR_ROLLBACK_BEHAVIOR,
case DSQL_ATTR_CURSOR_ROLLBACK_BEHAVIOR,
DSQL_ATTR_OSAUTH_TYPE, DSQL_ATTR_DDL_AUTOCOMMIT,
DSQL_ATTR_COMPATIBLE_MODE, DSQL_ATTR_SHAKE_CRYPTO,
DSQL_ATTR_NLS_NUMERIC_CHARACTERS,
Expand Down Expand Up @@ -304,6 +320,20 @@ func dpi_get_con_attr(hcon C.dhcon, attrID C.sdint4, val C.dpointer, bufLen C.sd
if valLen != nil {
*valLen = 4
}
case DSQL_ATTR_COMPRESS_MSG:
value := conn.compressMsg
if value < 0 {
value = 0
}
*(*C.sdint4)(val) = C.sdint4(value)
if valLen != nil {
*valLen = 4
}
case DSQL_ATTR_USE_STMT_POOL:
*(*C.sdint4)(val) = 0
if valLen != nil {
*valLen = 4
}
case DSQL_ATTR_AUTOCOMMIT:
v := C.udint4(0)
if conn.autocommit {
Expand Down Expand Up @@ -448,6 +478,9 @@ func dpi_login(hcon C.dhcon, svr *C.sdbyte, user *C.sdbyte, pwd *C.sdbyte) C.DPI
if conn.appName != "" {
params = append(params, "appName="+url.QueryEscape(conn.appName))
}
if conn.compressMsg >= 0 {
params = append(params, "compress="+strconv.Itoa(conn.compressMsg))
}
if conn.svcPath != "" {
params = append(params, "svcConfPath="+url.QueryEscape(filepath.Join(conn.svcPath, "dm_svc.conf")))
}
Expand Down Expand Up @@ -512,6 +545,11 @@ func dpi_login(hcon C.dhcon, svr *C.sdbyte, user *C.sdbyte, pwd *C.sdbyte) C.DPI

conn.db = db
conn.conn = dmConn
if dmConn.CompressionMode() != 0 {
conn.compressMsg = 1
} else {
conn.compressMsg = 0
}

// Try to get server version
var version string
Expand Down
9 changes: 9 additions & 0 deletions dpi_bridge/third_party/chunanyong_dm/PATCHES.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@
against both UTF-8 and GB18030 databases; `clob_encoding_test.go` covers
ASCII, two-byte, and four-byte boundaries.

## Patch: expose negotiated compression mode

- File: `bridge_options.go`
- Expose the negotiated compression mode so the DPI bridge can report the
effective `compress_msg` setting after login rather than only echoing the
requested value.
- Regression: `test_compress_msg_is_applied` connects with compression both
disabled and enabled against a real DM8 instance.

## Patch: initial connection timeout through endpoint groups

- Files: `a.go`, `n.go`, `x.go`, `y.go`, `m.go`
Expand Down
9 changes: 9 additions & 0 deletions dpi_bridge/third_party/chunanyong_dm/bridge_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package dm

// CompressionMode reports the negotiated wire-compression mode to the DPI bridge.
func (dc *DmConnection) CompressionMode() int {
if dc.dmConnector == nil {
return 0
}
return dc.dmConnector.compress
}
31 changes: 29 additions & 2 deletions tests/integration/test_p1_connection_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,7 @@ def test_port_rejects_non_numeric_value(conn_params):
[
{"connection_timeout": 5},
{"login_timeout": 5000},
{"compress_msg": 0},
{"use_stmt_pool": 1},
{"use_stmt_pool": 0},
],
)
def test_optional_setting_connects_and_queries(conn_params, option):
Expand All @@ -271,6 +270,34 @@ def test_optional_setting_connects_and_queries(conn_params, option):
assert cur.fetchone() == (1,)


@pytest.mark.parametrize("enabled", [dmPython.DSQL_FALSE, dmPython.DSQL_TRUE])
def test_compress_msg_is_applied(conn_params, enabled):
with dmPython.connect(**conn_params, compress_msg=enabled) as conn:
assert conn.compress_msg == enabled
with conn.cursor() as cur:
cur.execute("SELECT 1")
assert cur.fetchone() == (1,)


@pytest.mark.parametrize("value", [-1, 2])
def test_compress_msg_rejects_invalid_values(conn_params, value):
with pytest.raises((dmPython.Error, OverflowError)) as exc:
dmPython.connect(**conn_params, compress_msg=value)
if isinstance(exc.value, dmPython.Error):
assert "compress_msg" in str(exc.value)


def test_use_stmt_pool_rejects_unavailable_cache(conn_params):
with pytest.raises(dmPython.Error, match="use_stmt_pool.*not supported"):
dmPython.connect(**conn_params, use_stmt_pool=1)


def test_compress_msg_rejects_post_login_change(conn_params):
with dmPython.connect(**conn_params) as conn:
with pytest.raises(dmPython.Error, match="before login"):
conn.compress_msg = dmPython.DSQL_TRUE


def test_connection_timeout_options_are_reported(conn_params):
with dmPython.connect(
**conn_params,
Expand Down
Loading