Commit 532e0f5d authored by antirez's avatar antirez
Browse files

full conversion from Lua return value to redis reply. Partial conversion from...

full conversion from Lua return value to redis reply. Partial conversion from Redis reply to Lua type.
parent 4ae5b5e1
......@@ -5,6 +5,76 @@
#include <lauxlib.h>
#include <lualib.h>
char *redisProtocolToLuaType_Int(lua_State *lua, char *reply);
char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply);
char *redisProtocolToLuaType_Status(lua_State *lua, char *reply);
/* Take a Redis reply in the Redis protocol format and convert it into a
* Lua type. Thanks to this function, and the introduction of not connected
* clients, it is trvial to implement the redis() lua function.
*
* Basically we take the arguments, execute the Redis command in the context
* of a non connected client, then take the generated reply and convert it
* into a suitable Lua type. With this trick the scripting feature does not
* need the introduction of a full Redis internals API. Basically the script
* is like a normal client that bypasses all the slow I/O paths.
*
* Note: in this function we do not do any sanity check as the reply is
* generated by Redis directly. This allows use to go faster.
* The reply string can be altered during the parsing as it is discared
* after the conversion is completed.
*
* Errors are returned as a table with a single 'err' field set to the
* error string.
*/
char *redisProtocolToLuaType(lua_State *lua, char* reply) {
char *p = reply;
switch(*p) {
case ':':
p = redisProtocolToLuaType_Int(lua,reply);
break;
case '$':
p = redisProtocolToLuaType_Bulk(lua,reply);
break;
case '+':
p = redisProtocolToLuaType_Status(lua,reply);
break;
}
return p;
}
char *redisProtocolToLuaType_Int(lua_State *lua, char *reply) {
char *p = strchr(reply+1,'\r');
long long value;
string2ll(reply+1,p-reply-1,&value);
lua_pushnumber(lua,(lua_Number)value);
return p+2;
}
char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply) {
char *p = strchr(reply+1,'\r');
long long bulklen;
string2ll(reply+1,p-reply-1,&bulklen);
if (bulklen == 0) {
lua_pushnil(lua);
return p+2;
} else {
lua_pushlstring(lua,p+2,bulklen);
return p+2+bulklen+2;
}
}
char *redisProtocolToLuaType_Status(lua_State *lua, char *reply) {
char *p = strchr(reply+1,'\r');
lua_pushlstring(lua,reply+1,p-reply-1);
return p+2;
}
int luaRedisCommand(lua_State *lua) {
int j, argc = lua_gettop(lua);
struct redisCommand *cmd;
......@@ -12,18 +82,24 @@ int luaRedisCommand(lua_State *lua) {
redisClient *c = server.lua_client;
sds reply;
/* Build the arguments vector */
argv = zmalloc(sizeof(robj*)*argc);
for (j = 0; j < argc; j++)
argv[j] = createStringObject((char*)lua_tostring(lua,j+1),lua_strlen(lua,j+1));
argv[j] = createStringObject((char*)lua_tostring(lua,j+1),
lua_strlen(lua,j+1));
/* Command lookup */
cmd = lookupCommand(argv[0]->ptr);
if (!cmd) {
for (j = 0; j < argc; j++) decrRefCount(argv[j]);
zfree(argv);
lua_pushnil(lua);
lua_newtable(lua);
lua_pushstring(lua,"err");
lua_pushstring(lua,"Unknown Redis command called from Lua script");
return 2;
lua_settable(lua,-3);
return 1;
}
/* Run the command in the context of a fake client */
c->argv = argv;
c->argc = argc;
......@@ -43,7 +119,7 @@ int luaRedisCommand(lua_State *lua) {
sdscatlen(reply,o->ptr,sdslen(o->ptr));
listDelNode(c->reply,listFirst(c->reply));
}
lua_pushlstring(lua,reply,sdslen(reply));
redisProtocolToLuaType(lua,reply);
sdsfree(reply);
/* Clean up. Command code may have changed argv/argc so we use the
......@@ -104,6 +180,42 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
case LUA_TNUMBER:
addReplyLongLong(c,(long long)lua_tonumber(lua,1));
break;
case LUA_TTABLE:
/* We need to check if it is an array or an error.
* Error are returned as a single element table with 'err' field. */
lua_pushstring(lua,"err");
lua_gettable(lua,-2);
t = lua_type(lua,-1);
if (t == LUA_TSTRING) {
addReplyError(c,(char*)lua_tostring(lua,-1));
lua_pop(lua,1);
} else {
void *replylen = addDeferredMultiBulkLength(c);
int j = 1, mbulklen = 0;
lua_pop(lua,1); /* Discard the 'err' 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;
} else if (t == LUA_TSTRING) {
size_t len;
char *s = (char*) lua_tolstring(lua,-1,&len);
addReplyBulkCBuffer(c,s,len);
mbulklen++;
} else if (t == LUA_TNUMBER) {
addReplyLongLong(c,(long long)lua_tonumber(lua,-1));
mbulklen++;
}
lua_pop(lua,1);
}
setDeferredMultiBulkLength(c,replylen,mbulklen);
}
break;
default:
addReply(c,shared.nullbulk);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment