fix: layer-1 verbs always substitute params, so a % identifier reaches the server intact
T3-16: _norm_params turned an empty params list into None, so aiomysql skipped query % args and a no-condition get/get_one/exists/delete on a table named 50%_off sent `50%%_off` (table not found). layer 1 now passes its params list as-is; only the layer-2 methods normalize. Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
1 parent
febace7d5d
commit
7eb5d552f5
1 file changed
+10
-8
+10
-8
@@ -198,18 +198,20 @@ class MysqlDB:
|
|||||||
async def _run(self, query: str, params: Optional[Sequence] = None):
|
async def _run(self, query: str, params: Optional[Sequence] = None):
|
||||||
"""execute a statement on a pooled cursor; return (rowcount, lastrowid)
|
"""execute a statement on a pooled cursor; return (rowcount, lastrowid)
|
||||||
|
|
||||||
params go through `_norm_params` (see it for the no-substitution/str-bytes handling)
|
params are passed to aiomysql as-is: layer 1 always hands a list (even empty) so
|
||||||
|
`query % args` runs and collapses `_quote_ident`'s `%%`; layer 2 normalizes first
|
||||||
|
via `_norm_params`
|
||||||
"""
|
"""
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
async with conn.cursor() as cur:
|
async with conn.cursor() as cur:
|
||||||
await cur.execute(query, _norm_params(params))
|
await cur.execute(query, params)
|
||||||
return cur.rowcount, cur.lastrowid
|
return cur.rowcount, cur.lastrowid
|
||||||
|
|
||||||
async def _fetchall(self, query: str, params: Optional[Sequence] = None) -> List[dict]:
|
async def _fetchall(self, query: str, params: Optional[Sequence] = None) -> List[dict]:
|
||||||
"""execute a query and return all rows as dicts"""
|
"""execute a query and return all rows as dicts"""
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
async with conn.cursor() as cur:
|
async with conn.cursor() as cur:
|
||||||
await cur.execute(query, _norm_params(params))
|
await cur.execute(query, params)
|
||||||
rows = await cur.fetchall()
|
rows = await cur.fetchall()
|
||||||
return list(rows)
|
return list(rows)
|
||||||
|
|
||||||
@@ -217,7 +219,7 @@ class MysqlDB:
|
|||||||
"""execute a query and return the first row dict, or None"""
|
"""execute a query and return the first row dict, or None"""
|
||||||
async with self.pool.acquire() as conn:
|
async with self.pool.acquire() as conn:
|
||||||
async with conn.cursor() as cur:
|
async with conn.cursor() as cur:
|
||||||
await cur.execute(query, _norm_params(params))
|
await cur.execute(query, params)
|
||||||
return await cur.fetchone()
|
return await cur.fetchone()
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@@ -230,7 +232,7 @@ class MysqlDB:
|
|||||||
lastrowid, or reach for the raw pool.
|
lastrowid, or reach for the raw pool.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
rowcount, _ = await self._run(query, params)
|
rowcount, _ = await self._run(query, _norm_params(params))
|
||||||
except _DRIVER_ERRORS:
|
except _DRIVER_ERRORS:
|
||||||
log.debug("mysql.execute(): %s", query, exc_info=True)
|
log.debug("mysql.execute(): %s", query, exc_info=True)
|
||||||
raise
|
raise
|
||||||
@@ -239,7 +241,7 @@ class MysqlDB:
|
|||||||
async def fetch(self, query: str, params: Optional[Sequence] = None) -> List[dict]:
|
async def fetch(self, query: str, params: Optional[Sequence] = None) -> List[dict]:
|
||||||
"""run a query and return all rows as plain dicts (empty list = no rows)"""
|
"""run a query and return all rows as plain dicts (empty list = no rows)"""
|
||||||
try:
|
try:
|
||||||
return await self._fetchall(query, params)
|
return await self._fetchall(query, _norm_params(params))
|
||||||
except _DRIVER_ERRORS:
|
except _DRIVER_ERRORS:
|
||||||
log.debug("mysql.fetch(): %s", query, exc_info=True)
|
log.debug("mysql.fetch(): %s", query, exc_info=True)
|
||||||
raise
|
raise
|
||||||
@@ -247,7 +249,7 @@ class MysqlDB:
|
|||||||
async def fetchone(self, query: str, params: Optional[Sequence] = None) -> Optional[dict]:
|
async def fetchone(self, query: str, params: Optional[Sequence] = None) -> Optional[dict]:
|
||||||
"""run a query and return the first row as a dict, or None if no rows"""
|
"""run a query and return the first row as a dict, or None if no rows"""
|
||||||
try:
|
try:
|
||||||
return await self._fetchone(query, params)
|
return await self._fetchone(query, _norm_params(params))
|
||||||
except _DRIVER_ERRORS:
|
except _DRIVER_ERRORS:
|
||||||
log.debug("mysql.fetchone(): %s", query, exc_info=True)
|
log.debug("mysql.fetchone(): %s", query, exc_info=True)
|
||||||
raise
|
raise
|
||||||
@@ -255,7 +257,7 @@ class MysqlDB:
|
|||||||
async def fetchval(self, query: str, params: Optional[Sequence] = None) -> Any:
|
async def fetchval(self, query: str, params: Optional[Sequence] = None) -> Any:
|
||||||
"""run a query and return the first column of the first row, or None"""
|
"""run a query and return the first column of the first row, or None"""
|
||||||
try:
|
try:
|
||||||
row = await self._fetchone(query, params)
|
row = await self._fetchone(query, _norm_params(params))
|
||||||
except _DRIVER_ERRORS:
|
except _DRIVER_ERRORS:
|
||||||
log.debug("mysql.fetchval(): %s", query, exc_info=True)
|
log.debug("mysql.fetchval(): %s", query, exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|||||||
Reference in new issue
Block a user