Skip to content

Commit

Permalink
Java: Add SETRANGE command. (valkey-io#1235)
Browse files Browse the repository at this point in the history
* Add `SETRANGE` command. (#183)

* Add `SETRANGE` command.

Signed-off-by: Yury-Fridlyand <[email protected]>

* PR comments.

Signed-off-by: Yury-Fridlyand <[email protected]>

---------

Signed-off-by: Yury-Fridlyand <[email protected]>

* PR comments.

Signed-off-by: Yury-Fridlyand <[email protected]>

* PR comments.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Typo fix.

Signed-off-by: Yury-Fridlyand <[email protected]>

* Update UT.

Signed-off-by: Yury-Fridlyand <[email protected]>

---------

Signed-off-by: Yury-Fridlyand <[email protected]>
  • Loading branch information
Yury-Fridlyand authored and alex-arzola-imp committed Apr 12, 2024
1 parent ddb714a commit 9e0c1c4
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ enum RequestType {
RPushX = 102;
LPushX = 103;
ZMScore = 104;
SetRange = 107;
}

message Command {
Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ pub enum RequestType {
RPushX = 102,
LPushX = 103,
ZMScore = 104,
SetRange = 107,
}

fn get_two_word_command(first: &str, second: &str) -> Cmd {
Expand Down Expand Up @@ -229,6 +230,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::LInsert => RequestType::LInsert,
ProtobufRequestType::Spop => RequestType::Spop,
ProtobufRequestType::ZMScore => RequestType::ZMScore,
ProtobufRequestType::SetRange => RequestType::SetRange,
}
}
}
Expand Down Expand Up @@ -341,6 +343,7 @@ impl RequestType {
RequestType::LInsert => Some(cmd("LINSERT")),
RequestType::Spop => Some(cmd("SPOP")),
RequestType::ZMScore => Some(cmd("ZMSCORE")),
RequestType::SetRange => Some(cmd("SETRANGE")),
}
}
}
7 changes: 7 additions & 0 deletions java/client/src/main/java/glide/api/BaseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.SIsMember;
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
import static redis_request.RedisRequestOuterClass.RequestType.SetRange;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
import static redis_request.RedisRequestOuterClass.RequestType.Strlen;
import static redis_request.RedisRequestOuterClass.RequestType.TTL;
Expand Down Expand Up @@ -349,6 +350,12 @@ public CompletableFuture<Long> strlen(@NonNull String key) {
return commandManager.submitNewCommand(Strlen, new String[] {key}, this::handleLongResponse);
}

@Override
public CompletableFuture<Long> setrange(@NonNull String key, int offset, @NonNull String value) {
return commandManager.submitNewCommand(
SetRange, new String[] {key, Integer.toString(offset), value}, this::handleLongResponse);
}

@Override
public CompletableFuture<String> hget(@NonNull String key, @NonNull String field) {
return commandManager.submitNewCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,26 @@ public interface StringBaseCommands {
* }</pre>
*/
CompletableFuture<Long> strlen(String key);

/**
* Overwrites part of the string stored at <code>key</code>, starting at the specified <code>
* offset</code>, for the entire length of <code>value</code>.<br>
* If the <code>offset</code> is larger than the current length of the string at <code>key</code>,
* the string is padded with zero bytes to make <code>offset</code> fit. Creates the <code>key
* </code> if it doesn't exist.
*
* @see <a href="https://redis.io/commands/setrange/">redis.io</a> for details.
* @param key The key of the string to update.
* @param offset The position in the string where <code>value</code> should be written.
* @param value The string written with <code>offset</code>.
* @return The length of the string stored at <code>key</code> after it was modified.
* @example
* <pre>{@code
* Long len = client.setrange("key", 6, "GLIDE").get();
* assert len == 11L; // New key was created with length of 11 symbols
* String value = client.get("key").get();
* assert value.equals("\0\0\0\0\0\0GLIDE"); // The string was padded with zero bytes
* }</pre>
*/
CompletableFuture<Long> setrange(String key, int offset, String value);
}
21 changes: 21 additions & 0 deletions java/client/src/main/java/glide/api/models/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.SIsMember;
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
import static redis_request.RedisRequestOuterClass.RequestType.SetRange;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
import static redis_request.RedisRequestOuterClass.RequestType.Strlen;
import static redis_request.RedisRequestOuterClass.RequestType.TTL;
Expand Down Expand Up @@ -393,6 +394,26 @@ public T strlen(@NonNull String key) {
return getThis();
}

