Skip to content

Commit 98da96e

Browse files
committed
Adds HGETDEL Support to Valkey
Signed-off-by: Roshan Khatri <[email protected]>
1 parent a06cf15 commit 98da96e

File tree

5 files changed

+204
-0
lines changed

5 files changed

+204
-0
lines changed

src/commands.def

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3823,6 +3823,31 @@ struct COMMAND_ARG HGETALL_Args[] = {
38233823
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
38243824
};
38253825

3826+
/********** HGETDEL ********************/
3827+
3828+
#ifndef SKIP_CMD_HISTORY_TABLE
3829+
/* HGETDEL history */
3830+
#define HGETDEL_History NULL
3831+
#endif
3832+
3833+
#ifndef SKIP_CMD_TIPS_TABLE
3834+
/* HGETDEL tips */
3835+
#define HGETDEL_Tips NULL
3836+
#endif
3837+
3838+
#ifndef SKIP_CMD_KEY_SPECS_TABLE
3839+
/* HGETDEL key specs */
3840+
keySpec HGETDEL_Keyspecs[1] = {
3841+
{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
3842+
};
3843+
#endif
3844+
3845+
/* HGETDEL argument table */
3846+
struct COMMAND_ARG HGETDEL_Args[] = {
3847+
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
3848+
{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
3849+
};
3850+
38263851
/********** HGETEX ********************/
38273852

38283853
#ifndef SKIP_CMD_HISTORY_TABLE
@@ -11800,6 +11825,7 @@ struct COMMAND_STRUCT serverCommandTable[] = {
1180011825
{MAKE_CMD("hexpiretime","Returns Unix timestamps in seconds since the epoch at which the given key's field(s) will expire","O(1) for each field, so O(N) for N items when the command is called with multiple fields.","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXPIRETIME_History,0,HEXPIRETIME_Tips,0,hexpiretimeCommand,-5,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HEXPIRETIME_Keyspecs,1,NULL,2),.args=HEXPIRETIME_Args},
1180111826
{MAKE_CMD("hget","Returns the value of a field in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGET_History,0,HGET_Tips,0,hgetCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HGET_Keyspecs,1,NULL,2),.args=HGET_Args},
1180211827
{MAKE_CMD("hgetall","Returns all fields and values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETALL_History,0,HGETALL_Tips,1,hgetallCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,HGETALL_Keyspecs,1,NULL,1),.args=HGETALL_Args},
11828+
{MAKE_CMD("hgetdel","Returns the values of one or more fields and deletes them from a hash.","O(N) where N is the number of fields to be retrieved and deleted.","9.1.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETDEL_History,0,HGETDEL_Tips,0,hgetdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_HASH,HGETDEL_Keyspecs,1,NULL,2),.args=HGETDEL_Args},
1180311829
{MAKE_CMD("hgetex","Get the value of one or more fields of a given hash key, and optionally set their expiration time or time-to-live (TTL).","O(1)","9.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETEX_History,0,HGETEX_Tips,0,hgetexCommand,-5,CMD_WRITE|CMD_FAST,ACL_CATEGORY_HASH,HGETEX_Keyspecs,1,NULL,3),.args=HGETEX_Args},
1180411830
{MAKE_CMD("hincrby","Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBY_History,0,HINCRBY_Tips,0,hincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HINCRBY_Keyspecs,1,NULL,3),.args=HINCRBY_Args},
1180511831
{MAKE_CMD("hincrbyfloat","Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBYFLOAT_History,0,HINCRBYFLOAT_Tips,0,hincrbyfloatCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HINCRBYFLOAT_Keyspecs,1,NULL,3),.args=HINCRBYFLOAT_Args},

src/commands/hgetdel.json

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"HGETDEL": {
3+
"summary": "Returns the values of one or more fields and deletes them from a hash.",
4+
"complexity": "O(N) where N is the number of fields to be retrieved and deleted.",
5+
"group": "hash",
6+
"since": "9.1.0",
7+
"arity": -3,
8+
"function": "hgetdelCommand",
9+
"command_flags": [
10+
"WRITE",
11+
"FAST"
12+
],
13+
"acl_categories": [
14+
"HASH"
15+
],
16+
"key_specs": [
17+
{
18+
"flags": [
19+
"RW",
20+
"ACCESS",
21+
"DELETE"
22+
],
23+
"begin_search": {
24+
"index": {
25+
"pos": 1
26+
}
27+
},
28+
"find_keys": {
29+
"range": {
30+
"lastkey": 0,
31+
"step": 1,
32+
"limit": 0
33+
}
34+
}
35+
}
36+
],
37+
"reply_schema": {
38+
"description": "List of values associated with the given fields, in the same order as they are requested. Returns nil for fields that do not exist.",
39+
"type": "array",
40+
"minItems": 1,
41+
"items": {
42+
"oneOf": [
43+
{
44+
"type": "string"
45+
},
46+
{
47+
"type": "null"
48+
}
49+
]
50+
}
51+
},
52+
"arguments": [
53+
{
54+
"name": "key",
55+
"type": "key",
56+
"key_spec_index": 0
57+
},
58+
{
59+
"name": "field",
60+
"type": "string",
61+
"multiple": true
62+
}
63+
]
64+
}
65+
}

src/server.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3924,6 +3924,7 @@ void hsetnxCommand(client *c);
39243924
void hsetexCommand(client *c);
39253925
void hgetexCommand(client *c);
39263926
void hgetCommand(client *c);
3927+
void hgetdelCommand(client *c);
39273928
void hmgetCommand(client *c);
39283929
void hdelCommand(client *c);
39293930
void hlenCommand(client *c);

src/t_hash.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,52 @@ void hdelCommand(client *c) {
10531053
addReplyLongLong(c, deleted);
10541054
}
10551055

1056+
void hgetdelCommand(client *c) {
1057+
robj *o;
1058+
int i, deleted = 0;
1059+
bool keyremoved = false;
1060+
1061+
/* Don't abort when the key cannot be found. Non-existing keys are empty
1062+
* hashes, where HGETDEL should respond with a series of null bulks. */
1063+
o = lookupKeyRead(c->db, c->argv[1]);
1064+
if (checkType(c, o, OBJ_HASH)) return;
1065+
1066+
addReplyArrayLen(c, c->argc - 2);
1067+
for (i = 2; i < c->argc; i++) {
1068+
addHashFieldToReply(c, o, c->argv[i]->ptr);
1069+
}
1070+
1071+
/* If hash doesn't exist, we're done (already replied with NULLs) */
1072+
if (o == NULL) return;
1073+
1074+
/* Now delete the fields */
1075+
o = lookupKeyWrite(c->db, c->argv[1]);
1076+
if (o == NULL || checkType(c, o, OBJ_HASH)) return;
1077+
1078+
bool hash_volatile_items = hashTypeHasVolatileFields(o);
1079+
for (i = 2; i < c->argc; i++) {
1080+
if (hashTypeDelete(o, c->argv[i]->ptr)) {
1081+
deleted++;
1082+
if (hashTypeLength(o) == 0) {
1083+
if (hash_volatile_items) dbUntrackKeyWithVolatileItems(c->db, o);
1084+
dbDelete(c->db, c->argv[1]);
1085+
keyremoved = true;
1086+
break;
1087+
}
1088+
}
1089+
}
1090+
1091+
if (deleted) {
1092+
if (!keyremoved && hash_volatile_items != hashTypeHasVolatileFields(o)) {
1093+
dbUpdateObjectWithVolatileItemsTracking(c->db, o);
1094+
}
1095+
signalModifiedKey(c, c->db, c->argv[1]);
1096+
notifyKeyspaceEvent(NOTIFY_HASH, "hgetdel", c->argv[1], c->db->id);
1097+
if (keyremoved) notifyKeyspaceEvent(NOTIFY_GENERIC, "del", c->argv[1], c->db->id);
1098+
server.dirty += deleted;
1099+
}
1100+
}
1101+
10561102
void hlenCommand(client *c) {
10571103
robj *o;
10581104

tests/unit/type/hash.tcl

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,72 @@ start_server {tags {"hash"}} {
439439
r hgetall htest
440440
} {}
441441

442+
test {HGETDEL - single field} {
443+
r del myhash
444+
r hset myhash field1 value1
445+
set rv {}
446+
lappend rv [r hgetdel myhash field1]
447+
lappend rv [r hexists myhash field1]
448+
lappend rv [r exists myhash]
449+
set _ $rv
450+
} {value1 0 0}
451+
452+
test {HGETDEL - multiple fields} {
453+
r del myhash
454+
r hmset myhash field1 value1 field2 value2 field3 value3
455+
set rv {}
456+
lappend rv [r hgetdel myhash field1 field3]
457+
lappend rv [r hexists myhash field1]
458+
lappend rv [r hexists myhash field2]
459+
lappend rv [r hexists myhash field3]
460+
lappend rv [r hget myhash field2]
461+
set _ $rv
462+
} {{value1 value3} 0 1 0 value2}
463+
464+
test {HGETDEL - non-existing field} {
465+
r del myhash
466+
r hset myhash field1 value1
467+
set rv {}
468+
lappend rv [r hgetdel myhash nonexisting]
469+
lappend rv [r hexists myhash field1]
470+
set _ $rv
471+
} {{{}} 1}
472+
473+
test {HGETDEL - non-existing key} {
474+
r del myhash
475+
assert_equal {{}} [r hgetdel myhash field1]
476+
}
477+
478+
test {HGETDEL - mix of existing and non-existing fields} {
479+
r del myhash
480+
r hmset myhash a 1 b 2 c 3
481+
set rv {}
482+
lappend rv [r hgetdel myhash a nonexist b]
483+
lappend rv [r hexists myhash a]
484+
lappend rv [r hexists myhash b]
485+
lappend rv [r hexists myhash c]
486+
set _ $rv
487+
} {{1 {} 2} 0 0 1}
488+
489+
test {HGETDEL - hash becomes empty after deletion} {
490+
r del myhash
491+
r hmset myhash a 1 b 2
492+
set rv {}
493+
lappend rv [r hgetdel myhash a b]
494+
lappend rv [r exists myhash]
495+
set _ $rv
496+
} {{1 2} 0}
497+
498+
test {HGETDEL - wrong type} {
499+
r del wrongtype
500+
r set wrongtype somevalue
501+
assert_error "*WRONGTYPE*" {r hgetdel wrongtype field1}
502+
}
503+
504+
test {HGETDEL - wrong number of arguments} {
505+
assert_error "*wrong number of arguments*" {r hgetdel myhash}
506+
}
507+
442508
test {HDEL and return value} {
443509
set rv {}
444510
lappend rv [r hdel smallhash nokey]

0 commit comments

Comments
 (0)