]>
git.saurik.com Git - redis.git/blob - src/t_string.c
2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
31 #include <math.h> /* isnan(), isinf() */
33 /*-----------------------------------------------------------------------------
35 *----------------------------------------------------------------------------*/
37 static int checkStringLength(redisClient
*c
, long long size
) {
38 if (size
> 512*1024*1024) {
39 addReplyError(c
,"string exceeds maximum allowed size (512MB)");
45 void setGenericCommand(redisClient
*c
, int nx
, robj
*key
, robj
*val
, robj
*expire
, int unit
) {
46 long long milliseconds
= 0; /* initialized to avoid an harmness warning */
49 if (getLongLongFromObjectOrReply(c
, expire
, &milliseconds
, NULL
) != REDIS_OK
)
51 if (milliseconds
<= 0) {
52 addReplyError(c
,"invalid expire time in SETEX");
55 if (unit
== UNIT_SECONDS
) milliseconds
*= 1000;
58 if (nx
&& lookupKeyWrite(c
->db
,key
) != NULL
) {
59 addReply(c
,shared
.czero
);
62 setKey(c
->db
,key
,val
);
64 if (expire
) setExpire(c
->db
,key
,mstime()+milliseconds
);
65 addReply(c
, nx
? shared
.cone
: shared
.ok
);
68 void setCommand(redisClient
*c
) {
69 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
70 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[2],NULL
,0);
73 void setnxCommand(redisClient
*c
) {
74 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
75 setGenericCommand(c
,1,c
->argv
[1],c
->argv
[2],NULL
,0);
78 void setexCommand(redisClient
*c
) {
79 c
->argv
[3] = tryObjectEncoding(c
->argv
[3]);
80 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[3],c
->argv
[2],UNIT_SECONDS
);
83 void psetexCommand(redisClient
*c
) {
84 c
->argv
[3] = tryObjectEncoding(c
->argv
[3]);
85 setGenericCommand(c
,0,c
->argv
[1],c
->argv
[3],c
->argv
[2],UNIT_MILLISECONDS
);
88 int getGenericCommand(redisClient
*c
) {
91 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
)
94 if (o
->type
!= REDIS_STRING
) {
95 addReply(c
,shared
.wrongtypeerr
);
103 void getCommand(redisClient
*c
) {
104 getGenericCommand(c
);
107 void getsetCommand(redisClient
*c
) {
108 if (getGenericCommand(c
) == REDIS_ERR
) return;
109 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
110 setKey(c
->db
,c
->argv
[1],c
->argv
[2]);
114 void setrangeCommand(redisClient
*c
) {
117 sds value
= c
->argv
[3]->ptr
;
119 if (getLongFromObjectOrReply(c
,c
->argv
[2],&offset
,NULL
) != REDIS_OK
)
123 addReplyError(c
,"offset is out of range");
127 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
129 /* Return 0 when setting nothing on a non-existing string */
130 if (sdslen(value
) == 0) {
131 addReply(c
,shared
.czero
);
135 /* Return when the resulting string exceeds allowed size */
136 if (checkStringLength(c
,offset
+sdslen(value
)) != REDIS_OK
)
139 o
= createObject(REDIS_STRING
,sdsempty());
140 dbAdd(c
->db
,c
->argv
[1],o
);
144 /* Key exists, check type */
145 if (checkType(c
,o
,REDIS_STRING
))
148 /* Return existing string length when setting nothing */
149 olen
= stringObjectLen(o
);
150 if (sdslen(value
) == 0) {
151 addReplyLongLong(c
,olen
);
155 /* Return when the resulting string exceeds allowed size */
156 if (checkStringLength(c
,offset
+sdslen(value
)) != REDIS_OK
)
159 /* Create a copy when the object is shared or encoded. */
160 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
161 robj
*decoded
= getDecodedObject(o
);
162 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
163 decrRefCount(decoded
);
164 dbOverwrite(c
->db
,c
->argv
[1],o
);
168 if (sdslen(value
) > 0) {
169 o
->ptr
= sdsgrowzero(o
->ptr
,offset
+sdslen(value
));
170 memcpy((char*)o
->ptr
+offset
,value
,sdslen(value
));
171 signalModifiedKey(c
->db
,c
->argv
[1]);
174 addReplyLongLong(c
,sdslen(o
->ptr
));
177 void getrangeCommand(redisClient
*c
) {
180 char *str
, llbuf
[32];
183 if (getLongFromObjectOrReply(c
,c
->argv
[2],&start
,NULL
) != REDIS_OK
)
185 if (getLongFromObjectOrReply(c
,c
->argv
[3],&end
,NULL
) != REDIS_OK
)
187 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptybulk
)) == NULL
||
188 checkType(c
,o
,REDIS_STRING
)) return;
190 if (o
->encoding
== REDIS_ENCODING_INT
) {
192 strlen
= ll2string(llbuf
,sizeof(llbuf
),(long)o
->ptr
);
195 strlen
= sdslen(str
);
198 /* Convert negative indexes */
199 if (start
< 0) start
= strlen
+start
;
200 if (end
< 0) end
= strlen
+end
;
201 if (start
< 0) start
= 0;
202 if (end
< 0) end
= 0;
203 if ((unsigned)end
>= strlen
) end
= strlen
-1;
205 /* Precondition: end >= 0 && end < strlen, so the only condition where
206 * nothing can be returned is: start > end. */
208 addReply(c
,shared
.emptybulk
);
210 addReplyBulkCBuffer(c
,(char*)str
+start
,end
-start
+1);
214 void mgetCommand(redisClient
*c
) {
217 addReplyMultiBulkLen(c
,c
->argc
-1);
218 for (j
= 1; j
< c
->argc
; j
++) {
219 robj
*o
= lookupKeyRead(c
->db
,c
->argv
[j
]);
221 addReply(c
,shared
.nullbulk
);
223 if (o
->type
!= REDIS_STRING
) {
224 addReply(c
,shared
.nullbulk
);
232 void msetGenericCommand(redisClient
*c
, int nx
) {
235 if ((c
->argc
% 2) == 0) {
236 addReplyError(c
,"wrong number of arguments for MSET");
239 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
240 * set nothing at all if at least one already key exists. */
242 for (j
= 1; j
< c
->argc
; j
+= 2) {
243 if (lookupKeyWrite(c
->db
,c
->argv
[j
]) != NULL
) {
248 addReply(c
, shared
.czero
);
253 for (j
= 1; j
< c
->argc
; j
+= 2) {
254 c
->argv
[j
+1] = tryObjectEncoding(c
->argv
[j
+1]);
255 setKey(c
->db
,c
->argv
[j
],c
->argv
[j
+1]);
257 server
.dirty
+= (c
->argc
-1)/2;
258 addReply(c
, nx
? shared
.cone
: shared
.ok
);
261 void msetCommand(redisClient
*c
) {
262 msetGenericCommand(c
,0);
265 void msetnxCommand(redisClient
*c
) {
266 msetGenericCommand(c
,1);
269 void incrDecrCommand(redisClient
*c
, long long incr
) {
270 long long value
, oldvalue
;
273 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
274 if (o
!= NULL
&& checkType(c
,o
,REDIS_STRING
)) return;
275 if (getLongLongFromObjectOrReply(c
,o
,&value
,NULL
) != REDIS_OK
) return;
278 if ((incr
< 0 && oldvalue
< 0 && incr
< (LLONG_MIN
-oldvalue
)) ||
279 (incr
> 0 && oldvalue
> 0 && incr
> (LLONG_MAX
-oldvalue
))) {
280 addReplyError(c
,"increment or decrement would overflow");
284 new = createStringObjectFromLongLong(value
);
286 dbOverwrite(c
->db
,c
->argv
[1],new);
288 dbAdd(c
->db
,c
->argv
[1],new);
289 signalModifiedKey(c
->db
,c
->argv
[1]);
291 addReply(c
,shared
.colon
);
293 addReply(c
,shared
.crlf
);
296 void incrCommand(redisClient
*c
) {
297 incrDecrCommand(c
,1);
300 void decrCommand(redisClient
*c
) {
301 incrDecrCommand(c
,-1);
304 void incrbyCommand(redisClient
*c
) {
307 if (getLongLongFromObjectOrReply(c
, c
->argv
[2], &incr
, NULL
) != REDIS_OK
) return;
308 incrDecrCommand(c
,incr
);
311 void decrbyCommand(redisClient
*c
) {
314 if (getLongLongFromObjectOrReply(c
, c
->argv
[2], &incr
, NULL
) != REDIS_OK
) return;
315 incrDecrCommand(c
,-incr
);
318 void incrbyfloatCommand(redisClient
*c
) {
319 long double incr
, value
;
322 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
323 if (o
!= NULL
&& checkType(c
,o
,REDIS_STRING
)) return;
324 if (getLongDoubleFromObjectOrReply(c
,o
,&value
,NULL
) != REDIS_OK
||
325 getLongDoubleFromObjectOrReply(c
,c
->argv
[2],&incr
,NULL
) != REDIS_OK
)
329 if (isnan(value
) || isinf(value
)) {
330 addReplyError(c
,"increment would produce NaN or Infinity");
333 new = createStringObjectFromLongDouble(value
);
335 dbOverwrite(c
->db
,c
->argv
[1],new);
337 dbAdd(c
->db
,c
->argv
[1],new);
338 signalModifiedKey(c
->db
,c
->argv
[1]);
342 /* Always replicate INCRBYFLOAT as a SET command with the final value
343 * in order to make sure that differences in float pricision or formatting
344 * will not create differences in replicas or after an AOF restart. */
345 aux
= createStringObject("SET",3);
346 rewriteClientCommandArgument(c
,0,aux
);
348 rewriteClientCommandArgument(c
,2,new);
351 void appendCommand(redisClient
*c
) {
355 o
= lookupKeyWrite(c
->db
,c
->argv
[1]);
358 c
->argv
[2] = tryObjectEncoding(c
->argv
[2]);
359 dbAdd(c
->db
,c
->argv
[1],c
->argv
[2]);
360 incrRefCount(c
->argv
[2]);
361 totlen
= stringObjectLen(c
->argv
[2]);
363 /* Key exists, check type */
364 if (checkType(c
,o
,REDIS_STRING
))
367 /* "append" is an argument, so always an sds */
369 totlen
= stringObjectLen(o
)+sdslen(append
->ptr
);
370 if (checkStringLength(c
,totlen
) != REDIS_OK
)
373 /* If the object is shared or encoded, we have to make a copy */
374 if (o
->refcount
!= 1 || o
->encoding
!= REDIS_ENCODING_RAW
) {
375 robj
*decoded
= getDecodedObject(o
);
376 o
= createStringObject(decoded
->ptr
, sdslen(decoded
->ptr
));
377 decrRefCount(decoded
);
378 dbOverwrite(c
->db
,c
->argv
[1],o
);
381 /* Append the value */
382 o
->ptr
= sdscatlen(o
->ptr
,append
->ptr
,sdslen(append
->ptr
));
383 totlen
= sdslen(o
->ptr
);
385 signalModifiedKey(c
->db
,c
->argv
[1]);
387 addReplyLongLong(c
,totlen
);
390 void strlenCommand(redisClient
*c
) {
392 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
393 checkType(c
,o
,REDIS_STRING
)) return;
394 addReplyLongLong(c
,stringObjectLen(o
));