Skip to content

Commit

Permalink
colexec: don't infinite loop cross-join with zero-column left input
Browse files Browse the repository at this point in the history
Previously, the cross-joiner wouldn't advance its internal state when
its left input projected no columns. This would result in an infinite
loop as the right rows were repeatedly emitted. This patch advances
the state when there are no left columns as if values from the left
side were emitted.

Fixes cockroachdb#105882

Release note (bug fix): Fixed a bug introduced in v22.1 that could cause
a join to infinite-loop in rare cases when (1) the join filter is not an
equality and (2) no columns from the left input are returned.
  • Loading branch information
DrewKimball authored and yuzefovich committed Aug 15, 2023
1 parent 3d6afb8 commit 01f26f7
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
50 changes: 50 additions & 0 deletions pkg/sql/colexec/colexecjoin/crossjoiner.eg.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions pkg/sql/colexec/colexecjoin/crossjoiner_tmpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,32 @@ func buildFromLeftBatch(b *crossJoinerBase, currentBatch coldata.Batch, sel []in
colexecerror.InternalError(errors.AssertionFailedf("unhandled type %s", b.left.types[colIdx].String()))
}
}
// If there are no columns projected from the left input, simply advance the
// cross-joiner state according to the number of input rows.
if len(b.left.types) == 0 {
outStartIdx := destStartIdx
for bs.curSrcStartIdx < leftSrcEndIdx && outStartIdx < outputCapacity {
// Repeat each row leftNumRepeats times.
// {{/* toAppend will always be positive. */}}
toAppend := leftNumRepeats - bs.numRepeatsIdx
if outStartIdx+toAppend > outputCapacity {
// We don't have enough space to repeat the current
// value the required number of times, so we'll have
// to continue from here on the next call.
toAppend = outputCapacity - outStartIdx
bs.numRepeatsIdx += toAppend
} else {
// We fully processed the current tuple for the
// current column, and before moving on to the next
// one, we need to reset numRepeatsIdx (so that the
// next tuple would be repeated leftNumRepeats
// times).
bs.curSrcStartIdx++
bs.numRepeatsIdx = 0
}
outStartIdx += toAppend
}
}
}

// buildFromLeftInput builds part of the output of a cross join that comes from
Expand Down
15 changes: 15 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/cross_join
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,18 @@ SELECT *
)
WHERE r < .01
LIMIT 1

# Regression test for #105882 - don't infinite loop when the left input has
# no columns.
statement ok
CREATE TABLE t105882 (c0 INT);
UPSERT INTO t105882 (c0) VALUES(1);

query I
SELECT (
SELECT count(t2.rowid) FROM t105882 t2
WHERE ((t1.rowid) IN (SELECT max(t3.rowid) FROM t105882 t3))
)
FROM t105882 t1;
----
1

0 comments on commit 01f26f7

Please sign in to comment.