summaryrefslogtreecommitdiff
path: root/src/bitops.c
diff options
context:
space:
mode:
authorguybe7 <guy.benoish@redislabs.com>2022-03-21 11:33:27 +0200
committerGitHub <noreply@github.com>2022-03-21 11:33:27 +0200
commite82c1aedeaa7aee8c6290d6ceaafb347996eb671 (patch)
tree12b3b0b74deb09d20ae548bee3de31e592ee5055 /src/bitops.c
parent2f9cdcd73323053b789b4a2284b40b047f8e047c (diff)
downloadredis-e82c1aedeaa7aee8c6290d6ceaafb347996eb671.tar.gz
BITSET and BITFIELD SET should propagate even if just length changed (#10459)
Bug introduced in #9403, caused inconsistency between master and replica in case just the length (i.e. set a high-index bit to 0) changed.
Diffstat (limited to 'src/bitops.c')
-rw-r--r--src/bitops.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/src/bitops.c b/src/bitops.c
index 14bcc2371..8a6dee44d 100644
--- a/src/bitops.c
+++ b/src/bitops.c
@@ -478,19 +478,21 @@ int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {
* so that the 'maxbit' bit can be addressed. The object is finally
* returned. Otherwise if the key holds a wrong type NULL is returned and
* an error is sent to the client. */
-robj *lookupStringForBitCommand(client *c, uint64_t maxbit, int *created) {
+robj *lookupStringForBitCommand(client *c, uint64_t maxbit, int *dirty) {
size_t byte = maxbit >> 3;
robj *o = lookupKeyWrite(c->db,c->argv[1]);
if (checkType(c,o,OBJ_STRING)) return NULL;
+ if (dirty) *dirty = 0;
if (o == NULL) {
- if (created) *created = 1;
o = createObject(OBJ_STRING,sdsnewlen(NULL, byte+1));
dbAdd(c->db,c->argv[1],o);
+ if (dirty) *dirty = 1;
} else {
- if (created) *created = 0;
o = dbUnshareStringValue(c->db,c->argv[1],o);
+ size_t oldlen = sdslen(o->ptr);
o->ptr = sdsgrowzero(o->ptr,byte+1);
+ if (dirty && oldlen != sdslen(o->ptr)) *dirty = 1;
}
return o;
}
@@ -547,8 +549,8 @@ void setbitCommand(client *c) {
return;
}
- int created;
- if ((o = lookupStringForBitCommand(c,bitoffset,&created)) == NULL) return;
+ int dirty;
+ if ((o = lookupStringForBitCommand(c,bitoffset,&dirty)) == NULL) return;
/* Get current values */
byte = bitoffset >> 3;
@@ -556,10 +558,10 @@ void setbitCommand(client *c) {
bit = 7 - (bitoffset & 0x7);
bitval = byteval & (1 << bit);
- /* Either it is newly created, or the bit changes before and after.
+ /* Either it is newly created, changed length, or the bit changes before and after.
* Note that the bitval here is actually a decimal number.
* So we need to use `!!` to convert it to 0 or 1 for comparison. */
- if (created || (!!bitval != on)) {
+ if (dirty || (!!bitval != on)) {
/* Update byte with new bit value. */
byteval &= ~(1 << bit);
byteval |= ((on & 0x1) << bit);
@@ -1028,7 +1030,7 @@ struct bitfieldOp {
void bitfieldGeneric(client *c, int flags) {
robj *o;
uint64_t bitoffset;
- int j, numops = 0, changes = 0, created = 0;
+ int j, numops = 0, changes = 0, dirty = 0;
struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */
int owtype = BFOVERFLOW_WRAP; /* Overflow type. */
int readonly = 1;
@@ -1122,7 +1124,7 @@ void bitfieldGeneric(client *c, int flags) {
/* Lookup by making room up to the farthest bit reached by
* this operation. */
if ((o = lookupStringForBitCommand(c,
- highest_write_offset,&created)) == NULL) {
+ highest_write_offset,&dirty)) == NULL) {
zfree(ops);
return;
}
@@ -1172,7 +1174,7 @@ void bitfieldGeneric(client *c, int flags) {
setSignedBitfield(o->ptr,thisop->offset,
thisop->bits,newval);
- if (created || (oldval != newval))
+ if (dirty || (oldval != newval))
changes++;
} else {
addReplyNull(c);
@@ -1204,7 +1206,7 @@ void bitfieldGeneric(client *c, int flags) {
setUnsignedBitfield(o->ptr,thisop->offset,
thisop->bits,newval);
- if (created || (oldval != newval))
+ if (dirty || (oldval != newval))
changes++;
} else {
addReplyNull(c);