From: Federico Caselli Date: Tue, 13 Sep 2022 19:47:50 +0000 (+0200) Subject: Use ``;`` instead of ``select 1`` to ping PostgreSQL X-Git-Tag: rel_2_0_0b1~63^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2e302b2d8feca685c8f1b8e2174f239086d239fa;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Use ``;`` instead of ``select 1`` to ping PostgreSQL Fixes: #8491 Change-Id: I941d2a3cf92e5609e2045a53cec94522340951db --- diff --git a/doc/build/changelog/unreleased_20/8491.rst b/doc/build/changelog/unreleased_20/8491.rst new file mode 100644 index 0000000000..155515700b --- /dev/null +++ b/doc/build/changelog/unreleased_20/8491.rst @@ -0,0 +1,12 @@ +.. change:: + :tags: usecase, postgresql + :tickets: 8491 + + The "ping" query emitted when configuring + :paramref:`_sa.create_engine.pool_pre_ping` for psycopg, asyncpg and + pg8000, but not for psycopg2, has been changed to be an empty query (``;``) + instead of ``SELECT 1``; additionally, for the asyncpg driver, the + unnecessary use of a prepared statement for this query has been fixed. + Rationale is to eliminate the need for PostgreSQL to produce a query plan + when the ping is emitted. The operation is not currently supported by the + ``psycopg2`` driver which continues to use ``SELECT 1``. \ No newline at end of file diff --git a/lib/sqlalchemy/dialects/postgresql/asyncpg.py b/lib/sqlalchemy/dialects/postgresql/asyncpg.py index a84bece4fd..4cc04d20ae 100644 --- a/lib/sqlalchemy/dialects/postgresql/asyncpg.py +++ b/lib/sqlalchemy/dialects/postgresql/asyncpg.py @@ -741,6 +741,12 @@ class AsyncAdapt_asyncpg_connection(AdaptedConnection): else: self.isolation_level = self._isolation_setting + def ping(self): + try: + _ = self.await_(self._connection.fetchrow(";")) + except Exception as error: + self._handle_exception(error) + def set_isolation_level(self, level): if self._started: self.rollback() @@ -996,6 +1002,17 @@ class PGDialect_asyncpg(PGDialect): util.coerce_kw_type(opts, "port", int) return ([], opts) + def do_ping(self, dbapi_connection): + try: + dbapi_connection.ping() + except self.dbapi.Error as err: + if self.is_disconnect(err, dbapi_connection, None): + return False + else: + raise + else: + return True + @classmethod def get_pool_class(cls, url): diff --git a/lib/sqlalchemy/dialects/postgresql/pg8000.py b/lib/sqlalchemy/dialects/postgresql/pg8000.py index ce9a3bb6c1..d4b159b73b 100644 --- a/lib/sqlalchemy/dialects/postgresql/pg8000.py +++ b/lib/sqlalchemy/dialects/postgresql/pg8000.py @@ -559,5 +559,9 @@ class PGDialect_pg8000(PGDialect): else: return None + @util.memoized_property + def _dialect_specific_select_one(self): + return ";" + dialect = PGDialect_pg8000 diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg.py b/lib/sqlalchemy/dialects/postgresql/psycopg.py index 633357a740..371bf2bc23 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg.py @@ -505,6 +505,10 @@ class PGDialect_psycopg(_PGDialect_common_psycopg): else: self.do_commit(connection.connection) + @util.memoized_property + def _dialect_specific_select_one(self): + return ";" + class AsyncAdapt_psycopg_cursor: __slots__ = ("_cursor", "await_", "_rows") diff --git a/lib/sqlalchemy/testing/suite/test_dialect.py b/lib/sqlalchemy/testing/suite/test_dialect.py index 55276e21b2..bb2dd6574f 100644 --- a/lib/sqlalchemy/testing/suite/test_dialect.py +++ b/lib/sqlalchemy/testing/suite/test_dialect.py @@ -8,6 +8,7 @@ from .. import config from .. import engines from .. import eq_ from .. import fixtures +from .. import is_true from .. import ne_ from .. import provide_metadata from ..assertions import expect_raises_message @@ -24,6 +25,16 @@ from ... import select from ... import String +class PingTest(fixtures.TestBase): + __backend__ = True + + def test_do_ping(self): + with testing.db.connect() as conn: + is_true( + testing.db.dialect.do_ping(conn.connection.dbapi_connection) + ) + + class ExceptionTest(fixtures.TablesTest): """Test basic exception wrapping.