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 /* Encode given objects in-place when the hash uses a dict. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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. */ 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 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 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. */ 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`. */ 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`. */ 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. */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 774 void hkeysCommand(client *c) { 775 genericHgetallCommand(c,OBJ_HASH_KEY); 776 } 777 778 void hvalsCommand(client *c) { 779 genericHgetallCommand(c,OBJ_HASH_VALUE); 780 } 781 782 void hgetallCommand(client *c) { 783 genericHgetallCommand(c,OBJ_HASH_KEY|OBJ_HASH_VALUE); 784 } 785 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 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