}
/* Get the value from a hash identified by key. Returns either a string
- * object or NULL if the value cannot be found. The refcount of the object
- * is always increased by 1 when the value was found. */
+ * object or NULL if the value cannot be found.
+ * Note: the refcount for objects retrieved from a zipmap is set to 0.
+ * This is done, so addReply will increment and clean up the object.
+ * Make sure to clean up the object when it isn't added to a reply. */
static robj *hashGet(robj *o, robj *key) {
robj *value = NULL;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
key = getDecodedObject(key);
if (zipmapGet(o->ptr,key->ptr,sdslen(key->ptr),&v,&vlen)) {
value = createStringObject((char*)v,vlen);
+ value->refcount = 0;
}
decrRefCount(key);
} else {
dictEntry *de = dictFind(o->ptr,key);
if (de != NULL) {
value = dictGetEntryVal(de);
- incrRefCount(value);
}
}
return value;
return REDIS_OK;
}
-/* Get key or value object at current iteration position. This always
- * increases the refcount of the field object by 1. */
+/* Get key or value object at current iteration position.
+ * See comments at hashGet for a discussion on the refcount for
+ * keys and values retrieved from zipmaps. */
static inline robj *hashCurrent(hashIterator *hi, int what) {
robj *o;
if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
} else {
o = createStringObject((char*)hi->zv,hi->zvlen);
}
+ o->refcount = 0;
} else {
if (what & REDIS_HASH_KEY) {
o = dictGetEntryKey(hi->de);
} else {
o = dictGetEntryVal(hi->de);
}
- incrRefCount(o);
}
return o;
}
/* ============================= Hash commands ============================== */
static void hsetCommand(redisClient *c) {
- int update = 0;
+ int update;
robj *o;
if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
hashTryConversion(o,c->argv,2,3);
update = hashReplace(o,c->argv[2],c->argv[3]);
- addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",update == 0));
+ addReply(c, update ? shared.czero : shared.cone);
server.dirty++;
}
value = (long)current->ptr;
else
redisAssert(1 != 1);
- decrRefCount(current);
+
+ /* clean up object when it was retrieved from a zipmap */
+ if (current->refcount == 0) {
+ current->refcount = 1;
+ decrRefCount(current);
+ }
} else {
value = 0;
}
if ((value = hashGet(o,c->argv[2])) != NULL) {
addReplyBulk(c,value);
- decrRefCount(value);
} else {
addReply(c,shared.nullbulk);
}
for (i = 2; i < c->argc; i++) {
if (o != NULL && (value = hashGet(o,c->argv[i])) != NULL) {
addReplyBulk(c,value);
- decrRefCount(value);
} else {
addReply(c,shared.nullbulk);
}
if (flags & REDIS_HASH_KEY) {
obj = hashCurrent(&hi,REDIS_HASH_KEY);
addReplyBulk(c,obj);
- decrRefCount(obj);
count++;
}
if (flags & REDIS_HASH_VALUE) {
obj = hashCurrent(&hi,REDIS_HASH_VALUE);
addReplyBulk(c,obj);
- decrRefCount(obj);
count++;
}
}
/* Return the value associated to the key with a name obtained
* substituting the first occurence of '*' in 'pattern' with 'subst' */
static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
- char *p;
+ char *p, *f;
sds spat, ssub;
- robj keyobj;
- int prefixlen, sublen, postfixlen;
+ robj keyobj, fieldobj, *o;
+ int prefixlen, sublen, postfixlen, fieldlen;
/* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
struct {
long len;
long free;
char buf[REDIS_SORTKEY_MAX+1];
- } keyname;
+ } keyname, fieldname;
/* If the pattern is "#" return the substitution object itself in order
* to implement the "SORT ... GET #" feature. */
return NULL;
}
+ /* Find out if we're dealing with a hash dereference. */
+ if ((f = strstr(p+1, "->")) != NULL) {
+ fieldlen = sdslen(spat)-(f-spat);
+ /* this also copies \0 character */
+ memcpy(fieldname.buf,f+2,fieldlen-1);
+ fieldname.len = fieldlen-2;
+ } else {
+ fieldlen = 0;
+ }
+
prefixlen = p-spat;
sublen = sdslen(ssub);
- postfixlen = sdslen(spat)-(prefixlen+1);
+ postfixlen = sdslen(spat)-(prefixlen+1)-fieldlen;
memcpy(keyname.buf,spat,prefixlen);
memcpy(keyname.buf+prefixlen,ssub,sublen);
memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
keyname.buf[prefixlen+sublen+postfixlen] = '\0';
keyname.len = prefixlen+sublen+postfixlen;
-
- initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(long)*2))
decrRefCount(subst);
- /* printf("lookup '%s' => %p\n", keyname.buf,de); */
- return lookupKeyRead(db,&keyobj);
+ /* Lookup substituted key */
+ initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(long)*2));
+ o = lookupKeyRead(db,&keyobj);
+
+ /* Retrieve value from hash by the field name */
+ if (o != NULL && fieldlen > 0) {
+ if (o->type != REDIS_HASH || fieldname.len < 1) {
+ return NULL;
+ }
+ initStaticStringObject(fieldobj,((char*)&fieldname)+(sizeof(long)*2));
+ o = hashGet(o, &fieldobj);
+ }
+
+ return o;
}
/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
/* Now it's time to load the right scores in the sorting vector */
if (dontsort == 0) {
for (j = 0; j < vectorlen; j++) {
+ robj *byval;
if (sortby) {
- robj *byval;
-
+ /* lookup value to sort by */
byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
if (!byval || byval->type != REDIS_STRING) continue;
- if (alpha) {
- vector[j].u.cmpobj = getDecodedObject(byval);
+ } else {
+ /* use object itself to sort by */
+ byval = vector[j].obj;
+ }
+
+ if (alpha) {
+ /* getDecodedObject increments refcount, so the corresponding
+ * decrRefCount will clean up values coming from a zipmap. */
+ vector[j].u.cmpobj = getDecodedObject(byval);
+ } else {
+ if (byval->encoding == REDIS_ENCODING_RAW) {
+ vector[j].u.score = strtod(byval->ptr,NULL);
} else {
- if (byval->encoding == REDIS_ENCODING_RAW) {
- vector[j].u.score = strtod(byval->ptr,NULL);
- } else {
- /* Don't need to decode the object if it's
- * integer-encoded (the only encoding supported) so
- * far. We can just cast it */
- if (byval->encoding == REDIS_ENCODING_INT) {
- vector[j].u.score = (long)byval->ptr;
- } else
- redisAssert(1 != 1);
- }
+ /* Don't need to decode the object if it's
+ * integer-encoded (the only encoding supported) so
+ * far. We can just cast it */
+ if (byval->encoding == REDIS_ENCODING_INT) {
+ vector[j].u.score = (long)byval->ptr;
+ } else
+ redisAssert(1 != 1);
}
- } else {
- if (!alpha) {
- if (vector[j].obj->encoding == REDIS_ENCODING_RAW)
- vector[j].u.score = strtod(vector[j].obj->ptr,NULL);
- else {
- if (vector[j].obj->encoding == REDIS_ENCODING_INT)
- vector[j].u.score = (long) vector[j].obj->ptr;
- else
- redisAssert(1 != 1);
- }
+
+ /* clean up immediately if this value came from a zipmap */
+ if (byval->refcount == 0) {
+ byval->refcount = 1;
+ decrRefCount(byval);
}
}
}