diff --git a/psycopg2-2.9.1.dist-info/INSTALLER b/psycopg2-2.9.1.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/psycopg2-2.9.1.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/psycopg2-2.9.1.dist-info/LICENSE b/psycopg2-2.9.1.dist-info/LICENSE
deleted file mode 100644
index 9029e70..0000000
--- a/psycopg2-2.9.1.dist-info/LICENSE
+++ /dev/null
@@ -1,49 +0,0 @@
-psycopg2 and the LGPL
----------------------
-
-psycopg2 is free software: you can redistribute it and/or modify it
-under the terms of the GNU Lesser General Public License as published
-by the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-License for more details.
-
-In addition, as a special exception, the copyright holders give
-permission to link this program with the OpenSSL library (or with
-modified versions of OpenSSL that use the same license as OpenSSL),
-and distribute linked combinations including the two.
-
-You must obey the GNU Lesser General Public License in all respects for
-all of the code used other than OpenSSL. If you modify file(s) with this
-exception, you may extend this exception to your version of the file(s),
-but you are not obligated to do so. If you do not wish to do so, delete
-this exception statement from your version. If you delete this exception
-statement from all source files in the program, then also delete it here.
-
-You should have received a copy of the GNU Lesser General Public License
-along with psycopg2 (see the doc/ directory.)
-If not, see .
-
-
-Alternative licenses
---------------------
-
-The following BSD-like license applies (at your option) to the files following
-the pattern ``psycopg/adapter*.{h,c}`` and ``psycopg/microprotocol*.{h,c}``:
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this
- software in a product, an acknowledgment in the product documentation
- would be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not
- be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source distribution.
diff --git a/psycopg2-2.9.1.dist-info/METADATA b/psycopg2-2.9.1.dist-info/METADATA
deleted file mode 100644
index 73f4d9a..0000000
--- a/psycopg2-2.9.1.dist-info/METADATA
+++ /dev/null
@@ -1,110 +0,0 @@
-Metadata-Version: 2.1
-Name: psycopg2
-Version: 2.9.1
-Summary: psycopg2 - Python-PostgreSQL Database Adapter
-Home-page: https://psycopg.org/
-Author: Federico Di Gregorio
-Author-email: fog@initd.org
-Maintainer: Daniele Varrazzo
-Maintainer-email: daniele.varrazzo@gmail.org
-License: LGPL with exceptions
-Project-URL: Homepage, https://psycopg.org/
-Project-URL: Documentation, https://www.psycopg.org/docs/
-Project-URL: Code, https://github.com/psycopg/psycopg2
-Project-URL: Issue Tracker, https://github.com/psycopg/psycopg2/issues
-Project-URL: Download, https://pypi.org/project/psycopg2/
-Platform: any
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: C
-Classifier: Programming Language :: SQL
-Classifier: Topic :: Database
-Classifier: Topic :: Database :: Front-Ends
-Classifier: Topic :: Software Development
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: Unix
-Requires-Python: >=3.6
-License-File: LICENSE
-
-Psycopg is the most popular PostgreSQL database adapter for the Python
-programming language. Its main features are the complete implementation of
-the Python DB API 2.0 specification and the thread safety (several threads can
-share the same connection). It was designed for heavily multi-threaded
-applications that create and destroy lots of cursors and make a large number
-of concurrent "INSERT"s or "UPDATE"s.
-
-Psycopg 2 is mostly implemented in C as a libpq wrapper, resulting in being
-both efficient and secure. It features client-side and server-side cursors,
-asynchronous communication and notifications, "COPY TO/COPY FROM" support.
-Many Python types are supported out-of-the-box and adapted to matching
-PostgreSQL data types; adaptation can be extended and customized thanks to a
-flexible objects adaptation system.
-
-Psycopg 2 is both Unicode and Python 3 friendly.
-
-
-Documentation
--------------
-
-Documentation is included in the ``doc`` directory and is `available online`__.
-
-.. __: https://www.psycopg.org/docs/
-
-For any other resource (source code repository, bug tracker, mailing list)
-please check the `project homepage`__.
-
-.. __: https://psycopg.org/
-
-
-Installation
-------------
-
-Building Psycopg requires a few prerequisites (a C compiler, some development
-packages): please check the install_ and the faq_ documents in the ``doc`` dir
-or online for the details.
-
-If prerequisites are met, you can install psycopg like any other Python
-package, using ``pip`` to download it from PyPI_::
-
- $ pip install psycopg2
-
-or using ``setup.py`` if you have downloaded the source package locally::
-
- $ python setup.py build
- $ sudo python setup.py install
-
-You can also obtain a stand-alone package, not requiring a compiler or
-external libraries, by installing the `psycopg2-binary`_ package from PyPI::
-
- $ pip install psycopg2-binary
-
-The binary package is a practical choice for development and testing but in
-production it is advised to use the package built from sources.
-
-.. _PyPI: https://pypi.org/project/psycopg2/
-.. _psycopg2-binary: https://pypi.org/project/psycopg2-binary/
-.. _install: https://www.psycopg.org/docs/install.html#install-from-source
-.. _faq: https://www.psycopg.org/docs/faq.html#faq-compile
-
-:Linux/OSX: |gh-actions|
-:Windows: |appveyor|
-
-.. |gh-actions| image:: https://github.com/psycopg/psycopg2/actions/workflows/tests.yml/badge.svg
- :target: https://github.com/psycopg/psycopg2/actions/workflows/tests.yml
- :alt: Linux and OSX build status
-
-.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/psycopg/psycopg2?branch=master&svg=true
- :target: https://ci.appveyor.com/project/psycopg/psycopg2/branch/master
- :alt: Windows build status
-
-
diff --git a/psycopg2-2.9.1.dist-info/RECORD b/psycopg2-2.9.1.dist-info/RECORD
deleted file mode 100644
index 60ac44f..0000000
--- a/psycopg2-2.9.1.dist-info/RECORD
+++ /dev/null
@@ -1,30 +0,0 @@
-psycopg2-2.9.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-psycopg2-2.9.1.dist-info/LICENSE,sha256=lhS4XfyacsWyyjMUTB1-HtOxwpdFnZ-yimpXYsLo1xs,2238
-psycopg2-2.9.1.dist-info/METADATA,sha256=6cN6Z60mamjXtehIcpB-x9PfrpSBNKcjuJcal1zA3g4,4337
-psycopg2-2.9.1.dist-info/RECORD,,
-psycopg2-2.9.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-psycopg2-2.9.1.dist-info/WHEEL,sha256=JIE30nfOWUuazI4Vcfiuv_cYm-SkZCh6YOqQQjhm90A,109
-psycopg2-2.9.1.dist-info/top_level.txt,sha256=7dHGpLqQ3w-vGmGEVn-7uK90qU9fyrGdWWi7S-gTcnM,9
-psycopg2/__init__.py,sha256=9mo5Qd0uWHiEBx2CdogGos2kNqtlNNGzbtYlGC0hWS8,4768
-psycopg2/__pycache__/__init__.cpython-39.pyc,,
-psycopg2/__pycache__/_ipaddress.cpython-39.pyc,,
-psycopg2/__pycache__/_json.cpython-39.pyc,,
-psycopg2/__pycache__/_range.cpython-39.pyc,,
-psycopg2/__pycache__/errorcodes.cpython-39.pyc,,
-psycopg2/__pycache__/errors.cpython-39.pyc,,
-psycopg2/__pycache__/extensions.cpython-39.pyc,,
-psycopg2/__pycache__/extras.cpython-39.pyc,,
-psycopg2/__pycache__/pool.cpython-39.pyc,,
-psycopg2/__pycache__/sql.cpython-39.pyc,,
-psycopg2/__pycache__/tz.cpython-39.pyc,,
-psycopg2/_ipaddress.py,sha256=jkuyhLgqUGRBcLNWDM8QJysV6q1Npc_RYH4_kE7JZPU,2922
-psycopg2/_json.py,sha256=XPn4PnzbTg1Dcqz7n1JMv5dKhB5VFV6834GEtxSawt0,7153
-psycopg2/_psycopg.cpython-39-darwin.so,sha256=cvZhpDRhWLkoUzEYCI3zmLpPNVwhLnOZ0UmqssfxhnU,307448
-psycopg2/_range.py,sha256=79xD6i5_aIVYTj_q6lrqfQEz00y3V16nJ5kbScLqy6U,17608
-psycopg2/errorcodes.py,sha256=Z5gbq6FF4nAucL4eWxNwa_UQC7FmA-fz0nr_Ly4KToA,14277
-psycopg2/errors.py,sha256=aAS4dJyTg1bsDzJDCRQAMB_s7zv-Q4yB6Yvih26I-0M,1425
-psycopg2/extensions.py,sha256=CG0kG5vL8Ot503UGlDXXJJFdFWLg4HE2_c1-lLOLc8M,6797
-psycopg2/extras.py,sha256=XOQ6YkyaLeypg2_POPXt8c_MUbuSthdMYywGn9rMNfo,42863
-psycopg2/pool.py,sha256=UGEt8IdP3xNc2PGYNlG4sQvg8nhf4aeCnz39hTR0H8I,6316
-psycopg2/sql.py,sha256=OcFEAmpe2aMfrx0MEk4Lx00XvXXJCmvllaOVbJY-yoE,14779
-psycopg2/tz.py,sha256=r95kK7eGSpOYr_luCyYsznHMzjl52sLjsnSPXkXLzRI,4870
diff --git a/psycopg2-2.9.1.dist-info/REQUESTED b/psycopg2-2.9.1.dist-info/REQUESTED
deleted file mode 100644
index e69de29..0000000
diff --git a/psycopg2-2.9.1.dist-info/WHEEL b/psycopg2-2.9.1.dist-info/WHEEL
deleted file mode 100644
index 9c3644e..0000000
--- a/psycopg2-2.9.1.dist-info/WHEEL
+++ /dev/null
@@ -1,5 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.37.0)
-Root-Is-Purelib: false
-Tag: cp39-cp39-macosx_10_9_x86_64
-
diff --git a/psycopg2-2.9.1.dist-info/top_level.txt b/psycopg2-2.9.1.dist-info/top_level.txt
deleted file mode 100644
index 658130b..0000000
--- a/psycopg2-2.9.1.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-psycopg2
diff --git a/psycopg2/.dylibs/libcom_err.3.0.dylib b/psycopg2/.dylibs/libcom_err.3.0.dylib
deleted file mode 100644
index 82cfe73..0000000
Binary files a/psycopg2/.dylibs/libcom_err.3.0.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libcrypto.1.1.dylib b/psycopg2/.dylibs/libcrypto.1.1.dylib
deleted file mode 100644
index 460bfe8..0000000
Binary files a/psycopg2/.dylibs/libcrypto.1.1.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libgssapi_krb5.2.2.dylib b/psycopg2/.dylibs/libgssapi_krb5.2.2.dylib
deleted file mode 100644
index d343188..0000000
Binary files a/psycopg2/.dylibs/libgssapi_krb5.2.2.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libk5crypto.3.1.dylib b/psycopg2/.dylibs/libk5crypto.3.1.dylib
deleted file mode 100644
index 6b991fd..0000000
Binary files a/psycopg2/.dylibs/libk5crypto.3.1.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libkrb5.3.3.dylib b/psycopg2/.dylibs/libkrb5.3.3.dylib
deleted file mode 100644
index b194672..0000000
Binary files a/psycopg2/.dylibs/libkrb5.3.3.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libkrb5support.1.1.dylib b/psycopg2/.dylibs/libkrb5support.1.1.dylib
deleted file mode 100644
index 56fb47f..0000000
Binary files a/psycopg2/.dylibs/libkrb5support.1.1.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libpq.5.13.dylib b/psycopg2/.dylibs/libpq.5.13.dylib
deleted file mode 100644
index 90233f5..0000000
Binary files a/psycopg2/.dylibs/libpq.5.13.dylib and /dev/null differ
diff --git a/psycopg2/.dylibs/libssl.1.1.dylib b/psycopg2/.dylibs/libssl.1.1.dylib
deleted file mode 100644
index ae79196..0000000
Binary files a/psycopg2/.dylibs/libssl.1.1.dylib and /dev/null differ
diff --git a/psycopg2/__init__.py b/psycopg2/__init__.py
deleted file mode 100644
index 59a8938..0000000
--- a/psycopg2/__init__.py
+++ /dev/null
@@ -1,126 +0,0 @@
-"""A Python driver for PostgreSQL
-
-psycopg is a PostgreSQL_ database adapter for the Python_ programming
-language. This is version 2, a complete rewrite of the original code to
-provide new-style classes for connection and cursor objects and other sweet
-candies. Like the original, psycopg 2 was written with the aim of being very
-small and fast, and stable as a rock.
-
-Homepage: https://psycopg.org/
-
-.. _PostgreSQL: https://www.postgresql.org/
-.. _Python: https://www.python.org/
-
-:Groups:
- * `Connections creation`: connect
- * `Value objects constructors`: Binary, Date, DateFromTicks, Time,
- TimeFromTicks, Timestamp, TimestampFromTicks
-"""
-# psycopg/__init__.py - initialization of the psycopg module
-#
-# Copyright (C) 2003-2019 Federico Di Gregorio
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-# Import modules needed by _psycopg to allow tools like py2exe to do
-# their work without bothering about the module dependencies.
-
-# Note: the first internal import should be _psycopg, otherwise the real cause
-# of a failed loading of the C module may get hidden, see
-# https://archives.postgresql.org/psycopg/2011-02/msg00044.php
-
-# Import the DBAPI-2.0 stuff into top-level module.
-
-from psycopg2._psycopg import ( # noqa
- BINARY, NUMBER, STRING, DATETIME, ROWID,
-
- Binary, Date, Time, Timestamp,
- DateFromTicks, TimeFromTicks, TimestampFromTicks,
-
- Error, Warning, DataError, DatabaseError, ProgrammingError, IntegrityError,
- InterfaceError, InternalError, NotSupportedError, OperationalError,
-
- _connect, apilevel, threadsafety, paramstyle,
- __version__, __libpq_version__,
-)
-
-
-# Register default adapters.
-
-from psycopg2 import extensions as _ext
-_ext.register_adapter(tuple, _ext.SQL_IN)
-_ext.register_adapter(type(None), _ext.NoneAdapter)
-
-# Register the Decimal adapter here instead of in the C layer.
-# This way a new class is registered for each sub-interpreter.
-# See ticket #52
-from decimal import Decimal # noqa
-from psycopg2._psycopg import Decimal as Adapter # noqa
-_ext.register_adapter(Decimal, Adapter)
-del Decimal, Adapter
-
-
-def connect(dsn=None, connection_factory=None, cursor_factory=None, **kwargs):
- """
- Create a new database connection.
-
- The connection parameters can be specified as a string:
-
- conn = psycopg2.connect("dbname=test user=postgres password=secret")
-
- or using a set of keyword arguments:
-
- conn = psycopg2.connect(database="test", user="postgres", password="secret")
-
- Or as a mix of both. The basic connection parameters are:
-
- - *dbname*: the database name
- - *database*: the database name (only as keyword argument)
- - *user*: user name used to authenticate
- - *password*: password used to authenticate
- - *host*: database host address (defaults to UNIX socket if not provided)
- - *port*: connection port number (defaults to 5432 if not provided)
-
- Using the *connection_factory* parameter a different class or connections
- factory can be specified. It should be a callable object taking a dsn
- argument.
-
- Using the *cursor_factory* parameter, a new default cursor factory will be
- used by cursor().
-
- Using *async*=True an asynchronous connection will be created. *async_* is
- a valid alias (for Python versions where ``async`` is a keyword).
-
- Any other keyword parameter will be passed to the underlying client
- library: the list of supported parameters depends on the library version.
-
- """
- kwasync = {}
- if 'async' in kwargs:
- kwasync['async'] = kwargs.pop('async')
- if 'async_' in kwargs:
- kwasync['async_'] = kwargs.pop('async_')
-
- dsn = _ext.make_dsn(dsn, **kwargs)
- conn = _connect(dsn, connection_factory=connection_factory, **kwasync)
- if cursor_factory is not None:
- conn.cursor_factory = cursor_factory
-
- return conn
diff --git a/psycopg2/_ipaddress.py b/psycopg2/_ipaddress.py
deleted file mode 100644
index d38566c..0000000
--- a/psycopg2/_ipaddress.py
+++ /dev/null
@@ -1,90 +0,0 @@
-"""Implementation of the ipaddres-based network types adaptation
-"""
-
-# psycopg/_ipaddress.py - Ipaddres-based network types adaptation
-#
-# Copyright (C) 2016-2019 Daniele Varrazzo
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-from psycopg2.extensions import (
- new_type, new_array_type, register_type, register_adapter, QuotedString)
-
-# The module is imported on register_ipaddress
-ipaddress = None
-
-# The typecasters are created only once
-_casters = None
-
-
-def register_ipaddress(conn_or_curs=None):
- """
- Register conversion support between `ipaddress` objects and `network types`__.
-
- :param conn_or_curs: the scope where to register the type casters.
- If `!None` register them globally.
-
- After the function is called, PostgreSQL :sql:`inet` values will be
- converted into `~ipaddress.IPv4Interface` or `~ipaddress.IPv6Interface`
- objects, :sql:`cidr` values into into `~ipaddress.IPv4Network` or
- `~ipaddress.IPv6Network`.
-
- .. __: https://www.postgresql.org/docs/current/static/datatype-net-types.html
- """
- global ipaddress
- import ipaddress
-
- global _casters
- if _casters is None:
- _casters = _make_casters()
-
- for c in _casters:
- register_type(c, conn_or_curs)
-
- for t in [ipaddress.IPv4Interface, ipaddress.IPv6Interface,
- ipaddress.IPv4Network, ipaddress.IPv6Network]:
- register_adapter(t, adapt_ipaddress)
-
-
-def _make_casters():
- inet = new_type((869,), 'INET', cast_interface)
- ainet = new_array_type((1041,), 'INET[]', inet)
-
- cidr = new_type((650,), 'CIDR', cast_network)
- acidr = new_array_type((651,), 'CIDR[]', cidr)
-
- return [inet, ainet, cidr, acidr]
-
-
-def cast_interface(s, cur=None):
- if s is None:
- return None
- # Py2 version force the use of unicode. meh.
- return ipaddress.ip_interface(str(s))
-
-
-def cast_network(s, cur=None):
- if s is None:
- return None
- return ipaddress.ip_network(str(s))
-
-
-def adapt_ipaddress(obj):
- return QuotedString(str(obj))
diff --git a/psycopg2/_json.py b/psycopg2/_json.py
deleted file mode 100644
index 9502422..0000000
--- a/psycopg2/_json.py
+++ /dev/null
@@ -1,199 +0,0 @@
-"""Implementation of the JSON adaptation objects
-
-This module exists to avoid a circular import problem: pyscopg2.extras depends
-on psycopg2.extension, so I can't create the default JSON typecasters in
-extensions importing register_json from extras.
-"""
-
-# psycopg/_json.py - Implementation of the JSON adaptation objects
-#
-# Copyright (C) 2012-2019 Daniele Varrazzo
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import json
-
-from psycopg2._psycopg import ISQLQuote, QuotedString
-from psycopg2._psycopg import new_type, new_array_type, register_type
-
-
-# oids from PostgreSQL 9.2
-JSON_OID = 114
-JSONARRAY_OID = 199
-
-# oids from PostgreSQL 9.4
-JSONB_OID = 3802
-JSONBARRAY_OID = 3807
-
-
-class Json:
- """
- An `~psycopg2.extensions.ISQLQuote` wrapper to adapt a Python object to
- :sql:`json` data type.
-
- `!Json` can be used to wrap any object supported by the provided *dumps*
- function. If none is provided, the standard :py:func:`json.dumps()` is
- used.
-
- """
- def __init__(self, adapted, dumps=None):
- self.adapted = adapted
- self._conn = None
- self._dumps = dumps or json.dumps
-
- def __conform__(self, proto):
- if proto is ISQLQuote:
- return self
-
- def dumps(self, obj):
- """Serialize *obj* in JSON format.
-
- The default is to call `!json.dumps()` or the *dumps* function
- provided in the constructor. You can override this method to create a
- customized JSON wrapper.
- """
- return self._dumps(obj)
-
- def prepare(self, conn):
- self._conn = conn
-
- def getquoted(self):
- s = self.dumps(self.adapted)
- qs = QuotedString(s)
- if self._conn is not None:
- qs.prepare(self._conn)
- return qs.getquoted()
-
- def __str__(self):
- # getquoted is binary
- return self.getquoted().decode('ascii', 'replace')
-
-
-def register_json(conn_or_curs=None, globally=False, loads=None,
- oid=None, array_oid=None, name='json'):
- """Create and register typecasters converting :sql:`json` type to Python objects.
-
- :param conn_or_curs: a connection or cursor used to find the :sql:`json`
- and :sql:`json[]` oids; the typecasters are registered in a scope
- limited to this object, unless *globally* is set to `!True`. It can be
- `!None` if the oids are provided
- :param globally: if `!False` register the typecasters only on
- *conn_or_curs*, otherwise register them globally
- :param loads: the function used to parse the data into a Python object. If
- `!None` use `!json.loads()`, where `!json` is the module chosen
- according to the Python version (see above)
- :param oid: the OID of the :sql:`json` type if known; If not, it will be
- queried on *conn_or_curs*
- :param array_oid: the OID of the :sql:`json[]` array type if known;
- if not, it will be queried on *conn_or_curs*
- :param name: the name of the data type to look for in *conn_or_curs*
-
- The connection or cursor passed to the function will be used to query the
- database and look for the OID of the :sql:`json` type (or an alternative
- type if *name* if provided). No query is performed if *oid* and *array_oid*
- are provided. Raise `~psycopg2.ProgrammingError` if the type is not found.
-
- """
- if oid is None:
- oid, array_oid = _get_json_oids(conn_or_curs, name)
-
- JSON, JSONARRAY = _create_json_typecasters(
- oid, array_oid, loads=loads, name=name.upper())
-
- register_type(JSON, not globally and conn_or_curs or None)
-
- if JSONARRAY is not None:
- register_type(JSONARRAY, not globally and conn_or_curs or None)
-
- return JSON, JSONARRAY
-
-
-def register_default_json(conn_or_curs=None, globally=False, loads=None):
- """
- Create and register :sql:`json` typecasters for PostgreSQL 9.2 and following.
-
- Since PostgreSQL 9.2 :sql:`json` is a builtin type, hence its oid is known
- and fixed. This function allows specifying a customized *loads* function
- for the default :sql:`json` type without querying the database.
- All the parameters have the same meaning of `register_json()`.
- """
- return register_json(conn_or_curs=conn_or_curs, globally=globally,
- loads=loads, oid=JSON_OID, array_oid=JSONARRAY_OID)
-
-
-def register_default_jsonb(conn_or_curs=None, globally=False, loads=None):
- """
- Create and register :sql:`jsonb` typecasters for PostgreSQL 9.4 and following.
-
- As in `register_default_json()`, the function allows to register a
- customized *loads* function for the :sql:`jsonb` type at its known oid for
- PostgreSQL 9.4 and following versions. All the parameters have the same
- meaning of `register_json()`.
- """
- return register_json(conn_or_curs=conn_or_curs, globally=globally,
- loads=loads, oid=JSONB_OID, array_oid=JSONBARRAY_OID, name='jsonb')
-
-
-def _create_json_typecasters(oid, array_oid, loads=None, name='JSON'):
- """Create typecasters for json data type."""
- if loads is None:
- loads = json.loads
-
- def typecast_json(s, cur):
- if s is None:
- return None
- return loads(s)
-
- JSON = new_type((oid, ), name, typecast_json)
- if array_oid is not None:
- JSONARRAY = new_array_type((array_oid, ), f"{name}ARRAY", JSON)
- else:
- JSONARRAY = None
-
- return JSON, JSONARRAY
-
-
-def _get_json_oids(conn_or_curs, name='json'):
- # lazy imports
- from psycopg2.extensions import STATUS_IN_TRANSACTION
- from psycopg2.extras import _solve_conn_curs
-
- conn, curs = _solve_conn_curs(conn_or_curs)
-
- # Store the transaction status of the connection to revert it after use
- conn_status = conn.status
-
- # column typarray not available before PG 8.3
- typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
-
- # get the oid for the hstore
- curs.execute(
- "SELECT t.oid, %s FROM pg_type t WHERE t.typname = %%s;"
- % typarray, (name,))
- r = curs.fetchone()
-
- # revert the status of the connection as before the command
- if conn_status != STATUS_IN_TRANSACTION and not conn.autocommit:
- conn.rollback()
-
- if not r:
- raise conn.ProgrammingError(f"{name} data type not found")
-
- return r
diff --git a/psycopg2/_psycopg.cpython-37m-darwin.so b/psycopg2/_psycopg.cpython-37m-darwin.so
deleted file mode 100755
index 603dcc2..0000000
Binary files a/psycopg2/_psycopg.cpython-37m-darwin.so and /dev/null differ
diff --git a/psycopg2/_psycopg.cpython-39-darwin.so b/psycopg2/_psycopg.cpython-39-darwin.so
deleted file mode 100755
index 6705599..0000000
Binary files a/psycopg2/_psycopg.cpython-39-darwin.so and /dev/null differ
diff --git a/psycopg2/_range.py b/psycopg2/_range.py
deleted file mode 100644
index 19a05d3..0000000
--- a/psycopg2/_range.py
+++ /dev/null
@@ -1,537 +0,0 @@
-"""Implementation of the Range type and adaptation
-
-"""
-
-# psycopg/_range.py - Implementation of the Range type and adaptation
-#
-# Copyright (C) 2012-2019 Daniele Varrazzo
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import re
-
-from psycopg2._psycopg import ProgrammingError, InterfaceError
-from psycopg2.extensions import ISQLQuote, adapt, register_adapter
-from psycopg2.extensions import new_type, new_array_type, register_type
-
-
-class Range:
- """Python representation for a PostgreSQL |range|_ type.
-
- :param lower: lower bound for the range. `!None` means unbound
- :param upper: upper bound for the range. `!None` means unbound
- :param bounds: one of the literal strings ``()``, ``[)``, ``(]``, ``[]``,
- representing whether the lower or upper bounds are included
- :param empty: if `!True`, the range is empty
-
- """
- __slots__ = ('_lower', '_upper', '_bounds')
-
- def __init__(self, lower=None, upper=None, bounds='[)', empty=False):
- if not empty:
- if bounds not in ('[)', '(]', '()', '[]'):
- raise ValueError(f"bound flags not valid: {bounds!r}")
-
- self._lower = lower
- self._upper = upper
- self._bounds = bounds
- else:
- self._lower = self._upper = self._bounds = None
-
- def __repr__(self):
- if self._bounds is None:
- return f"{self.__class__.__name__}(empty=True)"
- else:
- return "{}({!r}, {!r}, {!r})".format(self.__class__.__name__,
- self._lower, self._upper, self._bounds)
-
- def __str__(self):
- if self._bounds is None:
- return 'empty'
-
- items = [
- self._bounds[0],
- str(self._lower),
- ', ',
- str(self._upper),
- self._bounds[1]
- ]
- return ''.join(items)
-
- @property
- def lower(self):
- """The lower bound of the range. `!None` if empty or unbound."""
- return self._lower
-
- @property
- def upper(self):
- """The upper bound of the range. `!None` if empty or unbound."""
- return self._upper
-
- @property
- def isempty(self):
- """`!True` if the range is empty."""
- return self._bounds is None
-
- @property
- def lower_inf(self):
- """`!True` if the range doesn't have a lower bound."""
- if self._bounds is None:
- return False
- return self._lower is None
-
- @property
- def upper_inf(self):
- """`!True` if the range doesn't have an upper bound."""
- if self._bounds is None:
- return False
- return self._upper is None
-
- @property
- def lower_inc(self):
- """`!True` if the lower bound is included in the range."""
- if self._bounds is None or self._lower is None:
- return False
- return self._bounds[0] == '['
-
- @property
- def upper_inc(self):
- """`!True` if the upper bound is included in the range."""
- if self._bounds is None or self._upper is None:
- return False
- return self._bounds[1] == ']'
-
- def __contains__(self, x):
- if self._bounds is None:
- return False
-
- if self._lower is not None:
- if self._bounds[0] == '[':
- if x < self._lower:
- return False
- else:
- if x <= self._lower:
- return False
-
- if self._upper is not None:
- if self._bounds[1] == ']':
- if x > self._upper:
- return False
- else:
- if x >= self._upper:
- return False
-
- return True
-
- def __bool__(self):
- return self._bounds is not None
-
- def __nonzero__(self):
- # Python 2 compatibility
- return type(self).__bool__(self)
-
- def __eq__(self, other):
- if not isinstance(other, Range):
- return False
- return (self._lower == other._lower
- and self._upper == other._upper
- and self._bounds == other._bounds)
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __hash__(self):
- return hash((self._lower, self._upper, self._bounds))
-
- # as the postgres docs describe for the server-side stuff,
- # ordering is rather arbitrary, but will remain stable
- # and consistent.
-
- def __lt__(self, other):
- if not isinstance(other, Range):
- return NotImplemented
- for attr in ('_lower', '_upper', '_bounds'):
- self_value = getattr(self, attr)
- other_value = getattr(other, attr)
- if self_value == other_value:
- pass
- elif self_value is None:
- return True
- elif other_value is None:
- return False
- else:
- return self_value < other_value
- return False
-
- def __le__(self, other):
- if self == other:
- return True
- else:
- return self.__lt__(other)
-
- def __gt__(self, other):
- if isinstance(other, Range):
- return other.__lt__(self)
- else:
- return NotImplemented
-
- def __ge__(self, other):
- if self == other:
- return True
- else:
- return self.__gt__(other)
-
- def __getstate__(self):
- return {slot: getattr(self, slot)
- for slot in self.__slots__ if hasattr(self, slot)}
-
- def __setstate__(self, state):
- for slot, value in state.items():
- setattr(self, slot, value)
-
-
-def register_range(pgrange, pyrange, conn_or_curs, globally=False):
- """Create and register an adapter and the typecasters to convert between
- a PostgreSQL |range|_ type and a PostgreSQL `Range` subclass.
-
- :param pgrange: the name of the PostgreSQL |range| type. Can be
- schema-qualified
- :param pyrange: a `Range` strict subclass, or just a name to give to a new
- class
- :param conn_or_curs: a connection or cursor used to find the oid of the
- range and its subtype; the typecaster is registered in a scope limited
- to this object, unless *globally* is set to `!True`
- :param globally: if `!False` (default) register the typecaster only on
- *conn_or_curs*, otherwise register it globally
- :return: `RangeCaster` instance responsible for the conversion
-
- If a string is passed to *pyrange*, a new `Range` subclass is created
- with such name and will be available as the `~RangeCaster.range` attribute
- of the returned `RangeCaster` object.
-
- The function queries the database on *conn_or_curs* to inspect the
- *pgrange* type and raises `~psycopg2.ProgrammingError` if the type is not
- found. If querying the database is not advisable, use directly the
- `RangeCaster` class and register the adapter and typecasters using the
- provided functions.
-
- """
- caster = RangeCaster._from_db(pgrange, pyrange, conn_or_curs)
- caster._register(not globally and conn_or_curs or None)
- return caster
-
-
-class RangeAdapter:
- """`ISQLQuote` adapter for `Range` subclasses.
-
- This is an abstract class: concrete classes must set a `name` class
- attribute or override `getquoted()`.
- """
- name = None
-
- def __init__(self, adapted):
- self.adapted = adapted
-
- def __conform__(self, proto):
- if self._proto is ISQLQuote:
- return self
-
- def prepare(self, conn):
- self._conn = conn
-
- def getquoted(self):
- if self.name is None:
- raise NotImplementedError(
- 'RangeAdapter must be subclassed overriding its name '
- 'or the getquoted() method')
-
- r = self.adapted
- if r.isempty:
- return b"'empty'::" + self.name.encode('utf8')
-
- if r.lower is not None:
- a = adapt(r.lower)
- if hasattr(a, 'prepare'):
- a.prepare(self._conn)
- lower = a.getquoted()
- else:
- lower = b'NULL'
-
- if r.upper is not None:
- a = adapt(r.upper)
- if hasattr(a, 'prepare'):
- a.prepare(self._conn)
- upper = a.getquoted()
- else:
- upper = b'NULL'
-
- return self.name.encode('utf8') + b'(' + lower + b', ' + upper \
- + b", '" + r._bounds.encode('utf8') + b"')"
-
-
-class RangeCaster:
- """Helper class to convert between `Range` and PostgreSQL range types.
-
- Objects of this class are usually created by `register_range()`. Manual
- creation could be useful if querying the database is not advisable: in
- this case the oids must be provided.
- """
- def __init__(self, pgrange, pyrange, oid, subtype_oid, array_oid=None):
- self.subtype_oid = subtype_oid
- self._create_ranges(pgrange, pyrange)
-
- name = self.adapter.name or self.adapter.__class__.__name__
-
- self.typecaster = new_type((oid,), name, self.parse)
-
- if array_oid is not None:
- self.array_typecaster = new_array_type(
- (array_oid,), name + "ARRAY", self.typecaster)
- else:
- self.array_typecaster = None
-
- def _create_ranges(self, pgrange, pyrange):
- """Create Range and RangeAdapter classes if needed."""
- # if got a string create a new RangeAdapter concrete type (with a name)
- # else take it as an adapter. Passing an adapter should be considered
- # an implementation detail and is not documented. It is currently used
- # for the numeric ranges.
- self.adapter = None
- if isinstance(pgrange, str):
- self.adapter = type(pgrange, (RangeAdapter,), {})
- self.adapter.name = pgrange
- else:
- try:
- if issubclass(pgrange, RangeAdapter) \
- and pgrange is not RangeAdapter:
- self.adapter = pgrange
- except TypeError:
- pass
-
- if self.adapter is None:
- raise TypeError(
- 'pgrange must be a string or a RangeAdapter strict subclass')
-
- self.range = None
- try:
- if isinstance(pyrange, str):
- self.range = type(pyrange, (Range,), {})
- if issubclass(pyrange, Range) and pyrange is not Range:
- self.range = pyrange
- except TypeError:
- pass
-
- if self.range is None:
- raise TypeError(
- 'pyrange must be a type or a Range strict subclass')
-
- @classmethod
- def _from_db(self, name, pyrange, conn_or_curs):
- """Return a `RangeCaster` instance for the type *pgrange*.
-
- Raise `ProgrammingError` if the type is not found.
- """
- from psycopg2.extensions import STATUS_IN_TRANSACTION
- from psycopg2.extras import _solve_conn_curs
- conn, curs = _solve_conn_curs(conn_or_curs)
-
- if conn.info.server_version < 90200:
- raise ProgrammingError("range types not available in version %s"
- % conn.info.server_version)
-
- # Store the transaction status of the connection to revert it after use
- conn_status = conn.status
-
- # Use the correct schema
- if '.' in name:
- schema, tname = name.split('.', 1)
- else:
- tname = name
- schema = 'public'
-
- # get the type oid and attributes
- try:
- curs.execute("""\
-select rngtypid, rngsubtype,
- (select typarray from pg_type where oid = rngtypid)
-from pg_range r
-join pg_type t on t.oid = rngtypid
-join pg_namespace ns on ns.oid = typnamespace
-where typname = %s and ns.nspname = %s;
-""", (tname, schema))
-
- except ProgrammingError:
- if not conn.autocommit:
- conn.rollback()
- raise
- else:
- rec = curs.fetchone()
-
- # revert the status of the connection as before the command
- if (conn_status != STATUS_IN_TRANSACTION
- and not conn.autocommit):
- conn.rollback()
-
- if not rec:
- raise ProgrammingError(
- f"PostgreSQL type '{name}' not found")
-
- type, subtype, array = rec
-
- return RangeCaster(name, pyrange,
- oid=type, subtype_oid=subtype, array_oid=array)
-
- _re_range = re.compile(r"""
- ( \(|\[ ) # lower bound flag
- (?: # lower bound:
- " ( (?: [^"] | "")* ) " # - a quoted string
- | ( [^",]+ ) # - or an unquoted string
- )? # - or empty (not catched)
- ,
- (?: # upper bound:
- " ( (?: [^"] | "")* ) " # - a quoted string
- | ( [^"\)\]]+ ) # - or an unquoted string
- )? # - or empty (not catched)
- ( \)|\] ) # upper bound flag
- """, re.VERBOSE)
-
- _re_undouble = re.compile(r'(["\\])\1')
-
- def parse(self, s, cur=None):
- if s is None:
- return None
-
- if s == 'empty':
- return self.range(empty=True)
-
- m = self._re_range.match(s)
- if m is None:
- raise InterfaceError(f"failed to parse range: '{s}'")
-
- lower = m.group(3)
- if lower is None:
- lower = m.group(2)
- if lower is not None:
- lower = self._re_undouble.sub(r"\1", lower)
-
- upper = m.group(5)
- if upper is None:
- upper = m.group(4)
- if upper is not None:
- upper = self._re_undouble.sub(r"\1", upper)
-
- if cur is not None:
- lower = cur.cast(self.subtype_oid, lower)
- upper = cur.cast(self.subtype_oid, upper)
-
- bounds = m.group(1) + m.group(6)
-
- return self.range(lower, upper, bounds)
-
- def _register(self, scope=None):
- register_type(self.typecaster, scope)
- if self.array_typecaster is not None:
- register_type(self.array_typecaster, scope)
-
- register_adapter(self.range, self.adapter)
-
-
-class NumericRange(Range):
- """A `Range` suitable to pass Python numeric types to a PostgreSQL range.
-
- PostgreSQL types :sql:`int4range`, :sql:`int8range`, :sql:`numrange` are
- casted into `!NumericRange` instances.
- """
- pass
-
-
-class DateRange(Range):
- """Represents :sql:`daterange` values."""
- pass
-
-
-class DateTimeRange(Range):
- """Represents :sql:`tsrange` values."""
- pass
-
-
-class DateTimeTZRange(Range):
- """Represents :sql:`tstzrange` values."""
- pass
-
-
-# Special adaptation for NumericRange. Allows to pass number range regardless
-# of whether they are ints, floats and what size of ints are, which are
-# pointless in Python world. On the way back, no numeric range is casted to
-# NumericRange, but only to their subclasses
-
-class NumberRangeAdapter(RangeAdapter):
- """Adapt a range if the subtype doesn't need quotes."""
- def getquoted(self):
- r = self.adapted
- if r.isempty:
- return b"'empty'"
-
- if not r.lower_inf:
- # not exactly: we are relying that none of these object is really
- # quoted (they are numbers). Also, I'm lazy and not preparing the
- # adapter because I assume encoding doesn't matter for these
- # objects.
- lower = adapt(r.lower).getquoted().decode('ascii')
- else:
- lower = ''
-
- if not r.upper_inf:
- upper = adapt(r.upper).getquoted().decode('ascii')
- else:
- upper = ''
-
- return (f"'{r._bounds[0]}{lower},{upper}{r._bounds[1]}'").encode('ascii')
-
-
-# TODO: probably won't work with infs, nans and other tricky cases.
-register_adapter(NumericRange, NumberRangeAdapter)
-
-# Register globally typecasters and adapters for builtin range types.
-
-# note: the adapter is registered more than once, but this is harmless.
-int4range_caster = RangeCaster(NumberRangeAdapter, NumericRange,
- oid=3904, subtype_oid=23, array_oid=3905)
-int4range_caster._register()
-
-int8range_caster = RangeCaster(NumberRangeAdapter, NumericRange,
- oid=3926, subtype_oid=20, array_oid=3927)
-int8range_caster._register()
-
-numrange_caster = RangeCaster(NumberRangeAdapter, NumericRange,
- oid=3906, subtype_oid=1700, array_oid=3907)
-numrange_caster._register()
-
-daterange_caster = RangeCaster('daterange', DateRange,
- oid=3912, subtype_oid=1082, array_oid=3913)
-daterange_caster._register()
-
-tsrange_caster = RangeCaster('tsrange', DateTimeRange,
- oid=3908, subtype_oid=1114, array_oid=3909)
-tsrange_caster._register()
-
-tstzrange_caster = RangeCaster('tstzrange', DateTimeTZRange,
- oid=3910, subtype_oid=1184, array_oid=3911)
-tstzrange_caster._register()
diff --git a/psycopg2/errorcodes.py b/psycopg2/errorcodes.py
deleted file mode 100644
index d511f1c..0000000
--- a/psycopg2/errorcodes.py
+++ /dev/null
@@ -1,447 +0,0 @@
-"""Error codes for PostgreSQL
-
-This module contains symbolic names for all PostgreSQL error codes.
-"""
-# psycopg2/errorcodes.py - PostgreSQL error codes
-#
-# Copyright (C) 2006-2019 Johan Dahlin
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-#
-# Based on:
-#
-# https://www.postgresql.org/docs/current/static/errcodes-appendix.html
-#
-
-
-def lookup(code, _cache={}):
- """Lookup an error code or class code and return its symbolic name.
-
- Raise `KeyError` if the code is not found.
- """
- if _cache:
- return _cache[code]
-
- # Generate the lookup map at first usage.
- tmp = {}
- for k, v in globals().items():
- if isinstance(v, str) and len(v) in (2, 5):
- # Strip trailing underscore used to disambiguate duplicate values
- tmp[v] = k.rstrip("_")
-
- assert tmp
-
- # Atomic update, to avoid race condition on import (bug #382)
- _cache.update(tmp)
-
- return _cache[code]
-
-
-# autogenerated data: do not edit below this point.
-
-# Error classes
-CLASS_SUCCESSFUL_COMPLETION = '00'
-CLASS_WARNING = '01'
-CLASS_NO_DATA = '02'
-CLASS_SQL_STATEMENT_NOT_YET_COMPLETE = '03'
-CLASS_CONNECTION_EXCEPTION = '08'
-CLASS_TRIGGERED_ACTION_EXCEPTION = '09'
-CLASS_FEATURE_NOT_SUPPORTED = '0A'
-CLASS_INVALID_TRANSACTION_INITIATION = '0B'
-CLASS_LOCATOR_EXCEPTION = '0F'
-CLASS_INVALID_GRANTOR = '0L'
-CLASS_INVALID_ROLE_SPECIFICATION = '0P'
-CLASS_DIAGNOSTICS_EXCEPTION = '0Z'
-CLASS_CASE_NOT_FOUND = '20'
-CLASS_CARDINALITY_VIOLATION = '21'
-CLASS_DATA_EXCEPTION = '22'
-CLASS_INTEGRITY_CONSTRAINT_VIOLATION = '23'
-CLASS_INVALID_CURSOR_STATE = '24'
-CLASS_INVALID_TRANSACTION_STATE = '25'
-CLASS_INVALID_SQL_STATEMENT_NAME = '26'
-CLASS_TRIGGERED_DATA_CHANGE_VIOLATION = '27'
-CLASS_INVALID_AUTHORIZATION_SPECIFICATION = '28'
-CLASS_DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST = '2B'
-CLASS_INVALID_TRANSACTION_TERMINATION = '2D'
-CLASS_SQL_ROUTINE_EXCEPTION = '2F'
-CLASS_INVALID_CURSOR_NAME = '34'
-CLASS_EXTERNAL_ROUTINE_EXCEPTION = '38'
-CLASS_EXTERNAL_ROUTINE_INVOCATION_EXCEPTION = '39'
-CLASS_SAVEPOINT_EXCEPTION = '3B'
-CLASS_INVALID_CATALOG_NAME = '3D'
-CLASS_INVALID_SCHEMA_NAME = '3F'
-CLASS_TRANSACTION_ROLLBACK = '40'
-CLASS_SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = '42'
-CLASS_WITH_CHECK_OPTION_VIOLATION = '44'
-CLASS_INSUFFICIENT_RESOURCES = '53'
-CLASS_PROGRAM_LIMIT_EXCEEDED = '54'
-CLASS_OBJECT_NOT_IN_PREREQUISITE_STATE = '55'
-CLASS_OPERATOR_INTERVENTION = '57'
-CLASS_SYSTEM_ERROR = '58'
-CLASS_SNAPSHOT_FAILURE = '72'
-CLASS_CONFIGURATION_FILE_ERROR = 'F0'
-CLASS_FOREIGN_DATA_WRAPPER_ERROR = 'HV'
-CLASS_PL_PGSQL_ERROR = 'P0'
-CLASS_INTERNAL_ERROR = 'XX'
-
-# Class 00 - Successful Completion
-SUCCESSFUL_COMPLETION = '00000'
-
-# Class 01 - Warning
-WARNING = '01000'
-NULL_VALUE_ELIMINATED_IN_SET_FUNCTION = '01003'
-STRING_DATA_RIGHT_TRUNCATION_ = '01004'
-PRIVILEGE_NOT_REVOKED = '01006'
-PRIVILEGE_NOT_GRANTED = '01007'
-IMPLICIT_ZERO_BIT_PADDING = '01008'
-DYNAMIC_RESULT_SETS_RETURNED = '0100C'
-DEPRECATED_FEATURE = '01P01'
-
-# Class 02 - No Data (this is also a warning class per the SQL standard)
-NO_DATA = '02000'
-NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED = '02001'
-
-# Class 03 - SQL Statement Not Yet Complete
-SQL_STATEMENT_NOT_YET_COMPLETE = '03000'
-
-# Class 08 - Connection Exception
-CONNECTION_EXCEPTION = '08000'
-SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION = '08001'
-CONNECTION_DOES_NOT_EXIST = '08003'
-SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION = '08004'
-CONNECTION_FAILURE = '08006'
-TRANSACTION_RESOLUTION_UNKNOWN = '08007'
-PROTOCOL_VIOLATION = '08P01'
-
-# Class 09 - Triggered Action Exception
-TRIGGERED_ACTION_EXCEPTION = '09000'
-
-# Class 0A - Feature Not Supported
-FEATURE_NOT_SUPPORTED = '0A000'
-
-# Class 0B - Invalid Transaction Initiation
-INVALID_TRANSACTION_INITIATION = '0B000'
-
-# Class 0F - Locator Exception
-LOCATOR_EXCEPTION = '0F000'
-INVALID_LOCATOR_SPECIFICATION = '0F001'
-
-# Class 0L - Invalid Grantor
-INVALID_GRANTOR = '0L000'
-INVALID_GRANT_OPERATION = '0LP01'
-
-# Class 0P - Invalid Role Specification
-INVALID_ROLE_SPECIFICATION = '0P000'
-
-# Class 0Z - Diagnostics Exception
-DIAGNOSTICS_EXCEPTION = '0Z000'
-STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = '0Z002'
-
-# Class 20 - Case Not Found
-CASE_NOT_FOUND = '20000'
-
-# Class 21 - Cardinality Violation
-CARDINALITY_VIOLATION = '21000'
-
-# Class 22 - Data Exception
-DATA_EXCEPTION = '22000'
-STRING_DATA_RIGHT_TRUNCATION = '22001'
-NULL_VALUE_NO_INDICATOR_PARAMETER = '22002'
-NUMERIC_VALUE_OUT_OF_RANGE = '22003'
-NULL_VALUE_NOT_ALLOWED_ = '22004'
-ERROR_IN_ASSIGNMENT = '22005'
-INVALID_DATETIME_FORMAT = '22007'
-DATETIME_FIELD_OVERFLOW = '22008'
-INVALID_TIME_ZONE_DISPLACEMENT_VALUE = '22009'
-ESCAPE_CHARACTER_CONFLICT = '2200B'
-INVALID_USE_OF_ESCAPE_CHARACTER = '2200C'
-INVALID_ESCAPE_OCTET = '2200D'
-ZERO_LENGTH_CHARACTER_STRING = '2200F'
-MOST_SPECIFIC_TYPE_MISMATCH = '2200G'
-SEQUENCE_GENERATOR_LIMIT_EXCEEDED = '2200H'
-NOT_AN_XML_DOCUMENT = '2200L'
-INVALID_XML_DOCUMENT = '2200M'
-INVALID_XML_CONTENT = '2200N'
-INVALID_XML_COMMENT = '2200S'
-INVALID_XML_PROCESSING_INSTRUCTION = '2200T'
-INVALID_INDICATOR_PARAMETER_VALUE = '22010'
-SUBSTRING_ERROR = '22011'
-DIVISION_BY_ZERO = '22012'
-INVALID_PRECEDING_OR_FOLLOWING_SIZE = '22013'
-INVALID_ARGUMENT_FOR_NTILE_FUNCTION = '22014'
-INTERVAL_FIELD_OVERFLOW = '22015'
-INVALID_ARGUMENT_FOR_NTH_VALUE_FUNCTION = '22016'
-INVALID_CHARACTER_VALUE_FOR_CAST = '22018'
-INVALID_ESCAPE_CHARACTER = '22019'
-INVALID_REGULAR_EXPRESSION = '2201B'
-INVALID_ARGUMENT_FOR_LOGARITHM = '2201E'
-INVALID_ARGUMENT_FOR_POWER_FUNCTION = '2201F'
-INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION = '2201G'
-INVALID_ROW_COUNT_IN_LIMIT_CLAUSE = '2201W'
-INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE = '2201X'
-INVALID_LIMIT_VALUE = '22020'
-CHARACTER_NOT_IN_REPERTOIRE = '22021'
-INDICATOR_OVERFLOW = '22022'
-INVALID_PARAMETER_VALUE = '22023'
-UNTERMINATED_C_STRING = '22024'
-INVALID_ESCAPE_SEQUENCE = '22025'
-STRING_DATA_LENGTH_MISMATCH = '22026'
-TRIM_ERROR = '22027'
-ARRAY_SUBSCRIPT_ERROR = '2202E'
-INVALID_TABLESAMPLE_REPEAT = '2202G'
-INVALID_TABLESAMPLE_ARGUMENT = '2202H'
-DUPLICATE_JSON_OBJECT_KEY_VALUE = '22030'
-INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION = '22031'
-INVALID_JSON_TEXT = '22032'
-INVALID_SQL_JSON_SUBSCRIPT = '22033'
-MORE_THAN_ONE_SQL_JSON_ITEM = '22034'
-NO_SQL_JSON_ITEM = '22035'
-NON_NUMERIC_SQL_JSON_ITEM = '22036'
-NON_UNIQUE_KEYS_IN_A_JSON_OBJECT = '22037'
-SINGLETON_SQL_JSON_ITEM_REQUIRED = '22038'
-SQL_JSON_ARRAY_NOT_FOUND = '22039'
-SQL_JSON_MEMBER_NOT_FOUND = '2203A'
-SQL_JSON_NUMBER_NOT_FOUND = '2203B'
-SQL_JSON_OBJECT_NOT_FOUND = '2203C'
-TOO_MANY_JSON_ARRAY_ELEMENTS = '2203D'
-TOO_MANY_JSON_OBJECT_MEMBERS = '2203E'
-SQL_JSON_SCALAR_REQUIRED = '2203F'
-FLOATING_POINT_EXCEPTION = '22P01'
-INVALID_TEXT_REPRESENTATION = '22P02'
-INVALID_BINARY_REPRESENTATION = '22P03'
-BAD_COPY_FILE_FORMAT = '22P04'
-UNTRANSLATABLE_CHARACTER = '22P05'
-NONSTANDARD_USE_OF_ESCAPE_CHARACTER = '22P06'
-
-# Class 23 - Integrity Constraint Violation
-INTEGRITY_CONSTRAINT_VIOLATION = '23000'
-RESTRICT_VIOLATION = '23001'
-NOT_NULL_VIOLATION = '23502'
-FOREIGN_KEY_VIOLATION = '23503'
-UNIQUE_VIOLATION = '23505'
-CHECK_VIOLATION = '23514'
-EXCLUSION_VIOLATION = '23P01'
-
-# Class 24 - Invalid Cursor State
-INVALID_CURSOR_STATE = '24000'
-
-# Class 25 - Invalid Transaction State
-INVALID_TRANSACTION_STATE = '25000'
-ACTIVE_SQL_TRANSACTION = '25001'
-BRANCH_TRANSACTION_ALREADY_ACTIVE = '25002'
-INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION = '25003'
-INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION = '25004'
-NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION = '25005'
-READ_ONLY_SQL_TRANSACTION = '25006'
-SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED = '25007'
-HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL = '25008'
-NO_ACTIVE_SQL_TRANSACTION = '25P01'
-IN_FAILED_SQL_TRANSACTION = '25P02'
-IDLE_IN_TRANSACTION_SESSION_TIMEOUT = '25P03'
-
-# Class 26 - Invalid SQL Statement Name
-INVALID_SQL_STATEMENT_NAME = '26000'
-
-# Class 27 - Triggered Data Change Violation
-TRIGGERED_DATA_CHANGE_VIOLATION = '27000'
-
-# Class 28 - Invalid Authorization Specification
-INVALID_AUTHORIZATION_SPECIFICATION = '28000'
-INVALID_PASSWORD = '28P01'
-
-# Class 2B - Dependent Privilege Descriptors Still Exist
-DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST = '2B000'
-DEPENDENT_OBJECTS_STILL_EXIST = '2BP01'
-
-# Class 2D - Invalid Transaction Termination
-INVALID_TRANSACTION_TERMINATION = '2D000'
-
-# Class 2F - SQL Routine Exception
-SQL_ROUTINE_EXCEPTION = '2F000'
-MODIFYING_SQL_DATA_NOT_PERMITTED_ = '2F002'
-PROHIBITED_SQL_STATEMENT_ATTEMPTED_ = '2F003'
-READING_SQL_DATA_NOT_PERMITTED_ = '2F004'
-FUNCTION_EXECUTED_NO_RETURN_STATEMENT = '2F005'
-
-# Class 34 - Invalid Cursor Name
-INVALID_CURSOR_NAME = '34000'
-
-# Class 38 - External Routine Exception
-EXTERNAL_ROUTINE_EXCEPTION = '38000'
-CONTAINING_SQL_NOT_PERMITTED = '38001'
-MODIFYING_SQL_DATA_NOT_PERMITTED = '38002'
-PROHIBITED_SQL_STATEMENT_ATTEMPTED = '38003'
-READING_SQL_DATA_NOT_PERMITTED = '38004'
-
-# Class 39 - External Routine Invocation Exception
-EXTERNAL_ROUTINE_INVOCATION_EXCEPTION = '39000'
-INVALID_SQLSTATE_RETURNED = '39001'
-NULL_VALUE_NOT_ALLOWED = '39004'
-TRIGGER_PROTOCOL_VIOLATED = '39P01'
-SRF_PROTOCOL_VIOLATED = '39P02'
-EVENT_TRIGGER_PROTOCOL_VIOLATED = '39P03'
-
-# Class 3B - Savepoint Exception
-SAVEPOINT_EXCEPTION = '3B000'
-INVALID_SAVEPOINT_SPECIFICATION = '3B001'
-
-# Class 3D - Invalid Catalog Name
-INVALID_CATALOG_NAME = '3D000'
-
-# Class 3F - Invalid Schema Name
-INVALID_SCHEMA_NAME = '3F000'
-
-# Class 40 - Transaction Rollback
-TRANSACTION_ROLLBACK = '40000'
-SERIALIZATION_FAILURE = '40001'
-TRANSACTION_INTEGRITY_CONSTRAINT_VIOLATION = '40002'
-STATEMENT_COMPLETION_UNKNOWN = '40003'
-DEADLOCK_DETECTED = '40P01'
-
-# Class 42 - Syntax Error or Access Rule Violation
-SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = '42000'
-INSUFFICIENT_PRIVILEGE = '42501'
-SYNTAX_ERROR = '42601'
-INVALID_NAME = '42602'
-INVALID_COLUMN_DEFINITION = '42611'
-NAME_TOO_LONG = '42622'
-DUPLICATE_COLUMN = '42701'
-AMBIGUOUS_COLUMN = '42702'
-UNDEFINED_COLUMN = '42703'
-UNDEFINED_OBJECT = '42704'
-DUPLICATE_OBJECT = '42710'
-DUPLICATE_ALIAS = '42712'
-DUPLICATE_FUNCTION = '42723'
-AMBIGUOUS_FUNCTION = '42725'
-GROUPING_ERROR = '42803'
-DATATYPE_MISMATCH = '42804'
-WRONG_OBJECT_TYPE = '42809'
-INVALID_FOREIGN_KEY = '42830'
-CANNOT_COERCE = '42846'
-UNDEFINED_FUNCTION = '42883'
-GENERATED_ALWAYS = '428C9'
-RESERVED_NAME = '42939'
-UNDEFINED_TABLE = '42P01'
-UNDEFINED_PARAMETER = '42P02'
-DUPLICATE_CURSOR = '42P03'
-DUPLICATE_DATABASE = '42P04'
-DUPLICATE_PREPARED_STATEMENT = '42P05'
-DUPLICATE_SCHEMA = '42P06'
-DUPLICATE_TABLE = '42P07'
-AMBIGUOUS_PARAMETER = '42P08'
-AMBIGUOUS_ALIAS = '42P09'
-INVALID_COLUMN_REFERENCE = '42P10'
-INVALID_CURSOR_DEFINITION = '42P11'
-INVALID_DATABASE_DEFINITION = '42P12'
-INVALID_FUNCTION_DEFINITION = '42P13'
-INVALID_PREPARED_STATEMENT_DEFINITION = '42P14'
-INVALID_SCHEMA_DEFINITION = '42P15'
-INVALID_TABLE_DEFINITION = '42P16'
-INVALID_OBJECT_DEFINITION = '42P17'
-INDETERMINATE_DATATYPE = '42P18'
-INVALID_RECURSION = '42P19'
-WINDOWING_ERROR = '42P20'
-COLLATION_MISMATCH = '42P21'
-INDETERMINATE_COLLATION = '42P22'
-
-# Class 44 - WITH CHECK OPTION Violation
-WITH_CHECK_OPTION_VIOLATION = '44000'
-
-# Class 53 - Insufficient Resources
-INSUFFICIENT_RESOURCES = '53000'
-DISK_FULL = '53100'
-OUT_OF_MEMORY = '53200'
-TOO_MANY_CONNECTIONS = '53300'
-CONFIGURATION_LIMIT_EXCEEDED = '53400'
-
-# Class 54 - Program Limit Exceeded
-PROGRAM_LIMIT_EXCEEDED = '54000'
-STATEMENT_TOO_COMPLEX = '54001'
-TOO_MANY_COLUMNS = '54011'
-TOO_MANY_ARGUMENTS = '54023'
-
-# Class 55 - Object Not In Prerequisite State
-OBJECT_NOT_IN_PREREQUISITE_STATE = '55000'
-OBJECT_IN_USE = '55006'
-CANT_CHANGE_RUNTIME_PARAM = '55P02'
-LOCK_NOT_AVAILABLE = '55P03'
-UNSAFE_NEW_ENUM_VALUE_USAGE = '55P04'
-
-# Class 57 - Operator Intervention
-OPERATOR_INTERVENTION = '57000'
-QUERY_CANCELED = '57014'
-ADMIN_SHUTDOWN = '57P01'
-CRASH_SHUTDOWN = '57P02'
-CANNOT_CONNECT_NOW = '57P03'
-DATABASE_DROPPED = '57P04'
-
-# Class 58 - System Error (errors external to PostgreSQL itself)
-SYSTEM_ERROR = '58000'
-IO_ERROR = '58030'
-UNDEFINED_FILE = '58P01'
-DUPLICATE_FILE = '58P02'
-
-# Class 72 - Snapshot Failure
-SNAPSHOT_TOO_OLD = '72000'
-
-# Class F0 - Configuration File Error
-CONFIG_FILE_ERROR = 'F0000'
-LOCK_FILE_EXISTS = 'F0001'
-
-# Class HV - Foreign Data Wrapper Error (SQL/MED)
-FDW_ERROR = 'HV000'
-FDW_OUT_OF_MEMORY = 'HV001'
-FDW_DYNAMIC_PARAMETER_VALUE_NEEDED = 'HV002'
-FDW_INVALID_DATA_TYPE = 'HV004'
-FDW_COLUMN_NAME_NOT_FOUND = 'HV005'
-FDW_INVALID_DATA_TYPE_DESCRIPTORS = 'HV006'
-FDW_INVALID_COLUMN_NAME = 'HV007'
-FDW_INVALID_COLUMN_NUMBER = 'HV008'
-FDW_INVALID_USE_OF_NULL_POINTER = 'HV009'
-FDW_INVALID_STRING_FORMAT = 'HV00A'
-FDW_INVALID_HANDLE = 'HV00B'
-FDW_INVALID_OPTION_INDEX = 'HV00C'
-FDW_INVALID_OPTION_NAME = 'HV00D'
-FDW_OPTION_NAME_NOT_FOUND = 'HV00J'
-FDW_REPLY_HANDLE = 'HV00K'
-FDW_UNABLE_TO_CREATE_EXECUTION = 'HV00L'
-FDW_UNABLE_TO_CREATE_REPLY = 'HV00M'
-FDW_UNABLE_TO_ESTABLISH_CONNECTION = 'HV00N'
-FDW_NO_SCHEMAS = 'HV00P'
-FDW_SCHEMA_NOT_FOUND = 'HV00Q'
-FDW_TABLE_NOT_FOUND = 'HV00R'
-FDW_FUNCTION_SEQUENCE_ERROR = 'HV010'
-FDW_TOO_MANY_HANDLES = 'HV014'
-FDW_INCONSISTENT_DESCRIPTOR_INFORMATION = 'HV021'
-FDW_INVALID_ATTRIBUTE_VALUE = 'HV024'
-FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH = 'HV090'
-FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER = 'HV091'
-
-# Class P0 - PL/pgSQL Error
-PLPGSQL_ERROR = 'P0000'
-RAISE_EXCEPTION = 'P0001'
-NO_DATA_FOUND = 'P0002'
-TOO_MANY_ROWS = 'P0003'
-ASSERT_FAILURE = 'P0004'
-
-# Class XX - Internal Error
-INTERNAL_ERROR = 'XX000'
-DATA_CORRUPTED = 'XX001'
-INDEX_CORRUPTED = 'XX002'
diff --git a/psycopg2/errors.py b/psycopg2/errors.py
deleted file mode 100644
index e4e47f5..0000000
--- a/psycopg2/errors.py
+++ /dev/null
@@ -1,38 +0,0 @@
-"""Error classes for PostgreSQL error codes
-"""
-
-# psycopg/errors.py - SQLSTATE and DB-API exceptions
-#
-# Copyright (C) 2018-2019 Daniele Varrazzo
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-#
-# NOTE: the exceptions are injected into this module by the C extention.
-#
-
-
-def lookup(code):
- """Lookup an error code and return its exception class.
-
- Raise `!KeyError` if the code is not found.
- """
- from psycopg2._psycopg import sqlstate_errors # avoid circular import
- return sqlstate_errors[code]
diff --git a/psycopg2/extensions.py b/psycopg2/extensions.py
deleted file mode 100644
index b938d0c..0000000
--- a/psycopg2/extensions.py
+++ /dev/null
@@ -1,213 +0,0 @@
-"""psycopg extensions to the DBAPI-2.0
-
-This module holds all the extensions to the DBAPI-2.0 provided by psycopg.
-
-- `connection` -- the new-type inheritable connection class
-- `cursor` -- the new-type inheritable cursor class
-- `lobject` -- the new-type inheritable large object class
-- `adapt()` -- exposes the PEP-246_ compatible adapting mechanism used
- by psycopg to adapt Python types to PostgreSQL ones
-
-.. _PEP-246: https://www.python.org/dev/peps/pep-0246/
-"""
-# psycopg/extensions.py - DBAPI-2.0 extensions specific to psycopg
-#
-# Copyright (C) 2003-2019 Federico Di Gregorio
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import re as _re
-
-from psycopg2._psycopg import ( # noqa
- BINARYARRAY, BOOLEAN, BOOLEANARRAY, BYTES, BYTESARRAY, DATE, DATEARRAY,
- DATETIMEARRAY, DECIMAL, DECIMALARRAY, FLOAT, FLOATARRAY, INTEGER,
- INTEGERARRAY, INTERVAL, INTERVALARRAY, LONGINTEGER, LONGINTEGERARRAY,
- ROWIDARRAY, STRINGARRAY, TIME, TIMEARRAY, UNICODE, UNICODEARRAY,
- AsIs, Binary, Boolean, Float, Int, QuotedString, )
-
-from psycopg2._psycopg import ( # noqa
- PYDATE, PYDATETIME, PYDATETIMETZ, PYINTERVAL, PYTIME, PYDATEARRAY,
- PYDATETIMEARRAY, PYDATETIMETZARRAY, PYINTERVALARRAY, PYTIMEARRAY,
- DateFromPy, TimeFromPy, TimestampFromPy, IntervalFromPy, )
-
-from psycopg2._psycopg import ( # noqa
- adapt, adapters, encodings, connection, cursor,
- lobject, Xid, libpq_version, parse_dsn, quote_ident,
- string_types, binary_types, new_type, new_array_type, register_type,
- ISQLQuote, Notify, Diagnostics, Column, ConnectionInfo,
- QueryCanceledError, TransactionRollbackError,
- set_wait_callback, get_wait_callback, encrypt_password, )
-
-
-"""Isolation level values."""
-ISOLATION_LEVEL_AUTOCOMMIT = 0
-ISOLATION_LEVEL_READ_UNCOMMITTED = 4
-ISOLATION_LEVEL_READ_COMMITTED = 1
-ISOLATION_LEVEL_REPEATABLE_READ = 2
-ISOLATION_LEVEL_SERIALIZABLE = 3
-ISOLATION_LEVEL_DEFAULT = None
-
-
-"""psycopg connection status values."""
-STATUS_SETUP = 0
-STATUS_READY = 1
-STATUS_BEGIN = 2
-STATUS_SYNC = 3 # currently unused
-STATUS_ASYNC = 4 # currently unused
-STATUS_PREPARED = 5
-
-# This is a useful mnemonic to check if the connection is in a transaction
-STATUS_IN_TRANSACTION = STATUS_BEGIN
-
-
-"""psycopg asynchronous connection polling values"""
-POLL_OK = 0
-POLL_READ = 1
-POLL_WRITE = 2
-POLL_ERROR = 3
-
-
-"""Backend transaction status values."""
-TRANSACTION_STATUS_IDLE = 0
-TRANSACTION_STATUS_ACTIVE = 1
-TRANSACTION_STATUS_INTRANS = 2
-TRANSACTION_STATUS_INERROR = 3
-TRANSACTION_STATUS_UNKNOWN = 4
-
-
-def register_adapter(typ, callable):
- """Register 'callable' as an ISQLQuote adapter for type 'typ'."""
- adapters[(typ, ISQLQuote)] = callable
-
-
-# The SQL_IN class is the official adapter for tuples starting from 2.0.6.
-class SQL_IN:
- """Adapt any iterable to an SQL quotable object."""
- def __init__(self, seq):
- self._seq = seq
- self._conn = None
-
- def prepare(self, conn):
- self._conn = conn
-
- def getquoted(self):
- # this is the important line: note how every object in the
- # list is adapted and then how getquoted() is called on it
- pobjs = [adapt(o) for o in self._seq]
- if self._conn is not None:
- for obj in pobjs:
- if hasattr(obj, 'prepare'):
- obj.prepare(self._conn)
- qobjs = [o.getquoted() for o in pobjs]
- return b'(' + b', '.join(qobjs) + b')'
-
- def __str__(self):
- return str(self.getquoted())
-
-
-class NoneAdapter:
- """Adapt None to NULL.
-
- This adapter is not used normally as a fast path in mogrify uses NULL,
- but it makes easier to adapt composite types.
- """
- def __init__(self, obj):
- pass
-
- def getquoted(self, _null=b"NULL"):
- return _null
-
-
-def make_dsn(dsn=None, **kwargs):
- """Convert a set of keywords into a connection strings."""
- if dsn is None and not kwargs:
- return ''
-
- # If no kwarg is specified don't mung the dsn, but verify it
- if not kwargs:
- parse_dsn(dsn)
- return dsn
-
- # Override the dsn with the parameters
- if 'database' in kwargs:
- if 'dbname' in kwargs:
- raise TypeError(
- "you can't specify both 'database' and 'dbname' arguments")
- kwargs['dbname'] = kwargs.pop('database')
-
- # Drop the None arguments
- kwargs = {k: v for (k, v) in kwargs.items() if v is not None}
-
- if dsn is not None:
- tmp = parse_dsn(dsn)
- tmp.update(kwargs)
- kwargs = tmp
-
- dsn = " ".join(["{}={}".format(k, _param_escape(str(v)))
- for (k, v) in kwargs.items()])
-
- # verify that the returned dsn is valid
- parse_dsn(dsn)
-
- return dsn
-
-
-def _param_escape(s,
- re_escape=_re.compile(r"([\\'])"),
- re_space=_re.compile(r'\s')):
- """
- Apply the escaping rule required by PQconnectdb
- """
- if not s:
- return "''"
-
- s = re_escape.sub(r'\\\1', s)
- if re_space.search(s):
- s = "'" + s + "'"
-
- return s
-
-
-# Create default json typecasters for PostgreSQL 9.2 oids
-from psycopg2._json import register_default_json, register_default_jsonb # noqa
-
-try:
- JSON, JSONARRAY = register_default_json()
- JSONB, JSONBARRAY = register_default_jsonb()
-except ImportError:
- pass
-
-del register_default_json, register_default_jsonb
-
-
-# Create default Range typecasters
-from psycopg2. _range import Range # noqa
-del Range
-
-
-# Add the "cleaned" version of the encodings to the key.
-# When the encoding is set its name is cleaned up from - and _ and turned
-# uppercase, so an encoding not respecting these rules wouldn't be found in the
-# encodings keys and would raise an exception with the unicode typecaster
-for k, v in list(encodings.items()):
- k = k.replace('_', '').replace('-', '').upper()
- encodings[k] = v
-
-del k, v
diff --git a/psycopg2/extras.py b/psycopg2/extras.py
deleted file mode 100644
index f921d2d..0000000
--- a/psycopg2/extras.py
+++ /dev/null
@@ -1,1306 +0,0 @@
-"""Miscellaneous goodies for psycopg2
-
-This module is a generic place used to hold little helper functions
-and classes until a better place in the distribution is found.
-"""
-# psycopg/extras.py - miscellaneous extra goodies for psycopg
-#
-# Copyright (C) 2003-2019 Federico Di Gregorio
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import os as _os
-import time as _time
-import re as _re
-from collections import namedtuple, OrderedDict
-
-import logging as _logging
-
-import psycopg2
-from psycopg2 import extensions as _ext
-from .extensions import cursor as _cursor
-from .extensions import connection as _connection
-from .extensions import adapt as _A, quote_ident
-from functools import lru_cache
-
-from psycopg2._psycopg import ( # noqa
- REPLICATION_PHYSICAL, REPLICATION_LOGICAL,
- ReplicationConnection as _replicationConnection,
- ReplicationCursor as _replicationCursor,
- ReplicationMessage)
-
-
-# expose the json adaptation stuff into the module
-from psycopg2._json import ( # noqa
- json, Json, register_json, register_default_json, register_default_jsonb)
-
-
-# Expose range-related objects
-from psycopg2._range import ( # noqa
- Range, NumericRange, DateRange, DateTimeRange, DateTimeTZRange,
- register_range, RangeAdapter, RangeCaster)
-
-
-# Expose ipaddress-related objects
-from psycopg2._ipaddress import register_ipaddress # noqa
-
-
-class DictCursorBase(_cursor):
- """Base class for all dict-like cursors."""
-
- def __init__(self, *args, **kwargs):
- if 'row_factory' in kwargs:
- row_factory = kwargs['row_factory']
- del kwargs['row_factory']
- else:
- raise NotImplementedError(
- "DictCursorBase can't be instantiated without a row factory.")
- super().__init__(*args, **kwargs)
- self._query_executed = False
- self._prefetch = False
- self.row_factory = row_factory
-
- def fetchone(self):
- if self._prefetch:
- res = super().fetchone()
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = super().fetchone()
- return res
-
- def fetchmany(self, size=None):
- if self._prefetch:
- res = super().fetchmany(size)
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = super().fetchmany(size)
- return res
-
- def fetchall(self):
- if self._prefetch:
- res = super().fetchall()
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = super().fetchall()
- return res
-
- def __iter__(self):
- try:
- if self._prefetch:
- res = super().__iter__()
- first = next(res)
- if self._query_executed:
- self._build_index()
- if not self._prefetch:
- res = super().__iter__()
- first = next(res)
-
- yield first
- while True:
- yield next(res)
- except StopIteration:
- return
-
-
-class DictConnection(_connection):
- """A connection that uses `DictCursor` automatically."""
- def cursor(self, *args, **kwargs):
- kwargs.setdefault('cursor_factory', self.cursor_factory or DictCursor)
- return super().cursor(*args, **kwargs)
-
-
-class DictCursor(DictCursorBase):
- """A cursor that keeps a list of column name -> index mappings__.
-
- .. __: https://docs.python.org/glossary.html#term-mapping
- """
-
- def __init__(self, *args, **kwargs):
- kwargs['row_factory'] = DictRow
- super().__init__(*args, **kwargs)
- self._prefetch = True
-
- def execute(self, query, vars=None):
- self.index = OrderedDict()
- self._query_executed = True
- return super().execute(query, vars)
-
- def callproc(self, procname, vars=None):
- self.index = OrderedDict()
- self._query_executed = True
- return super().callproc(procname, vars)
-
- def _build_index(self):
- if self._query_executed and self.description:
- for i in range(len(self.description)):
- self.index[self.description[i][0]] = i
- self._query_executed = False
-
-
-class DictRow(list):
- """A row object that allow by-column-name access to data."""
-
- __slots__ = ('_index',)
-
- def __init__(self, cursor):
- self._index = cursor.index
- self[:] = [None] * len(cursor.description)
-
- def __getitem__(self, x):
- if not isinstance(x, (int, slice)):
- x = self._index[x]
- return super().__getitem__(x)
-
- def __setitem__(self, x, v):
- if not isinstance(x, (int, slice)):
- x = self._index[x]
- super().__setitem__(x, v)
-
- def items(self):
- g = super().__getitem__
- return ((n, g(self._index[n])) for n in self._index)
-
- def keys(self):
- return iter(self._index)
-
- def values(self):
- g = super().__getitem__
- return (g(self._index[n]) for n in self._index)
-
- def get(self, x, default=None):
- try:
- return self[x]
- except Exception:
- return default
-
- def copy(self):
- return OrderedDict(self.items())
-
- def __contains__(self, x):
- return x in self._index
-
- def __reduce__(self):
- # this is apparently useless, but it fixes #1073
- return super().__reduce__()
-
- def __getstate__(self):
- return self[:], self._index.copy()
-
- def __setstate__(self, data):
- self[:] = data[0]
- self._index = data[1]
-
-
-class RealDictConnection(_connection):
- """A connection that uses `RealDictCursor` automatically."""
- def cursor(self, *args, **kwargs):
- kwargs.setdefault('cursor_factory', self.cursor_factory or RealDictCursor)
- return super().cursor(*args, **kwargs)
-
-
-class RealDictCursor(DictCursorBase):
- """A cursor that uses a real dict as the base type for rows.
-
- Note that this cursor is extremely specialized and does not allow
- the normal access (using integer indices) to fetched data. If you need
- to access database rows both as a dictionary and a list, then use
- the generic `DictCursor` instead of `!RealDictCursor`.
- """
- def __init__(self, *args, **kwargs):
- kwargs['row_factory'] = RealDictRow
- super().__init__(*args, **kwargs)
-
- def execute(self, query, vars=None):
- self.column_mapping = []
- self._query_executed = True
- return super().execute(query, vars)
-
- def callproc(self, procname, vars=None):
- self.column_mapping = []
- self._query_executed = True
- return super().callproc(procname, vars)
-
- def _build_index(self):
- if self._query_executed and self.description:
- self.column_mapping = [d[0] for d in self.description]
- self._query_executed = False
-
-
-class RealDictRow(OrderedDict):
- """A `!dict` subclass representing a data record."""
-
- def __init__(self, *args, **kwargs):
- if args and isinstance(args[0], _cursor):
- cursor = args[0]
- args = args[1:]
- else:
- cursor = None
-
- super().__init__(*args, **kwargs)
-
- if cursor is not None:
- # Required for named cursors
- if cursor.description and not cursor.column_mapping:
- cursor._build_index()
-
- # Store the cols mapping in the dict itself until the row is fully
- # populated, so we don't need to add attributes to the class
- # (hence keeping its maintenance, special pickle support, etc.)
- self[RealDictRow] = cursor.column_mapping
-
- def __setitem__(self, key, value):
- if RealDictRow in self:
- # We are in the row building phase
- mapping = self[RealDictRow]
- super().__setitem__(mapping[key], value)
- if key == len(mapping) - 1:
- # Row building finished
- del self[RealDictRow]
- return
-
- super().__setitem__(key, value)
-
-
-class NamedTupleConnection(_connection):
- """A connection that uses `NamedTupleCursor` automatically."""
- def cursor(self, *args, **kwargs):
- kwargs.setdefault('cursor_factory', self.cursor_factory or NamedTupleCursor)
- return super().cursor(*args, **kwargs)
-
-
-class NamedTupleCursor(_cursor):
- """A cursor that generates results as `~collections.namedtuple`.
-
- `!fetch*()` methods will return named tuples instead of regular tuples, so
- their elements can be accessed both as regular numeric items as well as
- attributes.
-
- >>> nt_cur = conn.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)
- >>> rec = nt_cur.fetchone()
- >>> rec
- Record(id=1, num=100, data="abc'def")
- >>> rec[1]
- 100
- >>> rec.data
- "abc'def"
- """
- Record = None
- MAX_CACHE = 1024
-
- def execute(self, query, vars=None):
- self.Record = None
- return super().execute(query, vars)
-
- def executemany(self, query, vars):
- self.Record = None
- return super().executemany(query, vars)
-
- def callproc(self, procname, vars=None):
- self.Record = None
- return super().callproc(procname, vars)
-
- def fetchone(self):
- t = super().fetchone()
- if t is not None:
- nt = self.Record
- if nt is None:
- nt = self.Record = self._make_nt()
- return nt._make(t)
-
- def fetchmany(self, size=None):
- ts = super().fetchmany(size)
- nt = self.Record
- if nt is None:
- nt = self.Record = self._make_nt()
- return list(map(nt._make, ts))
-
- def fetchall(self):
- ts = super().fetchall()
- nt = self.Record
- if nt is None:
- nt = self.Record = self._make_nt()
- return list(map(nt._make, ts))
-
- def __iter__(self):
- try:
- it = super().__iter__()
- t = next(it)
-
- nt = self.Record
- if nt is None:
- nt = self.Record = self._make_nt()
-
- yield nt._make(t)
-
- while True:
- yield nt._make(next(it))
- except StopIteration:
- return
-
- # ascii except alnum and underscore
- _re_clean = _re.compile(
- '[' + _re.escape(' !"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~') + ']')
-
- def _make_nt(self):
- key = tuple(d[0] for d in self.description) if self.description else ()
- return self._cached_make_nt(key)
-
- @classmethod
- def _do_make_nt(cls, key):
- fields = []
- for s in key:
- s = cls._re_clean.sub('_', s)
- # Python identifier cannot start with numbers, namedtuple fields
- # cannot start with underscore. So...
- if s[0] == '_' or '0' <= s[0] <= '9':
- s = 'f' + s
- fields.append(s)
-
- nt = namedtuple("Record", fields)
- return nt
-
-
-@lru_cache(512)
-def _cached_make_nt(cls, key):
- return cls._do_make_nt(key)
-
-
-# Exposed for testability, and if someone wants to monkeypatch to tweak
-# the cache size.
-NamedTupleCursor._cached_make_nt = classmethod(_cached_make_nt)
-
-
-class LoggingConnection(_connection):
- """A connection that logs all queries to a file or logger__ object.
-
- .. __: https://docs.python.org/library/logging.html
- """
-
- def initialize(self, logobj):
- """Initialize the connection to log to `!logobj`.
-
- The `!logobj` parameter can be an open file object or a Logger/LoggerAdapter
- instance from the standard logging module.
- """
- self._logobj = logobj
- if _logging and isinstance(
- logobj, (_logging.Logger, _logging.LoggerAdapter)):
- self.log = self._logtologger
- else:
- self.log = self._logtofile
-
- def filter(self, msg, curs):
- """Filter the query before logging it.
-
- This is the method to overwrite to filter unwanted queries out of the
- log or to add some extra data to the output. The default implementation
- just does nothing.
- """
- return msg
-
- def _logtofile(self, msg, curs):
- msg = self.filter(msg, curs)
- if msg:
- if isinstance(msg, bytes):
- msg = msg.decode(_ext.encodings[self.encoding], 'replace')
- self._logobj.write(msg + _os.linesep)
-
- def _logtologger(self, msg, curs):
- msg = self.filter(msg, curs)
- if msg:
- self._logobj.debug(msg)
-
- def _check(self):
- if not hasattr(self, '_logobj'):
- raise self.ProgrammingError(
- "LoggingConnection object has not been initialize()d")
-
- def cursor(self, *args, **kwargs):
- self._check()
- kwargs.setdefault('cursor_factory', self.cursor_factory or LoggingCursor)
- return super().cursor(*args, **kwargs)
-
-
-class LoggingCursor(_cursor):
- """A cursor that logs queries using its connection logging facilities."""
-
- def execute(self, query, vars=None):
- try:
- return super().execute(query, vars)
- finally:
- self.connection.log(self.query, self)
-
- def callproc(self, procname, vars=None):
- try:
- return super().callproc(procname, vars)
- finally:
- self.connection.log(self.query, self)
-
-
-class MinTimeLoggingConnection(LoggingConnection):
- """A connection that logs queries based on execution time.
-
- This is just an example of how to sub-class `LoggingConnection` to
- provide some extra filtering for the logged queries. Both the
- `initialize()` and `filter()` methods are overwritten to make sure
- that only queries executing for more than ``mintime`` ms are logged.
-
- Note that this connection uses the specialized cursor
- `MinTimeLoggingCursor`.
- """
- def initialize(self, logobj, mintime=0):
- LoggingConnection.initialize(self, logobj)
- self._mintime = mintime
-
- def filter(self, msg, curs):
- t = (_time.time() - curs.timestamp) * 1000
- if t > self._mintime:
- if isinstance(msg, bytes):
- msg = msg.decode(_ext.encodings[self.encoding], 'replace')
- return f"{msg}{_os.linesep} (execution time: {t} ms)"
-
- def cursor(self, *args, **kwargs):
- kwargs.setdefault('cursor_factory',
- self.cursor_factory or MinTimeLoggingCursor)
- return LoggingConnection.cursor(self, *args, **kwargs)
-
-
-class MinTimeLoggingCursor(LoggingCursor):
- """The cursor sub-class companion to `MinTimeLoggingConnection`."""
-
- def execute(self, query, vars=None):
- self.timestamp = _time.time()
- return LoggingCursor.execute(self, query, vars)
-
- def callproc(self, procname, vars=None):
- self.timestamp = _time.time()
- return LoggingCursor.callproc(self, procname, vars)
-
-
-class LogicalReplicationConnection(_replicationConnection):
-
- def __init__(self, *args, **kwargs):
- kwargs['replication_type'] = REPLICATION_LOGICAL
- super().__init__(*args, **kwargs)
-
-
-class PhysicalReplicationConnection(_replicationConnection):
-
- def __init__(self, *args, **kwargs):
- kwargs['replication_type'] = REPLICATION_PHYSICAL
- super().__init__(*args, **kwargs)
-
-
-class StopReplication(Exception):
- """
- Exception used to break out of the endless loop in
- `~ReplicationCursor.consume_stream()`.
-
- Subclass of `~exceptions.Exception`. Intentionally *not* inherited from
- `~psycopg2.Error` as occurrence of this exception does not indicate an
- error.
- """
- pass
-
-
-class ReplicationCursor(_replicationCursor):
- """A cursor used for communication on replication connections."""
-
- def create_replication_slot(self, slot_name, slot_type=None, output_plugin=None):
- """Create streaming replication slot."""
-
- command = f"CREATE_REPLICATION_SLOT {quote_ident(slot_name, self)} "
-
- if slot_type is None:
- slot_type = self.connection.replication_type
-
- if slot_type == REPLICATION_LOGICAL:
- if output_plugin is None:
- raise psycopg2.ProgrammingError(
- "output plugin name is required to create "
- "logical replication slot")
-
- command += f"LOGICAL {quote_ident(output_plugin, self)}"
-
- elif slot_type == REPLICATION_PHYSICAL:
- if output_plugin is not None:
- raise psycopg2.ProgrammingError(
- "cannot specify output plugin name when creating "
- "physical replication slot")
-
- command += "PHYSICAL"
-
- else:
- raise psycopg2.ProgrammingError(
- f"unrecognized replication type: {repr(slot_type)}")
-
- self.execute(command)
-
- def drop_replication_slot(self, slot_name):
- """Drop streaming replication slot."""
-
- command = f"DROP_REPLICATION_SLOT {quote_ident(slot_name, self)}"
- self.execute(command)
-
- def start_replication(
- self, slot_name=None, slot_type=None, start_lsn=0,
- timeline=0, options=None, decode=False, status_interval=10):
- """Start replication stream."""
-
- command = "START_REPLICATION "
-
- if slot_type is None:
- slot_type = self.connection.replication_type
-
- if slot_type == REPLICATION_LOGICAL:
- if slot_name:
- command += f"SLOT {quote_ident(slot_name, self)} "
- else:
- raise psycopg2.ProgrammingError(
- "slot name is required for logical replication")
-
- command += "LOGICAL "
-
- elif slot_type == REPLICATION_PHYSICAL:
- if slot_name:
- command += f"SLOT {quote_ident(slot_name, self)} "
- # don't add "PHYSICAL", before 9.4 it was just START_REPLICATION XXX/XXX
-
- else:
- raise psycopg2.ProgrammingError(
- f"unrecognized replication type: {repr(slot_type)}")
-
- if type(start_lsn) is str:
- lsn = start_lsn.split('/')
- lsn = f"{int(lsn[0], 16):X}/{int(lsn[1], 16):08X}"
- else:
- lsn = f"{start_lsn >> 32 & 4294967295:X}/{start_lsn & 4294967295:08X}"
-
- command += lsn
-
- if timeline != 0:
- if slot_type == REPLICATION_LOGICAL:
- raise psycopg2.ProgrammingError(
- "cannot specify timeline for logical replication")
-
- command += f" TIMELINE {timeline}"
-
- if options:
- if slot_type == REPLICATION_PHYSICAL:
- raise psycopg2.ProgrammingError(
- "cannot specify output plugin options for physical replication")
-
- command += " ("
- for k, v in options.items():
- if not command.endswith('('):
- command += ", "
- command += f"{quote_ident(k, self)} {_A(str(v))}"
- command += ")"
-
- self.start_replication_expert(
- command, decode=decode, status_interval=status_interval)
-
- # allows replication cursors to be used in select.select() directly
- def fileno(self):
- return self.connection.fileno()
-
-
-# a dbtype and adapter for Python UUID type
-
-class UUID_adapter:
- """Adapt Python's uuid.UUID__ type to PostgreSQL's uuid__.
-
- .. __: https://docs.python.org/library/uuid.html
- .. __: https://www.postgresql.org/docs/current/static/datatype-uuid.html
- """
-
- def __init__(self, uuid):
- self._uuid = uuid
-
- def __conform__(self, proto):
- if proto is _ext.ISQLQuote:
- return self
-
- def getquoted(self):
- return (f"'{self._uuid}'::uuid").encode('utf8')
-
- def __str__(self):
- return f"'{self._uuid}'::uuid"
-
-
-def register_uuid(oids=None, conn_or_curs=None):
- """Create the UUID type and an uuid.UUID adapter.
-
- :param oids: oid for the PostgreSQL :sql:`uuid` type, or 2-items sequence
- with oids of the type and the array. If not specified, use PostgreSQL
- standard oids.
- :param conn_or_curs: where to register the typecaster. If not specified,
- register it globally.
- """
-
- import uuid
-
- if not oids:
- oid1 = 2950
- oid2 = 2951
- elif isinstance(oids, (list, tuple)):
- oid1, oid2 = oids
- else:
- oid1 = oids
- oid2 = 2951
-
- _ext.UUID = _ext.new_type((oid1, ), "UUID",
- lambda data, cursor: data and uuid.UUID(data) or None)
- _ext.UUIDARRAY = _ext.new_array_type((oid2,), "UUID[]", _ext.UUID)
-
- _ext.register_type(_ext.UUID, conn_or_curs)
- _ext.register_type(_ext.UUIDARRAY, conn_or_curs)
- _ext.register_adapter(uuid.UUID, UUID_adapter)
-
- return _ext.UUID
-
-
-# a type, dbtype and adapter for PostgreSQL inet type
-
-class Inet:
- """Wrap a string to allow for correct SQL-quoting of inet values.
-
- Note that this adapter does NOT check the passed value to make
- sure it really is an inet-compatible address but DOES call adapt()
- on it to make sure it is impossible to execute an SQL-injection
- by passing an evil value to the initializer.
- """
- def __init__(self, addr):
- self.addr = addr
-
- def __repr__(self):
- return f"{self.__class__.__name__}({self.addr!r})"
-
- def prepare(self, conn):
- self._conn = conn
-
- def getquoted(self):
- obj = _A(self.addr)
- if hasattr(obj, 'prepare'):
- obj.prepare(self._conn)
- return obj.getquoted() + b"::inet"
-
- def __conform__(self, proto):
- if proto is _ext.ISQLQuote:
- return self
-
- def __str__(self):
- return str(self.addr)
-
-
-def register_inet(oid=None, conn_or_curs=None):
- """Create the INET type and an Inet adapter.
-
- :param oid: oid for the PostgreSQL :sql:`inet` type, or 2-items sequence
- with oids of the type and the array. If not specified, use PostgreSQL
- standard oids.
- :param conn_or_curs: where to register the typecaster. If not specified,
- register it globally.
- """
- import warnings
- warnings.warn(
- "the inet adapter is deprecated, it's not very useful",
- DeprecationWarning)
-
- if not oid:
- oid1 = 869
- oid2 = 1041
- elif isinstance(oid, (list, tuple)):
- oid1, oid2 = oid
- else:
- oid1 = oid
- oid2 = 1041
-
- _ext.INET = _ext.new_type((oid1, ), "INET",
- lambda data, cursor: data and Inet(data) or None)
- _ext.INETARRAY = _ext.new_array_type((oid2, ), "INETARRAY", _ext.INET)
-
- _ext.register_type(_ext.INET, conn_or_curs)
- _ext.register_type(_ext.INETARRAY, conn_or_curs)
-
- return _ext.INET
-
-
-def wait_select(conn):
- """Wait until a connection or cursor has data available.
-
- The function is an example of a wait callback to be registered with
- `~psycopg2.extensions.set_wait_callback()`. This function uses
- :py:func:`~select.select()` to wait for data to become available, and
- therefore is able to handle/receive SIGINT/KeyboardInterrupt.
- """
- import select
- from psycopg2.extensions import POLL_OK, POLL_READ, POLL_WRITE
-
- while True:
- try:
- state = conn.poll()
- if state == POLL_OK:
- break
- elif state == POLL_READ:
- select.select([conn.fileno()], [], [])
- elif state == POLL_WRITE:
- select.select([], [conn.fileno()], [])
- else:
- raise conn.OperationalError(f"bad state from poll: {state}")
- except KeyboardInterrupt:
- conn.cancel()
- # the loop will be broken by a server error
- continue
-
-
-def _solve_conn_curs(conn_or_curs):
- """Return the connection and a DBAPI cursor from a connection or cursor."""
- if conn_or_curs is None:
- raise psycopg2.ProgrammingError("no connection or cursor provided")
-
- if hasattr(conn_or_curs, 'execute'):
- conn = conn_or_curs.connection
- curs = conn.cursor(cursor_factory=_cursor)
- else:
- conn = conn_or_curs
- curs = conn.cursor(cursor_factory=_cursor)
-
- return conn, curs
-
-
-class HstoreAdapter:
- """Adapt a Python dict to the hstore syntax."""
- def __init__(self, wrapped):
- self.wrapped = wrapped
-
- def prepare(self, conn):
- self.conn = conn
-
- # use an old-style getquoted implementation if required
- if conn.info.server_version < 90000:
- self.getquoted = self._getquoted_8
-
- def _getquoted_8(self):
- """Use the operators available in PG pre-9.0."""
- if not self.wrapped:
- return b"''::hstore"
-
- adapt = _ext.adapt
- rv = []
- for k, v in self.wrapped.items():
- k = adapt(k)
- k.prepare(self.conn)
- k = k.getquoted()
-
- if v is not None:
- v = adapt(v)
- v.prepare(self.conn)
- v = v.getquoted()
- else:
- v = b'NULL'
-
- # XXX this b'ing is painfully inefficient!
- rv.append(b"(" + k + b" => " + v + b")")
-
- return b"(" + b'||'.join(rv) + b")"
-
- def _getquoted_9(self):
- """Use the hstore(text[], text[]) function."""
- if not self.wrapped:
- return b"''::hstore"
-
- k = _ext.adapt(list(self.wrapped.keys()))
- k.prepare(self.conn)
- v = _ext.adapt(list(self.wrapped.values()))
- v.prepare(self.conn)
- return b"hstore(" + k.getquoted() + b", " + v.getquoted() + b")"
-
- getquoted = _getquoted_9
-
- _re_hstore = _re.compile(r"""
- # hstore key:
- # a string of normal or escaped chars
- "((?: [^"\\] | \\. )*)"
- \s*=>\s* # hstore value
- (?:
- NULL # the value can be null - not catched
- # or a quoted string like the key
- | "((?: [^"\\] | \\. )*)"
- )
- (?:\s*,\s*|$) # pairs separated by comma or end of string.
- """, _re.VERBOSE)
-
- @classmethod
- def parse(self, s, cur, _bsdec=_re.compile(r"\\(.)")):
- """Parse an hstore representation in a Python string.
-
- The hstore is represented as something like::
-
- "a"=>"1", "b"=>"2"
-
- with backslash-escaped strings.
- """
- if s is None:
- return None
-
- rv = {}
- start = 0
- for m in self._re_hstore.finditer(s):
- if m is None or m.start() != start:
- raise psycopg2.InterfaceError(
- f"error parsing hstore pair at char {start}")
- k = _bsdec.sub(r'\1', m.group(1))
- v = m.group(2)
- if v is not None:
- v = _bsdec.sub(r'\1', v)
-
- rv[k] = v
- start = m.end()
-
- if start < len(s):
- raise psycopg2.InterfaceError(
- f"error parsing hstore: unparsed data after char {start}")
-
- return rv
-
- @classmethod
- def parse_unicode(self, s, cur):
- """Parse an hstore returning unicode keys and values."""
- if s is None:
- return None
-
- s = s.decode(_ext.encodings[cur.connection.encoding])
- return self.parse(s, cur)
-
- @classmethod
- def get_oids(self, conn_or_curs):
- """Return the lists of OID of the hstore and hstore[] types.
- """
- conn, curs = _solve_conn_curs(conn_or_curs)
-
- # Store the transaction status of the connection to revert it after use
- conn_status = conn.status
-
- # column typarray not available before PG 8.3
- typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
-
- rv0, rv1 = [], []
-
- # get the oid for the hstore
- curs.execute(f"""SELECT t.oid, {typarray}
-FROM pg_type t JOIN pg_namespace ns
- ON typnamespace = ns.oid
-WHERE typname = 'hstore';
-""")
- for oids in curs:
- rv0.append(oids[0])
- rv1.append(oids[1])
-
- # revert the status of the connection as before the command
- if (conn_status != _ext.STATUS_IN_TRANSACTION
- and not conn.autocommit):
- conn.rollback()
-
- return tuple(rv0), tuple(rv1)
-
-
-def register_hstore(conn_or_curs, globally=False, unicode=False,
- oid=None, array_oid=None):
- r"""Register adapter and typecaster for `!dict`\-\ |hstore| conversions.
-
- :param conn_or_curs: a connection or cursor: the typecaster will be
- registered only on this object unless *globally* is set to `!True`
- :param globally: register the adapter globally, not only on *conn_or_curs*
- :param unicode: if `!True`, keys and values returned from the database
- will be `!unicode` instead of `!str`. The option is not available on
- Python 3
- :param oid: the OID of the |hstore| type if known. If not, it will be
- queried on *conn_or_curs*.
- :param array_oid: the OID of the |hstore| array type if known. If not, it
- will be queried on *conn_or_curs*.
-
- The connection or cursor passed to the function will be used to query the
- database and look for the OID of the |hstore| type (which may be different
- across databases). If querying is not desirable (e.g. with
- :ref:`asynchronous connections `) you may specify it in the
- *oid* parameter, which can be found using a query such as :sql:`SELECT
- 'hstore'::regtype::oid`. Analogously you can obtain a value for *array_oid*
- using a query such as :sql:`SELECT 'hstore[]'::regtype::oid`.
-
- Note that, when passing a dictionary from Python to the database, both
- strings and unicode keys and values are supported. Dictionaries returned
- from the database have keys/values according to the *unicode* parameter.
-
- The |hstore| contrib module must be already installed in the database
- (executing the ``hstore.sql`` script in your ``contrib`` directory).
- Raise `~psycopg2.ProgrammingError` if the type is not found.
- """
- if oid is None:
- oid = HstoreAdapter.get_oids(conn_or_curs)
- if oid is None or not oid[0]:
- raise psycopg2.ProgrammingError(
- "hstore type not found in the database. "
- "please install it from your 'contrib/hstore.sql' file")
- else:
- array_oid = oid[1]
- oid = oid[0]
-
- if isinstance(oid, int):
- oid = (oid,)
-
- if array_oid is not None:
- if isinstance(array_oid, int):
- array_oid = (array_oid,)
- else:
- array_oid = tuple([x for x in array_oid if x])
-
- # create and register the typecaster
- HSTORE = _ext.new_type(oid, "HSTORE", HstoreAdapter.parse)
- _ext.register_type(HSTORE, not globally and conn_or_curs or None)
- _ext.register_adapter(dict, HstoreAdapter)
-
- if array_oid:
- HSTOREARRAY = _ext.new_array_type(array_oid, "HSTOREARRAY", HSTORE)
- _ext.register_type(HSTOREARRAY, not globally and conn_or_curs or None)
-
-
-class CompositeCaster:
- """Helps conversion of a PostgreSQL composite type into a Python object.
-
- The class is usually created by the `register_composite()` function.
- You may want to create and register manually instances of the class if
- querying the database at registration time is not desirable (such as when
- using an :ref:`asynchronous connections `).
-
- """
- def __init__(self, name, oid, attrs, array_oid=None, schema=None):
- self.name = name
- self.schema = schema
- self.oid = oid
- self.array_oid = array_oid
-
- self.attnames = [a[0] for a in attrs]
- self.atttypes = [a[1] for a in attrs]
- self._create_type(name, self.attnames)
- self.typecaster = _ext.new_type((oid,), name, self.parse)
- if array_oid:
- self.array_typecaster = _ext.new_array_type(
- (array_oid,), f"{name}ARRAY", self.typecaster)
- else:
- self.array_typecaster = None
-
- def parse(self, s, curs):
- if s is None:
- return None
-
- tokens = self.tokenize(s)
- if len(tokens) != len(self.atttypes):
- raise psycopg2.DataError(
- "expecting %d components for the type %s, %d found instead" %
- (len(self.atttypes), self.name, len(tokens)))
-
- values = [curs.cast(oid, token)
- for oid, token in zip(self.atttypes, tokens)]
-
- return self.make(values)
-
- def make(self, values):
- """Return a new Python object representing the data being casted.
-
- *values* is the list of attributes, already casted into their Python
- representation.
-
- You can subclass this method to :ref:`customize the composite cast
- `.
- """
-
- return self._ctor(values)
-
- _re_tokenize = _re.compile(r"""
- \(? ([,)]) # an empty token, representing NULL
-| \(? " ((?: [^"] | "")*) " [,)] # or a quoted string
-| \(? ([^",)]+) [,)] # or an unquoted string
- """, _re.VERBOSE)
-
- _re_undouble = _re.compile(r'(["\\])\1')
-
- @classmethod
- def tokenize(self, s):
- rv = []
- for m in self._re_tokenize.finditer(s):
- if m is None:
- raise psycopg2.InterfaceError(f"can't parse type: {s!r}")
- if m.group(1) is not None:
- rv.append(None)
- elif m.group(2) is not None:
- rv.append(self._re_undouble.sub(r"\1", m.group(2)))
- else:
- rv.append(m.group(3))
-
- return rv
-
- def _create_type(self, name, attnames):
- self.type = namedtuple(name, attnames)
- self._ctor = self.type._make
-
- @classmethod
- def _from_db(self, name, conn_or_curs):
- """Return a `CompositeCaster` instance for the type *name*.
-
- Raise `ProgrammingError` if the type is not found.
- """
- conn, curs = _solve_conn_curs(conn_or_curs)
-
- # Store the transaction status of the connection to revert it after use
- conn_status = conn.status
-
- # Use the correct schema
- if '.' in name:
- schema, tname = name.split('.', 1)
- else:
- tname = name
- schema = 'public'
-
- # column typarray not available before PG 8.3
- typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
-
- # get the type oid and attributes
- curs.execute("""\
-SELECT t.oid, %s, attname, atttypid
-FROM pg_type t
-JOIN pg_namespace ns ON typnamespace = ns.oid
-JOIN pg_attribute a ON attrelid = typrelid
-WHERE typname = %%s AND nspname = %%s
- AND attnum > 0 AND NOT attisdropped
-ORDER BY attnum;
-""" % typarray, (tname, schema))
-
- recs = curs.fetchall()
-
- # revert the status of the connection as before the command
- if (conn_status != _ext.STATUS_IN_TRANSACTION
- and not conn.autocommit):
- conn.rollback()
-
- if not recs:
- raise psycopg2.ProgrammingError(
- f"PostgreSQL type '{name}' not found")
-
- type_oid = recs[0][0]
- array_oid = recs[0][1]
- type_attrs = [(r[2], r[3]) for r in recs]
-
- return self(tname, type_oid, type_attrs,
- array_oid=array_oid, schema=schema)
-
-
-def register_composite(name, conn_or_curs, globally=False, factory=None):
- """Register a typecaster to convert a composite type into a tuple.
-
- :param name: the name of a PostgreSQL composite type, e.g. created using
- the |CREATE TYPE|_ command
- :param conn_or_curs: a connection or cursor used to find the type oid and
- components; the typecaster is registered in a scope limited to this
- object, unless *globally* is set to `!True`
- :param globally: if `!False` (default) register the typecaster only on
- *conn_or_curs*, otherwise register it globally
- :param factory: if specified it should be a `CompositeCaster` subclass: use
- it to :ref:`customize how to cast composite types `
- :return: the registered `CompositeCaster` or *factory* instance
- responsible for the conversion
- """
- if factory is None:
- factory = CompositeCaster
-
- caster = factory._from_db(name, conn_or_curs)
- _ext.register_type(caster.typecaster, not globally and conn_or_curs or None)
-
- if caster.array_typecaster is not None:
- _ext.register_type(
- caster.array_typecaster, not globally and conn_or_curs or None)
-
- return caster
-
-
-def _paginate(seq, page_size):
- """Consume an iterable and return it in chunks.
-
- Every chunk is at most `page_size`. Never return an empty chunk.
- """
- page = []
- it = iter(seq)
- while True:
- try:
- for i in range(page_size):
- page.append(next(it))
- yield page
- page = []
- except StopIteration:
- if page:
- yield page
- return
-
-
-def execute_batch(cur, sql, argslist, page_size=100):
- r"""Execute groups of statements in fewer server roundtrips.
-
- Execute *sql* several times, against all parameters set (sequences or
- mappings) found in *argslist*.
-
- The function is semantically similar to
-
- .. parsed-literal::
-
- *cur*\.\ `~cursor.executemany`\ (\ *sql*\ , *argslist*\ )
-
- but has a different implementation: Psycopg will join the statements into
- fewer multi-statement commands, each one containing at most *page_size*
- statements, resulting in a reduced number of server roundtrips.
-
- After the execution of the function the `cursor.rowcount` property will
- **not** contain a total result.
-
- """
- for page in _paginate(argslist, page_size=page_size):
- sqls = [cur.mogrify(sql, args) for args in page]
- cur.execute(b";".join(sqls))
-
-
-def execute_values(cur, sql, argslist, template=None, page_size=100, fetch=False):
- '''Execute a statement using :sql:`VALUES` with a sequence of parameters.
-
- :param cur: the cursor to use to execute the query.
-
- :param sql: the query to execute. It must contain a single ``%s``
- placeholder, which will be replaced by a `VALUES list`__.
- Example: ``"INSERT INTO mytable (id, f1, f2) VALUES %s"``.
-
- :param argslist: sequence of sequences or dictionaries with the arguments
- to send to the query. The type and content must be consistent with
- *template*.
-
- :param template: the snippet to merge to every item in *argslist* to
- compose the query.
-
- - If the *argslist* items are sequences it should contain positional
- placeholders (e.g. ``"(%s, %s, %s)"``, or ``"(%s, %s, 42)``" if there
- are constants value...).
-
- - If the *argslist* items are mappings it should contain named
- placeholders (e.g. ``"(%(id)s, %(f1)s, 42)"``).
-
- If not specified, assume the arguments are sequence and use a simple
- positional template (i.e. ``(%s, %s, ...)``), with the number of
- placeholders sniffed by the first element in *argslist*.
-
- :param page_size: maximum number of *argslist* items to include in every
- statement. If there are more items the function will execute more than
- one statement.
-
- :param fetch: if `!True` return the query results into a list (like in a
- `~cursor.fetchall()`). Useful for queries with :sql:`RETURNING`
- clause.
-
- .. __: https://www.postgresql.org/docs/current/static/queries-values.html
-
- After the execution of the function the `cursor.rowcount` property will
- **not** contain a total result.
-
- While :sql:`INSERT` is an obvious candidate for this function it is
- possible to use it with other statements, for example::
-
- >>> cur.execute(
- ... "create table test (id int primary key, v1 int, v2 int)")
-
- >>> execute_values(cur,
- ... "INSERT INTO test (id, v1, v2) VALUES %s",
- ... [(1, 2, 3), (4, 5, 6), (7, 8, 9)])
-
- >>> execute_values(cur,
- ... """UPDATE test SET v1 = data.v1 FROM (VALUES %s) AS data (id, v1)
- ... WHERE test.id = data.id""",
- ... [(1, 20), (4, 50)])
-
- >>> cur.execute("select * from test order by id")
- >>> cur.fetchall()
- [(1, 20, 3), (4, 50, 6), (7, 8, 9)])
-
- '''
- from psycopg2.sql import Composable
- if isinstance(sql, Composable):
- sql = sql.as_string(cur)
-
- # we can't just use sql % vals because vals is bytes: if sql is bytes
- # there will be some decoding error because of stupid codec used, and Py3
- # doesn't implement % on bytes.
- if not isinstance(sql, bytes):
- sql = sql.encode(_ext.encodings[cur.connection.encoding])
- pre, post = _split_sql(sql)
-
- result = [] if fetch else None
- for page in _paginate(argslist, page_size=page_size):
- if template is None:
- template = b'(' + b','.join([b'%s'] * len(page[0])) + b')'
- parts = pre[:]
- for args in page:
- parts.append(cur.mogrify(template, args))
- parts.append(b',')
- parts[-1:] = post
- cur.execute(b''.join(parts))
- if fetch:
- result.extend(cur.fetchall())
-
- return result
-
-
-def _split_sql(sql):
- """Split *sql* on a single ``%s`` placeholder.
-
- Split on the %s, perform %% replacement and return pre, post lists of
- snippets.
- """
- curr = pre = []
- post = []
- tokens = _re.split(br'(%.)', sql)
- for token in tokens:
- if len(token) != 2 or token[:1] != b'%':
- curr.append(token)
- continue
-
- if token[1:] == b's':
- if curr is pre:
- curr = post
- else:
- raise ValueError(
- "the query contains more than one '%s' placeholder")
- elif token[1:] == b'%':
- curr.append(b'%')
- else:
- raise ValueError("unsupported format character: '%s'"
- % token[1:].decode('ascii', 'replace'))
-
- if curr is pre:
- raise ValueError("the query doesn't contain any '%s' placeholder")
-
- return pre, post
diff --git a/psycopg2/pool.py b/psycopg2/pool.py
deleted file mode 100644
index 9d67d68..0000000
--- a/psycopg2/pool.py
+++ /dev/null
@@ -1,187 +0,0 @@
-"""Connection pooling for psycopg2
-
-This module implements thread-safe (and not) connection pools.
-"""
-# psycopg/pool.py - pooling code for psycopg
-#
-# Copyright (C) 2003-2019 Federico Di Gregorio
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import psycopg2
-from psycopg2 import extensions as _ext
-
-
-class PoolError(psycopg2.Error):
- pass
-
-
-class AbstractConnectionPool:
- """Generic key-based pooling code."""
-
- def __init__(self, minconn, maxconn, *args, **kwargs):
- """Initialize the connection pool.
-
- New 'minconn' connections are created immediately calling 'connfunc'
- with given parameters. The connection pool will support a maximum of
- about 'maxconn' connections.
- """
- self.minconn = int(minconn)
- self.maxconn = int(maxconn)
- self.closed = False
-
- self._args = args
- self._kwargs = kwargs
-
- self._pool = []
- self._used = {}
- self._rused = {} # id(conn) -> key map
- self._keys = 0
-
- for i in range(self.minconn):
- self._connect()
-
- def _connect(self, key=None):
- """Create a new connection and assign it to 'key' if not None."""
- conn = psycopg2.connect(*self._args, **self._kwargs)
- if key is not None:
- self._used[key] = conn
- self._rused[id(conn)] = key
- else:
- self._pool.append(conn)
- return conn
-
- def _getkey(self):
- """Return a new unique key."""
- self._keys += 1
- return self._keys
-
- def _getconn(self, key=None):
- """Get a free connection and assign it to 'key' if not None."""
- if self.closed:
- raise PoolError("connection pool is closed")
- if key is None:
- key = self._getkey()
-
- if key in self._used:
- return self._used[key]
-
- if self._pool:
- self._used[key] = conn = self._pool.pop()
- self._rused[id(conn)] = key
- return conn
- else:
- if len(self._used) == self.maxconn:
- raise PoolError("connection pool exhausted")
- return self._connect(key)
-
- def _putconn(self, conn, key=None, close=False):
- """Put away a connection."""
- if self.closed:
- raise PoolError("connection pool is closed")
-
- if key is None:
- key = self._rused.get(id(conn))
- if key is None:
- raise PoolError("trying to put unkeyed connection")
-
- if len(self._pool) < self.minconn and not close:
- # Return the connection into a consistent state before putting
- # it back into the pool
- if not conn.closed:
- status = conn.info.transaction_status
- if status == _ext.TRANSACTION_STATUS_UNKNOWN:
- # server connection lost
- conn.close()
- elif status != _ext.TRANSACTION_STATUS_IDLE:
- # connection in error or in transaction
- conn.rollback()
- self._pool.append(conn)
- else:
- # regular idle connection
- self._pool.append(conn)
- # If the connection is closed, we just discard it.
- else:
- conn.close()
-
- # here we check for the presence of key because it can happen that a
- # thread tries to put back a connection after a call to close
- if not self.closed or key in self._used:
- del self._used[key]
- del self._rused[id(conn)]
-
- def _closeall(self):
- """Close all connections.
-
- Note that this can lead to some code fail badly when trying to use
- an already closed connection. If you call .closeall() make sure
- your code can deal with it.
- """
- if self.closed:
- raise PoolError("connection pool is closed")
- for conn in self._pool + list(self._used.values()):
- try:
- conn.close()
- except Exception:
- pass
- self.closed = True
-
-
-class SimpleConnectionPool(AbstractConnectionPool):
- """A connection pool that can't be shared across different threads."""
-
- getconn = AbstractConnectionPool._getconn
- putconn = AbstractConnectionPool._putconn
- closeall = AbstractConnectionPool._closeall
-
-
-class ThreadedConnectionPool(AbstractConnectionPool):
- """A connection pool that works with the threading module."""
-
- def __init__(self, minconn, maxconn, *args, **kwargs):
- """Initialize the threading lock."""
- import threading
- AbstractConnectionPool.__init__(
- self, minconn, maxconn, *args, **kwargs)
- self._lock = threading.Lock()
-
- def getconn(self, key=None):
- """Get a free connection and assign it to 'key' if not None."""
- self._lock.acquire()
- try:
- return self._getconn(key)
- finally:
- self._lock.release()
-
- def putconn(self, conn=None, key=None, close=False):
- """Put away an unused connection."""
- self._lock.acquire()
- try:
- self._putconn(conn, key, close)
- finally:
- self._lock.release()
-
- def closeall(self):
- """Close all connections (even the one currently in use.)"""
- self._lock.acquire()
- try:
- self._closeall()
- finally:
- self._lock.release()
diff --git a/psycopg2/sql.py b/psycopg2/sql.py
deleted file mode 100644
index 69b352b..0000000
--- a/psycopg2/sql.py
+++ /dev/null
@@ -1,455 +0,0 @@
-"""SQL composition utility module
-"""
-
-# psycopg/sql.py - SQL composition utility module
-#
-# Copyright (C) 2016-2019 Daniele Varrazzo
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import string
-
-from psycopg2 import extensions as ext
-
-
-_formatter = string.Formatter()
-
-
-class Composable:
- """
- Abstract base class for objects that can be used to compose an SQL string.
-
- `!Composable` objects can be passed directly to `~cursor.execute()`,
- `~cursor.executemany()`, `~cursor.copy_expert()` in place of the query
- string.
-
- `!Composable` objects can be joined using the ``+`` operator: the result
- will be a `Composed` instance containing the objects joined. The operator
- ``*`` is also supported with an integer argument: the result is a
- `!Composed` instance containing the left argument repeated as many times as
- requested.
- """
- def __init__(self, wrapped):
- self._wrapped = wrapped
-
- def __repr__(self):
- return f"{self.__class__.__name__}({self._wrapped!r})"
-
- def as_string(self, context):
- """
- Return the string value of the object.
-
- :param context: the context to evaluate the string into.
- :type context: `connection` or `cursor`
-
- The method is automatically invoked by `~cursor.execute()`,
- `~cursor.executemany()`, `~cursor.copy_expert()` if a `!Composable` is
- passed instead of the query string.
- """
- raise NotImplementedError
-
- def __add__(self, other):
- if isinstance(other, Composed):
- return Composed([self]) + other
- if isinstance(other, Composable):
- return Composed([self]) + Composed([other])
- else:
- return NotImplemented
-
- def __mul__(self, n):
- return Composed([self] * n)
-
- def __eq__(self, other):
- return type(self) is type(other) and self._wrapped == other._wrapped
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
-
-class Composed(Composable):
- """
- A `Composable` object made of a sequence of `!Composable`.
-
- The object is usually created using `!Composable` operators and methods.
- However it is possible to create a `!Composed` directly specifying a
- sequence of `!Composable` as arguments.
-
- Example::
-
- >>> comp = sql.Composed(
- ... [sql.SQL("insert into "), sql.Identifier("table")])
- >>> print(comp.as_string(conn))
- insert into "table"
-
- `!Composed` objects are iterable (so they can be used in `SQL.join` for
- instance).
- """
- def __init__(self, seq):
- wrapped = []
- for i in seq:
- if not isinstance(i, Composable):
- raise TypeError(
- f"Composed elements must be Composable, got {i!r} instead")
- wrapped.append(i)
-
- super().__init__(wrapped)
-
- @property
- def seq(self):
- """The list of the content of the `!Composed`."""
- return list(self._wrapped)
-
- def as_string(self, context):
- rv = []
- for i in self._wrapped:
- rv.append(i.as_string(context))
- return ''.join(rv)
-
- def __iter__(self):
- return iter(self._wrapped)
-
- def __add__(self, other):
- if isinstance(other, Composed):
- return Composed(self._wrapped + other._wrapped)
- if isinstance(other, Composable):
- return Composed(self._wrapped + [other])
- else:
- return NotImplemented
-
- def join(self, joiner):
- """
- Return a new `!Composed` interposing the *joiner* with the `!Composed` items.
-
- The *joiner* must be a `SQL` or a string which will be interpreted as
- an `SQL`.
-
- Example::
-
- >>> fields = sql.Identifier('foo') + sql.Identifier('bar') # a Composed
- >>> print(fields.join(', ').as_string(conn))
- "foo", "bar"
-
- """
- if isinstance(joiner, str):
- joiner = SQL(joiner)
- elif not isinstance(joiner, SQL):
- raise TypeError(
- "Composed.join() argument must be a string or an SQL")
-
- return joiner.join(self)
-
-
-class SQL(Composable):
- """
- A `Composable` representing a snippet of SQL statement.
-
- `!SQL` exposes `join()` and `format()` methods useful to create a template
- where to merge variable parts of a query (for instance field or table
- names).
-
- The *string* doesn't undergo any form of escaping, so it is not suitable to
- represent variable identifiers or values: you should only use it to pass
- constant strings representing templates or snippets of SQL statements; use
- other objects such as `Identifier` or `Literal` to represent variable
- parts.
-
- Example::
-
- >>> query = sql.SQL("select {0} from {1}").format(
- ... sql.SQL(', ').join([sql.Identifier('foo'), sql.Identifier('bar')]),
- ... sql.Identifier('table'))
- >>> print(query.as_string(conn))
- select "foo", "bar" from "table"
- """
- def __init__(self, string):
- if not isinstance(string, str):
- raise TypeError("SQL values must be strings")
- super().__init__(string)
-
- @property
- def string(self):
- """The string wrapped by the `!SQL` object."""
- return self._wrapped
-
- def as_string(self, context):
- return self._wrapped
-
- def format(self, *args, **kwargs):
- """
- Merge `Composable` objects into a template.
-
- :param `Composable` args: parameters to replace to numbered
- (``{0}``, ``{1}``) or auto-numbered (``{}``) placeholders
- :param `Composable` kwargs: parameters to replace to named (``{name}``)
- placeholders
- :return: the union of the `!SQL` string with placeholders replaced
- :rtype: `Composed`
-
- The method is similar to the Python `str.format()` method: the string
- template supports auto-numbered (``{}``), numbered (``{0}``,
- ``{1}``...), and named placeholders (``{name}``), with positional
- arguments replacing the numbered placeholders and keywords replacing
- the named ones. However placeholder modifiers (``{0!r}``, ``{0:<10}``)
- are not supported. Only `!Composable` objects can be passed to the
- template.
-
- Example::
-
- >>> print(sql.SQL("select * from {} where {} = %s")
- ... .format(sql.Identifier('people'), sql.Identifier('id'))
- ... .as_string(conn))
- select * from "people" where "id" = %s
-
- >>> print(sql.SQL("select * from {tbl} where {pkey} = %s")
- ... .format(tbl=sql.Identifier('people'), pkey=sql.Identifier('id'))
- ... .as_string(conn))
- select * from "people" where "id" = %s
-
- """
- rv = []
- autonum = 0
- for pre, name, spec, conv in _formatter.parse(self._wrapped):
- if spec:
- raise ValueError("no format specification supported by SQL")
- if conv:
- raise ValueError("no format conversion supported by SQL")
- if pre:
- rv.append(SQL(pre))
-
- if name is None:
- continue
-
- if name.isdigit():
- if autonum:
- raise ValueError(
- "cannot switch from automatic field numbering to manual")
- rv.append(args[int(name)])
- autonum = None
-
- elif not name:
- if autonum is None:
- raise ValueError(
- "cannot switch from manual field numbering to automatic")
- rv.append(args[autonum])
- autonum += 1
-
- else:
- rv.append(kwargs[name])
-
- return Composed(rv)
-
- def join(self, seq):
- """
- Join a sequence of `Composable`.
-
- :param seq: the elements to join.
- :type seq: iterable of `!Composable`
-
- Use the `!SQL` object's *string* to separate the elements in *seq*.
- Note that `Composed` objects are iterable too, so they can be used as
- argument for this method.
-
- Example::
-
- >>> snip = sql.SQL(', ').join(
- ... sql.Identifier(n) for n in ['foo', 'bar', 'baz'])
- >>> print(snip.as_string(conn))
- "foo", "bar", "baz"
- """
- rv = []
- it = iter(seq)
- try:
- rv.append(next(it))
- except StopIteration:
- pass
- else:
- for i in it:
- rv.append(self)
- rv.append(i)
-
- return Composed(rv)
-
-
-class Identifier(Composable):
- """
- A `Composable` representing an SQL identifier or a dot-separated sequence.
-
- Identifiers usually represent names of database objects, such as tables or
- fields. PostgreSQL identifiers follow `different rules`__ than SQL string
- literals for escaping (e.g. they use double quotes instead of single).
-
- .. __: https://www.postgresql.org/docs/current/static/sql-syntax-lexical.html# \
- SQL-SYNTAX-IDENTIFIERS
-
- Example::
-
- >>> t1 = sql.Identifier("foo")
- >>> t2 = sql.Identifier("ba'r")
- >>> t3 = sql.Identifier('ba"z')
- >>> print(sql.SQL(', ').join([t1, t2, t3]).as_string(conn))
- "foo", "ba'r", "ba""z"
-
- Multiple strings can be passed to the object to represent a qualified name,
- i.e. a dot-separated sequence of identifiers.
-
- Example::
-
- >>> query = sql.SQL("select {} from {}").format(
- ... sql.Identifier("table", "field"),
- ... sql.Identifier("schema", "table"))
- >>> print(query.as_string(conn))
- select "table"."field" from "schema"."table"
-
- """
- def __init__(self, *strings):
- if not strings:
- raise TypeError("Identifier cannot be empty")
-
- for s in strings:
- if not isinstance(s, str):
- raise TypeError("SQL identifier parts must be strings")
-
- super().__init__(strings)
-
- @property
- def strings(self):
- """A tuple with the strings wrapped by the `Identifier`."""
- return self._wrapped
-
- @property
- def string(self):
- """The string wrapped by the `Identifier`.
- """
- if len(self._wrapped) == 1:
- return self._wrapped[0]
- else:
- raise AttributeError(
- "the Identifier wraps more than one than one string")
-
- def __repr__(self):
- return f"{self.__class__.__name__}({', '.join(map(repr, self._wrapped))})"
-
- def as_string(self, context):
- return '.'.join(ext.quote_ident(s, context) for s in self._wrapped)
-
-
-class Literal(Composable):
- """
- A `Composable` representing an SQL value to include in a query.
-
- Usually you will want to include placeholders in the query and pass values
- as `~cursor.execute()` arguments. If however you really really need to
- include a literal value in the query you can use this object.
-
- The string returned by `!as_string()` follows the normal :ref:`adaptation
- rules ` for Python objects.
-
- Example::
-
- >>> s1 = sql.Literal("foo")
- >>> s2 = sql.Literal("ba'r")
- >>> s3 = sql.Literal(42)
- >>> print(sql.SQL(', ').join([s1, s2, s3]).as_string(conn))
- 'foo', 'ba''r', 42
-
- """
- @property
- def wrapped(self):
- """The object wrapped by the `!Literal`."""
- return self._wrapped
-
- def as_string(self, context):
- # is it a connection or cursor?
- if isinstance(context, ext.connection):
- conn = context
- elif isinstance(context, ext.cursor):
- conn = context.connection
- else:
- raise TypeError("context must be a connection or a cursor")
-
- a = ext.adapt(self._wrapped)
- if hasattr(a, 'prepare'):
- a.prepare(conn)
-
- rv = a.getquoted()
- if isinstance(rv, bytes):
- rv = rv.decode(ext.encodings[conn.encoding])
-
- return rv
-
-
-class Placeholder(Composable):
- """A `Composable` representing a placeholder for query parameters.
-
- If the name is specified, generate a named placeholder (e.g. ``%(name)s``),
- otherwise generate a positional placeholder (e.g. ``%s``).
-
- The object is useful to generate SQL queries with a variable number of
- arguments.
-
- Examples::
-
- >>> names = ['foo', 'bar', 'baz']
-
- >>> q1 = sql.SQL("insert into table ({}) values ({})").format(
- ... sql.SQL(', ').join(map(sql.Identifier, names)),
- ... sql.SQL(', ').join(sql.Placeholder() * len(names)))
- >>> print(q1.as_string(conn))
- insert into table ("foo", "bar", "baz") values (%s, %s, %s)
-
- >>> q2 = sql.SQL("insert into table ({}) values ({})").format(
- ... sql.SQL(', ').join(map(sql.Identifier, names)),
- ... sql.SQL(', ').join(map(sql.Placeholder, names)))
- >>> print(q2.as_string(conn))
- insert into table ("foo", "bar", "baz") values (%(foo)s, %(bar)s, %(baz)s)
-
- """
-
- def __init__(self, name=None):
- if isinstance(name, str):
- if ')' in name:
- raise ValueError(f"invalid name: {name!r}")
-
- elif name is not None:
- raise TypeError(f"expected string or None as name, got {name!r}")
-
- super().__init__(name)
-
- @property
- def name(self):
- """The name of the `!Placeholder`."""
- return self._wrapped
-
- def __repr__(self):
- if self._wrapped is None:
- return f"{self.__class__.__name__}()"
- else:
- return f"{self.__class__.__name__}({self._wrapped!r})"
-
- def as_string(self, context):
- if self._wrapped is not None:
- return f"%({self._wrapped})s"
- else:
- return "%s"
-
-
-# Literals
-NULL = SQL("NULL")
-DEFAULT = SQL("DEFAULT")
diff --git a/psycopg2/tz.py b/psycopg2/tz.py
deleted file mode 100644
index d88ca37..0000000
--- a/psycopg2/tz.py
+++ /dev/null
@@ -1,158 +0,0 @@
-"""tzinfo implementations for psycopg2
-
-This module holds two different tzinfo implementations that can be used as
-the 'tzinfo' argument to datetime constructors, directly passed to psycopg
-functions or used to set the .tzinfo_factory attribute in cursors.
-"""
-# psycopg/tz.py - tzinfo implementation
-#
-# Copyright (C) 2003-2019 Federico Di Gregorio
-# Copyright (C) 2020-2021 The Psycopg Team
-#
-# psycopg2 is free software: you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# In addition, as a special exception, the copyright holders give
-# permission to link this program with the OpenSSL library (or with
-# modified versions of OpenSSL that use the same license as OpenSSL),
-# and distribute linked combinations including the two.
-#
-# You must obey the GNU Lesser General Public License in all respects for
-# all of the code used other than OpenSSL.
-#
-# psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-# License for more details.
-
-import datetime
-import time
-
-ZERO = datetime.timedelta(0)
-
-
-class FixedOffsetTimezone(datetime.tzinfo):
- """Fixed offset in minutes east from UTC.
-
- This is exactly the implementation__ found in Python 2.3.x documentation,
- with a small change to the `!__init__()` method to allow for pickling
- and a default name in the form ``sHH:MM`` (``s`` is the sign.).
-
- The implementation also caches instances. During creation, if a
- FixedOffsetTimezone instance has previously been created with the same
- offset and name that instance will be returned. This saves memory and
- improves comparability.
-
- .. versionchanged:: 2.9
-
- The constructor can take either a timedelta or a number of minutes of
- offset. Previously only minutes were supported.
-
- .. __: https://docs.python.org/library/datetime.html
- """
- _name = None
- _offset = ZERO
-
- _cache = {}
-
- def __init__(self, offset=None, name=None):
- if offset is not None:
- if not isinstance(offset, datetime.timedelta):
- offset = datetime.timedelta(minutes=offset)
- self._offset = offset
- if name is not None:
- self._name = name
-
- def __new__(cls, offset=None, name=None):
- """Return a suitable instance created earlier if it exists
- """
- key = (offset, name)
- try:
- return cls._cache[key]
- except KeyError:
- tz = super().__new__(cls, offset, name)
- cls._cache[key] = tz
- return tz
-
- def __repr__(self):
- return "psycopg2.tz.FixedOffsetTimezone(offset=%r, name=%r)" \
- % (self._offset, self._name)
-
- def __eq__(self, other):
- if isinstance(other, FixedOffsetTimezone):
- return self._offset == other._offset
- else:
- return NotImplemented
-
- def __ne__(self, other):
- if isinstance(other, FixedOffsetTimezone):
- return self._offset != other._offset
- else:
- return NotImplemented
-
- def __getinitargs__(self):
- return self._offset, self._name
-
- def utcoffset(self, dt):
- return self._offset
-
- def tzname(self, dt):
- if self._name is not None:
- return self._name
-
- minutes, seconds = divmod(self._offset.total_seconds(), 60)
- hours, minutes = divmod(minutes, 60)
- rv = "%+03d" % hours
- if minutes or seconds:
- rv += ":%02d" % minutes
- if seconds:
- rv += ":%02d" % seconds
-
- return rv
-
- def dst(self, dt):
- return ZERO
-
-
-STDOFFSET = datetime.timedelta(seconds=-time.timezone)
-if time.daylight:
- DSTOFFSET = datetime.timedelta(seconds=-time.altzone)
-else:
- DSTOFFSET = STDOFFSET
-DSTDIFF = DSTOFFSET - STDOFFSET
-
-
-class LocalTimezone(datetime.tzinfo):
- """Platform idea of local timezone.
-
- This is the exact implementation from the Python 2.3 documentation.
- """
- def utcoffset(self, dt):
- if self._isdst(dt):
- return DSTOFFSET
- else:
- return STDOFFSET
-
- def dst(self, dt):
- if self._isdst(dt):
- return DSTDIFF
- else:
- return ZERO
-
- def tzname(self, dt):
- return time.tzname[self._isdst(dt)]
-
- def _isdst(self, dt):
- tt = (dt.year, dt.month, dt.day,
- dt.hour, dt.minute, dt.second,
- dt.weekday(), 0, -1)
- stamp = time.mktime(tt)
- tt = time.localtime(stamp)
- return tt.tm_isdst > 0
-
-
-LOCAL = LocalTimezone()
-
-# TODO: pre-generate some interesting time zones?
diff --git a/psycopg2_binary-2.9.1.dist-info/INSTALLER b/psycopg2_binary-2.9.1.dist-info/INSTALLER
deleted file mode 100644
index a1b589e..0000000
--- a/psycopg2_binary-2.9.1.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/psycopg2_binary-2.9.1.dist-info/LICENSE b/psycopg2_binary-2.9.1.dist-info/LICENSE
deleted file mode 100644
index 9029e70..0000000
--- a/psycopg2_binary-2.9.1.dist-info/LICENSE
+++ /dev/null
@@ -1,49 +0,0 @@
-psycopg2 and the LGPL
----------------------
-
-psycopg2 is free software: you can redistribute it and/or modify it
-under the terms of the GNU Lesser General Public License as published
-by the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-psycopg2 is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-License for more details.
-
-In addition, as a special exception, the copyright holders give
-permission to link this program with the OpenSSL library (or with
-modified versions of OpenSSL that use the same license as OpenSSL),
-and distribute linked combinations including the two.
-
-You must obey the GNU Lesser General Public License in all respects for
-all of the code used other than OpenSSL. If you modify file(s) with this
-exception, you may extend this exception to your version of the file(s),
-but you are not obligated to do so. If you do not wish to do so, delete
-this exception statement from your version. If you delete this exception
-statement from all source files in the program, then also delete it here.
-
-You should have received a copy of the GNU Lesser General Public License
-along with psycopg2 (see the doc/ directory.)
-If not, see .
-
-
-Alternative licenses
---------------------
-
-The following BSD-like license applies (at your option) to the files following
-the pattern ``psycopg/adapter*.{h,c}`` and ``psycopg/microprotocol*.{h,c}``:
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this
- software in a product, an acknowledgment in the product documentation
- would be appreciated but is not required.
-
- 2. Altered source versions must be plainly marked as such, and must not
- be misrepresented as being the original software.
-
- 3. This notice may not be removed or altered from any source distribution.
diff --git a/psycopg2_binary-2.9.1.dist-info/METADATA b/psycopg2_binary-2.9.1.dist-info/METADATA
deleted file mode 100644
index 3317142..0000000
--- a/psycopg2_binary-2.9.1.dist-info/METADATA
+++ /dev/null
@@ -1,109 +0,0 @@
-Metadata-Version: 2.1
-Name: psycopg2-binary
-Version: 2.9.1
-Summary: psycopg2 - Python-PostgreSQL Database Adapter
-Home-page: https://psycopg.org/
-Author: Federico Di Gregorio
-Author-email: fog@initd.org
-Maintainer: Daniele Varrazzo
-Maintainer-email: daniele.varrazzo@gmail.org
-License: LGPL with exceptions
-Project-URL: Homepage, https://psycopg.org/
-Project-URL: Documentation, https://www.psycopg.org/docs/
-Project-URL: Code, https://github.com/psycopg/psycopg2
-Project-URL: Issue Tracker, https://github.com/psycopg/psycopg2/issues
-Project-URL: Download, https://pypi.org/project/psycopg2/
-Platform: any
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
-Classifier: Programming Language :: Python
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.6
-Classifier: Programming Language :: Python :: 3.7
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
-Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: Implementation :: CPython
-Classifier: Programming Language :: C
-Classifier: Programming Language :: SQL
-Classifier: Topic :: Database
-Classifier: Topic :: Database :: Front-Ends
-Classifier: Topic :: Software Development
-Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Classifier: Operating System :: Microsoft :: Windows
-Classifier: Operating System :: Unix
-Requires-Python: >=3.6
-
-Psycopg is the most popular PostgreSQL database adapter for the Python
-programming language. Its main features are the complete implementation of
-the Python DB API 2.0 specification and the thread safety (several threads can
-share the same connection). It was designed for heavily multi-threaded
-applications that create and destroy lots of cursors and make a large number
-of concurrent "INSERT"s or "UPDATE"s.
-
-Psycopg 2 is mostly implemented in C as a libpq wrapper, resulting in being
-both efficient and secure. It features client-side and server-side cursors,
-asynchronous communication and notifications, "COPY TO/COPY FROM" support.
-Many Python types are supported out-of-the-box and adapted to matching
-PostgreSQL data types; adaptation can be extended and customized thanks to a
-flexible objects adaptation system.
-
-Psycopg 2 is both Unicode and Python 3 friendly.
-
-
-Documentation
--------------
-
-Documentation is included in the ``doc`` directory and is `available online`__.
-
-.. __: https://www.psycopg.org/docs/
-
-For any other resource (source code repository, bug tracker, mailing list)
-please check the `project homepage`__.
-
-.. __: https://psycopg.org/
-
-
-Installation
-------------
-
-Building Psycopg requires a few prerequisites (a C compiler, some development
-packages): please check the install_ and the faq_ documents in the ``doc`` dir
-or online for the details.
-
-If prerequisites are met, you can install psycopg like any other Python
-package, using ``pip`` to download it from PyPI_::
-
- $ pip install psycopg2
-
-or using ``setup.py`` if you have downloaded the source package locally::
-
- $ python setup.py build
- $ sudo python setup.py install
-
-You can also obtain a stand-alone package, not requiring a compiler or
-external libraries, by installing the `psycopg2-binary`_ package from PyPI::
-
- $ pip install psycopg2-binary
-
-The binary package is a practical choice for development and testing but in
-production it is advised to use the package built from sources.
-
-.. _PyPI: https://pypi.org/project/psycopg2/
-.. _psycopg2-binary: https://pypi.org/project/psycopg2-binary/
-.. _install: https://www.psycopg.org/docs/install.html#install-from-source
-.. _faq: https://www.psycopg.org/docs/faq.html#faq-compile
-
-:Linux/OSX: |gh-actions|
-:Windows: |appveyor|
-
-.. |gh-actions| image:: https://github.com/psycopg/psycopg2/actions/workflows/tests.yml/badge.svg
- :target: https://github.com/psycopg/psycopg2/actions/workflows/tests.yml
- :alt: Linux and OSX build status
-
-.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/psycopg/psycopg2?branch=master&svg=true
- :target: https://ci.appveyor.com/project/psycopg/psycopg2/branch/master
- :alt: Windows build status
-
-
diff --git a/psycopg2_binary-2.9.1.dist-info/RECORD b/psycopg2_binary-2.9.1.dist-info/RECORD
deleted file mode 100644
index a5263eb..0000000
--- a/psycopg2_binary-2.9.1.dist-info/RECORD
+++ /dev/null
@@ -1,37 +0,0 @@
-psycopg2/.dylibs/libcom_err.3.0.dylib,sha256=C2sly_bRfUKs17LYm5sA6RK_8ZuDDB4T_yGdahVTU2A,52528
-psycopg2/.dylibs/libcrypto.1.1.dylib,sha256=vDzJPUTHrryhMV2FE0nICWHYURdImF1vriamuaJsSTg,2437024
-psycopg2/.dylibs/libgssapi_krb5.2.2.dylib,sha256=K0RKBWt_Cfm1xAkyL8MGzgwdfMbrJhw2_AOca23ffpw,304784
-psycopg2/.dylibs/libk5crypto.3.1.dylib,sha256=E6_1Xen80xpyePW89AOHk2tKopymPU4NWfOjZli9xEs,196240
-psycopg2/.dylibs/libkrb5.3.3.dylib,sha256=0V9kOsUQYkiaFp81rDIuYhTvPkdEjuV9bjZYQhMBra0,768704
-psycopg2/.dylibs/libkrb5support.1.1.dylib,sha256=0P-toSEm_j0z0xDc6ketcAb0_SofI1xvSxmT76XFn84,77336
-psycopg2/.dylibs/libpq.5.13.dylib,sha256=35Djva8ktrq_24RSmHt6UOtsc86toH5q2YVTjXg9T1E,297072
-psycopg2/.dylibs/libssl.1.1.dylib,sha256=eWGWQFZ7Zsj6PdNEm3kt6ISEFx9IeUV0BckztqMdiTY,502872
-psycopg2/__init__.py,sha256=9mo5Qd0uWHiEBx2CdogGos2kNqtlNNGzbtYlGC0hWS8,4768
-psycopg2/__pycache__/__init__.cpython-37.pyc,,
-psycopg2/__pycache__/_ipaddress.cpython-37.pyc,,
-psycopg2/__pycache__/_json.cpython-37.pyc,,
-psycopg2/__pycache__/_range.cpython-37.pyc,,
-psycopg2/__pycache__/errorcodes.cpython-37.pyc,,
-psycopg2/__pycache__/errors.cpython-37.pyc,,
-psycopg2/__pycache__/extensions.cpython-37.pyc,,
-psycopg2/__pycache__/extras.cpython-37.pyc,,
-psycopg2/__pycache__/pool.cpython-37.pyc,,
-psycopg2/__pycache__/sql.cpython-37.pyc,,
-psycopg2/__pycache__/tz.cpython-37.pyc,,
-psycopg2/_ipaddress.py,sha256=jkuyhLgqUGRBcLNWDM8QJysV6q1Npc_RYH4_kE7JZPU,2922
-psycopg2/_json.py,sha256=XPn4PnzbTg1Dcqz7n1JMv5dKhB5VFV6834GEtxSawt0,7153
-psycopg2/_psycopg.cpython-37m-darwin.so,sha256=bSneUsQMz_x_SLhvLIHvJiuH1G8tNgHXxt4oYoJgXXw,303712
-psycopg2/_range.py,sha256=79xD6i5_aIVYTj_q6lrqfQEz00y3V16nJ5kbScLqy6U,17608
-psycopg2/errorcodes.py,sha256=Z5gbq6FF4nAucL4eWxNwa_UQC7FmA-fz0nr_Ly4KToA,14277
-psycopg2/errors.py,sha256=aAS4dJyTg1bsDzJDCRQAMB_s7zv-Q4yB6Yvih26I-0M,1425
-psycopg2/extensions.py,sha256=CG0kG5vL8Ot503UGlDXXJJFdFWLg4HE2_c1-lLOLc8M,6797
-psycopg2/extras.py,sha256=XOQ6YkyaLeypg2_POPXt8c_MUbuSthdMYywGn9rMNfo,42863
-psycopg2/pool.py,sha256=UGEt8IdP3xNc2PGYNlG4sQvg8nhf4aeCnz39hTR0H8I,6316
-psycopg2/sql.py,sha256=OcFEAmpe2aMfrx0MEk4Lx00XvXXJCmvllaOVbJY-yoE,14779
-psycopg2/tz.py,sha256=r95kK7eGSpOYr_luCyYsznHMzjl52sLjsnSPXkXLzRI,4870
-psycopg2_binary-2.9.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-psycopg2_binary-2.9.1.dist-info/LICENSE,sha256=lhS4XfyacsWyyjMUTB1-HtOxwpdFnZ-yimpXYsLo1xs,2238
-psycopg2_binary-2.9.1.dist-info/METADATA,sha256=VPf4Sf-grXf4XxdA-FKdJOYHF--kra-6S8XI8fl5UGE,4322
-psycopg2_binary-2.9.1.dist-info/RECORD,,
-psycopg2_binary-2.9.1.dist-info/WHEEL,sha256=YglPFYw5KGRo4lYd8nnG23sfeQiYV_V6SgUtLfHfx7o,251
-psycopg2_binary-2.9.1.dist-info/top_level.txt,sha256=7dHGpLqQ3w-vGmGEVn-7uK90qU9fyrGdWWi7S-gTcnM,9
diff --git a/psycopg2_binary-2.9.1.dist-info/WHEEL b/psycopg2_binary-2.9.1.dist-info/WHEEL
deleted file mode 100644
index 45386da..0000000
--- a/psycopg2_binary-2.9.1.dist-info/WHEEL
+++ /dev/null
@@ -1,9 +0,0 @@
-Wheel-Version: 1.0
-Generator: bdist_wheel (0.36.2)
-Root-Is-Purelib: false
-Tag: cp37-cp37m-macosx_10_14_x86_64
-Tag: cp37-cp37m-macosx_10_9_intel
-Tag: cp37-cp37m-macosx_10_9_x86_64
-Tag: cp37-cp37m-macosx_10_10_intel
-Tag: cp37-cp37m-macosx_10_10_x86_64
-
diff --git a/psycopg2_binary-2.9.1.dist-info/top_level.txt b/psycopg2_binary-2.9.1.dist-info/top_level.txt
deleted file mode 100644
index 658130b..0000000
--- a/psycopg2_binary-2.9.1.dist-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-psycopg2