1 /* 2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 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. 16 * 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. 28 */ 29 30 #include "redis.h" 31 #include <math.h> /* isnan(), isinf() */ 32 33 /*----------------------------------------------------------------------------- 34 * String Commands 35 *----------------------------------------------------------------------------*/ 36 37 static int checkStringLength(redisClient *c, long long size) { 38 if (size > 512*1024*1024) { 39 addReplyError(c,"string exceeds maximum allowed size (512MB)"); 40 return REDIS_ERR; 41 } 42 return REDIS_OK; 43 } 44 45 void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) { 46 long long milliseconds = 0; /* initialized to avoid any harmness warning */ 47 48 if (expire) { 49 if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK) 50 return; 51 if (milliseconds <= 0) { 52 addReplyError(c,"invalid expire time in SETEX"); 53 return; 54 } 55 if (unit == UNIT_SECONDS) milliseconds *= 1000; 56 } 57 58 if (nx && lookupKeyWrite(c->db,key) != NULL) { 59 addReply(c,shared.czero); 60 return; 61 } 62 setKey(c->db,key,val); 63 server.dirty++; 64 if (expire) setExpire(c->db,key,mstime()+milliseconds); 65 addReply(c, nx ? shared.cone : shared.ok); 66 } 67 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); 71 } 72 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); 76 } 77 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); 81 } 82 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); 86 } 87 88 int getGenericCommand(redisClient *c) { 89 robj *o; 90 91 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) 92 return REDIS_OK; 93 94 if (o->type != REDIS_STRING) { 95 addReply(c,shared.wrongtypeerr); 96 return REDIS_ERR; 97 } else { 98 addReplyBulk(c,o); 99 return REDIS_OK; 100 } 101 } 102 103 void getCommand(redisClient *c) { 104 getGenericCommand(c); 105 } 106 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]); 111 server.dirty++; 112 } 113 114 void setrangeCommand(redisClient *c) { 115 robj *o; 116 long offset; 117 sds value = c->argv[3]->ptr; 118 119 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK) 120 return; 121 122 if (offset < 0) { 123 addReplyError(c,"offset is out of range"); 124 return; 125 } 126 127 o = lookupKeyWrite(c->db,c->argv[1]); 128 if (o == NULL) { 129 /* Return 0 when setting nothing on a non-existing string */ 130 if (sdslen(value) == 0) { 131 addReply(c,shared.czero); 132 return; 133 } 134 135 /* Return when the resulting string exceeds allowed size */ 136 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) 137 return; 138 139 o = createObject(REDIS_STRING,sdsempty()); 140 dbAdd(c->db,c->argv[1],o); 141 } else { 142 size_t olen; 143 144 /* Key exists, check type */ 145 if (checkType(c,o,REDIS_STRING)) 146 return; 147 148 /* Return existing string length when setting nothing */ 149 olen = stringObjectLen(o); 150 if (sdslen(value) == 0) { 151 addReplyLongLong(c,olen); 152 return; 153 } 154 155 /* Return when the resulting string exceeds allowed size */ 156 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) 157 return; 158 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); 165 } 166 } 167 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]); 172 server.dirty++; 173 } 174 addReplyLongLong(c,sdslen(o->ptr)); 175 } 176 177 void getrangeCommand(redisClient *c) { 178 robj *o; 179 long start, end; 180 char *str, llbuf[32]; 181 size_t strlen; 182 183 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) 184 return; 185 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK) 186 return; 187 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL || 188 checkType(c,o,REDIS_STRING)) return; 189 190 if (o->encoding == REDIS_ENCODING_INT) { 191 str = llbuf; 192 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); 193 } else { 194 str = o->ptr; 195 strlen = sdslen(str); 196 } 197 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; 204 205 /* Precondition: end >= 0 && end < strlen, so the only condition where 206 * nothing can be returned is: start > end. */ 207 if (start > end) { 208 addReply(c,shared.emptybulk); 209 } else { 210 addReplyBulkCBuffer(c,(char*)str+start,end-start+1); 211 } 212 } 213 214 void mgetCommand(redisClient *c) { 215 int j; 216 217 addReplyMultiBulkLen(c,c->argc-1); 218 for (j = 1; j < c->argc; j++) { 219 robj *o = lookupKeyRead(c->db,c->argv[j]); 220 if (o == NULL) { 221 addReply(c,shared.nullbulk); 222 } else { 223 if (o->type != REDIS_STRING) { 224 addReply(c,shared.nullbulk); 225 } else { 226 addReplyBulk(c,o); 227 } 228 } 229 } 230 } 231 232 void msetGenericCommand(redisClient *c, int nx) { 233 int j, busykeys = 0; 234 235 if ((c->argc % 2) == 0) { 236 addReplyError(c,"wrong number of arguments for MSET"); 237 return; 238 } 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. */ 241 if (nx) { 242 for (j = 1; j < c->argc; j += 2) { 243 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) { 244 busykeys++; 245 } 246 } 247 if (busykeys) { 248 addReply(c, shared.czero); 249 return; 250 } 251 } 252 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]); 256 } 257 server.dirty += (c->argc-1)/2; 258 addReply(c, nx ? shared.cone : shared.ok); 259 } 260 261 void msetCommand(redisClient *c) { 262 msetGenericCommand(c,0); 263 } 264 265 void msetnxCommand(redisClient *c) { 266 msetGenericCommand(c,1); 267 } 268 269 void incrDecrCommand(redisClient *c, long long incr) { 270 long long value, oldvalue; 271 robj *o, *new; 272 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; 276 277 oldvalue = value; 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"); 281 return; 282 } 283 value += incr; 284 new = createStringObjectFromLongLong(value); 285 if (o) 286 dbOverwrite(c->db,c->argv[1],new); 287 else 288 dbAdd(c->db,c->argv[1],new); 289 signalModifiedKey(c->db,c->argv[1]); 290 server.dirty++; 291 addReply(c,shared.colon); 292 addReply(c,new); 293 addReply(c,shared.crlf); 294 } 295 296 void incrCommand(redisClient *c) { 297 incrDecrCommand(c,1); 298 } 299 300 void decrCommand(redisClient *c) { 301 incrDecrCommand(c,-1); 302 } 303 304 void incrbyCommand(redisClient *c) { 305 long long incr; 306 307 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return; 308 incrDecrCommand(c,incr); 309 } 310 311 void decrbyCommand(redisClient *c) { 312 long long incr; 313 314 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return; 315 incrDecrCommand(c,-incr); 316 } 317 318 void incrbyfloatCommand(redisClient *c) { 319 long double incr, value; 320 robj *o, *new, *aux; 321 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) 326 return; 327 328 value += incr; 329 if (isnan(value) || isinf(value)) { 330 addReplyError(c,"increment would produce NaN or Infinity"); 331 return; 332 } 333 new = createStringObjectFromLongDouble(value); 334 if (o) 335 dbOverwrite(c->db,c->argv[1],new); 336 else 337 dbAdd(c->db,c->argv[1],new); 338 signalModifiedKey(c->db,c->argv[1]); 339 server.dirty++; 340 addReplyBulk(c,new); 341 342 /* Always replicate INCRBYFLOAT as a SET command with the final value 343 * in order to make sure that differences in float precision or formatting 344 * will not create differences in replicas or after an AOF restart. */ 345 aux = createStringObject("SET",3); 346 rewriteClientCommandArgument(c,0,aux); 347 decrRefCount(aux); 348 rewriteClientCommandArgument(c,2,new); 349 } 350 351 void appendCommand(redisClient *c) { 352 size_t totlen; 353 robj *o, *append; 354 355 o = lookupKeyWrite(c->db,c->argv[1]); 356 if (o == NULL) { 357 /* Create the key */ 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]); 362 } else { 363 /* Key exists, check type */ 364 if (checkType(c,o,REDIS_STRING)) 365 return; 366 367 /* "append" is an argument, so always an sds */ 368 append = c->argv[2]; 369 totlen = stringObjectLen(o)+sdslen(append->ptr); 370 if (checkStringLength(c,totlen) != REDIS_OK) 371 return; 372 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); 379 } 380 381 /* Append the value */ 382 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr)); 383 totlen = sdslen(o->ptr); 384 } 385 signalModifiedKey(c->db,c->argv[1]); 386 server.dirty++; 387 addReplyLongLong(c,totlen); 388 } 389 390 void strlenCommand(redisClient *c) { 391 robj *o; 392 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 393 checkType(c,o,REDIS_STRING)) return; 394 addReplyLongLong(c,stringObjectLen(o)); 395 } 396