Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

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.
base fork: georgebashi/redis
...
head fork: georgebashi/redis
Checking mergeability… Don’t worry, you can still create the pull request.
  • 10 commits
  • 7 files changed
  • 0 commit comments
  • 1 contributor
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.