]>
git.saurik.com Git - redis.git/blob - src/scripting.c
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 ;
190 if ( cmd
-> flags
& REDIS_CMD_WRITE
) server
. lua_write_dirty
= 1 ;
192 /* Run the command */
195 /* Convert the result of the Redis command into a suitable Lua type.
196 * The first thing we need is to create a single string from the client
200 reply
= sdscatlen ( reply
, c
-> buf
, c
-> bufpos
);
203 while ( listLength ( c
-> reply
)) {
204 robj
* o
= listNodeValue ( listFirst ( c
-> reply
));
206 reply
= sdscatlen ( reply
, o
-> ptr
, sdslen ( o
-> ptr
));
207 listDelNode ( c
-> reply
, listFirst ( c
-> reply
));
209 if ( raise_error
&& reply
[ 0 ] != '-' ) raise_error
= 0 ;
210 redisProtocolToLuaType ( lua
, reply
);
214 /* Clean up. Command code may have changed argv/argc so we use the
215 * argv/argc of the client instead of the local variables. */
216 for ( j
= 0 ; j
< c
-> argc
; j
++)
217 decrRefCount ( c
-> argv
[ j
]);
221 /* If we are here we should have an error in the stack, in the
222 * form of a table with an "err" field. Extract the string to
223 * return the plain error. */
224 lua_pushstring ( lua
, "err" );
225 lua_gettable ( lua
,- 2 );
226 return lua_error ( lua
);
231 int luaRedisCallCommand ( lua_State
* lua
) {
232 return luaRedisGenericCommand ( lua
, 1 );
235 int luaRedisPCallCommand ( lua_State
* lua
) {
236 return luaRedisGenericCommand ( lua
, 0 );
239 int luaLogCommand ( lua_State
* lua
) {
240 int j
, argc
= lua_gettop ( lua
);
245 luaPushError ( lua
, "redis.log() requires two arguments or more." );
247 } else if (! lua_isnumber ( lua
,- argc
)) {
248 luaPushError ( lua
, "First argument must be a number (log level)." );
251 level
= lua_tonumber ( lua
,- argc
);
252 if ( level
< REDIS_DEBUG
|| level
> REDIS_WARNING
) {
253 luaPushError ( lua
, "Invalid debug level." );
257 /* Glue together all the arguments */
259 for ( j
= 1 ; j
< argc
; j
++) {
263 s
= ( char *) lua_tolstring ( lua
,(- argc
)+ j
,& len
);
265 if ( j
!= 1 ) log
= sdscatlen ( log
, " " , 1 );
266 log
= sdscatlen ( log
, s
, len
);
269 redisLogRaw ( level
, log
);
274 void luaMaskCountHook ( lua_State
* lua
, lua_Debug
* ar
) {
279 elapsed
= ( ustime ()/ 1000 ) - server
. lua_time_start
;
280 if ( elapsed
>= server
. lua_time_limit
&& server
. lua_timedout
== 0 ) {
281 redisLog ( REDIS_WARNING
, "Lua slow script detected: still in execution after %l ld milliseconds. You can try killing the script using the SCRIPT KILL command." , elapsed
);
282 server
. lua_timedout
= 1 ;
283 /* Once the script timeouts we reenter the event loop to permit others
284 * to call SCRIPT KILL or SHUTDOWN NOSAVE if needed. For this reason
285 * we need to mask the client executing the script from the event loop.
286 * If we don't do that the client may disconnect and could no longer be
287 * here when the EVAL command will return. */
288 aeDeleteFileEvent ( server
. el
, server
. lua_caller
-> fd
, AE_READABLE
);
290 if ( server
. lua_timedout
)
291 aeProcessEvents ( server
. el
, AE_FILE_EVENTS
| AE_DONT_WAIT
);
292 if ( server
. lua_kill
) {
293 redisLog ( REDIS_WARNING
, "Lua script killed by user with SCRIPT KILL." );
294 lua_pushstring ( lua
, "Script killed by user with SCRIPT KILL..." );
299 void luaLoadLib ( lua_State
* lua
, const char * libname
, lua_CFunction luafunc
) {
300 lua_pushcfunction ( lua
, luafunc
);
301 lua_pushstring ( lua
, libname
);
305 LUALIB_API
int ( luaopen_cjson
) ( lua_State
* L
);
307 void luaLoadLibraries ( lua_State
* lua
) {
308 luaLoadLib ( lua
, "" , luaopen_base
);
309 luaLoadLib ( lua
, LUA_TABLIBNAME
, luaopen_table
);
310 luaLoadLib ( lua
, LUA_STRLIBNAME
, luaopen_string
);
311 luaLoadLib ( lua
, LUA_MATHLIBNAME
, luaopen_math
);
312 luaLoadLib ( lua
, LUA_DBLIBNAME
, luaopen_debug
);
313 luaLoadLib ( lua
, "cjson" , luaopen_cjson
);
315 #if 0 /* Stuff that we don't load currently, for sandboxing concerns. */
316 luaLoadLib ( lua
, LUA_LOADLIBNAME
, luaopen_package
);
317 luaLoadLib ( lua
, LUA_OSLIBNAME
, luaopen_os
);
321 /* Initialize the scripting environment.
322 * It is possible to call this function to reset the scripting environment
323 * assuming that we call scriptingRelease() before.
324 * See scriptingReset() for more information. */
325 void scriptingInit ( void ) {
326 lua_State
* lua
= lua_open ();
327 luaLoadLibraries ( lua
);
329 /* Initialize a dictionary we use to map SHAs to scripts.
330 * This is useful for replication, as we need to replicate EVALSHA
331 * as EVAL, so we need to remember the associated script. */
332 server
. lua_scripts
= dictCreate (& dbDictType
, NULL
);
334 /* Register the redis commands table and fields */
338 lua_pushstring ( lua
, "call" );
339 lua_pushcfunction ( lua
, luaRedisCallCommand
);
340 lua_settable ( lua
,- 3 );
343 lua_pushstring ( lua
, "pcall" );
344 lua_pushcfunction ( lua
, luaRedisPCallCommand
);
345 lua_settable ( lua
,- 3 );
347 /* redis.log and log levels. */
348 lua_pushstring ( lua
, "log" );
349 lua_pushcfunction ( lua
, luaLogCommand
);
350 lua_settable ( lua
,- 3 );
352 lua_pushstring ( lua
, "LOG_DEBUG" );
353 lua_pushnumber ( lua
, REDIS_DEBUG
);
354 lua_settable ( lua
,- 3 );
356 lua_pushstring ( lua
, "LOG_VERBOSE" );
357 lua_pushnumber ( lua
, REDIS_VERBOSE
);
358 lua_settable ( lua
,- 3 );
360 lua_pushstring ( lua
, "LOG_NOTICE" );
361 lua_pushnumber ( lua
, REDIS_NOTICE
);
362 lua_settable ( lua
,- 3 );
364 lua_pushstring ( lua
, "LOG_WARNING" );
365 lua_pushnumber ( lua
, REDIS_WARNING
);
366 lua_settable ( lua
,- 3 );
368 /* Finally set the table as 'redis' global var. */
369 lua_setglobal ( lua
, "redis" );
371 /* Replace math.random and math.randomseed with our implementations. */
372 lua_getglobal ( lua
, "math" );
374 lua_pushstring ( lua
, "random" );
375 lua_pushcfunction ( lua
, redis_math_random
);
376 lua_settable ( lua
,- 3 );
378 lua_pushstring ( lua
, "randomseed" );
379 lua_pushcfunction ( lua
, redis_math_randomseed
);
380 lua_settable ( lua
,- 3 );
382 lua_setglobal ( lua
, "math" );
384 /* Create the (non connected) client that we use to execute Redis commands
385 * inside the Lua interpreter.
386 * Note: there is no need to create it again when this function is called
387 * by scriptingReset(). */
388 if ( server
. lua_client
== NULL
) {
389 server
. lua_client
= createClient (- 1 );
390 server
. lua_client
-> flags
|= REDIS_LUA_CLIENT
;
396 /* Release resources related to Lua scripting.
397 * This function is used in order to reset the scripting environment. */
398 void scriptingRelease ( void ) {
399 dictRelease ( server
. lua_scripts
);
400 lua_close ( server
. lua
);
403 void scriptingReset ( void ) {
408 /* Hash the scripit into a SHA1 digest. We use this as Lua function name.
409 * Digest should point to a 41 bytes buffer: 40 for SHA1 converted into an
410 * hexadecimal number, plus 1 byte for null term. */
411 void hashScript ( char * digest
, char * script
, size_t len
) {
413 unsigned char hash
[ 20 ];
414 char * cset
= "0123456789abcdef" ;
418 SHA1Update (& ctx
,( unsigned char *) script
, len
);
419 SHA1Final ( hash
,& ctx
);
421 for ( j
= 0 ; j
< 20 ; j
++) {
422 digest
[ j
* 2 ] = cset
[(( hash
[ j
]& 0xF0 )>> 4 )];
423 digest
[ j
* 2 + 1 ] = cset
[( hash
[ j
]& 0xF )];
428 void luaReplyToRedisReply ( redisClient
* c
, lua_State
* lua
) {
429 int t
= lua_type ( lua
,- 1 );
433 addReplyBulkCBuffer ( c
,( char *) lua_tostring ( lua
,- 1 ), lua_strlen ( lua
,- 1 ));
436 addReply ( c
, lua_toboolean ( lua
,- 1 ) ? shared
. cone
: shared
. nullbulk
);
439 addReplyLongLong ( c
,( long long ) lua_tonumber ( lua
,- 1 ));
442 /* We need to check if it is an array, an error, or a status reply.
443 * Error are returned as a single element table with 'err' field.
444 * Status replies are returned as single elment table with 'ok' field */
445 lua_pushstring ( lua
, "err" );
446 lua_gettable ( lua
,- 2 );
447 t
= lua_type ( lua
,- 1 );
448 if ( t
== LUA_TSTRING
) {
449 sds err
= sdsnew ( lua_tostring ( lua
,- 1 ));
450 sdsmapchars ( err
, " \r\n " , " " , 2 );
451 addReplySds ( c
, sdscatprintf ( sdsempty (), "- %s \r\n " , err
));
458 lua_pushstring ( lua
, "ok" );
459 lua_gettable ( lua
,- 2 );
460 t
= lua_type ( lua
,- 1 );
461 if ( t
== LUA_TSTRING
) {
462 sds ok
= sdsnew ( lua_tostring ( lua
,- 1 ));
463 sdsmapchars ( ok
, " \r\n " , " " , 2 );
464 addReplySds ( c
, sdscatprintf ( sdsempty (), "+ %s \r\n " , ok
));
468 void * replylen
= addDeferredMultiBulkLength ( c
);
469 int j
= 1 , mbulklen
= 0 ;
471 lua_pop ( lua
, 1 ); /* Discard the 'ok' field value we popped */
473 lua_pushnumber ( lua
, j
++);
474 lua_gettable ( lua
,- 2 );
475 t
= lua_type ( lua
,- 1 );
480 luaReplyToRedisReply ( c
, lua
);
483 setDeferredMultiBulkLength ( c
, replylen
, mbulklen
);
487 addReply ( c
, shared
. nullbulk
);
492 /* Set an array of Redis String Objects as a Lua array (table) stored into a
493 * global variable. */
494 void luaSetGlobalArray ( lua_State
* lua
, char * var
, robj
** elev
, int elec
) {
498 for ( j
= 0 ; j
< elec
; j
++) {
499 lua_pushlstring ( lua
,( char *) elev
[ j
]-> ptr
, sdslen ( elev
[ j
]-> ptr
));
500 lua_rawseti ( lua
,- 2 , j
+ 1 );
502 lua_setglobal ( lua
, var
);
505 /* Define a lua function with the specified function name and body.
506 * The function name musts be a 2 characters long string, since all the
507 * functions we defined in the Lua context are in the form:
511 * On success REDIS_OK is returned, and nothing is left on the Lua stack.
512 * On error REDIS_ERR is returned and an appropriate error is set in the
514 int luaCreateFunction ( redisClient
* c
, lua_State
* lua
, char * funcname
, robj
* body
) {
515 sds funcdef
= sdsempty ();
517 funcdef
= sdscat ( funcdef
, "function " );
518 funcdef
= sdscatlen ( funcdef
, funcname
, 42 );
519 funcdef
= sdscatlen ( funcdef
, " () \n " , 4 );
520 funcdef
= sdscatlen ( funcdef
, body
-> ptr
, sdslen ( body
-> ptr
));
521 funcdef
= sdscatlen ( funcdef
, " \n end \n " , 5 );
523 if ( luaL_loadbuffer ( lua
, funcdef
, sdslen ( funcdef
), "func definition" )) {
524 addReplyErrorFormat ( c
, "Error compiling script (new function): %s \n " ,
525 lua_tostring ( lua
,- 1 ));
531 if ( lua_pcall ( lua
, 0 , 0 , 0 )) {
532 addReplyErrorFormat ( c
, "Error running script (new function): %s \n " ,
533 lua_tostring ( lua
,- 1 ));
538 /* We also save a SHA1 -> Original script map in a dictionary
539 * so that we can replicate / write in the AOF all the
540 * EVALSHA commands as EVAL using the original script. */
542 int retval
= dictAdd ( server
. lua_scripts
,
543 sdsnewlen ( funcname
+ 2 , 40 ), body
);
544 redisAssertWithInfo ( c
, NULL
, retval
== DICT_OK
);
550 void evalGenericCommand ( redisClient
* c
, int evalsha
) {
551 lua_State
* lua
= server
. lua
;
555 /* We want the same PRNG sequence at every call so that our PRNG is
556 * not affected by external state. */
559 /* We set this flag to zero to remember that so far no random command
560 * was called. This way we can allow the user to call commands like
561 * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command
562 * is called (otherwise the replication and AOF would end with non
563 * deterministic sequences).
565 * Thanks to this flag we'll raise an error every time a write command
566 * is called after a random command was used. */
567 server
. lua_random_dirty
= 0 ;
568 server
. lua_write_dirty
= 0 ;
570 /* Get the number of arguments that are keys */
571 if ( getLongLongFromObjectOrReply ( c
, c
-> argv
[ 2 ],& numkeys
, NULL
) != REDIS_OK
)
573 if ( numkeys
> ( c
-> argc
- 3 )) {
574 addReplyError ( c
, "Number of keys can't be greater than number of args" );
578 /* We obtain the script SHA1, then check if this function is already
579 * defined into the Lua state */
583 /* Hash the code if this is an EVAL call */
584 hashScript ( funcname
+ 2 , c
-> argv
[ 1 ]-> ptr
, sdslen ( c
-> argv
[ 1 ]-> ptr
));
586 /* We already have the SHA if it is a EVALSHA */
588 char * sha
= c
-> argv
[ 1 ]-> ptr
;
590 for ( j
= 0 ; j
< 40 ; j
++)
591 funcname
[ j
+ 2 ] = tolower ( sha
[ j
]);
595 /* Try to lookup the Lua function */
596 lua_getglobal ( lua
, funcname
);
597 if ( lua_isnil ( lua
, 1 )) {
598 lua_pop ( lua
, 1 ); /* remove the nil from the stack */
599 /* Function not defined... let's define it if we have the
600 * body of the funciton. If this is an EVALSHA call we can just
601 * return an error. */
603 addReply ( c
, shared
. noscripterr
);
606 if ( luaCreateFunction ( c
, lua
, funcname
, c
-> argv
[ 1 ]) == REDIS_ERR
) return ;
607 /* Now the following is guaranteed to return non nil */
608 lua_getglobal ( lua
, funcname
);
609 redisAssert (! lua_isnil ( lua
, 1 ));
612 /* Populate the argv and keys table accordingly to the arguments that
614 luaSetGlobalArray ( lua
, "KEYS" , c
-> argv
+ 3 , numkeys
);
615 luaSetGlobalArray ( lua
, "ARGV" , c
-> argv
+ 3 + numkeys
, c
-> argc
- 3 - numkeys
);
617 /* Select the right DB in the context of the Lua client */
618 selectDb ( server
. lua_client
, c
-> db
-> id
);
620 /* Set an hook in order to be able to stop the script execution if it
621 * is running for too much time.
622 * We set the hook only if the time limit is enabled as the hook will
623 * make the Lua script execution slower. */
624 if ( server
. lua_time_limit
> 0 && server
. masterhost
== NULL
) {
625 lua_sethook ( lua
, luaMaskCountHook
, LUA_MASKCOUNT
, 100000 );
627 lua_sethook ( lua
, luaMaskCountHook
, 0 , 0 );
630 /* At this point whatever this script was never seen before or if it was
631 * already defined, we can call it. We have zero arguments and expect
632 * a single return value. */
633 server
. lua_caller
= c
;
634 server
. lua_time_start
= ustime ()/ 1000 ;
636 if ( lua_pcall ( lua
, 0 , 1 , 0 )) {
637 if ( server
. lua_timedout
) {
638 server
. lua_timedout
= 0 ;
639 /* Restore the readable handler that was unregistered when the
640 * script timeout was detected. */
641 aeCreateFileEvent ( server
. el
, c
-> fd
, AE_READABLE
,
642 readQueryFromClient
, c
);
644 server
. lua_caller
= NULL
;
645 selectDb ( c
, server
. lua_client
-> db
-> id
); /* set DB ID from Lua client */
646 addReplyErrorFormat ( c
, "Error running script (call to %s ): %s \n " ,
647 funcname
, lua_tostring ( lua
,- 1 ));
649 lua_gc ( lua
, LUA_GCCOLLECT
, 0 );
652 server
. lua_timedout
= 0 ;
653 server
. lua_caller
= NULL
;
654 selectDb ( c
, server
. lua_client
-> db
-> id
); /* set DB ID from Lua client */
655 luaReplyToRedisReply ( c
, lua
);
656 lua_gc ( lua
, LUA_GCSTEP
, 1 );
658 /* If we have slaves attached we want to replicate this command as
659 * EVAL instead of EVALSHA. We do this also in the AOF as currently there
660 * is no easy way to propagate a command in a different way in the AOF
661 * and in the replication link.
663 * IMPROVEMENT POSSIBLE:
664 * 1) Replicate this command as EVALSHA in the AOF.
665 * 2) Remember what slave already received a given script, and replicate
666 * the EVALSHA against this slaves when possible.
669 robj
* script
= dictFetchValue ( server
. lua_scripts
, c
-> argv
[ 1 ]-> ptr
);
671 redisAssertWithInfo ( c
, NULL
, script
!= NULL
);
672 rewriteClientCommandArgument ( c
, 0 ,
673 resetRefCount ( createStringObject ( "EVAL" , 4 )));
674 rewriteClientCommandArgument ( c
, 1 , script
);
678 void evalCommand ( redisClient
* c
) {
679 evalGenericCommand ( c
, 0 );
682 void evalShaCommand ( redisClient
* c
) {
683 if ( sdslen ( c
-> argv
[ 1 ]-> ptr
) != 40 ) {
684 /* We know that a match is not possible if the provided SHA is
685 * not the right length. So we return an error ASAP, this way
686 * evalGenericCommand() can be implemented without string length
688 addReply ( c
, shared
. noscripterr
);
691 evalGenericCommand ( c
, 1 );
694 /* We replace math.random() with our implementation that is not affected
695 * by specific libc random() implementations and will output the same sequence
696 * (for the same seed) in every arch. */
698 /* The following implementation is the one shipped with Lua itself but with
699 * rand() replaced by redisLrand48(). */
700 int redis_math_random ( lua_State
* L
) {
701 /* the `%' avoids the (rare) case of r==1, and is needed also because on
702 some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
703 lua_Number r
= ( lua_Number
)( redisLrand48 () %REDIS_LRAND
48 _MAX
) /
704 ( lua_Number
) REDIS_LRAND48_MAX
;
705 switch ( lua_gettop ( L
)) { /* check number of arguments */
706 case 0 : { /* no arguments */
707 lua_pushnumber ( L
, r
); /* Number between 0 and 1 */
710 case 1 : { /* only upper limit */
711 int u
= luaL_checkint ( L
, 1 );
712 luaL_argcheck ( L
, 1 <= u
, 1 , "interval is empty" );
713 lua_pushnumber ( L
, floor ( r
* u
)+ 1 ); /* int between 1 and `u' */
716 case 2 : { /* lower and upper limits */
717 int l
= luaL_checkint ( L
, 1 );
718 int u
= luaL_checkint ( L
, 2 );
719 luaL_argcheck ( L
, l
<= u
, 2 , "interval is empty" );
720 lua_pushnumber ( L
, floor ( r
*( u
- l
+ 1 ))+ l
); /* int between `l' and `u' */
723 default : return luaL_error ( L
, "wrong number of arguments" );
728 int redis_math_randomseed ( lua_State
* L
) {
729 redisSrand48 ( luaL_checkint ( L
, 1 ));
733 /* ---------------------------------------------------------------------------
734 * SCRIPT command for script environment introspection and control
735 * ------------------------------------------------------------------------- */
737 void scriptCommand ( redisClient
* c
) {
738 if ( c
-> argc
== 2 && ! strcasecmp ( c
-> argv
[ 1 ]-> ptr
, "flush" )) {
740 addReply ( c
, shared
. ok
);
741 server
. dirty
++; /* Replicating this command is a good idea. */
742 } else if ( c
-> argc
>= 2 && ! strcasecmp ( c
-> argv
[ 1 ]-> ptr
, "exists" )) {
745 addReplyMultiBulkLen ( c
, c
-> argc
- 2 );
746 for ( j
= 2 ; j
< c
-> argc
; j
++) {
747 if ( dictFind ( server
. lua_scripts
, c
-> argv
[ j
]-> ptr
))
748 addReply ( c
, shared
. cone
);
750 addReply ( c
, shared
. czero
);
752 } else if ( c
-> argc
== 3 && ! strcasecmp ( c
-> argv
[ 1 ]-> ptr
, "load" )) {
758 hashScript ( funcname
+ 2 , c
-> argv
[ 2 ]-> ptr
, sdslen ( c
-> argv
[ 2 ]-> ptr
));
759 sha
= sdsnewlen ( funcname
+ 2 , 40 );
760 if ( dictFind ( server
. lua_scripts
, sha
) == NULL
) {
761 if ( luaCreateFunction ( c
, server
. lua
, funcname
, c
-> argv
[ 2 ])
767 addReplyBulkCBuffer ( c
, funcname
+ 2 , 40 );
769 } else if ( c
-> argc
== 2 && ! strcasecmp ( c
-> argv
[ 1 ]-> ptr
, "kill" )) {
770 if ( server
. lua_caller
== NULL
) {
771 addReplyError ( c
, "No scripts in execution right now." );
772 } else if ( server
. lua_write_dirty
) {
773 addReplyError ( c
, "Sorry the script already executed write commands against the dataset. You can either wait the script termination or kill the server in an hard way using the SHUTDOWN NOSAVE command." );
776 addReply ( c
, shared
. ok
);
779 addReplyError ( c
, "Unknown SCRIPT subcommand or wrong # of args." );