void setGenericCommand(redisClient *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) { long long milliseconds = 0; /* initialized to avoid any harmness warning */ if (expire) { if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK) return; if (milliseconds <= 0) { addReplyErrorFormat(c,"invalid expire time in %s",c->cmd->name); return; } if (unit == UNIT_SECONDS) milliseconds *= 1000; } if ((flags & REDIS_SET_NX && lookupKeyWrite(c->db,key) != NULL) || (flags & REDIS_SET_XX && lookupKeyWrite(c->db,key) == NULL)) { addReply(c, abort_reply ? abort_reply : shared.nullbulk); return; } setKey(c->db,key,val); server.dirty++; if (expire) setExpire(c->db,key,mstime()+milliseconds); notifyKeyspaceEvent(REDIS_NOTIFY_STRING,"set",key,c->db->id); if (expire) notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC, "expire",key,c->db->id); addReply(c, ok_reply ? ok_reply : shared.ok); }
//tair's new expire generic Command //something interesting with tair's expire protocol //if seconds < 0 then //we consider that you don't care with expire,will nothing to do //else if seconds == 0 then //we consider that you want remove the existing timeout on key(just as redis's persist) //else if seconds > 0 && seconds <= now time then //we consider that you give us duration, like 10s(just as redis's expire) //else if seconds > now time then //we conssider that you give us UNIX timestamp (seconds since January 1, 1970)(just as redis's expireat) //so it's equal persist + expire + expireat void expireXGenericCommand(redisClient *c, robj *key, robj *param) { dictEntry *de; long seconds; time_t when; if (getLongFromObject(param, &seconds) != REDIS_OK) { c->returncode = REDIS_ERR_IS_NOT_INTEGER; return; } de = dictFind(c->db->dict,key->ptr); if (de == NULL) { c->returncode = REDIS_OK_NOT_EXIST; return; } if (seconds > 0) { time_t now = time(NULL); if (seconds <= now) { when = now+seconds; } else { when = seconds; } setExpire(c->db,key,when); c->server->dirty++; } else if(seconds == 0 && removeExpire(c->db, c->argv[1])) { c->server->dirty++; } c->returncode = REDIS_OK; return; }
void renameGenericCommand(redisClient *c, int nx) { robj *o; long long expire; /* To use the same key as src and dst is probably an error */ if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) { addReply(c,shared.sameobjecterr); return; } if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL) return; incrRefCount(o); expire = getExpire(c->db,c->argv[1]); if (lookupKeyWrite(c->db,c->argv[2]) != NULL) { if (nx) { decrRefCount(o); addReply(c,shared.czero); return; } /* Overwrite: delete the old key before creating the new one with the same name. */ dbDelete(c->db,c->argv[2]); } dbAdd(c->db,c->argv[2],o); if (expire != -1) setExpire(c->db,c->argv[2],expire); dbDelete(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[2]); server.dirty++; addReply(c,nx ? shared.cone : shared.ok); }
void getsetCommand(redisClient *c) { uint16_t version = sdsversion(c->argv[1]->ptr); if (getGenericCommand(c) == REDIS_ERR) return; c->argv[2] = tryObjectEncoding(c->argv[2]); if (c->returncode == REDIS_OK_NOT_EXIST) { sdsversion_change(c->argv[1]->ptr, 0); } else { if(c->version_care && version != 0 && version != c->version) { c->returncode = REDIS_ERR_VERSION_ERROR; return; } else { sdsversion_change(c->argv[1]->ptr, c->version); } } if(c->version_care) { sdsversion_add(c->argv[1]->ptr, 1); } dbSuperReplace(c->db,c->argv[1],c->argv[2]); incrRefCount(c->argv[2]); c->db->dirty++; if (c->expiretime > 0) { setExpire(c->db,c->argv[1],c->expiretime); } else if(c->expiretime == 0) { removeXExpire(c->db, c->argv[1]); } c->returncode = REDIS_OK; }
void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) { dictEntry *de; long seconds; if (getLongFromObjectOrReply(c, param, &seconds, NULL) != REDIS_OK) return; seconds -= offset; de = dictFind(c->db->dict,key->ptr); if (de == NULL) { addReply(c,shared.czero); return; } if (seconds <= 0) { if (dbDelete(c->db,key)) server.dirty++; addReply(c, shared.cone); signalModifiedKey(c->db,key); return; } else { time_t when = time(NULL)+seconds; setExpire(c->db,key,when); addReply(c,shared.cone); signalModifiedKey(c->db,key); server.dirty++; return; } }
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { int retval; long seconds = 0; /* initialized to avoid an harmness warning */ if (expire) { if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK) return; if (seconds <= 0) { addReplySds(c,sdsnew("-ERR invalid expire time in SETEX\r\n")); return; } } touchWatchedKey(c->db,key); if (nx) deleteIfVolatile(c->db,key); retval = dbAdd(c->db,key,val); if (retval == REDIS_ERR) { if (!nx) { dbReplace(c->db,key,val); incrRefCount(val); } else { addReply(c,shared.czero); return; } } else { incrRefCount(val); } server.dirty++; removeExpire(c->db,key); if (expire) setExpire(c->db,key,time(NULL)+seconds); addReply(c, nx ? shared.cone : shared.ok); }
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { int retval; long seconds = 0; /* initialized to avoid an harmness warning */ robj *oldval; c->returncode = REDIS_ERR; if (expire) { if (getLongFromObject(expire, &seconds) != REDIS_OK) { c->returncode = REDIS_ERR_IS_NOT_INTEGER; return; } } oldval = lookupKeyWriteWithVersion(c->db,key,&(c->version)); /* Force expire of old key if needed */ if(oldval != NULL) { if(checkType(c, oldval, REDIS_STRING)) { c->returncode = REDIS_ERR_WRONG_TYPE_ERROR; return; } uint16_t version = sdsversion(key->ptr); if(c->version_care && version != 0 && version != c->version) { c->returncode = REDIS_ERR_VERSION_ERROR; return; } else { sdsversion_change(key->ptr, c->version); } } else { sdsversion_change(key->ptr, 0); } if(c->version_care) { sdsversion_add(key->ptr, 1); } retval = dbAdd(c->db,key,val); if (retval == REDIS_ERR) { if (!nx) { dbSuperReplace(c->db,key,val); incrRefCount(val); } else { c->returncode = REDIS_OK_BUT_ALREADY_EXIST; return; } } else { incrRefCount(val); } c->db->dirty++; if (expire) { setExpire(c->db,key,seconds); } else if(c->expiretime == 0) { removeXExpire(c->db, key); } c->returncode = REDIS_OK; }
void moveCommand(client *c) { robj *o; redisDb *src, *dst; int srcid; long long dbid, expire; if (server.cluster_enabled) { addReplyError(c,"MOVE is not allowed in cluster mode"); return; } /* Obtain source and target DB pointers */ src = c->db; srcid = c->db->id; if (getLongLongFromObject(c->argv[2],&dbid) == C_ERR || dbid < INT_MIN || dbid > INT_MAX || selectDb(c,dbid) == C_ERR) { addReply(c,shared.outofrangeerr); return; } dst = c->db; selectDb(c,srcid); /* Back to the source DB */ /* If the user is moving using as target the same * DB as the source DB it is probably an error. */ if (src == dst) { addReply(c,shared.sameobjecterr); return; } /* Check if the element exists and get a reference */ o = lookupKeyWrite(c->db,c->argv[1]); if (!o) { addReply(c,shared.czero); return; } expire = getExpire(c->db,c->argv[1]); /* Return zero if the key already exists in the target DB */ if (lookupKeyWrite(dst,c->argv[1]) != NULL) { addReply(c,shared.czero); return; } dbAdd(dst,c->argv[1],o); if (expire != -1) setExpire(dst,c->argv[1],expire); incrRefCount(o); /* OK! key moved, free the entry in the source DB */ dbDelete(src,c->argv[1]); server.dirty++; addReply(c,shared.cone); }
void expirekeysGenericCommand(redisClient *c, robj *keypattern, robj *param, long offset) { dictIterator *di; dictEntry *de; sds pattern = keypattern->ptr; int plen = sdslen(pattern); unsigned long numkeys = 0, allkeys; time_t seconds; time_t when; if (getLongFromObjectOrReply(c, param, &seconds, NULL) != REDIS_OK) return; seconds -= offset; when = time(NULL)+seconds; di = dictGetIterator(c->db->dict); allkeys = (pattern[0] == '*' && pattern[1] == '\0'); while((de = dictNext(di)) != NULL) { sds key = dictGetEntryKey(de); robj *keyobj; if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) { keyobj = createStringObject(key,sdslen(key)); if (seconds <= 0 && !server.loading && !server.masterhost) { robj *aux; redisAssert(dbDelete(c->db,keyobj)); server.dirty++; numkeys++; /* Replicate/AOF this as an explicit DEL. */ aux = createStringObject("DEL",3); rewriteClientCommandVector(c,2,aux,keyobj); decrRefCount(aux); touchWatchedKey(c->db,keyobj); } else { time_t when = time(NULL)+seconds; setExpire(c->db,keyobj,when); touchWatchedKey(c->db,keyobj); server.dirty++; numkeys++; } decrRefCount(keyobj); } } dictReleaseIterator(di); addReplyLongLong(c,numkeys); }
/* This is the generic command implementation for EXPIRE, PEXPIRE, EXPIREAT * and PEXPIREAT. Because the commad second argument may be relative or absolute * the "basetime" argument is used to signal what the base time is (either 0 * for *AT variants of the command, or the current time for relative expires). * * unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for * the argv[2] parameter. The basetime is always specified in milliseconds. */ void expireGenericCommand(client *c, long long basetime, int unit) { robj *key = c->argv[1], *param = c->argv[2]; long long when; /* unix time in milliseconds when the key will expire. */ if (getLongLongFromObjectOrReply(c, param, &when, NULL) != C_OK) return; if (unit == UNIT_SECONDS) when *= 1000; when += basetime; /* No key, return zero. */ if (lookupKeyWrite(c->db,key) == NULL) { addReply(c,shared.czero); return; } /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past * should never be executed as a DEL when load the AOF or in the context * of a slave instance. * * Instead we take the other branch of the IF statement setting an expire * (possibly in the past) and wait for an explicit DEL from the master. */ if (when <= mstime() && !server.loading && !server.masterhost) { robj *aux; serverAssertWithInfo(c,key,dbDelete(c->db,key)); server.dirty++; /* Replicate/AOF this as an explicit DEL. */ aux = createStringObject("DEL",3); rewriteClientCommandVector(c,2,aux,key); decrRefCount(aux); signalModifiedKey(c->db,key); notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id); addReply(c, shared.cone); return; } else { setExpire(c->db,key,when); addReply(c,shared.cone); signalModifiedKey(c->db,key); notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id); server.dirty++; return; } }
void expireGenericCommand(redisClient *c, robj *key, robj *param, ssize_t offset) { dictEntry *de; #ifdef _WIN64 long long seconds; #else long seconds; #endif if (getLongFromObjectOrReply(c, param, &seconds, NULL) != REDIS_OK) return; seconds -= offset; de = dictFind(c->db->dict,key->ptr); if (de == NULL) { addReply(c,shared.czero); return; } /* EXPIRE with negative TTL, or EXPIREAT with a timestamp into the past * should never be executed as a DEL when load the AOF or in the context * of a slave instance. * * Instead we take the other branch of the IF statement setting an expire * (possibly in the past) and wait for an explicit DEL from the master. */ if (seconds <= 0 && !server.loading && !server.masterhost) { robj *aux; redisAssert(dbDelete(c->db,key)); server.dirty++; /* Replicate/AOF this as an explicit DEL. */ aux = createStringObject("DEL",3); rewriteClientCommandVector(c,2,aux,key); decrRefCount(aux); signalModifiedKey(c->db,key); addReply(c, shared.cone); return; } else { time_t when = time(NULL)+seconds; setExpire(c->db,key,when); addReply(c,shared.cone); signalModifiedKey(c->db,key); server.dirty++; return; } }
void migpexpireatCommand (client * c) { dictEntry *de; robj *key = c->argv[1], *param = c->argv[2]; long long when; if (getLongLongFromObjectOrReply (c, param, &when, NULL) != C_OK) return; de = dictFind (c->db->dict, key->ptr); if (de == NULL) { addReply (c, shared.czero); return; } setExpire (c->db, key, when); addReply (c, shared.cone); signalModifiedKey (c->db, key); server.dirty++; }
void renameGenericCommand(client *c, int nx) { robj *o; long long expire; int samekey = 0; /* When source and dest key is the same, no operation is performed, * if the key exists, however we still return an error on unexisting key. */ if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) samekey = 1; if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL) return; if (samekey) { addReply(c,nx ? shared.czero : shared.ok); return; } incrRefCount(o); expire = getExpire(c->db,c->argv[1]); if (lookupKeyWrite(c->db,c->argv[2]) != NULL) { if (nx) { decrRefCount(o); addReply(c,shared.czero); return; } /* Overwrite: delete the old key before creating the new one * with the same name. */ dbDelete(c->db,c->argv[2]); } dbAdd(c->db,c->argv[2],o); if (expire != -1) setExpire(c->db,c->argv[2],expire); dbDelete(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[1]); signalModifiedKey(c->db,c->argv[2]); notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_from", c->argv[1],c->db->id); notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_to", c->argv[2],c->db->id); server.dirty++; addReply(c,nx ? shared.cone : shared.ok); }
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) { int retval; long seconds = 0; /* initialized to avoid an harmness warning */ #ifdef AUTH_FEATURE /* Check mod permissions */ if (authCheckModOrReply(c) == REDIS_ERR) return; /* Check permissions */ if (authCheckPathOrReply(c, key) == REDIS_ERR) return; #endif if (expire) { if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK) return; if (seconds <= 0) { addReplyError(c,"invalid expire time in SETEX"); return; } } retval = dbAdd(c->db,key,val); if (retval == REDIS_ERR) { if (!nx) { dbReplace(c->db,key,val); incrRefCount(val); } else { addReply(c,shared.czero); return; } } else { incrRefCount(val); } touchWatchedKey(c->db,key); server.dirty++; removeExpire(c->db,key); if (expire) setExpire(c->db,key,time(NULL)+seconds); addReply(c, nx ? shared.cone : shared.ok); }
robj *lookupKey(redisDb *db, robj *key) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { robj *val = dictGetEntryVal(de); /* Update the access time for the aging algorithm. * Don't do it if we have a saving child, as this will trigger * a copy on write madness. */ if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) val->lru = server.lruclock; if (server.ds_enabled && cacheScheduleIOGetFlags(db,key) & REDIS_IO_SAVEINPROG) { /* Need to wait for the key to get unbusy */ redisLog(REDIS_DEBUG,"Lookup found a key in SAVEINPROG state. Waiting. (Key was in the cache)"); lookupWaitBusyKey(db,key); } server.stat_keyspace_hits++; return val; } else { time_t expire; robj *val; /* Key not found in the in memory hash table, but if disk store is * enabled we may have this key on disk. If so load it in memory * in a blocking way. */ if (server.ds_enabled && cacheKeyMayExist(db,key)) { long flags = cacheScheduleIOGetFlags(db,key); /* They key is not in cache, but it has a SAVE op in queue? * The only possibility is that the key was deleted, since * dirty keys are not evicted. */ if (flags & REDIS_IO_SAVE) { server.stat_keyspace_misses++; return NULL; } /* At this point we need to blocking load the key in memory. * The first thing we do is waiting here if the key is busy. */ if (flags & REDIS_IO_SAVEINPROG) { redisLog(REDIS_DEBUG,"Lookup found a key in SAVEINPROG state. Waiting (while force loading)."); lookupWaitBusyKey(db,key); } redisLog(REDIS_DEBUG,"Force loading key %s via lookup", key->ptr); val = dsGet(db,key,&expire); if (val) { int retval = dbAdd(db,key,val); redisAssert(retval == REDIS_OK); if (expire != -1) setExpire(db,key,expire); server.stat_keyspace_hits++; return val; } else { cacheSetKeyDoesNotExist(db,key); } } server.stat_keyspace_misses++; return NULL; } }
/* * * slotsrestore key ttl val [key ttl val ...] * */ void slotsrestoreCommand(redisClient *c) { if (c->argc < 4 || (c->argc - 1) % 3 != 0) { addReplyErrorFormat(c, "wrong number of arguments for 'slotsrestore' command"); return; } int n = (c->argc - 1) / 3; long long *ttls = zmalloc(sizeof(long long) * n); robj **vals = zmalloc(sizeof(robj *) * n); for (int i = 0; i < n; i ++) { vals[i] = NULL; } for (int i = 0; i < n; i ++) { robj *key = c->argv[i * 3 + 1]; robj *ttl = c->argv[i * 3 + 2]; robj *val = c->argv[i * 3 + 3]; if (lookupKeyWrite(c->db, key) != NULL) { redisLog(REDIS_WARNING, "slotsrestore: slot = %d, key = '%s' already exists", slots_num(key->ptr, NULL), (char *)key->ptr); } if (getLongLongFromObjectOrReply(c, ttl, &ttls[i], NULL) != REDIS_OK) { goto cleanup; } else if (ttls[i] < 0) { addReplyError(c, "invalid ttl value, must be >= 0"); goto cleanup; } rio payload; int type; if (verifyDumpPayload(val->ptr, sdslen(val->ptr)) != REDIS_OK) { addReplyError(c, "dump payload version or checksum are wrong"); goto cleanup; } rioInitWithBuffer(&payload, val->ptr); if (((type = rdbLoadObjectType(&payload)) == -1) || ((vals[i] = rdbLoadObject(type, &payload)) == NULL)) { addReplyError(c, "bad data format"); goto cleanup; } } for (int i = 0; i < n; i ++) { robj *key = c->argv[i * 3 + 1]; long long ttl = ttls[i]; robj *val = vals[i]; dbDelete(c->db, key); dbAdd(c->db, key, val); incrRefCount(val); if (ttl) { setExpire(c->db, key, mstime() + ttl); } signalModifiedKey(c->db, key); server.dirty ++; } addReply(c, shared.ok); cleanup: for (int i = 0; i < n; i ++) { if (vals[i] != NULL) { decrRefCount(vals[i]); } } zfree(vals); zfree(ttls); }