]>
git.saurik.com Git - redis.git/blob - src/scripting.c
ad00123a90ce5aeb8058a9b170ec1874c63a5c15
   8 char *redisProtocolToLuaType_Int(lua_State 
*lua
, char *reply
); 
   9 char *redisProtocolToLuaType_Bulk(lua_State 
*lua
, char *reply
); 
  10 char *redisProtocolToLuaType_Status(lua_State 
*lua
, char *reply
); 
  11 char *redisProtocolToLuaType_Error(lua_State 
*lua
, char *reply
); 
  12 char *redisProtocolToLuaType_MultiBulk(lua_State 
*lua
, char *reply
); 
  14 /* Take a Redis reply in the Redis protocol format and convert it into a 
  15  * Lua type. Thanks to this function, and the introduction of not connected 
  16  * clients, it is trvial to implement the redis() lua function. 
  18  * Basically we take the arguments, execute the Redis command in the context 
  19  * of a non connected client, then take the generated reply and convert it 
  20  * into a suitable Lua type. With this trick the scripting feature does not 
  21  * need the introduction of a full Redis internals API. Basically the script 
  22  * is like a normal client that bypasses all the slow I/O paths. 
  24  * Note: in this function we do not do any sanity check as the reply is 
  25  * generated by Redis directly. This allows use to go faster. 
  26  * The reply string can be altered during the parsing as it is discared 
  27  * after the conversion is completed. 
  29  * Errors are returned as a table with a single 'err' field set to the 
  33 char *redisProtocolToLuaType(lua_State 
*lua
, char* reply
) { 
  38         p 
= redisProtocolToLuaType_Int(lua
,reply
); 
  41         p 
= redisProtocolToLuaType_Bulk(lua
,reply
); 
  44         p 
= redisProtocolToLuaType_Status(lua
,reply
); 
  47         p 
= redisProtocolToLuaType_Error(lua
,reply
); 
  50         p 
= redisProtocolToLuaType_MultiBulk(lua
,reply
); 
  56 char *redisProtocolToLuaType_Int(lua_State 
*lua
, char *reply
) { 
  57     char *p 
= strchr(reply
+1,'\r'); 
  60     string2ll(reply
+1,p
-reply
-1,&value
); 
  61     lua_pushnumber(lua
,(lua_Number
)value
); 
  65 char *redisProtocolToLuaType_Bulk(lua_State 
*lua
, char *reply
) { 
  66     char *p 
= strchr(reply
+1,'\r'); 
  69     string2ll(reply
+1,p
-reply
-1,&bulklen
); 
  74         lua_pushlstring(lua
,p
+2,bulklen
); 
  79 char *redisProtocolToLuaType_Status(lua_State 
*lua
, char *reply
) { 
  80     char *p 
= strchr(reply
+1,'\r'); 
  83     lua_pushstring(lua
,"ok"); 
  84     lua_pushlstring(lua
,reply
+1,p
-reply
-1); 
  89 char *redisProtocolToLuaType_Error(lua_State 
*lua
, char *reply
) { 
  90     char *p 
= strchr(reply
+1,'\r'); 
  93     lua_pushstring(lua
,"err"); 
  94     lua_pushlstring(lua
,reply
+1,p
-reply
-1); 
  99 char *redisProtocolToLuaType_MultiBulk(lua_State 
*lua
, char *reply
) { 
 100     char *p 
= strchr(reply
+1,'\r'); 
 104     string2ll(reply
+1,p
-reply
-1,&mbulklen
); 
 106     if (mbulklen 
== -1) { 
 111     for (j 
= 0; j 
< mbulklen
; j
++) { 
 112         lua_pushnumber(lua
,j
+1); 
 113         p 
= redisProtocolToLuaType(lua
,p
); 
 114         lua_settable(lua
,-3); 
 119 void luaPushError(lua_State 
*lua
, char *error
) { 
 121     lua_pushstring(lua
,"err"); 
 122     lua_pushstring(lua
, error
); 
 123     lua_settable(lua
,-3); 
 126 int luaRedisCommand(lua_State 
*lua
) { 
 127     int j
, argc 
= lua_gettop(lua
); 
 128     struct redisCommand 
*cmd
; 
 130     redisClient 
*c 
= server
.lua_client
; 
 133     /* Build the arguments vector */ 
 134     argv 
