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