From 7229d60d0356e94f553623e0492f636687ac39e6 Mon Sep 17 00:00:00 2001 From: antirez Date: Fri, 13 May 2011 22:02:38 +0200 Subject: [PATCH] EVALSHA implemented --- src/redis.c | 5 ++++- src/redis.h | 3 ++- src/scripting.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/redis.c b/src/redis.c index 72477356..d5470ff3 100644 --- a/src/redis.c +++ b/src/redis.c @@ -194,7 +194,8 @@ struct redisCommand redisCommandTable[] = { {"dump",dumpCommand,2,0,NULL,0,0,0,0,0}, {"object",objectCommand,-2,0,NULL,0,0,0,0,0}, {"client",clientCommand,-2,0,NULL,0,0,0,0,0}, - {"eval",evalCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0} + {"eval",evalCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0}, + {"evalsha",evalShaCommand,-3,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0} }; /*============================ Utility functions ============================ */ @@ -781,6 +782,8 @@ void createSharedObjects(void) { "-ERR source and destination objects are the same\r\n")); shared.outofrangeerr = createObject(REDIS_STRING,sdsnew( "-ERR index out of range\r\n")); + shared.noscripterr = createObject(REDIS_STRING,sdsnew( + "-NOSCRIPT No matching script. Please use EVAL.\r\n")); shared.loadingerr = createObject(REDIS_STRING,sdsnew( "-LOADING Redis is loading the dataset in memory\r\n")); shared.space = createObject(REDIS_STRING,sdsnew(" ")); diff --git a/src/redis.h b/src/redis.h index 50d669ae..c2551eb8 100644 --- a/src/redis.h +++ b/src/redis.h @@ -365,7 +365,7 @@ struct sharedObjectsStruct { robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *cnegone, *pong, *space, *colon, *nullbulk, *nullmultibulk, *queued, *emptymultibulk, *wrongtypeerr, *nokeyerr, *syntaxerr, *sameobjecterr, - *outofrangeerr, *loadingerr, *plus, + *outofrangeerr, *noscripterr, *loadingerr, *plus, *select0, *select1, *select2, *select3, *select4, *select5, *select6, *select7, *select8, *select9, *messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3, @@ -1217,6 +1217,7 @@ void dumpCommand(redisClient *c); void objectCommand(redisClient *c); void clientCommand(redisClient *c); void evalCommand(redisClient *c); +void evalShaCommand(redisClient *c); #if defined(__GNUC__) void *calloc(size_t count, size_t size) __attribute__ ((deprecated)); diff --git a/src/scripting.c b/src/scripting.c index 21b3214d..0b6eb616 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -4,6 +4,7 @@ #include #include #include +#include char *redisProtocolToLuaType_Int(lua_State *lua, char *reply); char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply); @@ -320,7 +321,7 @@ void luaSetGlobalArray(lua_State *lua, char *var, robj **elev, int elec) { lua_setglobal(lua,var); } -void evalCommand(redisClient *c) { +void evalGenericCommand(redisClient *c, int evalsha) { lua_State *lua = server.lua; char funcname[43]; long long numkeys; @@ -337,11 +338,32 @@ void evalCommand(redisClient *c) { * defined into the Lua state */ funcname[0] = 'f'; funcname[1] = '_'; - hashScript(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr)); + if (!evalsha) { + /* Hash the code if this is an EVAL call */ + hashScript(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr)); + } else { + /* We already have the SHA if it is a EVALSHA */ + int j; + char *sha = c->argv[1]->ptr; + + for (j = 0; j < 40; j++) + funcname[j+2] = tolower(sha[j]); + funcname[42] = '\0'; + } + lua_getglobal(lua, funcname); if (lua_isnil(lua,1)) { - /* Function not defined... let's define it. */ - sds funcdef = sdsempty(); + sds funcdef; + + /* Function not defined... let's define it if we have the + * body of the funciton. If this is an EVALSHA call we can just + * return an error. */ + if (evalsha) { + addReply(c, shared.noscripterr); + lua_pop(lua,1); /* remove the nil from the stack */ + return; + } + funcdef = sdsempty(); lua_pop(lua,1); /* remove the nil from the stack */ funcdef = sdscat(funcdef,"function "); @@ -402,3 +424,19 @@ void evalCommand(redisClient *c) { luaReplyToRedisReply(c,lua); lua_gc(lua,LUA_GCSTEP,1); } + +void evalCommand(redisClient *c) { + evalGenericCommand(c,0); +} + +void evalShaCommand(redisClient *c) { + if (sdslen(c->argv[1]->ptr) != 40) { + /* We know that a match is not possible if the provided SHA is + * not the right length. So we return an error ASAP, this way + * evalGenericCommand() can be implemented without string length + * sanity check */ + addReply(c, shared.noscripterr); + return; + } + evalGenericCommand(c,1); +} -- 2.45.2