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