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