diff options
author | michael-grunder <michael.grunder@gmail.com> | 2022-10-22 22:18:31 +0300 |
---|---|---|
committer | Michael Grunder <michael.grunder@gmail.com> | 2022-10-22 22:46:01 +0300 |
commit | 71bcbcb973413ae933cf30d58254dfb02425c1ed (patch) | |
tree | 4789de334e9f5900043a1e9fe7e680cb23b93c98 | |
parent | f3a408305a3df04b6aaaede5fa7f37ddca1f8efb (diff) |
Implement ZRANGESTORE and add ZRANGE options
* Add ZRANGESTORE command.
* Add Redis 6.2's `REV`, `BYLEX`, and `BYSCORE` to ZRANGE options.
* Refactor several ZRANGE family commands into a single reply and
options handler, using PHP's new argument parsing macros.
* Extend our tests to use the new ZRANGE options.
See #1894
-rw-r--r-- | cluster_library.c | 11 | ||||
-rw-r--r-- | cluster_library.h | 3 | ||||
-rw-r--r-- | library.c | 12 | ||||
-rw-r--r-- | library.h | 1 | ||||
-rw-r--r-- | redis.c | 59 | ||||
-rw-r--r-- | redis.stub.php | 58 | ||||
-rw-r--r-- | redis_arginfo.h | 25 | ||||
-rw-r--r-- | redis_cluster.c | 53 | ||||
-rw-r--r-- | redis_cluster.stub.php | 11 | ||||
-rw-r--r-- | redis_cluster_arginfo.h | 14 | ||||
-rw-r--r-- | redis_cluster_legacy_arginfo.h | 21 | ||||
-rw-r--r-- | redis_commands.c | 288 | ||||
-rw-r--r-- | redis_commands.h | 7 | ||||
-rw-r--r-- | redis_legacy_arginfo.h | 22 | ||||
-rw-r--r-- | tests/RedisTest.php | 33 |
15 files changed, 386 insertions, 232 deletions
diff --git a/cluster_library.c b/cluster_library.c index fe574e50..90a6f95e 100644 --- a/cluster_library.c +++ b/cluster_library.c @@ -2115,6 +2115,17 @@ cluster_variant_resp_generic(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, cluster_free_reply(r, 1); } +PHP_REDIS_API void +cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { + cluster_cb cb; + + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + + cb = ctx ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; + + cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, ctx); +} + PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx) { diff --git a/cluster_library.h b/cluster_library.h index 1f18c35a..74ede7a4 100644 --- a/cluster_library.h +++ b/cluster_library.h @@ -437,6 +437,9 @@ PHP_REDIS_API void cluster_sub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster * PHP_REDIS_API void cluster_unsub_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); +PHP_REDIS_API void cluster_zrange_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, + void *ctx); + PHP_REDIS_API void cluster_variant_resp(INTERNAL_FUNCTION_PARAMETERS, redisCluster *c, void *ctx); @@ -1160,6 +1160,18 @@ redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); } +PHP_REDIS_API int +redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { + FailableResultCallback cb; + + /* Whether or not we have WITHSCORES */ + ZEND_ASSERT(ctx == NULL || ctx == PHPREDIS_CTX_PTR); + + cb = ctx ? redis_mbulk_reply_zipped_keys_dbl : redis_sock_read_multibulk_reply; + + return cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, z_tab, ctx); +} + PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx) { char *response; int response_len; @@ -68,6 +68,7 @@ PHP_REDIS_API int redis_string_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock PHP_REDIS_API int redis_ping_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_info_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API int redis_config_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); +PHP_REDIS_API int redis_zrange_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); PHP_REDIS_API void redis_parse_info_response(char *response, zval *z_ret); PHP_REDIS_API void redis_parse_client_list_response(char *response, zval *z_ret); PHP_REDIS_API int redis_type_response(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, zval *z_tab, void *ctx); @@ -1906,77 +1906,38 @@ PHP_METHOD(Redis, zAdd) { } /* }}} */ -/* Handle ZRANGE and ZREVRANGE as they're the same except for keyword */ -static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, - zrange_cb fun) -{ - char *cmd; - int cmd_len; - RedisSock *redis_sock; - int withscores = 0; - - if ((redis_sock = redis_sock_get(getThis(), 0)) == NULL) { - RETURN_FALSE; - } - - if(fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, kw, &cmd, - &cmd_len, &withscores, NULL, NULL) == FAILURE) - { - RETURN_FALSE; - } - - REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); - if(withscores) { - if (IS_ATOMIC(redis_sock)) { - redis_mbulk_reply_zipped_keys_dbl(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, NULL, NULL); - } - REDIS_PROCESS_RESPONSE(redis_mbulk_reply_zipped_keys_dbl); - } else { - if (IS_ATOMIC(redis_sock)) { - if(redis_sock_read_multibulk_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU, - redis_sock, NULL, NULL) < 0) - { - RETURN_FALSE; - } - } - REDIS_PROCESS_RESPONSE(redis_sock_read_multibulk_reply); - } -} - /* {{{ proto array Redis::zRandMember(string key, array options) */ -PHP_METHOD(Redis, zRandMember) -{ +PHP_METHOD(Redis, zRandMember) { REDIS_PROCESS_CMD(zrandmember, redis_zrandmember_response); } /* }}} */ /* {{{ proto array Redis::zRange(string key,int start,int end,bool scores = 0) */ -PHP_METHOD(Redis, zRange) -{ - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", - redis_zrange_cmd); +PHP_METHOD(Redis, zRange) { + REDIS_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ +PHP_METHOD(Redis, zrangestore) { + REDIS_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, redis_long_response); +} + /* {{{ proto array Redis::zRevRange(string k, long s, long e, bool scores = 0) */ PHP_METHOD(Redis, zRevRange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", - redis_zrange_cmd); + REDIS_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ /* {{{ proto array Redis::zRangeByScore(string k,string s,string e,array opt) */ PHP_METHOD(Redis, zRangeByScore) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE", - redis_zrangebyscore_cmd); + REDIS_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ /* {{{ proto array Redis::zRevRangeByScore(string key, string start, string end, * array options) */ PHP_METHOD(Redis, zRevRangeByScore) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE", - redis_zrangebyscore_cmd); + REDIS_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, redis_zrange_response); } /* }}} */ diff --git a/redis.stub.php b/redis.stub.php index fd9ea3c2..51dd1fe3 100644 --- a/redis.stub.php +++ b/redis.stub.php @@ -694,12 +694,68 @@ class Redis { public function zPopMin(string $key, int $value = null): Redis|array|false; - public function zRange(string $key, int $start, int $end, mixed $scores = null): Redis|array|false; + /** + * Retreive a range of elements of a sorted set between a start and end point. + * How the command works in particular is greatly affected by the options that + * are passed in. + * + * @see https://https://redis.io/commands/zrange/ + * @category zset + * + * @param string $key The sorted set in question. + * @param mixed $start The starting index we want to return. + * @param mixed $end The final index we want to return. + * + * @param array|bool|null $options This value may either be an array of options to pass to + * the command, or for historical purposes a boolean which + * controls just the 'WITHSCORES' option. + * + * @return Redis|array|false An array with matching elements or false on failure. + * + * Detailed description of options array: + * + * <code> + * <?php + * $options = [ + * 'WITHSCORES' => true, // Return both scores and members. + * 'LIMIT' => [10, 10], // Start at offset 10 and return 10 elements. + * 'REV' // Return the elements in reverse order + * 'BYSCORE', // Treat `start` and `end` as scores instead + * 'BYLEX' // Treat `start` and `end` as lexographical values. + * ]; + * ?> + * </code> + * + * Note: 'BYLEX' and 'BYSCORE' are mutually exclusive. + * + */ + public function zRange(string $key, mixed $start, mixed $end, array|bool|null $options = null): Redis|array|false; public function zRangeByLex(string $key, string $min, string $max, int $offset = -1, int $count = -1): Redis|array|false; public function zRangeByScore(string $key, string $start, string $end, array $options = []): Redis|array|false; + /** + * This command is similar to ZRANGE except that instead of returning the values directly + * it will store them in a destination key provided by the user + * + * @see https://https://redis.io/commands/zrange/ + * @see Redis::zRange + * @category zset + * + * @param string $dstkey The key to store the resulting element(s) + * @param string $srckey The source key with element(s) to retreive + * @param string $start The starting index to store + * @param string $end The ending index to store + * @param array|bool|null $options Our options array that controls how the command will function. + * + * @return Redis|int|false The number of elements stored in dstkey or false on failure. + * + * See Redis::zRange for a full description of the possible options. + */ + public function zrangestore(string $dstkey, string $srckey, string $start, string $end, + array|bool|null $options = NULL): Redis|int|false; + public function zRandMember(string $key, array $options = null): Redis|string|array; public function zRank(string $key, mixed $member): Redis|int|false; diff --git a/redis_arginfo.h b/redis_arginfo.h index 482ad347..38b11ccf 100644 --- a/redis_arginfo.h +++ b/redis_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a27d28648f2d1a77237305083f36abc5e071f5b1 */ + * Stub hash: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null") @@ -1000,9 +1000,9 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null") + ZEND_ARG_TYPE_INFO(0, start, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, end, IS_MIXED, 0) + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByLex, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) @@ -1020,6 +1020,14 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRangeByScore, 0 ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zrangestore, 0, 4, Redis, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, start, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, end, IS_STRING, 0) + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "NULL") +ZEND_END_ARG_INFO() + #define arginfo_class_Redis_zRandMember arginfo_class_Redis_hRandField ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRank, 0, 2, Redis, MAY_BE_LONG|MAY_BE_FALSE) @@ -1043,7 +1051,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_zCount -#define arginfo_class_Redis_zRevRange arginfo_class_Redis_zRange +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_Redis_zRevRange, 0, 3, Redis, MAY_BE_ARRAY|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, scores, IS_MIXED, 0, "null") +ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex @@ -1314,6 +1327,7 @@ ZEND_METHOD(Redis, zPopMin); ZEND_METHOD(Redis, zRange); ZEND_METHOD(Redis, zRangeByLex); ZEND_METHOD(Redis, zRangeByScore); +ZEND_METHOD(Redis, zrangestore); ZEND_METHOD(Redis, zRandMember); ZEND_METHOD(Redis, zRank); ZEND_METHOD(Redis, zRem); @@ -1559,6 +1573,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, zrangestore, arginfo_class_Redis_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC) diff --git a/redis_cluster.c b/redis_cluster.c index 03e85d84..c5078521 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -1429,61 +1429,31 @@ PHP_METHOD(RedisCluster, setrange) { } /* }}} */ -/* Generic implementation for ZRANGE, ZREVRANGE, ZRANGEBYSCORE, ZREVRANGEBYSCORE */ -static void generic_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, - zrange_cb fun) -{ - redisCluster *c = GET_CONTEXT(); - c->readonly = CLUSTER_IS_ATOMIC(c); - cluster_cb cb; - char *cmd; int cmd_len; short slot; - int withscores = 0; - - if (fun(INTERNAL_FUNCTION_PARAM_PASSTHRU, c->flags, kw, &cmd, &cmd_len, - &withscores, &slot, NULL) == FAILURE) - { - efree(cmd); - RETURN_FALSE; - } - - if (cluster_send_command(c,slot,cmd,cmd_len) < 0 || c->err != NULL) { - efree(cmd); - RETURN_FALSE; - } - - efree(cmd); - - cb = withscores ? cluster_mbulk_zipdbl_resp : cluster_mbulk_resp; - if (CLUSTER_IS_ATOMIC(c)) { - cb(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); - } else { - void *ctx = NULL; - CLUSTER_ENQUEUE_RESPONSE(c, slot, cb, ctx); - RETURN_ZVAL(getThis(), 1, 0); - } -} - /* {{{ proto * array RedisCluster::zrange(string k, long s, long e, bool score = 0) */ PHP_METHOD(RedisCluster, zrange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGE", - redis_zrange_cmd); + CLUSTER_PROCESS_KW_CMD("ZRANGE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ /* {{{ proto + * array RedisCluster::zrange(string $dstkey, string $srckey, long s, long e, array|bool $options = false) */ +PHP_METHOD(RedisCluster, zrangestore) { + CLUSTER_PROCESS_KW_CMD("ZRANGESTORE", redis_zrange_cmd, cluster_long_resp, 0); +} + +/* }}} */ +/* {{{ proto * array RedisCluster::zrevrange(string k,long s,long e,bool scores = 0) */ PHP_METHOD(RedisCluster, zrevrange) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGE", - redis_zrange_cmd); + CLUSTER_PROCESS_KW_CMD("ZREVRANGE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ /* {{{ proto array * RedisCluster::zrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrangebyscore) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZRANGEBYSCORE", - redis_zrangebyscore_cmd); + CLUSTER_PROCESS_KW_CMD("ZRANGEBYSCORE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ @@ -1516,8 +1486,7 @@ PHP_METHOD(RedisCluster, zrem) { /* {{{ proto array * RedisCluster::zrevrangebyscore(string k, long s, long e, array opts) */ PHP_METHOD(RedisCluster, zrevrangebyscore) { - generic_zrange_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "ZREVRANGEBYSCORE", - redis_zrangebyscore_cmd); + CLUSTER_PROCESS_KW_CMD("ZREVRANGEBYSCORE", redis_zrange_cmd, cluster_zrange_resp, 1); } /* }}} */ diff --git a/redis_cluster.stub.php b/redis_cluster.stub.php index 598a8126..165dfd3b 100644 --- a/redis_cluster.stub.php +++ b/redis_cluster.stub.php @@ -412,7 +412,16 @@ class RedisCluster { public function zpopmin(string $key, int $value = null): RedisCluster|bool|array; - public function zrange(string $key, int $start, int $end, mixed $options_withscores = null): RedisCluster|array|bool; + /** + * @see Redis::zrange + */ + public function zrange(string $key, mixed $start, mixed $end, array|bool|null $options = null): RedisCluster|array|bool; + + /** + * @see Redis::zrangestore + */ + public function zrangestore(string $dstkey, string $srckey, int $start, int $end, + array|bool|null $options = null): RedisCluster|int|false; public function zrangebylex(string $key, string $min, string $max, int $offset = -1, int $count = -1): RedisCluster|array|false; diff --git a/redis_cluster_arginfo.h b/redis_cluster_arginfo.h index b6bde9f7..0b4619fb 100644 --- a/redis_cluster_arginfo.h +++ b/redis_cluster_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7a2f14794870618a17273a2ed9f60d81449c82af */ + * Stub hash: e761b2a65f9f57254e0201f9643b823e79e2a0a8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1) @@ -864,9 +864,17 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrange, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_BOOL) ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, start, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, end, IS_MIXED, 0) + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangestore, 0, 4, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, dstkey, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, srckey, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, start, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, end, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options_withscores, IS_MIXED, 0, "null") + ZEND_ARG_TYPE_MASK(0, options, MAY_BE_ARRAY|MAY_BE_BOOL|MAY_BE_NULL, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_zrangebylex, 0, 3, RedisCluster, MAY_BE_ARRAY|MAY_BE_FALSE) @@ -1122,6 +1130,7 @@ ZEND_METHOD(RedisCluster, zlexcount); ZEND_METHOD(RedisCluster, zpopmax); ZEND_METHOD(RedisCluster, zpopmin); ZEND_METHOD(RedisCluster, zrange); +ZEND_METHOD(RedisCluster, zrangestore); ZEND_METHOD(RedisCluster, zrangebylex); ZEND_METHOD(RedisCluster, zrangebyscore); ZEND_METHOD(RedisCluster, zrank); @@ -1327,6 +1336,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC) diff --git a/redis_cluster_legacy_arginfo.h b/redis_cluster_legacy_arginfo.h index db11ebf5..cc73a262 100644 --- a/redis_cluster_legacy_arginfo.h +++ b/redis_cluster_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7a2f14794870618a17273a2ed9f60d81449c82af */ + * Stub hash: e761b2a65f9f57254e0201f9643b823e79e2a0a8 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) @@ -743,7 +743,15 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options_withscores) + ZEND_ARG_INFO(0, options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangestore, 0, 0, 4) + ZEND_ARG_INFO(0, dstkey) + ZEND_ARG_INFO(0, srckey) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3) @@ -754,12 +762,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebylex, 0, 0, 3) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_zrangebyscore, 0, 0, 3) - ZEND_ARG_INFO(0, key) - ZEND_ARG_INFO(0, start) - ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, options) -ZEND_END_ARG_INFO() +#define arginfo_class_RedisCluster_zrangebyscore arginfo_class_RedisCluster_zrange #define arginfo_class_RedisCluster_zrank arginfo_class_RedisCluster_hexists @@ -979,6 +982,7 @@ ZEND_METHOD(RedisCluster, zlexcount); ZEND_METHOD(RedisCluster, zpopmax); ZEND_METHOD(RedisCluster, zpopmin); ZEND_METHOD(RedisCluster, zrange); +ZEND_METHOD(RedisCluster, zrangestore); ZEND_METHOD(RedisCluster, zrangebylex); ZEND_METHOD(RedisCluster, zrangebyscore); ZEND_METHOD(RedisCluster, zrank); @@ -1184,6 +1188,7 @@ static const zend_function_entry class_RedisCluster_methods[] = { ZEND_ME(RedisCluster, zpopmax, arginfo_class_RedisCluster_zpopmax, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zpopmin, arginfo_class_RedisCluster_zpopmin, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrange, arginfo_class_RedisCluster_zrange, ZEND_ACC_PUBLIC) + ZEND_ME(RedisCluster, zrangestore, arginfo_class_RedisCluster_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebylex, arginfo_class_RedisCluster_zrangebylex, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrangebyscore, arginfo_class_RedisCluster_zrangebyscore, ZEND_ACC_PUBLIC) ZEND_ME(RedisCluster, zrank, arginfo_class_RedisCluster_zrank, ZEND_ACC_PUBLIC) diff --git a/redis_commands.c b/redis_commands.c index a0a55151..08992c2c 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -72,6 +72,26 @@ typedef struct redisRestoreOptions { zend_long freq; } redisRestoreOptions; +#define REDIS_ZRANGE_HAS_DST_KEY (1 << 0) +#define REDIS_ZRANGE_HAS_WITHSCORES (1 << 1) +#define REDIS_ZRANGE_HAS_BY_LEX_SCORE (1 << 2) +#define REDIS_ZRANGE_HAS_REV (1 << 3) +#define REDIS_ZRANGE_HAS_LIMIT (1 << 4) +#define REDIS_ZRANGE_INT_RANGE (1 << 5) + +/* ZRANGE, ZRANGEBYSCORE, ZRANGESTORE options */ +typedef struct redisZrangeOptions { + zend_bool withscores; + zend_bool byscore; + zend_bool bylex; + zend_bool rev; + struct { + zend_bool enabled; + zend_long offset; + zend_long count; + } limit; +} redisZrangeOptions; + /* Local passthrough macro for command construction. Given that these methods * are generic (so they work whether the caller is Redis or RedisCluster) we * will always have redis_sock, slot*, and */ @@ -612,119 +632,184 @@ int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, return cmdstr.len; } -/* ZRANGE/ZREVRANGE */ -int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, int *withscores, - short *slot, void **ctx) -{ - char *key; - size_t key_len; - zend_long start, end; - zend_string *zkey; - zval *z_ws = NULL, *z_ele; +void redis_get_zrange_options(redisZrangeOptions *dst, zval *src, int flags) { + zval *zv, *zoff, *zcnt; + zend_string *key; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sll|z!", &key, &key_len, - &start, &end, &z_ws) == FAILURE) - { - return FAILURE; + ZEND_ASSERT(dst != NULL); + + memset(dst, 0, sizeof(*dst)); + + if (src == NULL) + return; + + if (Z_TYPE_P(src) != IS_ARRAY) { + if (Z_TYPE_P(src) == IS_TRUE && (flags & REDIS_ZRANGE_HAS_WITHSCORES)) + dst->withscores = 1; + return; } - // Clear withscores arg - *withscores = 0; + ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(src), key, zv) { + ZVAL_DEREF(zv); - /* Accept ['withscores' => true], or the legacy `true` value */ - if (z_ws) { - if (Z_TYPE_P(z_ws) == IS_ARRAY) { - ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(z_ws), zkey, z_ele) { - if (zkey != NULL) { - ZVAL_DEREF(z_ele); - if (zend_string_equals_literal_ci(zkey, "withscores")) { - *withscores = zval_is_true(z_ele); - break; - } + if (key) { + if ((flags & REDIS_ZRANGE_HAS_WITHSCORES) && zend_string_equals_literal_ci(key, "WITHSCORES")) + dst->withscores = zval_is_true(zv); + else if ((flags & REDIS_ZRANGE_HAS_LIMIT) && zend_string_equals_literal_ci(key, "LIMIT") && + Z_TYPE_P(zv) == IS_ARRAY) + { + if ((zoff = zend_hash_index_find(Z_ARRVAL_P(zv), 0)) != NULL && + (zcnt = zend_hash_index_find(Z_ARRVAL_P(zv), 1)) != NULL) + { + dst->limit.enabled = 1; + dst->limit.offset = zval_get_long(zoff); + dst->limit.count = zval_get_long(zcnt); + } else { + php_error_docref(NULL, E_WARNING, "LIMIT offset and count must be an array with twe elements"); } - } ZEND_HASH_FOREACH_END(); - } else if (Z_TYPE_P(z_ws) == IS_TRUE) { - *withscores = Z_TYPE_P(z_ws) == IS_TRUE; + } + } else if (Z_TYPE_P(zv) == IS_STRING) { + key = Z_STR_P(zv); + + if ((flags & REDIS_ZRANGE_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYSCORE")) + dst->byscore = 1, dst->bylex = 0; + else if ((flags & REDIS_ZRANGE_HAS_BY_LEX_SCORE) && zend_string_equals_literal_ci(key, "BYLEX")) + dst->bylex = 1, dst->byscore = 0; + else if ((flags & REDIS_ZRANGE_HAS_REV) && zend_string_equals_literal_ci(key, "REV")) + dst->rev = 1; + else if ((flags & REDIS_ZRANGE_HAS_WITHSCORES && zend_string_equals_literal_ci(key, "WITHSCORES"))) + dst->withscores = 1; } - } + } ZEND_HASH_FOREACH_END(); +} - if (*withscores) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdds", key, key_len, start, end, - "WITHSCORES", sizeof("WITHSCORES") - 1); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kdd", key, key_len, start, end); +// + ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES] +// + ZRANGESTORE dst src min max [BYSCORE | BYLEX] [REV] [LIMIT offset count] +// + ZREVRANGE key start stop [WITHSCORES] +// + ZRANGEBYSCORE key min max [LIMIT offset count] [WITHSCORES] +// + ZREVRANGEBYSCORE key max min [LIMIT offset count] [WITHSCORES] +// - ZRANGEBYLEX key min max [LIMIT offset count] +// - ZREVRANGEBYLEX key max min [LIMIT offset count] +static int redis_get_zrange_cmd_flags(const char *kw) { + size_t len = strlen(kw); + + if (REDIS_STRICMP_STATIC(kw, len, "ZRANGESTORE")) { + return REDIS_ZRANGE_HAS_DST_KEY | + REDIS_ZRANGE_HAS_WITHSCORES | + REDIS_ZRANGE_HAS_BY_LEX_SCORE | + REDIS_ZRANGE_HAS_REV | + REDIS_ZRANGE_HAS_LIMIT; + } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGE")) { + return REDIS_ZRANGE_HAS_WITHSCORES | + REDIS_ZRANGE_HAS_BY_LEX_SCORE | + REDIS_ZRANGE_HAS_REV | + REDIS_ZRANGE_HAS_LIMIT; + } else if (REDIS_STRICMP_STATIC(kw, len, "ZREVRANGE")) { + return REDIS_ZRANGE_HAS_WITHSCORES | + REDIS_ZRANGE_INT_RANGE; + } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYSCORE") || + REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYSCORE")) + { + return REDIS_ZRANGE_HAS_LIMIT | + REDIS_ZRANGE_HAS_WITHSCORES; + } else if (REDIS_STRICMP_STATIC(kw, len, "ZRANGEBYLEX") || + REDIS_STRICMP_STATIC(kw, len, "ZREVRANGEBYLEX")) + { + return REDIS_ZRANGE_HAS_LIMIT; } - return SUCCESS; + /* Reaching this line means a compile-time error */ + ZEND_ASSERT(0); } -int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, int *withscores, - short *slot, void **ctx) +/* Validate ZLEX* min/max argument strings */ +#define validate_zlex_arg_zstr(zs_) validate_zlex_arg(ZSTR_VAL((zs_)), ZSTR_LEN((zs_))) +static int validate_zlex_arg(const char *arg, size_t len) { + return (len > 1 && (*arg == '[' || *arg == '(')) || + (len == 1 && (*arg == '+' || *arg == '-')); +} + +int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char *kw, char **cmd, int *cmd_len, short *slot, + void **ctx) { - char *key, *start, *end; - int has_limit = 0; - long offset, count; - size_t key_len, start_len, end_len; - zval *z_opt=NULL, *z_ele; - zend_string *zkey; - HashTable *ht_opt; + zend_string *dst = NULL, *src = NULL, *sstart = NULL, *send = NULL; + struct redisZrangeOptions opt; + zend_long start = 0, end = 0; + smart_string cmdstr = {0}; + zval *zoptions = NULL; + int min_argc, flags; + short slot2; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss|a", &key, &key_len, - &start, &start_len, &end, &end_len, &z_opt) - ==FAILURE) - { - return FAILURE; + flags = redis_get_zrange_cmd_flags(kw); + + min_argc = 3 + (flags & REDIS_ZRANGE_HAS_DST_KEY); + ZEND_PARSE_PARAMETERS_START(min_argc, min_argc + 1) + if (flags & REDIS_ZRANGE_HAS_DST_KEY) { + Z_PARAM_STR(dst) + } + Z_PARAM_STR(src) + if (flags & REDIS_ZRANGE_INT_RANGE) { + Z_PARAM_LONG(start) + Z_PARAM_LONG(end) + } else { + Z_PARAM_STR(sstart) + Z_PARAM_STR(send) + } + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL_OR_NULL(zoptions) + ZEND_PARSE_PARAMETERS_END_EX(return FAILURE); + + redis_get_zrange_options(&opt, zoptions, flags); + + if (opt.bylex) { + ZEND_ASSERT(!(flags & REDIS_ZRANGE_INT_RANGE)); + if (!validate_zlex_arg_zstr(sstart) || !validate_zlex_arg_zstr(send)) { + php_error_docref(NULL, E_WARNING, "Legographical args must start with '[' or '(' or be '+' or '-'"); + return FAILURE; + } } - // Check for an options array - if (z_opt && Z_TYPE_P(z_opt) == IS_ARRAY) { - ht_opt = Z_ARRVAL_P(z_opt); - ZEND_HASH_FOREACH_STR_KEY_VAL(ht_opt, zkey, z_ele) { - /* All options require a string key type */ - if (!zkey) continue; - ZVAL_DEREF(z_ele); - /* Check for withscores and limit */ - if (zend_string_equals_literal_ci(zkey, "withscores")) { - *withscores = zval_is_true(z_ele); - } else if (zend_string_equals_literal_ci(zkey, "limit") && Z_TYPE_P(z_ele) == IS_ARRAY) { - HashTable *htlimit = Z_ARRVAL_P(z_ele); - zval *zoff, *zcnt; - - /* We need two arguments (offset and count) */ - if ((zoff = zend_hash_index_find(htlimit, 0)) != NULL && - (zcnt = zend_hash_index_find(htlimit, 1)) != NULL - ) { - /* Set our limit if we can get valid longs from both args */ - offset = zval_get_long(zoff); - count = zval_get_long(zcnt); - has_limit = 1; - } - } - } ZEND_HASH_FOREACH_END(); + redis_cmd_init_sstr(&cmdstr, min_argc + !!opt.bylex + !!opt.byscore + + !!opt.rev + !!opt.withscores + + (opt.limit.enabled ? 3 : 0), kw, strlen(kw)); + + if (flags & REDIS_ZRANGE_HAS_DST_KEY) + redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot); + redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, &slot2); + + /* Protect the user from crossslot errors */ + if ((flags & REDIS_ZRANGE_HAS_DST_KEY) && slot && *slot != slot2) { + php_error_docref(NULL, E_WARNING, "destination and source keys must map to the same slot"); + efree(cmdstr.c); + return FAILURE; } - // Construct our command - if (*withscores) { - if (has_limit) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssdds", key, key_len, - start, start_len, end, end_len, "LIMIT", 5, offset, count, - "WITHSCORES", 10); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksss", key, key_len, start, - start_len, end, end_len, "WITHSCORES", 10); - } + if (flags & REDIS_ZRANGE_INT_RANGE) { + redis_cmd_append_sstr_long(&cmdstr, start); + redis_cmd_append_sstr_long(&cmdstr, end); } else { - if (has_limit) { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "ksssdd", key, key_len, start, - start_len, end, end_len, "LIMIT", 5, offset, count); - } else { - *cmd_len = REDIS_CMD_SPPRINTF(cmd, kw, "kss", key, key_len, start, - start_len, end, end_len); - } + redis_cmd_append_sstr_zstr(&cmdstr, sstart); + redis_cmd_append_sstr_zstr(&cmdstr, send); + } + + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.byscore, "BYSCORE"); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.bylex, "BYLEX"); + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.rev, "REV"); + + if (opt.limit.enabled) { + REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "LIMIT"); + redis_cmd_append_sstr_long(&cmdstr, opt.limit.offset); + redis_cmd_append_sstr_long(&cmdstr, opt.limit.count); } + REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, opt.withscores, "WITHSCORES"); + + if (slot) *slot = slot2; + *ctx = opt.withscores ? PHPREDIS_CTX_PTR : NULL; + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + return SUCCESS; } @@ -1414,14 +1499,9 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* min and max must start with '(' or '[', or be either '-' or '+' */ - if (min_len < 1 || max_len < 1 || - (min[0] != '(' && min[0] != '[' && - (min[0] != '-' || min_len > 1) && (min[0] != '+' || min_len > 1)) || - (max[0] != '(' && max[0] != '[' && - (max[0] != '-' || max_len > 1) && (max[0] != '+' || max_len > 1))) - { - php_error_docref(0, E_WARNING, - "min and max arguments must start with '[' or '('"); + if (!validate_zlex_arg(min, min_len) || !validate_zlex_arg(max, max_len)) { + php_error_docref(NULL, E_WARNING, + "Min/Max args can be '-' or '+', or start with '[' or '('"); return FAILURE; } @@ -1437,12 +1517,6 @@ int redis_zrangebylex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } -/* Validate ZLEX* min/max argument strings */ -static int validate_zlex_arg(const char *arg, size_t len) { - return (len > 1 && (*arg == '[' || *arg == '(')) || - (len == 1 && (*arg == '+' || *arg == '-')); -} - /* ZLEXCOUNT/ZREMRANGEBYLEX */ int redis_gen_zlex_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, diff --git a/redis_commands.h b/redis_commands.h index 50512a3d..4d7a13e0 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -103,12 +103,7 @@ typedef int (*zrange_cb)(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *,char**,int*,int*,short*,void**); int redis_zrange_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, - void **ctx); - -int redis_zrangebyscore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, - char *kw, char **cmd, int *cmd_len, int *withscores, short *slot, - void **ctx); + char *kw, char **cmd, int *cmd_len, short *slot, void **ctx); int redis_config_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h index 2bdf5e12..b5b39586 100644 --- a/redis_legacy_arginfo.h +++ b/redis_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a27d28648f2d1a77237305083f36abc5e071f5b1 */ + * Stub hash: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0) ZEND_ARG_INFO(0, options) @@ -858,7 +858,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRange, 0, 0, 3) ZEND_ARG_INFO(0, key) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) - ZEND_ARG_INFO(0, scores) + ZEND_ARG_INFO(0, options) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 0, 3) @@ -869,8 +869,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByLex, 0, 0, 3) ZEND_ARG_INFO(0, count) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRangeByScore, 0, 0, 3) - ZEND_ARG_INFO(0, key) +#define arginfo_class_Redis_zRangeByScore arginfo_class_Redis_zRange + +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zrangestore, 0, 0, 4) + ZEND_ARG_INFO(0, dstkey) + ZEND_ARG_INFO(0, srckey) ZEND_ARG_INFO(0, start) ZEND_ARG_INFO(0, end) ZEND_ARG_INFO(0, options) @@ -888,11 +891,16 @@ ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRemRangeByScore arginfo_class_Redis_getRange -#define arginfo_class_Redis_zRevRange arginfo_class_Redis_zRange +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_zRevRange, 0, 0, 3) + ZEND_ARG_INFO(0, key) + ZEND_ARG_INFO(0, start) + ZEND_ARG_INFO(0, end) + ZEND_ARG_INFO(0, scores) +ZEND_END_ARG_INFO() #define arginfo_class_Redis_zRevRangeByLex arginfo_class_Redis_zRangeByLex -#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRangeByScore +#define arginfo_class_Redis_zRevRangeByScore arginfo_class_Redis_zRange #define arginfo_class_Redis_zRevRank arginfo_class_Redis_hExists @@ -1151,6 +1159,7 @@ ZEND_METHOD(Redis, zPopMin); ZEND_METHOD(Redis, zRange); ZEND_METHOD(Redis, zRangeByLex); ZEND_METHOD(Redis, zRangeByScore); +ZEND_METHOD(Redis, zrangestore); ZEND_METHOD(Redis, zRandMember); ZEND_METHOD(Redis, zRank); ZEND_METHOD(Redis, zRem); @@ -1396,6 +1405,7 @@ static const zend_function_entry class_Redis_methods[] = { ZEND_ME(Redis, zRange, arginfo_class_Redis_zRange, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByLex, arginfo_class_Redis_zRangeByLex, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRangeByScore, arginfo_class_Redis_zRangeByScore, ZEND_ACC_PUBLIC) + ZEND_ME(Redis, zrangestore, arginfo_class_Redis_zrangestore, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRandMember, arginfo_class_Redis_zRandMember, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRank, arginfo_class_Redis_zRank, ZEND_ACC_PUBLIC) ZEND_ME(Redis, zRem, arginfo_class_Redis_zRem, ZEND_ACC_PUBLIC) diff --git a/tests/RedisTest.php b/tests/RedisTest.php index 52b86330..149bbb84 100644 --- a/tests/RedisTest.php +++ b/tests/RedisTest.php @@ -2566,6 +2566,9 @@ class Redis_Test extends TestSuite $this->assertTrue(['val1', 'val2'] === $this->redis->zRangeByScore('key', 0, 3, ['limit' => [1, 2]])); $this->assertTrue(['val0', 'val1'] === $this->redis->zRangeByScore('key', 0, 1, ['limit' => [0, 100]])); + if ($this->minVersionCheck('6.2.0')) + $this->assertEquals(['val0', 'val1'], $this->redis->zrange('key', 0, 1, ['byscore', 'limit' => [0, 100]])); + // limits as references $limit = [0, 100]; foreach ($limit as &$val) {} @@ -2576,6 +2579,17 @@ class Redis_Test extends TestSuite $this->assertTrue(['val2', 'val1'] === $this->redis->zRevRangeByScore('key', 3, 0, ['limit' => [1, 2]])); $this->assertTrue(['val1', 'val0'] === $this->redis->zRevRangeByScore('key', 1, 0, ['limit' => [0, 100]])); + if ($this->minVersionCheck('6.2.0')) { + $this->assertEquals(['val1', 'val0'], $this->redis->zrange('key', 1, 0, ['byscore', 'rev', 'limit' => [0, 100]])); + $this->assertEquals(2, $this->redis->zrangestore('dst{key}', 'key', 1, 0, + ['byscore', 'rev', 'limit' => [0, 100]])); + $this->assertEquals(['val0', 'val1'], $this->redis->zRange('dst{key}', 0, -1)); + + $this->assertEquals(1, $this->redis->zrangestore('dst{key}', 'key', 1, 0, + ['byscore', 'rev', 'limit' => [0, 1]])); + $this->assertEquals(['val1'], $this->redis->zrange('dst{key}', 0, -1)); + } + $this->assertTrue(4 === $this->redis->zCard('key')); $this->assertTrue(1.0 === $this->redis->zScore('key', 'val1')); $this->assertFalse($this->redis->zScore('key', 'val')); @@ -2597,7 +2611,6 @@ class Redis_Test extends TestSuite $this->assertTrue(1 == $this->redis->zCount('zset', '(1', 2)); $this->assertTrue(0 == $this->redis->zCount('zset', '(1', '(2')); - // zincrby $this->redis->del('key'); $this->assertTrue(1.0 === $this->redis->zIncrBy('key', 1, 'val1')); @@ -2817,12 +2830,22 @@ class Redis_Test extends TestSuite $this->redis->zAdd('key', 0, $c); } - $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c'), ['a', 'b', 'c']); - $this->assertEquals($this->redis->zRangeByLex('key', '(e', '+'), ['f', 'g']); + $this->assertEquals(['a', 'b', 'c'], $this->redis->zRangeByLex('key', '-', '[c')); + $this->assertEquals(['f', 'g'], $this->redis->zRangeByLex('key', '(e', '+')); + // with limit offset - $this->assertEquals($this->redis->zRangeByLex('key', '-', '[c', 1, 2), ['b', 'c'] ); - $this->assertEquals($this->redis->zRangeByLex('key', '-', '(c', 1, 2), ['b']); + $this->assertEquals(['b', 'c'], $this->redis->zRangeByLex('key', '-', '[c', 1, 2) ); + $this->assertEquals(['b'], $this->redis->zRangeByLex('key', '-', '(c', 1, 2)); + + /* Test getting the same functionality via ZRANGE and options */ + if ($this->minVersionCheck("6.2.0")) { + $this->assertEquals(['a','b','c'], $this->redis->zRange('key', '-', '[c', ['BYLEX'])); + $this->assertEquals(['b', 'c'], $this->redis->zRange('key', '-', '[c', ['BYLEX', 'LIMIT' => [1, 2]])); + $this->assertEquals(['b'], $this->redis->zRange('key', '-', '(c', ['BYLEX', 'LIMIT' => [1, 2]])); + + $this->assertEquals(['b', 'a'], $this->redis->zRange('key', '[c', '-', ['BYLEX', 'REV', 'LIMIT' => [1, 2]])); + } } public function testZLexCount() { |