Implement COPY for RedisCluster

* Refactor `redis_copy_cmd` to use the new argument parsing macros.
* Add a handler in `RedisCluster`.

See #1894
This commit is contained in:
michael-grunder
2022-12-01 22:13:39 -08:00
committed by Michael Grunder
parent e222b85ecf
commit 40a2c254e2
7 changed files with 70 additions and 34 deletions

View File

@@ -3142,5 +3142,9 @@ PHP_METHOD(RedisCluster, command) {
CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0);
}
PHP_METHOD(RedisCluster, copy) {
CLUSTER_PROCESS_CMD(copy, cluster_1_resp, 0)
}
/* vim: set tabstop=4 softtabstop=4 expandtab shiftwidth=4: */

View File

@@ -227,6 +227,11 @@ class RedisCluster {
*/
public function dbsize(string|array $key_or_address): RedisCluster|int;
/**
* @see https://redis.io/commands/copy
*/
public function copy(string $src, string $dst, array $options = null): RedisCluster|bool;
/**
* @see Redis::decr()
*/

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 2225d10403eb94c52ad017310e783115b9ea869e */
* Stub hash: e6c2d8efa4150e1cb198470d8106e693661c1e4f */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 1)
@@ -165,6 +165,12 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_dbsize, 0
ZEND_ARG_TYPE_MASK(0, key_or_address, MAY_BE_STRING|MAY_BE_ARRAY, NULL)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_copy, 0, 2, RedisCluster, MAY_BE_BOOL)
ZEND_ARG_TYPE_INFO(0, src, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, dst, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 0, "null")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_RedisCluster_decr, 0, 1, RedisCluster, MAY_BE_LONG|MAY_BE_FALSE)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, by, IS_LONG, 0, "1")
@@ -1042,6 +1048,7 @@ ZEND_METHOD(RedisCluster, cluster);
ZEND_METHOD(RedisCluster, command);
ZEND_METHOD(RedisCluster, config);
ZEND_METHOD(RedisCluster, dbsize);
ZEND_METHOD(RedisCluster, copy);
ZEND_METHOD(RedisCluster, decr);
ZEND_METHOD(RedisCluster, decrby);
ZEND_METHOD(RedisCluster, decrbyfloat);
@@ -1260,6 +1267,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, copy, arginfo_class_RedisCluster_copy, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC)

View File

