You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					1053 lines
				
				38 KiB
			
		
		
			
		
	
	
					1053 lines
				
				38 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								# postgresql/psycopg2.py
							 | 
						||
| 
								 | 
							
								# Copyright (C) 2005-2022 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
							 | 
						||
| 
								 | 
							
								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 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`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``use_native_unicode``: Under Python 2 only, this can be set to False to
							 | 
						||
| 
								 | 
							
								  disable the use of psycopg2's native Unicode support.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  .. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :ref:`psycopg2_disable_native_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")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    `PQconnectdbParams \
							 | 
						||
| 
								 | 
							
								    <https://www.postgresql.org/docs/9.1/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::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    create_engine(
							 | 
						||
| 
								 | 
							
								        "postgresql+psycopg2://user:password@/dbname?host=HostA:port1&host=HostB&host=HostC"
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								A connection to each host is then attempted until either a connection is successful
							 | 
						||
| 
								 | 
							
								or all connections are unsuccessful in which case an error is raised.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionadded:: 1.3.20 Support for multiple hosts in PostgreSQL connection
							 | 
						||
| 
								 | 
							
								   string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    `PQConnString \
							 | 
						||
| 
								 | 
							
								    <https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-CONNSTRING>`_
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 multiple orders of magnitude.
							 | 
						||
| 
								 | 
							
								SQLAlchemy internally makes use of these extensions for ``executemany()`` style
							 | 
						||
| 
								 | 
							
								calls, which correspond to lists of parameters being passed to
							 | 
						||
| 
								 | 
							
								:meth:`_engine.Connection.execute` as detailed in :ref:`multiple parameter
							 | 
						||
| 
								 | 
							
								sets <execute_multiple>`.   The ORM also uses this mode internally whenever
							 | 
						||
| 
								 | 
							
								possible.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The two available extensions on the psycopg2 side are the ``execute_values()``
							 | 
						||
| 
								 | 
							
								and ``execute_batch()`` functions.  The psycopg2 dialect defaults to using the
							 | 
						||
| 
								 | 
							
								``execute_values()`` extension for all qualifying INSERT statements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 1.4  The psycopg2 dialect now defaults to a new mode
							 | 
						||
| 
								 | 
							
								   ``"values_only"`` for ``executemany_mode``, which allows an order of
							 | 
						||
| 
								 | 
							
								   magnitude performance improvement for INSERT statements, but does not
							 | 
						||
| 
								 | 
							
								   include "batch" mode for UPDATE and DELETE statements which removes the
							 | 
						||
| 
								 | 
							
								   ability of ``cursor.rowcount`` to function correctly.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The use of these extensions is controlled by 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.  the psycopg2 execute_values()
							 | 
						||
| 
								 | 
							
								  extension is used for qualifying INSERT statements, which rewrites the INSERT
							 | 
						||
| 
								 | 
							
								  to include multiple VALUES clauses so that many parameter sets can be
							 | 
						||
| 
								 | 
							
								  inserted with one statement.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  .. versionadded:: 1.4 Added ``"values_only"`` setting for ``executemany_mode``
							 | 
						||
| 
								 | 
							
								     which is also now the default.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``None`` - No psycopg2 extensions are not used, and the usual
							 | 
						||
| 
								 | 
							
								  ``cursor.executemany()`` method is used when invoking statements with
							 | 
						||
| 
								 | 
							
								  multiple parameter sets.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``'batch'`` - Uses ``psycopg2.extras.execute_batch`` for all qualifying
							 | 
						||
| 
								 | 
							
								  INSERT, UPDATE and DELETE statements, so that multiple copies
							 | 
						||
| 
								 | 
							
								  of a SQL query, each one corresponding to a parameter set passed to
							 | 
						||
| 
								 | 
							
								  ``executemany()``, are joined into a single SQL string separated by a
							 | 
						||
| 
								 | 
							
								  semicolon.  When using this mode, the :attr:`_engine.CursorResult.rowcount`
							 | 
						||
