mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-11-04 01:37:31 +03:00 
			
		
		
		
	Merge branch 'master' into named-callproc
This commit is contained in:
		
						commit
						faaef61c27
					
				
							
								
								
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								.travis.yml
									
									
									
									
									
								
							| 
						 | 
					@ -1,13 +1,24 @@
 | 
				
			||||||
 | 
					# Travis CI configuration file for psycopg2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dist: trusty
 | 
				
			||||||
 | 
					sudo: required
 | 
				
			||||||
language: python
 | 
					language: python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
python:
 | 
					python:
 | 
				
			||||||
  - 2.6
 | 
					 | 
				
			||||||
  - 2.7
 | 
					  - 2.7
 | 
				
			||||||
 | 
					  - 3.6-dev
 | 
				
			||||||
before_script:
 | 
					  - 2.6
 | 
				
			||||||
  - psql -c 'create database psycopg2_test;' -U postgres
 | 
					  - 3.5
 | 
				
			||||||
 | 
					  - 3.4
 | 
				
			||||||
 | 
					  - 3.3
 | 
				
			||||||
 | 
					  - 3.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
install:
 | 
					install:
 | 
				
			||||||
  - python setup.py install
 | 
					  - python setup.py install
 | 
				
			||||||
 | 
					  - sudo scripts/travis_prepare.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
script: make check
 | 
					script:
 | 
				
			||||||
 | 
					  - scripts/travis_test.sh
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					notifications:
 | 
				
			||||||
 | 
					  email: false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,10 +2,10 @@ recursive-include psycopg *.c *.h *.manifest
 | 
				
			||||||
recursive-include lib *.py
 | 
					recursive-include lib *.py
 | 
				
			||||||
recursive-include tests *.py
 | 
					recursive-include tests *.py
 | 
				
			||||||
recursive-include examples *.py somehackers.jpg whereareyou.jpg
 | 
					recursive-include examples *.py somehackers.jpg whereareyou.jpg
 | 
				
			||||||
recursive-include doc README SUCCESS COPYING.LESSER pep-0249.txt
 | 
					include doc/README.rst doc/SUCCESS doc/COPYING.LESSER doc/pep-0249.txt
 | 
				
			||||||
recursive-include doc Makefile requirements.txt
 | 
					include doc/Makefile doc/requirements.txt
 | 
				
			||||||
recursive-include doc/src *.rst *.py *.css Makefile
 | 
					recursive-include doc/src *.rst *.py *.css Makefile
 | 
				
			||||||
recursive-include scripts *.py *.sh
 | 
					recursive-include scripts *.py *.sh
 | 
				
			||||||
include scripts/maketypes.sh scripts/buildtypes.py
 | 
					include scripts/maketypes.sh scripts/buildtypes.py
 | 
				
			||||||
include AUTHORS README.rst INSTALL LICENSE NEWS
 | 
					include AUTHORS README.rst INSTALL LICENSE NEWS
 | 
				
			||||||
include PKG-INFO MANIFEST.in MANIFEST setup.py setup.cfg Makefile
 | 
					include MANIFEST.in setup.py setup.cfg Makefile
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										9
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								Makefile
									
									
									
									
									
								
							| 
						 | 
					@ -92,14 +92,9 @@ $(PACKAGE)/tests/%.py: tests/%.py
 | 
				
			||||||
	$(PYTHON) setup.py build_py $(BUILD_OPT)
 | 
						$(PYTHON) setup.py build_py $(BUILD_OPT)
 | 
				
			||||||
	touch $@
 | 
						touch $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(SDIST): MANIFEST $(SOURCE)
 | 
					$(SDIST): $(SOURCE)
 | 
				
			||||||
	$(PYTHON) setup.py sdist $(SDIST_OPT)
 | 
						$(PYTHON) setup.py sdist $(SDIST_OPT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
MANIFEST: MANIFEST.in $(SOURCE)
 | 
					 | 
				
			||||||
	# Run twice as MANIFEST.in includes MANIFEST
 | 
					 | 
				
			||||||
	$(PYTHON) setup.py sdist --manifest-only
 | 
					 | 
				
			||||||
	$(PYTHON) setup.py sdist --manifest-only
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# docs depend on the build as it partly use introspection.
 | 
					# docs depend on the build as it partly use introspection.
 | 
				
			||||||
doc/html/genindex.html: $(PLATLIB) $(PURELIB) $(SOURCE_DOC)
 | 
					doc/html/genindex.html: $(PLATLIB) $(PURELIB) $(SOURCE_DOC)
 | 
				
			||||||
	$(MAKE) -C doc html
 | 
						$(MAKE) -C doc html
 | 
				
			||||||
| 
						 | 
					@ -111,5 +106,5 @@ doc/docs.zip: doc/html/genindex.html
 | 
				
			||||||
	(cd doc/html && zip -r ../docs.zip *)
 | 
						(cd doc/html && zip -r ../docs.zip *)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
clean:
 | 
					clean:
 | 
				
			||||||
	rm -rf build MANIFEST
 | 
						rm -rf build
 | 
				
			||||||
	$(MAKE) -C doc clean
 | 
						$(MAKE) -C doc clean
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										38
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								NEWS
									
									
									
									
									
								
							| 
						 | 
					@ -6,7 +6,12 @@ What's new in psycopg 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
New features:
 | 
					New features:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Added `~psycopg2.extensions.parse_dsn()` function (:ticket:`#321`).
 | 
					- Added :ref:`replication-support` (:ticket:`#322`). Main authors are
 | 
				
			||||||
 | 
					  Oleksandr Shulgin and Craig Ringer, who deserve a huge thank you.
 | 
				
			||||||
 | 
					- Added `~psycopg2.extensions.parse_dsn()` and
 | 
				
			||||||
 | 
					  `~psycopg2.extensions.make_dsn()` functions (:tickets:`#321, #363`).
 | 
				
			||||||
 | 
					  `~psycopg2.connect()` now can take both *dsn* and keyword arguments, merging
 | 
				
			||||||
 | 
					  them together.
 | 
				
			||||||
- Added `~psycopg2.__libpq_version__` and
 | 
					- Added `~psycopg2.__libpq_version__` and
 | 
				
			||||||
  `~psycopg2.extensions.libpq_version()` to inspect the version of the
 | 
					  `~psycopg2.extensions.libpq_version()` to inspect the version of the
 | 
				
			||||||
  ``libpq`` library the module was compiled/loaded with
 | 
					  ``libpq`` library the module was compiled/loaded with
 | 
				
			||||||
| 
						 | 
					@ -14,19 +19,46 @@ New features:
 | 
				
			||||||
- The attributes `~connection.notices` and `~connection.notifies` can be
 | 
					- The attributes `~connection.notices` and `~connection.notifies` can be
 | 
				
			||||||
  customized replacing them with any object exposing an `!append()` method
 | 
					  customized replacing them with any object exposing an `!append()` method
 | 
				
			||||||
  (:ticket:`#326`).
 | 
					  (:ticket:`#326`).
 | 
				
			||||||
 | 
					- Adapt network types to `ipaddress` objects when available. When not
 | 
				
			||||||
 | 
					  enabled, convert arrays of network types to lists by default. The old `!Inet`
 | 
				
			||||||
 | 
					  adapter is deprecated (:tickets:`#317, #343, #387`).
 | 
				
			||||||
- Added `~psycopg2.extensions.quote_ident()` function (:ticket:`#359`).
 | 
					- Added `~psycopg2.extensions.quote_ident()` function (:ticket:`#359`).
 | 
				
			||||||
 | 
					- Added `~connection.get_dsn_parameters()` connection method (:ticket:`#364`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Other changes:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Dropped support for Python 2.5.
 | 
				
			||||||
 | 
					- Dropped support for client library older than PostgreSQL 9.1 (but older
 | 
				
			||||||
 | 
					  server versions are still supported).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					What's new in psycopg 2.6.3
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Throw an exception trying to pass ``NULL`` chars as parameters
 | 
				
			||||||
 | 
					  (:ticket:`#420`).
 | 
				
			||||||
 | 
					- Make `~psycopg2.extras.Range` objects picklable (:ticket:`#462`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
What's new in psycopg 2.6.2
 | 
					What's new in psycopg 2.6.2
 | 
				
			||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Report the server response status on errors (such as :ticket:`#281`).
 | 
					- Report the server response status on errors (such as :ticket:`#281`).
 | 
				
			||||||
 | 
					- Raise `!NotSupportedError` on unhandled server response status
 | 
				
			||||||
 | 
					  (:ticket:`#352`).
 | 
				
			||||||
 | 
					- Allow overriding string adapter encoding with no connection (:ticket:`#331`).
 | 
				
			||||||
- The `~psycopg2.extras.wait_select` callback allows interrupting a
 | 
					- The `~psycopg2.extras.wait_select` callback allows interrupting a
 | 
				
			||||||
  long-running query in an interactive shell using :kbd:`Ctrl-C`
 | 
					  long-running query in an interactive shell using :kbd:`Ctrl-C`
 | 
				
			||||||
  (:ticket:`#333`).
 | 
					  (:ticket:`#333`).
 | 
				
			||||||
- Raise `!NotSupportedError` on unhandled server response status
 | 
					 | 
				
			||||||
  (:ticket:`#352`).
 | 
					 | 
				
			||||||
- Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`).
 | 
					- Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`).
 | 
				
			||||||
 | 
					- Fixed segfault on `repr()` of an unitialized connection (:ticket:`#361`).
 | 
				
			||||||
 | 
					- Allow adapting bytes using QuotedString on Python 3 too (:ticket:`#365`).
 | 
				
			||||||
 | 
					- Added support for setuptools/wheel (:ticket:`#370`).
 | 
				
			||||||
 | 
					- Fix build on Windows with Python 3.5, VS 2015 (:ticket:`#380`).
 | 
				
			||||||
 | 
					- Fixed `!errorcodes.lookup` initialization thread-safety (:ticket:`#382`).
 | 
				
			||||||
 | 
					- Fixed `!read()` exception propagation in copy_from (:ticket:`#412`).
 | 
				
			||||||
 | 
					- Fixed possible NULL TZ decref  (:ticket:`#424`).
 | 
				
			||||||
 | 
					- `~psycopg2.errorcodes` map updated to PostgreSQL 9.5.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
What's new in psycopg 2.6.1
 | 
					What's new in psycopg 2.6.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,3 +44,8 @@ For any other resource (source code repository, bug tracker, mailing list)
 | 
				
			||||||
please check the `project homepage`__.
 | 
					please check the `project homepage`__.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. __: http://initd.org/psycopg/
 | 
					.. __: http://initd.org/psycopg/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. image:: https://travis-ci.org/psycopg/psycopg2.svg?branch=master
 | 
				
			||||||
 | 
					    :target: https://travis-ci.org/psycopg/psycopg2
 | 
				
			||||||
 | 
					    :alt: Build Status
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ it is the class where query building, execution and result type-casting into
 | 
				
			||||||
Python variables happens.
 | 
					Python variables happens.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `~psycopg2.extras` module contains several examples of :ref:`connection
 | 
					The `~psycopg2.extras` module contains several examples of :ref:`connection
 | 
				
			||||||
and cursor sublcasses <cursor-subclasses>`.
 | 
					and cursor subclasses <cursor-subclasses>`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. note::
 | 
					.. note::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,7 +270,7 @@ wasting resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
A simple application could poll the connection from time to time to check if
 | 
					A simple application could poll the connection from time to time to check if
 | 
				
			||||||
something new has arrived. A better strategy is to use some I/O completion
 | 
					something new has arrived. A better strategy is to use some I/O completion
 | 
				
			||||||
function such as :py:func:`~select.select` to sleep until awaken from the kernel when there is
 | 
					function such as :py:func:`~select.select` to sleep until awakened by the kernel when there is
 | 
				
			||||||
some data to read on the connection, thereby using no CPU unless there is
 | 
					some data to read on the connection, thereby using no CPU unless there is
 | 
				
			||||||
something to read::
 | 
					something to read::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -423,7 +423,7 @@ this will be probably implemented in a future release.
 | 
				
			||||||
Support for coroutine libraries
 | 
					Support for coroutine libraries
 | 
				
			||||||
-------------------------------
 | 
					-------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. versionadded:: 2.2.0
 | 
					.. versionadded:: 2.2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Psycopg can be used together with coroutine_\-based libraries and participate
 | 
					Psycopg can be used together with coroutine_\-based libraries and participate
 | 
				
			||||||
in cooperative multithreading.
 | 
					in cooperative multithreading.
 | 
				
			||||||
| 
						 | 
					@ -509,3 +509,90 @@ resources about the topic.
 | 
				
			||||||
    conn.commit()
 | 
					    conn.commit()
 | 
				
			||||||
    cur.close()
 | 
					    cur.close()
 | 
				
			||||||
    conn.close()
 | 
					    conn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. index::
 | 
				
			||||||
 | 
					    single: Replication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _replication-support:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Replication protocol support
 | 
				
			||||||
 | 
					----------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. versionadded:: 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Modern PostgreSQL servers (version 9.0 and above) support replication.  The
 | 
				
			||||||
 | 
					replication protocol is built on top of the client-server protocol and can be
 | 
				
			||||||
 | 
					operated using ``libpq``, as such it can be also operated by ``psycopg2``.
 | 
				
			||||||
 | 
					The replication protocol can be operated on both synchronous and
 | 
				
			||||||
 | 
					:ref:`asynchronous <async-support>` connections.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Server version 9.4 adds a new feature called *Logical Replication*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. seealso::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   - PostgreSQL `Streaming Replication Protocol`__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   .. __: http://www.postgresql.org/docs/current/static/protocol-replication.html
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Logical replication Quick-Start
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You must be using PostgreSQL server version 9.4 or above to run this quick
 | 
				
			||||||
 | 
					start.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Make sure that replication connections are permitted for user ``postgres`` in
 | 
				
			||||||
 | 
					``pg_hba.conf`` and reload the server configuration.  You also need to set
 | 
				
			||||||
 | 
					``wal_level=logical`` and ``max_wal_senders``, ``max_replication_slots`` to
 | 
				
			||||||
 | 
					value greater than zero in ``postgresql.conf`` (these changes require a server
 | 
				
			||||||
 | 
					restart).  Create a database ``psycopg2_test``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then run the following code to quickly try the replication support out.  This
 | 
				
			||||||
 | 
					is not production code -- it has no error handling, it sends feedback too
 | 
				
			||||||
 | 
					often, etc. -- and it's only intended as a simple demo of logical
 | 
				
			||||||
 | 
					replication::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  from __future__ import print_function
 | 
				
			||||||
 | 
					  import sys
 | 
				
			||||||
 | 
					  import psycopg2
 | 
				
			||||||
 | 
					  import psycopg2.extras
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  conn = psycopg2.connect('dbname=psycopg2_test user=postgres',
 | 
				
			||||||
 | 
					     connection_factory=psycopg2.extras.LogicalReplicationConnection)
 | 
				
			||||||
 | 
					  cur = conn.cursor()
 | 
				
			||||||
 | 
					  try:
 | 
				
			||||||
 | 
					      # test_decoding produces textual output
 | 
				
			||||||
 | 
					      cur.start_replication(slot_name='pytest', decode=True)
 | 
				
			||||||
 | 
					  except psycopg2.ProgrammingError:
 | 
				
			||||||
 | 
					      cur.create_replication_slot('pytest', output_plugin='test_decoding')
 | 
				
			||||||
 | 
					      cur.start_replication(slot_name='pytest', decode=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  class DemoConsumer(object):
 | 
				
			||||||
 | 
					      def __call__(self, msg):
 | 
				
			||||||
 | 
					          print(msg.payload)
 | 
				
			||||||
 | 
					          msg.cursor.send_feedback(flush_lsn=msg.data_start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  democonsumer = DemoConsumer()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  print("Starting streaming, press Control-C to end...", file=sys.stderr)
 | 
				
			||||||
 | 
					  try:
 | 
				
			||||||
 | 
					     cur.consume_stream(democonsumer)
 | 
				
			||||||
 | 
					  except KeyboardInterrupt:
 | 
				
			||||||
 | 
					     cur.close()
 | 
				
			||||||
 | 
					     conn.close()
 | 
				
			||||||
 | 
					     print("The slot 'pytest' still exists. Drop it with "
 | 
				
			||||||
 | 
					        "SELECT pg_drop_replication_slot('pytest'); if no longer needed.",
 | 
				
			||||||
 | 
					        file=sys.stderr)
 | 
				
			||||||
 | 
					     print("WARNING: Transaction logs will accumulate in pg_xlog "
 | 
				
			||||||
 | 
					        "until the slot is dropped.", file=sys.stderr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can now make changes to the ``psycopg2_test`` database using a normal
 | 
				
			||||||
 | 
					psycopg2 session, ``psql``, etc. and see the logical decoding stream printed
 | 
				
			||||||
 | 
					by this demo client.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This will continue running until terminated with ``Control-C``.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For the details see :ref:`replication-objects`.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,9 +42,7 @@ master_doc = 'index'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# General information about the project.
 | 
					# General information about the project.
 | 
				
			||||||
project = u'Psycopg'
 | 
					project = u'Psycopg'
 | 
				
			||||||
from datetime import date
 | 
					copyright = u'2001-2016, Federico Di Gregorio, Daniele Varrazzo'
 | 
				
			||||||
year = date.today().year
 | 
					 | 
				
			||||||
copyright = u'2001-%s, Federico Di Gregorio, Daniele Varrazzo' % year
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# The version info for the project you're documenting, acts as replacement for
 | 
					# The version info for the project you're documenting, acts as replacement for
 | 
				
			||||||
# |version| and |release|, also used in various other places throughout the
 | 
					# |version| and |release|, also used in various other places throughout the
 | 
				
			||||||
| 
						 | 
					@ -63,8 +61,8 @@ except ImportError:
 | 
				
			||||||
    release = version
 | 
					    release = version
 | 
				
			||||||
 | 
					
 | 
				
			||||||
intersphinx_mapping = {
 | 
					intersphinx_mapping = {
 | 
				
			||||||
    'py': ('http://docs.python.org/', None),
 | 
					    'py': ('http://docs.python.org/2', None),
 | 
				
			||||||
    'py3': ('http://docs.python.org/3.2', None),
 | 
					    'py3': ('http://docs.python.org/3', None),
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Pattern to generate links to the bug tracker
 | 
					# Pattern to generate links to the bug tracker
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -568,6 +568,29 @@ The ``connection`` class
 | 
				
			||||||
        .. versionadded:: 2.0.12
 | 
					        .. versionadded:: 2.0.12
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. index::
 | 
				
			||||||
 | 
					        pair: Connection; Parameters
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: get_dsn_parameters()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Get the effective dsn parameters for the connection as a dictionary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The *password* parameter is removed from the result.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            >>> conn.get_dsn_parameters()
 | 
				
			||||||
 | 
					            {'dbname': 'test', 'user': 'postgres', 'port': '5432', 'sslmode': 'prefer'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Requires libpq >= 9.3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. seealso:: libpq docs for `PQconninfo()`__ for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. versionadded:: 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. index::
 | 
					    .. index::
 | 
				
			||||||
        pair: Transaction; Status
 | 
					        pair: Transaction; Status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -499,6 +499,9 @@ The ``cursor`` class
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. rubric:: COPY-related methods
 | 
					    .. rubric:: COPY-related methods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Efficiently copy data from file-like objects to the database and back. See
 | 
				
			||||||
 | 
					    :ref:`copy` for an overview.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. extension::
 | 
					    .. extension::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        The :sql:`COPY` command is a PostgreSQL extension to the SQL standard.
 | 
					        The :sql:`COPY` command is a PostgreSQL extension to the SQL standard.
 | 
				
			||||||
| 
						 | 
					@ -507,7 +510,7 @@ The ``cursor`` class
 | 
				
			||||||
    .. method:: copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None)
 | 
					    .. method:: copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Read data *from* the file-like object *file* appending them to
 | 
					        Read data *from* the file-like object *file* appending them to
 | 
				
			||||||
        the table named *table*.  See :ref:`copy` for an overview.
 | 
					        the table named *table*.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        :param file: file-like object to read data from.  It must have both
 | 
					        :param file: file-like object to read data from.  It must have both
 | 
				
			||||||
            `!read()` and `!readline()` methods.
 | 
					            `!read()` and `!readline()` methods.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,17 +12,12 @@
 | 
				
			||||||
The module contains a few objects and function extending the minimum set of
 | 
					The module contains a few objects and function extending the minimum set of
 | 
				
			||||||
functionalities defined by the |DBAPI|_.
 | 
					functionalities defined by the |DBAPI|_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. function:: parse_dsn(dsn)
 | 
					Classes definitions
 | 
				
			||||||
 | 
					-------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Parse connection string into a dictionary of keywords and values.
 | 
					Instances of these classes are usually returned by factory functions or
 | 
				
			||||||
 | 
					attributes. Their definitions are exposed here to allow subclassing,
 | 
				
			||||||
    Uses libpq's ``PQconninfoParse`` to parse the string according to
 | 
					introspection etc.
 | 
				
			||||||
    accepted format(s) and check for supported keywords.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Example::
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        >>> psycopg2.extensions.parse_dsn('dbname=test user=postgres password=secret')
 | 
					 | 
				
			||||||
        {'password': 'secret', 'user': 'postgres', 'dbname': 'test'}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. class:: connection(dsn, async=False)
 | 
					.. class:: connection(dsn, async=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +29,7 @@ functionalities defined by the |DBAPI|_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    For a complete description of the class, see `connection`.
 | 
					    For a complete description of the class, see `connection`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. class:: cursor(conn, name=None)
 | 
					.. class:: cursor(conn, name=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    It is the class usually returned by the `connection.cursor()`
 | 
					    It is the class usually returned by the `connection.cursor()`
 | 
				
			||||||
| 
						 | 
					@ -44,6 +40,7 @@ functionalities defined by the |DBAPI|_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    For a complete description of the class, see `cursor`.
 | 
					    For a complete description of the class, see `cursor`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]])
 | 
					.. class:: lobject(conn [, oid [, mode [, new_oid [, new_file ]]]])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an
 | 
					    Wrapper for a PostgreSQL large object. See :ref:`large-objects` for an
 | 
				
			||||||
| 
						 | 
					@ -200,39 +197,6 @@ functionalities defined by the |DBAPI|_.
 | 
				
			||||||
        server versions.
 | 
					        server versions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autofunction:: set_wait_callback(f)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .. versionadded:: 2.2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autofunction:: get_wait_callback()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .. versionadded:: 2.2.0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. function:: libpq_version()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Return the version number of the ``libpq`` dynamic library loaded as an
 | 
					 | 
				
			||||||
    integer, in the same format of `~connection.server_version`.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was
 | 
					 | 
				
			||||||
    compiled with a ``libpq`` version lesser than 9.1 (which can be detected
 | 
					 | 
				
			||||||
    by the `~psycopg2.__libpq_version__` constant).
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .. seealso:: libpq docs for `PQlibVersion()`__.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. function:: quote_ident(str, scope)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Return quoted identifier according to PostgreSQL quoting rules.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    The *scope* must be a `connection` or a `cursor`, the underlying
 | 
					 | 
				
			||||||
    connection encoding is used for any necessary character conversion.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Requires libpq >= 9.0.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    .. seealso:: libpq docs for `PQescapeIdentifier()`__
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        .. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _sql-adaptation-objects:
 | 
					.. _sql-adaptation-objects:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -492,6 +456,106 @@ The module exports a few exceptions in addition to the :ref:`standard ones
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _coroutines-functions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Coroutines support functions
 | 
				
			||||||
 | 
					----------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					These functions are used to set and retrieve the callback function for
 | 
				
			||||||
 | 
					:ref:`cooperation with coroutine libraries <green-support>`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. versionadded:: 2.2.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autofunction:: set_wait_callback(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autofunction:: get_wait_callback()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Other functions
 | 
				
			||||||
 | 
					---------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. function:: libpq_version()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Return the version number of the ``libpq`` dynamic library loaded as an
 | 
				
			||||||
 | 
					    integer, in the same format of `~connection.server_version`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Raise `~psycopg2.NotSupportedError` if the ``psycopg2`` module was
 | 
				
			||||||
 | 
					    compiled with a ``libpq`` version lesser than 9.1 (which can be detected
 | 
				
			||||||
 | 
					    by the `~psycopg2.__libpq_version__` constant).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. seealso:: libpq docs for `PQlibVersion()`__.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. __: http://www.postgresql.org/docs/current/static/libpq-misc.html#LIBPQ-PQLIBVERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. function:: make_dsn(dsn=None, \*\*kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Create a valid connection string from arguments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Put together the arguments in *kwargs* into a connection string. If *dsn*
 | 
				
			||||||
 | 
					    is specified too, merge the arguments coming from both the sources. If the
 | 
				
			||||||
 | 
					    same argument name is specified in both the sources, the *kwargs* value
 | 
				
			||||||
 | 
					    overrides the *dsn* value.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The input arguments are validated: the output should always be a valid
 | 
				
			||||||
 | 
					    connection string (as far as `parse_dsn()` is concerned). If not raise
 | 
				
			||||||
 | 
					    `~psycopg2.ProgrammingError`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        >>> from psycopg2.extensions import make_dsn
 | 
				
			||||||
 | 
					        >>> make_dsn('dbname=foo host=example.com', password="s3cr3t")
 | 
				
			||||||
 | 
					        'host=example.com password=s3cr3t dbname=foo'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. function:: parse_dsn(dsn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parse connection string into a dictionary of keywords and values.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Parsing is delegated to the libpq: different versions of the client
 | 
				
			||||||
 | 
					    library may support different formats or parameters (for example,
 | 
				
			||||||
 | 
					    `connection URIs`__ are only supported from libpq 9.2).  Raise
 | 
				
			||||||
 | 
					    `~psycopg2.ProgrammingError` if the *dsn* is not valid.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        >>> from psycopg2.extensions import parse_dsn
 | 
				
			||||||
 | 
					        >>> parse_dsn('dbname=test user=postgres password=secret')
 | 
				
			||||||
 | 
					        {'password': 'secret', 'user': 'postgres', 'dbname': 'test'}
 | 
				
			||||||
 | 
					        >>> parse_dsn("postgresql://someone@example.com/somedb?connect_timeout=10")
 | 
				
			||||||
 | 
					        {'host': 'example.com', 'user': 'someone', 'dbname': 'somedb', 'connect_timeout': '10'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. seealso:: libpq docs for `PQconninfoParse()`__.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFOPARSE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. function:: quote_ident(str, scope)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Return quoted identifier according to PostgreSQL quoting rules.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The *scope* must be a `connection` or a `cursor`, the underlying
 | 
				
			||||||
 | 
					    connection encoding is used for any necessary character conversion.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Requires libpq >= 9.0.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionadded:: 2.7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. seealso:: libpq docs for `PQescapeIdentifier()`__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. __: http://www.postgresql.org/docs/current/static/libpq-exec.html#LIBPQ-PQESCAPEIDENTIFIER
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. index::
 | 
					.. index::
 | 
				
			||||||
    pair: Isolation level; Constants
 | 
					    pair: Isolation level; Constants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,6 +143,374 @@ Logging cursor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. _replication-objects:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Replication connection and cursor classes
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					See :ref:`replication-support` for an introduction to the topic.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The following replication types are defined:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. data:: REPLICATION_LOGICAL
 | 
				
			||||||
 | 
					.. data:: REPLICATION_PHYSICAL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. index::
 | 
				
			||||||
 | 
					    pair: Connection; replication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: LogicalReplicationConnection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This connection factory class can be used to open a special type of
 | 
				
			||||||
 | 
					    connection that is used for logical replication.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from psycopg2.extras import LogicalReplicationConnection
 | 
				
			||||||
 | 
					        log_conn = psycopg2.connect(dsn, connection_factory=LogicalReplicationConnection)
 | 
				
			||||||
 | 
					        log_cur = log_conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: PhysicalReplicationConnection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This connection factory class can be used to open a special type of
 | 
				
			||||||
 | 
					    connection that is used for physical replication.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from psycopg2.extras import PhysicalReplicationConnection
 | 
				
			||||||
 | 
					        phys_conn = psycopg2.connect(dsn, connection_factory=PhysicalReplicationConnection)
 | 
				
			||||||
 | 
					        phys_cur = phys_conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Both `LogicalReplicationConnection` and `PhysicalReplicationConnection` use
 | 
				
			||||||
 | 
					    `ReplicationCursor` for actual communication with the server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. index::
 | 
				
			||||||
 | 
					    pair: Message; replication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The individual messages in the replication stream are represented by
 | 
				
			||||||
 | 
					`ReplicationMessage` objects (both logical and physical type):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: ReplicationMessage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: payload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The actual data received from the server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        An instance of either `bytes()` or `unicode()`, depending on the value
 | 
				
			||||||
 | 
					        of `decode` option passed to `~ReplicationCursor.start_replication()`
 | 
				
			||||||
 | 
					        on the connection.  See `~ReplicationCursor.read_message()` for
 | 
				
			||||||
 | 
					        details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: data_size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The raw size of the message payload (before possible unicode
 | 
				
			||||||
 | 
					        conversion).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: data_start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LSN position of the start of the message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: wal_end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        LSN position of the current end of WAL on the server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: send_time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        A `~datetime` object representing the server timestamp at the moment
 | 
				
			||||||
 | 
					        when the message was sent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: cursor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        A reference to the corresponding `ReplicationCursor` object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. index::
 | 
				
			||||||
 | 
					    pair: Cursor; replication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: ReplicationCursor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: create_replication_slot(slot_name, slot_type=None, output_plugin=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Create streaming replication slot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param slot_name: name of the replication slot to be created
 | 
				
			||||||
 | 
					        :param slot_type: type of replication: should be either
 | 
				
			||||||
 | 
					                          `REPLICATION_LOGICAL` or `REPLICATION_PHYSICAL`
 | 
				
			||||||
 | 
					        :param output_plugin: name of the logical decoding output plugin to be
 | 
				
			||||||
 | 
					                              used by the slot; required for logical
 | 
				
			||||||
 | 
					                              replication connections, disallowed for physical
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            log_cur.create_replication_slot("logical1", "test_decoding")
 | 
				
			||||||
 | 
					            phys_cur.create_replication_slot("physical1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # either logical or physical replication connection
 | 
				
			||||||
 | 
					            cur.create_replication_slot("slot1", slot_type=REPLICATION_LOGICAL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        When creating a slot on a logical replication connection, a logical
 | 
				
			||||||
 | 
					        replication slot is created by default.  Logical replication requires
 | 
				
			||||||
 | 
					        name of the logical decoding output plugin to be specified.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        When creating a slot on a physical replication connection, a physical
 | 
				
			||||||
 | 
					        replication slot is created by default.  No output plugin parameter is
 | 
				
			||||||
 | 
					        required or allowed when creating a physical replication slot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        In either case the type of slot being created can be specified
 | 
				
			||||||
 | 
					        explicitly using *slot_type* parameter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Replication slots are a feature of PostgreSQL server starting with
 | 
				
			||||||
 | 
					        version 9.4.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: drop_replication_slot(slot_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Drop streaming replication slot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param slot_name: name of the replication slot to drop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # either logical or physical replication connection
 | 
				
			||||||
 | 
					            cur.drop_replication_slot("slot1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Replication slots are a feature of PostgreSQL server starting with
 | 
				
			||||||
 | 
					        version 9.4.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: start_replication(slot_name=None, slot_type=None, start_lsn=0, timeline=0, options=None, decode=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Start replication on the connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param slot_name: name of the replication slot to use; required for
 | 
				
			||||||
 | 
					                          logical replication, physical replication can work
 | 
				
			||||||
 | 
					                          with or without a slot
 | 
				
			||||||
 | 
					        :param slot_type: type of replication: should be either
 | 
				
			||||||
 | 
					                          `REPLICATION_LOGICAL` or `REPLICATION_PHYSICAL`
 | 
				
			||||||
 | 
					        :param start_lsn: the optional LSN position to start replicating from,
 | 
				
			||||||
 | 
					                          can be an integer or a string of hexadecimal digits
 | 
				
			||||||
 | 
					                          in the form ``XXX/XXX``
 | 
				
			||||||
 | 
					        :param timeline: WAL history timeline to start streaming from (optional,
 | 
				
			||||||
 | 
					                         can only be used with physical replication)
 | 
				
			||||||
 | 
					        :param options: a dictionary of options to pass to logical replication
 | 
				
			||||||
 | 
					                        slot (not allowed with physical replication)
 | 
				
			||||||
 | 
					        :param decode: a flag indicating that unicode conversion should be
 | 
				
			||||||
 | 
					                       performed on messages received from the server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If a *slot_name* is specified, the slot must exist on the server and
 | 
				
			||||||
 | 
					        its type must match the replication type used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If not specified using *slot_type* parameter, the type of replication
 | 
				
			||||||
 | 
					        is defined by the type of replication connection.  Logical replication
 | 
				
			||||||
 | 
					        is only allowed on logical replication connection, but physical
 | 
				
			||||||
 | 
					        replication can be used with both types of connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        On the other hand, physical replication doesn't require a named
 | 
				
			||||||
 | 
					        replication slot to be used, only logical replication does.  In any
 | 
				
			||||||
 | 
					        case logical replication and replication slots are a feature of
 | 
				
			||||||
 | 
					        PostgreSQL server starting with version 9.4.  Physical replication can
 | 
				
			||||||
 | 
					        be used starting with 9.0.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If *start_lsn* is specified, the requested stream will start from that
 | 
				
			||||||
 | 
					        LSN.  The default is `!None` which passes the LSN ``0/0`` causing
 | 
				
			||||||
 | 
					        replay to begin at the last point for which the server got flush
 | 
				
			||||||
 | 
					        confirmation from the client, or the oldest available point for a new
 | 
				
			||||||
 | 
					        slot.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The server might produce an error if a WAL file for the given LSN has
 | 
				
			||||||
 | 
					        already been recycled or it may silently start streaming from a later
 | 
				
			||||||
 | 
					        position: the client can verify the actual position using information
 | 
				
			||||||
 | 
					        provided by the `ReplicationMessage` attributes.  The exact server
 | 
				
			||||||
 | 
					        behavior depends on the type of replication and use of slots.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The *timeline* parameter can only be specified with physical
 | 
				
			||||||
 | 
					        replication and only starting with server version 9.3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        A dictionary of *options* may be passed to the logical decoding plugin
 | 
				
			||||||
 | 
					        on a logical replication slot.  The set of supported options depends
 | 
				
			||||||
 | 
					        on the output plugin that was used to create the slot.  Must be
 | 
				
			||||||
 | 
					        `!None` for physical replication.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If *decode* is set to `!True` the messages received from the server
 | 
				
			||||||
 | 
					        would be converted according to the connection `~connection.encoding`.
 | 
				
			||||||
 | 
					        *This parameter should not be set with physical replication or with
 | 
				
			||||||
 | 
					        logical replication plugins that produce binary output.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This function constructs a ``START_REPLICATION`` command and calls
 | 
				
			||||||
 | 
					        `start_replication_expert()` internally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        After starting the replication, to actually consume the incoming
 | 
				
			||||||
 | 
					        server messages use `consume_stream()` or implement a loop around
 | 
				
			||||||
 | 
					        `read_message()` in case of :ref:`asynchronous connection
 | 
				
			||||||
 | 
					        <async-support>`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: start_replication_expert(command, decode=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Start replication on the connection using provided
 | 
				
			||||||
 | 
					        ``START_REPLICATION`` command.  See `start_replication()` for
 | 
				
			||||||
 | 
					        description of *decode* parameter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: consume_stream(consume, keepalive_interval=10)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param consume: a callable object with signature :samp:`consume({msg})`
 | 
				
			||||||
 | 
					        :param keepalive_interval: interval (in seconds) to send keepalive
 | 
				
			||||||
 | 
					                                   messages to the server
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method can only be used with synchronous connection.  For
 | 
				
			||||||
 | 
					        asynchronous connections see `read_message()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Before using this method to consume the stream call
 | 
				
			||||||
 | 
					        `start_replication()` first.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method enters an endless loop reading messages from the server
 | 
				
			||||||
 | 
					        and passing them to ``consume()`` one at a time, then waiting for more
 | 
				
			||||||
 | 
					        messages from the server.  In order to make this method break out of
 | 
				
			||||||
 | 
					        the loop and return, ``consume()`` can throw a `StopReplication`
 | 
				
			||||||
 | 
					        exception.  Any unhandled exception will make it break out of the loop
 | 
				
			||||||
 | 
					        as well.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The *msg* object passed to ``consume()`` is an instance of
 | 
				
			||||||
 | 
					        `ReplicationMessage` class.  See `read_message()` for details about
 | 
				
			||||||
 | 
					        message decoding.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method also sends keepalive messages to the server in case there
 | 
				
			||||||
 | 
					        were no new data from the server for the duration of
 | 
				
			||||||
 | 
					        *keepalive_interval* (in seconds).  The value of this parameter must
 | 
				
			||||||
 | 
					        be set to at least 1 second, but it can have a fractional part.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        After processing certain amount of messages the client should send a
 | 
				
			||||||
 | 
					        confirmation message to the server.  This should be done by calling
 | 
				
			||||||
 | 
					        `send_feedback()` method on the corresponding replication cursor.  A
 | 
				
			||||||
 | 
					        reference to the cursor is provided in the `ReplicationMessage` as an
 | 
				
			||||||
 | 
					        attribute.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The following example is a sketch implementation of ``consume()``
 | 
				
			||||||
 | 
					        callable for logical replication::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            class LogicalStreamConsumer(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                def __call__(self, msg):
 | 
				
			||||||
 | 
					                    self.process_message(msg.payload)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if self.should_send_feedback(msg):
 | 
				
			||||||
 | 
					                        msg.cursor.send_feedback(flush_lsn=msg.data_start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            consumer = LogicalStreamConsumer()
 | 
				
			||||||
 | 
					            cur.consume_stream(consumer)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        .. warning::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            When using replication with slots, failure to constantly consume
 | 
				
			||||||
 | 
					            *and* report success to the server appropriately can eventually
 | 
				
			||||||
 | 
					            lead to "disk full" condition on the server, because the server
 | 
				
			||||||
 | 
					            retains all the WAL segments that might be needed to stream the
 | 
				
			||||||
 | 
					            changes via all of the currently open replication slots.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            On the other hand, it is not recommended to send confirmation
 | 
				
			||||||
 | 
					            after *every* processed message, since that will put an
 | 
				
			||||||
 | 
					            unnecessary load on network and the server.  A possible strategy
 | 
				
			||||||
 | 
					            is to confirm after every COMMIT message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: send_feedback(write_lsn=0, flush_lsn=0, apply_lsn=0, reply=False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param write_lsn: a LSN position up to which the client has written the data locally
 | 
				
			||||||
 | 
					        :param flush_lsn: a LSN position up to which the client has processed the
 | 
				
			||||||
 | 
					                          data reliably (the server is allowed to discard all
 | 
				
			||||||
 | 
					                          and every data that predates this LSN)
 | 
				
			||||||
 | 
					        :param apply_lsn: a LSN position up to which the warm standby server
 | 
				
			||||||
 | 
					                          has applied the changes (physical replication
 | 
				
			||||||
 | 
					                          master-slave protocol only)
 | 
				
			||||||
 | 
					        :param reply: request the server to send back a keepalive message immediately
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Use this method to report to the server that all messages up to a
 | 
				
			||||||
 | 
					        certain LSN position have been processed on the client and may be
 | 
				
			||||||
 | 
					        discarded on the server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method can also be called with all default parameters' values to
 | 
				
			||||||
 | 
					        just send a keepalive message to the server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Low-level replication cursor methods for :ref:`asynchronous connection
 | 
				
			||||||
 | 
					    <async-support>` operation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    With the synchronous connection a call to `consume_stream()` handles all
 | 
				
			||||||
 | 
					    the complexity of handling the incoming messages and sending keepalive
 | 
				
			||||||
 | 
					    replies, but at times it might be beneficial to use low-level interface
 | 
				
			||||||
 | 
					    for better control, in particular to `~select` on multiple sockets.  The
 | 
				
			||||||
 | 
					    following methods are provided for asynchronous operation:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: read_message()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Try to read the next message from the server without blocking and
 | 
				
			||||||
 | 
					        return an instance of `ReplicationMessage` or `!None`, in case there
 | 
				
			||||||
 | 
					        are no more data messages from the server at the moment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method should be used in a loop with asynchronous connections
 | 
				
			||||||
 | 
					        (after calling `start_replication()` once).  For synchronous
 | 
				
			||||||
 | 
					        connections see `consume_stream()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The returned message's `~ReplicationMessage.payload` is an instance of
 | 
				
			||||||
 | 
					        `!unicode` decoded according to connection `~connection.encoding`
 | 
				
			||||||
 | 
					        *iff* *decode* was set to `!True` in the initial call to
 | 
				
			||||||
 | 
					        `start_replication()` on this connection, otherwise it is an instance
 | 
				
			||||||
 | 
					        of `!bytes` with no decoding.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        It is expected that the calling code will call this method repeatedly
 | 
				
			||||||
 | 
					        in order to consume all of the messages that might have been buffered
 | 
				
			||||||
 | 
					        until `!None` is returned.  After receiving `!None` from this method
 | 
				
			||||||
 | 
					        the caller should use `~select.select()` or `~select.poll()` on the
 | 
				
			||||||
 | 
					        corresponding connection to block the process until there is more data
 | 
				
			||||||
 | 
					        from the server.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The server can send keepalive messages to the client periodically.
 | 
				
			||||||
 | 
					        Such messages are silently consumed by this method and are never
 | 
				
			||||||
 | 
					        reported to the caller.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. method:: fileno()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Call the corresponding connection's `~connection.fileno()` method and
 | 
				
			||||||
 | 
					        return the result.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This is a convenience method which allows replication cursor to be
 | 
				
			||||||
 | 
					        used directly in `~select.select()` or `~select.poll()` calls.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. attribute:: io_timestamp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        A `~datetime` object representing the timestamp at the moment of last
 | 
				
			||||||
 | 
					        communication with the server (a data or keepalive message in either
 | 
				
			||||||
 | 
					        direction).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    An actual example of asynchronous operation might look like this::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      from select import select
 | 
				
			||||||
 | 
					      from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      def consume(msg):
 | 
				
			||||||
 | 
					          ...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      keepalive_interval = 10.0
 | 
				
			||||||
 | 
					      while True:
 | 
				
			||||||
 | 
					          msg = cur.read_message()
 | 
				
			||||||
 | 
					          if msg:
 | 
				
			||||||
 | 
					              consume(msg)
 | 
				
			||||||
 | 
					          else:
 | 
				
			||||||
 | 
					              now = datetime.now()
 | 
				
			||||||
 | 
					              timeout = keepalive_interval - (now - cur.io_timestamp).total_seconds()
 | 
				
			||||||
 | 
					              try:
 | 
				
			||||||
 | 
					                  sel = select([cur], [], [], max(0, timeout))
 | 
				
			||||||
 | 
					                  if not any(sel):
 | 
				
			||||||
 | 
					                      cur.send_feedback()  # timed out, send keepalive message
 | 
				
			||||||
 | 
					              except InterruptedError:
 | 
				
			||||||
 | 
					                  pass  # recalculate timeout and continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. index::
 | 
				
			||||||
 | 
					    pair: Cursor; Replication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autoclass:: StopReplication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. index::
 | 
					.. index::
 | 
				
			||||||
    single: Data types; Additional
 | 
					    single: Data types; Additional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -562,12 +930,29 @@ UUID data type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. index::
 | 
					.. index::
 | 
				
			||||||
    pair: INET; Data types
 | 
					    pair: INET; Data types
 | 
				
			||||||
 | 
					    pair: CIDR; Data types
 | 
				
			||||||
 | 
					    pair: MACADDR; Data types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
:sql:`inet` data type
 | 
					.. _adapt-network:
 | 
				
			||||||
^^^^^^^^^^^^^^^^^^^^^^
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. versionadded:: 2.0.9
 | 
					Networking data types
 | 
				
			||||||
.. versionchanged:: 2.4.5 added inet array support.
 | 
					^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					By default Psycopg casts the PostgreSQL networking data types (:sql:`inet`,
 | 
				
			||||||
 | 
					:sql:`cidr`, :sql:`macaddr`) into ordinary strings; array of such types are
 | 
				
			||||||
 | 
					converted into lists of strings.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. versionchanged:: 2.7
 | 
				
			||||||
 | 
					    in previous version array of networking types were not treated as arrays.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autofunction:: register_ipaddress
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. autofunction:: register_inet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. deprecated:: 2.7
 | 
				
			||||||
 | 
					        this function will not receive further development and disappear in
 | 
				
			||||||
 | 
					        future versions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. doctest::
 | 
					.. doctest::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -582,10 +967,11 @@ UUID data type
 | 
				
			||||||
    '192.168.0.1/24'
 | 
					    '192.168.0.1/24'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. autofunction:: register_inet
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.. autoclass:: Inet
 | 
					.. autoclass:: Inet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. deprecated:: 2.7
 | 
				
			||||||
 | 
					        this object will not receive further development and may disappear in
 | 
				
			||||||
 | 
					        future versions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. index::
 | 
					.. index::
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,7 +73,7 @@ Why does `!cursor.execute()` raise the exception *can't adapt*?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
I can't pass an integer or a float parameter to my query: it says *a number is required*, but *it is* a number!
 | 
					I can't pass an integer or a float parameter to my query: it says *a number is required*, but *it is* a number!
 | 
				
			||||||
    In your query string, you always have to use ``%s``  placeholders,
 | 
					    In your query string, you always have to use ``%s``  placeholders,
 | 
				
			||||||
    event when passing a number.  All Python objects are converted by Psycopg
 | 
					    even when passing a number.  All Python objects are converted by Psycopg
 | 
				
			||||||
    in their SQL representation, so they get passed to the query as strings.
 | 
					    in their SQL representation, so they get passed to the query as strings.
 | 
				
			||||||
    See :ref:`query-parameters`. ::
 | 
					    See :ref:`query-parameters`. ::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -241,7 +241,7 @@ How do I interrupt a long-running query in an interactive shell?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. code-block:: pycon
 | 
					    .. code-block:: pycon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        >>> psycopg2.extensions.set_wait_callback(psycopg2.extensions.wait_select)
 | 
					        >>> psycopg2.extensions.set_wait_callback(psycopg2.extras.wait_select)
 | 
				
			||||||
        >>> cnn = psycopg2.connect('')
 | 
					        >>> cnn = psycopg2.connect('')
 | 
				
			||||||
        >>> cur = cnn.cursor()
 | 
					        >>> cur = cnn.cursor()
 | 
				
			||||||
        >>> cur.execute("select pg_sleep(10)")
 | 
					        >>> cur.execute("select pg_sleep(10)")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,9 +17,10 @@ The current `!psycopg2` implementation supports:
 | 
				
			||||||
..
 | 
					..
 | 
				
			||||||
    NOTE: keep consistent with setup.py and the /features/ page.
 | 
					    NOTE: keep consistent with setup.py and the /features/ page.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- Python 2 versions from 2.5 to 2.7
 | 
					- Python 2 versions from 2.6 to 2.7
 | 
				
			||||||
- Python 3 versions from 3.1 to 3.4
 | 
					- Python 3 versions from 3.1 to 3.5
 | 
				
			||||||
- PostgreSQL versions from 7.4 to 9.4
 | 
					- PostgreSQL server versions from 7.4 to 9.5
 | 
				
			||||||
 | 
					- PostgreSQL client library version from 9.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _PostgreSQL: http://www.postgresql.org/
 | 
					.. _PostgreSQL: http://www.postgresql.org/
 | 
				
			||||||
.. _Python: http://www.python.org/
 | 
					.. _Python: http://www.python.org/
 | 
				
			||||||
| 
						 | 
					@ -51,6 +52,16 @@ extension packages, *above all if you are a Windows or a Mac OS user*, please
 | 
				
			||||||
use a pre-compiled package and go straight to the :ref:`module usage <usage>`
 | 
					use a pre-compiled package and go straight to the :ref:`module usage <usage>`
 | 
				
			||||||
avoid bothering with the gory details.
 | 
					avoid bothering with the gory details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. note::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Regardless of the way `!psycopg2` is installed, at runtime it will need to
 | 
				
			||||||
 | 
					    use the libpq_ library. `!psycopg2` relies on the host OS to find the
 | 
				
			||||||
 | 
					    library file (usually ``libpq.so`` or ``libpq.dll``): if the library is
 | 
				
			||||||
 | 
					    installed in a standard location there is usually no problem; if the
 | 
				
			||||||
 | 
					    library is in a non-standard location you will have to tell somehow
 | 
				
			||||||
 | 
					    psycopg how to find it, which is OS-dependent (for instance setting a
 | 
				
			||||||
 | 
					    suitable :envvar:`LD_LIBRARY_PATH` on Linux).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _install-from-package:
 | 
					.. _install-from-package:
 | 
				
			||||||
| 
						 | 
					@ -95,7 +106,17 @@ Install from a package
 | 
				
			||||||
    pair: Install; Windows
 | 
					    pair: Install; Windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
**Microsoft Windows**
 | 
					**Microsoft Windows**
 | 
				
			||||||
    Jason Erickson maintains a packaged `Windows port of Psycopg`__ with
 | 
					    There are two options to install a precompiled `psycopg2` package under windows:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    **Option 1:** Using `pip`__ (Included in python 2.7.9+ and python 3.4+)
 | 
				
			||||||
 | 
					    and a binary wheel package.  Launch windows' command prompt (`cmd.exe`)
 | 
				
			||||||
 | 
					    and execute the following command::
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        pip install psycopg2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. __: https://pip.pypa.io/en/stable/installing/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    **Option 2:** Jason Erickson maintains a packaged `Windows port of Psycopg`__ with
 | 
				
			||||||
    installation executable. Download. Double click. Done.
 | 
					    installation executable. Download. Double click. Done.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. __: http://www.stickpeople.com/projects/python/win-psycopg/
 | 
					    .. __: http://www.stickpeople.com/projects/python/win-psycopg/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,37 +17,34 @@ The module interface respects the standard defined in the |DBAPI|_.
 | 
				
			||||||
    single: DSN (Database Source Name)
 | 
					    single: DSN (Database Source Name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. function::
 | 
					.. function::
 | 
				
			||||||
    connect(dsn, connection_factory=None, cursor_factory=None, async=False)
 | 
					    connect(dsn=None, connection_factory=None, cursor_factory=None, async=False, \*\*kwargs)
 | 
				
			||||||
    connect(\*\*kwargs, connection_factory=None, cursor_factory=None, async=False)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Create a new database session and return a new `connection` object.
 | 
					    Create a new database session and return a new `connection` object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The connection parameters can be specified either as a `libpq connection
 | 
					    The connection parameters can be specified as a `libpq connection
 | 
				
			||||||
    string`__ using the *dsn* parameter::
 | 
					    string`__ using the *dsn* parameter::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn = psycopg2.connect("dbname=test user=postgres password=secret")
 | 
					        conn = psycopg2.connect("dbname=test user=postgres password=secret")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    or using a set of keyword arguments::
 | 
					    or using a set of keyword arguments::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn = psycopg2.connect(database="test", user="postgres", password="secret")
 | 
					        conn = psycopg2.connect(dbname"test", user="postgres", password="secret")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The two call styles are mutually exclusive: you cannot specify connection
 | 
					    or using a mix of both: if the same parameter name is specified in both
 | 
				
			||||||
    parameters as keyword arguments together with a connection string; only
 | 
					    sources, the *kwargs* value will have precedence over the *dsn* value.
 | 
				
			||||||
    the parameters not needed for the database connection (*i.e.*
 | 
					    Note that either the *dsn* or at least one connection-related keyword
 | 
				
			||||||
    *connection_factory*, *cursor_factory*, and *async*) are supported
 | 
					    argument is required.
 | 
				
			||||||
    together with the *dsn* argument.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The basic connection parameters are:
 | 
					    The basic connection parameters are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - `!dbname` -- the database name (only in the *dsn* string)
 | 
					    - `!dbname` -- the database name (`!database` is a deprecated alias)
 | 
				
			||||||
    - `!database` -- the database name (only as keyword argument)
 | 
					 | 
				
			||||||
    - `!user` -- user name used to authenticate
 | 
					    - `!user` -- user name used to authenticate
 | 
				
			||||||
    - `!password` -- password used to authenticate
 | 
					    - `!password` -- password used to authenticate
 | 
				
			||||||
    - `!host` -- database host address (defaults to UNIX socket if not provided)
 | 
					    - `!host` -- database host address (defaults to UNIX socket if not provided)
 | 
				
			||||||
    - `!port` -- connection port number (defaults to 5432 if not provided)
 | 
					    - `!port` -- connection port number (defaults to 5432 if not provided)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Any other connection parameter supported by the client library/server can
 | 
					    Any other connection parameter supported by the client library/server can
 | 
				
			||||||
    be passed either in the connection string or as keywords. The PostgreSQL
 | 
					    be passed either in the connection string or as a keyword. The PostgreSQL
 | 
				
			||||||
    documentation contains the complete list of the `supported parameters`__.
 | 
					    documentation contains the complete list of the `supported parameters`__.
 | 
				
			||||||
    Also note that the same parameters can be passed to the client library
 | 
					    Also note that the same parameters can be passed to the client library
 | 
				
			||||||
    using `environment variables`__.
 | 
					    using `environment variables`__.
 | 
				
			||||||
| 
						 | 
					@ -76,6 +73,9 @@ The module interface respects the standard defined in the |DBAPI|_.
 | 
				
			||||||
    .. versionchanged:: 2.5
 | 
					    .. versionchanged:: 2.5
 | 
				
			||||||
        added the *cursor_factory* parameter.
 | 
					        added the *cursor_factory* parameter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .. versionchanged:: 2.7
 | 
				
			||||||
 | 
					        both *dsn* and keyword arguments can be specified.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. seealso::
 | 
					    .. seealso::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        - `~psycopg2.extensions.parse_dsn`
 | 
					        - `~psycopg2.extensions.parse_dsn`
 | 
				
			||||||
| 
						 | 
					@ -89,8 +89,8 @@ The module interface respects the standard defined in the |DBAPI|_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    .. extension::
 | 
					    .. extension::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        The parameters *connection_factory* and *async* are Psycopg extensions
 | 
					        The non-connection-related keyword parameters are Psycopg extensions
 | 
				
			||||||
        to the |DBAPI|.
 | 
					        to the |DBAPI|_.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. data:: apilevel
 | 
					.. data:: apilevel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -264,7 +264,10 @@ types:
 | 
				
			||||||
    +--------------------+-------------------------+--------------------------+
 | 
					    +--------------------+-------------------------+--------------------------+
 | 
				
			||||||
    | Anything\ |tm|     | :sql:`json`             | :ref:`adapt-json`        |
 | 
					    | Anything\ |tm|     | :sql:`json`             | :ref:`adapt-json`        |
 | 
				
			||||||
    +--------------------+-------------------------+--------------------------+
 | 
					    +--------------------+-------------------------+--------------------------+
 | 
				
			||||||
    | `uuid`             | :sql:`uuid`             | :ref:`adapt-uuid`        |
 | 
					    | `~uuid.UUID`       | :sql:`uuid`             | :ref:`adapt-uuid`        |
 | 
				
			||||||
 | 
					    +--------------------+-------------------------+--------------------------+
 | 
				
			||||||
 | 
					    | `ipaddress`        | | :sql:`inet`           | :ref:`adapt-network`     |
 | 
				
			||||||
 | 
					    | objects            | | :sql:`cidr`           |                          |
 | 
				
			||||||
    +--------------------+-------------------------+--------------------------+
 | 
					    +--------------------+-------------------------+--------------------------+
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. |tm| unicode:: U+2122
 | 
					.. |tm| unicode:: U+2122
 | 
				
			||||||
| 
						 | 
					@ -864,11 +867,19 @@ Using COPY TO and COPY FROM
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Psycopg `cursor` objects provide an interface to the efficient
 | 
					Psycopg `cursor` objects provide an interface to the efficient
 | 
				
			||||||
PostgreSQL |COPY|__ command to move data from files to tables and back.
 | 
					PostgreSQL |COPY|__ command to move data from files to tables and back.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Currently no adaptation is provided between Python and PostgreSQL types on
 | 
				
			||||||
 | 
					|COPY|: the file can be any Python file-like object but its format must be in
 | 
				
			||||||
 | 
					the format accepted by `PostgreSQL COPY command`__ (data fromat, escaped
 | 
				
			||||||
 | 
					characters, etc).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. __: COPY_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The methods exposed are:
 | 
					The methods exposed are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`~cursor.copy_from()`
 | 
					`~cursor.copy_from()`
 | 
				
			||||||
    Reads data *from* a file-like object appending them to a database table
 | 
					    Reads data *from* a file-like object appending them to a database table
 | 
				
			||||||
    (:sql:`COPY table FROM file` syntax). The source file must have both
 | 
					    (:sql:`COPY table FROM file` syntax). The source file must provide both
 | 
				
			||||||
    `!read()` and `!readline()` method.
 | 
					    `!read()` and `!readline()` method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`~cursor.copy_to()`
 | 
					`~cursor.copy_to()`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,19 +47,20 @@ Homepage: http://initd.org/projects/psycopg2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Import the DBAPI-2.0 stuff into top-level module.
 | 
					# Import the DBAPI-2.0 stuff into top-level module.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import BINARY, NUMBER, STRING, DATETIME, ROWID
 | 
					from psycopg2._psycopg import (                     # noqa
 | 
				
			||||||
 | 
					    BINARY, NUMBER, STRING, DATETIME, ROWID,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import Binary, Date, Time, Timestamp
 | 
					    Binary, Date, Time, Timestamp,
 | 
				
			||||||
from psycopg2._psycopg import DateFromTicks, TimeFromTicks, TimestampFromTicks
 | 
					    DateFromTicks, TimeFromTicks, TimestampFromTicks,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import Error, Warning, DataError, DatabaseError, ProgrammingError
 | 
					    Error, Warning, DataError, DatabaseError, ProgrammingError, IntegrityError,
 | 
				
			||||||
from psycopg2._psycopg import IntegrityError, InterfaceError, InternalError
 | 
					    InterfaceError, InternalError, NotSupportedError, OperationalError,
 | 
				
			||||||
from psycopg2._psycopg import NotSupportedError, OperationalError
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import _connect, apilevel, threadsafety, paramstyle
 | 
					    _connect, apilevel, threadsafety, paramstyle,
 | 
				
			||||||
from psycopg2._psycopg import __version__, __libpq_version__
 | 
					    __version__, __libpq_version__,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2 import tz
 | 
					from psycopg2 import tz                             # noqa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Register default adapters.
 | 
					# Register default adapters.
 | 
				
			||||||
| 
						 | 
					@ -80,32 +81,13 @@ else:
 | 
				
			||||||
    _ext.register_adapter(Decimal, Adapter)
 | 
					    _ext.register_adapter(Decimal, Adapter)
 | 
				
			||||||
    del Decimal, Adapter
 | 
					    del Decimal, Adapter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import re
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _param_escape(s,
 | 
					def connect(dsn=None, connection_factory=None, cursor_factory=None,
 | 
				
			||||||
        re_escape=re.compile(r"([\\'])"),
 | 
					            async=False, **kwargs):
 | 
				
			||||||
        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
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
del re
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def connect(dsn=None,
 | 
					 | 
				
			||||||
        database=None, user=None, password=None, host=None, port=None,
 | 
					 | 
				
			||||||
        connection_factory=None, cursor_factory=None, async=False, **kwargs):
 | 
					 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Create a new database connection.
 | 
					    Create a new database connection.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The connection parameters can be specified either as a string:
 | 
					    The connection parameters can be specified as a string:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn = psycopg2.connect("dbname=test user=postgres password=secret")
 | 
					        conn = psycopg2.connect("dbname=test user=postgres password=secret")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -113,9 +95,9 @@ def connect(dsn=None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn = psycopg2.connect(database="test", user="postgres", password="secret")
 | 
					        conn = psycopg2.connect(database="test", user="postgres", password="secret")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The basic connection parameters are:
 | 
					    Or as a mix of both. The basic connection parameters are:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - *dbname*: the database name (only in dsn string)
 | 
					    - *dbname*: the database name
 | 
				
			||||||
    - *database*: the database name (only as keyword argument)
 | 
					    - *database*: the database name (only as keyword argument)
 | 
				
			||||||
    - *user*: user name used to authenticate
 | 
					    - *user*: user name used to authenticate
 | 
				
			||||||
    - *password*: password used to authenticate
 | 
					    - *password*: password used to authenticate
 | 
				
			||||||
| 
						 | 
					@ -135,32 +117,10 @@ def connect(dsn=None,
 | 
				
			||||||
    library: the list of supported parameters depends on the library version.
 | 
					    library: the list of supported parameters depends on the library version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    items = []
 | 
					    if dsn is None and not kwargs:
 | 
				
			||||||
    if database is not None:
 | 
					 | 
				
			||||||
        items.append(('dbname', database))
 | 
					 | 
				
			||||||
    if user is not None:
 | 
					 | 
				
			||||||
        items.append(('user', user))
 | 
					 | 
				
			||||||
    if password is not None:
 | 
					 | 
				
			||||||
        items.append(('password', password))
 | 
					 | 
				
			||||||
    if host is not None:
 | 
					 | 
				
			||||||
        items.append(('host', host))
 | 
					 | 
				
			||||||
    if port is not None:
 | 
					 | 
				
			||||||
        items.append(('port', port))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    items.extend([(k, v) for (k, v) in kwargs.iteritems() if v is not None])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if dsn is not None and items:
 | 
					 | 
				
			||||||
        raise TypeError(
 | 
					 | 
				
			||||||
            "'%s' is an invalid keyword argument when the dsn is specified"
 | 
					 | 
				
			||||||
                % items[0][0])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if dsn is None:
 | 
					 | 
				
			||||||
        if not items:
 | 
					 | 
				
			||||||
        raise TypeError('missing dsn and no parameters')
 | 
					        raise TypeError('missing dsn and no parameters')
 | 
				
			||||||
        else:
 | 
					 | 
				
			||||||
            dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
 | 
					 | 
				
			||||||
                for (k, v) in items])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dsn = _ext.make_dsn(dsn, **kwargs)
 | 
				
			||||||
    conn = _connect(dsn, connection_factory=connection_factory, async=async)
 | 
					    conn = _connect(dsn, connection_factory=connection_factory, async=async)
 | 
				
			||||||
    if cursor_factory is not None:
 | 
					    if cursor_factory is not None:
 | 
				
			||||||
        conn.cursor_factory = cursor_factory
 | 
					        conn.cursor_factory = cursor_factory
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										89
									
								
								lib/_ipaddress.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								lib/_ipaddress.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					"""Implementation of the ipaddres-based network types adaptation
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# psycopg/_ipaddress.py - Ipaddres-based network types adaptation
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (C) 2016 Daniele Varrazzo  <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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(unicode(s))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def cast_network(s, cur=None):
 | 
				
			||||||
 | 
					    if s is None:
 | 
				
			||||||
 | 
					        return None
 | 
				
			||||||
 | 
					    return ipaddress.ip_network(unicode(s))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def adapt_ipaddress(obj):
 | 
				
			||||||
 | 
					    return QuotedString(str(obj))
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@ JSONARRAY_OID = 199
 | 
				
			||||||
JSONB_OID = 3802
 | 
					JSONB_OID = 3802
 | 
				
			||||||
JSONBARRAY_OID = 3807
 | 
					JSONBARRAY_OID = 3807
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Json(object):
 | 
					class Json(object):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    An `~psycopg2.extensions.ISQLQuote` wrapper to adapt a Python object to
 | 
					    An `~psycopg2.extensions.ISQLQuote` wrapper to adapt a Python object to
 | 
				
			||||||
| 
						 | 
					@ -143,6 +144,7 @@ def register_json(conn_or_curs=None, globally=False, loads=None,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return JSON, JSONARRAY
 | 
					    return JSON, JSONARRAY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_default_json(conn_or_curs=None, globally=False, loads=None):
 | 
					def register_default_json(conn_or_curs=None, globally=False, loads=None):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Create and register :sql:`json` typecasters for PostgreSQL 9.2 and following.
 | 
					    Create and register :sql:`json` typecasters for PostgreSQL 9.2 and following.
 | 
				
			||||||
| 
						 | 
					@ -155,6 +157,7 @@ def register_default_json(conn_or_curs=None, globally=False, loads=None):
 | 
				
			||||||
    return register_json(conn_or_curs=conn_or_curs, globally=globally,
 | 
					    return register_json(conn_or_curs=conn_or_curs, globally=globally,
 | 
				
			||||||
        loads=loads, oid=JSON_OID, array_oid=JSONARRAY_OID)
 | 
					        loads=loads, oid=JSON_OID, array_oid=JSONARRAY_OID)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_default_jsonb(conn_or_curs=None, globally=False, loads=None):
 | 
					def register_default_jsonb(conn_or_curs=None, globally=False, loads=None):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    Create and register :sql:`jsonb` typecasters for PostgreSQL 9.4 and following.
 | 
					    Create and register :sql:`jsonb` typecasters for PostgreSQL 9.4 and following.
 | 
				
			||||||
| 
						 | 
					@ -167,6 +170,7 @@ def register_default_jsonb(conn_or_curs=None, globally=False, loads=None):
 | 
				
			||||||
    return register_json(conn_or_curs=conn_or_curs, globally=globally,
 | 
					    return register_json(conn_or_curs=conn_or_curs, globally=globally,
 | 
				
			||||||
        loads=loads, oid=JSONB_OID, array_oid=JSONBARRAY_OID, name='jsonb')
 | 
					        loads=loads, oid=JSONB_OID, array_oid=JSONBARRAY_OID, name='jsonb')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _create_json_typecasters(oid, array_oid, loads=None, name='JSON'):
 | 
					def _create_json_typecasters(oid, array_oid, loads=None, name='JSON'):
 | 
				
			||||||
    """Create typecasters for json data type."""
 | 
					    """Create typecasters for json data type."""
 | 
				
			||||||
    if loads is None:
 | 
					    if loads is None:
 | 
				
			||||||
| 
						 | 
					@ -188,6 +192,7 @@ def _create_json_typecasters(oid, array_oid, loads=None, name='JSON'):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return JSON, JSONARRAY
 | 
					    return JSON, JSONARRAY
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _get_json_oids(conn_or_curs, name='json'):
 | 
					def _get_json_oids(conn_or_curs, name='json'):
 | 
				
			||||||
    # lazy imports
 | 
					    # lazy imports
 | 
				
			||||||
    from psycopg2.extensions import STATUS_IN_TRANSACTION
 | 
					    from psycopg2.extensions import STATUS_IN_TRANSACTION
 | 
				
			||||||
| 
						 | 
					@ -215,6 +220,3 @@ def _get_json_oids(conn_or_curs, name='json'):
 | 
				
			||||||
        raise conn.ProgrammingError("%s data type not found" % name)
 | 
					        raise conn.ProgrammingError("%s data type not found" % name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return r
 | 
					    return r
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,9 +27,10 @@
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import ProgrammingError, InterfaceError
 | 
					from psycopg2._psycopg import ProgrammingError, InterfaceError
 | 
				
			||||||
from psycopg2.extensions import ISQLQuote, adapt, register_adapter, b
 | 
					from psycopg2.extensions import ISQLQuote, adapt, register_adapter
 | 
				
			||||||
from psycopg2.extensions import new_type, new_array_type, register_type
 | 
					from psycopg2.extensions import new_type, new_array_type, register_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Range(object):
 | 
					class Range(object):
 | 
				
			||||||
    """Python representation for a PostgreSQL |range|_ type.
 | 
					    """Python representation for a PostgreSQL |range|_ type.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,42 +79,50 @@ class Range(object):
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def lower_inf(self):
 | 
					    def lower_inf(self):
 | 
				
			||||||
        """`!True` if the range doesn't have a lower bound."""
 | 
					        """`!True` if the range doesn't have a lower bound."""
 | 
				
			||||||
        if self._bounds is None: return False
 | 
					        if self._bounds is None:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
        return self._lower is None
 | 
					        return self._lower is None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def upper_inf(self):
 | 
					    def upper_inf(self):
 | 
				
			||||||
        """`!True` if the range doesn't have an upper bound."""
 | 
					        """`!True` if the range doesn't have an upper bound."""
 | 
				
			||||||
        if self._bounds is None: return False
 | 
					        if self._bounds is None:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
        return self._upper is None
 | 
					        return self._upper is None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def lower_inc(self):
 | 
					    def lower_inc(self):
 | 
				
			||||||
        """`!True` if the lower bound is included in the range."""
 | 
					        """`!True` if the lower bound is included in the range."""
 | 
				
			||||||
        if self._bounds is None: return False
 | 
					        if self._bounds is None or self._lower is None:
 | 
				
			||||||
        if self._lower is None: return False
 | 
					            return False
 | 
				
			||||||
        return self._bounds[0] == '['
 | 
					        return self._bounds[0] == '['
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def upper_inc(self):
 | 
					    def upper_inc(self):
 | 
				
			||||||
        """`!True` if the upper bound is included in the range."""
 | 
					        """`!True` if the upper bound is included in the range."""
 | 
				
			||||||
        if self._bounds is None: return False
 | 
					        if self._bounds is None or self._upper is None:
 | 
				
			||||||
        if self._upper is None: return False
 | 
					            return False
 | 
				
			||||||
        return self._bounds[1] == ']'
 | 
					        return self._bounds[1] == ']'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __contains__(self, x):
 | 
					    def __contains__(self, x):
 | 
				
			||||||
        if self._bounds is None: return False
 | 
					        if self._bounds is None:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self._lower is not None:
 | 
					        if self._lower is not None:
 | 
				
			||||||
            if self._bounds[0] == '[':
 | 
					            if self._bounds[0] == '[':
 | 
				
			||||||
                if x < self._lower: return False
 | 
					                if x < self._lower:
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if x <= self._lower: return False
 | 
					                if x <= self._lower:
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self._upper is not None:
 | 
					        if self._upper is not None:
 | 
				
			||||||
            if self._bounds[1] == ']':
 | 
					            if self._bounds[1] == ']':
 | 
				
			||||||
                if x > self._upper: return False
 | 
					                if x > self._upper:
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if x >= self._upper: return False
 | 
					                if x >= self._upper:
 | 
				
			||||||
 | 
					                    return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return True
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -171,6 +180,17 @@ class Range(object):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return self.__gt__(other)
 | 
					            return self.__gt__(other)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __getstate__(self):
 | 
				
			||||||
 | 
					        return dict(
 | 
				
			||||||
 | 
					            (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):
 | 
					def register_range(pgrange, pyrange, conn_or_curs, globally=False):
 | 
				
			||||||
    """Create and register an adapter and the typecasters to convert between
 | 
					    """Create and register an adapter and the typecasters to convert between
 | 
				
			||||||
| 
						 | 
					@ -229,7 +249,7 @@ class RangeAdapter(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        r = self.adapted
 | 
					        r = self.adapted
 | 
				
			||||||
        if r.isempty:
 | 
					        if r.isempty:
 | 
				
			||||||
            return b("'empty'::" + self.name)
 | 
					            return b"'empty'::" + self.name.encode('utf8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if r.lower is not None:
 | 
					        if r.lower is not None:
 | 
				
			||||||
            a = adapt(r.lower)
 | 
					            a = adapt(r.lower)
 | 
				
			||||||
| 
						 | 
					@ -237,7 +257,7 @@ class RangeAdapter(object):
 | 
				
			||||||
                a.prepare(self._conn)
 | 
					                a.prepare(self._conn)
 | 
				
			||||||
            lower = a.getquoted()
 | 
					            lower = a.getquoted()
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            lower = b('NULL')
 | 
					            lower = b'NULL'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if r.upper is not None:
 | 
					        if r.upper is not None:
 | 
				
			||||||
            a = adapt(r.upper)
 | 
					            a = adapt(r.upper)
 | 
				
			||||||
| 
						 | 
					@ -245,10 +265,10 @@ class RangeAdapter(object):
 | 
				
			||||||
                a.prepare(self._conn)
 | 
					                a.prepare(self._conn)
 | 
				
			||||||
            upper = a.getquoted()
 | 
					            upper = a.getquoted()
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            upper = b('NULL')
 | 
					            upper = b'NULL'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return b(self.name + '(') + lower + b(', ') + upper \
 | 
					        return self.name.encode('utf8') + b'(' + lower + b', ' + upper \
 | 
				
			||||||
                + b(", '%s')" % r._bounds)
 | 
					            + b", '" + r._bounds.encode('utf8') + b"')"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RangeCaster(object):
 | 
					class RangeCaster(object):
 | 
				
			||||||
| 
						 | 
					@ -284,7 +304,8 @@ class RangeCaster(object):
 | 
				
			||||||
            self.adapter.name = pgrange
 | 
					            self.adapter.name = pgrange
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                if issubclass(pgrange, RangeAdapter) and pgrange is not RangeAdapter:
 | 
					                if issubclass(pgrange, RangeAdapter) \
 | 
				
			||||||
 | 
					                        and pgrange is not RangeAdapter:
 | 
				
			||||||
                    self.adapter = pgrange
 | 
					                    self.adapter = pgrange
 | 
				
			||||||
            except TypeError:
 | 
					            except TypeError:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
| 
						 | 
					@ -425,14 +446,17 @@ class NumericRange(Range):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DateRange(Range):
 | 
					class DateRange(Range):
 | 
				
			||||||
    """Represents :sql:`daterange` values."""
 | 
					    """Represents :sql:`daterange` values."""
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DateTimeRange(Range):
 | 
					class DateTimeRange(Range):
 | 
				
			||||||
    """Represents :sql:`tsrange` values."""
 | 
					    """Represents :sql:`tsrange` values."""
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DateTimeTZRange(Range):
 | 
					class DateTimeTZRange(Range):
 | 
				
			||||||
    """Represents :sql:`tstzrange` values."""
 | 
					    """Represents :sql:`tstzrange` values."""
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
| 
						 | 
					@ -448,7 +472,7 @@ class NumberRangeAdapter(RangeAdapter):
 | 
				
			||||||
    def getquoted(self):
 | 
					    def getquoted(self):
 | 
				
			||||||
        r = self.adapted
 | 
					        r = self.adapted
 | 
				
			||||||
        if r.isempty:
 | 
					        if r.isempty:
 | 
				
			||||||
            return b("'empty'")
 | 
					            return b"'empty'"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not r.lower_inf:
 | 
					        if not r.lower_inf:
 | 
				
			||||||
            # not exactly: we are relying that none of these object is really
 | 
					            # not exactly: we are relying that none of these object is really
 | 
				
			||||||
| 
						 | 
					@ -497,5 +521,3 @@ tsrange_caster._register()
 | 
				
			||||||
tstzrange_caster = RangeCaster('tstzrange', DateTimeTZRange,
 | 
					tstzrange_caster = RangeCaster('tstzrange', DateTimeTZRange,
 | 
				
			||||||
    oid=3910, subtype_oid=1184, array_oid=3911)
 | 
					    oid=3910, subtype_oid=1184, array_oid=3911)
 | 
				
			||||||
tstzrange_caster._register()
 | 
					tstzrange_caster._register()
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ This module contains symbolic names for all PostgreSQL error codes.
 | 
				
			||||||
#   http://www.postgresql.org/docs/current/static/errcodes-appendix.html
 | 
					#   http://www.postgresql.org/docs/current/static/errcodes-appendix.html
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def lookup(code, _cache={}):
 | 
					def lookup(code, _cache={}):
 | 
				
			||||||
    """Lookup an error code or class code and return its symbolic name.
 | 
					    """Lookup an error code or class code and return its symbolic name.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,11 +39,17 @@ def lookup(code, _cache={}):
 | 
				
			||||||
        return _cache[code]
 | 
					        return _cache[code]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Generate the lookup map at first usage.
 | 
					    # Generate the lookup map at first usage.
 | 
				
			||||||
 | 
					    tmp = {}
 | 
				
			||||||
    for k, v in globals().iteritems():
 | 
					    for k, v in globals().iteritems():
 | 
				
			||||||
        if isinstance(v, str) and len(v) in (2, 5):
 | 
					        if isinstance(v, str) and len(v) in (2, 5):
 | 
				
			||||||
            _cache[v] = k
 | 
					            tmp[v] = k
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return lookup(code)
 | 
					    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.
 | 
					# autogenerated data: do not edit below this point.
 | 
				
			||||||
| 
						 | 
					@ -193,6 +200,8 @@ INVALID_ESCAPE_SEQUENCE = '22025'
 | 
				
			||||||
STRING_DATA_LENGTH_MISMATCH = '22026'
 | 
					STRING_DATA_LENGTH_MISMATCH = '22026'
 | 
				
			||||||
TRIM_ERROR = '22027'
 | 
					TRIM_ERROR = '22027'
 | 
				
			||||||
ARRAY_SUBSCRIPT_ERROR = '2202E'
 | 
					ARRAY_SUBSCRIPT_ERROR = '2202E'
 | 
				
			||||||
 | 
					INVALID_TABLESAMPLE_REPEAT = '2202G'
 | 
				
			||||||
 | 
					INVALID_TABLESAMPLE_ARGUMENT = '2202H'
 | 
				
			||||||
FLOATING_POINT_EXCEPTION = '22P01'
 | 
					FLOATING_POINT_EXCEPTION = '22P01'
 | 
				
			||||||
INVALID_TEXT_REPRESENTATION = '22P02'
 | 
					INVALID_TEXT_REPRESENTATION = '22P02'
 | 
				
			||||||
INVALID_BINARY_REPRESENTATION = '22P03'
 | 
					INVALID_BINARY_REPRESENTATION = '22P03'
 | 
				
			||||||
| 
						 | 
					@ -265,6 +274,7 @@ INVALID_SQLSTATE_RETURNED = '39001'
 | 
				
			||||||
NULL_VALUE_NOT_ALLOWED = '39004'
 | 
					NULL_VALUE_NOT_ALLOWED = '39004'
 | 
				
			||||||
TRIGGER_PROTOCOL_VIOLATED = '39P01'
 | 
					TRIGGER_PROTOCOL_VIOLATED = '39P01'
 | 
				
			||||||
SRF_PROTOCOL_VIOLATED = '39P02'
 | 
					SRF_PROTOCOL_VIOLATED = '39P02'
 | 
				
			||||||
 | 
					EVENT_TRIGGER_PROTOCOL_VIOLATED = '39P03'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Class 3B - Savepoint Exception
 | 
					# Class 3B - Savepoint Exception
 | 
				
			||||||
SAVEPOINT_EXCEPTION = '3B000'
 | 
					SAVEPOINT_EXCEPTION = '3B000'
 | 
				
			||||||
| 
						 | 
					@ -402,6 +412,7 @@ PLPGSQL_ERROR = 'P0000'
 | 
				
			||||||
RAISE_EXCEPTION = 'P0001'
 | 
					RAISE_EXCEPTION = 'P0001'
 | 
				
			||||||
NO_DATA_FOUND = 'P0002'
 | 
					NO_DATA_FOUND = 'P0002'
 | 
				
			||||||
TOO_MANY_ROWS = 'P0003'
 | 
					TOO_MANY_ROWS = 'P0003'
 | 
				
			||||||
 | 
					ASSERT_FAILURE = 'P0004'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Class XX - Internal Error
 | 
					# Class XX - Internal Error
 | 
				
			||||||
INTERNAL_ERROR = 'XX000'
 | 
					INTERNAL_ERROR = 'XX000'
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,40 +32,39 @@ This module holds all the extensions to the DBAPI-2.0 provided by psycopg.
 | 
				
			||||||
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 | 
					# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 | 
				
			||||||
# License for more details.
 | 
					# License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import UNICODE, INTEGER, LONGINTEGER, BOOLEAN, FLOAT
 | 
					import re as _re
 | 
				
			||||||
from psycopg2._psycopg import TIME, DATE, INTERVAL, DECIMAL
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import BINARYARRAY, BOOLEANARRAY, DATEARRAY, DATETIMEARRAY
 | 
					from psycopg2._psycopg import (                             # noqa
 | 
				
			||||||
from psycopg2._psycopg import DECIMALARRAY, FLOATARRAY, INTEGERARRAY, INTERVALARRAY
 | 
					    BINARYARRAY, BOOLEAN, BOOLEANARRAY, DATE, DATEARRAY, DATETIMEARRAY,
 | 
				
			||||||
from psycopg2._psycopg import LONGINTEGERARRAY, ROWIDARRAY, STRINGARRAY, TIMEARRAY
 | 
					    DECIMAL, DECIMALARRAY, FLOAT, FLOATARRAY, INTEGER, INTEGERARRAY,
 | 
				
			||||||
from psycopg2._psycopg import UNICODEARRAY
 | 
					    INTERVAL, INTERVALARRAY, LONGINTEGER, LONGINTEGERARRAY, ROWIDARRAY,
 | 
				
			||||||
 | 
					    STRINGARRAY, TIME, TIMEARRAY, UNICODE, UNICODEARRAY,
 | 
				
			||||||
 | 
					    AsIs, Binary, Boolean, Float, Int, QuotedString, )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import Binary, Boolean, Int, Float, QuotedString, AsIs
 | 
					 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from psycopg2._psycopg import MXDATE, MXDATETIME, MXINTERVAL, MXTIME
 | 
					    from psycopg2._psycopg import (                         # noqa
 | 
				
			||||||
    from psycopg2._psycopg import MXDATEARRAY, MXDATETIMEARRAY, MXINTERVALARRAY, MXTIMEARRAY
 | 
					        MXDATE, MXDATETIME, MXINTERVAL, MXTIME,
 | 
				
			||||||
    from psycopg2._psycopg import DateFromMx, TimeFromMx, TimestampFromMx
 | 
					        MXDATEARRAY, MXDATETIMEARRAY, MXINTERVALARRAY, MXTIMEARRAY,
 | 
				
			||||||
    from psycopg2._psycopg import IntervalFromMx
 | 
					        DateFromMx, TimeFromMx, TimestampFromMx, IntervalFromMx, )
 | 
				
			||||||
except ImportError:
 | 
					except ImportError:
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from psycopg2._psycopg import PYDATE, PYDATETIME, PYINTERVAL, PYTIME
 | 
					    from psycopg2._psycopg import (                         # noqa
 | 
				
			||||||
    from psycopg2._psycopg import PYDATEARRAY, PYDATETIMEARRAY, PYINTERVALARRAY, PYTIMEARRAY
 | 
					        PYDATE, PYDATETIME, PYINTERVAL, PYTIME,
 | 
				
			||||||
    from psycopg2._psycopg import DateFromPy, TimeFromPy, TimestampFromPy
 | 
					        PYDATEARRAY, PYDATETIMEARRAY, PYINTERVALARRAY, PYTIMEARRAY,
 | 
				
			||||||
    from psycopg2._psycopg import IntervalFromPy
 | 
					        DateFromPy, TimeFromPy, TimestampFromPy, IntervalFromPy, )
 | 
				
			||||||
except ImportError:
 | 
					except ImportError:
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid, libpq_version, parse_dsn, quote_ident
 | 
					from psycopg2._psycopg import (                             # noqa
 | 
				
			||||||
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
 | 
					    adapt, adapters, encodings, connection, cursor,
 | 
				
			||||||
from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics, Column
 | 
					    lobject, Xid, libpq_version, parse_dsn, quote_ident,
 | 
				
			||||||
 | 
					    string_types, binary_types, new_type, new_array_type, register_type,
 | 
				
			||||||
 | 
					    ISQLQuote, Notify, Diagnostics, Column,
 | 
				
			||||||
 | 
					    QueryCanceledError, TransactionRollbackError,
 | 
				
			||||||
 | 
					    set_wait_callback, get_wait_callback, )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
try:
 | 
					 | 
				
			||||||
    from psycopg2._psycopg import set_wait_callback, get_wait_callback
 | 
					 | 
				
			||||||
except ImportError:
 | 
					 | 
				
			||||||
    pass
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""Isolation level values."""
 | 
					"""Isolation level values."""
 | 
				
			||||||
ISOLATION_LEVEL_AUTOCOMMIT = 0
 | 
					ISOLATION_LEVEL_AUTOCOMMIT = 0
 | 
				
			||||||
| 
						 | 
					@ -74,6 +73,7 @@ ISOLATION_LEVEL_READ_COMMITTED      = 1
 | 
				
			||||||
ISOLATION_LEVEL_REPEATABLE_READ = 2
 | 
					ISOLATION_LEVEL_REPEATABLE_READ = 2
 | 
				
			||||||
ISOLATION_LEVEL_SERIALIZABLE = 3
 | 
					ISOLATION_LEVEL_SERIALIZABLE = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""psycopg connection status values."""
 | 
					"""psycopg connection status values."""
 | 
				
			||||||
STATUS_SETUP = 0
 | 
					STATUS_SETUP = 0
 | 
				
			||||||
STATUS_READY = 1
 | 
					STATUS_READY = 1
 | 
				
			||||||
| 
						 | 
					@ -85,12 +85,14 @@ STATUS_PREPARED = 5
 | 
				
			||||||
# This is a useful mnemonic to check if the connection is in a transaction
 | 
					# This is a useful mnemonic to check if the connection is in a transaction
 | 
				
			||||||
STATUS_IN_TRANSACTION = STATUS_BEGIN
 | 
					STATUS_IN_TRANSACTION = STATUS_BEGIN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""psycopg asynchronous connection polling values"""
 | 
					"""psycopg asynchronous connection polling values"""
 | 
				
			||||||
POLL_OK = 0
 | 
					POLL_OK = 0
 | 
				
			||||||
POLL_READ = 1
 | 
					POLL_READ = 1
 | 
				
			||||||
POLL_WRITE = 2
 | 
					POLL_WRITE = 2
 | 
				
			||||||
POLL_ERROR = 3
 | 
					POLL_ERROR = 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
"""Backend transaction status values."""
 | 
					"""Backend transaction status values."""
 | 
				
			||||||
TRANSACTION_STATUS_IDLE = 0
 | 
					TRANSACTION_STATUS_IDLE = 0
 | 
				
			||||||
TRANSACTION_STATUS_ACTIVE = 1
 | 
					TRANSACTION_STATUS_ACTIVE = 1
 | 
				
			||||||
| 
						 | 
					@ -98,15 +100,6 @@ TRANSACTION_STATUS_INTRANS = 2
 | 
				
			||||||
TRANSACTION_STATUS_INERROR = 3
 | 
					TRANSACTION_STATUS_INERROR = 3
 | 
				
			||||||
TRANSACTION_STATUS_UNKNOWN = 4
 | 
					TRANSACTION_STATUS_UNKNOWN = 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sys as _sys
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Return bytes from a string
 | 
					 | 
				
			||||||
if _sys.version_info[0] < 3:
 | 
					 | 
				
			||||||
    def b(s):
 | 
					 | 
				
			||||||
        return s
 | 
					 | 
				
			||||||
else:
 | 
					 | 
				
			||||||
    def b(s):
 | 
					 | 
				
			||||||
        return s.encode('utf8')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_adapter(typ, callable):
 | 
					def register_adapter(typ, callable):
 | 
				
			||||||
    """Register 'callable' as an ISQLQuote adapter for type 'typ'."""
 | 
					    """Register 'callable' as an ISQLQuote adapter for type 'typ'."""
 | 
				
			||||||
| 
						 | 
					@ -132,7 +125,7 @@ class SQL_IN(object):
 | 
				
			||||||
                if hasattr(obj, 'prepare'):
 | 
					                if hasattr(obj, 'prepare'):
 | 
				
			||||||
                    obj.prepare(self._conn)
 | 
					                    obj.prepare(self._conn)
 | 
				
			||||||
        qobjs = [o.getquoted() for o in pobjs]
 | 
					        qobjs = [o.getquoted() for o in pobjs]
 | 
				
			||||||
        return b('(') + b(', ').join(qobjs) + b(')')
 | 
					        return b'(' + b', '.join(qobjs) + b')'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return str(self.getquoted())
 | 
					        return str(self.getquoted())
 | 
				
			||||||
| 
						 | 
					@ -147,12 +140,59 @@ class NoneAdapter(object):
 | 
				
			||||||
    def __init__(self, obj):
 | 
					    def __init__(self, obj):
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getquoted(self, _null=b("NULL")):
 | 
					    def getquoted(self, _null=b"NULL"):
 | 
				
			||||||
        return _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')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if dsn is not None:
 | 
				
			||||||
 | 
					        tmp = parse_dsn(dsn)
 | 
				
			||||||
 | 
					        tmp.update(kwargs)
 | 
				
			||||||
 | 
					        kwargs = tmp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dsn = " ".join(["%s=%s" % (k, _param_escape(str(v)))
 | 
				
			||||||
 | 
					        for (k, v) in kwargs.iteritems()])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # 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
 | 
					# Create default json typecasters for PostgreSQL 9.2 oids
 | 
				
			||||||
from psycopg2._json import register_default_json, register_default_jsonb
 | 
					from psycopg2._json import register_default_json, register_default_jsonb    # noqa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    JSON, JSONARRAY = register_default_json()
 | 
					    JSON, JSONARRAY = register_default_json()
 | 
				
			||||||
| 
						 | 
					@ -164,7 +204,7 @@ del register_default_json, register_default_jsonb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Create default Range typecasters
 | 
					# Create default Range typecasters
 | 
				
			||||||
from psycopg2. _range import Range
 | 
					from psycopg2. _range import Range                              # noqa
 | 
				
			||||||
del Range
 | 
					del Range
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										213
									
								
								lib/extras.py
									
									
									
									
									
								
							
							
						
						
									
										213
									
								
								lib/extras.py
									
									
									
									
									
								
							| 
						 | 
					@ -39,8 +39,28 @@ import psycopg2
 | 
				
			||||||
from psycopg2 import extensions as _ext
 | 
					from psycopg2 import extensions as _ext
 | 
				
			||||||
from psycopg2.extensions import cursor as _cursor
 | 
					from psycopg2.extensions import cursor as _cursor
 | 
				
			||||||
from psycopg2.extensions import connection as _connection
 | 
					from psycopg2.extensions import connection as _connection
 | 
				
			||||||
from psycopg2.extensions import adapt as _A
 | 
					from psycopg2.extensions import adapt as _A, quote_ident
 | 
				
			||||||
from psycopg2.extensions import b
 | 
					
 | 
				
			||||||
 | 
					from psycopg2._psycopg import (                             # noqa
 | 
				
			||||||
 | 
					    REPLICATION_PHYSICAL, REPLICATION_LOGICAL,
 | 
				
			||||||
 | 
					    ReplicationConnection as _replicationConnection,
 | 
				
			||||||
 | 
					    ReplicationCursor as _replicationCursor,
 | 
				
			||||||
 | 
					    ReplicationMessage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# expose the json adaptation stuff into the module
 | 
				
			||||||
 | 
					from psycopg2._json import (                                # noqa
 | 
				
			||||||
 | 
					    json, Json, register_json, register_default_json, register_default_jsonb)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Expose range-related objects
 | 
				
			||||||
 | 
					from psycopg2._range import (                               # noqa
 | 
				
			||||||
 | 
					    Range, NumericRange, DateRange, DateTimeRange, DateTimeTZRange,
 | 
				
			||||||
 | 
					    register_range, RangeAdapter, RangeCaster)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Expose ipaddress-related objects
 | 
				
			||||||
 | 
					from psycopg2._ipaddress import register_ipaddress          # noqa
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DictCursorBase(_cursor):
 | 
					class DictCursorBase(_cursor):
 | 
				
			||||||
| 
						 | 
					@ -106,6 +126,7 @@ class DictConnection(_connection):
 | 
				
			||||||
        kwargs.setdefault('cursor_factory', DictCursor)
 | 
					        kwargs.setdefault('cursor_factory', DictCursor)
 | 
				
			||||||
        return super(DictConnection, self).cursor(*args, **kwargs)
 | 
					        return super(DictConnection, self).cursor(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DictCursor(DictCursorBase):
 | 
					class DictCursor(DictCursorBase):
 | 
				
			||||||
    """A cursor that keeps a list of column name -> index mappings."""
 | 
					    """A cursor that keeps a list of column name -> index mappings."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,6 +151,7 @@ class DictCursor(DictCursorBase):
 | 
				
			||||||
                self.index[self.description[i][0]] = i
 | 
					                self.index[self.description[i][0]] = i
 | 
				
			||||||
            self._query_executed = 0
 | 
					            self._query_executed = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DictRow(list):
 | 
					class DictRow(list):
 | 
				
			||||||
    """A row object that allow by-column-name access to data."""
 | 
					    """A row object that allow by-column-name access to data."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -192,10 +214,10 @@ class DictRow(list):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # drop the crusty Py2 methods
 | 
					    # drop the crusty Py2 methods
 | 
				
			||||||
    if _sys.version_info[0] > 2:
 | 
					    if _sys.version_info[0] > 2:
 | 
				
			||||||
        items = iteritems; del iteritems
 | 
					        items = iteritems               # noqa
 | 
				
			||||||
        keys = iterkeys; del iterkeys
 | 
					        keys = iterkeys                 # noqa
 | 
				
			||||||
        values = itervalues; del itervalues
 | 
					        values = itervalues             # noqa
 | 
				
			||||||
        del has_key
 | 
					        del iteritems, iterkeys, itervalues, has_key
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RealDictConnection(_connection):
 | 
					class RealDictConnection(_connection):
 | 
				
			||||||
| 
						 | 
					@ -204,6 +226,7 @@ class RealDictConnection(_connection):
 | 
				
			||||||
        kwargs.setdefault('cursor_factory', RealDictCursor)
 | 
					        kwargs.setdefault('cursor_factory', RealDictCursor)
 | 
				
			||||||
        return super(RealDictConnection, self).cursor(*args, **kwargs)
 | 
					        return super(RealDictConnection, self).cursor(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RealDictCursor(DictCursorBase):
 | 
					class RealDictCursor(DictCursorBase):
 | 
				
			||||||
    """A cursor that uses a real dict as the base type for rows.
 | 
					    """A cursor that uses a real dict as the base type for rows.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -233,6 +256,7 @@ class RealDictCursor(DictCursorBase):
 | 
				
			||||||
                self.column_mapping.append(self.description[i][0])
 | 
					                self.column_mapping.append(self.description[i][0])
 | 
				
			||||||
            self._query_executed = 0
 | 
					            self._query_executed = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RealDictRow(dict):
 | 
					class RealDictRow(dict):
 | 
				
			||||||
    """A `!dict` subclass representing a data record."""
 | 
					    """A `!dict` subclass representing a data record."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -265,6 +289,7 @@ class NamedTupleConnection(_connection):
 | 
				
			||||||
        kwargs.setdefault('cursor_factory', NamedTupleCursor)
 | 
					        kwargs.setdefault('cursor_factory', NamedTupleCursor)
 | 
				
			||||||
        return super(NamedTupleConnection, self).cursor(*args, **kwargs)
 | 
					        return super(NamedTupleConnection, self).cursor(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NamedTupleCursor(_cursor):
 | 
					class NamedTupleCursor(_cursor):
 | 
				
			||||||
    """A cursor that generates results as `~collections.namedtuple`.
 | 
					    """A cursor that generates results as `~collections.namedtuple`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -369,11 +394,13 @@ class LoggingConnection(_connection):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _logtofile(self, msg, curs):
 | 
					    def _logtofile(self, msg, curs):
 | 
				
			||||||
        msg = self.filter(msg, curs)
 | 
					        msg = self.filter(msg, curs)
 | 
				
			||||||
        if msg: self._logobj.write(msg + _os.linesep)
 | 
					        if msg:
 | 
				
			||||||
 | 
					            self._logobj.write(msg + _os.linesep)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _logtologger(self, msg, curs):
 | 
					    def _logtologger(self, msg, curs):
 | 
				
			||||||
        msg = self.filter(msg, curs)
 | 
					        msg = self.filter(msg, curs)
 | 
				
			||||||
        if msg: self._logobj.debug(msg)
 | 
					        if msg:
 | 
				
			||||||
 | 
					            self._logobj.debug(msg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _check(self):
 | 
					    def _check(self):
 | 
				
			||||||
        if not hasattr(self, '_logobj'):
 | 
					        if not hasattr(self, '_logobj'):
 | 
				
			||||||
| 
						 | 
					@ -385,6 +412,7 @@ class LoggingConnection(_connection):
 | 
				
			||||||
        kwargs.setdefault('cursor_factory', LoggingCursor)
 | 
					        kwargs.setdefault('cursor_factory', LoggingCursor)
 | 
				
			||||||
        return super(LoggingConnection, self).cursor(*args, **kwargs)
 | 
					        return super(LoggingConnection, self).cursor(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LoggingCursor(_cursor):
 | 
					class LoggingCursor(_cursor):
 | 
				
			||||||
    """A cursor that logs queries using its connection logging facilities."""
 | 
					    """A cursor that logs queries using its connection logging facilities."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -425,6 +453,7 @@ class MinTimeLoggingConnection(LoggingConnection):
 | 
				
			||||||
        kwargs.setdefault('cursor_factory', MinTimeLoggingCursor)
 | 
					        kwargs.setdefault('cursor_factory', MinTimeLoggingCursor)
 | 
				
			||||||
        return LoggingConnection.cursor(self, *args, **kwargs)
 | 
					        return LoggingConnection.cursor(self, *args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MinTimeLoggingCursor(LoggingCursor):
 | 
					class MinTimeLoggingCursor(LoggingCursor):
 | 
				
			||||||
    """The cursor sub-class companion to `MinTimeLoggingConnection`."""
 | 
					    """The cursor sub-class companion to `MinTimeLoggingConnection`."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -437,6 +466,133 @@ class MinTimeLoggingCursor(LoggingCursor):
 | 
				
			||||||
        return LoggingCursor.callproc(self, procname, vars)
 | 
					        return LoggingCursor.callproc(self, procname, vars)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class LogicalReplicationConnection(_replicationConnection):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        kwargs['replication_type'] = REPLICATION_LOGICAL
 | 
				
			||||||
 | 
					        super(LogicalReplicationConnection, self).__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PhysicalReplicationConnection(_replicationConnection):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        kwargs['replication_type'] = REPLICATION_PHYSICAL
 | 
				
			||||||
 | 
					        super(PhysicalReplicationConnection, self).__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class StopReplication(Exception):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Exception used to break out of the endless loop in
 | 
				
			||||||
 | 
					    `~ReplicationCursor.consume_stream()`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Subclass of `~exceptions.Exception`.  Intentionally *not* inherited from
 | 
				
			||||||
 | 
					    `~psycopg2.Error` as occurrence of this exception does not indicate an
 | 
				
			||||||
 | 
					    error.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReplicationCursor(_replicationCursor):
 | 
				
			||||||
 | 
					    """A cursor used for communication on replication connections."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_replication_slot(self, slot_name, slot_type=None, output_plugin=None):
 | 
				
			||||||
 | 
					        """Create streaming replication slot."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        command = "CREATE_REPLICATION_SLOT %s " % quote_ident(slot_name, self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if slot_type is None:
 | 
				
			||||||
 | 
					            slot_type = self.connection.replication_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if slot_type == REPLICATION_LOGICAL:
 | 
				
			||||||
 | 
					            if output_plugin is None:
 | 
				
			||||||
 | 
					                raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                    "output plugin name is required to create "
 | 
				
			||||||
 | 
					                    "logical replication slot")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            command += "LOGICAL %s" % quote_ident(output_plugin, self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        elif slot_type == REPLICATION_PHYSICAL:
 | 
				
			||||||
 | 
					            if output_plugin is not None:
 | 
				
			||||||
 | 
					                raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                    "cannot specify output plugin name when creating "
 | 
				
			||||||
 | 
					                    "physical replication slot")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            command += "PHYSICAL"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                "unrecognized replication type: %s" % repr(slot_type))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.execute(command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def drop_replication_slot(self, slot_name):
 | 
				
			||||||
 | 
					        """Drop streaming replication slot."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        command = "DROP_REPLICATION_SLOT %s" % quote_ident(slot_name, self)
 | 
				
			||||||
 | 
					        self.execute(command)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def start_replication(self, slot_name=None, slot_type=None, start_lsn=0,
 | 
				
			||||||
 | 
					                          timeline=0, options=None, decode=False):
 | 
				
			||||||
 | 
					        """Start replication stream."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        command = "START_REPLICATION "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if slot_type is None:
 | 
				
			||||||
 | 
					            slot_type = self.connection.replication_type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if slot_type == REPLICATION_LOGICAL:
 | 
				
			||||||
 | 
					            if slot_name:
 | 
				
			||||||
 | 
					                command += "SLOT %s " % quote_ident(slot_name, self)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                    "slot name is required for logical replication")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            command += "LOGICAL "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        elif slot_type == REPLICATION_PHYSICAL:
 | 
				
			||||||
 | 
					            if slot_name:
 | 
				
			||||||
 | 
					                command += "SLOT %s " % quote_ident(slot_name, self)
 | 
				
			||||||
 | 
					            # don't add "PHYSICAL", before 9.4 it was just START_REPLICATION XXX/XXX
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                "unrecognized replication type: %s" % repr(slot_type))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if type(start_lsn) is str:
 | 
				
			||||||
 | 
					            lsn = start_lsn.split('/')
 | 
				
			||||||
 | 
					            lsn = "%X/%08X" % (int(lsn[0], 16), int(lsn[1], 16))
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            lsn = "%X/%08X" % ((start_lsn >> 32) & 0xFFFFFFFF,
 | 
				
			||||||
 | 
					                               start_lsn & 0xFFFFFFFF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        command += lsn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if timeline != 0:
 | 
				
			||||||
 | 
					            if slot_type == REPLICATION_LOGICAL:
 | 
				
			||||||
 | 
					                raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                    "cannot specify timeline for logical replication")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            command += " TIMELINE %d" % timeline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if options:
 | 
				
			||||||
 | 
					            if slot_type == REPLICATION_PHYSICAL:
 | 
				
			||||||
 | 
					                raise psycopg2.ProgrammingError(
 | 
				
			||||||
 | 
					                    "cannot specify output plugin options for physical replication")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            command += " ("
 | 
				
			||||||
 | 
					            for k, v in options.iteritems():
 | 
				
			||||||
 | 
					                if not command.endswith('('):
 | 
				
			||||||
 | 
					                    command += ", "
 | 
				
			||||||
 | 
					                command += "%s %s" % (quote_ident(k, self), _A(str(v)))
 | 
				
			||||||
 | 
					            command += ")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.start_replication_expert(command, decode=decode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # allows replication cursors to be used in select.select() directly
 | 
				
			||||||
 | 
					    def fileno(self):
 | 
				
			||||||
 | 
					        return self.connection.fileno()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# a dbtype and adapter for Python UUID type
 | 
					# a dbtype and adapter for Python UUID type
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UUID_adapter(object):
 | 
					class UUID_adapter(object):
 | 
				
			||||||
| 
						 | 
					@ -454,11 +610,12 @@ class UUID_adapter(object):
 | 
				
			||||||
            return self
 | 
					            return self
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getquoted(self):
 | 
					    def getquoted(self):
 | 
				
			||||||
        return b("'%s'::uuid" % self._uuid)
 | 
					        return ("'%s'::uuid" % self._uuid).encode('utf8')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return "'%s'::uuid" % self._uuid
 | 
					        return "'%s'::uuid" % self._uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_uuid(oids=None, conn_or_curs=None):
 | 
					def register_uuid(oids=None, conn_or_curs=None):
 | 
				
			||||||
    """Create the UUID type and an uuid.UUID adapter.
 | 
					    """Create the UUID type and an uuid.UUID adapter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -514,7 +671,7 @@ class Inet(object):
 | 
				
			||||||
        obj = _A(self.addr)
 | 
					        obj = _A(self.addr)
 | 
				
			||||||
        if hasattr(obj, 'prepare'):
 | 
					        if hasattr(obj, 'prepare'):
 | 
				
			||||||
            obj.prepare(self._conn)
 | 
					            obj.prepare(self._conn)
 | 
				
			||||||
        return obj.getquoted() + b("::inet")
 | 
					        return obj.getquoted() + b"::inet"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __conform__(self, proto):
 | 
					    def __conform__(self, proto):
 | 
				
			||||||
        if proto is _ext.ISQLQuote:
 | 
					        if proto is _ext.ISQLQuote:
 | 
				
			||||||
| 
						 | 
					@ -523,6 +680,7 @@ class Inet(object):
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return str(self.addr)
 | 
					        return str(self.addr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_inet(oid=None, conn_or_curs=None):
 | 
					def register_inet(oid=None, conn_or_curs=None):
 | 
				
			||||||
    """Create the INET type and an Inet adapter.
 | 
					    """Create the INET type and an Inet adapter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -532,6 +690,11 @@ def register_inet(oid=None, conn_or_curs=None):
 | 
				
			||||||
    :param conn_or_curs: where to register the typecaster. If not specified,
 | 
					    :param conn_or_curs: where to register the typecaster. If not specified,
 | 
				
			||||||
        register it globally.
 | 
					        register it globally.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    import warnings
 | 
				
			||||||
 | 
					    warnings.warn(
 | 
				
			||||||
 | 
					        "the inet adapter is deprecated, it's not very useful",
 | 
				
			||||||
 | 
					        DeprecationWarning)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not oid:
 | 
					    if not oid:
 | 
				
			||||||
        oid1 = 869
 | 
					        oid1 = 869
 | 
				
			||||||
        oid2 = 1041
 | 
					        oid2 = 1041
 | 
				
			||||||
| 
						 | 
					@ -621,7 +784,7 @@ class HstoreAdapter(object):
 | 
				
			||||||
    def _getquoted_8(self):
 | 
					    def _getquoted_8(self):
 | 
				
			||||||
        """Use the operators available in PG pre-9.0."""
 | 
					        """Use the operators available in PG pre-9.0."""
 | 
				
			||||||
        if not self.wrapped:
 | 
					        if not self.wrapped:
 | 
				
			||||||
            return b("''::hstore")
 | 
					            return b"''::hstore"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        adapt = _ext.adapt
 | 
					        adapt = _ext.adapt
 | 
				
			||||||
        rv = []
 | 
					        rv = []
 | 
				
			||||||
| 
						 | 
					@ -635,23 +798,23 @@ class HstoreAdapter(object):
 | 
				
			||||||
                v.prepare(self.conn)
 | 
					                v.prepare(self.conn)
 | 
				
			||||||
                v = v.getquoted()
 | 
					                v = v.getquoted()
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                v = b('NULL')
 | 
					                v = b'NULL'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # XXX this b'ing is painfully inefficient!
 | 
					            # XXX this b'ing is painfully inefficient!
 | 
				
			||||||
            rv.append(b("(") + k + b(" => ") + v + b(")"))
 | 
					            rv.append(b"(" + k + b" => " + v + b")")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return b("(") + b('||').join(rv) + b(")")
 | 
					        return b"(" + b'||'.join(rv) + b")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _getquoted_9(self):
 | 
					    def _getquoted_9(self):
 | 
				
			||||||
        """Use the hstore(text[], text[]) function."""
 | 
					        """Use the hstore(text[], text[]) function."""
 | 
				
			||||||
        if not self.wrapped:
 | 
					        if not self.wrapped:
 | 
				
			||||||
            return b("''::hstore")
 | 
					            return b"''::hstore"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        k = _ext.adapt(self.wrapped.keys())
 | 
					        k = _ext.adapt(self.wrapped.keys())
 | 
				
			||||||
        k.prepare(self.conn)
 | 
					        k.prepare(self.conn)
 | 
				
			||||||
        v = _ext.adapt(self.wrapped.values())
 | 
					        v = _ext.adapt(self.wrapped.values())
 | 
				
			||||||
        v.prepare(self.conn)
 | 
					        v.prepare(self.conn)
 | 
				
			||||||
        return b("hstore(") + k.getquoted() + b(", ") + v.getquoted() + b(")")
 | 
					        return b"hstore(" + k.getquoted() + b", " + v.getquoted() + b")"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    getquoted = _getquoted_9
 | 
					    getquoted = _getquoted_9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -742,9 +905,10 @@ WHERE typname = 'hstore';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return tuple(rv0), tuple(rv1)
 | 
					        return tuple(rv0), tuple(rv1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_hstore(conn_or_curs, globally=False, unicode=False,
 | 
					def register_hstore(conn_or_curs, globally=False, unicode=False,
 | 
				
			||||||
                    oid=None, array_oid=None):
 | 
					                    oid=None, array_oid=None):
 | 
				
			||||||
    """Register adapter and typecaster for `!dict`\-\ |hstore| conversions.
 | 
					    r"""Register adapter and typecaster for `!dict`\-\ |hstore| conversions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    :param conn_or_curs: a connection or cursor: the typecaster will be
 | 
					    :param conn_or_curs: a connection or cursor: the typecaster will be
 | 
				
			||||||
        registered only on this object unless *globally* is set to `!True`
 | 
					        registered only on this object unless *globally* is set to `!True`
 | 
				
			||||||
| 
						 | 
					@ -942,6 +1106,7 @@ ORDER BY attnum;
 | 
				
			||||||
        return self(tname, type_oid, type_attrs,
 | 
					        return self(tname, type_oid, type_attrs,
 | 
				
			||||||
            array_oid=array_oid, schema=schema)
 | 
					            array_oid=array_oid, schema=schema)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def register_composite(name, conn_or_curs, globally=False, factory=None):
 | 
					def register_composite(name, conn_or_curs, globally=False, factory=None):
 | 
				
			||||||
    """Register a typecaster to convert a composite type into a tuple.
 | 
					    """Register a typecaster to convert a composite type into a tuple.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -964,17 +1129,7 @@ def register_composite(name, conn_or_curs, globally=False, factory=None):
 | 
				
			||||||
    _ext.register_type(caster.typecaster, not globally and conn_or_curs or None)
 | 
					    _ext.register_type(caster.typecaster, not globally and conn_or_curs or None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if caster.array_typecaster is not None:
 | 
					    if caster.array_typecaster is not None:
 | 
				
			||||||
        _ext.register_type(caster.array_typecaster, not globally and conn_or_curs or None)
 | 
					        _ext.register_type(
 | 
				
			||||||
 | 
					            caster.array_typecaster, not globally and conn_or_curs or None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return caster
 | 
					    return caster
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# expose the json adaptation stuff into the module
 | 
					 | 
				
			||||||
from psycopg2._json import json, Json, register_json
 | 
					 | 
				
			||||||
from psycopg2._json import register_default_json, register_default_jsonb
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Expose range-related objects
 | 
					 | 
				
			||||||
from psycopg2._range import Range, NumericRange
 | 
					 | 
				
			||||||
from psycopg2._range import DateRange, DateTimeRange, DateTimeTZRange
 | 
					 | 
				
			||||||
from psycopg2._range import register_range, RangeAdapter, RangeCaster
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										18
									
								
								lib/pool.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								lib/pool.py
									
									
									
									
									
								
							| 
						 | 
					@ -74,8 +74,10 @@ class AbstractConnectionPool(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _getconn(self, key=None):
 | 
					    def _getconn(self, key=None):
 | 
				
			||||||
        """Get a free connection and assign it to 'key' if not None."""
 | 
					        """Get a free connection and assign it to 'key' if not None."""
 | 
				
			||||||
        if self.closed: raise PoolError("connection pool is closed")
 | 
					        if self.closed:
 | 
				
			||||||
        if key is None: key = self._getkey()
 | 
					            raise PoolError("connection pool is closed")
 | 
				
			||||||
 | 
					        if key is None:
 | 
				
			||||||
 | 
					            key = self._getkey()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if key in self._used:
 | 
					        if key in self._used:
 | 
				
			||||||
            return self._used[key]
 | 
					            return self._used[key]
 | 
				
			||||||
| 
						 | 
					@ -91,8 +93,10 @@ class AbstractConnectionPool(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _putconn(self, conn, key=None, close=False):
 | 
					    def _putconn(self, conn, key=None, close=False):
 | 
				
			||||||
        """Put away a connection."""
 | 
					        """Put away a connection."""
 | 
				
			||||||
        if self.closed: raise PoolError("connection pool is closed")
 | 
					        if self.closed:
 | 
				
			||||||
        if key is None: key = self._rused.get(id(conn))
 | 
					            raise PoolError("connection pool is closed")
 | 
				
			||||||
 | 
					        if key is None:
 | 
				
			||||||
 | 
					            key = self._rused.get(id(conn))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if not key:
 | 
					        if not key:
 | 
				
			||||||
            raise PoolError("trying to put unkeyed connection")
 | 
					            raise PoolError("trying to put unkeyed connection")
 | 
				
			||||||
| 
						 | 
					@ -129,7 +133,8 @@ class AbstractConnectionPool(object):
 | 
				
			||||||
        an already closed connection. If you call .closeall() make sure
 | 
					        an already closed connection. If you call .closeall() make sure
 | 
				
			||||||
        your code can deal with it.
 | 
					        your code can deal with it.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self.closed: raise PoolError("connection pool is closed")
 | 
					        if self.closed:
 | 
				
			||||||
 | 
					            raise PoolError("connection pool is closed")
 | 
				
			||||||
        for conn in self._pool + list(self._used.values()):
 | 
					        for conn in self._pool + list(self._used.values()):
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                conn.close()
 | 
					                conn.close()
 | 
				
			||||||
| 
						 | 
					@ -221,7 +226,8 @@ class PersistentConnectionPool(AbstractConnectionPool):
 | 
				
			||||||
        key = self.__thread.get_ident()
 | 
					        key = self.__thread.get_ident()
 | 
				
			||||||
        self._lock.acquire()
 | 
					        self._lock.acquire()
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if not conn: conn = self._used[key]
 | 
					            if not conn:
 | 
				
			||||||
 | 
					                conn = self._used[key]
 | 
				
			||||||
            self._putconn(conn, key, close)
 | 
					            self._putconn(conn, key, close)
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            self._lock.release()
 | 
					            self._lock.release()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,14 +28,15 @@ old code while porting to psycopg 2. Import it as follows::
 | 
				
			||||||
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 | 
					# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 | 
				
			||||||
# License for more details.
 | 
					# License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import _psycopg as _2psycopg
 | 
					import psycopg2._psycopg as _2psycopg   # noqa
 | 
				
			||||||
from psycopg2.extensions import cursor as _2cursor
 | 
					from psycopg2.extensions import cursor as _2cursor
 | 
				
			||||||
from psycopg2.extensions import connection as _2connection
 | 
					from psycopg2.extensions import connection as _2connection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from psycopg2 import *
 | 
					from psycopg2 import *      # noqa
 | 
				
			||||||
import psycopg2.extensions as _ext
 | 
					import psycopg2.extensions as _ext
 | 
				
			||||||
_2connect = connect
 | 
					_2connect = connect
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def connect(*args, **kwargs):
 | 
					def connect(*args, **kwargs):
 | 
				
			||||||
    """connect(dsn, ...) -> new psycopg 1.1.x compatible connection object"""
 | 
					    """connect(dsn, ...) -> new psycopg 1.1.x compatible connection object"""
 | 
				
			||||||
    kwargs['connection_factory'] = connection
 | 
					    kwargs['connection_factory'] = connection
 | 
				
			||||||
| 
						 | 
					@ -43,6 +44,7 @@ def connect(*args, **kwargs):
 | 
				
			||||||
    conn.set_isolation_level(_ext.ISOLATION_LEVEL_READ_COMMITTED)
 | 
					    conn.set_isolation_level(_ext.ISOLATION_LEVEL_READ_COMMITTED)
 | 
				
			||||||
    return conn
 | 
					    return conn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class connection(_2connection):
 | 
					class connection(_2connection):
 | 
				
			||||||
    """psycopg 1.1.x connection."""
 | 
					    """psycopg 1.1.x connection."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,4 +94,3 @@ class cursor(_2cursor):
 | 
				
			||||||
        for row in rows:
 | 
					        for row in rows:
 | 
				
			||||||
            res.append(self.__build_dict(row))
 | 
					            res.append(self.__build_dict(row))
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,7 @@ import time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ZERO = datetime.timedelta(0)
 | 
					ZERO = datetime.timedelta(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FixedOffsetTimezone(datetime.tzinfo):
 | 
					class FixedOffsetTimezone(datetime.tzinfo):
 | 
				
			||||||
    """Fixed offset in minutes east from UTC.
 | 
					    """Fixed offset in minutes east from UTC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -102,6 +103,7 @@ else:
 | 
				
			||||||
    DSTOFFSET = STDOFFSET
 | 
					    DSTOFFSET = STDOFFSET
 | 
				
			||||||
DSTDIFF = DSTOFFSET - STDOFFSET
 | 
					DSTDIFF = DSTOFFSET - STDOFFSET
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LocalTimezone(datetime.tzinfo):
 | 
					class LocalTimezone(datetime.tzinfo):
 | 
				
			||||||
    """Platform idea of local timezone.
 | 
					    """Platform idea of local timezone.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,11 +39,9 @@ static unsigned char *
 | 
				
			||||||
binary_escape(unsigned char *from, size_t from_length,
 | 
					binary_escape(unsigned char *from, size_t from_length,
 | 
				
			||||||
               size_t *to_length, PGconn *conn)
 | 
					               size_t *to_length, PGconn *conn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#if PG_VERSION_NUM >= 80104
 | 
					 | 
				
			||||||
    if (conn)
 | 
					    if (conn)
 | 
				
			||||||
        return PQescapeByteaConn(conn, from, from_length, to_length);
 | 
					        return PQescapeByteaConn(conn, from, from_length, to_length);
 | 
				
			||||||
    else
 | 
					    else
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        return PQescapeBytea(from, from_length, to_length);
 | 
					        return PQescapeBytea(from, from_length, to_length);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -451,7 +451,7 @@ psyco_TimestampFromTicks(PyObject *self, PyObject *args)
 | 
				
			||||||
        tz);
 | 
					        tz);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit:
 | 
					exit:
 | 
				
			||||||
    Py_DECREF(tz);
 | 
					    Py_XDECREF(tz);
 | 
				
			||||||
    Py_XDECREF(m);
 | 
					    Py_XDECREF(m);
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,44 +36,56 @@ static const char *default_encoding = "latin1";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* qstring_quote - do the quote process on plain and unicode strings */
 | 
					/* qstring_quote - do the quote process on plain and unicode strings */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char *
 | 
				
			||||||
 | 
					_qstring_get_encoding(qstringObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /* if the wrapped object is an unicode object we can encode it to match
 | 
				
			||||||
 | 
					       conn->encoding but if the encoding is not specified we don't know what
 | 
				
			||||||
 | 
					       to do and we raise an exception */
 | 
				
			||||||
 | 
					    if (self->conn) {
 | 
				
			||||||
 | 
					        return self->conn->codec;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        return self->encoding ? self->encoding : default_encoding;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
qstring_quote(qstringObject *self)
 | 
					qstring_quote(qstringObject *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PyObject *str = NULL;
 | 
					    PyObject *str = NULL;
 | 
				
			||||||
    char *s, *buffer = NULL;
 | 
					    char *s, *buffer = NULL;
 | 
				
			||||||
    Py_ssize_t len, qlen;
 | 
					    Py_ssize_t len, qlen;
 | 
				
			||||||
    const char *encoding = default_encoding;
 | 
					    const char *encoding;
 | 
				
			||||||
    PyObject *rv = NULL;
 | 
					    PyObject *rv = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* if the wrapped object is an unicode object we can encode it to match
 | 
					    encoding = _qstring_get_encoding(self);
 | 
				
			||||||
       conn->encoding but if the encoding is not specified we don't know what
 | 
					 | 
				
			||||||
       to do and we raise an exception */
 | 
					 | 
				
			||||||
    if (self->conn) {
 | 
					 | 
				
			||||||
        encoding = self->conn->codec;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Dprintf("qstring_quote: encoding to %s", encoding);
 | 
					    Dprintf("qstring_quote: encoding to %s", encoding);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (PyUnicode_Check(self->wrapped) && encoding) {
 | 
					    if (PyUnicode_Check(self->wrapped)) {
 | 
				
			||||||
 | 
					        if (encoding) {
 | 
				
			||||||
            str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
 | 
					            str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
 | 
				
			||||||
            Dprintf("qstring_quote: got encoded object at %p", str);
 | 
					            Dprintf("qstring_quote: got encoded object at %p", str);
 | 
				
			||||||
            if (str == NULL) goto exit;
 | 
					            if (str == NULL) goto exit;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            PyErr_SetString(PyExc_TypeError,
 | 
				
			||||||
 | 
					                "missing encoding to encode unicode object");
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if PY_MAJOR_VERSION < 3
 | 
					    /* if the wrapped object is a binary string, we don't know how to
 | 
				
			||||||
    /* if the wrapped object is a simple string, we don't know how to
 | 
					 | 
				
			||||||
       (re)encode it, so we pass it as-is */
 | 
					       (re)encode it, so we pass it as-is */
 | 
				
			||||||
    else if (PyString_Check(self->wrapped)) {
 | 
					    else if (Bytes_Check(self->wrapped)) {
 | 
				
			||||||
        str = self->wrapped;
 | 
					        str = self->wrapped;
 | 
				
			||||||
        /* INCREF to make it ref-wise identical to unicode one */
 | 
					        /* INCREF to make it ref-wise identical to unicode one */
 | 
				
			||||||
        Py_INCREF(str);
 | 
					        Py_INCREF(str);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* if the wrapped object is not a string, this is an error */
 | 
					    /* if the wrapped object is not a string, this is an error */
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
        PyErr_SetString(PyExc_TypeError,
 | 
					        PyErr_SetString(PyExc_TypeError, "can't quote non-string object");
 | 
				
			||||||
                        "can't quote non-string object (or missing encoding)");
 | 
					 | 
				
			||||||
        goto exit;
 | 
					        goto exit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -150,13 +162,32 @@ qstring_conform(qstringObject *self, PyObject *args)
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
qstring_get_encoding(qstringObject *self)
 | 
					qstring_get_encoding(qstringObject *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    const char *encoding = default_encoding;
 | 
					    const char *encoding;
 | 
				
			||||||
 | 
					    encoding = _qstring_get_encoding(self);
 | 
				
			||||||
    if (self->conn) {
 | 
					    return Text_FromUTF8(encoding);
 | 
				
			||||||
        encoding = self->conn->codec;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return Text_FromUTF8(encoding);
 | 
					static int
 | 
				
			||||||
 | 
					qstring_set_encoding(qstringObject *self, PyObject *pyenc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int rv = -1;
 | 
				
			||||||
 | 
					    const char *tmp;
 | 
				
			||||||
 | 
					    char *cenc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* get a C copy of the encoding (which may come from unicode) */
 | 
				
			||||||
 | 
					    Py_INCREF(pyenc);
 | 
				
			||||||
 | 
					    if (!(pyenc = psycopg_ensure_bytes(pyenc))) { goto exit; }
 | 
				
			||||||
 | 
					    if (!(tmp = Bytes_AsString(pyenc))) { goto exit; }
 | 
				
			||||||
 | 
					    if (0 > psycopg_strdup(&cenc, tmp, 0)) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dprintf("qstring_set_encoding: encoding set to %s", cenc);
 | 
				
			||||||
 | 
					    PyMem_Free((void *)self->encoding);
 | 
				
			||||||
 | 
					    self->encoding = cenc;
 | 
				
			||||||
 | 
					    rv = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
					    Py_XDECREF(pyenc);
 | 
				
			||||||
 | 
					    return rv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** the QuotedString object **/
 | 
					/** the QuotedString object **/
 | 
				
			||||||
| 
						 | 
					@ -183,7 +214,7 @@ static PyMethodDef qstringObject_methods[] = {
 | 
				
			||||||
static PyGetSetDef qstringObject_getsets[] = {
 | 
					static PyGetSetDef qstringObject_getsets[] = {
 | 
				
			||||||
    { "encoding",
 | 
					    { "encoding",
 | 
				
			||||||
        (getter)qstring_get_encoding,
 | 
					        (getter)qstring_get_encoding,
 | 
				
			||||||
        (setter)NULL,
 | 
					        (setter)qstring_set_encoding,
 | 
				
			||||||
        "current encoding of the adapter" },
 | 
					        "current encoding of the adapter" },
 | 
				
			||||||
    {NULL}
 | 
					    {NULL}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -216,6 +247,7 @@ qstring_dealloc(PyObject* obj)
 | 
				
			||||||
    Py_CLEAR(self->wrapped);
 | 
					    Py_CLEAR(self->wrapped);
 | 
				
			||||||
    Py_CLEAR(self->buffer);
 | 
					    Py_CLEAR(self->buffer);
 | 
				
			||||||
    Py_CLEAR(self->conn);
 | 
					    Py_CLEAR(self->conn);
 | 
				
			||||||
 | 
					    PyMem_Free((void *)self->encoding);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
 | 
					    Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
 | 
				
			||||||
        FORMAT_CODE_PY_SSIZE_T,
 | 
					        FORMAT_CODE_PY_SSIZE_T,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,9 @@ typedef struct {
 | 
				
			||||||
    PyObject *buffer;
 | 
					    PyObject *buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    connectionObject *conn;
 | 
					    connectionObject *conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const char *encoding;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} qstringObject;
 | 
					} qstringObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __cplusplus
 | 
					#ifdef __cplusplus
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -494,6 +494,25 @@ conn_setup_cancel(connectionObject *self, PGconn *pgconn)
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Return 1 if the "replication" keyword is set in the DSN, 0 otherwise */
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					dsn_has_replication(char *pgdsn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int ret = 0;
 | 
				
			||||||
 | 
					    PQconninfoOption *connopts, *ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    connopts = PQconninfoParse(pgdsn, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for(ptr = connopts; ptr->keyword != NULL; ptr++) {
 | 
				
			||||||
 | 
					      if(strcmp(ptr->keyword, "replication") == 0 && ptr->val != NULL)
 | 
				
			||||||
 | 
					        ret = 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PQconninfoFree(connopts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Return 1 if the server datestyle allows us to work without problems,
 | 
					/* Return 1 if the server datestyle allows us to work without problems,
 | 
				
			||||||
   0 if it needs to be set to something better, e.g. ISO. */
 | 
					   0 if it needs to be set to something better, e.g. ISO. */
 | 
				
			||||||
| 
						 | 
					@ -522,28 +541,29 @@ conn_setup(connectionObject *self, PGconn *pgconn)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PGresult *pgres = NULL;
 | 
					    PGresult *pgres = NULL;
 | 
				
			||||||
    char *error = NULL;
 | 
					    char *error = NULL;
 | 
				
			||||||
 | 
					    int rv = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    self->equote = conn_get_standard_conforming_strings(pgconn);
 | 
					    self->equote = conn_get_standard_conforming_strings(pgconn);
 | 
				
			||||||
    self->server_version = conn_get_server_version(pgconn);
 | 
					    self->server_version = conn_get_server_version(pgconn);
 | 
				
			||||||
    self->protocol = conn_get_protocol_version(self->pgconn);
 | 
					    self->protocol = conn_get_protocol_version(self->pgconn);
 | 
				
			||||||
    if (3 != self->protocol) {
 | 
					    if (3 != self->protocol) {
 | 
				
			||||||
        PyErr_SetString(InterfaceError, "only protocol 3 supported");
 | 
					        PyErr_SetString(InterfaceError, "only protocol 3 supported");
 | 
				
			||||||
        return -1;
 | 
					        goto exit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (0 > conn_read_encoding(self, pgconn)) {
 | 
					    if (0 > conn_read_encoding(self, pgconn)) {
 | 
				
			||||||
        return -1;
 | 
					        goto exit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (0 > conn_setup_cancel(self, pgconn)) {
 | 
					    if (0 > conn_setup_cancel(self, pgconn)) {
 | 
				
			||||||
        return -1;
 | 
					        goto exit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_BEGIN_ALLOW_THREADS;
 | 
					    Py_BEGIN_ALLOW_THREADS;
 | 
				
			||||||
    pthread_mutex_lock(&self->lock);
 | 
					    pthread_mutex_lock(&self->lock);
 | 
				
			||||||
    Py_BLOCK_THREADS;
 | 
					    Py_BLOCK_THREADS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!conn_is_datestyle_ok(self->pgconn)) {
 | 
					    if (!dsn_has_replication(self->dsn) && !conn_is_datestyle_ok(self->pgconn)) {
 | 
				
			||||||
        int res;
 | 
					        int res;
 | 
				
			||||||
        Py_UNBLOCK_THREADS;
 | 
					        Py_UNBLOCK_THREADS;
 | 
				
			||||||
        res = pq_set_guc_locked(self, "datestyle", "ISO",
 | 
					        res = pq_set_guc_locked(self, "datestyle", "ISO",
 | 
				
			||||||
| 
						 | 
					@ -551,18 +571,23 @@ conn_setup(connectionObject *self, PGconn *pgconn)
 | 
				
			||||||
        Py_BLOCK_THREADS;
 | 
					        Py_BLOCK_THREADS;
 | 
				
			||||||
        if (res < 0) {
 | 
					        if (res < 0) {
 | 
				
			||||||
            pq_complete_error(self, &pgres, &error);
 | 
					            pq_complete_error(self, &pgres, &error);
 | 
				
			||||||
            return -1;
 | 
					            goto unlock;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* for reset */
 | 
					    /* for reset */
 | 
				
			||||||
    self->autocommit = 0;
 | 
					    self->autocommit = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* success */
 | 
				
			||||||
 | 
					    rv = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unlock:
 | 
				
			||||||
    Py_UNBLOCK_THREADS;
 | 
					    Py_UNBLOCK_THREADS;
 | 
				
			||||||
    pthread_mutex_unlock(&self->lock);
 | 
					    pthread_mutex_unlock(&self->lock);
 | 
				
			||||||
    Py_END_ALLOW_THREADS;
 | 
					    Py_END_ALLOW_THREADS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return 0;
 | 
					exit:
 | 
				
			||||||
 | 
					    return rv;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* conn_connect - execute a connection to the database */
 | 
					/* conn_connect - execute a connection to the database */
 | 
				
			||||||
| 
						 | 
					@ -859,8 +884,11 @@ _conn_poll_setup_async(connectionObject *self)
 | 
				
			||||||
        self->autocommit = 1;
 | 
					        self->autocommit = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* If the datestyle is ISO or anything else good,
 | 
					        /* If the datestyle is ISO or anything else good,
 | 
				
			||||||
         * we can skip the CONN_STATUS_DATESTYLE step. */
 | 
					         * we can skip the CONN_STATUS_DATESTYLE step.
 | 
				
			||||||
        if (!conn_is_datestyle_ok(self->pgconn)) {
 | 
					         * Note that we cannot change the datestyle on a replication
 | 
				
			||||||
 | 
					         * connection.
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        if (!dsn_has_replication(self->dsn) && !conn_is_datestyle_ok(self->pgconn)) {
 | 
				
			||||||
            Dprintf("conn_poll: status -> CONN_STATUS_DATESTYLE");
 | 
					            Dprintf("conn_poll: status -> CONN_STATUS_DATESTYLE");
 | 
				
			||||||
            self->status = CONN_STATUS_DATESTYLE;
 | 
					            self->status = CONN_STATUS_DATESTYLE;
 | 
				
			||||||
            if (0 == pq_send_query(self, psyco_datestyle)) {
 | 
					            if (0 == pq_send_query(self, psyco_datestyle)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -733,6 +733,37 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
 | 
				
			||||||
    return conn_text_from_chars(self, val);
 | 
					    return conn_text_from_chars(self, val);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* get_dsn_parameters method - Get connection parameters */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_conn_get_dsn_parameters_doc \
 | 
				
			||||||
 | 
					"get_dsn_parameters() -- Get effective connection parameters.\n\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_conn_get_dsn_parameters(connectionObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#if PG_VERSION_NUM >= 90300
 | 
				
			||||||
 | 
					    PyObject *res = NULL;
 | 
				
			||||||
 | 
					    PQconninfoOption *options = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CONN_CLOSED(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!(options = PQconninfo(self->pgconn))) {
 | 
				
			||||||
 | 
					        PyErr_NoMemory();
 | 
				
			||||||
 | 
					        goto exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
					    PQconninfoFree(options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    PyErr_SetString(NotSupportedError, "PQconninfo not available in libpq < 9.3");
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* lobject method - allocate a new lobject */
 | 
					/* lobject method - allocate a new lobject */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -977,6 +1008,8 @@ static struct PyMethodDef connectionObject_methods[] = {
 | 
				
			||||||
     METH_NOARGS, psyco_conn_get_transaction_status_doc},
 | 
					     METH_NOARGS, psyco_conn_get_transaction_status_doc},
 | 
				
			||||||
    {"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status,
 | 
					    {"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status,
 | 
				
			||||||
     METH_VARARGS, psyco_conn_get_parameter_status_doc},
 | 
					     METH_VARARGS, psyco_conn_get_parameter_status_doc},
 | 
				
			||||||
 | 
					    {"get_dsn_parameters", (PyCFunction)psyco_conn_get_dsn_parameters,
 | 
				
			||||||
 | 
					     METH_NOARGS, psyco_conn_get_dsn_parameters_doc},
 | 
				
			||||||
    {"get_backend_pid", (PyCFunction)psyco_conn_get_backend_pid,
 | 
					    {"get_backend_pid", (PyCFunction)psyco_conn_get_backend_pid,
 | 
				
			||||||
     METH_NOARGS, psyco_conn_get_backend_pid_doc},
 | 
					     METH_NOARGS, psyco_conn_get_backend_pid_doc},
 | 
				
			||||||
    {"lobject", (PyCFunction)psyco_conn_lobject,
 | 
					    {"lobject", (PyCFunction)psyco_conn_lobject,
 | 
				
			||||||
| 
						 | 
					@ -1171,7 +1204,7 @@ connection_repr(connectionObject *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return PyString_FromFormat(
 | 
					    return PyString_FromFormat(
 | 
				
			||||||
        "<connection object at %p; dsn: '%s', closed: %ld>",
 | 
					        "<connection object at %p; dsn: '%s', closed: %ld>",
 | 
				
			||||||
        self, self->dsn, self->closed);
 | 
					        self, (self->dsn ? self->dsn : "<unintialized>"), self->closed);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -335,7 +335,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
 | 
				
			||||||
        PyErr_Fetch(&err, &arg, &trace);
 | 
					        PyErr_Fetch(&err, &arg, &trace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) {
 | 
					        if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) {
 | 
				
			||||||
            Dprintf("psyco_curs_execute: TypeError exception catched");
 | 
					            Dprintf("psyco_curs_execute: TypeError exception caught");
 | 
				
			||||||
            PyErr_NormalizeException(&err, &arg, &trace);
 | 
					            PyErr_NormalizeException(&err, &arg, &trace);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (PyObject_HasAttrString(arg, "args")) {
 | 
					            if (PyObject_HasAttrString(arg, "args")) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										104
									
								
								psycopg/libpq_support.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								psycopg/libpq_support.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,104 @@
 | 
				
			||||||
 | 
					/* libpq_support.c - functions not provided by libpq, but which are
 | 
				
			||||||
 | 
					 * required for advanced communication with the server, such as
 | 
				
			||||||
 | 
					 * streaming replication
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2015 Federico Di Gregorio <fog@debian.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PSYCOPG_MODULE
 | 
				
			||||||
 | 
					#include "psycopg/psycopg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/libpq_support.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* htonl(), ntohl() */
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					#include <winsock2.h>
 | 
				
			||||||
 | 
					/* gettimeofday() */
 | 
				
			||||||
 | 
					#include "psycopg/win32_support.h"
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* support routines taken from pg_basebackup/streamutil.c */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Frontend version of GetCurrentTimestamp(), since we are not linked with
 | 
				
			||||||
 | 
					 * backend code. The protocol always uses integer timestamps, regardless of
 | 
				
			||||||
 | 
					 * server setting.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int64_t
 | 
				
			||||||
 | 
					feGetCurrentTimestamp(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int64_t result;
 | 
				
			||||||
 | 
					    struct timeval tp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gettimeofday(&tp, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = (int64_t) tp.tv_sec -
 | 
				
			||||||
 | 
					        ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = (result * USECS_PER_SEC) + tp.tv_usec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Converts an int64 to network byte order.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					fe_sendint64(int64_t i, char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    uint32_t n32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* High order half first, since we're doing MSB-first */
 | 
				
			||||||
 | 
					    n32 = (uint32_t) (i >> 32);
 | 
				
			||||||
 | 
					    n32 = htonl(n32);
 | 
				
			||||||
 | 
					    memcpy(&buf[0], &n32, 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* Now the low order half */
 | 
				
			||||||
 | 
					    n32 = (uint32_t) i;
 | 
				
			||||||
 | 
					    n32 = htonl(n32);
 | 
				
			||||||
 | 
					    memcpy(&buf[4], &n32, 4);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Converts an int64 from network byte order to native format.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int64_t
 | 
				
			||||||
 | 
					fe_recvint64(char *buf)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int64_t result;
 | 
				
			||||||
 | 
					    uint32_t h32;
 | 
				
			||||||
 | 
					    uint32_t l32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    memcpy(&h32, buf, 4);
 | 
				
			||||||
 | 
					    memcpy(&l32, buf + 4, 4);
 | 
				
			||||||
 | 
					    h32 = ntohl(h32);
 | 
				
			||||||
 | 
					    l32 = ntohl(l32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result = h32;
 | 
				
			||||||
 | 
					    result <<= 32;
 | 
				
			||||||
 | 
					    result |= l32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								psycopg/libpq_support.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								psycopg/libpq_support.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					/* libpq_support.h - definitions for libpq_support.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2015 Federico Di Gregorio <fog@debian.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef PSYCOPG_LIBPQ_SUPPORT_H
 | 
				
			||||||
 | 
					#define PSYCOPG_LIBPQ_SUPPORT_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* type and constant definitions from internal postgres include */
 | 
				
			||||||
 | 
					typedef unsigned PG_INT64_TYPE XLogRecPtr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* have to use lowercase %x, as PyString_FromFormat can't do %X */
 | 
				
			||||||
 | 
					#define XLOGFMTSTR "%x/%x"
 | 
				
			||||||
 | 
					#define XLOGFMTARGS(x) ((uint32_t)((x) >> 32)), ((uint32_t)((x) & 0xFFFFFFFF))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Julian-date equivalents of Day 0 in Unix and Postgres reckoning */
 | 
				
			||||||
 | 
					#define UNIX_EPOCH_JDATE	2440588 /* == date2j(1970, 1, 1) */
 | 
				
			||||||
 | 
					#define POSTGRES_EPOCH_JDATE	2451545 /* == date2j(2000, 1, 1) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SECS_PER_DAY	86400
 | 
				
			||||||
 | 
					#define USECS_PER_SEC	1000000LL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HIDDEN int64_t feGetCurrentTimestamp(void);
 | 
				
			||||||
 | 
					HIDDEN void fe_sendint64(int64_t i, char *buf);
 | 
				
			||||||
 | 
					HIDDEN int64_t fe_recvint64(char *buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !defined(PSYCOPG_LIBPQ_SUPPORT_H) */
 | 
				
			||||||
| 
						 | 
					@ -60,8 +60,8 @@ RAISES_NEG HIDDEN int lobject_export(lobjectObject *self, const char *filename);
 | 
				
			||||||
RAISES_NEG HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len);
 | 
					RAISES_NEG HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len);
 | 
				
			||||||
RAISES_NEG HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf,
 | 
					RAISES_NEG HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf,
 | 
				
			||||||
                                size_t len);
 | 
					                                size_t len);
 | 
				
			||||||
RAISES_NEG HIDDEN long lobject_seek(lobjectObject *self, long pos, int whence);
 | 
					RAISES_NEG HIDDEN Py_ssize_t lobject_seek(lobjectObject *self, Py_ssize_t pos, int whence);
 | 
				
			||||||
RAISES_NEG HIDDEN long lobject_tell(lobjectObject *self);
 | 
					RAISES_NEG HIDDEN Py_ssize_t lobject_tell(lobjectObject *self);
 | 
				
			||||||
RAISES_NEG HIDDEN int lobject_truncate(lobjectObject *self, size_t len);
 | 
					RAISES_NEG HIDDEN int lobject_truncate(lobjectObject *self, size_t len);
 | 
				
			||||||
RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
 | 
					RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -376,12 +376,12 @@ lobject_read(lobjectObject *self, char *buf, size_t len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* lobject_seek - move the current position in the lo */
 | 
					/* lobject_seek - move the current position in the lo */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RAISES_NEG long
 | 
					RAISES_NEG Py_ssize_t
 | 
				
			||||||
lobject_seek(lobjectObject *self, long pos, int whence)
 | 
					lobject_seek(lobjectObject *self, Py_ssize_t pos, int whence)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PGresult *pgres = NULL;
 | 
					    PGresult *pgres = NULL;
 | 
				
			||||||
    char *error = NULL;
 | 
					    char *error = NULL;
 | 
				
			||||||
    long where;
 | 
					    Py_ssize_t where;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Dprintf("lobject_seek: fd = %d, pos = %ld, whence = %d",
 | 
					    Dprintf("lobject_seek: fd = %d, pos = %ld, whence = %d",
 | 
				
			||||||
            self->fd, pos, whence);
 | 
					            self->fd, pos, whence);
 | 
				
			||||||
| 
						 | 
					@ -391,12 +391,12 @@ lobject_seek(lobjectObject *self, long pos, int whence)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef HAVE_LO64
 | 
					#ifdef HAVE_LO64
 | 
				
			||||||
    if (self->conn->server_version < 90300) {
 | 
					    if (self->conn->server_version < 90300) {
 | 
				
			||||||
        where = (long)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
 | 
					        where = (Py_ssize_t)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        where = lo_lseek64(self->conn->pgconn, self->fd, pos, whence);
 | 
					        where = (Py_ssize_t)lo_lseek64(self->conn->pgconn, self->fd, pos, whence);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    where = (long)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
 | 
					    where = (Py_ssize_t)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    Dprintf("lobject_seek: where = %ld", where);
 | 
					    Dprintf("lobject_seek: where = %ld", where);
 | 
				
			||||||
    if (where < 0)
 | 
					    if (where < 0)
 | 
				
			||||||
| 
						 | 
					@ -412,12 +412,12 @@ lobject_seek(lobjectObject *self, long pos, int whence)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* lobject_tell - tell the current position in the lo */
 | 
					/* lobject_tell - tell the current position in the lo */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RAISES_NEG long
 | 
					RAISES_NEG Py_ssize_t
 | 
				
			||||||
lobject_tell(lobjectObject *self)
 | 
					lobject_tell(lobjectObject *self)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PGresult *pgres = NULL;
 | 
					    PGresult *pgres = NULL;
 | 
				
			||||||
    char *error = NULL;
 | 
					    char *error = NULL;
 | 
				
			||||||
    long where;
 | 
					    Py_ssize_t where;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Dprintf("lobject_tell: fd = %d", self->fd);
 | 
					    Dprintf("lobject_tell: fd = %d", self->fd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -426,12 +426,12 @@ lobject_tell(lobjectObject *self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef HAVE_LO64
 | 
					#ifdef HAVE_LO64
 | 
				
			||||||
    if (self->conn->server_version < 90300) {
 | 
					    if (self->conn->server_version < 90300) {
 | 
				
			||||||
        where = (long)lo_tell(self->conn->pgconn, self->fd);
 | 
					        where = (Py_ssize_t)lo_tell(self->conn->pgconn, self->fd);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        where = lo_tell64(self->conn->pgconn, self->fd);
 | 
					        where = (Py_ssize_t)lo_tell64(self->conn->pgconn, self->fd);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    where = (long)lo_tell(self->conn->pgconn, self->fd);
 | 
					    where = (Py_ssize_t)lo_tell(self->conn->pgconn, self->fd);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    Dprintf("lobject_tell: where = %ld", where);
 | 
					    Dprintf("lobject_tell: where = %ld", where);
 | 
				
			||||||
    if (where < 0)
 | 
					    if (where < 0)
 | 
				
			||||||
| 
						 | 
					@ -474,8 +474,6 @@ lobject_export(lobjectObject *self, const char *filename)
 | 
				
			||||||
    return retvalue;
 | 
					    return retvalue;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if PG_VERSION_NUM >= 80300
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
RAISES_NEG int
 | 
					RAISES_NEG int
 | 
				
			||||||
lobject_truncate(lobjectObject *self, size_t len)
 | 
					lobject_truncate(lobjectObject *self, size_t len)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -510,5 +508,3 @@ lobject_truncate(lobjectObject *self, size_t len)
 | 
				
			||||||
    return retvalue;
 | 
					    return retvalue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif /* PG_VERSION_NUM >= 80300 */
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -105,7 +105,7 @@ psyco_lobj_write(lobjectObject *self, PyObject *args)
 | 
				
			||||||
        goto exit;
 | 
					        goto exit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    rv = PyInt_FromLong((long)res);
 | 
					    rv = PyInt_FromSsize_t((Py_ssize_t)res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit:
 | 
					exit:
 | 
				
			||||||
    Py_XDECREF(data);
 | 
					    Py_XDECREF(data);
 | 
				
			||||||
| 
						 | 
					@ -121,7 +121,7 @@ static PyObject *
 | 
				
			||||||
psyco_lobj_read(lobjectObject *self, PyObject *args)
 | 
					psyco_lobj_read(lobjectObject *self, PyObject *args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    PyObject *res;
 | 
					    PyObject *res;
 | 
				
			||||||
    long where, end;
 | 
					    Py_ssize_t where, end;
 | 
				
			||||||
    Py_ssize_t size = -1;
 | 
					    Py_ssize_t size = -1;
 | 
				
			||||||
    char *buffer;
 | 
					    char *buffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,10 +165,10 @@ psyco_lobj_read(lobjectObject *self, PyObject *args)
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
psyco_lobj_seek(lobjectObject *self, PyObject *args)
 | 
					psyco_lobj_seek(lobjectObject *self, PyObject *args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    long offset, pos=0;
 | 
					    Py_ssize_t offset, pos=0;
 | 
				
			||||||
    int whence=0;
 | 
					    int whence=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!PyArg_ParseTuple(args, "l|i", &offset, &whence))
 | 
					    if (!PyArg_ParseTuple(args, "n|i", &offset, &whence))
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    EXC_IF_LOBJ_CLOSED(self);
 | 
					    EXC_IF_LOBJ_CLOSED(self);
 | 
				
			||||||
| 
						 | 
					@ -187,8 +187,8 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    if (offset < INT_MIN || offset > INT_MAX) {
 | 
					    if (offset < INT_MIN || offset > INT_MAX) {
 | 
				
			||||||
        PyErr_Format(InterfaceError,
 | 
					        PyErr_Format(InterfaceError,
 | 
				
			||||||
            "offset out of range (%ld): this psycopg version was not built "
 | 
					            "offset out of range (" FORMAT_CODE_PY_SSIZE_T "): "
 | 
				
			||||||
            "with lobject 64 API support",
 | 
					            "this psycopg version was not built with lobject 64 API support",
 | 
				
			||||||
            offset);
 | 
					            offset);
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -197,7 +197,7 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
 | 
				
			||||||
    if ((pos = lobject_seek(self, offset, whence)) < 0)
 | 
					    if ((pos = lobject_seek(self, offset, whence)) < 0)
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return PyLong_FromLong(pos);
 | 
					    return PyInt_FromSsize_t(pos);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* tell method - tell current position in the lobject */
 | 
					/* tell method - tell current position in the lobject */
 | 
				
			||||||
| 
						 | 
					@ -208,7 +208,7 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
psyco_lobj_tell(lobjectObject *self, PyObject *args)
 | 
					psyco_lobj_tell(lobjectObject *self, PyObject *args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    long pos;
 | 
					    Py_ssize_t pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    EXC_IF_LOBJ_CLOSED(self);
 | 
					    EXC_IF_LOBJ_CLOSED(self);
 | 
				
			||||||
    EXC_IF_LOBJ_LEVEL0(self);
 | 
					    EXC_IF_LOBJ_LEVEL0(self);
 | 
				
			||||||
| 
						 | 
					@ -217,7 +217,7 @@ psyco_lobj_tell(lobjectObject *self, PyObject *args)
 | 
				
			||||||
    if ((pos = lobject_tell(self)) < 0)
 | 
					    if ((pos = lobject_tell(self)) < 0)
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return PyLong_FromLong(pos);
 | 
					    return PyInt_FromSsize_t(pos);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* unlink method - unlink (destroy) the lobject */
 | 
					/* unlink method - unlink (destroy) the lobject */
 | 
				
			||||||
| 
						 | 
					@ -266,17 +266,15 @@ psyco_lobj_get_closed(lobjectObject *self, void *closure)
 | 
				
			||||||
    return closed;
 | 
					    return closed;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if PG_VERSION_NUM >= 80300
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define psyco_lobj_truncate_doc \
 | 
					#define psyco_lobj_truncate_doc \
 | 
				
			||||||
"truncate(len=0) -- Truncate large object to given size."
 | 
					"truncate(len=0) -- Truncate large object to given size."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
psyco_lobj_truncate(lobjectObject *self, PyObject *args)
 | 
					psyco_lobj_truncate(lobjectObject *self, PyObject *args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    long len = 0;
 | 
					    Py_ssize_t len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!PyArg_ParseTuple(args, "|l", &len))
 | 
					    if (!PyArg_ParseTuple(args, "|n", &len))
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    EXC_IF_LOBJ_CLOSED(self);
 | 
					    EXC_IF_LOBJ_CLOSED(self);
 | 
				
			||||||
| 
						 | 
					@ -286,16 +284,16 @@ psyco_lobj_truncate(lobjectObject *self, PyObject *args)
 | 
				
			||||||
#ifdef HAVE_LO64
 | 
					#ifdef HAVE_LO64
 | 
				
			||||||
    if (len > INT_MAX && self->conn->server_version < 90300) {
 | 
					    if (len > INT_MAX && self->conn->server_version < 90300) {
 | 
				
			||||||
        PyErr_Format(NotSupportedError,
 | 
					        PyErr_Format(NotSupportedError,
 | 
				
			||||||
            "len out of range (%ld): server version %d "
 | 
					            "len out of range (" FORMAT_CODE_PY_SSIZE_T "): "
 | 
				
			||||||
            "does not support the lobject 64 API",
 | 
					            "server version %d does not support the lobject 64 API",
 | 
				
			||||||
            len, self->conn->server_version);
 | 
					            len, self->conn->server_version);
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
    if (len > INT_MAX) {
 | 
					    if (len > INT_MAX) {
 | 
				
			||||||
        PyErr_Format(InterfaceError,
 | 
					        PyErr_Format(InterfaceError,
 | 
				
			||||||
            "len out of range (%ld): this psycopg version was not built "
 | 
					            "len out of range (" FORMAT_CODE_PY_SSIZE_T "): "
 | 
				
			||||||
            "with lobject 64 API support",
 | 
					            "this psycopg version was not built with lobject 64 API support",
 | 
				
			||||||
            len);
 | 
					            len);
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -327,10 +325,8 @@ static struct PyMethodDef lobjectObject_methods[] = {
 | 
				
			||||||
     METH_NOARGS, psyco_lobj_unlink_doc},
 | 
					     METH_NOARGS, psyco_lobj_unlink_doc},
 | 
				
			||||||
    {"export",(PyCFunction)psyco_lobj_export,
 | 
					    {"export",(PyCFunction)psyco_lobj_export,
 | 
				
			||||||
     METH_VARARGS, psyco_lobj_export_doc},
 | 
					     METH_VARARGS, psyco_lobj_export_doc},
 | 
				
			||||||
#if PG_VERSION_NUM >= 80300
 | 
					 | 
				
			||||||
    {"truncate",(PyCFunction)psyco_lobj_truncate,
 | 
					    {"truncate",(PyCFunction)psyco_lobj_truncate,
 | 
				
			||||||
     METH_VARARGS, psyco_lobj_truncate_doc},
 | 
					     METH_VARARGS, psyco_lobj_truncate_doc},
 | 
				
			||||||
#endif /* PG_VERSION_NUM >= 80300 */
 | 
					 | 
				
			||||||
    {NULL}
 | 
					    {NULL}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -475,6 +471,3 @@ PyTypeObject lobjectType = {
 | 
				
			||||||
    0,          /*tp_alloc*/
 | 
					    0,          /*tp_alloc*/
 | 
				
			||||||
    lobject_new, /*tp_new*/
 | 
					    lobject_new, /*tp_new*/
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										320
									
								
								psycopg/pqpath.c
									
									
									
									
									
								
							
							
						
						
									
										320
									
								
								psycopg/pqpath.c
									
									
									
									
									
								
							| 
						 | 
					@ -35,13 +35,22 @@
 | 
				
			||||||
#include "psycopg/pqpath.h"
 | 
					#include "psycopg/pqpath.h"
 | 
				
			||||||
#include "psycopg/connection.h"
 | 
					#include "psycopg/connection.h"
 | 
				
			||||||
#include "psycopg/cursor.h"
 | 
					#include "psycopg/cursor.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_cursor.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_message.h"
 | 
				
			||||||
#include "psycopg/green.h"
 | 
					#include "psycopg/green.h"
 | 
				
			||||||
#include "psycopg/typecast.h"
 | 
					#include "psycopg/typecast.h"
 | 
				
			||||||
#include "psycopg/pgtypes.h"
 | 
					#include "psycopg/pgtypes.h"
 | 
				
			||||||
#include "psycopg/error.h"
 | 
					#include "psycopg/error.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string.h>
 | 
					#include "psycopg/libpq_support.h"
 | 
				
			||||||
 | 
					#include "libpq-fe.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					/* select() */
 | 
				
			||||||
 | 
					#include <winsock2.h>
 | 
				
			||||||
 | 
					/* gettimeofday() */
 | 
				
			||||||
 | 
					#include "win32_support.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern HIDDEN PyObject *psyco_DescriptionType;
 | 
					extern HIDDEN PyObject *psyco_DescriptionType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -161,11 +170,11 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (conn == NULL) {
 | 
					    if (conn == NULL) {
 | 
				
			||||||
        PyErr_SetString(DatabaseError,
 | 
					        PyErr_SetString(DatabaseError,
 | 
				
			||||||
            "psycopg went psycotic and raised a null error");
 | 
					            "psycopg went psychotic and raised a null error");
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* if the connection has somehow beed broken, we mark the connection
 | 
					    /* if the connection has somehow been broken, we mark the connection
 | 
				
			||||||
       object as closed but requiring cleanup */
 | 
					       object as closed but requiring cleanup */
 | 
				
			||||||
    if (conn->pgconn != NULL && PQstatus(conn->pgconn) == CONNECTION_BAD)
 | 
					    if (conn->pgconn != NULL && PQstatus(conn->pgconn) == CONNECTION_BAD)
 | 
				
			||||||
        conn->closed = 2;
 | 
					        conn->closed = 2;
 | 
				
			||||||
| 
						 | 
					@ -907,7 +916,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result, int
 | 
				
			||||||
        PyErr_SetString(OperationalError, PQerrorMessage(curs->conn->pgconn));
 | 
					        PyErr_SetString(OperationalError, PQerrorMessage(curs->conn->pgconn));
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Dprintf("curs_execute: pg connection at %p OK", curs->conn->pgconn);
 | 
					    Dprintf("pq_execute: pg connection at %p OK", curs->conn->pgconn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_BEGIN_ALLOW_THREADS;
 | 
					    Py_BEGIN_ALLOW_THREADS;
 | 
				
			||||||
    pthread_mutex_lock(&(curs->conn->lock));
 | 
					    pthread_mutex_lock(&(curs->conn->lock));
 | 
				
			||||||
| 
						 | 
					@ -932,7 +941,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result, int
 | 
				
			||||||
            Py_UNBLOCK_THREADS;
 | 
					            Py_UNBLOCK_THREADS;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* dont let pgres = NULL go to pq_fetch() */
 | 
					        /* don't let pgres = NULL go to pq_fetch() */
 | 
				
			||||||
        if (curs->pgres == NULL) {
 | 
					        if (curs->pgres == NULL) {
 | 
				
			||||||
            pthread_mutex_unlock(&(curs->conn->lock));
 | 
					            pthread_mutex_unlock(&(curs->conn->lock));
 | 
				
			||||||
            Py_BLOCK_THREADS;
 | 
					            Py_BLOCK_THREADS;
 | 
				
			||||||
| 
						 | 
					@ -1056,6 +1065,13 @@ pq_get_last_result(connectionObject *conn)
 | 
				
			||||||
            PQclear(result);
 | 
					            PQclear(result);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        result = res;
 | 
					        result = res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* After entering copy both mode, libpq will make a phony
 | 
				
			||||||
 | 
					         * PGresult for us every time we query for it, so we need to
 | 
				
			||||||
 | 
					         * break out of this endless loop. */
 | 
				
			||||||
 | 
					        if (PQresultStatus(result) == PGRES_COPY_BOTH) {
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return result;
 | 
					    return result;
 | 
				
			||||||
| 
						 | 
					@ -1393,7 +1409,11 @@ _pq_copy_in_v3(cursorObject *curs)
 | 
				
			||||||
                    Py_DECREF(str);
 | 
					                    Py_DECREF(str);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            PyErr_Restore(t, ex, tb);
 | 
					            /* Clear the Py exception: it will be re-raised from the libpq */
 | 
				
			||||||
 | 
					            Py_XDECREF(t);
 | 
				
			||||||
 | 
					            Py_XDECREF(ex);
 | 
				
			||||||
 | 
					            Py_XDECREF(tb);
 | 
				
			||||||
 | 
					            PyErr_Clear();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        res = PQputCopyEnd(curs->conn->pgconn, buf);
 | 
					        res = PQputCopyEnd(curs->conn->pgconn, buf);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -1516,6 +1536,281 @@ exit:
 | 
				
			||||||
    return ret;
 | 
					    return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Tries to read the next message from the replication stream, without
 | 
				
			||||||
 | 
					   blocking, in both sync and async connection modes.  If no message
 | 
				
			||||||
 | 
					   is ready in the CopyData buffer, tries to read from the server,
 | 
				
			||||||
 | 
					   again without blocking.  If that doesn't help, returns Py_None.
 | 
				
			||||||
 | 
					   The caller is then supposed to block on the socket(s) and call this
 | 
				
			||||||
 | 
					   function again.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Any keepalive messages from the server are silently consumed and
 | 
				
			||||||
 | 
					   are never returned to the caller.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					pq_read_replication_message(replicationCursorObject *repl, replicationMessageObject **msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &repl->cur;
 | 
				
			||||||
 | 
					    connectionObject *conn = curs->conn;
 | 
				
			||||||
 | 
					    PGconn *pgconn = conn->pgconn;
 | 
				
			||||||
 | 
					    char *buffer = NULL;
 | 
				
			||||||
 | 
					    int len, data_size, consumed, hdr, reply;
 | 
				
			||||||
 | 
					    XLogRecPtr data_start, wal_end;
 | 
				
			||||||
 | 
					    int64_t send_time;
 | 
				
			||||||
 | 
					    PyObject *str = NULL, *result = NULL;
 | 
				
			||||||
 | 
					    int ret = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dprintf("pq_read_replication_message");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *msg = NULL;
 | 
				
			||||||
 | 
					    consumed = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					retry:
 | 
				
			||||||
 | 
					    len = PQgetCopyData(pgconn, &buffer, 1 /* async */);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (len == 0) {
 | 
				
			||||||
 | 
					        /* If we've tried reading some data, but there was none, bail out. */
 | 
				
			||||||
 | 
					        if (consumed) {
 | 
				
			||||||
 | 
					            ret = 0;
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* We should only try reading more data when there is nothing
 | 
				
			||||||
 | 
					           available at the moment.  Otherwise, with a really highly loaded
 | 
				
			||||||
 | 
					           server we might be reading a number of messages for every single
 | 
				
			||||||
 | 
					           one we process, thus overgrowing the internal buffer until the
 | 
				
			||||||
 | 
					           client system runs out of memory. */
 | 
				
			||||||
 | 
					        if (!PQconsumeInput(pgconn)) {
 | 
				
			||||||
 | 
					            pq_raise(conn, curs, NULL);
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        /* But PQconsumeInput() doesn't tell us if it has actually read
 | 
				
			||||||
 | 
					           anything into the internal buffer and there is no (supported) way
 | 
				
			||||||
 | 
					           to ask libpq about this directly.  The way we check is setting the
 | 
				
			||||||
 | 
					           flag and re-trying PQgetCopyData(): if that returns 0 again,
 | 
				
			||||||
 | 
					           there's no more data available in the buffer, so we return None. */
 | 
				
			||||||
 | 
					        consumed = 1;
 | 
				
			||||||
 | 
					        goto retry;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (len == -2) {
 | 
				
			||||||
 | 
					        /* serious error */
 | 
				
			||||||
 | 
					        pq_raise(conn, curs, NULL);
 | 
				
			||||||
 | 
					        goto exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (len == -1) {
 | 
				
			||||||
 | 
					        /* EOF */
 | 
				
			||||||
 | 
					        curs->pgres = PQgetResult(pgconn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (curs->pgres && PQresultStatus(curs->pgres) == PGRES_FATAL_ERROR) {
 | 
				
			||||||
 | 
					            pq_raise(conn, curs, NULL);
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        CLEARPGRES(curs->pgres);
 | 
				
			||||||
 | 
					        ret = 0;
 | 
				
			||||||
 | 
					        goto exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* It also makes sense to set this flag here to make us return early in
 | 
				
			||||||
 | 
					       case of retry due to keepalive message.  Any pending data on the socket
 | 
				
			||||||
 | 
					       will trigger read condition in select() in the calling code anyway. */
 | 
				
			||||||
 | 
					    consumed = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* ok, we did really read something: update the io timestamp */
 | 
				
			||||||
 | 
					    gettimeofday(&repl->last_io, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dprintf("pq_read_replication_message: msg=%c, len=%d", buffer[0], len);
 | 
				
			||||||
 | 
					    if (buffer[0] == 'w') {
 | 
				
			||||||
 | 
					        /* XLogData: msgtype(1), dataStart(8), walEnd(8), sendTime(8) */
 | 
				
			||||||
 | 
					        hdr = 1 + 8 + 8 + 8;
 | 
				
			||||||
 | 
					        if (len < hdr + 1) {
 | 
				
			||||||
 | 
					            psyco_set_error(OperationalError, curs, "data message header too small");
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        data_size  = len - hdr;
 | 
				
			||||||
 | 
					        data_start = fe_recvint64(buffer + 1);
 | 
				
			||||||
 | 
					        wal_end    = fe_recvint64(buffer + 1 + 8);
 | 
				
			||||||
 | 
					        send_time  = fe_recvint64(buffer + 1 + 8 + 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Dprintf("pq_read_replication_message: data_start="XLOGFMTSTR", wal_end="XLOGFMTSTR,
 | 
				
			||||||
 | 
					                XLOGFMTARGS(data_start), XLOGFMTARGS(wal_end));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Dprintf("pq_read_replication_message: >>%.*s<<", data_size, buffer + hdr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (repl->decode) {
 | 
				
			||||||
 | 
					            str = PyUnicode_Decode(buffer + hdr, data_size, conn->codec, NULL);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            str = Bytes_FromStringAndSize(buffer + hdr, data_size);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!str) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result = PyObject_CallFunctionObjArgs((PyObject *)&replicationMessageType,
 | 
				
			||||||
 | 
					                                              curs, str, NULL);
 | 
				
			||||||
 | 
					        Py_DECREF(str);
 | 
				
			||||||
 | 
					        if (!result) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        *msg = (replicationMessageObject *)result;
 | 
				
			||||||
 | 
					        (*msg)->data_size  = data_size;
 | 
				
			||||||
 | 
					        (*msg)->data_start = data_start;
 | 
				
			||||||
 | 
					        (*msg)->wal_end    = wal_end;
 | 
				
			||||||
 | 
					        (*msg)->send_time  = send_time;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (buffer[0] == 'k') {
 | 
				
			||||||
 | 
					        /* Primary keepalive message: msgtype(1), walEnd(8), sendTime(8), reply(1) */
 | 
				
			||||||
 | 
					        hdr = 1 + 8 + 8;
 | 
				
			||||||
 | 
					        if (len < hdr + 1) {
 | 
				
			||||||
 | 
					            psyco_set_error(OperationalError, curs, "keepalive message header too small");
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        reply = buffer[hdr];
 | 
				
			||||||
 | 
					        if (reply && pq_send_replication_feedback(repl, 0) < 0) {
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PQfreemem(buffer);
 | 
				
			||||||
 | 
					        buffer = NULL;
 | 
				
			||||||
 | 
					        goto retry;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else {
 | 
				
			||||||
 | 
					        psyco_set_error(OperationalError, curs, "unrecognized replication message type");
 | 
				
			||||||
 | 
					        goto exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
					    if (buffer) {
 | 
				
			||||||
 | 
					        PQfreemem(buffer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					pq_send_replication_feedback(replicationCursorObject *repl, int reply_requested)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &repl->cur;
 | 
				
			||||||
 | 
					    connectionObject *conn = curs->conn;
 | 
				
			||||||
 | 
					    PGconn *pgconn = conn->pgconn;
 | 
				
			||||||
 | 
					    char replybuf[1 + 8 + 8 + 8 + 8 + 1];
 | 
				
			||||||
 | 
					    int len = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dprintf("pq_send_replication_feedback: write="XLOGFMTSTR", flush="XLOGFMTSTR", apply="XLOGFMTSTR,
 | 
				
			||||||
 | 
					            XLOGFMTARGS(repl->write_lsn),
 | 
				
			||||||
 | 
					            XLOGFMTARGS(repl->flush_lsn),
 | 
				
			||||||
 | 
					            XLOGFMTARGS(repl->apply_lsn));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    replybuf[len] = 'r'; len += 1;
 | 
				
			||||||
 | 
					    fe_sendint64(repl->write_lsn, &replybuf[len]); len += 8;
 | 
				
			||||||
 | 
					    fe_sendint64(repl->flush_lsn, &replybuf[len]); len += 8;
 | 
				
			||||||
 | 
					    fe_sendint64(repl->apply_lsn, &replybuf[len]); len += 8;
 | 
				
			||||||
 | 
					    fe_sendint64(feGetCurrentTimestamp(), &replybuf[len]); len += 8;
 | 
				
			||||||
 | 
					    replybuf[len] = reply_requested ? 1 : 0; len += 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (PQputCopyData(pgconn, replybuf, len) <= 0 || PQflush(pgconn) != 0) {
 | 
				
			||||||
 | 
					        pq_raise(conn, curs, NULL);
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    gettimeofday(&repl->last_io, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Calls pq_read_replication_message in an endless loop, until
 | 
				
			||||||
 | 
					   stop_replication is called or a fatal error occurs.  The messages
 | 
				
			||||||
 | 
					   are passed to the consumer object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   When no message is available, blocks on the connection socket, but
 | 
				
			||||||
 | 
					   manages to send keepalive messages to the server as needed.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					pq_copy_both(replicationCursorObject *repl, PyObject *consume, double keepalive_interval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &repl->cur;
 | 
				
			||||||
 | 
					    connectionObject *conn = curs->conn;
 | 
				
			||||||
 | 
					    PGconn *pgconn = conn->pgconn;
 | 
				
			||||||
 | 
					    replicationMessageObject *msg = NULL;
 | 
				
			||||||
 | 
					    PyObject *tmp = NULL;
 | 
				
			||||||
 | 
					    int fd, sel, ret = -1;
 | 
				
			||||||
 | 
					    fd_set fds;
 | 
				
			||||||
 | 
					    struct timeval keep_intr, curr_time, ping_time, timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyCallable_Check(consume)) {
 | 
				
			||||||
 | 
					        Dprintf("pq_copy_both: expected callable consume object");
 | 
				
			||||||
 | 
					        goto exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    CLEARPGRES(curs->pgres);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    keep_intr.tv_sec  = (int)keepalive_interval;
 | 
				
			||||||
 | 
					    keep_intr.tv_usec = (keepalive_interval - keep_intr.tv_sec)*1.0e6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        if (pq_read_replication_message(repl, &msg) < 0) {
 | 
				
			||||||
 | 
					            goto exit;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else if (msg == NULL) {
 | 
				
			||||||
 | 
					            fd = PQsocket(pgconn);
 | 
				
			||||||
 | 
					            if (fd < 0) {
 | 
				
			||||||
 | 
					                pq_raise(conn, curs, NULL);
 | 
				
			||||||
 | 
					                goto exit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            FD_ZERO(&fds);
 | 
				
			||||||
 | 
					            FD_SET(fd, &fds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            /* how long can we wait before we need to send a keepalive? */
 | 
				
			||||||
 | 
					            gettimeofday(&curr_time, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            timeradd(&repl->last_io, &keep_intr, &ping_time);
 | 
				
			||||||
 | 
					            timersub(&ping_time, &curr_time, &timeout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (timeout.tv_sec >= 0) {
 | 
				
			||||||
 | 
					                Py_BEGIN_ALLOW_THREADS;
 | 
				
			||||||
 | 
					                sel = select(fd + 1, &fds, NULL, NULL, &timeout);
 | 
				
			||||||
 | 
					                Py_END_ALLOW_THREADS;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                sel = 0; /* we're past target time, pretend select() timed out */
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sel < 0) {
 | 
				
			||||||
 | 
					                if (errno != EINTR) {
 | 
				
			||||||
 | 
					                    PyErr_SetFromErrno(PyExc_OSError);
 | 
				
			||||||
 | 
					                    goto exit;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (PyErr_CheckSignals()) {
 | 
				
			||||||
 | 
					                    goto exit;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sel == 0) {
 | 
				
			||||||
 | 
					                if (pq_send_replication_feedback(repl, 0) < 0) {
 | 
				
			||||||
 | 
					                    goto exit;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            tmp = PyObject_CallFunctionObjArgs(consume, msg, NULL);
 | 
				
			||||||
 | 
					            Py_DECREF(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (tmp == NULL) {
 | 
				
			||||||
 | 
					                Dprintf("pq_copy_both: consume returned NULL");
 | 
				
			||||||
 | 
					                goto exit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Py_DECREF(tmp);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ret = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int
 | 
					int
 | 
				
			||||||
pq_fetch(cursorObject *curs, int no_result)
 | 
					pq_fetch(cursorObject *curs, int no_result)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -1575,6 +1870,17 @@ pq_fetch(cursorObject *curs, int no_result)
 | 
				
			||||||
        CLEARPGRES(curs->pgres);
 | 
					        CLEARPGRES(curs->pgres);
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case PGRES_COPY_BOTH:
 | 
				
			||||||
 | 
					        Dprintf("pq_fetch: data from a streaming replication slot (no tuples)");
 | 
				
			||||||
 | 
					        curs->rowcount = -1;
 | 
				
			||||||
 | 
					        ex = 0;
 | 
				
			||||||
 | 
					        /* Nothing to do here: pq_copy_both will be called separately.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           Also don't clear the result status: it's checked in
 | 
				
			||||||
 | 
					           consume_stream. */
 | 
				
			||||||
 | 
					        /*CLEARPGRES(curs->pgres);*/
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    case PGRES_TUPLES_OK:
 | 
					    case PGRES_TUPLES_OK:
 | 
				
			||||||
        if (!no_result) {
 | 
					        if (!no_result) {
 | 
				
			||||||
            Dprintf("pq_fetch: got tuples");
 | 
					            Dprintf("pq_fetch: got tuples");
 | 
				
			||||||
| 
						 | 
					@ -1607,7 +1913,7 @@ pq_fetch(cursorObject *curs, int no_result)
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
        /* PGRES_COPY_BOTH, PGRES_SINGLE_TUPLE, future statuses */
 | 
					        /* PGRES_SINGLE_TUPLE, future statuses */
 | 
				
			||||||
        Dprintf("pq_fetch: got unsupported result: status = %d pgconn = %p",
 | 
					        Dprintf("pq_fetch: got unsupported result: status = %d pgconn = %p",
 | 
				
			||||||
            pgstatus, curs->conn);
 | 
					            pgstatus, curs->conn);
 | 
				
			||||||
        PyErr_Format(NotSupportedError,
 | 
					        PyErr_Format(NotSupportedError,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "psycopg/cursor.h"
 | 
					#include "psycopg/cursor.h"
 | 
				
			||||||
#include "psycopg/connection.h"
 | 
					#include "psycopg/connection.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_cursor.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_message.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* macro to clean the pg result */
 | 
					/* macro to clean the pg result */
 | 
				
			||||||
#define CLEARPGRES(pgres)   do { PQclear(pgres); pgres = NULL; } while (0)
 | 
					#define CLEARPGRES(pgres)   do { PQclear(pgres); pgres = NULL; } while (0)
 | 
				
			||||||
| 
						 | 
					@ -72,4 +74,11 @@ HIDDEN int pq_execute_command_locked(connectionObject *conn,
 | 
				
			||||||
RAISES HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres,
 | 
					RAISES HIDDEN void pq_complete_error(connectionObject *conn, PGresult **pgres,
 | 
				
			||||||
                              char **error);
 | 
					                              char **error);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* replication protocol support */
 | 
				
			||||||
 | 
					HIDDEN int pq_copy_both(replicationCursorObject *repl, PyObject *consumer,
 | 
				
			||||||
 | 
					                        double keepalive_interval);
 | 
				
			||||||
 | 
					HIDDEN int pq_read_replication_message(replicationCursorObject *repl,
 | 
				
			||||||
 | 
					                                       replicationMessageObject **msg);
 | 
				
			||||||
 | 
					HIDDEN int pq_send_replication_feedback(replicationCursorObject *repl, int reply_requested);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* !defined(PSYCOPG_PQPATH_H) */
 | 
					#endif /* !defined(PSYCOPG_PQPATH_H) */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,10 @@
 | 
				
			||||||
#ifndef PSYCOPG_H
 | 
					#ifndef PSYCOPG_H
 | 
				
			||||||
#define PSYCOPG_H 1
 | 
					#define PSYCOPG_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if PG_VERSION_NUM < 90100
 | 
				
			||||||
 | 
					#error "Psycopg requires PostgreSQL client library (libpq) >= 9.1"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PY_SSIZE_T_CLEAN
 | 
					#define PY_SSIZE_T_CLEAN
 | 
				
			||||||
#include <Python.h>
 | 
					#include <Python.h>
 | 
				
			||||||
#include <libpq-fe.h>
 | 
					#include <libpq-fe.h>
 | 
				
			||||||
| 
						 | 
					@ -117,6 +121,7 @@ HIDDEN PyObject *psyco_GetDecimalType(void);
 | 
				
			||||||
/* forward declarations */
 | 
					/* forward declarations */
 | 
				
			||||||
typedef struct cursorObject cursorObject;
 | 
					typedef struct cursorObject cursorObject;
 | 
				
			||||||
typedef struct connectionObject connectionObject;
 | 
					typedef struct connectionObject connectionObject;
 | 
				
			||||||
 | 
					typedef struct replicationMessageObject replicationMessageObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* some utility functions */
 | 
					/* some utility functions */
 | 
				
			||||||
RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg);
 | 
					RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg);
 | 
				
			||||||
| 
						 | 
					@ -132,6 +137,9 @@ STEALS(1) HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
 | 
					STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HIDDEN PyObject *psycopg_dict_from_conninfo_options(PQconninfoOption *options,
 | 
				
			||||||
 | 
					              int include_password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Exceptions docstrings */
 | 
					/* Exceptions docstrings */
 | 
				
			||||||
#define Error_doc \
 | 
					#define Error_doc \
 | 
				
			||||||
"Base class for error exceptions."
 | 
					"Base class for error exceptions."
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "psycopg/connection.h"
 | 
					#include "psycopg/connection.h"
 | 
				
			||||||
#include "psycopg/cursor.h"
 | 
					#include "psycopg/cursor.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_connection.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_cursor.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_message.h"
 | 
				
			||||||
#include "psycopg/green.h"
 | 
					#include "psycopg/green.h"
 | 
				
			||||||
#include "psycopg/lobject.h"
 | 
					#include "psycopg/lobject.h"
 | 
				
			||||||
#include "psycopg/notify.h"
 | 
					#include "psycopg/notify.h"
 | 
				
			||||||
| 
						 | 
					@ -111,14 +114,16 @@ psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
 | 
				
			||||||
    return conn;
 | 
					    return conn;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define psyco_parse_dsn_doc "parse_dsn(dsn) -> dict"
 | 
					
 | 
				
			||||||
 | 
					#define psyco_parse_dsn_doc \
 | 
				
			||||||
 | 
					"parse_dsn(dsn) -> dict -- parse a connection string into parameters"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static PyObject *
 | 
					static PyObject *
 | 
				
			||||||
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
 | 
					psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    char *err = NULL;
 | 
					    char *err = NULL;
 | 
				
			||||||
    PQconninfoOption *options = NULL, *o;
 | 
					    PQconninfoOption *options = NULL;
 | 
				
			||||||
    PyObject *dict = NULL, *res = NULL, *dsn;
 | 
					    PyObject *res = NULL, *dsn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static char *kwlist[] = {"dsn", NULL};
 | 
					    static char *kwlist[] = {"dsn", NULL};
 | 
				
			||||||
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) {
 | 
					    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) {
 | 
				
			||||||
| 
						 | 
					@ -131,7 +136,7 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
 | 
				
			||||||
    options = PQconninfoParse(Bytes_AS_STRING(dsn), &err);
 | 
					    options = PQconninfoParse(Bytes_AS_STRING(dsn), &err);
 | 
				
			||||||
    if (options == NULL) {
 | 
					    if (options == NULL) {
 | 
				
			||||||
        if (err != NULL) {
 | 
					        if (err != NULL) {
 | 
				
			||||||
            PyErr_Format(ProgrammingError, "error parsing the dsn: %s", err);
 | 
					            PyErr_Format(ProgrammingError, "invalid dsn: %s", err);
 | 
				
			||||||
            PQfreemem(err);
 | 
					            PQfreemem(err);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            PyErr_SetString(OperationalError, "PQconninfoParse() failed");
 | 
					            PyErr_SetString(OperationalError, "PQconninfoParse() failed");
 | 
				
			||||||
| 
						 | 
					@ -139,26 +144,10 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
 | 
				
			||||||
        goto exit;
 | 
					        goto exit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!(dict = PyDict_New())) { goto exit; }
 | 
					    res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 1);
 | 
				
			||||||
    for (o = options; o->keyword != NULL; o++) {
 | 
					 | 
				
			||||||
        if (o->val != NULL) {
 | 
					 | 
				
			||||||
            PyObject *value;
 | 
					 | 
				
			||||||
            if (!(value = Text_FromUTF8(o->val))) { goto exit; }
 | 
					 | 
				
			||||||
            if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
 | 
					 | 
				
			||||||
                Py_DECREF(value);
 | 
					 | 
				
			||||||
                goto exit;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            Py_DECREF(value);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* success */
 | 
					 | 
				
			||||||
    res = dict;
 | 
					 | 
				
			||||||
    dict = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit:
 | 
					exit:
 | 
				
			||||||
    PQconninfoFree(options);    /* safe on null */
 | 
					    PQconninfoFree(options);    /* safe on null */
 | 
				
			||||||
    Py_XDECREF(dict);
 | 
					 | 
				
			||||||
    Py_XDECREF(dsn);
 | 
					    Py_XDECREF(dsn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
| 
						 | 
					@ -289,9 +278,7 @@ psyco_libcrypto_threads_init(void)
 | 
				
			||||||
    if ((m = PyImport_ImportModule("ssl"))) {
 | 
					    if ((m = PyImport_ImportModule("ssl"))) {
 | 
				
			||||||
        /* disable libcrypto setup in libpq, so it won't stomp on the callbacks
 | 
					        /* disable libcrypto setup in libpq, so it won't stomp on the callbacks
 | 
				
			||||||
           that have already been set up */
 | 
					           that have already been set up */
 | 
				
			||||||
#if PG_VERSION_NUM >= 80400
 | 
					 | 
				
			||||||
        PQinitOpenSSL(1, 0);
 | 
					        PQinitOpenSSL(1, 0);
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
        Py_DECREF(m);
 | 
					        Py_DECREF(m);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
| 
						 | 
					@ -909,6 +896,15 @@ INIT_MODULE(_psycopg)(void)
 | 
				
			||||||
    Py_TYPE(&cursorType) = &PyType_Type;
 | 
					    Py_TYPE(&cursorType) = &PyType_Type;
 | 
				
			||||||
    if (PyType_Ready(&cursorType) == -1) goto exit;
 | 
					    if (PyType_Ready(&cursorType) == -1) goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_TYPE(&replicationConnectionType) = &PyType_Type;
 | 
				
			||||||
 | 
					    if (PyType_Ready(&replicationConnectionType) == -1) goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_TYPE(&replicationCursorType) = &PyType_Type;
 | 
				
			||||||
 | 
					    if (PyType_Ready(&replicationCursorType) == -1) goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_TYPE(&replicationMessageType) = &PyType_Type;
 | 
				
			||||||
 | 
					    if (PyType_Ready(&replicationMessageType) == -1) goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_TYPE(&typecastType) = &PyType_Type;
 | 
					    Py_TYPE(&typecastType) = &PyType_Type;
 | 
				
			||||||
    if (PyType_Ready(&typecastType) == -1) goto exit;
 | 
					    if (PyType_Ready(&typecastType) == -1) goto exit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -989,6 +985,8 @@ INIT_MODULE(_psycopg)(void)
 | 
				
			||||||
    /* Initialize the PyDateTimeAPI everywhere is used */
 | 
					    /* Initialize the PyDateTimeAPI everywhere is used */
 | 
				
			||||||
    PyDateTime_IMPORT;
 | 
					    PyDateTime_IMPORT;
 | 
				
			||||||
    if (psyco_adapter_datetime_init()) { goto exit; }
 | 
					    if (psyco_adapter_datetime_init()) { goto exit; }
 | 
				
			||||||
 | 
					    if (psyco_repl_curs_datetime_init()) { goto exit; }
 | 
				
			||||||
 | 
					    if (psyco_replmsg_datetime_init()) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Py_TYPE(&pydatetimeType) = &PyType_Type;
 | 
					    Py_TYPE(&pydatetimeType) = &PyType_Type;
 | 
				
			||||||
    if (PyType_Ready(&pydatetimeType) == -1) goto exit;
 | 
					    if (PyType_Ready(&pydatetimeType) == -1) goto exit;
 | 
				
			||||||
| 
						 | 
					@ -1024,6 +1022,8 @@ INIT_MODULE(_psycopg)(void)
 | 
				
			||||||
    PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION);
 | 
					    PyModule_AddStringConstant(module, "__version__", PSYCOPG_VERSION);
 | 
				
			||||||
    PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver");
 | 
					    PyModule_AddStringConstant(module, "__doc__", "psycopg PostgreSQL driver");
 | 
				
			||||||
    PyModule_AddIntConstant(module, "__libpq_version__", PG_VERSION_NUM);
 | 
					    PyModule_AddIntConstant(module, "__libpq_version__", PG_VERSION_NUM);
 | 
				
			||||||
 | 
					    PyModule_AddIntMacro(module, REPLICATION_PHYSICAL);
 | 
				
			||||||
 | 
					    PyModule_AddIntMacro(module, REPLICATION_LOGICAL);
 | 
				
			||||||
    PyModule_AddObject(module, "apilevel", Text_FromUTF8(APILEVEL));
 | 
					    PyModule_AddObject(module, "apilevel", Text_FromUTF8(APILEVEL));
 | 
				
			||||||
    PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY));
 | 
					    PyModule_AddObject(module, "threadsafety", PyInt_FromLong(THREADSAFETY));
 | 
				
			||||||
    PyModule_AddObject(module, "paramstyle", Text_FromUTF8(PARAMSTYLE));
 | 
					    PyModule_AddObject(module, "paramstyle", Text_FromUTF8(PARAMSTYLE));
 | 
				
			||||||
| 
						 | 
					@ -1031,6 +1031,9 @@ INIT_MODULE(_psycopg)(void)
 | 
				
			||||||
    /* put new types in module dictionary */
 | 
					    /* put new types in module dictionary */
 | 
				
			||||||
    PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
 | 
					    PyModule_AddObject(module, "connection", (PyObject*)&connectionType);
 | 
				
			||||||
    PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
 | 
					    PyModule_AddObject(module, "cursor", (PyObject*)&cursorType);
 | 
				
			||||||
 | 
					    PyModule_AddObject(module, "ReplicationConnection", (PyObject*)&replicationConnectionType);
 | 
				
			||||||
 | 
					    PyModule_AddObject(module, "ReplicationCursor", (PyObject*)&replicationCursorType);
 | 
				
			||||||
 | 
					    PyModule_AddObject(module, "ReplicationMessage", (PyObject*)&replicationMessageType);
 | 
				
			||||||
    PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
 | 
					    PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
 | 
				
			||||||
    PyModule_AddObject(module, "Notify", (PyObject*)¬ifyType);
 | 
					    PyModule_AddObject(module, "Notify", (PyObject*)¬ifyType);
 | 
				
			||||||
    PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
 | 
					    PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
 | 
				
			||||||
| 
						 | 
					@ -1070,6 +1073,9 @@ INIT_MODULE(_psycopg)(void)
 | 
				
			||||||
    if (0 != psyco_errors_init()) { goto exit; }
 | 
					    if (0 != psyco_errors_init()) { goto exit; }
 | 
				
			||||||
    psyco_errors_fill(dict);
 | 
					    psyco_errors_fill(dict);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    replicationPhysicalConst = PyDict_GetItemString(dict, "REPLICATION_PHYSICAL");
 | 
				
			||||||
 | 
					    replicationLogicalConst  = PyDict_GetItemString(dict, "REPLICATION_LOGICAL");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Dprintf("initpsycopg: module initialization complete");
 | 
					    Dprintf("initpsycopg: module initialization complete");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exit:
 | 
					exit:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,8 +31,8 @@
 | 
				
			||||||
#include <stringobject.h>
 | 
					#include <stringobject.h>
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if PY_VERSION_HEX < 0x02050000
 | 
					#if PY_VERSION_HEX < 0x02060000
 | 
				
			||||||
#  error "psycopg requires Python >= 2.5"
 | 
					#  error "psycopg requires Python >= 2.6"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* hash() return size changed around version 3.2a4 on 64bit platforms.  Before
 | 
					/* hash() return size changed around version 3.2a4 on 64bit platforms.  Before
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										55
									
								
								psycopg/replication_connection.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								psycopg/replication_connection.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,55 @@
 | 
				
			||||||
 | 
					/* replication_connection.h - definition for the psycopg replication connection type
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 Daniele Varrazzo <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef PSYCOPG_REPLICATION_CONNECTION_H
 | 
				
			||||||
 | 
					#define PSYCOPG_REPLICATION_CONNECTION_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/connection.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern HIDDEN PyTypeObject replicationConnectionType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct replicationConnectionObject {
 | 
				
			||||||
 | 
					    connectionObject conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    long int type;
 | 
				
			||||||
 | 
					} replicationConnectionObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The funny constant values should help to avoid mixups with some
 | 
				
			||||||
 | 
					   commonly used numbers like 1 and 2. */
 | 
				
			||||||
 | 
					#define REPLICATION_PHYSICAL 12345678
 | 
				
			||||||
 | 
					#define REPLICATION_LOGICAL  87654321
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern HIDDEN PyObject *replicationPhysicalConst;
 | 
				
			||||||
 | 
					extern HIDDEN PyObject *replicationLogicalConst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !defined(PSYCOPG_REPLICATION_CONNECTION_H) */
 | 
				
			||||||
							
								
								
									
										221
									
								
								psycopg/replication_connection_type.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								psycopg/replication_connection_type.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,221 @@
 | 
				
			||||||
 | 
					/* replication_connection_type.c - python interface to replication connection objects
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 Daniele Varrazzo <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PSYCOPG_MODULE
 | 
				
			||||||
 | 
					#include "psycopg/psycopg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/replication_connection.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_message.h"
 | 
				
			||||||
 | 
					#include "psycopg/green.h"
 | 
				
			||||||
 | 
					#include "psycopg/pqpath.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_repl_conn_type_doc \
 | 
				
			||||||
 | 
					"replication_type -- the replication connection type"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_repl_conn_get_type(replicationConnectionObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    connectionObject *conn = &self->conn;
 | 
				
			||||||
 | 
					    PyObject *res = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CONN_CLOSED(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (self->type == REPLICATION_PHYSICAL) {
 | 
				
			||||||
 | 
					        res = replicationPhysicalConst;
 | 
				
			||||||
 | 
					    } else if (self->type == REPLICATION_LOGICAL) {
 | 
				
			||||||
 | 
					        res = replicationLogicalConst;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_TypeError, "unknown replication type constant: %ld", self->type);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_XINCREF(res);
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					replicationConnection_init(PyObject *obj, PyObject *args, PyObject *kwargs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    replicationConnectionObject *self = (replicationConnectionObject *)obj;
 | 
				
			||||||
 | 
					    PyObject *dsn = NULL, *replication_type = NULL,
 | 
				
			||||||
 | 
					        *item = NULL, *ext = NULL, *make_dsn = NULL,
 | 
				
			||||||
 | 
					        *extras = NULL, *cursor = NULL;
 | 
				
			||||||
 | 
					    int async = 0;
 | 
				
			||||||
 | 
					    int ret = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* 'replication_type' is not actually optional, but there's no
 | 
				
			||||||
 | 
					       good way to put it before 'async' in the list */
 | 
				
			||||||
 | 
					    static char *kwlist[] = {"dsn", "async", "replication_type", NULL};
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|iO", kwlist,
 | 
				
			||||||
 | 
					                                     &dsn, &async, &replication_type)) { return ret; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					      We have to call make_dsn() to add replication-specific
 | 
				
			||||||
 | 
					      connection parameters, because the DSN might be an URI (if there
 | 
				
			||||||
 | 
					      were no keyword arguments to connect() it is passed unchanged).
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    /* we reuse args and kwargs to call make_dsn() and parent type's tp_init() */
 | 
				
			||||||
 | 
					    if (!(kwargs = PyDict_New())) { return ret; }
 | 
				
			||||||
 | 
					    Py_INCREF(args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* we also reuse the dsn to hold the result of the make_dsn() call */
 | 
				
			||||||
 | 
					    Py_INCREF(dsn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!(ext = PyImport_ImportModule("psycopg2.extensions"))) { goto exit; }
 | 
				
			||||||
 | 
					    if (!(make_dsn = PyObject_GetAttrString(ext, "make_dsn"))) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* all the nice stuff is located in python-level ReplicationCursor class */
 | 
				
			||||||
 | 
					    if (!(extras = PyImport_ImportModule("psycopg2.extras"))) { goto exit; }
 | 
				
			||||||
 | 
					    if (!(cursor = PyObject_GetAttrString(extras, "ReplicationCursor"))) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* checking the object reference helps to avoid recognizing
 | 
				
			||||||
 | 
					       unrelated integer constants as valid input values */
 | 
				
			||||||
 | 
					    if (replication_type == replicationPhysicalConst) {
 | 
				
			||||||
 | 
					        self->type = REPLICATION_PHYSICAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SET_ITEM(k, v) \
 | 
				
			||||||
 | 
					        if (!(item = Text_FromUTF8(#v))) { goto exit; } \
 | 
				
			||||||
 | 
					        if (PyDict_SetItemString(kwargs, #k, item) != 0) { goto exit; } \
 | 
				
			||||||
 | 
					        Py_DECREF(item); \
 | 
				
			||||||
 | 
					        item = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SET_ITEM(replication, true);
 | 
				
			||||||
 | 
					        SET_ITEM(dbname, replication);  /* required for .pgpass lookup */
 | 
				
			||||||
 | 
					    } else if (replication_type == replicationLogicalConst) {
 | 
				
			||||||
 | 
					        self->type = REPLICATION_LOGICAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        SET_ITEM(replication, database);
 | 
				
			||||||
 | 
					#undef SET_ITEM
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        PyErr_SetString(PyExc_TypeError,
 | 
				
			||||||
 | 
					                        "replication_type must be either REPLICATION_PHYSICAL or REPLICATION_LOGICAL");
 | 
				
			||||||
 | 
					        goto exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_DECREF(args);
 | 
				
			||||||
 | 
					    if (!(args = PyTuple_Pack(1, dsn))) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_DECREF(dsn);
 | 
				
			||||||
 | 
					    if (!(dsn = PyObject_Call(make_dsn, args, kwargs))) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_DECREF(args);
 | 
				
			||||||
 | 
					    if (!(args = Py_BuildValue("(Oi)", dsn, async))) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* only attempt the connection once we've handled all possible errors */
 | 
				
			||||||
 | 
					    if ((ret = connectionType.tp_init(obj, args, NULL)) < 0) { goto exit; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->conn.autocommit = 1;
 | 
				
			||||||
 | 
					    Py_INCREF(self->conn.cursor_factory = cursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
					    Py_XDECREF(item);
 | 
				
			||||||
 | 
					    Py_XDECREF(ext);
 | 
				
			||||||
 | 
					    Py_XDECREF(make_dsn);
 | 
				
			||||||
 | 
					    Py_XDECREF(extras);
 | 
				
			||||||
 | 
					    Py_XDECREF(cursor);
 | 
				
			||||||
 | 
					    Py_XDECREF(dsn);
 | 
				
			||||||
 | 
					    Py_XDECREF(args);
 | 
				
			||||||
 | 
					    Py_XDECREF(kwargs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					replicationConnection_repr(replicationConnectionObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return PyString_FromFormat(
 | 
				
			||||||
 | 
					        "<ReplicationConnection object at %p; dsn: '%s', closed: %ld>",
 | 
				
			||||||
 | 
					        self, self->conn.dsn, self->conn.closed);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object calculated member list */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct PyGetSetDef replicationConnectionObject_getsets[] = {
 | 
				
			||||||
 | 
					    /* override to prevent user tweaking these: */
 | 
				
			||||||
 | 
					    { "autocommit", NULL, NULL, NULL },
 | 
				
			||||||
 | 
					    { "isolation_level", NULL, NULL, NULL },
 | 
				
			||||||
 | 
					    { "set_session", NULL, NULL, NULL },
 | 
				
			||||||
 | 
					    { "set_isolation_level", NULL, NULL, NULL },
 | 
				
			||||||
 | 
					    { "reset", NULL, NULL, NULL },
 | 
				
			||||||
 | 
					    /* an actual getter */
 | 
				
			||||||
 | 
					    { "replication_type",
 | 
				
			||||||
 | 
					      (getter)psyco_repl_conn_get_type, NULL,
 | 
				
			||||||
 | 
					      psyco_repl_conn_type_doc, NULL },
 | 
				
			||||||
 | 
					    {NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object type */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define replicationConnectionType_doc \
 | 
				
			||||||
 | 
					"A replication connection."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyTypeObject replicationConnectionType = {
 | 
				
			||||||
 | 
					    PyVarObject_HEAD_INIT(NULL, 0)
 | 
				
			||||||
 | 
					    "psycopg2.extensions.ReplicationConnection",
 | 
				
			||||||
 | 
					    sizeof(replicationConnectionObject), 0,
 | 
				
			||||||
 | 
					    0,          /*tp_dealloc*/
 | 
				
			||||||
 | 
					    0,          /*tp_print*/
 | 
				
			||||||
 | 
					    0,          /*tp_getattr*/
 | 
				
			||||||
 | 
					    0,          /*tp_setattr*/
 | 
				
			||||||
 | 
					    0,          /*tp_compare*/
 | 
				
			||||||
 | 
					    (reprfunc)replicationConnection_repr, /*tp_repr*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_number*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_sequence*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_mapping*/
 | 
				
			||||||
 | 
					    0,          /*tp_hash*/
 | 
				
			||||||
 | 
					    0,          /*tp_call*/
 | 
				
			||||||
 | 
					    (reprfunc)replicationConnection_repr, /*tp_str*/
 | 
				
			||||||
 | 
					    0,          /*tp_getattro*/
 | 
				
			||||||
 | 
					    0,          /*tp_setattro*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_buffer*/
 | 
				
			||||||
 | 
					    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
 | 
				
			||||||
 | 
					      Py_TPFLAGS_HAVE_GC, /*tp_flags*/
 | 
				
			||||||
 | 
					    replicationConnectionType_doc, /*tp_doc*/
 | 
				
			||||||
 | 
					    0,          /*tp_traverse*/
 | 
				
			||||||
 | 
					    0,          /*tp_clear*/
 | 
				
			||||||
 | 
					    0,          /*tp_richcompare*/
 | 
				
			||||||
 | 
					    0,          /*tp_weaklistoffset*/
 | 
				
			||||||
 | 
					    0,          /*tp_iter*/
 | 
				
			||||||
 | 
					    0,          /*tp_iternext*/
 | 
				
			||||||
 | 
					    0,          /*tp_methods*/
 | 
				
			||||||
 | 
					    0,          /*tp_members*/
 | 
				
			||||||
 | 
					    replicationConnectionObject_getsets, /*tp_getset*/
 | 
				
			||||||
 | 
					    &connectionType, /*tp_base*/
 | 
				
			||||||
 | 
					    0,          /*tp_dict*/
 | 
				
			||||||
 | 
					    0,          /*tp_descr_get*/
 | 
				
			||||||
 | 
					    0,          /*tp_descr_set*/
 | 
				
			||||||
 | 
					    0,          /*tp_dictoffset*/
 | 
				
			||||||
 | 
					    replicationConnection_init, /*tp_init*/
 | 
				
			||||||
 | 
					    0,          /*tp_alloc*/
 | 
				
			||||||
 | 
					    0,          /*tp_new*/
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyObject *replicationPhysicalConst;
 | 
				
			||||||
 | 
					PyObject *replicationLogicalConst;
 | 
				
			||||||
							
								
								
									
										59
									
								
								psycopg/replication_cursor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								psycopg/replication_cursor.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					/* replication_cursor.h - definition for the psycopg replication cursor type
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 Daniele Varrazzo <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef PSYCOPG_REPLICATION_CURSOR_H
 | 
				
			||||||
 | 
					#define PSYCOPG_REPLICATION_CURSOR_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/cursor.h"
 | 
				
			||||||
 | 
					#include "libpq_support.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern HIDDEN PyTypeObject replicationCursorType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct replicationCursorObject {
 | 
				
			||||||
 | 
					    cursorObject cur;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int         consuming:1;      /* if running the consume loop */
 | 
				
			||||||
 | 
					    int         decode:1;         /* if we should use character decoding on the messages */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct timeval last_io;       /* timestamp of the last exchange with the server */
 | 
				
			||||||
 | 
					    struct timeval keepalive_interval;   /* interval for keepalive messages in replication mode */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    XLogRecPtr  write_lsn;        /* LSNs for replication feedback messages */
 | 
				
			||||||
 | 
					    XLogRecPtr  flush_lsn;
 | 
				
			||||||
 | 
					    XLogRecPtr  apply_lsn;
 | 
				
			||||||
 | 
					} replicationCursorObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RAISES_NEG int psyco_repl_curs_datetime_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !defined(PSYCOPG_REPLICATION_CURSOR_H) */
 | 
				
			||||||
							
								
								
									
										315
									
								
								psycopg/replication_cursor_type.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								psycopg/replication_cursor_type.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,315 @@
 | 
				
			||||||
 | 
					/* replication_cursor_type.c - python interface to replication cursor objects
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2015 Daniele Varrazzo <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PSYCOPG_MODULE
 | 
				
			||||||
 | 
					#include "psycopg/psycopg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/replication_cursor.h"
 | 
				
			||||||
 | 
					#include "psycopg/replication_message.h"
 | 
				
			||||||
 | 
					#include "psycopg/green.h"
 | 
				
			||||||
 | 
					#include "psycopg/pqpath.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* python */
 | 
				
			||||||
 | 
					#include "datetime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_repl_curs_start_replication_expert_doc \
 | 
				
			||||||
 | 
					"start_replication_expert(command, decode=False) -- Start replication with a given command."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_repl_curs_start_replication_expert(replicationCursorObject *self,
 | 
				
			||||||
 | 
					                                         PyObject *args, PyObject *kwargs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &self->cur;
 | 
				
			||||||
 | 
					    connectionObject *conn = self->cur.conn;
 | 
				
			||||||
 | 
					    PyObject *res = NULL;
 | 
				
			||||||
 | 
					    char *command;
 | 
				
			||||||
 | 
					    long int decode = 0;
 | 
				
			||||||
 | 
					    static char *kwlist[] = {"command", "decode", NULL};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s|l", kwlist, &command, &decode)) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CURS_CLOSED(curs);
 | 
				
			||||||
 | 
					    EXC_IF_GREEN(start_replication_expert);
 | 
				
			||||||
 | 
					    EXC_IF_TPC_PREPARED(conn, start_replication_expert);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dprintf("psyco_repl_curs_start_replication_expert: '%s'; decode: %ld", command, decode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pq_execute(curs, command, conn->async, 1 /* no_result */, 1 /* no_begin */) >= 0) {
 | 
				
			||||||
 | 
					        res = Py_None;
 | 
				
			||||||
 | 
					        Py_INCREF(res);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self->decode = decode;
 | 
				
			||||||
 | 
					        gettimeofday(&self->last_io, NULL);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_repl_curs_consume_stream_doc \
 | 
				
			||||||
 | 
					"consume_stream(consumer, keepalive_interval=10) -- Consume replication stream."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_repl_curs_consume_stream(replicationCursorObject *self,
 | 
				
			||||||
 | 
					                               PyObject *args, PyObject *kwargs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &self->cur;
 | 
				
			||||||
 | 
					    PyObject *consume = NULL, *res = NULL;
 | 
				
			||||||
 | 
					    double keepalive_interval = 10;
 | 
				
			||||||
 | 
					    static char *kwlist[] = {"consume", "keepalive_interval", NULL};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|id", kwlist,
 | 
				
			||||||
 | 
					                                     &consume, &keepalive_interval)) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CURS_CLOSED(curs);
 | 
				
			||||||
 | 
					    EXC_IF_CURS_ASYNC(curs, consume_stream);
 | 
				
			||||||
 | 
					    EXC_IF_GREEN(consume_stream);
 | 
				
			||||||
 | 
					    EXC_IF_TPC_PREPARED(self->cur.conn, consume_stream);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Dprintf("psyco_repl_curs_consume_stream");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (keepalive_interval < 1.0) {
 | 
				
			||||||
 | 
					        psyco_set_error(ProgrammingError, curs, "keepalive_interval must be >= 1 (sec)");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (self->consuming) {
 | 
				
			||||||
 | 
					        PyErr_SetString(ProgrammingError,
 | 
				
			||||||
 | 
					                        "consume_stream cannot be used when already in the consume loop");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (curs->pgres == NULL || PQresultStatus(curs->pgres) != PGRES_COPY_BOTH) {
 | 
				
			||||||
 | 
					        PyErr_SetString(ProgrammingError,
 | 
				
			||||||
 | 
					                        "consume_stream: not replicating, call start_replication first");
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    CLEARPGRES(curs->pgres);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->consuming = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pq_copy_both(self, consume, keepalive_interval) >= 0) {
 | 
				
			||||||
 | 
					        res = Py_None;
 | 
				
			||||||
 | 
					        Py_INCREF(res);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->consuming = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_repl_curs_read_message_doc \
 | 
				
			||||||
 | 
					"read_message() -- Try reading a replication message from the server (non-blocking)."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_repl_curs_read_message(replicationCursorObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &self->cur;
 | 
				
			||||||
 | 
					    replicationMessageObject *msg = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CURS_CLOSED(curs);
 | 
				
			||||||
 | 
					    EXC_IF_GREEN(read_message);
 | 
				
			||||||
 | 
					    EXC_IF_TPC_PREPARED(self->cur.conn, read_message);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pq_read_replication_message(self, &msg) < 0) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (msg) {
 | 
				
			||||||
 | 
					        return (PyObject *)msg;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_RETURN_NONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_repl_curs_send_feedback_doc \
 | 
				
			||||||
 | 
					"send_feedback(write_lsn=0, flush_lsn=0, apply_lsn=0, reply=False) -- Try sending a replication feedback message to the server and optionally request a reply."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_repl_curs_send_feedback(replicationCursorObject *self,
 | 
				
			||||||
 | 
					                              PyObject *args, PyObject *kwargs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &self->cur;
 | 
				
			||||||
 | 
					    XLogRecPtr write_lsn = 0, flush_lsn = 0, apply_lsn = 0;
 | 
				
			||||||
 | 
					    int reply = 0;
 | 
				
			||||||
 | 
					    static char* kwlist[] = {"write_lsn", "flush_lsn", "apply_lsn", "reply", NULL};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CURS_CLOSED(curs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|KKKi", kwlist,
 | 
				
			||||||
 | 
					                                     &write_lsn, &flush_lsn, &apply_lsn, &reply)) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (write_lsn > self->write_lsn)
 | 
				
			||||||
 | 
					        self->write_lsn = write_lsn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (flush_lsn > self->flush_lsn)
 | 
				
			||||||
 | 
					        self->flush_lsn = flush_lsn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (apply_lsn > self->apply_lsn)
 | 
				
			||||||
 | 
					        self->apply_lsn = apply_lsn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pq_send_replication_feedback(self, reply) < 0) {
 | 
				
			||||||
 | 
					        return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_RETURN_NONE;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RAISES_NEG int
 | 
				
			||||||
 | 
					psyco_repl_curs_datetime_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Dprintf("psyco_repl_curs_datetime_init: datetime init");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PyDateTime_IMPORT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyDateTimeAPI) {
 | 
				
			||||||
 | 
					        PyErr_SetString(PyExc_ImportError, "datetime initialization failed");
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_repl_curs_io_timestamp_doc \
 | 
				
			||||||
 | 
					"io_timestamp -- the timestamp of latest IO with the server"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_repl_curs_get_io_timestamp(replicationCursorObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cursorObject *curs = &self->cur;
 | 
				
			||||||
 | 
					    PyObject *tval, *res = NULL;
 | 
				
			||||||
 | 
					    double seconds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EXC_IF_CURS_CLOSED(curs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    seconds = self->last_io.tv_sec + self->last_io.tv_usec / 1.0e6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tval = Py_BuildValue("(d)", seconds);
 | 
				
			||||||
 | 
					    if (tval) {
 | 
				
			||||||
 | 
					        res = PyDateTime_FromTimestamp(tval);
 | 
				
			||||||
 | 
					        Py_DECREF(tval);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object method list */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct PyMethodDef replicationCursorObject_methods[] = {
 | 
				
			||||||
 | 
					    {"start_replication_expert", (PyCFunction)psyco_repl_curs_start_replication_expert,
 | 
				
			||||||
 | 
					     METH_VARARGS|METH_KEYWORDS, psyco_repl_curs_start_replication_expert_doc},
 | 
				
			||||||
 | 
					    {"consume_stream", (PyCFunction)psyco_repl_curs_consume_stream,
 | 
				
			||||||
 | 
					     METH_VARARGS|METH_KEYWORDS, psyco_repl_curs_consume_stream_doc},
 | 
				
			||||||
 | 
					    {"read_message", (PyCFunction)psyco_repl_curs_read_message,
 | 
				
			||||||
 | 
					     METH_NOARGS, psyco_repl_curs_read_message_doc},
 | 
				
			||||||
 | 
					    {"send_feedback", (PyCFunction)psyco_repl_curs_send_feedback,
 | 
				
			||||||
 | 
					     METH_VARARGS|METH_KEYWORDS, psyco_repl_curs_send_feedback_doc},
 | 
				
			||||||
 | 
					    {NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object calculated member list */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct PyGetSetDef replicationCursorObject_getsets[] = {
 | 
				
			||||||
 | 
					    { "io_timestamp",
 | 
				
			||||||
 | 
					      (getter)psyco_repl_curs_get_io_timestamp, NULL,
 | 
				
			||||||
 | 
					      psyco_repl_curs_io_timestamp_doc, NULL },
 | 
				
			||||||
 | 
					    {NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					replicationCursor_init(PyObject *obj, PyObject *args, PyObject *kwargs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    replicationCursorObject *self = (replicationCursorObject *)obj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->consuming = 0;
 | 
				
			||||||
 | 
					    self->decode = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->write_lsn = 0;
 | 
				
			||||||
 | 
					    self->flush_lsn = 0;
 | 
				
			||||||
 | 
					    self->apply_lsn = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return cursorType.tp_init(obj, args, kwargs);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					replicationCursor_repr(replicationCursorObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return PyString_FromFormat(
 | 
				
			||||||
 | 
					        "<ReplicationCursor object at %p; closed: %d>", self, self->cur.closed);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object type */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define replicationCursorType_doc \
 | 
				
			||||||
 | 
					"A database replication cursor."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyTypeObject replicationCursorType = {
 | 
				
			||||||
 | 
					    PyVarObject_HEAD_INIT(NULL, 0)
 | 
				
			||||||
 | 
					    "psycopg2.extensions.ReplicationCursor",
 | 
				
			||||||
 | 
					    sizeof(replicationCursorObject), 0,
 | 
				
			||||||
 | 
					    0,          /*tp_dealloc*/
 | 
				
			||||||
 | 
					    0,          /*tp_print*/
 | 
				
			||||||
 | 
					    0,          /*tp_getattr*/
 | 
				
			||||||
 | 
					    0,          /*tp_setattr*/
 | 
				
			||||||
 | 
					    0,          /*tp_compare*/
 | 
				
			||||||
 | 
					    (reprfunc)replicationCursor_repr, /*tp_repr*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_number*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_sequence*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_mapping*/
 | 
				
			||||||
 | 
					    0,          /*tp_hash*/
 | 
				
			||||||
 | 
					    0,          /*tp_call*/
 | 
				
			||||||
 | 
					    (reprfunc)replicationCursor_repr, /*tp_str*/
 | 
				
			||||||
 | 
					    0,          /*tp_getattro*/
 | 
				
			||||||
 | 
					    0,          /*tp_setattro*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_buffer*/
 | 
				
			||||||
 | 
					    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_ITER |
 | 
				
			||||||
 | 
					      Py_TPFLAGS_HAVE_GC, /*tp_flags*/
 | 
				
			||||||
 | 
					    replicationCursorType_doc, /*tp_doc*/
 | 
				
			||||||
 | 
					    0,          /*tp_traverse*/
 | 
				
			||||||
 | 
					    0,          /*tp_clear*/
 | 
				
			||||||
 | 
					    0,          /*tp_richcompare*/
 | 
				
			||||||
 | 
					    0,          /*tp_weaklistoffset*/
 | 
				
			||||||
 | 
					    0,          /*tp_iter*/
 | 
				
			||||||
 | 
					    0,          /*tp_iternext*/
 | 
				
			||||||
 | 
					    replicationCursorObject_methods, /*tp_methods*/
 | 
				
			||||||
 | 
					    0,          /*tp_members*/
 | 
				
			||||||
 | 
					    replicationCursorObject_getsets, /*tp_getset*/
 | 
				
			||||||
 | 
					    &cursorType, /*tp_base*/
 | 
				
			||||||
 | 
					    0,          /*tp_dict*/
 | 
				
			||||||
 | 
					    0,          /*tp_descr_get*/
 | 
				
			||||||
 | 
					    0,          /*tp_descr_set*/
 | 
				
			||||||
 | 
					    0,          /*tp_dictoffset*/
 | 
				
			||||||
 | 
					    replicationCursor_init, /*tp_init*/
 | 
				
			||||||
 | 
					    0,          /*tp_alloc*/
 | 
				
			||||||
 | 
					    0,          /*tp_new*/
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										57
									
								
								psycopg/replication_message.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								psycopg/replication_message.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					/* replication_message.h - definition for the psycopg ReplicationMessage type
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2015 Federico Di Gregorio <fog@debian.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef PSYCOPG_REPLICATION_MESSAGE_H
 | 
				
			||||||
 | 
					#define PSYCOPG_REPLICATION_MESSAGE_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "cursor.h"
 | 
				
			||||||
 | 
					#include "libpq_support.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern HIDDEN PyTypeObject replicationMessageType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* the typedef is forward-declared in psycopg.h */
 | 
				
			||||||
 | 
					struct replicationMessageObject {
 | 
				
			||||||
 | 
					    PyObject_HEAD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cursorObject *cursor;
 | 
				
			||||||
 | 
					    PyObject *payload;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int         data_size;
 | 
				
			||||||
 | 
					    XLogRecPtr  data_start;
 | 
				
			||||||
 | 
					    XLogRecPtr  wal_end;
 | 
				
			||||||
 | 
					    int64_t     send_time;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RAISES_NEG int psyco_replmsg_datetime_init(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !defined(PSYCOPG_REPLICATION_MESSAGE_H) */
 | 
				
			||||||
							
								
								
									
										191
									
								
								psycopg/replication_message_type.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								psycopg/replication_message_type.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,191 @@
 | 
				
			||||||
 | 
					/* replication_message_type.c - python interface to ReplcationMessage objects
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2015 Federico Di Gregorio <fog@debian.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PSYCOPG_MODULE
 | 
				
			||||||
 | 
					#include "psycopg/psycopg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/replication_message.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "datetime.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RAISES_NEG int
 | 
				
			||||||
 | 
					psyco_replmsg_datetime_init(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Dprintf("psyco_replmsg_datetime_init: datetime init");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    PyDateTime_IMPORT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyDateTimeAPI) {
 | 
				
			||||||
 | 
					        PyErr_SetString(PyExc_ImportError, "datetime initialization failed");
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					replmsg_repr(replicationMessageObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return PyString_FromFormat(
 | 
				
			||||||
 | 
					        "<ReplicationMessage object at %p; data_size: %d; "
 | 
				
			||||||
 | 
					        "data_start: "XLOGFMTSTR"; wal_end: "XLOGFMTSTR"; send_time: %ld>",
 | 
				
			||||||
 | 
					        self, self->data_size, XLOGFMTARGS(self->data_start), XLOGFMTARGS(self->wal_end),
 | 
				
			||||||
 | 
					        (long int)self->send_time);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					replmsg_init(PyObject *obj, PyObject *args, PyObject *kwargs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    replicationMessageObject *self = (replicationMessageObject*) obj;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!PyArg_ParseTuple(args, "O!O", &cursorType, &self->cursor, &self->payload))
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					    Py_XINCREF(self->cursor);
 | 
				
			||||||
 | 
					    Py_XINCREF(self->payload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    self->data_size = 0;
 | 
				
			||||||
 | 
					    self->data_start = 0;
 | 
				
			||||||
 | 
					    self->wal_end = 0;
 | 
				
			||||||
 | 
					    self->send_time = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					replmsg_traverse(replicationMessageObject *self, visitproc visit, void *arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Py_VISIT((PyObject* )self->cursor);
 | 
				
			||||||
 | 
					    Py_VISIT(self->payload);
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int
 | 
				
			||||||
 | 
					replmsg_clear(replicationMessageObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Py_CLEAR(self->cursor);
 | 
				
			||||||
 | 
					    Py_CLEAR(self->payload);
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					replmsg_dealloc(PyObject* obj)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyObject_GC_UnTrack(obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    replmsg_clear((replicationMessageObject*) obj);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Py_TYPE(obj)->tp_free(obj);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define psyco_replmsg_send_time_doc \
 | 
				
			||||||
 | 
					"send_time - Timestamp of the replication message departure from the server."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static PyObject *
 | 
				
			||||||
 | 
					psyco_replmsg_get_send_time(replicationMessageObject *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyObject *tval, *res = NULL;
 | 
				
			||||||
 | 
					    double t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    t = (double)self->send_time / USECS_PER_SEC +
 | 
				
			||||||
 | 
					        ((POSTGRES_EPOCH_JDATE - UNIX_EPOCH_JDATE) * SECS_PER_DAY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tval = Py_BuildValue("(d)", t);
 | 
				
			||||||
 | 
					    if (tval) {
 | 
				
			||||||
 | 
					        res = PyDateTime_FromTimestamp(tval);
 | 
				
			||||||
 | 
					        Py_DECREF(tval);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OFFSETOF(x) offsetof(replicationMessageObject, x)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object member list */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct PyMemberDef replicationMessageObject_members[] = {
 | 
				
			||||||
 | 
					    {"cursor", T_OBJECT, OFFSETOF(cursor), READONLY,
 | 
				
			||||||
 | 
					        "Related ReplcationCursor object."},
 | 
				
			||||||
 | 
					    {"payload", T_OBJECT, OFFSETOF(payload), READONLY,
 | 
				
			||||||
 | 
					        "The actual message data."},
 | 
				
			||||||
 | 
					    {"data_size", T_INT, OFFSETOF(data_size), READONLY,
 | 
				
			||||||
 | 
					        "Raw size of the message data in bytes."},
 | 
				
			||||||
 | 
					    {"data_start", T_ULONGLONG, OFFSETOF(data_start), READONLY,
 | 
				
			||||||
 | 
					        "LSN position of the start of this message."},
 | 
				
			||||||
 | 
					    {"wal_end", T_ULONGLONG, OFFSETOF(wal_end), READONLY,
 | 
				
			||||||
 | 
					        "LSN position of the current end of WAL on the server."},
 | 
				
			||||||
 | 
					    {NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct PyGetSetDef replicationMessageObject_getsets[] = {
 | 
				
			||||||
 | 
					    { "send_time", (getter)psyco_replmsg_get_send_time, NULL,
 | 
				
			||||||
 | 
					      psyco_replmsg_send_time_doc, NULL },
 | 
				
			||||||
 | 
					    {NULL}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* object type */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define replicationMessageType_doc \
 | 
				
			||||||
 | 
					"A replication protocol message."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PyTypeObject replicationMessageType = {
 | 
				
			||||||
 | 
					    PyVarObject_HEAD_INIT(NULL, 0)
 | 
				
			||||||
 | 
					    "psycopg2.extensions.ReplicationMessage",
 | 
				
			||||||
 | 
					    sizeof(replicationMessageObject), 0,
 | 
				
			||||||
 | 
					    replmsg_dealloc, /*tp_dealloc*/
 | 
				
			||||||
 | 
					    0,          /*tp_print*/
 | 
				
			||||||
 | 
					    0,          /*tp_getattr*/
 | 
				
			||||||
 | 
					    0,          /*tp_setattr*/
 | 
				
			||||||
 | 
					    0,          /*tp_compare*/
 | 
				
			||||||
 | 
					    (reprfunc)replmsg_repr, /*tp_repr*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_number*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_sequence*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_mapping*/
 | 
				
			||||||
 | 
					    0,          /*tp_hash */
 | 
				
			||||||
 | 
					    0,          /*tp_call*/
 | 
				
			||||||
 | 
					    0,          /*tp_str*/
 | 
				
			||||||
 | 
					    0,          /*tp_getattro*/
 | 
				
			||||||
 | 
					    0,          /*tp_setattro*/
 | 
				
			||||||
 | 
					    0,          /*tp_as_buffer*/
 | 
				
			||||||
 | 
					    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
 | 
				
			||||||
 | 
					      Py_TPFLAGS_HAVE_GC, /*tp_flags*/
 | 
				
			||||||
 | 
					    replicationMessageType_doc, /*tp_doc*/
 | 
				
			||||||
 | 
					    (traverseproc)replmsg_traverse, /*tp_traverse*/
 | 
				
			||||||
 | 
					    (inquiry)replmsg_clear, /*tp_clear*/
 | 
				
			||||||
 | 
					    0,          /*tp_richcompare*/
 | 
				
			||||||
 | 
					    0, /*tp_weaklistoffset*/
 | 
				
			||||||
 | 
					    0, /*tp_iter*/
 | 
				
			||||||
 | 
					    0, /*tp_iternext*/
 | 
				
			||||||
 | 
					    0, /*tp_methods*/
 | 
				
			||||||
 | 
					    replicationMessageObject_members, /*tp_members*/
 | 
				
			||||||
 | 
					    replicationMessageObject_getsets, /*tp_getset*/
 | 
				
			||||||
 | 
					    0, /*tp_base*/
 | 
				
			||||||
 | 
					    0,          /*tp_dict*/
 | 
				
			||||||
 | 
					    0,          /*tp_descr_get*/
 | 
				
			||||||
 | 
					    0,          /*tp_descr_set*/
 | 
				
			||||||
 | 
					    0,          /*tp_dictoffset*/
 | 
				
			||||||
 | 
					    replmsg_init, /*tp_init*/
 | 
				
			||||||
 | 
					    0,          /*tp_alloc*/
 | 
				
			||||||
 | 
					    PyType_GenericNew, /*tp_new*/
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,9 @@ static long int typecast_DATEARRAY_types[] = {1182, 0};
 | 
				
			||||||
static long int typecast_INTERVALARRAY_types[] = {1187, 0};
 | 
					static long int typecast_INTERVALARRAY_types[] = {1187, 0};
 | 
				
			||||||
static long int typecast_BINARYARRAY_types[] = {1001, 0};
 | 
					static long int typecast_BINARYARRAY_types[] = {1001, 0};
 | 
				
			||||||
static long int typecast_ROWIDARRAY_types[] = {1028, 1013, 0};
 | 
					static long int typecast_ROWIDARRAY_types[] = {1028, 1013, 0};
 | 
				
			||||||
 | 
					static long int typecast_INETARRAY_types[] = {1041, 0};
 | 
				
			||||||
 | 
					static long int typecast_CIDRARRAY_types[] = {651, 0};
 | 
				
			||||||
 | 
					static long int typecast_MACADDRARRAY_types[] = {1040, 0};
 | 
				
			||||||
static long int typecast_UNKNOWN_types[] = {705, 0};
 | 
					static long int typecast_UNKNOWN_types[] = {705, 0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +60,9 @@ static typecastObject_initlist typecast_builtins[] = {
 | 
				
			||||||
  {"BINARYARRAY", typecast_BINARYARRAY_types, typecast_BINARYARRAY_cast, "BINARY"},
 | 
					  {"BINARYARRAY", typecast_BINARYARRAY_types, typecast_BINARYARRAY_cast, "BINARY"},
 | 
				
			||||||
  {"ROWIDARRAY", typecast_ROWIDARRAY_types, typecast_ROWIDARRAY_cast, "ROWID"},
 | 
					  {"ROWIDARRAY", typecast_ROWIDARRAY_types, typecast_ROWIDARRAY_cast, "ROWID"},
 | 
				
			||||||
  {"UNKNOWN", typecast_UNKNOWN_types, typecast_UNKNOWN_cast, NULL},
 | 
					  {"UNKNOWN", typecast_UNKNOWN_types, typecast_UNKNOWN_cast, NULL},
 | 
				
			||||||
 | 
					  {"INETARRAY", typecast_INETARRAY_types, typecast_STRINGARRAY_cast, "STRING"},
 | 
				
			||||||
 | 
					  {"CIDRARRAY", typecast_CIDRARRAY_types, typecast_STRINGARRAY_cast, "STRING"},
 | 
				
			||||||
 | 
					  {"MACADDRARRAY", typecast_MACADDRARRAY_types, typecast_STRINGARRAY_cast, "STRING"},
 | 
				
			||||||
    {NULL, NULL, NULL, NULL}
 | 
					    {NULL, NULL, NULL, NULL}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,8 +50,13 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
 | 
				
			||||||
    Py_ssize_t ql;
 | 
					    Py_ssize_t ql;
 | 
				
			||||||
    int eq = (conn && (conn->equote)) ? 1 : 0;
 | 
					    int eq = (conn && (conn->equote)) ? 1 : 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (len == 0)
 | 
					    if (len == 0) {
 | 
				
			||||||
        len = strlen(from);
 | 
					        len = strlen(from);
 | 
				
			||||||
 | 
					    } else if (strchr(from, '\0') != from + len) {
 | 
				
			||||||
 | 
					        PyErr_Format(PyExc_ValueError, "A string literal cannot contain NUL (0x00) characters.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (to == NULL) {
 | 
					    if (to == NULL) {
 | 
				
			||||||
        to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char));
 | 
					        to = (char *)PyMem_Malloc((len * 2 + 4) * sizeof(char));
 | 
				
			||||||
| 
						 | 
					@ -62,12 +67,10 @@ psycopg_escape_string(connectionObject *conn, const char *from, Py_ssize_t len,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        #if PG_VERSION_NUM >= 80104
 | 
					 | 
				
			||||||
            int err;
 | 
					            int err;
 | 
				
			||||||
            if (conn && conn->pgconn)
 | 
					            if (conn && conn->pgconn)
 | 
				
			||||||
                ql = PQescapeStringConn(conn->pgconn, to+eq+1, from, len, &err);
 | 
					                ql = PQescapeStringConn(conn->pgconn, to+eq+1, from, len, &err);
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
        #endif
 | 
					 | 
				
			||||||
                ql = PQescapeString(to+eq+1, from, len);
 | 
					                ql = PQescapeString(to+eq+1, from, len);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -276,3 +279,32 @@ psycopg_is_text_file(PyObject *f)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Make a dict out of PQconninfoOption array */
 | 
				
			||||||
 | 
					PyObject *
 | 
				
			||||||
 | 
					psycopg_dict_from_conninfo_options(PQconninfoOption *options, int include_password)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    PyObject *dict, *res = NULL;
 | 
				
			||||||
 | 
					    PQconninfoOption *o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!(dict = PyDict_New())) { goto exit; }
 | 
				
			||||||
 | 
					    for (o = options; o->keyword != NULL; o++) {
 | 
				
			||||||
 | 
					        if (o->val != NULL &&
 | 
				
			||||||
 | 
					            (include_password || strcmp(o->keyword, "password") != 0)) {
 | 
				
			||||||
 | 
					            PyObject *value;
 | 
				
			||||||
 | 
					            if (!(value = Text_FromUTF8(o->val))) { goto exit; }
 | 
				
			||||||
 | 
					            if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
 | 
				
			||||||
 | 
					                Py_DECREF(value);
 | 
				
			||||||
 | 
					                goto exit;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Py_DECREF(value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res = dict;
 | 
				
			||||||
 | 
					    dict = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exit:
 | 
				
			||||||
 | 
					    Py_XDECREF(dict);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										76
									
								
								psycopg/win32_support.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								psycopg/win32_support.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,76 @@
 | 
				
			||||||
 | 
					/* win32_support.c - emulate some functions missing on Win32
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2015 Federico Di Gregorio <fog@debian.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PSYCOPG_MODULE
 | 
				
			||||||
 | 
					#include "psycopg/psycopg.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/win32_support.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __MINGW32__
 | 
				
			||||||
 | 
					/* millisecond-precision port of gettimeofday for Win32, taken from
 | 
				
			||||||
 | 
					   src/port/gettimeofday.c in PostgreSQL core */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* FILETIME of Jan 1 1970 00:00:00. */
 | 
				
			||||||
 | 
					static const unsigned __int64 epoch = 116444736000000000ULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * timezone information is stored outside the kernel so tzp isn't used anymore.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Note: this function is not for Win32 high precision timing purpose. See
 | 
				
			||||||
 | 
					 * elapsed_time().
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					gettimeofday(struct timeval * tp, struct timezone * tzp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    FILETIME	file_time;
 | 
				
			||||||
 | 
					    SYSTEMTIME	system_time;
 | 
				
			||||||
 | 
					    ULARGE_INTEGER ularge;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    GetSystemTime(&system_time);
 | 
				
			||||||
 | 
					    SystemTimeToFileTime(&system_time, &file_time);
 | 
				
			||||||
 | 
					    ularge.LowPart = file_time.dwLowDateTime;
 | 
				
			||||||
 | 
					    ularge.HighPart = file_time.dwHighDateTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    tp->tv_sec = (long) ((ularge.QuadPart - epoch) / 10000000L);
 | 
				
			||||||
 | 
					    tp->tv_usec = (long) (system_time.wMilliseconds * 1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif /* !defined(__MINGW32__) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* timersub is missing on mingw */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					timersub(struct timeval *a, struct timeval *b, struct timeval *c)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    c->tv_sec  = a->tv_sec  - b->tv_sec;
 | 
				
			||||||
 | 
					    c->tv_usec = a->tv_usec - b->tv_usec;
 | 
				
			||||||
 | 
					    if (tv_usec < 0) {
 | 
				
			||||||
 | 
					        c->tv_usec += 1000000;
 | 
				
			||||||
 | 
					        c->tv_sec  -= 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif /* defined(_WIN32) */
 | 
				
			||||||
							
								
								
									
										40
									
								
								psycopg/win32_support.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								psycopg/win32_support.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					/* win32_support.h - definitions for win32_support.c
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2003-2015 Federico Di Gregorio <fog@debian.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This file is part of psycopg.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * 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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef PSYCOPG_WIN32_SUPPORT_H
 | 
				
			||||||
 | 
					#define PSYCOPG_WIN32_SUPPORT_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "psycopg/config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef _WIN32
 | 
				
			||||||
 | 
					#ifndef __MINGW32__
 | 
				
			||||||
 | 
					HIDDEN int gettimeofday(struct timeval * tp, struct timezone * tzp);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					HIDDEN void timersub(struct timeval *a, struct timeval *b, struct timeval *c);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !defined(PSYCOPG_WIN32_SUPPORT_H) */
 | 
				
			||||||
| 
						 | 
					@ -85,14 +85,19 @@
 | 
				
			||||||
    <None Include="psycopg\config.h" />
 | 
					    <None Include="psycopg\config.h" />
 | 
				
			||||||
    <None Include="psycopg\connection.h" />
 | 
					    <None Include="psycopg\connection.h" />
 | 
				
			||||||
    <None Include="psycopg\cursor.h" />
 | 
					    <None Include="psycopg\cursor.h" />
 | 
				
			||||||
 | 
					    <None Include="psycopg\libpq_support.h" />
 | 
				
			||||||
    <None Include="psycopg\microprotocols.h" />
 | 
					    <None Include="psycopg\microprotocols.h" />
 | 
				
			||||||
    <None Include="psycopg\microprotocols_proto.h" />
 | 
					    <None Include="psycopg\microprotocols_proto.h" />
 | 
				
			||||||
    <None Include="psycopg\pgtypes.h" />
 | 
					    <None Include="psycopg\pgtypes.h" />
 | 
				
			||||||
    <None Include="psycopg\pqpath.h" />
 | 
					    <None Include="psycopg\pqpath.h" />
 | 
				
			||||||
    <None Include="psycopg\psycopg.h" />
 | 
					    <None Include="psycopg\psycopg.h" />
 | 
				
			||||||
    <None Include="psycopg\python.h" />
 | 
					    <None Include="psycopg\python.h" />
 | 
				
			||||||
 | 
					    <None Include="psycopg\replication_connection.h" />
 | 
				
			||||||
 | 
					    <None Include="psycopg\replication_cursor.h" />
 | 
				
			||||||
 | 
					    <None Include="psycopg\replication_message.h" />
 | 
				
			||||||
    <None Include="psycopg\typecast.h" />
 | 
					    <None Include="psycopg\typecast.h" />
 | 
				
			||||||
    <None Include="psycopg\typecast_binary.h" />
 | 
					    <None Include="psycopg\typecast_binary.h" />
 | 
				
			||||||
 | 
					    <None Include="psycopg\win32_support.h" />
 | 
				
			||||||
    <None Include="scripts\buildtypes.py" />
 | 
					    <None Include="scripts\buildtypes.py" />
 | 
				
			||||||
    <None Include="scripts\maketypes.sh" />
 | 
					    <None Include="scripts\maketypes.sh" />
 | 
				
			||||||
    <None Include="ZPsycopgDA\dtml\add.dtml" />
 | 
					    <None Include="ZPsycopgDA\dtml\add.dtml" />
 | 
				
			||||||
| 
						 | 
					@ -124,6 +129,7 @@
 | 
				
			||||||
    <None Include="tests\test_bugX000.py" />
 | 
					    <None Include="tests\test_bugX000.py" />
 | 
				
			||||||
    <None Include="tests\test_types_extras.py" />
 | 
					    <None Include="tests\test_types_extras.py" />
 | 
				
			||||||
    <None Include="tests\test_connection.py" />
 | 
					    <None Include="tests\test_connection.py" />
 | 
				
			||||||
 | 
					    <None Include="tests\test_replication.py" />
 | 
				
			||||||
    <None Include="tests\test_dates.py" />
 | 
					    <None Include="tests\test_dates.py" />
 | 
				
			||||||
    <None Include="tests\test_lobject.py" />
 | 
					    <None Include="tests\test_lobject.py" />
 | 
				
			||||||
    <None Include="tests\test_quote.py" />
 | 
					    <None Include="tests\test_quote.py" />
 | 
				
			||||||
| 
						 | 
					@ -217,10 +223,14 @@
 | 
				
			||||||
    <Compile Include="psycopg\connection_type.c" />
 | 
					    <Compile Include="psycopg\connection_type.c" />
 | 
				
			||||||
    <Compile Include="psycopg\cursor_int.c" />
 | 
					    <Compile Include="psycopg\cursor_int.c" />
 | 
				
			||||||
    <Compile Include="psycopg\cursor_type.c" />
 | 
					    <Compile Include="psycopg\cursor_type.c" />
 | 
				
			||||||
 | 
					    <Compile Include="psycopg\libpq_support.c" />
 | 
				
			||||||
    <Compile Include="psycopg\microprotocols.c" />
 | 
					    <Compile Include="psycopg\microprotocols.c" />
 | 
				
			||||||
    <Compile Include="psycopg\microprotocols_proto.c" />
 | 
					    <Compile Include="psycopg\microprotocols_proto.c" />
 | 
				
			||||||
    <Compile Include="psycopg\pqpath.c" />
 | 
					    <Compile Include="psycopg\pqpath.c" />
 | 
				
			||||||
    <Compile Include="psycopg\psycopgmodule.c" />
 | 
					    <Compile Include="psycopg\psycopgmodule.c" />
 | 
				
			||||||
 | 
					    <Compile Include="psycopg\replication_connection_type.c" />
 | 
				
			||||||
 | 
					    <Compile Include="psycopg\replication_cursor_type.c" />
 | 
				
			||||||
 | 
					    <Compile Include="psycopg\replication_message_type.c" />
 | 
				
			||||||
    <Compile Include="psycopg\typecast.c" />
 | 
					    <Compile Include="psycopg\typecast.c" />
 | 
				
			||||||
    <Compile Include="psycopg\typecast_array.c" />
 | 
					    <Compile Include="psycopg\typecast_array.c" />
 | 
				
			||||||
    <Compile Include="psycopg\typecast_basic.c" />
 | 
					    <Compile Include="psycopg\typecast_basic.c" />
 | 
				
			||||||
| 
						 | 
					@ -229,6 +239,7 @@
 | 
				
			||||||
    <Compile Include="psycopg\typecast_datetime.c" />
 | 
					    <Compile Include="psycopg\typecast_datetime.c" />
 | 
				
			||||||
    <Compile Include="psycopg\typecast_mxdatetime.c" />
 | 
					    <Compile Include="psycopg\typecast_mxdatetime.c" />
 | 
				
			||||||
    <Compile Include="psycopg\utils.c" />
 | 
					    <Compile Include="psycopg\utils.c" />
 | 
				
			||||||
 | 
					    <Compile Include="psycopg\win32_support.c" />
 | 
				
			||||||
    <Compile Include="psycopg\lobject_int.c" />
 | 
					    <Compile Include="psycopg\lobject_int.c" />
 | 
				
			||||||
    <Compile Include="psycopg\lobject_type.c" />
 | 
					    <Compile Include="psycopg\lobject_type.c" />
 | 
				
			||||||
    <Compile Include="psycopg\adapter_pfloat.c" />
 | 
					    <Compile Include="psycopg\adapter_pfloat.c" />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,8 @@
 | 
				
			||||||
# code defines the DBAPITypeObject fundamental types and warns for
 | 
					# code defines the DBAPITypeObject fundamental types and warns for
 | 
				
			||||||
# undefined types.
 | 
					# undefined types.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sys, os, string, copy
 | 
					import sys
 | 
				
			||||||
from string import split, join, strip
 | 
					from string import split, strip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# here is the list of the foundamental types we want to import from
 | 
					# here is the list of the foundamental types we want to import from
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,6 @@ def error(msg):
 | 
				
			||||||
    """Report an error on stderr."""
 | 
					    """Report an error on stderr."""
 | 
				
			||||||
    sys.stderr.write(msg + '\n')
 | 
					    sys.stderr.write(msg + '\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
# read couples from stdin and build list
 | 
					# read couples from stdin and build list
 | 
				
			||||||
read_types = []
 | 
					read_types = []
 | 
				
			||||||
for l in sys.stdin.readlines():
 | 
					for l in sys.stdin.readlines():
 | 
				
			||||||
| 
						 | 
					@ -98,7 +97,7 @@ for t in basic_types:
 | 
				
			||||||
            found_types[k].append(int(found[0][1]))
 | 
					            found_types[k].append(int(found[0][1]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# now outputs to stdout the right C-style definitions
 | 
					# now outputs to stdout the right C-style definitions
 | 
				
			||||||
stypes = "" ; sstruct = ""
 | 
					stypes = sstruct = ""
 | 
				
			||||||
for t in basic_types:
 | 
					for t in basic_types:
 | 
				
			||||||
    k = t[0]
 | 
					    k = t[0]
 | 
				
			||||||
    s = str(found_types[k])
 | 
					    s = str(found_types[k])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +0,0 @@
 | 
				
			||||||
"""Fixer to change b('string') into b'string'."""
 | 
					 | 
				
			||||||
# Author: Daniele Varrazzo
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import token
 | 
					 | 
				
			||||||
from lib2to3 import fixer_base
 | 
					 | 
				
			||||||
from lib2to3.pytree import Leaf
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class FixB(fixer_base.BaseFix):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    PATTERN = """
 | 
					 | 
				
			||||||
              power< wrapper='b' trailer< '(' arg=[any] ')' > rest=any* >
 | 
					 | 
				
			||||||
              """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def transform(self, node, results):
 | 
					 | 
				
			||||||
        arg = results['arg']
 | 
					 | 
				
			||||||
        wrapper = results["wrapper"]
 | 
					 | 
				
			||||||
        if len(arg) == 1 and arg[0].type == token.STRING:
 | 
					 | 
				
			||||||
            b = Leaf(token.STRING, 'b' + arg[0].value, prefix=wrapper.prefix)
 | 
					 | 
				
			||||||
            node.children = [ b ] + results['rest']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ from collections import defaultdict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from BeautifulSoup import BeautifulSoup as BS
 | 
					from BeautifulSoup import BeautifulSoup as BS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    if len(sys.argv) != 2:
 | 
					    if len(sys.argv) != 2:
 | 
				
			||||||
        print >>sys.stderr, "usage: %s /path/to/errorcodes.py" % sys.argv[0]
 | 
					        print >>sys.stderr, "usage: %s /path/to/errorcodes.py" % sys.argv[0]
 | 
				
			||||||
| 
						 | 
					@ -33,7 +34,7 @@ def main():
 | 
				
			||||||
    file_start = read_base_file(filename)
 | 
					    file_start = read_base_file(filename)
 | 
				
			||||||
    # If you add a version to the list fix the docs (errorcodes.rst, err.rst)
 | 
					    # If you add a version to the list fix the docs (errorcodes.rst, err.rst)
 | 
				
			||||||
    classes, errors = fetch_errors(
 | 
					    classes, errors = fetch_errors(
 | 
				
			||||||
        ['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2', '9.3', '9.4'])
 | 
					        ['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2', '9.3', '9.4', '9.5'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    f = open(filename, "w")
 | 
					    f = open(filename, "w")
 | 
				
			||||||
    for line in file_start:
 | 
					    for line in file_start:
 | 
				
			||||||
| 
						 | 
					@ -41,6 +42,7 @@ def main():
 | 
				
			||||||
    for line in generate_module_data(classes, errors):
 | 
					    for line in generate_module_data(classes, errors):
 | 
				
			||||||
        print >>f, line
 | 
					        print >>f, line
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def read_base_file(filename):
 | 
					def read_base_file(filename):
 | 
				
			||||||
    rv = []
 | 
					    rv = []
 | 
				
			||||||
    for line in open(filename):
 | 
					    for line in open(filename):
 | 
				
			||||||
| 
						 | 
					@ -50,6 +52,7 @@ def read_base_file(filename):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    raise ValueError("can't find the separator. Is this the right file?")
 | 
					    raise ValueError("can't find the separator. Is this the right file?")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_errors_txt(url):
 | 
					def parse_errors_txt(url):
 | 
				
			||||||
    classes = {}
 | 
					    classes = {}
 | 
				
			||||||
    errors = defaultdict(dict)
 | 
					    errors = defaultdict(dict)
 | 
				
			||||||
| 
						 | 
					@ -84,6 +87,7 @@ def parse_errors_txt(url):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return classes, errors
 | 
					    return classes, errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_errors_sgml(url):
 | 
					def parse_errors_sgml(url):
 | 
				
			||||||
    page = BS(urllib2.urlopen(url))
 | 
					    page = BS(urllib2.urlopen(url))
 | 
				
			||||||
    table = page('table')[1]('tbody')[0]
 | 
					    table = page('table')[1]('tbody')[0]
 | 
				
			||||||
| 
						 | 
					@ -130,6 +134,7 @@ errors_txt_url = \
 | 
				
			||||||
    "http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
 | 
					    "http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob_plain;" \
 | 
				
			||||||
    "f=src/backend/utils/errcodes.txt;hb=REL%s_STABLE"
 | 
					    "f=src/backend/utils/errcodes.txt;hb=REL%s_STABLE"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def fetch_errors(versions):
 | 
					def fetch_errors(versions):
 | 
				
			||||||
    classes = {}
 | 
					    classes = {}
 | 
				
			||||||
    errors = defaultdict(dict)
 | 
					    errors = defaultdict(dict)
 | 
				
			||||||
| 
						 | 
					@ -148,6 +153,7 @@ def fetch_errors(versions):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return classes, errors
 | 
					    return classes, errors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def generate_module_data(classes, errors):
 | 
					def generate_module_data(classes, errors):
 | 
				
			||||||
    yield ""
 | 
					    yield ""
 | 
				
			||||||
    yield "# Error classes"
 | 
					    yield "# Error classes"
 | 
				
			||||||
| 
						 | 
					@ -163,7 +169,6 @@ def generate_module_data(classes, errors):
 | 
				
			||||||
        for errcode, errlabel in sorted(errors[clscode].items()):
 | 
					        for errcode, errlabel in sorted(errors[clscode].items()):
 | 
				
			||||||
            yield "%s = %r" % (errlabel, errcode)
 | 
					            yield "%s = %r" % (errlabel, errcode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    sys.exit(main())
 | 
					    sys.exit(main())
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ import unittest
 | 
				
			||||||
from pprint import pprint
 | 
					from pprint import pprint
 | 
				
			||||||
from collections import defaultdict
 | 
					from collections import defaultdict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					def main():
 | 
				
			||||||
    opt = parse_args()
 | 
					    opt = parse_args()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +59,7 @@ def main():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return rv
 | 
					    return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def parse_args():
 | 
					def parse_args():
 | 
				
			||||||
    import optparse
 | 
					    import optparse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -104,4 +106,3 @@ def dump(i, opt):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
    sys.exit(main())
 | 
					    sys.exit(main())
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										60
									
								
								scripts/travis_prepare.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										60
									
								
								scripts/travis_prepare.sh
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Prepare the test databases in Travis CI.
 | 
				
			||||||
 | 
					# The script should be run with sudo.
 | 
				
			||||||
 | 
					# The script is not idempotent: it assumes the machine in a clean state
 | 
				
			||||||
 | 
					# and is designed for a sudo-enabled Trusty environment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set_param () {
 | 
				
			||||||
 | 
					    # Set a parameter in a postgresql.conf file
 | 
				
			||||||
 | 
					    version=$1
 | 
				
			||||||
 | 
					    param=$2
 | 
				
			||||||
 | 
					    value=$3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sed -i "s/^\s*#\?\s*$param.*/$param = $value/" \
 | 
				
			||||||
 | 
					        "/etc/postgresql/$version/psycopg/postgresql.conf"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					create () {
 | 
				
			||||||
 | 
					    version=$1
 | 
				
			||||||
 | 
					    port=$2
 | 
				
			||||||
 | 
					    dbname=psycopg2_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pg_createcluster -p $port --start-conf manual $version psycopg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # for two-phase commit testing
 | 
				
			||||||
 | 
					    set_param "$version" max_prepared_transactions 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # for replication testing
 | 
				
			||||||
 | 
					    set_param "$version" max_wal_senders 5
 | 
				
			||||||
 | 
					    set_param "$version" max_replication_slots 5
 | 
				
			||||||
 | 
					    if [ "$version" == "9.2" -o "$version" == "9.3" ]
 | 
				
			||||||
 | 
					    then
 | 
				
			||||||
 | 
					        set_param "$version" wal_level hot_standby
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        set_param "$version" wal_level logical
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    echo "local replication travis trust" \
 | 
				
			||||||
 | 
					        >> "/etc/postgresql/$version/psycopg/pg_hba.conf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pg_ctlcluster "$version" psycopg start
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sudo -u postgres psql -c "create user travis replication" "port=$port"
 | 
				
			||||||
 | 
					    sudo -u postgres psql -c "create database $dbname" "port=$port"
 | 
				
			||||||
 | 
					    sudo -u postgres psql -c "grant create on database $dbname to travis" "port=$port"
 | 
				
			||||||
 | 
					    sudo -u postgres psql -c "create extension hstore" "port=$port dbname=$dbname"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Would give a permission denied error in the travis build dir
 | 
				
			||||||
 | 
					cd /
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					create 9.6 54396
 | 
				
			||||||
 | 
					create 9.5 54395
 | 
				
			||||||
 | 
					create 9.4 54394
 | 
				
			||||||
 | 
					create 9.3 54393
 | 
				
			||||||
 | 
					create 9.2 54392
 | 
				
			||||||
							
								
								
									
										30
									
								
								scripts/travis_test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										30
									
								
								scripts/travis_test.sh
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Run the tests in all the databases
 | 
				
			||||||
 | 
					# The script is designed for a Trusty environment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					run_test () {
 | 
				
			||||||
 | 
					    version=$1
 | 
				
			||||||
 | 
					    port=$2
 | 
				
			||||||
 | 
					    dbname=psycopg2_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    printf "\n\nRunning tests against PostgreSQL $version\n\n"
 | 
				
			||||||
 | 
					    export PSYCOPG2_TESTDB=$dbname
 | 
				
			||||||
 | 
					    export PSYCOPG2_TESTDB_PORT=$port
 | 
				
			||||||
 | 
					    export PSYCOPG2_TESTDB_USER=travis
 | 
				
			||||||
 | 
					    export PSYCOPG2_TEST_REPL_DSN=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    python -c "from psycopg2 import tests; tests.unittest.main(defaultTest='tests.test_suite')" --verbose
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    printf "\n\nRunning tests against PostgreSQL $version (green mode)\n\n"
 | 
				
			||||||
 | 
					    export PSYCOPG2_TEST_GREEN=1
 | 
				
			||||||
 | 
					    python -c "from psycopg2 import tests; tests.unittest.main(defaultTest='tests.test_suite')" --verbose
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					run_test 9.6 54396
 | 
				
			||||||
 | 
					run_test 9.5 54395
 | 
				
			||||||
 | 
					run_test 9.4 54394
 | 
				
			||||||
 | 
					run_test 9.3 54393
 | 
				
			||||||
 | 
					run_test 9.2 54392
 | 
				
			||||||
							
								
								
									
										15
									
								
								setup.cfg
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								setup.cfg
									
									
									
									
									
								
							| 
						 | 
					@ -7,24 +7,23 @@ define=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# "pg_config" is required to locate PostgreSQL headers and libraries needed to
 | 
					# "pg_config" is required to locate PostgreSQL headers and libraries needed to
 | 
				
			||||||
# build psycopg2. If pg_config is not in the path or is installed under a
 | 
					# build psycopg2. If pg_config is not in the path or is installed under a
 | 
				
			||||||
# different name uncomment the following option and set it to the pg_config
 | 
					# different name set the following option to the pg_config full path.
 | 
				
			||||||
# full path.
 | 
					pg_config=
 | 
				
			||||||
#pg_config=
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Set to 1 to use Python datetime objects for default date/time representation.
 | 
					# Set to 1 to use Python datetime objects for default date/time representation.
 | 
				
			||||||
use_pydatetime=1
 | 
					use_pydatetime=1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# If the build system does not find the mx.DateTime headers, try
 | 
					# If the build system does not find the mx.DateTime headers, try
 | 
				
			||||||
# uncommenting the following line and setting its value to the right path.
 | 
					# setting its value to the right path.
 | 
				
			||||||
#mx_include_dir=
 | 
					mx_include_dir=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# For Windows only:
 | 
					# For Windows only:
 | 
				
			||||||
# Set to 1 if the PostgreSQL library was built with OpenSSL.
 | 
					# Set to 1 if the PostgreSQL library was built with OpenSSL.
 | 
				
			||||||
# Required to link in OpenSSL libraries and dependencies.
 | 
					# Required to link in OpenSSL libraries and dependencies.
 | 
				
			||||||
have_ssl=0
 | 
					have_ssl=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Statically link against the postgresql client library.
 | 
					# Set to 1 to statically link against the postgresql client library.
 | 
				
			||||||
#static_libpq=1
 | 
					static_libpq=0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Add here eventual extra libraries required to link the module.
 | 
					# Add here eventual extra libraries required to link the module.
 | 
				
			||||||
#libraries=
 | 
					libraries=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										82
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								setup.py
									
									
									
									
									
								
							| 
						 | 
					@ -25,38 +25,16 @@ UPDATEs. psycopg2 also provide full asynchronous operations and support
 | 
				
			||||||
for coroutine libraries.
 | 
					for coroutine libraries.
 | 
				
			||||||
"""
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# note: if you are changing the list of supported Python version please fix
 | 
					 | 
				
			||||||
# the docs in install.rst and the /features/ page on the website.
 | 
					 | 
				
			||||||
classifiers = """\
 | 
					 | 
				
			||||||
Development Status :: 5 - Production/Stable
 | 
					 | 
				
			||||||
Intended Audience :: Developers
 | 
					 | 
				
			||||||
License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
 | 
					 | 
				
			||||||
License :: OSI Approved :: Zope Public License
 | 
					 | 
				
			||||||
Programming Language :: Python
 | 
					 | 
				
			||||||
Programming Language :: Python :: 2.5
 | 
					 | 
				
			||||||
Programming Language :: Python :: 2.6
 | 
					 | 
				
			||||||
Programming Language :: Python :: 2.7
 | 
					 | 
				
			||||||
Programming Language :: Python :: 3
 | 
					 | 
				
			||||||
Programming Language :: Python :: 3.1
 | 
					 | 
				
			||||||
Programming Language :: Python :: 3.2
 | 
					 | 
				
			||||||
Programming Language :: Python :: 3.3
 | 
					 | 
				
			||||||
Programming Language :: Python :: 3.4
 | 
					 | 
				
			||||||
Programming Language :: C
 | 
					 | 
				
			||||||
Programming Language :: SQL
 | 
					 | 
				
			||||||
Topic :: Database
 | 
					 | 
				
			||||||
Topic :: Database :: Front-Ends
 | 
					 | 
				
			||||||
Topic :: Software Development
 | 
					 | 
				
			||||||
Topic :: Software Development :: Libraries :: Python Modules
 | 
					 | 
				
			||||||
Operating System :: Microsoft :: Windows
 | 
					 | 
				
			||||||
Operating System :: Unix
 | 
					 | 
				
			||||||
"""
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# Note: The setup.py must be compatible with both Python 2 and 3
 | 
					# Note: The setup.py must be compatible with both Python 2 and 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import subprocess
 | 
					import subprocess
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    from setuptools import setup, Extension
 | 
				
			||||||
 | 
					except ImportError:
 | 
				
			||||||
    from distutils.core import setup, Extension
 | 
					    from distutils.core import setup, Extension
 | 
				
			||||||
from distutils.command.build_ext import build_ext
 | 
					from distutils.command.build_ext import build_ext
 | 
				
			||||||
from distutils.sysconfig import get_python_inc
 | 
					from distutils.sysconfig import get_python_inc
 | 
				
			||||||
| 
						 | 
					@ -72,10 +50,6 @@ else:
 | 
				
			||||||
        # workaround subclass for ticket #153
 | 
					        # workaround subclass for ticket #153
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Configure distutils to run our custom 2to3 fixers as well
 | 
					 | 
				
			||||||
    from lib2to3.refactor import get_fixers_from_package
 | 
					 | 
				
			||||||
    build_py.fixer_names = get_fixers_from_package('lib2to3.fixes') \
 | 
					 | 
				
			||||||
        + [ 'fix_b' ]
 | 
					 | 
				
			||||||
    sys.path.insert(0, 'scripts')
 | 
					    sys.path.insert(0, 'scripts')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
| 
						 | 
					@ -88,6 +62,33 @@ except ImportError:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PSYCOPG_VERSION = '2.7.dev0'
 | 
					PSYCOPG_VERSION = '2.7.dev0'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# note: if you are changing the list of supported Python version please fix
 | 
				
			||||||
 | 
					# the docs in install.rst and the /features/ page on the website.
 | 
				
			||||||
 | 
					classifiers = """\
 | 
				
			||||||
 | 
					Development Status :: 5 - Production/Stable
 | 
				
			||||||
 | 
					Intended Audience :: Developers
 | 
				
			||||||
 | 
					License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
 | 
				
			||||||
 | 
					License :: OSI Approved :: Zope Public License
 | 
				
			||||||
 | 
					Programming Language :: Python
 | 
				
			||||||
 | 
					Programming Language :: Python :: 2.6
 | 
				
			||||||
 | 
					Programming Language :: Python :: 2.7
 | 
				
			||||||
 | 
					Programming Language :: Python :: 3
 | 
				
			||||||
 | 
					Programming Language :: Python :: 3.1
 | 
				
			||||||
 | 
					Programming Language :: Python :: 3.2
 | 
				
			||||||
 | 
					Programming Language :: Python :: 3.3
 | 
				
			||||||
 | 
					Programming Language :: Python :: 3.4
 | 
				
			||||||
 | 
					Programming Language :: Python :: 3.5
 | 
				
			||||||
 | 
					Programming Language :: C
 | 
				
			||||||
 | 
					Programming Language :: SQL
 | 
				
			||||||
 | 
					Topic :: Database
 | 
				
			||||||
 | 
					Topic :: Database :: Front-Ends
 | 
				
			||||||
 | 
					Topic :: Software Development
 | 
				
			||||||
 | 
					Topic :: Software Development :: Libraries :: Python Modules
 | 
				
			||||||
 | 
					Operating System :: Microsoft :: Windows
 | 
				
			||||||
 | 
					Operating System :: Unix
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
version_flags = ['dt', 'dec']
 | 
					version_flags = ['dt', 'dec']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
 | 
					PLATFORM_IS_WINDOWS = sys.platform.lower().startswith('win')
 | 
				
			||||||
| 
						 | 
					@ -301,6 +302,10 @@ class psycopg_build_ext(build_ext):
 | 
				
			||||||
            except AttributeError:
 | 
					            except AttributeError:
 | 
				
			||||||
                ext_path = os.path.join(self.build_lib,
 | 
					                ext_path = os.path.join(self.build_lib,
 | 
				
			||||||
                        'psycopg2', '_psycopg.pyd')
 | 
					                        'psycopg2', '_psycopg.pyd')
 | 
				
			||||||
 | 
					            # Make sure spawn() will work if compile() was never
 | 
				
			||||||
 | 
					            # called. https://github.com/psycopg/psycopg2/issues/380
 | 
				
			||||||
 | 
					            if not self.compiler.initialized:
 | 
				
			||||||
 | 
					                self.compiler.initialize()
 | 
				
			||||||
            self.compiler.spawn(
 | 
					            self.compiler.spawn(
 | 
				
			||||||
                ['mt.exe', '-nologo', '-manifest',
 | 
					                ['mt.exe', '-nologo', '-manifest',
 | 
				
			||||||
                 os.path.join('psycopg', manifest),
 | 
					                 os.path.join('psycopg', manifest),
 | 
				
			||||||
| 
						 | 
					@ -343,6 +348,7 @@ class psycopg_build_ext(build_ext):
 | 
				
			||||||
        self.libraries.append("advapi32")
 | 
					        self.libraries.append("advapi32")
 | 
				
			||||||
        if self.compiler_is_msvc():
 | 
					        if self.compiler_is_msvc():
 | 
				
			||||||
            # MSVC requires an explicit "libpq"
 | 
					            # MSVC requires an explicit "libpq"
 | 
				
			||||||
 | 
					            if "pq" in self.libraries:
 | 
				
			||||||
                self.libraries.remove("pq")
 | 
					                self.libraries.remove("pq")
 | 
				
			||||||
            self.libraries.append("secur32")
 | 
					            self.libraries.append("secur32")
 | 
				
			||||||
            self.libraries.append("libpq")
 | 
					            self.libraries.append("libpq")
 | 
				
			||||||
| 
						 | 
					@ -375,6 +381,11 @@ class psycopg_build_ext(build_ext):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def finalize_options(self):
 | 
					    def finalize_options(self):
 | 
				
			||||||
        """Complete the build system configuration."""
 | 
					        """Complete the build system configuration."""
 | 
				
			||||||
 | 
					        # An empty option in the setup.cfg causes self.libraries to include
 | 
				
			||||||
 | 
					        # an empty string in the list of libraries
 | 
				
			||||||
 | 
					        if self.libraries is not None and not self.libraries.strip():
 | 
				
			||||||
 | 
					            self.libraries = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        build_ext.finalize_options(self)
 | 
					        build_ext.finalize_options(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        pg_config_helper = PostgresConfig(self)
 | 
					        pg_config_helper = PostgresConfig(self)
 | 
				
			||||||
| 
						 | 
					@ -441,6 +452,7 @@ class psycopg_build_ext(build_ext):
 | 
				
			||||||
        if hasattr(self, "finalize_" + sys.platform):
 | 
					        if hasattr(self, "finalize_" + sys.platform):
 | 
				
			||||||
            getattr(self, "finalize_" + sys.platform)()
 | 
					            getattr(self, "finalize_" + sys.platform)()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def is_py_64():
 | 
					def is_py_64():
 | 
				
			||||||
    # sys.maxint not available since Py 3.1;
 | 
					    # sys.maxint not available since Py 3.1;
 | 
				
			||||||
    # sys.maxsize not available before Py 2.6;
 | 
					    # sys.maxsize not available before Py 2.6;
 | 
				
			||||||
| 
						 | 
					@ -462,9 +474,13 @@ data_files = []
 | 
				
			||||||
sources = [
 | 
					sources = [
 | 
				
			||||||
    'psycopgmodule.c',
 | 
					    'psycopgmodule.c',
 | 
				
			||||||
    'green.c', 'pqpath.c', 'utils.c', 'bytes_format.c',
 | 
					    'green.c', 'pqpath.c', 'utils.c', 'bytes_format.c',
 | 
				
			||||||
 | 
					    'libpq_support.c', 'win32_support.c',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'connection_int.c', 'connection_type.c',
 | 
					    'connection_int.c', 'connection_type.c',
 | 
				
			||||||
    'cursor_int.c', 'cursor_type.c',
 | 
					    'cursor_int.c', 'cursor_type.c',
 | 
				
			||||||
 | 
					    'replication_connection_type.c',
 | 
				
			||||||
 | 
					    'replication_cursor_type.c',
 | 
				
			||||||
 | 
					    'replication_message_type.c',
 | 
				
			||||||
    'diagnostics_type.c', 'error_type.c',
 | 
					    'diagnostics_type.c', 'error_type.c',
 | 
				
			||||||
    'lobject_int.c', 'lobject_type.c',
 | 
					    'lobject_int.c', 'lobject_type.c',
 | 
				
			||||||
    'notify_type.c', 'xid_type.c',
 | 
					    'notify_type.c', 'xid_type.c',
 | 
				
			||||||
| 
						 | 
					@ -480,7 +496,11 @@ depends = [
 | 
				
			||||||
    # headers
 | 
					    # headers
 | 
				
			||||||
    'config.h', 'pgtypes.h', 'psycopg.h', 'python.h', 'connection.h',
 | 
					    'config.h', 'pgtypes.h', 'psycopg.h', 'python.h', 'connection.h',
 | 
				
			||||||
    'cursor.h', 'diagnostics.h', 'error.h', 'green.h', 'lobject.h',
 | 
					    'cursor.h', 'diagnostics.h', 'error.h', 'green.h', 'lobject.h',
 | 
				
			||||||
 | 
					    'replication_connection.h',
 | 
				
			||||||
 | 
					    'replication_cursor.h',
 | 
				
			||||||
 | 
					    'replication_message.h',
 | 
				
			||||||
    'notify.h', 'pqpath.h', 'xid.h',
 | 
					    'notify.h', 'pqpath.h', 'xid.h',
 | 
				
			||||||
 | 
					    'libpq_support.h', 'win32_support.h',
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
 | 
					    'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
 | 
				
			||||||
    'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h',
 | 
					    'adapter_list.h', 'adapter_pboolean.h', 'adapter_pdecimal.h',
 | 
				
			||||||
| 
						 | 
					@ -506,7 +526,7 @@ if parser.has_option('build_ext', 'mx_include_dir'):
 | 
				
			||||||
    mxincludedir = parser.get('build_ext', 'mx_include_dir')
 | 
					    mxincludedir = parser.get('build_ext', 'mx_include_dir')
 | 
				
			||||||
else:
 | 
					else:
 | 
				
			||||||
    mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx")
 | 
					    mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx")
 | 
				
			||||||
if os.path.exists(mxincludedir):
 | 
					if mxincludedir.strip() and os.path.exists(mxincludedir):
 | 
				
			||||||
    # Build the support for mx: we will check at runtime if it can be imported
 | 
					    # Build the support for mx: we will check at runtime if it can be imported
 | 
				
			||||||
    include_dirs.append(mxincludedir)
 | 
					    include_dirs.append(mxincludedir)
 | 
				
			||||||
    define_macros.append(('HAVE_MXDATETIME', '1'))
 | 
					    define_macros.append(('HAVE_MXDATETIME', '1'))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,11 +31,14 @@ import test_bugX000
 | 
				
			||||||
import test_bug_gc
 | 
					import test_bug_gc
 | 
				
			||||||
import test_cancel
 | 
					import test_cancel
 | 
				
			||||||
import test_connection
 | 
					import test_connection
 | 
				
			||||||
 | 
					import test_replication
 | 
				
			||||||
import test_copy
 | 
					import test_copy
 | 
				
			||||||
import test_cursor
 | 
					import test_cursor
 | 
				
			||||||
import test_dates
 | 
					import test_dates
 | 
				
			||||||
 | 
					import test_errcodes
 | 
				
			||||||
import test_extras_dictcursor
 | 
					import test_extras_dictcursor
 | 
				
			||||||
import test_green
 | 
					import test_green
 | 
				
			||||||
 | 
					import test_ipaddress
 | 
				
			||||||
import test_lobject
 | 
					import test_lobject
 | 
				
			||||||
import test_module
 | 
					import test_module
 | 
				
			||||||
import test_notify
 | 
					import test_notify
 | 
				
			||||||
| 
						 | 
					@ -44,11 +47,8 @@ import test_quote
 | 
				
			||||||
import test_transaction
 | 
					import test_transaction
 | 
				
			||||||
import test_types_basic
 | 
					import test_types_basic
 | 
				
			||||||
import test_types_extras
 | 
					import test_types_extras
 | 
				
			||||||
 | 
					 | 
				
			||||||
if sys.version_info[:2] >= (2, 5):
 | 
					 | 
				
			||||||
import test_with
 | 
					import test_with
 | 
				
			||||||
else:
 | 
					
 | 
				
			||||||
    test_with = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    # If connection to test db fails, bail out early.
 | 
					    # If connection to test db fails, bail out early.
 | 
				
			||||||
| 
						 | 
					@ -68,11 +68,14 @@ def test_suite():
 | 
				
			||||||
    suite.addTest(test_bug_gc.test_suite())
 | 
					    suite.addTest(test_bug_gc.test_suite())
 | 
				
			||||||
    suite.addTest(test_cancel.test_suite())
 | 
					    suite.addTest(test_cancel.test_suite())
 | 
				
			||||||
    suite.addTest(test_connection.test_suite())
 | 
					    suite.addTest(test_connection.test_suite())
 | 
				
			||||||
 | 
					    suite.addTest(test_replication.test_suite())
 | 
				
			||||||
    suite.addTest(test_copy.test_suite())
 | 
					    suite.addTest(test_copy.test_suite())
 | 
				
			||||||
    suite.addTest(test_cursor.test_suite())
 | 
					    suite.addTest(test_cursor.test_suite())
 | 
				
			||||||
    suite.addTest(test_dates.test_suite())
 | 
					    suite.addTest(test_dates.test_suite())
 | 
				
			||||||
 | 
					    suite.addTest(test_errcodes.test_suite())
 | 
				
			||||||
    suite.addTest(test_extras_dictcursor.test_suite())
 | 
					    suite.addTest(test_extras_dictcursor.test_suite())
 | 
				
			||||||
    suite.addTest(test_green.test_suite())
 | 
					    suite.addTest(test_green.test_suite())
 | 
				
			||||||
 | 
					    suite.addTest(test_ipaddress.test_suite())
 | 
				
			||||||
    suite.addTest(test_lobject.test_suite())
 | 
					    suite.addTest(test_lobject.test_suite())
 | 
				
			||||||
    suite.addTest(test_module.test_suite())
 | 
					    suite.addTest(test_module.test_suite())
 | 
				
			||||||
    suite.addTest(test_notify.test_suite())
 | 
					    suite.addTest(test_notify.test_suite())
 | 
				
			||||||
| 
						 | 
					@ -81,7 +84,6 @@ def test_suite():
 | 
				
			||||||
    suite.addTest(test_transaction.test_suite())
 | 
					    suite.addTest(test_transaction.test_suite())
 | 
				
			||||||
    suite.addTest(test_types_basic.test_suite())
 | 
					    suite.addTest(test_types_basic.test_suite())
 | 
				
			||||||
    suite.addTest(test_types_extras.test_suite())
 | 
					    suite.addTest(test_types_extras.test_suite())
 | 
				
			||||||
    if test_with:
 | 
					 | 
				
			||||||
    suite.addTest(test_with.test_suite())
 | 
					    suite.addTest(test_with.test_suite())
 | 
				
			||||||
    return suite
 | 
					    return suite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,11 +29,11 @@ import psycopg2
 | 
				
			||||||
from psycopg2 import extensions
 | 
					from psycopg2 import extensions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import select
 | 
					 | 
				
			||||||
import StringIO
 | 
					import StringIO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testutils import ConnectingTestCase
 | 
					from testutils import ConnectingTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PollableStub(object):
 | 
					class PollableStub(object):
 | 
				
			||||||
    """A 'pollable' wrapper allowing analysis of the `poll()` calls."""
 | 
					    """A 'pollable' wrapper allowing analysis of the `poll()` calls."""
 | 
				
			||||||
    def __init__(self, pollable):
 | 
					    def __init__(self, pollable):
 | 
				
			||||||
| 
						 | 
					@ -66,24 +66,10 @@ class AsyncTests(ConnectingTestCase):
 | 
				
			||||||
            )''')
 | 
					            )''')
 | 
				
			||||||
        self.wait(curs)
 | 
					        self.wait(curs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def wait(self, cur_or_conn):
 | 
					 | 
				
			||||||
        pollable = cur_or_conn
 | 
					 | 
				
			||||||
        if not hasattr(pollable, 'poll'):
 | 
					 | 
				
			||||||
            pollable = cur_or_conn.connection
 | 
					 | 
				
			||||||
        while True:
 | 
					 | 
				
			||||||
            state = pollable.poll()
 | 
					 | 
				
			||||||
            if state == psycopg2.extensions.POLL_OK:
 | 
					 | 
				
			||||||
                break
 | 
					 | 
				
			||||||
            elif state == psycopg2.extensions.POLL_READ:
 | 
					 | 
				
			||||||
                select.select([pollable], [], [], 10)
 | 
					 | 
				
			||||||
            elif state == psycopg2.extensions.POLL_WRITE:
 | 
					 | 
				
			||||||
                select.select([], [pollable], [], 10)
 | 
					 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                raise Exception("Unexpected result from poll: %r", state)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_connection_setup(self):
 | 
					    def test_connection_setup(self):
 | 
				
			||||||
        cur = self.conn.cursor()
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
        sync_cur = self.sync_conn.cursor()
 | 
					        sync_cur = self.sync_conn.cursor()
 | 
				
			||||||
 | 
					        del cur, sync_cur
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assert_(self.conn.async)
 | 
					        self.assert_(self.conn.async)
 | 
				
			||||||
        self.assert_(not self.sync_conn.async)
 | 
					        self.assert_(not self.sync_conn.async)
 | 
				
			||||||
| 
						 | 
					@ -124,6 +110,7 @@ class AsyncTests(ConnectingTestCase):
 | 
				
			||||||
    def test_async_after_async(self):
 | 
					    def test_async_after_async(self):
 | 
				
			||||||
        cur = self.conn.cursor()
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
        cur2 = self.conn.cursor()
 | 
					        cur2 = self.conn.cursor()
 | 
				
			||||||
 | 
					        del cur2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cur.execute("insert into table1 values (1)")
 | 
					        cur.execute("insert into table1 values (1)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -438,14 +425,14 @@ class AsyncTests(ConnectingTestCase):
 | 
				
			||||||
    def test_async_cursor_gone(self):
 | 
					    def test_async_cursor_gone(self):
 | 
				
			||||||
        import gc
 | 
					        import gc
 | 
				
			||||||
        cur = self.conn.cursor()
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
        cur.execute("select 42;");
 | 
					        cur.execute("select 42;")
 | 
				
			||||||
        del cur
 | 
					        del cur
 | 
				
			||||||
        gc.collect()
 | 
					        gc.collect()
 | 
				
			||||||
        self.assertRaises(psycopg2.InterfaceError, self.wait, self.conn)
 | 
					        self.assertRaises(psycopg2.InterfaceError, self.wait, self.conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # The connection is still usable
 | 
					        # The connection is still usable
 | 
				
			||||||
        cur = self.conn.cursor()
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
        cur.execute("select 42;");
 | 
					        cur.execute("select 42;")
 | 
				
			||||||
        self.wait(self.conn)
 | 
					        self.wait(self.conn)
 | 
				
			||||||
        self.assertEqual(cur.fetchone(), (42,))
 | 
					        self.assertEqual(cur.fetchone(), (42,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -465,4 +452,3 @@ def test_suite():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,7 @@ import psycopg2
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import unittest
 | 
					import unittest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DateTimeAllocationBugTestCase(unittest.TestCase):
 | 
					class DateTimeAllocationBugTestCase(unittest.TestCase):
 | 
				
			||||||
    def test_date_time_allocation_bug(self):
 | 
					    def test_date_time_allocation_bug(self):
 | 
				
			||||||
        d1 = psycopg2.Date(2002, 12, 25)
 | 
					        d1 = psycopg2.Date(2002, 12, 25)
 | 
				
			||||||
| 
						 | 
					@ -35,6 +36,7 @@ class DateTimeAllocationBugTestCase(unittest.TestCase):
 | 
				
			||||||
        t1 = psycopg2.Timestamp(2002, 12, 25, 13, 45, 30)
 | 
					        t1 = psycopg2.Timestamp(2002, 12, 25, 13, 45, 30)
 | 
				
			||||||
        t2 = psycopg2.TimestampFromTicks(
 | 
					        t2 = psycopg2.TimestampFromTicks(
 | 
				
			||||||
            time.mktime((2002, 12, 25, 13, 45, 30, 0, 0, 0)))
 | 
					            time.mktime((2002, 12, 25, 13, 45, 30, 0, 0, 0)))
 | 
				
			||||||
 | 
					        del d1, d2, t1, t2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ import gc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testutils import ConnectingTestCase, skip_if_no_uuid
 | 
					from testutils import ConnectingTestCase, skip_if_no_uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StolenReferenceTestCase(ConnectingTestCase):
 | 
					class StolenReferenceTestCase(ConnectingTestCase):
 | 
				
			||||||
    @skip_if_no_uuid
 | 
					    @skip_if_no_uuid
 | 
				
			||||||
    def test_stolen_reference_bug(self):
 | 
					    def test_stolen_reference_bug(self):
 | 
				
			||||||
| 
						 | 
					@ -41,8 +42,10 @@ class StolenReferenceTestCase(ConnectingTestCase):
 | 
				
			||||||
        curs.execute("select 'b5219e01-19ab-4994-b71e-149225dc51e4'::uuid")
 | 
					        curs.execute("select 'b5219e01-19ab-4994-b71e-149225dc51e4'::uuid")
 | 
				
			||||||
        curs.fetchone()
 | 
					        curs.fetchone()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,6 +32,7 @@ from psycopg2 import extras
 | 
				
			||||||
from testconfig import dsn
 | 
					from testconfig import dsn
 | 
				
			||||||
from testutils import unittest, ConnectingTestCase, skip_before_postgres
 | 
					from testutils import unittest, ConnectingTestCase, skip_before_postgres
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CancelTests(ConnectingTestCase):
 | 
					class CancelTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
| 
						 | 
					@ -71,6 +72,7 @@ class CancelTests(ConnectingTestCase):
 | 
				
			||||||
            except Exception, e:
 | 
					            except Exception, e:
 | 
				
			||||||
                errors.append(e)
 | 
					                errors.append(e)
 | 
				
			||||||
                raise
 | 
					                raise
 | 
				
			||||||
 | 
					            del cur
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        thread1 = threading.Thread(target=neverending, args=(self.conn, ))
 | 
					        thread1 = threading.Thread(target=neverending, args=(self.conn, ))
 | 
				
			||||||
        # wait a bit to make sure that the other thread is already in
 | 
					        # wait a bit to make sure that the other thread is already in
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,16 +27,16 @@ import sys
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
import threading
 | 
					import threading
 | 
				
			||||||
from operator import attrgetter
 | 
					from operator import attrgetter
 | 
				
			||||||
from StringIO import StringIO
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
import psycopg2.errorcodes
 | 
					import psycopg2.errorcodes
 | 
				
			||||||
import psycopg2.extensions
 | 
					from psycopg2 import extensions as ext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from testutils import (
 | 
				
			||||||
 | 
					    unittest, decorate_all_tests, skip_if_no_superuser,
 | 
				
			||||||
 | 
					    skip_before_postgres, skip_after_postgres, skip_before_libpq,
 | 
				
			||||||
 | 
					    ConnectingTestCase, skip_if_tpc_disabled, skip_if_windows)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testutils import unittest, decorate_all_tests, skip_if_no_superuser
 | 
					 | 
				
			||||||
from testutils import skip_before_postgres, skip_after_postgres, skip_before_libpq
 | 
					 | 
				
			||||||
from testutils import ConnectingTestCase, skip_if_tpc_disabled
 | 
					 | 
				
			||||||
from testutils import skip_if_windows
 | 
					 | 
				
			||||||
from testconfig import dsn, dbname
 | 
					from testconfig import dsn, dbname
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,8 +111,14 @@ class ConnectionTests(ConnectingTestCase):
 | 
				
			||||||
        cur = conn.cursor()
 | 
					        cur = conn.cursor()
 | 
				
			||||||
        if self.conn.server_version >= 90300:
 | 
					        if self.conn.server_version >= 90300:
 | 
				
			||||||
            cur.execute("set client_min_messages=debug1")
 | 
					            cur.execute("set client_min_messages=debug1")
 | 
				
			||||||
        cur.execute("create temp table table1 (id serial); create temp table table2 (id serial);")
 | 
					        cur.execute("""
 | 
				
			||||||
        cur.execute("create temp table table3 (id serial); create temp table table4 (id serial);")
 | 
					            create temp table table1 (id serial);
 | 
				
			||||||
 | 
					            create temp table table2 (id serial);
 | 
				
			||||||
 | 
					            """)
 | 
				
			||||||
 | 
					        cur.execute("""
 | 
				
			||||||
 | 
					            create temp table table3 (id serial);
 | 
				
			||||||
 | 
					            create temp table table4 (id serial);
 | 
				
			||||||
 | 
					            """)
 | 
				
			||||||
        self.assertEqual(4, len(conn.notices))
 | 
					        self.assertEqual(4, len(conn.notices))
 | 
				
			||||||
        self.assert_('table1' in conn.notices[0])
 | 
					        self.assert_('table1' in conn.notices[0])
 | 
				
			||||||
        self.assert_('table2' in conn.notices[1])
 | 
					        self.assert_('table2' in conn.notices[1])
 | 
				
			||||||
| 
						 | 
					@ -125,7 +131,8 @@ class ConnectionTests(ConnectingTestCase):
 | 
				
			||||||
        if self.conn.server_version >= 90300:
 | 
					        if self.conn.server_version >= 90300:
 | 
				
			||||||
            cur.execute("set client_min_messages=debug1")
 | 
					            cur.execute("set client_min_messages=debug1")
 | 
				
			||||||
        for i in range(0, 100, 10):
 | 
					        for i in range(0, 100, 10):
 | 
				
			||||||
            sql = " ".join(["create temp table table%d (id serial);" % j for j in range(i, i+10)])
 | 
					            sql = " ".join(["create temp table table%d (id serial);" % j
 | 
				
			||||||
 | 
					                            for j in range(i, i + 10)])
 | 
				
			||||||
            cur.execute(sql)
 | 
					            cur.execute(sql)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(50, len(conn.notices))
 | 
					        self.assertEqual(50, len(conn.notices))
 | 
				
			||||||
| 
						 | 
					@ -140,8 +147,13 @@ class ConnectionTests(ConnectingTestCase):
 | 
				
			||||||
        if self.conn.server_version >= 90300:
 | 
					        if self.conn.server_version >= 90300:
 | 
				
			||||||
            cur.execute("set client_min_messages=debug1")
 | 
					            cur.execute("set client_min_messages=debug1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cur.execute("create temp table table1 (id serial); create temp table table2 (id serial);")
 | 
					        cur.execute("""
 | 
				
			||||||
        cur.execute("create temp table table3 (id serial); create temp table table4 (id serial);")
 | 
					            create temp table table1 (id serial);
 | 
				
			||||||
 | 
					            create temp table table2 (id serial);
 | 
				
			||||||
 | 
					            """)
 | 
				
			||||||
 | 
					        cur.execute("""
 | 
				
			||||||
 | 
					            create temp table table3 (id serial);
 | 
				
			||||||
 | 
					            create temp table table4 (id serial);""")
 | 
				
			||||||
        self.assertEqual(len(conn.notices), 4)
 | 
					        self.assertEqual(len(conn.notices), 4)
 | 
				
			||||||
        self.assert_('table1' in conn.notices.popleft())
 | 
					        self.assert_('table1' in conn.notices.popleft())
 | 
				
			||||||
        self.assert_('table2' in conn.notices.popleft())
 | 
					        self.assert_('table2' in conn.notices.popleft())
 | 
				
			||||||
| 
						 | 
					@ -151,7 +163,8 @@ class ConnectionTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # not limited, but no error
 | 
					        # not limited, but no error
 | 
				
			||||||
        for i in range(0, 100, 10):
 | 
					        for i in range(0, 100, 10):
 | 
				
			||||||
            sql = " ".join(["create temp table table2_%d (id serial);" % j for j in range(i, i+10)])
 | 
					            sql = " ".join(["create temp table table2_%d (id serial);" % j
 | 
				
			||||||
 | 
					                            for j in range(i, i + 10)])
 | 
				
			||||||
            cur.execute(sql)
 | 
					            cur.execute(sql)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]),
 | 
					        self.assertEqual(len([n for n in conn.notices if 'CREATE TABLE' in n]),
 | 
				
			||||||
| 
						 | 
					@ -313,16 +326,17 @@ class ConnectionTests(ConnectingTestCase):
 | 
				
			||||||
class ParseDsnTestCase(ConnectingTestCase):
 | 
					class ParseDsnTestCase(ConnectingTestCase):
 | 
				
			||||||
    def test_parse_dsn(self):
 | 
					    def test_parse_dsn(self):
 | 
				
			||||||
        from psycopg2 import ProgrammingError
 | 
					        from psycopg2 import ProgrammingError
 | 
				
			||||||
        from psycopg2.extensions import parse_dsn
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(parse_dsn('dbname=test user=tester password=secret'),
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ext.parse_dsn('dbname=test user=tester password=secret'),
 | 
				
			||||||
            dict(user='tester', password='secret', dbname='test'),
 | 
					            dict(user='tester', password='secret', dbname='test'),
 | 
				
			||||||
            "simple DSN parsed")
 | 
					            "simple DSN parsed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertRaises(ProgrammingError, parse_dsn,
 | 
					        self.assertRaises(ProgrammingError, ext.parse_dsn,
 | 
				
			||||||
                          "dbname=test 2 user=tester password=secret")
 | 
					                          "dbname=test 2 user=tester password=secret")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(parse_dsn("dbname='test 2' user=tester password=secret"),
 | 
					        self.assertEqual(
 | 
				
			||||||
 | 
					            ext.parse_dsn("dbname='test 2' user=tester password=secret"),
 | 
				
			||||||
            dict(user='tester', password='secret', dbname='test 2'),
 | 
					            dict(user='tester', password='secret', dbname='test 2'),
 | 
				
			||||||
            "DSN with quoting parsed")
 | 
					            "DSN with quoting parsed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -332,7 +346,7 @@ class ParseDsnTestCase(ConnectingTestCase):
 | 
				
			||||||
        raised = False
 | 
					        raised = False
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            # unterminated quote after dbname:
 | 
					            # unterminated quote after dbname:
 | 
				
			||||||
            parse_dsn("dbname='test 2 user=tester password=secret")
 | 
					            ext.parse_dsn("dbname='test 2 user=tester password=secret")
 | 
				
			||||||
        except ProgrammingError, e:
 | 
					        except ProgrammingError, e:
 | 
				
			||||||
            raised = True
 | 
					            raised = True
 | 
				
			||||||
            self.assertTrue(str(e).find('secret') < 0,
 | 
					            self.assertTrue(str(e).find('secret') < 0,
 | 
				
			||||||
| 
						 | 
					@ -343,16 +357,14 @@ class ParseDsnTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @skip_before_libpq(9, 2)
 | 
					    @skip_before_libpq(9, 2)
 | 
				
			||||||
    def test_parse_dsn_uri(self):
 | 
					    def test_parse_dsn_uri(self):
 | 
				
			||||||
        from psycopg2.extensions import parse_dsn
 | 
					        self.assertEqual(ext.parse_dsn('postgresql://tester:secret@/test'),
 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.assertEqual(parse_dsn('postgresql://tester:secret@/test'),
 | 
					 | 
				
			||||||
                         dict(user='tester', password='secret', dbname='test'),
 | 
					                         dict(user='tester', password='secret', dbname='test'),
 | 
				
			||||||
                         "valid URI dsn parsed")
 | 
					                         "valid URI dsn parsed")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        raised = False
 | 
					        raised = False
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            # extra '=' after port value
 | 
					            # extra '=' after port value
 | 
				
			||||||
            parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x')
 | 
					            ext.parse_dsn(dsn='postgresql://tester:secret@/test?port=1111=x')
 | 
				
			||||||
        except psycopg2.ProgrammingError, e:
 | 
					        except psycopg2.ProgrammingError, e:
 | 
				
			||||||
            raised = True
 | 
					            raised = True
 | 
				
			||||||
            self.assertTrue(str(e).find('secret') < 0,
 | 
					            self.assertTrue(str(e).find('secret') < 0,
 | 
				
			||||||
| 
						 | 
					@ -362,24 +374,98 @@ class ParseDsnTestCase(ConnectingTestCase):
 | 
				
			||||||
        self.assertTrue(raised, "ProgrammingError raised due to invalid URI")
 | 
					        self.assertTrue(raised, "ProgrammingError raised due to invalid URI")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_unicode_value(self):
 | 
					    def test_unicode_value(self):
 | 
				
			||||||
        from psycopg2.extensions import parse_dsn
 | 
					 | 
				
			||||||
        snowman = u"\u2603"
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
        d = parse_dsn('dbname=' + snowman)
 | 
					        d = ext.parse_dsn('dbname=' + snowman)
 | 
				
			||||||
        if sys.version_info[0] < 3:
 | 
					        if sys.version_info[0] < 3:
 | 
				
			||||||
            self.assertEqual(d['dbname'], snowman.encode('utf8'))
 | 
					            self.assertEqual(d['dbname'], snowman.encode('utf8'))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.assertEqual(d['dbname'], snowman)
 | 
					            self.assertEqual(d['dbname'], snowman)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_unicode_key(self):
 | 
					    def test_unicode_key(self):
 | 
				
			||||||
        from psycopg2.extensions import parse_dsn
 | 
					 | 
				
			||||||
        snowman = u"\u2603"
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
        self.assertRaises(psycopg2.ProgrammingError, parse_dsn,
 | 
					        self.assertRaises(psycopg2.ProgrammingError, ext.parse_dsn,
 | 
				
			||||||
            snowman + '=' + snowman)
 | 
					            snowman + '=' + snowman)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_bad_param(self):
 | 
					    def test_bad_param(self):
 | 
				
			||||||
        from psycopg2.extensions import parse_dsn
 | 
					        self.assertRaises(TypeError, ext.parse_dsn, None)
 | 
				
			||||||
        self.assertRaises(TypeError, parse_dsn, None)
 | 
					        self.assertRaises(TypeError, ext.parse_dsn, 42)
 | 
				
			||||||
        self.assertRaises(TypeError, parse_dsn, 42)
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MakeDsnTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					    def assertDsnEqual(self, dsn1, dsn2):
 | 
				
			||||||
 | 
					        self.assertEqual(set(dsn1.split()), set(dsn2.split()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_arguments(self):
 | 
				
			||||||
 | 
					        self.assertEqual(ext.make_dsn(), '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_string(self):
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn('')
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, '')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_validation(self):
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
 | 
					            ext.make_dsn, 'dbnamo=a')
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
 | 
					            ext.make_dsn, dbnamo='a')
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
 | 
					            ext.make_dsn, 'dbname=a', nosuchparam='b')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_empty_param(self):
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dbname='sony', password='')
 | 
				
			||||||
 | 
					        self.assertDsnEqual(dsn, "dbname=sony password=''")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_escape(self):
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dbname='hello world')
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, "dbname='hello world'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dbname=r'back\slash')
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, r"dbname=back\\slash")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dbname="quo'te")
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, r"dbname=quo\'te")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dbname="with\ttab")
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, "dbname='with\ttab'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dbname=r"\every thing'")
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, r"dbname='\\every thing\''")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_database_is_a_keyword(self):
 | 
				
			||||||
 | 
					        self.assertEqual(ext.make_dsn(database='sigh'), "dbname=sigh")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_params_merging(self):
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn('dbname=foo host=bar', host='baz')
 | 
				
			||||||
 | 
					        self.assertDsnEqual(dsn, 'dbname=foo host=baz')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn('dbname=foo', user='postgres')
 | 
				
			||||||
 | 
					        self.assertDsnEqual(dsn, 'dbname=foo user=postgres')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_no_dsn_munging(self):
 | 
				
			||||||
 | 
					        dsnin = 'dbname=a host=b user=c password=d'
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(dsnin)
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, dsnin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_libpq(9, 2)
 | 
				
			||||||
 | 
					    def test_url_is_cool(self):
 | 
				
			||||||
 | 
					        url = 'postgresql://tester:secret@/test?application_name=wat'
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(url)
 | 
				
			||||||
 | 
					        self.assertEqual(dsn, url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dsn = ext.make_dsn(url, application_name='woot')
 | 
				
			||||||
 | 
					        self.assertDsnEqual(dsn,
 | 
				
			||||||
 | 
					            'dbname=test user=tester password=secret application_name=woot')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
 | 
					            ext.make_dsn, 'postgresql://tester:secret@/test?nosuch=param')
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
 | 
					            ext.make_dsn, url, nosuch="param")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_libpq(9, 3)
 | 
				
			||||||
 | 
					    def test_get_dsn_parameters(self):
 | 
				
			||||||
 | 
					        conn = self.connect()
 | 
				
			||||||
 | 
					        d = conn.get_dsn_parameters()
 | 
				
			||||||
 | 
					        self.assertEqual(d['dbname'], dbname)  # the only param we can check reliably
 | 
				
			||||||
 | 
					        self.assert_('password' not in d, d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class IsolationLevelsTestCase(ConnectingTestCase):
 | 
					class IsolationLevelsTestCase(ConnectingTestCase):
 | 
				
			||||||
| 
						 | 
					@ -413,7 +499,8 @@ class IsolationLevelsTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        levels = [
 | 
					        levels = [
 | 
				
			||||||
            (None, psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT),
 | 
					            (None, psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT),
 | 
				
			||||||
            ('read uncommitted', psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED),
 | 
					            ('read uncommitted',
 | 
				
			||||||
 | 
					                psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED),
 | 
				
			||||||
            ('read committed', psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED),
 | 
					            ('read committed', psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED),
 | 
				
			||||||
            ('repeatable read', psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ),
 | 
					            ('repeatable read', psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ),
 | 
				
			||||||
            ('serializable', psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE),
 | 
					            ('serializable', psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE),
 | 
				
			||||||
| 
						 | 
					@ -761,13 +848,13 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
 | 
				
			||||||
    def test_status_after_recover(self):
 | 
					    def test_status_after_recover(self):
 | 
				
			||||||
        cnn = self.connect()
 | 
					        cnn = self.connect()
 | 
				
			||||||
        self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
 | 
					        self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
 | 
				
			||||||
        xns = cnn.tpc_recover()
 | 
					        cnn.tpc_recover()
 | 
				
			||||||
        self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
 | 
					        self.assertEqual(psycopg2.extensions.STATUS_READY, cnn.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        cur = cnn.cursor()
 | 
					        cur = cnn.cursor()
 | 
				
			||||||
        cur.execute("select 1")
 | 
					        cur.execute("select 1")
 | 
				
			||||||
        self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
 | 
					        self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
 | 
				
			||||||
        xns = cnn.tpc_recover()
 | 
					        cnn.tpc_recover()
 | 
				
			||||||
        self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
 | 
					        self.assertEqual(psycopg2.extensions.STATUS_BEGIN, cnn.status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_recovered_xids(self):
 | 
					    def test_recovered_xids(self):
 | 
				
			||||||
| 
						 | 
					@ -825,8 +912,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
 | 
				
			||||||
            cnn.close()
 | 
					            cnn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cnn = self.connect()
 | 
					            cnn = self.connect()
 | 
				
			||||||
            xids = [ xid for xid in cnn.tpc_recover()
 | 
					            xids = [x for x in cnn.tpc_recover() if x.database == dbname]
 | 
				
			||||||
                if xid.database == dbname ]
 | 
					 | 
				
			||||||
            self.assertEqual(1, len(xids))
 | 
					            self.assertEqual(1, len(xids))
 | 
				
			||||||
            xid = xids[0]
 | 
					            xid = xids[0]
 | 
				
			||||||
            self.assertEqual(xid.format_id, fid)
 | 
					            self.assertEqual(xid.format_id, fid)
 | 
				
			||||||
| 
						 | 
					@ -847,8 +933,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
 | 
				
			||||||
            cnn.close()
 | 
					            cnn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cnn = self.connect()
 | 
					            cnn = self.connect()
 | 
				
			||||||
            xids = [ xid for xid in cnn.tpc_recover()
 | 
					            xids = [x for x in cnn.tpc_recover() if x.database == dbname]
 | 
				
			||||||
                if xid.database == dbname ]
 | 
					 | 
				
			||||||
            self.assertEqual(1, len(xids))
 | 
					            self.assertEqual(1, len(xids))
 | 
				
			||||||
            xid = xids[0]
 | 
					            xid = xids[0]
 | 
				
			||||||
            self.assertEqual(xid.format_id, None)
 | 
					            self.assertEqual(xid.format_id, None)
 | 
				
			||||||
| 
						 | 
					@ -893,8 +978,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
 | 
				
			||||||
        cnn.tpc_begin(x1)
 | 
					        cnn.tpc_begin(x1)
 | 
				
			||||||
        cnn.tpc_prepare()
 | 
					        cnn.tpc_prepare()
 | 
				
			||||||
        cnn.reset()
 | 
					        cnn.reset()
 | 
				
			||||||
        xid = [ xid for xid in cnn.tpc_recover()
 | 
					        xid = [x for x in cnn.tpc_recover() if x.database == dbname][0]
 | 
				
			||||||
            if xid.database == dbname ][0]
 | 
					 | 
				
			||||||
        self.assertEqual(10, xid.format_id)
 | 
					        self.assertEqual(10, xid.format_id)
 | 
				
			||||||
        self.assertEqual('uni', xid.gtrid)
 | 
					        self.assertEqual('uni', xid.gtrid)
 | 
				
			||||||
        self.assertEqual('code', xid.bqual)
 | 
					        self.assertEqual('code', xid.bqual)
 | 
				
			||||||
| 
						 | 
					@ -909,8 +993,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
 | 
				
			||||||
        cnn.tpc_prepare()
 | 
					        cnn.tpc_prepare()
 | 
				
			||||||
        cnn.reset()
 | 
					        cnn.reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        xid = [ xid for xid in cnn.tpc_recover()
 | 
					        xid = [x for x in cnn.tpc_recover() if x.database == dbname][0]
 | 
				
			||||||
            if xid.database == dbname ][0]
 | 
					 | 
				
			||||||
        self.assertEqual(None, xid.format_id)
 | 
					        self.assertEqual(None, xid.format_id)
 | 
				
			||||||
        self.assertEqual('transaction-id', xid.gtrid)
 | 
					        self.assertEqual('transaction-id', xid.gtrid)
 | 
				
			||||||
        self.assertEqual(None, xid.bqual)
 | 
					        self.assertEqual(None, xid.bqual)
 | 
				
			||||||
| 
						 | 
					@ -929,7 +1012,7 @@ class ConnectionTwoPhaseTests(ConnectingTestCase):
 | 
				
			||||||
        cnn.reset()
 | 
					        cnn.reset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        xids = cnn.tpc_recover()
 | 
					        xids = cnn.tpc_recover()
 | 
				
			||||||
        xid = [ xid for xid in xids if xid.database == dbname ][0]
 | 
					        xid = [x for x in xids if x.database == dbname][0]
 | 
				
			||||||
        self.assertEqual(None, xid.format_id)
 | 
					        self.assertEqual(None, xid.format_id)
 | 
				
			||||||
        self.assertEqual('dict-connection', xid.gtrid)
 | 
					        self.assertEqual('dict-connection', xid.gtrid)
 | 
				
			||||||
        self.assertEqual(None, xid.bqual)
 | 
					        self.assertEqual(None, xid.bqual)
 | 
				
			||||||
| 
						 | 
					@ -1178,17 +1261,6 @@ class AutocommitTests(ConnectingTestCase):
 | 
				
			||||||
        self.assertEqual(cur.fetchone()[0], 'on')
 | 
					        self.assertEqual(cur.fetchone()[0], 'on')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ReplicationTest(ConnectingTestCase):
 | 
					 | 
				
			||||||
    @skip_before_postgres(9, 0)
 | 
					 | 
				
			||||||
    def test_replication_not_supported(self):
 | 
					 | 
				
			||||||
        conn = self.repl_connect()
 | 
					 | 
				
			||||||
        if conn is None: return
 | 
					 | 
				
			||||||
        cur = conn.cursor()
 | 
					 | 
				
			||||||
        f = StringIO()
 | 
					 | 
				
			||||||
        self.assertRaises(psycopg2.NotSupportedError,
 | 
					 | 
				
			||||||
            cur.copy_expert, "START_REPLICATION 0/0", f)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,6 +41,7 @@ if sys.version_info[0] < 3:
 | 
				
			||||||
else:
 | 
					else:
 | 
				
			||||||
    from io import TextIOBase as _base
 | 
					    from io import TextIOBase as _base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MinimalRead(_base):
 | 
					class MinimalRead(_base):
 | 
				
			||||||
    """A file wrapper exposing the minimal interface to copy from."""
 | 
					    """A file wrapper exposing the minimal interface to copy from."""
 | 
				
			||||||
    def __init__(self, f):
 | 
					    def __init__(self, f):
 | 
				
			||||||
| 
						 | 
					@ -52,6 +53,7 @@ class MinimalRead(_base):
 | 
				
			||||||
    def readline(self):
 | 
					    def readline(self):
 | 
				
			||||||
        return self.f.readline()
 | 
					        return self.f.readline()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class MinimalWrite(_base):
 | 
					class MinimalWrite(_base):
 | 
				
			||||||
    """A file wrapper exposing the minimal interface to copy to."""
 | 
					    """A file wrapper exposing the minimal interface to copy to."""
 | 
				
			||||||
    def __init__(self, f):
 | 
					    def __init__(self, f):
 | 
				
			||||||
| 
						 | 
					@ -110,6 +112,7 @@ class CopyTests(ConnectingTestCase):
 | 
				
			||||||
            f.write("%s\n" % (i,))
 | 
					            f.write("%s\n" % (i,))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        f.seek(0)
 | 
					        f.seek(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def cols():
 | 
					        def cols():
 | 
				
			||||||
            raise ZeroDivisionError()
 | 
					            raise ZeroDivisionError()
 | 
				
			||||||
            yield 'id'
 | 
					            yield 'id'
 | 
				
			||||||
| 
						 | 
					@ -209,9 +212,11 @@ class CopyTests(ConnectingTestCase):
 | 
				
			||||||
        exp_size = 123
 | 
					        exp_size = 123
 | 
				
			||||||
        # hack here to leave file as is, only check size when reading
 | 
					        # hack here to leave file as is, only check size when reading
 | 
				
			||||||
        real_read = f.read
 | 
					        real_read = f.read
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def read(_size, f=f, exp_size=exp_size):
 | 
					        def read(_size, f=f, exp_size=exp_size):
 | 
				
			||||||
            self.assertEqual(_size, exp_size)
 | 
					            self.assertEqual(_size, exp_size)
 | 
				
			||||||
            return real_read(_size)
 | 
					            return real_read(_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        f.read = read
 | 
					        f.read = read
 | 
				
			||||||
        curs.copy_expert('COPY tcopy (data) FROM STDIN', f, size=exp_size)
 | 
					        curs.copy_expert('COPY tcopy (data) FROM STDIN', f, size=exp_size)
 | 
				
			||||||
        curs.execute("select data from tcopy;")
 | 
					        curs.execute("select data from tcopy;")
 | 
				
			||||||
| 
						 | 
					@ -316,7 +321,7 @@ try:
 | 
				
			||||||
except psycopg2.ProgrammingError:
 | 
					except psycopg2.ProgrammingError:
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
conn.close()
 | 
					conn.close()
 | 
				
			||||||
""" % { 'dsn': dsn,})
 | 
					""" % {'dsn': dsn})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        proc = Popen([sys.executable, '-c', script_to_py3(script)])
 | 
					        proc = Popen([sys.executable, '-c', script_to_py3(script)])
 | 
				
			||||||
        proc.communicate()
 | 
					        proc.communicate()
 | 
				
			||||||
| 
						 | 
					@ -334,7 +339,7 @@ try:
 | 
				
			||||||
except psycopg2.ProgrammingError:
 | 
					except psycopg2.ProgrammingError:
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
conn.close()
 | 
					conn.close()
 | 
				
			||||||
""" % { 'dsn': dsn,})
 | 
					""" % {'dsn': dsn})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        proc = Popen([sys.executable, '-c', script_to_py3(script)], stdout=PIPE)
 | 
					        proc = Popen([sys.executable, '-c', script_to_py3(script)], stdout=PIPE)
 | 
				
			||||||
        proc.communicate()
 | 
					        proc.communicate()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,10 +26,10 @@ import time
 | 
				
			||||||
import pickle
 | 
					import pickle
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
import psycopg2.extensions
 | 
					import psycopg2.extensions
 | 
				
			||||||
from psycopg2.extensions import b
 | 
					 | 
				
			||||||
from testutils import unittest, ConnectingTestCase, skip_before_postgres
 | 
					from testutils import unittest, ConnectingTestCase, skip_before_postgres
 | 
				
			||||||
from testutils import skip_if_no_namedtuple, skip_if_no_getrefcount
 | 
					from testutils import skip_if_no_namedtuple, skip_if_no_getrefcount
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CursorTests(ConnectingTestCase):
 | 
					class CursorTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_close_idempotent(self):
 | 
					    def test_close_idempotent(self):
 | 
				
			||||||
| 
						 | 
					@ -48,8 +48,10 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
        conn = self.conn
 | 
					        conn = self.conn
 | 
				
			||||||
        cur = conn.cursor()
 | 
					        cur = conn.cursor()
 | 
				
			||||||
        cur.execute("create temp table test_exc (data int);")
 | 
					        cur.execute("create temp table test_exc (data int);")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def buggygen():
 | 
					        def buggygen():
 | 
				
			||||||
            yield 1 // 0
 | 
					            yield 1 // 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertRaises(ZeroDivisionError,
 | 
					        self.assertRaises(ZeroDivisionError,
 | 
				
			||||||
            cur.executemany, "insert into test_exc values (%s)", buggygen())
 | 
					            cur.executemany, "insert into test_exc values (%s)", buggygen())
 | 
				
			||||||
        cur.close()
 | 
					        cur.close()
 | 
				
			||||||
| 
						 | 
					@ -63,28 +65,34 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
        # unicode query containing only ascii data
 | 
					        # unicode query containing only ascii data
 | 
				
			||||||
        cur.execute(u"SELECT 'foo';")
 | 
					        cur.execute(u"SELECT 'foo';")
 | 
				
			||||||
        self.assertEqual('foo', cur.fetchone()[0])
 | 
					        self.assertEqual('foo', cur.fetchone()[0])
 | 
				
			||||||
        self.assertEqual(b("SELECT 'foo';"), cur.mogrify(u"SELECT 'foo';"))
 | 
					        self.assertEqual(b"SELECT 'foo';", cur.mogrify(u"SELECT 'foo';"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn.set_client_encoding('UTF8')
 | 
					        conn.set_client_encoding('UTF8')
 | 
				
			||||||
        snowman = u"\u2603"
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def b(s):
 | 
				
			||||||
 | 
					            if isinstance(s, unicode):
 | 
				
			||||||
 | 
					                return s.encode('utf8')
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                return s
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # unicode query with non-ascii data
 | 
					        # unicode query with non-ascii data
 | 
				
			||||||
        cur.execute(u"SELECT '%s';" % snowman)
 | 
					        cur.execute(u"SELECT '%s';" % snowman)
 | 
				
			||||||
        self.assertEqual(snowman.encode('utf8'), b(cur.fetchone()[0]))
 | 
					        self.assertEqual(snowman.encode('utf8'), b(cur.fetchone()[0]))
 | 
				
			||||||
        self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
 | 
					        self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
 | 
				
			||||||
            cur.mogrify(u"SELECT '%s';" % snowman).replace(b("E'"), b("'")))
 | 
					            cur.mogrify(u"SELECT '%s';" % snowman).replace(b"E'", b"'"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # unicode args
 | 
					        # unicode args
 | 
				
			||||||
        cur.execute("SELECT %s;", (snowman,))
 | 
					        cur.execute("SELECT %s;", (snowman,))
 | 
				
			||||||
        self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0]))
 | 
					        self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0]))
 | 
				
			||||||
        self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
 | 
					        self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
 | 
				
			||||||
            cur.mogrify("SELECT %s;", (snowman,)).replace(b("E'"), b("'")))
 | 
					            cur.mogrify("SELECT %s;", (snowman,)).replace(b"E'", b"'"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # unicode query and args
 | 
					        # unicode query and args
 | 
				
			||||||
        cur.execute(u"SELECT %s;", (snowman,))
 | 
					        cur.execute(u"SELECT %s;", (snowman,))
 | 
				
			||||||
        self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0]))
 | 
					        self.assertEqual(snowman.encode("utf-8"), b(cur.fetchone()[0]))
 | 
				
			||||||
        self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
 | 
					        self.assertEqual(("SELECT '%s';" % snowman).encode('utf8'),
 | 
				
			||||||
            cur.mogrify(u"SELECT %s;", (snowman,)).replace(b("E'"), b("'")))
 | 
					            cur.mogrify(u"SELECT %s;", (snowman,)).replace(b"E'", b"'"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_mogrify_decimal_explodes(self):
 | 
					    def test_mogrify_decimal_explodes(self):
 | 
				
			||||||
        # issue #7: explodes on windows with python 2.5 and psycopg 2.2.2
 | 
					        # issue #7: explodes on windows with python 2.5 and psycopg 2.2.2
 | 
				
			||||||
| 
						 | 
					@ -95,7 +103,7 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn = self.conn
 | 
					        conn = self.conn
 | 
				
			||||||
        cur = conn.cursor()
 | 
					        cur = conn.cursor()
 | 
				
			||||||
        self.assertEqual(b('SELECT 10.3;'),
 | 
					        self.assertEqual(b'SELECT 10.3;',
 | 
				
			||||||
            cur.mogrify("SELECT %s;", (Decimal("10.3"),)))
 | 
					            cur.mogrify("SELECT %s;", (Decimal("10.3"),)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @skip_if_no_getrefcount
 | 
					    @skip_if_no_getrefcount
 | 
				
			||||||
| 
						 | 
					@ -103,8 +111,7 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
        # issue #81: reference leak when a parameter value is referenced
 | 
					        # issue #81: reference leak when a parameter value is referenced
 | 
				
			||||||
        # more than once from a dict.
 | 
					        # more than once from a dict.
 | 
				
			||||||
        cur = self.conn.cursor()
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
        i = lambda x: x
 | 
					        foo = (lambda x: x)('foo') * 10
 | 
				
			||||||
        foo = i('foo') * 10
 | 
					 | 
				
			||||||
        import sys
 | 
					        import sys
 | 
				
			||||||
        nref1 = sys.getrefcount(foo)
 | 
					        nref1 = sys.getrefcount(foo)
 | 
				
			||||||
        cur.mogrify("select %(foo)s, %(foo)s, %(foo)s", {'foo': foo})
 | 
					        cur.mogrify("select %(foo)s, %(foo)s, %(foo)s", {'foo': foo})
 | 
				
			||||||
| 
						 | 
					@ -159,7 +166,8 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        w = ref(curs)
 | 
					        w = ref(curs)
 | 
				
			||||||
        del curs
 | 
					        del curs
 | 
				
			||||||
        import gc; gc.collect()
 | 
					        import gc
 | 
				
			||||||
 | 
					        gc.collect()
 | 
				
			||||||
        self.assert_(w() is None)
 | 
					        self.assert_(w() is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_null_name(self):
 | 
					    def test_null_name(self):
 | 
				
			||||||
| 
						 | 
					@ -194,16 +202,16 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self._create_withhold_table()
 | 
					        self._create_withhold_table()
 | 
				
			||||||
        curs = self.conn.cursor("W")
 | 
					        curs = self.conn.cursor("W")
 | 
				
			||||||
        self.assertEqual(curs.withhold, False);
 | 
					        self.assertEqual(curs.withhold, False)
 | 
				
			||||||
        curs.withhold = True
 | 
					        curs.withhold = True
 | 
				
			||||||
        self.assertEqual(curs.withhold, True);
 | 
					        self.assertEqual(curs.withhold, True)
 | 
				
			||||||
        curs.execute("select data from withhold order by data")
 | 
					        curs.execute("select data from withhold order by data")
 | 
				
			||||||
        self.conn.commit()
 | 
					        self.conn.commit()
 | 
				
			||||||
        self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)])
 | 
					        self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)])
 | 
				
			||||||
        curs.close()
 | 
					        curs.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs = self.conn.cursor("W", withhold=True)
 | 
					        curs = self.conn.cursor("W", withhold=True)
 | 
				
			||||||
        self.assertEqual(curs.withhold, True);
 | 
					        self.assertEqual(curs.withhold, True)
 | 
				
			||||||
        curs.execute("select data from withhold order by data")
 | 
					        curs.execute("select data from withhold order by data")
 | 
				
			||||||
        self.conn.commit()
 | 
					        self.conn.commit()
 | 
				
			||||||
        self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)])
 | 
					        self.assertEqual(curs.fetchall(), [(10,), (20,), (30,)])
 | 
				
			||||||
| 
						 | 
					@ -271,12 +279,12 @@ class CursorTests(ConnectingTestCase):
 | 
				
			||||||
        for t in range(2):
 | 
					        for t in range(2):
 | 
				
			||||||
            if not t:
 | 
					            if not t:
 | 
				
			||||||
                curs = self.conn.cursor("S")
 | 
					                curs = self.conn.cursor("S")
 | 
				
			||||||
                self.assertEqual(curs.scrollable, None);
 | 
					                self.assertEqual(curs.scrollable, None)
 | 
				
			||||||
                curs.scrollable = True
 | 
					                curs.scrollable = True
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                curs = self.conn.cursor("S", scrollable=True)
 | 
					                curs = self.conn.cursor("S", scrollable=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.assertEqual(curs.scrollable, True);
 | 
					            self.assertEqual(curs.scrollable, True)
 | 
				
			||||||
            curs.itersize = 10
 | 
					            curs.itersize = 10
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # complex enough to make postgres cursors declare without
 | 
					            # complex enough to make postgres cursors declare without
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@ import psycopg2
 | 
				
			||||||
from psycopg2.tz import FixedOffsetTimezone, ZERO
 | 
					from psycopg2.tz import FixedOffsetTimezone, ZERO
 | 
				
			||||||
from testutils import unittest, ConnectingTestCase, skip_before_postgres
 | 
					from testutils import unittest, ConnectingTestCase, skip_before_postgres
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CommonDatetimeTestsMixin:
 | 
					class CommonDatetimeTestsMixin:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def execute(self, *args):
 | 
					    def execute(self, *args):
 | 
				
			||||||
| 
						 | 
					@ -355,8 +356,10 @@ class mxDateTimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
 | 
				
			||||||
        psycopg2.extensions.register_type(self.INTERVAL, self.conn)
 | 
					        psycopg2.extensions.register_type(self.INTERVAL, self.conn)
 | 
				
			||||||
        psycopg2.extensions.register_type(psycopg2.extensions.MXDATEARRAY, self.conn)
 | 
					        psycopg2.extensions.register_type(psycopg2.extensions.MXDATEARRAY, self.conn)
 | 
				
			||||||
        psycopg2.extensions.register_type(psycopg2.extensions.MXTIMEARRAY, self.conn)
 | 
					        psycopg2.extensions.register_type(psycopg2.extensions.MXTIMEARRAY, self.conn)
 | 
				
			||||||
        psycopg2.extensions.register_type(psycopg2.extensions.MXDATETIMEARRAY, self.conn)
 | 
					        psycopg2.extensions.register_type(
 | 
				
			||||||
        psycopg2.extensions.register_type(psycopg2.extensions.MXINTERVALARRAY, self.conn)
 | 
					            psycopg2.extensions.MXDATETIMEARRAY, self.conn)
 | 
				
			||||||
 | 
					        psycopg2.extensions.register_type(
 | 
				
			||||||
 | 
					            psycopg2.extensions.MXINTERVALARRAY, self.conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def tearDown(self):
 | 
					    def tearDown(self):
 | 
				
			||||||
        self.conn.close()
 | 
					        self.conn.close()
 | 
				
			||||||
| 
						 | 
					@ -549,22 +552,30 @@ class FixedOffsetTimezoneTests(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_repr_with_positive_offset(self):
 | 
					    def test_repr_with_positive_offset(self):
 | 
				
			||||||
        tzinfo = FixedOffsetTimezone(5 * 60)
 | 
					        tzinfo = FixedOffsetTimezone(5 * 60)
 | 
				
			||||||
        self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=300, name=None)")
 | 
					        self.assertEqual(repr(tzinfo),
 | 
				
			||||||
 | 
					            "psycopg2.tz.FixedOffsetTimezone(offset=300, name=None)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_repr_with_negative_offset(self):
 | 
					    def test_repr_with_negative_offset(self):
 | 
				
			||||||
        tzinfo = FixedOffsetTimezone(-5 * 60)
 | 
					        tzinfo = FixedOffsetTimezone(-5 * 60)
 | 
				
			||||||
        self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=-300, name=None)")
 | 
					        self.assertEqual(repr(tzinfo),
 | 
				
			||||||
 | 
					            "psycopg2.tz.FixedOffsetTimezone(offset=-300, name=None)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_repr_with_name(self):
 | 
					    def test_repr_with_name(self):
 | 
				
			||||||
        tzinfo = FixedOffsetTimezone(name="FOO")
 | 
					        tzinfo = FixedOffsetTimezone(name="FOO")
 | 
				
			||||||
        self.assertEqual(repr(tzinfo), "psycopg2.tz.FixedOffsetTimezone(offset=0, name='FOO')")
 | 
					        self.assertEqual(repr(tzinfo),
 | 
				
			||||||
 | 
					            "psycopg2.tz.FixedOffsetTimezone(offset=0, name='FOO')")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_instance_caching(self):
 | 
					    def test_instance_caching(self):
 | 
				
			||||||
        self.assert_(FixedOffsetTimezone(name="FOO") is FixedOffsetTimezone(name="FOO"))
 | 
					        self.assert_(FixedOffsetTimezone(name="FOO")
 | 
				
			||||||
        self.assert_(FixedOffsetTimezone(7 * 60) is FixedOffsetTimezone(7 * 60))
 | 
					            is FixedOffsetTimezone(name="FOO"))
 | 
				
			||||||
        self.assert_(FixedOffsetTimezone(-9 * 60, 'FOO') is FixedOffsetTimezone(-9 * 60, 'FOO'))
 | 
					        self.assert_(FixedOffsetTimezone(7 * 60)
 | 
				
			||||||
        self.assert_(FixedOffsetTimezone(9 * 60) is not FixedOffsetTimezone(9 * 60, 'FOO'))
 | 
					            is FixedOffsetTimezone(7 * 60))
 | 
				
			||||||
        self.assert_(FixedOffsetTimezone(name='FOO') is not FixedOffsetTimezone(9 * 60, 'FOO'))
 | 
					        self.assert_(FixedOffsetTimezone(-9 * 60, 'FOO')
 | 
				
			||||||
 | 
					            is FixedOffsetTimezone(-9 * 60, 'FOO'))
 | 
				
			||||||
 | 
					        self.assert_(FixedOffsetTimezone(9 * 60)
 | 
				
			||||||
 | 
					            is not FixedOffsetTimezone(9 * 60, 'FOO'))
 | 
				
			||||||
 | 
					        self.assert_(FixedOffsetTimezone(name='FOO')
 | 
				
			||||||
 | 
					            is not FixedOffsetTimezone(9 * 60, 'FOO'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_pickle(self):
 | 
					    def test_pickle(self):
 | 
				
			||||||
        # ticket #135
 | 
					        # ticket #135
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										67
									
								
								tests/test_errcodes.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										67
									
								
								tests/test_errcodes.py
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# test_errcodes.py - unit test for psycopg2.errcodes module
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (C) 2015 Daniele Varrazzo  <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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 testutils import unittest, ConnectingTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    reload
 | 
				
			||||||
 | 
					except NameError:
 | 
				
			||||||
 | 
					    from imp import reload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from threading import Thread
 | 
				
			||||||
 | 
					from psycopg2 import errorcodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ErrocodeTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					    def test_lookup_threadsafe(self):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Increase if it does not fail with KeyError
 | 
				
			||||||
 | 
					        MAX_CYCLES = 2000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        errs = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def f(pg_code='40001'):
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                errorcodes.lookup(pg_code)
 | 
				
			||||||
 | 
					            except Exception, e:
 | 
				
			||||||
 | 
					                errs.append(e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for __ in xrange(MAX_CYCLES):
 | 
				
			||||||
 | 
					            reload(errorcodes)
 | 
				
			||||||
 | 
					            (t1, t2) = (Thread(target=f), Thread(target=f))
 | 
				
			||||||
 | 
					            (t1.start(), t2.start())
 | 
				
			||||||
 | 
					            (t1.join(), t2.join())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if errs:
 | 
				
			||||||
 | 
					                self.fail(
 | 
				
			||||||
 | 
					                    "raised %s errors in %s cycles (first is %s %s)" % (
 | 
				
			||||||
 | 
					                        len(errs), MAX_CYCLES,
 | 
				
			||||||
 | 
					                        errs[0].__class__.__name__, errs[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_suite():
 | 
				
			||||||
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,8 @@ class ExtrasDictCursorTests(ConnectingTestCase):
 | 
				
			||||||
        self.assert_(isinstance(cur, psycopg2.extras.DictCursor))
 | 
					        self.assert_(isinstance(cur, psycopg2.extras.DictCursor))
 | 
				
			||||||
        self.assertEqual(cur.name, None)
 | 
					        self.assertEqual(cur.name, None)
 | 
				
			||||||
        # overridable
 | 
					        # overridable
 | 
				
			||||||
        cur = self.conn.cursor('foo', cursor_factory=psycopg2.extras.NamedTupleCursor)
 | 
					        cur = self.conn.cursor('foo',
 | 
				
			||||||
 | 
					            cursor_factory=psycopg2.extras.NamedTupleCursor)
 | 
				
			||||||
        self.assertEqual(cur.name, 'foo')
 | 
					        self.assertEqual(cur.name, 'foo')
 | 
				
			||||||
        self.assert_(isinstance(cur, psycopg2.extras.NamedTupleCursor))
 | 
					        self.assert_(isinstance(cur, psycopg2.extras.NamedTupleCursor))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +81,6 @@ class ExtrasDictCursorTests(ConnectingTestCase):
 | 
				
			||||||
        self.failUnless(row[0] == 'bar')
 | 
					        self.failUnless(row[0] == 'bar')
 | 
				
			||||||
        return row
 | 
					        return row
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def testDictCursorWithPlainCursorRealFetchOne(self):
 | 
					    def testDictCursorWithPlainCursorRealFetchOne(self):
 | 
				
			||||||
        self._testWithPlainCursorReal(lambda curs: curs.fetchone())
 | 
					        self._testWithPlainCursorReal(lambda curs: curs.fetchone())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,7 +110,6 @@ class ExtrasDictCursorTests(ConnectingTestCase):
 | 
				
			||||||
        row = getter(curs)
 | 
					        row = getter(curs)
 | 
				
			||||||
        self.failUnless(row['foo'] == 'bar')
 | 
					        self.failUnless(row['foo'] == 'bar')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def testDictCursorWithNamedCursorFetchOne(self):
 | 
					    def testDictCursorWithNamedCursorFetchOne(self):
 | 
				
			||||||
        self._testWithNamedCursor(lambda curs: curs.fetchone())
 | 
					        self._testWithNamedCursor(lambda curs: curs.fetchone())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -146,7 +145,6 @@ class ExtrasDictCursorTests(ConnectingTestCase):
 | 
				
			||||||
        self.failUnless(row['foo'] == 'bar')
 | 
					        self.failUnless(row['foo'] == 'bar')
 | 
				
			||||||
        self.failUnless(row[0] == 'bar')
 | 
					        self.failUnless(row[0] == 'bar')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def testDictCursorRealWithNamedCursorFetchOne(self):
 | 
					    def testDictCursorRealWithNamedCursorFetchOne(self):
 | 
				
			||||||
        self._testWithNamedCursorReal(lambda curs: curs.fetchone())
 | 
					        self._testWithNamedCursorReal(lambda curs: curs.fetchone())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -176,12 +174,12 @@ class ExtrasDictCursorTests(ConnectingTestCase):
 | 
				
			||||||
        self._testIterRowNumber(curs)
 | 
					        self._testIterRowNumber(curs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _testWithNamedCursorReal(self, getter):
 | 
					    def _testWithNamedCursorReal(self, getter):
 | 
				
			||||||
        curs = self.conn.cursor('aname', cursor_factory=psycopg2.extras.RealDictCursor)
 | 
					        curs = self.conn.cursor('aname',
 | 
				
			||||||
 | 
					            cursor_factory=psycopg2.extras.RealDictCursor)
 | 
				
			||||||
        curs.execute("SELECT * FROM ExtrasDictCursorTests")
 | 
					        curs.execute("SELECT * FROM ExtrasDictCursorTests")
 | 
				
			||||||
        row = getter(curs)
 | 
					        row = getter(curs)
 | 
				
			||||||
        self.failUnless(row['foo'] == 'bar')
 | 
					        self.failUnless(row['foo'] == 'bar')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def _testNamedCursorNotGreedy(self, curs):
 | 
					    def _testNamedCursorNotGreedy(self, curs):
 | 
				
			||||||
        curs.itersize = 2
 | 
					        curs.itersize = 2
 | 
				
			||||||
        curs.execute("""select clock_timestamp() as ts from generate_series(1,3)""")
 | 
					        curs.execute("""select clock_timestamp() as ts from generate_series(1,3)""")
 | 
				
			||||||
| 
						 | 
					@ -235,7 +233,7 @@ class NamedTupleCursorTest(ConnectingTestCase):
 | 
				
			||||||
        from psycopg2.extras import NamedTupleConnection
 | 
					        from psycopg2.extras import NamedTupleConnection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from collections import namedtuple
 | 
					            from collections import namedtuple      # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -346,7 +344,7 @@ class NamedTupleCursorTest(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_error_message(self):
 | 
					    def test_error_message(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from collections import namedtuple
 | 
					            from collections import namedtuple          # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            # an import error somewhere
 | 
					            # an import error somewhere
 | 
				
			||||||
            from psycopg2.extras import NamedTupleConnection
 | 
					            from psycopg2.extras import NamedTupleConnection
 | 
				
			||||||
| 
						 | 
					@ -390,6 +388,7 @@ class NamedTupleCursorTest(ConnectingTestCase):
 | 
				
			||||||
        from psycopg2.extras import NamedTupleCursor
 | 
					        from psycopg2.extras import NamedTupleCursor
 | 
				
			||||||
        f_orig = NamedTupleCursor._make_nt
 | 
					        f_orig = NamedTupleCursor._make_nt
 | 
				
			||||||
        calls = [0]
 | 
					        calls = [0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def f_patched(self_):
 | 
					        def f_patched(self_):
 | 
				
			||||||
            calls[0] += 1
 | 
					            calls[0] += 1
 | 
				
			||||||
            return f_orig(self_)
 | 
					            return f_orig(self_)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ import psycopg2.extras
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testutils import ConnectingTestCase
 | 
					from testutils import ConnectingTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConnectionStub(object):
 | 
					class ConnectionStub(object):
 | 
				
			||||||
    """A `connection` wrapper allowing analysis of the `poll()` calls."""
 | 
					    """A `connection` wrapper allowing analysis of the `poll()` calls."""
 | 
				
			||||||
    def __init__(self, conn):
 | 
					    def __init__(self, conn):
 | 
				
			||||||
| 
						 | 
					@ -43,6 +44,7 @@ class ConnectionStub(object):
 | 
				
			||||||
        self.polls.append(rv)
 | 
					        self.polls.append(rv)
 | 
				
			||||||
        return rv
 | 
					        return rv
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class GreenTestCase(ConnectingTestCase):
 | 
					class GreenTestCase(ConnectingTestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self._cb = psycopg2.extensions.get_wait_callback()
 | 
					        self._cb = psycopg2.extensions.get_wait_callback()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										131
									
								
								tests/test_ipaddress.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										131
									
								
								tests/test_ipaddress.py
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,131 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					# # test_ipaddress.py - tests for ipaddress support #
 | 
				
			||||||
 | 
					# Copyright (C) 2016 Daniele Varrazzo  <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from __future__ import unicode_literals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					from functools import wraps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from testutils import unittest, ConnectingTestCase, decorate_all_tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import psycopg2
 | 
				
			||||||
 | 
					import psycopg2.extras
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def skip_if_no_ipaddress(f):
 | 
				
			||||||
 | 
					    @wraps(f)
 | 
				
			||||||
 | 
					    def skip_if_no_ipaddress_(self):
 | 
				
			||||||
 | 
					        if sys.version_info[:2] < (3, 3):
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                import ipaddress            # noqa
 | 
				
			||||||
 | 
					            except ImportError:
 | 
				
			||||||
 | 
					                return self.skipTest("'ipaddress' module not available")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return f(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return skip_if_no_ipaddress_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NetworkingTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					    def test_inet_cast(self):
 | 
				
			||||||
 | 
					        import ipaddress as ip
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        psycopg2.extras.register_ipaddress(cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select null::inet")
 | 
				
			||||||
 | 
					        self.assert_(cur.fetchone()[0] is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select '127.0.0.1/24'::inet")
 | 
				
			||||||
 | 
					        obj = cur.fetchone()[0]
 | 
				
			||||||
 | 
					        self.assert_(isinstance(obj, ip.IPv4Interface), repr(obj))
 | 
				
			||||||
 | 
					        self.assertEquals(obj, ip.ip_interface('127.0.0.1/24'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select '::ffff:102:300/128'::inet")
 | 
				
			||||||
 | 
					        obj = cur.fetchone()[0]
 | 
				
			||||||
 | 
					        self.assert_(isinstance(obj, ip.IPv6Interface), repr(obj))
 | 
				
			||||||
 | 
					        self.assertEquals(obj, ip.ip_interface('::ffff:102:300/128'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_inet_array_cast(self):
 | 
				
			||||||
 | 
					        import ipaddress as ip
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        psycopg2.extras.register_ipaddress(cur)
 | 
				
			||||||
 | 
					        cur.execute("select '{NULL,127.0.0.1,::ffff:102:300/128}'::inet[]")
 | 
				
			||||||
 | 
					        l = cur.fetchone()[0]
 | 
				
			||||||
 | 
					        self.assert_(l[0] is None)
 | 
				
			||||||
 | 
					        self.assertEquals(l[1], ip.ip_interface('127.0.0.1'))
 | 
				
			||||||
 | 
					        self.assertEquals(l[2], ip.ip_interface('::ffff:102:300/128'))
 | 
				
			||||||
 | 
					        self.assert_(isinstance(l[1], ip.IPv4Interface), l)
 | 
				
			||||||
 | 
					        self.assert_(isinstance(l[2], ip.IPv6Interface), l)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_inet_adapt(self):
 | 
				
			||||||
 | 
					        import ipaddress as ip
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        psycopg2.extras.register_ipaddress(cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select %s", [ip.ip_interface('127.0.0.1/24')])
 | 
				
			||||||
 | 
					        self.assertEquals(cur.fetchone()[0], '127.0.0.1/24')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select %s", [ip.ip_interface('::ffff:102:300/128')])
 | 
				
			||||||
 | 
					        self.assertEquals(cur.fetchone()[0], '::ffff:102:300/128')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_cidr_cast(self):
 | 
				
			||||||
 | 
					        import ipaddress as ip
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        psycopg2.extras.register_ipaddress(cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select null::cidr")
 | 
				
			||||||
 | 
					        self.assert_(cur.fetchone()[0] is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select '127.0.0.0/24'::cidr")
 | 
				
			||||||
 | 
					        obj = cur.fetchone()[0]
 | 
				
			||||||
 | 
					        self.assert_(isinstance(obj, ip.IPv4Network), repr(obj))
 | 
				
			||||||
 | 
					        self.assertEquals(obj, ip.ip_network('127.0.0.0/24'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select '::ffff:102:300/128'::cidr")
 | 
				
			||||||
 | 
					        obj = cur.fetchone()[0]
 | 
				
			||||||
 | 
					        self.assert_(isinstance(obj, ip.IPv6Network), repr(obj))
 | 
				
			||||||
 | 
					        self.assertEquals(obj, ip.ip_network('::ffff:102:300/128'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_cidr_array_cast(self):
 | 
				
			||||||
 | 
					        import ipaddress as ip
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        psycopg2.extras.register_ipaddress(cur)
 | 
				
			||||||
 | 
					        cur.execute("select '{NULL,127.0.0.1,::ffff:102:300/128}'::cidr[]")
 | 
				
			||||||
 | 
					        l = cur.fetchone()[0]
 | 
				
			||||||
 | 
					        self.assert_(l[0] is None)
 | 
				
			||||||
 | 
					        self.assertEquals(l[1], ip.ip_network('127.0.0.1'))
 | 
				
			||||||
 | 
					        self.assertEquals(l[2], ip.ip_network('::ffff:102:300/128'))
 | 
				
			||||||
 | 
					        self.assert_(isinstance(l[1], ip.IPv4Network), l)
 | 
				
			||||||
 | 
					        self.assert_(isinstance(l[2], ip.IPv6Network), l)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_cidr_adapt(self):
 | 
				
			||||||
 | 
					        import ipaddress as ip
 | 
				
			||||||
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
 | 
					        psycopg2.extras.register_ipaddress(cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select %s", [ip.ip_network('127.0.0.0/24')])
 | 
				
			||||||
 | 
					        self.assertEquals(cur.fetchone()[0], '127.0.0.0/24')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.execute("select %s", [ip.ip_network('::ffff:102:300/128')])
 | 
				
			||||||
 | 
					        self.assertEquals(cur.fetchone()[0], '::ffff:102:300/128')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					decorate_all_tests(NetworkingTestCase, skip_if_no_ipaddress)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_suite():
 | 
				
			||||||
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					@ -29,10 +29,10 @@ from functools import wraps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
import psycopg2.extensions
 | 
					import psycopg2.extensions
 | 
				
			||||||
from psycopg2.extensions import b
 | 
					 | 
				
			||||||
from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
 | 
					from testutils import unittest, decorate_all_tests, skip_if_tpc_disabled
 | 
				
			||||||
from testutils import ConnectingTestCase, skip_if_green
 | 
					from testutils import ConnectingTestCase, skip_if_green
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_lo(f):
 | 
					def skip_if_no_lo(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_no_lo_(self):
 | 
					    def skip_if_no_lo_(self):
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        lo2 = self.conn.lobject(lo.oid, "w")
 | 
					        lo2 = self.conn.lobject(lo.oid, "w")
 | 
				
			||||||
        self.assertEqual(lo2.mode[0], "w")
 | 
					        self.assertEqual(lo2.mode[0], "w")
 | 
				
			||||||
        lo2.write(b("some data"))
 | 
					        lo2.write(b"some data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_open_mode_n(self):
 | 
					    def test_open_mode_n(self):
 | 
				
			||||||
        # Openning an object in mode "n" gives us a closed lobject.
 | 
					        # Openning an object in mode "n" gives us a closed lobject.
 | 
				
			||||||
| 
						 | 
					@ -136,7 +136,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
        self.tmpdir = tempfile.mkdtemp()
 | 
					        self.tmpdir = tempfile.mkdtemp()
 | 
				
			||||||
        filename = os.path.join(self.tmpdir, "data.txt")
 | 
					        filename = os.path.join(self.tmpdir, "data.txt")
 | 
				
			||||||
        fp = open(filename, "wb")
 | 
					        fp = open(filename, "wb")
 | 
				
			||||||
        fp.write(b("some data"))
 | 
					        fp.write(b"some data")
 | 
				
			||||||
        fp.close()
 | 
					        fp.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        lo = self.conn.lobject(0, "r", 0, filename)
 | 
					        lo = self.conn.lobject(0, "r", 0, filename)
 | 
				
			||||||
| 
						 | 
					@ -150,7 +150,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_write(self):
 | 
					    def test_write(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        self.assertEqual(lo.write(b("some data")), len("some data"))
 | 
					        self.assertEqual(lo.write(b"some data"), len("some data"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_write_large(self):
 | 
					    def test_write_large(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
| 
						 | 
					@ -159,7 +159,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read(self):
 | 
					    def test_read(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        length = lo.write(b("some data"))
 | 
					        lo.write(b"some data")
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        lo = self.conn.lobject(lo.oid)
 | 
					        lo = self.conn.lobject(lo.oid)
 | 
				
			||||||
| 
						 | 
					@ -170,19 +170,19 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read_binary(self):
 | 
					    def test_read_binary(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        length = lo.write(b("some data"))
 | 
					        lo.write(b"some data")
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        lo = self.conn.lobject(lo.oid, "rb")
 | 
					        lo = self.conn.lobject(lo.oid, "rb")
 | 
				
			||||||
        x = lo.read(4)
 | 
					        x = lo.read(4)
 | 
				
			||||||
        self.assertEqual(type(x), type(b('')))
 | 
					        self.assertEqual(type(x), type(b''))
 | 
				
			||||||
        self.assertEqual(x, b("some"))
 | 
					        self.assertEqual(x, b"some")
 | 
				
			||||||
        self.assertEqual(lo.read(), b(" data"))
 | 
					        self.assertEqual(lo.read(), b" data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read_text(self):
 | 
					    def test_read_text(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        snowman = u"\u2603"
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
        length = lo.write(u"some data " + snowman)
 | 
					        lo.write(u"some data " + snowman)
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        lo = self.conn.lobject(lo.oid, "rt")
 | 
					        lo = self.conn.lobject(lo.oid, "rt")
 | 
				
			||||||
| 
						 | 
					@ -194,7 +194,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
    def test_read_large(self):
 | 
					    def test_read_large(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        data = "data" * 1000000
 | 
					        data = "data" * 1000000
 | 
				
			||||||
        length = lo.write("some" + data)
 | 
					        lo.write("some" + data)
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        lo = self.conn.lobject(lo.oid)
 | 
					        lo = self.conn.lobject(lo.oid)
 | 
				
			||||||
| 
						 | 
					@ -206,7 +206,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_seek_tell(self):
 | 
					    def test_seek_tell(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        length = lo.write(b("some data"))
 | 
					        length = lo.write(b"some data")
 | 
				
			||||||
        self.assertEqual(lo.tell(), length)
 | 
					        self.assertEqual(lo.tell(), length)
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
        lo = self.conn.lobject(lo.oid)
 | 
					        lo = self.conn.lobject(lo.oid)
 | 
				
			||||||
| 
						 | 
					@ -236,7 +236,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_export(self):
 | 
					    def test_export(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        lo.write(b("some data"))
 | 
					        lo.write(b"some data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.tmpdir = tempfile.mkdtemp()
 | 
					        self.tmpdir = tempfile.mkdtemp()
 | 
				
			||||||
        filename = os.path.join(self.tmpdir, "data.txt")
 | 
					        filename = os.path.join(self.tmpdir, "data.txt")
 | 
				
			||||||
| 
						 | 
					@ -244,7 +244,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
        self.assertTrue(os.path.exists(filename))
 | 
					        self.assertTrue(os.path.exists(filename))
 | 
				
			||||||
        f = open(filename, "rb")
 | 
					        f = open(filename, "rb")
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(f.read(), b("some data"))
 | 
					            self.assertEqual(f.read(), b"some data")
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            f.close()
 | 
					            f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -256,7 +256,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
    def test_write_after_close(self):
 | 
					    def test_write_after_close(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
        self.assertRaises(psycopg2.InterfaceError, lo.write, b("some data"))
 | 
					        self.assertRaises(psycopg2.InterfaceError, lo.write, b"some data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read_after_close(self):
 | 
					    def test_read_after_close(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
| 
						 | 
					@ -281,7 +281,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_export_after_close(self):
 | 
					    def test_export_after_close(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        lo.write(b("some data"))
 | 
					        lo.write(b"some data")
 | 
				
			||||||
        lo.close()
 | 
					        lo.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.tmpdir = tempfile.mkdtemp()
 | 
					        self.tmpdir = tempfile.mkdtemp()
 | 
				
			||||||
| 
						 | 
					@ -290,7 +290,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
        self.assertTrue(os.path.exists(filename))
 | 
					        self.assertTrue(os.path.exists(filename))
 | 
				
			||||||
        f = open(filename, "rb")
 | 
					        f = open(filename, "rb")
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(f.read(), b("some data"))
 | 
					            self.assertEqual(f.read(), b"some data")
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            f.close()
 | 
					            f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -307,7 +307,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
        self.lo_oid = lo.oid
 | 
					        self.lo_oid = lo.oid
 | 
				
			||||||
        self.conn.commit()
 | 
					        self.conn.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertRaises(psycopg2.ProgrammingError, lo.write, b("some data"))
 | 
					        self.assertRaises(psycopg2.ProgrammingError, lo.write, b"some data")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_read_after_commit(self):
 | 
					    def test_read_after_commit(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
| 
						 | 
					@ -340,7 +340,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_export_after_commit(self):
 | 
					    def test_export_after_commit(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
        lo.write(b("some data"))
 | 
					        lo.write(b"some data")
 | 
				
			||||||
        self.conn.commit()
 | 
					        self.conn.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.tmpdir = tempfile.mkdtemp()
 | 
					        self.tmpdir = tempfile.mkdtemp()
 | 
				
			||||||
| 
						 | 
					@ -349,7 +349,7 @@ class LargeObjectTests(LargeObjectTestCase):
 | 
				
			||||||
        self.assertTrue(os.path.exists(filename))
 | 
					        self.assertTrue(os.path.exists(filename))
 | 
				
			||||||
        f = open(filename, "rb")
 | 
					        f = open(filename, "rb")
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(f.read(), b("some data"))
 | 
					            self.assertEqual(f.read(), b"some data")
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            f.close()
 | 
					            f.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -400,6 +400,7 @@ def skip_if_no_truncate(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_truncate_
 | 
					    return skip_if_no_truncate_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LargeObjectTruncateTests(LargeObjectTestCase):
 | 
					class LargeObjectTruncateTests(LargeObjectTestCase):
 | 
				
			||||||
    def test_truncate(self):
 | 
					    def test_truncate(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
| 
						 | 
					@ -451,15 +452,19 @@ def _has_lo64(conn):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (True, "this server and build support the lo64 API")
 | 
					    return (True, "this server and build support the lo64 API")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_lo64(f):
 | 
					def skip_if_no_lo64(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_no_lo64_(self):
 | 
					    def skip_if_no_lo64_(self):
 | 
				
			||||||
        lo64, msg = _has_lo64(self.conn)
 | 
					        lo64, msg = _has_lo64(self.conn)
 | 
				
			||||||
        if not lo64: return self.skipTest(msg)
 | 
					        if not lo64:
 | 
				
			||||||
        else: return f(self)
 | 
					            return self.skipTest(msg)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return f(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_lo64_
 | 
					    return skip_if_no_lo64_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LargeObject64Tests(LargeObjectTestCase):
 | 
					class LargeObject64Tests(LargeObjectTestCase):
 | 
				
			||||||
    def test_seek_tell_truncate_greater_than_2gb(self):
 | 
					    def test_seek_tell_truncate_greater_than_2gb(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
| 
						 | 
					@ -478,11 +483,14 @@ def skip_if_lo64(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_lo64_(self):
 | 
					    def skip_if_lo64_(self):
 | 
				
			||||||
        lo64, msg = _has_lo64(self.conn)
 | 
					        lo64, msg = _has_lo64(self.conn)
 | 
				
			||||||
        if lo64: return self.skipTest(msg)
 | 
					        if lo64:
 | 
				
			||||||
        else: return f(self)
 | 
					            return self.skipTest(msg)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return f(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_lo64_
 | 
					    return skip_if_lo64_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class LargeObjectNot64Tests(LargeObjectTestCase):
 | 
					class LargeObjectNot64Tests(LargeObjectTestCase):
 | 
				
			||||||
    def test_seek_larger_than_2gb(self):
 | 
					    def test_seek_larger_than_2gb(self):
 | 
				
			||||||
        lo = self.conn.lobject()
 | 
					        lo = self.conn.lobject()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,9 +31,11 @@ from testutils import ConnectingTestCase, skip_copy_if_green, script_to_py3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ConnectTestCase(unittest.TestCase):
 | 
					class ConnectTestCase(unittest.TestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        self.args = None
 | 
					        self.args = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def conect_stub(dsn, connection_factory=None, async=False):
 | 
					        def conect_stub(dsn, connection_factory=None, async=False):
 | 
				
			||||||
            self.args = (dsn, connection_factory, async)
 | 
					            self.args = (dsn, connection_factory, async)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,6 +45,9 @@ class ConnectTestCase(unittest.TestCase):
 | 
				
			||||||
    def tearDown(self):
 | 
					    def tearDown(self):
 | 
				
			||||||
        psycopg2._connect = self._connect_orig
 | 
					        psycopg2._connect = self._connect_orig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def assertDsnEqual(self, dsn1, dsn2):
 | 
				
			||||||
 | 
					        self.assertEqual(set(dsn1.split()), set(dsn2.split()))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_there_has_to_be_something(self):
 | 
					    def test_there_has_to_be_something(self):
 | 
				
			||||||
        self.assertRaises(TypeError, psycopg2.connect)
 | 
					        self.assertRaises(TypeError, psycopg2.connect)
 | 
				
			||||||
        self.assertRaises(TypeError, psycopg2.connect,
 | 
					        self.assertRaises(TypeError, psycopg2.connect,
 | 
				
			||||||
| 
						 | 
					@ -57,8 +62,8 @@ class ConnectTestCase(unittest.TestCase):
 | 
				
			||||||
        self.assertEqual(self.args[2], False)
 | 
					        self.assertEqual(self.args[2], False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_dsn(self):
 | 
					    def test_dsn(self):
 | 
				
			||||||
        psycopg2.connect('dbname=blah x=y')
 | 
					        psycopg2.connect('dbname=blah host=y')
 | 
				
			||||||
        self.assertEqual(self.args[0], 'dbname=blah x=y')
 | 
					        self.assertEqual(self.args[0], 'dbname=blah host=y')
 | 
				
			||||||
        self.assertEqual(self.args[1], None)
 | 
					        self.assertEqual(self.args[1], None)
 | 
				
			||||||
        self.assertEqual(self.args[2], False)
 | 
					        self.assertEqual(self.args[2], False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,37 +88,43 @@ class ConnectTestCase(unittest.TestCase):
 | 
				
			||||||
        self.assertEqual(len(self.args[0].split()), 4)
 | 
					        self.assertEqual(len(self.args[0].split()), 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_generic_keywords(self):
 | 
					    def test_generic_keywords(self):
 | 
				
			||||||
        psycopg2.connect(foo='bar')
 | 
					        psycopg2.connect(options='stuff')
 | 
				
			||||||
        self.assertEqual(self.args[0], 'foo=bar')
 | 
					        self.assertEqual(self.args[0], 'options=stuff')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_factory(self):
 | 
					    def test_factory(self):
 | 
				
			||||||
        def f(dsn, async=False):
 | 
					        def f(dsn, async=False):
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        psycopg2.connect(database='foo', bar='baz', connection_factory=f)
 | 
					        psycopg2.connect(database='foo', host='baz', connection_factory=f)
 | 
				
			||||||
        self.assertEqual(self.args[0], 'dbname=foo bar=baz')
 | 
					        self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
 | 
				
			||||||
        self.assertEqual(self.args[1], f)
 | 
					        self.assertEqual(self.args[1], f)
 | 
				
			||||||
        self.assertEqual(self.args[2], False)
 | 
					        self.assertEqual(self.args[2], False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        psycopg2.connect("dbname=foo bar=baz", connection_factory=f)
 | 
					        psycopg2.connect("dbname=foo host=baz", connection_factory=f)
 | 
				
			||||||
        self.assertEqual(self.args[0], 'dbname=foo bar=baz')
 | 
					        self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
 | 
				
			||||||
        self.assertEqual(self.args[1], f)
 | 
					        self.assertEqual(self.args[1], f)
 | 
				
			||||||
        self.assertEqual(self.args[2], False)
 | 
					        self.assertEqual(self.args[2], False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_async(self):
 | 
					    def test_async(self):
 | 
				
			||||||
        psycopg2.connect(database='foo', bar='baz', async=1)
 | 
					        psycopg2.connect(database='foo', host='baz', async=1)
 | 
				
			||||||
        self.assertEqual(self.args[0], 'dbname=foo bar=baz')
 | 
					        self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
 | 
				
			||||||
        self.assertEqual(self.args[1], None)
 | 
					        self.assertEqual(self.args[1], None)
 | 
				
			||||||
        self.assert_(self.args[2])
 | 
					        self.assert_(self.args[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        psycopg2.connect("dbname=foo bar=baz", async=True)
 | 
					        psycopg2.connect("dbname=foo host=baz", async=True)
 | 
				
			||||||
        self.assertEqual(self.args[0], 'dbname=foo bar=baz')
 | 
					        self.assertDsnEqual(self.args[0], 'dbname=foo host=baz')
 | 
				
			||||||
        self.assertEqual(self.args[1], None)
 | 
					        self.assertEqual(self.args[1], None)
 | 
				
			||||||
        self.assert_(self.args[2])
 | 
					        self.assert_(self.args[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_int_port_param(self):
 | 
				
			||||||
 | 
					        psycopg2.connect(database='sony', port=6543)
 | 
				
			||||||
 | 
					        dsn = " %s " % self.args[0]
 | 
				
			||||||
 | 
					        self.assert_(" dbname=sony " in dsn, dsn)
 | 
				
			||||||
 | 
					        self.assert_(" port=6543 " in dsn, dsn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_empty_param(self):
 | 
					    def test_empty_param(self):
 | 
				
			||||||
        psycopg2.connect(database='sony', password='')
 | 
					        psycopg2.connect(database='sony', password='')
 | 
				
			||||||
        self.assertEqual(self.args[0], "dbname=sony password=''")
 | 
					        self.assertDsnEqual(self.args[0], "dbname=sony password=''")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_escape(self):
 | 
					    def test_escape(self):
 | 
				
			||||||
        psycopg2.connect(database='hello world')
 | 
					        psycopg2.connect(database='hello world')
 | 
				
			||||||
| 
						 | 
					@ -131,13 +142,12 @@ class ConnectTestCase(unittest.TestCase):
 | 
				
			||||||
        psycopg2.connect(database=r"\every thing'")
 | 
					        psycopg2.connect(database=r"\every thing'")
 | 
				
			||||||
        self.assertEqual(self.args[0], r"dbname='\\every thing\''")
 | 
					        self.assertEqual(self.args[0], r"dbname='\\every thing\''")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_no_kwargs_swallow(self):
 | 
					    def test_params_merging(self):
 | 
				
			||||||
        self.assertRaises(TypeError,
 | 
					        psycopg2.connect('dbname=foo', database='bar')
 | 
				
			||||||
            psycopg2.connect, 'dbname=foo', database='foo')
 | 
					        self.assertEqual(self.args[0], 'dbname=bar')
 | 
				
			||||||
        self.assertRaises(TypeError,
 | 
					
 | 
				
			||||||
            psycopg2.connect, 'dbname=foo', user='postgres')
 | 
					        psycopg2.connect('dbname=foo', user='postgres')
 | 
				
			||||||
        self.assertRaises(TypeError,
 | 
					        self.assertDsnEqual(self.args[0], 'dbname=foo user=postgres')
 | 
				
			||||||
            psycopg2.connect, 'dbname=foo', no_such_param='meh')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ExceptionsTestCase(ConnectingTestCase):
 | 
					class ExceptionsTestCase(ConnectingTestCase):
 | 
				
			||||||
| 
						 | 
					@ -203,7 +213,8 @@ class ExceptionsTestCase(ConnectingTestCase):
 | 
				
			||||||
        self.assertEqual(diag.sqlstate, '42P01')
 | 
					        self.assertEqual(diag.sqlstate, '42P01')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        del diag
 | 
					        del diag
 | 
				
			||||||
        gc.collect(); gc.collect()
 | 
					        gc.collect()
 | 
				
			||||||
 | 
					        gc.collect()
 | 
				
			||||||
        assert(w() is None)
 | 
					        assert(w() is None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @skip_copy_if_green
 | 
					    @skip_copy_if_green
 | 
				
			||||||
| 
						 | 
					@ -325,7 +336,7 @@ class TestVersionDiscovery(unittest.TestCase):
 | 
				
			||||||
        self.assertTrue(type(psycopg2.__libpq_version__) is int)
 | 
					        self.assertTrue(type(psycopg2.__libpq_version__) is int)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertTrue(type(psycopg2.extensions.libpq_version()) is int)
 | 
					            self.assertTrue(type(psycopg2.extensions.libpq_version()) is int)
 | 
				
			||||||
        except NotSupportedError:
 | 
					        except psycopg2.NotSupportedError:
 | 
				
			||||||
            self.assertTrue(psycopg2.__libpq_version__ < 90100)
 | 
					            self.assertTrue(psycopg2.__libpq_version__ < 90100)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ conn.close()
 | 
				
			||||||
        proc = self.notify('foo', 1)
 | 
					        proc = self.notify('foo', 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        t0 = time.time()
 | 
					        t0 = time.time()
 | 
				
			||||||
        ready = select.select([self.conn], [], [], 5)
 | 
					        select.select([self.conn], [], [], 5)
 | 
				
			||||||
        t1 = time.time()
 | 
					        t1 = time.time()
 | 
				
			||||||
        self.assert_(0.99 < t1 - t0 < 4, t1 - t0)
 | 
					        self.assert_(0.99 < t1 - t0 < 4, t1 - t0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -217,6 +217,6 @@ conn.close()
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,12 +30,14 @@ import psycopg2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testconfig import dsn
 | 
					from testconfig import dsn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
 | 
					class Psycopg2Tests(dbapi20.DatabaseAPI20Test):
 | 
				
			||||||
    driver = psycopg2
 | 
					    driver = psycopg2
 | 
				
			||||||
    connect_args = ()
 | 
					    connect_args = ()
 | 
				
			||||||
    connect_kw_args = {'dsn': dsn}
 | 
					    connect_kw_args = {'dsn': dsn}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    lower_func = 'lower' # For stored procedure test
 | 
					    lower_func = 'lower' # For stored procedure test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_callproc(self):
 | 
					    def test_callproc(self):
 | 
				
			||||||
        # Until DBAPI 2.0 compliance, callproc should return None or it's just
 | 
					        # Until DBAPI 2.0 compliance, callproc should return None or it's just
 | 
				
			||||||
        # misleading. Therefore, we will skip the return value test for
 | 
					        # misleading. Therefore, we will skip the return value test for
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,11 +23,12 @@
 | 
				
			||||||
# License for more details.
 | 
					# License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
from testutils import unittest, ConnectingTestCase, skip_before_libpq
 | 
					import testutils
 | 
				
			||||||
 | 
					from testutils import unittest, ConnectingTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
import psycopg2.extensions
 | 
					import psycopg2.extensions
 | 
				
			||||||
from psycopg2.extensions import b
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class QuotingTestCase(ConnectingTestCase):
 | 
					class QuotingTestCase(ConnectingTestCase):
 | 
				
			||||||
    r"""Checks the correct quoting of strings and binary objects.
 | 
					    r"""Checks the correct quoting of strings and binary objects.
 | 
				
			||||||
| 
						 | 
					@ -60,10 +61,22 @@ class QuotingTestCase(ConnectingTestCase):
 | 
				
			||||||
        self.assertEqual(res, data)
 | 
					        self.assertEqual(res, data)
 | 
				
			||||||
        self.assert_(not self.conn.notices)
 | 
					        self.assert_(not self.conn.notices)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_string_null_terminator(self):
 | 
				
			||||||
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
 | 
					        data = 'abcd\x01\x00cdefg'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            curs.execute("SELECT %s", (data,))
 | 
				
			||||||
 | 
					        except ValueError as e:
 | 
				
			||||||
 | 
					            self.assertEquals(str(e),
 | 
				
			||||||
 | 
					                'A string literal cannot contain NUL (0x00) characters.')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.fail("ValueError not raised")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_binary(self):
 | 
					    def test_binary(self):
 | 
				
			||||||
        data = b("""some data with \000\013 binary
 | 
					        data = b"""some data with \000\013 binary
 | 
				
			||||||
        stuff into, 'quotes' and \\ a backslash too.
 | 
					        stuff into, 'quotes' and \\ a backslash too.
 | 
				
			||||||
        """)
 | 
					        """
 | 
				
			||||||
        if sys.version_info[0] < 3:
 | 
					        if sys.version_info[0] < 3:
 | 
				
			||||||
            data += "".join(map(chr, range(256)))
 | 
					            data += "".join(map(chr, range(256)))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -76,7 +89,7 @@ class QuotingTestCase(ConnectingTestCase):
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            res = curs.fetchone()[0].tobytes()
 | 
					            res = curs.fetchone()[0].tobytes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if res[0] in (b('x'), ord(b('x'))) and self.conn.server_version >= 90000:
 | 
					        if res[0] in (b'x', ord(b'x')) and self.conn.server_version >= 90000:
 | 
				
			||||||
            return self.skipTest(
 | 
					            return self.skipTest(
 | 
				
			||||||
                "bytea broken with server >= 9.0, libpq < 9")
 | 
					                "bytea broken with server >= 9.0, libpq < 9")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -156,7 +169,7 @@ class QuotingTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestQuotedString(ConnectingTestCase):
 | 
					class TestQuotedString(ConnectingTestCase):
 | 
				
			||||||
    def test_encoding(self):
 | 
					    def test_encoding_from_conn(self):
 | 
				
			||||||
        q = psycopg2.extensions.QuotedString('hi')
 | 
					        q = psycopg2.extensions.QuotedString('hi')
 | 
				
			||||||
        self.assertEqual(q.encoding, 'latin1')
 | 
					        self.assertEqual(q.encoding, 'latin1')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -166,13 +179,13 @@ class TestQuotedString(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TestQuotedIdentifier(ConnectingTestCase):
 | 
					class TestQuotedIdentifier(ConnectingTestCase):
 | 
				
			||||||
    @skip_before_libpq(9, 0)
 | 
					    @testutils.skip_before_libpq(9, 0)
 | 
				
			||||||
    def test_identifier(self):
 | 
					    def test_identifier(self):
 | 
				
			||||||
        from psycopg2.extensions import quote_ident
 | 
					        from psycopg2.extensions import quote_ident
 | 
				
			||||||
        self.assertEqual(quote_ident('blah-blah', self.conn), '"blah-blah"')
 | 
					        self.assertEqual(quote_ident('blah-blah', self.conn), '"blah-blah"')
 | 
				
			||||||
        self.assertEqual(quote_ident('quote"inside', self.conn), '"quote""inside"')
 | 
					        self.assertEqual(quote_ident('quote"inside', self.conn), '"quote""inside"')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @skip_before_libpq(9, 0)
 | 
					    @testutils.skip_before_libpq(9, 0)
 | 
				
			||||||
    def test_unicode_ident(self):
 | 
					    def test_unicode_ident(self):
 | 
				
			||||||
        from psycopg2.extensions import quote_ident
 | 
					        from psycopg2.extensions import quote_ident
 | 
				
			||||||
        snowman = u"\u2603"
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
| 
						 | 
					@ -183,9 +196,59 @@ class TestQuotedIdentifier(ConnectingTestCase):
 | 
				
			||||||
            self.assertEqual(quote_ident(snowman, self.conn), quoted)
 | 
					            self.assertEqual(quote_ident(snowman, self.conn), quoted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestStringAdapter(ConnectingTestCase):
 | 
				
			||||||
 | 
					    def test_encoding_default(self):
 | 
				
			||||||
 | 
					        from psycopg2.extensions import adapt
 | 
				
			||||||
 | 
					        a = adapt("hello")
 | 
				
			||||||
 | 
					        self.assertEqual(a.encoding, 'latin1')
 | 
				
			||||||
 | 
					        self.assertEqual(a.getquoted(), b"'hello'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # NOTE: we can't really test an encoding different from utf8, because
 | 
				
			||||||
 | 
					        # when encoding without connection the libpq will use parameters from
 | 
				
			||||||
 | 
					        # a previous one, so what would happens depends jn the tests run order.
 | 
				
			||||||
 | 
					        # egrave = u'\xe8'
 | 
				
			||||||
 | 
					        # self.assertEqual(adapt(egrave).getquoted(), "'\xe8'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_encoding_error(self):
 | 
				
			||||||
 | 
					        from psycopg2.extensions import adapt
 | 
				
			||||||
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
 | 
					        a = adapt(snowman)
 | 
				
			||||||
 | 
					        self.assertRaises(UnicodeEncodeError, a.getquoted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_set_encoding(self):
 | 
				
			||||||
 | 
					        # Note: this works-ish mostly in case when the standard db connection
 | 
				
			||||||
 | 
					        # we test with is utf8, otherwise the encoding chosen by PQescapeString
 | 
				
			||||||
 | 
					        # may give bad results.
 | 
				
			||||||
 | 
					        from psycopg2.extensions import adapt
 | 
				
			||||||
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
 | 
					        a = adapt(snowman)
 | 
				
			||||||
 | 
					        a.encoding = 'utf8'
 | 
				
			||||||
 | 
					        self.assertEqual(a.encoding, 'utf8')
 | 
				
			||||||
 | 
					        self.assertEqual(a.getquoted(), b"'\xe2\x98\x83'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_connection_wins_anyway(self):
 | 
				
			||||||
 | 
					        from psycopg2.extensions import adapt
 | 
				
			||||||
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
 | 
					        a = adapt(snowman)
 | 
				
			||||||
 | 
					        a.encoding = 'latin9'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.conn.set_client_encoding('utf8')
 | 
				
			||||||
 | 
					        a.prepare(self.conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertEqual(a.encoding, 'utf_8')
 | 
				
			||||||
 | 
					        self.assertEqual(a.getquoted(), b"'\xe2\x98\x83'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @testutils.skip_before_python(3)
 | 
				
			||||||
 | 
					    def test_adapt_bytes(self):
 | 
				
			||||||
 | 
					        snowman = u"\u2603"
 | 
				
			||||||
 | 
					        self.conn.set_client_encoding('utf8')
 | 
				
			||||||
 | 
					        a = psycopg2.extensions.QuotedString(snowman.encode('utf8'))
 | 
				
			||||||
 | 
					        a.prepare(self.conn)
 | 
				
			||||||
 | 
					        self.assertEqual(a.getquoted(), b"'\xe2\x98\x83'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										232
									
								
								tests/test_replication.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										232
									
								
								tests/test_replication.py
									
									
									
									
									
										Executable file
									
								
							| 
						 | 
					@ -0,0 +1,232 @@
 | 
				
			||||||
 | 
					#!/usr/bin/env python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# test_replication.py - unit test for replication protocol
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Copyright (C) 2015 Daniele Varrazzo  <daniele.varrazzo@gmail.com>
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# 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.extras import (
 | 
				
			||||||
 | 
					    PhysicalReplicationConnection, LogicalReplicationConnection, StopReplication)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import testconfig
 | 
				
			||||||
 | 
					from testutils import unittest, ConnectingTestCase
 | 
				
			||||||
 | 
					from testutils import skip_before_postgres, skip_if_green
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					skip_repl_if_green = skip_if_green("replication not supported in green mode")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReplicationTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        super(ReplicationTestCase, self).setUp()
 | 
				
			||||||
 | 
					        self.slot = testconfig.repl_slot
 | 
				
			||||||
 | 
					        self._slots = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tearDown(self):
 | 
				
			||||||
 | 
					        # first close all connections, as they might keep the slot(s) active
 | 
				
			||||||
 | 
					        super(ReplicationTestCase, self).tearDown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        import time
 | 
				
			||||||
 | 
					        time.sleep(0.025)  # sometimes the slot is still active, wait a little
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self._slots:
 | 
				
			||||||
 | 
					            kill_conn = self.connect()
 | 
				
			||||||
 | 
					            if kill_conn:
 | 
				
			||||||
 | 
					                kill_cur = kill_conn.cursor()
 | 
				
			||||||
 | 
					                for slot in self._slots:
 | 
				
			||||||
 | 
					                    kill_cur.execute("SELECT pg_drop_replication_slot(%s)", (slot,))
 | 
				
			||||||
 | 
					                kill_conn.commit()
 | 
				
			||||||
 | 
					                kill_conn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_replication_slot(self, cur, slot_name=testconfig.repl_slot, **kwargs):
 | 
				
			||||||
 | 
					        cur.create_replication_slot(slot_name, **kwargs)
 | 
				
			||||||
 | 
					        self._slots.append(slot_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def drop_replication_slot(self, cur, slot_name=testconfig.repl_slot):
 | 
				
			||||||
 | 
					        cur.drop_replication_slot(slot_name)
 | 
				
			||||||
 | 
					        self._slots.remove(slot_name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # generate some events for our replication stream
 | 
				
			||||||
 | 
					    def make_replication_events(self):
 | 
				
			||||||
 | 
					        conn = self.connect()
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            cur.execute("DROP TABLE dummy1")
 | 
				
			||||||
 | 
					        except psycopg2.ProgrammingError:
 | 
				
			||||||
 | 
					            conn.rollback()
 | 
				
			||||||
 | 
					        cur.execute(
 | 
				
			||||||
 | 
					            "CREATE TABLE dummy1 AS SELECT * FROM generate_series(1, 5) AS id")
 | 
				
			||||||
 | 
					        conn.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReplicationTest(ReplicationTestCase):
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 0)
 | 
				
			||||||
 | 
					    def test_physical_replication_connection(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(connection_factory=PhysicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					        cur.execute("IDENTIFY_SYSTEM")
 | 
				
			||||||
 | 
					        cur.fetchall()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 0)
 | 
				
			||||||
 | 
					    def test_datestyle(self):
 | 
				
			||||||
 | 
					        if testconfig.repl_dsn is None:
 | 
				
			||||||
 | 
					            return self.skipTest("replication tests disabled by default")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        conn = self.repl_connect(
 | 
				
			||||||
 | 
					            dsn=testconfig.repl_dsn, options='-cdatestyle=german',
 | 
				
			||||||
 | 
					            connection_factory=PhysicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					        cur.execute("IDENTIFY_SYSTEM")
 | 
				
			||||||
 | 
					        cur.fetchall()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 4)
 | 
				
			||||||
 | 
					    def test_logical_replication_connection(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(connection_factory=LogicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					        cur.execute("IDENTIFY_SYSTEM")
 | 
				
			||||||
 | 
					        cur.fetchall()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 4)     # slots require 9.4
 | 
				
			||||||
 | 
					    def test_create_replication_slot(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(connection_factory=PhysicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.create_replication_slot(cur)
 | 
				
			||||||
 | 
					        self.assertRaises(
 | 
				
			||||||
 | 
					            psycopg2.ProgrammingError, self.create_replication_slot, cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 4)  # slots require 9.4
 | 
				
			||||||
 | 
					    @skip_repl_if_green
 | 
				
			||||||
 | 
					    def test_start_on_missing_replication_slot(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(connection_factory=PhysicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
 | 
					            cur.start_replication, self.slot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.create_replication_slot(cur)
 | 
				
			||||||
 | 
					        cur.start_replication(self.slot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 4)  # slots require 9.4
 | 
				
			||||||
 | 
					    @skip_repl_if_green
 | 
				
			||||||
 | 
					    def test_start_and_recover_from_error(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(connection_factory=LogicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.create_replication_slot(cur, output_plugin='test_decoding')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # try with invalid options
 | 
				
			||||||
 | 
					        cur.start_replication(
 | 
				
			||||||
 | 
					            slot_name=self.slot, options={'invalid_param': 'value'})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def consume(msg):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					        # we don't see the error from the server before we try to read the data
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.DataError, cur.consume_stream, consume)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # try with correct command
 | 
				
			||||||
 | 
					        cur.start_replication(slot_name=self.slot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 4)     # slots require 9.4
 | 
				
			||||||
 | 
					    @skip_repl_if_green
 | 
				
			||||||
 | 
					    def test_stop_replication(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(connection_factory=LogicalReplicationConnection)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.create_replication_slot(cur, output_plugin='test_decoding')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.make_replication_events()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.start_replication(self.slot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def consume(msg):
 | 
				
			||||||
 | 
					            raise StopReplication()
 | 
				
			||||||
 | 
					        self.assertRaises(StopReplication, cur.consume_stream, consume)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AsyncReplicationTest(ReplicationTestCase):
 | 
				
			||||||
 | 
					    @skip_before_postgres(9, 4)     # slots require 9.4
 | 
				
			||||||
 | 
					    @skip_repl_if_green
 | 
				
			||||||
 | 
					    def test_async_replication(self):
 | 
				
			||||||
 | 
					        conn = self.repl_connect(
 | 
				
			||||||
 | 
					            connection_factory=LogicalReplicationConnection, async=1)
 | 
				
			||||||
 | 
					        if conn is None:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur = conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.create_replication_slot(cur, output_plugin='test_decoding')
 | 
				
			||||||
 | 
					        self.wait(cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        cur.start_replication(self.slot)
 | 
				
			||||||
 | 
					        self.wait(cur)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.make_replication_events()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.msg_count = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def consume(msg):
 | 
				
			||||||
 | 
					            # just check the methods
 | 
				
			||||||
 | 
					            "%s: %s" % (cur.io_timestamp, repr(msg))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            self.msg_count += 1
 | 
				
			||||||
 | 
					            if self.msg_count > 3:
 | 
				
			||||||
 | 
					                cur.send_feedback(reply=True)
 | 
				
			||||||
 | 
					                raise StopReplication()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cur.send_feedback(flush_lsn=msg.data_start)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # cannot be used in asynchronous mode
 | 
				
			||||||
 | 
					        self.assertRaises(psycopg2.ProgrammingError, cur.consume_stream, consume)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def process_stream():
 | 
				
			||||||
 | 
					            from select import select
 | 
				
			||||||
 | 
					            while True:
 | 
				
			||||||
 | 
					                msg = cur.read_message()
 | 
				
			||||||
 | 
					                if msg:
 | 
				
			||||||
 | 
					                    consume(msg)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    select([cur], [], [])
 | 
				
			||||||
 | 
					        self.assertRaises(StopReplication, process_stream)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_suite():
 | 
				
			||||||
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    unittest.main()
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ import psycopg2
 | 
				
			||||||
from psycopg2.extensions import (
 | 
					from psycopg2.extensions import (
 | 
				
			||||||
    ISOLATION_LEVEL_SERIALIZABLE, STATUS_BEGIN, STATUS_READY)
 | 
					    ISOLATION_LEVEL_SERIALIZABLE, STATUS_BEGIN, STATUS_READY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TransactionTests(ConnectingTestCase):
 | 
					class TransactionTests(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
| 
						 | 
					@ -147,6 +148,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
 | 
				
			||||||
                self.thread1_error = exc
 | 
					                self.thread1_error = exc
 | 
				
			||||||
                step1.set()
 | 
					                step1.set()
 | 
				
			||||||
            conn.close()
 | 
					            conn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def task2():
 | 
					        def task2():
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                conn = self.connect()
 | 
					                conn = self.connect()
 | 
				
			||||||
| 
						 | 
					@ -195,6 +197,7 @@ class DeadlockSerializationTests(ConnectingTestCase):
 | 
				
			||||||
                self.thread1_error = exc
 | 
					                self.thread1_error = exc
 | 
				
			||||||
                step1.set()
 | 
					                step1.set()
 | 
				
			||||||
            conn.close()
 | 
					            conn.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def task2():
 | 
					        def task2():
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                conn = self.connect()
 | 
					                conn = self.connect()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,6 @@ import testutils
 | 
				
			||||||
from testutils import unittest, ConnectingTestCase, decorate_all_tests
 | 
					from testutils import unittest, ConnectingTestCase, decorate_all_tests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
from psycopg2.extensions import b
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TypesBasicTests(ConnectingTestCase):
 | 
					class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
| 
						 | 
					@ -69,13 +68,16 @@ class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
                        "wrong decimal quoting: " + str(s))
 | 
					                        "wrong decimal quoting: " + str(s))
 | 
				
			||||||
        s = self.execute("SELECT %s AS foo", (decimal.Decimal("NaN"),))
 | 
					        s = self.execute("SELECT %s AS foo", (decimal.Decimal("NaN"),))
 | 
				
			||||||
        self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s))
 | 
					        self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s))
 | 
				
			||||||
        self.failUnless(type(s) == decimal.Decimal, "wrong decimal conversion: " + repr(s))
 | 
					        self.failUnless(type(s) == decimal.Decimal,
 | 
				
			||||||
 | 
					                        "wrong decimal conversion: " + repr(s))
 | 
				
			||||||
        s = self.execute("SELECT %s AS foo", (decimal.Decimal("infinity"),))
 | 
					        s = self.execute("SELECT %s AS foo", (decimal.Decimal("infinity"),))
 | 
				
			||||||
        self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s))
 | 
					        self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s))
 | 
				
			||||||
        self.failUnless(type(s) == decimal.Decimal, "wrong decimal conversion: " + repr(s))
 | 
					        self.failUnless(type(s) == decimal.Decimal,
 | 
				
			||||||
 | 
					                        "wrong decimal conversion: " + repr(s))
 | 
				
			||||||
        s = self.execute("SELECT %s AS foo", (decimal.Decimal("-infinity"),))
 | 
					        s = self.execute("SELECT %s AS foo", (decimal.Decimal("-infinity"),))
 | 
				
			||||||
        self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s))
 | 
					        self.failUnless(str(s) == "NaN", "wrong decimal quoting: " + str(s))
 | 
				
			||||||
        self.failUnless(type(s) == decimal.Decimal, "wrong decimal conversion: " + repr(s))
 | 
					        self.failUnless(type(s) == decimal.Decimal,
 | 
				
			||||||
 | 
					                        "wrong decimal conversion: " + repr(s))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def testFloatNan(self):
 | 
					    def testFloatNan(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -151,9 +153,12 @@ class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
        # ticket #42
 | 
					        # ticket #42
 | 
				
			||||||
        import datetime
 | 
					        import datetime
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        curs.execute("create table array_test (id integer, col timestamp without time zone[])")
 | 
					        curs.execute(
 | 
				
			||||||
 | 
					            "create table array_test "
 | 
				
			||||||
 | 
					            "(id integer, col timestamp without time zone[])")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs.execute("insert into array_test values (%s, %s)", (1, [datetime.date(2011,2,14)]))
 | 
					        curs.execute("insert into array_test values (%s, %s)",
 | 
				
			||||||
 | 
					            (1, [datetime.date(2011, 2, 14)]))
 | 
				
			||||||
        curs.execute("select col from array_test where id = 1")
 | 
					        curs.execute("select col from array_test where id = 1")
 | 
				
			||||||
        self.assertEqual(curs.fetchone()[0], [datetime.datetime(2011, 2, 14, 0, 0)])
 | 
					        self.assertEqual(curs.fetchone()[0], [datetime.datetime(2011, 2, 14, 0, 0)])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,8 +195,9 @@ class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
        ss = ['', '{', '{}}', '{' * 20 + '}' * 20]
 | 
					        ss = ['', '{', '{}}', '{' * 20 + '}' * 20]
 | 
				
			||||||
        for s in ss:
 | 
					        for s in ss:
 | 
				
			||||||
            self.assertRaises(psycopg2.DataError,
 | 
					            self.assertRaises(psycopg2.DataError,
 | 
				
			||||||
                psycopg2.extensions.STRINGARRAY, b(s), curs)
 | 
					                psycopg2.extensions.STRINGARRAY, s.encode('utf8'), curs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @testutils.skip_before_postgres(8, 2)
 | 
				
			||||||
    def testArrayOfNulls(self):
 | 
					    def testArrayOfNulls(self):
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        curs.execute("""
 | 
					        curs.execute("""
 | 
				
			||||||
| 
						 | 
					@ -308,9 +314,9 @@ class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
    def testByteaHexCheckFalsePositive(self):
 | 
					    def testByteaHexCheckFalsePositive(self):
 | 
				
			||||||
        # the check \x -> x to detect bad bytea decode
 | 
					        # the check \x -> x to detect bad bytea decode
 | 
				
			||||||
        # may be fooled if the first char is really an 'x'
 | 
					        # may be fooled if the first char is really an 'x'
 | 
				
			||||||
        o1 = psycopg2.Binary(b('x'))
 | 
					        o1 = psycopg2.Binary(b'x')
 | 
				
			||||||
        o2 = self.execute("SELECT %s::bytea AS foo", (o1,))
 | 
					        o2 = self.execute("SELECT %s::bytea AS foo", (o1,))
 | 
				
			||||||
        self.assertEqual(b('x'), o2[0])
 | 
					        self.assertEqual(b'x', o2[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def testNegNumber(self):
 | 
					    def testNegNumber(self):
 | 
				
			||||||
        d1 = self.execute("select -%s;", (decimal.Decimal('-1.0'),))
 | 
					        d1 = self.execute("select -%s;", (decimal.Decimal('-1.0'),))
 | 
				
			||||||
| 
						 | 
					@ -331,7 +337,8 @@ class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
    @testutils.skip_before_postgres(8, 2)
 | 
					    @testutils.skip_before_postgres(8, 2)
 | 
				
			||||||
    def testGenericArrayNull(self):
 | 
					    def testGenericArrayNull(self):
 | 
				
			||||||
        def caster(s, cur):
 | 
					        def caster(s, cur):
 | 
				
			||||||
            if s is None: return "nada"
 | 
					            if s is None:
 | 
				
			||||||
 | 
					                return "nada"
 | 
				
			||||||
            return int(s) * 2
 | 
					            return int(s) * 2
 | 
				
			||||||
        base = psycopg2.extensions.new_type((23,), "INT4", caster)
 | 
					        base = psycopg2.extensions.new_type((23,), "INT4", caster)
 | 
				
			||||||
        array = psycopg2.extensions.new_array_type((1007,), "INT4ARRAY", base)
 | 
					        array = psycopg2.extensions.new_array_type((1007,), "INT4ARRAY", base)
 | 
				
			||||||
| 
						 | 
					@ -342,11 +349,23 @@ class TypesBasicTests(ConnectingTestCase):
 | 
				
			||||||
        a = self.execute("select '{1, 2, NULL}'::int4[]")
 | 
					        a = self.execute("select '{1, 2, NULL}'::int4[]")
 | 
				
			||||||
        self.assertEqual(a, [2, 4, 'nada'])
 | 
					        self.assertEqual(a, [2, 4, 'nada'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @testutils.skip_before_postgres(8, 2)
 | 
				
			||||||
 | 
					    def testNetworkArray(self):
 | 
				
			||||||
 | 
					        # we don't know these types, but we know their arrays
 | 
				
			||||||
 | 
					        a = self.execute("select '{192.168.0.1/24}'::inet[]")
 | 
				
			||||||
 | 
					        self.assertEqual(a, ['192.168.0.1/24'])
 | 
				
			||||||
 | 
					        a = self.execute("select '{192.168.0.0/24}'::cidr[]")
 | 
				
			||||||
 | 
					        self.assertEqual(a, ['192.168.0.0/24'])
 | 
				
			||||||
 | 
					        a = self.execute("select '{10:20:30:40:50:60}'::macaddr[]")
 | 
				
			||||||
 | 
					        self.assertEqual(a, ['10:20:30:40:50:60'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AdaptSubclassTest(unittest.TestCase):
 | 
					class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
    def test_adapt_subtype(self):
 | 
					    def test_adapt_subtype(self):
 | 
				
			||||||
        from psycopg2.extensions import adapt
 | 
					        from psycopg2.extensions import adapt
 | 
				
			||||||
        class Sub(str): pass
 | 
					
 | 
				
			||||||
 | 
					        class Sub(str):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
        s1 = "hel'lo"
 | 
					        s1 = "hel'lo"
 | 
				
			||||||
        s2 = Sub(s1)
 | 
					        s2 = Sub(s1)
 | 
				
			||||||
        self.assertEqual(adapt(s1).getquoted(), adapt(s2).getquoted())
 | 
					        self.assertEqual(adapt(s1).getquoted(), adapt(s2).getquoted())
 | 
				
			||||||
| 
						 | 
					@ -354,14 +373,19 @@ class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
    def test_adapt_most_specific(self):
 | 
					    def test_adapt_most_specific(self):
 | 
				
			||||||
        from psycopg2.extensions import adapt, register_adapter, AsIs
 | 
					        from psycopg2.extensions import adapt, register_adapter, AsIs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class A(object): pass
 | 
					        class A(object):
 | 
				
			||||||
        class B(A): pass
 | 
					            pass
 | 
				
			||||||
        class C(B): pass
 | 
					
 | 
				
			||||||
 | 
					        class B(A):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class C(B):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register_adapter(A, lambda a: AsIs("a"))
 | 
					        register_adapter(A, lambda a: AsIs("a"))
 | 
				
			||||||
        register_adapter(B, lambda b: AsIs("b"))
 | 
					        register_adapter(B, lambda b: AsIs("b"))
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(b('b'), adapt(C()).getquoted())
 | 
					            self.assertEqual(b'b', adapt(C()).getquoted())
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
					            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
            del psycopg2.extensions.adapters[B, psycopg2.extensions.ISQLQuote]
 | 
					            del psycopg2.extensions.adapters[B, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
| 
						 | 
					@ -370,8 +394,11 @@ class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
    def test_no_mro_no_joy(self):
 | 
					    def test_no_mro_no_joy(self):
 | 
				
			||||||
        from psycopg2.extensions import adapt, register_adapter, AsIs
 | 
					        from psycopg2.extensions import adapt, register_adapter, AsIs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class A: pass
 | 
					        class A:
 | 
				
			||||||
        class B(A): pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class B(A):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register_adapter(A, lambda a: AsIs("a"))
 | 
					        register_adapter(A, lambda a: AsIs("a"))
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -379,17 +406,19 @@ class AdaptSubclassTest(unittest.TestCase):
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
					            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    @testutils.skip_before_python(3)
 | 
					    @testutils.skip_before_python(3)
 | 
				
			||||||
    def test_adapt_subtype_3(self):
 | 
					    def test_adapt_subtype_3(self):
 | 
				
			||||||
        from psycopg2.extensions import adapt, register_adapter, AsIs
 | 
					        from psycopg2.extensions import adapt, register_adapter, AsIs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class A: pass
 | 
					        class A:
 | 
				
			||||||
        class B(A): pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class B(A):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        register_adapter(A, lambda a: AsIs("a"))
 | 
					        register_adapter(A, lambda a: AsIs("a"))
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            self.assertEqual(b("a"), adapt(B()).getquoted())
 | 
					            self.assertEqual(b"a", adapt(B()).getquoted())
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
					            del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -434,19 +463,20 @@ class ByteaParserTest(unittest.TestCase):
 | 
				
			||||||
        self.assertEqual(rv, None)
 | 
					        self.assertEqual(rv, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_blank(self):
 | 
					    def test_blank(self):
 | 
				
			||||||
        rv = self.cast(b(''))
 | 
					        rv = self.cast(b'')
 | 
				
			||||||
        self.assertEqual(rv, b(''))
 | 
					        self.assertEqual(rv, b'')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_blank_hex(self):
 | 
					    def test_blank_hex(self):
 | 
				
			||||||
        # Reported as problematic in ticket #48
 | 
					        # Reported as problematic in ticket #48
 | 
				
			||||||
        rv = self.cast(b('\\x'))
 | 
					        rv = self.cast(b'\\x')
 | 
				
			||||||
        self.assertEqual(rv, b(''))
 | 
					        self.assertEqual(rv, b'')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_full_hex(self, upper=False):
 | 
					    def test_full_hex(self, upper=False):
 | 
				
			||||||
        buf = ''.join(("%02x" % i) for i in range(256))
 | 
					        buf = ''.join(("%02x" % i) for i in range(256))
 | 
				
			||||||
        if upper: buf = buf.upper()
 | 
					        if upper:
 | 
				
			||||||
 | 
					            buf = buf.upper()
 | 
				
			||||||
        buf = '\\x' + buf
 | 
					        buf = '\\x' + buf
 | 
				
			||||||
        rv = self.cast(b(buf))
 | 
					        rv = self.cast(buf.encode('utf8'))
 | 
				
			||||||
        if sys.version_info[0] < 3:
 | 
					        if sys.version_info[0] < 3:
 | 
				
			||||||
            self.assertEqual(rv, ''.join(map(chr, range(256))))
 | 
					            self.assertEqual(rv, ''.join(map(chr, range(256))))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -457,7 +487,7 @@ class ByteaParserTest(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_full_escaped_octal(self):
 | 
					    def test_full_escaped_octal(self):
 | 
				
			||||||
        buf = ''.join(("\\%03o" % i) for i in range(256))
 | 
					        buf = ''.join(("\\%03o" % i) for i in range(256))
 | 
				
			||||||
        rv = self.cast(b(buf))
 | 
					        rv = self.cast(buf.encode('utf8'))
 | 
				
			||||||
        if sys.version_info[0] < 3:
 | 
					        if sys.version_info[0] < 3:
 | 
				
			||||||
            self.assertEqual(rv, ''.join(map(chr, range(256))))
 | 
					            self.assertEqual(rv, ''.join(map(chr, range(256))))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -469,7 +499,7 @@ class ByteaParserTest(unittest.TestCase):
 | 
				
			||||||
        buf += string.ascii_letters
 | 
					        buf += string.ascii_letters
 | 
				
			||||||
        buf += ''.join('\\' + c for c in string.ascii_letters)
 | 
					        buf += ''.join('\\' + c for c in string.ascii_letters)
 | 
				
			||||||
        buf += '\\\\'
 | 
					        buf += '\\\\'
 | 
				
			||||||
        rv = self.cast(b(buf))
 | 
					        rv = self.cast(buf.encode('utf8'))
 | 
				
			||||||
        if sys.version_info[0] < 3:
 | 
					        if sys.version_info[0] < 3:
 | 
				
			||||||
            tgt = ''.join(map(chr, range(32))) \
 | 
					            tgt = ''.join(map(chr, range(32))) \
 | 
				
			||||||
                + string.ascii_letters * 2 + '\\'
 | 
					                + string.ascii_letters * 2 + '\\'
 | 
				
			||||||
| 
						 | 
					@ -479,6 +509,7 @@ class ByteaParserTest(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(rv, tgt)
 | 
					        self.assertEqual(rv, tgt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_cant_cast(f):
 | 
					def skip_if_cant_cast(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_cant_cast_(self, *args, **kwargs):
 | 
					    def skip_if_cant_cast_(self, *args, **kwargs):
 | 
				
			||||||
| 
						 | 
					@ -498,4 +529,3 @@ def test_suite():
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,7 @@ import sys
 | 
				
			||||||
from decimal import Decimal
 | 
					from decimal import Decimal
 | 
				
			||||||
from datetime import date, datetime
 | 
					from datetime import date, datetime
 | 
				
			||||||
from functools import wraps
 | 
					from functools import wraps
 | 
				
			||||||
 | 
					from pickle import dumps, loads
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testutils import unittest, skip_if_no_uuid, skip_before_postgres
 | 
					from testutils import unittest, skip_if_no_uuid, skip_before_postgres
 | 
				
			||||||
from testutils import ConnectingTestCase, decorate_all_tests
 | 
					from testutils import ConnectingTestCase, decorate_all_tests
 | 
				
			||||||
| 
						 | 
					@ -28,14 +29,14 @@ from testutils import py3_raises_typeerror
 | 
				
			||||||
import psycopg2
 | 
					import psycopg2
 | 
				
			||||||
import psycopg2.extras
 | 
					import psycopg2.extras
 | 
				
			||||||
import psycopg2.extensions as ext
 | 
					import psycopg2.extensions as ext
 | 
				
			||||||
from psycopg2.extensions import b
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def filter_scs(conn, s):
 | 
					def filter_scs(conn, s):
 | 
				
			||||||
    if conn.get_parameter_status("standard_conforming_strings") == 'off':
 | 
					    if conn.get_parameter_status("standard_conforming_strings") == 'off':
 | 
				
			||||||
        return s
 | 
					        return s
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        return s.replace(b("E'"), b("'"))
 | 
					        return s.replace(b"E'", b"'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TypesExtrasTests(ConnectingTestCase):
 | 
					class TypesExtrasTests(ConnectingTestCase):
 | 
				
			||||||
    """Test that all type conversions are working."""
 | 
					    """Test that all type conversions are working."""
 | 
				
			||||||
| 
						 | 
					@ -60,7 +61,8 @@ class TypesExtrasTests(ConnectingTestCase):
 | 
				
			||||||
    def testUUIDARRAY(self):
 | 
					    def testUUIDARRAY(self):
 | 
				
			||||||
        import uuid
 | 
					        import uuid
 | 
				
			||||||
        psycopg2.extras.register_uuid()
 | 
					        psycopg2.extras.register_uuid()
 | 
				
			||||||
        u = [uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350'), uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e352')]
 | 
					        u = [uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e350'),
 | 
				
			||||||
 | 
					             uuid.UUID('9c6d5a77-7256-457e-9461-347b4358e352')]
 | 
				
			||||||
        s = self.execute("SELECT %s AS foo", (u,))
 | 
					        s = self.execute("SELECT %s AS foo", (u,))
 | 
				
			||||||
        self.failUnless(u == s)
 | 
					        self.failUnless(u == s)
 | 
				
			||||||
        # array with a NULL element
 | 
					        # array with a NULL element
 | 
				
			||||||
| 
						 | 
					@ -98,7 +100,7 @@ class TypesExtrasTests(ConnectingTestCase):
 | 
				
			||||||
        a = psycopg2.extensions.adapt(i)
 | 
					        a = psycopg2.extensions.adapt(i)
 | 
				
			||||||
        a.prepare(self.conn)
 | 
					        a.prepare(self.conn)
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            filter_scs(self.conn, b("E'192.168.1.0/24'::inet")),
 | 
					            filter_scs(self.conn, b"E'192.168.1.0/24'::inet"),
 | 
				
			||||||
            a.getquoted())
 | 
					            a.getquoted())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # adapts ok with unicode too
 | 
					        # adapts ok with unicode too
 | 
				
			||||||
| 
						 | 
					@ -106,11 +108,12 @@ class TypesExtrasTests(ConnectingTestCase):
 | 
				
			||||||
        a = psycopg2.extensions.adapt(i)
 | 
					        a = psycopg2.extensions.adapt(i)
 | 
				
			||||||
        a.prepare(self.conn)
 | 
					        a.prepare(self.conn)
 | 
				
			||||||
        self.assertEqual(
 | 
					        self.assertEqual(
 | 
				
			||||||
            filter_scs(self.conn, b("E'192.168.1.0/24'::inet")),
 | 
					            filter_scs(self.conn, b"E'192.168.1.0/24'::inet"),
 | 
				
			||||||
            a.getquoted())
 | 
					            a.getquoted())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_adapt_fail(self):
 | 
					    def test_adapt_fail(self):
 | 
				
			||||||
        class Foo(object): pass
 | 
					        class Foo(object):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
        self.assertRaises(psycopg2.ProgrammingError,
 | 
					        self.assertRaises(psycopg2.ProgrammingError,
 | 
				
			||||||
            psycopg2.extensions.adapt, Foo(), ext.ISQLQuote, None)
 | 
					            psycopg2.extensions.adapt, Foo(), ext.ISQLQuote, None)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -130,6 +133,7 @@ def skip_if_no_hstore(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_hstore_
 | 
					    return skip_if_no_hstore_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HstoreTestCase(ConnectingTestCase):
 | 
					class HstoreTestCase(ConnectingTestCase):
 | 
				
			||||||
    def test_adapt_8(self):
 | 
					    def test_adapt_8(self):
 | 
				
			||||||
        if self.conn.server_version >= 90000:
 | 
					        if self.conn.server_version >= 90000:
 | 
				
			||||||
| 
						 | 
					@ -145,17 +149,18 @@ class HstoreTestCase(ConnectingTestCase):
 | 
				
			||||||
        a.prepare(self.conn)
 | 
					        a.prepare(self.conn)
 | 
				
			||||||
        q = a.getquoted()
 | 
					        q = a.getquoted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assert_(q.startswith(b("((")), q)
 | 
					        self.assert_(q.startswith(b"(("), q)
 | 
				
			||||||
        ii = q[1:-1].split(b("||"))
 | 
					        ii = q[1:-1].split(b"||")
 | 
				
			||||||
        ii.sort()
 | 
					        ii.sort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(len(ii), len(o))
 | 
					        self.assertEqual(len(ii), len(o))
 | 
				
			||||||
        self.assertEqual(ii[0], filter_scs(self.conn, b("(E'a' => E'1')")))
 | 
					        self.assertEqual(ii[0], filter_scs(self.conn, b"(E'a' => E'1')"))
 | 
				
			||||||
        self.assertEqual(ii[1], filter_scs(self.conn, b("(E'b' => E'''')")))
 | 
					        self.assertEqual(ii[1], filter_scs(self.conn, b"(E'b' => E'''')"))
 | 
				
			||||||
        self.assertEqual(ii[2], filter_scs(self.conn, b("(E'c' => NULL)")))
 | 
					        self.assertEqual(ii[2], filter_scs(self.conn, b"(E'c' => NULL)"))
 | 
				
			||||||
        if 'd' in o:
 | 
					        if 'd' in o:
 | 
				
			||||||
            encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding])
 | 
					            encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding])
 | 
				
			||||||
            self.assertEqual(ii[3], filter_scs(self.conn, b("(E'd' => E'") + encc + b("')")))
 | 
					            self.assertEqual(ii[3],
 | 
				
			||||||
 | 
					                filter_scs(self.conn, b"(E'd' => E'" + encc + b"')"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_adapt_9(self):
 | 
					    def test_adapt_9(self):
 | 
				
			||||||
        if self.conn.server_version < 90000:
 | 
					        if self.conn.server_version < 90000:
 | 
				
			||||||
| 
						 | 
					@ -171,11 +176,11 @@ class HstoreTestCase(ConnectingTestCase):
 | 
				
			||||||
        a.prepare(self.conn)
 | 
					        a.prepare(self.conn)
 | 
				
			||||||
        q = a.getquoted()
 | 
					        q = a.getquoted()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        m = re.match(b(r'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)'), q)
 | 
					        m = re.match(br'hstore\(ARRAY\[([^\]]+)\], ARRAY\[([^\]]+)\]\)', q)
 | 
				
			||||||
        self.assert_(m, repr(q))
 | 
					        self.assert_(m, repr(q))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        kk = m.group(1).split(b(", "))
 | 
					        kk = m.group(1).split(b", ")
 | 
				
			||||||
        vv = m.group(2).split(b(", "))
 | 
					        vv = m.group(2).split(b", ")
 | 
				
			||||||
        ii = zip(kk, vv)
 | 
					        ii = zip(kk, vv)
 | 
				
			||||||
        ii.sort()
 | 
					        ii.sort()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,12 +188,12 @@ class HstoreTestCase(ConnectingTestCase):
 | 
				
			||||||
            return tuple([filter_scs(self.conn, s) for s in args])
 | 
					            return tuple([filter_scs(self.conn, s) for s in args])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(len(ii), len(o))
 | 
					        self.assertEqual(len(ii), len(o))
 | 
				
			||||||
        self.assertEqual(ii[0], f(b("E'a'"), b("E'1'")))
 | 
					        self.assertEqual(ii[0], f(b"E'a'", b"E'1'"))
 | 
				
			||||||
        self.assertEqual(ii[1], f(b("E'b'"), b("E''''")))
 | 
					        self.assertEqual(ii[1], f(b"E'b'", b"E''''"))
 | 
				
			||||||
        self.assertEqual(ii[2], f(b("E'c'"), b("NULL")))
 | 
					        self.assertEqual(ii[2], f(b"E'c'", b"NULL"))
 | 
				
			||||||
        if 'd' in o:
 | 
					        if 'd' in o:
 | 
				
			||||||
            encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding])
 | 
					            encc = u'\xe0'.encode(psycopg2.extensions.encodings[self.conn.encoding])
 | 
				
			||||||
            self.assertEqual(ii[3], f(b("E'd'"), b("E'") + encc + b("'")))
 | 
					            self.assertEqual(ii[3], f(b"E'd'", b"E'" + encc + b"'"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_parse(self):
 | 
					    def test_parse(self):
 | 
				
			||||||
        from psycopg2.extras import HstoreAdapter
 | 
					        from psycopg2.extras import HstoreAdapter
 | 
				
			||||||
| 
						 | 
					@ -402,7 +407,9 @@ class HstoreTestCase(ConnectingTestCase):
 | 
				
			||||||
        from psycopg2.extras import register_hstore
 | 
					        from psycopg2.extras import register_hstore
 | 
				
			||||||
        register_hstore(None, globally=True, oid=oid, array_oid=aoid)
 | 
					        register_hstore(None, globally=True, oid=oid, array_oid=aoid)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            cur.execute("select null::hstore, ''::hstore, 'a => b'::hstore, '{a=>b}'::hstore[]")
 | 
					            cur.execute("""
 | 
				
			||||||
 | 
					                select null::hstore, ''::hstore,
 | 
				
			||||||
 | 
					                'a => b'::hstore, '{a=>b}'::hstore[]""")
 | 
				
			||||||
            t = cur.fetchone()
 | 
					            t = cur.fetchone()
 | 
				
			||||||
            self.assert_(t[0] is None)
 | 
					            self.assert_(t[0] is None)
 | 
				
			||||||
            self.assertEqual(t[1], {})
 | 
					            self.assertEqual(t[1], {})
 | 
				
			||||||
| 
						 | 
					@ -449,12 +456,13 @@ def skip_if_no_composite(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_composite_
 | 
					    return skip_if_no_composite_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AdaptTypeTestCase(ConnectingTestCase):
 | 
					class AdaptTypeTestCase(ConnectingTestCase):
 | 
				
			||||||
    @skip_if_no_composite
 | 
					    @skip_if_no_composite
 | 
				
			||||||
    def test_none_in_record(self):
 | 
					    def test_none_in_record(self):
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        s = curs.mogrify("SELECT %s;", [(42, None)])
 | 
					        s = curs.mogrify("SELECT %s;", [(42, None)])
 | 
				
			||||||
        self.assertEqual(b("SELECT (42, NULL);"), s)
 | 
					        self.assertEqual(b"SELECT (42, NULL);", s)
 | 
				
			||||||
        curs.execute("SELECT %s;", [(42, None)])
 | 
					        curs.execute("SELECT %s;", [(42, None)])
 | 
				
			||||||
        d = curs.fetchone()[0]
 | 
					        d = curs.fetchone()[0]
 | 
				
			||||||
        self.assertEqual("(42,)", d)
 | 
					        self.assertEqual("(42,)", d)
 | 
				
			||||||
| 
						 | 
					@ -463,8 +471,11 @@ class AdaptTypeTestCase(ConnectingTestCase):
 | 
				
			||||||
        # the None adapter is not actually invoked in regular adaptation
 | 
					        # the None adapter is not actually invoked in regular adaptation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class WonkyAdapter(object):
 | 
					        class WonkyAdapter(object):
 | 
				
			||||||
            def __init__(self, obj): pass
 | 
					            def __init__(self, obj):
 | 
				
			||||||
            def getquoted(self): return "NOPE!"
 | 
					                pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            def getquoted(self):
 | 
				
			||||||
 | 
					                return "NOPE!"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -474,13 +485,14 @@ class AdaptTypeTestCase(ConnectingTestCase):
 | 
				
			||||||
            self.assertEqual(ext.adapt(None).getquoted(), "NOPE!")
 | 
					            self.assertEqual(ext.adapt(None).getquoted(), "NOPE!")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            s = curs.mogrify("SELECT %s;", (None,))
 | 
					            s = curs.mogrify("SELECT %s;", (None,))
 | 
				
			||||||
            self.assertEqual(b("SELECT NULL;"), s)
 | 
					            self.assertEqual(b"SELECT NULL;", s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            ext.register_adapter(type(None), orig_adapter)
 | 
					            ext.register_adapter(type(None), orig_adapter)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_tokenization(self):
 | 
					    def test_tokenization(self):
 | 
				
			||||||
        from psycopg2.extras import CompositeCaster
 | 
					        from psycopg2.extras import CompositeCaster
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def ok(s, v):
 | 
					        def ok(s, v):
 | 
				
			||||||
            self.assertEqual(CompositeCaster.tokenize(s), v)
 | 
					            self.assertEqual(CompositeCaster.tokenize(s), v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -531,7 +543,7 @@ class AdaptTypeTestCase(ConnectingTestCase):
 | 
				
			||||||
        self.assertEqual(v[2], date(2011, 1, 2))
 | 
					        self.assertEqual(v[2], date(2011, 1, 2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from collections import namedtuple
 | 
					            from collections import namedtuple          # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -581,7 +593,7 @@ class AdaptTypeTestCase(ConnectingTestCase):
 | 
				
			||||||
        self.assertEqual(r, v)
 | 
					        self.assertEqual(r, v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from collections import namedtuple
 | 
					            from collections import namedtuple              # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -666,7 +678,7 @@ class AdaptTypeTestCase(ConnectingTestCase):
 | 
				
			||||||
    @skip_if_no_composite
 | 
					    @skip_if_no_composite
 | 
				
			||||||
    @skip_before_postgres(8, 4)
 | 
					    @skip_before_postgres(8, 4)
 | 
				
			||||||
    def test_composite_array(self):
 | 
					    def test_composite_array(self):
 | 
				
			||||||
        oid = self._create_type("type_isd",
 | 
					        self._create_type("type_isd",
 | 
				
			||||||
            [('anint', 'integer'), ('astring', 'text'), ('adate', 'date')])
 | 
					            [('anint', 'integer'), ('astring', 'text'), ('adate', 'date')])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        t = psycopg2.extras.register_composite("type_isd", self.conn)
 | 
					        t = psycopg2.extras.register_composite("type_isd", self.conn)
 | 
				
			||||||
| 
						 | 
					@ -825,6 +837,7 @@ def skip_if_json_module(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_json_module_
 | 
					    return skip_if_json_module_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_json_module(f):
 | 
					def skip_if_no_json_module(f):
 | 
				
			||||||
    """Skip a test if no Python json module is available"""
 | 
					    """Skip a test if no Python json module is available"""
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
| 
						 | 
					@ -836,6 +849,7 @@ def skip_if_no_json_module(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_json_module_
 | 
					    return skip_if_no_json_module_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_json_type(f):
 | 
					def skip_if_no_json_type(f):
 | 
				
			||||||
    """Skip a test if PostgreSQL json type is not available"""
 | 
					    """Skip a test if PostgreSQL json type is not available"""
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
| 
						 | 
					@ -849,6 +863,7 @@ def skip_if_no_json_type(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_json_type_
 | 
					    return skip_if_no_json_type_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class JsonTestCase(ConnectingTestCase):
 | 
					class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
    @skip_if_json_module
 | 
					    @skip_if_json_module
 | 
				
			||||||
    def test_module_not_available(self):
 | 
					    def test_module_not_available(self):
 | 
				
			||||||
| 
						 | 
					@ -858,6 +873,7 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
    @skip_if_json_module
 | 
					    @skip_if_json_module
 | 
				
			||||||
    def test_customizable_with_module_not_available(self):
 | 
					    def test_customizable_with_module_not_available(self):
 | 
				
			||||||
        from psycopg2.extras import Json
 | 
					        from psycopg2.extras import Json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class MyJson(Json):
 | 
					        class MyJson(Json):
 | 
				
			||||||
            def dumps(self, obj):
 | 
					            def dumps(self, obj):
 | 
				
			||||||
                assert obj is None
 | 
					                assert obj is None
 | 
				
			||||||
| 
						 | 
					@ -889,9 +905,11 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        obj = Decimal('123.45')
 | 
					        obj = Decimal('123.45')
 | 
				
			||||||
        dumps = lambda obj: json.dumps(obj, cls=DecimalEncoder)
 | 
					
 | 
				
			||||||
 | 
					        def dumps(obj):
 | 
				
			||||||
 | 
					            return json.dumps(obj, cls=DecimalEncoder)
 | 
				
			||||||
        self.assertEqual(curs.mogrify("%s", (Json(obj, dumps=dumps),)),
 | 
					        self.assertEqual(curs.mogrify("%s", (Json(obj, dumps=dumps),)),
 | 
				
			||||||
            b("'123.45'"))
 | 
					            b"'123.45'")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @skip_if_no_json_module
 | 
					    @skip_if_no_json_module
 | 
				
			||||||
    def test_adapt_subclass(self):
 | 
					    def test_adapt_subclass(self):
 | 
				
			||||||
| 
						 | 
					@ -909,8 +927,7 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        obj = Decimal('123.45')
 | 
					        obj = Decimal('123.45')
 | 
				
			||||||
        self.assertEqual(curs.mogrify("%s", (MyJson(obj),)),
 | 
					        self.assertEqual(curs.mogrify("%s", (MyJson(obj),)), b"'123.45'")
 | 
				
			||||||
            b("'123.45'"))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @skip_if_no_json_module
 | 
					    @skip_if_no_json_module
 | 
				
			||||||
    def test_register_on_dict(self):
 | 
					    def test_register_on_dict(self):
 | 
				
			||||||
| 
						 | 
					@ -920,12 +937,10 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            curs = self.conn.cursor()
 | 
					            curs = self.conn.cursor()
 | 
				
			||||||
            obj = {'a': 123}
 | 
					            obj = {'a': 123}
 | 
				
			||||||
            self.assertEqual(curs.mogrify("%s", (obj,)),
 | 
					            self.assertEqual(curs.mogrify("%s", (obj,)), b"""'{"a": 123}'""")
 | 
				
			||||||
                b("""'{"a": 123}'"""))
 | 
					 | 
				
			||||||
        finally:
 | 
					        finally:
 | 
				
			||||||
            del psycopg2.extensions.adapters[dict, ext.ISQLQuote]
 | 
					            del psycopg2.extensions.adapters[dict, ext.ISQLQuote]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def test_type_not_available(self):
 | 
					    def test_type_not_available(self):
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        curs.execute("select oid from pg_type where typname = 'json'")
 | 
					        curs.execute("select oid from pg_type where typname = 'json'")
 | 
				
			||||||
| 
						 | 
					@ -984,7 +999,9 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
    @skip_if_no_json_type
 | 
					    @skip_if_no_json_type
 | 
				
			||||||
    def test_loads(self):
 | 
					    def test_loads(self):
 | 
				
			||||||
        json = psycopg2.extras.json
 | 
					        json = psycopg2.extras.json
 | 
				
			||||||
        loads = lambda x: json.loads(x, parse_float=Decimal)
 | 
					
 | 
				
			||||||
 | 
					        def loads(s):
 | 
				
			||||||
 | 
					            return json.loads(s, parse_float=Decimal)
 | 
				
			||||||
        psycopg2.extras.register_json(self.conn, loads=loads)
 | 
					        psycopg2.extras.register_json(self.conn, loads=loads)
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        curs.execute("""select '{"a": 100.0, "b": null}'::json""")
 | 
					        curs.execute("""select '{"a": 100.0, "b": null}'::json""")
 | 
				
			||||||
| 
						 | 
					@ -1000,7 +1017,9 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        old = psycopg2.extensions.string_types.get(114)
 | 
					        old = psycopg2.extensions.string_types.get(114)
 | 
				
			||||||
        olda = psycopg2.extensions.string_types.get(199)
 | 
					        olda = psycopg2.extensions.string_types.get(199)
 | 
				
			||||||
        loads = lambda x: psycopg2.extras.json.loads(x, parse_float=Decimal)
 | 
					
 | 
				
			||||||
 | 
					        def loads(s):
 | 
				
			||||||
 | 
					            return psycopg2.extras.json.loads(s, parse_float=Decimal)
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            new, newa = psycopg2.extras.register_json(
 | 
					            new, newa = psycopg2.extras.register_json(
 | 
				
			||||||
                loads=loads, oid=oid, array_oid=array_oid)
 | 
					                loads=loads, oid=oid, array_oid=array_oid)
 | 
				
			||||||
| 
						 | 
					@ -1022,7 +1041,8 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
    def test_register_default(self):
 | 
					    def test_register_default(self):
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loads = lambda x: psycopg2.extras.json.loads(x, parse_float=Decimal)
 | 
					        def loads(s):
 | 
				
			||||||
 | 
					            return psycopg2.extras.json.loads(s, parse_float=Decimal)
 | 
				
			||||||
        psycopg2.extras.register_default_json(curs, loads=loads)
 | 
					        psycopg2.extras.register_default_json(curs, loads=loads)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs.execute("""select '{"a": 100.0, "b": null}'::json""")
 | 
					        curs.execute("""select '{"a": 100.0, "b": null}'::json""")
 | 
				
			||||||
| 
						 | 
					@ -1072,6 +1092,7 @@ class JsonTestCase(ConnectingTestCase):
 | 
				
			||||||
def skip_if_no_jsonb_type(f):
 | 
					def skip_if_no_jsonb_type(f):
 | 
				
			||||||
    return skip_before_postgres(9, 4)(f)
 | 
					    return skip_before_postgres(9, 4)(f)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class JsonbTestCase(ConnectingTestCase):
 | 
					class JsonbTestCase(ConnectingTestCase):
 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def myloads(s):
 | 
					    def myloads(s):
 | 
				
			||||||
| 
						 | 
					@ -1120,7 +1141,10 @@ class JsonbTestCase(ConnectingTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_loads(self):
 | 
					    def test_loads(self):
 | 
				
			||||||
        json = psycopg2.extras.json
 | 
					        json = psycopg2.extras.json
 | 
				
			||||||
        loads = lambda x: json.loads(x, parse_float=Decimal)
 | 
					
 | 
				
			||||||
 | 
					        def loads(s):
 | 
				
			||||||
 | 
					            return json.loads(s, parse_float=Decimal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        psycopg2.extras.register_json(self.conn, loads=loads, name='jsonb')
 | 
					        psycopg2.extras.register_json(self.conn, loads=loads, name='jsonb')
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
        curs.execute("""select '{"a": 100.0, "b": null}'::jsonb""")
 | 
					        curs.execute("""select '{"a": 100.0, "b": null}'::jsonb""")
 | 
				
			||||||
| 
						 | 
					@ -1136,7 +1160,9 @@ class JsonbTestCase(ConnectingTestCase):
 | 
				
			||||||
    def test_register_default(self):
 | 
					    def test_register_default(self):
 | 
				
			||||||
        curs = self.conn.cursor()
 | 
					        curs = self.conn.cursor()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        loads = lambda x: psycopg2.extras.json.loads(x, parse_float=Decimal)
 | 
					        def loads(s):
 | 
				
			||||||
 | 
					            return psycopg2.extras.json.loads(s, parse_float=Decimal)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        psycopg2.extras.register_default_jsonb(curs, loads=loads)
 | 
					        psycopg2.extras.register_default_jsonb(curs, loads=loads)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        curs.execute("""select '{"a": 100.0, "b": null}'::jsonb""")
 | 
					        curs.execute("""select '{"a": 100.0, "b": null}'::jsonb""")
 | 
				
			||||||
| 
						 | 
					@ -1202,7 +1228,7 @@ class RangeTestCase(unittest.TestCase):
 | 
				
			||||||
                ('[)', True, False),
 | 
					                ('[)', True, False),
 | 
				
			||||||
                ('(]', False, True),
 | 
					                ('(]', False, True),
 | 
				
			||||||
                ('()', False, False),
 | 
					                ('()', False, False),
 | 
				
			||||||
                ('[]', True, True),]:
 | 
					                ('[]', True, True)]:
 | 
				
			||||||
            r = Range(10, 20, bounds)
 | 
					            r = Range(10, 20, bounds)
 | 
				
			||||||
            self.assertEqual(r.lower, 10)
 | 
					            self.assertEqual(r.lower, 10)
 | 
				
			||||||
            self.assertEqual(r.upper, 20)
 | 
					            self.assertEqual(r.upper, 20)
 | 
				
			||||||
| 
						 | 
					@ -1296,11 +1322,11 @@ class RangeTestCase(unittest.TestCase):
 | 
				
			||||||
        self.assert_(not Range(empty=True))
 | 
					        self.assert_(not Range(empty=True))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_eq_hash(self):
 | 
					    def test_eq_hash(self):
 | 
				
			||||||
        from psycopg2.extras import Range
 | 
					 | 
				
			||||||
        def assert_equal(r1, r2):
 | 
					        def assert_equal(r1, r2):
 | 
				
			||||||
            self.assert_(r1 == r2)
 | 
					            self.assert_(r1 == r2)
 | 
				
			||||||
            self.assert_(hash(r1) == hash(r2))
 | 
					            self.assert_(hash(r1) == hash(r2))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        from psycopg2.extras import Range
 | 
				
			||||||
        assert_equal(Range(empty=True), Range(empty=True))
 | 
					        assert_equal(Range(empty=True), Range(empty=True))
 | 
				
			||||||
        assert_equal(Range(), Range())
 | 
					        assert_equal(Range(), Range())
 | 
				
			||||||
        assert_equal(Range(10, None), Range(10, None))
 | 
					        assert_equal(Range(10, None), Range(10, None))
 | 
				
			||||||
| 
						 | 
					@ -1323,8 +1349,11 @@ class RangeTestCase(unittest.TestCase):
 | 
				
			||||||
    def test_eq_subclass(self):
 | 
					    def test_eq_subclass(self):
 | 
				
			||||||
        from psycopg2.extras import Range, NumericRange
 | 
					        from psycopg2.extras import Range, NumericRange
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class IntRange(NumericRange): pass
 | 
					        class IntRange(NumericRange):
 | 
				
			||||||
        class PositiveIntRange(IntRange): pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class PositiveIntRange(IntRange):
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.assertEqual(Range(10, 20), IntRange(10, 20))
 | 
					        self.assertEqual(Range(10, 20), IntRange(10, 20))
 | 
				
			||||||
        self.assertEqual(PositiveIntRange(10, 20), IntRange(10, 20))
 | 
					        self.assertEqual(PositiveIntRange(10, 20), IntRange(10, 20))
 | 
				
			||||||
| 
						 | 
					@ -1397,6 +1426,12 @@ class RangeTestCase(unittest.TestCase):
 | 
				
			||||||
        with py3_raises_typeerror():
 | 
					        with py3_raises_typeerror():
 | 
				
			||||||
            self.assert_(Range(1, 2) >= 1)
 | 
					            self.assert_(Range(1, 2) >= 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_pickling(self):
 | 
				
			||||||
 | 
					        from psycopg2.extras import Range
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        r = Range(0, 4)
 | 
				
			||||||
 | 
					        self.assertEqual(loads(dumps(r)), r)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_range(f):
 | 
					def skip_if_no_range(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
| 
						 | 
					@ -1504,7 +1539,8 @@ class RangeCasterTestCase(ConnectingTestCase):
 | 
				
			||||||
        from psycopg2.tz import FixedOffsetTimezone
 | 
					        from psycopg2.tz import FixedOffsetTimezone
 | 
				
			||||||
        cur = self.conn.cursor()
 | 
					        cur = self.conn.cursor()
 | 
				
			||||||
        ts1 = datetime(2000, 1, 1, tzinfo=FixedOffsetTimezone(600))
 | 
					        ts1 = datetime(2000, 1, 1, tzinfo=FixedOffsetTimezone(600))
 | 
				
			||||||
        ts2 = datetime(2000,12,31,23,59,59,999, tzinfo=FixedOffsetTimezone(600))
 | 
					        ts2 = datetime(2000, 12, 31, 23, 59, 59, 999,
 | 
				
			||||||
 | 
					                       tzinfo=FixedOffsetTimezone(600))
 | 
				
			||||||
        cur.execute("select tstzrange(%s, %s, '[]')", (ts1, ts2))
 | 
					        cur.execute("select tstzrange(%s, %s, '[]')", (ts1, ts2))
 | 
				
			||||||
        r = cur.fetchone()[0]
 | 
					        r = cur.fetchone()[0]
 | 
				
			||||||
        self.assert_(isinstance(r, DateTimeTZRange))
 | 
					        self.assert_(isinstance(r, DateTimeTZRange))
 | 
				
			||||||
| 
						 | 
					@ -1595,7 +1631,8 @@ class RangeCasterTestCase(ConnectingTestCase):
 | 
				
			||||||
        self.assert_(r1.isempty)
 | 
					        self.assert_(r1.isempty)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ts1 = datetime(2000, 1, 1, tzinfo=FixedOffsetTimezone(600))
 | 
					        ts1 = datetime(2000, 1, 1, tzinfo=FixedOffsetTimezone(600))
 | 
				
			||||||
        ts2 = datetime(2000,12,31,23,59,59,999, tzinfo=FixedOffsetTimezone(600))
 | 
					        ts2 = datetime(2000, 12, 31, 23, 59, 59, 999,
 | 
				
			||||||
 | 
					                       tzinfo=FixedOffsetTimezone(600))
 | 
				
			||||||
        r = DateTimeTZRange(ts1, ts2, '(]')
 | 
					        r = DateTimeTZRange(ts1, ts2, '(]')
 | 
				
			||||||
        cur.execute("select %s", (r,))
 | 
					        cur.execute("select %s", (r,))
 | 
				
			||||||
        r1 = cur.fetchone()[0]
 | 
					        r1 = cur.fetchone()[0]
 | 
				
			||||||
| 
						 | 
					@ -1732,6 +1769,6 @@ decorate_all_tests(RangeCasterTestCase, skip_if_no_range)
 | 
				
			||||||
def test_suite():
 | 
					def test_suite():
 | 
				
			||||||
    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
					    return unittest.TestLoader().loadTestsFromName(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    unittest.main()
 | 
					    unittest.main()
 | 
				
			||||||
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ import psycopg2.extensions as ext
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from testutils import unittest, ConnectingTestCase
 | 
					from testutils import unittest, ConnectingTestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class WithTestCase(ConnectingTestCase):
 | 
					class WithTestCase(ConnectingTestCase):
 | 
				
			||||||
    def setUp(self):
 | 
					    def setUp(self):
 | 
				
			||||||
        ConnectingTestCase.setUp(self)
 | 
					        ConnectingTestCase.setUp(self)
 | 
				
			||||||
| 
						 | 
					@ -113,6 +114,7 @@ class WithConnectionTestCase(WithTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_subclass_commit(self):
 | 
					    def test_subclass_commit(self):
 | 
				
			||||||
        commits = []
 | 
					        commits = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class MyConn(ext.connection):
 | 
					        class MyConn(ext.connection):
 | 
				
			||||||
            def commit(self):
 | 
					            def commit(self):
 | 
				
			||||||
                commits.append(None)
 | 
					                commits.append(None)
 | 
				
			||||||
| 
						 | 
					@ -131,6 +133,7 @@ class WithConnectionTestCase(WithTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_subclass_rollback(self):
 | 
					    def test_subclass_rollback(self):
 | 
				
			||||||
        rollbacks = []
 | 
					        rollbacks = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class MyConn(ext.connection):
 | 
					        class MyConn(ext.connection):
 | 
				
			||||||
            def rollback(self):
 | 
					            def rollback(self):
 | 
				
			||||||
                rollbacks.append(None)
 | 
					                rollbacks.append(None)
 | 
				
			||||||
| 
						 | 
					@ -189,6 +192,7 @@ class WithCursorTestCase(WithTestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_subclass(self):
 | 
					    def test_subclass(self):
 | 
				
			||||||
        closes = []
 | 
					        closes = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class MyCurs(ext.cursor):
 | 
					        class MyCurs(ext.cursor):
 | 
				
			||||||
            def close(self):
 | 
					            def close(self):
 | 
				
			||||||
                closes.append(None)
 | 
					                closes.append(None)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,8 +7,6 @@ dbhost = os.environ.get('PSYCOPG2_TESTDB_HOST', None)
 | 
				
			||||||
dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None)
 | 
					dbport = os.environ.get('PSYCOPG2_TESTDB_PORT', None)
 | 
				
			||||||
dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None)
 | 
					dbuser = os.environ.get('PSYCOPG2_TESTDB_USER', None)
 | 
				
			||||||
dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None)
 | 
					dbpass = os.environ.get('PSYCOPG2_TESTDB_PASSWORD', None)
 | 
				
			||||||
repl_dsn = os.environ.get('PSYCOPG2_TEST_REPL_DSN',
 | 
					 | 
				
			||||||
    "dbname=psycopg2_test replication=1")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Check if we want to test psycopg's green path.
 | 
					# Check if we want to test psycopg's green path.
 | 
				
			||||||
green = os.environ.get('PSYCOPG2_TEST_GREEN', None)
 | 
					green = os.environ.get('PSYCOPG2_TEST_GREEN', None)
 | 
				
			||||||
| 
						 | 
					@ -35,4 +33,10 @@ if dbuser is not None:
 | 
				
			||||||
if dbpass is not None:
 | 
					if dbpass is not None:
 | 
				
			||||||
    dsn += ' password=%s' % dbpass
 | 
					    dsn += ' password=%s' % dbpass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Don't run replication tests if REPL_DSN is not set, default to normal DSN if
 | 
				
			||||||
 | 
					# set to empty string.
 | 
				
			||||||
 | 
					repl_dsn = os.environ.get('PSYCOPG2_TEST_REPL_DSN', None)
 | 
				
			||||||
 | 
					if repl_dsn == '':
 | 
				
			||||||
 | 
					    repl_dsn = dsn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					repl_slot = os.environ.get('PSYCOPG2_TEST_REPL_SLOT', 'psycopg2_test_slot')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import platform
 | 
					import platform
 | 
				
			||||||
import sys
 | 
					import sys
 | 
				
			||||||
 | 
					import select
 | 
				
			||||||
from functools import wraps
 | 
					from functools import wraps
 | 
				
			||||||
from testconfig import dsn, repl_dsn
 | 
					from testconfig import dsn, repl_dsn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,8 +69,8 @@ else:
 | 
				
			||||||
# Silence warnings caused by the stubbornness of the Python unittest
 | 
					# Silence warnings caused by the stubbornness of the Python unittest
 | 
				
			||||||
# maintainers
 | 
					# maintainers
 | 
				
			||||||
# http://bugs.python.org/issue9424
 | 
					# http://bugs.python.org/issue9424
 | 
				
			||||||
if not hasattr(unittest.TestCase, 'assert_') \
 | 
					if (not hasattr(unittest.TestCase, 'assert_')
 | 
				
			||||||
or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue:
 | 
					        or unittest.TestCase.assert_ is not unittest.TestCase.assertTrue):
 | 
				
			||||||
    # mavaff...
 | 
					    # mavaff...
 | 
				
			||||||
    unittest.TestCase.assert_ = unittest.TestCase.assertTrue
 | 
					    unittest.TestCase.assert_ = unittest.TestCase.assertTrue
 | 
				
			||||||
    unittest.TestCase.failUnless = unittest.TestCase.assertTrue
 | 
					    unittest.TestCase.failUnless = unittest.TestCase.assertTrue
 | 
				
			||||||
| 
						 | 
					@ -100,7 +101,7 @@ class ConnectingTestCase(unittest.TestCase):
 | 
				
			||||||
            self._conns
 | 
					            self._conns
 | 
				
			||||||
        except AttributeError, e:
 | 
					        except AttributeError, e:
 | 
				
			||||||
            raise AttributeError(
 | 
					            raise AttributeError(
 | 
				
			||||||
                "%s (did you remember calling ConnectingTestCase.setUp()?)"
 | 
					                "%s (did you forget to call ConnectingTestCase.setUp()?)"
 | 
				
			||||||
                % e)
 | 
					                % e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if 'dsn' in kwargs:
 | 
					        if 'dsn' in kwargs:
 | 
				
			||||||
| 
						 | 
					@ -121,15 +122,25 @@ class ConnectingTestCase(unittest.TestCase):
 | 
				
			||||||
        Should raise a skip test if not available, but guard for None on
 | 
					        Should raise a skip test if not available, but guard for None on
 | 
				
			||||||
        old Python versions.
 | 
					        old Python versions.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        if repl_dsn is None:
 | 
				
			||||||
 | 
					            return self.skipTest("replication tests disabled by default")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if 'dsn' not in kwargs:
 | 
					        if 'dsn' not in kwargs:
 | 
				
			||||||
            kwargs['dsn'] = repl_dsn
 | 
					            kwargs['dsn'] = repl_dsn
 | 
				
			||||||
        import psycopg2
 | 
					        import psycopg2
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            conn = self.connect(**kwargs)
 | 
					            conn = self.connect(**kwargs)
 | 
				
			||||||
 | 
					            if conn.async == 1:
 | 
				
			||||||
 | 
					                self.wait(conn)
 | 
				
			||||||
        except psycopg2.OperationalError, e:
 | 
					        except psycopg2.OperationalError, e:
 | 
				
			||||||
 | 
					            # If pgcode is not set it is a genuine connection error
 | 
				
			||||||
 | 
					            # Otherwise we tried to run some bad operation in the connection
 | 
				
			||||||
 | 
					            # (e.g. bug #482) and we'd rather know that.
 | 
				
			||||||
 | 
					            if e.pgcode is None:
 | 
				
			||||||
                return self.skipTest("replication db not configured: %s" % e)
 | 
					                return self.skipTest("replication db not configured: %s" % e)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        conn.autocommit = True
 | 
					 | 
				
			||||||
        return conn
 | 
					        return conn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _get_conn(self):
 | 
					    def _get_conn(self):
 | 
				
			||||||
| 
						 | 
					@ -143,6 +154,23 @@ class ConnectingTestCase(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    conn = property(_get_conn, _set_conn)
 | 
					    conn = property(_get_conn, _set_conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # for use with async connections only
 | 
				
			||||||
 | 
					    def wait(self, cur_or_conn):
 | 
				
			||||||
 | 
					        import psycopg2.extensions
 | 
				
			||||||
 | 
					        pollable = cur_or_conn
 | 
				
			||||||
 | 
					        if not hasattr(pollable, 'poll'):
 | 
				
			||||||
 | 
					            pollable = cur_or_conn.connection
 | 
				
			||||||
 | 
					        while True:
 | 
				
			||||||
 | 
					            state = pollable.poll()
 | 
				
			||||||
 | 
					            if state == psycopg2.extensions.POLL_OK:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            elif state == psycopg2.extensions.POLL_READ:
 | 
				
			||||||
 | 
					                select.select([pollable], [], [], 10)
 | 
				
			||||||
 | 
					            elif state == psycopg2.extensions.POLL_WRITE:
 | 
				
			||||||
 | 
					                select.select([], [pollable], [], 10)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                raise Exception("Unexpected result from poll: %r", state)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def decorate_all_tests(cls, *decorators):
 | 
					def decorate_all_tests(cls, *decorators):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -159,7 +187,7 @@ def skip_if_no_uuid(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_no_uuid_(self):
 | 
					    def skip_if_no_uuid_(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            import uuid
 | 
					            import uuid             # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            return self.skipTest("uuid not available in this Python version")
 | 
					            return self.skipTest("uuid not available in this Python version")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -207,7 +235,7 @@ def skip_if_no_namedtuple(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_no_namedtuple_(self):
 | 
					    def skip_if_no_namedtuple_(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from collections import namedtuple
 | 
					            from collections import namedtuple              # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            return self.skipTest("collections.namedtuple not available")
 | 
					            return self.skipTest("collections.namedtuple not available")
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -221,7 +249,7 @@ def skip_if_no_iobase(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_no_iobase_(self):
 | 
					    def skip_if_no_iobase_(self):
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            from io import TextIOBase
 | 
					            from io import TextIOBase                       # noqa
 | 
				
			||||||
        except ImportError:
 | 
					        except ImportError:
 | 
				
			||||||
            return self.skipTest("io.TextIOBase not found.")
 | 
					            return self.skipTest("io.TextIOBase not found.")
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
| 
						 | 
					@ -233,6 +261,7 @@ def skip_if_no_iobase(f):
 | 
				
			||||||
def skip_before_postgres(*ver):
 | 
					def skip_before_postgres(*ver):
 | 
				
			||||||
    """Skip a test on PostgreSQL before a certain version."""
 | 
					    """Skip a test on PostgreSQL before a certain version."""
 | 
				
			||||||
    ver = ver + (0,) * (3 - len(ver))
 | 
					    ver = ver + (0,) * (3 - len(ver))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def skip_before_postgres_(f):
 | 
					    def skip_before_postgres_(f):
 | 
				
			||||||
        @wraps(f)
 | 
					        @wraps(f)
 | 
				
			||||||
        def skip_before_postgres__(self):
 | 
					        def skip_before_postgres__(self):
 | 
				
			||||||
| 
						 | 
					@ -245,9 +274,11 @@ def skip_before_postgres(*ver):
 | 
				
			||||||
        return skip_before_postgres__
 | 
					        return skip_before_postgres__
 | 
				
			||||||
    return skip_before_postgres_
 | 
					    return skip_before_postgres_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_after_postgres(*ver):
 | 
					def skip_after_postgres(*ver):
 | 
				
			||||||
    """Skip a test on PostgreSQL after (including) a certain version."""
 | 
					    """Skip a test on PostgreSQL after (including) a certain version."""
 | 
				
			||||||
    ver = ver + (0,) * (3 - len(ver))
 | 
					    ver = ver + (0,) * (3 - len(ver))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def skip_after_postgres_(f):
 | 
					    def skip_after_postgres_(f):
 | 
				
			||||||
        @wraps(f)
 | 
					        @wraps(f)
 | 
				
			||||||
        def skip_after_postgres__(self):
 | 
					        def skip_after_postgres__(self):
 | 
				
			||||||
| 
						 | 
					@ -260,6 +291,7 @@ def skip_after_postgres(*ver):
 | 
				
			||||||
        return skip_after_postgres__
 | 
					        return skip_after_postgres__
 | 
				
			||||||
    return skip_after_postgres_
 | 
					    return skip_after_postgres_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def libpq_version():
 | 
					def libpq_version():
 | 
				
			||||||
    import psycopg2
 | 
					    import psycopg2
 | 
				
			||||||
    v = psycopg2.__libpq_version__
 | 
					    v = psycopg2.__libpq_version__
 | 
				
			||||||
| 
						 | 
					@ -267,9 +299,11 @@ def libpq_version():
 | 
				
			||||||
        v = psycopg2.extensions.libpq_version()
 | 
					        v = psycopg2.extensions.libpq_version()
 | 
				
			||||||
    return v
 | 
					    return v
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_before_libpq(*ver):
 | 
					def skip_before_libpq(*ver):
 | 
				
			||||||
    """Skip a test if libpq we're linked to is older than a certain version."""
 | 
					    """Skip a test if libpq we're linked to is older than a certain version."""
 | 
				
			||||||
    ver = ver + (0,) * (3 - len(ver))
 | 
					    ver = ver + (0,) * (3 - len(ver))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def skip_before_libpq_(f):
 | 
					    def skip_before_libpq_(f):
 | 
				
			||||||
        @wraps(f)
 | 
					        @wraps(f)
 | 
				
			||||||
        def skip_before_libpq__(self):
 | 
					        def skip_before_libpq__(self):
 | 
				
			||||||
| 
						 | 
					@ -282,9 +316,11 @@ def skip_before_libpq(*ver):
 | 
				
			||||||
        return skip_before_libpq__
 | 
					        return skip_before_libpq__
 | 
				
			||||||
    return skip_before_libpq_
 | 
					    return skip_before_libpq_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_after_libpq(*ver):
 | 
					def skip_after_libpq(*ver):
 | 
				
			||||||
    """Skip a test if libpq we're linked to is newer than a certain version."""
 | 
					    """Skip a test if libpq we're linked to is newer than a certain version."""
 | 
				
			||||||
    ver = ver + (0,) * (3 - len(ver))
 | 
					    ver = ver + (0,) * (3 - len(ver))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def skip_after_libpq_(f):
 | 
					    def skip_after_libpq_(f):
 | 
				
			||||||
        @wraps(f)
 | 
					        @wraps(f)
 | 
				
			||||||
        def skip_after_libpq__(self):
 | 
					        def skip_after_libpq__(self):
 | 
				
			||||||
| 
						 | 
					@ -297,6 +333,7 @@ def skip_after_libpq(*ver):
 | 
				
			||||||
        return skip_after_libpq__
 | 
					        return skip_after_libpq__
 | 
				
			||||||
    return skip_after_libpq_
 | 
					    return skip_after_libpq_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_before_python(*ver):
 | 
					def skip_before_python(*ver):
 | 
				
			||||||
    """Skip a test on Python before a certain version."""
 | 
					    """Skip a test on Python before a certain version."""
 | 
				
			||||||
    def skip_before_python_(f):
 | 
					    def skip_before_python_(f):
 | 
				
			||||||
| 
						 | 
					@ -311,6 +348,7 @@ def skip_before_python(*ver):
 | 
				
			||||||
        return skip_before_python__
 | 
					        return skip_before_python__
 | 
				
			||||||
    return skip_before_python_
 | 
					    return skip_before_python_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_from_python(*ver):
 | 
					def skip_from_python(*ver):
 | 
				
			||||||
    """Skip a test on Python after (including) a certain version."""
 | 
					    """Skip a test on Python after (including) a certain version."""
 | 
				
			||||||
    def skip_from_python_(f):
 | 
					    def skip_from_python_(f):
 | 
				
			||||||
| 
						 | 
					@ -325,6 +363,7 @@ def skip_from_python(*ver):
 | 
				
			||||||
        return skip_from_python__
 | 
					        return skip_from_python__
 | 
				
			||||||
    return skip_from_python_
 | 
					    return skip_from_python_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_superuser(f):
 | 
					def skip_if_no_superuser(f):
 | 
				
			||||||
    """Skip a test if the database user running the test is not a superuser"""
 | 
					    """Skip a test if the database user running the test is not a superuser"""
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
| 
						 | 
					@ -341,6 +380,7 @@ def skip_if_no_superuser(f):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return skip_if_no_superuser_
 | 
					    return skip_if_no_superuser_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_green(reason):
 | 
					def skip_if_green(reason):
 | 
				
			||||||
    def skip_if_green_(f):
 | 
					    def skip_if_green_(f):
 | 
				
			||||||
        @wraps(f)
 | 
					        @wraps(f)
 | 
				
			||||||
| 
						 | 
					@ -356,6 +396,7 @@ def skip_if_green(reason):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
skip_copy_if_green = skip_if_green("copy in async mode currently not supported")
 | 
					skip_copy_if_green = skip_if_green("copy in async mode currently not supported")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_no_getrefcount(f):
 | 
					def skip_if_no_getrefcount(f):
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
    def skip_if_no_getrefcount_(self):
 | 
					    def skip_if_no_getrefcount_(self):
 | 
				
			||||||
| 
						 | 
					@ -365,6 +406,7 @@ def skip_if_no_getrefcount(f):
 | 
				
			||||||
            return f(self)
 | 
					            return f(self)
 | 
				
			||||||
    return skip_if_no_getrefcount_
 | 
					    return skip_if_no_getrefcount_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def skip_if_windows(f):
 | 
					def skip_if_windows(f):
 | 
				
			||||||
    """Skip a test if run on windows"""
 | 
					    """Skip a test if run on windows"""
 | 
				
			||||||
    @wraps(f)
 | 
					    @wraps(f)
 | 
				
			||||||
| 
						 | 
					@ -403,6 +445,7 @@ def script_to_py3(script):
 | 
				
			||||||
        f2.close()
 | 
					        f2.close()
 | 
				
			||||||
        os.remove(filename)
 | 
					        os.remove(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class py3_raises_typeerror(object):
 | 
					class py3_raises_typeerror(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __enter__(self):
 | 
					    def __enter__(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user