From 269471729cbf1902e87afcfa736743b294c49fb3 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Fri, 5 Dec 2025 17:40:35 +0530 Subject: [PATCH] feat(qb_query)!: return query object if requested Signed-off-by: Akhil Narang --- .../server_script/test_server_script.py | 2 +- frappe/model/qb_query.py | 3 +- frappe/tests/test_db_query.py | 40 ++++++++++--------- frappe/tests/test_nestedset.py | 8 ++-- frappe/tests/test_perf.py | 2 +- 5 files changed, 29 insertions(+), 26 deletions(-) diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index 7360ca94d2..c6ed3ca17d 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -157,7 +157,7 @@ class TestServerScript(IntegrationTestCase): self.assertEqual(frappe.get_doc("Server Script", "test_return_value").execute_method(), "hello") def test_permission_query(self): - sql = frappe.db.get_list("ToDo", run=False) + sql = frappe.db.get_list("ToDo", run=False).get_sql() self.assertTrue("where (1 = 1)" in sql.lower()) self.assertTrue(isinstance(frappe.db.get_list("ToDo"), list)) diff --git a/frappe/model/qb_query.py b/frappe/model/qb_query.py index 526529cfa6..d4fb1d9f06 100644 --- a/frappe/model/qb_query.py +++ b/frappe/model/qb_query.py @@ -200,8 +200,7 @@ class DatabaseQuery: query = frappe.qb.get_query(**kwargs) if not run: - # Return the SQL query string instead of executing - return str(query.get_sql()) + return query # Run the query if pluck: diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index d6d444eb34..e52fd6ee5b 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -1047,28 +1047,32 @@ class TestDBQuery(IntegrationTestCase): self.assertIn("count", result[0]) def test_coalesce_with_in_ops(self): - self.assertNotIn("IF", frappe.get_all("User", {"first_name": ("in", ["a", "b"])}, run=0)) - self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("in", ["a", None])}, run=0)) - self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("in", ["a", ""])}, run=0)) - self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("in", [])}, run=0)) - self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("not in", ["a"])}, run=0)) - self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("not in", [])}, run=0)) - self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("not in", [""])}, run=0)) + self.assertNotIn("IF", frappe.get_all("User", {"first_name": ("in", ["a", "b"])}, run=0).get_sql()) + self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("in", ["a", None])}, run=0).get_sql()) + self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("in", ["a", ""])}, run=0).get_sql()) + self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("in", [])}, run=0).get_sql()) + self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("not in", ["a"])}, run=0).get_sql()) + self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("not in", [])}, run=0).get_sql()) + self.assertIn("IFNULL", frappe.get_all("User", {"first_name": ("not in", [""])}, run=0).get_sql()) # primary key is never nullable - self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", ["a", None])}, run=0)) - self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", ["a", ""])}, run=0)) - self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", (""))}, run=0)) - self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", ())}, run=0)) + self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", ["a", None])}, run=0).get_sql()) + self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", ["a", ""])}, run=0).get_sql()) + self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", (""))}, run=0).get_sql()) + self.assertNotIn("IFNULL", frappe.get_all("User", {"name": ("in", ())}, run=0).get_sql()) def test_coalesce_with_datetime_ops(self): - self.assertNotIn("IFNULL", frappe.get_all("User", {"last_active": (">", "2022-01-01")}, run=0)) - self.assertNotIn("IFNULL", frappe.get_all("User", {"creation": ("<", "2022-01-01")}, run=0)) + self.assertNotIn( + "IFNULL", frappe.get_all("User", {"last_active": (">", "2022-01-01")}, run=0).get_sql() + ) + self.assertNotIn("IFNULL", frappe.get_all("User", {"creation": ("<", "2022-01-01")}, run=0).get_sql()) self.assertNotIn( "IFNULL", - frappe.get_all("User", {"last_active": ("between", ("2022-01-01", "2023-01-01"))}, run=0), + frappe.get_all( + "User", {"last_active": ("between", ("2022-01-01", "2023-01-01"))}, run=0 + ).get_sql(), ) - self.assertIn("IFNULL", frappe.get_all("User", {"last_active": ("<", "2022-01-01")}, run=0)) + self.assertIn("IFNULL", frappe.get_all("User", {"last_active": ("<", "2022-01-01")}, run=0).get_sql()) def test_ambiguous_linked_tables(self): from frappe.desk.reportview import get @@ -1145,16 +1149,16 @@ class TestDBQuery(IntegrationTestCase): self.assertEqual(count[1], frappe.db.count("Language")) def test_ifnull_none(self): - query = frappe.get_all("DocField", {"fieldname": None}, run=0) + query = frappe.get_all("DocField", {"fieldname": None}, run=0).get_sql() self.assertIn("IS NULL", query) self.assertNotIn("\\'", query) self.assertNotIn("ifnull", query) self.assertFalse(frappe.get_all("DocField", {"name": None})) self.assertFalse(frappe.get_all("DocField", {"parent": None})) - self.assertNotIn("0", frappe.get_all("DocField", {"parent": None}, run=0)) + self.assertNotIn("0", frappe.get_all("DocField", {"parent": None}, run=0).get_sql()) def test_ifnull_fallback_types(self): - query = frappe.get_all("DocField", {"fieldname": ("!=", None)}, run=0) + query = frappe.get_all("DocField", {"fieldname": ("!=", None)}, run=0).get_sql() # Fallbacks should always be of correct type self.assertIn("''", query) self.assertNotIn("0", query) diff --git a/frappe/tests/test_nestedset.py b/frappe/tests/test_nestedset.py index cb2a3c42cc..c8916c4f14 100644 --- a/frappe/tests/test_nestedset.py +++ b/frappe/tests/test_nestedset.py @@ -285,10 +285,10 @@ class TestNestedSet(IntegrationTestCase): inclusive_link = {"link_field": ("descendants of (inclusive)", record)} # db_query - self.assertNotIn(record, frappe.get_all(TEST_DOCTYPE, exclusive_filter, run=0)) - self.assertIn(record, frappe.get_all(TEST_DOCTYPE, inclusive_filter, run=0)) - self.assertNotIn(record, frappe.get_all(linked_doctype, exclusive_link, run=0)) - self.assertIn(record, frappe.get_all(linked_doctype, inclusive_link, run=0)) + self.assertNotIn(record, frappe.get_all(TEST_DOCTYPE, exclusive_filter, run=0).get_sql()) + self.assertIn(record, frappe.get_all(TEST_DOCTYPE, inclusive_filter, run=0).get_sql()) + self.assertNotIn(record, frappe.get_all(linked_doctype, exclusive_link, run=0).get_sql()) + self.assertIn(record, frappe.get_all(linked_doctype, inclusive_link, run=0).get_sql()) # QB self.assertNotIn(record, str(frappe.qb.get_query(TEST_DOCTYPE, filters=exclusive_filter))) diff --git a/frappe/tests/test_perf.py b/frappe/tests/test_perf.py index a5575344a1..bb300dfeaf 100644 --- a/frappe/tests/test_perf.py +++ b/frappe/tests/test_perf.py @@ -179,7 +179,7 @@ class TestPerformance(IntegrationTestCase): frappe.get_list("User") def test_no_ifnull_checks(self): - query = frappe.get_all("DocType", {"autoname": ("is", "set")}, run=0).lower() + query = frappe.get_all("DocType", {"autoname": ("is", "set")}, run=0).get_sql().lower() self.assertNotIn("coalesce", query) self.assertNotIn("ifnull", query)