diff options
Diffstat (limited to 'venv/lib/python3.11/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py')
-rw-r--r-- | venv/lib/python3.11/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py | 876 |
1 files changed, 0 insertions, 876 deletions
diff --git a/venv/lib/python3.11/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py b/venv/lib/python3.11/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py deleted file mode 100644 index 9bf2e49..0000000 --- a/venv/lib/python3.11/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py +++ /dev/null @@ -1,876 +0,0 @@ -# dialects/postgresql/psycopg2.py -# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -# mypy: ignore-errors - -r""" -.. dialect:: postgresql+psycopg2 - :name: psycopg2 - :dbapi: psycopg2 - :connectstring: postgresql+psycopg2://user:password@host:port/dbname[?key=value&key=value...] - :url: https://pypi.org/project/psycopg2/ - -.. _psycopg2_toplevel: - -psycopg2 Connect Arguments --------------------------- - -Keyword arguments that are specific to the SQLAlchemy psycopg2 dialect -may be passed to :func:`_sa.create_engine()`, and include the following: - - -* ``isolation_level``: This option, available for all PostgreSQL dialects, - includes the ``AUTOCOMMIT`` isolation level when using the psycopg2 - dialect. This option sets the **default** isolation level for the - connection that is set immediately upon connection to the database before - the connection is pooled. This option is generally superseded by the more - modern :paramref:`_engine.Connection.execution_options.isolation_level` - execution option, detailed at :ref:`dbapi_autocommit`. - - .. seealso:: - - :ref:`psycopg2_isolation_level` - - :ref:`dbapi_autocommit` - - -* ``client_encoding``: sets the client encoding in a libpq-agnostic way, - using psycopg2's ``set_client_encoding()`` method. - - .. seealso:: - - :ref:`psycopg2_unicode` - - -* ``executemany_mode``, ``executemany_batch_page_size``, - ``executemany_values_page_size``: Allows use of psycopg2 - extensions for optimizing "executemany"-style queries. See the referenced - section below for details. - - .. seealso:: - - :ref:`psycopg2_executemany_mode` - -.. tip:: - - The above keyword arguments are **dialect** keyword arguments, meaning - that they are passed as explicit keyword arguments to :func:`_sa.create_engine()`:: - - engine = create_engine( - "postgresql+psycopg2://scott:tiger@localhost/test", - isolation_level="SERIALIZABLE", - ) - - These should not be confused with **DBAPI** connect arguments, which - are passed as part of the :paramref:`_sa.create_engine.connect_args` - dictionary and/or are passed in the URL query string, as detailed in - the section :ref:`custom_dbapi_args`. - -.. _psycopg2_ssl: - -SSL Connections ---------------- - -The psycopg2 module has a connection argument named ``sslmode`` for -controlling its behavior regarding secure (SSL) connections. The default is -``sslmode=prefer``; it will attempt an SSL connection and if that fails it -will fall back to an unencrypted connection. ``sslmode=require`` may be used -to ensure that only secure connections are established. Consult the -psycopg2 / libpq documentation for further options that are available. - -Note that ``sslmode`` is specific to psycopg2 so it is included in the -connection URI:: - - engine = sa.create_engine( - "postgresql+psycopg2://scott:tiger@192.168.0.199:5432/test?sslmode=require" - ) - - -Unix Domain Connections ------------------------- - -psycopg2 supports connecting via Unix domain connections. When the ``host`` -portion of the URL is omitted, SQLAlchemy passes ``None`` to psycopg2, -which specifies Unix-domain communication rather than TCP/IP communication:: - - create_engine("postgresql+psycopg2://user:password@/dbname") - -By default, the socket file used is to connect to a Unix-domain socket -in ``/tmp``, or whatever socket directory was specified when PostgreSQL -was built. This value can be overridden by passing a pathname to psycopg2, -using ``host`` as an additional keyword argument:: - - create_engine("postgresql+psycopg2://user:password@/dbname?host=/var/lib/postgresql") - -.. warning:: The format accepted here allows for a hostname in the main URL - in addition to the "host" query string argument. **When using this URL - format, the initial host is silently ignored**. That is, this URL:: - - engine = create_engine("postgresql+psycopg2://user:password@myhost1/dbname?host=myhost2") - - Above, the hostname ``myhost1`` is **silently ignored and discarded.** The - host which is connected is the ``myhost2`` host. - - This is to maintain some degree of compatibility with PostgreSQL's own URL - format which has been tested to behave the same way and for which tools like - PifPaf hardcode two hostnames. - -.. seealso:: - - `PQconnectdbParams \ - <https://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS>`_ - -.. _psycopg2_multi_host: - -Specifying multiple fallback hosts ------------------------------------ - -psycopg2 supports multiple connection points in the connection string. -When the ``host`` parameter is used multiple times in the query section of -the URL, SQLAlchemy will create a single string of the host and port -information provided to make the connections. Tokens may consist of -``host::port`` or just ``host``; in the latter case, the default port -is selected by libpq. In the example below, three host connections -are specified, for ``HostA::PortA``, ``HostB`` connecting to the default port, -and ``HostC::PortC``:: - - create_engine( - "postgresql+psycopg2://user:password@/dbname?host=HostA:PortA&host=HostB&host=HostC:PortC" - ) - -As an alternative, libpq query string format also may be used; this specifies -``host`` and ``port`` as single query string arguments with comma-separated -lists - the default port can be chosen by indicating an empty value -in the comma separated list:: - - create_engine( - "postgresql+psycopg2://user:password@/dbname?host=HostA,HostB,HostC&port=PortA,,PortC" - ) - -With either URL style, connections to each host is attempted based on a -configurable strategy, which may be configured using the libpq -``target_session_attrs`` parameter. Per libpq this defaults to ``any`` -which indicates a connection to each host is then attempted until a connection is successful. -Other strategies include ``primary``, ``prefer-standby``, etc. The complete -list is documented by PostgreSQL at -`libpq connection strings <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_. - -For example, to indicate two hosts using the ``primary`` strategy:: - - create_engine( - "postgresql+psycopg2://user:password@/dbname?host=HostA:PortA&host=HostB&host=HostC:PortC&target_session_attrs=primary" - ) - -.. versionchanged:: 1.4.40 Port specification in psycopg2 multiple host format - is repaired, previously ports were not correctly interpreted in this context. - libpq comma-separated format is also now supported. - -.. versionadded:: 1.3.20 Support for multiple hosts in PostgreSQL connection - string. - -.. seealso:: - - `libpq connection strings <https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>`_ - please refer - to this section in the libpq documentation for complete background on multiple host support. - - -Empty DSN Connections / Environment Variable Connections ---------------------------------------------------------- - -The psycopg2 DBAPI can connect to PostgreSQL by passing an empty DSN to the -libpq client library, which by default indicates to connect to a localhost -PostgreSQL database that is open for "trust" connections. This behavior can be -further tailored using a particular set of environment variables which are -prefixed with ``PG_...``, which are consumed by ``libpq`` to take the place of -any or all elements of the connection string. - -For this form, the URL can be passed without any elements other than the -initial scheme:: - - engine = create_engine('postgresql+psycopg2://') - -In the above form, a blank "dsn" string is passed to the ``psycopg2.connect()`` -function which in turn represents an empty DSN passed to libpq. - -.. versionadded:: 1.3.2 support for parameter-less connections with psycopg2. - -.. seealso:: - - `Environment Variables\ - <https://www.postgresql.org/docs/current/libpq-envars.html>`_ - - PostgreSQL documentation on how to use ``PG_...`` - environment variables for connections. - -.. _psycopg2_execution_options: - -Per-Statement/Connection Execution Options -------------------------------------------- - -The following DBAPI-specific options are respected when used with -:meth:`_engine.Connection.execution_options`, -:meth:`.Executable.execution_options`, -:meth:`_query.Query.execution_options`, -in addition to those not specific to DBAPIs: - -* ``isolation_level`` - Set the transaction isolation level for the lifespan - of a :class:`_engine.Connection` (can only be set on a connection, - not a statement - or query). See :ref:`psycopg2_isolation_level`. - -* ``stream_results`` - Enable or disable usage of psycopg2 server side - cursors - this feature makes use of "named" cursors in combination with - special result handling methods so that result rows are not fully buffered. - Defaults to False, meaning cursors are buffered by default. - -* ``max_row_buffer`` - when using ``stream_results``, an integer value that - specifies the maximum number of rows to buffer at a time. This is - interpreted by the :class:`.BufferedRowCursorResult`, and if omitted the - buffer will grow to ultimately store 1000 rows at a time. - - .. versionchanged:: 1.4 The ``max_row_buffer`` size can now be greater than - 1000, and the buffer will grow to that size. - -.. _psycopg2_batch_mode: - -.. _psycopg2_executemany_mode: - -Psycopg2 Fast Execution Helpers -------------------------------- - -Modern versions of psycopg2 include a feature known as -`Fast Execution Helpers \ -<https://initd.org/psycopg/docs/extras.html#fast-execution-helpers>`_, which -have been shown in benchmarking to improve psycopg2's executemany() -performance, primarily with INSERT statements, by at least -an order of magnitude. - -SQLAlchemy implements a native form of the "insert many values" -handler that will rewrite a single-row INSERT statement to accommodate for -many values at once within an extended VALUES clause; this handler is -equivalent to psycopg2's ``execute_values()`` handler; an overview of this -feature and its configuration are at :ref:`engine_insertmanyvalues`. - -.. versionadded:: 2.0 Replaced psycopg2's ``execute_values()`` fast execution - helper with a native SQLAlchemy mechanism known as - :ref:`insertmanyvalues <engine_insertmanyvalues>`. - -The psycopg2 dialect retains the ability to use the psycopg2-specific -``execute_batch()`` feature, although it is not expected that this is a widely -used feature. The use of this extension may be enabled using the -``executemany_mode`` flag which may be passed to :func:`_sa.create_engine`:: - - engine = create_engine( - "postgresql+psycopg2://scott:tiger@host/dbname", - executemany_mode='values_plus_batch') - - -Possible options for ``executemany_mode`` include: - -* ``values_only`` - this is the default value. SQLAlchemy's native - :ref:`insertmanyvalues <engine_insertmanyvalues>` handler is used for qualifying - INSERT statements, assuming - :paramref:`_sa.create_engine.use_insertmanyvalues` is left at - its default value of ``True``. This handler rewrites simple - INSERT statements to include multiple VALUES clauses so that many - parameter sets can be inserted with one statement. - -* ``'values_plus_batch'``- SQLAlchemy's native - :ref:`insertmanyvalues <engine_insertmanyvalues>` handler is used for qualifying - INSERT statements, assuming - :paramref:`_sa.create_engine.use_insertmanyvalues` is left at its default - value of ``True``. Then, psycopg2's ``execute_batch()`` handler is used for - qualifying UPDATE and DELETE statements when executed with multiple parameter - sets. When using this mode, the :attr:`_engine.CursorResult.rowcount` - attribute will not contain a value for executemany-style executions against - UPDATE and DELETE statements. - -.. versionchanged:: 2.0 Removed the ``'batch'`` and ``'None'`` options - from psycopg2 ``executemany_mode``. Control over batching for INSERT - statements is now configured via the - :paramref:`_sa.create_engine.use_insertmanyvalues` engine-level parameter. - -The term "qualifying statements" refers to the statement being executed -being a Core :func:`_expression.insert`, :func:`_expression.update` -or :func:`_expression.delete` construct, and **not** a plain textual SQL -string or one constructed using :func:`_expression.text`. It also may **not** be -a special "extension" statement such as an "ON CONFLICT" "upsert" statement. -When using the ORM, all insert/update/delete statements used by the ORM flush process -are qualifying. - -The "page size" for the psycopg2 "batch" strategy can be affected -by using the ``executemany_batch_page_size`` parameter, which defaults to -100. - -For the "insertmanyvalues" feature, the page size can be controlled using the -:paramref:`_sa.create_engine.insertmanyvalues_page_size` parameter, -which defaults to 1000. An example of modifying both parameters -is below:: - - engine = create_engine( - "postgresql+psycopg2://scott:tiger@host/dbname", - executemany_mode='values_plus_batch', - insertmanyvalues_page_size=5000, executemany_batch_page_size=500) - -.. seealso:: - - :ref:`engine_insertmanyvalues` - background on "insertmanyvalues" - - :ref:`tutorial_multiple_parameters` - General information on using the - :class:`_engine.Connection` - object to execute statements in such a way as to make - use of the DBAPI ``.executemany()`` method. - - -.. _psycopg2_unicode: - -Unicode with Psycopg2 ----------------------- - -The psycopg2 DBAPI driver supports Unicode data transparently. - -The client character encoding can be controlled for the psycopg2 dialect -in the following ways: - -* For PostgreSQL 9.1 and above, the ``client_encoding`` parameter may be - passed in the database URL; this parameter is consumed by the underlying - ``libpq`` PostgreSQL client library:: - - engine = create_engine("postgresql+psycopg2://user:pass@host/dbname?client_encoding=utf8") - - Alternatively, the above ``client_encoding`` value may be passed using - :paramref:`_sa.create_engine.connect_args` for programmatic establishment with - ``libpq``:: - - engine = create_engine( - "postgresql+psycopg2://user:pass@host/dbname", - connect_args={'client_encoding': 'utf8'} - ) - -* For all PostgreSQL versions, psycopg2 supports a client-side encoding - value that will be passed to database connections when they are first - established. The SQLAlchemy psycopg2 dialect supports this using the - ``client_encoding`` parameter passed to :func:`_sa.create_engine`:: - - engine = create_engine( - "postgresql+psycopg2://user:pass@host/dbname", - client_encoding="utf8" - ) - - .. tip:: The above ``client_encoding`` parameter admittedly is very similar - in appearance to usage of the parameter within the - :paramref:`_sa.create_engine.connect_args` dictionary; the difference - above is that the parameter is consumed by psycopg2 and is - passed to the database connection using ``SET client_encoding TO - 'utf8'``; in the previously mentioned style, the parameter is instead - passed through psycopg2 and consumed by the ``libpq`` library. - -* A common way to set up client encoding with PostgreSQL databases is to - ensure it is configured within the server-side postgresql.conf file; - this is the recommended way to set encoding for a server that is - consistently of one encoding in all databases:: - - # postgresql.conf file - - # client_encoding = sql_ascii # actually, defaults to database - # encoding - client_encoding = utf8 - - - -Transactions ------------- - -The psycopg2 dialect fully supports SAVEPOINT and two-phase commit operations. - -.. _psycopg2_isolation_level: - -Psycopg2 Transaction Isolation Level -------------------------------------- - -As discussed in :ref:`postgresql_isolation_level`, -all PostgreSQL dialects support setting of transaction isolation level -both via the ``isolation_level`` parameter passed to :func:`_sa.create_engine` -, -as well as the ``isolation_level`` argument used by -:meth:`_engine.Connection.execution_options`. When using the psycopg2 dialect -, these -options make use of psycopg2's ``set_isolation_level()`` connection method, -rather than emitting a PostgreSQL directive; this is because psycopg2's -API-level setting is always emitted at the start of each transaction in any -case. - -The psycopg2 dialect supports these constants for isolation level: - -* ``READ COMMITTED`` -* ``READ UNCOMMITTED`` -* ``REPEATABLE READ`` -* ``SERIALIZABLE`` -* ``AUTOCOMMIT`` - -.. seealso:: - - :ref:`postgresql_isolation_level` - - :ref:`pg8000_isolation_level` - - -NOTICE logging ---------------- - -The psycopg2 dialect will log PostgreSQL NOTICE messages -via the ``sqlalchemy.dialects.postgresql`` logger. When this logger -is set to the ``logging.INFO`` level, notice messages will be logged:: - - import logging - - logging.getLogger('sqlalchemy.dialects.postgresql').setLevel(logging.INFO) - -Above, it is assumed that logging is configured externally. If this is not -the case, configuration such as ``logging.basicConfig()`` must be utilized:: - - import logging - - logging.basicConfig() # log messages to stdout - logging.getLogger('sqlalchemy.dialects.postgresql').setLevel(logging.INFO) - -.. seealso:: - - `Logging HOWTO <https://docs.python.org/3/howto/logging.html>`_ - on the python.org website - -.. _psycopg2_hstore: - -HSTORE type ------------- - -The ``psycopg2`` DBAPI includes an extension to natively handle marshalling of -the HSTORE type. The SQLAlchemy psycopg2 dialect will enable this extension -by default when psycopg2 version 2.4 or greater is used, and -it is detected that the target database has the HSTORE type set up for use. -In other words, when the dialect makes the first -connection, a sequence like the following is performed: - -1. Request the available HSTORE oids using - ``psycopg2.extras.HstoreAdapter.get_oids()``. - If this function returns a list of HSTORE identifiers, we then determine - that the ``HSTORE`` extension is present. - This function is **skipped** if the version of psycopg2 installed is - less than version 2.4. - -2. If the ``use_native_hstore`` flag is at its default of ``True``, and - we've detected that ``HSTORE`` oids are available, the - ``psycopg2.extensions.register_hstore()`` extension is invoked for all - connections. - -The ``register_hstore()`` extension has the effect of **all Python -dictionaries being accepted as parameters regardless of the type of target -column in SQL**. The dictionaries are converted by this extension into a -textual HSTORE expression. If this behavior is not desired, disable the -use of the hstore extension by setting ``use_native_hstore`` to ``False`` as -follows:: - - engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/test", - use_native_hstore=False) - -The ``HSTORE`` type is **still supported** when the -``psycopg2.extensions.register_hstore()`` extension is not used. It merely -means that the coercion between Python dictionaries and the HSTORE -string format, on both the parameter side and the result side, will take -place within SQLAlchemy's own marshalling logic, and not that of ``psycopg2`` -which may be more performant. - -""" # noqa -from __future__ import annotations - -import collections.abc as collections_abc -import logging -import re -from typing import cast - -from . import ranges -from ._psycopg_common import _PGDialect_common_psycopg -from ._psycopg_common import _PGExecutionContext_common_psycopg -from .base import PGIdentifierPreparer -from .json import JSON -from .json import JSONB -from ... import types as sqltypes -from ... import util -from ...util import FastIntFlag -from ...util import parse_user_argument_for_enum - -logger = logging.getLogger("sqlalchemy.dialects.postgresql") - - -class _PGJSON(JSON): - def result_processor(self, dialect, coltype): - return None - - -class _PGJSONB(JSONB): - def result_processor(self, dialect, coltype): - return None - - -class _Psycopg2Range(ranges.AbstractSingleRangeImpl): - _psycopg2_range_cls = "none" - - def bind_processor(self, dialect): - psycopg2_Range = getattr( - cast(PGDialect_psycopg2, dialect)._psycopg2_extras, - self._psycopg2_range_cls, - ) - - def to_range(value): - if isinstance(value, ranges.Range): - value = psycopg2_Range( - value.lower, value.upper, value.bounds, value.empty - ) - return value - - return to_range - - def result_processor(self, dialect, coltype): - def to_range(value): - if value is not None: - value = ranges.Range( - value._lower, - value._upper, - bounds=value._bounds if value._bounds else "[)", - empty=not value._bounds, - ) - return value - - return to_range - - -class _Psycopg2NumericRange(_Psycopg2Range): - _psycopg2_range_cls = "NumericRange" - - -class _Psycopg2DateRange(_Psycopg2Range): - _psycopg2_range_cls = "DateRange" - - -class _Psycopg2DateTimeRange(_Psycopg2Range): - _psycopg2_range_cls = "DateTimeRange" - - -class _Psycopg2DateTimeTZRange(_Psycopg2Range): - _psycopg2_range_cls = "DateTimeTZRange" - - -class PGExecutionContext_psycopg2(_PGExecutionContext_common_psycopg): - _psycopg2_fetched_rows = None - - def post_exec(self): - self._log_notices(self.cursor) - - def _log_notices(self, cursor): - # check also that notices is an iterable, after it's already - # established that we will be iterating through it. This is to get - # around test suites such as SQLAlchemy's using a Mock object for - # cursor - if not cursor.connection.notices or not isinstance( - cursor.connection.notices, collections_abc.Iterable - ): - return - - for notice in cursor.connection.notices: - # NOTICE messages have a - # newline character at the end - logger.info(notice.rstrip()) - - cursor.connection.notices[:] = [] - - -class PGIdentifierPreparer_psycopg2(PGIdentifierPreparer): - pass - - -class ExecutemanyMode(FastIntFlag): - EXECUTEMANY_VALUES = 0 - EXECUTEMANY_VALUES_PLUS_BATCH = 1 - - -( - EXECUTEMANY_VALUES, - EXECUTEMANY_VALUES_PLUS_BATCH, -) = ExecutemanyMode.__members__.values() - - -class PGDialect_psycopg2(_PGDialect_common_psycopg): - driver = "psycopg2" - - supports_statement_cache = True - supports_server_side_cursors = True - - default_paramstyle = "pyformat" - # set to true based on psycopg2 version - supports_sane_multi_rowcount = False - execution_ctx_cls = PGExecutionContext_psycopg2 - preparer = PGIdentifierPreparer_psycopg2 - psycopg2_version = (0, 0) - use_insertmanyvalues_wo_returning = True - - returns_native_bytes = False - - _has_native_hstore = True - - colspecs = util.update_copy( - _PGDialect_common_psycopg.colspecs, - { - JSON: _PGJSON, - sqltypes.JSON: _PGJSON, - JSONB: _PGJSONB, - ranges.INT4RANGE: _Psycopg2NumericRange, - ranges.INT8RANGE: _Psycopg2NumericRange, - ranges.NUMRANGE: _Psycopg2NumericRange, - ranges.DATERANGE: _Psycopg2DateRange, - ranges.TSRANGE: _Psycopg2DateTimeRange, - ranges.TSTZRANGE: _Psycopg2DateTimeTZRange, - }, - ) - - def __init__( - self, - executemany_mode="values_only", - executemany_batch_page_size=100, - **kwargs, - ): - _PGDialect_common_psycopg.__init__(self, **kwargs) - - if self._native_inet_types: - raise NotImplementedError( - "The psycopg2 dialect does not implement " - "ipaddress type handling; native_inet_types cannot be set " - "to ``True`` when using this dialect." - ) - - # Parse executemany_mode argument, allowing it to be only one of the - # symbol names - self.executemany_mode = parse_user_argument_for_enum( - executemany_mode, - { - EXECUTEMANY_VALUES: ["values_only"], - EXECUTEMANY_VALUES_PLUS_BATCH: ["values_plus_batch"], - }, - "executemany_mode", - ) - - self.executemany_batch_page_size = executemany_batch_page_size - - if self.dbapi and hasattr(self.dbapi, "__version__"): - m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", self.dbapi.__version__) - if m: - self.psycopg2_version = tuple( - int(x) for x in m.group(1, 2, 3) if x is not None - ) - - if self.psycopg2_version < (2, 7): - raise ImportError( - "psycopg2 version 2.7 or higher is required." - ) - - def initialize(self, connection): - super().initialize(connection) - self._has_native_hstore = ( - self.use_native_hstore - and self._hstore_oids(connection.connection.dbapi_connection) - is not None - ) - - self.supports_sane_multi_rowcount = ( - self.executemany_mode is not EXECUTEMANY_VALUES_PLUS_BATCH - ) - - @classmethod - def import_dbapi(cls): - import psycopg2 - - return psycopg2 - - @util.memoized_property - def _psycopg2_extensions(cls): - from psycopg2 import extensions - - return extensions - - @util.memoized_property - def _psycopg2_extras(cls): - from psycopg2 import extras - - return extras - - @util.memoized_property - def _isolation_lookup(self): - extensions = self._psycopg2_extensions - return { - "AUTOCOMMIT": extensions.ISOLATION_LEVEL_AUTOCOMMIT, - "READ COMMITTED": extensions.ISOLATION_LEVEL_READ_COMMITTED, - "READ UNCOMMITTED": extensions.ISOLATION_LEVEL_READ_UNCOMMITTED, - "REPEATABLE READ": extensions.ISOLATION_LEVEL_REPEATABLE_READ, - "SERIALIZABLE": extensions.ISOLATION_LEVEL_SERIALIZABLE, - } - - def set_isolation_level(self, dbapi_connection, level): - dbapi_connection.set_isolation_level(self._isolation_lookup[level]) - - def set_readonly(self, connection, value): - connection.readonly = value - - def get_readonly(self, connection): - return connection.readonly - - def set_deferrable(self, connection, value): - connection.deferrable = value - - def get_deferrable(self, connection): - return connection.deferrable - - def on_connect(self): - extras = self._psycopg2_extras - - fns = [] - if self.client_encoding is not None: - - def on_connect(dbapi_conn): - dbapi_conn.set_client_encoding(self.client_encoding) - - fns.append(on_connect) - - if self.dbapi: - - def on_connect(dbapi_conn): - extras.register_uuid(None, dbapi_conn) - - fns.append(on_connect) - - if self.dbapi and self.use_native_hstore: - - def on_connect(dbapi_conn): - hstore_oids = self._hstore_oids(dbapi_conn) - if hstore_oids is not None: - oid, array_oid = hstore_oids - kw = {"oid": oid} - kw["array_oid"] = array_oid - extras.register_hstore(dbapi_conn, **kw) - - fns.append(on_connect) - - if self.dbapi and self._json_deserializer: - - def on_connect(dbapi_conn): - extras.register_default_json( - dbapi_conn, loads=self._json_deserializer - ) - extras.register_default_jsonb( - dbapi_conn, loads=self._json_deserializer - ) - - fns.append(on_connect) - - if fns: - - def on_connect(dbapi_conn): - for fn in fns: - fn(dbapi_conn) - - return on_connect - else: - return None - - def do_executemany(self, cursor, statement, parameters, context=None): - if self.executemany_mode is EXECUTEMANY_VALUES_PLUS_BATCH: - if self.executemany_batch_page_size: - kwargs = {"page_size": self.executemany_batch_page_size} - else: - kwargs = {} - self._psycopg2_extras.execute_batch( - cursor, statement, parameters, **kwargs - ) - else: - cursor.executemany(statement, parameters) - - def do_begin_twophase(self, connection, xid): - connection.connection.tpc_begin(xid) - - def do_prepare_twophase(self, connection, xid): - connection.connection.tpc_prepare() - - def _do_twophase(self, dbapi_conn, operation, xid, recover=False): - if recover: - if dbapi_conn.status != self._psycopg2_extensions.STATUS_READY: - dbapi_conn.rollback() - operation(xid) - else: - operation() - - def do_rollback_twophase( - self, connection, xid, is_prepared=True, recover=False - ): - dbapi_conn = connection.connection.dbapi_connection - self._do_twophase( - dbapi_conn, dbapi_conn.tpc_rollback, xid, recover=recover - ) - - def do_commit_twophase( - self, connection, xid, is_prepared=True, recover=False - ): - dbapi_conn = connection.connection.dbapi_connection - self._do_twophase( - dbapi_conn, dbapi_conn.tpc_commit, xid, recover=recover - ) - - @util.memoized_instancemethod - def _hstore_oids(self, dbapi_connection): - extras = self._psycopg2_extras - oids = extras.HstoreAdapter.get_oids(dbapi_connection) - if oids is not None and oids[0]: - return oids[0:2] - else: - return None - - def is_disconnect(self, e, connection, cursor): - if isinstance(e, self.dbapi.Error): - # check the "closed" flag. this might not be - # present on old psycopg2 versions. Also, - # this flag doesn't actually help in a lot of disconnect - # situations, so don't rely on it. - if getattr(connection, "closed", False): - return True - - # checks based on strings. in the case that .closed - # didn't cut it, fall back onto these. - str_e = str(e).partition("\n")[0] - for msg in [ - # these error messages from libpq: interfaces/libpq/fe-misc.c - # and interfaces/libpq/fe-secure.c. - "terminating connection", - "closed the connection", - "connection not open", - "could not receive data from server", - "could not send data to server", - # psycopg2 client errors, psycopg2/connection.h, - # psycopg2/cursor.h - "connection already closed", - "cursor already closed", - # not sure where this path is originally from, it may - # be obsolete. It really says "losed", not "closed". - "losed the connection unexpectedly", - # these can occur in newer SSL - "connection has been closed unexpectedly", - "SSL error: decryption failed or bad record mac", - "SSL SYSCALL error: Bad file descriptor", - "SSL SYSCALL error: EOF detected", - "SSL SYSCALL error: Operation timed out", - "SSL SYSCALL error: Bad address", - ]: - idx = str_e.find(msg) - if idx >= 0 and '"' not in str_e[:idx]: - return True - return False - - -dialect = PGDialect_psycopg2 |