From 4c6ffd51250076f934cc4029bc2bc23579b286b8 Mon Sep 17 00:00:00 2001 From: yzc-yzc <96833212+yzc-yzc@users.noreply.github.com> Date: Fri, 20 Jun 2025 13:51:52 +0800 Subject: [PATCH] Fix negative offset issue for ZRANGEBY[SCORE|LEX] command (#14043) Fix #13952 This PR ensures that ZRANGE_SCORE/LEX command with a negative offset will return empty. --- src/t_zset.c | 8 +++++++- tests/unit/type/zset.tcl | 10 +++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/t_zset.c b/src/t_zset.c index 8533ff12b..3379a8f01 100644 --- a/src/t_zset.c +++ b/src/t_zset.c @@ -3277,7 +3277,7 @@ void genericZrangebyscoreCommand(zrange_result_handler *handler, handler->beginResultEmission(handler, -1); /* For invalid offset, return directly. */ - if (offset > 0 && offset >= (long)zsetLength(zobj)) { + if (offset < 0 || (offset > 0 && offset >= (long)zsetLength(zobj))) { handler->finalizeResultEmission(handler, 0); return; } @@ -3551,6 +3551,12 @@ void genericZrangebylexCommand(zrange_result_handler *handler, handler->beginResultEmission(handler, -1); + /* For invalid offset, return directly. */ + if (offset < 0 || (offset > 0 && offset >= (long)zsetLength(zobj))) { + handler->finalizeResultEmission(handler, 0); + return; + } + if (zobj->encoding == OBJ_ENCODING_LISTPACK) { unsigned char *zl = zobj->ptr; unsigned char *eptr, *sptr; diff --git a/tests/unit/type/zset.tcl b/tests/unit/type/zset.tcl index dc0554d84..9eb1ea86d 100644 --- a/tests/unit/type/zset.tcl +++ b/tests/unit/type/zset.tcl @@ -583,20 +583,24 @@ start_server {tags {"zset"}} { assert_equal {d e f} [r zrangebyscore zset 0 10 LIMIT 2 3] assert_equal {d e f} [r zrangebyscore zset 0 10 LIMIT 2 10] assert_equal {} [r zrangebyscore zset 0 10 LIMIT 20 10] + assert_equal {} [r zrangebyscore zset 0 10 LIMIT -1 2] assert_equal {f e} [r zrevrangebyscore zset 10 0 LIMIT 0 2] assert_equal {d c b} [r zrevrangebyscore zset 10 0 LIMIT 2 3] assert_equal {d c b} [r zrevrangebyscore zset 10 0 LIMIT 2 10] assert_equal {} [r zrevrangebyscore zset 10 0 LIMIT 20 10] + assert_equal {} [r zrevrangebyscore zset 10 0 LIMIT -1 2] # zrangebyscore uses different logic when offset > ZSKIPLIST_MAX_SEARCH create_long_zset zset 30 assert_equal {i12 i13 i14} [r zrangebyscore zset 0 20 LIMIT 12 3] assert_equal {i14 i15} [r zrangebyscore zset 0 20 LIMIT 14 2] assert_equal {i19 i20 i21} [r zrangebyscore zset 0 30 LIMIT 19 3] - assert_equal {i29} [r zrangebyscore zset 10 30 LIMIT 19 2] + assert_equal {i29} [r zrangebyscore zset 10 30 LIMIT 19 2] + assert_equal {} [r zrangebyscore zset 0 20 LIMIT -1 3] assert_equal {i17 i16 i15} [r zrevrangebyscore zset 30 10 LIMIT 12 3] assert_equal {i6 i5} [r zrevrangebyscore zset 20 0 LIMIT 14 2] assert_equal {i2 i1 i0} [r zrevrangebyscore zset 20 0 LIMIT 18 5] assert_equal {i0} [r zrevrangebyscore zset 20 0 LIMIT 20 5] + assert_equal {} [r zrevrangebyscore zset 30 10 LIMIT -1 3] } test "ZRANGEBYSCORE with LIMIT and WITHSCORES - $encoding" { @@ -680,9 +684,11 @@ start_server {tags {"zset"}} { assert_equal {bar} [r zrangebylex zset \[bar \[down LIMIT 0 1] assert_equal {cool} [r zrangebylex zset \[bar \[down LIMIT 1 1] assert_equal {bar cool down} [r zrangebylex zset \[bar \[down LIMIT 0 100] + assert_equal {} [r zrangebylex zset - \[cool LIMIT -1 2] assert_equal {omega hill great foo elephant} [r zrevrangebylex zset + \[d LIMIT 0 5] assert_equal {omega hill great foo} [r zrevrangebylex zset + \[d LIMIT 0 4] assert_equal {great foo elephant} [r zrevrangebylex zset + \[d LIMIT 2 3] + assert_equal {} [r zrevrangebylex zset + \[d LIMIT -1 5] # zrangebylex uses different logic when offset > ZSKIPLIST_MAX_SEARCH create_long_lex_zset assert_equal {max null} [r zrangebylex zset - \[tree LIMIT 12 2] @@ -692,12 +698,14 @@ start_server {tags {"zset"}} { assert_equal {max} [r zrangebylex zset \[max \[null LIMIT 0 1] assert_equal {null} [r zrangebylex zset \[max \[null LIMIT 1 1] assert_equal {max null omega point} [r zrangebylex zset \[max \[point LIMIT 0 100] + assert_equal {} [r zrangebylex zset - \[tree LIMIT -1 2] assert_equal {tree sea result query point} [r zrevrangebylex zset + \[o LIMIT 0 5] assert_equal {tree sea result query} [r zrevrangebylex zset + \[o LIMIT 0 4] assert_equal {omega null max lip} [r zrevrangebylex zset + \[l LIMIT 5 4] assert_equal {elephant down} [r zrevrangebylex zset + \[a LIMIT 15 2] assert_equal {bar alpha} [r zrevrangebylex zset + - LIMIT 18 6] assert_equal {hill great foo} [r zrevrangebylex zset + \[c LIMIT 12 3] + assert_equal {} [r zrevrangebylex zset + \[o LIMIT -1 5] } test "ZRANGEBYLEX with invalid lex range specifiers - $encoding" {