Skip to content
This repository
Browse code

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...
commit 6dd1693c0e4bfae0ca0983b2a0bf70ae234a6bcb 1 parent db100c4
Salvatore Sanfilippo authored September 28, 2012
39  src/scripting.c
@@ -516,6 +516,14 @@ void scriptingInit(void) {
516 516
     lua_pushcfunction(lua, luaRedisSha1hexCommand);
517 517
     lua_settable(lua, -3);
518 518
 
  519
+    /* redis.NIL */
  520
+    lua_pushstring(lua, "NIL");
  521
+    lua_newtable(lua);
  522
+    lua_pushstring(lua, "nilbulk");
  523
+    lua_pushboolean(lua, 1);
  524
+    lua_settable(lua, -3);
  525
+    lua_settable(lua, -3);
  526
+
519 527
     /* Finally set the table as 'redis' global var. */
520 528
     lua_setglobal(lua,"redis");
521 529
 
@@ -610,9 +618,30 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
610 618
         addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
611 619
         break;
612 620
     case LUA_TTABLE:
613  
-        /* We need to check if it is an array, an error, or a status reply.
614  
-         * Error are returned as a single element table with 'err' field.
615  
-         * Status replies are returned as single elment table with 'ok' field */
  621
+        /* The table can be an array or it may be in a special format that
  622
+         * Lua uses to return special Redis protocol data types.
  623
+         *
  624
+         * 1) Errors are retuned as a single element table with 'err' field.
  625
+         * 2) Status reply are returned as a single element table with 'ok'
  626
+         *    field.
  627
+         * 3) A Redis nil bulk reply is returned as a single element table
  628
+         *    with 'nilbulk' field set to true.
  629
+         *
  630
+         * All the rest is considered just an array and is translated into
  631
+         * a Redis multi bulk reply. */
  632
+
  633
+        /* Nil bulk reply */
  634
+        lua_pushstring(lua,"nilbulk");
  635
+        lua_gettable(lua,-2);
  636
+        t = lua_type(lua,-1);
  637
+        if (t == LUA_TBOOLEAN) {
  638
+            addReply(c,shared.nullbulk);
  639
+            lua_pop(lua,2);
  640
+            return;
  641
+        }
  642
+        lua_pop(lua,1);
  643
+
  644
+        /* Error reply */
616 645
         lua_pushstring(lua,"err");
617 646
         lua_gettable(lua,-2);
618 647
         t = lua_type(lua,-1);
@@ -624,8 +653,9 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
624 653
             lua_pop(lua,2);
625 654
             return;
626 655
         }
627  
-
628 656
         lua_pop(lua,1);
  657
+
  658
+        /* Status reply */
629 659
         lua_pushstring(lua,"ok");
630 660
         lua_gettable(lua,-2);
631 661
         t = lua_type(lua,-1);
@@ -636,6 +666,7 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
636 666
             sdsfree(ok);
637 667
             lua_pop(lua,1);
638 668
         } else {
  669
+            /* Multi bulk reply. */
639 670
             void *replylen = addDeferredMultiBulkLength(c);
640 671
             int j = 1, mbulklen = 0;
641 672
 
4  tests/unit/scripting.tcl
@@ -30,6 +30,10 @@ start_server {tags {"scripting"}} {
30 30
         set _ $e
31 31
     } {this is an error}
32 32
 
  33
+    test {EVAL - Lua nil reply -> Redis protocol type conversion} {
  34
+        r eval {return {1,redis.NIL,{nilbulk=true},4}} 0
  35
+    } {1 {} {} 4}
  36
+
33 37
     test {EVAL - Lua table -> Redis protocol type conversion} {
34 38
         r eval {return {1,2,3,'ciao',{1,2}}} 0
35 39
     } {1 2 3 ciao {1 2}}

0 notes on commit 6dd1693

Please sign in to comment.
Something went wrong with that request. Please try again.