]> git.saurik.com Git - redis.git/blob - src/t_string.c
Merge remote branch 'pietern/testverbosity'
[redis.git] / src / t_string.c
1 #include "redis.h"
2
3 /*-----------------------------------------------------------------------------
4 * String Commands
5 *----------------------------------------------------------------------------*/
6
7 void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
8 int retval;
9 long seconds = 0; /* initialized to avoid an harmness warning */
10
11 if (expire) {
12 if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
13 return;
14 if (seconds <= 0) {
15 addReplyError(c,"invalid expire time in SETEX");
16 return;
17 }
18 }
19
20 retval = dbAdd(c->db,key,val);
21 if (retval == REDIS_ERR) {
22 if (!nx) {
23 dbReplace(c->db,key,val);
24 incrRefCount(val);
25 } else {
26 addReply(c,shared.czero);
27 return;
28 }
29 } else {
30 incrRefCount(val);
31 }
32 touchWatchedKey(c->db,key);
33 server.dirty++;
34 removeExpire(c->db,key);
35 if (expire) setExpire(c->db,key,time(NULL)+seconds);
36 addReply(c, nx ? shared.cone : shared.ok);
37 }
38
39 void setCommand(redisClient *c) {
40 c->argv[2] = tryObjectEncoding(c->argv[2]);
41 setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
42 }
43
44 void setnxCommand(redisClient *c) {
45 c->argv[2] = tryObjectEncoding(c->argv[2]);
46 setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
47 }
48
49 void setexCommand(redisClient *c) {
50 c->argv[3] = tryObjectEncoding(c->argv[3]);
51 setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
52 }
53
54 int getGenericCommand(redisClient *c) {
55 robj *o;
56
57 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
58 return REDIS_OK;
59
60 if (o->type != REDIS_STRING) {
61 addReply(c,shared.wrongtypeerr);
62 return REDIS_ERR;
63 } else {
64 addReplyBulk(c,o);
65 return REDIS_OK;
66 }
67 }
68
69 void getCommand(redisClient *c) {
70 getGenericCommand(c);
71 }
72
73 void getsetCommand(redisClient *c) {
74 if (getGenericCommand(c) == REDIS_ERR) return;
75 c->argv[2] = tryObjectEncoding(c->argv[2]);
76 dbReplace(c->db,c->argv[1],c->argv[2]);
77 incrRefCount(c->argv[2]);
78 touchWatchedKey(c->db,c->argv[1]);
79 server.dirty++;
80 removeExpire(c->db,c->argv[1]);
81 }
82
83 void mgetCommand(redisClient *c) {
84 int j;
85
86 addReplyMultiBulkLen(c,c->argc-1);
87 for (j = 1; j < c->argc; j++) {
88 robj *o = lookupKeyRead(c->db,c->argv[j]);
89 if (o == NULL) {
90 addReply(c,shared.nullbulk);
91 } else {
92 if (o->type != REDIS_STRING) {
93 addReply(c,shared.nullbulk);
94 } else {
95 addReplyBulk(c,o);
96 }
97 }
98 }
99 }
100
101 void msetGenericCommand(redisClient *c, int nx) {
102 int j, busykeys = 0;
103
104 if ((c->argc % 2) == 0) {
105 addReplyError(c,"wrong number of arguments for MSET");
106 return;
107 }
108 /* Handle the NX flag. The MSETNX semantic is to return zero and don't
109 * set nothing at all if at least one already key exists. */
110 if (nx) {
111 for (j = 1; j < c->argc; j += 2) {
112 if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {
113 busykeys++;
114 }
115 }
116 }
117 if (busykeys) {
118 addReply(c, shared.czero);
119 return;
120 }
121
122 for (j = 1; j < c->argc; j += 2) {
123 c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
124 dbReplace(c->db,c->argv[j],c->argv[j+1]);
125 incrRefCount(c->argv[j+1]);
126 removeExpire(c->db,c->argv[j]);
127 touchWatchedKey(c->db,c->argv[j]);
128 }
129 server.dirty += (c->argc-1)/2;
130 addReply(c, nx ? shared.cone : shared.ok);
131 }
132
133 void msetCommand(redisClient *c) {
134 msetGenericCommand(c,0);
135 }
136
137 void msetnxCommand(redisClient *c) {
138 msetGenericCommand(c,1);
139 }
140
141 void incrDecrCommand(redisClient *c, long long incr) {
142 long long value;
143 robj *o;
144
145 o = lookupKeyWrite(c->db,c->argv[1]);
146 if (o != NULL && checkType(c,o,REDIS_STRING)) return;
147
148 /* Fast path if the object is integer encoded and is not shared. */
149 if (o && o->refcount == 1 && o->encoding == REDIS_ENCODING_INT) {
150 long long newval = ((long)o->ptr) + incr;
151
152 if (newval < 0 && newval >= REDIS_SHARED_INTEGERS &&
153 newval >= LONG_MIN && newval <= LONG_MAX) {
154 o->ptr = (void*) (long) newval;
155 touchWatchedKey(c->db,c->argv[1]);
156 server.dirty++;
157 addReplyLongLong(c,newval);
158 return;
159 }
160 /* ... else take the usual safe path */
161 }
162
163 /* Otherwise we create a new object and replace the old one. */
164 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
165 value += incr;
166 o = createStringObjectFromLongLong(value);
167 dbReplace(c->db,c->argv[1],o);
168 touchWatchedKey(c->db,c->argv[1]);
169 server.dirty++;
170 addReply(c,shared.colon);
171 addReply(c,o);
172 addReply(c,shared.crlf);
173 }
174
175 void incrCommand(redisClient *c) {
176 incrDecrCommand(c,1);
177 }
178
179 void decrCommand(redisClient *c) {
180 incrDecrCommand(c,-1);
181 }
182
183 void incrbyCommand(redisClient *c) {
184 long long incr;
185
186 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
187 incrDecrCommand(c,incr);
188 }
189
190 void decrbyCommand(redisClient *c) {
191 long long incr;
192
193 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
194 incrDecrCommand(c,-incr);
195 }
196
197 void appendCommand(redisClient *c) {
198 int retval;
199 size_t totlen;
200 robj *o;
201
202 o = lookupKeyWrite(c->db,c->argv[1]);
203 c->argv[2] = tryObjectEncoding(c->argv[2]);
204 if (o == NULL) {
205 /* Create the key */
206 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
207 incrRefCount(c->argv[2]);
208 totlen = stringObjectLen(c->argv[2]);
209 } else {
210 if (o->type != REDIS_STRING) {
211 addReply(c,shared.wrongtypeerr);
212 return;
213 }
214 /* If the object is specially encoded or shared we have to make
215 * a copy */
216 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
217 robj *decoded = getDecodedObject(o);
218
219 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
220 decrRefCount(decoded);
221 dbReplace(c->db,c->argv[1],o);
222 }
223 /* APPEND! */
224 if (c->argv[2]->encoding == REDIS_ENCODING_RAW) {
225 o->ptr = sdscatlen(o->ptr,
226 c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
227 } else {
228 o->ptr = sdscatprintf(o->ptr, "%ld",
229 (unsigned long) c->argv[2]->ptr);
230 }
231 totlen = sdslen(o->ptr);
232 }
233 touchWatchedKey(c->db,c->argv[1]);
234 server.dirty++;
235 addReplyLongLong(c,totlen);
236 }
237
238 void substrCommand(redisClient *c) {
239 robj *o;
240 long start = atoi(c->argv[2]->ptr);
241 long end = atoi(c->argv[3]->ptr);
242 size_t rangelen, strlen;
243 sds range;
244
245 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
246 checkType(c,o,REDIS_STRING)) return;
247
248 o = getDecodedObject(o);
249 strlen = sdslen(o->ptr);
250
251 /* convert negative indexes */
252 if (start < 0) start = strlen+start;
253 if (end < 0) end = strlen+end;
254 if (start < 0) start = 0;
255 if (end < 0) end = 0;
256
257 /* indexes sanity checks */
258 if (start > end || (size_t)start >= strlen) {
259 /* Out of range start or start > end result in null reply */
260 addReply(c,shared.nullbulk);
261 decrRefCount(o);
262 return;
263 }
264 if ((size_t)end >= strlen) end = strlen-1;
265 rangelen = (end-start)+1;
266
267 /* Return the result */
268 addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
269 range = sdsnewlen((char*)o->ptr+start,rangelen);
270 addReplySds(c,range);
271 addReply(c,shared.crlf);
272 decrRefCount(o);
273 }
274
275 void strlenCommand(redisClient *c) {
276 robj *o;
277
278 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
279 checkType(c,o,REDIS_STRING)) return;
280
281 o = getDecodedObject(o);
282 addReplyLongLong(c,sdslen(o->ptr));
283 decrRefCount(o);
284 }