Describe the problem
The index-constraint OR-union path is not bounded by optimizer_span_limit.
makeSpansForOr collects contiguous ORs and unions them via
binaryMergeSpansForOr (divide-and-conquer Constraint.UnionWith), with no
spanLimit check and no cancellation check anywhere in the recursion. A large
single-column OR-of-equalities on an indexed column therefore builds and retains
an O(N)-span constraint in ScanPrivate.Constraint (peak transient
O(N log N) from the merges). There is no OR→IN normalization, so such a
predicate never reaches the span-limit guard on the IN path.
These are plain Go heap allocations during planning, not covered by
--max-sql-memory. EXPLAIN alone triggers it.
To Reproduce
On a node limited to ~2GB (e.g. cockroach demo under a 2GB cgroup, or with
GOMEMLIMIT=1100000000 --max-sql-memory=512MiB --cache=512MiB):
python3 - <<'PY' | cockroach demo --no-example-database --insecure --max-sql-memory=512MiB --cache=512MiB
N = 1000000
ors = " OR ".join(f"c = {2*i}" for i in range(1, N+1))
print("CREATE TABLE t (k INT PRIMARY KEY, c INT, INDEX (c));")
print(f"EXPLAIN SELECT * FROM t WHERE {ors};")
PY
Retained growth is O(N) in the number of OR disjuncts (linear in query text).
Observed
N = 100000: completes, ~1.2GB peak RSS.
N = 300000: completes, ~1.9GB peak RSS.
N = 1000000 (~14 MB of SQL): heap grows past 2GB, OOM-killed during
EXPLAIN within a few seconds.
Environment
- CockroachDB
v26.4.0-alpha (master), CCL, cockroach demo single node.
- Client:
cockroach sql.
Code reference
makeSpansForOr / binaryMergeSpansForOr in
pkg/sql/opt/idxconstraint/index_constraints.go; Constraint.UnionWith in
pkg/sql/opt/constraint/constraint.go.
Jira issue: CRDB-67031
Describe the problem
The index-constraint OR-union path is not bounded by
optimizer_span_limit.makeSpansForOrcollects contiguous ORs and unions them viabinaryMergeSpansForOr(divide-and-conquerConstraint.UnionWith), with nospanLimitcheck and no cancellation check anywhere in the recursion. A largesingle-column OR-of-equalities on an indexed column therefore builds and retains
an
O(N)-span constraint inScanPrivate.Constraint(peak transientO(N log N)from the merges). There is no OR→IN normalization, so such apredicate never reaches the span-limit guard on the IN path.
These are plain Go heap allocations during planning, not covered by
--max-sql-memory.EXPLAINalone triggers it.To Reproduce
On a node limited to ~2GB (e.g.
cockroach demounder a 2GB cgroup, or withGOMEMLIMIT=1100000000 --max-sql-memory=512MiB --cache=512MiB):Retained growth is
O(N)in the number of OR disjuncts (linear in query text).Observed
N = 100000: completes, ~1.2GB peak RSS.N = 300000: completes, ~1.9GB peak RSS.N = 1000000(~14 MB of SQL): heap grows past 2GB, OOM-killed duringEXPLAINwithin a few seconds.Environment
v26.4.0-alpha(master), CCL,cockroach demosingle node.cockroach sql.Code reference
makeSpansForOr/binaryMergeSpansForOrinpkg/sql/opt/idxconstraint/index_constraints.go;Constraint.UnionWithinpkg/sql/opt/constraint/constraint.go.Jira issue: CRDB-67031