Permalink
Browse files

Merge pull request #60 from ferenyx/eval_pull

EVAL and EVALSHA support
  • Loading branch information...
2 parents b4b7993 + bea4dcd commit 4e117f814caa9e336b959fec0000f77d73abb449 Manju Rajashekhar committed Jan 30, 2013
Showing with 83 additions and 4 deletions.
  1. +3 −2 notes/redis.md
  2. +2 −0 src/nc_message.h
  3. +78 −2 src/proto/nc_redis.c
View
@@ -277,9 +277,9 @@
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
| Command | Supported? | Format |
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
- | EVAL | No | EVAL script numkeys key [key ...] arg [arg ...] |
+ | EVAL | Yes* | EVAL script numkeys key [key ...] arg [arg ...] |
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
- | EVALSHA | No | EVALSHA sha1 numkeys key [key ...] arg [arg ...] |
+ | EVALSHA | Yes* | EVALSHA sha1 numkeys key [key ...] arg [arg ...] |
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
| SCRIPT EXISTS | No | SCRIPT EXISTS script [script ...] |
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
@@ -289,6 +289,7 @@
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
| SCRIPT LOAD | No | SCRIPT LOAD script |
+-------------------+------------+---------------------------------------------------------------------------------------------------------------------+
+ *EVAL/EVALSHA support is limited to scripts that take at least 1 key. If multiple keys are used, all keys must hash to the same server (use the same hashtag).
### Connection
View
@@ -133,6 +133,8 @@ typedef enum msg_type {
MSG_REQ_REDIS_ZREVRANGEBYSCORE,
MSG_REQ_REDIS_ZREVRANK,
MSG_REQ_REDIS_ZSCORE,
+ MSG_REQ_REDIS_EVAL, /* redis requests - eval */
+ MSG_REQ_REDIS_EVALSHA,
MSG_RSP_REDIS_STATUS, /* redis response */
MSG_RSP_REDIS_ERROR,
MSG_RSP_REDIS_INTEGER,
View
@@ -217,6 +217,27 @@ redis_argx(struct msg *r)
}
/*
+ * Return true, if the redis command is either EVAL or EVALSHA. These commands
+ * have a special format with exactly 2 arguments, followed by one or more keys,
+ * followed by zero or more arguments (the documentation online seems to suggest
+ * that at least one argument is required, but that shouldn't be the case).
+ */
+static bool
+redis_argeval(struct msg *r)
+{
+ switch (r->type) {
+ case MSG_REQ_REDIS_EVAL:
+ case MSG_REQ_REDIS_EVALSHA:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/*
* Reference: http://redis.io/topics/protocol
*
* Redis >= 1.2 uses the unified protocol to send requests to the Redis
@@ -511,6 +532,11 @@ redis_parse_req(struct msg *r)
break;
}
+ if (str4icmp(m, 'e', 'v', 'a', 'l')) {
+ r->type = MSG_REQ_REDIS_EVAL;
+ break;
+ }
+
break;
case 5:
@@ -705,6 +731,11 @@ redis_parse_req(struct msg *r)
break;
}
+ if (str7icmp(m, 'e', 'v', 'a', 'l', 's', 'h', 'a')) {
+ r->type = MSG_REQ_REDIS_EVALSHA;
+ break;
+ }
+
break;
case 8:
@@ -826,7 +857,11 @@ redis_parse_req(struct msg *r)
case SW_REQ_TYPE_LF:
switch (ch) {
case LF:
- state = SW_KEY_LEN;
+ if (redis_argeval(r)) {
+ state = SW_ARG1_LEN;
+ } else {
+ state = SW_KEY_LEN;
+ }
break;
default:
@@ -942,6 +977,11 @@ redis_parse_req(struct msg *r)
goto done;
}
state = SW_FRAGMENT;
+ } else if (redis_argeval(r)) {
+ if (r->rnarg == 0) {
+ goto done;
+ }
+ state = SW_ARGN_LEN;
} else {
goto error;
}
@@ -1035,6 +1075,11 @@ redis_parse_req(struct msg *r)
goto done;
}
state = SW_ARGN_LEN;
+ } else if (redis_argeval(r)) {
+ if (r->rnarg < 2) {
+ goto error;
+ }
+ state = SW_ARG2_LEN;
} else {
goto error;
}
@@ -1082,6 +1127,10 @@ redis_parse_req(struct msg *r)
break;
case SW_ARG2:
+ if (r->token == NULL) {
+ r->token = p;
+ }
+
m = p + r->rlen;
if (m >= b->last) {
r->rlen -= (uint32_t)(b->last - p);
@@ -1096,6 +1145,28 @@ redis_parse_req(struct msg *r)
p = m; /* move forward by rlen bytes */
r->rlen = 0;
+
+ if (redis_argeval(r)) {
+ // For EVAL/EVALSHA, we need to find the integer value of this
+ // argument. It tells us the number of keys in the script, and
+ // we need to error out if number of keys is 0.
+ // At this point, both p and m point to the end of the argument
+ // and r->token points to the start.
+ if (p - r->token < 1)
+ goto error;
+ uint32_t nkeys = 0;
+ uint8_t *chp = r->token;
+ for (; chp < p; chp++) {
+ if (isdigit(*chp))
+ nkeys = nkeys * 10 + (uint32_t)(*chp - '0');
+ else
+ goto error;
+ }
+ if (nkeys == 0)
+ goto error;
+ }
+
+ r->token = NULL;
state = SW_ARG2_LF;
break;
@@ -1118,6 +1189,11 @@ redis_parse_req(struct msg *r)
goto done;
}
state = SW_ARGN_LEN;
+ } else if (redis_argeval(r)) {
+ if (r->rnarg < 1) {
+ goto error;
+ }
+ state = SW_KEY_LEN;
} else {
goto error;
}
@@ -1264,7 +1340,7 @@ redis_parse_req(struct msg *r)
case SW_ARGN_LF:
switch (ch) {
case LF:
- if (redis_argn(r)) {
+ if (redis_argn(r) || redis_argeval(r)) {
if (r->rnarg == 0) {
goto done;
}

0 comments on commit 4e117f8

Please sign in to comment.