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);
}
Beispiel #2
0
//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;
}
Beispiel #3
0
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);
}
Beispiel #4
0
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;
}
Beispiel #5
0
Datei: db.c Projekt: andmej/redis
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;
    }
}
Beispiel #6
0
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);
}
Beispiel #7
0
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;
}
Beispiel #8
0
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);
}
Beispiel #9
0
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);
}
Beispiel #10
0
/* 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;
    }
}
Beispiel #11
0
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;
    }
}
Beispiel #12
0
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++;
}
Beispiel #13
0
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);
}
Beispiel #14
0
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);
}
Beispiel #15
0
Datei: db.c Projekt: andmej/redis
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;
    }
}
Beispiel #16
0
/* *
 * 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);
}