psycopg.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  1. # dialects/postgresql/psycopg.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. # mypy: ignore-errors
  8. r"""
  9. .. dialect:: postgresql+psycopg
  10. :name: psycopg (a.k.a. psycopg 3)
  11. :dbapi: psycopg
  12. :connectstring: postgresql+psycopg://user:password@host:port/dbname[?key=value&key=value...]
  13. :url: https://pypi.org/project/psycopg/
  14. ``psycopg`` is the package and module name for version 3 of the ``psycopg``
  15. database driver, formerly known as ``psycopg2``. This driver is different
  16. enough from its ``psycopg2`` predecessor that SQLAlchemy supports it
  17. via a totally separate dialect; support for ``psycopg2`` is expected to remain
  18. for as long as that package continues to function for modern Python versions,
  19. and also remains the default dialect for the ``postgresql://`` dialect
  20. series.
  21. The SQLAlchemy ``psycopg`` dialect provides both a sync and an async
  22. implementation under the same dialect name. The proper version is
  23. selected depending on how the engine is created:
  24. * calling :func:`_sa.create_engine` with ``postgresql+psycopg://...`` will
  25. automatically select the sync version, e.g.::
  26. from sqlalchemy import create_engine
  27. sync_engine = create_engine(
  28. "postgresql+psycopg://scott:tiger@localhost/test"
  29. )
  30. * calling :func:`_asyncio.create_async_engine` with
  31. ``postgresql+psycopg://...`` will automatically select the async version,
  32. e.g.::
  33. from sqlalchemy.ext.asyncio import create_async_engine
  34. asyncio_engine = create_async_engine(
  35. "postgresql+psycopg://scott:tiger@localhost/test"
  36. )
  37. The asyncio version of the dialect may also be specified explicitly using the
  38. ``psycopg_async`` suffix, as::
  39. from sqlalchemy.ext.asyncio import create_async_engine
  40. asyncio_engine = create_async_engine(
  41. "postgresql+psycopg_async://scott:tiger@localhost/test"
  42. )
  43. .. seealso::
  44. :ref:`postgresql_psycopg2` - The SQLAlchemy ``psycopg``
  45. dialect shares most of its behavior with the ``psycopg2`` dialect.
  46. Further documentation is available there.
  47. Using a different Cursor class
  48. ------------------------------
  49. One of the differences between ``psycopg`` and the older ``psycopg2``
  50. is how bound parameters are handled: ``psycopg2`` would bind them
  51. client side, while ``psycopg`` by default will bind them server side.
  52. It's possible to configure ``psycopg`` to do client side binding by
  53. specifying the ``cursor_factory`` to be ``ClientCursor`` when creating
  54. the engine::
  55. from psycopg import ClientCursor
  56. client_side_engine = create_engine(
  57. "postgresql+psycopg://...",
  58. connect_args={"cursor_factory": ClientCursor},
  59. )
  60. Similarly when using an async engine the ``AsyncClientCursor`` can be
  61. specified::
  62. from psycopg import AsyncClientCursor
  63. client_side_engine = create_async_engine(
  64. "postgresql+psycopg://...",
  65. connect_args={"cursor_factory": AsyncClientCursor},
  66. )
  67. .. seealso::
  68. `Client-side-binding cursors <https://www.psycopg.org/psycopg3/docs/advanced/cursors.html#client-side-binding-cursors>`_
  69. """ # noqa
  70. from __future__ import annotations
  71. from collections import deque
  72. import logging
  73. import re
  74. from typing import cast
  75. from typing import TYPE_CHECKING
  76. from . import ranges
  77. from ._psycopg_common import _PGDialect_common_psycopg
  78. from ._psycopg_common import _PGExecutionContext_common_psycopg
  79. from .base import INTERVAL
  80. from .base import PGCompiler
  81. from .base import PGIdentifierPreparer
  82. from .base import REGCONFIG
  83. from .json import JSON
  84. from .json import JSONB
  85. from .json import JSONPathType
  86. from .types import CITEXT
  87. from ... import pool
  88. from ... import util
  89. from ...engine import AdaptedConnection
  90. from ...sql import sqltypes
  91. from ...util.concurrency import await_fallback
  92. from ...util.concurrency import await_only
  93. if TYPE_CHECKING:
  94. from typing import Iterable
  95. from psycopg import AsyncConnection
  96. logger = logging.getLogger("sqlalchemy.dialects.postgresql")
  97. class _PGString(sqltypes.String):
  98. render_bind_cast = True
  99. class _PGREGCONFIG(REGCONFIG):
  100. render_bind_cast = True
  101. class _PGJSON(JSON):
  102. def bind_processor(self, dialect):
  103. return self._make_bind_processor(None, dialect._psycopg_Json)
  104. def result_processor(self, dialect, coltype):
  105. return None
  106. class _PGJSONB(JSONB):
  107. def bind_processor(self, dialect):
  108. return self._make_bind_processor(None, dialect._psycopg_Jsonb)
  109. def result_processor(self, dialect, coltype):
  110. return None
  111. class _PGJSONIntIndexType(sqltypes.JSON.JSONIntIndexType):
  112. __visit_name__ = "json_int_index"
  113. render_bind_cast = True
  114. class _PGJSONStrIndexType(sqltypes.JSON.JSONStrIndexType):
  115. __visit_name__ = "json_str_index"
  116. render_bind_cast = True
  117. class _PGJSONPathType(JSONPathType):
  118. pass
  119. class _PGInterval(INTERVAL):
  120. render_bind_cast = True
  121. class _PGTimeStamp(sqltypes.DateTime):
  122. render_bind_cast = True
  123. class _PGDate(sqltypes.Date):
  124. render_bind_cast = True
  125. class _PGTime(sqltypes.Time):
  126. render_bind_cast = True
  127. class _PGInteger(sqltypes.Integer):
  128. render_bind_cast = True
  129. class _PGSmallInteger(sqltypes.SmallInteger):
  130. render_bind_cast = True
  131. class _PGNullType(sqltypes.NullType):
  132. render_bind_cast = True
  133. class _PGBigInteger(sqltypes.BigInteger):
  134. render_bind_cast = True
  135. class _PGBoolean(sqltypes.Boolean):
  136. render_bind_cast = True
  137. class _PsycopgRange(ranges.AbstractSingleRangeImpl):
  138. def bind_processor(self, dialect):
  139. psycopg_Range = cast(PGDialect_psycopg, dialect)._psycopg_Range
  140. def to_range(value):
  141. if isinstance(value, ranges.Range):
  142. value = psycopg_Range(
  143. value.lower, value.upper, value.bounds, value.empty
  144. )
  145. return value
  146. return to_range
  147. def result_processor(self, dialect, coltype):
  148. def to_range(value):
  149. if value is not None:
  150. value = ranges.Range(
  151. value._lower,
  152. value._upper,
  153. bounds=value._bounds if value._bounds else "[)",
  154. empty=not value._bounds,
  155. )
  156. return value
  157. return to_range
  158. class _PsycopgMultiRange(ranges.AbstractMultiRangeImpl):
  159. def bind_processor(self, dialect):
  160. psycopg_Range = cast(PGDialect_psycopg, dialect)._psycopg_Range
  161. psycopg_Multirange = cast(
  162. PGDialect_psycopg, dialect
  163. )._psycopg_Multirange
  164. NoneType = type(None)
  165. def to_range(value):
  166. if isinstance(value, (str, NoneType, psycopg_Multirange)):
  167. return value
  168. return psycopg_Multirange(
  169. [
  170. psycopg_Range(
  171. element.lower,
  172. element.upper,
  173. element.bounds,
  174. element.empty,
  175. )
  176. for element in cast("Iterable[ranges.Range]", value)
  177. ]
  178. )
  179. return to_range
  180. def result_processor(self, dialect, coltype):
  181. def to_range(value):
  182. if value is None:
  183. return None
  184. else:
  185. return ranges.MultiRange(
  186. ranges.Range(
  187. elem._lower,
  188. elem._upper,
  189. bounds=elem._bounds if elem._bounds else "[)",
  190. empty=not elem._bounds,
  191. )
  192. for elem in value
  193. )
  194. return to_range
  195. class PGExecutionContext_psycopg(_PGExecutionContext_common_psycopg):
  196. pass
  197. class PGCompiler_psycopg(PGCompiler):
  198. pass
  199. class PGIdentifierPreparer_psycopg(PGIdentifierPreparer):
  200. pass
  201. def _log_notices(diagnostic):
  202. logger.info("%s: %s", diagnostic.severity, diagnostic.message_primary)
  203. class PGDialect_psycopg(_PGDialect_common_psycopg):
  204. driver = "psycopg"
  205. supports_statement_cache = True
  206. supports_server_side_cursors = True
  207. default_paramstyle = "pyformat"
  208. supports_sane_multi_rowcount = True
  209. execution_ctx_cls = PGExecutionContext_psycopg
  210. statement_compiler = PGCompiler_psycopg
  211. preparer = PGIdentifierPreparer_psycopg
  212. psycopg_version = (0, 0)
  213. _has_native_hstore = True
  214. _psycopg_adapters_map = None
  215. colspecs = util.update_copy(
  216. _PGDialect_common_psycopg.colspecs,
  217. {
  218. sqltypes.String: _PGString,
  219. REGCONFIG: _PGREGCONFIG,
  220. JSON: _PGJSON,
  221. CITEXT: CITEXT,
  222. sqltypes.JSON: _PGJSON,
  223. JSONB: _PGJSONB,
  224. sqltypes.JSON.JSONPathType: _PGJSONPathType,
  225. sqltypes.JSON.JSONIntIndexType: _PGJSONIntIndexType,
  226. sqltypes.JSON.JSONStrIndexType: _PGJSONStrIndexType,
  227. sqltypes.Interval: _PGInterval,
  228. INTERVAL: _PGInterval,
  229. sqltypes.Date: _PGDate,
  230. sqltypes.DateTime: _PGTimeStamp,
  231. sqltypes.Time: _PGTime,
  232. sqltypes.Integer: _PGInteger,
  233. sqltypes.SmallInteger: _PGSmallInteger,
  234. sqltypes.BigInteger: _PGBigInteger,
  235. ranges.AbstractSingleRange: _PsycopgRange,
  236. ranges.AbstractMultiRange: _PsycopgMultiRange,
  237. },
  238. )
  239. def __init__(self, **kwargs):
  240. super().__init__(**kwargs)
  241. if self.dbapi:
  242. m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", self.dbapi.__version__)
  243. if m:
  244. self.psycopg_version = tuple(
  245. int(x) for x in m.group(1, 2, 3) if x is not None
  246. )
  247. if self.psycopg_version < (3, 0, 2):
  248. raise ImportError(
  249. "psycopg version 3.0.2 or higher is required."
  250. )
  251. from psycopg.adapt import AdaptersMap
  252. self._psycopg_adapters_map = adapters_map = AdaptersMap(
  253. self.dbapi.adapters
  254. )
  255. if self._native_inet_types is False:
  256. import psycopg.types.string
  257. adapters_map.register_loader(
  258. "inet", psycopg.types.string.TextLoader
  259. )
  260. adapters_map.register_loader(
  261. "cidr", psycopg.types.string.TextLoader
  262. )
  263. if self._json_deserializer:
  264. from psycopg.types.json import set_json_loads
  265. set_json_loads(self._json_deserializer, adapters_map)
  266. if self._json_serializer:
  267. from psycopg.types.json import set_json_dumps
  268. set_json_dumps(self._json_serializer, adapters_map)
  269. def create_connect_args(self, url):
  270. # see https://github.com/psycopg/psycopg/issues/83
  271. cargs, cparams = super().create_connect_args(url)
  272. if self._psycopg_adapters_map:
  273. cparams["context"] = self._psycopg_adapters_map
  274. if self.client_encoding is not None:
  275. cparams["client_encoding"] = self.client_encoding
  276. return cargs, cparams
  277. def _type_info_fetch(self, connection, name):
  278. from psycopg.types import TypeInfo
  279. return TypeInfo.fetch(connection.connection.driver_connection, name)
  280. def initialize(self, connection):
  281. super().initialize(connection)
  282. # PGDialect.initialize() checks server version for <= 8.2 and sets
  283. # this flag to False if so
  284. if not self.insert_returning:
  285. self.insert_executemany_returning = False
  286. # HSTORE can't be registered until we have a connection so that
  287. # we can look up its OID, so we set up this adapter in
  288. # initialize()
  289. if self.use_native_hstore:
  290. info = self._type_info_fetch(connection, "hstore")
  291. self._has_native_hstore = info is not None
  292. if self._has_native_hstore:
  293. from psycopg.types.hstore import register_hstore
  294. # register the adapter for connections made subsequent to
  295. # this one
  296. assert self._psycopg_adapters_map
  297. register_hstore(info, self._psycopg_adapters_map)
  298. # register the adapter for this connection
  299. assert connection.connection
  300. register_hstore(info, connection.connection.driver_connection)
  301. @classmethod
  302. def import_dbapi(cls):
  303. import psycopg
  304. return psycopg
  305. @classmethod
  306. def get_async_dialect_cls(cls, url):
  307. return PGDialectAsync_psycopg
  308. @util.memoized_property
  309. def _isolation_lookup(self):
  310. return {
  311. "READ COMMITTED": self.dbapi.IsolationLevel.READ_COMMITTED,
  312. "READ UNCOMMITTED": self.dbapi.IsolationLevel.READ_UNCOMMITTED,
  313. "REPEATABLE READ": self.dbapi.IsolationLevel.REPEATABLE_READ,
  314. "SERIALIZABLE": self.dbapi.IsolationLevel.SERIALIZABLE,
  315. }
  316. @util.memoized_property
  317. def _psycopg_Json(self):
  318. from psycopg.types import json
  319. return json.Json
  320. @util.memoized_property
  321. def _psycopg_Jsonb(self):
  322. from psycopg.types import json
  323. return json.Jsonb
  324. @util.memoized_property
  325. def _psycopg_TransactionStatus(self):
  326. from psycopg.pq import TransactionStatus
  327. return TransactionStatus
  328. @util.memoized_property
  329. def _psycopg_Range(self):
  330. from psycopg.types.range import Range
  331. return Range
  332. @util.memoized_property
  333. def _psycopg_Multirange(self):
  334. from psycopg.types.multirange import Multirange
  335. return Multirange
  336. def _do_isolation_level(self, connection, autocommit, isolation_level):
  337. connection.autocommit = autocommit
  338. connection.isolation_level = isolation_level
  339. def get_isolation_level(self, dbapi_connection):
  340. status_before = dbapi_connection.info.transaction_status
  341. value = super().get_isolation_level(dbapi_connection)
  342. # don't rely on psycopg providing enum symbols, compare with
  343. # eq/ne
  344. if status_before == self._psycopg_TransactionStatus.IDLE:
  345. dbapi_connection.rollback()
  346. return value
  347. def set_isolation_level(self, dbapi_connection, level):
  348. if level == "AUTOCOMMIT":
  349. self._do_isolation_level(
  350. dbapi_connection, autocommit=True, isolation_level=None
  351. )
  352. else:
  353. self._do_isolation_level(
  354. dbapi_connection,
  355. autocommit=False,
  356. isolation_level=self._isolation_lookup[level],
  357. )
  358. def set_readonly(self, connection, value):
  359. connection.read_only = value
  360. def get_readonly(self, connection):
  361. return connection.read_only
  362. def on_connect(self):
  363. def notices(conn):
  364. conn.add_notice_handler(_log_notices)
  365. fns = [notices]
  366. if self.isolation_level is not None:
  367. def on_connect(conn):
  368. self.set_isolation_level(conn, self.isolation_level)
  369. fns.append(on_connect)
  370. # fns always has the notices function
  371. def on_connect(conn):
  372. for fn in fns:
  373. fn(conn)
  374. return on_connect
  375. def is_disconnect(self, e, connection, cursor):
  376. if isinstance(e, self.dbapi.Error) and connection is not None:
  377. if connection.closed or connection.broken:
  378. return True
  379. return False
  380. def _do_prepared_twophase(self, connection, command, recover=False):
  381. dbapi_conn = connection.connection.dbapi_connection
  382. if (
  383. recover
  384. # don't rely on psycopg providing enum symbols, compare with
  385. # eq/ne
  386. or dbapi_conn.info.transaction_status
  387. != self._psycopg_TransactionStatus.IDLE
  388. ):
  389. dbapi_conn.rollback()
  390. before_autocommit = dbapi_conn.autocommit
  391. try:
  392. if not before_autocommit:
  393. self._do_autocommit(dbapi_conn, True)
  394. dbapi_conn.execute(command)
  395. finally:
  396. if not before_autocommit:
  397. self._do_autocommit(dbapi_conn, before_autocommit)
  398. def do_rollback_twophase(
  399. self, connection, xid, is_prepared=True, recover=False
  400. ):
  401. if is_prepared:
  402. self._do_prepared_twophase(
  403. connection, f"ROLLBACK PREPARED '{xid}'", recover=recover
  404. )
  405. else:
  406. self.do_rollback(connection.connection)
  407. def do_commit_twophase(
  408. self, connection, xid, is_prepared=True, recover=False
  409. ):
  410. if is_prepared:
  411. self._do_prepared_twophase(
  412. connection, f"COMMIT PREPARED '{xid}'", recover=recover
  413. )
  414. else:
  415. self.do_commit(connection.connection)
  416. @util.memoized_property
  417. def _dialect_specific_select_one(self):
  418. return ";"
  419. class AsyncAdapt_psycopg_cursor:
  420. __slots__ = ("_cursor", "await_", "_rows")
  421. _psycopg_ExecStatus = None
  422. def __init__(self, cursor, await_) -> None:
  423. self._cursor = cursor
  424. self.await_ = await_
  425. self._rows = deque()
  426. def __getattr__(self, name):
  427. return getattr(self._cursor, name)
  428. @property
  429. def arraysize(self):
  430. return self._cursor.arraysize
  431. @arraysize.setter
  432. def arraysize(self, value):
  433. self._cursor.arraysize = value
  434. def close(self):
  435. self._rows.clear()
  436. # Normal cursor just call _close() in a non-sync way.
  437. self._cursor._close()
  438. def execute(self, query, params=None, **kw):
  439. result = self.await_(self._cursor.execute(query, params, **kw))
  440. # sqlalchemy result is not async, so need to pull all rows here
  441. res = self._cursor.pgresult
  442. # don't rely on psycopg providing enum symbols, compare with
  443. # eq/ne
  444. if res and res.status == self._psycopg_ExecStatus.TUPLES_OK:
  445. rows = self.await_(self._cursor.fetchall())
  446. self._rows = deque(rows)
  447. return result
  448. def executemany(self, query, params_seq):
  449. return self.await_(self._cursor.executemany(query, params_seq))
  450. def __iter__(self):
  451. while self._rows:
  452. yield self._rows.popleft()
  453. def fetchone(self):
  454. if self._rows:
  455. return self._rows.popleft()
  456. else:
  457. return None
  458. def fetchmany(self, size=None):
  459. if size is None:
  460. size = self._cursor.arraysize
  461. rr = self._rows
  462. return [rr.popleft() for _ in range(min(size, len(rr)))]
  463. def fetchall(self):
  464. retval = list(self._rows)
  465. self._rows.clear()
  466. return retval
  467. class AsyncAdapt_psycopg_ss_cursor(AsyncAdapt_psycopg_cursor):
  468. def execute(self, query, params=None, **kw):
  469. self.await_(self._cursor.execute(query, params, **kw))
  470. return self
  471. def close(self):
  472. self.await_(self._cursor.close())
  473. def fetchone(self):
  474. return self.await_(self._cursor.fetchone())
  475. def fetchmany(self, size=0):
  476. return self.await_(self._cursor.fetchmany(size))
  477. def fetchall(self):
  478. return self.await_(self._cursor.fetchall())
  479. def __iter__(self):
  480. iterator = self._cursor.__aiter__()
  481. while True:
  482. try:
  483. yield self.await_(iterator.__anext__())
  484. except StopAsyncIteration:
  485. break
  486. class AsyncAdapt_psycopg_connection(AdaptedConnection):
  487. _connection: AsyncConnection
  488. __slots__ = ()
  489. await_ = staticmethod(await_only)
  490. def __init__(self, connection) -> None:
  491. self._connection = connection
  492. def __getattr__(self, name):
  493. return getattr(self._connection, name)
  494. def execute(self, query, params=None, **kw):
  495. cursor = self.await_(self._connection.execute(query, params, **kw))
  496. return AsyncAdapt_psycopg_cursor(cursor, self.await_)
  497. def cursor(self, *args, **kw):
  498. cursor = self._connection.cursor(*args, **kw)
  499. if hasattr(cursor, "name"):
  500. return AsyncAdapt_psycopg_ss_cursor(cursor, self.await_)
  501. else:
  502. return AsyncAdapt_psycopg_cursor(cursor, self.await_)
  503. def commit(self):
  504. self.await_(self._connection.commit())
  505. def rollback(self):
  506. self.await_(self._connection.rollback())
  507. def close(self):
  508. self.await_(self._connection.close())
  509. @property
  510. def autocommit(self):
  511. return self._connection.autocommit
  512. @autocommit.setter
  513. def autocommit(self, value):
  514. self.set_autocommit(value)
  515. def set_autocommit(self, value):
  516. self.await_(self._connection.set_autocommit(value))
  517. def set_isolation_level(self, value):
  518. self.await_(self._connection.set_isolation_level(value))
  519. def set_read_only(self, value):
  520. self.await_(self._connection.set_read_only(value))
  521. def set_deferrable(self, value):
  522. self.await_(self._connection.set_deferrable(value))
  523. class AsyncAdaptFallback_psycopg_connection(AsyncAdapt_psycopg_connection):
  524. __slots__ = ()
  525. await_ = staticmethod(await_fallback)
  526. class PsycopgAdaptDBAPI:
  527. def __init__(self, psycopg) -> None:
  528. self.psycopg = psycopg
  529. for k, v in self.psycopg.__dict__.items():
  530. if k != "connect":
  531. self.__dict__[k] = v
  532. def connect(self, *arg, **kw):
  533. async_fallback = kw.pop("async_fallback", False)
  534. creator_fn = kw.pop(
  535. "async_creator_fn", self.psycopg.AsyncConnection.connect
  536. )
  537. if util.asbool(async_fallback):
  538. return AsyncAdaptFallback_psycopg_connection(
  539. await_fallback(creator_fn(*arg, **kw))
  540. )
  541. else:
  542. return AsyncAdapt_psycopg_connection(
  543. await_only(creator_fn(*arg, **kw))
  544. )
  545. class PGDialectAsync_psycopg(PGDialect_psycopg):
  546. is_async = True
  547. supports_statement_cache = True
  548. @classmethod
  549. def import_dbapi(cls):
  550. import psycopg
  551. from psycopg.pq import ExecStatus
  552. AsyncAdapt_psycopg_cursor._psycopg_ExecStatus = ExecStatus
  553. return PsycopgAdaptDBAPI(psycopg)
  554. @classmethod
  555. def get_pool_class(cls, url):
  556. async_fallback = url.query.get("async_fallback", False)
  557. if util.asbool(async_fallback):
  558. return pool.FallbackAsyncAdaptedQueuePool
  559. else:
  560. return pool.AsyncAdaptedQueuePool
  561. def _type_info_fetch(self, connection, name):
  562. from psycopg.types import TypeInfo
  563. adapted = connection.connection
  564. return adapted.await_(TypeInfo.fetch(adapted.driver_connection, name))
  565. def _do_isolation_level(self, connection, autocommit, isolation_level):
  566. connection.set_autocommit(autocommit)
  567. connection.set_isolation_level(isolation_level)
  568. def _do_autocommit(self, connection, value):
  569. connection.set_autocommit(value)
  570. def set_readonly(self, connection, value):
  571. connection.set_read_only(value)
  572. def set_deferrable(self, connection, value):
  573. connection.set_deferrable(value)
  574. def get_driver_connection(self, connection):
  575. return connection._connection
  576. dialect = PGDialect_psycopg
  577. dialect_async = PGDialectAsync_psycopg