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