mirror of
https://github.com/fluencelabs/redis
synced 2025-06-16 18:51:20 +00:00
Merge branch 'unstable' into modules_fork
This commit is contained in:
258
src/scripting.c
258
src/scripting.c
@ -42,7 +42,10 @@ char *redisProtocolToLuaType_Int(lua_State *lua, char *reply);
|
||||
char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply);
|
||||
char *redisProtocolToLuaType_Status(lua_State *lua, char *reply);
|
||||
char *redisProtocolToLuaType_Error(lua_State *lua, char *reply);
|
||||
char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype);
|
||||
char *redisProtocolToLuaType_Aggregate(lua_State *lua, char *reply, int atype);
|
||||
char *redisProtocolToLuaType_Null(lua_State *lua, char *reply);
|
||||
char *redisProtocolToLuaType_Bool(lua_State *lua, char *reply, int tf);
|
||||
char *redisProtocolToLuaType_Double(lua_State *lua, char *reply);
|
||||
int redis_math_random (lua_State *L);
|
||||
int redis_math_randomseed (lua_State *L);
|
||||
void ldbInit(void);
|
||||
@ -132,9 +135,12 @@ char *redisProtocolToLuaType(lua_State *lua, char* reply) {
|
||||
case '$': p = redisProtocolToLuaType_Bulk(lua,reply); break;
|
||||
case '+': p = redisProtocolToLuaType_Status(lua,reply); break;
|
||||
case '-': p = redisProtocolToLuaType_Error(lua,reply); break;
|
||||
case '*': p = redisProtocolToLuaType_MultiBulk(lua,reply,*p); break;
|
||||
case '%': p = redisProtocolToLuaType_MultiBulk(lua,reply,*p); break;
|
||||
case '~': p = redisProtocolToLuaType_MultiBulk(lua,reply,*p); break;
|
||||
case '*': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;
|
||||
case '%': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;
|
||||
case '~': p = redisProtocolToLuaType_Aggregate(lua,reply,*p); break;
|
||||
case '_': p = redisProtocolToLuaType_Null(lua,reply); break;
|
||||
case '#': p = redisProtocolToLuaType_Bool(lua,reply,p[1]); break;
|
||||
case ',': p = redisProtocolToLuaType_Double(lua,reply); break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -182,13 +188,13 @@ char *redisProtocolToLuaType_Error(lua_State *lua, char *reply) {
|
||||
return p+2;
|
||||
}
|
||||
|
||||
char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype) {
|
||||
char *redisProtocolToLuaType_Aggregate(lua_State *lua, char *reply, int atype) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
long long mbulklen;
|
||||
int j = 0;
|
||||
|
||||
string2ll(reply+1,p-reply-1,&mbulklen);
|
||||
if (server.lua_caller->resp == 2 || atype == '*') {
|
||||
if (server.lua_client->resp == 2 || atype == '*') {
|
||||
p += 2;
|
||||
if (mbulklen == -1) {
|
||||
lua_pushboolean(lua,0);
|
||||
@ -200,11 +206,15 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype) {
|
||||
p = redisProtocolToLuaType(lua,p);
|
||||
lua_settable(lua,-3);
|
||||
}
|
||||
} else if (server.lua_caller->resp == 3) {
|
||||
} else if (server.lua_client->resp == 3) {
|
||||
/* Here we handle only Set and Map replies in RESP3 mode, since arrays
|
||||
* follow the above RESP2 code path. */
|
||||
* follow the above RESP2 code path. Note that those are represented
|
||||
* as a table with the "map" or "set" field populated with the actual
|
||||
* table representing the set or the map type. */
|
||||
p += 2;
|
||||
lua_newtable(lua);
|
||||
lua_pushstring(lua,atype == '%' ? "map" : "set");
|
||||
lua_newtable(lua);
|
||||
for (j = 0; j < mbulklen; j++) {
|
||||
p = redisProtocolToLuaType(lua,p);
|
||||
if (atype == '%') {
|
||||
@ -214,10 +224,44 @@ char *redisProtocolToLuaType_MultiBulk(lua_State *lua, char *reply, int atype) {
|
||||
}
|
||||
lua_settable(lua,-3);
|
||||
}
|
||||
lua_settable(lua,-3);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char *redisProtocolToLuaType_Null(lua_State *lua, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
lua_pushnil(lua);
|
||||
return p+2;
|
||||
}
|
||||
|
||||
char *redisProtocolToLuaType_Bool(lua_State *lua, char *reply, int tf) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
lua_pushboolean(lua,tf == 't');
|
||||
return p+2;
|
||||
}
|
||||
|
||||
char *redisProtocolToLuaType_Double(lua_State *lua, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
char buf[MAX_LONG_DOUBLE_CHARS+1];
|
||||
size_t len = p-reply-1;
|
||||
double d;
|
||||
|
||||
if (len <= MAX_LONG_DOUBLE_CHARS) {
|
||||
memcpy(buf,reply+1,len);
|
||||
buf[len] = '\0';
|
||||
d = strtod(buf,NULL); /* We expect a valid representation. */
|
||||
} else {
|
||||
d = 0;
|
||||
}
|
||||
|
||||
lua_newtable(lua);
|
||||
lua_pushstring(lua,"double");
|
||||
lua_pushnumber(lua,d);
|
||||
lua_settable(lua,-3);
|
||||
return p+2;
|
||||
}
|
||||
|
||||
/* This function is used in order to push an error on the Lua stack in the
|
||||
* format used by redis.pcall to return errors, which is a lua table
|
||||
* with a single "err" field set to the error string. Note that this
|
||||
@ -292,6 +336,8 @@ void luaSortArray(lua_State *lua) {
|
||||
* Lua reply to Redis reply conversion functions.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Reply to client 'c' converting the top element in the Lua stack to a
|
||||
* Redis reply. As a side effect the element is consumed from the stack. */
|
||||
void luaReplyToRedisReply(client *c, lua_State *lua) {
|
||||
int t = lua_type(lua,-1);
|
||||
|
||||
@ -300,7 +346,11 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
|
||||
addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1));
|
||||
break;
|
||||
case LUA_TBOOLEAN:
|
||||
addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.null[c->resp]);
|
||||
if (server.lua_client->resp == 2)
|
||||
addReply(c,lua_toboolean(lua,-1) ? shared.cone :
|
||||
shared.null[c->resp]);
|
||||
else
|
||||
addReplyBool(c,lua_toboolean(lua,-1));
|
||||
break;
|
||||
case LUA_TNUMBER:
|
||||
addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
|
||||
@ -310,6 +360,8 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
|
||||
* Error are returned as a single element table with 'err' field.
|
||||
* Status replies are returned as single element table with 'ok'
|
||||
* field. */
|
||||
|
||||
/* Handle error reply. */
|
||||
lua_pushstring(lua,"err");
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
@ -321,8 +373,9 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
|
||||
lua_pop(lua,2);
|
||||
return;
|
||||
}
|
||||
lua_pop(lua,1); /* Discard field name pushed before. */
|
||||
|
||||
lua_pop(lua,1);
|
||||
/* Handle status reply. */
|
||||
lua_pushstring(lua,"ok");
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
@ -331,25 +384,81 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
|
||||
sdsmapchars(ok,"\r\n"," ",2);
|
||||
addReplySds(c,sdscatprintf(sdsempty(),"+%s\r\n",ok));
|
||||
sdsfree(ok);
|
||||
lua_pop(lua,1);
|
||||
} else {
|
||||
void *replylen = addReplyDeferredLen(c);
|
||||
int j = 1, mbulklen = 0;
|
||||
|
||||
lua_pop(lua,1); /* Discard the 'ok' field value we popped */
|
||||
while(1) {
|
||||
lua_pushnumber(lua,j++);
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
if (t == LUA_TNIL) {
|
||||
lua_pop(lua,1);
|
||||
break;
|
||||
}
|
||||
luaReplyToRedisReply(c, lua);
|
||||
mbulklen++;
|
||||
}
|
||||
setDeferredArrayLen(c,replylen,mbulklen);
|
||||
lua_pop(lua,2);
|
||||
return;
|
||||
}
|
||||
lua_pop(lua,1); /* Discard field name pushed before. */
|
||||
|
||||
/* Handle double reply. */
|
||||
lua_pushstring(lua,"double");
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
if (t == LUA_TNUMBER) {
|
||||
addReplyDouble(c,lua_tonumber(lua,-1));
|
||||
lua_pop(lua,2);
|
||||
return;
|
||||
}
|
||||
lua_pop(lua,1); /* Discard field name pushed before. */
|
||||
|
||||
/* Handle map reply. */
|
||||
lua_pushstring(lua,"map");
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
if (t == LUA_TTABLE) {
|
||||
int maplen = 0;
|
||||
void *replylen = addReplyDeferredLen(c);
|
||||
lua_pushnil(lua); /* Use nil to start iteration. */
|
||||
while (lua_next(lua,-2)) {
|
||||
/* Stack now: table, key, value */
|
||||
luaReplyToRedisReply(c, lua); /* Return value. */
|
||||
lua_pushvalue(lua,-1); /* Dup key before consuming. */
|
||||
luaReplyToRedisReply(c, lua); /* Return key. */
|
||||
/* Stack now: table, key. */
|
||||
maplen++;
|
||||
}
|
||||
setDeferredMapLen(c,replylen,maplen);
|
||||
lua_pop(lua,2);
|
||||
return;
|
||||
}
|
||||
lua_pop(lua,1); /* Discard field name pushed before. */
|
||||
|
||||
/* Handle set reply. */
|
||||
lua_pushstring(lua,"set");
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
if (t == LUA_TTABLE) {
|
||||
int setlen = 0;
|
||||
void *replylen = addReplyDeferredLen(c);
|
||||
lua_pushnil(lua); /* Use nil to start iteration. */
|
||||
while (lua_next(lua,-2)) {
|
||||
/* Stack now: table, key, true */
|
||||
lua_pop(lua,1); /* Discard the boolean value. */
|
||||
lua_pushvalue(lua,-1); /* Dup key before consuming. */
|
||||
luaReplyToRedisReply(c, lua); /* Return key. */
|
||||
/* Stack now: table, key. */
|
||||
setlen++;
|
||||
}
|
||||
setDeferredSetLen(c,replylen,setlen);
|
||||
lua_pop(lua,2);
|
||||
return;
|
||||
}
|
||||
lua_pop(lua,1); /* Discard field name pushed before. */
|
||||
|
||||
/* Handle the array reply. */
|
||||
void *replylen = addReplyDeferredLen(c);
|
||||
int j = 1, mbulklen = 0;
|
||||
while(1) {
|
||||
lua_pushnumber(lua,j++);
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
if (t == LUA_TNIL) {
|
||||
lua_pop(lua,1);
|
||||
break;
|
||||
}
|
||||
luaReplyToRedisReply(c, lua);
|
||||
mbulklen++;
|
||||
}
|
||||
setDeferredArrayLen(c,replylen,mbulklen);
|
||||
break;
|
||||
default:
|
||||
addReplyNull(c);
|
||||
@ -859,6 +968,25 @@ int luaLogCommand(lua_State *lua) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* redis.setresp() */
|
||||
int luaSetResp(lua_State *lua) {
|
||||
int argc = lua_gettop(lua);
|
||||
|
||||
if (argc != 1) {
|
||||
lua_pushstring(lua, "redis.setresp() requires one argument.");
|
||||
return lua_error(lua);
|
||||
}
|
||||
|
||||
int resp = lua_tonumber(lua,-argc);
|
||||
if (resp != 2 && resp != 3) {
|
||||
lua_pushstring(lua, "RESP version must be 2 or 3.");
|
||||
return lua_error(lua);
|
||||
}
|
||||
|
||||
server.lua_client->resp = resp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* Lua engine initialization and reset.
|
||||
* ------------------------------------------------------------------------- */
|
||||
@ -986,6 +1114,11 @@ void scriptingInit(int setup) {
|
||||
lua_pushcfunction(lua,luaLogCommand);
|
||||
lua_settable(lua,-3);
|
||||
|
||||
/* redis.setresp */
|
||||
lua_pushstring(lua,"setresp");
|
||||
lua_pushcfunction(lua,luaSetResp);
|
||||
lua_settable(lua,-3);
|
||||
|
||||
lua_pushstring(lua,"LOG_DEBUG");
|
||||
lua_pushnumber(lua,LL_DEBUG);
|
||||
lua_settable(lua,-3);
|
||||
@ -1379,8 +1512,9 @@ void evalGenericCommand(client *c, int evalsha) {
|
||||
luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys);
|
||||
luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys);
|
||||
|
||||
/* Select the right DB in the context of the Lua client */
|
||||
/* Set the Lua client database and protocol. */
|
||||
selectDb(server.lua_client,c->db->id);
|
||||
server.lua_client->resp = 2; /* Default is RESP2, scripts can change it. */
|
||||
|
||||
/* Set a hook in order to be able to stop the script execution if it
|
||||
* is running for too much time.
|
||||
@ -2052,6 +2186,11 @@ char *ldbRedisProtocolToHuman_Int(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Bulk(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Status(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_MultiBulk(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Set(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Map(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Null(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Bool(sds *o, char *reply);
|
||||
char *ldbRedisProtocolToHuman_Double(sds *o, char *reply);
|
||||
|
||||
/* Get Redis protocol from 'reply' and appends it in human readable form to
|
||||
* the passed SDS string 'o'.
|
||||
@ -2066,6 +2205,11 @@ char *ldbRedisProtocolToHuman(sds *o, char *reply) {
|
||||
case '+': p = ldbRedisProtocolToHuman_Status(o,reply); break;
|
||||
case '-': p = ldbRedisProtocolToHuman_Status(o,reply); break;
|
||||
case '*': p = ldbRedisProtocolToHuman_MultiBulk(o,reply); break;
|
||||
case '~': p = ldbRedisProtocolToHuman_Set(o,reply); break;
|
||||
case '%': p = ldbRedisProtocolToHuman_Map(o,reply); break;
|
||||
case '_': p = ldbRedisProtocolToHuman_Null(o,reply); break;
|
||||
case '#': p = ldbRedisProtocolToHuman_Bool(o,reply); break;
|
||||
case ',': p = ldbRedisProtocolToHuman_Double(o,reply); break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
@ -2120,6 +2264,62 @@ char *ldbRedisProtocolToHuman_MultiBulk(sds *o, char *reply) {
|
||||
return p;
|
||||
}
|
||||
|
||||
char *ldbRedisProtocolToHuman_Set(sds *o, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
long long mbulklen;
|
||||
int j = 0;
|
||||
|
||||
string2ll(reply+1,p-reply-1,&mbulklen);
|
||||
p += 2;
|
||||
*o = sdscatlen(*o,"~(",2);
|
||||
for (j = 0; j < mbulklen; j++) {
|
||||
p = ldbRedisProtocolToHuman(o,p);
|
||||
if (j != mbulklen-1) *o = sdscatlen(*o,",",1);
|
||||
}
|
||||
*o = sdscatlen(*o,")",1);
|
||||
return p;
|
||||
}
|
||||
|
||||
char *ldbRedisProtocolToHuman_Map(sds *o, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
long long mbulklen;
|
||||
int j = 0;
|
||||
|
||||
string2ll(reply+1,p-reply-1,&mbulklen);
|
||||
p += 2;
|
||||
*o = sdscatlen(*o,"{",1);
|
||||
for (j = 0; j < mbulklen; j++) {
|
||||
p = ldbRedisProtocolToHuman(o,p);
|
||||
*o = sdscatlen(*o," => ",4);
|
||||
p = ldbRedisProtocolToHuman(o,p);
|
||||
if (j != mbulklen-1) *o = sdscatlen(*o,",",1);
|
||||
}
|
||||
*o = sdscatlen(*o,"}",1);
|
||||
return p;
|
||||
}
|
||||
|
||||
char *ldbRedisProtocolToHuman_Null(sds *o, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
*o = sdscatlen(*o,"(null)",6);
|
||||
return p+2;
|
||||
}
|
||||
|
||||
char *ldbRedisProtocolToHuman_Bool(sds *o, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
if (reply[1] == 't')
|
||||
*o = sdscatlen(*o,"#true",5);
|
||||
else
|
||||
*o = sdscatlen(*o,"#false",6);
|
||||
return p+2;
|
||||
}
|
||||
|
||||
char *ldbRedisProtocolToHuman_Double(sds *o, char *reply) {
|
||||
char *p = strchr(reply+1,'\r');
|
||||
*o = sdscatlen(*o,"(double) ",9);
|
||||
*o = sdscatlen(*o,reply+1,p-reply-1);
|
||||
return p+2;
|
||||
}
|
||||
|
||||
/* Log a Redis reply as debugger output, in an human readable format.
|
||||
* If the resulting string is longer than 'len' plus a few more chars
|
||||
* used as prefix, it gets truncated. */
|
||||
|
Reference in New Issue
Block a user