]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Support for multiple hosts in PostgreSQL connection string
authorRamonWill <ramonwilliams@hotmail.co.uk>
Wed, 2 Sep 2020 22:43:53 +0000 (18:43 -0400)
committerRamonWill <ramonwilliams@hotmail.co.uk>
Wed, 23 Sep 2020 10:20:54 +0000 (11:20 +0100)
Provide support for multiple hosts in the PostgreSQL connection string.

A user requested for SQLAlchemy to support multiple hosts within a PostgreSQL URL string. The proposed fix allows this. In the event that the url contains multiple hosts the proposed code will convert the query["hosts"] tuple into a single string. This allows the hosts to then get converted into a valid dsn variable in the psycopg2 connect function.

This pull request is:

- [ ] A documentation / typographical error fix
- Good to go, no issue or tests are needed
- [X ] A short code fix
- please include the issue number, and create an issue if none exists, which
  must include a complete example of the issue.  one line code fixes without an
  issue and demonstration will not be accepted.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.   one line code fixes without tests will not be accepted.
- [ ] A new feature implementation
- please include the issue number, and create an issue if none exists, which must
  include a complete example of how the feature would look.
- Please include: `Fixes: #<issue number>` in the commit message
- please include tests.

**Have a nice day!**
Fixes: #4392
Closes: #5554
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5554
Pull-request-sha: 3f7a0ab8df9f1411a9f1ac0e152583bc7bf0c365

Change-Id: I3f3768d51b8331de786ffdc025b7ecfc662eafe5

doc/build/changelog/unreleased_13/4392.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/postgresql/psycopg2.py
test/dialect/postgresql/test_dialect.py

diff --git a/doc/build/changelog/unreleased_13/4392.rst b/doc/build/changelog/unreleased_13/4392.rst
new file mode 100644 (file)
index 0000000..ecb10ca
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: engine, postgresql, usecase
+    :tickets: 4392
+
+    A :class:`.URL` connection now supports multiple hosts for PostgreSQL. Any
+    query that contains multiple hosts will now group the hosts as a string
+    instead of a tuple of strings. Pull request courtesy Ramon Williams.
index 91576c4d2019d6e66e08226b6d7f49d81e5ca55c..3c9ef72c4428a5c4a2f91dd4a875dc817ddd3ac4 100644 (file)
@@ -71,6 +71,28 @@ using ``host`` as an additional keyword argument::
     `PQconnectdbParams \
     <http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS>`_
 
+Multiple Hosts in Connection String
+------------------------
+psycopg2 supports multiple connection points in the connection string.
+When the ``host`` parameter is used multiple times in the query section of
+the URL, SQLAlchemy will create a single string of the host and port
+information provided to make the connections::
+
+    create_engine(
+        "postgresql+psycopg2://user:password@/dbname?host=HostA:port1&host=HostB&host=HostC"
+    )
+
+A connection to each host is then attempted until either a connection is successful
+or all connections are unsuccessful in which case an error is raised.
+
+.. versionadded:: 1.3.20 Support for multiple hosts in PostgreSQL connection
+   string.
+
+.. seealso::
+
+    `PQConnString \
+    <https://www.postgresql.org/docs/10/libpq-connect.html#LIBPQ-CONNSTRING>`_
+
 Empty DSN Connections / Environment Variable Connections
 ---------------------------------------------------------
 
@@ -929,16 +951,25 @@ class PGDialect_psycopg2(PGDialect):
 
     def create_connect_args(self, url):
         opts = url.translate_connect_args(username="user")
+
+        is_multihost = False
+        if "host" in url.query:
+            is_multihost = isinstance(url.query["host"], (list, tuple))
+
         if opts:
             if "port" in opts:
                 opts["port"] = int(opts["port"])
             opts.update(url.query)
+            if is_multihost:
+                opts["host"] = ",".join(url.query["host"])
             # send individual dbname, user, password, host, port
             # parameters to psycopg2.connect()
             return ([], opts)
         elif url.query:
             # any other connection arguments, pass directly
             opts.update(url.query)
+            if is_multihost:
+                opts["host"] = ",".join(url.query["host"])
             return ([], opts)
         else:
             # no connection arguments whatsoever; psycopg2.connect()
index 971d4f12f71af06dfd85272cc513c7c6cb8fe47c..46a01860d5633564528a8768a5779a9e643c9ba9 100644 (file)
@@ -159,6 +159,25 @@ class DialectTest(fixtures.TestBase):
         eq_(cargs, [])
         eq_(cparams, {"host": "somehost", "any_random_thing": "yes"})
 
+    def test_psycopg2_nonempty_connection_string_w_query_two(self):
+        dialect = psycopg2_dialect.dialect()
+        url_string = "postgresql://USER:PASS@/DB?host=hostA"
+        u = url.make_url(url_string)
+        cargs, cparams = dialect.create_connect_args(u)
+        eq_(cargs, [])
+        eq_(cparams["host"], "hostA")
+
+    def test_psycopg2_nonempty_connection_string_w_query_three(self):
+        dialect = psycopg2_dialect.dialect()
+        url_string = (
+            "postgresql://USER:PASS@/DB"
+            "?host=hostA:portA&host=hostB&host=hostC"
+        )
+        u = url.make_url(url_string)
+        cargs, cparams = dialect.create_connect_args(u)
+        eq_(cargs, [])
+        eq_(cparams["host"], "hostA:portA,hostB,hostC")
+
 
 class ExecuteManyMode(object):
     __only_on__ = "postgresql+psycopg2"