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