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