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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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 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. */ 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. */ 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 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 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. */ 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`. */ 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`. */ 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. */ 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. */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 801 void hkeysCommand(client *c) { 802 genericHgetallCommand(c,OBJ_HASH_KEY); 803 } 804 805 void hvalsCommand(client *c) { 806 genericHgetallCommand(c,OBJ_HASH_VALUE); 807 } 808 809 void hgetallCommand(client *c) { 810 genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE); 811 } 812 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 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