1 #include "redis.h" 2 #include <math.h> /* isnan(), isinf() */ 3 4 /*----------------------------------------------------------------------------- 5 * String Commands 6 *----------------------------------------------------------------------------*/ 7 8 static int checkStringLength(redisClient *c, long long size) { 9 if (size > 512*1024*1024) { 10 addReplyError(c,"string exceeds maximum allowed size (512MB)"); 11 return REDIS_ERR; 12 } 13 return REDIS_OK; 14 } 15 16 void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) { 17 long long milliseconds = 0; /* initialized to avoid an harmness warning */ 18 19 if (expire) { 20 if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK) 21 return; 22 if (milliseconds <= 0) { 23 addReplyError(c,"invalid expire time in SETEX"); 24 return; 25 } 26 if (unit == UNIT_SECONDS) milliseconds *= 1000; 27 } 28 29 if (lookupKeyWrite(c->db,key) != NULL && nx) { 30 addReply(c,shared.czero); 31 return; 32 } 33 setKey(c->db,key,val); 34 server.dirty++; 35 if (expire) setExpire(c->db,key,mstime()+milliseconds); 36 addReply(c, nx ? shared.cone : shared.ok); 37 } 38 39 void setCommand(redisClient *c) { 40 c->argv[2] = tryObjectEncoding(c->argv[2]); 41 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL,0); 42 } 43 44 void setnxCommand(redisClient *c) { 45 c->argv[2] = tryObjectEncoding(c->argv[2]); 46 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL,0); 47 } 48 49 void setexCommand(redisClient *c) { 50 c->argv[3] = tryObjectEncoding(c->argv[3]); 51 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_SECONDS); 52 } 53 54 void psetexCommand(redisClient *c) { 55 c->argv[3] = tryObjectEncoding(c->argv[3]); 56 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2],UNIT_MILLISECONDS); 57 } 58 59 int getGenericCommand(redisClient *c) { 60 robj *o; 61 62 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) 63 return REDIS_OK; 64 65 if (o->type != REDIS_STRING) { 66 addReply(c,shared.wrongtypeerr); 67 return REDIS_ERR; 68 } else { 69 addReplyBulk(c,o); 70 return REDIS_OK; 71 } 72 } 73 74 void getCommand(redisClient *c) { 75 getGenericCommand(c); 76 } 77 78 void getsetCommand(redisClient *c) { 79 if (getGenericCommand(c) == REDIS_ERR) return; 80 c->argv[2] = tryObjectEncoding(c->argv[2]); 81 setKey(c->db,c->argv[1],c->argv[2]); 82 server.dirty++; 83 } 84 85 static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) { 86 long long loffset; 87 char *err = "bit offset is not an integer or out of range"; 88 89 if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK) 90 return REDIS_ERR; 91 92 /* Limit offset to 512MB in bytes */ 93 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024)) 94 { 95 addReplyError(c,err); 96 return REDIS_ERR; 97 } 98 99 *offset = (size_t)loffset; 100 return REDIS_OK; 101 } 102 103 void setbitCommand(redisClient *c) { 104 robj *o; 105 char *err = "bit is not an integer or out of range"; 106 size_t bitoffset; 107 int byte, bit; 108 int byteval, bitval; 109 long on; 110 111 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK) 112 return; 113 114 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK) 115 return; 116 117 /* Bits can only be set or cleared... */ 118 if (on & ~1) { 119 addReplyError(c,err); 120 return; 121 } 122 123 o = lookupKeyWrite(c->db,c->argv[1]); 124 if (o == NULL) { 125 o = createObject(REDIS_STRING,sdsempty()); 126 dbAdd(c->db,c->argv[1],o); 127 } else { 128 if (checkType(c,o,REDIS_STRING)) return; 129 130 /* Create a copy when the object is shared or encoded. */ 131 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) { 132 robj *decoded = getDecodedObject(o); 133 o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); 134 decrRefCount(decoded); 135 dbOverwrite(c->db,c->argv[1],o); 136 } 137 } 138 139 /* Grow sds value to the right length if necessary */ 140 byte = bitoffset >> 3; 141 o->ptr = sdsgrowzero(o->ptr,byte+1); 142 143 /* Get current values */ 144 byteval = ((char*)o->ptr)[byte]; 145 bit = 7 - (bitoffset & 0x7); 146 bitval = byteval & (1 << bit); 147 148 /* Update byte with new bit value and return original value */ 149 byteval &= ~(1 << bit); 150 byteval |= ((on & 0x1) << bit); 151 ((char*)o->ptr)[byte] = byteval; 152 signalModifiedKey(c->db,c->argv[1]); 153 server.dirty++; 154 addReply(c, bitval ? shared.cone : shared.czero); 155 } 156 157 void getbitCommand(redisClient *c) { 158 robj *o; 159 char llbuf[32]; 160 size_t bitoffset; 161 size_t byte, bit; 162 size_t bitval = 0; 163 164 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK) 165 return; 166 167 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 168 checkType(c,o,REDIS_STRING)) return; 169 170 byte = bitoffset >> 3; 171 bit = 7 - (bitoffset & 0x7); 172 if (o->encoding != REDIS_ENCODING_RAW) { 173 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr)) 174 bitval = llbuf[byte] & (1 << bit); 175 } else { 176 if (byte < sdslen(o->ptr)) 177 bitval = ((char*)o->ptr)[byte] & (1 << bit); 178 } 179 180 addReply(c, bitval ? shared.cone : shared.czero); 181 } 182 183 void setrangeCommand(redisClient *c) { 184 robj *o; 185 long offset; 186 sds value = c->argv[3]->ptr; 187 188 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK) 189 return; 190 191 if (offset < 0) { 192 addReplyError(c,"offset is out of range"); 193 return; 194 } 195 196 o = lookupKeyWrite(c->db,c->argv[1]); 197 if (o == NULL) { 198 /* Return 0 when setting nothing on a non-existing string */ 199 if (sdslen(value) == 0) { 200 addReply(c,shared.czero); 201 return; 202 } 203 204 /* Return when the resulting string exceeds allowed size */ 205 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) 206 return; 207 208 o = createObject(REDIS_STRING,sdsempty()); 209 dbAdd(c->db,c->argv[1],o); 210 } else { 211 size_t olen; 212 213 /* Key exists, check type */ 214 if (checkType(c,o,REDIS_STRING)) 215 return; 216 217 /* Return existing string length when setting nothing */ 218 olen = stringObjectLen(o); 219 if (sdslen(value) == 0) { 220 addReplyLongLong(c,olen); 221 return; 222 } 223 224 /* Return when the resulting string exceeds allowed size */ 225 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK) 226 return; 227 228 /* Create a copy when the object is shared or encoded. */ 229 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) { 230 robj *decoded = getDecodedObject(o); 231 o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); 232 decrRefCount(decoded); 233 dbOverwrite(c->db,c->argv[1],o); 234 } 235 } 236 237 if (sdslen(value) > 0) { 238 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value)); 239 memcpy((char*)o->ptr+offset,value,sdslen(value)); 240 signalModifiedKey(c->db,c->argv[1]); 241 server.dirty++; 242 } 243 addReplyLongLong(c,sdslen(o->ptr)); 244 } 245 246 void getrangeCommand(redisClient *c) { 247 robj *o; 248 long start, end; 249 char *str, llbuf[32]; 250 size_t strlen; 251 252 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) 253 return; 254 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK) 255 return; 256 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL || 257 checkType(c,o,REDIS_STRING)) return; 258 259 if (o->encoding == REDIS_ENCODING_INT) { 260 str = llbuf; 261 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); 262 } else { 263 str = o->ptr; 264 strlen = sdslen(str); 265 } 266 267 /* Convert negative indexes */ 268 if (start < 0) start = strlen+start; 269 if (end < 0) end = strlen+end; 270 if (start < 0) start = 0; 271 if (end < 0) end = 0; 272 if ((unsigned)end >= strlen) end = strlen-1; 273 274 /* Precondition: end >= 0 && end < strlen, so the only condition where 275 * nothing can be returned is: start > end. */ 276 if (start > end) { 277 addReply(c,shared.emptybulk); 278 } else { 279 addReplyBulkCBuffer(c,(char*)str+start,end-start+1); 280 } 281 } 282 283 void mgetCommand(redisClient *c) { 284 int j; 285 286 addReplyMultiBulkLen(c,c->argc-1); 287 for (j = 1; j < c->argc; j++) { 288 robj *o = lookupKeyRead(c->db,c->argv[j]); 289 if (o == NULL) { 290 addReply(c,shared.nullbulk); 291 } else { 292 if (o->type != REDIS_STRING) { 293 addReply(c,shared.nullbulk); 294 } else { 295 addReplyBulk(c,o); 296 } 297 } 298 } 299 } 300 301 void msetGenericCommand(redisClient *c, int nx) { 302 int j, busykeys = 0; 303 304 if ((c->argc % 2) == 0) { 305 addReplyError(c,"wrong number of arguments for MSET"); 306 return; 307 } 308 /* Handle the NX flag. The MSETNX semantic is to return zero and don't 309 * set nothing at all if at least one already key exists. */ 310 if (nx) { 311 for (j = 1; j < c->argc; j += 2) { 312 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) { 313 busykeys++; 314 } 315 } 316 if (busykeys) { 317 addReply(c, shared.czero); 318 return; 319 } 320 } 321 322 for (j = 1; j < c->argc; j += 2) { 323 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]); 324 setKey(c->db,c->argv[j],c->argv[j+1]); 325 } 326 server.dirty += (c->argc-1)/2; 327 addReply(c, nx ? shared.cone : shared.ok); 328 } 329 330 void msetCommand(redisClient *c) { 331 msetGenericCommand(c,0); 332 } 333 334 void msetnxCommand(redisClient *c) { 335 msetGenericCommand(c,1); 336 } 337 338 void incrDecrCommand(redisClient *c, long long incr) { 339 long long value, oldvalue; 340 robj *o, *new; 341 342 o = lookupKeyWrite(c->db,c->argv[1]); 343 if (o != NULL && checkType(c,o,REDIS_STRING)) return; 344 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return; 345 346 oldvalue = value; 347 value += incr; 348 if ((incr < 0 && value > oldvalue) || (incr > 0 && value < oldvalue)) { 349 addReplyError(c,"increment or decrement would overflow"); 350 return; 351 } 352 new = createStringObjectFromLongLong(value); 353 if (o) 354 dbOverwrite(c->db,c->argv[1],new); 355 else 356 dbAdd(c->db,c->argv[1],new); 357 signalModifiedKey(c->db,c->argv[1]); 358 server.dirty++; 359 addReply(c,shared.colon); 360 addReply(c,new); 361 addReply(c,shared.crlf); 362 } 363 364 void incrCommand(redisClient *c) { 365 incrDecrCommand(c,1); 366 } 367 368 void decrCommand(redisClient *c) { 369 incrDecrCommand(c,-1); 370 } 371 372 void incrbyCommand(redisClient *c) { 373 long long incr; 374 375 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return; 376 incrDecrCommand(c,incr); 377 } 378 379 void decrbyCommand(redisClient *c) { 380 long long incr; 381 382 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return; 383 incrDecrCommand(c,-incr); 384 } 385 386 void incrbyfloatCommand(redisClient *c) { 387 long double incr, value; 388 robj *o, *new, *aux; 389 390 o = lookupKeyWrite(c->db,c->argv[1]); 391 if (o != NULL && checkType(c,o,REDIS_STRING)) return; 392 if (getLongDoubleFromObjectOrReply(c,o,&value,NULL) != REDIS_OK || 393 getLongDoubleFromObjectOrReply(c,c->argv[2],&incr,NULL) != REDIS_OK) 394 return; 395 396 value += incr; 397 if (isnan(value) || isinf(value)) { 398 addReplyError(c,"increment would produce NaN or Infinity"); 399 return; 400 } 401 new = createStringObjectFromLongDouble(value); 402 if (o) 403 dbOverwrite(c->db,c->argv[1],new); 404 else 405 dbAdd(c->db,c->argv[1],new); 406 signalModifiedKey(c->db,c->argv[1]); 407 server.dirty++; 408 addReplyBulk(c,new); 409 410 /* Always replicate INCRBYFLOAT as a SET command with the final value 411 * in order to make sure that differences in float pricision or formatting 412 * will not create differences in replicas or after an AOF restart. */ 413 aux = createStringObject("SET",3); 414 rewriteClientCommandArgument(c,0,aux); 415 decrRefCount(aux); 416 rewriteClientCommandArgument(c,2,new); 417 } 418 419 void appendCommand(redisClient *c) { 420 size_t totlen; 421 robj *o, *append; 422 423 o = lookupKeyWrite(c->db,c->argv[1]); 424 if (o == NULL) { 425 /* Create the key */ 426 c->argv[2] = tryObjectEncoding(c->argv[2]); 427 dbAdd(c->db,c->argv[1],c->argv[2]); 428 incrRefCount(c->argv[2]); 429 totlen = stringObjectLen(c->argv[2]); 430 } else { 431 /* Key exists, check type */ 432 if (checkType(c,o,REDIS_STRING)) 433 return; 434 435 /* "append" is an argument, so always an sds */ 436 append = c->argv[2]; 437 totlen = stringObjectLen(o)+sdslen(append->ptr); 438 if (checkStringLength(c,totlen) != REDIS_OK) 439 return; 440 441 /* If the object is shared or encoded, we have to make a copy */ 442 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) { 443 robj *decoded = getDecodedObject(o); 444 o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); 445 decrRefCount(decoded); 446 dbOverwrite(c->db,c->argv[1],o); 447 } 448 449 /* Append the value */ 450 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr)); 451 totlen = sdslen(o->ptr); 452 } 453 signalModifiedKey(c->db,c->argv[1]); 454 server.dirty++; 455 addReplyLongLong(c,totlen); 456 } 457 458 void strlenCommand(redisClient *c) { 459 robj *o; 460 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 461 checkType(c,o,REDIS_STRING)) return; 462 addReplyLongLong(c,stringObjectLen(o)); 463 } 464 465