Skip to content

Commit

Permalink
Support DNS-less connections for psycopg2
Browse files Browse the repository at this point in the history
Added support for parameter-less connection URLs for the psycopg2 dialect,
meaning, the URL can be passed to :func:`.create_engine` as
``"postgresql+psycopg2://"`` with no additional arguments to indicate an
empty DSN passed to libpq, which indicates to connect to "localhost" with
no username, password, or database given. Pull request courtesy Julian
Mehnle.

Fixes: sqlalchemy#4562
Closes: sqlalchemy#4563
Pull-request: sqlalchemy#4563
Pull-request-sha: 8a05c96

Change-Id: Ib6fca3c3c9eebeaf590d7f7fb0bc8cd4b6e4a55a
  • Loading branch information
jmehnle authored and zzzeek committed Mar 24, 2019
1 parent 8acbc26 commit a31da95
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 4 deletions.
10 changes: 10 additions & 0 deletions doc/build/changelog/unreleased_13/4562.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.. change::
:tags: feature, postgresql
:tickets: 4562

Added support for parameter-less connection URLs for the psycopg2 dialect,
meaning, the URL can be passed to :func:`.create_engine` as
``"postgresql+psycopg2://"`` with no additional arguments to indicate an
empty DSN passed to libpq, which indicates to connect to "localhost" with
no username, password, or database given. Pull request courtesy Julian
Mehnle.
41 changes: 37 additions & 4 deletions lib/sqlalchemy/dialects/postgresql/psycopg2.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,33 @@
`PQconnectdbParams \
<http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS>`_
Empty DSN Connections / Environment Variable Connections
---------------------------------------------------------
The psycopg2 DBAPI can connect to PostgreSQL by passing an empty DSN to the
libpq client library, which by default indicates to connect to a localhost
PostgreSQL database that is open for "trust" connections. This behavior can be
further tailored using a particular set of environment variables which are
prefixed with ``PG_...``, which are consumed by ``libpq`` to take the place of
any or all elements of the connection string.
For this form, the URL can be passed without any elements other than the
initial scheme::
engine = create_engine('postgresql+psycopg2://')
In the above form, a blank "dsn" string is passed to the ``psycopg2.connect()``
function which in turn represents an empty DSN passed to libpq.
.. versionadded:: 1.3.2 support for parameter-less connections with psycopg2.
.. seealso::
`Environment Variables\
<https://www.postgresql.org/docs/current/libpq-envars.html>`_ -
PostgreSQL documentation on how to use ``PG_...``
environment variables for connections.
.. _psycopg2_execution_options:
Per-Statement/Connection Execution Options
Expand Down Expand Up @@ -735,10 +762,16 @@ def _hstore_oids(self, conn):

def create_connect_args(self, url):
opts = url.translate_connect_args(username="user")
if "port" in opts:
opts["port"] = int(opts["port"])
opts.update(url.query)
return ([], opts)
if opts:
if "port" in opts:
opts["port"] = int(opts["port"])
opts.update(url.query)
# send individual dbname, user, password, host, port
# parameters to psycopg2.connect()
return ([], opts)
else:
# send a blank string for "dsn" to psycopg2.connect()
return ([''], opts)

def is_disconnect(self, e, connection, cursor):
if isinstance(e, self.dbapi.Error):
Expand Down
15 changes: 15 additions & 0 deletions test/dialect/postgresql/test_dialect.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from sqlalchemy import text
from sqlalchemy import TypeDecorator
from sqlalchemy.dialects.postgresql import base as postgresql
from sqlalchemy.dialects.postgresql import psycopg2 as psycopg2_dialect
from sqlalchemy.engine import engine_from_config
from sqlalchemy.engine import url
from sqlalchemy.testing import engines
Expand Down Expand Up @@ -114,6 +115,20 @@ def test_pg_dialect_use_native_unicode_from_config(self):
e = engine_from_config(config, _initialize=False)
eq_(e.dialect.use_native_unicode, True)

def test_psycopg2_empty_connection_string(self):
dialect = psycopg2_dialect.dialect()
u = url.make_url("postgresql://")
cargs, cparams = dialect.create_connect_args(u)
eq_(cargs, [''])
eq_(cparams, {})

def test_psycopg2_nonempty_connection_string(self):
dialect = psycopg2_dialect.dialect()
u = url.make_url("postgresql://host")
cargs, cparams = dialect.create_connect_args(u)
eq_(cargs, [])
eq_(cparams, {"host": "host"})


class BatchInsertsTest(fixtures.TablesTest):
__only_on__ = "postgresql+psycopg2"
Expand Down

0 comments on commit a31da95

Please sign in to comment.