]>
git.saurik.com Git - redis.git/blob - src/db.c
ca520c8259d0b7758baf11fd0037345b653885af
   5 /*----------------------------------------------------------------------------- 
   7  *----------------------------------------------------------------------------*/ 
   9 robj 
*lookupKey(redisDb 
*db
, robj 
*key
) { 
  10     dictEntry 
*de 
= dictFind(db
->dict
,key
->ptr
); 
  12         robj 
*val 
= dictGetEntryVal(de
); 
  14         if (server
.vm_enabled
) { 
  15             if (val
->storage 
== REDIS_VM_MEMORY 
|| 
  16                 val
->storage 
== REDIS_VM_SWAPPING
) 
  18                 /* If we were swapping the object out, cancel the operation */ 
  19                 if (val
->storage 
== REDIS_VM_SWAPPING
) 
  20                     vmCancelThreadedIOJob(val
); 
  21                 /* Update the access time for the aging algorithm. */ 
  22                 val
->lru 
= server
.lruclock
; 
  24                 int notify 
= (val
->storage 
== REDIS_VM_LOADING
); 
  26                 /* Our value was swapped on disk. Bring it at home. */ 
  27                 redisAssert(val
->type 
== REDIS_VMPOINTER
); 
  28                 val 
= vmLoadObject(val
); 
  29                 dictGetEntryVal(de
) = val
; 
  31                 /* Clients blocked by the VM subsystem may be waiting for 
  33                 if (notify
) handleClientsBlockedOnSwappedKey(db
,key
); 
  42 robj 
*lookupKeyRead(redisDb 
*db
, robj 
*key
) { 
  43     expireIfNeeded(db
,key
); 
  44     return lookupKey(db
,key
); 
  47 robj 
*lookupKeyWrite(redisDb 
*db
, robj 
*key
) { 
  48     expireIfNeeded(db
,key
); 
  49     return lookupKey(db
,key
); 
  52 robj 
*lookupKeyReadOrReply(redisClient 
*c
, robj 
*key
, robj 
*reply
) { 
  53     robj 
*o 
= lookupKeyRead(c
->db
, key
); 
  54     if (!o
) addReply(c
,reply
); 
  58 robj 
*lookupKeyWriteOrReply(redisClient 
*c
, robj 
*key
, robj 
*reply
) { 
  59     robj 
*o 
= lookupKeyWrite(c
->db
, key
); 
  60     if (!o
) addReply(c
,reply
); 
  64 /* Add the key to the DB. If the key already exists REDIS_ERR is returned, 
  65  * otherwise REDIS_OK is returned, and the caller should increment the 
  66  * refcount of 'val'. */ 
  67 int dbAdd(redisDb 
*db
, robj 
*key
, robj 
*val
) { 
  68     /* Perform a lookup before adding the key, as we need to copy the 
  70     if (dictFind(db
->dict
, key
->ptr
) != NULL
) { 
  73         sds copy 
= sdsdup(key
->ptr
); 
  74         dictAdd(db
->dict
, copy
, val
); 
  79 /* If the key does not exist, this is just like dbAdd(). Otherwise 
  80  * the value associated to the key is replaced with the new one. 
  82  * On update (key already existed) 0 is returned. Otherwise 1. */ 
  83 int dbReplace(redisDb 
*db
, robj 
*key
, robj 
*val
) { 
  84     if (dictFind(db
->dict
,key
->ptr
) == NULL
) { 
  85         sds copy 
= sdsdup(key
->ptr
); 
  86         dictAdd(db
->dict
, copy
, val
); 
  89         dictReplace(db
->dict
, key
->ptr
, val
); 
  94 int dbExists(redisDb 
*db
, robj 
*key
) { 
  95     return dictFind(db
->dict
,key
->ptr
) != NULL
; 
  98 /* Return a random key, in form of a Redis object. 
  99  * If there are no keys, NULL is returned. 
 101  * The function makes sure to return keys not already expired. */ 
 102 robj 
*dbRandomKey(redisDb 
*db
) { 
 103     struct dictEntry 
*de
; 
 109         de 
= dictGetRandomKey(db
->dict
); 
 110         if (de 
== NULL
) return NULL
; 
 112         key 
= dictGetEntryKey(de
); 
 113         keyobj 
= createStringObject(key
,sdslen(key
)); 
 114         if (dictFind(db
->expires
,key
)) { 
 115             if (expireIfNeeded(db
,keyobj
)) { 
 116                 decrRefCount(keyobj
); 
 117                 continue; /* search for another key. This expired. */ 
 124 /* Delete a key, value, and associated expiration entry if any, from the DB */ 
 125 int dbDelete(redisDb 
*db
, robj 
*key
) { 
 126     /* If VM is enabled make sure to awake waiting clients for this key: 
 127      * deleting the key will kill the I/O thread bringing the key from swap 
 128      * to memory, so the client will never be notified and unblocked if we 
 129      * don't do it now. */ 
 130     handleClientsBlockedOnSwappedKey(db
,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     if (dictSize(db
->expires
) > 0) dictDelete(db
->expires
,key
->ptr
); 
 134     return dictDelete(db
->dict
,key
->ptr
) == DICT_OK
; 
 137 /* Empty the whole database */ 
 138 long long emptyDb() { 
 140     long long removed 
= 0; 
 142     for (j 
= 0; j 
< server
.dbnum
; j
++) { 
 143         removed 
+= dictSize(server
.db
[j
].dict
); 
 144         dictEmpty(server
.db
[j
].dict
); 
 145         dictEmpty(server
.db
[j
].expires
); 
 150 int selectDb(redisClient 
*c
, int id
) { 
 151     if (id 
< 0 || id 
>= server
.dbnum
) 
 153     c
->db 
= &server
.db
[id
]; 
 157 /*----------------------------------------------------------------------------- 
 158  * Type agnostic commands operating on the key space 
 159  *----------------------------------------------------------------------------*/ 
 161 void flushdbCommand(redisClient 
*c
) { 
 162     server
.dirty 
+= dictSize(c
->db
->dict
); 
 163     touchWatchedKeysOnFlush(c
->db
->id
); 
 164     dictEmpty(c
->db
->dict
); 
 165     dictEmpty(c
->db
->expires
); 
 166     addReply(c
,shared
.ok
); 
 169 void flushallCommand(redisClient 
*c
) { 
 170     touchWatchedKeysOnFlush(-1); 
 171     server
.dirty 
+= emptyDb(); 
 172     addReply(c
,shared
.ok
); 
 173     if (server
.bgsavechildpid 
!= -1) { 
 174         kill(server
.bgsavechildpid
,SIGKILL
); 
 175         rdbRemoveTempFile(server
.bgsavechildpid
); 
 177     rdbSave(server
.dbfilename
); 
 181 void delCommand(redisClient 
*c
) { 
 184     for (j 
= 1; j 
< c
->argc
; j
++) { 
 185         if (dbDelete(c
->db
,c
->argv
[j
])) { 
 186             touchWatchedKey(c
->db
,c
->argv
[j
]); 
 191     addReplyLongLong(c
,deleted
); 
 194 void existsCommand(redisClient 
*c
) { 
 195     expireIfNeeded(c
->db
,c
->argv
[1]); 
 196     if (dbExists(c
->db
,c
->argv
[1])) { 
 197         addReply(c
, shared
.cone
); 
 199         addReply(c
, shared
.czero
); 
 203 void selectCommand(redisClient 
*c
) { 
 204     int id 
= atoi(c
->argv
[1]->ptr
); 
 206     if (selectDb(c
,id
) == REDIS_ERR
) { 
 207         addReplySds(c
,sdsnew("-ERR invalid DB index\r\n")); 
 209         addReply(c
,shared
.ok
); 
 213 void randomkeyCommand(redisClient 
*c
) { 
 216     if ((key 
= dbRandomKey(c
->db
)) == NULL
) { 
 217         addReply(c
,shared
.nullbulk
); 
 225 void keysCommand(redisClient 
*c
) { 
 228     sds pattern 
= c
->argv
[1]->ptr
; 
 229     int plen 
= sdslen(pattern
), allkeys
; 
 230     unsigned long numkeys 
= 0; 
 231     robj 
*lenobj 
= createObject(REDIS_STRING
,NULL
); 
 233     di 
= dictGetIterator(c
->db
->dict
); 
 235     decrRefCount(lenobj
); 
 236     allkeys 
= (pattern
[0] == '*' && pattern
[1] == '\0'); 
 237     while((de 
= dictNext(di
)) != NULL
) { 
 238         sds key 
= dictGetEntryKey(de
); 
 241         if (allkeys 
|| stringmatchlen(pattern
,plen
,key
,sdslen(key
),0)) { 
 242             keyobj 
= createStringObject(key
,sdslen(key
)); 
 243             if (expireIfNeeded(c
->db
,keyobj
) == 0) { 
 244                 addReplyBulk(c
,keyobj
); 
 247             decrRefCount(keyobj
); 
 250     dictReleaseIterator(di
); 
 251     lenobj
->ptr 
= sdscatprintf(sdsempty(),"*%lu\r\n",numkeys
); 
 254 void dbsizeCommand(redisClient 
*c
) { 
 256         sdscatprintf(sdsempty(),":%lu\r\n",dictSize(c
->db
->dict
))); 
 259 void lastsaveCommand(redisClient 
*c
) { 
 261         sdscatprintf(sdsempty(),":%lu\r\n",server
.lastsave
)); 
 264 void typeCommand(redisClient 
*c
) { 
 268     o 
= lookupKeyRead(c
->db
,c
->argv
[1]); 
 273         case REDIS_STRING
: type 
= "+string"; break; 
 274         case REDIS_LIST
: type 
= "+list"; break; 
 275         case REDIS_SET
: type 
= "+set"; break; 
 276         case REDIS_ZSET
: type 
= "+zset"; break; 
 277         case REDIS_HASH
: type 
= "+hash"; break; 
 278         default: type 
= "+unknown"; break; 
 281     addReplySds(c
,sdsnew(type
)); 
 282     addReply(c
,shared
.crlf
); 
 285 void saveCommand(redisClient 
*c
) { 
 286     if (server
.bgsavechildpid 
!= -1) { 
 287         addReplySds(c
,sdsnew("-ERR background save in progress\r\n")); 
 290     if (rdbSave(server
.dbfilename
) == REDIS_OK
) { 
 291         addReply(c
,shared
.ok
); 
 293         addReply(c
,shared
.err
); 
 297 void bgsaveCommand(redisClient 
*c
) { 
 298     if (server
.bgsavechildpid 
!= -1) { 
 299         addReplySds(c
,sdsnew("-ERR background save already in progress\r\n")); 
 302     if (rdbSaveBackground(server
.dbfilename
) == REDIS_OK
) { 
 303         char *status 
= "+Background saving started\r\n"; 
 304         addReplySds(c
,sdsnew(status
)); 
 306         addReply(c
,shared
.err
); 
 310 void shutdownCommand(redisClient 
*c
) { 
 311     if (prepareForShutdown() == REDIS_OK
) 
 313     addReplySds(c
, sdsnew("-ERR Errors trying to SHUTDOWN. Check logs.\r\n")); 
 316 void renameGenericCommand(redisClient 
*c
, int nx
) { 
 319     /* To use the same key as src and dst is probably an error */ 
 320     if (sdscmp(c
->argv
[1]->ptr
,c
->argv
[2]->ptr
) == 0) { 
 321         addReply(c
,shared
.sameobjecterr
); 
 325     if ((o 
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.nokeyerr
)) == NULL
) 
 329     if (dbAdd(c
->db
,c
->argv
[2],o
) == REDIS_ERR
) { 
 332             addReply(c
,shared
.czero
); 
 335         dbReplace(c
->db
,c
->argv
[2],o
); 
 337     dbDelete(c
->db
,c
->argv
[1]); 
 338     touchWatchedKey(c
->db
,c
->argv
[1]); 
 339     touchWatchedKey(c
->db
,c
->argv
[2]); 
 341     addReply(c
,nx 
? shared
.cone 
: shared
.ok
); 
 344 void renameCommand(redisClient 
*c
) { 
 345     renameGenericCommand(c
,0); 
 348 void renamenxCommand(redisClient 
*c
) { 
 349     renameGenericCommand(c
,1); 
 352 void moveCommand(redisClient 
*c
) { 
 357     /* Obtain source and target DB pointers */ 
 360     if (selectDb(c
,atoi(c
->argv
[2]->ptr
)) == REDIS_ERR
) { 
 361         addReply(c
,shared
.outofrangeerr
); 
 365     selectDb(c
,srcid
); /* Back to the source DB */ 
 367     /* If the user is moving using as target the same 
 368      * DB as the source DB it is probably an error. */ 
 370         addReply(c
,shared
.sameobjecterr
); 
 374     /* Check if the element exists and get a reference */ 
 375     o 
= lookupKeyWrite(c
->db
,c
->argv
[1]); 
 377         addReply(c
,shared
.czero
); 
 381     /* Try to add the element to the target DB */ 
 382     if (dbAdd(dst
,c
->argv
[1],o
) == REDIS_ERR
) { 
 383         addReply(c
,shared
.czero
); 
 388     /* OK! key moved, free the entry in the source DB */ 
 389     dbDelete(src
,c
->argv
[1]); 
 391     addReply(c
,shared
.cone
); 
 394 /*----------------------------------------------------------------------------- 
 396  *----------------------------------------------------------------------------*/ 
 398 int removeExpire(redisDb 
*db
, robj 
*key
) { 
 399     /* An expire may only be removed if there is a corresponding entry in the 
 400      * main dict. Otherwise, the key will never be freed. */ 
 401     redisAssert(dictFind(db
->dict
,key
->ptr
) != NULL
); 
 402     return dictDelete(db
->expires
,key
->ptr
) == DICT_OK
; 
 405 void setExpire(redisDb 
*db
, robj 
*key
, time_t when
) { 
 408     /* Reuse the sds from the main dict in the expire dict */ 
 409     de 
= dictFind(db
->dict
,key
->ptr
); 
 410     redisAssert(de 
!= NULL
); 
 411     dictReplace(db
->expires
,dictGetEntryKey(de
),(void*)when
); 
 414 /* Return the expire time of the specified key, or -1 if no expire 
 415  * is associated with this key (i.e. the key is non volatile) */ 
 416 time_t getExpire(redisDb 
*db
, robj 
*key
) { 
 419     /* No expire? return ASAP */ 
 420     if (dictSize(db
->expires
) == 0 || 
 421        (de 
= dictFind(db
->expires
,key
->ptr
)) == NULL
) return -1; 
 423     /* The entry was found in the expire dict, this means it should also 
 424      * be present in the main dict (safety check). */ 
 425     redisAssert(dictFind(db
->dict
,key
->ptr
) != NULL
); 
 426     return (time_t) dictGetEntryVal(de
); 
 429 /* Propagate expires into slaves and the AOF file. 
 430  * When a key expires in the master, a DEL operation for this key is sent 
 431  * to all the slaves and the AOF file if enabled. 
 433  * This way the key expiry is centralized in one place, and since both 
 434  * AOF and the master->slave link guarantee operation ordering, everything 
 435  * will be consistent even if we allow write operations against expiring 
 437 void propagateExpire(redisDb 
*db
, robj 
*key
) { 
 438     struct redisCommand 
*cmd
; 
 441     cmd 
= lookupCommand("del"); 
 442     argv
[0] = createStringObject("DEL",3); 
 446     if (server
.appendonly
) 
 447         feedAppendOnlyFile(cmd
,db
->id
,argv
,2); 
 448     if (listLength(server
.slaves
)) 
 449         replicationFeedSlaves(server
.slaves
,db
->id
,argv
,2); 
 451     decrRefCount(argv
[0]); 
 452     decrRefCount(argv
[1]); 
 455 int expireIfNeeded(redisDb 
*db
, robj 
*key
) { 
 456     time_t when 
= getExpire(db
,key
); 
 458     /* If we are running in the context of a slave, return ASAP: 
 459      * the slave key expiration is controlled by the master that will 
 460      * send us synthesized DEL operations for expired keys. 
 462      * Still we try to return the right information to the caller,  
 463      * that is, 0 if we think the key should be still valid, 1 if 
 464      * we think the key is expired at this time. */ 
 465     if (server
.masterhost 
!= NULL
) { 
 466         return time(NULL
) > when
; 
 469     if (when 
< 0) return 0; 
 471     /* Return when this key has not expired */ 
 472     if (time(NULL
) <= when
) return 0; 
 475     server
.stat_expiredkeys
++; 
 477     propagateExpire(db
,key
); 
 478     return dbDelete(db
,key
); 
 481 /*----------------------------------------------------------------------------- 
 483  *----------------------------------------------------------------------------*/ 
 485 void expireGenericCommand(redisClient 
*c
, robj 
*key
, robj 
*param
, long offset
) { 
 489     if (getLongFromObjectOrReply(c
, param
, &seconds
, NULL
) != REDIS_OK
) return; 
 493     de 
= dictFind(c
->db
->dict
,key
->ptr
); 
 495         addReply(c
,shared
.czero
); 
 499         if (dbDelete(c
->db
,key
)) server
.dirty
++; 
 500         addReply(c
, shared
.cone
); 
 501         touchWatchedKey(c
->db
,key
); 
 504         time_t when 
= time(NULL
)+seconds
; 
 505         setExpire(c
->db
,key
,when
); 
 506         addReply(c
,shared
.cone
); 
 507         touchWatchedKey(c
->db
,key
); 
 513 void expireCommand(redisClient 
*c
) { 
 514     expireGenericCommand(c
,c
->argv
[1],c
->argv
[2],0); 
 517 void expireatCommand(redisClient 
*c
) { 
 518     expireGenericCommand(c
,c
->argv
[1],c
->argv
[2],time(NULL
)); 
 521 void ttlCommand(redisClient 
*c
) { 
 522     time_t expire
, ttl 
= -1; 
 524     expire 
= getExpire(c
->db
,c
->argv
[1]); 
 526         ttl 
= (expire
-time(NULL
)); 
 527         if (ttl 
< 0) ttl 
= -1; 
 529     addReplyLongLong(c
,(long long)ttl
); 
 532 void persistCommand(redisClient 
*c
) { 
 535     de 
= dictFind(c
->db
->dict
,c
->argv
[1]->ptr
); 
 537         addReply(c
,shared
.czero
); 
 539         if (removeExpire(c
->db
,c
->argv
[1])) { 
 540             addReply(c
,shared
.cone
); 
 543             addReply(c
,shared
.czero
);