} else {
dictEntry *de = dictFind(o->ptr,key);
if (de == NULL) return -1;
- *objval = dictGetEntryVal(de);
+ *objval = dictGetVal(de);
}
return o->encoding;
}
} else if (hi->encoding == REDIS_ENCODING_HT) {
hi->di = dictGetIterator(subject->ptr);
} else {
- redisAssert(NULL);
+ redisAssertWithInfo(NULL,subject,0);
}
return hi;
}
}
/* Get key or value object at current iteration position.
- * This increases the refcount of the field object by 1. */
-robj *hashTypeCurrent(hashTypeIterator *hi, int what) {
- robj *o;
+ * The returned item differs with the hash object encoding:
+ * - When encoding is REDIS_ENCODING_HT, the objval pointer is populated
+ * with the original object.
+ * - When encoding is REDIS_ENCODING_ZIPMAP, a pointer to the string and
+ * its length is retunred populating the v and vlen pointers.
+ * This function is copy on write friendly as accessing objects in read only
+ * does not require writing to any memory page.
+ *
+ * The function returns the encoding of the object, so that the caller
+ * can underestand if the key or value was returned as object or C string. */
+int hashTypeCurrent(hashTypeIterator *hi, int what, robj **objval, unsigned char **v, unsigned int *vlen) {
if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
if (what & REDIS_HASH_KEY) {
- o = createStringObject((char*)hi->zk,hi->zklen);
+ *v = hi->zk;
+ *vlen = hi->zklen;
} else {
- o = createStringObject((char*)hi->zv,hi->zvlen);
+ *v = hi->zv;
+ *vlen = hi->zvlen;
}
} else {
- if (what & REDIS_HASH_KEY) {
- o = dictGetEntryKey(hi->de);
- } else {
- o = dictGetEntryVal(hi->de);
- }
- incrRefCount(o);
+ if (what & REDIS_HASH_KEY)
+ *objval = dictGetKey(hi->de);
+ else
+ *objval = dictGetVal(hi->de);
+ }
+ return hi->encoding;
+}
+
+/* A non copy-on-write friendly but higher level version of hashTypeCurrent()
+ * that always returns an object with refcount incremented by one (or a new
+ * object), so it's up to the caller to decrRefCount() the object if no
+ * reference is retained. */
+robj *hashTypeCurrentObject(hashTypeIterator *hi, int what) {
+ robj *obj;
+ unsigned char *v = NULL;
+ unsigned int vlen = 0;
+ int encoding = hashTypeCurrent(hi,what,&obj,&v,&vlen);
+
+ if (encoding == REDIS_ENCODING_HT) {
+ incrRefCount(obj);
+ return obj;
+ } else {
+ return createStringObject((char*)v,vlen);
}
- return o;
}
robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {
unsigned int klen, vlen;
dict *dict = dictCreate(&hashDictType,NULL);
- redisAssert(o->type == REDIS_HASH && o->encoding != REDIS_ENCODING_HT);
+ redisAssertWithInfo(NULL,o,o->type == REDIS_HASH && o->encoding != REDIS_ENCODING_HT);
p = zipmapRewind(zm);
while((p = zipmapNext(p,&key,&klen,&val,&vlen)) != NULL) {
robj *keyobj, *valobj;
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
update = hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, update ? shared.czero : shared.cone);
- touchWatchedKey(c->db,c->argv[1]);
+ signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
hashTypeSet(o,c->argv[2],c->argv[3]);
addReply(c, shared.cone);
- touchWatchedKey(c->db,c->argv[1]);
+ signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
}
hashTypeSet(o,c->argv[i],c->argv[i+1]);
}
addReply(c, shared.ok);
- touchWatchedKey(c->db,c->argv[1]);
+ signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
hashTypeSet(o,c->argv[2],new);
decrRefCount(new);
addReplyLongLong(c,value);
- touchWatchedKey(c->db,c->argv[1]);
+ signalModifiedKey(c->db,c->argv[1]);
+ server.dirty++;
+}
+
+void hincrbyfloatCommand(redisClient *c) {
+ double long value, incr;
+ robj *o, *current, *new;
+
+ if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
+ if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+ if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
+ if (getLongDoubleFromObjectOrReply(c,current,&value,
+ "hash value is not a valid float") != REDIS_OK) {
+ decrRefCount(current);
+ return;
+ }
+ decrRefCount(current);
+ } else {
+ value = 0;
+ }
+
+ value += incr;
+ new = createStringObjectFromLongDouble(value);
+ hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
+ hashTypeSet(o,c->argv[2],new);
+ addReplyBulk(c,new);
+ decrRefCount(new);
+ signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
void hdelCommand(redisClient *c) {
robj *o;
+ int j, deleted = 0;
+
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
- if (hashTypeDelete(o,c->argv[2])) {
- if (hashTypeLength(o) == 0) dbDelete(c->db,c->argv[1]);
- addReply(c,shared.cone);
- touchWatchedKey(c->db,c->argv[1]);
- server.dirty++;
- } else {
- addReply(c,shared.czero);
+ for (j = 2; j < c->argc; j++) {
+ if (hashTypeDelete(o,c->argv[j])) {
+ deleted++;
+ if (hashTypeLength(o) == 0) {
+ dbDelete(c->db,c->argv[1]);
+ break;
+ }
+ }
+ }
+ if (deleted) {
+ signalModifiedKey(c->db,c->argv[1]);
+ server.dirty += deleted;
}
+ addReplyLongLong(c,deleted);
}
void hlenCommand(redisClient *c) {
}
void genericHgetallCommand(redisClient *c, int flags) {
- robj *o, *obj;
+ robj *o;
unsigned long count = 0;
hashTypeIterator *hi;
void *replylen = NULL;
replylen = addDeferredMultiBulkLength(c);
hi = hashTypeInitIterator(o);
while (hashTypeNext(hi) != REDIS_ERR) {
+ robj *obj;
+ unsigned char *v = NULL;
+ unsigned int vlen = 0;
+ int encoding;
+
if (flags & REDIS_HASH_KEY) {
- obj = hashTypeCurrent(hi,REDIS_HASH_KEY);
- addReplyBulk(c,obj);
- decrRefCount(obj);
+ encoding = hashTypeCurrent(hi,REDIS_HASH_KEY,&obj,&v,&vlen);
+ if (encoding == REDIS_ENCODING_HT)
+ addReplyBulk(c,obj);
+ else
+ addReplyBulkCBuffer(c,v,vlen);
count++;
}
if (flags & REDIS_HASH_VALUE) {
- obj = hashTypeCurrent(hi,REDIS_HASH_VALUE);
- addReplyBulk(c,obj);
- decrRefCount(obj);
+ encoding = hashTypeCurrent(hi,REDIS_HASH_VALUE,&obj,&v,&vlen);
+ if (encoding == REDIS_ENCODING_HT)
+ addReplyBulk(c,obj);
+ else
+ addReplyBulkCBuffer(c,v,vlen);
count++;
}
}