cockroachdb / cockroachdb/cockroach

sql/opt: unbounded constant folding of string || during planning can OOM

Open
#173,720 0 comments 0 reactions 0 assignees View on GitHub
A-sql-optimizer branch-master C-bug O-agent T-sql-queries
Dominant language
Go
Stars
32.5k
Forks
4.1k
PR merge metrics
PR metrics pending

Description

**Describe the problem**

Constant folding of string `||` (concat) during normalization has no size cap.
`EvalConcatOp` / `EvalConcatStringOp` perform a bare concat, and the folded
datum is interned unconditionally with no limit. While each `repeat()` operand
is capped at 128 MiB, the `||` that combines them is not, so chaining a handful
of compact `repeat()` calls folds — at planning time — to an arbitrarily large
interned string (plus left-fold intermediates) on the plain Go heap, which is
not covered by `--max-sql-memory`.

Because `repeat('x', 100000000)` expresses a 100 MB operand in ~26 bytes of SQL,
the SQL text needed to OOM is tiny. `EXPLAIN` alone triggers it (no execution);
wrapping in `length(...)` keeps the giant value out of the result set but the
folder still materializes 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`), run this
**171-byte** statement:

```sql
EXPLAIN SELECT length(
repeat('x',100000000) || repeat('x',100000000) || repeat('x',100000000) ||
repeat('x',100000000) || repeat('x',100000000) || repeat('x',100000000));
```

The folded constant is `O(N × 128 MiB)` for `N` operands while the SQL is
`O(N × ~26 bytes)`.

**Observed**

- 3 operands: completes, ~1.4GB peak RSS.
- 6 operands (171 bytes 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**

`EvalConcatOp` / `EvalConcatStringOp` in `pkg/sql/sem/eval/binary_op.go`;
folding gated by `canFoldOperator` and interned by `ConstructConstVal` in
`pkg/sql/opt/norm/fold_constants_funcs.go`.

Jira issue: CRDB-67027

Contributor guide

Open the contributing guide

Research direction

Reproduce the six-operand EXPLAIN case, then inspect EvalConcatOp and EvalConcatStringOp in pkg/sql/sem/eval/binary_op.go. Follow canFoldOperator and ConstructConstVal in pkg/sql/opt/norm/fold_constants_funcs.go to understand how concatenated constants are folded and interned. Done means the reproducer no longer permits unbounded planner-time string growth or OOM while constant folding remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, sql
Domain
databases, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.