14365e5b2Santirez /*
24365e5b2Santirez * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
34365e5b2Santirez * All rights reserved.
44365e5b2Santirez *
54365e5b2Santirez * Redistribution and use in source and binary forms, with or without
64365e5b2Santirez * modification, are permitted provided that the following conditions are met:
74365e5b2Santirez *
84365e5b2Santirez * * Redistributions of source code must retain the above copyright notice,
94365e5b2Santirez * this list of conditions and the following disclaimer.
104365e5b2Santirez * * Redistributions in binary form must reproduce the above copyright
114365e5b2Santirez * notice, this list of conditions and the following disclaimer in the
124365e5b2Santirez * documentation and/or other materials provided with the distribution.
134365e5b2Santirez * * Neither the name of Redis nor the names of its contributors may be used
144365e5b2Santirez * to endorse or promote products derived from this software without
154365e5b2Santirez * specific prior written permission.
164365e5b2Santirez *
174365e5b2Santirez * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
184365e5b2Santirez * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
194365e5b2Santirez * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
204365e5b2Santirez * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
214365e5b2Santirez * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
224365e5b2Santirez * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
234365e5b2Santirez * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
244365e5b2Santirez * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
254365e5b2Santirez * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
264365e5b2Santirez * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
274365e5b2Santirez * POSSIBILITY OF SUCH DAMAGE.
284365e5b2Santirez */
294365e5b2Santirez
30cef054e8Santirez #include "server.h"
315574b53eSantirez #include <math.h> /* isnan(), isinf() */
32e2641e09Santirez
33e2641e09Santirez /*-----------------------------------------------------------------------------
34e2641e09Santirez * String Commands
35e2641e09Santirez *----------------------------------------------------------------------------*/
36e2641e09Santirez
checkStringLength(client * c,long long size)37554bd0e7Santirez static int checkStringLength(client *c, long long size) {
389f9e1ceaSPieter Noordhuis if (size > 512*1024*1024) {
399f9e1ceaSPieter Noordhuis addReplyError(c,"string exceeds maximum allowed size (512MB)");
4040eb548aSantirez return C_ERR;
419f9e1ceaSPieter Noordhuis }
4240eb548aSantirez return C_OK;
439f9e1ceaSPieter Noordhuis }
449f9e1ceaSPieter Noordhuis
4530d5d416Santirez /* The setGenericCommand() function implements the SET operation with different
4630d5d416Santirez * options and variants. This function is called in order to implement the
4730d5d416Santirez * following commands: SET, SETEX, PSETEX, SETNX.
4830d5d416Santirez *
4930d5d416Santirez * 'flags' changes the behavior of the command (NX or XX, see belove).
5030d5d416Santirez *
5130d5d416Santirez * 'expire' represents an expire to set in form of a Redis object as passed
5230d5d416Santirez * by the user. It is interpreted according to the specified 'unit'.
5330d5d416Santirez *
5430d5d416Santirez * 'ok_reply' and 'abort_reply' is what the function will reply to the client
5530d5d416Santirez * if the operation is performed, or when it is not because of NX or
5630d5d416Santirez * XX flags.
5730d5d416Santirez *
5830d5d416Santirez * If ok_reply is NULL "+OK" is used.
5930d5d416Santirez * If abort_reply is NULL, "$-1" is used. */
6030d5d416Santirez
6114ff5724Santirez #define OBJ_SET_NO_FLAGS 0
6214ff5724Santirez #define OBJ_SET_NX (1<<0) /* Set if key not exists. */
6314ff5724Santirez #define OBJ_SET_XX (1<<1) /* Set if key exists. */
6414ff5724Santirez #define OBJ_SET_EX (1<<2) /* Set if time in seconds is given */
6514ff5724Santirez #define OBJ_SET_PX (1<<3) /* Set if time in ms in given */
6630d5d416Santirez
setGenericCommand(client * c,int flags,robj * key,robj * val,robj * expire,int unit,robj * ok_reply,robj * abort_reply)67554bd0e7Santirez void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
689d09ce39Sguiquanz long long milliseconds = 0; /* initialized to avoid any harmness warning */
69e2641e09Santirez
70e2641e09Santirez if (expire) {
7140eb548aSantirez if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != C_OK)
72e2641e09Santirez return;
7312d293caSantirez if (milliseconds <= 0) {
74a6edfceaSantirez addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name);
75e2641e09Santirez return;
76e2641e09Santirez }
7712d293caSantirez if (unit == UNIT_SECONDS) milliseconds *= 1000;
78e2641e09Santirez }
79e2641e09Santirez
8014ff5724Santirez if ((flags & OBJ_SET_NX && lookupKeyWrite(c->db,key) != NULL) ||
8114ff5724Santirez (flags & OBJ_SET_XX && lookupKeyWrite(c->db,key) == NULL))
8230d5d416Santirez {
8330d5d416Santirez addReply(c, abort_reply ? abort_reply : shared.nullbulk);
84e2641e09Santirez return;
85e2641e09Santirez }
86f85cd526Santirez setKey(c->db,key,val);
87e2641e09Santirez server.dirty++;
8812d293caSantirez if (expire) setExpire(c->db,key,mstime()+milliseconds);
8932f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
9032f80e2fSantirez if (expire) notifyKeyspaceEvent(NOTIFY_GENERIC,
91fce016d3Santirez "expire",key,c->db->id);
9230d5d416Santirez addReply(c, ok_reply ? ok_reply : shared.ok);
93e2641e09Santirez }
94e2641e09Santirez
9530d5d416Santirez /* SET key value [NX] [XX] [EX <seconds>] [PX <milliseconds>] */
setCommand(client * c)96554bd0e7Santirez void setCommand(client *c) {
9730d5d416Santirez int j;
9830d5d416Santirez robj *expire = NULL;
9930d5d416Santirez int unit = UNIT_SECONDS;
10014ff5724Santirez int flags = OBJ_SET_NO_FLAGS;
10130d5d416Santirez
10230d5d416Santirez for (j = 3; j < c->argc; j++) {
10330d5d416Santirez char *a = c->argv[j]->ptr;
10430d5d416Santirez robj *next = (j == c->argc-1) ? NULL : c->argv[j+1];
10530d5d416Santirez
10686d87e35Scharsyam if ((a[0] == 'n' || a[0] == 'N') &&
107352172a7SMihir Joshi (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
10814ff5724Santirez !(flags & OBJ_SET_XX))
1091dbd8e94Santirez {
11014ff5724Santirez flags |= OBJ_SET_NX;
11186d87e35Scharsyam } else if ((a[0] == 'x' || a[0] == 'X') &&
112352172a7SMihir Joshi (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
11314ff5724Santirez !(flags & OBJ_SET_NX))
1141dbd8e94Santirez {
11514ff5724Santirez flags |= OBJ_SET_XX;
11686d87e35Scharsyam } else if ((a[0] == 'e' || a[0] == 'E') &&
117352172a7SMihir Joshi (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
11814ff5724Santirez !(flags & OBJ_SET_PX) && next)
1191dbd8e94Santirez {
12014ff5724Santirez flags |= OBJ_SET_EX;
12130d5d416Santirez unit = UNIT_SECONDS;
12230d5d416Santirez expire = next;
12330d5d416Santirez j++;
12486d87e35Scharsyam } else if ((a[0] == 'p' || a[0] == 'P') &&
125352172a7SMihir Joshi (a[1] == 'x' || a[1] == 'X') && a[2] == '\0' &&
12614ff5724Santirez !(flags & OBJ_SET_EX) && next)
1271dbd8e94Santirez {
12814ff5724Santirez flags |= OBJ_SET_PX;
12930d5d416Santirez unit = UNIT_MILLISECONDS;
13030d5d416Santirez expire = next;
13130d5d416Santirez j++;
13230d5d416Santirez } else {
13330d5d416Santirez addReply(c,shared.syntaxerr);
13430d5d416Santirez return;
13530d5d416Santirez }
13630d5d416Santirez }
13730d5d416Santirez
13875b41de8SPieter Noordhuis c->argv[2] = tryObjectEncoding(c->argv[2]);
13930d5d416Santirez setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
140e2641e09Santirez }
141e2641e09Santirez
setnxCommand(client * c)142554bd0e7Santirez void setnxCommand(client *c) {
14375b41de8SPieter Noordhuis c->argv[2] = tryObjectEncoding(c->argv[2]);
14414ff5724Santirez setGenericCommand(c,OBJ_SET_NX,c->argv[1],c->argv[2],NULL,0,shared.cone,shared.czero);
145e2641e09Santirez }
146e2641e09Santirez
setexCommand(client * c)147554bd0e7Santirez void setexCommand(client *c) {
14875b41de8SPieter Noordhuis c->argv[3] = tryObjectEncoding(c->argv[3]);
14914ff5724Santirez setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS,NULL,NULL);
15012d293caSantirez }
15112d293caSantirez
psetexCommand(client * c)152554bd0e7Santirez void psetexCommand(client *c) {
15312d293caSantirez c->argv[3] = tryObjectEncoding(c->argv[3]);
15414ff5724Santirez setGenericCommand(c,OBJ_SET_NO_FLAGS,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS,NULL,NULL);
155e2641e09Santirez }
156e2641e09Santirez
getGenericCommand(client * c)157554bd0e7Santirez int getGenericCommand(client *c) {
158e2641e09Santirez robj *o;
159e2641e09Santirez
160e2641e09Santirez if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
16140eb548aSantirez return C_OK;
162e2641e09Santirez
16314ff5724Santirez if (o->type != OBJ_STRING) {
164e2641e09Santirez addReply(c,shared.wrongtypeerr);
16540eb548aSantirez return C_ERR;
166e2641e09Santirez } else {
167e2641e09Santirez addReplyBulk(c,o);
16840eb548aSantirez return C_OK;
169e2641e09Santirez }
170e2641e09Santirez }
171e2641e09Santirez
getCommand(client * c)172554bd0e7Santirez void getCommand(client *c) {
173e2641e09Santirez getGenericCommand(c);
174e2641e09Santirez }
175e2641e09Santirez
getsetCommand(client * c)176554bd0e7Santirez void getsetCommand(client *c) {
17740eb548aSantirez if (getGenericCommand(c) == C_ERR) return;
17875b41de8SPieter Noordhuis c->argv[2] = tryObjectEncoding(c->argv[2]);
179f85cd526Santirez setKey(c->db,c->argv[1],c->argv[2]);
18032f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[1],c->db->id);
181e2641e09Santirez server.dirty++;
182e2641e09Santirez }
183e2641e09Santirez
setrangeCommand(client * c)184554bd0e7Santirez void setrangeCommand(client *c) {
1859f9e1ceaSPieter Noordhuis robj *o;
1869f9e1ceaSPieter Noordhuis long offset;
1879f9e1ceaSPieter Noordhuis sds value = c->argv[3]->ptr;
1889f9e1ceaSPieter Noordhuis
18940eb548aSantirez if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != C_OK)
1909f9e1ceaSPieter Noordhuis return;
1919f9e1ceaSPieter Noordhuis
1928f8eeffeSPieter Noordhuis if (offset < 0) {
1938f8eeffeSPieter Noordhuis addReplyError(c,"offset is out of range");
1948f8eeffeSPieter Noordhuis return;
1958f8eeffeSPieter Noordhuis }
1968f8eeffeSPieter Noordhuis
1979f9e1ceaSPieter Noordhuis o = lookupKeyWrite(c->db,c->argv[1]);
1989f9e1ceaSPieter Noordhuis if (o == NULL) {
1999f9e1ceaSPieter Noordhuis /* Return 0 when setting nothing on a non-existing string */
2009f9e1ceaSPieter Noordhuis if (sdslen(value) == 0) {
2019f9e1ceaSPieter Noordhuis addReply(c,shared.czero);
2029f9e1ceaSPieter Noordhuis return;
2039f9e1ceaSPieter Noordhuis }
2049f9e1ceaSPieter Noordhuis
2059f9e1ceaSPieter Noordhuis /* Return when the resulting string exceeds allowed size */
20640eb548aSantirez if (checkStringLength(c,offset+sdslen(value)) != C_OK)
2079f9e1ceaSPieter Noordhuis return;
2089f9e1ceaSPieter Noordhuis
20914ff5724Santirez o = createObject(OBJ_STRING,sdsnewlen(NULL, offset+sdslen(value)));
2109f9e1ceaSPieter Noordhuis dbAdd(c->db,c->argv[1],o);
2119f9e1ceaSPieter Noordhuis } else {
212ad1b4f4fSPieter Noordhuis size_t olen;
2139f9e1ceaSPieter Noordhuis
2149f9e1ceaSPieter Noordhuis /* Key exists, check type */
21514ff5724Santirez if (checkType(c,o,OBJ_STRING))
2169f9e1ceaSPieter Noordhuis return;
2179f9e1ceaSPieter Noordhuis
2189f9e1ceaSPieter Noordhuis /* Return existing string length when setting nothing */
219ad1b4f4fSPieter Noordhuis olen = stringObjectLen(o);
2209f9e1ceaSPieter Noordhuis if (sdslen(value) == 0) {
2219f9e1ceaSPieter Noordhuis addReplyLongLong(c,olen);
2229f9e1ceaSPieter Noordhuis return;
2239f9e1ceaSPieter Noordhuis }
2249f9e1ceaSPieter Noordhuis
2259f9e1ceaSPieter Noordhuis /* Return when the resulting string exceeds allowed size */
22640eb548aSantirez if (checkStringLength(c,offset+sdslen(value)) != C_OK)
2279f9e1ceaSPieter Noordhuis return;
2289f9e1ceaSPieter Noordhuis
2299f9e1ceaSPieter Noordhuis /* Create a copy when the object is shared or encoded. */
230543ede03Santirez o = dbUnshareStringValue(c->db,c->argv[1],o);
2319f9e1ceaSPieter Noordhuis }
2329f9e1ceaSPieter Noordhuis
2339f9e1ceaSPieter Noordhuis if (sdslen(value) > 0) {
2349f9e1ceaSPieter Noordhuis o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
2359f9e1ceaSPieter Noordhuis memcpy((char*)o->ptr+offset,value,sdslen(value));
236cea8c5cdSantirez signalModifiedKey(c->db,c->argv[1]);
23732f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,
238fce016d3Santirez "setrange",c->argv[1],c->db->id);
2399f9e1ceaSPieter Noordhuis server.dirty++;
2409f9e1ceaSPieter Noordhuis }
2419f9e1ceaSPieter Noordhuis addReplyLongLong(c,sdslen(o->ptr));
2429f9e1ceaSPieter Noordhuis }
2439f9e1ceaSPieter Noordhuis
getrangeCommand(client * c)244554bd0e7Santirez void getrangeCommand(client *c) {
245ef11bcccSPieter Noordhuis robj *o;
246f0e306f4SMatt Stancliff long long start, end;
247ef11bcccSPieter Noordhuis char *str, llbuf[32];
248ef11bcccSPieter Noordhuis size_t strlen;
249ef11bcccSPieter Noordhuis
25040eb548aSantirez if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != C_OK)
251ef11bcccSPieter Noordhuis return;
25240eb548aSantirez if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != C_OK)
253ef11bcccSPieter Noordhuis return;
2540b537972Santirez if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
25514ff5724Santirez checkType(c,o,OBJ_STRING)) return;
256ef11bcccSPieter Noordhuis
25714ff5724Santirez if (o->encoding == OBJ_ENCODING_INT) {
258ef11bcccSPieter Noordhuis str = llbuf;
259ef11bcccSPieter Noordhuis strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
260ef11bcccSPieter Noordhuis } else {
261ef11bcccSPieter Noordhuis str = o->ptr;
262ef11bcccSPieter Noordhuis strlen = sdslen(str);
263ef11bcccSPieter Noordhuis }
264ef11bcccSPieter Noordhuis
265ef11bcccSPieter Noordhuis /* Convert negative indexes */
266*d4831e32Santirez if (start < 0 && end < 0 && start > end) {
267*d4831e32Santirez addReply(c,shared.emptybulk);
268*d4831e32Santirez return;
269*d4831e32Santirez }
270ef11bcccSPieter Noordhuis if (start < 0) start = strlen+start;
271ef11bcccSPieter Noordhuis if (end < 0) end = strlen+end;
272ef11bcccSPieter Noordhuis if (start < 0) start = 0;
273ef11bcccSPieter Noordhuis if (end < 0) end = 0;
274f0e306f4SMatt Stancliff if ((unsigned long long)end >= strlen) end = strlen-1;
275ef11bcccSPieter Noordhuis
276ef11bcccSPieter Noordhuis /* Precondition: end >= 0 && end < strlen, so the only condition where
277ef11bcccSPieter Noordhuis * nothing can be returned is: start > end. */
278b20df972SMatt Stancliff if (start > end || strlen == 0) {
2790b537972Santirez addReply(c,shared.emptybulk);
280ef11bcccSPieter Noordhuis } else {
281ef11bcccSPieter Noordhuis addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
282ef11bcccSPieter Noordhuis }
283ef11bcccSPieter Noordhuis }
284ef11bcccSPieter Noordhuis
mgetCommand(client * c)285554bd0e7Santirez void mgetCommand(client *c) {
286e2641e09Santirez int j;
287e2641e09Santirez
2880537e7bfSPieter Noordhuis addReplyMultiBulkLen(c,c->argc-1);
289e2641e09Santirez for (j = 1; j < c->argc; j++) {
290e2641e09Santirez robj *o = lookupKeyRead(c->db,c->argv[j]);
291e2641e09Santirez if (o == NULL) {
292e2641e09Santirez addReply(c,shared.nullbulk);
293e2641e09Santirez } else {
29414ff5724Santirez if (o->type != OBJ_STRING) {
295e2641e09Santirez addReply(c,shared.nullbulk);
296e2641e09Santirez } else {
297e2641e09Santirez addReplyBulk(c,o);
298e2641e09Santirez }
299e2641e09Santirez }
300e2641e09Santirez }
301e2641e09Santirez }
302e2641e09Santirez
msetGenericCommand(client * c,int nx)303554bd0e7Santirez void msetGenericCommand(client *c, int nx) {
304e2641e09Santirez int j, busykeys = 0;
305e2641e09Santirez
306e2641e09Santirez if ((c->argc % 2) == 0) {
3073ab20376SPieter Noordhuis addReplyError(c,"wrong number of arguments for MSET");
308e2641e09Santirez return;
309e2641e09Santirez }
310e2641e09Santirez /* Handle the NX flag. The MSETNX semantic is to return zero and don't
311e2641e09Santirez * set nothing at all if at least one already key exists. */
312e2641e09Santirez if (nx) {
313e2641e09Santirez for (j = 1; j < c->argc; j += 2) {
314e2641e09Santirez if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
315e2641e09Santirez busykeys++;
316e2641e09Santirez }
317e2641e09Santirez }
318e2641e09Santirez if (busykeys) {
319e2641e09Santirez addReply(c, shared.czero);
320e2641e09Santirez return;
321e2641e09Santirez }
322f85cd526Santirez }
323e2641e09Santirez
324e2641e09Santirez for (j = 1; j < c->argc; j += 2) {
325e2641e09Santirez c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
326f85cd526Santirez setKey(c->db,c->argv[j],c->argv[j+1]);
32732f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);
328e2641e09Santirez }
329e2641e09Santirez server.dirty += (c->argc-1)/2;
330e2641e09Santirez addReply(c, nx ? shared.cone : shared.ok);
331e2641e09Santirez }
332e2641e09Santirez
msetCommand(client * c)333554bd0e7Santirez void msetCommand(client *c) {
334e2641e09Santirez msetGenericCommand(c,0);
335e2641e09Santirez }
336e2641e09Santirez
msetnxCommand(client * c)337554bd0e7Santirez void msetnxCommand(client *c) {
338e2641e09Santirez msetGenericCommand(c,1);
339e2641e09Santirez }
340e2641e09Santirez
incrDecrCommand(client * c,long long incr)341554bd0e7Santirez void incrDecrCommand(client *c, long long incr) {
3429d7165e8Santirez long long value, oldvalue;
343f85cd526Santirez robj *o, *new;
344e2641e09Santirez
345e2641e09Santirez o = lookupKeyWrite(c->db,c->argv[1]);
34614ff5724Santirez if (o != NULL && checkType(c,o,OBJ_STRING)) return;
34740eb548aSantirez if (getLongLongFromObjectOrReply(c,o,&value,NULL) != C_OK) return;
348e2641e09Santirez
3499d7165e8Santirez oldvalue = value;
3507c96b467Santirez if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) ||
3517c96b467Santirez (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) {
3529d7165e8Santirez addReplyError(c,"increment or decrement would overflow");
3539d7165e8Santirez return;
3549d7165e8Santirez }
3557c96b467Santirez value += incr;
35616559b46Santirez
35714ff5724Santirez if (o && o->refcount == 1 && o->encoding == OBJ_ENCODING_INT &&
35832f80e2fSantirez (value < 0 || value >= OBJ_SHARED_INTEGERS) &&
35916559b46Santirez value >= LONG_MIN && value <= LONG_MAX)
36016559b46Santirez {
36116559b46Santirez new = o;
36216559b46Santirez o->ptr = (void*)((long)value);
36316559b46Santirez } else {
364f85cd526Santirez new = createStringObjectFromLongLong(value);
36516559b46Santirez if (o) {
366f85cd526Santirez dbOverwrite(c->db,c->argv[1],new);
36716559b46Santirez } else {
368f85cd526Santirez dbAdd(c->db,c->argv[1],new);
36916559b46Santirez }
37016559b46Santirez }
371cea8c5cdSantirez signalModifiedKey(c->db,c->argv[1]);
37232f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
373e2641e09Santirez server.dirty++;
374e2641e09Santirez addReply(c,shared.colon);
375f85cd526Santirez addReply(c,new);
376e2641e09Santirez addReply(c,shared.crlf);
377e2641e09Santirez }
378e2641e09Santirez
incrCommand(client * c)379554bd0e7Santirez void incrCommand(client *c) {
380e2641e09Santirez incrDecrCommand(c,1);
381e2641e09Santirez }
382e2641e09Santirez
decrCommand(client * c)383554bd0e7Santirez void decrCommand(client *c) {
384e2641e09Santirez incrDecrCommand(c,-1);
385e2641e09Santirez }
386e2641e09Santirez
incrbyCommand(client * c)387554bd0e7Santirez void incrbyCommand(client *c) {
388e2641e09Santirez long long incr;
389e2641e09Santirez
39040eb548aSantirez if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
391e2641e09Santirez incrDecrCommand(c,incr);
392e2641e09Santirez }
393e2641e09Santirez
decrbyCommand(client * c)394554bd0e7Santirez void decrbyCommand(client *c) {
395e2641e09Santirez long long incr;
396e2641e09Santirez
39740eb548aSantirez if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != C_OK) return;
398e2641e09Santirez incrDecrCommand(c,-incr);
399e2641e09Santirez }
400e2641e09Santirez
incrbyfloatCommand(client * c)401554bd0e7Santirez void incrbyfloatCommand(client *c) {
4025574b53eSantirez long double incr, value;
4035244d6e5Santirez robj *o, *new, *aux;
4045574b53eSantirez
4055574b53eSantirez o = lookupKeyWrite(c->db,c->argv[1]);
40614ff5724Santirez if (o != NULL && checkType(c,o,OBJ_STRING)) return;
40740eb548aSantirez if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != C_OK ||
40840eb548aSantirez getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != C_OK)
4095574b53eSantirez return;
4105574b53eSantirez
4115574b53eSantirez value += incr;
4125574b53eSantirez if (isnan(value) || isinf(value)) {
4135574b53eSantirez addReplyError(c,"increment would produce NaN or Infinity");
4145574b53eSantirez return;
4155574b53eSantirez }
41692c5ab40Santirez new = createStringObjectFromLongDouble(value,1);
4175574b53eSantirez if (o)
4185574b53eSantirez dbOverwrite(c->db,c->argv[1],new);
4195574b53eSantirez else
4205574b53eSantirez dbAdd(c->db,c->argv[1],new);
4215574b53eSantirez signalModifiedKey(c->db,c->argv[1]);
42232f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,"incrbyfloat",c->argv[1],c->db->id);
4235574b53eSantirez server.dirty++;
4245574b53eSantirez addReplyBulk(c,new);
4255244d6e5Santirez
4265244d6e5Santirez /* Always replicate INCRBYFLOAT as a SET command with the final value
427e50cdbe4Santirez * in order to make sure that differences in float precision or formatting
4285244d6e5Santirez * will not create differences in replicas or after an AOF restart. */
4295244d6e5Santirez aux = createStringObject("SET",3);
4305244d6e5Santirez rewriteClientCommandArgument(c,0,aux);
4315244d6e5Santirez decrRefCount(aux);
4325244d6e5Santirez rewriteClientCommandArgument(c,2,new);
4335574b53eSantirez }
4345574b53eSantirez
appendCommand(client * c)435554bd0e7Santirez void appendCommand(client *c) {
436e2641e09Santirez size_t totlen;
437076f88d6SPieter Noordhuis robj *o, *append;
438e2641e09Santirez
439e2641e09Santirez o = lookupKeyWrite(c->db,c->argv[1]);
440e2641e09Santirez if (o == NULL) {
441e2641e09Santirez /* Create the key */
4421333f98dSPieter Noordhuis c->argv[2] = tryObjectEncoding(c->argv[2]);
4431333f98dSPieter Noordhuis dbAdd(c->db,c->argv[1],c->argv[2]);
444e2641e09Santirez incrRefCount(c->argv[2]);
445e2641e09Santirez totlen = stringObjectLen(c->argv[2]);
446e2641e09Santirez } else {
4471333f98dSPieter Noordhuis /* Key exists, check type */
44814ff5724Santirez if (checkType(c,o,OBJ_STRING))
449e2641e09Santirez return;
450076f88d6SPieter Noordhuis
4511333f98dSPieter Noordhuis /* "append" is an argument, so always an sds */
4521333f98dSPieter Noordhuis append = c->argv[2];
4531333f98dSPieter Noordhuis totlen = stringObjectLen(o)+sdslen(append->ptr);
45440eb548aSantirez if (checkStringLength(c,totlen) != C_OK)
455076f88d6SPieter Noordhuis return;
456076f88d6SPieter Noordhuis
457076f88d6SPieter Noordhuis /* Append the value */
458543ede03Santirez o = dbUnshareStringValue(c->db,c->argv[1],o);
459076f88d6SPieter Noordhuis o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
460e2641e09Santirez totlen = sdslen(o->ptr);
461e2641e09Santirez }
462cea8c5cdSantirez signalModifiedKey(c->db,c->argv[1]);
46332f80e2fSantirez notifyKeyspaceEvent(NOTIFY_STRING,"append",c->argv[1],c->db->id);
464e2641e09Santirez server.dirty++;
465b70d3555SPieter Noordhuis addReplyLongLong(c,totlen);
466e2641e09Santirez }
467e2641e09Santirez
strlenCommand(client * c)468554bd0e7Santirez void strlenCommand(client *c) {
46980091bbaSantirez robj *o;
47080091bbaSantirez if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
47114ff5724Santirez checkType(c,o,OBJ_STRING)) return;
472ad1b4f4fSPieter Noordhuis addReplyLongLong(c,stringObjectLen(o));
4737ecd4644SPieter Noordhuis }
474