diff --git a/pkg/sql/logictest/testdata/logic_test/sql_keys b/pkg/sql/logictest/testdata/logic_test/sql_keys index 0bed4efcf91f..f62399586de3 100644 --- a/pkg/sql/logictest/testdata/logic_test/sql_keys +++ b/pkg/sql/logictest/testdata/logic_test/sql_keys @@ -66,6 +66,23 @@ SELECT crdb_internal.pretty_key(key, 0) FROM crdb_internal.scan(crdb_internal.ta /10/Table/106/1/1/0 /10/Table/106/1/2/0 +# Regression test for a panic when the end key was equal to the Next() key of the last key returned. +statement ok +SELECT + crdb_internal.pretty_key(key, 0) +FROM + crdb_internal.scan( + ARRAY[ + crdb_internal.table_span($tableid)[1], + ( + SELECT key || '\x00'::BYTES + FROM crdb_internal.scan(crdb_internal.table_span($tableid)) + ORDER BY key DESC + LIMIT 1 + ) + ] + ) + # An error should be returned when an invalid range ID is specified. statement error pq: range with ID 1000000 not found SELECT key FROM crdb_internal.list_sql_keys_in_range(1000000) diff --git a/pkg/sql/sem/builtins/generator_builtins.go b/pkg/sql/sem/builtins/generator_builtins.go index d5287c37856c..05034272874a 100644 --- a/pkg/sql/sem/builtins/generator_builtins.go +++ b/pkg/sql/sem/builtins/generator_builtins.go @@ -2158,6 +2158,10 @@ type spanKeyIterator struct { // the iterator maintains a small set of K/V pairs in the span, // and accesses more in a streaming fashion. kvs []roachpb.KeyValue + + // resumeSpan is the resume span from the last ScanRequest. + resumeSpan *roachpb.Span + // index maintains the current position of the iterator in kvs. index int // A buffer to avoid allocating an array on every call to Values(). @@ -2189,15 +2193,14 @@ func (sp *spanKeyIterator) Next(ctx context.Context) (bool, error) { return true, nil } - // If we don't have any K/V pairs at all, then we're out of results. - if len(sp.kvs) == 0 { + // If we don't have a resume span, then we're out of results. + if sp.resumeSpan == nil { return false, nil } // If we had some K/V pairs already, use the last key to constrain // the result of the next scan. - startKey := sp.kvs[len(sp.kvs)-1].Key.Next() - err := sp.scan(ctx, startKey, sp.span.EndKey) + err := sp.scan(ctx, sp.resumeSpan.Key, sp.span.EndKey) if err != nil { return false, err } @@ -2224,6 +2227,7 @@ func (sp *spanKeyIterator) scan( } resp := br.Responses[0].GetScan() sp.kvs = resp.Rows + sp.resumeSpan = resp.ResumeSpan // The user of the generator first calls Next(), then Values(), so the index // managing the iterator's position needs to start at -1 instead of 0. sp.index = -1