xref: /redis-3.2.3/src/t_string.c (revision 4942145d)
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