From d5c02ecc3442a50e04dd23c67f4878210f415024 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 17:13:29 +0400 Subject: [PATCH 01/16] added support and test cases for pyscorg3 --- debug_toolbar/panels/sql/panel.py | 58 +++++++++++++++++++--------- debug_toolbar/panels/sql/tracking.py | 27 ++++++++----- tests/panels/test_sql.py | 40 ++++++++++++++----- tox.ini | 8 ++-- 4 files changed, 94 insertions(+), 39 deletions(-) diff --git a/debug_toolbar/panels/sql/panel.py b/debug_toolbar/panels/sql/panel.py index d8099f25b..9a617fcca 100644 --- a/debug_toolbar/panels/sql/panel.py +++ b/debug_toolbar/panels/sql/panel.py @@ -17,15 +17,26 @@ def get_isolation_level_display(vendor, level): if vendor == "postgresql": - import psycopg2.extensions - - choices = { - psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT: _("Autocommit"), - psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED: _("Read uncommitted"), - psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED: _("Read committed"), - psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: _("Repeatable read"), - psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE: _("Serializable"), - } + + try: + import psycopg + choices = { + # AUTOCOMMIT level does not exists in psycopg3 + psycopg.IsolationLevel.READ_UNCOMMITTED : _("Read uncommitted"), + psycopg.IsolationLevel.READ_COMMITTED: _("Read committed"), + psycopg.IsolationLevel.REPEATABLE_READ: _("Repeatable read"), + psycopg.IsolationLevel.SERIALIZABLE: _("Serializable"), + } + except ImportError: + import psycopg2.extensions + choices = { + psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT: _("Autocommit"), + psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED: _("Read uncommitted"), + psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED: _("Read committed"), + psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: _("Repeatable read"), + psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE: _("Serializable"), + } + else: raise ValueError(vendor) return choices.get(level) @@ -33,15 +44,26 @@ def get_isolation_level_display(vendor, level): def get_transaction_status_display(vendor, level): if vendor == "postgresql": - import psycopg2.extensions - - choices = { - psycopg2.extensions.TRANSACTION_STATUS_IDLE: _("Idle"), - psycopg2.extensions.TRANSACTION_STATUS_ACTIVE: _("Active"), - psycopg2.extensions.TRANSACTION_STATUS_INTRANS: _("In transaction"), - psycopg2.extensions.TRANSACTION_STATUS_INERROR: _("In error"), - psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN: _("Unknown"), - } + + try: + import psycopg + choices = { + psycopg.pq.TransactionStatus.IDLE: _("Idle"), + psycopg.pq.TransactionStatus.ACTIVE: _("Active"), + psycopg.pq.TransactionStatus.INTRANS: _("In transaction"), + psycopg.pq.TransactionStatus.INERROR: _("In error"), + psycopg.pq.TransactionStatus.UNKNOWN: _("Unknown"), + } + except ImportError: + import psycopg2.extensions + choices = { + psycopg2.extensions.TRANSACTION_STATUS_IDLE: _("Idle"), + psycopg2.extensions.TRANSACTION_STATUS_ACTIVE: _("Active"), + psycopg2.extensions.TRANSACTION_STATUS_INTRANS: _("In transaction"), + psycopg2.extensions.TRANSACTION_STATUS_INERROR: _("In error"), + psycopg2.extensions.TRANSACTION_STATUS_UNKNOWN: _("Unknown"), + } + else: raise ValueError(vendor) return choices.get(level) diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 9fda4eba5..0b2242ccf 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -9,11 +9,16 @@ from debug_toolbar.utils import get_stack_trace, get_template_info try: - from psycopg2._json import Json as PostgresJson - from psycopg2.extensions import STATUS_IN_TRANSACTION + import psycopg + PostgresJson = psycopg.types.json.Jsonb + STATUS_IN_TRANSACTION = psycopg.pq.TransactionStatus.INTRANS except ImportError: - PostgresJson = None - STATUS_IN_TRANSACTION = None + try: + from psycopg2._json import Json as PostgresJson + from psycopg2.extensions import STATUS_IN_TRANSACTION + except ImportError: + PostgresJson = None + STATUS_IN_TRANSACTION = None # Prevents SQL queries from being sent to the DB. It's used # by the TemplatePanel to prevent the toolbar from issuing @@ -126,7 +131,8 @@ def _quote_params(self, params): def _decode(self, param): if PostgresJson and isinstance(param, PostgresJson): - return param.dumps(param.adapted) + return param.dumps(param.obj) + # If a sequence type, decode each element separately if isinstance(param, (tuple, list)): return [self._decode(element) for element in param] @@ -149,7 +155,7 @@ def _record(self, method, sql, params): if vendor == "postgresql": # The underlying DB connection (as opposed to Django's wrapper) conn = self.db.connection - initial_conn_status = conn.status + initial_conn_status = conn.info.status start_time = time() try: @@ -166,7 +172,10 @@ def _record(self, method, sql, params): # Sql might be an object (such as psycopg Composed). # For logging purposes, make sure it's str. - sql = str(sql) + if vendor == "postgresql" and not isinstance(sql, str): + sql = sql.as_string(conn) + else: + sql = str(sql) params = { "vendor": vendor, @@ -205,7 +214,7 @@ def _record(self, method, sql, params): # case where Django can start a transaction before the first query # executes, so in that case logger.current_transaction_id() will # generate a new transaction ID since one does not already exist. - final_conn_status = conn.status + final_conn_status = conn.info.status if final_conn_status == STATUS_IN_TRANSACTION: if initial_conn_status == STATUS_IN_TRANSACTION: trans_id = self.logger.current_transaction_id(alias) @@ -217,7 +226,7 @@ def _record(self, method, sql, params): params.update( { "trans_id": trans_id, - "trans_status": conn.get_transaction_status(), + "trans_status": conn.info.transaction_status, "iso_level": iso_level, } ) diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index ee9a134b9..80f06055e 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -225,14 +225,33 @@ def test_json_param_conversion(self): connection.vendor == "postgresql", "Test valid only on PostgreSQL" ) def test_tuple_param_conversion(self): + """ + For psycopg3 You cannot use IN %s with a tuple + https://www.psycopg.org/psycopg3/docs/basic/from_pg2.html#you-cannot-use-in-s-with-a-tuple + """ + + is_psycopg3 = True + try: + from psycopg import sql + except ImportError: + is_psycopg3 = False + self.assertEqual(len(self.panel._queries), 0) - list( - PostgresJSON.objects.raw( - "SELECT * FROM tests_postgresjson WHERE field ->> 'key' IN %s", - [("a", "b'")], + if is_psycopg3: + list( + PostgresJSON.objects.raw( + "SELECT * FROM tests_postgresjson WHERE field ->> 'key' = ANY(%s)", + [["a", "b'"]], + ) + ) + else: + list( + PostgresJSON.objects.raw( + "SELECT * FROM tests_postgresjson WHERE field ->> 'key' IN %s", + [("a", "b'")], + ) ) - ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) @@ -377,12 +396,15 @@ def test_erroneous_query(self): @unittest.skipUnless( connection.vendor == "postgresql", "Test valid only on PostgreSQL" ) - def test_execute_with_psycopg2_composed_sql(self): + def test_execute_with_psycopg_composed_sql(self): """ - Test command executed using a Composed psycopg2 object is logged. - Ref: http://initd.org/psycopg/docs/sql.html + Test command executed using a Composed psycopg object is logged. + Ref: https://www.psycopg.org/psycopg3/docs/api/sql.html """ - from psycopg2 import sql + try: + from psycopg import sql + except ImportError: + from psycopg2 import sql self.assertEqual(len(self.panel._queries), 0) diff --git a/tox.ini b/tox.ini index 58fc31907..2d0b1ee44 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ isolated_build = true envlist = docs packaging - py{38,39,310}-dj{32,41,42}-{sqlite,postgresql,postgis,mysql} + py{38,39,310}-dj{32,41,42}-{sqlite,legacy_postgresql,postgis,mysql} py{310}-dj{40}-{sqlite} - py{310,311}-dj{41,42,main}-{sqlite,postgresql,postgis,mysql} + py{310,311}-dj{41,42,main}-{sqlite,legacy_postgresql,postgresql,postgis,mysql} [testenv] deps = @@ -13,7 +13,8 @@ deps = dj40: django~=4.0.0 dj41: django~=4.1.3 dj42: django>=4.2a1,<5 - postgresql: psycopg2-binary + legacy_postgresql: psycopg2-binary + postgresql: psycopg[binary] postgis: psycopg2-binary mysql: mysqlclient djmain: https://github.com/django/django/archive/main.tar.gz @@ -97,5 +98,6 @@ python = DB_BACKEND = mysql: mysql postgresql: postgresql + legacy_postgresql: postgresql postgis: postgresql sqlite3: sqlite From 6426c0bf86aa2721d2143014bb4cbadcea3b321b Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 17:37:33 +0400 Subject: [PATCH 02/16] removed redundunt test case and kept psycopg3 version --- tests/panels/test_sql.py | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index 80f06055e..d66f94ce3 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -229,29 +229,14 @@ def test_tuple_param_conversion(self): For psycopg3 You cannot use IN %s with a tuple https://www.psycopg.org/psycopg3/docs/basic/from_pg2.html#you-cannot-use-in-s-with-a-tuple """ - - is_psycopg3 = True - try: - from psycopg import sql - except ImportError: - is_psycopg3 = False - self.assertEqual(len(self.panel._queries), 0) - if is_psycopg3: - list( - PostgresJSON.objects.raw( - "SELECT * FROM tests_postgresjson WHERE field ->> 'key' = ANY(%s)", - [["a", "b'"]], - ) - ) - else: - list( - PostgresJSON.objects.raw( - "SELECT * FROM tests_postgresjson WHERE field ->> 'key' IN %s", - [("a", "b'")], - ) + list( + PostgresJSON.objects.raw( + "SELECT * FROM tests_postgresjson WHERE field ->> 'key' = ANY(%s)", + [["a", "b'"]], ) + ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) From 793c81e9fc95b6ffb60440435e6dc165485cdf0a Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 18:50:54 +0400 Subject: [PATCH 03/16] updated trans_id to rely on transaction status instead of connection status as it works with pyscorg3 --- debug_toolbar/panels/sql/tracking.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 0b2242ccf..76065e6df 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -155,7 +155,7 @@ def _record(self, method, sql, params): if vendor == "postgresql": # The underlying DB connection (as opposed to Django's wrapper) conn = self.db.connection - initial_conn_status = conn.info.status + initial_conn_status = conn.info.transaction_status start_time = time() try: @@ -214,7 +214,7 @@ def _record(self, method, sql, params): # case where Django can start a transaction before the first query # executes, so in that case logger.current_transaction_id() will # generate a new transaction ID since one does not already exist. - final_conn_status = conn.info.status + final_conn_status = conn.info.transaction_status if final_conn_status == STATUS_IN_TRANSACTION: if initial_conn_status == STATUS_IN_TRANSACTION: trans_id = self.logger.current_transaction_id(alias) @@ -223,6 +223,7 @@ def _record(self, method, sql, params): else: trans_id = None + print(trans_id) params.update( { "trans_id": trans_id, From d8d6b7301b5aba2d0773440a24083a1de6eb57c8 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 18:51:28 +0400 Subject: [PATCH 04/16] updated changes.rst --- docs/changes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changes.rst b/docs/changes.rst index a38df519d..db42bba65 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -14,6 +14,8 @@ Pending memory leaks and sometimes very verbose and hard to silence output in some environments (but not others). The maintainers judged that time and effort is better invested elsewhere. +* Added support for pyscorg3. +* Added pyscorg3 to the CI. 3.8.1 (2022-12-03) ------------------ From c2e291ab432cfce07f5802d265985d0b9db25ba4 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 20:03:46 +0400 Subject: [PATCH 05/16] black --- debug_toolbar/panels/sql/panel.py | 16 +++++++++++----- debug_toolbar/panels/sql/tracking.py | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/debug_toolbar/panels/sql/panel.py b/debug_toolbar/panels/sql/panel.py index 9a617fcca..90e2ba812 100644 --- a/debug_toolbar/panels/sql/panel.py +++ b/debug_toolbar/panels/sql/panel.py @@ -17,23 +17,28 @@ def get_isolation_level_display(vendor, level): if vendor == "postgresql": - try: import psycopg + choices = { # AUTOCOMMIT level does not exists in psycopg3 - psycopg.IsolationLevel.READ_UNCOMMITTED : _("Read uncommitted"), + psycopg.IsolationLevel.READ_UNCOMMITTED: _("Read uncommitted"), psycopg.IsolationLevel.READ_COMMITTED: _("Read committed"), psycopg.IsolationLevel.REPEATABLE_READ: _("Repeatable read"), psycopg.IsolationLevel.SERIALIZABLE: _("Serializable"), } except ImportError: import psycopg2.extensions + choices = { psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT: _("Autocommit"), - psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED: _("Read uncommitted"), + psycopg2.extensions.ISOLATION_LEVEL_READ_UNCOMMITTED: _( + "Read uncommitted" + ), psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED: _("Read committed"), - psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: _("Repeatable read"), + psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ: _( + "Repeatable read" + ), psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE: _("Serializable"), } @@ -44,9 +49,9 @@ def get_isolation_level_display(vendor, level): def get_transaction_status_display(vendor, level): if vendor == "postgresql": - try: import psycopg + choices = { psycopg.pq.TransactionStatus.IDLE: _("Idle"), psycopg.pq.TransactionStatus.ACTIVE: _("Active"), @@ -56,6 +61,7 @@ def get_transaction_status_display(vendor, level): } except ImportError: import psycopg2.extensions + choices = { psycopg2.extensions.TRANSACTION_STATUS_IDLE: _("Idle"), psycopg2.extensions.TRANSACTION_STATUS_ACTIVE: _("Active"), diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 76065e6df..4a4e35271 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -10,6 +10,7 @@ try: import psycopg + PostgresJson = psycopg.types.json.Jsonb STATUS_IN_TRANSACTION = psycopg.pq.TransactionStatus.INTRANS except ImportError: From 15a5bb35aeb94e53382b8d6b3cba01f439fdc6ae Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 21:00:19 +0400 Subject: [PATCH 06/16] Update tox.ini Co-authored-by: Matthias Kestenholz --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 2d0b1ee44..3426c13f4 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,8 @@ envlist = packaging py{38,39,310}-dj{32,41,42}-{sqlite,legacy_postgresql,postgis,mysql} py{310}-dj{40}-{sqlite} - py{310,311}-dj{41,42,main}-{sqlite,legacy_postgresql,postgresql,postgis,mysql} + py{310,311}-dj{41}-{sqlite,legacy_postgresql,postgis,mysql} + py{310,311}-dj{42,main}-{sqlite,legacy_postgresql,postgresql,postgis,mysql} [testenv] deps = From 455de3867c6ec2e90429a923ad34627612712c68 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 21:02:06 +0400 Subject: [PATCH 07/16] removed a print statement --- debug_toolbar/panels/sql/tracking.py | 1 - 1 file changed, 1 deletion(-) diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 4a4e35271..45514c34f 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -224,7 +224,6 @@ def _record(self, method, sql, params): else: trans_id = None - print(trans_id) params.update( { "trans_id": trans_id, From 435b5c440dc42eacda0c6cf59d877658ef9c494e Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Mon, 6 Feb 2023 23:35:32 +0400 Subject: [PATCH 08/16] better envlist matrix --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 3426c13f4..b3309daad 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ isolated_build = true envlist = docs packaging - py{38,39,310}-dj{32,41,42}-{sqlite,legacy_postgresql,postgis,mysql} + py{38,39,310}-dj{32,40,41}-{sqlite,legacy_postgresql,postgis,mysql} py{310}-dj{40}-{sqlite} - py{310,311}-dj{41}-{sqlite,legacy_postgresql,postgis,mysql} + py{310,311}-dj{40,41}-{sqlite,legacy_postgresql,postgis,mysql} py{310,311}-dj{42,main}-{sqlite,legacy_postgresql,postgresql,postgis,mysql} [testenv] From 188035f1ec03192f9acb29d834a0b5c513b98a74 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Tue, 7 Feb 2023 00:39:56 +0400 Subject: [PATCH 09/16] removed tests for django 40 --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index b3309daad..79f62af2b 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ isolated_build = true envlist = docs packaging - py{38,39,310}-dj{32,40,41}-{sqlite,legacy_postgresql,postgis,mysql} + py{38,39,310}-dj{32}-{sqlite,legacy_postgresql,postgis,mysql} py{310}-dj{40}-{sqlite} - py{310,311}-dj{40,41}-{sqlite,legacy_postgresql,postgis,mysql} + py{310,311}-dj{41}-{sqlite,legacy_postgresql,postgis,mysql} py{310,311}-dj{42,main}-{sqlite,legacy_postgresql,postgresql,postgis,mysql} [testenv] From 34733c80b69a796bef4c8f5ba36597201bbbb4a2 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Tue, 7 Feb 2023 15:59:41 +0400 Subject: [PATCH 10/16] attempt to prevent python3.8 from running django42 on github actions --- tox.ini | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 79f62af2b..e94487b3d 100644 --- a/tox.ini +++ b/tox.ini @@ -49,7 +49,13 @@ allowlist_externals = make pip_pre = True commands = python -b -W always -m coverage run -m django test -v2 {posargs:tests} -[testenv:py{38,39,310,311}-dj{32,40,41,42,main}-postgresql] +[testenv:py{38,39,310,311}-dj{32,40,41}-legacy_postgresql] +setenv = + {[testenv]setenv} + DB_BACKEND = postgresql + DB_PORT = {env:DB_PORT:5432} + +[testenv:py{310,311}-dj{42,main}-postgresql] setenv = {[testenv]setenv} DB_BACKEND = postgresql From 42874bb498f6fc44c997a5aab379b270990a9418 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Wed, 8 Feb 2023 13:07:11 +0400 Subject: [PATCH 11/16] fixed use correct json property based on psycopg version --- debug_toolbar/panels/sql/tracking.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 45514c34f..7970828a6 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -132,7 +132,12 @@ def _quote_params(self, params): def _decode(self, param): if PostgresJson and isinstance(param, PostgresJson): - return param.dumps(param.obj) + # psycopg3 + if hasattr(param, 'obj'): + return param.dumps(param.obj) + # psycopg2 + if hasattr(param, 'adapted'): + return param.dumps(param.adapted) # If a sequence type, decode each element separately if isinstance(param, (tuple, list)): From d0ddc22416981719685f2642e373886ae02e0979 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Wed, 8 Feb 2023 13:07:41 +0400 Subject: [PATCH 12/16] keep test_tuple_param_conversion the same for psycopg2 --- tests/panels/test_sql.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index d66f94ce3..72e575ee6 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -229,14 +229,28 @@ def test_tuple_param_conversion(self): For psycopg3 You cannot use IN %s with a tuple https://www.psycopg.org/psycopg3/docs/basic/from_pg2.html#you-cannot-use-in-s-with-a-tuple """ + is_psycopg3 = True + try: + from psycopg import sql + except ImportError: + is_psycopg3 = False + self.assertEqual(len(self.panel._queries), 0) - list( - PostgresJSON.objects.raw( - "SELECT * FROM tests_postgresjson WHERE field ->> 'key' = ANY(%s)", - [["a", "b'"]], + if is_psycopg3: + list( + PostgresJSON.objects.raw( + "SELECT * FROM tests_postgresjson WHERE field ->> 'key' = ANY(%s)", + [["a", "b'"]], + ) + ) + else: + list( + PostgresJSON.objects.raw( + "SELECT * FROM tests_postgresjson WHERE field ->> 'key' IN %s", + [("a", "b'")], + ) ) - ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response) From 747d1e5988dfd0e6cb0de7b35d12a9d04640507a Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Wed, 8 Feb 2023 13:10:06 +0400 Subject: [PATCH 13/16] lint --- debug_toolbar/panels/sql/tracking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_toolbar/panels/sql/tracking.py b/debug_toolbar/panels/sql/tracking.py index 7970828a6..565d9244b 100644 --- a/debug_toolbar/panels/sql/tracking.py +++ b/debug_toolbar/panels/sql/tracking.py @@ -133,10 +133,10 @@ def _quote_params(self, params): def _decode(self, param): if PostgresJson and isinstance(param, PostgresJson): # psycopg3 - if hasattr(param, 'obj'): + if hasattr(param, "obj"): return param.dumps(param.obj) # psycopg2 - if hasattr(param, 'adapted'): + if hasattr(param, "adapted"): return param.dumps(param.adapted) # If a sequence type, decode each element separately From 4b15cfcfafbce3a8506c8bd17d5d34dbbbce2c19 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Wed, 8 Feb 2023 13:17:35 +0400 Subject: [PATCH 14/16] lint --- tests/panels/test_sql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index 72e575ee6..36e42dcbe 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -231,7 +231,7 @@ def test_tuple_param_conversion(self): """ is_psycopg3 = True try: - from psycopg import sql + import psycopg except ImportError: is_psycopg3 = False From 5bdf22ad23150a498cca642d4b605f2de60aa2a2 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Wed, 8 Feb 2023 13:29:59 +0400 Subject: [PATCH 15/16] lint --- tests/panels/test_sql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index 36e42dcbe..2d9e8e52d 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -231,7 +231,7 @@ def test_tuple_param_conversion(self): """ is_psycopg3 = True try: - import psycopg + import psycopg # noqa: F401 except ImportError: is_psycopg3 = False From 8a8366fcec8da0fed231d41eb1f5b3a62fcb6b87 Mon Sep 17 00:00:00 2001 From: Ahmad Nofal Date: Wed, 8 Feb 2023 19:13:44 +0400 Subject: [PATCH 16/16] skip test_tuple_param_conversion if using psycopg3 --- tests/panels/test_sql.py | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tests/panels/test_sql.py b/tests/panels/test_sql.py index 2d9e8e52d..b54c96931 100644 --- a/tests/panels/test_sql.py +++ b/tests/panels/test_sql.py @@ -229,28 +229,21 @@ def test_tuple_param_conversion(self): For psycopg3 You cannot use IN %s with a tuple https://www.psycopg.org/psycopg3/docs/basic/from_pg2.html#you-cannot-use-in-s-with-a-tuple """ - is_psycopg3 = True try: import psycopg # noqa: F401 + + self.skipTest("test not supported for psycopg3") except ImportError: - is_psycopg3 = False + pass self.assertEqual(len(self.panel._queries), 0) - if is_psycopg3: - list( - PostgresJSON.objects.raw( - "SELECT * FROM tests_postgresjson WHERE field ->> 'key' = ANY(%s)", - [["a", "b'"]], - ) - ) - else: - list( - PostgresJSON.objects.raw( - "SELECT * FROM tests_postgresjson WHERE field ->> 'key' IN %s", - [("a", "b'")], - ) + list( + PostgresJSON.objects.raw( + "SELECT * FROM tests_postgresjson WHERE field ->> 'key' IN %s", + [("a", "b'")], ) + ) response = self.panel.process_request(self.request) self.panel.generate_stats(self.request, response)