Skip to content

Commit

Permalink
Merge #137585
Browse files Browse the repository at this point in the history
137585: explain: collapse large ExprContainers in verbose mode r=yuzefovich a=yuzefovich

Previously, in `VERBOSE` mode (that is used for the stmt bundles) we would include all rows fully that are included in the ExprContainer (an example being a VALUES clause), with each column for each row taking up a separate line. In some cases this could lead to tens of thousands of lines in the output to be taken by a single operator. This seems a bit annoying, so this commit introduces a hard cap of 30 lines for each container. It uses the following heuristics:
- the first and the last row are always shown, regardless of the width
- if we have at least three rows and the full container would take up more than 30 lines, we will start omitting lines from the middle. All omitted lines will be replaced by a single one with `...` text.

This should make reading `plan.txt` files easier overall.

Inspired by bundle from [this](https://github.com/cockroachlabs/support/issues/3146) support tickeet.

Epic: None
Release note: None

Co-authored-by: Yahor Yuzefovich <[email protected]>
  • Loading branch information
craig[bot] and yuzefovich committed Jan 4, 2025
2 parents d6ffb8c + 3ed4965 commit 6e405b0
Show file tree
Hide file tree
Showing 2 changed files with 164 additions and 4 deletions.
128 changes: 128 additions & 0 deletions pkg/sql/opt/exec/execbuilder/testdata/values
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,131 @@ vectorized: true
row 1, expr 1: 4
row 2, expr 0: 5
row 2, expr 1: 6

# Test for collapsing the tuples to be under 30 lines.

# 30 rows, so at the limit and all rows are shown.
query I
SELECT count(*) FROM
[EXPLAIN (VERBOSE) VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30)]
WHERE info LIKE '%row%, expr%'
----
30

# 35 rows, so over the limit and some middle rows are omitted.
query I
SELECT count(*) FROM
[EXPLAIN (VERBOSE) VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35)]
WHERE info LIKE '%row%, expr%'
----
30

query T
EXPLAIN (VERBOSE) VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32), (33), (34), (35)
----
distribution: local
vectorized: true
·
• values
columns: (column1)
size: 1 column, 35 rows
row 0, expr 0: 1
row 1, expr 0: 2
row 2, expr 0: 3
row 3, expr 0: 4
row 4, expr 0: 5
row 5, expr 0: 6
row 6, expr 0: 7
row 7, expr 0: 8
row 8, expr 0: 9
row 9, expr 0: 10
row 10, expr 0: 11
row 11, expr 0: 12
row 12, expr 0: 13
row 13, expr 0: 14
row 14, expr 0: 15
...
row 20, expr 0: 21
row 21, expr 0: 22
row 22, expr 0: 23
row 23, expr 0: 24
row 24, expr 0: 25
row 25, expr 0: 26
row 26, expr 0: 27
row 27, expr 0: 28
row 28, expr 0: 29
row 29, expr 0: 30
row 30, expr 0: 31
row 31, expr 0: 32
row 32, expr 0: 33
row 33, expr 0: 34
row 34, expr 0: 35

# First and last rows are always included.
query I
SELECT count(*) FROM
[EXPLAIN (VERBOSE) VALUES ((1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16), (17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31))]
WHERE info LIKE '%row%, expr%'
----
31

query I
SELECT count(*) FROM
[EXPLAIN (VERBOSE) VALUES ((1), (2), (3), (4), (5), (6), (7), (8), (9), (10), (11), (12), (13), (14), (15), (16)),
((17), (18), (19), (20), (21), (22), (23), (24), (25), (26), (27), (28), (29), (30), (31), (32))]
WHERE info LIKE '%row%, expr%'
----
32

# 4 rows with 9 columns, so 36 lines total - over the limit and the third row is
# skipped.
query I
SELECT count(*) FROM
[EXPLAIN (VERBOSE) VALUES ((1), (2), (3), (4), (5), (6), (7), (8), (9)),
((10), (11), (12), (13), (14), (15), (16), (17), (18)),
((19), (20), (21), (22), (23), (24), (25), (26), (27)),
((28), (29), (30), (31), (32), (33), (34), (35), (36))]
WHERE info LIKE '%row%, expr%'
----
27

