]>
git.saurik.com Git - redis.git/blob - src/t_hash.c
5 /*-----------------------------------------------------------------------------
7 *----------------------------------------------------------------------------*/
9 /* Check the length of a number of objects to see if we need to convert a
10 * zipmap to a real hash. Note that we only check string encoded objects
11 * as their string length can be queried in constant time. */
12 void hashTypeTryConversion(robj
*subject
, robj
**argv
, int start
, int end
) {
14 if (subject
->encoding
!= REDIS_ENCODING_ZIPMAP
) return;
16 for (i
= start
; i
<= end
; i
++) {
17 if (argv
[i
]->encoding
== REDIS_ENCODING_RAW
&&
18 sdslen(argv
[i
]->ptr
) > server
.hash_max_zipmap_value
)
20 convertToRealHash(subject
);
26 /* Encode given objects in-place when the hash uses a dict. */
27 void hashTypeTryObjectEncoding(robj
*subject
, robj
**o1
, robj
**o2
) {
28 if (subject
->encoding
== REDIS_ENCODING_HT
) {
29 if (o1
) *o1
= tryObjectEncoding(*o1
);
30 if (o2
) *o2
= tryObjectEncoding(*o2
);
34 /* Get the value from a hash identified by key. Returns either a string
35 * object or NULL if the value cannot be found. The refcount of the object
36 * is always increased by 1 when the value was found. */
37 robj
*hashTypeGet(robj
*o
, robj
*key
) {
39 if (o
->encoding
== REDIS_ENCODING_ZIPMAP
) {
42 key
= getDecodedObject(key
);
43 if (zipmapGet(o
->ptr
,key
->ptr
,sdslen(key
->ptr
),&v
,&vlen
)) {
44 value
= createStringObject((char*)v
,vlen
);
48 dictEntry
*de
= dictFind(o
->ptr
,key
);
50 value
= dictGetEntryVal(de
);
57 /* Test if the key exists in the given hash. Returns 1 if the key
58 * exists and 0 when it doesn't. */
59 int hashTypeExists(robj
*o
, robj
*key
) {
60 if (o
->encoding
== REDIS_ENCODING_ZIPMAP
) {
61 key
= getDecodedObject(key
);
62 if (zipmapExists(o
->ptr
,key
->ptr
,sdslen(key
->ptr
))) {
68 if (dictFind(o
->ptr
,key
) != NULL
) {
75 /* Add an element, discard the old if the key already exists.
76 * Return 0 on insert and 1 on update. */
77 int hashTypeSet(robj
*o
, robj
*key
, robj
*value
) {
79 if (o
->encoding
== REDIS_ENCODING_ZIPMAP
) {
80 key
= getDecodedObject(key
);
81 value
= getDecodedObject(value
);
82 o
->ptr
= zipmapSet(o
->ptr
,
83 key
->ptr
,sdslen(key
->ptr
),
84 value
->ptr
,sdslen(value
->ptr
), &update
);
88 /* Check if the zipmap needs to be upgraded to a real hash table */
89 if (zipmapLen(o
->ptr
) > server
.hash_max_zipmap_entries
)
92 if (dictReplace(o
->ptr
,key
,value
)) {
104 /* Delete an element from a hash.
105 * Return 1 on deleted and 0 on not found. */
106 int hashTypeDelete(robj
*o
, robj
*key
) {
108 if (o
->encoding
== REDIS_ENCODING_ZIPMAP
) {
109 key
= getDecodedObject(key
);
110 o
->ptr
= zipmapDel(o
->ptr
,key
->ptr
,sdslen(key
->ptr
), &deleted
);
113 deleted
= dictDelete((dict
*)o
->ptr
,key
) == DICT_OK
;
114 /* Always check if the dictionary needs a resize after a delete. */
115 if (deleted
&& htNeedsResize(o
->ptr
)) dictResize(o
->ptr
);
120 /* Return the number of elements in a hash. */
121 unsigned long hashTypeLength(robj
*o
) {
122 return (o
->encoding
== REDIS_ENCODING_ZIPMAP
) ?
123 zipmapLen((unsigned char*)o
->ptr
) : dictSize((dict
*)o
->ptr
);
126 hashTypeIterator
*hashTypeInitIterator(robj
*subject
) {
127 hashTypeIterator
*hi
= zmalloc(sizeof(hashTypeIterator
));
128 hi
->encoding
= subject
->encoding
;
129 if (hi
->encoding
== REDIS_ENCODING_ZIPMAP
) {
130 hi
->zi
= zipmapRewind(subject
->ptr
);
131 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
132 hi
->di
= dictGetIterator(subject
->ptr
);
139 void hashTypeReleaseIterator(hashTypeIterator
*hi
) {
140 if (hi
->encoding
== REDIS_ENCODING_HT
) {
141 dictReleaseIterator(hi
->di
);
146 /* Move to the next entry in the hash. Return REDIS_OK when the next entry
147 * could be found and REDIS_ERR when the iterator reaches the end. */
148 int hashTypeNext(hashTypeIterator
*hi
) {
149 if (hi
->encoding
== REDIS_ENCODING_ZIPMAP
) {
150 if ((hi
->zi
= zipmapNext(hi
->zi
, &hi
->zk
, &hi
->zklen
,
151 &hi
->zv
, &hi
->zvlen
)) == NULL
) return REDIS_ERR
;
153 if ((hi
->de
= dictNext(hi
->di
)) == NULL
) return REDIS_ERR
;
158 /* Get key or value object at current iteration position.
159 * This increases the refcount of the field object by 1. */
160 robj
*hashTypeCurrent(hashTypeIterator
*hi
, int what
) {
162 if (hi
->encoding
== REDIS_ENCODING_ZIPMAP
) {
163 if (what
& REDIS_HASH_KEY
) {
164 o
= createStringObject((char*)hi
->zk
,hi
->zklen
);
166 o
= createStringObject((char*)hi
->zv
,hi
->zvlen
);
169 if (what
& REDIS_HASH_KEY
) {
170 o
= dictGetEntryKey(hi
->de
);
172 o
= dictGetEntryVal(hi
->de
);
179 robj
*hashTypeLookupWriteOrCreate(redisClient
*c
, robj
*key
) {
180 robj
*o
= lookupKeyWrite(c
->db
,key
);
182 o
= createHashObject();
185 if (o
->type
!= REDIS_HASH
) {
186 addReply(c
,shared
.wrongtypeerr
);
193 void convertToRealHash(robj
*o
) {
194 unsigned char *key
, *val
, *p
, *zm
= o
->ptr
;
195 unsigned int klen
, vlen
;
196 dict
*dict
= dictCreate(&hashDictType
,NULL
);
198 redisAssert(o
->type
== REDIS_HASH
&& o
->encoding
!= REDIS_ENCODING_HT
);
199 p
= zipmapRewind(zm
);
200 while((p
= zipmapNext(p
,&key
,&klen
,&val
,&vlen
)) != NULL
) {
201 robj
*keyobj
, *valobj
;
203 keyobj
= createStringObject((char*)key
,klen
);
204 valobj
= createStringObject((char*)val
,vlen
);
205 keyobj
= tryObjectEncoding(keyobj
);
206 valobj
= tryObjectEncoding(valobj
);
207 dictAdd(dict
,keyobj
,valobj
);
209 o
->encoding
= REDIS_ENCODING_HT
;
214 /*-----------------------------------------------------------------------------
216 *----------------------------------------------------------------------------*/
218 void hsetCommand(redisClient
*c
) {
222 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
223 hashTypeTryConversion(o
,c
->argv
,2,3);
224 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
225 update
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
226 addReply(c
, update
? shared
.czero
: shared
.cone
);
227 touchWatchedKey(c
->db
,c
->argv
[1]);
231 void hsetnxCommand(redisClient
*c
) {
233 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
234 hashTypeTryConversion(o
,c
->argv
,2,3);
236 if (hashTypeExists(o
, c
->argv
[2])) {
237 addReply(c
, shared
.czero
);
239 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
240 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
241 addReply(c
, shared
.cone
);
242 touchWatchedKey(c
->db
,c
->argv
[1]);
247 void hmsetCommand(redisClient
*c
) {
251 if ((c
->argc
% 2) == 1) {
252 addReplySds(c
,sdsnew("-ERR wrong number of arguments for HMSET\r\n"));
256 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
257 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
258 for (i
= 2; i
< c
->argc
; i
+= 2) {
259 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
260 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
262 addReply(c
, shared
.ok
);
263 touchWatchedKey(c
->db
,c
->argv
[1]);
267 void hincrbyCommand(redisClient
*c
) {
268 long long value
, incr
;
269 robj
*o
, *current
, *new;
271 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
272 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
273 if ((current
= hashTypeGet(o
,c
->argv
[2])) != NULL
) {
274 if (getLongLongFromObjectOrReply(c
,current
,&value
,
275 "hash value is not an integer") != REDIS_OK
) {
276 decrRefCount(current
);
279 decrRefCount(current
);
285 new = createStringObjectFromLongLong(value
);
286 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
287 hashTypeSet(o
,c
->argv
[2],new);
289 addReplyLongLong(c
,value
);
290 touchWatchedKey(c
->db
,c
->argv
[1]);
294 void hgetCommand(redisClient
*c
) {
296 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
297 checkType(c
,o
,REDIS_HASH
)) return;
299 if ((value
= hashTypeGet(o
,c
->argv
[2])) != NULL
) {
300 addReplyBulk(c
,value
);
303 addReply(c
,shared
.nullbulk
);
307 void hmgetCommand(redisClient
*c
) {
310 o
= lookupKeyRead(c
->db
,c
->argv
[1]);
311 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
312 addReply(c
,shared
.wrongtypeerr
);
315 /* Note the check for o != NULL happens inside the loop. This is
316 * done because objects that cannot be found are considered to be
317 * an empty hash. The reply should then be a series of NULLs. */
318 addReplySds(c
,sdscatprintf(sdsempty(),"*%d\r\n",c
->argc
-2));
319 for (i
= 2; i
< c
->argc
; i
++) {
320 if (o
!= NULL
&& (value
= hashTypeGet(o
,c
->argv
[i
])) != NULL
) {
321 addReplyBulk(c
,value
);
324 addReply(c
,shared
.nullbulk
);
329 void hdelCommand(redisClient
*c
) {
331 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
332 checkType(c
,o
,REDIS_HASH
)) return;
334 if (hashTypeDelete(o
,c
->argv
[2])) {
335 if (hashTypeLength(o
) == 0) dbDelete(c
->db
,c
->argv
[1]);
336 addReply(c
,shared
.cone
);
337 touchWatchedKey(c
->db
,c
->argv
[1]);
340 addReply(c
,shared
.czero
);
344 void hlenCommand(redisClient
*c
) {
346 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
347 checkType(c
,o
,REDIS_HASH
)) return;
349 addReplyUlong(c
,hashTypeLength(o
));
352 void genericHgetallCommand(redisClient
*c
, int flags
) {
353 robj
*o
, *lenobj
, *obj
;
354 unsigned long count
= 0;
355 hashTypeIterator
*hi
;
357 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
358 || checkType(c
,o
,REDIS_HASH
)) return;
360 lenobj
= createObject(REDIS_STRING
,NULL
);
362 decrRefCount(lenobj
);
364 hi
= hashTypeInitIterator(o
);
365 while (hashTypeNext(hi
) != REDIS_ERR
) {
366 if (flags
& REDIS_HASH_KEY
) {
367 obj
= hashTypeCurrent(hi
,REDIS_HASH_KEY
);
372 if (flags
& REDIS_HASH_VALUE
) {
373 obj
= hashTypeCurrent(hi
,REDIS_HASH_VALUE
);
379 hashTypeReleaseIterator(hi
);
381 lenobj
->ptr
= sdscatprintf(sdsempty(),"*%lu\r\n",count
);
384 void hkeysCommand(redisClient
*c
) {
385 genericHgetallCommand(c
,REDIS_HASH_KEY
);
388 void hvalsCommand(redisClient
*c
) {
389 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
392 void hgetallCommand(redisClient
*c
) {
393 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
396 void hexistsCommand(redisClient
*c
) {
398 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
399 checkType(c
,o
,REDIS_HASH
)) return;
401 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);