Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
9f9e1cea
Commit
9f9e1cea
authored
Dec 14, 2010
by
Pieter Noordhuis
Browse files
Add SETRANGE command implementation and tests
parent
7ecd4644
Changes
4
Show whitespace changes
Inline
Side-by-side
src/redis.c
View file @
9f9e1cea
...
...
@@ -80,6 +80,7 @@ struct redisCommand readonlyCommandTable[] = {
{
"exists"
,
existsCommand
,
2
,
0
,
NULL
,
1
,
1
,
1
},
{
"setbit"
,
setbitCommand
,
4
,
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"getbit"
,
getbitCommand
,
3
,
0
,
NULL
,
1
,
1
,
1
},
{
"setrange"
,
setrangeCommand
,
4
,
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"incr"
,
incrCommand
,
2
,
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"decr"
,
decrCommand
,
2
,
REDIS_CMD_DENYOOM
,
NULL
,
1
,
1
,
1
},
{
"mget"
,
mgetCommand
,
-
2
,
0
,
NULL
,
1
,
-
1
,
1
},
...
...
src/redis.h
View file @
9f9e1cea
...
...
@@ -889,6 +889,7 @@ void delCommand(redisClient *c);
void
existsCommand
(
redisClient
*
c
);
void
setbitCommand
(
redisClient
*
c
);
void
getbitCommand
(
redisClient
*
c
);
void
setrangeCommand
(
redisClient
*
c
);
void
incrCommand
(
redisClient
*
c
);
void
decrCommand
(
redisClient
*
c
);
void
incrbyCommand
(
redisClient
*
c
);
...
...
src/t_string.c
View file @
9f9e1cea
...
...
@@ -5,6 +5,14 @@
* String Commands
*----------------------------------------------------------------------------*/
static
int
checkStringLength
(
redisClient
*
c
,
long
long
size
)
{
if
(
size
>
512
*
1024
*
1024
)
{
addReplyError
(
c
,
"string exceeds maximum allowed size (512MB)"
);
return
REDIS_ERR
;
}
return
REDIS_OK
;
}
void
setGenericCommand
(
redisClient
*
c
,
int
nx
,
robj
*
key
,
robj
*
val
,
robj
*
expire
)
{
int
retval
;
long
seconds
=
0
;
/* initialized to avoid an harmness warning */
...
...
@@ -172,6 +180,83 @@ void getbitCommand(redisClient *c) {
addReply
(
c
,
on
?
shared
.
cone
:
shared
.
czero
);
}
void
setrangeCommand
(
redisClient
*
c
)
{
robj
*
o
;
long
offset
;
sds
value
=
c
->
argv
[
3
]
->
ptr
;
if
(
getLongFromObjectOrReply
(
c
,
c
->
argv
[
2
],
&
offset
,
NULL
)
!=
REDIS_OK
)
return
;
o
=
lookupKeyWrite
(
c
->
db
,
c
->
argv
[
1
]);
if
(
o
==
NULL
)
{
/* Negative offset is always 0 for non-existing keys */
if
(
offset
<
0
)
offset
=
0
;
/* Return 0 when setting nothing on a non-existing string */
if
(
sdslen
(
value
)
==
0
)
{
addReply
(
c
,
shared
.
czero
);
return
;
}
/* Return when the resulting string exceeds allowed size */
if
(
checkStringLength
(
c
,
offset
+
sdslen
(
value
))
!=
REDIS_OK
)
return
;
o
=
createObject
(
REDIS_STRING
,
sdsempty
());
dbAdd
(
c
->
db
,
c
->
argv
[
1
],
o
);
}
else
{
int
olen
;
/* Key exists, check type */
if
(
checkType
(
c
,
o
,
REDIS_STRING
))
return
;
/* Find out existing value length */
if
(
o
->
encoding
==
REDIS_ENCODING_INT
)
{
char
llbuf
[
32
];
olen
=
ll2string
(
llbuf
,
sizeof
(
llbuf
),(
long
)
o
->
ptr
);
}
else
{
olen
=
sdslen
(
o
->
ptr
);
}
/* Return existing string length when setting nothing */
if
(
sdslen
(
value
)
==
0
)
{
addReplyLongLong
(
c
,
olen
);
return
;
}
/* Convert negative indexes. Note that for SETRANGE, the meaning of a
* negative index is a little different than for other commands.
* Here, an offset of -1 points to the trailing NULL byte of the
* string instead of the last character. */
if
(
offset
<
0
)
{
offset
=
olen
+
1
+
offset
;
if
(
offset
<
0
)
offset
=
0
;
}
/* Return when the resulting string exceeds allowed size */
if
(
checkStringLength
(
c
,
offset
+
sdslen
(
value
))
!=
REDIS_OK
)
return
;
/* Create a copy when the object is shared or encoded. */
if
(
o
->
refcount
!=
1
||
o
->
encoding
!=
REDIS_ENCODING_RAW
)
{
robj
*
decoded
=
getDecodedObject
(
o
);
o
=
createStringObject
(
decoded
->
ptr
,
sdslen
(
decoded
->
ptr
));
decrRefCount
(
decoded
);
dbReplace
(
c
->
db
,
c
->
argv
[
1
],
o
);
}
}
if
(
sdslen
(
value
)
>
0
)
{
o
->
ptr
=
sdsgrowzero
(
o
->
ptr
,
offset
+
sdslen
(
value
));
memcpy
((
char
*
)
o
->
ptr
+
offset
,
value
,
sdslen
(
value
));
touchWatchedKey
(
c
->
db
,
c
->
argv
[
1
]);
server
.
dirty
++
;
}
addReplyLongLong
(
c
,
sdslen
(
o
->
ptr
));
}
void
mgetCommand
(
redisClient
*
c
)
{
int
j
;
...
...
tests/unit/basic.tcl
View file @
9f9e1cea
...
...
@@ -458,4 +458,108 @@ start_server {tags {"basic"}} {
assert_equal 0
[
r getbit mykey 100
]
assert_equal 0
[
r getbit mykey 10000
]
}
test
"SETRANGE against non-existing key"
{
r del mykey
assert_equal 3
[
r setrange mykey 0 foo
]
assert_equal
"foo"
[
r get mykey
]
r del mykey
assert_equal 0
[
r setrange mykey 0
""
]
assert_equal 0
[
r exists mykey
]
r del mykey
assert_equal 4
[
r setrange mykey 1 foo
]
assert_equal
"
\000
foo"
[
r get mykey
]
r del mykey
assert_equal 3
[
r setrange mykey -1 foo
]
assert_equal
"foo"
[
r get mykey
]
r del mykey
assert_equal 3
[
r setrange mykey -100 foo
]
assert_equal
"foo"
[
r get mykey
]
}
test
"SETRANGE against string-encoded key"
{
r set mykey
"foo"
assert_equal 3
[
r setrange mykey 0 b
]
assert_equal
"boo"
[
r get mykey
]
r set mykey
"foo"
assert_equal 3
[
r setrange mykey 0
""
]
assert_equal
"foo"
[
r get mykey
]
r set mykey
"foo"
assert_equal 3
[
r setrange mykey 1 b
]
assert_equal
"fbo"
[
r get mykey
]
r set mykey
"foo"
assert_equal 6
[
r setrange mykey -1 bar
]
assert_equal
"foobar"
[
r get mykey
]
r set mykey
"foo"
assert_equal 5
[
r setrange mykey -2 bar
]
assert_equal
"fobar"
[
r get mykey
]
r set mykey
"foo"
assert_equal 3
[
r setrange mykey -20 bar
]
assert_equal
"bar"
[
r get mykey
]
r set mykey
"foo"
assert_equal 7
[
r setrange mykey 4 bar
]
assert_equal
"foo
\000
bar"
[
r get mykey
]
}
test
"SETRANGE against integer-encoded key"
{
r set mykey 1234
assert_encoding int mykey
assert_equal 4
[
r setrange mykey 0 2
]
assert_encoding raw mykey
assert_equal 2234
[
r get mykey
]
# Shouldn't change encoding when nothing is set
r set mykey 1234
assert_encoding int mykey
assert_equal 4
[
r setrange mykey 0
""
]
assert_encoding int mykey
assert_equal 1234
[
r get mykey
]
r set mykey 1234
assert_encoding int mykey
assert_equal 4
[
r setrange mykey 1 3
]
assert_encoding raw mykey
assert_equal 1334
[
r get mykey
]
r set mykey 1234
assert_encoding int mykey
assert_equal 5
[
r setrange mykey -1 5
]
assert_encoding raw mykey
assert_equal 12345
[
r get mykey
]
r set mykey 1234
assert_encoding int mykey
assert_equal 4
[
r setrange mykey -2 5
]
assert_encoding raw mykey
assert_equal 1235
[
r get mykey
]
r set mykey 1234
assert_encoding int mykey
assert_equal 6
[
r setrange mykey 5 2
]
assert_encoding raw mykey
assert_equal
"1234
\000
2"
[
r get mykey
]
}
test
"SETRANGE against key with wrong type"
{
r del mykey
r lpush mykey
"foo"
assert_error
"*wrong kind*"
{
r setrange mykey 0 bar
}
}
test
"SETRANGE with out of range offset"
{
r del mykey
assert_error
"*maximum allowed size*"
{
r setrange mykey
[
expr 512*1024*1024-4
]
world
}
r set mykey
"hello"
assert_error
"*maximum allowed size*"
{
r setrange mykey
[
expr 512*1024*1024-4
]
world
}
}
}
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