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