diff --git a/frappe/tests/test_query_builder.py b/frappe/tests/test_query_builder.py index 9375d17ef2..a8befd0541 100644 --- a/frappe/tests/test_query_builder.py +++ b/frappe/tests/test_query_builder.py @@ -4,6 +4,7 @@ from datetime import time import frappe from frappe.core.doctype.doctype.test_doctype import new_doctype +from frappe.database.operator_map import func_in from frappe.query_builder import Case from frappe.query_builder.builder import Function from frappe.query_builder.custom import ConstantColumn @@ -503,3 +504,70 @@ class TestMisc(IntegrationTestCase): roles = frappe.qb.from_(role).select(role.name) self.assertEqual(set(users.run() + roles.run()), set((users + roles).run())) + + +class TestOperatorIn(IntegrationTestCase): + def test_func_in_without_empty_values(self): + note = frappe.qb.DocType("Note") + query = func_in(note.name, ["n1", "n2", "n3"]) + sql_str = str(query).lower() + + self.assertIn("in", sql_str) + self.assertNotIn("coalesce", sql_str) + + def test_func_in_with_none_converts_to_empty_string(self): + note = frappe.qb.DocType("Note") + query = func_in(note.name, [None, "user1"]) + sql_str = str(query).lower() + + self.assertIn("coalesce", sql_str) + self.assertIn("''", sql_str) + + def test_func_in_with_empty_string_uses_coalesce(self): + note = frappe.qb.DocType("Note") + query = func_in(note.name, ["", "user1"]) + sql_str = str(query).lower() + + self.assertIn("coalesce", sql_str) + self.assertIn("''", sql_str) + + def test_func_in_with_mixed_none_and_values(self): + note = frappe.qb.DocType("Note") + query = func_in(note.name, ["val1", None, "val2"]) + sql_str = str(query).lower() + + self.assertIn("coalesce", sql_str) + + def test_in_filter_matches_null_and_empty_columns(self): + test_doctype = new_doctype( + fields=[ + { + "fieldname": "test_field", + "fieldtype": "Data", + "label": "Test Field", + }, + ], + ) + test_doctype.insert() + self.test_doctype_name = test_doctype.name + self.addCleanup(frappe.delete_doc, "DocType", self.test_doctype_name) + + frappe.db.sql(f"DELETE FROM `tab{self.test_doctype_name}`") + frappe.db.commit() + + doc_null = frappe.get_doc({"doctype": self.test_doctype_name, "test_field": None}) + doc_null.insert() + doc_empty = frappe.get_doc({"doctype": self.test_doctype_name, "test_field": ""}) + doc_empty.insert() + doc_user = frappe.get_doc({"doctype": self.test_doctype_name, "test_field": "user1"}) + doc_user.insert() + + results = frappe.get_all( + self.test_doctype_name, + filters={"test_field": ["in", [None, "user1"]]}, + pluck="test_field", + ) + + self.assertIn(None, results) + self.assertIn("", results) + self.assertIn("user1", results)