]>
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
);
230 void hsetnxCommand(redisClient
*c
) {
232 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
233 hashTypeTryConversion(o
,c
->argv
,2,3);
235 if (hashTypeExists(o
, c
->argv
[2])) {
236 addReply(c
, shared
.czero
);
238 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
239 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
240 addReply(c
, shared
.cone
);
245 void hmsetCommand(redisClient
*c
) {
249 if ((c
->argc
% 2) == 1) {
250 addReplySds(c
,sdsnew("-ERR wrong number of arguments for HMSET\r\n"));
254 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
255 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
256 for (i
= 2; i
< c
->argc
; i
+= 2) {
257 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
258 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
260 addReply(c
, shared
.ok
);
264 void hincrbyCommand(redisClient
*c
) {
265 long long value
, incr
;
266 robj
*o
, *current
, *new;
268 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
269 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
270 if ((current
= hashTypeGet(o
,c
->argv
[2])) != NULL
) {
271 if (getLongLongFromObjectOrReply(c
,current
,&value
,
272 "hash value is not an integer") != REDIS_OK
) {
273 decrRefCount(current
);
276 decrRefCount(current
);
282 new = createStringObjectFromLongLong(value
);
283 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
284 hashTypeSet(o
,c
->argv
[2],new);
286 addReplyLongLong(c
,value
);
290 void hgetCommand(redisClient
*c
) {
292 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
293 checkType(c
,o
,REDIS_HASH
)) return;
295 if ((value
= hashTypeGet(o
,c
->argv
[2])) != NULL
) {
296 addReplyBulk(c
,value
);
299 addReply(c
,shared
.nullbulk
);
303 void hmgetCommand(redisClient
*c
) {
306 o
= lookupKeyRead(c
->db
,c
->argv
[1]);
307 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
308 addReply(c
,shared
.wrongtypeerr
);
311 /* Note the check for o != NULL happens inside the loop. This is
312 * done because objects that cannot be found are considered to be
313 * an empty hash. The reply should then be a series of NULLs. */
314 addReplySds(c
,sdscatprintf(sdsempty(),"*%d\r\n",c
->argc
-2));
315 for (i
= 2; i
< c
->argc
; i
++) {
316 if (o
!= NULL
&& (value
= hashTypeGet(o
,c
->argv
[i
])) != NULL
) {
317 addReplyBulk(c
,value
);
320 addReply(c
,shared
.nullbulk
);
325 void hdelCommand(redisClient
*c
) {
327 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
328 checkType(c
,o
,REDIS_HASH
)) return;
330 if (hashTypeDelete(o
,c
->argv
[2])) {
331 if (hashTypeLength(o
) == 0) dbDelete(c
->db
,c
->argv
[1]);
332 addReply(c
,shared
.cone
);
335 addReply(c
,shared
.czero
);
339 void hlenCommand(redisClient
*c
) {
341 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
342 checkType(c
,o
,REDIS_HASH
)) return;
344 addReplyUlong(c
,hashTypeLength(o
));
347 void genericHgetallCommand(redisClient
*c
, int flags
) {
348 robj
*o
, *lenobj
, *obj
;
349 unsigned long count
= 0;
350 hashTypeIterator
*hi
;
352 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
353 || checkType(c
,o
,REDIS_HASH
)) return;
355 lenobj
= createObject(REDIS_STRING
,NULL
);
357 decrRefCount(lenobj
);
359 hi
= hashTypeInitIterator(o
);
360 while (hashTypeNext(hi
) != REDIS_ERR
) {
361 if (flags
& REDIS_HASH_KEY
) {
362 obj
= hashTypeCurrent(hi
,REDIS_HASH_KEY
);
367 if (flags
& REDIS_HASH_VALUE
) {
368 obj
= hashTypeCurrent(hi
,REDIS_HASH_VALUE
);
374 hashTypeReleaseIterator(hi
);
376 lenobj
->ptr
= sdscatprintf(sdsempty(),"*%lu\r\n",count
);
379 void hkeysCommand(redisClient
*c
) {
380 genericHgetallCommand(c
,REDIS_HASH_KEY
);
383 void hvalsCommand(redisClient
*c
) {
384 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
387 void hgetallCommand(redisClient
*c
) {
388 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
391 void hexistsCommand(redisClient
*c
) {
393 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
394 checkType(c
,o
,REDIS_HASH
)) return;
396 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);