v2.1.0
New Features¶
1. Connection Init SQL (init_sql)¶
Added init_sql parameter to automatically execute preset SQL on each new TCP connection's first use. Ideal for ClickHouse async insert configuration, MySQL session variable setup, and similar scenarios.
Background:
In databases like ClickHouse, SET statements are scoped at the TCP connection level. Previously, SET had to be manually executed before every INSERT to configure async insert parameters, which was redundant and inefficient. The init_sql feature moves this capability down to the engine layer for automatic per-connection initialization.
How It Works:
init_sqlexecution is implemented inResult.__call__()- Uses
weakref.WeakKeyDictionarywith Pool as key to track initialized connection IDs (id(conn)) - Executes
init_sqlon first SQL execution of a new connection; skips on subsequent reuses - When a connection is recycled via
pool_recycle, the new connection re-executesinit_sql - After
init_sqlexecution,commit()is called immediately to ensure SET statements take effect
Usage:
from asmysql import Engine
# Via keyword argument
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()
# Via URL parameter
engine = Engine(
url="mysql://user:pass@127.0.0.1:3306/?init_sql=SET SESSION wait_timeout=600"
)
await engine.connect()
Backward Compatible:
init_sql defaults to None. When not provided, behavior is identical to v2.0.0 — no impact on existing code.
Breaking Changes¶
1. Auto-Commit Mechanism Refactored¶
Removed auto_commit Engine parameter and commit execute/Result parameter, replaced with SQL statement type-based auto-commit mechanism:
Resultinternally uses_should_commit()method to analyze the first keyword of SQL statements- Write operation keywords (INSERT, UPDATE, DELETE, REPLACE, CREATE, ALTER, DROP, etc.) automatically
COMMITafter execution - Read operations (SELECT) do not trigger
COMMIT stream=Truemode does not auto-COMMIT(to avoid interrupting the stream)
Migration Guide:
# v2.0.0 usage (deprecated)
engine = Engine(url="...", auto_commit=False)
result = await engine.execute("INSERT ...", commit=False)
# v2.1.0 usage
engine = Engine(url="...")
async with engine.execute("INSERT ...") as result:
pass # Write operations auto-commit
2. Removed Engine stream Parameter¶
stream is no longer an Engine class attribute or constructor parameter, only available at the execute() / execute_many() method level.
3. Removed Engine result_class Parameter¶
result_class is no longer an Engine class attribute or constructor parameter, only available at the execute() / execute_many() method level, defaulting to tuple.
Migration Guide:
# v2.0.0 usage (deprecated)
engine = Engine(url="...", result_class=dict)
async with engine.execute("SELECT ...") as result:
data = await result.fetch_one() # data is dict
# v2.1.0 usage
engine = Engine(url="...")
async with engine.execute("SELECT ...", result_class=dict) as result:
data = await result.fetch_one() # data is dict
Bug Fixes¶
- Fix
fetch_all()connection leak for custom result types: Whenresult_classis a custom class (e.g., Pydantic BaseModel),return _dataexited beforeclose(), leaking one connection per call. Unified return path to ensureclose()always executes - Fix double connection release after query error:
__call__()except branch closed cursor and released connection but didn't nullify__cursor. Addedself.__cursor = Nonein except block - Fix
async for+breakconnection leak:__aiter__returnedself, so afterbreak, connection was never released. Rewritten as async generator withtry/finally - Fix
Result.__call__()only catchingMySQLErrorcausing connection leak: Non-MySQL exceptions were uncaught, leaving acquired connections unreleased. Changed toexcept Exceptionwith re-raise for non-MySQL errors - Fix
__del__safety net crashes:__del__during GC had no error handling andself.poolcould beNone. Addedtry/exceptand pool null check - Fix
echo_sql_logURL parameter cannot be set to False:?echo_sql_log=falsewas parsed asTrue. Now correctly parses boolean strings - Fix
oroperator overriding legitimate falsy values:Engine(min_pool_size=0)had0overridden to default. Unified tox if x is not None else default - Fix
connect()/disconnect()concurrent race conditions: Multiple concurrent coroutines could create multiple pools or double-close. Addedasyncio.Lockprotection - Fix
execute()passingself.__pool(possibly None): Disconnected calls produced Result with None pool. Now usesself.poolproperty - Fix
Result[type[T]]generic parameter semantic error: Should beResult[T]. Corrected generic parameter - Fix
release_connections()missing null check: Called before connect(), raisedAttributeError. Added null check - Remove
@lru_cachedecorators:@lru_cacheon__repr__/__str__held strong references toself, preventing GC. Removed caching - Fix
error_msgnot usingerr_msg()formatting: Lost fallback handling for empty strings. Now callserr_msg()function - Fix
iterate()truthiness check inconsistency:if data:treated empty tuples as falsy. Unified tois not Nonecheck - Fix
row_count/last_rowid/row_numberAttributeErrorafter close: Accessing these properties after cursor was nullified caused crashes. Added null check - DDL/DML statements auto-release connection: When
cursor.description is None(DDL/INSERT/UPDATE etc.), automatically closes cursor and releases connection back to pool, while cachingrowcount/lastrowid
Documentation¶
- Fixed homepage examples: Corrected non-existent API usage in both Chinese and English
index.md, unified to use the correctasync with engine.execute()+result.iterate()pattern.execute()/execute_many()returnResultobjects (not coroutines) and must be used viaasync withorasync for - Added
init_sqldocumentation: Synchronizedinit_sqlparameter descriptions and usage examples acrossconnection.md,api.md, andexamples.md(both Chinese and English versions) - Fixed transaction control documentation: Removed
auto_commit/commitparameter descriptions, replaced with SQL statement type-based auto-commit mechanism description - Fixed API documentation signatures: Removed
closeparameter fromfetch_one()/fetch_many(), removedcommitparameter fromexecute()/execute_many()
Testing¶
- Added
test_engine_init_sql.pytest file coveringinit_sqlcore functionality and edge cases - Rewrote integration test
enginefixture to auto-select a test database viainit_sql=USE <test_db>on each pool connection - Removed Engine
result_classrelated test cases fromtest_mock_engine.py