]> git.saurik.com Git - redis.git/blame - src/t_string.c
DB API refactoring. The changes were designed together with Pieter Noordhuis.
[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++;
76 removeExpire(c->db,c->argv[1]);
3c1bf495
PN
77}
78
79static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
80 long long loffset;
81 char *err = "bit offset is not an integer or out of range";
82
83 if (getLongLongFromObjectOrReply(c,o,&loffset,err) != REDIS_OK)
84 return REDIS_ERR;
85
648e9654 86 /* Limit offset to 512MB in bytes */
87 if ((loffset < 0) || ((unsigned long long)loffset >> 3) >= (512*1024*1024))
3c1bf495
PN
88 {
89 addReplyError(c,err);
90 return REDIS_ERR;
91 }
92
93 *offset = (size_t)loffset;
94 return REDIS_OK;
95}
96
97void setbitCommand(redisClient *c) {
98 robj *o;
eae33c1c 99 char *err = "bit is not an integer or out of range";
3c1bf495 100 size_t bitoffset;
30407e1f
PN
101 int byte, bit;
102 int byteval, bitval;
103 long on;
3c1bf495
PN
104
105 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
106 return;
107
30407e1f 108 if (getLongFromObjectOrReply(c,c->argv[3],&on,err) != REDIS_OK)
eae33c1c
PN
109 return;
110
30407e1f
PN
111 /* Bits can only be set or cleared... */
112 if (on & ~1) {
eae33c1c 113 addReplyError(c,err);
3c1bf495
PN
114 return;
115 }
116
117 o = lookupKeyWrite(c->db,c->argv[1]);
118 if (o == NULL) {
eae33c1c 119 o = createObject(REDIS_STRING,sdsempty());
3c1bf495
PN
120 dbAdd(c->db,c->argv[1],o);
121 } else {
122 if (checkType(c,o,REDIS_STRING)) return;
123
124 /* Create a copy when the object is shared or encoded. */
125 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
126 robj *decoded = getDecodedObject(o);
127 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
128 decrRefCount(decoded);
f85cd526 129 dbOverwrite(c->db,c->argv[1],o);
3c1bf495 130 }
3c1bf495 131 }
eae33c1c 132
30407e1f 133 /* Grow sds value to the right length if necessary */
eae33c1c 134 byte = bitoffset >> 3;
cc209063 135 o->ptr = sdsgrowzero(o->ptr,byte+1);
eae33c1c 136
30407e1f
PN
137 /* Get current values */
138 byteval = ((char*)o->ptr)[byte];
139 bit = 7 - (bitoffset & 0x7);
140 bitval = byteval & (1 << bit);
141
142 /* Update byte with new bit value and return original value */
143 byteval &= ~(1 << bit);
144 byteval |= ((on & 0x1) << bit);
145 ((char*)o->ptr)[byte] = byteval;
cea8c5cd 146 signalModifiedKey(c->db,c->argv[1]);
3c1bf495 147 server.dirty++;
30407e1f 148 addReply(c, bitval ? shared.cone : shared.czero);
3c1bf495
PN
149}
150
151void getbitCommand(redisClient *c) {
152 robj *o;
3c1bf495 153 char llbuf[32];
30407e1f
PN
154 size_t bitoffset;
155 size_t byte, bit;
156 size_t bitval = 0;
3c1bf495
PN
157
158 if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset) != REDIS_OK)
159 return;
160
161 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
162 checkType(c,o,REDIS_STRING)) return;
163
164 byte = bitoffset >> 3;
30407e1f 165 bit = 7 - (bitoffset & 0x7);
3c1bf495
PN
166 if (o->encoding != REDIS_ENCODING_RAW) {
167 if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)o->ptr))
30407e1f 168 bitval = llbuf[byte] & (1 << bit);
3c1bf495
PN
169 } else {
170 if (byte < sdslen(o->ptr))
30407e1f 171 bitval = ((char*)o->ptr)[byte] & (1 << bit);
3c1bf495 172 }
30407e1f
PN
173
174 addReply(c, bitval ? shared.cone : shared.czero);
e2641e09 175}
176
9f9e1cea
PN
177void setrangeCommand(redisClient *c) {
178 robj *o;
179 long offset;
180 sds value = c->argv[3]->ptr;
181
182 if (getLongFromObjectOrReply(c,c->argv[2],&offset,NULL) != REDIS_OK)
183 return;
184
8f8eeffe
PN
185 if (offset < 0) {
186 addReplyError(c,"offset is out of range");
187 return;
188 }
189
9f9e1cea
PN
190 o = lookupKeyWrite(c->db,c->argv[1]);
191 if (o == NULL) {
9f9e1cea
PN
192 /* Return 0 when setting nothing on a non-existing string */
193 if (sdslen(value) == 0) {
194 addReply(c,shared.czero);
195 return;
196 }
197
198 /* Return when the resulting string exceeds allowed size */
199 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
200 return;
201
202 o = createObject(REDIS_STRING,sdsempty());
203 dbAdd(c->db,c->argv[1],o);
204 } else {
ad1b4f4f 205 size_t olen;
9f9e1cea
PN
206
207 /* Key exists, check type */
208 if (checkType(c,o,REDIS_STRING))
209 return;
210
9f9e1cea 211 /* Return existing string length when setting nothing */
ad1b4f4f 212 olen = stringObjectLen(o);
9f9e1cea
PN
213 if (sdslen(value) == 0) {
214 addReplyLongLong(c,olen);
215 return;
216 }
217
9f9e1cea
PN
218 /* Return when the resulting string exceeds allowed size */
219 if (checkStringLength(c,offset+sdslen(value)) != REDIS_OK)
220 return;
221
222 /* Create a copy when the object is shared or encoded. */
223 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
224 robj *decoded = getDecodedObject(o);
225 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
226 decrRefCount(decoded);
f85cd526 227 dbOverwrite(c->db,c->argv[1],o);
9f9e1cea
PN
228 }
229 }
230
231 if (sdslen(value) > 0) {
232 o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
233 memcpy((char*)o->ptr+offset,value,sdslen(value));
cea8c5cd 234 signalModifiedKey(c->db,c->argv[1]);
9f9e1cea
PN
235 server.dirty++;
236 }
237 addReplyLongLong(c,sdslen(o->ptr));
238}
239
ef11bccc
PN
240void getrangeCommand(redisClient *c) {
241 robj *o;
242 long start, end;
243 char *str, llbuf[32];
244 size_t strlen;
245
246 if (getLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK)
247 return;
248 if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
249 return;
0b537972 250 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
ef11bccc
PN
251 checkType(c,o,REDIS_STRING)) return;
252
253 if (o->encoding == REDIS_ENCODING_INT) {
254 str = llbuf;
255 strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr);
256 } else {
257 str = o->ptr;
258 strlen = sdslen(str);
259 }
260
261 /* Convert negative indexes */
262 if (start < 0) start = strlen+start;
263 if (end < 0) end = strlen+end;
264 if (start < 0) start = 0;
265 if (end < 0) end = 0;
266 if ((unsigned)end >= strlen) end = strlen-1;
267
268 /* Precondition: end >= 0 && end < strlen, so the only condition where
269 * nothing can be returned is: start > end. */
270 if (start > end) {
0b537972 271 addReply(c,shared.emptybulk);
ef11bccc
PN
272 } else {
273 addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
274 }
275}
276
e2641e09 277void mgetCommand(redisClient *c) {
278 int j;
279
0537e7bf 280 addReplyMultiBulkLen(c,c->argc-1);
e2641e09 281 for (j = 1; j < c->argc; j++) {
282 robj *o = lookupKeyRead(c->db,c->argv[j]);
283 if (o == NULL) {
284 addReply(c,shared.nullbulk);
285 } else {
286 if (o->type != REDIS_STRING) {
287 addReply(c,shared.nullbulk);
288 } else {
289 addReplyBulk(c,o);
290 }
291 }
292 }
293}
294
295void msetGenericCommand(redisClient *c, int nx) {
296 int j, busykeys = 0;
297
298 if ((c->argc % 2) == 0) {
3ab20376 299 addReplyError(c,"wrong number of arguments for MSET");
e2641e09 300 return;
301 }
302 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
303 * set nothing at all if at least one already key exists. */
304 if (nx) {
305 for (j = 1; j < c->argc; j += 2) {
306 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
307 busykeys++;
308 }
309 }
f85cd526 310 if (busykeys) {
311 addReply(c, shared.czero);
312 return;
313 }
e2641e09 314 }
315
316 for (j = 1; j < c->argc; j += 2) {
317 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
f85cd526 318 setKey(c->db,c->argv[j],c->argv[j+1]);
e2641e09 319 }
320 server.dirty += (c->argc-1)/2;
321 addReply(c, nx ? shared.cone : shared.ok);
322}
323
324void msetCommand(redisClient *c) {
325 msetGenericCommand(c,0);
326}
327
328void msetnxCommand(redisClient *c) {
329 msetGenericCommand(c,1);
330}
331
332void incrDecrCommand(redisClient *c, long long incr) {
9d7165e8 333 long long value, oldvalue;
f85cd526 334 robj *o, *new;
e2641e09 335
336 o = lookupKeyWrite(c->db,c->argv[1]);
337 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
338 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
339
9d7165e8 340 oldvalue = value;
e2641e09 341 value += incr;
9d7165e8 342 if ((incr < 0 && value > oldvalue) || (incr > 0 && value < oldvalue)) {
343 addReplyError(c,"increment or decrement would overflow");
344 return;
345 }
f85cd526 346 new = createStringObjectFromLongLong(value);
347 if (o)
348 dbOverwrite(c->db,c->argv[1],new);
349 else
350 dbAdd(c->db,c->argv[1],new);
cea8c5cd 351 signalModifiedKey(c->db,c->argv[1]);
e2641e09 352 server.dirty++;
353 addReply(c,shared.colon);
f85cd526 354 addReply(c,new);
e2641e09 355 addReply(c,shared.crlf);
356}
357
358void incrCommand(redisClient *c) {
359 incrDecrCommand(c,1);
360}
361
362void decrCommand(redisClient *c) {
363 incrDecrCommand(c,-1);
364}
365
366void incrbyCommand(redisClient *c) {
367 long long incr;
368
369 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
370 incrDecrCommand(c,incr);
371}
372
373void decrbyCommand(redisClient *c) {
374 long long incr;
375
376 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
377 incrDecrCommand(c,-incr);
378}
379
380void appendCommand(redisClient *c) {
e2641e09 381 size_t totlen;
076f88d6 382 robj *o, *append;
e2641e09 383
384 o = lookupKeyWrite(c->db,c->argv[1]);
385 if (o == NULL) {
386 /* Create the key */
1333f98d
PN
387 c->argv[2] = tryObjectEncoding(c->argv[2]);
388 dbAdd(c->db,c->argv[1],c->argv[2]);
e2641e09 389 incrRefCount(c->argv[2]);
390 totlen = stringObjectLen(c->argv[2]);
391 } else {
1333f98d
PN
392 /* Key exists, check type */
393 if (checkType(c,o,REDIS_STRING))
e2641e09 394 return;
076f88d6 395
1333f98d
PN
396 /* "append" is an argument, so always an sds */
397 append = c->argv[2];
398 totlen = stringObjectLen(o)+sdslen(append->ptr);
399 if (checkStringLength(c,totlen) != REDIS_OK)
076f88d6 400 return;
076f88d6
PN
401
402 /* If the object is shared or encoded, we have to make a copy */
e2641e09 403 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
404 robj *decoded = getDecodedObject(o);
e2641e09 405 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
406 decrRefCount(decoded);
f85cd526 407 dbOverwrite(c->db,c->argv[1],o);
e2641e09 408 }
076f88d6
PN
409
410 /* Append the value */
411 o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
e2641e09 412 totlen = sdslen(o->ptr);
413 }
cea8c5cd 414 signalModifiedKey(c->db,c->argv[1]);
e2641e09 415 server.dirty++;
b70d3555 416 addReplyLongLong(c,totlen);
e2641e09 417}
418
80091bba 419void strlenCommand(redisClient *c) {
420 robj *o;
80091bba 421 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
422 checkType(c,o,REDIS_STRING)) return;
ad1b4f4f 423 addReplyLongLong(c,stringObjectLen(o));
80091bba 424}
7ecd4644 425