]>
git.saurik.com Git - redis.git/blob - src/scripting.c
725c123eb739b020e0a5bbab24b094c178b7514e
  11 char *redisProtocolToLuaType_Int(lua_State 
*lua
, char *reply
); 
  12 char *redisProtocolToLuaType_Bulk(lua_State 
*lua
, char *reply
); 
  13 char *redisProtocolToLuaType_Status(lua_State 
*lua
, char *reply
); 
  14 char *redisProtocolToLuaType_Error(lua_State 
*lua
, char *reply
); 
  15 char *redisProtocolToLuaType_MultiBulk(lua_State 
*lua
, char *reply
); 
  16 int redis_math_random (lua_State 
*L
); 
  17 int redis_math_randomseed (lua_State 
*L
); 
  19 /* Take a Redis reply in the Redis protocol format and convert it into a 
  20  * Lua type. Thanks to this function, and the introduction of not connected 
  21  * clients, it is trvial to implement the redis() lua function. 
  23  * Basically we take the arguments, execute the Redis command in the context 
  24  * of a non connected client, then take the generated reply and convert it 
  25  * into a suitable Lua type. With this trick the scripting feature does not 
  26  * need the introduction of a full Redis internals API. Basically the script 
  27  * is like a normal client that bypasses all the slow I/O paths. 
  29  * Note: in this function we do not do any sanity check as the reply is 
  30  * generated by Redis directly. This allows use to go faster. 
  31  * The reply string can be altered during the parsing as it is discared 
  32  * after the conversion is completed. 
  34  * Errors are returned as a table with a single 'err' field set to the 
  38 char *redisProtocolToLuaType(lua_State 
*lua
, char* reply
) { 
  43         p 
= redisProtocolToLuaType_Int(lua
,reply
); 
  46         p 
= redisProtocolToLuaType_Bulk(lua
,reply
); 
  49         p 
= redisProtocolToLuaType_Status(lua
,reply
); 
  52         p 
= redisProtocolToLuaType_Error(lua
,reply
); 
  55         p 
= redisProtocolToLuaType_MultiBulk(lua
,reply
); 
  61 char *redisProtocolToLuaType_Int(lua_State 
*lua
, char *reply
) { 
  62     char *p 
= strchr(reply
+1,'\r'); 
  65     string2ll(reply
+1,p
-reply
-1,&value
); 
  66     lua_pushnumber(lua
,(lua_Number
)value
); 
  70 char *redisProtocolToLuaType_Bulk(lua_State 
*lua
, char *reply
) { 
  71     char *p 
= strchr(reply
+1,'\r'); 
  74     string2ll(reply
+1,p
-reply
-1,&bulklen
); 
  76         lua_pushboolean(lua
,0); 
  79         lua_pushlstring(lua
,p
+2,bulklen
); 
  84 char *redisProtocolToLuaType_Status(lua_State 
*lua
, char *reply
) { 
  85     char *p 
= strchr(reply
+1,'\r'); 
  88     lua_pushstring(lua
,"ok"); 
  89     lua_pushlstring(lua
,reply
+1,p
-reply
-1); 
  94 char *redisProtocolToLuaType_Error(lua_State 
*lua
, char *reply
) { 
  95     char *p 
= strchr(reply
+1,'\r'); 
  98     lua_pushstring(lua
,"err"); 
  99     lua_pushlstring(lua
,reply
+1,p
-reply
-1); 
 100     lua_settable(lua
,-3); 
 104 char *redisProtocolToLuaType_MultiBulk(lua_State 
*lua
, char *reply
) { 
 105     char *p 
= strchr(reply
+1,'\r'); 
 109     string2ll(reply
+1,p
-reply
-1,&mbulklen
); 
 111     if (mbulklen 
== -1) { 
 112         lua_pushboolean(lua
,0); 
 116     for (j 
= 0; j 
< mbulklen
; j
++) { 
 117         lua_pushnumber(lua
,j
+1); 
 118         p 
= redisProtocolToLuaType(lua
,p
); 
 119         lua_settable(lua
,-3); 
 124 void luaPushError(lua_State 
*lua
, char *error
) { 
 126     lua_pushstring(lua
,"err"); 
 127     lua_pushstring(lua
, error
); 
 128     lua_settable(lua
,-3); 
 131 int luaRedisGenericCommand(lua_State 
*lua
, int raise_error
) { 
 132     int j
, argc 
= lua_gettop(lua
); 
 133     struct redisCommand 
*cmd
; 
 135     redisClient 
*c 
= server
.lua_client
; 
 138     /* Build the arguments vector */ 
 139     argv 
