summaryrefslogtreecommitdiff
path: root/src/scripting.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/scripting.c')
-rw-r--r--src/scripting.c347
1 files changed, 246 insertions, 101 deletions
diff --git a/src/scripting.c b/src/scripting.c
index b485d01ed..87b3b036b 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -32,6 +32,7 @@
#include "rand.h"
#include "cluster.h"
#include "monotonic.h"
+#include "resp_parser.h"
#include <lua.h>
#include <lauxlib.h>
@@ -39,14 +40,21 @@
#include <ctype.h>
#include <math.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);
-char *redisProtocolToLuaType_Error(lua_State *lua, char *reply);
-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);
+static void redisProtocolToLuaType_Int(void *ctx, long long val, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_BulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_NullBulkString(void *ctx, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_NullArray(void *ctx, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_Status(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_Error(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_Array(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
+static void redisProtocolToLuaType_Map(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
+static void redisProtocolToLuaType_Set(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
+static void redisProtocolToLuaType_Null(void *ctx, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_Bool(void *ctx, int val, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_BigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_VerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len);
+static void redisProtocolToLuaType_Attribute(struct ReplyParser *parser, void *ctx, size_t len, const char *proto);
int redis_math_random (lua_State *L);
int redis_math_randomseed (lua_State *L);
void ldbInit(void);
@@ -128,139 +136,238 @@ void sha1hex(char *digest, char *script, size_t len) {
* 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;
- case '-': p = redisProtocolToLuaType_Error(lua,reply); 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;
+static const ReplyParserCallbacks DefaultLuaTypeParserCallbacks = {
+ .null_array_callback = redisProtocolToLuaType_NullArray,
+ .bulk_string_callback = redisProtocolToLuaType_BulkString,
+ .null_bulk_string_callback = redisProtocolToLuaType_NullBulkString,
+ .error_callback = redisProtocolToLuaType_Error,
+ .simple_str_callback = redisProtocolToLuaType_Status,
+ .long_callback = redisProtocolToLuaType_Int,
+ .array_callback = redisProtocolToLuaType_Array,
+ .set_callback = redisProtocolToLuaType_Set,
+ .map_callback = redisProtocolToLuaType_Map,
+ .bool_callback = redisProtocolToLuaType_Bool,
+ .double_callback = redisProtocolToLuaType_Double,
+ .null_callback = redisProtocolToLuaType_Null,
+ .big_number_callback = redisProtocolToLuaType_BigNumber,
+ .verbatim_string_callback = redisProtocolToLuaType_VerbatimString,
+ .attribute_callback = redisProtocolToLuaType_Attribute,
+ .error = NULL,
+};
+
+void redisProtocolToLuaType(lua_State *lua, char* reply) {
+ ReplyParser parser = {.curr_location = reply, .callbacks = DefaultLuaTypeParserCallbacks};
+
+ parseReply(&parser, lua);
+}
+
+static void redisProtocolToLuaType_Int(void *ctx, long long val, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
}
- return p;
+
+ lua_State *lua = ctx;
+ lua_pushnumber(lua,(lua_Number)val);
}
-char *redisProtocolToLuaType_Int(lua_State *lua, char *reply) {
- char *p = strchr(reply+1,'\r');
- long long value;
+static void redisProtocolToLuaType_NullBulkString(void *ctx, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
- string2ll(reply+1,p-reply-1,&value);
- lua_pushnumber(lua,(lua_Number)value);
- return p+2;
+ lua_State *lua = ctx;
+ lua_pushboolean(lua,0);
}
-char *redisProtocolToLuaType_Bulk(lua_State *lua, char *reply) {
- char *p = strchr(reply+1,'\r');
- long long bulklen;
+static void redisProtocolToLuaType_NullArray(void *ctx, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+ lua_State *lua = ctx;
+ lua_pushboolean(lua,0);
+}
- string2ll(reply+1,p-reply-1,&bulklen);
- if (bulklen == -1) {
- lua_pushboolean(lua,0);
- return p+2;
- } else {
- lua_pushlstring(lua,p+2,bulklen);
- return p+2+bulklen+2;
+
+static void redisProtocolToLuaType_BulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
}
+
+ lua_State *lua = ctx;
+ lua_pushlstring(lua,str,len);
}
-char *redisProtocolToLuaType_Status(lua_State *lua, char *reply) {
- char *p = strchr(reply+1,'\r');
+static void redisProtocolToLuaType_Status(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+
+ lua_State *lua = ctx;
lua_newtable(lua);
lua_pushstring(lua,"ok");
- lua_pushlstring(lua,reply+1,p-reply-1);
+ lua_pushlstring(lua,str,len);
lua_settable(lua,-3);
- return p+2;
}
-char *redisProtocolToLuaType_Error(lua_State *lua, char *reply) {
- char *p = strchr(reply+1,'\r');
+static void redisProtocolToLuaType_Error(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+
+ lua_State *lua = ctx;
lua_newtable(lua);
lua_pushstring(lua,"err");
- lua_pushlstring(lua,reply+1,p-reply-1);
+ lua_pushlstring(lua,str,len);
lua_settable(lua,-3);
- return p+2;
}
-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_client->resp == 2 || atype == '*') {
- p += 2;
- if (mbulklen == -1) {
- lua_pushboolean(lua,0);
- return p;
- }
+static void redisProtocolToLuaType_Map(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
+ UNUSED(proto);
+ lua_State *lua = ctx;
+ if (lua) {
lua_newtable(lua);
- for (j = 0; j < mbulklen; j++) {
- lua_pushnumber(lua,j+1);
- p = redisProtocolToLuaType(lua,p);
- lua_settable(lua,-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. 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_pushstring(lua, "map");
+ lua_newtable(lua);
+ }
+ for (size_t j = 0; j < len; j++) {
+ parseReply(parser,lua);
+ parseReply(parser,lua);
+ if (lua) lua_settable(lua,-3);
+ }
+ if (lua) lua_settable(lua,-3);
+}
+
+static void redisProtocolToLuaType_Set(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
+ UNUSED(proto);
+
+ lua_State *lua = ctx;
+ if (lua) {
lua_newtable(lua);
- lua_pushstring(lua,atype == '%' ? "map" : "set");
+ lua_pushstring(lua, "set");
lua_newtable(lua);
- for (j = 0; j < mbulklen; j++) {
- p = redisProtocolToLuaType(lua,p);
- if (atype == '%') {
- p = redisProtocolToLuaType(lua,p);
- } else {
- lua_pushboolean(lua,1);
- }
+ }
+ for (size_t j = 0; j < len; j++) {
+ parseReply(parser,lua);
+ if (lua) {
+ lua_pushboolean(lua,1);
lua_settable(lua,-3);
}
- lua_settable(lua,-3);
}
- return p;
+ if (lua) lua_settable(lua,-3);
}
-char *redisProtocolToLuaType_Null(lua_State *lua, char *reply) {
- char *p = strchr(reply+1,'\r');
+static void redisProtocolToLuaType_Array(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
+ UNUSED(proto);
+
+ lua_State *lua = ctx;
+ if (lua) lua_newtable(lua);
+ for (size_t j = 0; j < len; j++) {
+ if (lua) lua_pushnumber(lua,j+1);
+ parseReply(parser,lua);
+ if (lua) lua_settable(lua,-3);
+ }
+}
+
+static void redisProtocolToLuaType_Attribute(struct ReplyParser *parser, void *ctx, size_t len, const char *proto) {
+ UNUSED(proto);
+
+ /* Parse the attribute reply.
+ * Currently, we do not expose the attribute to the Lua script so
+ * we just need to continue parsing and ignore it (the NULL ensures that the
+ * reply will be ignored). */
+ for (size_t j = 0; j < len; j++) {
+ parseReply(parser,NULL);
+ parseReply(parser,NULL);
+ }
+
+ /* Parse the reply itself. */
+ parseReply(parser,ctx);
+}
+
+static void redisProtocolToLuaType_VerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+
+ lua_State *lua = ctx;
+
+ lua_newtable(lua);
+ lua_pushstring(lua,"verbatim_string");
+ lua_newtable(lua);
+ lua_pushstring(lua,"string");
+ lua_pushlstring(lua,str,len);
+ lua_settable(lua,-3);
+ lua_pushstring(lua,"format");
+ lua_pushlstring(lua,format,3);
+ lua_settable(lua,-3);
+ lua_settable(lua,-3);
+}
+
+static void redisProtocolToLuaType_BigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+
+ lua_State *lua = ctx;
+
+ lua_newtable(lua);
+ lua_pushstring(lua,"big_number");
+ lua_pushlstring(lua,str,len);
+ lua_settable(lua,-3);
+}
+
+static void redisProtocolToLuaType_Null(void *ctx, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+
+ lua_State *lua = ctx;
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;
+static void redisProtocolToLuaType_Bool(void *ctx, int val, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
+ }
+
+ lua_State *lua = ctx;
+ lua_pushboolean(lua,val);
}
-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;
+static void redisProtocolToLuaType_Double(void *ctx, double d, const char *proto, size_t proto_len) {
+ UNUSED(proto);
+ UNUSED(proto_len);
+ if (!ctx) {
+ return;
}
+ lua_State *lua = ctx;
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
@@ -398,6 +505,43 @@ void luaReplyToRedisReply(client *c, lua_State *lua) {
}
lua_pop(lua,1); /* Discard field name pushed before. */
+ /* Handle big number reply. */
+ lua_pushstring(lua,"big_number");
+ lua_gettable(lua,-2);
+ t = lua_type(lua,-1);
+ if (t == LUA_TSTRING) {
+ addReplyBigNum(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1));
+ lua_pop(lua,2);
+ return;
+ }
+ lua_pop(lua,1); /* Discard field name pushed before. */
+
+ /* Handle verbatim reply. */
+ lua_pushstring(lua,"verbatim_string");
+ lua_gettable(lua,-2);
+ t = lua_type(lua,-1);
+ if (t == LUA_TTABLE) {
+ lua_pushstring(lua,"format");
+ lua_gettable(lua,-2);
+ t = lua_type(lua,-1);
+ if (t == LUA_TSTRING){
+ char* format = (char*)lua_tostring(lua,-1);
+ lua_pushstring(lua,"string");
+ lua_gettable(lua,-3);
+ t = lua_type(lua,-1);
+ if (t == LUA_TSTRING){
+ size_t len;
+ char* str = (char*)lua_tolstring(lua,-1,&len);
+ addReplyVerbatim(c, str, len, format);
+ lua_pop(lua,4);
+ return;
+ }
+ lua_pop(lua,1);
+ }
+ lua_pop(lua,1);
+ }
+ lua_pop(lua,1); /* Discard field name pushed before. */
+
/* Handle map reply. */
lua_pushstring(lua,"map");
lua_gettable(lua,-2);
@@ -598,7 +742,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
c->cmd = c->lastcmd = cmd;
/* There are commands that are not allowed inside scripts. */
- if (cmd->flags & CMD_NOSCRIPT) {
+ if (!server.lua_disable_deny_script && (cmd->flags & CMD_NOSCRIPT)) {
luaPushError(lua, "This Redis command is not allowed from scripts");
goto cleanup;
}
@@ -1115,6 +1259,7 @@ void scriptingInit(int setup) {
server.lua_caller = NULL;
server.lua_cur_script = NULL;
server.lua_timedout = 0;
+ server.lua_disable_deny_script = 0;
ldbInit();
}