AsyncConnection.exec_driver_sql with asyncpg not support named parameters
See original GitHub issueDescribe the bug
AsyncConnection.exec_driver_sql
with asyncpg
not support named parameters.
Expected behavior
Support it.
To Reproduce
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine("postgresql+asyncpg://user:pass@host/dbname", echo=True)
async with engine.connect() as con:
result = await con.exec_driver_sql(
'SELECT %(named_parameter)s;',
dict(named_parameter=1)
)
Error
2021-05-09 09:53:53,355 INFO sqlalchemy.engine.Engine select version()
2021-05-09 09:53:53,355 INFO sqlalchemy.engine.Engine [raw sql] ()
2021-05-09 09:53:53,359 INFO sqlalchemy.engine.Engine select current_schema()
2021-05-09 09:53:53,359 INFO sqlalchemy.engine.Engine [raw sql] ()
2021-05-09 09:53:53,363 INFO sqlalchemy.engine.Engine show standard_conforming_strings
2021-05-09 09:53:53,363 INFO sqlalchemy.engine.Engine [raw sql] ()
2021-05-09 09:53:53,366 INFO sqlalchemy.engine.Engine BEGIN (implicit)
2021-05-09 09:53:53,366 INFO sqlalchemy.engine.Engine SELECT %(named_parameter)s;
2021-05-09 09:53:53,366 INFO sqlalchemy.engine.Engine [raw sql] {'named_parameter': 1}
2021-05-09 09:53:53,367 INFO sqlalchemy.engine.Engine ROLLBACK
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-659f13350761> in <module>
1 async with engine.connect() as con:
----> 2 result = await con.exec_driver_sql(
3 'SELECT %(named_parameter)s;',
4 dict(named_parameter=1)
5 )
/usr/local/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/engine.py in exec_driver_sql(self, statement, parameters, execution_options)
328 conn = self._sync_connection()
329
--> 330 result = await greenlet_spawn(
331 conn.exec_driver_sql,
332 statement,
/usr/local/lib/python3.8/site-packages/sqlalchemy/util/_concurrency_py3k.py in greenlet_spawn(fn, _require_await, *args, **kwargs)
118 # the moderated greenlet so that it can continue
119 # its expected flow.
--> 120 result = context.throw(*sys.exc_info())
121 else:
122 result = context.switch(value)
/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/base.py in exec_driver_sql(self, statement, parameters, execution_options)
1635 args_10style, kwargs_10style = _distill_params_20(parameters)
1636
-> 1637 return self._exec_driver_sql(
1638 statement,
1639 args_10style,
/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/base.py in _exec_driver_sql(self, statement, multiparams, params, execution_options, future)
1544
1545 dialect = self.dialect
-> 1546 ret = self._execute_context(
1547 dialect,
1548 dialect.execution_ctx_cls._init_statement,
/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, execution_options, *args, **kw)
1811
1812 except BaseException as e:
-> 1813 self._handle_dbapi_exception(
1814 e, statement, parameters, cursor, context
1815 )
/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/base.py in _handle_dbapi_exception(self, e, statement, parameters, cursor, context)
1996 )
1997 else:
-> 1998 util.raise_(exc_info[1], with_traceback=exc_info[2])
1999
2000 finally:
/usr/local/lib/python3.8/site-packages/sqlalchemy/util/compat.py in raise_(***failed resolving arguments***)
209
210 try:
--> 211 raise exception
212 finally:
213 # credit to
/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/base.py in _execute_context(self, dialect, constructor, statement, parameters, execution_options, *args, **kw)
1768 break
1769 if not evt_handled:
-> 1770 self.dialect.do_execute(
1771 cursor, statement, parameters, context
1772 )
/usr/local/lib/python3.8/site-packages/sqlalchemy/engine/default.py in do_execute(self, cursor, statement, parameters, context)
715
716 def do_execute(self, cursor, statement, parameters, context=None):
--> 717 cursor.execute(statement, parameters)
718
719 def do_execute_no_params(self, cursor, statement, context=None):
/usr/local/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py in execute(self, operation, parameters)
447
448 def execute(self, operation, parameters=None):
--> 449 self._adapt_connection.await_(
450 self._prepare_and_execute(operation, parameters)
451 )
/usr/local/lib/python3.8/site-packages/sqlalchemy/util/_concurrency_py3k.py in await_only(awaitable)
60 # switches back to this greenlet with the result of awaitable that is
61 # then returned to the caller (or raised as error)
---> 62 return current.driver.switch(awaitable)
63
64
/usr/local/lib/python3.8/site-packages/sqlalchemy/util/_concurrency_py3k.py in greenlet_spawn(fn, _require_await, *args, **kwargs)
113 # wait for a coroutine from await_ and then return its
114 # result back to it.
--> 115 value = await result
116 except Exception:
117 # this allows an exception to be raised within
/usr/local/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py in _prepare_and_execute(self, operation, parameters)
379
380 if parameters is not None:
--> 381 operation = operation % self._parameter_placeholders(
382 parameters
383 )
TypeError: format requires a mapping
Versions.
- OS: Debian10
- Python: 3.8.7
- SQLAlchemy: 1.4.14
- Database: PostgreSQL 13.1
- DBAPI: asyncpg 0.22.0
Additional context
https://github.com/sqlalchemy/sqlalchemy/blob/96d8a4acbbecfa699ad28a8441402ea166bc95c6/lib/sqlalchemy/dialects/postgresql/asyncpg.py#L360
This function not process params
type of dict
.
def _parameter_placeholders(self, params):
if not self._inputsizes:
return tuple("$%d" % idx for idx, _ in enumerate(params, 1))
else:
return tuple(
"$%d::%s" % (idx, typ) if typ else "$%d" % idx
for idx, typ in enumerate(
(_pg_types.get(typ) for typ in self._inputsizes), 1
)
)
Have a nice day!
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (3 by maintainers)
Top Results From Across the Web
API Reference — asyncpg Documentation
The connection parameters may be specified either as a connection URI in dsn, or as specific keyword arguments, or both. If both dsn...
Read more >Asynchronous I/O (asyncio) - SQLAlchemy 1.4 Documentation
Asynchronous IO Support for Core and ORM - initial feature announcement ... This may be either a dictionary of parameter names to values, ......
Read more >Async SQLAlchemy with FastAPI
Other dependencies include FastAPI with uvicorn, asyncpg ... The async support can be used only with PostgreSQL database at the moment.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Paste the complete code to help others.
let me clarify that asyncpg by itself does not provide a pep 249 DBAPI implementation. SQLAlchemy’s asyncpg dialect provides one. This internal DBAPI uses the format paramstyle. You can use that paramstyle, or asyncpg’s dollar-numeric style that is not part of the DBAPI. if you want named parameters, use text() with connection.execute()