Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpredis/phpredis.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormichael-grunder <michael.grunder@gmail.com>2022-10-22 22:46:26 +0300
committerMichael Grunder <michael.grunder@gmail.com>2022-10-23 21:37:20 +0300
commit1343f5008301a256fcbfa0161931e2a39791055f (patch)
tree998e0199f979755ea09e2ab3209353211a4dee3e
parent71bcbcb973413ae933cf30d58254dfb02425c1ed (diff)
XGROUP DELCONSUMER and ENTRIESREAD
Refactor XGROUP and implement the new DELCONSUMER (Redis 6.2.0) and ENTRIESREAD (Redis 7.0.0) options. Additionally, add a proper phpdoc block to the stub file. See #2068
-rw-r--r--redis.stub.php39
-rw-r--r--redis_arginfo.h9
-rw-r--r--redis_commands.c90
-rw-r--r--redis_legacy_arginfo.h9
-rw-r--r--tests/RedisTest.php34
5 files changed, 137 insertions, 44 deletions
diff --git a/redis.stub.php b/redis.stub.php
index 51dd1fe3..3c3c78e5 100644
--- a/redis.stub.php
+++ b/redis.stub.php
@@ -660,7 +660,44 @@ class Redis {
public function xdel(string $key, array $ids): Redis|int|false;
- public function xgroup(string $operation, string $key = null, string $arg1 = null, string $arg2 = null, bool $arg3 = false): mixed;
+ /**
+ * XGROUP
+ *
+ * Perform various operation on consumer groups for a particular Redis STREAM.
+ * What the command does is primarily based on which operation is passed.
+ *
+ * @see https://redis.io/commands/xgroup/
+ *
+ * @param string $operation The subcommand you intend to execute. Valid options are as follows
+ * 'HELP' - Redis will return information about the command
+ * Requires: none
+ * 'CREATE' - Create a consumer group.
+ * Requires: Key, group, consumer.
+ * 'SETID' - Set the ID of an existing consumer group for the stream.
+ * Requires: Key, group, id.
+ * 'CREATECONSUMER - Create a new consumer group for the stream. You must
+ * also pass key, group, and the consumer name you wish to
+ * create.
+ * Requires: Key, group, consumer.
+ * 'DELCONSUMER' - Delete a consumer from group attached to the stream.
+ * Requires: Key, group, consumer.
+ * 'DESTROY' - Delete a consumer group from a stream.
+ * Requires: Key, group.
+ * @param string $key The STREAM we're operating on.
+ * @param string $group The consumer group wse want to create/modify/delete.
+ * @param string $id_or_consumer The STREAM id (e.g. '$') or consumer group. See the operation section
+ * for information about which to send.
+ * @param bool $mkstream This flag may be sent in combination with the 'CREATE' operation, and
+ * cause Redis to also create the STREAM if it doesn't currently exist.
+ *
+ * @param bool $entriesread Allows you to set Redis's 'entries-read' STREAM value. This argument is
+ * only relevant to the 'CREATE' and 'SETID' operations.
+ * Note: Requires Redis >= 7.0.0.
+ *
+ * @return mixed This command return various results depending on the operation performed.
+ */
+ public function xgroup(string $operation, string $key = null, string $group = null, string $id_or_consumer = null,
+ bool $mkstream = false, int $entries_read = -2): mixed;
public function xinfo(string $operation, ?string $arg1 = null, ?string $arg2 = null, int $count = -1): mixed;
diff --git a/redis_arginfo.h b/redis_arginfo.h
index 38b11ccf..7d350910 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: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */
+ * Stub hash: c04531e86379ab5c0de12e8e82868b7c7f024068 */
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")
@@ -909,9 +909,10 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xgroup, 0, 1, IS_MIXED, 0)
ZEND_ARG_TYPE_INFO(0, operation, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, key, IS_STRING, 0, "null")
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg1, IS_STRING, 0, "null")
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg2, IS_STRING, 0, "null")
- ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, arg3, _IS_BOOL, 0, "false")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, group, IS_STRING, 0, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, id_or_consumer, IS_STRING, 0, "null")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, mkstream, _IS_BOOL, 0, "false")
+ ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, entries_read, IS_LONG, 0, "-2")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Redis_xinfo, 0, 1, IS_MIXED, 0)
diff --git a/redis_commands.c b/redis_commands.c
index 08992c2c..416972d6 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -5817,52 +5817,72 @@ int redis_xclaim_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
}
/* XGROUP HELP
- * XGROUP CREATE key groupname id [MKSTREAM]
- * XGROUP SETID key group id
- * XGROUP DESTROY key groupname
- * XGROUP DELCONSUMER key groupname consumername */
+ * XGROUP CREATE key group id [MKSTREAM] [ENTRIESREAD <n>]
+ * XGROUP SETID key group id [ENTRIESREAD <n>]
+ * XGROUP CREATECONSUMER key group consumer
+ * XGROUP DELCONSUMER key group consumer
+ * XGROUP DESTROY key group
+ */
int redis_xgroup_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx)
{
- char *op, *key = NULL, *arg1 = NULL, *arg2 = NULL;
- size_t oplen, keylen, arg1len, arg2len;
+ zend_string *op = NULL, *key = NULL, *group = NULL, *id_or_consumer = NULL;
+ int nargs, is_create = 0, is_setid = 0;
+ zend_long entries_read = -2;
+ smart_string cmdstr = {0};
zend_bool mkstream = 0;
- int argc = ZEND_NUM_ARGS();
- if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|sssb", &op, &oplen,
- &key, &keylen, &arg1, &arg1len, &arg2, &arg2len,
- &mkstream) == FAILURE)
+ ZEND_PARSE_PARAMETERS_START(1, 6)
+ Z_PARAM_STR(op)
+ Z_PARAM_OPTIONAL
+ Z_PARAM_STR(key)
+ Z_PARAM_STR(group)
+ Z_PARAM_STR(id_or_consumer)
+ Z_PARAM_BOOL(mkstream)
+ Z_PARAM_LONG(entries_read)
+ ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
+
+ if (zend_string_equals_literal_ci(op, "HELP")) {
+ nargs = 0;
+ } else if ((is_create = zend_string_equals_literal_ci(op, "CREATE")) ||
+ (is_setid = zend_string_equals_literal_ci(op, "SETID")) ||
+ zend_string_equals_literal_ci(op, "CREATECONSUMER") ||
+ zend_string_equals_literal_ci(op, "DELCONSUMER"))
{
+ nargs = 3;
+ } else if (zend_string_equals_literal_ci(op, "DESTROY")) {
+ nargs = 2;
+ } else {
+ php_error_docref(NULL, E_WARNING, "Unknown XGROUP operation '%s'", ZSTR_VAL(op));
return FAILURE;
}
- if (argc == 1 && oplen == 4 && !strncasecmp(op, "HELP", 4)) {
- *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "s", "HELP", 4);
- return SUCCESS;
- } else if (argc >= 4 && (oplen == 6 && !strncasecmp(op, "CREATE", 6))) {
- if (mkstream) {
- *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sksss", op, oplen, key, keylen,
- arg1, arg1len, arg2, arg2len, "MKSTREAM",
- sizeof("MKSTREAM") - 1);
- } else {
- *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen,
- arg1, arg1len, arg2, arg2len);
- }
- return SUCCESS;
- } else if (argc == 4 && ((oplen == 5 && !strncasecmp(op, "SETID", 5)) ||
- (oplen == 11 && !strncasecmp(op, "DELCONSUMER", 11))))
- {
- *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "skss", op, oplen, key, keylen,
- arg1, arg1len, arg2, arg2len);
- return SUCCESS;
- } else if (argc == 3 && ((oplen == 7 && !strncasecmp(op, "DESTROY", 7)))) {
- *cmd_len = REDIS_CMD_SPPRINTF(cmd, "XGROUP", "sks", op, oplen, key,
- keylen, arg1, arg1len);
- return SUCCESS;
+ if (ZEND_NUM_ARGS() < nargs) {
+ php_error_docref(NULL, E_WARNING, "Operation '%s' requires %d arguments", ZSTR_VAL(op), nargs);
+ return FAILURE;
}
- /* Didn't detect any valid XGROUP command pattern */
- return FAILURE;
+ mkstream &= is_create;
+ if (!(is_create || is_setid))
+ entries_read = -2;
+
+ REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 1 + nargs + !!mkstream + (entries_read != -2 ? 2 : 0), "XGROUP");
+ redis_cmd_append_sstr_zstr(&cmdstr, op);
+
+ if (nargs-- > 0) redis_cmd_append_sstr_key_zstr(&cmdstr, key, redis_sock, slot);
+ if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, group);
+ if (nargs-- > 0) redis_cmd_append_sstr_zstr(&cmdstr, id_or_consumer);
+
+ REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, !!mkstream, "MKSTREAM");
+ if (entries_read != -2) {
+ REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "ENTRIESREAD");
+ redis_cmd_append_sstr_long(&cmdstr, entries_read);
+ }
+
+ *cmd = cmdstr.c;
+ *cmd_len = cmdstr.len;
+
+ return SUCCESS;
}
/* XINFO CONSUMERS key group
diff --git a/redis_legacy_arginfo.h b/redis_legacy_arginfo.h
index b5b39586..0b6fbd23 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: 73bbd79b67c155a90acfa2b5ca1be49effdaf8ba */
+ * Stub hash: c04531e86379ab5c0de12e8e82868b7c7f024068 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis___construct, 0, 0, 0)
ZEND_ARG_INFO(0, options)
@@ -769,9 +769,10 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xgroup, 0, 0, 1)
ZEND_ARG_INFO(0, operation)
ZEND_ARG_INFO(0, key)
- ZEND_ARG_INFO(0, arg1)
- ZEND_ARG_INFO(0, arg2)
- ZEND_ARG_INFO(0, arg3)
+ ZEND_ARG_INFO(0, group)
+ ZEND_ARG_INFO(0, id_or_consumer)
+ ZEND_ARG_INFO(0, mkstream)
+ ZEND_ARG_INFO(0, entries_read)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Redis_xinfo, 0, 0, 1)
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 149bbb84..ea036ebc 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -6521,6 +6521,40 @@ class Redis_Test extends TestSuite
$this->assertFalse($this->redis->xGroup('SETID', 's', 'mygroup', 'BAD_ID'));
$this->assertEquals($this->redis->xGroup('DELCONSUMER', 's', 'mygroup', 'myconsumer'),0);
+
+ if (!$this->minVersionCheck('6.2.0'))
+ return;
+
+ /* CREATECONSUMER */
+ $this->assertTrue($this->redis->del('s'));
+ $this->assertTrue($this->redis->xgroup('create', 's', 'mygroup', '$', true));
+ for ($i = 0; $i < 3; $i++) {
+ $this->assertTrue($this->redis->xgroup('createconsumer', 's', 'mygroup', "c:$i"));
+ $info = $this->redis->xinfo('consumers', 's', 'mygroup');
+ $this->assertTrue(is_array($info) && count($info) == $i + 1);
+ for ($j = 0; $j <= $i; $j++) {
+ $this->assertTrue(isset($info[$j]) && isset($info[$j]['name']) && $info[$j]['name'] == "c:$j");
+ }
+ }
+
+ /* Make sure we don't erroneously send options that don't belong to the operation */
+ $this->assertTrue($this->redis->xGroup('CREATECONSUMER', 's', 'mygroup', 'fake-consumer', true, 1337));
+
+ /* Make sure we handle the case where the user doesn't send enough arguments */
+ $this->redis->clearLastError();
+ $this->assertFalse(@$this->redis->xGroup('CREATECONSUMER'));
+ $this->assertEquals(NULL, $this->redis->getLastError());
+ $this->assertFalse(@$this->redis->xGroup('create'));
+ $this->assertEquals(NULL, $this->redis->getLastError());
+
+ if (!$this->minVersionCheck('7.0.0'))
+ return;
+
+ /* ENTRIESREAD */
+ $this->assertTrue($this->redis->del('s'));
+ $this->assertTrue($this->redis->xGroup('create', 's', 'mygroup', '$', true, 1337));
+ $info = $this->redis->xinfo('groups', 's');
+ $this->assertTrue(isset($info[0]['entries-read']) && 1337 == (int)$info[0]['entries-read']);
}
public function testXAck() {