1 : #include "redis.h"
2 :
3 : #include <signal.h>
4 : #include <ctype.h>
5 :
6 : void SlotToKeyAdd(robj *key);
7 : void SlotToKeyDel(robj *key);
8 :
9 : /*-----------------------------------------------------------------------------
10 : * C-level DB API
11 : *----------------------------------------------------------------------------*/
12 :
13 2438679 : robj *lookupKey(redisDb *db, robj *key) {
14 2438679 : dictEntry *de = dictFind(db->dict,key->ptr);
15 2438679 : if (de) {
16 664634 : robj *val = dictGetVal(de);
17 :
18 : /* Update the access time for the aging algorithm.
19 : * Don't do it if we have a saving child, as this will trigger
20 : * a copy on write madness. */
21 664634 : if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
22 663262 : val->lru = server.lruclock;
23 664634 : return val;
24 : } else {
25 1774045 : return NULL;
26 : }
27 : }
28 :
29 445815 : robj *lookupKeyRead(redisDb *db, robj *key) {
30 : robj *val;
31 :
32 445815 : expireIfNeeded(db,key);
33 445815 : val = lookupKey(db,key);
34 445815 : if (val == NULL)
35 54206 : server.stat_keyspace_misses++;
36 : else
37 391609 : server.stat_keyspace_hits++;
38 445815 : return val;
39 : }
40 :
41 1992864 : robj *lookupKeyWrite(redisDb *db, robj *key) {
42 1992864 : expireIfNeeded(db,key);
43 1992864 : return lookupKey(db,key);
44 : }
45 :
46 232955 : robj *lookupKeyReadOrReply(redisClient *c, robj *key, robj *reply) {
47 232955 : robj *o = lookupKeyRead(c->db, key);
48 232955 : if (!o) addReply(c,reply);
49 232955 : return o;
50 : }
51 :
52 42306 : robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {
53 42306 : robj *o = lookupKeyWrite(c->db, key);
54 42306 : if (!o) addReply(c,reply);
55 42306 : return o;
56 : }
57 :
58 : /* Add the key to the DB. It's up to the caller to increment the reference
59 : * counte of the value if needed.
60 : *
61 : * The program is aborted if the key already exists. */
62 1911745 : void dbAdd(redisDb *db, robj *key, robj *val) {
63 1911745 : sds copy = sdsdup(key->ptr);
64 1911745 : int retval = dictAdd(db->dict, copy, val);
65 :
66 1911745 : redisAssertWithInfo(NULL,key,retval == REDIS_OK);
67 1911745 : if (server.cluster_enabled) SlotToKeyAdd(key);
68 1911745 : }
69 :
70 : /* Overwrite an existing key with a new value. Incrementing the reference
71 : * count of the new value is up to the caller.
72 : * This function does not modify the expire time of the existing key.
73 : *
74 : * The program is aborted if the key was not already present. */
75 16405 : void dbOverwrite(redisDb *db, robj *key, robj *val) {
76 16405 : struct dictEntry *de = dictFind(db->dict,key->ptr);
77 :
78 16405 : redisAssertWithInfo(NULL,key,de != NULL);
79 16405 : dictReplace(db->dict, key->ptr, val);
80 16405 : }
81 :
82 : /* High level Set operation. This function can be used in order to set
83 : * a key, whatever it was existing or not, to a new object.
84 : *
85 : * 1) The ref count of the value object is incremented.
86 : * 2) clients WATCHing for the destination key notified.
87 : * 3) The expire time of the key is reset (the key is made persistent). */
88 841535 : void setKey(redisDb *db, robj *key, robj *val) {
89 841535 : if (lookupKeyWrite(db,key) == NULL) {
90 825152 : dbAdd(db,key,val);
91 : } else {
92 16383 : dbOverwrite(db,key,val);
93 : }
94 841535 : incrRefCount(val);
95 841535 : removeExpire(db,key);
96 : signalModifiedKey(db,key);
97 841535 : }
98 :
99 1437 : int dbExists(redisDb *db, robj *key) {
100 1437 : return dictFind(db->dict,key->ptr) != NULL;
101 : }
102 :
103 : /* Return a random key, in form of a Redis object.
104 : * If there are no keys, NULL is returned.
105 : *
106 : * The function makes sure to return keys not already expired. */
107 25998 : robj *dbRandomKey(redisDb *db) {
108 : struct dictEntry *de;
109 :
110 : while(1) {
111 : sds key;
112 : robj *keyobj;
113 :
114 25998 : de = dictGetRandomKey(db->dict);
115 25998 : if (de == NULL) return NULL;
116 :
117 25996 : key = dictGetKey(de);
118 25996 : keyobj = createStringObject(key,sdslen(key));
119 25996 : if (dictFind(db->expires,key)) {
120 12 : if (expireIfNeeded(db,keyobj)) {
121 1 : decrRefCount(keyobj);
122 1 : continue; /* search for another key. This expired. */
123 : }
124 : }
125 25995 : return keyobj;
126 1 : }
127 : }
128 :
129 : /* Delete a key, value, and associated expiration entry if any, from the DB */
130 71337 : int dbDelete(redisDb *db, robj *key) {
131 : /* Deleting an entry from the expires dict will not free the sds of
132 : * the key, because it is shared with the main dictionary. */
133 71337 : if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
134 71337 : if (dictDelete(db->dict,key->ptr) == DICT_OK) {
135 54231 : if (server.cluster_enabled) SlotToKeyDel(key);
136 54231 : return 1;
137 : } else {
138 17106 : return 0;
139 : }
140 : }
141 :
142 71 : long long emptyDb() {
143 : int j;
144 71 : long long removed = 0;
145 :
146 1207 : for (j = 0; j < server.dbnum; j++) {
147 1136 : removed += dictSize(server.db[j].dict);
148 1136 : dictEmpty(server.db[j].dict);
149 1136 : dictEmpty(server.db[j].expires);
150 : }
151 71 : return removed;
152 : }
153 :
154 351 : int selectDb(redisClient *c, int id) {
155 29206 : if (id < 0 || id >= server.dbnum)
156 1 : return REDIS_ERR;
157 29205 : c->db = &server.db[id];
158 29205 : return REDIS_OK;
159 : }
160 :
161 : /*-----------------------------------------------------------------------------
162 : * Hooks for key space changes.
163 : *
164 : * Every time a key in the database is modified the function
165 : * signalModifiedKey() is called.
166 : *
167 : * Every time a DB is flushed the function signalFlushDb() is called.
168 : *----------------------------------------------------------------------------*/
169 :
170 292234 : void signalModifiedKey(redisDb *db, robj *key) {
171 1154479 : touchWatchedKey(db,key);
172 292234 : }
173 :
174 0 : void signalFlushedDb(int dbid) {
175 51 : touchWatchedKeysOnFlush(dbid);
176 0 : }
177 :
178 : /*-----------------------------------------------------------------------------
179 : * Type agnostic commands operating on the key space
180 : *----------------------------------------------------------------------------*/
181 :
182 18 : void flushdbCommand(redisClient *c) {
183 18 : server.dirty += dictSize(c->db->dict);
184 18 : signalFlushedDb(c->db->id);
185 18 : dictEmpty(c->db->dict);
186 18 : dictEmpty(c->db->expires);
187 18 : addReply(c,shared.ok);
188 18 : }
189 :
190 33 : void flushallCommand(redisClient *c) {
191 : signalFlushedDb(-1);
192 33 : server.dirty += emptyDb();
193 33 : addReply(c,shared.ok);
194 33 : if (server.rdb_child_pid != -1) {
195 0 : kill(server.rdb_child_pid,SIGKILL);
196 0 : rdbRemoveTempFile(server.rdb_child_pid);
197 : }
198 33 : if (server.saveparamslen > 0) {
199 : /* Normally rdbSave() will reset dirty, but we don't want this here
200 : * as otherwise FLUSHALL will not be replicated nor put into the AOF. */
201 33 : int saved_dirty = server.dirty;
202 33 : rdbSave(server.rdb_filename);
203 33 : server.dirty = saved_dirty;
204 : }
205 33 : server.dirty++;
206 33 : }
207 :
208 28422 : void delCommand(redisClient *c) {
209 28422 : int deleted = 0, j;
210 :
211 56904 : for (j = 1; j < c->argc; j++) {
212 28482 : if (dbDelete(c->db,c->argv[j])) {
213 20612 : signalModifiedKey(c->db,c->argv[j]);
214 20612 : server.dirty++;
215 20612 : deleted++;
216 : }
217 : }
218 28422 : addReplyLongLong(c,deleted);
219 28422 : }
220 :
221 1437 : void existsCommand(redisClient *c) {
222 1437 : expireIfNeeded(c->db,c->argv[1]);
223 1437 : if (dbExists(c->db,c->argv[1])) {
224 1389 : addReply(c, shared.cone);
225 : } else {
226 48 : addReply(c, shared.czero);
227 : }
228 1437 : }
229 :
230 28851 : void selectCommand(redisClient *c) {
231 28851 : int id = atoi(c->argv[1]->ptr);
232 :
233 28851 : if (server.cluster_enabled && id != 0) {
234 0 : addReplyError(c,"SELECT is not allowed in cluster mode");
235 0 : return;
236 : }
237 28851 : if (selectDb(c,id) == REDIS_ERR) {
238 1 : addReplyError(c,"invalid DB index");
239 : } else {
240 28850 : addReply(c,shared.ok);
241 : }
242 : }
243 :
244 25997 : void randomkeyCommand(redisClient *c) {
245 : robj *key;
246 :
247 25997 : if ((key = dbRandomKey(c->db)) == NULL) {
248 2 : addReply(c,shared.nullbulk);
249 2 : return;
250 : }
251 :
252 25995 : addReplyBulk(c,key);
253 25995 : decrRefCount(key);
254 : }
255 :
256 9 : void keysCommand(redisClient *c) {
257 : dictIterator *di;
258 : dictEntry *de;
259 9 : sds pattern = c->argv[1]->ptr;
260 9 : int plen = sdslen(pattern), allkeys;
261 9 : unsigned long numkeys = 0;
262 9 : void *replylen = addDeferredMultiBulkLength(c);
263 :
264 9 : di = dictGetIterator(c->db->dict);
265 9 : allkeys = (pattern[0] == '*' && pattern[1] == '\0');
266 23490 : while((de = dictNext(di)) != NULL) {
267 23472 : sds key = dictGetKey(de);
268 : robj *keyobj;
269 :
270 23478 : if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
271 23469 : keyobj = createStringObject(key,sdslen(key));
272 23469 : if (expireIfNeeded(c->db,keyobj) == 0) {
273 23469 : addReplyBulk(c,keyobj);
274 23469 : numkeys++;
275 : }
276 23469 : decrRefCount(keyobj);
277 : }
278 : }
279 9 : dictReleaseIterator(di);
280 9 : setDeferredMultiBulkLength(c,replylen,numkeys);
281 9 : }
282 :
283 15 : void dbsizeCommand(redisClient *c) {
284 15 : addReplyLongLong(c,dictSize(c->db->dict));
285 15 : }
286 :
287 0 : void lastsaveCommand(redisClient *c) {
288 0 : addReplyLongLong(c,server.lastsave);
289 0 : }
290 :
291 125972 : void typeCommand(redisClient *c) {
292 : robj *o;
293 : char *type;
294 :
295 125972 : o = lookupKeyRead(c->db,c->argv[1]);
296 125972 : if (o == NULL) {
297 54092 : type = "none";
298 : } else {
299 71880 : switch(o->type) {
300 16688 : case REDIS_STRING: type = "string"; break;
301 11338 : case REDIS_LIST: type = "list"; break;
302 15879 : case REDIS_SET: type = "set"; break;
303 15458 : case REDIS_ZSET: type = "zset"; break;
304 12517 : case REDIS_HASH: type = "hash"; break;
305 0 : default: type = "unknown"; break;
306 : }
307 : }
308 125972 : addReplyStatus(c,type);
309 125972 : }
310 :
311 0 : void shutdownCommand(redisClient *c) {
312 0 : int flags = 0;
313 :
314 0 : if (c->argc > 2) {
315 0 : addReply(c,shared.syntaxerr);
316 0 : return;
317 0 : } else if (c->argc == 2) {
318 0 : if (!strcasecmp(c->argv[1]->ptr,"nosave")) {
319 0 : flags |= REDIS_SHUTDOWN_NOSAVE;
320 0 : } else if (!strcasecmp(c->argv[1]->ptr,"save")) {
321 0 : flags |= REDIS_SHUTDOWN_SAVE;
322 : } else {
323 0 : addReply(c,shared.syntaxerr);
324 0 : return;
325 : }
326 : }
327 0 : if (prepareForShutdown(flags) == REDIS_OK) exit(0);
328 0 : addReplyError(c,"Errors trying to SHUTDOWN. Check logs.");
329 : }
330 :
331 9 : void renameGenericCommand(redisClient *c, int nx) {
332 : robj *o;
333 : long long expire;
334 :
335 : /* To use the same key as src and dst is probably an error */
336 9 : if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) {
337 1 : addReply(c,shared.sameobjecterr);
338 1 : return;
339 : }
340 :
341 8 : if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)
342 : return;
343 :
344 7 : incrRefCount(o);
345 7 : expire = getExpire(c->db,c->argv[1]);
346 7 : if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {
347 3 : if (nx) {
348 1 : decrRefCount(o);
349 1 : addReply(c,shared.czero);
350 1 : return;
351 : }
352 : /* Overwrite: delete the old key before creating the new one with the same name. */
353 2 : dbDelete(c->db,c->argv[2]);
354 : }
355 6 : dbAdd(c->db,c->argv[2],o);
356 6 : if (expire != -1) setExpire(c->db,c->argv[2],expire);
357 6 : dbDelete(c->db,c->argv[1]);
358 6 : signalModifiedKey(c->db,c->argv[1]);
359 6 : signalModifiedKey(c->db,c->argv[2]);
360 6 : server.dirty++;
361 6 : addReply(c,nx ? shared.cone : shared.ok);
362 : }
363 :
364 7 : void renameCommand(redisClient *c) {
365 7 : renameGenericCommand(c,0);
366 7 : }
367 :
368 2 : void renamenxCommand(redisClient *c) {
369 2 : renameGenericCommand(c,1);
370 2 : }
371 :
372 2 : void moveCommand(redisClient *c) {
373 : robj *o;
374 : redisDb *src, *dst;
375 : int srcid;
376 :
377 2 : if (server.cluster_enabled) {
378 0 : addReplyError(c,"MOVE is not allowed in cluster mode");
379 0 : return;
380 : }
381 :
382 : /* Obtain source and target DB pointers */
383 2 : src = c->db;
384 2 : srcid = c->db->id;
385 4 : if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_ERR) {
386 0 : addReply(c,shared.outofrangeerr);
387 0 : return;
388 : }
389 2 : dst = c->db;
390 : selectDb(c,srcid); /* Back to the source DB */
391 :
392 : /* If the user is moving using as target the same
393 : * DB as the source DB it is probably an error. */
394 2 : if (src == dst) {
395 0 : addReply(c,shared.sameobjecterr);
396 0 : return;
397 : }
398 :
399 : /* Check if the element exists and get a reference */
400 2 : o = lookupKeyWrite(c->db,c->argv[1]);
401 2 : if (!o) {
402 0 : addReply(c,shared.czero);
403 0 : return;
404 : }
405 :
406 : /* Return zero if the key already exists in the target DB */
407 2 : if (lookupKeyWrite(dst,c->argv[1]) != NULL) {
408 1 : addReply(c,shared.czero);
409 1 : return;
410 : }
411 1 : dbAdd(dst,c->argv[1],o);
412 1 : incrRefCount(o);
413 :
414 : /* OK! key moved, free the entry in the source DB */
415 1 : dbDelete(src,c->argv[1]);
416 1 : server.dirty++;
417 1 : addReply(c,shared.cone);
418 : }
419 :
420 : /*-----------------------------------------------------------------------------
421 : * Expires API
422 : *----------------------------------------------------------------------------*/
423 :
424 841536 : int removeExpire(redisDb *db, robj *key) {
425 : /* An expire may only be removed if there is a corresponding entry in the
426 : * main dict. Otherwise, the key will never be freed. */
427 841536 : redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
428 841536 : return dictDelete(db->expires,key->ptr) == DICT_OK;
429 : }
430 :
431 19301 : void setExpire(redisDb *db, robj *key, long long when) {
432 : dictEntry *kde, *de;
433 :
434 : /* Reuse the sds from the main dict in the expire dict */
435 19301 : kde = dictFind(db->dict,key->ptr);
436 19301 : redisAssertWithInfo(NULL,key,kde != NULL);
437 19301 : de = dictReplaceRaw(db->expires,dictGetKey(kde));
438 19301 : dictSetSignedIntegerVal(de,when);
439 19301 : }
440 :
441 : /* Return the expire time of the specified key, or -1 if no expire
442 : * is associated with this key (i.e. the key is non volatile) */
443 7607791 : long long getExpire(redisDb *db, robj *key) {
444 : dictEntry *de;
445 :
446 : /* No expire? return ASAP */
447 7998339 : if (dictSize(db->expires) == 0 ||
448 7987485 : (de = dictFind(db->expires,key->ptr)) == NULL) return -1;
449 :
450 : /* The entry was found in the expire dict, this means it should also
451 : * be present in the main dict (safety check). */
452 10854 : redisAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
453 10854 : return dictGetSignedIntegerVal(de);
454 : }
455 :
456 : /* Propagate expires into slaves and the AOF file.
457 : * When a key expires in the master, a DEL operation for this key is sent
458 : * to all the slaves and the AOF file if enabled.
459 : *
460 : * This way the key expiry is centralized in one place, and since both
461 : * AOF and the master->slave link guarantee operation ordering, everything
462 : * will be consistent even if we allow write operations against expiring
463 : * keys. */
464 6175 : void propagateExpire(redisDb *db, robj *key) {
465 : robj *argv[2];
466 :
467 6175 : argv[0] = shared.del;
468 6175 : argv[1] = key;
469 6175 : incrRefCount(argv[0]);
470 6175 : incrRefCount(argv[1]);
471 :
472 6175 : if (server.aof_state != REDIS_AOF_OFF)
473 1 : feedAppendOnlyFile(server.delCommand,db->id,argv,2);
474 6175 : if (listLength(server.slaves))
475 16 : replicationFeedSlaves(server.slaves,db->id,argv,2);
476 :
477 6175 : decrRefCount(argv[0]);
478 6175 : decrRefCount(argv[1]);
479 6175 : }
480 :
481 2463597 : int expireIfNeeded(redisDb *db, robj *key) {
482 2463597 : long long when = getExpire(db,key);
483 :
484 2463597 : if (when < 0) return 0; /* No expire for this key */
485 :
486 : /* Don't expire anything while loading. It will be done later. */
487 10340 : if (server.loading) return 0;
488 :
489 : /* If we are running in the context of a slave, return ASAP:
490 : * the slave key expiration is controlled by the master that will
491 : * send us synthesized DEL operations for expired keys.
492 : *
493 : * Still we try to return the right information to the caller,
494 : * that is, 0 if we think the key should be still valid, 1 if
495 : * we think the key is expired at this time. */
496 10339 : if (server.masterhost != NULL) {
497 6 : return mstime() > when;
498 : }
499 :
500 : /* Return when this key has not expired */
501 10333 : if (mstime() <= when) return 0;
502 :
503 : /* Delete the key */
504 2 : server.stat_expiredkeys++;
505 2 : propagateExpire(db,key);
506 2 : return dbDelete(db,key);
507 : }
508 :
509 : /*-----------------------------------------------------------------------------
510 : * Expires Commands
511 : *----------------------------------------------------------------------------*/
512 :
513 : /* Given an string object return true if it contains exactly the "ms"
514 : * or "MS" string. This is used in order to check if the last argument
515 : * of EXPIRE, EXPIREAT or TTL is "ms" to switch into millisecond input/output */
516 0 : int stringObjectEqualsMs(robj *a) {
517 0 : char *arg = a->ptr;
518 0 : return tolower(arg[0]) == 'm' && tolower(arg[1]) == 's' && arg[2] == '\0';
519 : }
520 :
521 592 : void expireGenericCommand(redisClient *c, long long offset, int unit) {
522 : dictEntry *de;
523 592 : robj *key = c->argv[1], *param = c->argv[2];
524 : long long milliseconds;
525 :
526 592 : if (getLongLongFromObjectOrReply(c, param, &milliseconds, NULL) != REDIS_OK)
527 : return;
528 :
529 592 : if (unit == UNIT_SECONDS) milliseconds *= 1000;
530 592 : milliseconds -= offset;
531 :
532 592 : de = dictFind(c->db->dict,key->ptr);
533 592 : if (de == NULL) {
534 506 : addReply(c,shared.czero);
535 506 : return;
536 : }
537 : /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past
538 : * should never be executed as a DEL when load the AOF or in the context
539 : * of a slave instance.
540 : *
541 : * Instead we take the other branch of the IF statement setting an expire
542 : * (possibly in the past) and wait for an explicit DEL from the master. */
543 86 : if (milliseconds <= 0 && !server.loading && !server.masterhost) {
544 : robj *aux;
545 :
546 24 : redisAssertWithInfo(c,key,dbDelete(c->db,key));
547 24 : server.dirty++;
548 :
549 : /* Replicate/AOF this as an explicit DEL. */
550 24 : aux = createStringObject("DEL",3);
551 24 : rewriteClientCommandVector(c,2,aux,key);
552 24 : decrRefCount(aux);
553 24 : signalModifiedKey(c->db,key);
554 24 : addReply(c, shared.cone);
555 24 : return;
556 : } else {
557 62 : long long when = mstime()+milliseconds;
558 62 : setExpire(c->db,key,when);
559 62 : addReply(c,shared.cone);
560 62 : signalModifiedKey(c->db,key);
561 62 : server.dirty++;
562 62 : return;
563 : }
564 : }
565 :
566 574 : void expireCommand(redisClient *c) {
567 574 : expireGenericCommand(c,0,UNIT_SECONDS);
568 574 : }
569 :
570 3 : void expireatCommand(redisClient *c) {
571 3 : expireGenericCommand(c,mstime(),UNIT_SECONDS);
572 3 : }
573 :
574 4 : void pexpireCommand(redisClient *c) {
575 4 : expireGenericCommand(c,0,UNIT_MILLISECONDS);
576 4 : }
577 :
578 11 : void pexpireatCommand(redisClient *c) {
579 11 : expireGenericCommand(c,mstime(),UNIT_MILLISECONDS);
580 11 : }
581 :
582 24 : void ttlGenericCommand(redisClient *c, int output_ms) {
583 24 : long long expire, ttl = -1;
584 :
585 24 : expire = getExpire(c->db,c->argv[1]);
586 24 : if (expire != -1) {
587 20 : ttl = expire-mstime();
588 20 : if (ttl < 0) ttl = -1;
589 : }
590 24 : if (ttl == -1) {
591 4 : addReplyLongLong(c,-1);
592 : } else {
593 20 : addReplyLongLong(c,output_ms ? ttl : ((ttl+500)/1000));
594 : }
595 24 : }
596 :
597 22 : void ttlCommand(redisClient *c) {
598 22 : ttlGenericCommand(c, 0);
599 22 : }
600 :
601 2 : void pttlCommand(redisClient *c) {
602 2 : ttlGenericCommand(c, 1);
603 2 : }
604 :
605 3 : void persistCommand(redisClient *c) {
606 : dictEntry *de;
607 :
608 3 : de = dictFind(c->db->dict,c->argv[1]->ptr);
609 3 : if (de == NULL) {
610 2 : addReply(c,shared.czero);
611 : } else {
612 1 : if (removeExpire(c->db,c->argv[1])) {
613 1 : addReply(c,shared.cone);
614 1 : server.dirty++;
615 : } else {
616 0 : addReply(c,shared.czero);
617 : }
618 : }
619 3 : }
620 :
621 : /* -----------------------------------------------------------------------------
622 : * API to get key arguments from commands
623 : * ---------------------------------------------------------------------------*/
624 :
625 0 : int *getKeysUsingCommandTable(struct redisCommand *cmd,robj **argv, int argc, int *numkeys) {
626 0 : int j, i = 0, last, *keys;
627 : REDIS_NOTUSED(argv);
628 :
629 0 : if (cmd->firstkey == 0) {
630 0 : *numkeys = 0;
631 0 : return NULL;
632 : }
633 0 : last = cmd->lastkey;
634 0 : if (last < 0) last = argc+last;
635 0 : keys = zmalloc(sizeof(int)*((last - cmd->firstkey)+1));
636 0 : for (j = cmd->firstkey; j <= last; j += cmd->keystep) {
637 0 : redisAssert(j < argc);
638 0 : keys[i++] = j;
639 : }
640 0 : *numkeys = i;
641 0 : return keys;
642 : }
643 :
644 0 : int *getKeysFromCommand(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
645 0 : if (cmd->getkeys_proc) {
646 0 : return cmd->getkeys_proc(cmd,argv,argc,numkeys,flags);
647 : } else {
648 0 : return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
649 : }
650 : }
651 :
652 0 : void getKeysFreeResult(int *result) {
653 0 : zfree(result);
654 0 : }
655 :
656 0 : int *noPreloadGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
657 0 : if (flags & REDIS_GETKEYS_PRELOAD) {
658 0 : *numkeys = 0;
659 0 : return NULL;
660 : } else {
661 0 : return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
662 : }
663 : }
664 :
665 0 : int *renameGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
666 0 : if (flags & REDIS_GETKEYS_PRELOAD) {
667 0 : int *keys = zmalloc(sizeof(int));
668 0 : *numkeys = 1;
669 0 : keys[0] = 1;
670 0 : return keys;
671 : } else {
672 0 : return getKeysUsingCommandTable(cmd,argv,argc,numkeys);
673 : }
674 : }
675 :
676 0 : int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *numkeys, int flags) {
677 : int i, num, *keys;
678 : REDIS_NOTUSED(cmd);
679 : REDIS_NOTUSED(flags);
680 :
681 0 : num = atoi(argv[2]->ptr);
682 : /* Sanity check. Don't return any key if the command is going to
683 : * reply with syntax error. */
684 0 : if (num > (argc-3)) {
685 0 : *numkeys = 0;
686 0 : return NULL;
687 : }
688 0 : keys = zmalloc(sizeof(int)*num);
689 0 : for (i = 0; i < num; i++) keys[i] = 3+i;
690 0 : *numkeys = num;
691 0 : return keys;
692 : }
693 :
694 : /* Slot to Key API. This is used by Redis Cluster in order to obtain in
695 : * a fast way a key that belongs to a specified hash slot. This is useful
696 : * while rehashing the cluster. */
697 0 : void SlotToKeyAdd(robj *key) {
698 0 : unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));
699 :
700 0 : zslInsert(server.cluster.slots_to_keys,hashslot,key);
701 0 : incrRefCount(key);
702 0 : }
703 :
704 0 : void SlotToKeyDel(robj *key) {
705 0 : unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));
706 :
707 0 : zslDelete(server.cluster.slots_to_keys,hashslot,key);
708 0 : }
709 :
710 0 : unsigned int GetKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count) {
711 : zskiplistNode *n;
712 : zrangespec range;
713 0 : int j = 0;
714 :
715 0 : range.min = range.max = hashslot;
716 0 : range.minex = range.maxex = 0;
717 :
718 0 : n = zslFirstInRange(server.cluster.slots_to_keys, range);
719 0 : while(n && n->score == hashslot && count--) {
720 0 : keys[j++] = n->obj;
721 0 : n = n->level[0].forward;
722 : }
723 0 : return j;
724 : }
|