Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
b4eb9ccd
Commit
b4eb9ccd
authored
Apr 16, 2010
by
antirez
Browse files
Merge branch 'hash-refactor' of
git://github.com/pietern/redis
parents
58e1c9c1
b6f07345
Changes
3
Show whitespace changes
Inline
Side-by-side
redis.c
View file @
b4eb9ccd
...
...
@@ -705,6 +705,7 @@ static void substrCommand(redisClient *c);
static
void
zrankCommand
(
redisClient
*
c
);
static
void
zrevrankCommand
(
redisClient
*
c
);
static
void
hsetCommand
(
redisClient
*
c
);
static
void
hsetnxCommand
(
redisClient
*
c
);
static
void
hgetCommand
(
redisClient
*
c
);
static
void
hmsetCommand
(
redisClient
*
c
);
static
void
hmgetCommand
(
redisClient
*
c
);
...
...
@@ -783,6 +784,7 @@ static struct redisCommand cmdTable[] = {
{
"zrank"
,
zrankCommand
,
3
,
REDIS_CMD_BULK
,
NULL
,
1
,
1
,
1
},
{
"zrevrank"
,
zrevrankCommand
,
3
,
REDIS_CMD_BULK
,
NULL
,
1
,
1
,
1
},
{
"hset"
,
hsetCommand
,
4
,
REDIS_CMD_BULK
|
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"hsetnx"
,
hsetnxCommand
,
4
,
REDIS_CMD_BULK
|
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"hget"
,
hgetCommand
,
3
,
REDIS_CMD_BULK
,
NULL
,
1
,
1
,
1
},
{
"hmset"
,
hmsetCommand
,
-
4
,
REDIS_CMD_BULK
|
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"hmget"
,
hmgetCommand
,
-
3
,
REDIS_CMD_BULK
,
NULL
,
1
,
1
,
1
},
...
...
@@ -6042,325 +6044,332 @@ static void zrevrankCommand(redisClient *c) {
zrankGenericCommand
(
c
,
1
);
}
/* =================================== Hashes =============================== */
static void hsetCommand(redisClient *c) {
int update = 0;
robj *o = lookupKeyWrite(c->db,c->argv[1]);
/* ========================= Hashes utility functions ======================= */
#define REDIS_HASH_KEY 1
#define REDIS_HASH_VALUE 2
if (o == NULL) {
o = createHashObject();
dictAdd(c->db->dict,c->argv[1],o);
incrRefCount(c->argv[1]);
} else {
if (o->type != REDIS_HASH) {
addReply(c,shared.wrongtypeerr);
/* Check the length of a number of objects to see if we need to convert a
* zipmap to a real hash. Note that we only check string encoded objects
* as their string length can be queried in constant time. */
static
void
hashTryConversion
(
robj
*
subject
,
robj
**
argv
,
int
start
,
int
end
)
{
int
i
;
if
(
subject
->
encoding
!=
REDIS_ENCODING_ZIPMAP
)
return
;
for
(
i
=
start
;
i
<=
end
;
i
++
)
{
if
(
argv
[
i
]
->
encoding
==
REDIS_ENCODING_RAW
&&
sdslen
(
argv
[
i
]
->
ptr
)
>
server
.
hash_max_zipmap_value
)
{
convertToRealHash
(
subject
);
return
;
}
}
/* We want to convert the zipmap into an hash table right now if the
* entry to be added is too big. Note that we check if the object
* is integer encoded before to try fetching the length in the test below.
* This is because integers are small, but currently stringObjectLen()
* performs a slow conversion: not worth it. */
if (o->encoding == REDIS_ENCODING_ZIPMAP &&
((c->argv[2]->encoding == REDIS_ENCODING_RAW &&
sdslen(c->argv[2]->ptr) > server.hash_max_zipmap_value) ||
(c->argv[3]->encoding == REDIS_ENCODING_RAW &&
sdslen(c->argv[3]->ptr) > server.hash_max_zipmap_value)))
{
convertToRealHash(o);
}
}
/* Get the value from a hash identified by key. Returns either a string
* object or NULL if the value cannot be found. The refcount of the object
* is always increased by 1 when the value was found. */
static
robj
*
hashGet
(
robj
*
o
,
robj
*
key
)
{
robj
*
value
=
NULL
;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
unsigned char *zm = o->ptr;
robj *valobj = getDecodedObject(c->argv[3]);
zm = zipmapSet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
valobj->ptr,sdslen(valobj->ptr),&update);
decrRefCount(valobj);
o->ptr = zm;
/* And here there is the second check for hash conversion. */
if (zipmapLen(zm) > server.hash_max_zipmap_entries)
convertToRealHash(o);
} else {
c->argv[2] = tryObjectEncoding(c->argv[2]);
/* note that c->argv[3] is already encoded, as the latest arg
* of a bulk command is always integer encoded if possible. */
if (dictReplace(o->ptr,c->argv[2],c->argv[3])) {
incrRefCount(c->argv[2]);
unsigned
char
*
v
;
unsigned
int
vlen
;
key
=
getDecodedObject
(
key
);
if
(
zipmapGet
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
),
&
v
,
&
vlen
))
{
value
=
createStringObject
((
char
*
)
v
,
vlen
);
}
decrRefCount
(
key
);
}
else
{
update = 1;
dictEntry
*
de
=
dictFind
(
o
->
ptr
,
key
);
if
(
de
!=
NULL
)
{
value
=
dictGetEntryVal
(
de
);
incrRefCount
(
value
);
}
incrRefCount(c->argv[3]);
}
server.dirty++;
addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",update == 0));
return
value
;
}
static void hmsetCommand(redisClient *c) {
int i;
robj *o, *key, *val;
if ((c->argc % 2) == 1) {
addReplySds(c,sdsnew("-ERR wrong number of arguments for HMSET\r\n"));
return;
/* Test if the key exists in the given hash. Returns 1 if the key
* exists and 0 when it doesn't. */
static
int
hashExists
(
robj
*
o
,
robj
*
key
)
{
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
key
=
getDecodedObject
(
key
);
if
(
zipmapExists
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
)))
{
decrRefCount
(
key
);
return
1
;
}
if ((o = lookupKeyWrite(c->db,c->argv[1])) == NULL) {
o = createHashObject();
dictAdd(c->db->dict,c->argv[1],o);
incrRefCount(c->argv[1]);
decrRefCount
(
key
);
}
else
{
if (o->type != REDIS_HASH) {
addReply(c,shared.wrongtypeerr);
return;
if
(
dictFind
(
o
->
ptr
,
key
)
!=
NULL
)
{
return
1
;
}
}
return
0
;
}
/* We want to convert the zipmap into an hash table right now if the
* entry to be added is too big. */
/* Add an element, discard the old if the key already exists.
* Return 0 on insert and 1 on update. */
static
int
hashSet
(
robj
*
o
,
robj
*
key
,
robj
*
value
)
{
int
update
=
0
;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
for (i = 2; i < c->argc; i+=2) {
if ((c->argv[i]->encoding == REDIS_ENCODING_RAW &&
sdslen(c->argv[i]->ptr) > server.hash_max_zipmap_value) ||
(c->argv[i+1]->encoding == REDIS_ENCODING_RAW &&
sdslen(c->argv[i+1]->ptr) > server.hash_max_zipmap_value)) {
key
=
getDecodedObject
(
key
);
value
=
getDecodedObject
(
value
);
o
->
ptr
=
zipmapSet
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
),
value
->
ptr
,
sdslen
(
value
->
ptr
),
&
update
);
decrRefCount
(
key
);
decrRefCount
(
value
);
/* Check if the zipmap needs to be upgraded to a real hash table */
if
(
zipmapLen
(
o
->
ptr
)
>
server
.
hash_max_zipmap_entries
)
convertToRealHash
(
o
);
break;
}
}
else
{
value
=
tryObjectEncoding
(
value
);
if
(
dictReplace
(
o
->
ptr
,
key
,
value
))
{
/* Insert */
incrRefCount
(
key
);
}
else
{
/* Update */
update
=
1
;
}
incrRefCount
(
value
);
}
return
update
;
}
/* Delete an element from a hash.
* Return 1 on deleted and 0 on not found. */
static
int
hashDelete
(
robj
*
o
,
robj
*
key
)
{
int
deleted
=
0
;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
unsigned char *zm = o->ptr;
for (i = 2; i < c->argc; i+=2) {
key = getDecodedObject(c->argv[i]);
val = getDecodedObject(c->argv[i+1]);
zm = zipmapSet(zm,key->ptr,sdslen(key->ptr),
val->ptr,sdslen(val->ptr),NULL);
key
=
getDecodedObject
(
key
);
o
->
ptr
=
zipmapDel
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
),
&
deleted
);
decrRefCount
(
key
);
decrRefCount(val);
o->ptr = zm;
}
else
{
deleted
=
dictDelete
((
dict
*
)
o
->
ptr
,
key
)
==
DICT_OK
;
/* Always check if the dictionary needs a resize after a delete. */
if
(
deleted
&&
htNeedsResize
(
o
->
ptr
))
dictResize
(
o
->
ptr
);
}
return
deleted
;
}
/* And here there is the second check for hash conversion. */
if (zipmapLen(zm) > server.hash_max_zipmap_entries)
convertToRealHash(o);
/* Return the number of elements in a hash. */
static
unsigned
long
hashLength
(
robj
*
o
)
{
return
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
?
zipmapLen
((
unsigned
char
*
)
o
->
ptr
)
:
dictSize
((
dict
*
)
o
->
ptr
);
}
/* Structure to hold hash iteration abstration. Note that iteration over
* hashes involves both fields and values. Because it is possible that
* not both are required, store pointers in the iterator to avoid
* unnecessary memory allocation for fields/values. */
typedef
struct
{
int
encoding
;
unsigned
char
*
zi
;
unsigned
char
*
zk
,
*
zv
;
unsigned
int
zklen
,
zvlen
;
dictIterator
*
di
;
dictEntry
*
de
;
}
hashIterator
;
static
hashIterator
*
hashInitIterator
(
robj
*
subject
)
{
hashIterator
*
hi
=
zmalloc
(
sizeof
(
hashIterator
));
hi
->
encoding
=
subject
->
encoding
;
if
(
hi
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
hi
->
zi
=
zipmapRewind
(
subject
->
ptr
);
}
else
if
(
hi
->
encoding
==
REDIS_ENCODING_HT
)
{
hi
->
di
=
dictGetIterator
(
subject
->
ptr
);
}
else
{
for (i = 2; i < c->argc; i+=2) {
key = tryObjectEncoding(c->argv[i]);
val = tryObjectEncoding(c->argv[i+1]);
if (dictReplace(o->ptr,key,val)) {
incrRefCount(key);
}
incrRefCount(val);
redisAssert
(
NULL
);
}
return
hi
;
}
static
void
hashReleaseIterator
(
hashIterator
*
hi
)
{
if
(
hi
->
encoding
==
REDIS_ENCODING_HT
)
{
dictReleaseIterator
(
hi
->
di
);
}
zfree
(
hi
);
}
addReply(c, shared.ok);
/* Move to the next entry in the hash. Return REDIS_OK when the next entry
* could be found and REDIS_ERR when the iterator reaches the end. */
static
int
hashNext
(
hashIterator
*
hi
)
{
if
(
hi
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
if
((
hi
->
zi
=
zipmapNext
(
hi
->
zi
,
&
hi
->
zk
,
&
hi
->
zklen
,
&
hi
->
zv
,
&
hi
->
zvlen
))
==
NULL
)
return
REDIS_ERR
;
}
else
{
if
((
hi
->
de
=
dictNext
(
hi
->
di
))
==
NULL
)
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
static void hincrbyCommand(redisClient *c) {
long long value = 0, incr = 0;
robj *o = lookupKeyWrite(c->db,c->argv[1]);
/* Get key or value object at current iteration position.
* This increases the refcount of the field object by 1. */
static
robj
*
hashCurrent
(
hashIterator
*
hi
,
int
what
)
{
robj
*
o
;
if
(
hi
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
if
(
what
&
REDIS_HASH_KEY
)
{
o
=
createStringObject
((
char
*
)
hi
->
zk
,
hi
->
zklen
);
}
else
{
o
=
createStringObject
((
char
*
)
hi
->
zv
,
hi
->
zvlen
);
}
}
else
{
if
(
what
&
REDIS_HASH_KEY
)
{
o
=
dictGetEntryKey
(
hi
->
de
);
}
else
{
o
=
dictGetEntryVal
(
hi
->
de
);
}
incrRefCount
(
o
);
}
return
o
;
}
static
robj
*
hashLookupWriteOrCreate
(
redisClient
*
c
,
robj
*
key
)
{
robj
*
o
=
lookupKeyWrite
(
c
->
db
,
key
);
if
(
o
==
NULL
)
{
o
=
createHashObject
();
dictAdd(c->db->dict,
c->argv[1]
,o);
incrRefCount(
c->argv[1]
);
dictAdd
(
c
->
db
->
dict
,
key
,
o
);
incrRefCount
(
key
);
}
else
{
if
(
o
->
type
!=
REDIS_HASH
)
{
addReply
(
c
,
shared
.
wrongtypeerr
);
return;
return
NULL
;
}
}
return
o
;
}
if (getLongLongFromObject(c, c->argv[3], &incr) != REDIS_OK) return;
/* ============================= Hash commands ============================== */
static
void
hsetCommand
(
redisClient
*
c
)
{
int
update
;
robj
*
o
;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned char *zm = o->ptr;
unsigned char *zval;
unsigned int zvlen;
if
((
o
=
hashLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
hashTryConversion
(
o
,
c
->
argv
,
2
,
3
);
update
=
hashSet
(
o
,
c
->
argv
[
2
],
c
->
argv
[
3
]);
addReply
(
c
,
update
?
shared
.
czero
:
shared
.
cone
);
server
.
dirty
++
;
}
static
void
hsetnxCommand
(
redisClient
*
c
)
{
robj
*
o
;
if
((
o
=
hashLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
hashTryConversion
(
o
,
c
->
argv
,
2
,
3
);
/* Find value if already present in hash */
if (zipmapGet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
&zval,&zvlen)) {
/* strtoll needs the char* to have a trailing \0, but
* the zipmap doesn't include them. */
sds szval = sdsnewlen(zval, zvlen);
value = strtoll(szval,NULL,10);
sdsfree(szval);
if
(
hashExists
(
o
,
c
->
argv
[
2
]))
{
addReply
(
c
,
shared
.
czero
);
}
else
{
hashSet
(
o
,
c
->
argv
[
2
],
c
->
argv
[
3
]);
addReply
(
c
,
shared
.
cone
);
server
.
dirty
++
;
}
}
value += incr;
sds svalue = sdscatprintf(sdsempty(),"%lld",value);
zm = zipmapSet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
(unsigned char*)svalue,sdslen(svalue),NULL);
sdsfree(svalue);
o->ptr = zm;
static
void
hmsetCommand
(
redisClient
*
c
)
{
int
i
;
robj
*
o
;
/* Check if the zipmap needs to be converted. */
if (zipmapLen(zm) > server.hash_max_zipmap_entries)
convertToRealHash(o);
} else {
robj *hval;
dictEntry *de;
if
((
c
->
argc
%
2
)
==
1
)
{
addReplySds
(
c
,
sdsnew
(
"-ERR wrong number of arguments for HMSET
\r\n
"
));
return
;
}
/* Find value if already present in hash */
de = dictFind(o->ptr,c->argv[2]);
if (de != NULL) {
hval = dictGetEntryVal(de);
if (hval->encoding == REDIS_ENCODING_RAW)
value = strtoll(hval->ptr,NULL,10);
else if (hval->encoding == REDIS_ENCODING_INT)
value = (long)hval->ptr;
if
((
o
=
hashLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
hashTryConversion
(
o
,
c
->
argv
,
2
,
c
->
argc
-
1
);
for
(
i
=
2
;
i
<
c
->
argc
;
i
+=
2
)
{
hashSet
(
o
,
c
->
argv
[
i
],
c
->
argv
[
i
+
1
]);
}
addReply
(
c
,
shared
.
ok
);
}
static
void
hincrbyCommand
(
redisClient
*
c
)
{
long
long
value
,
incr
;
robj
*
o
,
*
current
,
*
new
;
if
(
getLongLongFromObject
(
c
,
c
->
argv
[
3
],
&
incr
)
!=
REDIS_OK
)
return
;
if
((
o
=
hashLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
if
((
current
=
hashGet
(
o
,
c
->
argv
[
2
]))
!=
NULL
)
{
if
(
current
->
encoding
==
REDIS_ENCODING_RAW
)
value
=
strtoll
(
current
->
ptr
,
NULL
,
10
);
else
if
(
current
->
encoding
==
REDIS_ENCODING_INT
)
value
=
(
long
)
current
->
ptr
;
else
redisAssert
(
1
!=
1
);
decrRefCount
(
current
);
}
else
{
value
=
0
;
}
value
+=
incr
;
hval = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
hval = tryObjectEncoding(hval);
if (dictReplace(o->ptr,c->argv[2],hval)) {
incrRefCount(c->argv[2]);
}
}
new
=
createObject
(
REDIS_STRING
,
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
));
hashSet
(
o
,
c
->
argv
[
2
],
new
);
decrRefCount
(
new
);
addReplyLongLong
(
c
,
value
);
server
.
dirty
++
;
addReplyLongLong(c, value);
}
static
void
hgetCommand
(
redisClient
*
c
)
{
robj *o;
robj
*
o
,
*
value
;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
nullbulk
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_HASH
))
return
;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned char *zm = o->ptr;
unsigned char *val;
unsigned int vlen;
robj *field;
field = getDecodedObject(c->argv[2]);
if (zipmapGet(zm,field->ptr,sdslen(field->ptr), &val,&vlen)) {
addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen));
addReplySds(c,sdsnewlen(val,vlen));
addReply(c,shared.crlf);
decrRefCount(field);
return;
} else {
addReply(c,shared.nullbulk);
decrRefCount(field);
return;
}
if
((
value
=
hashGet
(
o
,
c
->
argv
[
2
]))
!=
NULL
)
{
addReplyBulk
(
c
,
value
);
decrRefCount
(
value
);
}
else
{
struct dictEntry *de;
de = dictFind(o->ptr,c->argv[2]);
if (de == NULL) {
addReply
(
c
,
shared
.
nullbulk
);
} else {
robj *e = dictGetEntryVal(de);
addReplyBulk(c,e);
}
}
}
static
void
hmgetCommand
(
redisClient
*
c
)
{
int
i
;
robj *o = lookupKeyRead(c->db, c->argv[1]);
if (o == NULL) {
addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2));
for (i = 2; i < c->argc; i++) {
addReply(c,shared.nullbulk);
}
return;
} else {
if (o->type != REDIS_HASH) {
robj
*
o
,
*
value
;
o
=
lookupKeyRead
(
c
->
db
,
c
->
argv
[
1
]);
if
(
o
!=
NULL
&&
o
->
type
!=
REDIS_HASH
)
{
addReply
(
c
,
shared
.
wrongtypeerr
);
return;
}
}
/* Note the check for o != NULL happens inside the loop. This is
* done because objects that cannot be found are considered to be
* an empty hash. The reply should then be a series of NULLs. */
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
"*%d
\r\n
"
,
c
->
argc
-
2
));
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned char *zm = o->ptr;
unsigned char *v;
unsigned int vlen;
robj *field;
for (i = 2; i < c->argc; i++) {
field = getDecodedObject(c->argv[i]);
if (zipmapGet(zm,field->ptr,sdslen(field->ptr),&v,&vlen)) {
addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen));
addReplySds(c,sdsnewlen(v,vlen));
addReply(c,shared.crlf);
} else {
addReply(c,shared.nullbulk);
}
decrRefCount(field);
}
} else {
dictEntry *de;
for
(
i
=
2
;
i
<
c
->
argc
;
i
++
)
{
de = dictFind(o->ptr,c->argv[i]);
if (de != NULL) {
addReplyBulk(c,(robj*)dictGetEntryVal(de)
);
if
(
o
!=
NULL
&&
(
value
=
hashGet
(
o
,
c
->
argv
[
i
]))
!=
NULL
)
{
addReplyBulk
(
c
,
value
);
decrRefCount
(
value
);
}
else
{
addReply
(
c
,
shared
.
nullbulk
);
}
}
}
}
static
void
hdelCommand
(
redisClient
*
c
)
{
robj
*
o
;
int deleted = 0;
if
((
o
=
lookupKeyWriteOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_HASH
))
return
;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
robj *field = getDecodedObject(c->argv[2]);
o->ptr = zipmapDel((unsigned char*) o->ptr,
(unsigned char*) field->ptr,
sdslen(field->ptr), &deleted);
decrRefCount(field);
if (zipmapLen((unsigned char*) o->ptr) == 0)
deleteKey(c->db,c->argv[1]);
if
(
hashDelete
(
o
,
c
->
argv
[
2
]))
{
if
(
hashLength
(
o
)
==
0
)
deleteKey
(
c
->
db
,
c
->
argv
[
1
]);
addReply
(
c
,
shared
.
cone
);
server
.
dirty
++
;
}
else
{
deleted = dictDelete((dict*)o->ptr,c->argv[2]) == DICT_OK;
if (htNeedsResize(o->ptr)) dictResize(o->ptr);
if (dictSize((dict*)o->ptr) == 0) deleteKey(c->db,c->argv[1]);
addReply
(
c
,
shared
.
czero
);
}
if (deleted) server.dirty++;
addReply(c,deleted ? shared.cone : shared.czero);
}
static
void
hlenCommand
(
redisClient
*
c
)
{
robj
*
o
;
unsigned long len;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_HASH
))
return
;
len = (o->encoding == REDIS_ENCODING_ZIPMAP) ?
zipmapLen((unsigned char*)o->ptr) : dictSize((dict*)o->ptr);
addReplyUlong(c,len);
addReplyUlong
(
c
,
hashLength
(
o
));
}
#define REDIS_GETALL_KEYS 1
#define REDIS_GETALL_VALS 2
static
void
genericHgetallCommand
(
redisClient
*
c
,
int
flags
)
{
robj *o, *lenobj;
robj
*
o
,
*
lenobj
,
*
obj
;
unsigned
long
count
=
0
;
hashIterator
*
hi
;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
emptymultibulk
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_HASH
))
return
;
...
...
@@ -6369,79 +6378,44 @@ static void genericHgetallCommand(redisClient *c, int flags) {
addReply
(
c
,
lenobj
);
decrRefCount
(
lenobj
);
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned char *p = zipmapRewind(o->ptr);
unsigned char *field, *val;
unsigned int flen, vlen;
while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
robj *aux;
if (flags & REDIS_GETALL_KEYS) {
aux = createStringObject((char*)field,flen);
addReplyBulk(c,aux);
decrRefCount(aux);
hi
=
hashInitIterator
(
o
);
while
(
hashNext
(
hi
)
!=
REDIS_ERR
)
{
if
(
flags
&
REDIS_HASH_KEY
)
{
obj
=
hashCurrent
(
hi
,
REDIS_HASH_KEY
);
addReplyBulk
(
c
,
obj
);
decrRefCount
(
obj
);
count
++
;
}
if (flags & REDIS_
GETALL
_VAL
S
) {
aux = createStringObject((char*)val,vlen
);
addReplyBulk(c,
aux
);
decrRefCount(
aux
);
if
(
flags
&
REDIS_
HASH
_VAL
UE
)
{
obj
=
hashCurrent
(
hi
,
REDIS_HASH_VALUE
);
addReplyBulk
(
c
,
obj
);
decrRefCount
(
obj
);
count
++
;
}
}
} else {
dictIterator *di = dictGetIterator(o->ptr);
dictEntry *de;
while((de = dictNext(di)) != NULL) {
robj *fieldobj = dictGetEntryKey(de);
robj *valobj = dictGetEntryVal(de);
hashReleaseIterator
(
hi
);
if (flags & REDIS_GETALL_KEYS) {
addReplyBulk(c,fieldobj);
count++;
}
if (flags & REDIS_GETALL_VALS) {
addReplyBulk(c,valobj);
count++;
}
}
dictReleaseIterator(di);
}
lenobj
->
ptr
=
sdscatprintf
(
sdsempty
(),
"*%lu
\r\n
"
,
count
);
}
static
void
hkeysCommand
(
redisClient
*
c
)
{
genericHgetallCommand(c,REDIS_
GETALL
_KEY
S
);
genericHgetallCommand
(
c
,
REDIS_
HASH
_KEY
);
}
static
void
hvalsCommand
(
redisClient
*
c
)
{
genericHgetallCommand(c,REDIS_
GETALL
_VAL
S
);
genericHgetallCommand
(
c
,
REDIS_
HASH
_VAL
UE
);
}
static
void
hgetallCommand
(
redisClient
*
c
)
{
genericHgetallCommand(c,REDIS_
GETALL
_KEY
S
|REDIS_
GETALL
_VAL
S
);
genericHgetallCommand
(
c
,
REDIS_
HASH
_KEY
|
REDIS_
HASH
_VAL
UE
);
}
static
void
hexistsCommand
(
redisClient
*
c
)
{
robj
*
o
;
int exists = 0;
if
((
o
=
lookupKeyReadOrReply
(
c
,
c
->
argv
[
1
],
shared
.
czero
))
==
NULL
||
checkType
(
c
,
o
,
REDIS_HASH
))
return
;
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
robj *field;
unsigned char *zm = o->ptr;
field = getDecodedObject(c->argv[2]);
exists = zipmapExists(zm,field->ptr,sdslen(field->ptr));
decrRefCount(field);
} else {
exists = dictFind(o->ptr,c->argv[2]) != NULL;
}
addReply(c,exists ? shared.cone : shared.czero);
addReply
(
c
,
hashExists
(
o
,
c
->
argv
[
2
])
?
shared
.
cone
:
shared
.
czero
);
}
static
void
convertToRealHash
(
robj
*
o
)
{
...
...
@@ -6495,16 +6469,16 @@ static redisSortOperation *createSortOperation(int type, robj *pattern) {
/* Return the value associated to the key with a name obtained
* substituting the first occurence of '*' in 'pattern' with 'subst' */
static
robj
*
lookupKeyByPattern
(
redisDb
*
db
,
robj
*
pattern
,
robj
*
subst
)
{
char *p;
char
*
p
,
*
f
;
sds
spat
,
ssub
;
robj keyobj;
int prefixlen, sublen, postfixlen;
robj
keyobj
,
fieldobj
,
*
o
;
int
prefixlen
,
sublen
,
postfixlen
,
fieldlen
;
/* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
struct
{
long
len
;
long
free
;
char
buf
[
REDIS_SORTKEY_MAX
+
1
];
} keyname;
}
keyname
,
fieldname
;
/* If the pattern is "#" return the substitution object itself in order
* to implement the "SORT ... GET #" feature. */
...
...
@@ -6526,20 +6500,49 @@ static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
return
NULL
;
}
/* Find out if we're dealing with a hash dereference. */
if
((
f
=
strstr
(
p
+
1
,
"->"
))
!=
NULL
)
{
fieldlen
=
sdslen
(
spat
)
-
(
f
-
spat
);
/* this also copies \0 character */
memcpy
(
fieldname
.
buf
,
f
+
2
,
fieldlen
-
1
);
fieldname
.
len
=
fieldlen
-
2
;
}
else
{
fieldlen
=
0
;
}
prefixlen
=
p
-
spat
;
sublen
=
sdslen
(
ssub
);
postfixlen = sdslen(spat)-(prefixlen+1);
postfixlen
=
sdslen
(
spat
)
-
(
prefixlen
+
1
)
-
fieldlen
;
memcpy
(
keyname
.
buf
,
spat
,
prefixlen
);
memcpy
(
keyname
.
buf
+
prefixlen
,
ssub
,
sublen
);
memcpy
(
keyname
.
buf
+
prefixlen
+
sublen
,
p
+
1
,
postfixlen
);
keyname
.
buf
[
prefixlen
+
sublen
+
postfixlen
]
=
'\0'
;
keyname
.
len
=
prefixlen
+
sublen
+
postfixlen
;
initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(long)*2))
decrRefCount
(
subst
);
/* printf("lookup '%s' => %p\n", keyname.buf,de); */
return lookupKeyRead(db,&keyobj);
/* Lookup substituted key */
initStaticStringObject
(
keyobj
,((
char
*
)
&
keyname
)
+
(
sizeof
(
long
)
*
2
));
o
=
lookupKeyRead
(
db
,
&
keyobj
);
if
(
o
!=
NULL
&&
fieldlen
>
0
)
{
/* Retrieve value from hash by the field name. This operation
* already increases the refcount of the returned object. */
if
(
o
->
type
!=
REDIS_HASH
||
fieldname
.
len
<
1
)
{
return
NULL
;
}
initStaticStringObject
(
fieldobj
,((
char
*
)
&
fieldname
)
+
(
sizeof
(
long
)
*
2
));
o
=
hashGet
(
o
,
&
fieldobj
);
}
else
{
if
(
o
->
type
!=
REDIS_STRING
)
{
return
NULL
;
}
/* Every object that this function returns needs to have its refcount
* increased. sortCommand decreases it again. */
incrRefCount
(
o
);
}
return
o
;
}
/* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
...
...
@@ -6574,14 +6577,9 @@ static int sortCompare(const void *s1, const void *s2) {
cmp
=
strcoll
(
so1
->
u
.
cmpobj
->
ptr
,
so2
->
u
.
cmpobj
->
ptr
);
}
}
else
{
/* Compare elements directly */
robj *dec1, *dec2;
dec1 = getDecodedObject(so1->obj);
dec2 = getDecodedObject(so2->obj);
cmp = strcoll(dec1->ptr,dec2->ptr);
decrRefCount(dec1);
decrRefCount(dec2);
/* Compare elements directly. Note that these objects already
* need to be non-encoded (see sortCommand). */
cmp
=
strcoll
(
so1
->
obj
->
ptr
,
so2
->
obj
->
ptr
);
}
}
return
server
.
sort_desc
?
-
cmp
:
cmp
;
...
...
@@ -6708,37 +6706,35 @@ static void sortCommand(redisClient *c) {
/* Now it's time to load the right scores in the sorting vector */
if
(
dontsort
==
0
)
{
for
(
j
=
0
;
j
<
vectorlen
;
j
++
)
{
if (sortby) {
robj
*
byval
;
if
(
sortby
)
{
/* lookup value to sort by */
byval
=
lookupKeyByPattern
(
c
->
db
,
sortby
,
vector
[
j
].
obj
);
if (!byval || byval->type != REDIS_STRING) continue;
if
(
!
byval
)
continue
;
}
else
{
/* use object itself to sort by */
byval
=
vector
[
j
].
obj
;
}
if
(
alpha
)
{
vector
[
j
].
u
.
cmpobj
=
getDecodedObject
(
byval
);
}
else
{
if
(
byval
->
encoding
==
REDIS_ENCODING_RAW
)
{
vector
[
j
].
u
.
score
=
strtod
(
byval
->
ptr
,
NULL
);
} else {
}
else
if
(
byval
->
encoding
==
REDIS_ENCODING_INT
)
{
/* Don't need to decode the object if it's
* integer-encoded (the only encoding supported) so
* far. We can just cast it */
if (byval->encoding == REDIS_ENCODING_INT) {
vector
[
j
].
u
.
score
=
(
long
)
byval
->
ptr
;
} else
redisAssert(1 != 1);
}
}
}
else
{
if (!alpha) {
if (vector[j].obj->encoding == REDIS_ENCODING_RAW)
vector[j].u.score = strtod(vector[j].obj->ptr,NULL);
else {
if (vector[j].obj->encoding == REDIS_ENCODING_INT)
vector[j].u.score = (long) vector[j].obj->ptr;
else
redisAssert
(
1
!=
1
);
}
}
/* when the object was retrieved using lookupKeyByPattern,
* its refcount needs to be decreased. */
if
(
sortby
)
{
decrRefCount
(
byval
);
}
}
}
...
...
@@ -6836,7 +6832,7 @@ static void sortCommand(redisClient *c) {
decrRefCount
(
sortval
);
listRelease
(
operations
);
for
(
j
=
0
;
j
<
vectorlen
;
j
++
)
{
if (
sortby &&
alpha && vector[j].u.cmpobj)
if
(
alpha
&&
vector
[
j
].
u
.
cmpobj
)
decrRefCount
(
vector
[
j
].
u
.
cmpobj
);
}
zfree
(
vector
);
...
...
redis.tcl
View file @
b4eb9ccd
...
...
@@ -46,7 +46,7 @@ foreach redis_bulk_cmd {
# Flag commands requiring last argument as a bulk write operation
foreach redis_multibulk_cmd
{
mset msetnx hset hmset hmget
mset msetnx hset
hsetnx
hmset hmget
}
{
set ::redis::multibulkarg
(
$redis
_multibulk_cmd
)
{}
}
...
...
test-redis.tcl
View file @
b4eb9ccd
...
...
@@ -1652,6 +1652,30 @@ proc main {} {
set _ $rv
}
{
0 newval1 1 0 newval2 1 1 1
}
test
{
HSETNX target key missing - small hash
}
{
$r hsetnx smallhash __123123123__ foo
$r hget smallhash __123123123__
}
{
foo
}
test
{
HSETNX target key exists - small hash
}
{
$r hsetnx smallhash __123123123__ bar
set result
[
$r
hget smallhash __123123123__
]
$r hdel smallhash __123123123__
set _ $result
}
{
foo
}
test
{
HSETNX target key missing - big hash
}
{
$r hsetnx bighash __123123123__ foo
$r hget bighash __123123123__
}
{
foo
}
test
{
HSETNX target key exists - big hash
}
{
$r hsetnx bighash __123123123__ bar
set result
[
$r
hget bighash __123123123__
]
$r hdel bighash __123123123__
set _ $result
}
{
foo
}
test
{
HMSET wrong number of args
}
{
catch
{
$r
hmset smallhash key1 val1 key2
}
err
format $err
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment