Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: georgebashi/redis
...
head fork: georgebashi/redis
  • 10 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
10 src/Makefile
View
@@ -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
147 src/lua.c
View
@@ -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;
+}
+
11 src/lua.h
View
@@ -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
2  src/networking.c
View
@@ -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
4 src/redis-benchmark.c
View
@@ -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) {
6 src/redis.c
View
@@ -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();
3  src/redis.h
View
@@ -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.