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
Hide 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.
/* Get the value from a hash identified by key. Returns either a string
* This is because integers are small, but currently stringObjectLen()
* object or NULL if the value cannot be found. The refcount of the object
* performs a slow conversion: not worth it. */
* is always increased by 1 when the value was found. */
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
&&
static
robj
*
hashGet
(
robj
*
o
,
robj
*
key
)
{
((
c
->
argv
[
2
]
->
encoding
==
REDIS_ENCODING_RAW
&&
robj
*
value
=
NULL
;
sdslen
(
c
->
argv
[
2
]
->
ptr
)
>
server
.
hash_max_zipmap_value
)
||
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
(
c
->
argv
[
3
]
->
encoding
==
REDIS_ENCODING_RAW
&&
unsigned
char
*
v
;
sdslen
(
c
->
argv
[
3
]
->
ptr
)
>
server
.
hash_max_zipmap_value
)))
unsigned
int
vlen
;
{
key
=
getDecodedObject
(
key
);
convertToRealHash
(
o
);
if
(
zipmapGet
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
),
&
v
,
&
vlen
))
{
value
=
createStringObject
((
char
*
)
v
,
vlen
);
}
decrRefCount
(
key
);
}
else
{
dictEntry
*
de
=
dictFind
(
o
->
ptr
,
key
);
if
(
de
!=
NULL
)
{
value
=
dictGetEntryVal
(
de
);
incrRefCount
(
value
);
}
}
}
return
value
;
}
/* 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
)
{
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
unsigned
char
*
zm
=
o
->
ptr
;
key
=
getDecodedObject
(
key
);
robj
*
valobj
=
getDecodedObject
(
c
->
argv
[
3
]);
if
(
zipmapExists
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
)))
{
decrRefCount
(
key
);
return
1
;
}
decrRefCount
(
key
);
}
else
{
if
(
dictFind
(
o
->
ptr
,
key
)
!=
NULL
)
{
return
1
;
}
}
return
0
;
}
zm
=
zipmapSet
(
zm
,
c
->
argv
[
2
]
->
ptr
,
sdslen
(
c
->
argv
[
2
]
->
ptr
),
/* Add an element, discard the old if the key already exists.
valobj
->
ptr
,
sdslen
(
valobj
->
ptr
),
&
update
);
* Return 0 on insert and 1 on update. */
decrRefCount
(
valobj
);
static
int
hashSet
(
robj
*
o
,
robj
*
key
,
robj
*
value
)
{
o
->
ptr
=
zm
;
int
update
=
0
;
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
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
);
/*
And here there is the second check for hash conversion.
*/
/*
Check if the zipmap needs to be upgraded to a real hash table
*/
if
(
zipmapLen
(
zm
)
>
server
.
hash_max_zipmap_entries
)
if
(
zipmapLen
(
o
->
ptr
)
>
server
.
hash_max_zipmap_entries
)
convertToRealHash
(
o
);
convertToRealHash
(
o
);
}
else
{
}
else
{
c
->
argv
[
2
]
=
tryObjectEncoding
(
c
->
argv
[
2
]);
value
=
tryObjectEncoding
(
value
);
/* note that c->argv[3] is already encoded, as the latest arg
if
(
dictReplace
(
o
->
ptr
,
key
,
value
))
{
* of a bulk command is always integer encoded if possible. */
/* Insert */
if
(
dictReplace
(
o
->
ptr
,
c
->
argv
[
2
],
c
->
argv
[
3
]))
{
incrRefCount
(
key
);
incrRefCount
(
c
->
argv
[
2
]);
}
else
{
}
else
{
/* Update */
update
=
1
;
update
=
1
;
}
}
incrRefCount
(
c
->
argv
[
3
]
);
incrRefCount
(
value
);
}
}
server
.
dirty
++
;
return
update
;
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
":%d
\r\n
"
,
update
==
0
));
}
}
static
void
hmsetCommand
(
redisClient
*
c
)
{
/* Delete an element from a hash.
int
i
;
* Return 1 on deleted and 0 on not found. */
robj
*
o
,
*
key
,
*
val
;
static
int
hashDelete
(
robj
*
o
,
robj
*
key
)
{
int
deleted
=
0
;
if
((
c
->
argc
%
2
)
==
1
)
{
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
addReplySds
(
c
,
sdsnew
(
"-ERR wrong number of arguments for HMSET
\r\n
"
));
key
=
getDecodedObject
(
key
);
return
;
o
->
ptr
=
zipmapDel
(
o
->
ptr
,
key
->
ptr
,
sdslen
(
key
->
ptr
),
&
deleted
);
decrRefCount
(
key
);
}
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
;
}
if
((
o
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]))
==
NULL
)
{
/* Return the number of elements in a hash. */
o
=
createHashObject
();
static
unsigned
long
hashLength
(
robj
*
o
)
{
dictAdd
(
c
->
db
->
dict
,
c
->
argv
[
1
],
o
);
return
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
?
incrRefCount
(
c
->
argv
[
1
]);
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
{
if
(
o
->
type
!=
REDIS_HASH
)
{
redisAssert
(
NULL
);
addReply
(
c
,
shared
.
wrongtypeerr
);
return
;
}
}
}
return
hi
;
}
/* We want to convert the zipmap into an hash table right now if the
static
void
hashReleaseIterator
(
hashIterator
*
hi
)
{
* entry to be added is too big. */
if
(
hi
->
encoding
==
REDIS_ENCODING_HT
)
{
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
dictReleaseIterator
(
hi
->
di
);
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
))
{
convertToRealHash
(
o
);
break
;
}
}
}
}
zfree
(
hi
);
}
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
/* Move to the next entry in the hash. Return REDIS_OK when the next entry
unsigned
char
*
zm
=
o
->
ptr
;
* 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
;
}
for
(
i
=
2
;
i
<
c
->
argc
;
i
+=
2
)
{
/* Get key or value object at current iteration position.
key
=
getDecodedObject
(
c
->
argv
[
i
]);
* This increases the refcount of the field object by 1. */
val
=
getDecodedObject
(
c
->
argv
[
i
+
1
]);
static
robj
*
hashCurrent
(
hashIterator
*
hi
,
int
what
)
{
zm
=
zipmapSet
(
zm
,
key
->
ptr
,
sdslen
(
key
->
ptr
),
robj
*
o
;
val
->
ptr
,
sdslen
(
val
->
ptr
),
NULL
);
if
(
hi
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
decrRefCount
(
key
);
if
(
what
&
REDIS_HASH_KEY
)
{
decrRefCount
(
val
);
o
=
createStringObject
((
char
*
)
hi
->
zk
,
hi
->
zklen
);
o
->
ptr
=
zm
;
}
else
{
o
=
createStringObject
((
char
*
)
hi
->
zv
,
hi
->
zvlen
);
}
}
/* And here there is the second check for hash conversion. */
if
(
zipmapLen
(
zm
)
>
server
.
hash_max_zipmap_entries
)
convertToRealHash
(
o
);
}
else
{
}
else
{
for
(
i
=
2
;
i
<
c
->
argc
;
i
+=
2
)
{
if
(
what
&
REDIS_HASH_KEY
)
{
key
=
tryObjectEncoding
(
c
->
argv
[
i
]);
o
=
dictGetEntryKey
(
hi
->
de
);
val
=
tryObjectEncoding
(
c
->
argv
[
i
+
1
]);
}
else
{
if
(
dictReplace
(
o
->
ptr
,
key
,
val
))
{
o
=
dictGetEntryVal
(
hi
->
de
);
incrRefCount
(
key
);
}
incrRefCount
(
val
);
}
}
incrRefCount
(
o
);
}
}
return
o
;
addReply
(
c
,
shared
.
ok
);
}
}
static
void
hincrbyCommand
(
redisClient
*
c
)
{
static
robj
*
hashLookupWriteOrCreate
(
redisClient
*
c
,
robj
*
key
)
{
long
long
value
=
0
,
incr
=
0
;
robj
*
o
=
lookupKeyWrite
(
c
->
db
,
key
);
robj
*
o
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]);
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
++
;
/* Find value if already present in hash */
}
if
(
zipmapGet
(
zm
,
c
->
argv
[
2
]
->
ptr
,
sdslen
(
c
->
argv
[
2
]
->
ptr
),
&
zval
,
&
zvlen
))
{
static
void
hsetnxCommand
(
redisClient
*
c
)
{
/* strtoll needs the char* to have a trailing \0, but
robj
*
o
;
* the zipmap doesn't include them. */
if
((
o
=
hashLookupWriteOrCreate
(
c
,
c
->
argv
[
1
]))
==
NULL
)
return
;
sds
szval
=
sdsnewlen
(
zval
,
zvlen
);
hashTryConversion
(
o
,
c
->
argv
,
2
,
3
);
value
=
strtoll
(
szval
,
NULL
,
10
);
sdsfree
(
szval
);
if
(
hashExists
(
o
,
c
->
argv
[
2
]))
{
}
addReply
(
c
,
shared
.
czero
);
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
;
/* Check if the zipmap needs to be converted. */
if
(
zipmapLen
(
zm
)
>
server
.
hash_max_zipmap_entries
)
convertToRealHash
(
o
);
}
else
{
}
else
{
robj
*
hval
;
hashSet
(
o
,
c
->
argv
[
2
],
c
->
argv
[
3
]);
dictEntry
*
de
;
addReply
(
c
,
shared
.
cone
);
server
.
dirty
++
;
}
}
/* Find value if already present in hash */
static
void
hmsetCommand
(
redisClient
*
c
)
{
de
=
dictFind
(
o
->
ptr
,
c
->
argv
[
2
]);
int
i
;
if
(
de
!=
NULL
)
{
robj
*
o
;
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
;
else
redisAssert
(
1
!=
1
);
}
value
+=
incr
;
if
((
c
->
argc
%
2
)
==
1
)
{
hval
=
createObject
(
REDIS_STRING
,
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
));
addReplySds
(
c
,
sdsnew
(
"-ERR wrong number of arguments for HMSET
\r\n
"
));
hval
=
tryObjectEncoding
(
hval
);
return
;
if
(
dictReplace
(
o
->
ptr
,
c
->
argv
[
2
],
hval
))
{
incrRefCount
(
c
->
argv
[
2
]);
}
}
}
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
;
new
=
createObject
(
REDIS_STRING
,
sdscatprintf
(
sdsempty
(),
"%lld"
,
value
));
hashSet
(
o
,
c
->
argv
[
2
],
new
);
decrRefCount
(
new
);
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
;
addReply
(
c
,
shared
.
nullbulk
);
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
)
{
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
));
addReply
(
c
,
shared
.
wrongtypeerr
);
for
(
i
=
2
;
i
<
c
->
argc
;
i
++
)
{
addReply
(
c
,
shared
.
nullbulk
);
}
return
;
}
else
{
if
(
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
));
addReplySds
(
c
,
sdscatprintf
(
sdsempty
(),
"*%d
\r\n
"
,
c
->
argc
-
2
));
if
(
o
->
encoding
==
REDIS_ENCODING_ZIPMAP
)
{
for
(
i
=
2
;
i
<
c
->
argc
;
i
++
)
{
unsigned
char
*
zm
=
o
->
ptr
;
if
(
o
!=
NULL
&&
(
value
=
hashGet
(
o
,
c
->
argv
[
i
]))
!=
NULL
)
{
unsigned
char
*
v
;
addReplyBulk
(
c
,
value
);
unsigned
int
vlen
;
decrRefCount
(
value
);
robj
*
field
;
}
else
{
addReply
(
c
,
shared
.
nullbulk
);
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
));
}
else
{
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
;
count
++
;
if
(
flags
&
REDIS_GETALL_KEYS
)
{
aux
=
createStringObject
((
char
*
)
field
,
flen
);
addReplyBulk
(
c
,
aux
);
decrRefCount
(
aux
);
count
++
;
}
if
(
flags
&
REDIS_GETALL_VALS
)
{
aux
=
createStringObject
((
char
*
)
val
,
vlen
);
addReplyBulk
(
c
,
aux
);
decrRefCount
(
aux
);
count
++
;
}
}
}
}
else
{
if
(
flags
&
REDIS_HASH_VALUE
)
{
dictIterator
*
di
=
dictGetIterator
(
o
->
ptr
);
obj
=
hashCurrent
(
hi
,
REDIS_HASH_VALUE
);
dictEntry
*
de
;
addReplyBulk
(
c
,
obj
);
decrRefCount
(
obj
);
while
((
de
=
dictNext
(
di
))
!=
NULL
)
{
count
++
;
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
);
}
}
hashReleaseIterator
(
hi
);
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,38 +6706,36 @@ static void sortCommand(redisClient *c) {
...
@@ -6708,38 +6706,36 @@ 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
++
)
{
robj
*
byval
;
if
(
sortby
)
{
if
(
sortby
)
{
robj
*
byval
;
/* 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
;
if
(
alpha
)
{
vector
[
j
].
u
.
cmpobj
=
getDecodedObject
(
byval
);
}
else
{
if
(
byval
->
encoding
==
REDIS_ENCODING_RAW
)
{
vector
[
j
].
u
.
score
=
strtod
(
byval
->
ptr
,
NULL
);
}
else
{
/* 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
{
}
else
{
if
(
!
alpha
)
{
/* use object itself to sort by */
if
(
vector
[
j
].
obj
->
encoding
==
REDIS_ENCODING_RAW
)
byval
=
vector
[
j
].
obj
;
vector
[
j
].
u
.
score
=
strtod
(
vector
[
j
].
obj
->
ptr
,
NULL
);
}
else
{
if
(
vector
[
j
].
obj
->
encoding
==
REDIS_ENCODING_INT
)
if
(
alpha
)
{
vector
[
j
].
u
.
score
=
(
long
)
vector
[
j
].
obj
->
ptr
;
vector
[
j
].
u
.
cmpobj
=
getDecodedObject
(
byval
);
else
}
else
{
redisAssert
(
1
!=
1
);
if
(
byval
->
encoding
==
REDIS_ENCODING_RAW
)
{
}
vector
[
j
].
u
.
score
=
strtod
(
byval
->
ptr
,
NULL
);
}
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 */
vector
[
j
].
u
.
score
=
(
long
)
byval
->
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) {
...
@@ -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