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);
...
@@ -705,6 +705,7 @@ static void substrCommand(redisClient *c);
static void zrankCommand(redisClient *c);
static void zrankCommand(redisClient *c);
static void zrevrankCommand(redisClient *c);
static void zrevrankCommand(redisClient *c);
static void hsetCommand(redisClient *c);
static void hsetCommand(redisClient *c);
static void hsetnxCommand(redisClient *c);
static void hgetCommand(redisClient *c);
static void hgetCommand(redisClient *c);
static void hmsetCommand(redisClient *c);
static void hmsetCommand(redisClient *c);
static void hmgetCommand(redisClient *c);
static void hmgetCommand(redisClient *c);
...
@@ -783,6 +784,7 @@ static struct redisCommand cmdTable[] = {
...
@@ -783,6 +784,7 @@ static struct redisCommand cmdTable[] = {
{"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"zrevrank",zrevrankCommand,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},
{"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},
{"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
{"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,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},
{"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1},
...
@@ -6042,325 +6044,332 @@ static void zrevrankCommand(redisClient *c) {
...
@@ -6042,325 +6044,332 @@ static void zrevrankCommand(redisClient *c) {
zrankGenericCommand(c, 1);
zrankGenericCommand(c, 1);
}
}
/* =================================== Hashes =============================== */
/* ========================= Hashes utility functions ======================= */
static
void
hsetCommand
(
redisClient
*
c
)
{
#define REDIS_HASH_KEY 1
int
update
=
0
;
#define REDIS_HASH_VALUE 2
robj
*
o
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]);
if
(
o
==
NULL
)
{
/* Check the length of a number of objects to see if we need to convert a
o
=
createHashObject
();
* zipmap to a real hash. Note that we only check string encoded objects
dictAdd
(
c
->
db
->
dict
,
c
->
argv
[
1
],
o
);
* as their string length can be queried in constant time. */
incrRefCount
(
c
->
argv
[
1
]);
static void hashTryConversion(robj *subject, robj **argv, int start, int end) {
}
else
{
int i;
if
(
o
->
type
!=
REDIS_HASH
)
{
if (subject->encoding != REDIS_ENCODING_ZIPMAP) return;
addReply
(
c
,
shared
.
wrongtypeerr
);
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;
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) {
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned
char
*
zm
=
o
->
ptr
;
unsigned char *v;
robj
*
valobj
=
getDecodedObject
(
c
->
argv
[
3
]);
unsigned int vlen;
key = getDecodedObject(key);
zm
=
zipmapSet
(
zm
,
c
->
argv
[
2
]
->
ptr
,
sdslen
(
c
->
argv
[
2
]
->
ptr
),
if (zipmapGet(o->ptr,key->ptr,sdslen(key->ptr),&v,&vlen)) {
valobj
->
ptr
,
sdslen
(
valobj
->
ptr
),
&
update
);
value = createStringObject((char*)v,vlen);
decrRefCount
(
valobj
);
}
o
->
ptr
=
zm
;
decrRefCount(key);
/* 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
]);
} else {
} else {
update
=
1
;
dictEntry *de = dictFind(o->ptr,key);
if (de != NULL) {
value = dictGetEntryVal(de);
incrRefCount(value);
}
}
incrRefCount
(
c
->
argv
[
3
]);
}
}
server
.
dirty
++
;
return value;
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
":%d
\r\n
"
,
update
==
0
));
}
}
static
void
hmsetCommand
(
redisClient
*
c
)
{
/* Test if the key exists in the given hash. Returns 1 if the key
int
i
;
* exists and 0 when it doesn't. */
robj
*
o
,
*
key
,
*
val
;
static int hashExists(robj *o, robj *key) {
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
if
((
c
->
argc
%
2
)
==
1
)
{
key = getDecodedObject(key);
addReplySds
(
c
,
sdsnew
(
"-ERR wrong number of arguments for HMSET
\r\n
"
));
if (zipmapExists(o->ptr,key->ptr,sdslen(key->ptr))) {
return
;
decrRefCount(key);
return 1;
}
}
decrRefCount(key);
if
((
o
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]))
==
NULL
)
{
o
=
createHashObject
();
dictAdd
(
c
->
db
->
dict
,
c
->
argv
[
1
],
o
);
incrRefCount
(
c
->
argv
[
1
]);
} else {
} else {
if
(
o
->
type
!=
REDIS_HASH
)
{
if (dictFind(o->ptr,key) != NULL) {
addReply
(
c
,
shared
.
wrongtypeerr
);
return 1;
return
;
}
}
}
}
return 0;
}
/* We want to convert the zipmap into an hash table right now if the
/* Add an element, discard the old if the key already exists.
* entry to be added is too big. */
* 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) {
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
for
(
i
=
2
;
i
<
c
->
argc
;
i
+=
2
)
{
key = getDecodedObject(key);
if
((
c
->
argv
[
i
]
->
encoding
==
REDIS_ENCODING_RAW
&&
value = getDecodedObject(value);
sdslen
(
c
->
argv
[
i
]
->
ptr
)
>
server
.
hash_max_zipmap_value
)
||
o->ptr = zipmapSet(o->ptr,
(
c
->
argv
[
i
+
1
]
->
encoding
==
REDIS_ENCODING_RAW
&&
key->ptr,sdslen(key->ptr),
sdslen
(
c
->
argv
[
i
+
1
]
->
ptr
)
>
server
.
hash_max_zipmap_value
))
{
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);
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) {
if (o->encoding == REDIS_ENCODING_ZIPMAP) {
unsigned
char
*
zm
=
o
->
ptr
;
key = getDecodedObject(key);
o->ptr = zipmapDel(o->ptr,key->ptr,sdslen(key->ptr), &deleted);
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
);
decrRefCount(key);
decrRefCount(key);
decrRefCount
(
val
);
} else {
o
->
ptr
=
zm
;
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. */
/* Return the number of elements in a hash. */
if
(
zipmapLen
(
zm
)
>
server
.
hash_max_zipmap_entries
)
static unsigned long hashLength(robj *o) {
convertToRealHash
(
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 {
} else {
for
(
i
=
2
;
i
<
c
->
argc
;
i
+=
2
)
{
redisAssert(NULL);
key
=
tryObjectEncoding
(
c
->
argv
[
i
]);
val
=
tryObjectEncoding
(
c
->
argv
[
i
+
1
]);
if
(
dictReplace
(
o
->
ptr
,
key
,
val
))
{
incrRefCount
(
key
);
}
incrRefCount
(
val
);
}
}
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
)
{
/* Get key or value object at current iteration position.
long
long
value
=
0
,
incr
=
0
;
* This increases the refcount of the field object by 1. */
robj
*
o
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
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) {
if (o == NULL) {
o = createHashObject();
o = createHashObject();
dictAdd
(
c
->
db
->
dict
,
c
->
argv
[
1
]
,
o
);
dictAdd(c->db->dict,
key
,o);
incrRefCount
(
c
->
argv
[
1
]
);
incrRefCount(
key
);
} else {
} else {
if (o->type != REDIS_HASH) {
if (o->type != REDIS_HASH) {
addReply(c,shared.wrongtypeerr);
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
)
{
if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
unsigned
char
*
zm
=
o
->
ptr
;
hashTryConversion(o,c->argv,2,3);
unsigned
char
*
zval
;
update = hashSet(o,c->argv[2],c->argv[3]);
unsigned
int
zvlen
;
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 (hashExists(o, c->argv[2])) {
if
(
zipmapGet
(
zm
,
c
->
argv
[
2
]
->
ptr
,
sdslen
(
c
->
argv
[
2
]
->
ptr
),
addReply(c, shared.czero);
&
zval
,
&
zvlen
))
{
} else {
/* strtoll needs the char* to have a trailing \0, but
hashSet(o,c->argv[2],c->argv[3]);
* the zipmap doesn't include them. */
addReply(c, shared.cone);
sds
szval
=
sdsnewlen
(
zval
,
zvlen
);
server.dirty++;
value
=
strtoll
(
szval
,
NULL
,
10
);
sdsfree
(
szval
);
}
}
}
value
+=
incr
;
static void hmsetCommand(redisClient *c) {
sds
svalue
=
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
);
int i;
zm
=
zipmapSet
(
zm
,
c
->
argv
[
2
]
->
ptr
,
sdslen
(
c
->
argv
[
2
]
->
ptr
),
robj *o;
(
unsigned
char
*
)
svalue
,
sdslen
(
svalue
),
NULL
);
sdsfree
(
svalue
);
o
->
ptr
=
zm
;
/* Check if the zipmap needs to be converted. */
if ((c->argc % 2) == 1) {
if
(
zipmapLen
(
zm
)
>
server
.
hash_max_zipmap_entries
)
addReplySds(c,sdsnew("-ERR wrong number of arguments for HMSET\r\n"));
convertToRealHash
(
o
);
return;
}
else
{
}
robj
*
hval
;
dictEntry
*
de
;
/* Find value if already present in hash */
if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
de
=
dictFind
(
o
->
ptr
,
c
->
argv
[
2
]);
hashTryConversion(o,c->argv,2,c->argc-1);
if
(
de
!=
NULL
)
{
for (i = 2; i < c->argc; i += 2) {
hval
=
dictGetEntryVal
(
de
);
hashSet(o,c->argv[i],c->argv[i+1]);
if
(
hval
->
encoding
==
REDIS_ENCODING_RAW
)
}
value
=
strtoll
(
hval
->
ptr
,
NULL
,
10
);
addReply(c, shared.ok);
else
if
(
hval
->
encoding
==
REDIS_ENCODING_INT
)
}
value
=
(
long
)
hval
->
ptr
;
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
else
redisAssert(1 != 1);
redisAssert(1 != 1);
decrRefCount(current);
} else {
value = 0;
}
}
value += incr;
value += incr;
hval
=
createObject
(
REDIS_STRING
,
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
));
new = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
hval
=
tryObjectEncoding
(
hval
);
hashSet(o,c->argv[2],new);
if
(
dictReplace
(
o
->
ptr
,
c
->
argv
[
2
],
hval
))
{
decrRefCount(new);
incrRefCount
(
c
->
argv
[
2
]);
addReplyLongLong(c,value);
}
}
server.dirty++;
server.dirty++;
addReplyLongLong
(
c
,
value
);
}
}
static void hgetCommand(redisClient *c) {
static void hgetCommand(redisClient *c) {
robj
*
o
;
robj *o, *value;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
checkType(c,o,REDIS_HASH)) return;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
if ((value = hashGet(o,c->argv[2])) != NULL) {
unsigned
char
*
zm
=
o
->
ptr
;
addReplyBulk(c,value);
unsigned
char
*
val
;
decrRefCount(value);
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
;
}
} else {
} else {
struct
dictEntry
*
de
;
de
=
dictFind
(
o
->
ptr
,
c
->
argv
[
2
]);
if
(
de
==
NULL
)
{
addReply(c,shared.nullbulk);
addReply(c,shared.nullbulk);
}
else
{
robj
*
e
=
dictGetEntryVal
(
de
);
addReplyBulk
(
c
,
e
);
}
}
}
}
}
static void hmgetCommand(redisClient *c) {
static void hmgetCommand(redisClient *c) {
int i;
int i;
robj *o, *value;
robj
*
o
=
lookupKeyRead
(
c
->
db
,
c
->
argv
[
1
]);
o = lookupKeyRead(c->db,c->argv[1]);
if
(
o
==
NULL
)
{
if (o != NULL && o->type != REDIS_HASH) {
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
)
{
addReply(c,shared.wrongtypeerr);
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));
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++) {
for (i = 2; i < c->argc; i++) {
de
=
dictFind
(
o
->
ptr
,
c
->
argv
[
i
]);
if (o != NULL && (value = hashGet(o,c->argv[i])) != NULL) {
if
(
de
!=
NULL
)
{
addReplyBulk(c,value);
addReplyBulk
(
c
,(
robj
*
)
dictGetEntryVal
(
de
)
);
decrRefCount(value
);
} else {
} else {
addReply(c,shared.nullbulk);
addReply(c,shared.nullbulk);
}
}
}
}
}
}
}
static void hdelCommand(redisClient *c) {
static void hdelCommand(redisClient *c) {
robj *o;
robj *o;
int
deleted
=
0
;
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
checkType(c,o,REDIS_HASH)) return;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
if (hashDelete(o,c->argv[2])) {
robj
*
field
=
getDecodedObject
(
c
->
argv
[
2
]);
if (hashLength(o) == 0) deleteKey(c->db,c->argv[1]);
addReply(c,shared.cone);
o
->
ptr
=
zipmapDel
((
unsigned
char
*
)
o
->
ptr
,
server.dirty++;
(
unsigned
char
*
)
field
->
ptr
,
sdslen
(
field
->
ptr
),
&
deleted
);
decrRefCount
(
field
);
if
(
zipmapLen
((
unsigned
char
*
)
o
->
ptr
)
==
0
)
deleteKey
(
c
->
db
,
c
->
argv
[
1
]);
} else {
} else {
deleted
=
dictDelete
((
dict
*
)
o
->
ptr
,
c
->
argv
[
2
])
==
DICT_OK
;
addReply(c,shared.czero);
if
(
htNeedsResize
(
o
->
ptr
))
dictResize
(
o
->
ptr
);
if
(
dictSize
((
dict
*
)
o
->
ptr
)
==
0
)
deleteKey
(
c
->
db
,
c
->
argv
[
1
]);
}
}
if
(
deleted
)
server
.
dirty
++
;
addReply
(
c
,
deleted
?
shared
.
cone
:
shared
.
czero
);
}
}
static void hlenCommand(redisClient *c) {
static void hlenCommand(redisClient *c) {
robj *o;
robj *o;
unsigned
long
len
;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
checkType(c,o,REDIS_HASH)) return;
len
=
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
?
addReplyUlong(c,hashLength(o));
zipmapLen
((
unsigned
char
*
)
o
->
ptr
)
:
dictSize
((
dict
*
)
o
->
ptr
);
addReplyUlong
(
c
,
len
);
}
}
#define REDIS_GETALL_KEYS 1
#define REDIS_GETALL_VALS 2
static void genericHgetallCommand(redisClient *c, int flags) {
static void genericHgetallCommand(redisClient *c, int flags) {
robj
*
o
,
*
lenobj
;
robj *o, *lenobj
, *obj
;
unsigned long count = 0;
unsigned long count = 0;
hashIterator *hi;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
|| checkType(c,o,REDIS_HASH)) return;
|| checkType(c,o,REDIS_HASH)) return;
...
@@ -6369,79 +6378,44 @@ static void genericHgetallCommand(redisClient *c, int flags) {
...
@@ -6369,79 +6378,44 @@ static void genericHgetallCommand(redisClient *c, int flags) {
addReply(c,lenobj);
addReply(c,lenobj);
decrRefCount(lenobj);
decrRefCount(lenobj);
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
hi = hashInitIterator(o);
unsigned
char
*
p
=
zipmapRewind
(
o
->
ptr
);
while (hashNext(hi) != REDIS_ERR) {
unsigned
char
*
field
,
*
val
;
if (flags & REDIS_HASH_KEY) {
unsigned
int
flen
,
vlen
;
obj = hashCurrent(hi,REDIS_HASH_KEY);
addReplyBulk(c,obj);
while
((
p
=
zipmapNext
(
p
,
&
field
,
&
flen
,
&
val
,
&
vlen
))
!=
NULL
)
{
decrRefCount(obj);
robj
*
aux
;
if
(
flags
&
REDIS_GETALL_KEYS
)
{
aux
=
createStringObject
((
char
*
)
field
,
flen
);
addReplyBulk
(
c
,
aux
);
decrRefCount
(
aux
);
count++;
count++;
}
}
if
(
flags
&
REDIS_
GETALL
_VAL
S
)
{
if (flags & REDIS_
HASH
_VAL
UE
) {
aux
=
createStringObject
((
char
*
)
val
,
vlen
);
obj = hashCurrent(hi,REDIS_HASH_VALUE
);
addReplyBulk
(
c
,
aux
);
addReplyBulk(c,
obj
);
decrRefCount
(
aux
);
decrRefCount(
obj
);
count++;
count++;
}
}
}
}
}
else
{
hashReleaseIterator(hi);
dictIterator
*
di
=
dictGetIterator
(
o
->
ptr
);
dictEntry
*
de
;
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
robj
*
fieldobj
=
dictGetEntryKey
(
de
);
robj
*
valobj
=
dictGetEntryVal
(
de
);
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);
lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count);
}
}
static void hkeysCommand(redisClient *c) {
static void hkeysCommand(redisClient *c) {
genericHgetallCommand
(
c
,
REDIS_
GETALL
_KEY
S
);
genericHgetallCommand(c,REDIS_
HASH
_KEY);
}
}
static void hvalsCommand(redisClient *c) {
static void hvalsCommand(redisClient *c) {
genericHgetallCommand
(
c
,
REDIS_
GETALL
_VAL
S
);
genericHgetallCommand(c,REDIS_
HASH
_VAL
UE
);
}
}
static void hgetallCommand(redisClient *c) {
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) {
static void hexistsCommand(redisClient *c) {
robj *o;
robj *o;
int
exists
=
0
;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_HASH)) return;
checkType(c,o,REDIS_HASH)) return;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
addReply(c, hashExists(o,c->argv[2]) ? shared.cone : shared.czero);
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
);
}
}
static void convertToRealHash(robj *o) {
static void convertToRealHash(robj *o) {
...
@@ -6495,16 +6469,16 @@ static redisSortOperation *createSortOperation(int type, robj *pattern) {
...
@@ -6495,16 +6469,16 @@ static redisSortOperation *createSortOperation(int type, robj *pattern) {
/* Return the value associated to the key with a name obtained
/* Return the value associated to the key with a name obtained
* substituting the first occurence of '*' in 'pattern' with 'subst' */
* substituting the first occurence of '*' in 'pattern' with 'subst' */
static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
char
*
p
;
char *p
, *f
;
sds spat, ssub;
sds spat, ssub;
robj
keyobj
;
robj keyobj
, fieldobj, *o
;
int
prefixlen
,
sublen
,
postfixlen
;
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 */
/* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
struct {
struct {
long len;
long len;
long free;
long free;
char buf[REDIS_SORTKEY_MAX+1];
char buf[REDIS_SORTKEY_MAX+1];
}
keyname
;
} keyname
, fieldname
;
/* If the pattern is "#" return the substitution object itself in order
/* If the pattern is "#" return the substitution object itself in order
* to implement the "SORT ... GET #" feature. */
* to implement the "SORT ... GET #" feature. */
...
@@ -6526,20 +6500,49 @@ static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
...
@@ -6526,20 +6500,49 @@ static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
return NULL;
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;
prefixlen = p-spat;
sublen = sdslen(ssub);
sublen = sdslen(ssub);
postfixlen
=
sdslen
(
spat
)
-
(
prefixlen
+
1
);
postfixlen = sdslen(spat)-(prefixlen+1)
-fieldlen
;
memcpy(keyname.buf,spat,prefixlen);
memcpy(keyname.buf,spat,prefixlen);
memcpy(keyname.buf+prefixlen,ssub,sublen);
memcpy(keyname.buf+prefixlen,ssub,sublen);
memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
keyname.buf[prefixlen+sublen+postfixlen] = '\0';
keyname.buf[prefixlen+sublen+postfixlen] = '\0';
keyname.len = prefixlen+sublen+postfixlen;
keyname.len = prefixlen+sublen+postfixlen;
initStaticStringObject
(
keyobj
,((
char
*
)
&
keyname
)
+
(
sizeof
(
long
)
*
2
))
decrRefCount(subst);
decrRefCount(subst);
/* printf("lookup '%s' => %p\n", keyname.buf,de); */
/* Lookup substituted key */
return
lookupKeyRead
(
db
,
&
keyobj
);
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
/* 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) {
...
@@ -6574,14 +6577,9 @@ static int sortCompare(const void *s1, const void *s2) {
cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
}
}
} else {
} else {
/* Compare elements directly */
/* Compare elements directly. Note that these objects already
robj
*
dec1
,
*
dec2
;
* need to be non-encoded (see sortCommand). */
cmp = strcoll(so1->obj->ptr,so2->obj->ptr);
dec1
=
getDecodedObject
(
so1
->
obj
);
dec2
=
getDecodedObject
(
so2
->
obj
);
cmp
=
strcoll
(
dec1
->
ptr
,
dec2
->
ptr
);
decrRefCount
(
dec1
);
decrRefCount
(
dec2
);
}
}
}
}
return server.sort_desc ? -cmp : cmp;
return server.sort_desc ? -cmp : cmp;
...
@@ -6708,37 +6706,35 @@ static void sortCommand(redisClient *c) {
...
@@ -6708,37 +6706,35 @@ static void sortCommand(redisClient *c) {
/* Now it's time to load the right scores in the sorting vector */
/* Now it's time to load the right scores in the sorting vector */
if (dontsort == 0) {
if (dontsort == 0) {
for (j = 0; j < vectorlen; j++) {
for (j = 0; j < vectorlen; j++) {
if
(
sortby
)
{
robj *byval;
robj *byval;
if (sortby) {
/* lookup value to sort by */
byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
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) {
if (alpha) {
vector[j].u.cmpobj = getDecodedObject(byval);
vector[j].u.cmpobj = getDecodedObject(byval);
} else {
} else {
if (byval->encoding == REDIS_ENCODING_RAW) {
if (byval->encoding == REDIS_ENCODING_RAW) {
vector[j].u.score = strtod(byval->ptr,NULL);
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
/* Don't need to decode the object if it's
* integer-encoded (the only encoding supported) so
* integer-encoded (the only encoding supported) so
* far. We can just cast it */
* far. We can just cast it */
if
(
byval
->
encoding
==
REDIS_ENCODING_INT
)
{
vector[j].u.score = (long)byval->ptr;
vector[j].u.score = (long)byval->ptr;
}
else
redisAssert
(
1
!=
1
);
}
}
} else {
} 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);
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) {
...
@@ -6836,7 +6832,7 @@ static void sortCommand(redisClient *c) {
decrRefCount(sortval);
decrRefCount(sortval);
listRelease(operations);
listRelease(operations);
for (j = 0; j < vectorlen; j++) {
for (j = 0; j < vectorlen; j++) {
if
(
sortby
&&
alpha
&&
vector
[
j
].
u
.
cmpobj
)
if (alpha && vector[j].u.cmpobj)
decrRefCount(vector[j].u.cmpobj);
decrRefCount(vector[j].u.cmpobj);
}
}
zfree(vector);
zfree(vector);
...
...
redis.tcl
View file @
b4eb9ccd
...
@@ -46,7 +46,7 @@ foreach redis_bulk_cmd {
...
@@ -46,7 +46,7 @@ foreach redis_bulk_cmd {
# Flag commands requiring last argument as a bulk write operation
# Flag commands requiring last argument as a bulk write operation
foreach redis_multibulk_cmd
{
foreach redis_multibulk_cmd
{
mset msetnx hset hmset hmget
mset msetnx hset
hsetnx
hmset hmget
}
{
}
{
set ::redis::multibulkarg
(
$redis
_multibulk_cmd
)
{}
set ::redis::multibulkarg
(
$redis
_multibulk_cmd
)
{}
}
}
...
...
test-redis.tcl
View file @
b4eb9ccd
...
@@ -1652,6 +1652,30 @@ proc main {} {
...
@@ -1652,6 +1652,30 @@ proc main {} {
set _ $rv
set _ $rv
}
{
0 newval1 1 0 newval2 1 1 1
}
}
{
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
}
{
test
{
HMSET wrong number of args
}
{
catch
{
$r
hmset smallhash key1 val1 key2
}
err
catch
{
$r
hmset smallhash key1 val1 key2
}
err
format $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