Skip to content

Commit 3e523d8

Browse files
committed
Fix handling of zero length arrays
Previously it was hanging. This logic is unfortunately gnarly.
1 parent df92b6f commit 3e523d8

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

client_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ func TestClient_NotFound(t *testing.T) {
5353
require.Equal(t, "", got)
5454
}
5555

56+
func TestClient_List(t *testing.T) {
57+
t.Parallel()
58+
59+
_, client := redtest.StartRedisServer(t)
60+
61+
ctx := context.Background()
62+
63+
arr, err := client.Command(ctx, "LRANGE", "foo", "-1", -1).Strings()
64+
require.NoError(t, err)
65+
require.Empty(t, arr)
66+
67+
n, err := client.Command(ctx, "RPUSH", "foo", "bar").Int()
68+
require.NoError(t, err)
69+
require.Equal(t, 1, n)
70+
71+
arr, err = client.Command(ctx, "LRANGE", "foo", "-1", -1).Strings()
72+
require.NoError(t, err)
73+
require.Equal(t, []string{"bar"}, arr)
74+
}
75+
5676
func TestClient_Race(t *testing.T) {
5777
t.Parallel()
5878

result.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ func (r *Result) Strings() ([]string, error) {
224224
return nil, fmt.Errorf("read array length: %w", err)
225225
}
226226

227-
if ln < 0 {
227+
if ln <= 0 {
228228
return nil, nil
229229
}
230230

@@ -309,18 +309,27 @@ func (r *Result) writeTo(w io.Writer) (int64, replyType, error) {
309309
}
310310

311311
isNewArray := typ == '*'
312+
var newArraySize int
312313
if isNewArray {
314+
var err error
313315
// New array
314-
n, err := strconv.Atoi(string(s))
316+
newArraySize, err = strconv.Atoi(string(s))
315317
if err != nil {
316318
return 0, typ, fmt.Errorf("invalid array length %q", s)
317319
}
318-
r.arrayStack = append(r.arrayStack, n)
320+
if newArraySize > 0 {
321+
r.arrayStack = append(r.arrayStack, newArraySize)
322+
}
319323
}
320324

321325
var n int
322326
n, r.err = w.Write(s)
323327
if !isNewArray {
328+
// On new arrays, we don't want to advance any state
329+
// as we've just modified the array stack.
330+
incrRead()
331+
} else if newArraySize == 0 {
332+
// Nil array, so we want to advance pipeline.
324333
incrRead()
325334
}
326335
return int64(n), typ, r.err
@@ -397,10 +406,11 @@ func (r *Result) ArrayLength() (int, error) {
397406
}
398407

399408
// -1 is a nil array.
400-
if gotN < 0 {
409+
if gotN <= 0 {
401410
return gotN, nil
402411
}
403412

413+
// Sanity check that we've populated the array stack correctly.
404414
if r.arrayStack[len(r.arrayStack)-1] != gotN {
405415
// This should be impossible.
406416
return 0, fmt.Errorf("array stack mismatch (expected %d, got %d)", r.arrayStack[len(r.arrayStack)-1], gotN)
@@ -432,16 +442,21 @@ func (r *Result) Next() bool {
432442
r.mu.Lock()
433443
defer r.mu.Unlock()
434444

435-
return r.next()
445+
return r.hasMore()
436446
}
437447

438-
// next returns true if there are more results to read.
439-
func (r *Result) next() bool {
448+
// hasMore returns true if there are more results to read.
449+
func (r *Result) hasMore() bool {
440450
if r.err != nil {
441451
return false
442452
}
443453

444-
return r.pipeline.at < r.pipeline.end || len(r.arrayStack) > 0
454+
var arrays int
455+
for _, n := range r.arrayStack {
456+
arrays += n
457+
}
458+
459+
return r.pipeline.at < r.pipeline.end || arrays > 0
445460
}
446461

447462
// Close releases all resources associated with the result.
@@ -458,7 +473,7 @@ func (r *Result) Close() error {
458473
}
459474

460475
func (r *Result) close() error {
461-
for r.next() {
476+
for r.hasMore() {
462477
// Read the result into discard so that the connection can be reused.
463478
_, _, err := r.writeTo(io.Discard)
464479
if errors.Is(err, errClosed) {

0 commit comments

Comments
 (0)