]> git.saurik.com Git - redis.git/blame - src/t_string.c
make test depends on redis-check-aof as well.
[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) {
e2641e09 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) {
3ab20376 22 addReplyError(c,"invalid expire time in SETEX");
e2641e09 23 return;
24 }
25 }
26
f85cd526 27 if (lookupKeyWrite(c->db,key) != NULL && nx) {
28 addReply(c,shared.czero);
29 return;
e2641e09 30 }
f85cd526 31 setKey(c->db,key,val);
e2641e09 32 server.dirty++;
e2641e09 33 if (expire) setExpire(c->db,key,time(NULL)+seconds);
34 addReply(c, nx ? shared.cone : shared.ok);
35}
36
37void setCommand(redisClient *c) {
75b41de8 38 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 39 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
40}
41
42void setnxCommand(redisClient *c) {
75b41de8 43 c->argv[2] = tryObjectEncoding(c->argv[2]);
e2641e09 44 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
45}
46
47void setexCommand(redisClient *c) {
75b41de8 48 c->argv[3] = tryObjectEncoding(c->argv[3]);
e2641e09 49 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
50}
51
52int 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
67void getCommand(redisClient *c) {
68 getGenericCommand(c);
69}
70
71void getsetCommand(redisClient *c) {
72 if (getGenericCommand(c) == REDIS_ERR) return;
75b41de8 73 c->argv[2] = tryObjectEncoding(c->argv[2]);
f85cd526 74 setKey(c->db,c->argv[1],c->argv[2]);
e2641e09 75 server.dirty++;
3c1bf495
PN
76}
77
78static 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
648e9654 85 /* Limit offset to 512MB in bytes */
86 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
3c1bf495
PN
87 {
88 addReplyError(c,err);
89 return REDIS_ERR;
90 }
91
92 *offset = (size_t)loffset;
93 return REDIS_OK;
94}
95
96void setbitCommand(redisClient *c) {
97 robj *o;
eae33c1c 98 char *err = "bit is not an integer or out of range";
3c1bf495 99 size_t bitoffset;
30407e1f
PN
100 int byte, bit;
101 int byteval, bitval;
102 long on;
3c1bf495
PN
103
104 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
105 return;
106
30407e1f 107 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
eae33c1c
PN
108 return;
109
30407e1f
PN
110 /* Bits can only be set or cleared... */
111 if (on & ~1) {
eae33c1c 112 addReplyError(c,err);
3c1bf495
PN
113 return;
114 }
115
116 o = lookupKeyWrite(c->db,c->argv[1]);
117 if (o == NULL) {
eae33c1c 118 o = createObject(REDIS_STRING,sdsempty());
3c1bf495
PN
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);
f85cd526 128 dbOverwrite(c->db,c->argv[1],o);
3c1bf495 129 }
3c1bf495 130 }
eae33c1c 131
30407e1f 132 /* Grow sds value to the right length if necessary */
eae33c1c 133 byte = bitoffset >> 3;
cc209063 134 o->ptr = sdsgrowzero(o->ptr,byte+1);
eae33c1c 135
30407e1f
PN
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;
cea8c5cd 145 signalModifiedKey(c->db,c->argv[1]);
3c1bf495 146 server.dirty++;
30407e1f 147 addReply(c, bitval ? shared.cone : shared.czero);
3c1bf495
PN
148}
149
150void getbitCommand(redisClient *c) {
151 robj *o;
3c1bf495 152 char llbuf[32];
30407e1f
PN
153 size_t bitoffset;
154 size_t byte, bit;
155 size_t bitval = 0;
3c1bf495
PN
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;
30407e1f 164 bit = 7 - (bitoffset & 0x7);
3c1bf495
PN
165 if (o->encoding != REDIS_ENCODING_RAW) {
166 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
30407e1f 167 bitval = llbuf[byte] & (1 << bit);
3c1bf495
PN
168 } else {
169 if (byte < sdslen(o->ptr))
30407e1f 170 bitval = ((char*)o->ptr)[byte] & (1 << bit);
3c1bf495 171 }
30407e1f
PN
172
173 addReply(c, bitval ? shared.cone : shared.czero);
e2641e09 174}
175
9f9e1cea
PN
176void 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
8f8eeffe
PN
184 if (offset < 0) {
185 addReplyError(c,"offset is out of range");
186 return;
187 }
188
9f9e1cea
PN
189 o = lookupKeyWrite(c->db,c->argv[1]);
190 if (o == NULL) {
9f9e1cea
PN
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 {
ad1b4f4f 204 size_t olen;
9f9e1cea
PN
205
206 /* Key exists, check type */
207 if (checkType(c,o,REDIS_STRING))
208 return;
209
9f9e1cea 210 /* Return existing string length when setting nothing */
ad1b4f4f 211 olen = stringObjectLen(o);
9f9e1cea
PN
212 if (sdslen(value) == 0) {
213 addReplyLongLong(c,olen);
214 return;
215 }
216
9f9e1cea
PN
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);
f85cd526 226 dbOverwrite(c->db,c->argv[1],o);
9f9e1cea
PN
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));
cea8c5cd 233 signalModifiedKey(c->db,c->argv[1]);
9f9e1cea
PN
234 server.dirty++;
235 }
236 addReplyLongLong(c,sdslen(o->ptr));
237}
238
ef11bccc
PN
239void 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;
0b537972 249 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
ef11bccc
PN
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) {
0b537972 270 addReply(c,shared.emptybulk);
ef11bccc
PN
271 } else {
272 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
273 }
274}
275
e2641e09 276void mgetCommand(redisClient *c) {
277 int j;
278
0537e7bf 279 addReplyMultiBulkLen(c,c->argc-1);
e2641e09 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
294void msetGenericCommand(redisClient *c, int nx) {
295 int j, busykeys = 0;
296
297 if ((c->argc % 2) == 0) {
3ab20376 298 addReplyError(c,"wrong number of arguments for MSET");
e2641e09 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 }
f85cd526 309 if (busykeys) {
310 addReply(c, shared.czero);
311 return;
312 }
e2641e09 313 }
314
315 for (j = 1; j < c->argc; j += 2) {
316 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
f85cd526 317 setKey(c->db,c->argv[j],c->argv[j+1]);
e2641e09 318 }
319 server.dirty += (c->argc-1)/2;
320 addReply(c, nx ? shared.cone : shared.ok);
321}
322
323void msetCommand(redisClient *c) {
324 msetGenericCommand(c,0);
325}
326
327void msetnxCommand(redisClient *c) {
328 msetGenericCommand(c,1);
329}
330
331void incrDecrCommand(redisClient *c, long long incr) {
9d7165e8 332 long long value, oldvalue;
f85cd526 333 robj *o, *new;
e2641e09 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
9d7165e8 339 oldvalue = value;
e2641e09 340 value += incr;
9d7165e8 341 if ((incr < 0 && value > oldvalue) || (incr > 0 && value < oldvalue)) {
342 addReplyError(c,"increment or decrement would overflow");
343 return;
344 }
f85cd526 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);
cea8c5cd 350 signalModifiedKey(c->db,c->argv[1]);
e2641e09 351 server.dirty++;
352 addReply(c,shared.colon);
f85cd526 353 addReply(c,new);
e2641e09 354 addReply(c,shared.crlf);
355}
356
357void incrCommand(redisClient *c) {
358 incrDecrCommand(c,1);
359}
360
361void decrCommand(redisClient *c) {
362 incrDecrCommand(c,-1);
363}
364
365void 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
372void 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
379void appendCommand(redisClient *c) {
e2641e09 380 size_t totlen;
076f88d6 381 robj *o, *append;
e2641e09 382
383 o = lookupKeyWrite(c->db,c->argv[1]);
384 if (o == NULL) {
385 /* Create the key */
1333f98d
PN
386 c->argv[2] = tryObjectEncoding(c->argv[2]);
387 dbAdd(c->db,c->argv[1],c->argv[2]);
e2641e09 388 incrRefCount(c->argv[2]);
389 totlen = stringObjectLen(c->argv[2]);
390 } else {
1333f98d
PN
391 /* Key exists, check type */
392 if (checkType(c,o,REDIS_STRING))
e2641e09 393 return;
076f88d6 394
1333f98d
PN
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)
076f88d6 399 return;
076f88d6
PN
400
401 /* If the object is shared or encoded, we have to make a copy */
e2641e09 402 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
403 robj *decoded = getDecodedObject(o);
e2641e09 404 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
405 decrRefCount(decoded);
f85cd526 406 dbOverwrite(c->db,c->argv[1],o);
e2641e09 407 }
076f88d6
PN
408
409 /* Append the value */
410 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
e2641e09 411 totlen = sdslen(o->ptr);
412 }
cea8c5cd 413 signalModifiedKey(c->db,c->argv[1]);
e2641e09 414 server.dirty++;
b70d3555 415 addReplyLongLong(c,totlen);
e2641e09 416}
417
80091bba 418void strlenCommand(redisClient *c) {
419 robj *o;
80091bba 420 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
421 checkType(c,o,REDIS_STRING)) return;
ad1b4f4f 422 addReplyLongLong(c,stringObjectLen(o));
80091bba 423}
7ecd4644 424