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>2018-01-17 20:36:38 +0300
committermichael-grunder <michael.grunder@gmail.com>2018-01-17 20:36:38 +0300
commit9e65c429318d93bd37c9d42abf79709395e6e805 (patch)
tree379dc0f44cb4b7219eab0c4c31184f734f1d881d
parent837dee471ccac86581d306594ee945e82027572f (diff)
Implement UNLINK command
This commit implements UNLINK for Redis, RedisCluster, and RedisArray. To a client library UNLINK behaves identically to DEL so we can use the same handlers for both.
-rw-r--r--php_redis.h1
-rw-r--r--redis.c9
-rw-r--r--redis_array.c33
-rw-r--r--redis_array.h1
-rw-r--r--redis_cluster.c24
-rw-r--r--redis_cluster.h1
-rw-r--r--redis_commands.c8
-rw-r--r--redis_commands.h3
-rw-r--r--tests/RedisArrayTest.php55
-rw-r--r--tests/RedisTest.php65
10 files changed, 154 insertions, 46 deletions
diff --git a/php_redis.h b/php_redis.h
index 895ad710..d1989cfc 100644
--- a/php_redis.h
+++ b/php_redis.h
@@ -46,6 +46,7 @@ PHP_METHOD(Redis, renameNx);
PHP_METHOD(Redis, getMultiple);
PHP_METHOD(Redis, exists);
PHP_METHOD(Redis, delete);
+PHP_METHOD(Redis, unlink);
PHP_METHOD(Redis, incr);
PHP_METHOD(Redis, incrBy);
PHP_METHOD(Redis, incrByFloat);
diff --git a/redis.c b/redis.c
index c226a48d..15f32f6d 100644
--- a/redis.c
+++ b/redis.c
@@ -390,6 +390,7 @@ static zend_function_entry redis_functions[] = {
PHP_ME(Redis, time, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(Redis, ttl, arginfo_key, ZEND_ACC_PUBLIC)
PHP_ME(Redis, type, arginfo_key, ZEND_ACC_PUBLIC)
+ PHP_ME(Redis, unlink, arginfo_nkeys, ZEND_ACC_PUBLIC)
PHP_ME(Redis, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
PHP_ME(Redis, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(Redis, wait, arginfo_wait, ZEND_ACC_PUBLIC)
@@ -1176,6 +1177,13 @@ PHP_METHOD(Redis, delete)
}
/* }}} */
+/* {{{ proto long Redis::unlink(string $key1, string $key2 [, string $key3...]) }}}
+ * {{{ proto long Redis::unlink(array $keys) */
+PHP_METHOD(Redis, unlink)
+
+ REDIS_PROCESS_CMD(unlink, redis_long_response);
+}
+
PHP_REDIS_API void redis_set_watch(RedisSock *redis_sock)
{
redis_sock->watching = 1;
@@ -1512,6 +1520,7 @@ PHP_METHOD(Redis, sDiffStore) {
}
/* }}} */
+
/* {{{ proto array Redis::sort(string key, array options) */
PHP_METHOD(Redis, sort) {
char *cmd;
diff --git a/redis_array.c b/redis_array.c
index 01a48e5f..4d70b50a 100644
--- a/redis_array.c
+++ b/redis_array.c
@@ -122,6 +122,7 @@ zend_function_entry redis_array_functions[] = {
PHP_ME(RedisArray, save, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(RedisArray, select, arginfo_select, ZEND_ACC_PUBLIC)
PHP_ME(RedisArray, setOption,arginfo_setopt, ZEND_ACC_PUBLIC)
+ PHP_ME(RedisArray, unlink, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(RedisArray, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
PHP_MALIAS(RedisArray, delete, del, arginfo_del, ZEND_ACC_PUBLIC)
PHP_MALIAS(RedisArray, getMultiple, mget, arginfo_mget, ZEND_ACC_PUBLIC)
@@ -849,7 +850,7 @@ PHP_METHOD(RedisArray, select)
zval_dtor(&z_fun);
}
#if (PHP_MAJOR_VERSION < 7)
-#define HANDLE_MULTI_EXEC(ra, cmd) do { \
+#define HANDLE_MULTI_EXEC(ra, cmd, cmdlen) do { \
if (ra && ra->z_multi_exec) { \
int i, num_varargs;\
zval ***varargs = NULL, *z_arg_array; \
@@ -867,7 +868,7 @@ PHP_METHOD(RedisArray, select)
add_next_index_zval(z_arg_array, z_tmp); \
}\
/* call */\
- ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, z_arg_array, NULL); \
+ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, z_arg_array, NULL); \
zval_ptr_dtor(&z_arg_array); \
if(varargs) {\
efree(varargs);\
@@ -892,7 +893,7 @@ PHP_METHOD(RedisArray, select)
add_next_index_zval(&z_arg_array, &z_tmp); \
} \
/* call */\
- ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, sizeof(cmd) - 1, &z_arg_array, NULL); \
+ ra_forward_call(INTERNAL_FUNCTION_PARAM_PASSTHRU, ra, cmd, cmdlen, &z_arg_array, NULL); \
zval_dtor(&z_arg_array); \
return; \
} \
@@ -914,7 +915,7 @@ PHP_METHOD(RedisArray, mget)
}
/* Multi/exec support */
- HANDLE_MULTI_EXEC(ra, "MGET");
+ HANDLE_MULTI_EXEC(ra, "MGET", sizeof("MGET") - 1);
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
&object, redis_array_ce, &z_keys) == FAILURE) {
@@ -1068,7 +1069,7 @@ PHP_METHOD(RedisArray, mset)
}
/* Multi/exec support */
- HANDLE_MULTI_EXEC(ra, "MSET");
+ HANDLE_MULTI_EXEC(ra, "MSET", sizeof("MSET") - 1);
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa",
&object, redis_array_ce, &z_keys) == FAILURE)
@@ -1179,9 +1180,8 @@ PHP_METHOD(RedisArray, mset)
RETURN_TRUE;
}
-/* DEL will distribute the call to several nodes and regroup the values. */
-PHP_METHOD(RedisArray, del)
-{
+/* Generic handler for DEL or UNLINK which behave identically to phpredis */
+static void ra_generic_del(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len) {
zval *object, z_keys, z_fun, *data, z_ret, *z_tmp, *z_args;
int i, n;
RedisArray *ra;
@@ -1194,8 +1194,9 @@ PHP_METHOD(RedisArray, del)
if ((ra = redis_array_get(getThis() TSRMLS_CC)) == NULL) {
RETURN_FALSE;
}
+
/* Multi/exec support */
- HANDLE_MULTI_EXEC(ra, "DEL");
+ HANDLE_MULTI_EXEC(ra, kw, kw_len);
/* get all args in z_args */
z_args = emalloc(argc * sizeof(zval));
@@ -1259,7 +1260,7 @@ PHP_METHOD(RedisArray, del)
} ZEND_HASH_FOREACH_END();
/* prepare call */
- ZVAL_STRINGL(&z_fun, "DEL", 3);
+ ZVAL_STRINGL(&z_fun, kw, kw_len);
/* calls */
for(n = 0; n < ra->count; ++n) { /* for each node */
@@ -1285,7 +1286,7 @@ PHP_METHOD(RedisArray, del)
found++;
}
- if(!found) { /* don't run empty DELs */
+ if(!found) { /* don't run empty DEL or UNLINK commands */
zval_dtor(&z_argarray);
continue;
}
@@ -1322,6 +1323,16 @@ PHP_METHOD(RedisArray, del)
RETURN_LONG(total);
}
+/* DEL will distribute the call to several nodes and regroup the values. */
+PHP_METHOD(RedisArray, del)
+{
+ ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL")-1);
+}
+
+PHP_METHOD(RedisArray, unlink) {
+ ra_generic_del(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
+}
+
PHP_METHOD(RedisArray, multi)
{
zval *object;
diff --git a/redis_array.h b/redis_array.h
index d6a00fa0..7bacef41 100644
--- a/redis_array.h
+++ b/redis_array.h
@@ -25,6 +25,7 @@ PHP_METHOD(RedisArray, flushall);
PHP_METHOD(RedisArray, mget);
PHP_METHOD(RedisArray, mset);
PHP_METHOD(RedisArray, del);
+PHP_METHOD(RedisArray, unlink);
PHP_METHOD(RedisArray, keys);
PHP_METHOD(RedisArray, getOption);
PHP_METHOD(RedisArray, setOption);
diff --git a/redis_cluster.c b/redis_cluster.c
index 95bcba40..ede16b94 100644
--- a/redis_cluster.c
+++ b/redis_cluster.c
@@ -244,6 +244,7 @@ zend_function_entry redis_cluster_functions[] = {
PHP_ME(RedisCluster, ttl, arginfo_key, ZEND_ACC_PUBLIC)
PHP_ME(RedisCluster, type, arginfo_key, ZEND_ACC_PUBLIC)
PHP_ME(RedisCluster, unsubscribe, arginfo_unsubscribe, ZEND_ACC_PUBLIC)
+ PHP_ME(RedisCluster, unlink, arginfo_del, ZEND_ACC_PUBLIC)
PHP_ME(RedisCluster, unwatch, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(RedisCluster, watch, arginfo_watch, ZEND_ACC_PUBLIC)
PHP_ME(RedisCluster, zadd, arginfo_zadd, ZEND_ACC_PUBLIC)
@@ -708,7 +709,8 @@ static HashTable *method_args_to_ht(zval *z_args, int argc) {
return ht_ret;
}
-/* Handler for both MGET and DEL */
+/* Convienience handler for commands that take multiple keys such as
+ * MGET, DEL, and UNLINK */
static int cluster_mkey_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
zval *z_ret, cluster_cb cb)
{
@@ -935,8 +937,10 @@ static int cluster_mset_cmd(INTERNAL_FUNCTION_PARAMETERS, char *kw, int kw_len,
return 0;
}
-/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */
-PHP_METHOD(RedisCluster, del) {
+/* Generic passthru for DEL and UNLINK which act identically */
+static void cluster_generic_delete(INTERNAL_FUNCTION_PARAMETERS,
+ char *kw, int kw_len)
+{
zval *z_ret;
#if (PHP_MAJOR_VERSION < 7)
@@ -949,14 +953,24 @@ PHP_METHOD(RedisCluster, del) {
ZVAL_LONG(z_ret, 0);
// Parse args, process
- if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL",
- sizeof("DEL")-1, z_ret, cluster_del_resp)<0)
+ if(cluster_mkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, kw, kw_len, z_ret,
+ cluster_del_resp) < 0)
{
efree(z_ret);
RETURN_FALSE;
}
}
+/* {{{ proto array RedisCluster::del(string key1, string key2, ... keyN) */
+PHP_METHOD(RedisCluster, del) {
+ cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "DEL", sizeof("DEL") - 1);
+}
+
+/* {{{ proto array RedisCluster::unlink(string key1, string key2, ... keyN) */
+PHP_METHOD(RedisCluster, unlink) {
+ cluster_generic_delete(INTERNAL_FUNCTION_PARAM_PASSTHRU, "UNLINK", sizeof("UNLINK") - 1);
+}
+
/* {{{ proto array RedisCluster::mget(array keys) */
PHP_METHOD(RedisCluster, mget) {
zval *z_ret;
diff --git a/redis_cluster.h b/redis_cluster.h
index 5ecd8204..74903e67 100644
--- a/redis_cluster.h
+++ b/redis_cluster.h
@@ -130,6 +130,7 @@ PHP_METHOD(RedisCluster, mset);
PHP_METHOD(RedisCluster, msetnx);
PHP_METHOD(RedisCluster, mset);
PHP_METHOD(RedisCluster, del);
+PHP_METHOD(RedisCluster, unlink);
PHP_METHOD(RedisCluster, dump);
PHP_METHOD(RedisCluster, setex);
PHP_METHOD(RedisCluster, psetex);
diff --git a/redis_commands.c b/redis_commands.c
index ac0ed30d..e2db37c6 100644
--- a/redis_commands.c
+++ b/redis_commands.c
@@ -2949,6 +2949,14 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
"SDIFFSTORE", sizeof("SDIFFSTORE")-1, 1, 0, cmd, cmd_len, slot);
}
+/* UNLINK */
+int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+ char **cmd, int *cmd_len, short *slot, void **ctx)
+{
+ return gen_varkey_cmd(INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock,
+ "UNLINK", sizeof("UNLINK")-1, 1, 0, cmd, cmd_len, slot);
+}
+
/* COMMAND */
int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx)
diff --git a/redis_commands.h b/redis_commands.h
index 51d0b9f1..b3175364 100644
--- a/redis_commands.h
+++ b/redis_commands.h
@@ -236,6 +236,9 @@ int redis_sdiff_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx);
+int redis_unlink_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
+ char **cmd, int *cmd_len, short *slot, void **ctx);
+
int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock,
char **cmd, int *cmd_len, short *slot, void **ctx);
diff --git a/tests/RedisArrayTest.php b/tests/RedisArrayTest.php
index 0233b6d2..01e1c2f2 100644
--- a/tests/RedisArrayTest.php
+++ b/tests/RedisArrayTest.php
@@ -18,8 +18,30 @@ function parseHostPort($str, &$host, &$port) {
$port = substr($str, $pos+1);
}
+function getRedisVersion($obj_r) {
+ $arr_info = $obj_r->info();
+ if (!$arr_info || !isset($arr_info['redis_version'])) {
+ return "0.0.0";
+ }
+ return $arr_info['redis_version'];
+}
+
+/* Determine the lowest redis version attached to this RedisArray object */
+function getMinVersion($obj_ra) {
+ $min_version = "0.0.0";
+ foreach ($obj_ra->_hosts() as $host) {
+ $version = getRedisVersion($obj_ra->_instance($host));
+ if (version_compare($version, $min_version) > 0) {
+ $min_version = $version;
+ }
+ }
+
+ return $min_version;
+}
+
class Redis_Array_Test extends TestSuite
{
+ private $min_version;
private $strings;
public $ra = NULL;
private $data = NULL;
@@ -34,6 +56,7 @@ class Redis_Array_Test extends TestSuite
global $newRing, $oldRing, $useIndex;
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
+ $this->min_version = getMinVersion($this->ra);
}
public function testMSet() {
@@ -141,6 +164,8 @@ class Redis_Rehashing_Test extends TestSuite
public $ra = NULL;
private $useIndex;
+ private $min_version;
+
// data
private $strings;
private $sets;
@@ -185,6 +210,7 @@ class Redis_Rehashing_Test extends TestSuite
// create array
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
+ $this->min_version = getMinVersion($this->ra);
}
public function testFlush() {
@@ -206,12 +232,12 @@ class Redis_Rehashing_Test extends TestSuite
foreach($this->strings as $k => $v) {
$this->ra->set($k, $v);
}
-
+
// sets
foreach($this->sets as $k => $v) {
call_user_func_array(array($this->ra, 'sadd'), array_merge(array($k), $v));
}
-
+
// lists
foreach($this->lists as $k => $v) {
call_user_func_array(array($this->ra, 'rpush'), array_merge(array($k), $v));
@@ -221,7 +247,7 @@ class Redis_Rehashing_Test extends TestSuite
foreach($this->hashes as $k => $v) {
$this->ra->hmset($k, $v);
}
-
+
// sorted sets
foreach($this->zsets as $k => $v) {
call_user_func_array(array($this->ra, 'zadd'), array_merge(array($k), $v));
@@ -314,6 +340,7 @@ class Redis_Rehashing_Test extends TestSuite
class Redis_Auto_Rehashing_Test extends TestSuite {
public $ra = NULL;
+ private $min_version;
// data
private $strings;
@@ -330,6 +357,7 @@ class Redis_Auto_Rehashing_Test extends TestSuite {
// create array
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'autorehash' => TRUE));
+ $this->min_version = getMinVersion($this->ra);
}
public function testDistribute() {
@@ -378,11 +406,14 @@ class Redis_Auto_Rehashing_Test extends TestSuite {
// Test node-specific multi/exec
class Redis_Multi_Exec_Test extends TestSuite {
public $ra = NULL;
+ private $min_version;
public function setUp() {
global $newRing, $oldRing, $useIndex;
+
// create array
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex));
+ $this->min_version = getMinVersion($this->ra);
}
public function testInit() {
@@ -464,6 +495,22 @@ class Redis_Multi_Exec_Test extends TestSuite {
$this->assertEquals(0, $this->ra->exists('1_{employee:joe}_salary'));
}
+ public function testMutliExecUnlink() {
+ if (version_compare($this->min_version, "4.0.0", "lt")) {
+ var_dump($this->min_version);
+ $this->markTestSkipped();
+ }
+
+ $this->ra->set('{unlink}:key1', 'bar');
+ $this->ra->set('{unlink}:key2', 'bar');
+
+ $out = $this->ra->multi($this->ra->_target('{unlink}'))
+ ->del('{unlink}:key1', '{unlink}:key2')
+ ->exec();
+
+ $this->assertTrue($out[0] === 2);
+ }
+
public function testDiscard() {
/* phpredis issue #87 */
$key = 'test_err';
@@ -502,11 +549,13 @@ class Redis_Multi_Exec_Test extends TestSuite {
class Redis_Distributor_Test extends TestSuite {
public $ra = NULL;
+ private $min_version;
public function setUp() {
global $newRing, $oldRing, $useIndex;
// create array
$this->ra = new RedisArray($newRing, array('previous' => $oldRing, 'index' => $useIndex, 'distributor' => array($this, 'distribute')));
+ $this->min_version = getMinVersion($this->ra);
}
public function testInit() {
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index f2ec3015..b7a0ba2b 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -456,7 +456,7 @@ class Redis_Test extends TestSuite
$this->assertTrue($this->redis->ttl('key') ===7);
$this->assertTrue($this->redis->get('key') === 'val');
}
-
+
public function testPSetEx() {
$this->redis->del('key');
$this->assertTrue($this->redis->psetex('key', 7 * 1000, 'val') === TRUE);
@@ -626,38 +626,49 @@ class Redis_Test extends TestSuite
$this->assertEquals(array(), $this->redis->keys(rand().rand().rand().'*'));
}
- public function testDelete()
- {
+ protected function genericDelUnlink($cmd) {
$key = 'key' . rand();
$this->redis->set($key, 'val');
$this->assertEquals('val', $this->redis->get($key));
- $this->assertEquals(1, $this->redis->del($key));
+ $this->assertEquals(1, $this->redis->$cmd($key));
$this->assertEquals(false, $this->redis->get($key));
- // multiple, all existing
- $this->redis->set('x', 0);
- $this->redis->set('y', 1);
- $this->redis->set('z', 2);
- $this->assertEquals(3, $this->redis->del('x', 'y', 'z'));
- $this->assertEquals(false, $this->redis->get('x'));
- $this->assertEquals(false, $this->redis->get('y'));
- $this->assertEquals(false, $this->redis->get('z'));
-
- // multiple, none existing
- $this->assertEquals(0, $this->redis->del('x', 'y', 'z'));
- $this->assertEquals(false, $this->redis->get('x'));
- $this->assertEquals(false, $this->redis->get('y'));
- $this->assertEquals(false, $this->redis->get('z'));
-
- // multiple, some existing
- $this->redis->set('y', 1);
- $this->assertEquals(1, $this->redis->del('x', 'y', 'z'));
- $this->assertEquals(false, $this->redis->get('y'));
-
- $this->redis->set('x', 0);
- $this->redis->set('y', 1);
- $this->assertEquals(2, $this->redis->del(array('x', 'y')));
+ // multiple, all existing
+ $this->redis->set('x', 0);
+ $this->redis->set('y', 1);
+ $this->redis->set('z', 2);
+ $this->assertEquals(3, $this->redis->$cmd('x', 'y', 'z'));
+ $this->assertEquals(false, $this->redis->get('x'));
+ $this->assertEquals(false, $this->redis->get('y'));
+ $this->assertEquals(false, $this->redis->get('z'));
+
+ // multiple, none existing
+ $this->assertEquals(0, $this->redis->$cmd('x', 'y', 'z'));
+ $this->assertEquals(false, $this->redis->get('x'));
+ $this->assertEquals(false, $this->redis->get('y'));
+ $this->assertEquals(false, $this->redis->get('z'));
+
+ // multiple, some existing
+ $this->redis->set('y', 1);
+ $this->assertEquals(1, $this->redis->$cmd('x', 'y', 'z'));
+ $this->assertEquals(false, $this->redis->get('y'));
+
+ $this->redis->set('x', 0);
+ $this->redis->set('y', 1);
+ $this->assertEquals(2, $this->redis->$cmd(array('x', 'y')));
+ }
+
+ public function testDelete() {
+ $this->genericDelUnlink("DEL");
+ }
+
+ public function testUnlink() {
+ if (version_compare($this->version, "4.0.0", "lt")) {
+ $this->markTestSkipped();
+ return;
+ }
+ $this->genericDelUnlink("UNLINK");
}
public function testType()