Skip to content

Commit eb83f71

Browse files
authored
Merge pull request #16 from hanxizh9910/feature/hsetex-nx-xx-support
Feature/hsetex nx xx support
2 parents f39a809 + 64f09c6 commit eb83f71

5 files changed

Lines changed: 61 additions & 3 deletions

File tree

src/commands.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4314,6 +4314,8 @@ keySpec HSETEX_Keyspecs[1] = {
43144314
struct COMMAND_ARG HSETEX_fields_condition_Subargs[] = {
43154315
{MAKE_ARG("fnx",ARG_TYPE_PURE_TOKEN,-1,"FNX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
43164316
{MAKE_ARG("fxx",ARG_TYPE_PURE_TOKEN,-1,"FXX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
4317+
{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
4318+
{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
43174319
};
43184320

43194321
/* HSETEX expiration argument table */
@@ -4340,7 +4342,7 @@ struct COMMAND_ARG HSETEX_fields_Subargs[] = {
43404342
/* HSETEX argument table */
43414343
struct COMMAND_ARG HSETEX_Args[] = {
43424344
{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
4343-
{MAKE_ARG("fields-condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=HSETEX_fields_condition_Subargs},
4345+
{MAKE_ARG("fields-condition",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=HSETEX_fields_condition_Subargs},
43444346
{MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=HSETEX_expiration_Subargs},
43454347
{MAKE_ARG("fields",ARG_TYPE_BLOCK,-1,"FIELDS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=HSETEX_fields_Subargs},
43464348
};

src/commands/hsetex.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@
6666
"name": "fxx",
6767
"type": "pure-token",
6868
"token": "FXX"
69+
},
70+
{
71+
"name": "nx",
72+
"type": "pure-token",
73+
"token": "NX"
74+
},
75+
{
76+
"name": "xx",
77+
"type": "pure-token",
78+
"token": "XX"
6979
}
7080
]
7181
},

src/server.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7445,12 +7445,12 @@ int parseExtendedCommandArgumentsOrReply(client *c, int *flags, int *unit, robj
74457445
/* clang-format off */
74467446
if ((opt[0] == 'n' || opt[0] == 'N') &&
74477447
(opt[1] == 'x' || opt[1] == 'X') && opt[2] == '\0' &&
7448-
!(*flags & ARGS_SET_XX || *flags & ARGS_SET_IFEQ) && (command_type == COMMAND_SET))
7448+
!(*flags & ARGS_SET_XX || *flags & ARGS_SET_IFEQ) && (command_type == COMMAND_SET || command_type == COMMAND_HSET))
74497449
{
74507450
*flags |= ARGS_SET_NX;
74517451
} else if ((opt[0] == 'x' || opt[0] == 'X') &&
74527452
(opt[1] == 'x' || opt[1] == 'X') && opt[2] == '\0' &&
7453-
!(*flags & ARGS_SET_NX || *flags & ARGS_SET_IFEQ) && (command_type == COMMAND_SET))
7453+
!(*flags & ARGS_SET_NX || *flags & ARGS_SET_IFEQ) && (command_type == COMMAND_SET || command_type == COMMAND_HSET))
74547454
{
74557455
*flags |= ARGS_SET_XX;
74567456
} else if ((opt[0] == 'f' || opt[0] == 'F') &&

src/t_hash.c

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

1212+
/* Check NX/XX key-level conditions before creating a new object */
1213+
if ((flags & ARGS_SET_NX) && o != NULL) {
1214+
addReply(c, shared.czero); // NX fails if key exists
1215+
return;
1216+
}
1217+
1218+
if ((flags & ARGS_SET_XX) && o == NULL) {
1219+
addReply(c, shared.czero); // XX fails if key does not exist
1220+
return;
1221+
}
1222+
12121223
if (o == NULL) {
12131224
o = createHashObject();
12141225
dbAdd(c->db, c->argv[1], &o);

tests/unit/hashexpire.tcl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,42 @@ start_server {tags {"hashexpire"}} {
627627
assert_error {ERR numfields should be greater than 0 and match the provided number of fields} {r HSETEX myhash PX 100 FIELDS 1 field1 val1 extra}
628628
}
629629

630+
## NX/XX key-level tests
630631

632+
test {HSETEX NX - non-existing key creates the key} {
633+
r FLUSHALL
634+
set res [r HSETEX myhash NX FIELDS 2 f1 v1 f2 v2]
635+
assert_equal 1 $res
636+
assert_equal v1 [r HGET myhash f1]
637+
assert_equal v2 [r HGET myhash f2]
638+
}
639+
640+
test {HSETEX NX - existing key blocked} {
641+
r FLUSHALL
642+
r HSET myhash f1 v1
643+
set res [r HSETEX myhash NX FIELDS 2 f1 new1 f2 new2]
644+
assert_equal 0 $res
645+
assert_equal v1 [r HGET myhash f1]
646+
assert_equal 0 [r HEXISTS myhash f2]
647+
}
648+
649+
test {HSETEX XX - existing key updates fields} {
650+
r FLUSHALL
651+
r HSET myhash f1 v1 f2 v2
652+
set res [r HSETEX myhash XX FIELDS 2 f1 new1 f2 new2]
653+
assert_equal 1 $res
654+
assert_equal new1 [r HGET myhash f1]
655+
assert_equal new2 [r HGET myhash f2]
656+
}
657+
658+
test {HSETEX XX - non-existing key blocked} {
659+
r FLUSHALL
660+
set res [r HSETEX myhash XX FIELDS 2 f1 v1 f2 v2]
661+
assert_equal 0 $res
662+
assert_equal 0 [r HEXISTS myhash f1]
663+
assert_equal 0 [r HEXISTS myhash f2]
664+
}
665+
631666
## FNX/FXX
632667

633668
# hsetex throws ERR *, it shouldn't

0 commit comments

Comments
 (0)