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); } }
/* SYNTAX 1.) SCANSELECT * FROM tbl 2.) SCANSELECT * FROM tbl ORDER_BY_CLAUSE 3.) SCANSELECT * FROM tbl WHERE clause [ORDER_BY_CLAUSE] */ void tscanCommand(redisClient *c) { int cmatchs[MAX_COLUMN_PER_TABLE]; bool nowc = 0; /* NO WHERE CLAUSE */ bool cstar = 0; int qcols = 0; int tmatch = -1; bool join = 0; sds where = (c->argc > 4) ? c->argv[4]->ptr : NULL; sds wc = (c->argc > 5) ? c->argv[5]->ptr : NULL; if ((where && !*where) || (wc && !*wc)) { addReply(c, shared.scanselectsyntax); return; } if (!parseSelectReply(c, 1, &nowc, &tmatch, cmatchs, &qcols, &join, &cstar, c->argv[1]->ptr, c->argv[2]->ptr, c->argv[3]->ptr, where)) return; if (join) { addReply(c, shared.scan_join); return; } if (!nowc && !wc) { addReply(c, shared.scanselectsyntax); return; } cswc_t w; list *ll = NULL; /* B4 GOTO */ init_check_sql_where_clause(&w, tmatch, wc); /* on error: GOTO tscan_end */ if (nowc && c->argc > 4) { /* ORDER BY or STORE w/o WHERE CLAUSE */ if (!strncasecmp(where, "ORDER ", 6) || !strncasecmp(where, "STORE ", 6)) { if (!parseWCAddtlSQL(c, c->argv[4]->ptr, &w)) goto tscan_end; if (w.lvr) { w.lvr = sdsnewlen(w.lvr, strlen(w.lvr)); if (!leftoverParsingReply(c, w.lvr)) goto tscan_end; } if (w.wtype > SQL_STORE_LOOKUP_MASK) { /* STORE after ORDER BY */ addReply(c, shared.scan_store); goto tscan_end; } } } if (nowc && !w.nob && c->argc > 4) { /* argv[4] parse error */ w.lvr = sdsdup(where); leftoverParsingReply(c, w.lvr); goto tscan_end; } if (!nowc && !w.nob) { /* WhereClause exists and no ORDER BY */ parseWCReply(c, &w, SQL_SCANSELECT, 1); if (w.wtype == SQL_ERR_LOOKUP) goto tscan_end; if (!leftoverParsingReply(c, w.lvr)) goto tscan_end; if (w.imatch != -1) { /* disallow SCANSELECT on indexed columns */ addReply(c, shared.scan_on_index); goto tscan_end; } if (w.wtype > SQL_STORE_LOOKUP_MASK) { /* no SCAN STOREs (for now) */ addReply(c, shared.scan_store); goto tscan_end; } } if (cstar && w.nob) { /* SCANSELECT COUNT(*) ORDER BY -> stupid */ addReply(c, shared.orderby_count); goto tscan_end; } robj *btt = lookupKeyRead(c->db, Tbl[server.dbid][w.tmatch].name); bt *btr = (bt *)btt->ptr; if (cstar && nowc) { /* SCANSELECT COUNT(*) FROM tbl */ addReplyLongLong(c, (long long)btr->numkeys); goto tscan_end; } // TODO on "fk_lim" iterate on FK (not PK) //if (w.nob) w.imatch = find_index(w.tmatch, w.obc); fr_t fr; qr_t q; setQueued(&w, &q); ll = initOBsort(q.qed, &w); init_filter_row(&fr, c, btr, &w, &q, qcols, cmatchs, nowc, ll, cstar, OBY_FREE_ROBJ); //dumpW(&w, w.wtype); LEN_OBJ btEntry *be; long sent = 0; long loops = -1; btSIter *bi = q.pk_lo ? btGetFullIteratorXth(btr, w.ofst): btGetFullRangeIterator(btr); while ((be = btRangeNext(bi)) != NULL) { loops++; if (q.pk_lim) { if (!q.pk_lo && w.ofst != -1 && loops < w.ofst) continue; sent++; if (w.lim == card) break; /* ORDRBY PK LIM */ } condSelectReply(&fr, be->key, be->val, &card); } btReleaseRangeIterator(bi); if (q.qed && card) opSelectOnSort(c, ll, &w, fr.ofree, &sent); if (w.lim != -1 && sent < card) card = sent; if (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); tscan_end: releaseOBsort(ll); destroy_check_sql_where_clause(&w); }