/* Helper function to free the context. */ static void __redisAsyncFree(redisAsyncContext *ac) { redisContext *c = &(ac->c); redisCallback cb; dictIterator *it; dictEntry *de; /* Execute pending callbacks with NULL reply. */ while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) __redisRunCallback(ac,&cb,NULL); /* Execute callbacks for invalid commands */ while (__redisShiftCallback(&ac->sub.invalid,&cb) == REDIS_OK) __redisRunCallback(ac,&cb,NULL); /* Run subscription callbacks callbacks with NULL reply */ it = dictGetIterator(ac->sub.channels); while ((de = dictNext(it)) != NULL) __redisRunCallback(ac,dictGetEntryVal(de),NULL); dictReleaseIterator(it); dictRelease(ac->sub.channels); it = dictGetIterator(ac->sub.patterns); while ((de = dictNext(it)) != NULL) __redisRunCallback(ac,dictGetEntryVal(de),NULL); dictReleaseIterator(it); dictRelease(ac->sub.patterns); /* Signal event lib to clean up */ _EL_CLEANUP(ac); /* Execute disconnect callback. When redisAsyncFree() initiated destroying * this context, the status will always be REDIS_OK. */ if (ac->onDisconnect && (c->flags & REDIS_CONNECTED)) { if (c->flags & REDIS_FREEING) { ac->onDisconnect(ac,REDIS_OK); } else { ac->onDisconnect(ac,(ac->err == 0) ? REDIS_OK : REDIS_ERR); } } /* Cleanup self */ redisFree(c); }
void movekeysCommand(redisClient *c) { redisDb *src, *dst; int srcid; dictIterator *di; dictEntry *de; sds pattern = c->argv[1]->ptr; int plen = sdslen(pattern), allkeys; unsigned long numkeys = 0; /* Obtain source and target DB pointers */ src = c->db; srcid = c->db->id; if (selectDb(c,atoi(c->argv[2]->ptr)) == REDIS_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; } 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 (expireIfNeeded(c->db,keyobj) == 0) { robj *val = dictGetEntryVal(de); /* Try to add the element to the target DB */ if (dbAdd(dst,keyobj,val) != REDIS_ERR) { incrRefCount(val); /* OK! key moved, free the entry in the source DB */ dbDelete(src,keyobj); server.dirty++; numkeys++; } } decrRefCount(keyobj); } } dictReleaseIterator(di); addReplyLongLong(c,numkeys); }
/* Move to the next entry in the set. Returns the object at the current * position. * * Since set elements can be internally be stored as redis objects or * simple arrays of integers, setTypeNext returns the encoding of the * set object you are iterating, and will populate the appropriate pointer * (eobj) or (llobj) accordingly. * * When there are no longer elements -1 is returned. * Returned objects ref count is not incremented, so this function is * copy on write friendly. */ int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) { if (si->encoding == REDIS_ENCODING_HT) { dictEntry *de = dictNext(si->di); if (de == NULL) return -1; *objele = dictGetEntryKey(de); } else if (si->encoding == REDIS_ENCODING_INTSET) { if (!intsetGet(si->subject->ptr,si->ii++,llele)) return -1; } return si->encoding; }
static bool buildJRowReply(build_jrow_reply_t *b, int lvl, robj *rset[MAX_JOIN_INDXS]) { dictIterator *iter; dictEntry *rde = dictFind(rset[lvl]->ptr, b->jk); if (rde) { robj *setobj = dictGetEntryVal(rde); iter = dictGetIterator(setobj->ptr); } else { // this table does not have this column if (b->j.obt == Index[server.dbid][b->j_indxs[lvl]].table) { Order_by_col_val = NULL; } for (int j = 0; j < b->j.jind_ncols[lvl]; j++) { Rcols[lvl][j] = &EMPTY_STRING; Rc_lens[lvl][j] = 0; } if (lvl + 1 < b->n_ind) { if(!buildJRowReply(b, lvl + 1, rset)) return 0; } else { if (!jRowReply(&(b->j), lvl)) return 0; *b->card = *b->card + 1; } return 1; } dictEntry *sde; while ((sde = dictNext(iter)) != NULL) { char *first_entry; robj *item = sde->key; if (b->j.obt == Index[server.dbid][b->j_indxs[lvl]].table) { obsl_t *ob = (obsl_t *)item->ptr; Order_by_col_val = ob->keys[0]; first_entry = (char *)ob->row; } else { first_entry = (char *)item->ptr; } for (int j = 0; j < b->j.jind_ncols[lvl]; j++) { Rcols[lvl][j] = (char **)first_entry; first_entry += PTR_SIZE; memcpy(&(Rc_lens[lvl][j]), first_entry, UINT_SIZE); first_entry += UINT_SIZE; } if (lvl + 1 < b->n_ind) { if(!buildJRowReply(b, lvl + 1, rset)) return 0; } else { if (!jRowReply(&(b->j), lvl)) return 0; *b->card = *b->card + 1; } } dictReleaseIterator(iter); return 1; }
void destroyTaskMap(dict* p) { if (p) { dictIterator* di = dictGetIterator(p); dictEntry* de; while ((de = dictNext(di)) != NULL) { ugTaskType* ptask = dictGetEntryVal(de); luaworkUnrefFunction(server.ls, ptask->handle, NULL); } dictReleaseIterator(di); dictRelease(p); } }
/* Unsubscribe from all the channels. Return the number of channels the * client was subscribed from. */ int pubsubUnsubscribeAllChannels(redisClient *c, int notify) { dictIterator *di = dictGetSafeIterator(c->pubsub_channels); dictEntry *de; int count = 0; while((de = dictNext(di)) != NULL) { robj *channel = dictGetKey(de); count += pubsubUnsubscribeChannel(c,channel,notify); } dictReleaseIterator(di); return count; }
void _recurse_each(Callback *cb, dict *hash) { dictIterator * iterator = dictGetIterator(hash); dictEntry *next; while((next = dictNext(iterator))) { QueryNode *node = dictGetVal(next); cb->handler(cb, node); if(node->children) { _recurse_each(cb, node->children); } } dictReleaseIterator(iterator); }
static void shutdown_server(EV_P_ ev_signal *w, int revents) { IGNORE_VAR(w); IGNORE_VAR(revents); IGNORE_VAR(dictGenHashFunction); IGNORE_VAR(dictCreate); IGNORE_VAR(dictReplace); IGNORE_VAR(dictRelease); dictEntry *de; dictIterator *iter = dictGetIterator(active_connections); while ((de = dictNext(iter)) != NULL) { muxConn *mc = de->val; // stop any pending writes ev_io_stop(EV_A_ mc->watcher); disconnectAndClean(mc); } dictReleaseIterator(iter); ev_break(EV_A_ EVBREAK_ALL); }
/* Move to the next entry in the set. Returns the object at the current * position. * * Since set elements can be internally be stored as redis objects or * simple arrays of integers, setTypeNext returns the encoding of the * set object you are iterating, and will populate the appropriate pointer * (objele) or (llele) accordingly. * * Note that both the objele and llele pointers should be passed and cannot * be NULL since the function will try to defensively populate the non * used field with values which are easy to trap if misused. * * When there are no longer elements -1 is returned. * Returned objects ref count is not incremented, so this function is * copy on write friendly. */ int setTypeNext(setTypeIterator *si, robj **objele, int64_t *llele) { if (si->encoding == REDIS_ENCODING_HT) { dictEntry *de = dictNext(si->di); if (de == NULL) return -1; *objele = dictGetKey(de); *llele = -123456789; /* Not needed. Defensive. */ } else if (si->encoding == REDIS_ENCODING_INTSET) { if (!intsetGet(si->subject->ptr,si->ii++,llele)) return -1; *objele = NULL; /* Not needed. Defensive. */ } else { redisPanic("Wrong set encoding in setTypeNext"); } return si->encoding; }
void slotsmgrt_cleanup() { dictIterator *di = dictGetSafeIterator(server.slotsmgrt_cached_sockfds); dictEntry *de; while((de = dictNext(di)) != NULL) { slotsmgrt_sockfd *pfd = dictGetVal(de); if ((server.unixtime - pfd->lasttime) > 30) { redisLog(REDIS_WARNING, "slotsmgrt: timeout target %s, lasttime = %ld, now = %ld", (char *)dictGetKey(de), pfd->lasttime, server.unixtime); dictDelete(server.slotsmgrt_cached_sockfds, dictGetKey(de)); close(pfd->fd); zfree(pfd); } } dictReleaseIterator(di); }
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); }
void call_bridge_event(struct redisClient *c,int triggle_place,int event_type) { if(event_type==-1) { sds cmds=(sds)sdsdup(c->argv[0]->ptr); sdstolower(cmds); if(triggle_place==TRIGGLE_BEFORE) //no event given { cmds=sdscat(cmds,"ing"); } if(triggle_place==TRIGGLE_AFTER) { cmds=sdscat(cmds,"ed"); } event_type=process_trigglecmd(cmds); // redisLog(REDIS_NOTICE,"cmds:%s id:%d",cmds,event_type); sdsfree(cmds); } if(event_type==-1) return; struct dictIterator *iter=dictGetIterator(server.bridge_db.triggle_scipts[c->db->id]); dictEntry *trigs; do{ trigs=dictNext(iter); if(trigs!=NULL) { struct bridge_db_triggle_t * tmptrg=dictGetVal(trigs); if(tmptrg->event==event_type&&strncmp(c->argv[1]->ptr,dictGetKey(trigs),sdslen(dictGetKey(trigs)))==0){ //找到指定的类型事件 //if(tmptrg->event==event_type){ //找到指定的类型事件 // redisLog(REDIS_NOTICE,"triggle_event:%d,%s",event_type,(char *)dictGetKey(trigs)); triggle_event(c,dictGetKey(trigs)); } } }while(trigs!=NULL); dictReleaseIterator(iter); }
/* Reset data for the specified event, or all the events data if 'event' is * NULL. * * Note: this is O(N) even when event_to_reset is not NULL because makes * the code simpler and we have a small fixed max number of events. */ int latencyResetEvent(char *event_to_reset) { dictIterator *di; dictEntry *de; int resets = 0; di = dictGetSafeIterator(server.latency_events); while((de = dictNext(di)) != NULL) { char *event = dictGetKey(de); if (event_to_reset == NULL || strcasecmp(event,event_to_reset) == 0) { dictDelete(server.latency_events, event); resets++; } } dictReleaseIterator(di); return resets; }
void rdb_save_triggles(rio *rdb) { //save event //db_num int int int int //db //scripts_num //key event lua_scripts //key event lua_scripts //....... dictIterator *di = NULL; dictEntry *de; int i=0; for(i=0;i<server.dbnum;i++){ int eventid=server.bridge_db.bridge_event[i]; rioWrite(rdb,&eventid,4); } for(i=0;i<server.dbnum;i++) { dict *d = server.bridge_db.triggle_scipts[i]; int mysize=dictSize(d); rioWrite(rdb,&mysize,4); if (dictSize(d) == 0) continue; di = dictGetSafeIterator(d); if (!di) { return ; } /* Iterate this DB writing every entry */ while((de = dictNext(di)) != NULL) { sds keystr = dictGetKey(de); robj key; initStaticStringObject(key,keystr); if (rdbSaveStringObject(rdb,&key) == -1) return; struct bridge_db_triggle_t * tmptrg=dictGetVal(de); int event_id=tmptrg->event; rioWrite(rdb,&event_id,4); int db_id=tmptrg->dbid; rioWrite(rdb,&db_id,4); if (rdbSaveObjectType(rdb,tmptrg->lua_scripts) == -1) return ; if (rdbSaveObject(rdb,tmptrg->lua_scripts) == -1) return ; } } if (di) dictReleaseIterator(di); }
/* PUBSUB command for Pub/Sub introspection. */ void pubsubCommand(client *c) { if (!strcasecmp(c->argv[1]->ptr,"channels") && (c->argc == 2 || c->argc ==3)) { /* PUBSUB CHANNELS [<pattern>] */ sds pat = (c->argc == 2) ? NULL : c->argv[2]->ptr; dictIterator *di = dictGetIterator(server.pubsub_channels); dictEntry *de; long mblen = 0; void *replylen; replylen = addDeferredMultiBulkLength(c); while((de = dictNext(di)) != NULL) { robj *cobj = dictGetKey(de); sds channel = cobj->ptr; if (!pat || stringmatchlen(pat, sdslen(pat), channel, sdslen(channel),0)) { addReplyBulk(c,cobj); mblen++; } } dictReleaseIterator(di); setDeferredMultiBulkLength(c,replylen,mblen); } else if (!strcasecmp(c->argv[1]->ptr,"numsub") && c->argc >= 2) { /* PUBSUB NUMSUB [Channel_1 ... Channel_N] */ int j; addReplyMultiBulkLen(c,(c->argc-2)*2); for (j = 2; j < c->argc; j++) { list *l = dictFetchValue(server.pubsub_channels,c->argv[j]); addReplyBulk(c,c->argv[j]); addReplyLongLong(c,l ? listLength(l) : 0); } } else if (!strcasecmp(c->argv[1]->ptr,"numpat") && c->argc == 2) { /* PUBSUB NUMPAT */ addReplyLongLong(c,listLength(server.pubsub_patterns)); } else { addReplyErrorFormat(c, "Unknown PUBSUB subcommand or wrong number of arguments for '%s'", (char*)c->argv[1]->ptr); } }
/* Emit the commands needed to rebuild a set object. * The function returns 0 on error, 1 on success. */ int rewriteSetObject(rio *r, robj *key, robj *o) { long long count = 0, items = setTypeSize(o); if (o->encoding == REDIS_ENCODING_INTSET) { int ii = 0; int64_t llval; while(intsetGet(o->ptr,ii++,&llval)) { if (count == 0) { int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ? REDIS_AOF_REWRITE_ITEMS_PER_CMD : items; if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; if (rioWriteBulkString(r,"SADD",4) == 0) return 0; if (rioWriteBulkObject(r,key) == 0) return 0; } if (rioWriteBulkLongLong(r,llval) == 0) return 0; if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } } else if (o->encoding == REDIS_ENCODING_HT) { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; while((de = dictNext(di)) != NULL) { robj *eleobj = dictGetKey(de); if (count == 0) { int cmd_items = (items > REDIS_AOF_REWRITE_ITEMS_PER_CMD) ? REDIS_AOF_REWRITE_ITEMS_PER_CMD : items; if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; if (rioWriteBulkString(r,"SADD",4) == 0) return 0; if (rioWriteBulkObject(r,key) == 0) return 0; } if (rioWriteBulkObject(r,eleobj) == 0) return 0; if (++count == REDIS_AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } dictReleaseIterator(di); } else { redisPanic("Unknown set encoding"); } return 1; }
/* latencyCommand() helper to produce the reply for the LATEST subcommand, * listing the last latency sample for every event type registered so far. */ void latencyCommandReplyWithLatestEvents(client *c) { dictIterator *di; dictEntry *de; addReplyMultiBulkLen(c,dictSize(server.latency_events)); di = dictGetIterator(server.latency_events); while((de = dictNext(di)) != NULL) { char *event = dictGetKey(de); struct latencyTimeSeries *ts = dictGetVal(de); int last = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN; addReplyMultiBulkLen(c,4); addReplyBulkCString(c,event); addReplyLongLong(c,ts->samples[last].time); addReplyLongLong(c,ts->samples[last].latency); addReplyLongLong(c,ts->max); } dictReleaseIterator(di); }
sds _masterGetStatus() { /*TODO: calculate cache increase speed, * then adopt a suitable stale-cache freeing strategy * Three involved params: * (1) master sleep, * (2) ae loop wait, * and (3) number of stale entries in one ae loop */ sds status = sdsempty();//sdsfromlonglong(master_total_mem); status = sdscatprintf(status,"TOL RAM: %-6.2lfMB\tUSED RAM: %-6.2lf\n", BYTES_TO_MEGABYTES(MASTER_MAX_AVAIL_MEM), BYTES_TO_MEGABYTES(master_total_mem)); #if (CCACHE_LOG_LEVEL == CCACHE_DEBUG) status = sdscatprintf(status,"Detail:\n"); status = sdscatprintf(status,"%-3s %-32s: %-6s\n"," ","KEY","MEM"); dictIterator *di = dictGetIterator(master_cache); dictEntry *de; int idx = 1; while((de = dictNext(di)) != NULL) { objSds *value = (objSds*)dictGetEntryVal(de); if(value) { if(value->ptr) { status = sdscatprintf(status,"%-3d %-32s: %-6ld\n", idx++, (char*)dictGetEntryKey(de), sdslen(value->ptr)); } else { status = sdscatprintf(status,"%-3d %-32s: %-6s\n", idx++, (char*)dictGetEntryKey(de), "WAITING"); } } } dictReleaseIterator(di); #endif sds status_reply = sdsnew("HTTP/1.1 200 OK\r\n"); status_reply = sdscatprintf(status_reply,"Content-Length: %ld\r\n\r\n%s",sdslen(status),status); sdsfree(status); return status_reply; }
/* COMMAND <subcommand> <args> */ void commandCommand(client *c) { dictIterator *di; dictEntry *de; if (c->argc == 1) { addReplyMultiBulkLen(c, dictSize(server.commands)); di = dictGetIterator(server.commands); while ((de = dictNext(di)) != NULL) { addReplyCommand(c, dictGetVal(de)); } dictReleaseIterator(di); } else if (!strcasecmp(c->argv[1]->ptr, "info")) { int i; addReplyMultiBulkLen(c, c->argc-2); for (i = 2; i < c->argc; i++) { addReplyCommand(c, dictFetchValue(server.commands, c->argv[i]->ptr)); } } else if (!strcasecmp(c->argv[1]->ptr, "count") && c->argc == 2) { addReplyLongLong(c, dictSize(server.commands)); } else if (!strcasecmp(c->argv[1]->ptr,"getkeys") && c->argc >= 3) { struct redisCommand *cmd = lookupCommand(c->argv[2]->ptr); int *keys, numkeys, j; if (!cmd) { addReplyErrorFormat(c,"Invalid command specified"); return; } else if ((cmd->arity > 0 && cmd->arity != c->argc-2) || ((c->argc-2) < -cmd->arity)) { addReplyError(c,"Invalid number of arguments specified for command"); return; } keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys); addReplyMultiBulkLen(c,numkeys); for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]); getKeysFreeResult(keys); } else { addReplyError(c, "Unknown subcommand or wrong number of arguments."); return; } }
dictEntry *roZDictNext(roZDictIter *iter) { dictEntry *de = NULL; cowLock(); if (iter->ar != NULL) { if (iter->pos >= 0 && iter->pos < iter->ar->numele) { de = &iter->ar->zde[iter->pos].de; iter->pos++; } } else if (iter->di != NULL) { de = dictNext(iter->di); iter->pos++; } if (de == NULL) { iter->pos = -1; } cowUnlock(); return de; }
/* convert a hash dictionary encoding to a dictionary array encoding */ cowDictZArray *cowConvertDictToZArray(dict *hdict) { dictIterator * di; dictEntry *de; int dsize; cowDictZArray *dar; int dcount = 0; dictZEntry *dezNew; dictZEntry *dezPrev; /* create copy */ dsize = dictSize(hdict) > dictSlots(hdict) ? dictSize(hdict) : dictSlots(hdict); dar = (cowDictZArray *)zmalloc(sizeof(cowDictZArray) + (dsize * sizeof(dictZEntry)) ); /* copy all entries without refcounting or copying values */ /* can't just memcpy the whole dictionary because entries are allocated */ di = dictGetSafeIterator(hdict); dezNew = &dar->zde[0]; dezPrev = NULL; while((de = dictNext(di)) != NULL && dcount < dsize) { double *score = (double *)dictGetEntryVal(de); /* copy score value into array and point val to score. */ dezNew->de.key = de->key; dezNew->score = *score; dezNew->de.val = &dezNew->score; /* fix next ptr of prev entry */ if (dezPrev != NULL) { dezPrev->de.next = &dezNew->de; } dezPrev = dezNew; dezNew++; dcount++; } if (dezPrev != NULL) { dezPrev->de.next = NULL; } dar->numele = dcount; dictReleaseIterator(di); return dar; }
/* Unsubscribe from all the channels. Return the number of channels the * client was subscribed to. */ int smempubsubUnsubscribeAllChannels(client *c, int notify) { dictIterator *di = dictGetSafeIterator(c->smempubsub_channels); dictEntry *de; int count = 0; while((de = dictNext(di)) != NULL) { robj *channel = dictGetKey(de); count += smempubsubUnsubscribeChannel(c,channel,notify); } /* We were subscribed to nothing? Still reply to the client. */ if (notify && count == 0) { addReply(c,shared.mbulkhdr[3]); addReply(c,shared.unsubscribebulk); addReply(c,shared.nullbulk); addReplyLongLong(c,dictSize(c->smempubsub_channels)); } dictReleaseIterator(di); return count; }
valType dbSetTrunc(void) { valType freed = 0, i; dictIterator *iter = NULL; dictEntry *entry = NULL; lockWrite(objectIndex); lockRead(sets); if (NULL == (iter = dictGetIterator(sets))) { unlockRead(sets); unlockWrite(objectIndex); return 0; } while (NULL != (entry = dictNext(iter))) { lockWrite(entry); freed += setTrunc((set *) dictGetEntryVal(entry)); unlockWrite(entry); } for (i = 0; i < objectIndexLength; i++) { if (NULL != objectIndex[i] && objectSet == objectIndex[i]->objectType) { freed += setTrunc(objectIndex[i]->objectPtr.setPtr); } } dictReleaseIterator(iter); unlockRead(sets); unlockWrite(objectIndex); return freed; }
/* convert a hash dictionary encoding to a dictionary array encoding */ cowDictArray *cowConvertDictToArray(dict *hdict) { dictIterator * di; dictEntry *de; int dsize; cowDictArray *dar; int dcount = 0; dictEntry *deNew; dictEntry *dePrev; /* create copy */ dsize = dictSize(hdict) > dictSlots(hdict) ? dictSize(hdict) : dictSlots(hdict); dar = (cowDictArray *)zmalloc(sizeof(cowDictArray) + (dsize * sizeof(dictEntry))); /* copy all entries without refcounting or copying values */ /* can't just memcpy the whole dictionary because entries are allocated */ di = dictGetSafeIterator(hdict); deNew = &dar->de[0]; dePrev = NULL; while((de = dictNext(di)) != NULL && dcount < dsize) { /* copy object value to dict array Do not incr ref count. */ deNew->val = de->val; deNew->key = de->key; /* fix next ptr of prev entry */ if (dePrev != NULL) { dePrev->next = deNew; } dePrev = deNew; deNew++; dcount++; } if (dePrev != NULL) { dePrev->next = NULL; } dar->numele = dcount; dictReleaseIterator(di); return dar; }
void cleanupDbEngine(void) { dictIterator *i = NULL; dictEntry *de = NULL; valType c; unregisterSyncObject(sets); unregisterSyncObject(objectIndex); if (NULL == sets) return; i = dictGetIterator(sets); if (NULL != i) { while (NULL != (de = dictNext(i))) { set *setToDelete = (set *) dictGetEntryVal(de); if (!setToDelete->registered) setDestroy(setToDelete); } } dictReleaseIterator(i); dictRelease(sets); for (c = 0; c < objectIndexLength; c++) if (NULL != objectIndex[c]) { dbObjectRelease((dbObject *) objectIndex[c]); free((void *) objectIndex[c]); } free((void *) objectIndex); }
void countCommand(redisClient *c) { dictIterator *di; dictEntry *de; sds pattern = c->argv[1]->ptr; int plen = sdslen(pattern), allkeys; unsigned long numkeys = 0; 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 (expireIfNeeded(c->db,keyobj) == 0) { numkeys++; } decrRefCount(keyobj); } } dictReleaseIterator(di); addReplyLongLong(c,numkeys); }
void keysCommand(client *c) { dictIterator *di; dictEntry *de; sds pattern = c->argv[1]->ptr; int plen = sdslen(pattern), allkeys; unsigned long numkeys = 0; void *replylen = addDeferredMultiBulkLength(c); di = dictGetSafeIterator(server.counters); allkeys = (pattern[0] == '*' && pattern[1] == '\0'); while((de = dictNext(di)) != NULL) { sds key = dictGetKey(de); robj *keyobj; if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) { keyobj = createStringObject(key,sdslen(key)); addReplyBulk(c,keyobj); numkeys++; decrRefCount(keyobj); } } dictReleaseIterator(di); setDeferredMultiBulkLength(c,replylen,numkeys); }
char *params_map_serialize(params_map *p_map) { dictIterator *dict_iterator; dictEntry *dict_entry; param_entry *param; char *line, *result = NULL; dict_iterator = dictGetIterator(p_map); while((dict_entry = dictNext(dict_iterator)) != NULL) { param = (param_entry *)dictGetEntryVal(dict_entry); line = malloc_str(strlen("[") + strlen(param->name) + strlen(":") + strlen(param->val) + strlen("]")); sprintf(line, "[%s:%s]", param->name, param->val); result = str_concat(result, line); free(line); } free(dict_iterator); if (result == NULL) result = strdup("Params hash is empty"); return result; }
void joinGeneric(redisClient *c, jb_t *jb) { if (jb->w.nob > 1) { addReply(c, shared.join_m_obc); return; } Order_by = jb->w.nob; Order_by_col_val = NULL; /* sort queried-columns to queried-indices */ jqo_t o_csort_order[MAX_COLUMN_PER_TABLE]; jqo_t csort_order [MAX_COLUMN_PER_TABLE]; for (int i = 0; i < jb->qcols; i++) { for (int j = 0; j < jb->n_ind; j++) { if (jb->j_tbls[i] == Index[server.dbid][jb->j_indxs[j]].table) { csort_order[i].t = jb->j_tbls[i]; csort_order[i].i = j; csort_order[i].c = jb->j_cols[i]; csort_order[i].n = i; } } } memcpy(&o_csort_order, &csort_order, sizeof(jqo_t) * jb->qcols); qsort(&csort_order, jb->qcols, sizeof(jqo_t), cmp_jqo); /* reorder queried-columns to queried-indices, will sort @ output time */ bool reordered = 0; for (int i = 0; i < jb->qcols; i++) { if (jb->j_tbls[i] != csort_order[i].t || jb->j_cols[i] != csort_order[i].c) { reordered = 1; jb->j_tbls[i] = csort_order[i].t; jb->j_cols[i] = csort_order[i].c; } } cswc_t *w = &jb->w; /* makes coding more compact */ w->tmatch = w->obt[0]; /* HACK: initOBsort needs w->tmatch */ list *ll = initOBsort(Order_by, w); uchar pk1type = Tbl[server.dbid] [Index[server.dbid][jb->j_indxs[0]].table].col_type[0]; bt *jbtr = createJoinResultSet(pk1type); robj *rset[MAX_JOIN_INDXS]; for (int i = 1; i < jb->n_ind; i++) { rset[i] = createValSetObject(); } int j_ind_len [MAX_JOIN_INDXS]; int jind_ncols[MAX_JOIN_INDXS]; join_add_cols_t jc; /* these dont change in the loop below */ jc.qcols = jb->qcols; jc.j_tbls = jb->j_tbls; jc.j_cols = jb->j_cols; jc.jind_ncols = jind_ncols; jc.j_ind_len = j_ind_len; jc.jbtr = jbtr; for (int i = 0; i < jb->n_ind; i++) { /* iterate join indices */ btEntry *be, *nbe; j_ind_len[i] = 0; jc.index = i; jc.itable = Index[server.dbid][jb->j_indxs[i]].table; robj *btt = lookupKeyRead(c->db, Tbl[server.dbid][jc.itable].name); jc.btr = (bt *)btt->ptr; jc.virt = Index[server.dbid][jb->j_indxs[i]].virt; if (w->low) { /* RANGE QUERY */ if (jc.virt) { /* PK */ btSIter *bi = btGetRangeIterator(jc.btr, w->low, w->high); while ((be = btRangeNext(bi)) != NULL) { jc.ajk = be->key; jc.rrow = be->val; joinAddColsFromInd(&jc, rset, w); } btReleaseRangeIterator(bi); } else { /* FK */ robj *ind = Index[server.dbid][jb->j_indxs[i]].obj; robj *ibtt = lookupKey(c->db, ind); bt *ibtr = (bt *)ibtt->ptr; btSIter *bi = btGetRangeIterator(ibtr, w->low, w->high); while ((be = btRangeNext(bi)) != NULL) { jc.ajk = be->key; bt *nbtr = be->val; btSIter *nbi = btGetFullRangeIterator(nbtr); while ((nbe = btRangeNext(nbi)) != NULL) { jc.rrow = btFindVal(jc.btr, nbe->key); joinAddColsFromInd(&jc, rset, w); } btReleaseRangeIterator(nbi); } btReleaseRangeIterator(bi); } } else { /* IN() QUERY */ listNode *ln; listIter *li = listGetIterator(w->inl, AL_START_HEAD); if (jc.virt) { while((ln = listNext(li)) != NULL) { jc.ajk = ln->value; jc.rrow = btFindVal(jc.btr, jc.ajk); if (jc.rrow) joinAddColsFromInd(&jc, rset, w); } } else { btSIter *nbi; robj *ind = Index[server.dbid][jb->j_indxs[i]].obj; robj *ibtt = lookupKey(c->db, ind); bt *ibtr = (bt *)ibtt->ptr; while((ln = listNext(li)) != NULL) { jc.ajk = ln->value; bt *nbtr = btIndFindVal(ibtr, jc.ajk); if (nbtr) { nbi = btGetFullRangeIterator(nbtr); while ((nbe = btRangeNext(nbi)) != NULL) { jc.rrow = btFindVal(jc.btr, nbe->key); joinAddColsFromInd(&jc, rset, w); } btReleaseRangeIterator(nbi); } } } listReleaseIterator(li); } } /* cant join if one table had ZERO rows */ bool one_empty = 0; if (jbtr->numkeys == 0) one_empty = 1; else { for (int i = 1; i < jb->n_ind; i++) { if (dictSize((dict *)rset[i]->ptr) == 0) { one_empty = 1; break; } } } LEN_OBJ bool err = 0; long sent = 0; btIterator *bi = NULL; /* B4 GOTO */ char *reply = NULL; /* B4 GOTO */ if (!one_empty) { int reply_size = 0; for (int i = 0; i < jb->n_ind; i++) { // get maxlen possbl 4 joined row reply_size += j_ind_len[i] + 1; } reply = malloc(reply_size); /* freed after while() loop */ build_jrow_reply_t bjr; /* none of these change during a join */ bzero(&bjr, sizeof(build_jrow_reply_t)); bjr.j.c = c; bjr.j.jind_ncols = jind_ncols; bjr.j.reply = reply; bjr.j.csort_order = csort_order; bjr.j.reordered = reordered; bjr.j.qcols = jb->qcols; bjr.n_ind = jb->n_ind; bjr.card = &card; bjr.j.obt = w->obt[0]; bjr.j.obc = w->obc[0]; bjr.j_indxs = jb->j_indxs; bjr.j.ll = ll; bjr.j.cstar = jb->cstar; joinRowEntry *be; bi = btGetJoinFullRangeIterator(jbtr, pk1type); while ((be = btJoinRangeNext(bi, pk1type)) != NULL) { /* iter BT */ listNode *ln; bjr.jk = be->key; list *jll = (list *)be->val; listIter *li = listGetIterator(jll, AL_START_HEAD); while((ln = listNext(li)) != NULL) { /* iter LIST */ char *first_entry; char *item = ln->value; if (bjr.j.obt == Index[server.dbid][bjr.j_indxs[0]].table) { obsl_t *ob = (obsl_t *)item; Order_by_col_val = ob->keys[0]; first_entry = (char *)ob->row; } else { first_entry = item; } for (int j = 0; j < jind_ncols[0]; j++) { Rcols[0][j] = (char **)first_entry; first_entry += PTR_SIZE; memcpy(&Rc_lens[0][j], first_entry, UINT_SIZE); first_entry += UINT_SIZE; } if (!buildJRowReply(&bjr, 1, rset)) { err = 1; goto join_end; } } listReleaseIterator(li); } if (Order_by) { sent = sortJoinOrderByAndReply(c, &bjr, w); if (sent == -1) err = 1; releaseOBsort(ll); } } join_end: if (bi) btReleaseJoinRangeIterator(bi); if (reply) free(reply); /* free joinRowEntry malloc from joinAddColsFromInd() */ bool is_ob = (w->obt[0] == Index[server.dbid][jb->j_indxs[0]].table); btJoinRelease(jbtr, jind_ncols[0], is_ob, freeListOfIndRow); /* free joinRowEntry malloc from joinAddColsFromInd() */ dictEntry *de; for (int i = 1; i < jb->n_ind; i++) { dict *set = rset[i]->ptr; bool is_ob = (w->obt[0] == Index[server.dbid][jb->j_indxs[i]].table); dictIterator *di = dictGetIterator(set); while((de = dictNext(di)) != NULL) { robj *val = dictGetEntryVal(de); dict *iset = val->ptr; freeDictOfIndRow(iset, jind_ncols[i], is_ob); } dictReleaseIterator(di); } for (int i = 1; i < jb->n_ind; i++) { decrRefCount(rset[i]); } if (err) return; if (w->lim != -1 && sent < card) card = sent; if (jb->cstar) { lenobj->ptr = sdscatprintf(sdsempty(), ":%ld\r\n", card); } else { lenobj->ptr = sdscatprintf(sdsempty(), "*%ld\r\n", card); if (w->ovar) incrOffsetVar(c, w, card); } }
/* The SORT command is the most complex command in Redis. Warning: this code * is optimized for speed and a bit less for readability */ void sortCommand(redisClient *c) { list *operations; unsigned int outputlen = 0; int desc = 0, alpha = 0; long limit_start = 0, limit_count = -1, start, end; int j, dontsort = 0, vectorlen; int getop = 0; /* GET operation counter */ int int_convertion_error = 0; robj *sortval, *sortby = NULL, *storekey = NULL; redisSortObject *vector; /* Resulting vector to sort */ /* Lookup the key to sort. It must be of the right types */ sortval = lookupKeyRead(c->db,c->argv[1]); if (sortval && sortval->type != REDIS_SET && sortval->type != REDIS_LIST && sortval->type != REDIS_ZSET) { addReply(c,shared.wrongtypeerr); return; } /* Create a list of operations to perform for every sorted element. * Operations can be GET/DEL/INCR/DECR */ operations = listCreate(); listSetFreeMethod(operations,zfree); j = 2; /* Now we need to protect sortval incrementing its count, in the future * SORT may have options able to overwrite/delete keys during the sorting * and the sorted key itself may get destroied */ if (sortval) incrRefCount(sortval); else sortval = createListObject(); /* The SORT command has an SQL-alike syntax, parse it */ while(j < c->argc) { int leftargs = c->argc-j-1; if (!strcasecmp(c->argv[j]->ptr,"asc")) { desc = 0; } else if (!strcasecmp(c->argv[j]->ptr,"desc")) { desc = 1; } else if (!strcasecmp(c->argv[j]->ptr,"alpha")) { alpha = 1; } else if (!strcasecmp(c->argv[j]->ptr,"limit") && leftargs >= 2) { if ((getLongFromObjectOrReply(c, c->argv[j+1], &limit_start, NULL) != REDIS_OK) || (getLongFromObjectOrReply(c, c->argv[j+2], &limit_count, NULL) != REDIS_OK)) return; j+=2; } else if (!strcasecmp(c->argv[j]->ptr,"store") && leftargs >= 1) { storekey = c->argv[j+1]; j++; } else if (!strcasecmp(c->argv[j]->ptr,"by") && leftargs >= 1) { sortby = c->argv[j+1]; /* If the BY pattern does not contain '*', i.e. it is constant, * we don't need to sort nor to lookup the weight keys. */ if (strchr(c->argv[j+1]->ptr,'*') == NULL) dontsort = 1; j++; } else if (!strcasecmp(c->argv[j]->ptr,"get") && leftargs >= 1) { listAddNodeTail(operations,createSortOperation( REDIS_SORT_GET,c->argv[j+1])); getop++; j++; } else { decrRefCount(sortval); listRelease(operations); addReply(c,shared.syntaxerr); return; } j++; } /* If we have STORE we need to force sorting for deterministic output * and replication. We use alpha sorting since this is guaranteed to * work with any input. */ if (storekey && dontsort) { dontsort = 0; alpha = 1; sortby = NULL; } /* Destructively convert encoded sorted sets for SORT. */ if (sortval->type == REDIS_ZSET) zsetConvert(sortval, REDIS_ENCODING_SKIPLIST); /* Load the sorting vector with all the objects to sort */ switch(sortval->type) { case REDIS_LIST: vectorlen = listTypeLength(sortval); break; case REDIS_SET: vectorlen = setTypeSize(sortval); break; case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break; default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */ } vector = zmalloc(sizeof(redisSortObject)*vectorlen); j = 0; if (sortval->type == REDIS_LIST) { listTypeIterator *li = listTypeInitIterator(sortval,0,REDIS_TAIL); listTypeEntry entry; while(listTypeNext(li,&entry)) { vector[j].obj = listTypeGet(&entry); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } listTypeReleaseIterator(li); } else if (sortval->type == REDIS_SET) { setTypeIterator *si = setTypeInitIterator(sortval); robj *ele; while((ele = setTypeNextObject(si)) != NULL) { vector[j].obj = ele; vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } setTypeReleaseIterator(si); } else if (sortval->type == REDIS_ZSET) { dict *set = ((zset*)sortval->ptr)->dict; dictIterator *di; dictEntry *setele; di = dictGetIterator(set); while((setele = dictNext(di)) != NULL) { vector[j].obj = dictGetKey(setele); vector[j].u.score = 0; vector[j].u.cmpobj = NULL; j++; } dictReleaseIterator(di); } else { redisPanic("Unknown type"); } redisAssertWithInfo(c,sortval,j == vectorlen); /* Now it's time to load the right scores in the sorting vector */ if (dontsort == 0) { for (j = 0; j < vectorlen; j++) { robj *byval; if (sortby) { /* lookup value to sort by */ byval = lookupKeyByPattern(c->db,sortby,vector[j].obj); if (!byval) continue; } else { /* use object itself to sort by */ byval = vector[j].obj; } if (alpha) { if (sortby) vector[j].u.cmpobj = getDecodedObject(byval); } else { if (byval->encoding == REDIS_ENCODING_RAW) { char *eptr; vector[j].u.score = strtod(byval->ptr,&eptr); if (eptr[0] != '\0' || errno == ERANGE || isnan(vector[j].u.score)) { int_convertion_error = 1; } } else if (byval->encoding == REDIS_ENCODING_INT) { /* Don't need to decode the object if it's * integer-encoded (the only encoding supported) so * far. We can just cast it */ vector[j].u.score = (long)byval->ptr; } else { redisAssertWithInfo(c,sortval,1 != 1); } } /* when the object was retrieved using lookupKeyByPattern, * its refcount needs to be decreased. */ if (sortby) { decrRefCount(byval); } } } /* We are ready to sort the vector... perform a bit of sanity check * on the LIMIT option too. We'll use a partial version of quicksort. */ start = (limit_start < 0) ? 0 : limit_start; end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1; if (start >= vectorlen) { start = vectorlen-1; end = vectorlen-2; } if (end >= vectorlen) end = vectorlen-1; server.sort_dontsort = dontsort; if (dontsort == 0) { server.sort_desc = desc; server.sort_alpha = alpha; server.sort_bypattern = sortby ? 1 : 0; if (sortby && (start != 0 || end != vectorlen-1)) pqsort(vector,vectorlen,sizeof(redisSortObject),sortCompare, start,end); else qsort(vector,vectorlen,sizeof(redisSortObject),sortCompare); } /* Send command output to the output buffer, performing the specified * GET/DEL/INCR/DECR operations if any. */ outputlen = getop ? getop*(end-start+1) : end-start+1; if (int_convertion_error) { addReplyError(c,"One or more scores can't be converted into double"); } else if (storekey == NULL) { /* STORE option not specified, sent the sorting result to client */ addReplyMultiBulkLen(c,outputlen); for (j = start; j <= end; j++) { listNode *ln; listIter li; if (!getop) addReplyBulk(c,vector[j].obj); listRewind(operations,&li); while((ln = listNext(&li))) { redisSortOperation *sop = ln->value; robj *val = lookupKeyByPattern(c->db,sop->pattern, vector[j].obj); if (sop->type == REDIS_SORT_GET) { if (!val) { addReply(c,shared.nullbulk); } else { addReplyBulk(c,val); decrRefCount(val); } } else { /* Always fails */ redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET); } } } } else { robj *sobj = createZiplistObject(); /* STORE option specified, set the sorting result as a List object */ for (j = start; j <= end; j++) { listNode *ln; listIter li; if (!getop) { listTypePush(sobj,vector[j].obj,REDIS_TAIL); } else { listRewind(operations,&li); while((ln = listNext(&li))) { redisSortOperation *sop = ln->value; robj *val = lookupKeyByPattern(c->db,sop->pattern, vector[j].obj); if (sop->type == REDIS_SORT_GET) { if (!val) val = createStringObject("",0); /* listTypePush does an incrRefCount, so we should take care * care of the incremented refcount caused by either * lookupKeyByPattern or createStringObject("",0) */ listTypePush(sobj,val,REDIS_TAIL); decrRefCount(val); } else { /* Always fails */ redisAssertWithInfo(c,sortval,sop->type == REDIS_SORT_GET); } } } } if (outputlen) { setKey(c->db,storekey,sobj); server.dirty += outputlen; } else if (dbDelete(c->db,storekey)) { signalModifiedKey(c->db,storekey); server.dirty++; } decrRefCount(sobj); addReplyLongLong(c,outputlen); } /* Cleanup */ if (sortval->type == REDIS_LIST || sortval->type == REDIS_SET) for (j = 0; j < vectorlen; j++) decrRefCount(vector[j].obj); decrRefCount(sortval); listRelease(operations); for (j = 0; j < vectorlen; j++) { if (alpha && vector[j].u.cmpobj) decrRefCount(vector[j].u.cmpobj); } zfree(vector); }