Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
Checking mergeability… Don’t worry, you can still create the pull request.
  • 10 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
Showing with 177 additions and 6 deletions.
  1. +5 −5 src/Makefile
  2. +147 −0 src/lua.c
  3. +11 −0 src/lua.h
  4. +2 −0 src/networking.c
  5. +4 −0 src/redis-benchmark.c
  6. +5 −1 src/redis.c
  7. +3 −0 src/redis.h
View
10 src/Makefile
@@ -6,12 +6,12 @@ release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
OPTIMIZATION?=-O2
ifeq ($(uname_S),SunOS)
- CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6
- CCLINK?= -ldl -lnsl -lsocket -lm -lpthread
+ CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W -D__EXTENSIONS__ -D_XPG6 `pkg-config --cflags lua`
+ CCLINK?= -ldl -lnsl -lsocket -lm -lpthread `pkg-config --libs lua`
DEBUG?= -g -ggdb
else
- CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF)
- CCLINK?= -lm -pthread
+ CFLAGS?= -std=c99 -pedantic $(OPTIMIZATION) -Wall -W $(ARCH) $(PROF) `pkg-config --cflags lua`
+ CCLINK?= -lm -pthread `pkg-config --libs lua`
DEBUG?= -g -rdynamic -ggdb
endif
@@ -25,7 +25,7 @@ PREFIX= /usr/local
INSTALL_BIN= $(PREFIX)/bin
INSTALL= cp -p
-OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o
+OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o vm.o pubsub.o multi.o debug.o sort.o intset.o syncio.o lua.o
BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
View
147 src/lua.c
@@ -0,0 +1,147 @@
+#include "redis.h"
+#include "lua.h"
+
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+
+// lua interpretter state
+lua_State *luaState;
+
+// a fake client, used to capture command replies
+redisClient *fakeClient;
+
+// wrapper function to allow lua to use our zalloc
+static void *luaZalloc (void *ud, void *ptr, size_t osize, size_t nsize) {
+ (void)ud;
+ (void)osize;
+ if (nsize == 0) {
+ zfree(ptr);
+ return NULL;
+ }
+ else
+ return zrealloc(ptr, nsize);
+}
+
+// last-chance lua error capture
+static int luaPanic (lua_State *L) {
+ (void)L; /* to avoid warnings */
+ redisPanic(sprintf("PANIC: unprotected error in call to Lua API (%s)\n",
+ lua_tostring(L, -1)));
+ return 0;
+}
+
+// set up the lua interpretter, called when redis starts
+void initLua(void) {
+ // allocate and initialize just the essential parts
+ // of our fake client
+ fakeClient = zmalloc(sizeof(redisClient));
+ selectDb(fakeClient, 0);
+ fakeClient->flags = REDIS_LUA;
+ fakeClient->reply = listCreate();
+ listSetFreeMethod(fakeClient->reply,decrRefCount);
+ listSetDupMethod(fakeClient->reply,dupClientReplyValue);
+
+ luaState = lua_newstate(luaZalloc, NULL);
+ if (!luaState) {
+ redisPanic("Unable to initialize Lua!");
+ }
+ lua_atpanic(luaState, &luaPanic);
+ luaL_openlibs(luaState);
+
+ // provide lua script with a hook to call redis commands
+ lua_pushcfunction(luaState, luaExecRedisCommand);
+ lua_setglobal(luaState, "redis");
+}
+
+void luaexecCommand(redisClient *c) {
+ luaGenericExecCommand(c, c->argv[1]->ptr);
+}
+
+void luaexeckeyCommand(redisClient *c) {
+ robj *o;
+
+ if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) {
+ return;
+ }
+
+ if (o->type != REDIS_STRING) {
+ addReply(c,shared.wrongtypeerr);
+ return;
+ }
+
+ luaGenericExecCommand(c, o->ptr);
+}
+
+// execute lua code
+void luaGenericExecCommand(redisClient *c, const char *code) {
+ int err;
+
+ // load the code into lua
+ if ((err = luaL_loadstring(luaState, code))) {
+ if (err == LUA_ERRSYNTAX) {
+ addReply(c, shared.syntaxerr);
+ } else if (err == LUA_ERRMEM) {
+ addReply(c, createObject(REDIS_STRING,sdsnew("-ERR Memory allocation error in LUA script")));
+ }
+ return;
+ }
+
+ // call the lua code, capturing output / error
+ sds output;
+ if ((err = lua_pcall(luaState, 0, 1, 0))) {
+ output = sdsnew("-ERR Lua error: ");
+ output = sdscat(output, lua_tostring(luaState, -1));
+ } else {
+ output = sdsnew(lua_tostring(luaState, -1));
+ }
+
+ // send the lua output back as a single string
+ // TODO handle multiple return values
+ addReply(c,shared.plus);
+ addReplySds(c, output);
+ addReply(c,shared.crlf);
+}
+
+// lua hook to execute redis commands
+int luaExecRedisCommand(lua_State *L) {
+ // find the redis command and check arity
+ int nargs = lua_gettop(L);
+ const char *cmdname = lua_tostring(L, 1);
+ struct redisCommand *cmd = lookupCommandByCString(cmdname);
+ if (nargs != cmd->arity) {
+ lua_pushstring(L, "incorrect number of arguments");
+ lua_error(L);
+ }
+
+ // set up argc and argv as if they'd been read from a real client
+ fakeClient->argc = nargs;
+ fakeClient->argv = zmalloc(sizeof(robj) * nargs);
+ for (int i = 0; i < nargs; i++) {
+ const char *param = lua_tostring(L, i + 1);
+ fakeClient->argv[i] = createStringObject(param, strlen(param));
+ }
+ lua_pop(L, nargs);
+
+ // execute the redis command
+ call(fakeClient, cmd);
+ for (int i = 0; i < nargs; i++) {
+ decrRefCount(fakeClient->argv[i]);
+ }
+ zfree(fakeClient->argv);
+ fakeClient->argv = NULL;
+
+ // read the response and return to lua
+ int returnedValues = 0;
+ listNode *node;
+ while (listLength(fakeClient->reply)) {
+ node = listFirst(fakeClient->reply);
+ robj *o = listNodeValue(node);
+ lua_pushstring(L, o->ptr);
+ listDelNode(fakeClient->reply, node);
+ returnedValues++;
+ }
+
+ return returnedValues;
+}
+
View
11 src/lua.h
@@ -0,0 +1,11 @@
+#ifndef __LUA_H
+#define __LUA_H
+
+#include <lua.h>
+#include "redis.h"
+
+void initLua(void);
+void luaGenericExecCommand(redisClient *c, const char *);
+int luaExecRedisCommand(lua_State *L);
+
+#endif
View
2 src/networking.c
@@ -60,6 +60,7 @@ redisClient *createClient(int fd) {
/* Set the event loop to listen for write events on the client's socket.
* Typically gets called every time a reply is built. */
int _installWriteEvent(redisClient *c) {
+ if (c->flags & REDIS_LUA) { return REDIS_OK; }
/* When CLOSE_AFTER_REPLY is set, no more replies may be added! */
redisAssert(!(c->flags & REDIS_CLOSE_AFTER_REPLY));
@@ -89,6 +90,7 @@ robj *dupLastObjectIfNeeded(list *reply) {
}
int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
+ if (c->flags & REDIS_LUA) { return REDIS_ERR; }
size_t available = sizeof(c->buf)-c->bufpos;
/* If there already are entries in the reply list, we cannot
View
4 src/redis-benchmark.c
@@ -465,6 +465,10 @@ int main(int argc, char **argv) {
benchmark("PING",cmd,len);
free(cmd);
+ len = redisFormatCommand(&cmd,"LUAEXEC redis('ping')");
+ benchmark("PING (Lua)",cmd,len);
+ free(cmd);
+
const char *argv[21];
argv[0] = "MSET";
for (i = 1; i < 21; i += 2) {
View
6 src/redis.c
@@ -52,6 +52,7 @@
#include <math.h>
#include <pthread.h>
#include <sys/resource.h>
+#include "lua.h"
/* Our shared "common" objects */
@@ -187,7 +188,9 @@ struct redisCommand readonlyCommandTable[] = {
{"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0},
{"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
{"watch",watchCommand,-2,0,NULL,0,0,0},
- {"unwatch",unwatchCommand,1,0,NULL,0,0,0}
+ {"unwatch",unwatchCommand,1,0,NULL,0,0,0},
+ {"luaexec",luaexecCommand,2,REDIS_CMD_FORCE_REPLICATION|REDIS_CMD_DENYOOM,NULL,0,0,0},
+ {"luaexeckey",luaexeckeyCommand,2,REDIS_CMD_FORCE_REPLICATION|REDIS_CMD_DENYOOM,NULL,1,1,1}
};
/*============================ Utility functions ============================ */
@@ -1528,6 +1531,7 @@ int main(int argc, char **argv) {
if (server.daemonize) daemonize();
initServer();
if (server.daemonize) createPidFile();
+ initLua();
redisLog(REDIS_NOTICE,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
linuxOvercommitMemoryWarning();
View
3 src/redis.h
@@ -146,6 +146,7 @@
#define REDIS_IO_WAIT 32 /* The client is waiting for Virtual Memory I/O */
#define REDIS_DIRTY_CAS 64 /* Watched keys modified. EXEC will fail. */
#define REDIS_CLOSE_AFTER_REPLY 128 /* Close after writing entire reply. */
+#define REDIS_LUA 256
/* Client request types */
#define REDIS_REQ_INLINE 1
@@ -1011,6 +1012,8 @@ void punsubscribeCommand(redisClient *c);
void publishCommand(redisClient *c);
void watchCommand(redisClient *c);
void unwatchCommand(redisClient *c);
+void luaexecCommand(redisClient *c);
+void luaexeckeyCommand(redisClient *c);
#if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));

No commit comments for this range

Something went wrong with that request. Please try again.