= zmalloc(sizeof(robj
*)*argc
); 
 140     for (j 
= 0; j 
< argc
; j
++) { 
 141         if (!lua_isstring(lua
,j
+1)) break; 
 142         argv
[j
] = createStringObject((char*)lua_tostring(lua
,j
+1), 
 143                                      lua_strlen(lua
,j
+1)); 
 146     /* Check if one of the arguments passed by the Lua script 
 147      * is not a string or an integer (lua_isstring() return true for 
 148      * integers as well). */ 
 152             decrRefCount(argv
[j
]); 
 157             "Lua redis() command arguments must be strings or integers"); 
 161     /* Setup our fake client for command execution */ 
 166     cmd 
= lookupCommand(argv
[0]->ptr
); 
 167     if (!cmd 
|| ((cmd
->arity 
> 0 && cmd
->arity 
!= argc
) || 
 168                    (argc 
< -cmd
->arity
))) 
 172                 "Wrong number of args calling Redis command From Lua script"); 
 174             luaPushError(lua
,"Unknown Redis command called from Lua script"); 
 178     if (cmd
->flags 
& REDIS_CMD_NOSCRIPT
) { 
 179         luaPushError(lua
, "This Redis command is not allowed from scripts"); 
 183     if (cmd
->flags 
& REDIS_CMD_WRITE 
&& server
.lua_random_dirty
) { 
 185             "Write commands not allowed after non deterministic commands"); 
 189     if (cmd
->flags 
& REDIS_CMD_RANDOM
) server
.lua_random_dirty 
= 1; 
 191     /* Run the command */ 
 194     /* Convert the result of the Redis command into a suitable Lua type. 
 195      * The first thing we need is to create a single string from the client 
 199         reply 
= sdscatlen(reply
,c
->buf
,c
->bufpos
); 
 202     while(listLength(c
->reply
)) { 
 203         robj 
*o 
= listNodeValue(listFirst(c
->reply
)); 
 205         reply 
= sdscatlen(reply
,o
->ptr
,sdslen(o
->ptr
)); 
 206         listDelNode(c
->reply
,listFirst(c
->reply
)); 
 208     if (raise_error 
&& reply
[0] != '-') raise_error 
= 0; 
 209     redisProtocolToLuaType(lua
,reply
); 
 213     /* Clean up. Command code may have changed argv/argc so we use the 
 214      * argv/argc of the client instead of the local variables. */ 
 215     for (j 
= 0; j 
< c
->argc
; j
++) 
 216         decrRefCount(c
->argv
[j
]); 
 220         /* If we are here we should have an error in the stack, in the 
 221          * form of a table with an "err" field. Extract the string to 
 222          * return the plain error. */ 
 223         lua_pushstring(lua
,"err"); 
 224         lua_gettable(lua
,-2); 
 225         return lua_error(lua
); 
 230 int luaRedisCallCommand(lua_State 
*lua
) { 
 231     return luaRedisGenericCommand(lua
,1); 
 234 int luaRedisPCallCommand(lua_State 
*lua
) { 
 235     return luaRedisGenericCommand(lua
,0); 
 238 int luaLogCommand(lua_State 
*lua
) { 
 239     int j
, argc 
= lua_gettop(lua
); 
 244         luaPushError(lua
, "redis.log() requires two arguments or more."); 
 246     } else if (!lua_isnumber(lua
,-argc
)) { 
 247         luaPushError(lua
, "First argument must be a number (log level)."); 
 250     level 
= lua_tonumber(lua
,-argc
); 
 251     if (level 
< REDIS_DEBUG 
|| level 
> REDIS_WARNING
) { 
 252         luaPushError(lua
, "Invalid debug level."); 
 256     /* Glue together all the arguments */ 
 258     for (j 
= 1; j 
< argc
; j
++) { 
 262         s 
= (char*)lua_tolstring(lua
,(-argc
)+j
,&len
); 
 264             if (j 
!= 1) log 
= sdscatlen(log
," ",1); 
 265             log 
= sdscatlen(log
,s
,len
); 
 268     redisLogRaw(level
,log
); 
 273 void luaMaskCountHook(lua_State 
*lua
, lua_Debug 
*ar
) { 
 277     elapsed 
= (ustime()/1000) - server
.lua_time_start
; 
 278     if (elapsed 
>= server
.lua_time_limit
) { 
 279         redisLog(REDIS_NOTICE
,"Lua script aborted for max execution time after %lld milliseconds of running time.",elapsed
); 
 280         lua_pushstring(lua
,"Script aborted for max execution time."); 
 285 void luaLoadLib(lua_State 
*lua
, const char *libname
, lua_CFunction luafunc
) { 
 286   lua_pushcfunction(lua
, luafunc
); 
 287   lua_pushstring(lua
, libname
); 
 291 LUALIB_API 
int (luaopen_cjson
) (lua_State 
*L
); 
 293 void luaLoadLibraries(lua_State 
*lua
) { 
 294     luaLoadLib(lua
, "", luaopen_base
); 
 295     luaLoadLib(lua
, LUA_TABLIBNAME
, luaopen_table
); 
 296     luaLoadLib(lua
, LUA_STRLIBNAME
, luaopen_string
); 
 297     luaLoadLib(lua
, LUA_MATHLIBNAME
, luaopen_math
); 
 298     luaLoadLib(lua
, LUA_DBLIBNAME
, luaopen_debug
);  
 299     luaLoadLib(lua
, "cjson", luaopen_cjson
);  
 301 #if 0 /* Stuff that we don't load currently, for sandboxing concerns. */ 
 302     luaLoadLib(lua
, LUA_LOADLIBNAME
, luaopen_package
); 
 303     luaLoadLib(lua
, LUA_OSLIBNAME
, luaopen_os
); 
 307 /* Initialize the scripting environment. 
 308  * It is possible to call this function to reset the scripting environment 
 309  * assuming that we call scriptingRelease() before. 
 310  * See scriptingReset() for more information. */ 
 311 void scriptingInit(void) { 
 312     lua_State 
*lua 
= lua_open(); 
 313     luaLoadLibraries(lua
); 
 315     /* Initialize a dictionary we use to map SHAs to scripts. 
 316      * This is useful for replication, as we need to replicate EVALSHA 
 317      * as EVAL, so we need to remember the associated script. */ 
 318     server
.lua_scripts 
= dictCreate(&dbDictType
,NULL
); 
 320     /* Register the redis commands table and fields */ 
 324     lua_pushstring(lua
,"call"); 
 325     lua_pushcfunction(lua
,luaRedisCallCommand
); 
 326     lua_settable(lua
,-3); 
 329     lua_pushstring(lua
,"pcall"); 
 330     lua_pushcfunction(lua
,luaRedisPCallCommand
); 
 331     lua_settable(lua
,-3); 
 333     /* redis.log and log levels. */ 
 334     lua_pushstring(lua
,"log"); 
 335     lua_pushcfunction(lua
,luaLogCommand
); 
 336     lua_settable(lua
,-3); 
 338     lua_pushstring(lua
,"LOG_DEBUG"); 
 339     lua_pushnumber(lua
,REDIS_DEBUG
); 
 340     lua_settable(lua
,-3); 
 342     lua_pushstring(lua
,"LOG_VERBOSE"); 
 343     lua_pushnumber(lua
,REDIS_VERBOSE
); 
 344     lua_settable(lua
,-3); 
 346     lua_pushstring(lua
,"LOG_NOTICE"); 
 347     lua_pushnumber(lua
,REDIS_NOTICE
); 
 348     lua_settable(lua
,-3); 
 350     lua_pushstring(lua
,"LOG_WARNING"); 
 351     lua_pushnumber(lua
,REDIS_WARNING
); 
 352     lua_settable(lua
,-3); 
 354     /* Finally set the table as 'redis' global var. */ 
 355     lua_setglobal(lua
,"redis"); 
 357     /* Replace math.random and math.randomseed with our implementations. */ 
 358     lua_getglobal(lua
,"math"); 
 360     lua_pushstring(lua
,"random"); 
 361     lua_pushcfunction(lua
,redis_math_random
); 
 362     lua_settable(lua
,-3); 
 364     lua_pushstring(lua
,"randomseed"); 
 365     lua_pushcfunction(lua
,redis_math_randomseed
); 
 366     lua_settable(lua
,-3); 
 368     lua_setglobal(lua
,"math"); 
 370     /* Create the (non connected) client that we use to execute Redis commands 
 371      * inside the Lua interpreter. 
 372      * Note: there is no need to create it again when this function is called 
 373      * by scriptingReset(). */ 
 374     if (server
.lua_client 
== NULL
) { 
 375         server
.lua_client 
= createClient(-1); 
 376         server
.lua_client
->flags 
|= REDIS_LUA_CLIENT
; 
 382 /* Release resources related to Lua scripting. 
 383  * This function is used in order to reset the scripting environment. */ 
 384 void scriptingRelease(void) { 
 385     dictRelease(server
.lua_scripts
); 
 386     lua_close(server
.lua
); 
 389 void scriptingReset(void) { 
 394 /* Hash the scripit into a SHA1 digest. We use this as Lua function name. 
 395  * Digest should point to a 41 bytes buffer: 40 for SHA1 converted into an 
 396  * hexadecimal number, plus 1 byte for null term. */ 
 397 void hashScript(char *digest
, char *script
, size_t len
) { 
 399     unsigned char hash
[20]; 
 400     char *cset 
= "0123456789abcdef"; 
 404     SHA1Update(&ctx
,(unsigned char*)script
,len
); 
 405     SHA1Final(hash
,&ctx
); 
 407     for (j 
= 0; j 
< 20; j
++) { 
 408         digest
[j
*2] = cset
[((hash
[j
]&0xF0)>>4)]; 
 409         digest
[j
*2+1] = cset
[(hash
[j
]&0xF)]; 
 414 void luaReplyToRedisReply(redisClient 
*c
, lua_State 
*lua
) { 
 415     int t 
= lua_type(lua
,-1); 
 419         addReplyBulkCBuffer(c
,(char*)lua_tostring(lua
,-1),lua_strlen(lua
,-1)); 
 422         addReply(c
,lua_toboolean(lua
,-1) ? shared
.cone 
: shared
.nullbulk
); 
 425         addReplyLongLong(c
,(long long)lua_tonumber(lua
,-1)); 
 428         /* We need to check if it is an array, an error, or a status reply. 
 429          * Error are returned as a single element table with 'err' field. 
 430          * Status replies are returned as single elment table with 'ok' field */ 
 431         lua_pushstring(lua
,"err"); 
 432         lua_gettable(lua
,-2); 
 433         t 
= lua_type(lua
,-1); 
 434         if (t 
== LUA_TSTRING
) { 
 435             sds err 
= sdsnew(lua_tostring(lua
,-1)); 
 436             sdsmapchars(err
,"\r\n","  ",2); 
 437             addReplySds(c
,sdscatprintf(sdsempty(),"-%s\r\n",err
)); 
 444         lua_pushstring(lua
,"ok"); 
 445         lua_gettable(lua
,-2); 
 446         t 
= lua_type(lua
,-1); 
 447         if (t 
== LUA_TSTRING
) { 
 448             sds ok 
= sdsnew(lua_tostring(lua
,-1)); 
 449             sdsmapchars(ok
,"\r\n","  ",2); 
 450             addReplySds(c
,sdscatprintf(sdsempty(),"+%s\r\n",ok
)); 
 454             void *replylen 
= addDeferredMultiBulkLength(c
); 
 455             int j 
= 1, mbulklen 
= 0; 
 457             lua_pop(lua
,1); /* Discard the 'ok' field value we popped */ 
 459                 lua_pushnumber(lua
,j
++); 
 460                 lua_gettable(lua
,-2); 
 461                 t 
= lua_type(lua
,-1); 
 466                 luaReplyToRedisReply(c
, lua
); 
 469             setDeferredMultiBulkLength(c
,replylen
,mbulklen
); 
 473         addReply(c
,shared
.nullbulk
); 
 478 /* Set an array of Redis String Objects as a Lua array (table) stored into a 
 479  * global variable. */ 
 480 void luaSetGlobalArray(lua_State 
*lua
, char *var
, robj 
**elev
, int elec
) { 
 484     for (j 
= 0; j 
< elec
; j
++) { 
 485         lua_pushlstring(lua
,(char*)elev
[j
]->ptr
,sdslen(elev
[j
]->ptr
)); 
 486         lua_rawseti(lua
,-2,j
+1); 
 488     lua_setglobal(lua
,var
); 
 491 void evalGenericCommand(redisClient 
*c
, int evalsha
) { 
 492     lua_State 
*lua 
= server
.lua
; 
 496     /* We want the same PRNG sequence at every call so that our PRNG is 
 497      * not affected by external state. */ 
 500     /* We set this flag to zero to remember that so far no random command 
 501      * was called. This way we can allow the user to call commands like 
 502      * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command 
 503      * is called (otherwise the replication and AOF would end with non 
 504      * deterministic sequences). 
 506      * Thanks to this flag we'll raise an error every time a write command 
 507      * is called after a random command was used. */ 
 508     server
.lua_random_dirty 
= 0; 
 510     /* Get the number of arguments that are keys */ 
 511     if (getLongLongFromObjectOrReply(c
,c
->argv
[2],&numkeys
,NULL
) != REDIS_OK
) 
 513     if (numkeys 
> (c
->argc 
- 3)) { 
 514         addReplyError(c
,"Number of keys can't be greater than number of args"); 
 518     /* We obtain the script SHA1, then check if this function is already 
 519      * defined into the Lua state */ 
 523         /* Hash the code if this is an EVAL call */ 
 524         hashScript(funcname
+2,c
->argv
[1]->ptr
,sdslen(c
->argv
[1]->ptr
)); 
 526         /* We already have the SHA if it is a EVALSHA */ 
 528         char *sha 
= c
->argv
[1]->ptr
; 
 530         for (j 
= 0; j 
< 40; j
++) 
 531             funcname
[j
+2] = tolower(sha
[j
]); 
 535     lua_getglobal(lua
, funcname
); 
 536     if (lua_isnil(lua
,1)) { 
 539         /* Function not defined... let's define it if we have the 
 540          * body of the funciton. If this is an EVALSHA call we can just 
 541          * return an error. */ 
 543             addReply(c
, shared
.noscripterr
); 
 544             lua_pop(lua
,1); /* remove the nil from the stack */ 
 547         funcdef 
= sdsempty(); 
 549         lua_pop(lua
,1); /* remove the nil from the stack */ 
 550         funcdef 
= sdscat(funcdef
,"function "); 
 551         funcdef 
= sdscatlen(funcdef
,funcname
,42); 
 552         funcdef 
= sdscatlen(funcdef
," ()\n",4); 
 553         funcdef 
= sdscatlen(funcdef
,c
->argv
[1]->ptr
,sdslen(c
->argv
[1]->ptr
)); 
 554         funcdef 
= sdscatlen(funcdef
,"\nend\n",5); 
 555         /* printf("Defining:\n%s\n",funcdef); */ 
 557         if (luaL_loadbuffer(lua
,funcdef
,sdslen(funcdef
),"func definition")) { 
 558             addReplyErrorFormat(c
,"Error compiling script (new function): %s\n", 
 559                 lua_tostring(lua
,-1)); 
 565         if (lua_pcall(lua
,0,0,0)) { 
 566             addReplyErrorFormat(c
,"Error running script (new function): %s\n", 
 567                 lua_tostring(lua
,-1)); 
 571         lua_getglobal(lua
, funcname
); 
 573         /* We also save a SHA1 -> Original script map in a dictionary 
 574          * so that we can replicate / write in the AOF all the 
 575          * EVALSHA commands as EVAL using the original script. */ 
 577             int retval 
= dictAdd(server
.lua_scripts
, 
 578                                  sdsnewlen(funcname
+2,40),c
->argv
[1]); 
 579             redisAssertWithInfo(c
,NULL
,retval 
== DICT_OK
); 
 580             incrRefCount(c
->argv
[1]); 
 584     /* Populate the argv and keys table accordingly to the arguments that 
 586     luaSetGlobalArray(lua
,"KEYS",c
->argv
+3,numkeys
); 
 587     luaSetGlobalArray(lua
,"ARGV",c
->argv
+3+numkeys
,c
->argc
-3-numkeys
); 
 589     /* Select the right DB in the context of the Lua client */ 
 590     selectDb(server
.lua_client
,c
->db
->id
); 
 592     /* Set an hook in order to be able to stop the script execution if it 
 593      * is running for too much time. 
 594      * We set the hook only if the time limit is enabled as the hook will 
 595      * make the Lua script execution slower. */ 
 596     if (server
.lua_time_limit 
> 0) { 
 597         lua_sethook(lua
,luaMaskCountHook
,LUA_MASKCOUNT
,100000); 
 598         server
.lua_time_start 
= ustime()/1000; 
 600         lua_sethook(lua
,luaMaskCountHook
,0,0); 
 603     /* At this point whatever this script was never seen before or if it was 
 604      * already defined, we can call it. We have zero arguments and expect 
 605      * a single return value. */ 
 606     if (lua_pcall(lua
,0,1,0)) { 
 607         selectDb(c
,server
.lua_client
->db
->id
); /* set DB ID from Lua client */ 
 608         addReplyErrorFormat(c
,"Error running script (call to %s): %s\n", 
 609             funcname
, lua_tostring(lua
,-1)); 
 611         lua_gc(lua
,LUA_GCCOLLECT
,0); 
 614     selectDb(c
,server
.lua_client
->db
->id
); /* set DB ID from Lua client */ 
 615     luaReplyToRedisReply(c
,lua
); 
 616     lua_gc(lua
,LUA_GCSTEP
,1); 
 618     /* If we have slaves attached we want to replicate this command as 
 619      * EVAL instead of EVALSHA. We do this also in the AOF as currently there 
 620      * is no easy way to propagate a command in a different way in the AOF 
 621      * and in the replication link. 
 623      * IMPROVEMENT POSSIBLE: 
 624      * 1) Replicate this command as EVALSHA in the AOF. 
 625      * 2) Remember what slave already received a given script, and replicate 
 626      *    the EVALSHA against this slaves when possible. 
 629         robj 
*script 
= dictFetchValue(server
.lua_scripts
,c
->argv
[1]->ptr
); 
 631         redisAssertWithInfo(c
,NULL
,script 
!= NULL
); 
 632         rewriteClientCommandArgument(c
,0, 
 633             resetRefCount(createStringObject("EVAL",4))); 
 634         rewriteClientCommandArgument(c
,1,script
); 
 638 void evalCommand(redisClient 
*c
) { 
 639     evalGenericCommand(c
,0); 
 642 void evalShaCommand(redisClient 
*c
) { 
 643     if (sdslen(c
->argv
[1]->ptr
) != 40) { 
 644         /* We know that a match is not possible if the provided SHA is 
 645          * not the right length. So we return an error ASAP, this way 
 646          * evalGenericCommand() can be implemented without string length 
 648         addReply(c
, shared
.noscripterr
); 
 651     evalGenericCommand(c
,1); 
 654 /* We replace math.random() with our implementation that is not affected 
 655  * by specific libc random() implementations and will output the same sequence 
 656  * (for the same seed) in every arch. */ 
 658 /* The following implementation is the one shipped with Lua itself but with 
 659  * rand() replaced by redisLrand48(). */ 
 660 int redis_math_random (lua_State 
*L
) { 
 661   /* the `%' avoids the (rare) case of r==1, and is needed also because on 
 662      some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ 
 663   lua_Number r 
= (lua_Number
)(redisLrand48()%REDIS_LRAND
48_MAX
) / 
 664                                 (lua_Number
)REDIS_LRAND48_MAX
; 
 665   switch (lua_gettop(L
)) {  /* check number of arguments */ 
 666     case 0: {  /* no arguments */ 
 667       lua_pushnumber(L
, r
);  /* Number between 0 and 1 */ 
 670     case 1: {  /* only upper limit */ 
 671       int u 
= luaL_checkint(L
, 1); 
 672       luaL_argcheck(L
, 1<=u
, 1, "interval is empty"); 
 673       lua_pushnumber(L
, floor(r
*u
)+1);  /* int between 1 and `u' */ 
 676     case 2: {  /* lower and upper limits */ 
 677       int l 
= luaL_checkint(L
, 1); 
 678       int u 
= luaL_checkint(L
, 2); 
 679       luaL_argcheck(L
, l
<=u
, 2, "interval is empty"); 
 680       lua_pushnumber(L
, floor(r
*(u
-l
+1))+l
);  /* int between `l' and `u' */ 
 683     default: return luaL_error(L
, "wrong number of arguments"); 
 688 int redis_math_randomseed (lua_State 
*L
) { 
 689   redisSrand48(luaL_checkint(L
, 1)); 
 693 /* --------------------------------------------------------------------------- 
 694  * SCRIPT command for script environment introspection and control 
 695  * ------------------------------------------------------------------------- */ 
 697 void scriptCommand(redisClient 
*c
) { 
 698     if (c
->argc 
== 2 && !strcasecmp(c
->argv
[1]->ptr
,"flush")) { 
 700         addReply(c
,shared
.ok
); 
 701         server
.dirty
++; /* Replicating this command is a good idea. */ 
 702     } else if (c
->argc 
>= 2 && !strcasecmp(c
->argv
[1]->ptr
,"exists")) { 
 705         addReplyMultiBulkLen(c
, c
->argc
-2); 
 706         for (j 
= 2; j 
< c
->argc
; j
++) { 
 707             if (dictFind(server
.lua_scripts
,c
->argv
[j
]->ptr
)) 
 708                 addReply(c
,shared
.cone
); 
 710                 addReply(c
,shared
.czero
); 
 713         addReplyError(c
, "Unknown SCRIPT subcommand or wrong # of args.");