/* get data for socket / fd being monitored. Create if not found*/ aeSockState *aeGetSockState(void *apistate, int fd) { int sindex; listNode *node; list *socklist; aeSockState *sockState; if (apistate == NULL) return NULL; sindex = aeSocketIndex(fd); socklist = &(((aeApiState *)apistate)->lookup[sindex]); node = listFirst(socklist); while (node != NULL) { sockState = (aeSockState *)listNodeValue(node); if (sockState->fd == fd) { return sockState; } node = listNextNode(node); } // not found. Do lazy create of sockState. sockState = (aeSockState *) zmalloc(sizeof(aeSockState)); if (sockState != NULL) { sockState->fd = fd; sockState->masks = 0; sockState->wreqs = 0; sockState->reqs = NULL; memset(&sockState->wreqlist, 0, sizeof(sockState->wreqlist)); memset(&sockState->remoteAddress, 0, sizeof(sockState->remoteAddress)); if (listAddNodeHead(socklist, sockState) != NULL) { return sockState; } else { zfree(sockState); } } return NULL; }
/* put object in list if deletes are deferred. * return 1 if deferred, 0 otherwise */ int deferFreeObject(void *obj) { if (deferObjDelete != NULL && server.isBackgroundSaving == 1) { listAddNodeHead(deferObjDelete, obj); return 1; } return 0; }
/* delete data for socket / fd being monitored or move to the closing queue if operations are pending. Return 1 if deleted or not found, 0 if pending*/ void aeDelSockState(void *apistate, aeSockState *sockState) { int sindex; list *socklist; if (apistate == NULL) return; if (sockState->wreqs == 0 && (sockState->masks & (READ_QUEUED | CONNECT_PENDING | SOCKET_ATTACHED | CLOSE_PENDING)) == 0) { // see if in active list sindex = aeSocketIndex(sockState->fd); socklist = &(((aeApiState *)apistate)->lookup[sindex]); if (removeMatchFromList(socklist, sockState) == 1) { zfree(sockState); return; } // try closing list socklist = &(((aeApiState *)apistate)->closing); if (removeMatchFromList(socklist, sockState) == 1) { zfree(sockState); return; } } else { // not safe to delete. Move to closing sindex = aeSocketIndex(sockState->fd); socklist = &(((aeApiState *)apistate)->lookup[sindex]); if (removeMatchFromList(socklist, sockState) == 1) { // removed from active list. add to closing list socklist = &(((aeApiState *)apistate)->closing); listAddNodeHead(socklist, sockState); } } }
static void glueReplyBuffersIfNeeded(redisClient *c) { int copylen = 0; char buf[GLUEREPLY_UP_TO]; listNode *ln; listIter li; robj *o; listRewind(c->reply,&li); while((ln = listNext(&li))) { int objlen; o = ln->value; objlen = sdslen(o->ptr); if (copylen + objlen <= GLUEREPLY_UP_TO) { memcpy(buf+copylen,o->ptr,objlen); copylen += objlen; listDelNode(c->reply,ln); } else { if (copylen == 0) return; break; } } /* Now the output buffer is empty, add the new single element */ o = createObject(REDIS_STRING,sdsnewlen(buf,copylen)); listAddNodeHead(c->reply,o); }
/* Push a new entry into the slow log. * This function will make sure to trim the slow log accordingly to the * configured max length. */ void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) { if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ if (duration >= server.slowlog_log_slower_than) listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration)); /* Remove old entries if needed. */ while (listLength(server.slowlog) > server.slowlog_max_len) listDelNode(server.slowlog,listLast(server.slowlog)); }
static void decrRefCount(void *obj) { robj *o = obj; /* Object is a key of a swapped out value, or in the process of being * loaded. */ if (server.vm_enabled && (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING)) { if (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING) { redisAssert(o->refcount == 1); } if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(obj); redisAssert(o->type == REDIS_STRING); freeStringObject(o); vmMarkPagesFree(o->vm.page,o->vm.usedpages); pthread_mutex_lock(&server.obj_freelist_mutex); if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX || !listAddNodeHead(server.objfreelist,o)) zfree(o); pthread_mutex_unlock(&server.obj_freelist_mutex); server.vm_stats_swapped_objects--; return; } /* Object is in memory, or in the process of being swapped out. */ if (--(o->refcount) == 0) { if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(obj); switch(o->type) { case REDIS_STRING: freeStringObject(o); break; case REDIS_LIST: freeListObject(o); break; case REDIS_SET: freeSetObject(o); break; case REDIS_ZSET: freeZsetObject(o); break; case REDIS_HASH: freeHashObject(o); break; default: redisAssert(0); break; } if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex); if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX || !listAddNodeHead(server.objfreelist,o)) zfree(o); if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex); } }
pList *listInsertNode(pList *list, int direction, void *value) { if (list == NULL) return NULL; unsigned int mid = list->len/2; if (direction < mid) { if (direction < 0) return listAddNodeHead(list, value); } else { } }
int main() { list * ls = listCreate(); listIter *iter; listNode *node; char val1[] = "value1"; char val2[] = "value2"; char val3[] = "value3"; listAddNodeHead(ls, val1); listAddNodeHead(ls, val2); listAddNodeHead(ls, val3); iter = listGetIterator(ls, AL_START_HEAD); while((node = listNext(iter)) != NULL) { printf("value = %s\n", (char*)node->value); } listReleaseIterator(iter); return 0; }
/* serperated by comma */ int initUserList(list *l, char *s) { ASSERT(l && s && (strlen(s) > 0)); l->match = userMatch; char *p1 = NULL; char *head = s; int i = 0; while (NULL != (p1 = strchr(head, TOKEN))) { p1[0] = '\0'; dump(L_DEBUG, "user:%s", head); listAddNodeHead(l, head); head = p1 + 1; i++; } dump(L_DEBUG, "user:%s", head); listAddNodeHead(l, head); i++; return i; }
/* Push a new entry into the slow log. * * 如果参数 duration 超过服务器设置的上限时间, * 那么将一个新条目以 FIFO 顺序推入到慢查询日志中。 * * This function will make sure to trim the slow log accordingly to the * configured max length. * * 根据服务器设置的最大日志长度,可能会对日志进行截断(trim) */ void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) { // 慢查询功能未开启,直接返回 if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */ // 如果执行时间超过服务器设置的上限,那么将命令添加到慢查询日志 if (duration >= server.slowlog_log_slower_than) // 新日志添加到链表表头 listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration)); /* Remove old entries if needed. */ // 如果日志数量过多,那么进行删除 while (listLength(server.slowlog) > server.slowlog_max_len) listDelNode(server.slowlog,listLast(server.slowlog)); }
void decrRefCount(void *obj) { robj *o = obj; /* Object is a swapped out value, or in the process of being loaded. */ if (server.vm_enabled && (o->storage == REDIS_VM_SWAPPED || o->storage == REDIS_VM_LOADING)) { vmpointer *vp = obj; if (o->storage == REDIS_VM_LOADING) vmCancelThreadedIOJob(o); vmMarkPagesFree(vp->page,vp->usedpages); server.vm_stats_swapped_objects--; zfree(vp); return; } if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0"); /* Object is in memory, or in the process of being swapped out. * * If the object is being swapped out, abort the operation on * decrRefCount even if the refcount does not drop to 0: the object * is referenced at least two times, as value of the key AND as * job->val in the iojob. So if we don't invalidate the iojob, when it is * done but the relevant key was removed in the meantime, the * complete jobs handler will not find the key about the job and the * assert will fail. */ if (server.vm_enabled && o->storage == REDIS_VM_SWAPPING) vmCancelThreadedIOJob(o); if (--(o->refcount) == 0) { switch(o->type) { case REDIS_STRING: freeStringObject(o); break; case REDIS_LIST: freeListObject(o); break; case REDIS_SET: freeSetObject(o); break; case REDIS_ZSET: freeZsetObject(o); break; case REDIS_HASH: freeHashObject(o); break; default: redisPanic("Unknown object type"); break; } o->ptr = NULL; /* defensive programming. We'll see NULL in traces. */ if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex); if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX || !listAddNodeHead(server.objfreelist,o)) zfree(o); if (server.vm_enabled) pthread_mutex_unlock(&server.obj_freelist_mutex); } }
/* * prepend small(small than a mbuf) content into msg */ int msg_prepend(struct msg *msg, uint8_t *pos, size_t n) { mbuf_base *mb = msg->mb; struct mbuf *mbuf; mbuf = mbuf_get(mb); if (mbuf == NULL) { return RMT_ENOMEM; } ASSERT(n <= mbuf_size(mbuf)); mbuf_copy(mbuf, pos, n); msg->mlen += (uint32_t)n; listAddNodeHead(msg->data, mbuf); return RMT_OK; }
static bool fts_index_add(fts_t *fts, fts_doc_t *doc) { int i, len, nonstopwords; sds *terms; terms = sds_tokenize(doc->doc->ptr, &len, &nonstopwords); if (!terms) return false; for (i = 0; i < len; i++) { sds term = terms[i]; list *idx; listNode *ln; if (sdslen(term) == 0) { sdsfree(term); continue; } idx = dict_get(fts->index, term); if (!idx) { idx = index_list_create(); dict_set(fts->index, term, idx); } ln = listSearchKey(idx, doc); if (ln) { index_item_t *idi = ln->value; idi->tf++; } else { index_item_t *idi = rr_malloc(sizeof(*idi)); idi->doc = doc; idi->tf = 1; listAddNodeHead(idx, idi); } sdsfree(term); } rr_free(terms); doc->len = nonstopwords; fts->len += doc->len; return true; }
void listTypePush(robj *subject, robj *value, int where) { /* Check if we need to convert the ziplist */ listTypeTryConversion(subject,value); if (subject->encoding == REDIS_ENCODING_ZIPLIST && ziplistLen(subject->ptr) >= server.list_max_ziplist_entries) listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST); if (subject->encoding == REDIS_ENCODING_ZIPLIST) { int pos = (where == REDIS_HEAD) ? ZIPLIST_HEAD : ZIPLIST_TAIL; value = getDecodedObject(value); subject->ptr = ziplistPush(subject->ptr,value->ptr,sdslen(value->ptr),pos); decrRefCount(value); } else if (subject->encoding == REDIS_ENCODING_LINKEDLIST) { if (where == REDIS_HEAD) { listAddNodeHead(subject->ptr,value); } else { listAddNodeTail(subject->ptr,value); } incrRefCount(value); } else { redisPanic("Unknown list encoding"); } }
/* * prepend small(small than a mbuf) content into msg */ int msg_prepend_format(struct msg *msg, const char *fmt, ...) { mbuf_base *mb = msg->mb; struct mbuf *mbuf; int32_t n; va_list args; mbuf = mbuf_get(mb); if (mbuf == NULL) { return RMT_ENOMEM; } va_start(args, fmt); n = rmt_vscnprintf(mbuf->last, mbuf_size(mbuf), fmt, args); va_end(args); mbuf->last += n; msg->mlen += (uint32_t)n; listAddNodeHead(msg->data, mbuf); return RMT_OK; }
int main(int argc, char *argv[]) { unsigned char *zl, *p; unsigned char *entry; unsigned int elen; long long value; /* If an argument is given, use it as the random seed. */ if (argc == 2) srand(atoi(argv[1])); zl = createIntList(); ziplistRepr(zl); zl = createList(); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); pop(zl,ZIPLIST_HEAD); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); pop(zl,ZIPLIST_TAIL); ziplistRepr(zl); printf("Get element at index 3:\n"); { zl = createList(); p = ziplistIndex(zl, 3); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index 3\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index 4 (out of range):\n"); { zl = createList(); p = ziplistIndex(zl, 4); if (p == NULL) { printf("No entry\n"); } else { printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); return 1; } printf("\n"); } printf("Get element at index -1 (last element):\n"); { zl = createList(); p = ziplistIndex(zl, -1); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index -1\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index -4 (first element):\n"); { zl = createList(); p = ziplistIndex(zl, -4); if (!ziplistGet(p, &entry, &elen, &value)) { printf("ERROR: Could not access index -4\n"); return 1; } if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); printf("\n"); } else { printf("%lld\n", value); } printf("\n"); } printf("Get element at index -5 (reverse out of range):\n"); { zl = createList(); p = ziplistIndex(zl, -5); if (p == NULL) { printf("No entry\n"); } else { printf("ERROR: Out of range index should return NULL, returned offset: %ld\n", p-zl); return 1; } printf("\n"); } printf("Iterate list from 0 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 0); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate list from 1 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate list from 2 to end:\n"); { zl = createList(); p = ziplistIndex(zl, 2); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistNext(zl,p); printf("\n"); } printf("\n"); } printf("Iterate starting out of range:\n"); { zl = createList(); p = ziplistIndex(zl, 4); if (!ziplistGet(p, &entry, &elen, &value)) { printf("No entry\n"); } else { printf("ERROR\n"); } printf("\n"); } printf("Iterate from back to front:\n"); { zl = createList(); p = ziplistIndex(zl, -1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } p = ziplistPrev(zl,p); printf("\n"); } printf("\n"); } printf("Iterate from back to front, deleting all items:\n"); { zl = createList(); p = ziplistIndex(zl, -1); while (ziplistGet(p, &entry, &elen, &value)) { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld", value); } zl = ziplistDelete(zl,&p); p = ziplistPrev(zl,p); printf("\n"); } printf("\n"); } printf("Delete inclusive range 0,0:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 0, 1); ziplistRepr(zl); } printf("Delete inclusive range 0,1:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 0, 2); ziplistRepr(zl); } printf("Delete inclusive range 1,2:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 1, 2); ziplistRepr(zl); } printf("Delete with start index out of range:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 5, 1); ziplistRepr(zl); } printf("Delete with num overflow:\n"); { zl = createList(); zl = ziplistDeleteRange(zl, 1, 5); ziplistRepr(zl); } printf("Delete foo while iterating:\n"); { zl = createList(); p = ziplistIndex(zl,0); while (ziplistGet(p,&entry,&elen,&value)) { if (entry && strncmp("foo",(char*)entry,elen) == 0) { printf("Delete foo\n"); zl = ziplistDelete(zl,&p); } else { printf("Entry: "); if (entry) { if (elen && fwrite(entry,elen,1,stdout) == 0) perror("fwrite"); } else { printf("%lld",value); } p = ziplistNext(zl,p); printf("\n"); } } printf("\n"); ziplistRepr(zl); } printf("Regression test for >255 byte strings:\n"); { char v1[257],v2[257]; memset(v1,'x',256); memset(v2,'y',256); zl = ziplistNew(); zl = ziplistPush(zl,(unsigned char*)v1,strlen(v1),ZIPLIST_TAIL); zl = ziplistPush(zl,(unsigned char*)v2,strlen(v2),ZIPLIST_TAIL); /* Pop values again and compare their value. */ p = ziplistIndex(zl,0); assert(ziplistGet(p,&entry,&elen,&value)); assert(strncmp(v1,(char*)entry,elen) == 0); p = ziplistIndex(zl,1); assert(ziplistGet(p,&entry,&elen,&value)); assert(strncmp(v2,(char*)entry,elen) == 0); printf("SUCCESS\n\n"); } printf("Regression test deleting next to last entries:\n"); { char v[3][257]; zlentry e[3]; int i; for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) { memset(v[i], 'a' + i, sizeof(v[0])); } v[0][256] = '\0'; v[1][ 1] = '\0'; v[2][256] = '\0'; zl = ziplistNew(); for (i = 0; i < (sizeof(v)/sizeof(v[0])); i++) { zl = ziplistPush(zl, (unsigned char *) v[i], strlen(v[i]), ZIPLIST_TAIL); } verify(zl, e); assert(e[0].prevrawlensize == 1); assert(e[1].prevrawlensize == 5); assert(e[2].prevrawlensize == 1); /* Deleting entry 1 will increase `prevrawlensize` for entry 2 */ unsigned char *p = e[1].p; zl = ziplistDelete(zl, &p); verify(zl, e); assert(e[0].prevrawlensize == 1); assert(e[1].prevrawlensize == 5); printf("SUCCESS\n\n"); } printf("Create long list and check indices:\n"); { zl = ziplistNew(); char buf[32]; int i,len; for (i = 0; i < 1000; i++) { len = sprintf(buf,"%d",i); zl = ziplistPush(zl,(unsigned char*)buf,len,ZIPLIST_TAIL); } for (i = 0; i < 1000; i++) { p = ziplistIndex(zl,i); assert(ziplistGet(p,NULL,NULL,&value)); assert(i == value); p = ziplistIndex(zl,-i-1); assert(ziplistGet(p,NULL,NULL,&value)); assert(999-i == value); } printf("SUCCESS\n\n"); } printf("Compare strings with ziplist entries:\n"); { zl = createList(); p = ziplistIndex(zl,0); if (!ziplistCompare(p,(unsigned char*)"hello",5)) { printf("ERROR: not \"hello\"\n"); return 1; } if (ziplistCompare(p,(unsigned char*)"hella",5)) { printf("ERROR: \"hella\"\n"); return 1; } p = ziplistIndex(zl,3); if (!ziplistCompare(p,(unsigned char*)"1024",4)) { printf("ERROR: not \"1024\"\n"); return 1; } if (ziplistCompare(p,(unsigned char*)"1025",4)) { printf("ERROR: \"1025\"\n"); return 1; } printf("SUCCESS\n\n"); } printf("Stress with random payloads of different encoding:\n"); { int i,j,len,where; unsigned char *p; char buf[1024]; int buflen; list *ref; listNode *refnode; /* Hold temp vars from ziplist */ unsigned char *sstr; unsigned int slen; long long sval; for (i = 0; i < 20000; i++) { zl = ziplistNew(); ref = listCreate(); listSetFreeMethod(ref, sdsfree); len = rand() % 256; /* Create lists */ for (j = 0; j < len; j++) { where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL; if (rand() % 2) { buflen = randstring(buf,1,sizeof(buf)-1); } else { switch(rand() % 3) { case 0: buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20); break; case 1: buflen = sprintf(buf,"%lld",(0LL + rand())); break; case 2: buflen = sprintf(buf,"%lld",(0LL + rand()) << 20); break; default: assert(NULL); } } /* Add to ziplist */ zl = ziplistPush(zl, (unsigned char*)buf, buflen, where); /* Add to reference list */ if (where == ZIPLIST_HEAD) { listAddNodeHead(ref,sdsnewlen(buf, buflen)); } else if (where == ZIPLIST_TAIL) { listAddNodeTail(ref,sdsnewlen(buf, buflen)); } else { assert(NULL); } } assert(listLength(ref) == ziplistLen(zl)); for (j = 0; j < len; j++) { /* Naive way to get elements, but similar to the stresser * executed from the Tcl test suite. */ p = ziplistIndex(zl,j); refnode = listIndex(ref,j); assert(ziplistGet(p,&sstr,&slen,&sval)); if (sstr == NULL) { buflen = sprintf(buf,"%lld",sval); } else { buflen = slen; memcpy(buf,sstr,buflen); buf[buflen] = '\0'; } assert(memcmp(buf,listNodeValue(refnode),buflen) == 0); } zfree(zl); listRelease(ref); } printf("SUCCESS\n\n"); } printf("Stress with variable ziplist size:\n"); { stress(ZIPLIST_HEAD,100000,16384,256); stress(ZIPLIST_TAIL,100000,16384,256); } return 0; }
static void dictDbValDeferDestructor(void *privdata, void *val) { DICT_NOTUSED(privdata); listAddNodeHead(deferObjDelete, val); }
/* dictionary destructors for defering delete */ void dictDbKeyDeferDestructor(void *privdata, void *val) { DICT_NOTUSED(privdata); listAddNodeHead(deferSdsDelete, val); }
/* EXPLANATION: The join algorithm 0.) for each table in join 1.) pointers to ALL candidate columns & lengths are saved to a tuplet 2.) this tuplet is then stored in a (Btree or HashTable) for L8R joining NOTE: (CON) This is only efficient for small table joins [if at all :)] */ static void joinAddColsFromInd(join_add_cols_t *a, robj *rset[], cswc_t *w) { cu32_t cresp[MAX_JOIN_INDXS]; int row_len = 0; int nresp = 0; aobj *akey = a->ajk; for (int i = 0; i < a->qcols; i++) { int tmatch = a->j_tbls[i]; if (tmatch == a->itable) { cresp[nresp].s = getCopyColStr(a->btr, a->rrow, a->j_cols[i], akey, tmatch, &cresp[nresp].len); row_len += cresp[nresp].len + 1; // +1 for OUTPUT_DELIM nresp++; } } row_len--; // no DELIM on final col if (a->j_ind_len[a->index] < row_len) a->j_ind_len[a->index] = row_len; a->jind_ncols[a->index] = nresp; char *ind_row = malloc(nresp * (PTR_SIZE + UINT_SIZE)); char *list_val = ind_row; for (int i = 0; i < nresp; i++) { /* fill in ind_row */ memcpy(ind_row, &cresp[i].s, PTR_SIZE); ind_row += PTR_SIZE; memcpy(ind_row, &cresp[i].len, UINT_SIZE); ind_row += UINT_SIZE; } /* only index matching "obt" needs to have ORDER_BY column */ /* TODO does this for every column for this index - only 1st is needed */ if (w->obt[0] == a->itable) { obsl_t *ob = create_obsl(list_val, 1); /* DESTROY ME 011 */ list_val = (char *)ob; /* in ORDER BY list_val -> obsl_t */ OB_asc[0] = w->asc[0]; OB_ctype[0] = Tbl[server.dbid][w->tmatch].col_type[w->obc[0]]; assignObKey(w, a->btr, a->rrow, akey, 0, ob); } // TODO unify, refactor, etc.. // // NOTE: (clarification of clusterFUcT implementation) // 1st index: BT of Lists // 2nd index: ValSetDict of AppendSets (should be List) if (a->index == 0) { // first joined index needs BTREE (i.e. sorted) joinRowEntry k; k.key = createStringRobjFromAobj(a->ajk); joinRowEntry *x = btJoinFindVal(a->jbtr, &k); if (x) { list *ll = (list *)x->val; listAddNodeTail(ll, list_val); } else { joinRowEntry *jre = (joinRowEntry *)malloc(sizeof(joinRowEntry)); list *ll = listCreate(); /* listRelease freeListOfIndRow*/ jre->key = k.key; jre->val = (void *)ll; listAddNodeHead(ll, list_val); btJoinAddRow(a->jbtr, jre); } } else { // rest of the joined indices are redis DICTs for speed robj *res_setobj; robj *ind_row_obj = createObject(REDIS_JOINROW, list_val); robj *key = createStringRobjFromAobj(a->ajk); dictEntry *rde = dictFind(rset[a->index]->ptr, key); if (!rde) { // create AppendSet "list" res_setobj = createAppendSetObject(); dictAdd(rset[a->index]->ptr, key, res_setobj); } else { res_setobj = dictGetEntryVal(rde); decrRefCount(key); } dictAdd(res_setobj->ptr, ind_row_obj, NULL); // push to (list per jk) } }