= zmalloc(sizeof(robj
*)*argc
); 
 135     for (j 
= 0; j 
< argc
; j
++) { 
 136         if (!lua_isstring(lua
,j
+1)) break; 
 137         argv
[j
] = createStringObject((char*)lua_tostring(lua
,j
+1), 
 138                                      lua_strlen(lua
,j
+1)); 
 141     /* Check if one of the arguments passed by the Lua script 
 142      * is not a string or an integer (lua_isstring() return true for 
 143      * integers as well). */ 
 147             decrRefCount(argv
[j
]); 
 152             "Lua redis() command arguments must be strings or integers"); 
 157     cmd 
= lookupCommand(argv
[0]->ptr
); 
 158     if (!cmd 
|| ((cmd
->arity 
> 0 && cmd
->arity 
!= argc
) || 
 159                    (argc 
< -cmd
->arity
))) 
 161         for (j 
= 0; j 
< argc
; j
++) decrRefCount(argv
[j
]); 
 165                 "Wrong number of args calling Redis command From Lua script"); 
 167             luaPushError(lua
,"Unknown Redis command called from Lua script"); 
 171     /* Run the command in the context of a fake client */ 
 176     /* Convert the result of the Redis command into a suitable Lua type. 
 177      * The first thing we need is to create a single string from the client 
 181         reply 
= sdscatlen(reply
,c
->buf
,c
->bufpos
); 
 184     while(listLength(c
->reply
)) { 
 185         robj 
*o 
= listNodeValue(listFirst(c
->reply
)); 
 187         reply 
= sdscatlen(reply
,o
->ptr
,sdslen(o
->ptr
)); 
 188         listDelNode(c
->reply
,listFirst(c
->reply
)); 
 190     redisProtocolToLuaType(lua
,reply
); 
 193     /* Clean up. Command code may have changed argv/argc so we use the 
 194      * argv/argc of the client instead of the local variables. */ 
 195     for (j 
= 0; j 
< c
->argc
; j
++) 
 196         decrRefCount(c
->argv
[j
]); 
 202 void scriptingInit(void) { 
 203     lua_State 
*lua 
= lua_open(); 
 206     /* Register the 'r' command */ 
 207     lua_pushcfunction(lua
,luaRedisCommand
); 
 208     lua_setglobal(lua
,"redis"); 
 210     /* Create the (non connected) client that we use to execute Redis commands 
 211      * inside the Lua interpreter */ 
 212     server
.lua_client 
= createClient(-1); 
 213     server
.lua_client
->flags 
|= REDIS_LUA_CLIENT
; 
 218 /* Hash the scripit into a SHA1 digest. We use this as Lua function name. 
 219  * Digest should point to a 41 bytes buffer: 40 for SHA1 converted into an 
 220  * hexadecimal number, plus 1 byte for null term. */ 
 221 void hashScript(char *digest
, char *script
, size_t len
) { 
 223     unsigned char hash
[20]; 
 224     char *cset 
= "0123456789abcdef"; 
 228     SHA1Update(&ctx
,(unsigned char*)script
,len
); 
 229     SHA1Final(hash
,&ctx
); 
 231     for (j 
= 0; j 
< 20; j
++) { 
 232         digest
[j
*2] = cset
[((hash
[j
]&0xF0)>>4)]; 
 233         digest
[j
*2+1] = cset
[(hash
[j
]&0xF)]; 
 238 void luaReplyToRedisReply(redisClient 
*c
, lua_State 
*lua
) { 
 239     int t 
= lua_type(lua
,1); 
 243         addReplyBulkCBuffer(c
,(char*)lua_tostring(lua
,1),lua_strlen(lua
,1)); 
 246         addReply(c
,lua_toboolean(lua
,1) ? shared
.cone 
: shared
.czero
); 
 249         addReplyLongLong(c
,(long long)lua_tonumber(lua
,1)); 
 252         /* We need to check if it is an array, an error, or a status reply. 
 253          * Error are returned as a single element table with 'err' field. 
 254          * Status replies are returned as single elment table with 'ok' field */ 
 255         lua_pushstring(lua
,"err"); 
 256         lua_gettable(lua
,-2); 
 257         t 
= lua_type(lua
,-1); 
 258         if (t 
== LUA_TSTRING
) { 
 259             addReplySds(c
,sdscatprintf(sdsempty(), 
 260                     "-%s\r\n",(char*)lua_tostring(lua
,-1))); 
 266         lua_pushstring(lua
,"ok"); 
 267         lua_gettable(lua
,-2); 
 268         t 
= lua_type(lua
,-1); 
 269         if (t 
== LUA_TSTRING
) { 
 270             addReplySds(c
,sdscatprintf(sdsempty(), 
 271                     "+%s\r\n",(char*)lua_tostring(lua
,-1))); 
 274             void *replylen 
= addDeferredMultiBulkLength(c
); 
 275             int j 
= 1, mbulklen 
= 0; 
 277             lua_pop(lua
,1); /* Discard the 'ok' field value we popped */ 
 279                 lua_pushnumber(lua
,j
++); 
 280                 lua_gettable(lua
,-2); 
 281                 t 
= lua_type(lua
,-1); 
 285                 } else if (t 
== LUA_TSTRING
) { 
 287                     char *s 
= (char*) lua_tolstring(lua
,-1,&len
); 
 289                     addReplyBulkCBuffer(c
,s
,len
); 
 291                 } else if (t 
== LUA_TNUMBER
) { 
 292                     addReplyLongLong(c
,(long long)lua_tonumber(lua
,-1)); 
 297             setDeferredMultiBulkLength(c
,replylen
,mbulklen
); 
 301         addReply(c
,shared
.nullbulk
); 
 306 /* Set an array of Redis String Objects as a Lua array (table) stored into a 
 307  * global variable. */ 
 308 void luaSetGlobalArray(lua_State 
*lua
, char *var
, robj 
**elev
, int elec
) { 
 312     for (j 
= 0; j 
< elec
; j
++) { 
 313         lua_pushlstring(lua
,(char*)elev
[j
]->ptr
,sdslen(elev
[j
]->ptr
)); 
 314         lua_rawseti(lua
,-2,j
+1); 
 316     lua_setglobal(lua
,var
); 
 319 void evalCommand(redisClient 
*c
) { 
 320     lua_State 
*lua 
= server
.lua
; 
 324     /* Get the number of arguments that are keys */ 
 325     if (getLongLongFromObjectOrReply(c
,c
->argv
[2],&numkeys
,NULL
) != REDIS_OK
) 
 327     if (numkeys 
> (c
->argc 
- 3)) { 
 328         addReplyError(c
,"Number of keys can't be greater than number of args"); 
 332     /* We obtain the script SHA1, then check if this function is already 
 333      * defined into the Lua state */ 
 336     hashScript(funcname
+2,c
->argv
[1]->ptr
,sdslen(c
->argv
[1]->ptr
)); 
 337     lua_getglobal(lua
, funcname
); 
 338     if (lua_isnil(lua
,1)) { 
 339         /* Function not defined... let's define it. */ 
 340         sds funcdef 
= sdsempty(); 
 342         lua_pop(lua
,1); /* remove the nil from the stack */ 
 343         funcdef 
= sdscat(funcdef
,"function "); 
 344         funcdef 
= sdscatlen(funcdef
,funcname
,42); 
 345         funcdef 
= sdscatlen(funcdef
," ()\n",4); 
 346         funcdef 
= sdscatlen(funcdef
,c
->argv
[1]->ptr
,sdslen(c
->argv
[1]->ptr
)); 
 347         funcdef 
= sdscatlen(funcdef
,"\nend\n",5); 
 348         /* printf("Defining:\n%s\n",funcdef); */ 
 350         if (luaL_loadbuffer(lua
,funcdef
,sdslen(funcdef
),"func definition")) { 
 351             addReplyErrorFormat(c
,"Error compiling script (new function): %s\n", 
 352                 lua_tostring(lua
,-1)); 
 358         if (lua_pcall(lua
,0,0,0)) { 
 359             addReplyErrorFormat(c
,"Error running script (new function): %s\n", 
 360                 lua_tostring(lua
,-1)); 
 364         lua_getglobal(lua
, funcname
); 
 367     /* Populate the argv and keys table accordingly to the arguments that 
 369     luaSetGlobalArray(lua
,"KEYS",c
->argv
+3,numkeys
); 
 370     luaSetGlobalArray(lua
,"ARGV",c
->argv
+3+numkeys
,c
->argc
-3-numkeys
); 
 372     /* Select the right DB in the context of the Lua client */ 
 373     selectDb(server
.lua_client
,c
->db
->id
); 
 375     /* At this point whatever this script was never seen before or if it was 
 376      * already defined, we can call it. We have zero arguments and expect 
 377      * a single return value. */ 
 378     if (lua_pcall(lua
,0,1,0)) { 
 379         selectDb(c
,server
.lua_client
->db
->id
); /* set DB ID from Lua client */ 
 380         addReplyErrorFormat(c
,"Error running script (call to %s): %s\n", 
 381             funcname
, lua_tostring(lua
,-1)); 
 383         lua_gc(lua
,LUA_GCCOLLECT
,0); 
 386     selectDb(c
,server
.lua_client
->db
->id
); /* set DB ID from Lua client */ 
 387     luaReplyToRedisReply(c
,lua
); 
 388     lua_gc(lua
,LUA_GCSTEP
,1);