Permalink
Browse files

Scripting: redis.NIL to return nil bulk replies.

Lua arrays can't contain nil elements (see
http://www.lua.org/pil/19.1.html for more information), so Lua scripts
were not able to return a multi-bulk reply containing nil bulk
elements inside.

This commit introduces a special conversion: a table with just
a "nilbulk" field set to a boolean value is converted by Redis as a nil
bulk reply, but at the same time for Lua this type is not a "nil" so can
be used inside Lua arrays.

This type is also assigned to redis.NIL, so the following two forms
are equivalent and will be able to return a nil bulk reply as second
element of a three elements array:

    EVAL "return {1,redis.NIL,3}" 0
    EVAL "return {1,{nilbulk=true},3}" 0

The result in redis-cli will be:

    1) (integer) 1
    2) (nil)
    3) (integer) 3
  • Loading branch information...
1 parent db100c4 commit 6dd1693c0e4bfae0ca0983b2a0bf70ae234a6bcb @antirez committed Sep 28, 2012
Showing with 39 additions and 4 deletions.
  1. +35 −4 src/scripting.c
  2. +4 −0 tests/unit/scripting.tcl
View
@@ -516,6 +516,14 @@ void scriptingInit(void) {
lua_pushcfunction(lua, luaRedisSha1hexCommand);
lua_settable(lua, -3);
+ /* redis.NIL */
+ lua_pushstring(lua, "NIL");
+ lua_newtable(lua);
+ lua_pushstring(lua, "nilbulk");
+ lua_pushboolean(lua, 1);
+ lua_settable(lua, -3);
+ lua_settable(lua, -3);
+
/* Finally set the table as 'redis' global var. */
lua_setglobal(lua,"redis");
@@ -610,9 +618,30 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
break;
case LUA_TTABLE:
- /* We need to check if it is an array, an error, or a status reply.
- * Error are returned as a single element table with 'err' field.
- * Status replies are returned as single elment table with 'ok' field */
+ /* The table can be an array or it may be in a special format that
+ * Lua uses to return special Redis protocol data types.
+ *
+ * 1) Errors are retuned as a single element table with 'err' field.
+ * 2) Status reply are returned as a single element table with 'ok'
+ * field.
+ * 3) A Redis nil bulk reply is returned as a single element table
+ * with 'nilbulk' field set to true.
+ *
+ * All the rest is considered just an array and is translated into
+ * a Redis multi bulk reply. */
+
+ /* Nil bulk reply */
+ lua_pushstring(lua,"nilbulk");
+ lua_gettable(lua,-2);
+ t = lua_type(lua,-1);
+ if (t == LUA_TBOOLEAN) {
+ addReply(c,shared.nullbulk);
+ lua_pop(lua,2);
+ return;
+ }
+ lua_pop(lua,1);
+
+ /* Error reply */
lua_pushstring(lua,"err");
lua_gettable(lua,-2);
t = lua_type(lua,-1);
@@ -624,8 +653,9 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
lua_pop(lua,2);
return;
}
-
lua_pop(lua,1);
+
+ /* Status reply */
lua_pushstring(lua,"ok");
lua_gettable(lua,-2);
t = lua_type(lua,-1);
@@ -636,6 +666,7 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
sdsfree(ok);
lua_pop(lua,1);
} else {
+ /* Multi bulk reply. */
void *replylen = addDeferredMultiBulkLength(c);
int j = 1, mbulklen = 0;
View
@@ -30,6 +30,10 @@ start_server {tags {"scripting"}} {
set _ $e
} {this is an error}
+ test {EVAL - Lua nil reply -> Redis protocol type conversion} {
+ r eval {return {1,redis.NIL,{nilbulk=true},4}} 0
+ } {1 {} {} 4}
+
test {EVAL - Lua table -> Redis protocol type conversion} {
r eval {return {1,2,3,'ciao',{1,2}}} 0
} {1 2 3 ciao {1 2}}

0 comments on commit 6dd1693

Please sign in to comment.