xref: /f-stack/app/redis-5.0.5/src/t_hash.c (revision 572c4311)
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 /* Get the value from a ziplist encoded hash, identified by field.
56  * Returns -1 when the field cannot be found. */
hashTypeGetFromZiplist(robj * o,sds field,unsigned char ** vstr,unsigned int * vlen,long long * vll)57 int hashTypeGetFromZiplist(robj *o, sds field,
58                            unsigned char **vstr,
59                            unsigned int *vlen,
60                            long long *vll)
61 {
62     unsigned char *zl, *fptr = NULL, *vptr = NULL;
63     int ret;
64 
65     serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
66 
67     zl = o->ptr;
68     fptr = ziplistIndex(zl, ZIPLIST_HEAD);
69     if (fptr != NULL) {
70         fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
71         if (fptr != NULL) {
72             /* Grab pointer to the value (fptr points to the field) */
73             vptr = ziplistNext(zl, fptr);
74             serverAssert(vptr != NULL);
75         }
76     }
77 
78     if (vptr != NULL) {
79         ret = ziplistGet(vptr, vstr, vlen, vll);
80         serverAssert(ret);
81         return 0;
82     }
83 
84     return -1;
85 }
86 
87 /* Get the value from a hash table encoded hash, identified by field.
88  * Returns NULL when the field cannot be found, otherwise the SDS value
89  * is returned. */
hashTypeGetFromHashTable(robj * o,sds field)90 sds hashTypeGetFromHashTable(robj *o, sds field) {
91     dictEntry *de;
92 
93     serverAssert(o->encoding == OBJ_ENCODING_HT);
94 
95     de = dictFind(o->ptr, field);
96     if (de == NULL) return NULL;
97     return dictGetVal(de);
98 }
99 
100 /* Higher level function of hashTypeGet*() that returns the hash value
101  * associated with the specified field. If the field is found C_OK
102  * is returned, otherwise C_ERR. The returned object is returned by
103  * reference in either *vstr and *vlen if it's returned in string form,
104  * or stored in *vll if it's returned as a number.
105  *
106  * If *vll is populated *vstr is set to NULL, so the caller
107  * can always check the function return by checking the return value
108  * for C_OK and checking if vll (or vstr) is NULL. */
hashTypeGetValue(robj * o,sds field,unsigned char ** vstr,unsigned int * vlen,long long * vll)109 int hashTypeGetValue(robj *o, sds field, unsigned char **vstr, unsigned int *vlen, long long *vll) {
110     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
111         *vstr = NULL;
112         if (hashTypeGetFromZiplist(o, field, vstr, vlen, vll) == 0)
113             return C_OK;
114     } else if (o->encoding == OBJ_ENCODING_HT) {
115         sds value;
116         if ((value = hashTypeGetFromHashTable(o, field)) != NULL) {
117             *vstr = (unsigned char*) value;
118             *vlen = sdslen(value);
119             return C_OK;
120         }
121     } else {
122         serverPanic("Unknown hash encoding");
123     }
124     return C_ERR;
125 }
126 
127 /* Like hashTypeGetValue() but returns a Redis object, which is useful for
128  * interaction with the hash type outside t_hash.c.
129  * The function returns NULL if the field is not found in the hash. Otherwise
130  * a newly allocated string object with the value is returned. */
hashTypeGetValueObject(robj * o,sds field)131 robj *hashTypeGetValueObject(robj *o, sds field) {
132     unsigned char *vstr;
133     unsigned int vlen;
134     long long vll;
135 
136     if (hashTypeGetValue(o,field,&vstr,&vlen,&vll) == C_ERR) return NULL;
137     if (vstr) return createStringObject((char*)vstr,vlen);
138     else return createStringObjectFromLongLong(vll);
139 }
140 
141 /* Higher level function using hashTypeGet*() to return the length of the
142  * object associated with the requested field, or 0 if the field does not
143  * exist. */
hashTypeGetValueLength(robj * o,sds field)144 size_t hashTypeGetValueLength(robj *o, sds field) {
145     size_t len = 0;
146     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
147         unsigned char *vstr = NULL;
148         unsigned int vlen = UINT_MAX;
149         long long vll = LLONG_MAX;
150 
151         if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0)
152             len = vstr ? vlen : sdigits10(vll);
153     } else if (o->encoding == OBJ_ENCODING_HT) {
154         sds aux;
155 
156         if ((aux = hashTypeGetFromHashTable(o, field)) != NULL)
157             len = sdslen(aux);
158     } else {
159         serverPanic("Unknown hash encoding");
160     }
161     return len;
162 }
163 
164 /* Test if the specified field exists in the given hash. Returns 1 if the field
165  * exists, and 0 when it doesn't. */
hashTypeExists(robj * o,sds field)166 int hashTypeExists(robj *o, sds field) {
167     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
168         unsigned char *vstr = NULL;
169         unsigned int vlen = UINT_MAX;
170         long long vll = LLONG_MAX;
171 
172         if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;
173     } else if (o->encoding == OBJ_ENCODING_HT) {
174         if (hashTypeGetFromHashTable(o, field) != NULL) return 1;
175     } else {
176         serverPanic("Unknown hash encoding");
177     }
178     return 0;
179 }
180 
181 /* Add a new field, overwrite the old with the new value if it already exists.
182  * Return 0 on insert and 1 on update.
183  *
184  * By default, the key and value SDS strings are copied if needed, so the
185  * caller retains ownership of the strings passed. However this behavior
186  * can be effected by passing appropriate flags (possibly bitwise OR-ed):
187  *
188  * HASH_SET_TAKE_FIELD -- The SDS field ownership passes to the function.
189  * HASH_SET_TAKE_VALUE -- The SDS value ownership passes to the function.
190  *
191  * When the flags are used the caller does not need to release the passed
192  * SDS string(s). It's up to the function to use the string to create a new
193  * entry or to free the SDS string before returning to the caller.
194  *
195  * HASH_SET_COPY corresponds to no flags passed, and means the default
196  * semantics of copying the values if needed.
197  *
198  */
199 #define HASH_SET_TAKE_FIELD (1<<0)
200 #define HASH_SET_TAKE_VALUE (1<<1)
201 #define HASH_SET_COPY 0
hashTypeSet(robj * o,sds field,sds value,int flags)202 int hashTypeSet(robj *o, sds field, sds value, int flags) {
203     int update = 0;
204 
205     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
206         unsigned char *zl, *fptr, *vptr;
207 
208         zl = o->ptr;
209         fptr = ziplistIndex(zl, ZIPLIST_HEAD);
210         if (fptr != NULL) {
211             fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
212             if (fptr != NULL) {
213                 /* Grab pointer to the value (fptr points to the field) */
214                 vptr = ziplistNext(zl, fptr);
215                 serverAssert(vptr != NULL);
216                 update = 1;
217 
218                 /* Delete value */
219                 zl = ziplistDelete(zl, &vptr);
220 
221                 /* Insert new value */
222                 zl = ziplistInsert(zl, vptr, (unsigned char*)value,
223                         sdslen(value));
224             }
225         }
226 
227         if (!update) {
228             /* Push new field/value pair onto the tail of the ziplist */
229             zl = ziplistPush(zl, (unsigned char*)field, sdslen(field),
230                     ZIPLIST_TAIL);
231             zl = ziplistPush(zl, (unsigned char*)value, sdslen(value),
232                     ZIPLIST_TAIL);
233         }
234         o->ptr = zl;
235 
236         /* Check if the ziplist needs to be converted to a hash table */
237         if (hashTypeLength(o) > server.hash_max_ziplist_entries)
238             hashTypeConvert(o, OBJ_ENCODING_HT);
239     } else if (o->encoding == OBJ_ENCODING_HT) {
240         dictEntry *de = dictFind(o->ptr,field);
241         if (de) {
242             sdsfree(dictGetVal(de));
243             if (flags & HASH_SET_TAKE_VALUE) {
244                 dictGetVal(de) = value;
245                 value = NULL;
246             } else {
247                 dictGetVal(de) = sdsdup(value);
248             }
249             update = 1;
250         } else {
251             sds f,v;
252             if (flags & HASH_SET_TAKE_FIELD) {
253                 f = field;
254                 field = NULL;
255             } else {
256                 f = sdsdup(field);
257             }
258             if (flags & HASH_SET_TAKE_VALUE) {
259                 v = value;
260                 value = NULL;
261             } else {
262                 v = sdsdup(value);
263             }
264             dictAdd(o->ptr,f,v);
265         }
266     } else {
267         serverPanic("Unknown hash encoding");
268     }
269 
270     /* Free SDS strings we did not referenced elsewhere if the flags
271      * want this function to be responsible. */
272     if (flags & HASH_SET_TAKE_FIELD && field) sdsfree(field);
273     if (flags & HASH_SET_TAKE_VALUE && value) sdsfree(value);
274     return update;
275 }
276 
277 /* Delete an element from a hash.
278  * Return 1 on deleted and 0 on not found. */
hashTypeDelete(robj * o,sds field)279 int hashTypeDelete(robj *o, sds field) {
280     int deleted = 0;
281 
282     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
283         unsigned char *zl, *fptr;
284 
285         zl = o->ptr;
286         fptr = ziplistIndex(zl, ZIPLIST_HEAD);
287         if (fptr != NULL) {
288             fptr = ziplistFind(fptr, (unsigned char*)field, sdslen(field), 1);
289             if (fptr != NULL) {
290                 zl = ziplistDelete(zl,&fptr); /* Delete the key. */
291                 zl = ziplistDelete(zl,&fptr); /* Delete the value. */
292                 o->ptr = zl;
293                 deleted = 1;
294             }
295         }
296     } else if (o->encoding == OBJ_ENCODING_HT) {
297         if (dictDelete((dict*)o->ptr, field) == C_OK) {
298             deleted = 1;
299 
300             /* Always check if the dictionary needs a resize after a delete. */
301             if (htNeedsResize(o->ptr)) dictResize(o->ptr);
302         }
303 
304     } else {
305         serverPanic("Unknown hash encoding");
306     }
307     return deleted;
308 }
309 
310 /* Return the number of elements in a hash. */
hashTypeLength(const robj * o)311 unsigned long hashTypeLength(const robj *o) {
312     unsigned long length = ULONG_MAX;
313 
314     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
315         length = ziplistLen(o->ptr) / 2;
316     } else if (o->encoding == OBJ_ENCODING_HT) {
317         length = dictSize((const dict*)o->ptr);
318     } else {
319         serverPanic("Unknown hash encoding");
320     }
321     return length;
322 }
323 
hashTypeInitIterator(robj * subject)324 hashTypeIterator *hashTypeInitIterator(robj *subject) {
325     hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
326     hi->subject = subject;
327     hi->encoding = subject->encoding;
328 
329     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
330         hi->fptr = NULL;
331         hi->vptr = NULL;
332     } else if (hi->encoding == OBJ_ENCODING_HT) {
333         hi->di = dictGetIterator(subject->ptr);
334     } else {
335         serverPanic("Unknown hash encoding");
336     }
337     return hi;
338 }
339 
hashTypeReleaseIterator(hashTypeIterator * hi)340 void hashTypeReleaseIterator(hashTypeIterator *hi) {
341     if (hi->encoding == OBJ_ENCODING_HT)
342         dictReleaseIterator(hi->di);
343     zfree(hi);
344 }
345 
346 /* Move to the next entry in the hash. Return C_OK when the next entry
347  * could be found and C_ERR when the iterator reaches the end. */
hashTypeNext(hashTypeIterator * hi)348 int hashTypeNext(hashTypeIterator *hi) {
349     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
350         unsigned char *zl;
351         unsigned char *fptr, *vptr;
352 
353         zl = hi->subject->ptr;
354         fptr = hi->fptr;
355         vptr = hi->vptr;
356 
357         if (fptr == NULL) {
358             /* Initialize cursor */
359             serverAssert(vptr == NULL);
360             fptr = ziplistIndex(zl, 0);
361         } else {
362             /* Advance cursor */
363             serverAssert(vptr != NULL);
364             fptr = ziplistNext(zl, vptr);
365         }
366         if (fptr == NULL) return C_ERR;
367 
368         /* Grab pointer to the value (fptr points to the field) */
369         vptr = ziplistNext(zl, fptr);
370         serverAssert(vptr != NULL);
371 
372         /* fptr, vptr now point to the first or next pair */
373         hi->fptr = fptr;
374         hi->vptr = vptr;
375     } else if (hi->encoding == OBJ_ENCODING_HT) {
376         if ((hi->de = dictNext(hi->di)) == NULL) return C_ERR;
377     } else {
378         serverPanic("Unknown hash encoding");
379     }
380     return C_OK;
381 }
382 
383 /* Get the field or value at iterator cursor, for an iterator on a hash value
384  * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
hashTypeCurrentFromZiplist(hashTypeIterator * hi,int what,unsigned char ** vstr,unsigned int * vlen,long long * vll)385 void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
386                                 unsigned char **vstr,
387                                 unsigned int *vlen,
388                                 long long *vll)
389 {
390     int ret;
391 
392     serverAssert(hi->encoding == OBJ_ENCODING_ZIPLIST);
393 
394     if (what & OBJ_HASH_KEY) {
395         ret = ziplistGet(hi->fptr, vstr, vlen, vll);
396         serverAssert(ret);
397     } else {
398         ret = ziplistGet(hi->vptr, vstr, vlen, vll);
399         serverAssert(ret);
400     }
401 }
402 
403 /* Get the field or value at iterator cursor, for an iterator on a hash value
404  * encoded as a hash table. Prototype is similar to
405  * `hashTypeGetFromHashTable`. */
hashTypeCurrentFromHashTable(hashTypeIterator * hi,int what)406 sds hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what) {
407     serverAssert(hi->encoding == OBJ_ENCODING_HT);
408 
409     if (what & OBJ_HASH_KEY) {
410         return dictGetKey(hi->de);
411     } else {
412         return dictGetVal(hi->de);
413     }
414 }
415 
416 /* Higher level function of hashTypeCurrent*() that returns the hash value
417  * at current iterator position.
418  *
419  * The returned element is returned by reference in either *vstr and *vlen if
420  * it's returned in string form, or stored in *vll if it's returned as
421  * a number.
422  *
423  * If *vll is populated *vstr is set to NULL, so the caller
424  * can always check the function return by checking the return value
425  * type checking if vstr == NULL. */
hashTypeCurrentObject(hashTypeIterator * hi,int what,unsigned char ** vstr,unsigned int * vlen,long long * vll)426 void hashTypeCurrentObject(hashTypeIterator *hi, int what, unsigned char **vstr, unsigned int *vlen, long long *vll) {
427     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
428         *vstr = NULL;
429         hashTypeCurrentFromZiplist(hi, what, vstr, vlen, vll);
430     } else if (hi->encoding == OBJ_ENCODING_HT) {
431         sds ele = hashTypeCurrentFromHashTable(hi, what);
432         *vstr = (unsigned char*) ele;
433         *vlen = sdslen(ele);
434     } else {
435         serverPanic("Unknown hash encoding");
436     }
437 }
438 
439 /* Return the key or value at the current iterator position as a new
440  * SDS string. */
hashTypeCurrentObjectNewSds(hashTypeIterator * hi,int what)441 sds hashTypeCurrentObjectNewSds(hashTypeIterator *hi, int what) {
442     unsigned char *vstr;
443     unsigned int vlen;
444     long long vll;
445 
446     hashTypeCurrentObject(hi,what,&vstr,&vlen,&vll);
447     if (vstr) return sdsnewlen(vstr,vlen);
448     return sdsfromlonglong(vll);
449 }
450 
hashTypeLookupWriteOrCreate(client * c,robj * key)451 robj *hashTypeLookupWriteOrCreate(client *c, robj *key) {
452     robj *o = lookupKeyWrite(c->db,key);
453     if (o == NULL) {
454         o = createHashObject();
455         dbAdd(c->db,key,o);
456     } else {
457         if (o->type != OBJ_HASH) {
458             addReply(c,shared.wrongtypeerr);
459             return NULL;
460         }
461     }
462     return o;
463 }
464 
hashTypeConvertZiplist(robj * o,int enc)465 void hashTypeConvertZiplist(robj *o, int enc) {
466     serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
467 
468     if (enc == OBJ_ENCODING_ZIPLIST) {
469         /* Nothing to do... */
470 
471     } else if (enc == OBJ_ENCODING_HT) {
472         hashTypeIterator *hi;
473         dict *dict;
474         int ret;
475 
476         hi = hashTypeInitIterator(o);
477         dict = dictCreate(&hashDictType, NULL);
478 
479         while (hashTypeNext(hi) != C_ERR) {
480             sds key, value;
481 
482             key = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_KEY);
483             value = hashTypeCurrentObjectNewSds(hi,OBJ_HASH_VALUE);
484             ret = dictAdd(dict, key, value);
485             if (ret != DICT_OK) {
486                 serverLogHexDump(LL_WARNING,"ziplist with dup elements dump",
487                     o->ptr,ziplistBlobLen(o->ptr));
488                 serverPanic("Ziplist corruption detected");
489             }
490         }
491         hashTypeReleaseIterator(hi);
492         zfree(o->ptr);
493         o->encoding = OBJ_ENCODING_HT;
494         o->ptr = dict;
495     } else {
496         serverPanic("Unknown hash encoding");
497     }
498 }
499 
hashTypeConvert(robj * o,int enc)500 void hashTypeConvert(robj *o, int enc) {
501     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
502         hashTypeConvertZiplist(o, enc);
503     } else if (o->encoding == OBJ_ENCODING_HT) {
504         serverPanic("Not implemented");
505     } else {
506         serverPanic("Unknown hash encoding");
507     }
508 }
509 
510 /*-----------------------------------------------------------------------------
511  * Hash type commands
512  *----------------------------------------------------------------------------*/
513 
hsetnxCommand(client * c)514 void hsetnxCommand(client *c) {
515     robj *o;
516     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
517     hashTypeTryConversion(o,c->argv,2,3);
518 
519     if (hashTypeExists(o, c->argv[2]->ptr)) {
520         addReply(c, shared.czero);
521     } else {
522         hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);
523         addReply(c, shared.cone);
524         signalModifiedKey(c->db,c->argv[1]);
525         notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
526         server.dirty++;
527     }
528 }
529 
hsetCommand(client * c)530 void hsetCommand(client *c) {
531     int i, created = 0;
532     robj *o;
533 
534     if ((c->argc % 2) == 1) {
535         addReplyError(c,"wrong number of arguments for HMSET");
536         return;
537     }
538 
539     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
540     hashTypeTryConversion(o,c->argv,2,c->argc-1);
541 
542     for (i = 2; i < c->argc; i += 2)
543         created += !hashTypeSet(o,c->argv[i]->ptr,c->argv[i+1]->ptr,HASH_SET_COPY);
544 
545     /* HMSET (deprecated) and HSET return value is different. */
546     char *cmdname = c->argv[0]->ptr;
547     if (cmdname[1] == 's' || cmdname[1] == 'S') {
548         /* HSET */
549         addReplyLongLong(c, created);
550     } else {
551         /* HMSET */
552         addReply(c, shared.ok);
553     }
554     signalModifiedKey(c->db,c->argv[1]);
555     notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
556     server.dirty++;
557 }
558 
hincrbyCommand(client * c)559 void hincrbyCommand(client *c) {
560     long long value, incr, oldvalue;
561     robj *o;
562     sds new;
563     unsigned char *vstr;
564     unsigned int vlen;
565 
566     if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
567     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
568     if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&value) == C_OK) {
569         if (vstr) {
570             if (string2ll((char*)vstr,vlen,&value) == 0) {
571                 addReplyError(c,"hash value is not an integer");
572                 return;
573             }
574         } /* Else hashTypeGetValue() already stored it into &value */
575     } else {
576         value = 0;
577     }
578 
579     oldvalue = value;
580     if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
581         (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
582         addReplyError(c,"increment or decrement would overflow");
583         return;
584     }
585     value += incr;
586     new = sdsfromlonglong(value);
587     hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
588     addReplyLongLong(c,value);
589     signalModifiedKey(c->db,c->argv[1]);
590     notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
591     server.dirty++;
592 }
593 
hincrbyfloatCommand(client * c)594 void hincrbyfloatCommand(client *c) {
595     long double value, incr;
596     long long ll;
597     robj *o;
598     sds new;
599     unsigned char *vstr;
600     unsigned int vlen;
601 
602     if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != C_OK) return;
603     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
604     if (hashTypeGetValue(o,c->argv[2]->ptr,&vstr,&vlen,&ll) == C_OK) {
605         if (vstr) {
606             if (string2ld((char*)vstr,vlen,&value) == 0) {
607                 addReplyError(c,"hash value is not a float");
608                 return;
609             }
610         } else {
611             value = (long double)ll;
612         }
613     } else {
614         value = 0;
615     }
616 
617     value += incr;
618     if (isnan(value) || isinf(value)) {
619         addReplyError(c,"increment would produce NaN or Infinity");
620         return;
621     }
622 
623     char buf[MAX_LONG_DOUBLE_CHARS];
624     int len = ld2string(buf,sizeof(buf),value,1);
625     new = sdsnewlen(buf,len);
626     hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
627     addReplyBulkCBuffer(c,buf,len);
628     signalModifiedKey(c->db,c->argv[1]);
629     notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
630     server.dirty++;
631 
632     /* Always replicate HINCRBYFLOAT as an HSET command with the final value
633      * in order to make sure that differences in float pricision or formatting
634      * will not create differences in replicas or after an AOF restart. */
635     robj *aux, *newobj;
636     aux = createStringObject("HSET",4);
637     newobj = createRawStringObject(buf,len);
638     rewriteClientCommandArgument(c,0,aux);
639     decrRefCount(aux);
640     rewriteClientCommandArgument(c,3,newobj);
641     decrRefCount(newobj);
642 }
643 
addHashFieldToReply(client * c,robj * o,sds field)644 static void addHashFieldToReply(client *c, robj *o, sds field) {
645     int ret;
646 
647     if (o == NULL) {
648         addReply(c, shared.nullbulk);
649         return;
650     }
651 
652     if (o->encoding == OBJ_ENCODING_ZIPLIST) {
653         unsigned char *vstr = NULL;
654         unsigned int vlen = UINT_MAX;
655         long long vll = LLONG_MAX;
656 
657         ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
658         if (ret < 0) {
659             addReply(c, shared.nullbulk);
660         } else {
661             if (vstr) {
662                 addReplyBulkCBuffer(c, vstr, vlen);
663             } else {
664                 addReplyBulkLongLong(c, vll);
665             }
666         }
667 
668     } else if (o->encoding == OBJ_ENCODING_HT) {
669         sds value = hashTypeGetFromHashTable(o, field);
670         if (value == NULL)
671             addReply(c, shared.nullbulk);
672         else
673             addReplyBulkCBuffer(c, value, sdslen(value));
674     } else {
675         serverPanic("Unknown hash encoding");
676     }
677 }
678 
hgetCommand(client * c)679 void hgetCommand(client *c) {
680     robj *o;
681 
682     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
683         checkType(c,o,OBJ_HASH)) return;
684 
685     addHashFieldToReply(c, o, c->argv[2]->ptr);
686 }
687 
hmgetCommand(client * c)688 void hmgetCommand(client *c) {
689     robj *o;
690     int i;
691 
692     /* Don't abort when the key cannot be found. Non-existing keys are empty
693      * hashes, where HMGET should respond with a series of null bulks. */
694     o = lookupKeyRead(c->db, c->argv[1]);
695     if (o != NULL && o->type != OBJ_HASH) {
696         addReply(c, shared.wrongtypeerr);
697         return;
698     }
699 
700     addReplyMultiBulkLen(c, c->argc-2);
701     for (i = 2; i < c->argc; i++) {
702         addHashFieldToReply(c, o, c->argv[i]->ptr);
703     }
704 }
705 
hdelCommand(client * c)706 void hdelCommand(client *c) {
707     robj *o;
708     int j, deleted = 0, keyremoved = 0;
709 
710     if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
711         checkType(c,o,OBJ_HASH)) return;
712 
713     for (j = 2; j < c->argc; j++) {
714         if (hashTypeDelete(o,c->argv[j]->ptr)) {
715             deleted++;
716             if (hashTypeLength(o) == 0) {
717                 dbDelete(c->db,c->argv[1]);
718                 keyremoved = 1;
719                 break;
720             }
721         }
722     }
723     if (deleted) {
724         signalModifiedKey(c->db,c->argv[1]);
725         notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
726         if (keyremoved)
727             notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
728                                 c->db->id);
729         server.dirty += deleted;
730     }
731     addReplyLongLong(c,deleted);
732 }
733 
hlenCommand(client * c)734 void hlenCommand(client *c) {
735     robj *o;
736 
737     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
738         checkType(c,o,OBJ_HASH)) return;
739 
740     addReplyLongLong(c,hashTypeLength(o));
741 }
742 
hstrlenCommand(client * c)743 void hstrlenCommand(client *c) {
744     robj *o;
745 
746     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
747         checkType(c,o,OBJ_HASH)) return;
748     addReplyLongLong(c,hashTypeGetValueLength(o,c->argv[2]->ptr));
749 }
750 
addHashIteratorCursorToReply(client * c,hashTypeIterator * hi,int what)751 static void addHashIteratorCursorToReply(client *c, hashTypeIterator *hi, int what) {
752     if (hi->encoding == OBJ_ENCODING_ZIPLIST) {
753         unsigned char *vstr = NULL;
754         unsigned int vlen = UINT_MAX;
755         long long vll = LLONG_MAX;
756 
757         hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
758         if (vstr)
759             addReplyBulkCBuffer(c, vstr, vlen);
760         else
761             addReplyBulkLongLong(c, vll);
762     } else if (hi->encoding == OBJ_ENCODING_HT) {
763         sds value = hashTypeCurrentFromHashTable(hi, what);
764         addReplyBulkCBuffer(c, value, sdslen(value));
765     } else {
766         serverPanic("Unknown hash encoding");
767     }
768 }
769 
genericHgetallCommand(client * c,int flags)770 void genericHgetallCommand(client *c, int flags) {
771     robj *o;
772     hashTypeIterator *hi;
773     int multiplier = 0;
774     int length, count = 0;
775 
776     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
777         || checkType(c,o,OBJ_HASH)) return;
778 
779     if (flags & OBJ_HASH_KEY) multiplier++;
780     if (flags & OBJ_HASH_VALUE) multiplier++;
781 
782     length = hashTypeLength(o) * multiplier;
783     addReplyMultiBulkLen(c, length);
784 
785     hi = hashTypeInitIterator(o);
786     while (hashTypeNext(hi) != C_ERR) {
787         if (flags & OBJ_HASH_KEY) {
788             addHashIteratorCursorToReply(c, hi, OBJ_HASH_KEY);
789             count++;
790         }
791         if (flags & OBJ_HASH_VALUE) {
792             addHashIteratorCursorToReply(c, hi, OBJ_HASH_VALUE);
793             count++;
794         }
795     }
796 
797     hashTypeReleaseIterator(hi);
798     serverAssert(count == length);
799 }
800 
hkeysCommand(client * c)801 void hkeysCommand(client *c) {
802     genericHgetallCommand(c,OBJ_HASH_KEY);
803 }
804 
hvalsCommand(client * c)805 void hvalsCommand(client *c) {
806     genericHgetallCommand(c,OBJ_HASH_VALUE);
807 }
808 
hgetallCommand(client * c)809 void hgetallCommand(client *c) {
810     genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE);
811 }
812 
hexistsCommand(client * c)813 void hexistsCommand(client *c) {
814     robj *o;
815     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
816         checkType(c,o,OBJ_HASH)) return;
817 
818     addReply(c, hashTypeExists(o,c->argv[2]->ptr) ? shared.cone : shared.czero);
819 }
820 
hscanCommand(client * c)821 void hscanCommand(client *c) {
822     robj *o;
823     unsigned long cursor;
824 
825     if (parseScanCursorOrReply(c,c->argv[2],&cursor) == C_ERR) return;
826     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptyscan)) == NULL ||
827         checkType(c,o,OBJ_HASH)) return;
828     scanGenericCommand(c,o,cursor);
829 }
830