]> git.saurik.com Git - redis.git/blob - src/t_string.c
Convert objects in the command procs instead of the protocol code
[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 if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
148
149 value += incr;
150 o = createStringObjectFromLongLong(value);
151 dbReplace(c->db,c->argv[1],o);
152 touchWatchedKey(c->db,c->argv[1]);
153 server.dirty++;
154 addReply(c,shared.colon);
155 addReply(c,o);
156 addReply(c,shared.crlf);
157 }
158
159 void incrCommand(redisClient *c) {
160 incrDecrCommand(c,1);
161 }
162
163 void decrCommand(redisClient *c) {
164 incrDecrCommand(c,-1);
165 }
166
167 void incrbyCommand(redisClient *c) {
168 long long incr;
169
170 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
171 incrDecrCommand(c,incr);
172 }
173
174 void decrbyCommand(redisClient *c) {
175 long long incr;
176
177 if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
178 incrDecrCommand(c,-incr);
179 }
180
181 void appendCommand(redisClient *c) {
182 int retval;
183 size_t totlen;
184 robj *o;
185
186 o = lookupKeyWrite(c->db,c->argv[1]);
187 c->argv[2] = tryObjectEncoding(c->argv[2]);
188 if (o == NULL) {
189 /* Create the key */
190 retval = dbAdd(c->db,c->argv[1],c->argv[2]);
191 incrRefCount(c->argv[2]);
192 totlen = stringObjectLen(c->argv[2]);
193 } else {
194 if (o->type != REDIS_STRING) {
195 addReply(c,shared.wrongtypeerr);
196 return;
197 }
198 /* If the object is specially encoded or shared we have to make
199 * a copy */
200 if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) {
201 robj *decoded = getDecodedObject(o);
202
203 o = createStringObject(decoded->ptr, sdslen(decoded->ptr));
204 decrRefCount(decoded);
205 dbReplace(c->db,c->argv[1],o);
206 }
207 /* APPEND! */
208 if (c->argv[2]->encoding == REDIS_ENCODING_RAW) {
209 o->ptr = sdscatlen(o->ptr,
210 c->argv[2]->ptr, sdslen(c->argv[2]->ptr));
211 } else {
212 o->ptr = sdscatprintf(o->ptr, "%ld",
213 (unsigned long) c->argv[2]->ptr);
214 }
215 totlen = sdslen(o->ptr);
216 }
217 touchWatchedKey(c->db,c->argv[1]);
218 server.dirty++;
219 addReplyLongLong(c,totlen);
220 }
221
222 void substrCommand(redisClient *c) {
223 robj *o;
224 long start = atoi(c->argv[2]->ptr);
225 long end = atoi(c->argv[3]->ptr);
226 size_t rangelen, strlen;
227 sds range;
228
229 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
230 checkType(c,o,REDIS_STRING)) return;
231
232 o = getDecodedObject(o);
233 strlen = sdslen(o->ptr);
234
235 /* convert negative indexes */
236 if (start < 0) start = strlen+start;
237 if (end < 0) end = strlen+end;
238 if (start < 0) start = 0;
239 if (end < 0) end = 0;
240
241 /* indexes sanity checks */
242 if (start > end || (size_t)start >= strlen) {
243 /* Out of range start or start > end result in null reply */
244 addReply(c,shared.nullbulk);
245 decrRefCount(o);
246 return;
247 }
248 if ((size_t)end >= strlen) end = strlen-1;
249 rangelen = (end-start)+1;
250
251 /* Return the result */
252 addReplySds(c,sdscatprintf(sdsempty(),"$%zu\r\n",rangelen));
253 range = sdsnewlen((char*)o->ptr+start,rangelen);
254 addReplySds(c,range);
255 addReply(c,shared.crlf);
256 decrRefCount(o);
257 }
258
259 void strlenCommand(redisClient *c) {
260 robj *o;
261
262 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
263 checkType(c,o,REDIS_STRING)) return;
264
265 o = getDecodedObject(o);
266 addReplyLongLong(c,sdslen(o->ptr));
267 decrRefCount(o);
268 }