xref: /f-stack/app/redis-5.0.5/src/t_string.c (revision 572c4311)
1*572c4311Sfengbojiang /*
2*572c4311Sfengbojiang  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
3*572c4311Sfengbojiang  * All rights reserved.
4*572c4311Sfengbojiang  *
5*572c4311Sfengbojiang  * Redistribution and use in source and binary forms, with or without
6*572c4311Sfengbojiang  * modification, are permitted provided that the following conditions are met:
7*572c4311Sfengbojiang  *
8*572c4311Sfengbojiang  *   * Redistributions of source code must retain the above copyright notice,
9*572c4311Sfengbojiang  *     this list of conditions and the following disclaimer.
10*572c4311Sfengbojiang  *   * Redistributions in binary form must reproduce the above copyright
11*572c4311Sfengbojiang  *     notice, this list of conditions and the following disclaimer in the
12*572c4311Sfengbojiang  *     documentation and/or other materials provided with the distribution.
13*572c4311Sfengbojiang  *   * Neither the name of Redis nor the names of its contributors may be used
14*572c4311Sfengbojiang  *     to endorse or promote products derived from this software without
15*572c4311Sfengbojiang  *     specific prior written permission.
16*572c4311Sfengbojiang  *
17*572c4311Sfengbojiang  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18*572c4311Sfengbojiang  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*572c4311Sfengbojiang  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*572c4311Sfengbojiang  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21*572c4311Sfengbojiang  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22*572c4311Sfengbojiang  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23*572c4311Sfengbojiang  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24*572c4311Sfengbojiang  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25*572c4311Sfengbojiang  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26*572c4311Sfengbojiang  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27*572c4311Sfengbojiang  * POSSIBILITY OF SUCH DAMAGE.
28*572c4311Sfengbojiang  */
29*572c4311Sfengbojiang 
30*572c4311Sfengbojiang #include "server.h"
31*572c4311Sfengbojiang #include <math.h> /* isnan(), isinf() */
32*572c4311Sfengbojiang 
33*572c4311Sfengbojiang /*-----------------------------------------------------------------------------
34*572c4311Sfengbojiang  * String Commands
35*572c4311Sfengbojiang  *----------------------------------------------------------------------------*/
36*572c4311Sfengbojiang 
checkStringLength(client * c,long long size)37*572c4311Sfengbojiang static int checkStringLength(client *c, long long size) {
38*572c4311Sfengbojiang     if (size > 512*1024*1024) {
39*572c4311Sfengbojiang         addReplyError(c,"string exceeds maximum allowed size (512MB)");
40*572c4311Sfengbojiang         return C_ERR;
41*572c4311Sfengbojiang     }
42*572c4311Sfengbojiang     return C_OK;
43*572c4311Sfengbojiang }
44*572c4311Sfengbojiang 
45*572c4311Sfengbojiang /* The setGenericCommand() function implements the SET operation with different
46*572c4311Sfengbojiang  * options and variants. This function is called in order to implement the
47*572c4311Sfengbojiang  * following commands: SET, SETEX, PSETEX, SETNX.
48*572c4311Sfengbojiang  *
49*572c4311Sfengbojiang  * 'flags' changes the behavior of the command (NX or XX, see belove).
50*572c4311Sfengbojiang  *
51*572c4311Sfengbojiang  * 'expire' represents an expire to set in form of a Redis object as passed
52*572c4311Sfengbojiang  * by the user. It is interpreted according to the specified 'unit'.
53*572c4311Sfengbojiang  *
54*572c4311Sfengbojiang  * 'ok_reply' and 'abort_reply' is what the function will reply to the client
55*572c4311Sfengbojiang  * if the operation is performed, or when it is not because of NX or
56*572c4311Sfengbojiang  * XX flags.
57*572c4311Sfengbojiang  *
58*572c4311Sfengbojiang  * If ok_reply is NULL "+OK" is used.
59*572c4311Sfengbojiang  * If abort_reply is NULL, "$-1" is used. */
60*572c4311Sfengbojiang 
61*572c4311Sfengbojiang #define OBJ_SET_NO_FLAGS 0
62*572c4311Sfengbojiang #define OBJ_SET_NX (1<<0)     /* Set if key not exists. */
63*572c4311Sfengbojiang #define OBJ_SET_XX (1<<1)     /* Set if key exists. */
64*572c4311Sfengbojiang #define OBJ_SET_EX (1<<2)     /* Set if time in seconds is given */
65*572c4311Sfengbojiang #define OBJ_SET_PX (1<<3)     /* Set if time in ms in given */
66*572c4311Sfengbojiang 
setGenericCommand(client * c,int flags,robj * key,robj * val,robj * expire,int unit,robj * ok_reply,robj * abort_reply)67*572c4311Sfengbojiang void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
68*572c4311Sfengbojiang     long long milliseconds = 0; /* initialized to avoid any harmness warning */
69*572c4311Sfengbojiang 
70*572c4311Sfengbojiang     if (expire) {
71*572c4311Sfengbojiang         if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK)
72*572c4311Sfengbojiang             return;
73*572c4311Sfengbojiang         if (milliseconds <= 0) {
74*572c4311Sfengbojiang             addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
75*572c4311Sfengbojiang             return;
76*572c4311Sfengbojiang         }
77*572c4311Sfengbojiang         if (unit == UNIT_SECONDS) milliseconds *= 1000;
78*572c4311Sfengbojiang     }
79*572c4311Sfengbojiang 
80*572c4311Sfengbojiang     if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
81*572c4311Sfengbojiang         (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))
82*572c4311Sfengbojiang     {
83*572c4311Sfengbojiang         addReply(c, abort_reply ? abort_reply : shared.nullbulk);
84*572c4311Sfengbojiang         return;
85*572c4311Sfengbojiang     }
86*572c4311Sfengbojiang     setKey(c->db,key,val);
87*572c4311Sfengbojiang     server.dirty++;
88*572c4311Sfengbojiang     if (expire) setExpire(c,c->db,key,mstime()+milliseconds);
89*572c4311Sfengbojiang     notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
90*572c4311Sfengbojiang     if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,
91*572c4311Sfengbojiang         "expire",key,c->db->id);
92*572c4311Sfengbojiang     addReply(c, ok_reply ? ok_reply : shared.ok);
93*572c4311Sfengbojiang }
94*572c4311Sfengbojiang 
95*572c4311Sfengbojiang /* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */
setCommand(client * c)96*572c4311Sfengbojiang void setCommand(client *c) {
97*572c4311Sfengbojiang     int j;
98*572c4311Sfengbojiang     robj *expire = NULL;
99*572c4311Sfengbojiang     int unit = UNIT_SECONDS;
100*572c4311Sfengbojiang     int flags = OBJ_SET_NO_FLAGS;
101*572c4311Sfengbojiang 
102*572c4311Sfengbojiang     for (j = 3; j < c->argc; j++) {
103*572c4311Sfengbojiang         char *a = c->argv[j]->ptr;
104*572c4311Sfengbojiang         robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
105*572c4311Sfengbojiang 
106*572c4311Sfengbojiang         if ((a[0] == 'n' || a[0] == 'N') &&
107*572c4311Sfengbojiang             (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
108*572c4311Sfengbojiang             !(flags & OBJ_SET_XX))
109*572c4311Sfengbojiang         {
110*572c4311Sfengbojiang             flags |= OBJ_SET_NX;
111*572c4311Sfengbojiang         } else if ((a[0] == 'x' || a[0] == 'X') &&
112*572c4311Sfengbojiang                    (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
113*572c4311Sfengbojiang                    !(flags & OBJ_SET_NX))
114*572c4311Sfengbojiang         {
115*572c4311Sfengbojiang             flags |= OBJ_SET_XX;
116*572c4311Sfengbojiang         } else if ((a[0] == 'e' || a[0] == 'E') &&
117*572c4311Sfengbojiang                    (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
118*572c4311Sfengbojiang                    !(flags & OBJ_SET_PX) && next)
119*572c4311Sfengbojiang         {
120*572c4311Sfengbojiang             flags |= OBJ_SET_EX;
121*572c4311Sfengbojiang             unit = UNIT_SECONDS;
122*572c4311Sfengbojiang             expire = next;
123*572c4311Sfengbojiang             j++;
124*572c4311Sfengbojiang         } else if ((a[0] == 'p' || a[0] == 'P') &&
125*572c4311Sfengbojiang                    (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
126*572c4311Sfengbojiang                    !(flags & OBJ_SET_EX) && next)
127*572c4311Sfengbojiang         {
128*572c4311Sfengbojiang             flags |= OBJ_SET_PX;
129*572c4311Sfengbojiang             unit = UNIT_MILLISECONDS;
130*572c4311Sfengbojiang             expire = next;
131*572c4311Sfengbojiang             j++;
132*572c4311Sfengbojiang         } else {
133*572c4311Sfengbojiang             addReply(c,shared.syntaxerr);
134*572c4311Sfengbojiang             return;
135*572c4311Sfengbojiang         }
136*572c4311Sfengbojiang     }
137*572c4311Sfengbojiang 
138*572c4311Sfengbojiang     c->argv[2] = tryObjectEncoding(c->argv[2]);
139*572c4311Sfengbojiang     setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
140*572c4311Sfengbojiang }
141*572c4311Sfengbojiang 
setnxCommand(client * c)142*572c4311Sfengbojiang void setnxCommand(client *c) {
143*572c4311Sfengbojiang     c->argv[2] = tryObjectEncoding(c->argv[2]);
144*572c4311Sfengbojiang     setGenericCommand(c,OBJ_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);
145*572c4311Sfengbojiang }
146*572c4311Sfengbojiang 
setexCommand(client * c)147*572c4311Sfengbojiang void setexCommand(client *c) {
148*572c4311Sfengbojiang     c->argv[3] = tryObjectEncoding(c->argv[3]);
149*572c4311Sfengbojiang     setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);
150*572c4311Sfengbojiang }
151*572c4311Sfengbojiang 
psetexCommand(client * c)152*572c4311Sfengbojiang void psetexCommand(client *c) {
153*572c4311Sfengbojiang     c->argv[3] = tryObjectEncoding(c->argv[3]);
154*572c4311Sfengbojiang     setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);
155*572c4311Sfengbojiang }
156*572c4311Sfengbojiang 
getGenericCommand(client * c)157*572c4311Sfengbojiang int getGenericCommand(client *c) {
158*572c4311Sfengbojiang     robj *o;
159*572c4311Sfengbojiang 
160*572c4311Sfengbojiang     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
161*572c4311Sfengbojiang         return C_OK;
162*572c4311Sfengbojiang 
163*572c4311Sfengbojiang     if (o->type != OBJ_STRING) {
164*572c4311Sfengbojiang         addReply(c,shared.wrongtypeerr);
165*572c4311Sfengbojiang         return C_ERR;
166*572c4311Sfengbojiang     } else {
167*572c4311Sfengbojiang         addReplyBulk(c,o);
168*572c4311Sfengbojiang         return C_OK;
169*572c4311Sfengbojiang     }
170*572c4311Sfengbojiang }
171*572c4311Sfengbojiang 
getCommand(client * c)172*572c4311Sfengbojiang void getCommand(client *c) {
173*572c4311Sfengbojiang     getGenericCommand(c);
174*572c4311Sfengbojiang }
175*572c4311Sfengbojiang 
getsetCommand(client * c)176*572c4311Sfengbojiang void getsetCommand(client *c) {
177*572c4311Sfengbojiang     if (getGenericCommand(c) == C_ERR) return;
178*572c4311Sfengbojiang     c->argv[2] = tryObjectEncoding(c->argv[2]);
179*572c4311Sfengbojiang     setKey(c->db,c->argv[1],c->argv[2]);
180*572c4311Sfengbojiang     notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[1],c->db->id);
181*572c4311Sfengbojiang     server.dirty++;
182*572c4311Sfengbojiang }
183*572c4311Sfengbojiang 
setrangeCommand(client * c)184*572c4311Sfengbojiang void setrangeCommand(client *c) {
185*572c4311Sfengbojiang     robj *o;
186*572c4311Sfengbojiang     long offset;
187*572c4311Sfengbojiang     sds value = c->argv[3]->ptr;
188*572c4311Sfengbojiang 
189*572c4311Sfengbojiang     if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != C_OK)
190*572c4311Sfengbojiang         return;
191*572c4311Sfengbojiang 
192*572c4311Sfengbojiang     if (offset < 0) {
193*572c4311Sfengbojiang         addReplyError(c,"offset is out of range");
194*572c4311Sfengbojiang         return;
195*572c4311Sfengbojiang     }
196*572c4311Sfengbojiang 
197*572c4311Sfengbojiang     o = lookupKeyWrite(c->db,c->argv[1]);
198*572c4311Sfengbojiang     if (o == NULL) {
199*572c4311Sfengbojiang         /* Return 0 when setting nothing on a non-existing string */
200*572c4311Sfengbojiang         if (sdslen(value) == 0) {
201*572c4311Sfengbojiang             addReply(c,shared.czero);
202*572c4311Sfengbojiang             return;
203*572c4311Sfengbojiang         }
204*572c4311Sfengbojiang 
205*572c4311Sfengbojiang         /* Return when the resulting string exceeds allowed size */
206*572c4311Sfengbojiang         if (checkStringLength(c,offset+sdslen(value)) != C_OK)
207*572c4311Sfengbojiang             return;
208*572c4311Sfengbojiang 
209*572c4311Sfengbojiang         o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));
210*572c4311Sfengbojiang         dbAdd(c->db,c->argv[1],o);
211*572c4311Sfengbojiang     } else {
212*572c4311Sfengbojiang         size_t olen;
213*572c4311Sfengbojiang 
214*572c4311Sfengbojiang         /* Key exists, check type */
215*572c4311Sfengbojiang         if (checkType(c,o,OBJ_STRING))
216*572c4311Sfengbojiang             return;
217*572c4311Sfengbojiang 
218*572c4311Sfengbojiang         /* Return existing string length when setting nothing */
219*572c4311Sfengbojiang         olen = stringObjectLen(o);
220*572c4311Sfengbojiang         if (sdslen(value) == 0) {
221*572c4311Sfengbojiang             addReplyLongLong(c,olen);
222*572c4311Sfengbojiang             return;
223*572c4311Sfengbojiang         }
224*572c4311Sfengbojiang 
225*572c4311Sfengbojiang         /* Return when the resulting string exceeds allowed size */
226*572c4311Sfengbojiang         if (checkStringLength(c,offset+sdslen(value)) != C_OK)
227*572c4311Sfengbojiang             return;
228*572c4311Sfengbojiang 
229*572c4311Sfengbojiang         /* Create a copy when the object is shared or encoded. */
230*572c4311Sfengbojiang         o = dbUnshareStringValue(c->db,c->argv[1],o);
231*572c4311Sfengbojiang     }
232*572c4311Sfengbojiang 
233*572c4311Sfengbojiang     if (sdslen(value) > 0) {
234*572c4311Sfengbojiang         o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
235*572c4311Sfengbojiang         memcpy((char*)o->ptr+offset,value,sdslen(value));
236*572c4311Sfengbojiang         signalModifiedKey(c->db,c->argv[1]);
237*572c4311Sfengbojiang         notifyKeyspaceEvent(NOTIFY_STRING,
238*572c4311Sfengbojiang             "setrange",c->argv[1],c->db->id);
239*572c4311Sfengbojiang         server.dirty++;
240*572c4311Sfengbojiang     }
241*572c4311Sfengbojiang     addReplyLongLong(c,sdslen(o->ptr));
242*572c4311Sfengbojiang }
243*572c4311Sfengbojiang 
getrangeCommand(client * c)244*572c4311Sfengbojiang void getrangeCommand(client *c) {
245*572c4311Sfengbojiang     robj *o;
246*572c4311Sfengbojiang     long long start, end;
247*572c4311Sfengbojiang     char *str, llbuf[32];
248*572c4311Sfengbojiang     size_t strlen;
249*572c4311Sfengbojiang 
250*572c4311Sfengbojiang     if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)
251*572c4311Sfengbojiang         return;
252*572c4311Sfengbojiang     if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)
253*572c4311Sfengbojiang         return;
254*572c4311Sfengbojiang     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
255*572c4311Sfengbojiang         checkType(c,o,OBJ_STRING)) return;
256*572c4311Sfengbojiang 
257*572c4311Sfengbojiang     if (o->encoding == OBJ_ENCODING_INT) {
258*572c4311Sfengbojiang         str = llbuf;
259*572c4311Sfengbojiang         strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
260*572c4311Sfengbojiang     } else {
261*572c4311Sfengbojiang         str = o->ptr;
262*572c4311Sfengbojiang         strlen = sdslen(str);
263*572c4311Sfengbojiang     }
264*572c4311Sfengbojiang 
265*572c4311Sfengbojiang     /* Convert negative indexes */
266*572c4311Sfengbojiang     if (start < 0 && end < 0 && start > end) {
267*572c4311Sfengbojiang         addReply(c,shared.emptybulk);
268*572c4311Sfengbojiang         return;
269*572c4311Sfengbojiang     }
270*572c4311Sfengbojiang     if (start < 0) start = strlen+start;
271*572c4311Sfengbojiang     if (end < 0) end = strlen+end;
272*572c4311Sfengbojiang     if (start < 0) start = 0;
273*572c4311Sfengbojiang     if (end < 0) end = 0;
274*572c4311Sfengbojiang     if ((unsigned long long)end >= strlen) end = strlen-1;
275*572c4311Sfengbojiang 
276*572c4311Sfengbojiang     /* Precondition: end >= 0 && end < strlen, so the only condition where
277*572c4311Sfengbojiang      * nothing can be returned is: start > end. */
278*572c4311Sfengbojiang     if (start > end || strlen == 0) {
279*572c4311Sfengbojiang         addReply(c,shared.emptybulk);
280*572c4311Sfengbojiang     } else {
281*572c4311Sfengbojiang         addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
282*572c4311Sfengbojiang     }
283*572c4311Sfengbojiang }
284*572c4311Sfengbojiang 
mgetCommand(client * c)285*572c4311Sfengbojiang void mgetCommand(client *c) {
286*572c4311Sfengbojiang     int j;
287*572c4311Sfengbojiang 
288*572c4311Sfengbojiang     addReplyMultiBulkLen(c,c->argc-1);
289*572c4311Sfengbojiang     for (j = 1; j < c->argc; j++) {
290*572c4311Sfengbojiang         robj *o = lookupKeyRead(c->db,c->argv[j]);
291*572c4311Sfengbojiang         if (o == NULL) {
292*572c4311Sfengbojiang             addReply(c,shared.nullbulk);
293*572c4311Sfengbojiang         } else {
294*572c4311Sfengbojiang             if (o->type != OBJ_STRING) {
295*572c4311Sfengbojiang                 addReply(c,shared.nullbulk);
296*572c4311Sfengbojiang             } else {
297*572c4311Sfengbojiang                 addReplyBulk(c,o);
298*572c4311Sfengbojiang             }
299*572c4311Sfengbojiang         }
300*572c4311Sfengbojiang     }
301*572c4311Sfengbojiang }
302*572c4311Sfengbojiang 
msetGenericCommand(client * c,int nx)303*572c4311Sfengbojiang void msetGenericCommand(client *c, int nx) {
304*572c4311Sfengbojiang     int j;
305*572c4311Sfengbojiang 
306*572c4311Sfengbojiang     if ((c->argc % 2) == 0) {
307*572c4311Sfengbojiang         addReplyError(c,"wrong number of arguments for MSET");
308*572c4311Sfengbojiang         return;
309*572c4311Sfengbojiang     }
310*572c4311Sfengbojiang 
311*572c4311Sfengbojiang     /* Handle the NX flag. The MSETNX semantic is to return zero and don't
312*572c4311Sfengbojiang      * set anything if at least one key alerady exists. */
313*572c4311Sfengbojiang     if (nx) {
314*572c4311Sfengbojiang         for (j = 1; j < c->argc; j += 2) {
315*572c4311Sfengbojiang             if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
316*572c4311Sfengbojiang                 addReply(c, shared.czero);
317*572c4311Sfengbojiang                 return;
318*572c4311Sfengbojiang             }
319*572c4311Sfengbojiang         }
320*572c4311Sfengbojiang     }
321*572c4311Sfengbojiang 
322*572c4311Sfengbojiang     for (j = 1; j < c->argc; j += 2) {
323*572c4311Sfengbojiang         c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
324*572c4311Sfengbojiang         setKey(c->db,c->argv[j],c->argv[j+1]);
325*572c4311Sfengbojiang         notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);
326*572c4311Sfengbojiang     }
327*572c4311Sfengbojiang     server.dirty += (c->argc-1)/2;
328*572c4311Sfengbojiang     addReply(c, nx ? shared.cone : shared.ok);
329*572c4311Sfengbojiang }
330*572c4311Sfengbojiang 
msetCommand(client * c)331*572c4311Sfengbojiang void msetCommand(client *c) {
332*572c4311Sfengbojiang     msetGenericCommand(c,0);
333*572c4311Sfengbojiang }
334*572c4311Sfengbojiang 
msetnxCommand(client * c)335*572c4311Sfengbojiang void msetnxCommand(client *c) {
336*572c4311Sfengbojiang     msetGenericCommand(c,1);
337*572c4311Sfengbojiang }
338*572c4311Sfengbojiang 
incrDecrCommand(client * c,long long incr)339*572c4311Sfengbojiang void incrDecrCommand(client *c, long long incr) {
340*572c4311Sfengbojiang     long long value, oldvalue;
341*572c4311Sfengbojiang     robj *o, *new;
342*572c4311Sfengbojiang 
343*572c4311Sfengbojiang     o = lookupKeyWrite(c->db,c->argv[1]);
344*572c4311Sfengbojiang     if (o != NULL && checkType(c,o,OBJ_STRING)) return;
345*572c4311Sfengbojiang     if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;
346*572c4311Sfengbojiang 
347*572c4311Sfengbojiang     oldvalue = value;
348*572c4311Sfengbojiang     if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
349*572c4311Sfengbojiang         (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
350*572c4311Sfengbojiang         addReplyError(c,"increment or decrement would overflow");
351*572c4311Sfengbojiang         return;
352*572c4311Sfengbojiang     }
353*572c4311Sfengbojiang     value += incr;
354*572c4311Sfengbojiang 
355*572c4311Sfengbojiang     if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&
356*572c4311Sfengbojiang         (value < 0 || value >= OBJ_SHARED_INTEGERS) &&
357*572c4311Sfengbojiang         value >= LONG_MIN && value <= LONG_MAX)
358*572c4311Sfengbojiang     {
359*572c4311Sfengbojiang         new = o;
360*572c4311Sfengbojiang         o->ptr = (void*)((long)value);
361*572c4311Sfengbojiang     } else {
362*572c4311Sfengbojiang         new = createStringObjectFromLongLongForValue(value);
363*572c4311Sfengbojiang         if (o) {
364*572c4311Sfengbojiang             dbOverwrite(c->db,c->argv[1],new);
365*572c4311Sfengbojiang         } else {
366*572c4311Sfengbojiang             dbAdd(c->db,c->argv[1],new);
367*572c4311Sfengbojiang         }
368*572c4311Sfengbojiang     }
369*572c4311Sfengbojiang     signalModifiedKey(c->db,c->argv[1]);
370*572c4311Sfengbojiang     notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
371*572c4311Sfengbojiang     server.dirty++;
372*572c4311Sfengbojiang     addReply(c,shared.colon);
373*572c4311Sfengbojiang     addReply(c,new);
374*572c4311Sfengbojiang     addReply(c,shared.crlf);
375*572c4311Sfengbojiang }
376*572c4311Sfengbojiang 
incrCommand(client * c)377*572c4311Sfengbojiang void incrCommand(client *c) {
378*572c4311Sfengbojiang     incrDecrCommand(c,1);
379*572c4311Sfengbojiang }
380*572c4311Sfengbojiang 
decrCommand(client * c)381*572c4311Sfengbojiang void decrCommand(client *c) {
382*572c4311Sfengbojiang     incrDecrCommand(c,-1);
383*572c4311Sfengbojiang }
384*572c4311Sfengbojiang 
incrbyCommand(client * c)385*572c4311Sfengbojiang void incrbyCommand(client *c) {
386*572c4311Sfengbojiang     long long incr;
387*572c4311Sfengbojiang 
388*572c4311Sfengbojiang     if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
389*572c4311Sfengbojiang     incrDecrCommand(c,incr);
390*572c4311Sfengbojiang }
391*572c4311Sfengbojiang 
decrbyCommand(client * c)392*572c4311Sfengbojiang void decrbyCommand(client *c) {
393*572c4311Sfengbojiang     long long incr;
394*572c4311Sfengbojiang 
395*572c4311Sfengbojiang     if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
396*572c4311Sfengbojiang     incrDecrCommand(c,-incr);
397*572c4311Sfengbojiang }
398*572c4311Sfengbojiang 
incrbyfloatCommand(client * c)399*572c4311Sfengbojiang void incrbyfloatCommand(client *c) {
400*572c4311Sfengbojiang     long double incr, value;
401*572c4311Sfengbojiang     robj *o, *new, *aux;
402*572c4311Sfengbojiang 
403*572c4311Sfengbojiang     o = lookupKeyWrite(c->db,c->argv[1]);
404*572c4311Sfengbojiang     if (o != NULL && checkType(c,o,OBJ_STRING)) return;
405*572c4311Sfengbojiang     if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != C_OK ||
406*572c4311Sfengbojiang         getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != C_OK)
407*572c4311Sfengbojiang         return;
408*572c4311Sfengbojiang 
409*572c4311Sfengbojiang     value += incr;
410*572c4311Sfengbojiang     if (isnan(value) || isinf(value)) {
411*572c4311Sfengbojiang         addReplyError(c,"increment would produce NaN or Infinity");
412*572c4311Sfengbojiang         return;
413*572c4311Sfengbojiang     }
414*572c4311Sfengbojiang     new = createStringObjectFromLongDouble(value,1);
415*572c4311Sfengbojiang     if (o)
416*572c4311Sfengbojiang         dbOverwrite(c->db,c->argv[1],new);
417*572c4311Sfengbojiang     else
418*572c4311Sfengbojiang         dbAdd(c->db,c->argv[1],new);
419*572c4311Sfengbojiang     signalModifiedKey(c->db,c->argv[1]);
420*572c4311Sfengbojiang     notifyKeyspaceEvent(NOTIFY_STRING,"incrbyfloat",c->argv[1],c->db->id);
421*572c4311Sfengbojiang     server.dirty++;
422*572c4311Sfengbojiang     addReplyBulk(c,new);
423*572c4311Sfengbojiang 
424*572c4311Sfengbojiang     /* Always replicate INCRBYFLOAT as a SET command with the final value
425*572c4311Sfengbojiang      * in order to make sure that differences in float precision or formatting
426*572c4311Sfengbojiang      * will not create differences in replicas or after an AOF restart. */
427*572c4311Sfengbojiang     aux = createStringObject("SET",3);
428*572c4311Sfengbojiang     rewriteClientCommandArgument(c,0,aux);
429*572c4311Sfengbojiang     decrRefCount(aux);
430*572c4311Sfengbojiang     rewriteClientCommandArgument(c,2,new);
431*572c4311Sfengbojiang }
432*572c4311Sfengbojiang 
appendCommand(client * c)433*572c4311Sfengbojiang void appendCommand(client *c) {
434*572c4311Sfengbojiang     size_t totlen;
435*572c4311Sfengbojiang     robj *o, *append;
436*572c4311Sfengbojiang 
437*572c4311Sfengbojiang     o = lookupKeyWrite(c->db,c->argv[1]);
438*572c4311Sfengbojiang     if (o == NULL) {
439*572c4311Sfengbojiang         /* Create the key */
440*572c4311Sfengbojiang         c->argv[2] = tryObjectEncoding(c->argv[2]);
441*572c4311Sfengbojiang         dbAdd(c->db,c->argv[1],c->argv[2]);
442*572c4311Sfengbojiang         incrRefCount(c->argv[2]);
443*572c4311Sfengbojiang         totlen = stringObjectLen(c->argv[2]);
444*572c4311Sfengbojiang     } else {
445*572c4311Sfengbojiang         /* Key exists, check type */
446*572c4311Sfengbojiang         if (checkType(c,o,OBJ_STRING))
447*572c4311Sfengbojiang             return;
448*572c4311Sfengbojiang 
449*572c4311Sfengbojiang         /* "append" is an argument, so always an sds */
450*572c4311Sfengbojiang         append = c->argv[2];
451*572c4311Sfengbojiang         totlen = stringObjectLen(o)+sdslen(append->ptr);
452*572c4311Sfengbojiang         if (checkStringLength(c,totlen) != C_OK)
453*572c4311Sfengbojiang             return;
454*572c4311Sfengbojiang 
455*572c4311Sfengbojiang         /* Append the value */
456*572c4311Sfengbojiang         o = dbUnshareStringValue(c->db,c->argv[1],o);
457*572c4311Sfengbojiang         o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
458*572c4311Sfengbojiang         totlen = sdslen(o->ptr);
459*572c4311Sfengbojiang     }
460*572c4311Sfengbojiang     signalModifiedKey(c->db,c->argv[1]);
461*572c4311Sfengbojiang     notifyKeyspaceEvent(NOTIFY_STRING,"append",c->argv[1],c->db->id);
462*572c4311Sfengbojiang     server.dirty++;
463*572c4311Sfengbojiang     addReplyLongLong(c,totlen);
464*572c4311Sfengbojiang }
465*572c4311Sfengbojiang 
strlenCommand(client * c)466*572c4311Sfengbojiang void strlenCommand(client *c) {
467*572c4311Sfengbojiang     robj *o;
468*572c4311Sfengbojiang     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
469*572c4311Sfengbojiang         checkType(c,o,OBJ_STRING)) return;
470*572c4311Sfengbojiang     addReplyLongLong(c,stringObjectLen(o));
471*572c4311Sfengbojiang }
472