From e9d1dcb2bffb281f63ef28a52005b28878f628d1 Mon Sep 17 00:00:00 2001 From: tobymao Date: Wed, 27 May 2026 22:57:17 -0700 Subject: [PATCH] perf(optimizer): skip O(n^2) connector simplification when no operands can combine _flat_simplify scans operand pairs in O(n^2). For connectors, a pair can only combine when one side is a constant or both are comparisons (see _simplify_connectors / _simplify_comparison), so when no operand is combinable the scan is a guaranteed no-op. Returning early in that case avoids the quadratic blowup on large connectors of inert operands (e.g. a 1000-way OR of ANDs). The check is placed inside _flat_simplify so it reuses the already-flattened operand queue (no extra traversal) and is gated on exp.Connector, leaving the simplify_equality caller unaffected. Output is unchanged. Benchmarks (optimize, best of 5): condition_100: 67.8ms -> 54ms condition_500: 648.7ms -> 297ms (2.2x) condition_1000: 2124ms -> 609ms (3.5x) TPC-H is neutral (verified via interleaved A/B). --- sqlglot/optimizer/simplify.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sqlglot/optimizer/simplify.py b/sqlglot/optimizer/simplify.py index 57929ca0a9..b83af2a48c 100644 --- a/sqlglot/optimizer/simplify.py +++ b/sqlglot/optimizer/simplify.py @@ -526,6 +526,10 @@ def __init__(self, dialect: DialectType = None, annotate_new_expressions: bool = exp.Is, ) + # Operand types that allow a connector pair to combine in _flat_simplify: constants + # (matched by the is_false/is_null/is_zero/always_true checks) and comparisons. + CONNECTOR_COMBINABLE: t.ClassVar = (exp.Boolean, exp.Literal, exp.Null, *COMPARISONS) + INVERSE_COMPARISONS: t.ClassVar[dict[type[exp.Expr], type[exp.Expr]]] = { exp.LT: exp.GT, exp.GT: exp.LT, @@ -1467,6 +1471,16 @@ def _flat_simplify( queue = deque(expression.flatten(unnest=False)) size = len(queue) + # The pairwise scan below is O(n^2). For connectors, a pair only combines if one side + # is a constant or both are comparisons (see _simplify_connectors / _simplify_comparison); + # if no operand is combinable the scan is a guaranteed no-op, so return early. This + # avoids the quadratic blowup on large connectors of inert operands (e.g. a 1000-way OR + # of ANDs). Non-connector callers (simplify_equality) are unaffected by the type guard. + if isinstance(expression, exp.Connector) and not any( + isinstance(op, self.CONNECTOR_COMBINABLE) for op in queue + ): + return expression + while queue: a = queue.popleft()