]> git.saurik.com Git - redis.git/blame - src/t_string.c
Fix for ZUNIONSTORE bug when there is an empty set among input sets. Regression test...
[redis.git] / src / t_string.c
CommitLineData
e2641e09 1#include "redis.h"
2
3/*-----------------------------------------------------------------------------
4 * String Commands
5 *----------------------------------------------------------------------------*/
6
9f9e1cea
PN
7static 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
e2641e09 15void 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) {
3ab20376 23 addReplyError(c,"invalid expire time in SETEX");
e2641e09 24 return;
25 }
26 }
27
e2641e09 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 }
cea8c5cd 40 signalModifiedKey(c->db,key);
e2641e09 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
47void setCommand(redisClient *c) {
75b41de8 48 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 49 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
50}
51
52void setnxCommand(redisClient *c) {
75b41de8 53 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 54 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
55}
56
57void setexCommand(redisClient *c) {
75b41de8 58 c->argv[3] = tryObjectEncoding(c->argv[3]);
e2641e09 59 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
60}
61
62int 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
77void getCommand(redisClient *c) {
78 getGenericCommand(c);
79}
80
81void getsetCommand(redisClient *c) {
82 if (getGenericCommand(c) == REDIS_ERR) return;
75b41de8 83 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 84 dbReplace(c->db,c->argv[1],c->argv[2]);
85 incrRefCount(c->argv[2]);
cea8c5cd 86 signalModifiedKey(c->db,c->argv[1]);
e2641e09 87 server.dirty++;
88 removeExpire(c->db,c->argv[1]);
3c1bf495
PN
89}
90
91static 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
648e9654 98 /* Limit offset to 512MB in bytes */
99 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
3c1bf495
PN
100 {
101 addReplyError(c,err);
102 return REDIS_ERR;
103 }
104
105 *offset = (size_t)loffset;
106 return REDIS_OK;
107}
108
109void setbitCommand(redisClient *c) {
110 robj *o;
eae33c1c 111 char *err = "bit is not an integer or out of range";
3c1bf495 112 size_t bitoffset;
30407e1f
PN
113 int byte, bit;
114 int byteval, bitval;
115 long on;
3c1bf495
PN
116
117 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
118 return;
119
30407e1f 120 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
eae33c1c
PN
121 return;
122
30407e1f
PN
123 /* Bits can only be set or cleared... */
124 if (on & ~1) {
eae33c1c 125 addReplyError(c,err);
3c1bf495
PN
126 return;
127 }
128
129 o = lookupKeyWrite(c->db,c->argv[1]);
130 if (o == NULL) {
eae33c1c 131 o = createObject(REDIS_STRING,sdsempty());
3c1bf495
PN
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 }
3c1bf495 143 }
eae33c1c 144
30407e1f 145 /* Grow sds value to the right length if necessary */
eae33c1c 146 byte = bitoffset >> 3;
cc209063 147 o->ptr = sdsgrowzero(o->ptr,byte+1);
eae33c1c 148
30407e1f
PN
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;
cea8c5cd 158 signalModifiedKey(c->db,c->argv[1]);
3c1bf495 159 server.dirty++;
30407e1f 160 addReply(c, bitval ? shared.cone : shared.czero);
3c1bf495
PN
161}
162
163void getbitCommand(redisClient *c) {
164 robj *o;
3c1bf495 165 char llbuf[32];
30407e1f
PN
166 size_t bitoffset;
167 size_t byte, bit;
168 size_t bitval = 0;
3c1bf495
PN
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;
30407e1f 177 bit = 7 - (bitoffset & 0x7);
3c1bf495
PN
178 if (o->encoding != REDIS_ENCODING_RAW) {
179 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
30407e1f 180 bitval = llbuf[byte] & (1 << bit);
3c1bf495
PN
181 } else {
182 if (byte < sdslen(o->ptr))
30407e1f 183 bitval = ((char*)o->ptr)[byte] & (1 << bit);
3c1bf495 184 }
30407e1f
PN
185
186 addReply(c, bitval ? shared.cone : shared.czero);
e2641e09 187}
188
9f9e1cea
PN
189void 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
8f8eeffe
PN
197 if (offset < 0) {
198 addReplyError(c,"offset is out of range");
199 return;
200 }
201
9f9e1cea
PN
202 o = lookupKeyWrite(c->db,c->argv[1]);
203 if (o == NULL) {
9f9e1cea
PN
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 {
ad1b4f4f 217 size_t olen;
9f9e1cea
PN
218
219 /* Key exists, check type */
220 if (checkType(c,o,REDIS_STRING))
221 return;
222
9f9e1cea 223 /* Return existing string length when setting nothing */
ad1b4f4f 224 olen = stringObjectLen(o);
9f9e1cea
PN
225 if (sdslen(value) == 0) {
226 addReplyLongLong(c,olen);
227 return;
228 }
229
9f9e1cea
PN
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));
cea8c5cd 246 signalModifiedKey(c->db,c->argv[1]);
9f9e1cea
PN
247 server.dirty++;
248 }
249 addReplyLongLong(c,sdslen(o->ptr));
250}
251
ef11bccc
PN
252void 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;
0b537972 262 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
ef11bccc
PN
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) {
0b537972 283 addReply(c,shared.emptybulk);
ef11bccc
PN
284 } else {
285 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
286 }
287}
288
e2641e09 289void mgetCommand(redisClient *c) {
290 int j;
291
0537e7bf 292 addReplyMultiBulkLen(c,c->argc-1);
e2641e09 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
307void msetGenericCommand(redisClient *c, int nx) {
308 int j, busykeys = 0;
309
310 if ((c->argc % 2) == 0) {
3ab20376 311 addReplyError(c,"wrong number of arguments for MSET");
e2641e09 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]);
cea8c5cd 333 signalModifiedKey(c->db,c->argv[j]);
e2641e09 334 }
335 server.dirty += (c->argc-1)/2;
336 addReply(c, nx ? shared.cone : shared.ok);
337}
338
339void msetCommand(redisClient *c) {
340 msetGenericCommand(c,0);
341}
342
343void msetnxCommand(redisClient *c) {
344 msetGenericCommand(c,1);
345}
346
347void incrDecrCommand(redisClient *c, long long incr) {
9d7165e8 348 long long value, oldvalue;
e2641e09 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
9d7165e8 355 oldvalue = value;
e2641e09 356 value += incr;
9d7165e8 357 if ((incr < 0 && value > oldvalue) || (incr > 0 && value < oldvalue)) {
358 addReplyError(c,"increment or decrement would overflow");
359 return;
360 }
e2641e09 361 o = createStringObjectFromLongLong(value);
362 dbReplace(c->db,c->argv[1],o);
cea8c5cd 363 signalModifiedKey(c->db,c->argv[1]);
e2641e09 364 server.dirty++;
365 addReply(c,shared.colon);
366 addReply(c,o);
367 addReply(c,shared.crlf);
368}
369
370void incrCommand(redisClient *c) {
371 incrDecrCommand(c,1);
372}
373
374void decrCommand(redisClient *c) {
375 incrDecrCommand(c,-1);
376}
377
378void 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
385void 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
392void appendCommand(redisClient *c) {
e2641e09 393 size_t totlen;
076f88d6 394 robj *o, *append;
e2641e09 395
396 o = lookupKeyWrite(c->db,c->argv[1]);
397 if (o == NULL) {
398 /* Create the key */
1333f98d
PN
399 c->argv[2] = tryObjectEncoding(c->argv[2]);
400 dbAdd(c->db,c->argv[1],c->argv[2]);
e2641e09 401 incrRefCount(c->argv[2]);
402 totlen = stringObjectLen(c->argv[2]);
403 } else {
1333f98d
PN
404 /* Key exists, check type */
405 if (checkType(c,o,REDIS_STRING))
e2641e09 406 return;
076f88d6 407
1333f98d
PN
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)
076f88d6 412 return;
076f88d6
PN
413
414 /* If the object is shared or encoded, we have to make a copy */
e2641e09 415 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
416 robj *decoded = getDecodedObject(o);
e2641e09 417 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
418 decrRefCount(decoded);
419 dbReplace(c->db,c->argv[1],o);
420 }
076f88d6
PN
421
422 /* Append the value */
423 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
e2641e09 424 totlen = sdslen(o->ptr);
425 }
cea8c5cd 426 signalModifiedKey(c->db,c->argv[1]);
e2641e09 427 server.dirty++;
b70d3555 428 addReplyLongLong(c,totlen);
e2641e09 429}
430
80091bba 431void strlenCommand(redisClient *c) {
432 robj *o;
80091bba 433 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
434 checkType(c,o,REDIS_STRING)) return;
ad1b4f4f 435 addReplyLongLong(c,stringObjectLen(o));
80091bba 436}
7ecd4644 437