/**
* Overwrites part of the string stored at <code>key</code>, starting at the specified <code>
* offset</code>, for the entire length of <code>value</code>.<br>
* If the <code>offset</code> is larger than the current length of the string at <code>key</code>,
* the string is padded with zero bytes to make <code>offset</code> fit. Creates the <code>key
* </code> if it doesn't exist.
*
* @see <a href="https://redis.io/commands/setrange/">redis.io</a> for details.
* @param key The key of the string to update.
* @param offset The position in the string where <code>value</code> should be written.
* @param value The string written with <code>offset</code>.
* @return Command Response - The length of the string stored at <code>key</code> after it was
* modified.
*/
public T setrange(@NonNull String key, int offset, @NonNull String value) {
ArgsArray commandArgs = buildArgs(key, Integer.toString(offset), value);
protobufTransaction.addCommands(buildCommand(SetRange, commandArgs));
return getThis();
}

/**
* Retrieves the value associated with <code>field</code> in the hash stored at <code>key</code>.
*
Expand Down
27 changes: 27 additions & 0 deletions java/client/src/test/java/glide/api/RedisClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
import static redis_request.RedisRequestOuterClass.RequestType.Select;
import static redis_request.RedisRequestOuterClass.RequestType.SetRange;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
import static redis_request.RedisRequestOuterClass.RequestType.Strlen;
import static redis_request.RedisRequestOuterClass.RequestType.TTL;
Expand Down Expand Up @@ -1004,6 +1005,32 @@ public void strlen_returns_success() {
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void setrange_returns_success() {
// setup
String key = "testKey";
int offset = 42;
String str = "pewpew";
String[] arguments = new String[] {key, Integer.toString(offset), str};
Long value = 10L;

CompletableFuture<Long> testResponse = new CompletableFuture<>();
testResponse.complete(value);

// match on protobuf request
when(commandManager.<Long>submitNewCommand(eq(SetRange), eq(arguments), any()))
.thenReturn(testResponse);

// exercise
CompletableFuture<Long> response = service.setrange(key, offset, str);
Long payload = response.get();

// verify
assertEquals(testResponse, response);
assertEquals(value, payload);
}

@SneakyThrows
@Test
public void hget_success() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import static redis_request.RedisRequestOuterClass.RequestType.SIsMember;
import static redis_request.RedisRequestOuterClass.RequestType.SMembers;
import static redis_request.RedisRequestOuterClass.RequestType.SRem;
import static redis_request.RedisRequestOuterClass.RequestType.SetRange;
import static redis_request.RedisRequestOuterClass.RequestType.SetString;
import static redis_request.RedisRequestOuterClass.RequestType.Strlen;
import static redis_request.RedisRequestOuterClass.RequestType.TTL;
Expand Down Expand Up @@ -169,6 +170,9 @@ public void transaction_builds_protobuf_request(BaseTransaction<?> transaction)
transaction.strlen("key");
results.add(Pair.of(Strlen, buildArgs("key")));

transaction.setrange("key", 42, "str");
results.add(Pair.of(SetRange, buildArgs("key", "42", "str")));

transaction.hset("key", Map.of("field", "value"));
results.add(Pair.of(HashSet, buildArgs("key", "field", "value")));

Expand Down
28 changes: 28 additions & 0 deletions java/integTest/src/test/java/glide/SharedCommandTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,34 @@ public void strlen(BaseClient client) {
assertTrue(exception.getCause() instanceof RequestException);
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
public void setrange(BaseClient client) {
String stringKey = UUID.randomUUID().toString();
String nonStringKey = UUID.randomUUID().toString();
// new key
assertEquals(11L, client.setrange(stringKey, 0, "Hello world").get());
// existing key
assertEquals(11L, client.setrange(stringKey, 6, "GLIDE").get());
assertEquals("Hello GLIDE", client.get(stringKey).get());

// offset > len
assertEquals(20L, client.setrange(stringKey, 15, "GLIDE").get());
assertEquals("Hello GLIDE\0\0\0\0GLIDE", client.get(stringKey).get());

// non-string key
assertEquals(1, client.lpush(nonStringKey, new String[] {"_"}).get());
Exception exception =
assertThrows(ExecutionException.class, () -> client.setrange(nonStringKey, 0, "_").get());
assertTrue(exception.getCause() instanceof RequestException);
exception =
assertThrows(
ExecutionException.class,
() -> client.setrange(stringKey, Integer.MAX_VALUE, "_").get());
assertTrue(exception.getCause() instanceof RequestException);
}

@SneakyThrows
@ParameterizedTest
@MethodSource("getClients")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static BaseTransaction<?> transactionTest(BaseTransaction<?> baseTransact
baseTransaction.incrByFloat(key3, 0.5);

baseTransaction.unlink(new String[] {key3});
baseTransaction.setrange(key3, 0, "GLIDE");

baseTransaction.hset(key4, Map.of(field1, value1, field2, value2));
baseTransaction.hget(key4, field1);
Expand Down Expand Up @@ -161,6 +162,7 @@ public static Object[] transactionTestResult() {
0L,
0.5,
1L,
5L, // setrange(key3, 0, "GLIDE")
2L,
value1,
2L, // hlen(key4)
Expand Down

0 comments on commit 9e0c1c4

Please sign in to comment.