feat: Enhance IN/NOT IN operator handling for empty lists

Added logic to return appropriate criteria for empty lists in IN and NOT IN operators. An empty list with IN now returns 0 results (1=0), while NOT IN returns all results (1=1). Updated tests to verify this behavior.
This commit is contained in:
Sumit Jain 2026-02-03 12:31:29 +05:30
parent 9b84264fa5
commit c6868b11c6
3 changed files with 34 additions and 1 deletions

View file

@ -579,6 +579,20 @@ class Engine:
v.strip().strip("'") for v in get_between_date_filter(_value, df).split(" AND ")
)
# Handle empty lists for IN/NOT IN operators before conversion
# IN with empty list should return 0 results (always False)
# NOT IN with empty list should return all results (always True)
if _operator.lower() in ("in", "not in"):
if isinstance(_value, (list, tuple, set)) and len(_value) == 0:
if _operator.lower() == "in":
# Return a criterion that always evaluates to False (1=0)
# This ensures IN with empty list returns 0 results
return RawCriterion("1=0")
else: # not in
# Return a criterion that always evaluates to True (1=1)
# NOT IN with empty set matches all rows since nothing is excluded
return RawCriterion("1=1")
if not _value and isinstance(_value, list | tuple | set):
_value = ("",)

View file

@ -868,6 +868,15 @@ from {tables}
if f.operator.lower() == "in":
can_be_null &= not f.value or any(v is None or v == "" for v in f.value)
# Handle empty lists for IN/NOT IN operators before processing
# IN with empty list should return 0 results (always False: 1=0)
# NOT IN with empty list should return all results (always True: 1=1)
if isinstance(f.value, (list, tuple)) and len(f.value) == 0:
if f.operator.lower() == "in":
return "1=0"
else: # not in
return "1=1"
if value is None:
values = f.value or ""
if isinstance(values, str):

View file

@ -410,12 +410,22 @@ class TestQuery(IntegrationTestCase):
"SELECT `name` FROM `tabDocType` WHERE `name` IN ('ToDo','Note')",
)
# Empty list with IN operator should return 0 results (1=0 condition)
self.assertQueryEqual(
frappe.qb.get_query(
"DocType",
filters={"name": ("in", [])},
).get_sql(),
"SELECT `name` FROM `tabDocType` WHERE `name` IN ('')",
"SELECT `name` FROM `tabDocType` WHERE 1=0",
)
# Empty list with NOT IN operator should return all results (1=1 condition)
self.assertQueryEqual(
frappe.qb.get_query(
"DocType",
filters={"name": ("not in", [])},
).get_sql(),
"SELECT `name` FROM `tabDocType` WHERE 1=1",
)
self.assertQueryEqual(