@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 2225d10403eb94c52ad017310e783115b9ea869e */
* Stub hash: e6c2d8efa4150e1cb198470d8106e693661c1e4f */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster___construct, 0, 0, 1)
ZEND_ARG_INFO(0, name)
@@ -151,6 +151,12 @@ ZEND_END_ARG_INFO()
#define arginfo_class_RedisCluster_dbsize arginfo_class_RedisCluster_bgrewriteaof
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_copy, 0, 0, 2)
ZEND_ARG_INFO(0, src)
ZEND_ARG_INFO(0, dst)
ZEND_ARG_INFO(0, options)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_RedisCluster_decr, 0, 0, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, by)
@@ -886,6 +892,7 @@ ZEND_METHOD(RedisCluster, cluster);
ZEND_METHOD(RedisCluster, command);
ZEND_METHOD(RedisCluster, config);
ZEND_METHOD(RedisCluster, dbsize);
ZEND_METHOD(RedisCluster, copy);
ZEND_METHOD(RedisCluster, decr);
ZEND_METHOD(RedisCluster, decrby);
ZEND_METHOD(RedisCluster, decrbyfloat);
@@ -1104,6 +1111,7 @@ static const zend_function_entry class_RedisCluster_methods[] = {
ZEND_ME(RedisCluster, command, arginfo_class_RedisCluster_command, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, config, arginfo_class_RedisCluster_config, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, dbsize, arginfo_class_RedisCluster_dbsize, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, copy, arginfo_class_RedisCluster_copy, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, decr, arginfo_class_RedisCluster_decr, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, decrby, arginfo_class_RedisCluster_decrby, ZEND_ACC_PUBLIC)
ZEND_ME(RedisCluster, decrbyfloat, arginfo_class_RedisCluster_decrbyfloat, ZEND_ACC_PUBLIC)

View File

@@ -5345,44 +5345,56 @@ int
redis_copy_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx)
{
zend_string *src = NULL, *dst = NULL;
smart_string cmdstr = {0};
char *src, *dst;
size_t src_len, dst_len;
zend_long db = -1;
HashTable *opts = NULL;
zend_bool replace = 0;
zval *opts = NULL, *z_ele;
zend_string *zkey;
zend_long db = -1;
short slot2;
zval *zv;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|a",
&src, &src_len, &dst, &dst_len, &opts) == FAILURE)
{
return FAILURE;
}
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_STR(src)
Z_PARAM_STR(dst)
Z_PARAM_OPTIONAL
Z_PARAM_ARRAY_HT_OR_NULL(opts)
ZEND_PARSE_PARAMETERS_END_EX(return FAILURE);
if (opts != NULL) {
ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(opts), zkey, z_ele) {
if (zkey != NULL) {
ZVAL_DEREF(z_ele);
if (zend_string_equals_literal_ci(zkey, "db")) {
db = zval_get_long(z_ele);
} else if (zend_string_equals_literal_ci(zkey, "replace")) {
replace = zval_is_true(z_ele);
}
ZEND_HASH_FOREACH_STR_KEY_VAL(opts, zkey, zv) {
if (zkey == NULL)
continue;
ZVAL_DEREF(zv);
if (zend_string_equals_literal_ci(zkey, "db")) {
db = zval_get_long(zv);
} else if (zend_string_equals_literal_ci(zkey, "replace")) {
replace = zval_is_true(zv);
}
} ZEND_HASH_FOREACH_END();
}
if (slot && db != -1) {
php_error_docref(NULL, E_WARNING, "Cant copy to a specific DB in cluster mode");
return FAILURE;
}
REDIS_CMD_INIT_SSTR_STATIC(&cmdstr, 2 + (db > -1 ? 2 : 0) + replace, "COPY");
redis_cmd_append_sstr(&cmdstr, src, src_len);
redis_cmd_append_sstr(&cmdstr, dst, dst_len);
redis_cmd_append_sstr_key_zstr(&cmdstr, src, redis_sock, slot);
redis_cmd_append_sstr_key_zstr(&cmdstr, dst, redis_sock, slot ? &slot2 : NULL);
if (slot && *slot != slot2) {
php_error_docref(NULL, E_WARNING, "Keys must hash to the same slot!");
efree(cmdstr.c);
return FAILURE;
}
if (db > -1) {
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "DB");
redis_cmd_append_sstr_long(&cmdstr, db);
}
if (replace) {
REDIS_CMD_APPEND_SSTR_STATIC(&cmdstr, "REPLACE");
}
REDIS_CMD_APPEND_SSTR_OPT_STATIC(&cmdstr, replace, "REPLACE");
*cmd = cmdstr.c;
*cmd_len = cmdstr.len;

View File

@@ -57,7 +57,6 @@ class Redis_Cluster_Test extends Redis_Test {
public function testzDiffStore() { return $this->markTestSkipped(); }
public function testzMscore() { return $this->marktestSkipped(); }
public function testZRandMember() { return $this->marktestSkipped(); }
public function testCopy() { return $this->marktestSkipped(); }
public function testConfig() { return $this->markTestSkipped(); }
public function testFlushDB() { return $this->markTestSkipped(); }

View File

@@ -7531,17 +7531,17 @@ class Redis_Test extends TestSuite
return;
}
$this->redis->del('key2');
$this->redis->set('key', 'foo');
$this->assertTrue($this->redis->copy('key', 'key2'));
$this->assertEquals('foo', $this->redis->get('key2'));
$this->redis->del('{key}dst');
$this->redis->set('{key}src', 'foo');
$this->assertTrue($this->redis->copy('{key}src', '{key}dst'));
$this->assertEquals('foo', $this->redis->get('{key}dst'));
$this->redis->set('key', 'bar');
$this->assertFalse($this->redis->copy('key', 'key2'));
$this->assertEquals('foo', $this->redis->get('key2'));
$this->redis->set('{key}src', 'bar');
$this->assertFalse($this->redis->copy('{key}src', '{key}dst'));
$this->assertEquals('foo', $this->redis->get('{key}dst'));
$this->assertTrue($this->redis->copy('key', 'key2', ['replace' => true]));
$this->assertEquals('bar', $this->redis->get('key2'));
$this->assertTrue($this->redis->copy('{key}src', '{key}dst', ['replace' => true]));
$this->assertEquals('bar', $this->redis->get('{key}dst'));
}
public function testCommand()