Skip to content

Commit fa198ef

Browse files
committed
explain: harden gist decoding logic for invalid gists
Our randomized test just encountered a couple of cases when an invalid gist resulted in an internal error when being decoded. The problem is that in such case we can have different fields of `Node`s left unset, leading to nil pointers or index-out-of-bounds. This commit hardens the code against such scenarios. We did something similar in a561201. Release note: None
1 parent 6bfc8db commit fa198ef

File tree

4 files changed

+33
-0
lines changed

4 files changed

+33
-0
lines changed

pkg/sql/opt/exec/execbuilder/testdata/explain_gist

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,3 +288,21 @@ SELECT crdb_internal.decode_plan_gist('AgHk+v//3xoEAKAFAgAABQQGBA==');
288288
• virtual table
289289
table: @?
290290
spans: 1+ spans
291+
292+
# Regression tests for #154300. Gracefully handle invalid gists.
293+
query T nosort
294+
SELECT crdb_internal.decode_external_plan_gist('AgYd':::STRING);
295+
----
296+
297+
query T nosort
298+
SELECT crdb_internal.decode_external_plan_gist('Agwc':::STRING);
299+
----
300+
• root
301+
302+
├── • explain
303+
304+
└── • subquery
305+
│ id: @S1
306+
│ exec mode: exists
307+
308+
└── • group (scalar)

pkg/sql/opt/exec/explain/emit.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,17 @@ func emitInternal(
7777
e := makeEmitter(ob, spanFormatFn)
7878
var walk func(n *Node) error
7979
walk = func(n *Node) error {
80+
if n == nil {
81+
return nil
82+
}
8083
// In non-verbose mode, we skip all projections.
8184
// In verbose mode, we only skip trivial projections (which just rearrange
8285
// or rename the columns).
8386
if !ob.flags.Verbose {
8487
if n.op == serializingProjectOp || n.op == simpleProjectOp {
88+
if len(n.children) == 0 {
89+
return nil
90+
}
8591
return walk(n.children[0])
8692
}
8793
}
@@ -233,6 +239,9 @@ func omitTrivialProjections(n *Node) (*Node, colinfo.ResultColumns, colinfo.Colu
233239
return n, n.Columns(), n.Ordering()
234240
}
235241

242+
if len(n.children) == 0 {
243+
return n, n.Columns(), n.Ordering()
244+
}
236245
input, inputColumns, inputOrdering := omitTrivialProjections(n.children[0])
237246

238247
// Check if the projection is a bijection (i.e. permutation of all input

pkg/sql/opt/exec/explain/explain_factory.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ func (n *Node) Child(idx int) *Node {
6464

6565
// Columns returns the ResultColumns for this node.
6666
func (n *Node) Columns() colinfo.ResultColumns {
67+
if n == nil {
68+
return nil
69+
}
6770
return n.columns
6871
}
6972

pkg/sql/opt/exec/explain/result_columns.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ func getResultColumns(
167167
return tableColumns(a.Table, a.OutCols), nil
168168

169169
case vectorMutationSearchOp:
170+
if len(inputs) == 0 {
171+
return nil, nil
172+
}
170173
a := args.(*vectorMutationSearchArgs)
171174
cols := appendColumns(inputs[0], colinfo.ResultColumn{Name: "partition-key", Typ: types.Int})
172175
if a.IsIndexPut {

0 commit comments

Comments
 (0)