static void sendLuaFuncReply(cli *c, sds file) { //printf("sendLuaFuncReply\n"); int argc; robj **rargv = NULL; if (!sdslen(file) || !strcmp(file, "/")) { argc = 2; //NOTE: rargv[0] is ignored rargv = zmalloc(sizeof(robj *) * argc); rargv[1] = _createStringObject(server.alc.WebServerIndexFunc); } else if (c->http.post && c->http.req_clen) { int urgc; sds *urgv = sdssplitlen(file, sdslen(file), "/", 1, &urgc); sds pb = c->http.post_body; sds *argv = sdssplitlen(pb, sdslen(pb), "&", 1, &argc); rargv = zmalloc(sizeof(robj *) * (argc + urgc + 1)); for (int i = 0; i < urgc; i++) { rargv[i + 1] = createStringObject(urgv[i], sdslen(urgv[i])); } for (int i = 0; i < argc; i++) { char *x = strchr(argv[i], '='); if (!x) continue; x++; rargv[i + urgc + 1] = createStringObject(x, strlen(x)); } for (int i = 0; i < urgc; i++) sdsfree(urgv[i]); zfree(urgv); zfree(argv); argc += (urgc + 1); //NOTE: rargv[0] is ignored } else { sds *argv = sdssplitlen(file, sdslen(file), "/", 1, &argc); rargv = zmalloc(sizeof(robj *) * (argc + 1)); for (int i = 0; i < argc; i++) { rargv[i + 1] = createStringObject(argv[i], sdslen(argv[i])); } for (int i = 0; i < argc; i++) sdsfree(argv[i]); zfree(argv); argc++; //NOTE: rargv[0] is ignored } if (!luafunc_call(c, argc, rargv)) { robj *resp = luaReplyToHTTPReply(server.lua); SEND_REPLY_FROM_STRING(send_http_reponse_header(c, sdslen(resp->ptr))); if (c->http.get || c->http.post) addReply(c, resp); decrRefCount(resp); } for (int i = 1; i < argc; i++) decrRefCount(rargv[i]); zfree(rargv); CLEAR_LUA_STACK }
void slaveofCommand(redisClient *c) { if (!strcasecmp(c->argv[1]->ptr,"no") && !strcasecmp(c->argv[2]->ptr,"one")) { if (server.masterhost) { sdsfree(server.masterhost); server.masterhost = NULL; if (server.master) freeClient(server.master); if (server.repl_state == REDIS_REPL_TRANSFER) replicationAbortSyncTransfer(); else if (server.repl_state == REDIS_REPL_CONNECTING || server.repl_state == REDIS_REPL_RECEIVE_PONG) undoConnectWithMaster(); server.repl_state = REDIS_REPL_NONE; redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)"); } } else { long port; if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != REDIS_OK)) return; /* Check if we are already attached to the specified slave */ if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr) && server.masterport == port) { redisLog(REDIS_NOTICE,"SLAVE OF would result into synchronization with the master we are already connected with. No operation performed."); addReplySds(c,sdsnew("+OK Already connected to specified master\r\n")); return; } /* There was no previous master or the user specified a different one, * we can continue. */ sdsfree(server.masterhost); server.masterhost = sdsdup(c->argv[1]->ptr); server.masterport = port; if (server.master) freeClient(server.master); disconnectSlaves(); /* Force our slaves to resync with us as well. */ if (server.repl_state == REDIS_REPL_TRANSFER) replicationAbortSyncTransfer(); server.repl_state = REDIS_REPL_CONNECT; redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)", server.masterhost, server.masterport); } addReply(c,shared.ok); }
void pushxGenericCommand(client *c, robj *refval, robj *val, int where) { robj *subject; listTypeIterator *iter; listTypeEntry entry; int inserted = 0; if ((subject = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,subject,OBJ_LIST)) return; if (refval != NULL) { /* Seek refval from head to tail */ iter = listTypeInitIterator(subject,0,LIST_TAIL); while (listTypeNext(iter,&entry)) { if (listTypeEqual(&entry,refval)) { listTypeInsert(&entry,val,where); inserted = 1; break; } } listTypeReleaseIterator(iter); if (inserted) { signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,"linsert", c->argv[1],c->db->id); server.dirty++; } else { /* Notify client of a failed insert */ addReply(c,shared.cnegone); return; } } else { char *event = (where == LIST_HEAD) ? "lpush" : "rpush"; listTypePush(subject,val,where); signalModifiedKey(c->db,c->argv[1]); notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); server.dirty++; } addReplyLongLong(c,listTypeLength(subject)); }
int getGenericCommand(redisClient *c) { robj *o; #ifdef AUTH_FEATURE if (authCheckPathOrReply(c, c->argv[1]) == REDIS_ERR) { return REDIS_ERR; } #endif if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL) return REDIS_OK; if (o->type != REDIS_STRING) { addReply(c,shared.wrongtypeerr); return REDIS_ERR; } else { addReplyBulk(c,o); return REDIS_OK; } }
/* FLUSHALL [ASYNC] * * Flushes the whole server data set. */ void flushallCommand(client *c) { int flags; if (getFlushCommandFlags(c,&flags) == C_ERR) return; signalFlushedDb(-1); server.dirty += emptyDb(-1,flags,NULL); addReply(c,shared.ok); if (server.rdb_child_pid != -1) { kill(server.rdb_child_pid,SIGUSR1); rdbRemoveTempFile(server.rdb_child_pid); } if (server.saveparamslen > 0) { /* Normally rdbSave() will reset dirty, but we don't want this here * as otherwise FLUSHALL will not be replicated nor put into the AOF. */ int saved_dirty = server.dirty; rdbSave(server.rdb_filename); server.dirty = saved_dirty; } server.dirty++; }
void typeCommand(redisClient *c) { robj *o; char *type; o = lookupKeyRead(c->db,c->argv[1]); if (o == NULL) { type = "+none"; } else { switch(o->type) { case REDIS_STRING: type = "+string"; break; case REDIS_LIST: type = "+list"; break; case REDIS_SET: type = "+set"; break; case REDIS_ZSET: type = "+zset"; break; case REDIS_HASH: type = "+hash"; break; default: type = "+unknown"; break; } } addReplySds(c,sdsnew(type)); addReply(c,shared.crlf); }
void end_http_session(cli *c) { if (c->http.get || c->http.post || c->http.head) { bool ws = (server.alc.WebServerMode != -1); bool rs = (server.alc.RestAPIMode != -1); if (!ws && !rs) addReply(c, shared.http_not_on); else { sds file = c->http.file->ptr; bool done = rs ? sendRestAPIReply(c, file) : 0; if (!done) { if (!strncasecmp(file, "STATIC/", 7)) sendStaticFileReply(c); else sendLuaFuncReply(c, file); } } if (c->http.post_body) sdsfree(c->http.post_body); } if (c->http.file) { decrRefCount(c->http.file); c->http.file = NULL; } if (!c->http.ka && !sdslen(c->querybuf)) { // not KeepAlive, not Pipelined c->flags |= REDIS_CLOSE_AFTER_REPLY; } }
//PUSH命令的底层实现,where保存push的位置 void pushGenericCommand(client *c, int where) { int j, waiting = 0, pushed = 0; robj *lobj = lookupKeyWrite(c->db,c->argv[1]); //以写操作读取key对象的value //如果value对象不是列表类型则发送错误信息,返回 if (lobj && lobj->type != OBJ_LIST) { addReply(c,shared.wrongtypeerr); return; } //从第一个value开始遍历 for (j = 2; j < c->argc; j++) { c->argv[j] = tryObjectEncoding(c->argv[j]); //将value对象优化编码 //如果没有找到key对象 if (!lobj) { //创建一个quicklist类型的对象 lobj = createQuicklistObject(); //设置ziplist最大的长度和压缩程度,配置文件指定 quicklistSetOptions(lobj->ptr, server.list_max_ziplist_size, server.list_compress_depth); //将新的key对象和优化编码过的value对象进行组成键值对 dbAdd(c->db,c->argv[1],lobj); } //在where推入一个value对象 listTypePush(lobj,c->argv[j],where); pushed++; //更新计数器 } //发送当前列表中元素的个数 addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0)); //如果推入元素成功 if (pushed) { char *event = (where == LIST_HEAD) ? "lpush" : "rpush"; //当数据库的键被改动,则会调用该函数发送信号 signalModifiedKey(c->db,c->argv[1]); //发送"lpush"或"rpush"事件通知 notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id); } server.dirty += pushed; //更新脏键 }
void slaveofCommand(redisClient *c) { if (!strcasecmp(c->argv[1]->ptr,"no") && !strcasecmp(c->argv[2]->ptr,"one")) { if (server.masterhost) { sdsfree(server.masterhost); server.masterhost = NULL; if (server.master) freeClient(server.master); server.replstate = REDIS_REPL_NONE; redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)"); } } else { sdsfree(server.masterhost); server.masterhost = sdsdup(c->argv[1]->ptr); server.masterport = atoi(c->argv[2]->ptr); if (server.master) freeClient(server.master); server.replstate = REDIS_REPL_CONNECT; redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)", server.masterhost, server.masterport); } addReply(c,shared.ok); }
void appendCommand(redisClient *c) { int retval; size_t totlen; robj *o; o = lookupKeyWrite(c->db,c->argv[1]); c->argv[2] = tryObjectEncoding(c->argv[2]); if (o == NULL) { /* Create the key */ retval = dbAdd(c->db,c->argv[1],c->argv[2]); incrRefCount(c->argv[2]); totlen = stringObjectLen(c->argv[2]); } else { if (o->type != REDIS_STRING) { addReply(c,shared.wrongtypeerr); return; } /* If the object is specially encoded or shared we have to make * a copy */ if (o->refcount != 1 || o->encoding != REDIS_ENCODING_RAW) { robj *decoded = getDecodedObject(o); o = createStringObject(decoded->ptr, sdslen(decoded->ptr)); decrRefCount(decoded); dbReplace(c->db,c->argv[1],o); } /* APPEND! */ if (c->argv[2]->encoding == REDIS_ENCODING_RAW) { o->ptr = sdscatlen(o->ptr, c->argv[2]->ptr, sdslen(c->argv[2]->ptr)); } else { o->ptr = sdscatprintf(o->ptr, "%ld", (unsigned long) c->argv[2]->ptr); } totlen = sdslen(o->ptr); } touchWatchedKey(c->db,c->argv[1]); server.dirty++; addReplyLongLong(c,totlen); }
void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **argv, int argc) { listNode *ln; listIter li; int j, port; sds cmdrepr = sdsnew("+"); robj *cmdobj; char ip[32]; struct timeval tv; gettimeofday(&tv,NULL); cmdrepr = sdscatprintf(cmdrepr,"%ld.%06ld ",(long)tv.tv_sec,(long)tv.tv_usec); if (c->flags & REDIS_LUA_CLIENT) { cmdrepr = sdscatprintf(cmdrepr,"[%d lua] ",dictid); } else if (c->flags & REDIS_UNIX_SOCKET) { cmdrepr = sdscatprintf(cmdrepr,"[%d unix:%s] ",dictid,server.unixsocket); } else { anetPeerToString(c->fd,ip,&port); cmdrepr = sdscatprintf(cmdrepr,"[%d %s:%d] ",dictid,ip,port); } for (j = 0; j < argc; j++) { if (argv[j]->encoding == REDIS_ENCODING_INT) { cmdrepr = sdscatprintf(cmdrepr, "\"%ld\"", (long)argv[j]->ptr); } else { cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr, sdslen(argv[j]->ptr)); } if (j != argc-1) cmdrepr = sdscatlen(cmdrepr," ",1); } cmdrepr = sdscatlen(cmdrepr,"\r\n",2); cmdobj = createObject(REDIS_STRING,cmdrepr); listRewind(monitors,&li); while((ln = listNext(&li))) { redisClient *monitor = ln->value; addReply(monitor,cmdobj); } decrRefCount(cmdobj); }
/* The SLOWLOG command. Implements all the subcommands needed to handle the * Redis slow log. */ void slowlogCommand(client *c) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"reset")) { slowlogReset(); addReply(c,shared.ok); } else if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"len")) { addReplyLongLong(c,listLength(server.slowlog)); } else if ((c->argc == 2 || c->argc == 3) && !strcasecmp(c->argv[1]->ptr,"get")) { long count = 10, sent = 0; listIter li; void *totentries; listNode *ln; slowlogEntry *se; if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&count,NULL) != C_OK) return; listRewind(server.slowlog,&li); totentries = addDeferredMultiBulkLength(c); while(count-- && (ln = listNext(&li))) { int j; se = ln->value; addReplyMultiBulkLen(c,4); addReplyLongLong(c,se->id); addReplyLongLong(c,se->time); addReplyLongLong(c,se->duration); addReplyMultiBulkLen(c,se->argc); for (j = 0; j < se->argc; j++) addReplyBulk(c,se->argv[j]); sent++; } setDeferredMultiBulkLength(c,totentries,sent); } else { addReplyError(c, "Unknown SLOWLOG subcommand or wrong # of args. Try GET, RESET, LEN."); } }
void pushGenericCommand(redisClient *c, int where) { int j, waiting = 0, pushed = 0; robj *lobj = lookupKeyWrite(c->db,c->argv[1]); if (lobj && lobj->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); return; } for (j = 2; j < c->argc; j++) { c->argv[j] = tryObjectEncoding(c->argv[j]); if (!lobj) { lobj = createZiplistObject(); dbAdd(c->db,c->argv[1],lobj); } listTypePush(lobj,c->argv[j],where); pushed++; } addReplyLongLong(c, waiting + (lobj ? listTypeLength(lobj) : 0)); if (pushed) { signalModifiedKey(c->db,c->argv[1]); } }
/* argv includes command itself */ static void mig_start (client * c, int argc, robj ** argv) { serverLog (LL_NOTICE, "Client ask for migrate start"); if (arc.migrate_slot != NULL) { addReplyError (c, "Another migration is in progress. Unable to perform MIGSTART."); return; } arc.migrate_slot = get_bitmap_from_argv (argc - 1, argv + 1); if (!arc.migrate_slot) { addReplyError (c, "Invalid argument format."); return; } serverLog (LL_NOTICE, "Starting migration"); addReply (c, shared.ok); return; }
static bool addDouble(redisClient *c, redisClient *fc, robj *key, robj *val, long *instd, bool val_is_dbl) { robj *vals = createObject(REDIS_STRING, NULL); if (val_is_dbl) { double d = *((double *)val); vals->ptr = (key->encoding == REDIS_ENCODING_RAW) ? sdscatprintf(sdsempty(), "%ld,%s,%f", *instd, (char *)key->ptr, d) : sdscatprintf(sdsempty(), "%ld,%ld,%f", *instd, (long) key->ptr, d); } else if (val->encoding == REDIS_ENCODING_RAW) { vals->ptr = (key->encoding == REDIS_ENCODING_RAW) ? sdscatprintf(sdsempty(), "%ld,%s,%s", *instd, (char *)key->ptr, (char *)val->ptr) : sdscatprintf(sdsempty(), "%ld,%ld,%s", *instd, (long) key->ptr, (char *)val->ptr); } else { vals->ptr = (key->encoding == REDIS_ENCODING_RAW) ? sdscatprintf(sdsempty(), "%ld,%s,%ld", *instd, (char *)key->ptr, (long)val->ptr) : sdscatprintf(sdsempty(), "%ld,%ld,%ld", *instd, (long) key->ptr, (long)val->ptr); } fc->argv[2] = vals; //RL4 "DBL: INSERTING [1]: %s [2]: %s", fc->argv[1]->ptr, fc->argv[2]->ptr); legacyInsertCommand(fc); decrRefCount(vals); if (!respOk(fc)) { /* insert error */ listNode *ln = listFirst(fc->reply); addReply(c, ln->value); return 0; } *instd = *instd + 1; return 1; }
void rpoplpushCommand(redisClient *c) { robj *sobj, *value; if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2]); if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); rpoplpushHandlePush(c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ if (listTypeLength(sobj) == 0) dbDelete(c->db,c->argv[1]); touchWatchedKey(c->db,c->argv[1]); server.dirty++; } }
void popGenericCommand(redisClient *c, int where) { robj *o = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk); if (o == NULL || checkType(c,o,REDIS_LIST)) return; robj *value = listTypePop(o,where); if (value == NULL) { addReply(c,shared.nullbulk); } else { char *event = (where == REDIS_HEAD) ? "lpop" : "rpop"; addReplyBulk(c,value); decrRefCount(value); notifyKeyspaceEvent(REDIS_NOTIFY_LIST,event,c->argv[1],c->db->id); if (listTypeLength(o) == 0) { notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", c->argv[1],c->db->id); dbDelete(c->db,c->argv[1]); } signalModifiedKey(c->db,c->argv[1]); server.dirty++; } }
void slaveofCommand(redisClient *c) { if (!strcasecmp(c->argv[1]->ptr,"no") && !strcasecmp(c->argv[2]->ptr,"one")) { if (server.masterhost) {//已经是个slave了,需要关闭之 sdsfree(server.masterhost); server.masterhost = NULL; if (server.master) freeClient(server.master); cancelReplicationHandshake(); server.repl_state = REDIS_REPL_NONE; redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)"); } } else { long port; if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != REDIS_OK)) return; /* Check if we are already attached to the specified slave */ if (server.masterhost && !strcasecmp(server.masterhost,c->argv[1]->ptr) && server.masterport == port) { redisLog(REDIS_NOTICE,"SLAVE OF would result into synchronization with the master we are already connected with. No operation performed."); addReplySds(c,sdsnew("+OK Already connected to specified master\r\n")); return; } /* There was no previous master or the user specified a different one, * we can continue. */ sdsfree(server.masterhost); server.masterhost = sdsdup(c->argv[1]->ptr); server.masterport = port; if (server.master) freeClient(server.master);//直接关闭之前的master连接,readSyncBulkPayload接收完RDB文件会设置这个的。 disconnectSlaves(); /* Force our slaves to resync with us as well. */ cancelReplicationHandshake(); //下面设置这个的状态为需要连接master, 这样在serverCron定时任务会每秒调用replicationCron,进而会调用connectWithMaster进行重连的。 server.repl_state = REDIS_REPL_CONNECT; redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)", server.masterhost, server.masterport); } addReply(c,shared.ok); }
void zremrangebyrankCommand(redisClient *c) { long start; long end; int llen; long deleted; robj *zsetobj; zset *zs; if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) || (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return; if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || checkType(c,zsetobj,REDIS_ZSET)) return; zs = zsetobj->ptr; llen = zs->zsl->length; /* convert negative indexes */ if (start < 0) start = llen+start; if (end < 0) end = llen+end; if (start < 0) start = 0; /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */ if (start > end || start >= llen) { addReply(c,shared.czero); return; } if (end >= llen) end = llen-1; /* increment start and end because zsl*Rank functions * use 1-based rank */ deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict); if (htNeedsResize(zs->dict)) dictResize(zs->dict); if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]); if (deleted) touchWatchedKey(c->db,c->argv[1]); server.dirty += deleted; addReplyLongLong(c, deleted); }
void rpoplpushCommand(redisClient *c) { robj *sobj, *value; int slotnum1 = keyHashSlot(c->argv[1]->ptr, sdslen(c->argv[1]->ptr)); int slotnum2 = keyHashSlot(c->argv[2]->ptr, sdslen(c->argv[2]->ptr)); if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk,slotnum1)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { /* This may only happen after loading very old RDB files. Recent * versions of Redis delete keys of empty lists. */ addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2],slotnum2); robj *touchedkey = c->argv[1]; if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); /* We saved touched key, and protect it, since rpoplpushHandlePush * may change the client command argument vector (it does not * currently). */ incrRefCount(touchedkey); rpoplpushHandlePush(c,c->argv[2],dobj,value,slotnum2); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ notifyKeyspaceEvent(REDIS_NOTIFY_LIST,"rpop",touchedkey,c->db->id); if (listTypeLength(sobj) == 0) { dbDelete(c->db,touchedkey,slotnum1); notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del", touchedkey,c->db->id); } signalModifiedKey(c->db,touchedkey,slotnum1); decrRefCount(touchedkey); server.dirty++; } }
void srandmemberCommand(redisClient *c) { robj *set, *ele; int64_t llele; int encoding; if (c->argc == 3) { srandmemberWithCountCommand(c); return; } else if (c->argc > 3) { addReply(c,shared.syntaxerr); return; } if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,set,REDIS_SET)) return; encoding = setTypeRandomElement(set,&ele,&llele); if (encoding == REDIS_ENCODING_INTSET) { addReplyBulkLongLong(c,llele); } else { addReplyBulk(c,ele); } }
void saddCommand(redisClient *c) { robj *set; int j, added = 0; set = lookupKeyWrite(c->db,c->argv[1]); if (set == NULL) { set = setTypeCreate(c->argv[2]); dbAdd(c->db,c->argv[1],set); } else { if (set->type != REDIS_SET) { addReply(c,shared.wrongtypeerr); return; } } for (j = 2; j < c->argc; j++) { c->argv[j] = tryObjectEncoding(c->argv[j]); if (setTypeAdd(set,c->argv[j])) added++; } if (added) signalModifiedKey(c->db,c->argv[1]); server.dirty += added; addReplyLongLong(c,added); }
void rpoplpushCommand(redisClient *c) { robj *sobj, *value; if ((sobj = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL || checkType(c,sobj,REDIS_LIST)) return; if (listTypeLength(sobj) == 0) { /* This may only happen after loading very old RDB files. Recent * versions of Redis delete keys of empty lists. */ addReply(c,shared.nullbulk); } else { robj *dobj = lookupKeyWrite(c->db,c->argv[2]); robj *touchedkey = c->argv[1]; if (dobj && checkType(c,dobj,REDIS_LIST)) return; value = listTypePop(sobj,REDIS_TAIL); /* We saved touched key, and protect it, since rpoplpushHandlePush * may change the client command argument vector. */ incrRefCount(touchedkey); rpoplpushHandlePush(c,c,c->argv[2],dobj,value); /* listTypePop returns an object with its refcount incremented */ decrRefCount(value); /* Delete the source list when it is empty */ if (listTypeLength(sobj) == 0) dbDelete(c->db,touchedkey); signalModifiedKey(c->db,touchedkey); decrRefCount(touchedkey); server.dirty++; /* Replicate this as a simple RPOP since the LPUSH side is replicated * by rpoplpushHandlePush() call if needed (it may not be needed * if a client is blocking wait a push against the list). */ rewriteClientCommandVector(c,2, resetRefCount(createStringObject("RPOP",4)), c->argv[1]); } }
int MoodBox::DrawingWindow::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QWidget::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { switch (_id) { case 0: clipartWindowRequest(); break; case 1: messageSent(); break; case 2: replyChanged(); break; case 3: clear(); break; case 4: reset(); break; case 5: addImage((*reinterpret_cast< const QImage(*)>(_a[1]))); break; case 6: addReply((*reinterpret_cast< const QImage(*)>(_a[1]))); break; case 7: on_penToolButton_clicked(); break; case 8: on_simplebrushToolButton_clicked(); break; case 9: on_oilbrushToolButton_clicked(); break; case 10: on_sprayToolButton_clicked(); break; case 11: on_eraserToolButton_clicked(); break; case 12: on_textToolButton_clicked(); break; case 13: on_picturesToolButton_clicked(); break; case 14: on_sendButton_clicked(); break; case 15: onItemAdded((*reinterpret_cast< QGraphicsItem*(*)>(_a[1]))); break; case 16: onItemRemoved((*reinterpret_cast< QGraphicsItem*(*)>(_a[1]))); break; case 17: onSceneCleared(); break; case 18: onSaveScene(); break; case 19: onSceneColorChanged(); break; case 20: onUndoChanged(); break; case 21: onContactListChanged(); break; case 22: onContactAuthorizationChanged((*reinterpret_cast< qint32(*)>(_a[1]))); break; case 23: onUserOnlineChanged(); break; default: ; } _id -= 24; } return _id; }
void lrangeCommand(redisClient *c) { robj *o, *value; int start = atoi(c->argv[2]->ptr); int end = atoi(c->argv[3]->ptr); int llen; int rangelen, j; listTypeEntry entry; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL || checkType(c,o,REDIS_LIST)) return; llen = listTypeLength(o); /* convert negative indexes */ if (start < 0) start = llen+start; if (end < 0) end = llen+end; if (start < 0) start = 0; /* Invariant: start >= 0, so this test will be true when end < 0. * The range is empty when start > end or start >= length. */ if (start > end || start >= llen) { addReply(c,shared.emptymultibulk); return; } if (end >= llen) end = llen-1; rangelen = (end-start)+1; /* Return the result in form of a multi-bulk reply */ addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",rangelen)); listTypeIterator *li = listTypeInitIterator(o,start,REDIS_TAIL); for (j = 0; j < rangelen; j++) { redisAssert(listTypeNext(li,&entry)); value = listTypeGet(&entry); addReplyBulk(c,value); decrRefCount(value); } listTypeReleaseIterator(li); }
void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) { //propagateExpire删除超时key,或者propagate函数分发同步客户端写操作时会调用这里。 //将这条指令发送给所有的slaves,放到其缓冲里面以待发送。 listNode *ln; listIter li; int j; listRewind(slaves,&li);//获取slaves的第一个节点 while((ln = listNext(&li))) { redisClient *slave = ln->value; //下面不发送给这个slave,那么数据怎么办? 后面记着了的。这个状态的客户端是个从库,并且sync后rdb还没有开始,没必要为其追加写操作日志。 /* Don't feed slaves that are still waiting for BGSAVE to start */ if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue; /* Feed slaves that are waiting for the initial SYNC (so these commands * are queued in the output buffer until the initial SYNC completes), * or are already in sync with the master. */ if (slave->slaveseldb != dictid) { robj *selectcmd; if (dictid >= 0 && dictid < REDIS_SHARED_SELECT_CMDS) { selectcmd = shared.select[dictid]; incrRefCount(selectcmd); } else { selectcmd = createObject(REDIS_STRING, sdscatprintf(sdsempty(),"select %d\r\n",dictid)); } addReply(slave,selectcmd);//追加一条选择db的指令给当前这个slave decrRefCount(selectcmd); slave->slaveseldb = dictid; } addReplyMultiBulkLen(slave,argc);//增加参数数目指令 for (j = 0; j < argc; j++) //一个个将参数追加到slave上面去。 addReplyBulk(slave,argv[j]); } }
void getrangeCommand(redisClient *c) { robj *o; long long start, end; char *str, llbuf[32]; size_t strlen; if (getLongLongFromObjectOrReply(c,c->argv[2],&start,NULL) != REDIS_OK) return; if (getLongLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK) return; if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL || checkType(c,o,REDIS_STRING)) return; if (o->encoding == REDIS_ENCODING_INT) { str = llbuf; strlen = ll2string(llbuf,sizeof(llbuf),(long)o->ptr); } else { str = o->ptr; strlen = sdslen(str); } /* Convert negative indexes */ if (start < 0) start = strlen+start; if (end < 0) end = strlen+end; if (start < 0) start = 0; if (end < 0) end = 0; if ((unsigned long long)end >= strlen) end = strlen-1; /* Precondition: end >= 0 && end < strlen, so the only condition where * nothing can be returned is: start > end. */ if (start > end || strlen == 0) { addReply(c,shared.emptybulk); } else { addReplyBulkCBuffer(c,(char*)str+start,end-start+1); } }
/* TODO the protocol-parsing does not exactly follow the line protocol, it follow what the code does ... the code could change */ void createTableAsObjectOperation(redisClient *c, bool is_ins) { robj *wargv[3]; struct redisClient *wfc = rsql_createFakeClient(); /* client to write */ wfc->argc = 3; wfc->argv = wargv; wfc->argv[1] = c->argv[2]; /* table name */ robj **rargv = malloc(sizeof(robj *) * c->argc); struct redisClient *rfc = rsql_createFakeClient(); /* client to read */ rfc->argv = rargv; for (int i = 4; i < c->argc; i++) { rfc->argv[i - 4] = c->argv[i]; } rfc->argc = c->argc - 4; rfc->db = c->db; fakeClientPipe(c, rfc, wfc, is_ins, addSingle); rsql_freeFakeClient(rfc); rsql_freeFakeClient(wfc); free(rargv); addReply(c, shared.ok); return; }
void evalGenericCommand(redisClient *c, int evalsha) { lua_State *lua = server.lua; char funcname[43]; long long numkeys; int delhook = 0, err; /* We want the same PRNG sequence at every call so that our PRNG is * not affected by external state. */ redisSrand48(0); /* We set this flag to zero to remember that so far no random command * was called. This way we can allow the user to call commands like * SRANDMEMBER or RANDOMKEY from Lua scripts as far as no write command * is called (otherwise the replication and AOF would end with non * deterministic sequences). * * Thanks to this flag we'll raise an error every time a write command * is called after a random command was used. */ server.lua_random_dirty = 0; server.lua_write_dirty = 0; /* Get the number of arguments that are keys */ if (getLongLongFromObjectOrReply(c,c->argv[2],&numkeys,NULL) != REDIS_OK) return; if (numkeys > (c->argc - 3)) { addReplyError(c,"Number of keys can't be greater than number of args"); return; } /* We obtain the script SHA1, then check if this function is already * defined into the Lua state */ funcname[0] = 'f'; funcname[1] = '_'; if (!evalsha) { /* Hash the code if this is an EVAL call */ sha1hex(funcname+2,c->argv[1]->ptr,sdslen(c->argv[1]->ptr)); } else { /* We already have the SHA if it is a EVALSHA */ int j; char *sha = c->argv[1]->ptr; for (j = 0; j < 40; j++) funcname[j+2] = tolower(sha[j]); funcname[42] = '\0'; } /* Push the pcall error handler function on the stack. */ lua_getglobal(lua, "__redis__err__handler"); /* Try to lookup the Lua function */ lua_getglobal(lua, funcname); if (lua_isnil(lua,-1)) { lua_pop(lua,1); /* remove the nil from the stack */ /* Function not defined... let's define it if we have the * body of the function. If this is an EVALSHA call we can just * return an error. */ if (evalsha) { lua_pop(lua,1); /* remove the error handler from the stack. */ addReply(c, shared.noscripterr); return; } if (luaCreateFunction(c,lua,funcname,c->argv[1]) == REDIS_ERR) { lua_pop(lua,1); /* remove the error handler from the stack. */ /* The error is sent to the client by luaCreateFunction() * itself when it returns REDIS_ERR. */ return; } /* Now the following is guaranteed to return non nil */ lua_getglobal(lua, funcname); redisAssert(!lua_isnil(lua,-1)); } /* Populate the argv and keys table accordingly to the arguments that * EVAL received. */ luaSetGlobalArray(lua,"KEYS",c->argv+3,numkeys); luaSetGlobalArray(lua,"ARGV",c->argv+3+numkeys,c->argc-3-numkeys); /* Select the right DB in the context of the Lua client */ selectDb(server.lua_client,c->db->id); /* Set a hook in order to be able to stop the script execution if it * is running for too much time. * We set the hook only if the time limit is enabled as the hook will * make the Lua script execution slower. */ server.lua_caller = c; server.lua_time_start = mstime(); server.lua_kill = 0; if (server.lua_time_limit > 0 && server.masterhost == NULL) { lua_sethook(lua,luaMaskCountHook,LUA_MASKCOUNT,100000); delhook = 1; } /* At this point whether this script was never seen before or if it was * already defined, we can call it. We have zero arguments and expect * a single return value. */ err = lua_pcall(lua,0,1,-2); /* Perform some cleanup that we need to do both on error and success. */ if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */ if (server.lua_timedout) { server.lua_timedout = 0; /* Restore the readable handler that was unregistered when the * script timeout was detected. */ aeCreateFileEvent(server.el,c->fd,AE_READABLE, readQueryFromClient,c); } server.lua_caller = NULL; selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */ lua_gc(lua,LUA_GCSTEP,1); if (err) { addReplyErrorFormat(c,"Error running script (call to %s): %s\n", funcname, lua_tostring(lua,-1)); lua_pop(lua,2); /* Consume the Lua reply and remove error handler. */ } else { /* On success convert the Lua return value into Redis protocol, and * send it to * the client. */ luaReplyToRedisReply(c,lua); /* Convert and consume the reply. */ lua_pop(lua,1); /* Remove the error handler. */ } /* EVALSHA should be propagated to Slave and AOF file as full EVAL, unless * we are sure that the script was already in the context of all the * attached slaves *and* the current AOF file if enabled. * * To do so we use a cache of SHA1s of scripts that we already propagated * as full EVAL, that's called the Replication Script Cache. * * For repliation, everytime a new slave attaches to the master, we need to * flush our cache of scripts that can be replicated as EVALSHA, while * for AOF we need to do so every time we rewrite the AOF file. */ if (evalsha) { if (!replicationScriptCacheExists(c->argv[1]->ptr)) { /* This script is not in our script cache, replicate it as * EVAL, then add it into the script cache, as from now on * slaves and AOF know about it. */ robj *script = dictFetchValue(server.lua_scripts,c->argv[1]->ptr); replicationScriptCacheAdd(c->argv[1]->ptr); redisAssertWithInfo(c,NULL,script != NULL); rewriteClientCommandArgument(c,0, resetRefCount(createStringObject("EVAL",4))); rewriteClientCommandArgument(c,1,script); forceCommandPropagation(c,REDIS_PROPAGATE_REPL|REDIS_PROPAGATE_AOF); } } }
void luaReplyToRedisReply(redisClient *c, lua_State *lua) { int t = lua_type(lua,-1); switch(t) { case LUA_TSTRING: addReplyBulkCBuffer(c,(char*)lua_tostring(lua,-1),lua_strlen(lua,-1)); break; case LUA_TBOOLEAN: addReply(c,lua_toboolean(lua,-1) ? shared.cone : shared.nullbulk); break; case LUA_TNUMBER: addReplyLongLong(c,(long long)lua_tonumber(lua,-1)); break; case LUA_TTABLE: /* We need to check if it is an array, an error, or a status reply. * Error are returned as a single element table with 'err' field. * Status replies are returned as single element table with 'ok' field */ lua_pushstring(lua,"err"); lua_gettable(lua,-2); t = lua_type(lua,-1); if (t == LUA_TSTRING) { sds err = sdsnew(lua_tostring(lua,-1)); sdsmapchars(err,"\r\n"," ",2); addReplySds(c,sdscatprintf(sdsempty(),"-%s\r\n",err)); sdsfree(err); lua_pop(lua,2); return; } lua_pop(lua,1); lua_pushstring(lua,"ok"); lua_gettable(lua,-2); t = lua_type(lua,-1); if (t == LUA_TSTRING) { sds ok = sdsnew(lua_tostring(lua,-1)); sdsmapchars(ok,"\r\n"," ",2); addReplySds(c,sdscatprintf(sdsempty(),"+%s\r\n",ok)); sdsfree(ok); lua_pop(lua,1); } else { void *replylen = addDeferredMultiBulkLength(c); int j = 1, mbulklen = 0; lua_pop(lua,1); /* Discard the 'ok' field value we popped */ while(1) { lua_pushnumber(lua,j++); lua_gettable(lua,-2); t = lua_type(lua,-1); if (t == LUA_TNIL) { lua_pop(lua,1); break; } luaReplyToRedisReply(c, lua); mbulklen++; } setDeferredMultiBulkLength(c,replylen,mbulklen); } break; default: addReply(c,shared.nullbulk); } lua_pop(lua,1); }