From 3bcbf585d592afad9fa7cb912724cf43828320c8 Mon Sep 17 00:00:00 2001 From: Erik Darling <2136037+erikdarlingdata@users.noreply.github.com> Date: Fri, 27 Feb 2026 12:48:46 -0500 Subject: [PATCH] Rule 11: Skip PROBE-only bitmap filter predicates PROBE() expressions pushed down from hash joins are bitmap filters, not real residual predicates. Rule 11 now ignores scans where the predicate is exclusively PROBE(). When a real predicate exists alongside PROBE (e.g. [col]=(0) AND PROBE(...)), the rule still fires. Adds IsProbeOnly() helper to both Dashboard and Lite PlanAnalyzers. Co-Authored-By: Claude Opus 4.6 --- Dashboard/Services/PlanAnalyzer.cs | 23 ++++++++++++++++++++++- Lite/Services/PlanAnalyzer.cs | 23 ++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Dashboard/Services/PlanAnalyzer.cs b/Dashboard/Services/PlanAnalyzer.cs index 13f10f3d..2a378005 100644 --- a/Dashboard/Services/PlanAnalyzer.cs +++ b/Dashboard/Services/PlanAnalyzer.cs @@ -341,7 +341,9 @@ private static void AnalyzeNode(PlanNode node, PlanStatement stmt) } // Rule 11: Scan with residual predicate (skip if non-SARGable already flagged) - if (nonSargableReason == null && IsRowstoreScan(node) && !string.IsNullOrEmpty(node.Predicate)) + // A PROBE() alone is just a bitmap filter — not a real residual predicate. + if (nonSargableReason == null && IsRowstoreScan(node) && !string.IsNullOrEmpty(node.Predicate) && + !IsProbeOnly(node.Predicate)) { node.Warnings.Add(new PlanWarning { @@ -538,6 +540,25 @@ private static bool IsRowstoreScan(PlanNode node) !node.PhysicalOp.Contains("Columnstore", StringComparison.OrdinalIgnoreCase); } + /// + /// Returns true when the predicate contains ONLY PROBE() bitmap filter(s) + /// with no real residual predicate. PROBE alone is a bitmap filter pushed + /// down from a hash join — not interesting by itself. If a real predicate + /// exists alongside PROBE (e.g. "[col]=(1) AND PROBE(...)"), returns false. + /// + private static bool IsProbeOnly(string predicate) + { + // Strip all PROBE(...) expressions — PROBE args can contain nested parens + var stripped = Regex.Replace(predicate, @"PROBE\s*\([^()]*(?:\([^()]*\)[^()]*)*\)", "", + RegexOptions.IgnoreCase).Trim(); + + // Remove leftover AND/OR connectors and whitespace + stripped = Regex.Replace(stripped, @"\b(AND|OR)\b", "", RegexOptions.IgnoreCase).Trim(); + + // If nothing meaningful remains, it was PROBE-only + return stripped.Length == 0; + } + /// /// Returns true for any scan operator including columnstore. /// Excludes spools and constant scans. diff --git a/Lite/Services/PlanAnalyzer.cs b/Lite/Services/PlanAnalyzer.cs index efa9b234..ae1984e1 100644 --- a/Lite/Services/PlanAnalyzer.cs +++ b/Lite/Services/PlanAnalyzer.cs @@ -341,7 +341,9 @@ private static void AnalyzeNode(PlanNode node, PlanStatement stmt) } // Rule 11: Scan with residual predicate (skip if non-SARGable already flagged) - if (nonSargableReason == null && IsRowstoreScan(node) && !string.IsNullOrEmpty(node.Predicate)) + // A PROBE() alone is just a bitmap filter — not a real residual predicate. + if (nonSargableReason == null && IsRowstoreScan(node) && !string.IsNullOrEmpty(node.Predicate) && + !IsProbeOnly(node.Predicate)) { node.Warnings.Add(new PlanWarning { @@ -538,6 +540,25 @@ private static bool IsRowstoreScan(PlanNode node) !node.PhysicalOp.Contains("Columnstore", StringComparison.OrdinalIgnoreCase); } + /// + /// Returns true when the predicate contains ONLY PROBE() bitmap filter(s) + /// with no real residual predicate. PROBE alone is a bitmap filter pushed + /// down from a hash join — not interesting by itself. If a real predicate + /// exists alongside PROBE (e.g. "[col]=(1) AND PROBE(...)"), returns false. + /// + private static bool IsProbeOnly(string predicate) + { + // Strip all PROBE(...) expressions — PROBE args can contain nested parens + var stripped = Regex.Replace(predicate, @"PROBE\s*\([^()]*(?:\([^()]*\)[^()]*)*\)", "", + RegexOptions.IgnoreCase).Trim(); + + // Remove leftover AND/OR connectors and whitespace + stripped = Regex.Replace(stripped, @"\b(AND|OR)\b", "", RegexOptions.IgnoreCase).Trim(); + + // If nothing meaningful remains, it was PROBE-only + return stripped.Length == 0; + } + /// /// Returns true for any scan operator including columnstore. /// Excludes spools and constant scans.