psql
This commit is contained in:
parent
7baf47ffae
commit
4b5315b8dd
1
psycopg2-2.9.1.dist-info/INSTALLER
Normal file
1
psycopg2-2.9.1.dist-info/INSTALLER
Normal file
@ -0,0 +1 @@
|
|||||||
|
pip
|
49
psycopg2-2.9.1.dist-info/LICENSE
Normal file
49
psycopg2-2.9.1.dist-info/LICENSE
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
110
psycopg2-2.9.1.dist-info/METADATA
Normal file
110
psycopg2-2.9.1.dist-info/METADATA
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
30
psycopg2-2.9.1.dist-info/RECORD
Normal file
30
psycopg2-2.9.1.dist-info/RECORD
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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
|
0
psycopg2-2.9.1.dist-info/REQUESTED
Normal file
0
psycopg2-2.9.1.dist-info/REQUESTED
Normal file
5
psycopg2-2.9.1.dist-info/WHEEL
Normal file
5
psycopg2-2.9.1.dist-info/WHEEL
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
Wheel-Version: 1.0
|
||||||
|
Generator: bdist_wheel (0.37.0)
|
||||||
|
Root-Is-Purelib: false
|
||||||
|
Tag: cp39-cp39-macosx_10_9_x86_64
|
||||||
|
|
1
psycopg2-2.9.1.dist-info/top_level.txt
Normal file
1
psycopg2-2.9.1.dist-info/top_level.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
psycopg2
|
BIN
psycopg2/.dylibs/libcom_err.3.0.dylib
Normal file
BIN
psycopg2/.dylibs/libcom_err.3.0.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libcrypto.1.1.dylib
Normal file
BIN
psycopg2/.dylibs/libcrypto.1.1.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libgssapi_krb5.2.2.dylib
Normal file
BIN
psycopg2/.dylibs/libgssapi_krb5.2.2.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libk5crypto.3.1.dylib
Normal file
BIN
psycopg2/.dylibs/libk5crypto.3.1.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libkrb5.3.3.dylib
Normal file
BIN
psycopg2/.dylibs/libkrb5.3.3.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libkrb5support.1.1.dylib
Normal file
BIN
psycopg2/.dylibs/libkrb5support.1.1.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libpq.5.13.dylib
Normal file
BIN
psycopg2/.dylibs/libpq.5.13.dylib
Normal file
Binary file not shown.
BIN
psycopg2/.dylibs/libssl.1.1.dylib
Normal file
BIN
psycopg2/.dylibs/libssl.1.1.dylib
Normal file
Binary file not shown.
126
psycopg2/__init__.py
Normal file
126
psycopg2/__init__.py
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
"""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 <fog@debian.org>
|
||||||
|
# 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
|
90
psycopg2/_ipaddress.py
Normal file
90
psycopg2/_ipaddress.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
"""Implementation of the ipaddres-based network types adaptation
|
||||||
|
"""
|
||||||
|
|
||||||
|
# psycopg/_ipaddress.py - Ipaddres-based network types adaptation
|
||||||
|
#
|
||||||
|
# Copyright (C) 2016-2019 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
# 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))
|
199
psycopg2/_json.py
Normal file
199
psycopg2/_json.py
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
"""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 <daniele.varrazzo@gmail.com>
|
||||||
|
# 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
|
BIN
psycopg2/_psycopg.cpython-37m-darwin.so
Executable file
BIN
psycopg2/_psycopg.cpython-37m-darwin.so
Executable file
Binary file not shown.
BIN
psycopg2/_psycopg.cpython-39-darwin.so
Executable file
BIN
psycopg2/_psycopg.cpython-39-darwin.so
Executable file
Binary file not shown.
537
psycopg2/_range.py
Normal file
537
psycopg2/_range.py
Normal file
@ -0,0 +1,537 @@
|
|||||||
|
"""Implementation of the Range type and adaptation
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# psycopg/_range.py - Implementation of the Range type and adaptation
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012-2019 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
# 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()
|
447
psycopg2/errorcodes.py
Normal file
447
psycopg2/errorcodes.py
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
"""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 <jdahlin@async.com.br>
|
||||||
|
# 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'
|
38
psycopg2/errors.py
Normal file
38
psycopg2/errors.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
"""Error classes for PostgreSQL error codes
|
||||||
|
"""
|
||||||
|
|
||||||
|
# psycopg/errors.py - SQLSTATE and DB-API exceptions
|
||||||
|
#
|
||||||
|
# Copyright (C) 2018-2019 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
# 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]
|
213
psycopg2/extensions.py
Normal file
213
psycopg2/extensions.py
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
"""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 <fog@debian.org>
|
||||||
|
# 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
|
1306
psycopg2/extras.py
Normal file
1306
psycopg2/extras.py
Normal file
File diff suppressed because it is too large
Load Diff
187
psycopg2/pool.py
Normal file
187
psycopg2/pool.py
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
"""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 <fog@debian.org>
|
||||||
|
# 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()
|
455
psycopg2/sql.py
Normal file
455
psycopg2/sql.py
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
"""SQL composition utility module
|
||||||
|
"""
|
||||||
|
|
||||||
|
# psycopg/sql.py - SQL composition utility module
|
||||||
|
#
|
||||||
|
# Copyright (C) 2016-2019 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
# 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 <python-types-adaptation>` 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")
|
158
psycopg2/tz.py
Normal file
158
psycopg2/tz.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
"""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 <fog@debian.org>
|
||||||
|
# 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?
|
1
psycopg2_binary-2.9.1.dist-info/INSTALLER
Normal file
1
psycopg2_binary-2.9.1.dist-info/INSTALLER
Normal file
@ -0,0 +1 @@
|
|||||||
|
pip
|
49
psycopg2_binary-2.9.1.dist-info/LICENSE
Normal file
49
psycopg2_binary-2.9.1.dist-info/LICENSE
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
109
psycopg2_binary-2.9.1.dist-info/METADATA
Normal file
109
psycopg2_binary-2.9.1.dist-info/METADATA
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
37
psycopg2_binary-2.9.1.dist-info/RECORD
Normal file
37
psycopg2_binary-2.9.1.dist-info/RECORD
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
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
|
9
psycopg2_binary-2.9.1.dist-info/WHEEL
Normal file
9
psycopg2_binary-2.9.1.dist-info/WHEEL
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
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
|
||||||
|
|
1
psycopg2_binary-2.9.1.dist-info/top_level.txt
Normal file
1
psycopg2_binary-2.9.1.dist-info/top_level.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
psycopg2
|
Loading…
Reference in New Issue
Block a user