perf: Avoid coalesce for between filters (#26531)
- Avoid on `between` + date - Avoid on timestamp fields - Avoid on `>` and `>=` comparisons
This commit is contained in:
parent
4fbeb4617a
commit
005e74b20d
2 changed files with 30 additions and 2 deletions
|
|
@ -728,7 +728,7 @@ class DatabaseQuery:
|
|||
df = df[0] if df else None
|
||||
|
||||
# primary key is never nullable, modified is usually indexed by default and always present
|
||||
can_be_null = f.fieldname not in ("name", "modified")
|
||||
can_be_null = f.fieldname not in ("name", "modified", "creation")
|
||||
|
||||
value = None
|
||||
|
||||
|
|
@ -810,12 +810,20 @@ class DatabaseQuery:
|
|||
|
||||
if f.operator.lower() in ("previous", "next", "timespan"):
|
||||
date_range = get_date_range(f.operator.lower(), f.value)
|
||||
f.operator = "Between"
|
||||
f.operator = "between"
|
||||
f.value = date_range
|
||||
fallback = f"'{FallBackDateTimeStr}'"
|
||||
|
||||
if f.operator.lower() in (">", ">=") and (
|
||||
f.fieldname in ("creation", "modified")
|
||||
or (df and (df.fieldtype == "Date" or df.fieldtype == "Datetime"))
|
||||
):
|
||||
# Null values can never be greater than any non-null value
|
||||
can_be_null = False
|
||||
|
||||
if f.operator in (">", "<", ">=", "<=") and (f.fieldname in ("creation", "modified")):
|
||||
value = cstr(f.value)
|
||||
can_be_null = False
|
||||
fallback = f"'{FallBackDateTimeStr}'"
|
||||
|
||||
elif f.operator.lower() in ("between") and (
|
||||
|
|
@ -823,6 +831,17 @@ class DatabaseQuery:
|
|||
or (df and (df.fieldtype == "Date" or df.fieldtype == "Datetime"))
|
||||
):
|
||||
escape = False
|
||||
# Between operator never needs to check for null
|
||||
# Explanation: Consider SQL -> `COLUMN between X and Y`
|
||||
# Actual computation:
|
||||
# for row in rows:
|
||||
# if Y > row.COLUMN > X:
|
||||
# yield row
|
||||
|
||||
# Since Y and X can't be null, null value in column will never match filter, so
|
||||
# coalesce is extra cost that prevents index usage
|
||||
can_be_null = False
|
||||
|
||||
value = get_between_date_filter(f.value, df)
|
||||
fallback = f"'{FallBackDateTimeStr}'"
|
||||
|
||||
|
|
|
|||
|
|
@ -1109,6 +1109,15 @@ class TestDBQuery(FrappeTestCase):
|
|||
self.assertNotIn("ifnull", frappe.get_all("User", {"name": ("in", (""))}, run=0))
|
||||
self.assertNotIn("ifnull", frappe.get_all("User", {"name": ("in", ())}, run=0))
|
||||
|
||||
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": ("between", ("2022-01-01", "2023-01-01"))}, run=0),
|
||||
)
|
||||
self.assertIn("ifnull", frappe.get_all("User", {"last_active": ("<", "2022-01-01")}, run=0))
|
||||
|
||||
def test_ambiguous_linked_tables(self):
|
||||
from frappe.desk.reportview import get
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue