1 /*
2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "server.h"
31 #include <math.h>
32
33 /*-----------------------------------------------------------------------------
34 * Hash type API
35 *----------------------------------------------------------------------------*/
36
37 /* Check the length of a number of objects to see if we need to convert a
38 * ziplist to a real hash. Note that we only check string encoded objects
39 * as their string length can be queried in constant time. */
hashTypeTryConversion(robj * o,robj ** argv,int start,int end)40 void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
41 int i;
42
43 if (o->encoding != OBJ_ENCODING_ZIPLIST) return;
44
45 for (i = start; i <= end; i++) {
46 if (sdsEncodedObject(argv[i]) &&
47 sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
48 {
49 hashTypeConvert(o, OBJ_ENCODING_HT);
50 break;
51 }
52 }
53 }
54
55 /* Encode given objects in-place when the hash uses a dict. */
hashTypeTryObjectEncoding(robj * subject,robj ** o1,robj ** o2)56 void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
57 if (subject->encoding == OBJ_ENCODING_HT) {
58 if (o1) *o1 = tryObjectEncoding(*o1);
59 if (o2) *o2 = tryObjectEncoding(*o2);
60 }
61 }
62
63 /* Get the value from a ziplist encoded hash, identified by field.
64 * Returns -1 when the field cannot be found. */
hashTypeGetFromZiplist(robj * o,robj * field,unsigned char ** vstr,unsigned int * vlen,long long * vll)65 int hashTypeGetFromZiplist(robj *o, robj *field,
66 unsigned char **vstr,
67 unsigned int *vlen,
68 long long *vll)
69 {
70 unsigned char *zl, *fptr = NULL, *vptr = NULL;
71 int ret;
72
73 serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
74
75 field = getDecodedObject(field);
76
77 zl = o->ptr;
78 fptr = ziplistIndex(zl, ZIPLIST_HEAD);
79 if (fptr != NULL) {
80 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
81 if (fptr != NULL) {
82 /* Grab pointer to the value (fptr points to the field) */
83 vptr = ziplistNext(zl, fptr);
84 serverAssert(vptr != NULL);
85 }
86 }
87
88 decrRefCount(field);
89
90 if (vptr != NULL) {
91 ret = ziplistGet(vptr, vstr, vlen, vll);
92 serverAssert(ret);
93 return 0;
94 }
95
96 return -1;
97 }
98
99 /* Get the value from a hash table encoded hash, identified by field.
100 * Returns -1 when the field cannot be found. */
hashTypeGetFromHashTable(robj * o,robj * field,robj ** value)101 int hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {
102 dictEntry *de;
103
104 serverAssert(o->encoding == OBJ_ENCODING_HT);
105
106 de = dictFind(o->ptr, field);
107 if (de == NULL) return -1;
108 *value = dictGetVal(de);
109 return 0;
110 }
111
112 /* Higher level function of hashTypeGet*() that always returns a Redis
113 * object (either new or with refcount incremented), so that the caller
114 * can retain a reference or call decrRefCount after the usage.
115 *
116 * The lower level function can prevent copy on write so it is
117 * the preferred way of doing read operations. */
hashTypeGetObject(robj * o,robj * field)118 robj *hashTypeGetObject(robj *o, robj *field) {
119 robj *value = NULL;
120
121 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
122 unsigned char *vstr = NULL;
123 unsigned int vlen = UINT_MAX;
124 long long vll = LLONG_MAX;
125
126 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) {
127 if (vstr) {
128 value = createStringObject((char*)vstr, vlen);
129 } else {
130 value = createStringObjectFromLongLong(vll);
131 }
132 }
133 } else if (o->encoding == OBJ_ENCODING_HT) {
134 robj *aux;
135
136 if (hashTypeGetFromHashTable(o, field, &aux) == 0) {
137 incrRefCount(aux);
138 value = aux;
139 }
140 } else {
141 serverPanic("Unknown hash encoding");
142 }
143 return value;
144 }
145
146 /* Higher level function using hashTypeGet*() to return the length of the
147 * object associated with the requested field, or 0 if the field does not
148 * exist. */
hashTypeGetValueLength(robj * o,robj * field)149 size_t hashTypeGetValueLength(robj *o, robj *field) {
150 size_t len = 0;
151 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
152 unsigned char *vstr = NULL;
153 unsigned int vlen = UINT_MAX;
154 long long vll = LLONG_MAX;
155
156 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)
157 len = vstr ? vlen : sdigits10(vll);
158 } else if (o->encoding == OBJ_ENCODING_HT) {
159 robj *aux;
160
161 if (hashTypeGetFromHashTable(o, field, &aux) == 0)
162 len = stringObjectLen(aux);
163 } else {
164 serverPanic("Unknown hash encoding");
165 }
166 return len;
167 }
168
169 /* Test if the specified field exists in the given hash. Returns 1 if the field
170 * exists, and 0 when it doesn't. */
hashTypeExists(robj * o,robj * field)171 int hashTypeExists(robj *o, robj *field) {
172 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
173 unsigned char *vstr = NULL;
174 unsigned int vlen = UINT_MAX;
175 long long vll = LLONG_MAX;
176
177 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;
178 } else if (o->encoding == OBJ_ENCODING_HT) {
179 robj *aux;
180
181 if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1;
182 } else {
183 serverPanic("Unknown hash encoding");
184 }
185 return 0;
186 }
187
188 /* Add an element, discard the old if the key already exists.
189 * Return 0 on insert and 1 on update.
190 * This function will take care of incrementing the reference count of the
191 * retained fields and value objects. */
hashTypeSet(robj * o,robj * field,robj * value)192 int hashTypeSet(robj *o, robj *field, robj *value) {
193 int update = 0;
194
195 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
196 unsigned char *zl, *fptr, *vptr;
197
198 field = getDecodedObject(field);
199 value = getDecodedObject(value);
200
201 zl = o->ptr;
202 fptr = ziplistIndex(zl, ZIPLIST_HEAD);
203 if (fptr != NULL) {
204 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
205 if (fptr != NULL) {
206 /* Grab pointer to the value (fptr points to the field) */
207 vptr = ziplistNext(zl, fptr);
208 serverAssert(vptr != NULL);
209 update = 1;
210
211 /* Delete value */
212 zl = ziplistDelete(zl, &vptr);
213
214 /* Insert new value */
215 zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
216 }
217 }
218
219 if (!update) {
220 /* Push new field/value pair onto the tail of the ziplist */
221 zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
222 zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
223 }
224 o->ptr = zl;
225 decrRefCount(field);
226 decrRefCount(value);
227
228 /* Check if the ziplist needs to be converted to a hash table */
229 if (hashTypeLength(o) > server.hash_max_ziplist_entries)
230 hashTypeConvert(o, OBJ_ENCODING_HT);
231 } else if (o->encoding == OBJ_ENCODING_HT) {
232 if (dictReplace(o->ptr, field, value)) { /* Insert */
233 incrRefCount(field);
234 } else { /* Update */
235 update = 1;
236 }
237 incrRefCount(value);
238 } else {
239 serverPanic("Unknown hash encoding");
240 }
241 return update;
242 }
243
244 /* Delete an element from a hash.
245 * Return 1 on deleted and 0 on not found. */
hashTypeDelete(robj * o,robj * field)246 int hashTypeDelete(robj *o, robj *field) {
247 int deleted = 0;
248
249 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
250 unsigned char *zl, *fptr;
251
252 field = getDecodedObject(field);
253
254 zl = o->ptr;
255 fptr = ziplistIndex(zl, ZIPLIST_HEAD);
256 if (fptr != NULL) {
257 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
258 if (fptr != NULL) {
259 zl = ziplistDelete(zl,&fptr);
260 zl = ziplistDelete(zl,&fptr);
261 o->ptr = zl;
262 deleted = 1;
263 }
264 }
265
266 decrRefCount(field);
267
268 } else if (o->encoding == OBJ_ENCODING_HT) {
269 if (dictDelete((dict*)o->ptr, field) == C_OK) {
270 deleted = 1;
271
272 /* Always check if the dictionary needs a resize after a delete. */
273 if (htNeedsResize(o->ptr)) dictResize(o->ptr);
274 }
275
276 } else {
277 serverPanic("Unknown hash encoding");
278 }
279
280 return deleted;
281 }
282
283 /* Return the number of elements in a hash. */
hashTypeLength(robj * o)284 unsigned long hashTypeLength(robj *o) {
285 unsigned long length = ULONG_MAX;
286
287 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
288 length = ziplistLen(o->ptr) / 2;
289 } else if (o->encoding == OBJ_ENCODING_HT) {
290 length = dictSize((dict*)o->ptr);
291 } else {
292 serverPanic("Unknown hash encoding");
293 }
294
295 return length;
296 }
297
hashTypeInitIterator(robj * subject)298 hashTypeIterator *hashTypeInitIterator(robj *subject) {
299 hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
300 hi->subject = subject;
301 hi->encoding = subject->encoding;
302
303 if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
304 hi->fptr = NULL;
305 hi->vptr = NULL;
306 } else if (hi->encoding == OBJ_ENCODING_HT) {
307 hi->di = dictGetIterator(subject->ptr);
308 } else {
309 serverPanic("Unknown hash encoding");
310 }
311
312 return hi;
313 }
314
hashTypeReleaseIterator(hashTypeIterator * hi)315 void hashTypeReleaseIterator(hashTypeIterator *hi) {
316 if (hi->encoding == OBJ_ENCODING_HT) {
317 dictReleaseIterator(hi->di);
318 }
319
320 zfree(hi);
321 }
322
323 /* Move to the next entry in the hash. Return C_OK when the next entry
324 * could be found and C_ERR when the iterator reaches the end. */
hashTypeNext(hashTypeIterator * hi)325 int hashTypeNext(hashTypeIterator *hi) {
326 if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
327 unsigned char *zl;
328 unsigned char *fptr, *vptr;
329
330 zl = hi->subject->ptr;
331 fptr = hi->fptr;
332 vptr = hi->vptr;
333
334 if (fptr == NULL) {
335 /* Initialize cursor */
336 serverAssert(vptr == NULL);
337 fptr = ziplistIndex(zl, 0);
338 } else {
339 /* Advance cursor */
340 serverAssert(vptr != NULL);
341 fptr = ziplistNext(zl, vptr);
342 }
343 if (fptr == NULL) return C_ERR;
344
345 /* Grab pointer to the value (fptr points to the field) */
346 vptr = ziplistNext(zl, fptr);
347 serverAssert(vptr != NULL);
348
349 /* fptr, vptr now point to the first or next pair */
350 hi->fptr = fptr;
351 hi->vptr = vptr;
352 } else if (hi->encoding == OBJ_ENCODING_HT) {
353 if ((hi->de = dictNext(hi->di)) == NULL) return C_ERR;
354 } else {
355 serverPanic("Unknown hash encoding");
356 }
357 return C_OK;
358 }
359
360 /* Get the field or value at iterator cursor, for an iterator on a hash value
361 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
hashTypeCurrentFromZiplist(hashTypeIterator * hi,int what,unsigned char ** vstr,unsigned int * vlen,long long * vll)362 void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
363 unsigned char **vstr,
364 unsigned int *vlen,
365 long long *vll)
366 {
367 int ret;
368
369 serverAssert(hi->encoding == OBJ_ENCODING_ZIPLIST);
370
371 if (what & OBJ_HASH_KEY) {
372 ret = ziplistGet(hi->fptr, vstr, vlen, vll);
373 serverAssert(ret);
374 } else {
375 ret = ziplistGet(hi->vptr, vstr, vlen, vll);
376 serverAssert(ret);
377 }
378 }
379
380 /* Get the field or value at iterator cursor, for an iterator on a hash value
381 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
hashTypeCurrentFromHashTable(hashTypeIterator * hi,int what,robj ** dst)382 void hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) {
383 serverAssert(hi->encoding == OBJ_ENCODING_HT);
384
385 if (what & OBJ_HASH_KEY) {
386 *dst = dictGetKey(hi->de);
387 } else {
388 *dst = dictGetVal(hi->de);
389 }
390 }
391
392 /* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
393 * that returns an object with incremented refcount (or a new object). It is up
394 * to the caller to decrRefCount() the object if no reference is retained. */
hashTypeCurrentObject(hashTypeIterator * hi,int what)395 robj *hashTypeCurrentObject(hashTypeIterator *hi, int what) {
396 robj *dst;
397
398 if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
399 unsigned char *vstr = NULL;
400 unsigned int vlen = UINT_MAX;
401 long long vll = LLONG_MAX;
402
403 hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
404 if (vstr) {
405 dst = createStringObject((char*)vstr, vlen);
406 } else {
407 dst = createStringObjectFromLongLong(vll);
408 }
409 } else if (hi->encoding == OBJ_ENCODING_HT) {
410 hashTypeCurrentFromHashTable(hi, what, &dst);
411 incrRefCount(dst);
412 } else {
413 serverPanic("Unknown hash encoding");
414 }
415 return dst;
416 }
417
hashTypeLookupWriteOrCreate(client * c,robj * key)418 robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {
419 robj *o = lookupKeyWrite(c->db,key);
420 if (o == NULL) {
421 o = createHashObject();
422 dbAdd(c->db,key,o);
423 } else {
424 if (o->type != OBJ_HASH) {
425 addReply(c,shared.wrongtypeerr);
426 return NULL;
427 }
428 }
429 return o;
430 }
431
hashTypeConvertZiplist(robj * o,int enc)432 void hashTypeConvertZiplist(robj *o, int enc) {
433 serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
434
435 if (enc == OBJ_ENCODING_ZIPLIST) {
436 /* Nothing to do... */
437
438 } else if (enc == OBJ_ENCODING_HT) {
439 hashTypeIterator *hi;
440 dict *dict;
441 int ret;
442
443 hi = hashTypeInitIterator(o);
444 dict = dictCreate(&hashDictType, NULL);
445
446 while (hashTypeNext(hi) != C_ERR) {
447 robj *field, *value;
448
449 field = hashTypeCurrentObject(hi, OBJ_HASH_KEY);
450 field = tryObjectEncoding(field);
451 value = hashTypeCurrentObject(hi, OBJ_HASH_VALUE);
452 value = tryObjectEncoding(value);
453 ret = dictAdd(dict, field, value);
454 if (ret != DICT_OK) {
455 serverLogHexDump(LL_WARNING,"ziplist with dup elements dump",
456 o->ptr,ziplistBlobLen(o->ptr));
457 serverAssert(ret == DICT_OK);
458 }
459 }
460
461 hashTypeReleaseIterator(hi);
462 zfree(o->ptr);
463
464 o->encoding = OBJ_ENCODING_HT;
465 o->ptr = dict;
466
467 } else {
468 serverPanic("Unknown hash encoding");
469 }
470 }
471
hashTypeConvert(robj * o,int enc)472 void hashTypeConvert(robj *o, int enc) {
473 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
474 hashTypeConvertZiplist(o, enc);
475 } else if (o->encoding == OBJ_ENCODING_HT) {
476 serverPanic("Not implemented");
477 } else {
478 serverPanic("Unknown hash encoding");
479 }
480 }
481
482 /*-----------------------------------------------------------------------------
483 * Hash type commands
484 *----------------------------------------------------------------------------*/
485
hsetCommand(client * c)486 void hsetCommand(client *c) {
487 int update;
488 robj *o;
489
490 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
491 hashTypeTryConversion(o,c->argv,2,3);
492 hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
493 update = hashTypeSet(o,c->argv[2],c->argv[3]);
494 addReply(c, update ? shared.czero : shared.cone);
495 signalModifiedKey(c->db,c->argv[1]);
496 notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
497 server.dirty++;
498 }
499
hsetnxCommand(client * c)500 void hsetnxCommand(client *c) {
501 robj *o;
502 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
503 hashTypeTryConversion(o,c->argv,2,3);
504
505 if (hashTypeExists(o, c->argv[2])) {
506 addReply(c, shared.czero);
507 } else {
508 hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
509 hashTypeSet(o,c->argv[2],c->argv[3]);
510 addReply(c, shared.cone);
511 signalModifiedKey(c->db,c->argv[1]);
512 notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
513 server.dirty++;
514 }
515 }
516
hmsetCommand(client * c)517 void hmsetCommand(client *c) {
518 int i;
519 robj *o;
520
521 if ((c->argc % 2) == 1) {
522 addReplyError(c,"wrong number of arguments for HMSET");
523 return;
524 }
525
526 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
527 hashTypeTryConversion(o,c->argv,2,c->argc-1);
528 for (i = 2; i < c->argc; i += 2) {
529 hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
530 hashTypeSet(o,c->argv[i],c->argv[i+1]);
531 }
532 addReply(c, shared.ok);
533 signalModifiedKey(c->db,c->argv[1]);
534 notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
535 server.dirty++;
536 }
537
hincrbyCommand(client * c)538 void hincrbyCommand(client *c) {
539 long long value, incr, oldvalue;
540 robj *o, *current, *new;
541
542 if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
543 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
544 if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
545 if (getLongLongFromObjectOrReply(c,current,&value,
546 "hash value is not an integer") != C_OK) {
547 decrRefCount(current);
548 return;
549 }
550 decrRefCount(current);
551 } else {
552 value = 0;
553 }
554
555 oldvalue = value;
556 if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
557 (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
558 addReplyError(c,"increment or decrement would overflow");
559 return;
560 }
561 value += incr;
562 new = createStringObjectFromLongLong(value);
563 hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
564 hashTypeSet(o,c->argv[2],new);
565 decrRefCount(new);
566 addReplyLongLong(c,value);
567 signalModifiedKey(c->db,c->argv[1]);
568 notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
569 server.dirty++;
570 }
571
hincrbyfloatCommand(client * c)572 void hincrbyfloatCommand(client *c) {
573 double long value, incr;
574 robj *o, *current, *new, *aux;
575
576 if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
577 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
578 if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
579 if (getLongDoubleFromObjectOrReply(c,current,&value,
580 "hash value is not a valid float") != C_OK) {
581 decrRefCount(current);
582 return;
583 }
584 decrRefCount(current);
585 } else {
586 value = 0;
587 }
588
589 value += incr;
590 new = createStringObjectFromLongDouble(value,1);
591 hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
592 hashTypeSet(o,c->argv[2],new);
593 addReplyBulk(c,new);
594 signalModifiedKey(c->db,c->argv[1]);
595 notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
596 server.dirty++;
597
598 /* Always replicate HINCRBYFLOAT as an HSET command with the final value
599 * in order to make sure that differences in float pricision or formatting
600 * will not create differences in replicas or after an AOF restart. */
601 aux = createStringObject("HSET",4);
602 rewriteClientCommandArgument(c,0,aux);
603 decrRefCount(aux);
604 rewriteClientCommandArgument(c,3,new);
605 decrRefCount(new);
606 }
607
addHashFieldToReply(client * c,robj * o,robj * field)608 static void addHashFieldToReply(client *c, robj *o, robj *field) {
609 int ret;
610
611 if (o == NULL) {
612 addReply(c, shared.nullbulk);
613 return;
614 }
615
616 if (o->encoding == OBJ_ENCODING_ZIPLIST) {
617 unsigned char *vstr = NULL;
618 unsigned int vlen = UINT_MAX;
619 long long vll = LLONG_MAX;
620
621 ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
622 if (ret < 0) {
623 addReply(c, shared.nullbulk);
624 } else {
625 if (vstr) {
626 addReplyBulkCBuffer(c, vstr, vlen);
627 } else {
628 addReplyBulkLongLong(c, vll);
629 }
630 }
631
632 } else if (o->encoding == OBJ_ENCODING_HT) {
633 robj *value;
634
635 ret = hashTypeGetFromHashTable(o, field, &value);
636 if (ret < 0) {
637 addReply(c, shared.nullbulk);
638 } else {
639 addReplyBulk(c, value);
640 }
641
642 } else {
643 serverPanic("Unknown hash encoding");
644 }
645 }
646
hgetCommand(client * c)647 void hgetCommand(client *c) {
648 robj *o;
649
650 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
651 checkType(c,o,OBJ_HASH)) return;
652
653 addHashFieldToReply(c, o, c->argv[2]);
654 }
655
hmgetCommand(client * c)656 void hmgetCommand(client *c) {
657 robj *o;
658 int i;
659
660 /* Don't abort when the key cannot be found. Non-existing keys are empty
661 * hashes, where HMGET should respond with a series of null bulks. */
662 o = lookupKeyRead(c->db, c->argv[1]);
663 if (o != NULL && o->type != OBJ_HASH) {
664 addReply(c, shared.wrongtypeerr);
665 return;
666 }
667
668 addReplyMultiBulkLen(c, c->argc-2);
669 for (i = 2; i < c->argc; i++) {
670 addHashFieldToReply(c, o, c->argv[i]);
671 }
672 }
673
hdelCommand(client * c)674 void hdelCommand(client *c) {
675 robj *o;
676 int j, deleted = 0, keyremoved = 0;
677
678 if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
679 checkType(c,o,OBJ_HASH)) return;
680
681 for (j = 2; j < c->argc; j++) {
682 if (hashTypeDelete(o,c->argv[j])) {
683 deleted++;
684 if (hashTypeLength(o) == 0) {
685 dbDelete(c->db,c->argv[1]);
686 keyremoved = 1;
687 break;
688 }
689 }
690 }
691 if (deleted) {
692 signalModifiedKey(c->db,c->argv[1]);
693 notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
694 if (keyremoved)
695 notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
696 c->db->id);
697 server.dirty += deleted;
698 }
699 addReplyLongLong(c,deleted);
700 }
701
hlenCommand(client * c)702 void hlenCommand(client *c) {
703 robj *o;
704
705 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
706 checkType(c,o,OBJ_HASH)) return;
707
708 addReplyLongLong(c,hashTypeLength(o));
709 }
710
hstrlenCommand(client * c)711 void hstrlenCommand(client *c) {
712 robj *o;
713
714 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
715 checkType(c,o,OBJ_HASH)) return;
716 addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]));
717 }
718
addHashIteratorCursorToReply(client * c,hashTypeIterator * hi,int what)719 static void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {
720 if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
721 unsigned char *vstr = NULL;
722 unsigned int vlen = UINT_MAX;
723 long long vll = LLONG_MAX;
724
725 hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
726 if (vstr) {
727 addReplyBulkCBuffer(c, vstr, vlen);
728 } else {
729 addReplyBulkLongLong(c, vll);
730 }
731
732 } else if (hi->encoding == OBJ_ENCODING_HT) {
733 robj *value;
734
735 hashTypeCurrentFromHashTable(hi, what, &value);
736 addReplyBulk(c, value);
737
738 } else {
739 serverPanic("Unknown hash encoding");
740 }
741 }
742
genericHgetallCommand(client * c,int flags)743 void genericHgetallCommand(client *c, int flags) {
744 robj *o;
745 hashTypeIterator *hi;
746 int multiplier = 0;
747 int length, count = 0;
748
749 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
750 || checkType(c,o,OBJ_HASH)) return;
751
752 if (flags & OBJ_HASH_KEY) multiplier++;
753 if (flags & OBJ_HASH_VALUE) multiplier++;
754
755 length = hashTypeLength(o) * multiplier;
756 addReplyMultiBulkLen(c, length);
757
758 hi = hashTypeInitIterator(o);
759 while (hashTypeNext(hi) != C_ERR) {
760 if (flags & OBJ_HASH_KEY) {
761 addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
762 count++;
763 }
764 if (flags & OBJ_HASH_VALUE) {
765 addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
766 count++;
767 }
768 }
769
770 hashTypeReleaseIterator(hi);
771 serverAssert(count == length);
772 }
773
hkeysCommand(client * c)774 void hkeysCommand(client *c) {
775 genericHgetallCommand(c,OBJ_HASH_KEY);
776 }
777
hvalsCommand(client * c)778 void hvalsCommand(client *c) {
779 genericHgetallCommand(c,OBJ_HASH_VALUE);
780 }
781
hgetallCommand(client * c)782 void hgetallCommand(client *c) {
783 genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);
784 }
785
hexistsCommand(client * c)786 void hexistsCommand(client *c) {
787 robj *o;
788 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
789 checkType(c,o,OBJ_HASH)) return;
790
791 addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero);
792 }
793
hscanCommand(client * c)794 void hscanCommand(client *c) {
795 robj *o;
796 unsigned long cursor;
797
798 if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
799 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
800 checkType(c,o,OBJ_HASH)) return;
801 scanGenericCommand(c,o,cursor);
802 }
803