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 : }
|