diff --git a/src/scripting.c b/src/scripting.c index 1f441ae4..af22d501 100644 --- a/src/scripting.c +++ b/src/scripting.c @@ -128,7 +128,7 @@ void luaPushError(lua_State *lua, char *error) { lua_settable(lua,-3); } -int luaRedisCommand(lua_State *lua) { +int luaRedisGenericCommand(lua_State *lua, int raise_error) { int j, argc = lua_gettop(lua); struct redisCommand *cmd; robj **argv; @@ -205,6 +205,7 @@ int luaRedisCommand(lua_State *lua) { reply = sdscatlen(reply,o->ptr,sdslen(o->ptr)); listDelNode(c->reply,listFirst(c->reply)); } + if (raise_error && reply[0] != '-') raise_error = 0; redisProtocolToLuaType(lua,reply); sdsfree(reply); @@ -215,9 +216,25 @@ cleanup: decrRefCount(c->argv[j]); zfree(c->argv); + if (raise_error) { + /* If we are here we should have an error in the stack, in the + * form of a table with an "err" field. Extract the string to + * return the plain error. */ + lua_pushstring(lua,"err"); + lua_gettable(lua,-2); + return lua_error(lua); + } return 1; } +int luaRedisCallCommand(lua_State *lua) { + return luaRedisGenericCommand(lua,1); +} + +int luaRedisPCallCommand(lua_State *lua) { + return luaRedisGenericCommand(lua,0); +} + int luaLogCommand(lua_State *lua) { int j, argc = lua_gettop(lua); int level; @@ -301,7 +318,12 @@ void scriptingInit(void) { /* redis.call */ lua_pushstring(lua,"call"); - lua_pushcfunction(lua,luaRedisCommand); + lua_pushcfunction(lua,luaRedisCallCommand); + lua_settable(lua,-3); + + /* redis.pcall */ + lua_pushstring(lua,"pcall"); + lua_pushcfunction(lua,luaRedisPCallCommand); lua_settable(lua,-3); /* redis.log and log levels. */ diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index d8a2e023..5bac5687 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -54,7 +54,7 @@ start_server {tags {"scripting"}} { test {EVAL - Redis integer -> Lua type conversion} { r eval { - local foo = redis.call('incr','x') + local foo = redis.pcall('incr','x') return {type(foo),foo} } 0 } {number 1} @@ -62,7 +62,7 @@ start_server {tags {"scripting"}} { test {EVAL - Redis bulk -> Lua type conversion} { r set mykey myval r eval { - local foo = redis.call('get','mykey') + local foo = redis.pcall('get','mykey') return {type(foo),foo} } 0 } {string myval} @@ -73,14 +73,14 @@ start_server {tags {"scripting"}} { r rpush mylist b r rpush mylist c r eval { - local foo = redis.call('lrange','mylist',0,-1) + local foo = redis.pcall('lrange','mylist',0,-1) return {type(foo),foo[1],foo[2],foo[3],# foo} } 0 } {table a b c 3} test {EVAL - Redis status reply -> Lua type conversion} { r eval { - local foo = redis.call('set','mykey','myval') + local foo = redis.pcall('set','mykey','myval') return {type(foo),foo['ok']} } 0 } {table OK} @@ -88,7 +88,7 @@ start_server {tags {"scripting"}} { test {EVAL - Redis error reply -> Lua type conversion} { r set mykey myval r eval { - local foo = redis.call('incr','mykey') + local foo = redis.pcall('incr','mykey') return {type(foo),foo['err']} } 0 } {table {ERR value is not an integer or out of range}} @@ -96,7 +96,7 @@ start_server {tags {"scripting"}} { test {EVAL - Redis nil bulk reply -> Lua type conversion} { r del mykey r eval { - local foo = redis.call('get','mykey') + local foo = redis.pcall('get','mykey') return {type(foo),foo == false} } 0 } {boolean 1} @@ -105,11 +105,11 @@ start_server {tags {"scripting"}} { r set mykey "this is DB 9" r select 10 r set mykey "this is DB 10" - r eval {return redis.call('get','mykey')} 0 + r eval {return redis.pcall('get','mykey')} 0 } {this is DB 10} test {EVAL - Is Lua seleced DB retained?} { - r eval {return redis.call('select','9')} 0 + r eval {return redis.pcall('select','9')} 0 r get mykey } {this is DB 9} @@ -126,18 +126,42 @@ start_server {tags {"scripting"}} { test {EVAL - Scripts can't run certain commands} { set e {} - catch {r eval {return redis.call('spop','x')} 0} e + catch {r eval {return redis.pcall('spop','x')} 0} e set e } {*not allowed*} test {EVAL - Scripts can't run certain commands} { set e {} catch { - r eval "redis.call('randomkey'); return redis.call('set','x','ciao')" 0 + r eval "redis.pcall('randomkey'); return redis.pcall('set','x','ciao')" 0 } e set e } {*not allowed after*} + test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { + set e {} + catch { + r eval "redis.call('nosuchcommand')" 0 + } e + set e + } {*Unknown Redis*} + + test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { + set e {} + catch { + r eval "redis.call('get','a','b','c')" 0 + } e + set e + } {*number of args*} + + test {EVAL - redis.call variant raises a Lua error on Redis cmd error (1)} { + set e {} + r set foo bar + catch { + r eval "redis.call('lpush','foo','val')" 0 + } e + set e + } {*against a key*} } start_server {tags {"scripting repl"}} {