From 3497253d709e73d0ea16b087509801e7fe1db181 Mon Sep 17 00:00:00 2001 From: Kowalczyk Date: Sun, 20 Dec 2020 21:20:33 +0100 Subject: [PATCH] Support COPY command #1508 Original pull request: #1565. --- .../core/AbstractRedisAsyncCommands.java | 10 ++ .../core/AbstractRedisReactiveCommands.java | 10 ++ src/main/java/io/lettuce/core/CopyArgs.java | 101 ++++++++++++++++++ .../io/lettuce/core/RedisCommandBuilder.java | 18 ++++ .../core/api/async/RedisKeyAsyncCommands.java | 27 +++++ .../reactive/RedisKeyReactiveCommands.java | 27 +++++ .../core/api/sync/RedisKeyCommands.java | 27 +++++ .../lettuce/core/protocol/CommandKeyword.java | 2 +- .../io/lettuce/core/protocol/CommandType.java | 2 +- .../io/lettuce/core/api/RedisKeyCommands.java | 27 +++++ .../commands/KeyCommandIntegrationTests.java | 24 ++++- 11 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/lettuce/core/CopyArgs.java diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 712a4bbb76..ea971e0c27 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -1571,6 +1571,16 @@ public RedisFuture unlink(Iterable keys) { return dispatch(commandBuilder.unlink(keys)); } + @Override + public RedisFuture copy(K source, K destination) { + return dispatch(commandBuilder.copy(source, destination)); + } + + @Override + public RedisFuture copy(K source, K destination, CopyArgs copyArgs) { + return dispatch(commandBuilder.copy(source, destination, copyArgs)); + } + @Override public RedisFuture unwatch() { return dispatch(commandBuilder.unwatch()); diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index ffbb488d16..af8888ccbf 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -1647,6 +1647,16 @@ public Mono unlink(Iterable keys) { return createMono(() -> commandBuilder.unlink(keys)); } + @Override + public Mono copy(K source, K destination) { + return createMono(() -> commandBuilder.copy(source, destination)); + } + + @Override + public Mono copy(K source, K destination, CopyArgs copyArgs) { + return createMono(() -> commandBuilder.copy(source, destination, copyArgs)); + } + @Override public Mono unwatch() { return createMono(commandBuilder::unwatch); diff --git a/src/main/java/io/lettuce/core/CopyArgs.java b/src/main/java/io/lettuce/core/CopyArgs.java new file mode 100644 index 0000000000..29aa1dfda9 --- /dev/null +++ b/src/main/java/io/lettuce/core/CopyArgs.java @@ -0,0 +1,101 @@ +/* + * Copyright 2018-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.lettuce.core; + +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +/** + * Argument list builder for the Redis COPY command. Static import the methods from + * {@link CopyArgs.Builder} and call the methods: {@code destinationDb(…)} {@code replace(…)}. + * + * {@link CopyArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @since 6.2 + */ +public class CopyArgs { + + private Long destinationDb; + + private Boolean replace; + + /** + * Builder entry points for {@link CopyArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates new {@link CopyArgs} and sets {@literal DB}. + * + * @return new {@link CopyArgs} with {@literal DB} set. + */ + public static CopyArgs destinationDb(long destinationDb) { + return new CopyArgs().destinationDb(destinationDb); + } + + /** + * Creates new {@link CopyArgs} and sets {@literal REPLACE}. + * + * @return new {@link CopyArgs} with {@literal REPLACE} set. + */ + public static CopyArgs replace(boolean replace) { + return new CopyArgs().replace(replace); + } + + } + + /** + * Specify an alternative logical database index for the destination key. + * + * @param destinationDb logical database index to apply for {@literal DB}. + * @return {@code this}. + */ + public CopyArgs destinationDb(long destinationDb) { + + this.destinationDb = destinationDb; + return this; + } + + /** + * Hint redis to remove the destination key before copying the value to it. + * + * @param replace remove destination key before copying the value {@literal REPLACE}. + * @return {@code this}. + */ + public CopyArgs replace(boolean replace) { + + this.replace = replace; + return this; + } + + public void build(CommandArgs args) { + + if (destinationDb != null) { + args.add(CommandKeyword.DB).add(destinationDb); + } + + if(replace != null) { + args.add(CommandKeyword.REPLACE); + } + } + +} diff --git a/src/main/java/io/lettuce/core/RedisCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCommandBuilder.java index bff09f6381..e117779fe3 100644 --- a/src/main/java/io/lettuce/core/RedisCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCommandBuilder.java @@ -18,6 +18,7 @@ import static io.lettuce.core.internal.LettuceStrings.*; import static io.lettuce.core.protocol.CommandKeyword.*; import static io.lettuce.core.protocol.CommandType.*; +import static io.lettuce.core.protocol.CommandType.COPY; import java.nio.ByteBuffer; import java.util.Arrays; @@ -2192,6 +2193,23 @@ Command unlink(Iterable keys) { return createCommand(UNLINK, new IntegerOutput<>(codec), args); } + Command copy(K source, K destination) { + LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL); + LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL); + + CommandArgs args = new CommandArgs<>(codec).addKey(source).addKey(destination); + return createCommand(COPY, new BooleanOutput<>(codec), args); + } + + Command copy(K source, K destination, CopyArgs copyArgs) { + LettuceAssert.notNull(source, "Source " + MUST_NOT_BE_NULL); + LettuceAssert.notNull(destination, "Destination " + MUST_NOT_BE_NULL); + + CommandArgs args = new CommandArgs<>(codec).addKey(source).addKey(destination); + copyArgs.build(args); + return createCommand(COPY, new BooleanOutput<>(codec), args); + } + Command unwatch() { return createCommand(UNWATCH, new StatusOutput<>(codec)); } diff --git a/src/main/java/io/lettuce/core/api/async/RedisKeyAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisKeyAsyncCommands.java index 77202cdf0d..ec8e6d1691 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisKeyAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisKeyAsyncCommands.java @@ -57,6 +57,33 @@ public interface RedisKeyAsyncCommands { */ RedisFuture unlink(K... keys); + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + RedisFuture copy(K source, K destination); + + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @param copyArgs the copyArgs. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + RedisFuture copy(K source, K destination, CopyArgs copyArgs); + /** * Return a serialized version of the value stored at the specified key. * diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisKeyReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisKeyReactiveCommands.java index cc80c87759..0acb2f34db 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisKeyReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisKeyReactiveCommands.java @@ -56,6 +56,33 @@ public interface RedisKeyReactiveCommands { */ Mono unlink(K... keys); + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + Mono copy(K source, K destination); + + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @param copyArgs the copyArgs. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + Mono copy(K source, K destination, CopyArgs copyArgs); + /** * Return a serialized version of the value stored at the specified key. * diff --git a/src/main/java/io/lettuce/core/api/sync/RedisKeyCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisKeyCommands.java index d24d80d629..a6a8129653 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisKeyCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisKeyCommands.java @@ -56,6 +56,33 @@ public interface RedisKeyCommands { */ Long unlink(K... keys); + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + Boolean copy(K source, K destination); + + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @param copyArgs the copyArgs. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + Boolean copy(K source, K destination, CopyArgs copyArgs); + /** * Return a serialized version of the value stored at the specified key. * diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index a4c95dd33b..e2a4f74bbc 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -29,7 +29,7 @@ public enum CommandKeyword implements ProtocolKeyword { ADDR, ADDSLOTS, AFTER, AGGREGATE, ALPHA, AND, ASK, ASC, ASYNC, BEFORE, BLOCK, BUMPEPOCH, - BY, CACHING, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DELSLOTS, DESC, SOFT, HARD, ENCODING, + BY, CACHING, CHANNELS, COPY, COUNT, COUNTKEYSINSLOT, CONSUMERS, CREATE, DB, DELSLOTS, DESC, SOFT, HARD, ENCODING, FAILOVER, FORGET, FLUSH, FORCE, FLUSHSLOTS, GETNAME, GETKEYSINSLOT, GETREDIR, GROUP, GROUPS, HTSTATS, ID, IDLE, diff --git a/src/main/java/io/lettuce/core/protocol/CommandType.java b/src/main/java/io/lettuce/core/protocol/CommandType.java index 103d421724..f80272f0be 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandType.java +++ b/src/main/java/io/lettuce/core/protocol/CommandType.java @@ -42,7 +42,7 @@ public enum CommandType implements ProtocolKeyword { // Keys - DEL, DUMP, EXISTS, EXPIRE, EXPIREAT, KEYS, MIGRATE, MOVE, OBJECT, PERSIST, PEXPIRE, PEXPIREAT, PTTL, RANDOMKEY, RENAME, RENAMENX, RESTORE, TOUCH, TTL, TYPE, SCAN, UNLINK, + COPY, DEL, DUMP, EXISTS, EXPIRE, EXPIREAT, KEYS, MIGRATE, MOVE, OBJECT, PERSIST, PEXPIRE, PEXPIREAT, PTTL, RANDOMKEY, RENAME, RENAMENX, RESTORE, TOUCH, TTL, TYPE, SCAN, UNLINK, // String diff --git a/src/main/templates/io/lettuce/core/api/RedisKeyCommands.java b/src/main/templates/io/lettuce/core/api/RedisKeyCommands.java index fb91f88acc..6b558aee4f 100644 --- a/src/main/templates/io/lettuce/core/api/RedisKeyCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisKeyCommands.java @@ -48,6 +48,33 @@ public interface RedisKeyCommands { */ Long unlink(K... keys); + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + Boolean copy(K source, K destination); + + /** + * Copy the value stored at the source key to the destination key. + * + * @param source the source. + * @param destination the destination. + * @param copyArgs the copyArgs. + * @return Boolean integer-reply specifically: + * + * {@code 1} if source was copied. {@code 0} if source was not copied. + * + * @since 6.2 + */ + Boolean copy(K source, K destination, CopyArgs copyArgs); + /** * Return a serialized version of the value stored at the specified key. * diff --git a/src/test/java/io/lettuce/core/commands/KeyCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/KeyCommandIntegrationTests.java index 6eda7d3557..7b7f9c3d5b 100644 --- a/src/test/java/io/lettuce/core/commands/KeyCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/KeyCommandIntegrationTests.java @@ -77,7 +77,6 @@ void del() { @Test @EnabledOnCommand("UNLINK") void unlink() { - redis.set(key, value); assertThat((long) redis.unlink(key)).isEqualTo(1); redis.set(key + "1", value); @@ -85,6 +84,29 @@ void unlink() { assertThat(redis.unlink(key + "1", key + "2")).isEqualTo(2); } + @Test + void copy() { + redis.set(key, value); + redis.copy(key, key + "2"); + assertThat(redis.get(key + "2")).isEqualTo(value); + } + + @Test + void copyWithReplace() { + redis.set(key, value); + redis.set(key + 2, "value to be overridden"); + redis.copy(key, key + "2", CopyArgs.Builder.replace(true)); + assertThat(redis.get(key + "2")).isEqualTo(value); + } + + @Test + void copyWithDestinationDb() { + redis.set(key, value); + redis.copy(key, key, CopyArgs.Builder.destinationDb(2)); + redis.select(2); + assertThat(redis.get(key)).isEqualTo(value); + } + @Test void dump() { assertThat(redis.dump("invalid")).isNull();