]> git.saurik.com Git - redis.git/blame - src/t_hash.c
First implementation of --test-memory. Still a work in progress.
[redis.git] / src / t_hash.c
CommitLineData
e2641e09 1#include "redis.h"
e2641e09 2#include <math.h>
3
4/*-----------------------------------------------------------------------------
5 * Hash type API
6 *----------------------------------------------------------------------------*/
7
8/* Check the length of a number of objects to see if we need to convert a
ebd85e9a 9 * ziplist to a real hash. Note that we only check string encoded objects
e2641e09 10 * as their string length can be queried in constant time. */
ebd85e9a 11void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
e2641e09 12 int i;
ebd85e9a
PN
13
14 if (o->encoding != REDIS_ENCODING_ZIPLIST) return;
e2641e09 15
16 for (i = start; i <= end; i++) {
17 if (argv[i]->encoding == REDIS_ENCODING_RAW &&
ebd85e9a 18 sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
e2641e09 19 {
ebd85e9a
PN
20 hashTypeConvert(o, REDIS_ENCODING_HT);
21 break;
e2641e09 22 }
23 }
24}
25
26/* Encode given objects in-place when the hash uses a dict. */
27void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
28 if (subject->encoding == REDIS_ENCODING_HT) {
29 if (o1) *o1 = tryObjectEncoding(*o1);
30 if (o2) *o2 = tryObjectEncoding(*o2);
31 }
32}
33
ebd85e9a
PN
34/* Get the value from a ziplist encoded hash, identified by field.
35 * Returns -1 when the field cannot be found. */
36int hashTypeGetFromZiplist(robj *o, robj *field,
37 unsigned char **vstr,
38 unsigned int *vlen,
39 long long *vll)
3d24304f 40{
ebd85e9a
PN
41 unsigned char *zl, *fptr = NULL, *vptr = NULL;
42 int ret;
3d24304f 43
ebd85e9a
PN
44 redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);
45
46 field = getDecodedObject(field);
47
48 zl = o->ptr;
49 fptr = ziplistIndex(zl, ZIPLIST_HEAD);
fe458402
PN
50 if (fptr != NULL) {
51 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
52 if (fptr != NULL) {
53 /* Grab pointer to the value (fptr points to the field) */
54 vptr = ziplistNext(zl, fptr);
55 redisAssert(vptr != NULL);
ebd85e9a 56 }
3d24304f 57 }
ebd85e9a
PN
58
59 decrRefCount(field);
60
fe458402 61 if (vptr != NULL) {
ebd85e9a
PN
62 ret = ziplistGet(vptr, vstr, vlen, vll);
63 redisAssert(ret);
64 return 0;
65 }
66
67 return -1;
3d24304f 68}
69
ebd85e9a
PN
70/* Get the value from a hash table encoded hash, identified by field.
71 * Returns -1 when the field cannot be found. */
72int hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {
73 dictEntry *de;
74
75 redisAssert(o->encoding == REDIS_ENCODING_HT);
76
77 de = dictFind(o->ptr, field);
c0caa1cf 78 if (de == NULL) return -1;
ebd85e9a
PN
79 *value = dictGetVal(de);
80 return 0;
81}
82
83/* Higher level function of hashTypeGet*() that always returns a Redis
3d24304f 84 * object (either new or with refcount incremented), so that the caller
85 * can retain a reference or call decrRefCount after the usage.
86 *
87 * The lower level function can prevent copy on write so it is
88 * the preferred way of doing read operations. */
ebd85e9a
PN
89robj *hashTypeGetObject(robj *o, robj *field) {
90 robj *value = NULL;
91
92 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
93 unsigned char *vstr = NULL;
94 unsigned int vlen = UINT_MAX;
95 long long vll = LLONG_MAX;
96
97 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) {
98 if (vstr) {
99 value = createStringObject((char*)vstr, vlen);
100 } else {
101 value = createStringObjectFromLongLong(vll);
102 }
103 }
104
105 } else if (o->encoding == REDIS_ENCODING_HT) {
106 robj *aux;
107
108 if (hashTypeGetFromHashTable(o, field, &aux) == 0) {
109 incrRefCount(aux);
110 value = aux;
e2641e09 111 }
e2641e09 112 } else {
ebd85e9a
PN
113 redisPanic("Unknown hash encoding");
114 }
ebd85e9a
PN
115 return value;
116}
117
118/* Test if the specified field exists in the given hash. Returns 1 if the field
119 * exists, and 0 when it doesn't. */
120int hashTypeExists(robj *o, robj *field) {
121 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
122 unsigned char *vstr = NULL;
123 unsigned int vlen = UINT_MAX;
124 long long vll = LLONG_MAX;
125
753bb3dc 126 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;
ebd85e9a
PN
127 } else if (o->encoding == REDIS_ENCODING_HT) {
128 robj *aux;
129
753bb3dc 130 if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1;
ebd85e9a
PN
131 } else {
132 redisPanic("Unknown hash encoding");
e2641e09 133 }
134 return 0;
135}
136
137/* Add an element, discard the old if the key already exists.
138 * Return 0 on insert and 1 on update. */
ebd85e9a 139int hashTypeSet(robj *o, robj *field, robj *value) {
e2641e09 140 int update = 0;
ebd85e9a
PN
141
142 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
143 unsigned char *zl, *fptr, *vptr;
144
145 field = getDecodedObject(field);
e2641e09 146 value = getDecodedObject(value);
ebd85e9a
PN
147
148 zl = o->ptr;
149 fptr = ziplistIndex(zl, ZIPLIST_HEAD);
fe458402
PN
150 if (fptr != NULL) {
151 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
152 if (fptr != NULL) {
153 /* Grab pointer to the value (fptr points to the field) */
154 vptr = ziplistNext(zl, fptr);
155 redisAssert(vptr != NULL);
ebd85e9a 156 update = 1;
ebd85e9a 157
fe458402
PN
158 /* Delete value */
159 zl = ziplistDelete(zl, &vptr);
160
161 /* Insert new value */
162 zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
163 }
164 }
ebd85e9a 165
fe458402
PN
166 if (!update) {
167 /* Push new field/value pair onto the tail of the ziplist */
168 zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
169 zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
ebd85e9a
PN
170 }
171
ebd85e9a
PN
172 o->ptr = zl;
173
174 decrRefCount(field);
e2641e09 175 decrRefCount(value);
176
ebd85e9a
PN
177 /* Check if the ziplist needs to be converted to a hash table */
178 if (hashTypeLength(o) > server.hash_max_ziplist_entries) {
179 hashTypeConvert(o, REDIS_ENCODING_HT);
180 }
181
182 } else if (o->encoding == REDIS_ENCODING_HT) {
183 if (dictReplace(o->ptr, field, value)) { /* Insert */
184 incrRefCount(field);
185 } else { /* Update */
e2641e09 186 update = 1;
187 }
ebd85e9a 188
e2641e09 189 incrRefCount(value);
ebd85e9a
PN
190
191 } else {
192 redisPanic("Unknown hash encoding");
e2641e09 193 }
ebd85e9a 194
e2641e09 195 return update;
196}
197
198/* Delete an element from a hash.
199 * Return 1 on deleted and 0 on not found. */
ebd85e9a 200int hashTypeDelete(robj *o, robj *field) {
e2641e09 201 int deleted = 0;
ebd85e9a
PN
202
203 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
fe458402 204 unsigned char *zl, *fptr;
ebd85e9a
PN
205
206 field = getDecodedObject(field);
207
208 zl = o->ptr;
209 fptr = ziplistIndex(zl, ZIPLIST_HEAD);
fe458402
PN
210 if (fptr != NULL) {
211 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
212 if (fptr != NULL) {
ebd85e9a
PN
213 zl = ziplistDelete(zl,&fptr);
214 zl = ziplistDelete(zl,&fptr);
215 o->ptr = zl;
216 deleted = 1;
ebd85e9a 217 }
ebd85e9a
PN
218 }
219
220 decrRefCount(field);
221
222 } else if (o->encoding == REDIS_ENCODING_HT) {
223 if (dictDelete((dict*)o->ptr, field) == REDIS_OK) {
224 deleted = 1;
225
226 /* Always check if the dictionary needs a resize after a delete. */
227 if (htNeedsResize(o->ptr)) dictResize(o->ptr);
228 }
229
e2641e09 230 } else {
ebd85e9a 231 redisPanic("Unknown hash encoding");
e2641e09 232 }
ebd85e9a 233
e2641e09 234 return deleted;
235}
236
237/* Return the number of elements in a hash. */
238unsigned long hashTypeLength(robj *o) {
ebd85e9a
PN
239 unsigned long length = ULONG_MAX;
240
241 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
242 length = ziplistLen(o->ptr) / 2;
243 } else if (o->encoding == REDIS_ENCODING_HT) {
244 length = dictSize((dict*)o->ptr);
245 } else {
246 redisPanic("Unknown hash encoding");
247 }
248
249 return length;
e2641e09 250}
251
252hashTypeIterator *hashTypeInitIterator(robj *subject) {
253 hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
ebd85e9a 254 hi->subject = subject;
e2641e09 255 hi->encoding = subject->encoding;
ebd85e9a
PN
256
257 if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
258 hi->fptr = NULL;
259 hi->vptr = NULL;
e2641e09 260 } else if (hi->encoding == REDIS_ENCODING_HT) {
261 hi->di = dictGetIterator(subject->ptr);
262 } else {
ebd85e9a 263 redisPanic("Unknown hash encoding");
e2641e09 264 }
ebd85e9a 265
e2641e09 266 return hi;
267}
268
269void hashTypeReleaseIterator(hashTypeIterator *hi) {
270 if (hi->encoding == REDIS_ENCODING_HT) {
271 dictReleaseIterator(hi->di);
272 }
ebd85e9a 273
e2641e09 274 zfree(hi);
275}
276
277/* Move to the next entry in the hash. Return REDIS_OK when the next entry
278 * could be found and REDIS_ERR when the iterator reaches the end. */
279int hashTypeNext(hashTypeIterator *hi) {
ebd85e9a
PN
280 if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
281 unsigned char *zl;
282 unsigned char *fptr, *vptr;
283
284 zl = hi->subject->ptr;
285 fptr = hi->fptr;
286 vptr = hi->vptr;
287
288 if (fptr == NULL) {
289 /* Initialize cursor */
290 redisAssert(vptr == NULL);
291 fptr = ziplistIndex(zl, 0);
292 } else {
293 /* Advance cursor */
294 redisAssert(vptr != NULL);
295 fptr = ziplistNext(zl, vptr);
296 }
753bb3dc 297 if (fptr == NULL) return REDIS_ERR;
ebd85e9a
PN
298
299 /* Grab pointer to the value (fptr points to the field) */
300 vptr = ziplistNext(zl, fptr);
301 redisAssert(vptr != NULL);
302
303 /* fptr, vptr now point to the first or next pair */
304 hi->fptr = fptr;
305 hi->vptr = vptr;
ebd85e9a 306 } else if (hi->encoding == REDIS_ENCODING_HT) {
753bb3dc 307 if ((hi->de = dictNext(hi->di)) == NULL) return REDIS_ERR;
e2641e09 308 } else {
ebd85e9a 309 redisPanic("Unknown hash encoding");
e2641e09 310 }
311 return REDIS_OK;
312}
313
ebd85e9a
PN
314/* Get the field or value at iterator cursor, for an iterator on a hash value
315 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
316void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
317 unsigned char **vstr,
318 unsigned int *vlen,
319 long long *vll)
320{
321 int ret;
322
323 redisAssert(hi->encoding == REDIS_ENCODING_ZIPLIST);
324
325 if (what & REDIS_HASH_KEY) {
326 ret = ziplistGet(hi->fptr, vstr, vlen, vll);
327 redisAssert(ret);
328 } else {
329 ret = ziplistGet(hi->vptr, vstr, vlen, vll);
330 redisAssert(ret);
331 }
332}
333
334/* Get the field or value at iterator cursor, for an iterator on a hash value
335 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
336void hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) {
337 redisAssert(hi->encoding == REDIS_ENCODING_HT);
338
339 if (what & REDIS_HASH_KEY) {
340 *dst = dictGetKey(hi->de);
e2641e09 341 } else {
ebd85e9a 342 *dst = dictGetVal(hi->de);
8c304be3 343 }
8c304be3 344}
345
ebd85e9a
PN
346/* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
347 * that returns an object with incremented refcount (or a new object). It is up
348 * to the caller to decrRefCount() the object if no reference is retained. */
8c304be3 349robj *hashTypeCurrentObject(hashTypeIterator *hi, int what) {
ebd85e9a
PN
350 robj *dst;
351
352 if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
353 unsigned char *vstr = NULL;
354 unsigned int vlen = UINT_MAX;
355 long long vll = LLONG_MAX;
356
357 hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
358 if (vstr) {
359 dst = createStringObject((char*)vstr, vlen);
360 } else {
361 dst = createStringObjectFromLongLong(vll);
362 }
363
364 } else if (hi->encoding == REDIS_ENCODING_HT) {
365 hashTypeCurrentFromHashTable(hi, what, &dst);
366 incrRefCount(dst);
367
8c304be3 368 } else {
ebd85e9a 369 redisPanic("Unknown hash encoding");
e2641e09 370 }
ebd85e9a
PN
371
372 return dst;
e2641e09 373}
374
375robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {
376 robj *o = lookupKeyWrite(c->db,key);
377 if (o == NULL) {
378 o = createHashObject();
379 dbAdd(c->db,key,o);
380 } else {
381 if (o->type != REDIS_HASH) {
382 addReply(c,shared.wrongtypeerr);
383 return NULL;
384 }
385 }
386 return o;
387}
388
ebd85e9a
PN
389void hashTypeConvertZiplist(robj *o, int enc) {
390 redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);
391
392 if (enc == REDIS_ENCODING_ZIPLIST) {
393 /* Nothing to do... */
394
395 } else if (enc == REDIS_ENCODING_HT) {
396 hashTypeIterator *hi;
397 dict *dict;
398 int ret;
e2641e09 399
ebd85e9a
PN
400 hi = hashTypeInitIterator(o);
401 dict = dictCreate(&hashDictType, NULL);
e2641e09 402
ebd85e9a
PN
403 while (hashTypeNext(hi) != REDIS_ERR) {
404 robj *field, *value;
405
406 field = hashTypeCurrentObject(hi, REDIS_HASH_KEY);
407 field = tryObjectEncoding(field);
408 value = hashTypeCurrentObject(hi, REDIS_HASH_VALUE);
409 value = tryObjectEncoding(value);
410 ret = dictAdd(dict, field, value);
411 redisAssert(ret == DICT_OK);
412 }
413
414 hashTypeReleaseIterator(hi);
415 zfree(o->ptr);
416
417 o->encoding = REDIS_ENCODING_HT;
418 o->ptr = dict;
419
420 } else {
421 redisPanic("Unknown hash encoding");
422 }
423}
424
425void hashTypeConvert(robj *o, int enc) {
426 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
427 hashTypeConvertZiplist(o, enc);
428 } else if (o->encoding == REDIS_ENCODING_HT) {
429 redisPanic("Not implemented");
430 } else {
431 redisPanic("Unknown hash encoding");
e2641e09 432 }
e2641e09 433}
434
435/*-----------------------------------------------------------------------------
436 * Hash type commands
437 *----------------------------------------------------------------------------*/
438
439void hsetCommand(redisClient *c) {
440 int update;
441 robj *o;
442
443 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
444 hashTypeTryConversion(o,c->argv,2,3);
445 hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
446 update = hashTypeSet(o,c->argv[2],c->argv[3]);
447 addReply(c, update ? shared.czero : shared.cone);
cea8c5cd 448 signalModifiedKey(c->db,c->argv[1]);
e2641e09 449 server.dirty++;
450}
451
452void hsetnxCommand(redisClient *c) {
453 robj *o;
454 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
455 hashTypeTryConversion(o,c->argv,2,3);
456
457 if (hashTypeExists(o, c->argv[2])) {
458 addReply(c, shared.czero);
459 } else {
460 hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
461 hashTypeSet(o,c->argv[2],c->argv[3]);
462 addReply(c, shared.cone);
cea8c5cd 463 signalModifiedKey(c->db,c->argv[1]);
e2641e09 464 server.dirty++;
465 }
466}
467
468void hmsetCommand(redisClient *c) {
469 int i;
470 robj *o;
471
472 if ((c->argc % 2) == 1) {
3ab20376 473 addReplyError(c,"wrong number of arguments for HMSET");
e2641e09 474 return;
475 }
476
477 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
478 hashTypeTryConversion(o,c->argv,2,c->argc-1);
479 for (i = 2; i < c->argc; i += 2) {
480 hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
481 hashTypeSet(o,c->argv[i],c->argv[i+1]);
482 }
483 addReply(c, shared.ok);
cea8c5cd 484 signalModifiedKey(c->db,c->argv[1]);
e2641e09 485 server.dirty++;
486}
487
488void hincrbyCommand(redisClient *c) {
a400a9b2 489 long long value, incr, oldvalue;
e2641e09 490 robj *o, *current, *new;
491
492 if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
493 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
3d24304f 494 if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
e2641e09 495 if (getLongLongFromObjectOrReply(c,current,&value,
496 "hash value is not an integer") != REDIS_OK) {
497 decrRefCount(current);
498 return;
499 }
500 decrRefCount(current);
501 } else {
502 value = 0;
503 }
504
a400a9b2 505 oldvalue = value;
7c96b467 506 if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
507 (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
a400a9b2 508 addReplyError(c,"increment or decrement would overflow");
509 return;
510 }
7c96b467 511 value += incr;
e2641e09 512 new = createStringObjectFromLongLong(value);
513 hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
514 hashTypeSet(o,c->argv[2],new);
515 decrRefCount(new);
516 addReplyLongLong(c,value);
cea8c5cd 517 signalModifiedKey(c->db,c->argv[1]);
e2641e09 518 server.dirty++;
519}
520
68bfe993 521void hincrbyfloatCommand(redisClient *c) {
522 double long value, incr;
523 robj *o, *current, *new;
524
525 if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
526 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
527 if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
528 if (getLongDoubleFromObjectOrReply(c,current,&value,
529 "hash value is not a valid float") != REDIS_OK) {
530 decrRefCount(current);
531 return;
532 }
533 decrRefCount(current);
534 } else {
535 value = 0;
536 }
537
538 value += incr;
539 new = createStringObjectFromLongDouble(value);
540 hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
541 hashTypeSet(o,c->argv[2],new);
542 addReplyBulk(c,new);
543 decrRefCount(new);
544 signalModifiedKey(c->db,c->argv[1]);
545 server.dirty++;
546}
547
ebd85e9a
PN
548static void addHashFieldToReply(redisClient *c, robj *o, robj *field) {
549 int ret;
550
551 if (o == NULL) {
552 addReply(c, shared.nullbulk);
553 return;
554 }
555
556 if (o->encoding == REDIS_ENCODING_ZIPLIST) {
557 unsigned char *vstr = NULL;
558 unsigned int vlen = UINT_MAX;
559 long long vll = LLONG_MAX;
560
561 ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
562 if (ret < 0) {
563 addReply(c, shared.nullbulk);
564 } else {
565 if (vstr) {
566 addReplyBulkCBuffer(c, vstr, vlen);
567 } else {
568 addReplyBulkLongLong(c, vll);
569 }
570 }
571
572 } else if (o->encoding == REDIS_ENCODING_HT) {
573 robj *value;
574
575 ret = hashTypeGetFromHashTable(o, field, &value);
576 if (ret < 0) {
577 addReply(c, shared.nullbulk);
578 } else {
579 addReplyBulk(c, value);
580 }
581
582 } else {
583 redisPanic("Unknown hash encoding");
584 }
585}
586
e2641e09 587void hgetCommand(redisClient *c) {
ebd85e9a 588 robj *o;
3d24304f 589
e2641e09 590 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
591 checkType(c,o,REDIS_HASH)) return;
592
ebd85e9a 593 addHashFieldToReply(c, o, c->argv[2]);
e2641e09 594}
595
596void hmgetCommand(redisClient *c) {
ebd85e9a
PN
597 robj *o;
598 int i;
3d24304f 599
ebd85e9a
PN
600 /* Don't abort when the key cannot be found. Non-existing keys are empty
601 * hashes, where HMGET should respond with a series of null bulks. */
602 o = lookupKeyRead(c->db, c->argv[1]);
e2641e09 603 if (o != NULL && o->type != REDIS_HASH) {
ebd85e9a 604 addReply(c, shared.wrongtypeerr);
e584d82f 605 return;
e2641e09 606 }
607
ebd85e9a 608 addReplyMultiBulkLen(c, c->argc-2);
e2641e09 609 for (i = 2; i < c->argc; i++) {
ebd85e9a 610 addHashFieldToReply(c, o, c->argv[i]);
e2641e09 611 }
612}
613
614void hdelCommand(redisClient *c) {
615 robj *o;
64a13a36 616 int j, deleted = 0;
617
e2641e09 618 if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
619 checkType(c,o,REDIS_HASH)) return;
620
64a13a36 621 for (j = 2; j < c->argc; j++) {
622 if (hashTypeDelete(o,c->argv[j])) {
64a13a36 623 deleted++;
2d7162bb
PN
624 if (hashTypeLength(o) == 0) {
625 dbDelete(c->db,c->argv[1]);
626 break;
627 }
64a13a36 628 }
629 }
630 if (deleted) {
cea8c5cd 631 signalModifiedKey(c->db,c->argv[1]);
64a13a36 632 server.dirty += deleted;
e2641e09 633 }
64a13a36 634 addReplyLongLong(c,deleted);
e2641e09 635}
636
637void hlenCommand(redisClient *c) {
638 robj *o;
639 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
640 checkType(c,o,REDIS_HASH)) return;
641
b70d3555 642 addReplyLongLong(c,hashTypeLength(o));
e2641e09 643}
644
ebd85e9a
PN
645static void addHashIteratorCursorToReply(redisClient *c, hashTypeIterator *hi, int what) {
646 if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
647 unsigned char *vstr = NULL;
648 unsigned int vlen = UINT_MAX;
649 long long vll = LLONG_MAX;
650
651 hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
652 if (vstr) {
653 addReplyBulkCBuffer(c, vstr, vlen);
654 } else {
655 addReplyBulkLongLong(c, vll);
656 }
657
658 } else if (hi->encoding == REDIS_ENCODING_HT) {
659 robj *value;
660
661 hashTypeCurrentFromHashTable(hi, what, &value);
662 addReplyBulk(c, value);
663
664 } else {
665 redisPanic("Unknown hash encoding");
666 }
667}
668
e2641e09 669void genericHgetallCommand(redisClient *c, int flags) {
8c304be3 670 robj *o;
e2641e09 671 hashTypeIterator *hi;
ebd85e9a
PN
672 int multiplier = 0;
673 int length, count = 0;
e2641e09 674
675 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
676 || checkType(c,o,REDIS_HASH)) return;
677
ebd85e9a
PN
678 if (flags & REDIS_HASH_KEY) multiplier++;
679 if (flags & REDIS_HASH_VALUE) multiplier++;
680
681 length = hashTypeLength(o) * multiplier;
682 addReplyMultiBulkLen(c, length);
683
e2641e09 684 hi = hashTypeInitIterator(o);
685 while (hashTypeNext(hi) != REDIS_ERR) {
686 if (flags & REDIS_HASH_KEY) {
ebd85e9a 687 addHashIteratorCursorToReply(c, hi, REDIS_HASH_KEY);
e2641e09 688 count++;
689 }
690 if (flags & REDIS_HASH_VALUE) {
ebd85e9a 691 addHashIteratorCursorToReply(c, hi, REDIS_HASH_VALUE);
e2641e09 692 count++;
693 }
694 }
ebd85e9a 695
e2641e09 696 hashTypeReleaseIterator(hi);
ebd85e9a 697 redisAssert(count == length);
e2641e09 698}
699
700void hkeysCommand(redisClient *c) {
701 genericHgetallCommand(c,REDIS_HASH_KEY);
702}
703
704void hvalsCommand(redisClient *c) {
705 genericHgetallCommand(c,REDIS_HASH_VALUE);
706}
707
708void hgetallCommand(redisClient *c) {
709 genericHgetallCommand(c,REDIS_HASH_KEY|REDIS_HASH_VALUE);
710}
711
712void hexistsCommand(redisClient *c) {
713 robj *o;
714 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
715 checkType(c,o,REDIS_HASH)) return;
716
717 addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero);
718}