query T
EXPLAIN (VERBOSE) VALUES ((1), (2), (3), (4), (5), (6), (7), (8), (9)),
((10), (11), (12), (13), (14), (15), (16), (17), (18)),
((19), (20), (21), (22), (23), (24), (25), (26), (27)),
((28), (29), (30), (31), (32), (33), (34), (35), (36))
----
distribution: local
vectorized: true
·
• values
columns: (column1, column2, column3, column4, column5, column6, column7, column8, column9)
size: 9 columns, 4 rows
row 0, expr 0: 1
row 0, expr 1: 2
row 0, expr 2: 3
row 0, expr 3: 4
row 0, expr 4: 5
row 0, expr 5: 6
row 0, expr 6: 7
row 0, expr 7: 8
row 0, expr 8: 9
row 1, expr 0: 10
row 1, expr 1: 11
row 1, expr 2: 12
row 1, expr 3: 13
row 1, expr 4: 14
row 1, expr 5: 15
row 1, expr 6: 16
row 1, expr 7: 17
row 1, expr 8: 18
...
row 3, expr 0: 28
row 3, expr 1: 29
row 3, expr 2: 30
row 3, expr 3: 31
row 3, expr 4: 32
row 3, expr 5: 33
row 3, expr 6: 34
row 3, expr 7: 35
row 3, expr 8: 36
40 changes: 36 additions & 4 deletions pkg/sql/opt/exec/explain/emit.go
Original file line number Diff line number Diff line change
Expand Up @@ -1262,15 +1262,47 @@ func (e *emitter) emitTuples(rows tree.ExprContainer, numColumns int) {
rows.NumRows(), util.Pluralize(int64(rows.NumRows())),
)
if e.ob.flags.Verbose {
for i := 0; i < rows.NumRows(); i++ {
for j := 0; j < rows.NumCols(); j++ {
expr := rows.Get(i, j).(tree.TypedExpr)
e.ob.Expr(fmt.Sprintf("row %d, expr %d", i, j), expr, nil /* varColumns */)
const maxLines = 30
if rows.NumRows()*rows.NumCols() <= maxLines || rows.NumRows() <= 2 {
// Emit all rows fully when we'll use a handful of lines, or we have
// at most two rows.
e.emitTuplesRange(rows, 0 /* rowStartIdx */, rows.NumRows())
} else {
// We have at least three rows and need to collapse the output.
//
// Always emit the first and the last rows.
headEndIdx, tailStartIdx := 1, rows.NumRows()-1
// Split the remaining "line budget" evenly, favoring the "head" a
// bit, without exceeding the limit.
availableLines := (maxLines - 2*rows.NumCols()) / rows.NumCols()
extraHeadLength, extraTailLength := availableLines-availableLines/2, availableLines/2
headEndIdx += extraHeadLength
tailStartIdx -= extraTailLength
if headEndIdx >= tailStartIdx {
// This should never happen, but just to be safe we'll handle
// the case when "head" and "tail" combine, and we end up
// emitting all rows.
e.emitTuplesRange(rows, 0 /* rowStartIdx */, rows.NumRows())
} else {
e.emitTuplesRange(rows, 0 /* rowStartIdx */, headEndIdx)
e.ob.AddField("...", "")
e.emitTuplesRange(rows, tailStartIdx, rows.NumRows())
}
}
}
}

// emitTuplesRange emits all tuples in the [rowStartIdx, rowEndIdx) range from
// the given container.
func (e *emitter) emitTuplesRange(rows tree.ExprContainer, rowStartIdx, rowEndIdx int) {
for i := rowStartIdx; i < rowEndIdx; i++ {
for j := 0; j < rows.NumCols(); j++ {
expr := rows.Get(i, j).(tree.TypedExpr)
e.ob.Expr(fmt.Sprintf("row %d, expr %d", i, j), expr, nil /* varColumns */)
}
}
}

func (e *emitter) emitGroupByAttributes(
inputCols colinfo.ResultColumns,
aggs []exec.AggInfo,
Expand Down

0 comments on commit 6e405b0

Please sign in to comment.