LCOV - code coverage report
Current view: directory - redis/src - t_hash.c (source / functions) Found Hit Coverage
Test: redis.info Lines: 386 369 95.6 %
Date: 2012-04-04 Functions: 34 34 100.0 %
Colors: not hit hit

       1                 : #include "redis.h"
       2                 : #include <math.h>
       3                 : 
       4                 : /*-----------------------------------------------------------------------------
       5                 :  * Hash type API
       6                 :  *----------------------------------------------------------------------------*/
       7                 : 
       8                 : /* Check the length of a number of objects to see if we need to convert a
       9                 :  * ziplist to a real hash. Note that we only check string encoded objects
      10                 :  * as their string length can be queried in constant time. */
      11           48415 : void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
      12                 :     int i;
      13                 : 
      14           48415 :     if (o->encoding != REDIS_ENCODING_ZIPLIST) return;
      15                 : 
      16          137171 :     for (i = start; i <= end; i++) {
      17          183272 :         if (argv[i]->encoding == REDIS_ENCODING_RAW &&
      18          183272 :             sdslen(argv[i]->ptr) > server.hash_max_ziplist_value)
      19                 :         {
      20               1 :             hashTypeConvert(o, REDIS_ENCODING_HT);
      21               1 :             break;
      22                 :         }
      23                 :     }
      24                 : }
      25                 : 
      26                 : /* Encode given objects in-place when the hash uses a dict. */
      27           51380 : void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
      28           51380 :     if (subject->encoding == REDIS_ENCODING_HT) {
      29            5687 :         if (o1) *o1 = tryObjectEncoding(*o1);
      30            5687 :         if (o2) *o2 = tryObjectEncoding(*o2);
      31                 :     }
      32           51380 : }
      33                 : 
      34                 : /* Get the value from a ziplist encoded hash, identified by field.
      35                 :  * Returns -1 when the field cannot be found. */
      36           32098 : int hashTypeGetFromZiplist(robj *o, robj *field,
      37                 :                            unsigned char **vstr,
      38                 :                            unsigned int *vlen,
      39                 :                            long long *vll)
      40                 : {
      41           32098 :     unsigned char *zl, *fptr = NULL, *vptr = NULL;
      42                 :     int ret;
      43                 : 
      44           32098 :     redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);
      45                 : 
      46           32098 :     field = getDecodedObject(field);
      47                 : 
      48           32098 :     zl = o->ptr;
      49           32098 :     fptr = ziplistIndex(zl, ZIPLIST_HEAD);
      50           32098 :     if (fptr != NULL) {
      51           64192 :         fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
      52           32096 :         if (fptr != NULL) {
      53                 :             /* Grab pointer to the value (fptr points to the field) */
      54           32090 :             vptr = ziplistNext(zl, fptr);
      55           32090 :             redisAssert(vptr != NULL);
      56                 :         }
      57                 :     }
      58                 : 
      59           32098 :     decrRefCount(field);
      60                 : 
      61           32098 :     if (vptr != NULL) {
      62           32090 :         ret = ziplistGet(vptr, vstr, vlen, vll);
      63           32090 :         redisAssert(ret);
      64           32090 :         return 0;
      65                 :     }
      66                 : 
      67               8 :     return -1;
      68                 : }
      69                 : 
      70                 : /* Get the value from a hash table encoded hash, identified by field.
      71                 :  * Returns -1 when the field cannot be found. */
      72            2099 : int hashTypeGetFromHashTable(robj *o, robj *field, robj **value) {
      73                 :     dictEntry *de;
      74                 : 
      75            2099 :     redisAssert(o->encoding == REDIS_ENCODING_HT);
      76                 : 
      77            2099 :     de = dictFind(o->ptr, field);
      78            2099 :     if (de == NULL) return -1;
      79            2089 :     *value = dictGetVal(de);
      80            2089 :     return 0;
      81                 : }
      82                 : 
      83                 : /* Higher level function of hashTypeGet*() that always returns a Redis
      84                 :  * object (either new or with refcount incremented), so that the caller
      85                 :  * can retain a reference or call decrRefCount after the usage.
      86                 :  *
      87                 :  * The lower level function can prevent copy on write so it is
      88                 :  * the preferred way of doing read operations. */
      89           32096 : robj *hashTypeGetObject(robj *o, robj *field) {
      90           32096 :     robj *value = NULL;
      91                 : 
      92           32096 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
      93           32068 :         unsigned char *vstr = NULL;
      94           32068 :         unsigned int vlen = UINT_MAX;
      95           32068 :         long long vll = LLONG_MAX;
      96                 : 
      97           32068 :         if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) {
      98           32066 :             if (vstr) {
      99           16551 :                 value = createStringObject((char*)vstr, vlen);
     100                 :             } else {
     101           15515 :                 value = createStringObjectFromLongLong(vll);
     102                 :             }
     103                 :         }
     104                 : 
     105              28 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     106                 :         robj *aux;
     107                 : 
     108              28 :         if (hashTypeGetFromHashTable(o, field, &aux) == 0) {
     109              24 :             incrRefCount(aux);
     110              24 :             value = aux;
     111                 :         }
     112                 :     } else {
     113               0 :         redisPanic("Unknown hash encoding");
     114                 :     }
     115           32096 :     return value;
     116                 : }
     117                 : 
     118                 : /* Test if the specified field exists in the given hash. Returns 1 if the field
     119                 :  * exists, and 0 when it doesn't. */
     120               8 : int hashTypeExists(robj *o, robj *field) {
     121               8 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
     122               4 :         unsigned char *vstr = NULL;
     123               4 :         unsigned int vlen = UINT_MAX;
     124               4 :         long long vll = LLONG_MAX;
     125                 : 
     126               4 :         if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1;
     127               4 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     128                 :         robj *aux;
     129                 : 
     130               4 :         if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1;
     131                 :     } else {
     132               0 :         redisPanic("Unknown hash encoding");
     133                 :     }
     134               4 :     return 0;
     135                 : }
     136                 : 
     137                 : /* Add an element, discard the old if the key already exists.
     138                 :  * Return 0 on insert and 1 on update.
     139                 :  * This function will take care of incrementing the reference count of the
     140                 :  * retained fields and value objects. */
     141           51380 : int hashTypeSet(robj *o, robj *field, robj *value) {
     142           51380 :     int update = 0;
     143                 : 
     144           51380 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
     145                 :         unsigned char *zl, *fptr, *vptr;
     146                 : 
     147           45693 :         field = getDecodedObject(field);
     148           45693 :         value = getDecodedObject(value);
     149                 : 
     150           45693 :         zl = o->ptr;
     151           45693 :         fptr = ziplistIndex(zl, ZIPLIST_HEAD);
     152           45693 :         if (fptr != NULL) {
     153           41268 :             fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
     154           20634 :             if (fptr != NULL) {
     155                 :                 /* Grab pointer to the value (fptr points to the field) */
     156           19433 :                 vptr = ziplistNext(zl, fptr);
     157           19433 :                 redisAssert(vptr != NULL);
     158           19433 :                 update = 1;
     159                 : 
     160                 :                 /* Delete value */
     161           19433 :                 zl = ziplistDelete(zl, &vptr);
     162                 : 
     163                 :                 /* Insert new value */
     164           38866 :                 zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));
     165                 :             }
     166                 :         }
     167                 : 
     168           45693 :         if (!update) {
     169                 :             /* Push new field/value pair onto the tail of the ziplist */
     170           52520 :             zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);
     171           52520 :             zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);
     172                 :         }
     173           45693 :         o->ptr = zl;
     174           45693 :         decrRefCount(field);
     175           45693 :         decrRefCount(value);
     176                 : 
     177                 :         /* Check if the ziplist needs to be converted to a hash table */
     178           45693 :         if (hashTypeLength(o) > server.hash_max_ziplist_entries)
     179               5 :             hashTypeConvert(o, REDIS_ENCODING_HT);
     180            5687 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     181            5687 :         if (dictReplace(o->ptr, field, value)) { /* Insert */
     182            4552 :             incrRefCount(field);
     183                 :         } else { /* Update */
     184            1135 :             update = 1;
     185                 :         }
     186            5687 :         incrRefCount(value);
     187                 :     } else {
     188               0 :         redisPanic("Unknown hash encoding");
     189                 :     }
     190           51380 :     return update;
     191                 : }
     192                 : 
     193                 : /* Delete an element from a hash.
     194                 :  * Return 1 on deleted and 0 on not found. */
     195            7938 : int hashTypeDelete(robj *o, robj *field) {
     196            7938 :     int deleted = 0;
     197                 : 
     198            7938 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
     199                 :         unsigned char *zl, *fptr;
     200                 : 
     201            7929 :         field = getDecodedObject(field);
     202                 : 
     203            7929 :         zl = o->ptr;
     204            7929 :         fptr = ziplistIndex(zl, ZIPLIST_HEAD);
     205            7929 :         if (fptr != NULL) {
     206           15858 :             fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);
     207            7929 :             if (fptr != NULL) {
     208            7491 :                 zl = ziplistDelete(zl,&fptr);
     209            7491 :                 zl = ziplistDelete(zl,&fptr);
     210            7491 :                 o->ptr = zl;
     211            7491 :                 deleted = 1;
     212                 :             }
     213                 :         }
     214                 : 
     215            7929 :         decrRefCount(field);
     216                 : 
     217               9 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     218               9 :         if (dictDelete((dict*)o->ptr, field) == REDIS_OK) {
     219               5 :             deleted = 1;
     220                 : 
     221                 :             /* Always check if the dictionary needs a resize after a delete. */
     222               5 :             if (htNeedsResize(o->ptr)) dictResize(o->ptr);
     223                 :         }
     224                 : 
     225                 :     } else {
     226               0 :         redisPanic("Unknown hash encoding");
     227                 :     }
     228                 : 
     229            7938 :     return deleted;
     230                 : }
     231                 : 
     232                 : /* Return the number of elements in a hash. */
     233           53775 : unsigned long hashTypeLength(robj *o) {
     234           53775 :     unsigned long length = ULONG_MAX;
     235                 : 
     236           53775 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
     237           53764 :         length = ziplistLen(o->ptr) / 2;
     238              11 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     239              11 :         length = dictSize((dict*)o->ptr);
     240                 :     } else {
     241               0 :         redisPanic("Unknown hash encoding");
     242                 :     }
     243                 : 
     244           53775 :     return length;
     245                 : }
     246                 : 
     247           20539 : hashTypeIterator *hashTypeInitIterator(robj *subject) {
     248           20539 :     hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator));
     249           20539 :     hi->subject = subject;
     250           20539 :     hi->encoding = subject->encoding;
     251                 : 
     252           20539 :     if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
     253           20532 :         hi->fptr = NULL;
     254           20532 :         hi->vptr = NULL;
     255               7 :     } else if (hi->encoding == REDIS_ENCODING_HT) {
     256               7 :         hi->di = dictGetIterator(subject->ptr);
     257                 :     } else {
     258               0 :         redisPanic("Unknown hash encoding");
     259                 :     }
     260                 : 
     261           20539 :     return hi;
     262                 : }
     263                 : 
     264           20539 : void hashTypeReleaseIterator(hashTypeIterator *hi) {
     265           20539 :     if (hi->encoding == REDIS_ENCODING_HT) {
     266               7 :         dictReleaseIterator(hi->di);
     267                 :     }
     268                 : 
     269           20539 :     zfree(hi);
     270           20539 : }
     271                 : 
     272                 : /* Move to the next entry in the hash. Return REDIS_OK when the next entry
     273                 :  * could be found and REDIS_ERR when the iterator reaches the end. */
     274           50392 : int hashTypeNext(hashTypeIterator *hi) {
     275           50392 :     if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
     276                 :         unsigned char *zl;
     277                 :         unsigned char *fptr, *vptr;
     278                 : 
     279           43469 :         zl = hi->subject->ptr;
     280           43469 :         fptr = hi->fptr;
     281           43469 :         vptr = hi->vptr;
     282                 : 
     283           43469 :         if (fptr == NULL) {
     284                 :             /* Initialize cursor */
     285           20532 :             redisAssert(vptr == NULL);
     286           20532 :             fptr = ziplistIndex(zl, 0);
     287                 :         } else {
     288                 :             /* Advance cursor */
     289           22937 :             redisAssert(vptr != NULL);
     290           22937 :             fptr = ziplistNext(zl, vptr);
     291                 :         }
     292           43469 :         if (fptr == NULL) return REDIS_ERR;
     293                 : 
     294                 :         /* Grab pointer to the value (fptr points to the field) */
     295           22937 :         vptr = ziplistNext(zl, fptr);
     296           22937 :         redisAssert(vptr != NULL);
     297                 : 
     298                 :         /* fptr, vptr now point to the first or next pair */
     299           22937 :         hi->fptr = fptr;
     300           22937 :         hi->vptr = vptr;
     301            6923 :     } else if (hi->encoding == REDIS_ENCODING_HT) {
     302            6923 :         if ((hi->de = dictNext(hi->di)) == NULL) return REDIS_ERR;
     303                 :     } else {
     304               0 :         redisPanic("Unknown hash encoding");
     305                 :     }
     306           29853 :     return REDIS_OK;
     307                 : }
     308                 : 
     309                 : /* Get the field or value at iterator cursor, for an iterator on a hash value
     310                 :  * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
     311           45858 : void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what,
     312                 :                                 unsigned char **vstr,
     313                 :                                 unsigned int *vlen,
     314                 :                                 long long *vll)
     315                 : {
     316                 :     int ret;
     317                 : 
     318           45858 :     redisAssert(hi->encoding == REDIS_ENCODING_ZIPLIST);
     319                 : 
     320           45858 :     if (what & REDIS_HASH_KEY) {
     321           22929 :         ret = ziplistGet(hi->fptr, vstr, vlen, vll);
     322           22929 :         redisAssert(ret);
     323                 :     } else {
     324           22929 :         ret = ziplistGet(hi->vptr, vstr, vlen, vll);
     325           22929 :         redisAssert(ret);
     326                 :     }
     327           45858 : }
     328                 : 
     329                 : /* Get the field or value at iterator cursor, for an iterator on a hash value
     330                 :  * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
     331           11784 : void hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) {
     332           11784 :     redisAssert(hi->encoding == REDIS_ENCODING_HT);
     333                 : 
     334           11784 :     if (what & REDIS_HASH_KEY) {
     335            5892 :         *dst = dictGetKey(hi->de);
     336                 :     } else {
     337            5892 :         *dst = dictGetVal(hi->de);
     338                 :     }
     339           11784 : }
     340                 : 
     341                 : /* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
     342                 :  * that returns an object with incremented refcount (or a new object). It is up
     343                 :  * to the caller to decrRefCount() the object if no reference is retained. */
     344           53304 : robj *hashTypeCurrentObject(hashTypeIterator *hi, int what) {
     345                 :     robj *dst;
     346                 : 
     347           53304 :     if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
     348           45616 :         unsigned char *vstr = NULL;
     349           45616 :         unsigned int vlen = UINT_MAX;
     350           45616 :         long long vll = LLONG_MAX;
     351                 : 
     352           45616 :         hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
     353           45616 :         if (vstr) {
     354           11156 :             dst = createStringObject((char*)vstr, vlen);
     355                 :         } else {
     356           34460 :             dst = createStringObjectFromLongLong(vll);
     357                 :         }
     358                 : 
     359            7688 :     } else if (hi->encoding == REDIS_ENCODING_HT) {
     360            7688 :         hashTypeCurrentFromHashTable(hi, what, &dst);
     361            7688 :         incrRefCount(dst);
     362                 : 
     363                 :     } else {
     364               0 :         redisPanic("Unknown hash encoding");
     365                 :     }
     366                 : 
     367           53304 :     return dst;
     368                 : }
     369                 : 
     370           48447 : robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {
     371           48447 :     robj *o = lookupKeyWrite(c->db,key);
     372           48447 :     if (o == NULL) {
     373           25059 :         o = createHashObject();
     374           25059 :         dbAdd(c->db,key,o);
     375                 :     } else {
     376           23388 :         if (o->type != REDIS_HASH) {
     377               0 :             addReply(c,shared.wrongtypeerr);
     378               0 :             return NULL;
     379                 :         }
     380                 :     }
     381           48447 :     return o;
     382                 : }
     383                 : 
     384               8 : void hashTypeConvertZiplist(robj *o, int enc) {
     385               8 :     redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST);
     386                 : 
     387               8 :     if (enc == REDIS_ENCODING_ZIPLIST) {
     388                 :         /* Nothing to do... */
     389                 : 
     390               8 :     } else if (enc == REDIS_ENCODING_HT) {
     391                 :         hashTypeIterator *hi;
     392                 :         dict *dict;
     393                 :         int ret;
     394                 : 
     395               8 :         hi = hashTypeInitIterator(o);
     396               8 :         dict = dictCreate(&hashDictType, NULL);
     397                 : 
     398             352 :         while (hashTypeNext(hi) != REDIS_ERR) {
     399                 :             robj *field, *value;
     400                 : 
     401             336 :             field = hashTypeCurrentObject(hi, REDIS_HASH_KEY);
     402             336 :             field = tryObjectEncoding(field);
     403             336 :             value = hashTypeCurrentObject(hi, REDIS_HASH_VALUE);
     404             336 :             value = tryObjectEncoding(value);
     405             336 :             ret = dictAdd(dict, field, value);
     406             336 :             redisAssert(ret == DICT_OK);
     407                 :         }
     408                 : 
     409               8 :         hashTypeReleaseIterator(hi);
     410               8 :         zfree(o->ptr);
     411                 : 
     412               8 :         o->encoding = REDIS_ENCODING_HT;
     413               8 :         o->ptr = dict;
     414                 : 
     415                 :     } else {
     416               0 :         redisPanic("Unknown hash encoding");
     417                 :     }
     418               8 : }
     419                 : 
     420               8 : void hashTypeConvert(robj *o, int enc) {
     421               8 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
     422               8 :         hashTypeConvertZiplist(o, enc);
     423               0 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     424               0 :         redisPanic("Not implemented");
     425                 :     } else {
     426               0 :         redisPanic("Unknown hash encoding");
     427                 :     }
     428               8 : }
     429                 : 
     430                 : /*-----------------------------------------------------------------------------
     431                 :  * Hash type commands
     432                 :  *----------------------------------------------------------------------------*/
     433                 : 
     434           48285 : void hsetCommand(redisClient *c) {
     435                 :     int update;
     436                 :     robj *o;
     437                 : 
     438           48285 :     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
     439           48285 :     hashTypeTryConversion(o,c->argv,2,3);
     440           48285 :     hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
     441           48285 :     update = hashTypeSet(o,c->argv[2],c->argv[3]);
     442           48285 :     addReply(c, update ? shared.czero : shared.cone);
     443           48285 :     signalModifiedKey(c->db,c->argv[1]);
     444           48285 :     server.dirty++;
     445                 : }
     446                 : 
     447               4 : void hsetnxCommand(redisClient *c) {
     448                 :     robj *o;
     449               4 :     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
     450               4 :     hashTypeTryConversion(o,c->argv,2,3);
     451                 : 
     452               4 :     if (hashTypeExists(o, c->argv[2])) {
     453               2 :         addReply(c, shared.czero);
     454                 :     } else {
     455               2 :         hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
     456               2 :         hashTypeSet(o,c->argv[2],c->argv[3]);
     457               2 :         addReply(c, shared.cone);
     458               2 :         signalModifiedKey(c->db,c->argv[1]);
     459               2 :         server.dirty++;
     460                 :     }
     461                 : }
     462                 : 
     463             127 : void hmsetCommand(redisClient *c) {
     464                 :     int i;
     465                 :     robj *o;
     466                 : 
     467             127 :     if ((c->argc % 2) == 1) {
     468               1 :         addReplyError(c,"wrong number of arguments for HMSET");
     469               1 :         return;
     470                 :     }
     471                 : 
     472             126 :     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
     473             126 :     hashTypeTryConversion(o,c->argv,2,c->argc-1);
     474            3196 :     for (i = 2; i < c->argc; i += 2) {
     475            3070 :         hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
     476            3070 :         hashTypeSet(o,c->argv[i],c->argv[i+1]);
     477                 :     }
     478             126 :     addReply(c, shared.ok);
     479             126 :     signalModifiedKey(c->db,c->argv[1]);
     480             126 :     server.dirty++;
     481                 : }
     482                 : 
     483              17 : void hincrbyCommand(redisClient *c) {
     484                 :     long long value, incr, oldvalue;
     485                 :     robj *o, *current, *new;
     486                 : 
     487              17 :     if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
     488              17 :     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
     489              17 :     if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
     490              14 :         if (getLongLongFromObjectOrReply(c,current,&value,
     491                 :             "hash value is not an integer") != REDIS_OK) {
     492               4 :             decrRefCount(current);
     493               4 :             return;
     494                 :         }
     495              10 :         decrRefCount(current);
     496                 :     } else {
     497               3 :         value = 0;
     498                 :     }
     499                 : 
     500              13 :     oldvalue = value;
     501              33 :     if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
     502              20 :         (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
     503               1 :         addReplyError(c,"increment or decrement would overflow");
     504               1 :         return;
     505                 :     }
     506              12 :     value += incr;
     507              12 :     new = createStringObjectFromLongLong(value);
     508              12 :     hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
     509              12 :     hashTypeSet(o,c->argv[2],new);
     510              12 :     decrRefCount(new);
     511              12 :     addReplyLongLong(c,value);
     512              12 :     signalModifiedKey(c->db,c->argv[1]);
     513              12 :     server.dirty++;
     514                 : }
     515                 : 
     516              15 : void hincrbyfloatCommand(redisClient *c) {
     517                 :     double long value, incr;
     518                 :     robj *o, *current, *new, *aux;
     519                 : 
     520              15 :     if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
     521              15 :     if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
     522              15 :     if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) {
     523              12 :         if (getLongDoubleFromObjectOrReply(c,current,&value,
     524                 :             "hash value is not a valid float") != REDIS_OK) {
     525               4 :             decrRefCount(current);
     526               4 :             return;
     527                 :         }
     528               8 :         decrRefCount(current);
     529                 :     } else {
     530               3 :         value = 0;
     531                 :     }
     532                 : 
     533              11 :     value += incr;
     534              11 :     new = createStringObjectFromLongDouble(value);
     535              11 :     hashTypeTryObjectEncoding(o,&c->argv[2],NULL);
     536              11 :     hashTypeSet(o,c->argv[2],new);
     537              11 :     addReplyBulk(c,new);
     538              11 :     signalModifiedKey(c->db,c->argv[1]);
     539              11 :     server.dirty++;
     540                 : 
     541                 :     /* Always replicate HINCRBYFLOAT as an HSET command with the final value
     542                 :      * in order to make sure that differences in float pricision or formatting
     543                 :      * will not create differences in replicas or after an AOF restart. */
     544              11 :     aux = createStringObject("HSET",4);
     545              11 :     rewriteClientCommandArgument(c,0,aux);
     546              11 :     decrRefCount(aux);
     547              11 :     rewriteClientCommandArgument(c,3,new);
     548              11 :     decrRefCount(new);
     549                 : }
     550                 : 
     551            2095 : static void addHashFieldToReply(redisClient *c, robj *o, robj *field) {
     552                 :     int ret;
     553                 : 
     554            2095 :     if (o == NULL) {
     555               2 :         addReply(c, shared.nullbulk);
     556               2 :         return;
     557                 :     }
     558                 : 
     559            2093 :     if (o->encoding == REDIS_ENCODING_ZIPLIST) {
     560              26 :         unsigned char *vstr = NULL;
     561              26 :         unsigned int vlen = UINT_MAX;
     562              26 :         long long vll = LLONG_MAX;
     563                 : 
     564              26 :         ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll);
     565              26 :         if (ret < 0) {
     566               4 :             addReply(c, shared.nullbulk);
     567                 :         } else {
     568              22 :             if (vstr) {
     569              22 :                 addReplyBulkCBuffer(c, vstr, vlen);
     570                 :             } else {
     571               0 :                 addReplyBulkLongLong(c, vll);
     572                 :             }
     573                 :         }
     574                 : 
     575            2067 :     } else if (o->encoding == REDIS_ENCODING_HT) {
     576                 :         robj *value;
     577                 : 
     578            2067 :         ret = hashTypeGetFromHashTable(o, field, &value);
     579            2067 :         if (ret < 0) {
     580               4 :             addReply(c, shared.nullbulk);
     581                 :         } else {
     582            2063 :             addReplyBulk(c, value);
     583                 :         }
     584                 : 
     585                 :     } else {
     586               0 :         redisPanic("Unknown hash encoding");
     587                 :     }
     588                 : }
     589                 : 
     590            1051 : void hgetCommand(redisClient *c) {
     591                 :     robj *o;
     592                 : 
     593            2102 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
     594            1051 :         checkType(c,o,REDIS_HASH)) return;
     595                 : 
     596            1051 :     addHashFieldToReply(c, o, c->argv[2]);
     597                 : }
     598                 : 
     599               9 : void hmgetCommand(redisClient *c) {
     600                 :     robj *o;
     601                 :     int i;
     602                 : 
     603                 :     /* Don't abort when the key cannot be found. Non-existing keys are empty
     604                 :      * hashes, where HMGET should respond with a series of null bulks. */
     605               9 :     o = lookupKeyRead(c->db, c->argv[1]);
     606               9 :     if (o != NULL && o->type != REDIS_HASH) {
     607               1 :         addReply(c, shared.wrongtypeerr);
     608               1 :         return;
     609                 :     }
     610                 : 
     611               8 :     addReplyMultiBulkLen(c, c->argc-2);
     612            1052 :     for (i = 2; i < c->argc; i++) {
     613            1044 :         addHashFieldToReply(c, o, c->argv[i]);
     614                 :     }
     615                 : }
     616                 : 
     617            7933 : void hdelCommand(redisClient *c) {
     618                 :     robj *o;
     619            7933 :     int j, deleted = 0;
     620                 : 
     621           15866 :     if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
     622            7933 :         checkType(c,o,REDIS_HASH)) return;
     623                 : 
     624            8387 :     for (j = 2; j < c->argc; j++) {
     625            7938 :         if (hashTypeDelete(o,c->argv[j])) {
     626            7496 :             deleted++;
     627            7496 :             if (hashTypeLength(o) == 0) {
     628            7484 :                 dbDelete(c->db,c->argv[1]);
     629            7484 :                 break;
     630                 :             }
     631                 :         }
     632                 :     }
     633            7933 :     if (deleted) {
     634            7493 :         signalModifiedKey(c->db,c->argv[1]);
     635            7493 :         server.dirty += deleted;
     636                 :     }
     637            7933 :     addReplyLongLong(c,deleted);
     638                 : }
     639                 : 
     640               5 : void hlenCommand(redisClient *c) {
     641                 :     robj *o;
     642              10 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
     643               5 :         checkType(c,o,REDIS_HASH)) return;
     644                 : 
     645               5 :     addReplyLongLong(c,hashTypeLength(o));
     646                 : }
     647                 : 
     648            4338 : static void addHashIteratorCursorToReply(redisClient *c, hashTypeIterator *hi, int what) {
     649            4338 :     if (hi->encoding == REDIS_ENCODING_ZIPLIST) {
     650             242 :         unsigned char *vstr = NULL;
     651             242 :         unsigned int vlen = UINT_MAX;
     652             242 :         long long vll = LLONG_MAX;
     653                 : 
     654             242 :         hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll);
     655             242 :         if (vstr) {
     656              94 :             addReplyBulkCBuffer(c, vstr, vlen);
     657                 :         } else {
     658             148 :             addReplyBulkLongLong(c, vll);
     659                 :         }
     660                 : 
     661            4096 :     } else if (hi->encoding == REDIS_ENCODING_HT) {
     662                 :         robj *value;
     663                 : 
     664            4096 :         hashTypeCurrentFromHashTable(hi, what, &value);
     665            4096 :         addReplyBulk(c, value);
     666                 : 
     667                 :     } else {
     668               0 :         redisPanic("Unknown hash encoding");
     669                 :     }
     670            4338 : }
     671                 : 
     672              98 : void genericHgetallCommand(redisClient *c, int flags) {
     673                 :     robj *o;
     674                 :     hashTypeIterator *hi;
     675              98 :     int multiplier = 0;
     676              98 :     int length, count = 0;
     677                 : 
     678             196 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
     679              98 :         || checkType(c,o,REDIS_HASH)) return;
     680                 : 
     681              98 :     if (flags & REDIS_HASH_KEY) multiplier++;
     682              98 :     if (flags & REDIS_HASH_VALUE) multiplier++;
     683                 : 
     684              98 :     length = hashTypeLength(o) * multiplier;
     685              98 :     addReplyMultiBulkLen(c, length);
     686                 : 
     687              98 :     hi = hashTypeInitIterator(o);
     688            3397 :     while (hashTypeNext(hi) != REDIS_ERR) {
     689            3201 :         if (flags & REDIS_HASH_KEY) {
     690            2169 :             addHashIteratorCursorToReply(c, hi, REDIS_HASH_KEY);
     691            2169 :             count++;
     692                 :         }
     693            3201 :         if (flags & REDIS_HASH_VALUE) {
     694            2169 :             addHashIteratorCursorToReply(c, hi, REDIS_HASH_VALUE);
     695            2169 :             count++;
     696                 :         }
     697                 :     }
     698                 : 
     699              98 :     hashTypeReleaseIterator(hi);
     700              98 :     redisAssert(count == length);
     701                 : }
     702                 : 
     703               2 : void hkeysCommand(redisClient *c) {
     704               2 :     genericHgetallCommand(c,REDIS_HASH_KEY);
     705               2 : }
     706                 : 
     707               2 : void hvalsCommand(redisClient *c) {
     708               2 :     genericHgetallCommand(c,REDIS_HASH_VALUE);
     709               2 : }
     710                 : 
     711              94 : void hgetallCommand(redisClient *c) {
     712              94 :     genericHgetallCommand(c,REDIS_HASH_KEY|REDIS_HASH_VALUE);
     713              94 : }
     714                 : 
     715               4 : void hexistsCommand(redisClient *c) {
     716                 :     robj *o;
     717               8 :     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
     718               4 :         checkType(c,o,REDIS_HASH)) return;
     719                 : 
     720               4 :     addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero);
     721                 : }

Generated by: LCOV version 1.7