Commit Graph
11 Commits
Author SHA1 Message Date
dsql 1ffb45dd9d fix: no query%args on no-param DDL (literal % survives); don't shred a str/bytes param; reject autocommit=None
three mysql-only defects from the fable deep-scan, all from the 0e01c4e/50388de waves:
- create_table/create_database/drop passed an explicit () that forced query%args, so a
  literal % in a caller's column decl (COMMENT 'save 10%') raised a TypeError that bypassed
  the _DRIVER_ERRORS wrapper. no-param statements now pass None (no substitution) and quote
  identifiers via _quote_ident_literal (no %-doubling, since nothing collapses it).
- _run/_fetchall/_fetchone blind-tuple(params) exploded a bare str/bytes param into
  per-character args; _norm_params now passes a str/bytes through as one bound value and
  normalizes an empty sequence to None. multi-param tuples/lists/dicts unchanged.
- the autocommit guard checked 'is False' only, so autocommit=None slipped past and, against
  a server with autocommit=0, silently never committed; it now rejects any non-True value.
psql is unaffected (asyncpg binds $1 with no query%args) - Layer-1 signatures still byte-match.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-06 16:38:05 -04:00
dsql eba5cd41f6 docs: add __aenter__/__aexit__ one-liner docstrings for twin parity with redis
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-06 02:55:57 -04:00
dsql 0e01c4e77f fix: %-escape identifiers only on the params path (no more %% wrong-target on DDL)
_quote_ident() always doubles % to %%, but aiomysql only runs query % args
substitution when args is not None - _run/_fetchall/_fetchone were passing
params or None, so no-params DDL (create_database/create_table/drop) and
no-condition get/delete/exists shipped literal %% to the server, silently
targeting the wrong object and making IF EXISTS no-op (regression 2e837da).

_run/_fetchall/_fetchone now forward None as-is (preserving layer-2's
documented "no params, no substitution" contract) but always pass a real
tuple otherwise, so layer-1's %%-escaped identifiers always collapse back
to a single % as intended. The three no-params DDL verbs now pass an
explicit empty tuple so substitution runs for them too.

connect()/close() had the same lock asymmetry as psql (close() wasn't
guarded by _connect_lock, so a close() racing an in-flight connect() could
no-op while the new pool went live) - fixed in lockstep with the psql twin
fix, and create_pool() is now inside the same try/log/re-raise as the
SELECT-1 validation.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-03 19:11:39 -04:00
dsql ebf1f80652 refactor: derive __version__ from package metadata (single source)
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-03 17:00:32 -04:00
dsql f45db389ba docs: compress prose/module docstrings, em-dash->hyphen (de-bloat wave 1)
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-03 00:16:12 -04:00
dsql 2e837da7df fix: close() nulls _pool + connect() lock (psql-7/8 twin), % in identifiers, doc caveats
close() now nulls self._pool so a closed instance reports not-connected instead of
masquerading as live (twin of psql-7); connect() is guarded by an internal asyncio.Lock
so concurrent connect() calls serialize instead of racing to create and orphan multiple
live pools (twin of psql-8). _quote_ident now escapes a literal % in identifiers (mysql-7:
PyMySQL's query % args substitution otherwise breaks any Layer-1 call against a %-bearing
table/column name). fetchval/exists tolerate a caller-overridden tuple cursorclass via a
new _first_value helper (mysql-8). Documents the %%-escaping rule for literal % in raw SQL
text (mysql-6) and the upsert() VALUES() deprecation on MySQL 8.0.20+ (mysql-9).

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-02 23:32:53 -04:00
dsql 50388de22c fix: reject autocommit=False override and IS NULL condition handling
_where() rendered None conditions as col = %s bound to NULL, which sql
never matches, so get/get_one/exists/delete silently missed NULL rows
despite insert() writing NULL fine — fixed to emit col IS NULL, in
lockstep with the psql lib's identical fix.

Separately, __init__ now rejects autocommit=False in pool_kwargs: with
it, _run/_fetchall never commit, so on pool release aiomysql closes the
in-transaction connection and MySQL rolls back server-side while
insert()/delete()/upsert()/execute() still return success signals
(lastrowid/rowcount) for writes that were silently discarded.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-02 16:44:34 -04:00
dsql a46c4b13fc fix: connect() orphan (lockstep psql-3) + await pool.release() (mysql-1)
- connect() closes an existing pool on re-connect and tears down the built pool on SELECT-1
  validation failure (sibling of psql-3).
- _Transaction now awaits pool.release() in both __aexit__ and the __aenter__ failure path,
  matching psql and aiomysql's idiom (no un-awaited _wakeup task per transaction).
- README: insert-returns-lastrowid headline precision + upsert-rowcount convention note.
verified vs real MariaDB (re-connect, 5-tx release, full suite). bump v0.1.1 -> v0.1.2

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-07-01 00:28:27 -04:00
dsql 2372b1ecd1 fix: transaction() releases the pooled connection when begin() fails (lockstep w/ psql-1)
same acquire-then-fail leak as psql: begin() failing after acquire leaked the conn. release
on failure. verified against MariaDB: 6 forced begin-failures no longer drain the pool.
bump v0.1.0 -> v0.1.1

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-30 21:04:43 -04:00
dsql 9034ce32c2 add package: pyproject + src (layer-1 identical to psql, raw layer-2, fail-loud, dict rows)
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-30 20:12:40 -04:00
dsql 9beeee4d9f init: async mysql/mariadb wrapper over aiomysql (two-layer API, class MysqlDB)
Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-30 20:12:40 -04:00