Skip to content

Commit 8182f4a

Browse files
authored
HSETEX with FXX should not create an object if it does not exist (#2716)
When the hash object does not exist FXX should simply fail the check without creating the object while FNX should be trivial and succeed. Note - also fix a potential compilation warning on some COMPILERS doing constant folding of variable length array when size is const expression. Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
1 parent 18214be commit 8182f4a

File tree

3 files changed

+29
-12
lines changed

3 files changed

+29
-12
lines changed

src/db.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,7 @@ void propagateDeletion(serverDb *db, robj *key, int lazy, int slot) {
19711971
server.replication_allowed = prev_replication_allowed;
19721972
}
19731973

1974-
static const size_t EXPIRE_BULK_LIMIT = 1024; /* Maximum number of fields to active-expire (per replicated HDEL command */
1974+
#define EXPIRE_BULK_LIMIT ((size_t)1024) /* Maximum number of fields to active-expire (per replicated HDEL command */
19751975

19761976
/* Propagate HDEL commands for deleted hash fields to AOF and replicas.
19771977
*

src/t_hash.c

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,13 +1209,6 @@ void hsetexCommand(client *c) {
12091209
if (checkType(c, o, OBJ_HASH))
12101210
return;
12111211

1212-
if (o == NULL) {
1213-
o = createHashObject();
1214-
dbAdd(c->db, c->argv[1], &o);
1215-
}
1216-
1217-
bool has_volatile_fields = hashTypeHasVolatileFields(o);
1218-
12191212
/* Handle parsing and calculating the expiration time. */
12201213
if (flags & ARGS_KEEPTTL)
12211214
set_flags |= HASH_SET_KEEP_EXPIRY;
@@ -1230,17 +1223,35 @@ void hsetexCommand(client *c) {
12301223
}
12311224
}
12321225

1233-
/* Check for all fields condition */
1226+
/* Check FNX/FXX field-level conditions */
12341227
if (flags & (ARGS_SET_FNX | ARGS_SET_FXX)) {
1235-
for (i = fields_index; i < c->argc; i += 2) {
1236-
if (((flags & ARGS_SET_FNX) && hashTypeExists(o, c->argv[i]->ptr)) ||
1237-
((flags & ARGS_SET_FXX) && !hashTypeExists(o, c->argv[i]->ptr))) {
1228+
if (o) {
1229+
/* Key exists: check fields normally */
1230+
for (i = fields_index; i < c->argc; i += 2) {
1231+
if (((flags & ARGS_SET_FNX) && hashTypeExists(o, c->argv[i]->ptr)) ||
1232+
((flags & ARGS_SET_FXX) && !hashTypeExists(o, c->argv[i]->ptr))) {
1233+
addReply(c, shared.czero);
1234+
return;
1235+
}
1236+
}
1237+
} else {
1238+
/* Key does not exist */
1239+
if (flags & ARGS_SET_FXX) {
1240+
/* Any FXX fails because no fields exist */
12381241
addReply(c, shared.czero);
12391242
return;
12401243
}
1244+
/* FNX automatically passes if key doesn't exist, nothing to check */
12411245
}
12421246
}
12431247

1248+
if (o == NULL) {
1249+
o = createHashObject();
1250+
dbAdd(c->db, c->argv[1], &o);
1251+
}
1252+
1253+
bool has_volatile_fields = hashTypeHasVolatileFields(o);
1254+
12441255
/* In case we are expiring all the elements prepare a new argv since we are going to delete all the expired fields. */
12451256
if (set_expired) {
12461257
new_argv = zmalloc(sizeof(robj *) * (num_fields + 2));

tests/unit/hashexpire.tcl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,12 @@ start_server {tags {"hashexpire"}} {
684684
set e
685685
} {ERR *}
686686

687+
test {HSETEX EX - FXX does not create object in case key does not exist} {
688+
r FLUSHALL
689+
assert_equal 0 [r HSETEX myhash EX 10 FXX FIELDS 1 x y]
690+
assert_equal 0 [r EXISTS myhash]
691+
}
692+
687693
###### Test EXPIRE #############
688694

689695

0 commit comments

Comments
 (0)