v2.1.0
新功能 (New Features)¶
1. 连接初始化 SQL(init_sql)¶
新增 init_sql 参数,支持在每条新 TCP 连接首次使用时自动执行预设 SQL。适用于 ClickHouse 异步插入配置、MySQL Session 变量设置等场景。
背景:
在 ClickHouse 等数据库中,SET 语句的作用域为 TCP 连接级别。之前每次执行 INSERT 前都需要手动执行 SET 语句来配置异步插入等参数,冗余且低效。init_sql 特性将这一能力下沉到引擎层,实现连接级自动化。
工作原理:
init_sql的执行在Result.__call__()中实现- 使用
weakref.WeakKeyDictionary以 Pool 为 key 追踪已初始化的连接 ID(id(conn)) - 新连接首次执行 SQL 时自动执行
init_sql,后续复用同一连接时跳过 - 连接被
pool_recycle回收重建后,新连接会重新执行init_sql init_sql执行后会立即commit(),确保 SET 语句生效
使用方式:
from asmysql import Engine
# 通过关键字参数设置
engine = Engine(
host="192.168.62.195",
port=9004,
user="default",
password="",
init_sql=(
"SET async_insert = 1, wait_for_async_insert = 0, "
"async_insert_busy_timeout_ms = 1000, "
"max_execution_time = 30"
),
)
await engine.connect()
# 通过 URL 参数设置
engine = Engine(
url="mysql://user:pass@127.0.0.1:3306/?init_sql=SET SESSION wait_timeout=600"
)
await engine.connect()
向后兼容:
init_sql 默认值为 None,不传时行为与 v2.0.0 完全一致,不影响现有代码。
重大变更 (Breaking Changes)¶
1. 自动提交机制重构¶
移除了 auto_commit Engine 参数和 commit execute/Result 参数,改为基于 SQL 语句类型的自动提交机制:
Result内部通过_should_commit()方法分析 SQL 语句的首关键词- 写操作关键词(INSERT、UPDATE、DELETE、REPLACE、CREATE、ALTER、DROP 等)执行后自动
COMMIT - 读操作(SELECT)不触发
COMMIT stream=True模式下不自动COMMIT(避免中断流)
迁移指南:
# v2.0.0 写法(已废弃)
engine = Engine(url="...", auto_commit=False)
result = await engine.execute("INSERT ...", commit=False)
# v2.1.0 写法
engine = Engine(url="...")
async with engine.execute("INSERT ...") as result:
pass # 写操作自动提交
2. 移除 Engine 的 stream 参数¶
stream 不再是 Engine 类属性或构造参数,仅在 execute() / execute_many() 方法级别使用。
3. 移除 Engine 的 result_class 参数¶
result_class 不再是 Engine 类属性或构造参数,仅在 execute() / execute_many() 方法级别使用,默认值为 tuple。
迁移指南:
# v2.0.0 写法(已废弃)
engine = Engine(url="...", result_class=dict)
async with engine.execute("SELECT ...") as result:
data = await result.fetch_one() # data is dict
# v2.1.0 写法
engine = Engine(url="...")
async with engine.execute("SELECT ...", result_class=dict) as result:
data = await result.fetch_one() # data is dict
缺陷修复 (Bug Fixes)¶
- 修复
fetch_all()自定义结果类型连接泄漏:当result_class为自定义类(如 Pydantic BaseModel)时,return _data在close()之前退出,每次调用泄漏一个连接。已统一return路径确保close()始终执行 - 修复查询错误后
close()双重释放:__call__()的 except 分支关闭 cursor 并释放连接后未将__cursor置空,后续close()会再次释放同一连接。已在 except 中添加self.__cursor = None - 修复
async for+break连接泄漏:__aiter__返回self导致break后连接无法释放。已重写为 async generator +try/finally模式 - 修复
Result.__call__()仅捕获MySQLError导致连接泄漏:非 MySQL 异常未被捕获,已获取的连接不会释放。已改为except Exception并在非 MySQL 错误时 re-raise - 修复
__del__安全网崩溃:__del__在 GC 期间无错误处理,self.pool可能为 None。已添加try/except和 pool 空值检查 - 修复
echo_sql_logURL 参数无法设为 False:?echo_sql_log=false被解析为True。已改为正确解析布尔字符串 - 修复
or运算符覆盖合法 falsy 值:Engine(min_pool_size=0)的0被or覆盖为默认值。已统一改为x if x is not None else default - 修复
connect()/disconnect()并发竞态条件:多个协程同时调用可创建多个连接池或双重关闭。已添加asyncio.Lock保护 - 修复
execute()传入self.__pool(可能为 None):未连接时 Result 获得 None pool。已改用self.pool属性 - 修复
Result[type[T]]泛型参数语义错误:应为Result[T]。已修正泛型参数 - 修复
release_connections()缺少空值检查:未连接时调用抛出AttributeError。已添加空值检查 - 移除
@lru_cache装饰器:__repr__/__str__上的@lru_cache持有对self的强引用,阻止 GC。已移除缓存 - 修复
error_msg未使用err_msg()格式化:丢失了对空字符串的回退处理。已改为调用err_msg()函数 - 修复
iterate()使用 truthiness 检查不一致:if data:将空元组视为 falsy。已统一为is not None检查 - 修复
row_count/last_rowid/row_number在 close 后AttributeError:cursor 被置空后访问崩溃。已添加空值检查 - DDL/DML 语句自动释放连接:
cursor.description is None时(DDL/INSERT/UPDATE 等),自动关闭游标并释放连接回连接池,同时缓存rowcount/lastrowid
文档 (Documentation)¶
- 修正首页示例代码:修复中英文
index.md中使用了不存在的 API,统一使用正确的async with engine.execute()+result.iterate()模式。execute()/execute_many()返回Result对象(非协程),必须通过async with或async for使用 - 新增
init_sql文档:在connection.md、api.md、examples.md中同步添加init_sql参数说明和使用示例(中英文版本) - 修正事务控制文档:移除
auto_commit/commit参数描述,改为描述基于 SQL 语句类型的自动提交机制 - 修正 API 文档签名:移除
fetch_one()/fetch_many()的close参数,移除execute()/execute_many()的commit参数
测试 (Testing)¶
- 新增
test_engine_init_sql.py测试文件,覆盖init_sql核心功能与边界场景 - 重写集成测试
enginefixture,通过init_sql=USE <test_db>在每条连接池连接上自动选择测试数据库 - 移除
test_mock_engine.py中与 Engineresult_class相关的测试用例