| 
								 | 
							
								  attribute will not contain a value for executemany-style executions.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* ``'values_plus_batch'``- ``execute_values`` is used for qualifying INSERT
							 | 
						||
| 
								 | 
							
								  statements, ``execute_batch`` is used for UPDATE and DELETE.
							 | 
						||
| 
								 | 
							
								  When using this mode, the :attr:`_engine.CursorResult.rowcount`
							 | 
						||
| 
								 | 
							
								  attribute will not contain a value for executemany-style executions against
							 | 
						||
| 
								 | 
							
								  UPDATE and DELETE statements.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								By "qualifying statements", we mean that the statement being executed
							 | 
						||
| 
								 | 
							
								must be 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`.  When using the
							 | 
						||
| 
								 | 
							
								ORM, all insert/update/delete statements used by the ORM flush process
							 | 
						||
| 
								 | 
							
								are qualifying.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								The "page size" for the "values" and "batch" strategies can be affected
							 | 
						||
| 
								 | 
							
								by using the ``executemany_batch_page_size`` and
							 | 
						||
| 
								 | 
							
								``executemany_values_page_size`` engine parameters.  These
							 | 
						||
| 
								 | 
							
								control how many parameter sets
							 | 
						||
| 
								 | 
							
								should be represented in each execution.    The "values" page size defaults
							 | 
						||
| 
								 | 
							
								to 1000, which is different that psycopg2's default.  The "batch" page
							 | 
						||
| 
								 | 
							
								size defaults to 100.  These can be affected by passing new values to
							 | 
						||
| 
								 | 
							
								:func:`_engine.create_engine`::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine = create_engine(
							 | 
						||
| 
								 | 
							
								        "postgresql+psycopg2://scott:tiger@host/dbname",
							 | 
						||
| 
								 | 
							
								        executemany_mode='values',
							 | 
						||
| 
								 | 
							
								        executemany_values_page_size=10000, executemany_batch_page_size=500)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. versionchanged:: 1.4
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    The default for ``executemany_values_page_size`` is now 1000, up from
							 | 
						||
| 
								 | 
							
								    100.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. seealso::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :ref:`execute_multiple` - 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.   Under Python 2
							 | 
						||
| 
								 | 
							
								only, the SQLAlchemy psycopg2 dialect will enable the
							 | 
						||
| 
								 | 
							
								``psycopg2.extensions.UNICODE`` extension by default to ensure Unicode is
							 | 
						||
| 
								 | 
							
								handled properly; under Python 3, this is psycopg2's default behavior.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								.. _psycopg2_disable_native_unicode:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Disabling Native Unicode
							 | 
						||
| 
								 | 
							
								^^^^^^^^^^^^^^^^^^^^^^^^
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Under Python 2 only, SQLAlchemy can also be instructed to skip the usage of the
							 | 
						||
| 
								 | 
							
								psycopg2 ``UNICODE`` extension and to instead utilize its own unicode
							 | 
						||
| 
								 | 
							
								encode/decode services, which are normally reserved only for those DBAPIs that
							 | 
						||
| 
								 | 
							
								don't fully support unicode directly.  Passing ``use_native_unicode=False`` to
							 | 
						||
| 
								 | 
							
								:func:`_sa.create_engine` will disable usage of ``psycopg2.extensions.
							 | 
						||
| 
								 | 
							
								UNICODE``. SQLAlchemy will instead encode data itself into Python bytestrings
							 | 
						||
| 
								 | 
							
								on the way in and coerce from bytes on the way back, using the value of the
							 | 
						||
| 
								 | 
							
								:func:`_sa.create_engine` ``encoding`` parameter, which defaults to ``utf-8``.
							 | 
						||
| 
								 | 
							
								SQLAlchemy's own unicode encode/decode functionality is steadily becoming
							 | 
						||
| 
								 | 
							
								obsolete as most DBAPIs now support unicode fully.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 absolute_import
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import decimal
							 | 
						||
| 
								 | 
							
								import logging
							 | 
						||
| 
								 | 
							
								import re
							 | 
						||
| 
								 | 
							
								from uuid import UUID as _python_UUID
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from .array import ARRAY as PGARRAY
							 | 
						||
| 
								 | 
							
								from .base import _ColonCast
							 | 
						||
| 
								 | 
							
								from .base import _DECIMAL_TYPES
							 | 
						||
| 
								 | 
							
								from .base import _FLOAT_TYPES
							 | 
						||
| 
								 | 
							
								from .base import _INT_TYPES
							 | 
						||
| 
								 | 
							
								from .base import ENUM
							 | 
						||
| 
								 | 
							
								from .base import PGCompiler
							 | 
						||
| 
								 | 
							
								from .base import PGDialect
							 | 
						||
| 
								 | 
							
								from .base import PGExecutionContext
							 | 
						||
| 
								 | 
							
								from .base import PGIdentifierPreparer
							 | 
						||
| 
								 | 
							
								from .base import UUID
							 | 
						||
| 
								 | 
							
								from .hstore import HSTORE
							 | 
						||
| 
								 | 
							
								from .json import JSON
							 | 
						||
| 
								 | 
							
								from .json import JSONB
							 | 
						||
| 
								 | 
							
								from ... import exc
							 | 
						||
| 
								 | 
							
								from ... import processors
							 | 
						||
| 
								 | 
							
								from ... import types as sqltypes
							 | 
						||
| 
								 | 
							
								from ... import util
							 | 
						||
| 
								 | 
							
								from ...engine import cursor as _cursor
							 | 
						||
| 
								 | 
							
								from ...util import collections_abc
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								logger = logging.getLogger("sqlalchemy.dialects.postgresql")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGNumeric(sqltypes.Numeric):
							 | 
						||
| 
								 | 
							
								    def bind_processor(self, dialect):
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def result_processor(self, dialect, coltype):
							 | 
						||
| 
								 | 
							
								        if self.asdecimal:
							 | 
						||
| 
								 | 
							
								            if coltype in _FLOAT_TYPES:
							 | 
						||
| 
								 | 
							
								                return processors.to_decimal_processor_factory(
							 | 
						||
| 
								 | 
							
								                    decimal.Decimal, self._effective_decimal_return_scale
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								            elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES:
							 | 
						||
| 
								 | 
							
								                # pg8000 returns Decimal natively for 1700
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                raise exc.InvalidRequestError(
							 | 
						||
| 
								 | 
							
								                    "Unknown PG numeric type: %d" % coltype
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if coltype in _FLOAT_TYPES:
							 | 
						||
| 
								 | 
							
								                # pg8000 returns float natively for 701
							 | 
						||
| 
								 | 
							
								                return None
							 | 
						||
| 
								 | 
							
								            elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES:
							 | 
						||
| 
								 | 
							
								                return processors.to_float
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                raise exc.InvalidRequestError(
							 | 
						||
| 
								 | 
							
								                    "Unknown PG numeric type: %d" % coltype
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGEnum(ENUM):
							 | 
						||
| 
								 | 
							
								    def result_processor(self, dialect, coltype):
							 | 
						||
| 
								 | 
							
								        if util.py2k and self._expect_unicode is True:
							 | 
						||
| 
								 | 
							
								            # for py2k, if the enum type needs unicode data (which is set up as
							 | 
						||
| 
								 | 
							
								            # part of the Enum() constructor based on values passed as py2k
							 | 
						||
| 
								 | 
							
								            # unicode objects) we have to use our own converters since
							 | 
						||
| 
								 | 
							
								            # psycopg2's don't work, a rare exception to the "modern DBAPIs
							 | 
						||
| 
								 | 
							
								            # support unicode everywhere" theme of deprecating
							 | 
						||
| 
								 | 
							
								            # convert_unicode=True. Use the special "force_nocheck" directive
							 | 
						||
| 
								 | 
							
								            # which forces unicode conversion to happen on the Python side
							 | 
						||
| 
								 | 
							
								            # without an isinstance() check.   in py3k psycopg2 does the right
							 | 
						||
| 
								 | 
							
								            # thing automatically.
							 | 
						||
| 
								 | 
							
								            self._expect_unicode = "force_nocheck"
							 | 
						||
| 
								 | 
							
								        return super(_PGEnum, self).result_processor(dialect, coltype)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGHStore(HSTORE):
							 | 
						||
| 
								 | 
							
								    def bind_processor(self, dialect):
							 | 
						||
| 
								 | 
							
								        if dialect._has_native_hstore:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return super(_PGHStore, self).bind_processor(dialect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def result_processor(self, dialect, coltype):
							 | 
						||
| 
								 | 
							
								        if dialect._has_native_hstore:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return super(_PGHStore, self).result_processor(dialect, coltype)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGARRAY(PGARRAY):
							 | 
						||
| 
								 | 
							
								    def bind_expression(self, bindvalue):
							 | 
						||
| 
								 | 
							
								        return _ColonCast(bindvalue, self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGJSON(JSON):
							 | 
						||
| 
								 | 
							
								    def result_processor(self, dialect, coltype):
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGJSONB(JSONB):
							 | 
						||
| 
								 | 
							
								    def result_processor(self, dialect, coltype):
							 | 
						||
| 
								 | 
							
								        return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class _PGUUID(UUID):
							 | 
						||
| 
								 | 
							
								    def bind_processor(self, dialect):
							 | 
						||
| 
								 | 
							
								        if not self.as_uuid and dialect.use_native_uuid:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def process(value):
							 | 
						||
| 
								 | 
							
								                if value is not None:
							 | 
						||
| 
								 | 
							
								                    value = _python_UUID(value)
							 | 
						||
| 
								 | 
							
								                return value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return process
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def result_processor(self, dialect, coltype):
							 | 
						||
| 
								 | 
							
								        if not self.as_uuid and dialect.use_native_uuid:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def process(value):
							 | 
						||
| 
								 | 
							
								                if value is not None:
							 | 
						||
| 
								 | 
							
								                    value = str(value)
							 | 
						||
| 
								 | 
							
								                return value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return process
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								_server_side_id = util.counter()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class PGExecutionContext_psycopg2(PGExecutionContext):
							 | 
						||
| 
								 | 
							
								    _psycopg2_fetched_rows = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def create_server_side_cursor(self):
							 | 
						||
| 
								 | 
							
								        # use server-side cursors:
							 | 
						||
| 
								 | 
							
								        # https://lists.initd.org/pipermail/psycopg/2007-January/005251.html
							 | 
						||
| 
								 | 
							
								        ident = "c_%s_%s" % (hex(id(self))[2:], hex(_server_side_id())[2:])
							 | 
						||
| 
								 | 
							
								        return self._dbapi_connection.cursor(ident)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def post_exec(self):
							 | 
						||
| 
								 | 
							
								        if (
							 | 
						||
| 
								 | 
							
								            self._psycopg2_fetched_rows
							 | 
						||
| 
								 | 
							
								            and self.compiled
							 | 
						||
| 
								 | 
							
								            and self.compiled.returning
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            # psycopg2 execute_values will provide for a real cursor where
							 | 
						||
| 
								 | 
							
								            # cursor.description works correctly. however, it executes the
							 | 
						||
| 
								 | 
							
								            # INSERT statement multiple times for multiple pages of rows, so
							 | 
						||
| 
								 | 
							
								            # while this cursor also supports calling .fetchall() directly, in
							 | 
						||
| 
								 | 
							
								            # order to get the list of all rows inserted across multiple pages,
							 | 
						||
| 
								 | 
							
								            # we have to retrieve the aggregated list from the execute_values()
							 | 
						||
| 
								 | 
							
								            # function directly.
							 | 
						||
| 
								 | 
							
								            strat_cls = _cursor.FullyBufferedCursorFetchStrategy
							 | 
						||
| 
								 | 
							
								            self.cursor_fetch_strategy = strat_cls(
							 | 
						||
| 
								 | 
							
								                self.cursor, initial_buffer=self._psycopg2_fetched_rows
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        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 PGCompiler_psycopg2(PGCompiler):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class PGIdentifierPreparer_psycopg2(PGIdentifierPreparer):
							 | 
						||
| 
								 | 
							
								    pass
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								EXECUTEMANY_PLAIN = util.symbol("executemany_plain", canonical=0)
							 | 
						||
| 
								 | 
							
								EXECUTEMANY_BATCH = util.symbol("executemany_batch", canonical=1)
							 | 
						||
| 
								 | 
							
								EXECUTEMANY_VALUES = util.symbol("executemany_values", canonical=2)
							 | 
						||
| 
								 | 
							
								EXECUTEMANY_VALUES_PLUS_BATCH = util.symbol(
							 | 
						||
| 
								 | 
							
								    "executemany_values_plus_batch",
							 | 
						||
| 
								 | 
							
								    canonical=EXECUTEMANY_BATCH | EXECUTEMANY_VALUES,
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class PGDialect_psycopg2(PGDialect):
							 | 
						||
| 
								 | 
							
								    driver = "psycopg2"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    supports_statement_cache = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if util.py2k:
							 | 
						||
| 
								 | 
							
								        # turn off supports_unicode_statements for Python 2. psycopg2 supports
							 | 
						||
| 
								 | 
							
								        # unicode statements in Py2K. But!  it does not support unicode *bound
							 | 
						||
| 
								 | 
							
								        # parameter names* because it uses the Python "%" operator to
							 | 
						||
| 
								 | 
							
								        # interpolate these into the string, and this fails.   So for Py2K, we
							 | 
						||
| 
								 | 
							
								        # have to use full-on encoding for statements and parameters before
							 | 
						||
| 
								 | 
							
								        # passing to cursor.execute().
							 | 
						||
| 
								 | 
							
								        supports_unicode_statements = False
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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
							 | 
						||
| 
								 | 
							
								    statement_compiler = PGCompiler_psycopg2
							 | 
						||
| 
								 | 
							
								    preparer = PGIdentifierPreparer_psycopg2
							 | 
						||
| 
								 | 
							
								    psycopg2_version = (0, 0)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    _has_native_hstore = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    engine_config_types = PGDialect.engine_config_types.union(
							 | 
						||
| 
								 | 
							
								        {"use_native_unicode": util.asbool}
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    colspecs = util.update_copy(
							 | 
						||
| 
								 | 
							
								        PGDialect.colspecs,
							 | 
						||
| 
								 | 
							
								        {
							 | 
						||
| 
								 | 
							
								            sqltypes.Numeric: _PGNumeric,
							 | 
						||
| 
								 | 
							
								            ENUM: _PGEnum,  # needs force_unicode
							 | 
						||
| 
								 | 
							
								            sqltypes.Enum: _PGEnum,  # needs force_unicode
							 | 
						||
| 
								 | 
							
								            HSTORE: _PGHStore,
							 | 
						||
| 
								 | 
							
								            JSON: _PGJSON,
							 | 
						||
| 
								 | 
							
								            sqltypes.JSON: _PGJSON,
							 | 
						||
| 
								 | 
							
								            JSONB: _PGJSONB,
							 | 
						||
| 
								 | 
							
								            UUID: _PGUUID,
							 | 
						||
| 
								 | 
							
								            sqltypes.ARRAY: _PGARRAY,
							 | 
						||
| 
								 | 
							
								        },
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(
							 | 
						||
| 
								 | 
							
								        self,
							 | 
						||
| 
								 | 
							
								        use_native_unicode=True,
							 | 
						||
| 
								 | 
							
								        client_encoding=None,
							 | 
						||
| 
								 | 
							
								        use_native_hstore=True,
							 | 
						||
| 
								 | 
							
								        use_native_uuid=True,
							 | 
						||
| 
								 | 
							
								        executemany_mode="values_only",
							 | 
						||
| 
								 | 
							
								        executemany_batch_page_size=100,
							 | 
						||
| 
								 | 
							
								        executemany_values_page_size=1000,
							 | 
						||
| 
								 | 
							
								        **kwargs
							 | 
						||
| 
								 | 
							
								    ):
							 | 
						||
| 
								 | 
							
								        PGDialect.__init__(self, **kwargs)
							 | 
						||
| 
								 | 
							
								        self.use_native_unicode = use_native_unicode
							 | 
						||
| 
								 | 
							
								        if not use_native_unicode and not util.py2k:
							 | 
						||
| 
								 | 
							
								            raise exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                "psycopg2 native_unicode mode is required under Python 3"
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        if not use_native_hstore:
							 | 
						||
| 
								 | 
							
								            self._has_native_hstore = False
							 | 
						||
| 
								 | 
							
								        self.use_native_hstore = use_native_hstore
							 | 
						||
| 
								 | 
							
								        self.use_native_uuid = use_native_uuid
							 | 
						||
| 
								 | 
							
								        self.supports_unicode_binds = use_native_unicode
							 | 
						||
| 
								 | 
							
								        self.client_encoding = client_encoding
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # Parse executemany_mode argument, allowing it to be only one of the
							 | 
						||
| 
								 | 
							
								        # symbol names
							 | 
						||
| 
								 | 
							
								        self.executemany_mode = util.symbol.parse_user_argument(
							 | 
						||
| 
								 | 
							
								            executemany_mode,
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                EXECUTEMANY_PLAIN: [None],
							 | 
						||
| 
								 | 
							
								                EXECUTEMANY_BATCH: ["batch"],
							 | 
						||
| 
								 | 
							
								                EXECUTEMANY_VALUES: ["values_only"],
							 | 
						||
| 
								 | 
							
								                EXECUTEMANY_VALUES_PLUS_BATCH: ["values_plus_batch", "values"],
							 | 
						||
| 
								 | 
							
								            },
							 | 
						||
| 
								 | 
							
								            "executemany_mode",
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.executemany_mode & EXECUTEMANY_VALUES:
							 | 
						||
| 
								 | 
							
								            self.insert_executemany_returning = True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.executemany_batch_page_size = executemany_batch_page_size
							 | 
						||
| 
								 | 
							
								        self.executemany_values_page_size = executemany_values_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(PGDialect_psycopg2, self).initialize(connection)
							 | 
						||
| 
								 | 
							
								        self._has_native_hstore = (
							 | 
						||
| 
								 | 
							
								            self.use_native_hstore
							 | 
						||
| 
								 | 
							
								            and self._hstore_oids(connection.connection) is not None
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # PGDialect.initialize() checks server version for <= 8.2 and sets
							 | 
						||
| 
								 | 
							
								        # this flag to False if so
							 | 
						||
| 
								 | 
							
								        if not self.full_returning:
							 | 
						||
| 
								 | 
							
								            self.insert_executemany_returning = False
							 | 
						||
| 
								 | 
							
								            self.executemany_mode = EXECUTEMANY_PLAIN
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.supports_sane_multi_rowcount = not (
							 | 
						||
| 
								 | 
							
								            self.executemany_mode & EXECUTEMANY_BATCH
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def dbapi(cls):
							 | 
						||
| 
								 | 
							
								        import psycopg2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return psycopg2
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    def _psycopg2_extensions(cls):
							 | 
						||
| 
								 | 
							
								        from psycopg2 import extensions
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return extensions
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @classmethod
							 | 
						||
| 
								 | 
							
								    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, connection, level):
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            level = self._isolation_lookup[level.replace("_", " ")]
							 | 
						||
| 
								 | 
							
								        except KeyError as err:
							 | 
						||
| 
								 | 
							
								            util.raise_(
							 | 
						||
| 
								 | 
							
								                exc.ArgumentError(
							 | 
						||
| 
								 | 
							
								                    "Invalid value '%s' for isolation_level. "
							 | 
						||
| 
								 | 
							
								                    "Valid isolation levels for %s are %s"
							 | 
						||
| 
								 | 
							
								                    % (level, self.name, ", ".join(self._isolation_lookup))
							 | 
						||
| 
								 | 
							
								                ),
							 | 
						||
| 
								 | 
							
								                replace_context=err,
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        connection.set_isolation_level(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 do_ping(self, dbapi_connection):
							 | 
						||
| 
								 | 
							
								        cursor = None
							 | 
						||
| 
								 | 
							
								        before_autocommit = dbapi_connection.autocommit
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            if not before_autocommit:
							 | 
						||
| 
								 | 
							
								                dbapi_connection.autocommit = True
							 | 
						||
| 
								 | 
							
								            cursor = dbapi_connection.cursor()
							 | 
						||
| 
								 | 
							
								            try:
							 | 
						||
| 
								 | 
							
								                cursor.execute(self._dialect_specific_select_one)
							 | 
						||
| 
								 | 
							
								            finally:
							 | 
						||
| 
								 | 
							
								                cursor.close()
							 | 
						||
| 
								 | 
							
								                if not before_autocommit and not dbapi_connection.closed:
							 | 
						||
| 
								 | 
							
								                    dbapi_connection.autocommit = before_autocommit
							 | 
						||
| 
								 | 
							
								        except self.dbapi.Error as err:
							 | 
						||
| 
								 | 
							
								            if self.is_disconnect(err, dbapi_connection, cursor):
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                raise
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def on_connect(self):
							 | 
						||
| 
								 | 
							
								        extras = self._psycopg2_extras()
							 | 
						||
| 
								 | 
							
								        extensions = self._psycopg2_extensions()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        fns = []
							 | 
						||
| 
								 | 
							
								        if self.client_encoding is not None:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                conn.set_client_encoding(self.client_encoding)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fns.append(on_connect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.isolation_level is not None:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                self.set_isolation_level(conn, self.isolation_level)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fns.append(on_connect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.dbapi and self.use_native_uuid:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                extras.register_uuid(None, conn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fns.append(on_connect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if util.py2k and self.dbapi and self.use_native_unicode:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                extensions.register_type(extensions.UNICODE, conn)
							 | 
						||
| 
								 | 
							
								                extensions.register_type(extensions.UNICODEARRAY, conn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fns.append(on_connect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.dbapi and self.use_native_hstore:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                hstore_oids = self._hstore_oids(conn)
							 | 
						||
| 
								 | 
							
								                if hstore_oids is not None:
							 | 
						||
| 
								 | 
							
								                    oid, array_oid = hstore_oids
							 | 
						||
| 
								 | 
							
								                    kw = {"oid": oid}
							 | 
						||
| 
								 | 
							
								                    if util.py2k:
							 | 
						||
| 
								 | 
							
								                        kw["unicode"] = True
							 | 
						||
| 
								 | 
							
								                    kw["array_oid"] = array_oid
							 | 
						||
| 
								 | 
							
								                    extras.register_hstore(conn, **kw)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fns.append(on_connect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if self.dbapi and self._json_deserializer:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                extras.register_default_json(
							 | 
						||
| 
								 | 
							
								                    conn, loads=self._json_deserializer
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								                extras.register_default_jsonb(
							 | 
						||
| 
								 | 
							
								                    conn, loads=self._json_deserializer
							 | 
						||
| 
								 | 
							
								                )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            fns.append(on_connect)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if fns:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            def on_connect(conn):
							 | 
						||
| 
								 | 
							
								                for fn in fns:
							 | 
						||
| 
								 | 
							
								                    fn(conn)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            return on_connect
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def do_executemany(self, cursor, statement, parameters, context=None):
							 | 
						||
| 
								 | 
							
								        if (
							 | 
						||
| 
								 | 
							
								            self.executemany_mode & EXECUTEMANY_VALUES
							 | 
						||
| 
								 | 
							
								            and context
							 | 
						||
| 
								 | 
							
								            and context.isinsert
							 | 
						||
| 
								 | 
							
								            and context.compiled._is_safe_for_fast_insert_values_helper
							 | 
						||
| 
								 | 
							
								        ):
							 | 
						||
| 
								 | 
							
								            executemany_values = (
							 | 
						||
| 
								 | 
							
								                "(%s)" % context.compiled.insert_single_values_expr
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								            if not self.supports_unicode_statements:
							 | 
						||
| 
								 | 
							
								                executemany_values = executemany_values.encode(self.encoding)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # guard for statement that was altered via event hook or similar
							 | 
						||
| 
								 | 
							
								            if executemany_values not in statement:
							 | 
						||
| 
								 | 
							
								                executemany_values = None
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            executemany_values = None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if executemany_values:
							 | 
						||
| 
								 | 
							
								            statement = statement.replace(executemany_values, "%s")
							 | 
						||
| 
								 | 
							
								            if self.executemany_values_page_size:
							 | 
						||
| 
								 | 
							
								                kwargs = {"page_size": self.executemany_values_page_size}
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                kwargs = {}
							 | 
						||
| 
								 | 
							
								            xtras = self._psycopg2_extras()
							 | 
						||
| 
								 | 
							
								            context._psycopg2_fetched_rows = xtras.execute_values(
							 | 
						||
| 
								 | 
							
								                cursor,
							 | 
						||
| 
								 | 
							
								                statement,
							 | 
						||
| 
								 | 
							
								                parameters,
							 | 
						||
| 
								 | 
							
								                template=executemany_values,
							 | 
						||
| 
								 | 
							
								                fetch=bool(context.compiled.returning),
							 | 
						||
| 
								 | 
							
								                **kwargs
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        elif self.executemany_mode & EXECUTEMANY_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)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @util.memoized_instancemethod
							 | 
						||
| 
								 | 
							
								    def _hstore_oids(self, conn):
							 | 
						||
| 
								 | 
							
								        extras = self._psycopg2_extras()
							 | 
						||
| 
								 | 
							
								        if hasattr(conn, "dbapi_connection"):
							 | 
						||
| 
								 | 
							
								            conn = conn.dbapi_connection
							 | 
						||
| 
								 | 
							
								        oids = extras.HstoreAdapter.get_oids(conn)
							 | 
						||
| 
								 | 
							
								        if oids is not None and oids[0]:
							 | 
						||
| 
								 | 
							
								            return oids[0:2]
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return None
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def create_connect_args(self, url):
							 | 
						||
| 
								 | 
							
								        opts = url.translate_connect_args(username="user")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        is_multihost = False
							 | 
						||
| 
								 | 
							
								        if "host" in url.query:
							 | 
						||
| 
								 | 
							
								            is_multihost = isinstance(url.query["host"], (list, tuple))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if opts:
							 | 
						||
| 
								 | 
							
								            if "port" in opts:
							 | 
						||
| 
								 | 
							
								                opts["port"] = int(opts["port"])
							 | 
						||
| 
								 | 
							
								            opts.update(url.query)
							 | 
						||
| 
								 | 
							
								            if is_multihost:
							 | 
						||
| 
								 | 
							
								                opts["host"] = ",".join(url.query["host"])
							 | 
						||
| 
								 | 
							
								            # send individual dbname, user, password, host, port
							 | 
						||
| 
								 | 
							
								            # parameters to psycopg2.connect()
							 | 
						||
| 
								 | 
							
								            return ([], opts)
							 | 
						||
| 
								 | 
							
								        elif url.query:
							 | 
						||
| 
								 | 
							
								            # any other connection arguments, pass directly
							 | 
						||
| 
								 | 
							
								            opts.update(url.query)
							 | 
						||
| 
								 | 
							
								            if is_multihost:
							 | 
						||
| 
								 | 
							
								                opts["host"] = ",".join(url.query["host"])
							 | 
						||
| 
								 | 
							
								            return ([], opts)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # no connection arguments whatsoever; psycopg2.connect()
							 | 
						||
| 
								 | 
							
								            # requires that "dsn" be present as a blank string.
							 | 
						||
| 
								 | 
							
								            return ([""], opts)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    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
							 |