diff --git a/src/main/java/redis/clients/jedis/BinaryClient.java b/src/main/java/redis/clients/jedis/BinaryClient.java index 9997e58..bbaa867 100644 --- a/src/main/java/redis/clients/jedis/BinaryClient.java +++ b/src/main/java/redis/clients/jedis/BinaryClient.java @@ -724,4 +724,46 @@ public class BinaryClient extends Connection { db = 0; super.disconnect(); } + + private void sendEvalCommand(Command command, byte[] script, byte[] keyCount, byte[][] params){ + + final byte[][] allArgs = new byte[params.length + 2][]; + + allArgs[0] = script; + allArgs[1] = keyCount; + + for(int i=0;i + * + * @return Script result + */ + public Object eval(byte[] script, List keys, List args) { + client.setTimeoutInfinite(); + client.eval(script, toByteArray(keys.size()), getParams(keys, args)); + return client.getOne(); + } + private byte[][] getParams(List keys, List args){ + int keyCount = keys.size(); + byte[][] params = new byte[keyCount + args.size()][]; + + for(int i=0;i scriptExists(byte[]... sha1){ + client.scriptExists(sha1); + return client.getIntegerMultiBulkReply(); + } + + public byte[] scriptLoad(byte[] script){ + client.scriptLoad(script); + return client.getBinaryBulkReply(); + } + + public byte[] scriptKill(){ + client.scriptKill(); + return client.getBinaryBulkReply(); + } } diff --git a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java index 8aa8344..fa99a60 100644 --- a/src/main/java/redis/clients/jedis/BinaryJedisCommands.java +++ b/src/main/java/redis/clients/jedis/BinaryJedisCommands.java @@ -154,4 +154,5 @@ public interface BinaryJedisCommands { Long zremrangeByScore(byte[] key, double start, double end); Long linsert(byte[] key, LIST_POSITION where, byte[] pivot, byte[] value); + } diff --git a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java index fb782e8..7547759 100644 --- a/src/main/java/redis/clients/jedis/BinaryShardedJedis.java +++ b/src/main/java/redis/clients/jedis/BinaryShardedJedis.java @@ -5,6 +5,7 @@ import redis.clients.util.Hashing; import redis.clients.util.Sharded; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; diff --git a/src/main/java/redis/clients/jedis/Client.java b/src/main/java/redis/clients/jedis/Client.java index 3f07d8e..53d6c34 100644 --- a/src/main/java/redis/clients/jedis/Client.java +++ b/src/main/java/redis/clients/jedis/Client.java @@ -1,12 +1,16 @@ package redis.clients.jedis; +import redis.clients.jedis.Protocol.Keyword; import redis.clients.util.SafeEncoder; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import static redis.clients.jedis.Protocol.toByteArray; +import static redis.clients.jedis.Protocol.Command.SCRIPT; public class Client extends BinaryClient implements Commands { public Client(final String host) { @@ -605,4 +609,32 @@ public class Client extends BinaryClient implements Commands { public void configGet(String pattern) { configGet(SafeEncoder.encode(pattern)); } + + private byte[][] getByteParams(String... params){ + byte[][] p = new byte[params.length][]; + for(int i=0;i) protocol.read(inputStream); } + + @SuppressWarnings("unchecked") + public List getIntegerMultiBulkReply() { + flush(); + pipelinedCommands--; + return (List) protocol.read(inputStream); + } public List getAll() { return getAll(0); diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 591ac2a..0a4699b 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Set; import redis.clients.jedis.BinaryClient.LIST_POSITION; +import redis.clients.util.SafeEncoder; public class Jedis extends BinaryJedis implements JedisCommands { public Jedis(final String host) { @@ -2688,5 +2689,90 @@ public class Jedis extends BinaryJedis implements JedisCommands { public String configSet(final String parameter, final String value) { client.configSet(parameter, value); return client.getStatusCodeReply(); - } + } + + public Object eval(String script, int keyCount, String... params) { + client.setTimeoutInfinite(); + client.eval(script, keyCount, params); + + return getEvalResult(); + } + + private String[] getParams(List keys, List args){ + int keyCount = keys.size(); + int argCount = args.size(); + + String[] params = new String[keyCount + args.size()]; + + for(int i=0;i keys, List args) { + return eval(script, keys.size(), getParams(keys, args)); + } + + public Object eval(String script) { + return eval(script,0); + } + + public Object evalsha(String script) { + return evalsha(script,0); + } + + private Object getEvalResult(){ + Object result = client.getOne(); + + if(result instanceof byte[]) + return SafeEncoder.encode((byte[])result); + + if(result instanceof List) { + List list = (List)result; + List listResult = new ArrayList(list.size()); + for(Object bin: list) + listResult.add(SafeEncoder.encode((byte[])bin)); + + return listResult; + } + + return result; + } + + public Object evalsha(String sha1, List keys, List args) { + return evalsha(sha1, keys.size(), getParams(keys, args)); + } + + public Object evalsha(String sha1, int keyCount, String... params) { + checkIsInMulti(); + client.evalsha(sha1, keyCount, params); + + return getEvalResult(); + } + + public Boolean scriptExists(String sha1){ + String[] a = new String[1]; + a[0] = sha1; + return scriptExists(a).get(0); + } + + public List scriptExists(String... sha1){ + client.scriptExists(sha1); + List result = client.getIntegerMultiBulkReply(); + List exists = new ArrayList(); + + for(Long value : result) + exists.add(value == 1); + + return exists; + } + + public String scriptLoad(String script){ + client.scriptLoad(script); + return client.getBulkReply(); + } } diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index a4f1c7e..72756df 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -140,7 +140,7 @@ public final class Protocol { } public static enum Command { - PING, SET, GET, QUIT, EXISTS, DEL, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, SETRANGE, GETRANGE; + PING, SET, GET, QUIT, EXISTS, DEL, TYPE, FLUSHDB, KEYS, RANDOMKEY, RENAME, RENAMENX, RENAMEX, DBSIZE, EXPIRE, EXPIREAT, TTL, SELECT, MOVE, FLUSHALL, GETSET, MGET, SETNX, SETEX, MSET, MSETNX, DECRBY, DECR, INCRBY, INCR, APPEND, SUBSTR, HSET, HGET, HSETNX, HMSET, HMGET, HINCRBY, HEXISTS, HDEL, HLEN, HKEYS, HVALS, HGETALL, RPUSH, LPUSH, LLEN, LRANGE, LTRIM, LINDEX, LSET, LREM, LPOP, RPOP, RPOPLPUSH, SADD, SMEMBERS, SREM, SPOP, SMOVE, SCARD, SISMEMBER, SINTER, SINTERSTORE, SUNION, SUNIONSTORE, SDIFF, SDIFFSTORE, SRANDMEMBER, ZADD, ZRANGE, ZREM, ZINCRBY, ZRANK, ZREVRANK, ZREVRANGE, ZCARD, ZSCORE, MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, BLPOP, BRPOP, AUTH, SUBSCRIBE, PUBLISH, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, ZCOUNT, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZUNIONSTORE, ZINTERSTORE, SAVE, BGSAVE, BGREWRITEAOF, LASTSAVE, SHUTDOWN, INFO, MONITOR, SLAVEOF, CONFIG, STRLEN, SYNC, LPUSHX, PERSIST, RPUSHX, ECHO, LINSERT, DEBUG, BRPOPLPUSH, SETBIT, GETBIT, SETRANGE, GETRANGE, EVAL, EVALSHA, SCRIPT; public final byte[] raw; @@ -150,7 +150,7 @@ public final class Protocol { } public static enum Keyword { - AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE, PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES, RESETSTAT; + AGGREGATE, ALPHA, ASC, BY, DESC, GET, LIMIT, MESSAGE, NO, NOSORT, PMESSAGE, PSUBSCRIBE, PUNSUBSCRIBE, OK, ONE, QUEUED, SET, STORE, SUBSCRIBE, UNSUBSCRIBE, WEIGHTS, WITHSCORES, RESETSTAT, FLUSH, EXISTS, LOAD, KILL; public final byte[] raw; Keyword() { diff --git a/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java b/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java new file mode 100644 index 0000000..acee2cf --- /dev/null +++ b/src/test/java/redis/clients/jedis/tests/commands/ScriptingCommandsTest.java @@ -0,0 +1,138 @@ +package redis.clients.jedis.tests.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import redis.clients.jedis.exceptions.JedisDataException; +import redis.clients.jedis.exceptions.JedisException; +import redis.clients.util.SafeEncoder; + +public class ScriptingCommandsTest extends JedisCommandTestBase { + + @SuppressWarnings("unchecked") + @Test + public void evalMultiBulk() { + String script = "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"; + List keys = new ArrayList(); + keys.add("key1"); + keys.add("key2"); + + List args = new ArrayList(); + args.add("first"); + args.add("second"); + + List response = (List)jedis.eval(script, keys, args ); + + assertEquals(4, response.size()); + assertEquals("key1", response.get(0)); + assertEquals("key2", response.get(1)); + assertEquals("first", response.get(2)); + assertEquals("second", response.get(3)); + } + + @Test + public void evalBulk() { + String script = "return KEYS[1]"; + List keys = new ArrayList(); + keys.add("key1"); + + List args = new ArrayList(); + args.add("first"); + + String response = (String)jedis.eval(script, keys, args); + + assertEquals("key1", response); + } + + @Test + public void evalInt() { + String script = "return 2"; + List keys = new ArrayList(); + keys.add("key1"); + + Long response = (Long)jedis.eval(script, keys, new ArrayList()); + + assertEquals(new Long(2), response); + } + + @Test + public void evalNoArgs() { + String script = "return KEYS[1]"; + List keys = new ArrayList(); + keys.add("key1"); + String response = (String)jedis.eval(script, keys, new ArrayList()); + + assertEquals("key1", response); + } + + @SuppressWarnings("unchecked") + @Test + public void evalsha() { + jedis.set("foo", "bar"); + jedis.eval("return redis.call('get','foo')"); + String result = (String)jedis.evalsha("6b1bf486c81ceb7edf3c093f4c48582e38c0e791"); + + assertEquals("bar", result); + } + + @SuppressWarnings("unchecked") + @Test(expected=JedisDataException.class) + public void evalshaShaNotFound() { + jedis.evalsha("ffffffffffffffffffffffffffffffffffffffff"); + } + + @SuppressWarnings("unchecked") + @Test + public void scriptFlush() { + jedis.set("foo", "bar"); + jedis.eval("return redis.call('get','foo')"); + jedis.scriptFlush(); + assertFalse(jedis.scriptExists("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); + } + + @SuppressWarnings("unchecked") + @Test + public void scriptExists() { + jedis.scriptLoad("return redis.call('get','foo')"); + List exists = jedis.scriptExists("ffffffffffffffffffffffffffffffffffffffff","6b1bf486c81ceb7edf3c093f4c48582e38c0e791"); + assertFalse(exists.get(0)); + assertTrue(exists.get(1)); + } + + @SuppressWarnings("unchecked") + @Test + public void scriptExistsBinary() { + jedis.scriptLoad(SafeEncoder.encode("return redis.call('get','foo')")); + List exists = jedis.scriptExists(SafeEncoder.encode("ffffffffffffffffffffffffffffffffffffffff"),SafeEncoder.encode("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); + assertEquals(new Long(0), exists.get(0)); + assertEquals(new Long(1), exists.get(1)); + } + + @SuppressWarnings("unchecked") + @Test + public void scriptLoad() { + jedis.scriptLoad("return redis.call('get','foo')"); + assertTrue(jedis.scriptExists("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); + } + + @SuppressWarnings("unchecked") + @Test + public void scriptLoadBinary() { + jedis.scriptLoad(SafeEncoder.encode("return redis.call('get','foo')")); + List exists = jedis.scriptExists(SafeEncoder.encode("6b1bf486c81ceb7edf3c093f4c48582e38c0e791")); + assertEquals(new Long(1), exists.get(0)); + } + + @SuppressWarnings("unchecked") + @Test + public void scriptKill() { + try { + jedis.scriptKill(); + } + catch(JedisDataException e) { + assertEquals("ERR No scripts in execution right now.", e.getMessage()); + } + } +} \ No newline at end of file