diff options
author | michael-grunder <michael.grunder@gmail.com> | 2015-05-07 06:29:54 +0300 |
---|---|---|
committer | michael-grunder <michael.grunder@gmail.com> | 2015-05-07 06:29:54 +0300 |
commit | 91b5a37b8873632fed4bd0949a23cf77319125c5 (patch) | |
tree | 0a175ee315ce8001ea08f445526bf22a46d5998b | |
parent | 90a7cf48179e649e0f9f1010d28b52a33cf9cecf (diff) |
Implement rawCommand() properly, as it's not the COMMAND command in Redis.
-rw-r--r-- | php_redis.h | 2 | ||||
-rw-r--r-- | redis.c | 50 | ||||
-rw-r--r-- | redis_cluster.c | 111 | ||||
-rw-r--r-- | redis_cluster.h | 1 | ||||
-rw-r--r-- | redis_commands.c | 49 | ||||
-rw-r--r-- | redis_commands.h | 8 |
6 files changed, 184 insertions, 37 deletions
diff --git a/php_redis.h b/php_redis.h index 078d3ec5..aa621f18 100644 --- a/php_redis.h +++ b/php_redis.h @@ -197,6 +197,7 @@ PHP_METHOD(Redis, wait); PHP_METHOD(Redis, pubsub); PHP_METHOD(Redis, client); +PHP_METHOD(Redis, command); PHP_METHOD(Redis, rawcommand); /* SCAN and friends */ @@ -220,7 +221,6 @@ PHP_METHOD(Redis, isConnected); PHP_METHOD(Redis, getPersistentID); PHP_METHOD(Redis, getAuth); PHP_METHOD(Redis, getMode); -PHP_METHOD(Redis, rawcommand); #ifdef ZTS #include "TSRM.h" @@ -266,7 +266,8 @@ static zend_function_entry redis_functions[] = { PHP_ME(Redis, _unserialize, NULL, ZEND_ACC_PUBLIC) PHP_ME(Redis, client, NULL, ZEND_ACC_PUBLIC) - + PHP_ME(Redis, command, NULL, ZEND_ACC_PUBLIC) + /* SCAN and friends */ PHP_ME(Redis, scan, arginfo_scan, ZEND_ACC_PUBLIC) PHP_ME(Redis, hscan, arginfo_kscan, ZEND_ACC_PUBLIC) @@ -3684,11 +3685,50 @@ PHP_METHOD(Redis, client) { } } -/* proto array Redis::rawcommand() - * proto array Redis::rawcommand('info', string cmd) - * proto array Redis::rawcommand('getkeys', array cmd_args) */ +/* {{{ proto mixed Redis::rawcommand(string $command, [ $arg1 ... $argN]) */ PHP_METHOD(Redis, rawcommand) { - REDIS_PROCESS_CMD(rawcommand, redis_read_variant_reply); + int argc = ZEND_NUM_ARGS(), cmd_len; + char *cmd = NULL; + RedisSock *redis_sock; + zval **z_args; + + /* Sanity check on arguments */ + z_args = emalloc(argc * sizeof(zval*)); + if (argc < 1) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Must pass at least one command keyword"); + efree(z_args); + RETURN_FALSE; + } else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Internal PHP error parsing arguments"); + efree(z_args); + RETURN_FALSE; + } else if (redis_build_raw_cmd(z_args, argc, &cmd, &cmd_len TSRMLS_CC) < 0 || + redis_sock_get(getThis(), &redis_sock TSRMLS_CC, 0) < 0) + { + if (cmd) efree(cmd); + efree(z_args); + RETURN_FALSE; + } + + /* Clean up command array */ + efree(z_args); + + /* Execute our command */ + REDIS_PROCESS_REQUEST(redis_sock, cmd, cmd_len); + IF_ATOMIC() { + redis_read_variant_reply(INTERNAL_FUNCTION_PARAM_PASSTHRU,redis_sock,NULL,NULL); + } + REDIS_PROCESS_RESPONSE(redis_read_variant_reply); +} +/* }}} */ + +/* {{{ proto array Redis::command() + * proto array Redis::command('info', string cmd) + * proto array Redis::command('getkeys', array cmd_args) */ +PHP_METHOD(Redis, command) { + REDIS_PROCESS_CMD(command, redis_read_variant_reply); } /* }}} */ diff --git a/redis_cluster.c b/redis_cluster.c index de9dcf07..3019b40e 100644 --- a/redis_cluster.c +++ b/redis_cluster.c @@ -209,6 +209,7 @@ zend_function_entry redis_cluster_functions[] = { PHP_ME(RedisCluster, randomkey, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, ping, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, echo, NULL, ZEND_ACC_PUBLIC) + PHP_ME(RedisCluster, command, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, rawcommand, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, cluster, NULL, ZEND_ACC_PUBLIC) PHP_ME(RedisCluster, client, NULL, ZEND_ACC_PUBLIC) @@ -2199,27 +2200,8 @@ PHP_METHOD(RedisCluster, exec) { CLUSTER_RESET_MULTI(c); } -/* {{{ proto bool RedisCluster::discard() */ -PHP_METHOD(RedisCluster, discard) { - redisCluster *c = GET_CONTEXT(); - - if(CLUSTER_IS_ATOMIC(c)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); - RETURN_FALSE; - } - - if(cluster_abort_exec(c TSRMLS_CC)<0) { - CLUSTER_RESET_MULTI(c); - } - - CLUSTER_FREE_QUEUE(c); - - RETURN_TRUE; -} - /* Get a slot either by key (string) or host/port array */ -static short -cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) +static short cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) { int key_len, key_free; zval **z_host, **z_port, *z_tmp = NULL; @@ -2229,7 +2211,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) /* If it's a string, treat it as a key. Otherwise, look for a two * element array */ if(Z_TYPE_P(z_arg)==IS_STRING || Z_TYPE_P(z_arg)==IS_LONG || - Z_TYPE_P(z_arg)==IS_DOUBLE) + Z_TYPE_P(z_arg)==IS_DOUBLE) { /* Allow for any scalar here */ if (Z_TYPE_P(z_arg) != IS_STRING) { @@ -2253,7 +2235,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) zval_dtor(z_tmp); efree(z_tmp); } - } else if (Z_TYPE_P(z_arg) == IS_ARRAY && + } else if (Z_TYPE_P(z_arg) == IS_ARRAY && zend_hash_index_find(Z_ARRVAL_P(z_arg),0,(void**)&z_host)!=FAILURE && zend_hash_index_find(Z_ARRVAL_P(z_arg),1,(void**)&z_port)!=FAILURE && Z_TYPE_PP(z_host)==IS_STRING && Z_TYPE_PP(z_port)==IS_LONG) @@ -2263,7 +2245,7 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) (unsigned short)Z_LVAL_PP(z_port)); /* Inform the caller if they've passed bad data */ - if(slot < 0) { + if(slot < 0) { php_error_docref(0 TSRMLS_CC, E_WARNING, "Unknown node %s:%ld", Z_STRVAL_PP(z_host), Z_LVAL_PP(z_port)); } @@ -2276,6 +2258,24 @@ cluster_cmd_get_slot(redisCluster *c, zval *z_arg TSRMLS_DC) return slot; } +/* {{{ proto bool RedisCluster::discard() */ +PHP_METHOD(RedisCluster, discard) { + redisCluster *c = GET_CONTEXT(); + + if(CLUSTER_IS_ATOMIC(c)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cluster is not in MULTI mode"); + RETURN_FALSE; + } + + if(cluster_abort_exec(c TSRMLS_CC)<0) { + CLUSTER_RESET_MULTI(c); + } + + CLUSTER_FREE_QUEUE(c); + + RETURN_TRUE; +} + /* Generic handler for things we want directed at a given node, like SAVE, * BGSAVE, FLUSHDB, FLUSHALL, etc */ static void @@ -2856,7 +2856,7 @@ PHP_METHOD(RedisCluster, echo) { rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_BULK : TYPE_LINE; if(cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { zend_throw_exception(redis_cluster_exception_ce, - "Unable to send commnad at the specificed node", 0 TSRMLS_CC); + "Unable to send command at the specificed node", 0 TSRMLS_CC); efree(cmd); RETURN_FALSE; } @@ -2873,11 +2873,66 @@ PHP_METHOD(RedisCluster, echo) { } /* }}} */ -/* {{{ proto array RedisCluster::rawcommand() - * proto array RedisCluster::rawcommand('INFO', string cmd) - * proto array RedisCluster::rawcommand('GETKEYS', array cmd_args) */ +/* {{{ proto mixed RedisCluster::rawcommand(string $key, string $cmd, [ $argv1 .. $argvN]) + * proto mixed RedisCluster::rawcommand(array $host_port, string $cmd, [ $argv1 .. $argvN]) */ PHP_METHOD(RedisCluster, rawcommand) { - CLUSTER_PROCESS_CMD(rawcommand, cluster_variant_resp, 0); + REDIS_REPLY_TYPE rtype; + int argc = ZEND_NUM_ARGS(), cmd_len; + redisCluster *c = GET_CONTEXT(); + char *cmd = NULL; + zval **z_args; + short slot; + + /* Sanity check on our arguments */ + z_args = emalloc(argc * sizeof(zval*)); + if (argc < 2) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "You must pass at least node information as well as at least a command."); + efree(z_args); + RETURN_FALSE; + } else if (zend_get_parameters_array(ht, argc, z_args) == FAILURE) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Internal PHP error parsing method parameters."); + efree(z_args); + RETURN_FALSE; + } else if (redis_build_raw_cmd(z_args+1, argc-1, &cmd, &cmd_len TSRMLS_CC) || + (slot = cluster_cmd_get_slot(c, z_args[0] TSRMLS_CC))<0) + { + if (cmd) efree(cmd); + efree(z_args); + RETURN_FALSE; + } + + /* Free argument array */ + efree(z_args); + + /* Direct the command */ + rtype = CLUSTER_IS_ATOMIC(c) ? TYPE_EOF : TYPE_LINE; + if (cluster_send_slot(c,slot,cmd,cmd_len,rtype TSRMLS_CC)<0) { + zend_throw_exception(redis_cluster_exception_ce, + "Unable to send command to the specified node", 0 TSRMLS_CC); + efree(cmd); + efree(z_args); + RETURN_FALSE; + } + + /* Process variant response */ + if (CLUSTER_IS_ATOMIC(c)) { + cluster_variant_resp(INTERNAL_FUNCTION_PARAM_PASSTHRU, c, NULL); + } else { + void *ctx = NULL; + CLUSTER_ENQUEUE_RESPONSE(c, slot, cluster_variant_resp, ctx); + } + + efree(cmd); +} +/* }}} */ + +/* {{{ proto array RedisCluster::command() + * proto array RedisCluster::command('INFO', string cmd) + * proto array RedisCluster::command('GETKEYS', array cmd_args) */ +PHP_METHOD(RedisCluster, command) { + CLUSTER_PROCESS_CMD(command, cluster_variant_resp, 0); } /* }}} */ diff --git a/redis_cluster.h b/redis_cluster.h index a5a0758a..3659d00e 100644 --- a/redis_cluster.h +++ b/redis_cluster.h @@ -237,6 +237,7 @@ PHP_METHOD(RedisCluster, config); PHP_METHOD(RedisCluster, pubsub); PHP_METHOD(RedisCluster, script); PHP_METHOD(RedisCluster, slowlog); +PHP_METHOD(RedisCluster, command); /* SCAN and friends */ PHP_METHOD(RedisCluster, scan); diff --git a/redis_commands.c b/redis_commands.c index 03da0115..8d951dda 100644 --- a/redis_commands.c +++ b/redis_commands.c @@ -34,6 +34,51 @@ int redis_empty_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, return SUCCESS; } +/* Helper to construct a raw command. Given that the cluster and non cluster + * versions are different (RedisCluster needs an additional argument to direct + * the command) we take the start of our array and count */ +int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC) +{ + smart_str cmdstr = {0}; + int i; + + /* Make sure our first argument is a string */ + if (Z_TYPE_P(z_args[0]) != IS_STRING) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "When sending a 'raw' command, the first argument must be a string!"); + return FAILURE; + } + + /* Initialize our command string */ + redis_cmd_init_sstr(&cmdstr, argc-1, Z_STRVAL_P(z_args[0]), Z_STRLEN_P(z_args[0])); + + for (i = 1; i < argc; i++) { + switch (Z_TYPE_P(z_args[i])) { + case IS_STRING: + redis_cmd_append_sstr(&cmdstr, Z_STRVAL_P(z_args[i]), + Z_STRLEN_P(z_args[i])); + break; + case IS_LONG: + redis_cmd_append_sstr_long(&cmdstr,Z_LVAL_P(z_args[i])); + break; + case IS_DOUBLE: + redis_cmd_append_sstr_dbl(&cmdstr,Z_DVAL_P(z_args[i])); + break; + default: + php_error_docref(NULL TSRMLS_CC, E_WARNING, + "Raw command arguments must be scalar values!"); + efree(cmdstr.c); + return FAILURE; + } + } + + /* Push command and length to caller */ + *cmd = cmdstr.c; + *cmd_len = cmdstr.len; + + return SUCCESS; +} + /* Generic command where we just take a string and do nothing to it*/ int redis_str_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char *kw, char **cmd, int *cmd_len, short *slot, void **ctx) @@ -2699,8 +2744,8 @@ int redis_sdiffstore_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, } /* COMMAND */ -int redis_rawcommand_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) { char *kw=NULL; zval *z_arg; diff --git a/redis_commands.h b/redis_commands.h index 1e456b59..053fd65a 100644 --- a/redis_commands.h +++ b/redis_commands.h @@ -21,6 +21,9 @@ typedef struct subscribeContext { zend_fcall_info_cache cb_cache; } subscribeContext; +/* Construct a raw command */ +int redis_build_raw_cmd(zval **z_args, int argc, char **cmd, int *cmd_len TSRMLS_DC); + /* Redis command generics. Many commands share common prototypes meaning that * we can write one function to handle all of them. For example, there are * many COMMAND key value commands, or COMMAND key commands. */ @@ -212,9 +215,12 @@ 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_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, +int redis_command_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, char **cmd, int *cmd_len, short *slot, void **ctx); +int redis_rawcommand_cmd(INTERNAL_FUNCTION_PARAMETERS, RedisSock *redis_sock, + char **cmd, int *cmd_len, short *slot, void **ctx); + int redis_fmt_scan_cmd(char **cmd, REDIS_SCAN_TYPE type, char *key, int key_len, long it, char *pat, int pat_len, long count); |