From: Kirill Shcherbatov <kshcherbatov@tarantool.org> To: tarantool-patches@freelists.org Cc: korablev@tarantool.org, Kirill Shcherbatov <kshcherbatov@tarantool.org> Subject: [tarantool-patches] [PATCH v1 1/1] sql: replace sql column mask with core mask Date: Thu, 30 Aug 2018 18:18:01 +0300 [thread overview] Message-ID: <cc088d98c8b0cf564164542aac0a6d9a91c74838.1535642184.git.kshcherbatov@tarantool.org> (raw) Refactored SQL Bitmask with tarantool core uint64_t column mask and API functions. Closes #3571. --- Branch: http://github.com/tarantool/tarantool/tree/kshch/gh-3571-replace-sql-column-mask Issue: https://github.com/tarantool/tarantool/issues/3571 src/box/column_mask.h | 23 +- src/box/sql/delete.c | 4 +- src/box/sql/expr.c | 76 ++-- src/box/sql/resolve.c | 25 +- src/box/sql/sqliteInt.h | 69 ++-- src/box/sql/update.c | 10 +- src/box/sql/vdbeaux.c | 2 +- src/box/sql/where.c | 1051 ++++++++++++++++++++++++----------------------- src/box/sql/whereInt.h | 149 +++++-- src/box/sql/wherecode.c | 509 ++++++++++++----------- src/box/sql/whereexpr.c | 160 ++++---- 11 files changed, 1069 insertions(+), 1009 deletions(-) diff --git a/src/box/column_mask.h b/src/box/column_mask.h index d71911d..cf52913 100644 --- a/src/box/column_mask.h +++ b/src/box/column_mask.h @@ -50,7 +50,9 @@ * in such case we set not one bit, but a range of bits. */ -#define COLUMN_MASK_FULL UINT64_MAX +#define COLUMN_MASK_FULL UINT64_MAX +#define COLUMN_MASK_BIT(n) (((uint64_t)1)<<(n)) +#define COLUMN_MASK_SIZE ((int)(sizeof(uint64_t)*8)) /** * Set a bit in the bitmask corresponding to a @@ -61,12 +63,12 @@ static inline void column_mask_set_fieldno(uint64_t *column_mask, uint32_t fieldno) { - if (fieldno >= 63) + if (fieldno >= COLUMN_MASK_SIZE - 1) /* * @sa column_mask key_def declaration for * details. */ - *column_mask |= ((uint64_t) 1) << 63; + *column_mask |= ((uint64_t) 1) << (COLUMN_MASK_SIZE - 1); else *column_mask |= ((uint64_t) 1) << fieldno; } @@ -80,7 +82,7 @@ column_mask_set_fieldno(uint64_t *column_mask, uint32_t fieldno) static inline void column_mask_set_range(uint64_t *column_mask, uint32_t first_fieldno_in_range) { - if (first_fieldno_in_range < 63) { + if (first_fieldno_in_range < COLUMN_MASK_SIZE - 1) { /* * Set all bits by default via COLUMN_MASK_FULL * and then unset bits preceding the operation @@ -90,11 +92,22 @@ column_mask_set_range(uint64_t *column_mask, uint32_t first_fieldno_in_range) *column_mask |= COLUMN_MASK_FULL << first_fieldno_in_range; } else { /* A range outside "short" range. */ - *column_mask |= ((uint64_t) 1) << 63; + *column_mask |= ((uint64_t) 1) << (COLUMN_MASK_SIZE - 1); } } /** + * Test if overflow flag is set in mask. + * @param column_mask Mask to test. + * @retval true If mask overflowed, false otherwise. + */ +static inline bool +column_mask_is_overflowed(uint64_t column_mask) +{ + return column_mask & (((uint64_t) 1) << (COLUMN_MASK_SIZE - 1)); +} + +/** * True if the update operation does not change the key. * @param key_mask Key mask. * @param update_mask Column mask of the update operation. diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index 93ada3a..d146201 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -501,10 +501,8 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table, */ sqlite3VdbeAddOp2(v, OP_Copy, reg_pk, first_old_reg); for (int i = 0; i < (int)table->def->field_count; i++) { - testcase(mask != 0xffffffff && iCol == 31); - testcase(mask != 0xffffffff && iCol == 32); if (mask == 0xffffffff - || (i <= 31 && (mask & MASKBIT32(i)) != 0)) { + || (i <= 31 && (mask & COLUMN_MASK_BIT(i)) != 0)) { sqlite3ExprCodeGetColumnOfTable(v, table->def, cursor, i, first_old_reg + diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index 50505b1..fd0b2b2 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -1539,7 +1539,7 @@ sqlite3SrcListDup(sqlite3 * db, SrcList * p, int flags) sqlite3SelectDup(db, pOldItem->pSelect, flags); pNewItem->pOn = sqlite3ExprDup(db, pOldItem->pOn, flags); pNewItem->pUsing = sqlite3IdListDup(db, pOldItem->pUsing); - pNewItem->colUsed = pOldItem->colUsed; + pNewItem->column_used_mask = pOldItem->column_used_mask; } return pNew; } @@ -2402,20 +2402,18 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */ /* Search for an existing index that will work for this IN operator */ for (pIdx = pTab->pIndex; pIdx && eType == 0; pIdx = pIdx->pNext) { - Bitmask colUsed; /* Columns of the index used */ - Bitmask mCol; /* Mask for the current column */ uint32_t part_count = pIdx->def->key_def->part_count; struct key_part *parts = pIdx->def->key_def->parts; if ((int)part_count < nExpr) continue; - /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute - * BITMASK(nExpr) without overflowing + /* + * Maximum nColumn is COLUMN_MASK_SIZE-2, not + * COLUMN_MASK_SIZE-1, so that we can compute + * bitmasks without overflowing. */ - testcase(part_count == BMS - 2); - testcase(part_count == BMS - 1); - if (part_count >= BMS - 1) + if (part_count >= COLUMN_MASK_SIZE - 1) continue; if (mustBeUnique && ((int)part_count > nExpr || @@ -2427,8 +2425,8 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */ */ continue; } - - colUsed = 0; /* Columns of index used so far */ + /* Columns of the index used mask. */ + uint64_t col_used_mask = 0; for (i = 0; i < nExpr; i++) { Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i); Expr *pRhs = pEList->a[i].pExpr; @@ -2451,20 +2449,25 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */ } if (j == nExpr) break; - mCol = MASKBIT(j); - if (mCol & colUsed) - break; /* Each column used only once */ - colUsed |= mCol; + /* Each column used only once. */ + if (COLUMN_MASK_BIT(j) & col_used_mask) + break; + column_mask_set_fieldno(&col_used_mask, + j); if (aiMap) aiMap[i] = pRhs->iColumn; else if (pSingleIdxCol && nExpr == 1) *pSingleIdxCol = pRhs->iColumn; } - assert(i == nExpr - || colUsed != (MASKBIT(nExpr) - 1)); - if (colUsed == (MASKBIT(nExpr) - 1)) { - /* If we reach this point, that means the index pIdx is usable */ + assert((COLUMN_MASK_BIT(nExpr) - 1) != + col_used_mask || i == nExpr); + if (col_used_mask == + (COLUMN_MASK_BIT(nExpr) - 1)) { + /* + * If we reach this point, that means + * the index pIdx is usable. + */ int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_Explain, @@ -2486,18 +2489,12 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */ if (prRhsHasNull) { #ifdef SQLITE_ENABLE_COLUMN_USED_MASK - i64 mask = - (1 << nExpr) - 1; - sqlite3VdbeAddOp4Dup8(v, - OP_ColumnsUsed, - iTab, - 0, - 0, - (u8 - *) - & - mask, - P4_INT64); + uint64_t mask = (1 << nExpr) - 1; + sqlite3VdbeAddOp4Dup8(v, + OP_ColumnsUsed, + iTab, 0, 0, + (u8*)&mask, + P4_INT64); #endif *prRhsHasNull = ++pParse->nMem; if (nExpr == 1) { @@ -3943,7 +3940,6 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target) int nFarg; /* Number of function arguments */ FuncDef *pDef; /* The function definition object */ const char *zId; /* The function name */ - u32 constMask = 0; /* Mask of function arguments that are constant */ int i; /* Loop counter */ sqlite3 *db = pParse->db; /* The database connection */ struct coll *coll = NULL; @@ -4005,12 +4001,13 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target) target); } + /* Mask of function arguments that are constant. */ + uint64_t farg_const_mask = 0; for (i = 0; i < nFarg; i++) { - if (i < 32 - && sqlite3ExprIsConstant(pFarg->a[i]. - pExpr)) { - testcase(i == 31); - constMask |= MASKBIT32(i); + if (i < 32 && + sqlite3ExprIsConstant(pFarg->a[i].pExpr)) { + column_mask_set_fieldno(&farg_const_mask, + i); } if ((pDef->funcFlags & SQLITE_FUNC_NEEDCOLL) != 0 && coll == NULL) { @@ -4022,7 +4019,7 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target) } } if (pFarg) { - if (constMask) { + if (farg_const_mask != 0) { r1 = pParse->nMem + 1; pParse->nMem += nFarg; } else { @@ -4070,12 +4067,11 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target) sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)coll, P4_COLLSEQ); } - sqlite3VdbeAddOp4(v, OP_Function0, constMask, r1, + sqlite3VdbeAddOp4(v, OP_Function0, farg_const_mask, r1, target, (char *)pDef, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8) nFarg); - if (nFarg && constMask == 0) { + if (nFarg != 0 && farg_const_mask == 0) sqlite3ReleaseTempRange(pParse, r1, nFarg); - } return target; } case TK_EXISTS: diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 9a2d6ff..134292a 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -437,20 +437,18 @@ lookupName(Parse * pParse, /* The parsing context */ pTopNC->nErr++; } - /* If a column from a table in pSrcList is referenced, then record - * this fact in the pSrcList.a[].colUsed bitmask. Column 0 causes - * bit 0 to be set. Column 1 sets bit 1. And so forth. If the - * column number is greater than the number of bits in the bitmask - * then set the high-order bit of the bitmask. + /* + * If a column from a table in pSrcList is referenced, + * then record this fact in pSrcList.a.column_used_mask + * bitmask. Column 0 causes bit 0 to be set. Column 1 + * sets bit 1. And so forth. If the column number is + * greater than the number of bits in the bitmask then + * set the high-order bit of the bitmask. */ if (pExpr->iColumn >= 0 && pMatch != 0) { - int n = pExpr->iColumn; - testcase(n == BMS - 1); - if (n >= BMS) { - n = BMS - 1; - } assert(pMatch->iCursor == pExpr->iTable); - pMatch->colUsed |= ((Bitmask) 1) << n; + column_mask_set_fieldno(&pMatch->column_used_mask, + pExpr->iColumn); } /* Clean up and return @@ -492,10 +490,7 @@ sqlite3CreateColumnExpr(sqlite3 * db, SrcList * pSrc, int iSrc, int iCol) p->space_def = pItem->pTab->def; p->iTable = pItem->iCursor; p->iColumn = (ynVar) iCol; - testcase(iCol == BMS); - testcase(iCol == BMS - 1); - pItem->colUsed |= - ((Bitmask) 1) << (iCol >= BMS ? BMS - 1 : iCol); + column_mask_set_fieldno(&pItem->column_used_mask, iCol); ExprSetProperty(p, EP_Resolved); } return p; diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 1d32c9a..6945121 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -67,6 +67,7 @@ #include <stdbool.h> +#include "box/column_mask.h" #include "box/field_def.h" #include "box/sql.h" #include "box/txn.h" @@ -2296,49 +2297,28 @@ struct IdList { int nId; /* Number of identifiers on the list */ }; -/* - * The bitmask datatype defined below is used for various optimizations. - * - * Changing this from a 64-bit to a 32-bit type limits the number of - * tables in a join to 32 instead of 64. But it also reduces the size - * of the library by 738 bytes on ix86. - */ -#ifdef SQLITE_BITMASK_TYPE -typedef SQLITE_BITMASK_TYPE Bitmask; -#else -typedef u64 Bitmask; -#endif - -/* - * The number of bits in a Bitmask. "BMS" means "BitMask Size". - */ -#define BMS ((int)(sizeof(Bitmask)*8)) - -/* - * A bit in a Bitmask - */ -#define MASKBIT(n) (((Bitmask)1)<<(n)) -#define MASKBIT32(n) (((unsigned int)1)<<(n)) -#define ALLBITS ((Bitmask)-1) - -/* - * The following structure describes the FROM clause of a SELECT statement. - * Each table or subquery in the FROM clause is a separate element of - * the SrcList.a[] array. - * - * With the addition of multiple database support, the following structure - * can also be used to describe a particular table such as the table that - * is modified by an INSERT, DELETE, or UPDATE statement. In standard SQL, - * such a table must be a simple name: ID. But in SQLite, the table can - * now be identified by a database name, a dot, then the table name: ID.ID. - * - * The jointype starts out showing the join type between the current table - * and the next table on the list. The parser builds the list this way. - * But sqlite3SrcListShiftJoinType() later shifts the jointypes so that each - * jointype expresses the join between the table and the previous table. - * - * In the colUsed field, the high-order bit (bit 63) is set if the table - * contains more than 63 columns and the 64-th or later column is used. +/** + * The following structure describes the FROM clause of a SELECT + * statement. Each table or subquery in the FROM clause is a + * separate element of the SrcList.a[] array. + * + * With the addition of multiple database support, the following + * structure can also be used to describe a particular table such + * as the table that is modified by an INSERT, DELETE, or UPDATE + * statement. In standard SQL, + * such a table must be a simple name: ID. But in SQLite, the + * table can now be identified by a database name, a dot, then + * the table name: ID.ID. + * + * The jointype starts out showing the join type between the + * current table and the next table on the list. The parser + * builds the list this way. But sqlite3SrcListShiftJoinType() + * later shifts the jointypes so that each jointype expresses the + * join between the table and the previous table. + * + * In the column_used_mask field, the high-order bit (bit 63) is + * set if thetable contains more than 63 columns and the 64-th or + * later column is used. */ struct SrcList { int nSrc; /* Number of tables or subqueries in the FROM clause */ @@ -2365,7 +2345,8 @@ struct SrcList { int iCursor; /* The VDBE cursor number used to access this table */ Expr *pOn; /* The ON clause of a join */ IdList *pUsing; /* The USING clause of a join */ - Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */ + /** Bit N (1<<N) set if column N of pTab is used. */ + uint64_t column_used_mask; union { char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */ ExprList *pFuncArg; /* Arguments to table-valued-function */ diff --git a/src/box/sql/update.c b/src/box/sql/update.c index 3e39688..4d9abdc 100644 --- a/src/box/sql/update.c +++ b/src/box/sql/update.c @@ -195,9 +195,9 @@ sqlite3Update(Parse * pParse, /* The parser context */ } /* * The SET expressions are not actually used inside the - * WHERE loop. So reset the colUsed mask. + * WHERE loop. So reset the column_used_mask mask. */ - pTabList->a[0].colUsed = 0; + pTabList->a[0].column_used_mask = 0; hasFK = fkey_is_required(pTab->def->id, aXRef); @@ -325,13 +325,13 @@ sqlite3Update(Parse * pParse, /* The parser context */ if (is_pk_modified || hasFK != 0 || trigger != NULL) { struct space *space = space_by_id(pTab->def->id); assert(space != NULL); - u32 oldmask = hasFK ? space->fkey_mask : 0; + uint64_t oldmask = hasFK != 0 ? space->fkey_mask : 0; oldmask |= sql_trigger_colmask(pParse, trigger, pChanges, 0, TRIGGER_BEFORE | TRIGGER_AFTER, pTab, on_error); for (i = 0; i < (int)def->field_count; i++) { if (oldmask == 0xffffffff - || (i < 32 && (oldmask & MASKBIT32(i)) != 0) + || (i < 32 && (oldmask & COLUMN_MASK_BIT(i)) != 0) || table_column_is_in_pk(pTab, i)) { testcase(oldmask != 0xffffffff && i == 31); sqlite3ExprCodeGetColumnOfTable(v, def, @@ -365,7 +365,7 @@ sqlite3Update(Parse * pParse, /* The parser context */ sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew + i); } else if (0 == (tmask & TRIGGER_BEFORE) || i > 31 - || (newmask & MASKBIT32(i))) { + || (newmask & COLUMN_MASK_BIT(i))) { /* This branch loads the value of a column that will not be changed * into a register. This is done if there are no BEFORE triggers, or * if there are one or more BEFORE triggers that use this value via diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c index 3b0c90c..7569970 100644 --- a/src/box/sql/vdbeaux.c +++ b/src/box/sql/vdbeaux.c @@ -2713,7 +2713,7 @@ sqlite3VdbeDeleteAuxData(sqlite3 * db, AuxData ** pp, int iOp, int mask) AuxData *pAux = *pp; if ((iOp < 0) || (pAux->iOp == iOp - && (pAux->iArg > 31 || !(mask & MASKBIT32(pAux->iArg)))) + && (pAux->iArg > 31 || !(mask & COLUMN_MASK_BIT(pAux->iArg)))) ) { testcase(pAux->iArg == 31); if (pAux->xDelete) { diff --git a/src/box/sql/where.c b/src/box/sql/where.c index a4a1c45..850e2a7 100644 --- a/src/box/sql/where.c +++ b/src/box/sql/where.c @@ -166,62 +166,61 @@ whereOrMove(WhereOrSet * pDest, WhereOrSet * pSrc) memcpy(pDest->a, pSrc->a, pDest->n * sizeof(pDest->a[0])); } -/* - * Try to insert a new prerequisite/cost entry into the WhereOrSet pSet. - * +/** + * Try to insert a new prerequisite/cost entry into the WhereOrSet @set. * The new entry might overwrite an existing entry, or it might be * appended, or it might be discarded. Do whatever is the right thing * so that pSet keeps the N_OR_COST best entries seen so far. + * + * @param set The WhereOrSet to be updated. + * @param prereq_mask Prerequisites of the new entry. + * @param run_cost Run-cost of the new entry. + * @param outputs_cnt Number of outputs for the new entry. + * @retval 1 on new record has been inserted, 0 otherwise. */ static int -whereOrInsert(WhereOrSet * pSet, /* The WhereOrSet to be updated */ - Bitmask prereq, /* Prerequisites of the new entry */ - LogEst rRun, /* Run-cost of the new entry */ - LogEst nOut) /* Number of outputs for the new entry */ +sql_where_or_set_append(struct WhereOrSet *set, uint64_t prereq_mask, + LogEst run_cost, LogEst outputs_cnt) { u16 i; WhereOrCost *p; - for (i = pSet->n, p = pSet->a; i > 0; i--, p++) { - if (rRun <= p->rRun && (prereq & p->prereq) == prereq) { + for (i = set->n, p = set->a; i > 0; i--, p++) { + if (run_cost <= p->rRun && + (prereq_mask & p->prereq_mask) == prereq_mask) goto whereOrInsert_done; - } - if (p->rRun <= rRun && (p->prereq & prereq) == p->prereq) { + if (p->rRun <= run_cost && + (p->prereq_mask & prereq_mask) == p->prereq_mask) return 0; - } } - if (pSet->n < N_OR_COST) { - p = &pSet->a[pSet->n++]; - p->nOut = nOut; + if (set->n < N_OR_COST) { + p = &set->a[set->n++]; + p->nOut = outputs_cnt; } else { - p = pSet->a; - for (i = 1; i < pSet->n; i++) { - if (p->rRun > pSet->a[i].rRun) - p = pSet->a + i; + p = set->a; + for (i = 1; i < set->n; i++) { + if (p->rRun > set->a[i].rRun) + p = set->a + i; } - if (p->rRun <= rRun) + if (p->rRun <= run_cost) return 0; } whereOrInsert_done: - p->prereq = prereq; - p->rRun = rRun; - if (p->nOut > nOut) - p->nOut = nOut; + p->prereq_mask = prereq_mask; + p->rRun = run_cost; + if (p->nOut > outputs_cnt) + p->nOut = outputs_cnt; return 1; } -/* - * Return the bitmask for the given cursor number. Return 0 if - * iCursor is not in the set. - */ -Bitmask -sqlite3WhereGetMask(WhereMaskSet * pMaskSet, int iCursor) + +uint64_t +sql_where_get_mask(struct WhereMaskSet *mask_set, int cursor) { int i; - assert(pMaskSet->n <= (int)sizeof(Bitmask) * 8); - for (i = 0; i < pMaskSet->n; i++) { - if (pMaskSet->ix[i] == iCursor) { - return MASKBIT(i); - } + assert(mask_set->n <= COLUMN_MASK_SIZE); + for (i = 0; i < mask_set->n; i++) { + if (mask_set->ix[i] == cursor) + return COLUMN_MASK_BIT(i); } return 0; } @@ -441,55 +440,24 @@ where_scan_init(struct WhereScan *scan, struct WhereClause *clause, return whereScanNext(scan); } -/* - * Search for a term in the WHERE clause that is of the form "X <op> <expr>" - * where X is a reference to the iColumn of table iCur or of index pIdx - * if pIdx!=0 and <op> is one of the WO_xx operator codes specified by - * the op parameter. Return a pointer to the term. Return 0 if not found. - * - * If pIdx!=0 then it must be one of the indexes of table iCur. - * Search for terms matching the iColumn-th column of pIdx - * rather than the iColumn-th column of table iCur. - * - * The term returned might by Y=<expr> if there is another constraint in - * the WHERE clause that specifies that X=Y. Any such constraints will be - * identified by the WO_EQUIV bit in the pTerm->eOperator field. The - * aiCur[]/iaColumn[] arrays hold X and all its equivalents. There are 11 - * slots in aiCur[]/aiColumn[] so that means we can look for X plus up to 10 - * other equivalent values. Hence a search for X will return <expr> if X=A1 - * and A1=A2 and A2=A3 and ... and A9=A10 and A10=<expr>. - * - * If there are multiple terms in the WHERE clause of the form "X <op> <expr>" - * then try for the one with no dependencies on <expr> - in other words where - * <expr> is a constant expression of some kind. Only return entries of - * the form "X <op> Y" where Y is a column in another table if no terms of - * the form "X <op> <const-expr>" exist. If no terms with a constant RHS - * exist, try to return a term that does not use WO_EQUIV. - */ -WhereTerm * -sqlite3WhereFindTerm(WhereClause * pWC, /* The WHERE clause to be searched */ - int iCur, /* Cursor number of LHS */ - int iColumn, /* Column number of LHS */ - Bitmask notReady, /* RHS must not overlap with this mask */ - u32 op, /* Mask of WO_xx values describing operator */ - Index * pIdx) /* Must be compatible with this index, if not NULL */ +struct WhereTerm * +sql_where_find_term(struct WhereClause *where, int cursor, int column, + uint64_t is_not_ready, u32 op, struct Index *index) { - WhereTerm *pResult = 0; - WhereTerm *p; - WhereScan scan; - - p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx); + struct WhereScan scan; + struct WhereTerm *p = + whereScanInit(&scan, where, cursor, column, op, index); + struct WhereTerm *result = NULL; op &= WO_EQ; - while (p) { - if ((p->prereqRight & notReady) == 0) { - if (p->prereqRight == 0 && (p->eOperator & op) != 0) - return p; - if (pResult == 0) - pResult = p; - } - p = whereScanNext(&scan); + for (; p != NULL; p = whereScanNext(&scan)) { + if ((p->prereq_right_mask & is_not_ready) != 0) + continue; + if (p->prereq_right_mask == 0 && (p->eOperator & op) != 0) + return p; + if (result == NULL) + result = p; } - return pResult; + return result; } /** @@ -511,7 +479,7 @@ sqlite3WhereFindTerm(WhereClause * pWC, /* The WHERE clause to be searched */ */ static inline struct WhereTerm * where_clause_find_term(struct WhereClause *where_clause, int cursor, int column, - Bitmask is_ready, u32 op, struct space_def *space_def, + uint64_t is_ready, u32 op, struct space_def *space_def, struct key_def *key_def) { struct WhereTerm *result = NULL; @@ -520,8 +488,9 @@ where_clause_find_term(struct WhereClause *where_clause, int cursor, int column, column, op, space_def, key_def); op &= WO_EQ; while (p != NULL) { - if ((p->prereqRight & is_ready) == 0) { - if (p->prereqRight == 0 && (p->eOperator & op) != 0) + if ((p->prereq_right_mask & is_ready) == 0) { + if (p->prereq_right_mask == 0 && + (p->eOperator & op) != 0) return p; if (result == NULL) result = p; @@ -618,13 +587,14 @@ isDistinctRedundant(Parse * pParse, /* Parsing context */ continue; int col_count = pIdx->def->key_def->part_count; for (i = 0; i < col_count; i++) { - if (0 == - sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask) 0, - WO_EQ, pIdx)) { - if (findIndexCol - (pParse, pDistinct, iBase, pIdx, i) < 0) + if (sql_where_find_term(pWC, iBase, i, + COLUMN_MASK_FULL, WO_EQ, + pIdx) == NULL) { + if (findIndexCol(pParse, pDistinct, iBase, + pIdx, i) < 0) break; - uint32_t j = pIdx->def->key_def->parts[i].fieldno; + uint32_t j = + pIdx->def->key_def->parts[i].fieldno; if (pIdx->pTable->def->fields[j].is_nullable) break; } @@ -691,7 +661,7 @@ termCanDriveIndex(WhereTerm * pTerm, /* WHERE clause term to check */ return 0; if ((pTerm->eOperator & WO_EQ) == 0) return 0; - if ((pTerm->prereqRight & notReady) != 0) + if ((pTerm->prereq_right_mask & notReady) != 0) return 0; if (pTerm->u.leftColumn < 0) return 0; @@ -1643,8 +1613,8 @@ whereTermPrint(WhereTerm * pTerm, int iTerm) } else if ((pTerm->eOperator & WO_OR) != 0 && pTerm->u.pOrInfo != 0) { sqlite3_snprintf(sizeof(zLeft), zLeft, - "indexable=0x%lld", - pTerm->u.pOrInfo->indexable); + "indexable_mask=0x%lld", + pTerm->u.pOrInfo->indexable_mask); } else { sqlite3_snprintf(sizeof(zLeft), zLeft, "left=%d", pTerm->leftCursor); @@ -1688,10 +1658,11 @@ whereLoopPrint(WhereLoop * p, WhereClause * pWC) int nb = 1 + (pWInfo->pTabList->nSrc + 3) / 4; struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab; Table *pTab = pItem->pTab; - Bitmask mAll = (((Bitmask) 1) << (nb * 4)) - 1; + uint64_t all_mask = COLUMN_MASK_BIT(nb * 4) - 1;; #ifdef SQLITE_DEBUG sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId, - p->iTab, nb, p->maskSelf, nb, p->prereq & mAll); + p->iTab, nb, p->self_mask, nb, + p->prereq_mask & all_mask); sqlite3DebugPrintf(" %12s", pItem->zAlias ? pItem->zAlias : pTab->def->name); #endif @@ -1883,10 +1854,10 @@ whereLoopCheaperProperSubset(const WhereLoop * pX, /* First WhereLoop to compare if (j < 0) return 0; /* X not a subset of Y since term X[i] not used by Y */ } - if ((pX->wsFlags & WHERE_IDX_ONLY) != 0 - && (pY->wsFlags & WHERE_IDX_ONLY) == 0) { - return 0; /* Constraint (5) */ - } + /* Constraint (5). */ + if ((pX->wsFlags & WHERE_IDX_ONLY) != 0 && + (pY->wsFlags & WHERE_IDX_ONLY) == 0) + return 0; return 1; /* All conditions meet */ } @@ -1972,9 +1943,11 @@ whereLoopFindLesser(WhereLoop ** ppPrev, const WhereLoop * pTemplate) assert(p->rSetup == 0 || pTemplate->rSetup == 0 || p->rSetup == pTemplate->rSetup); - /* whereLoopAddBtree() always generates and inserts the automatic index - * case first. Hence compatible candidate WhereLoops never have a larger - * rSetup. Call this SETUP-INVARIANT + /* + * sql_where_loop_add_btree() always generates + * and inserts the automatic index case first. + * Hence compatible candidate WhereLoops never + * have a larger rSetup. Call this SETUP-INVARIANT. */ assert(p->rSetup >= pTemplate->rSetup); @@ -1986,32 +1959,28 @@ whereLoopFindLesser(WhereLoop ** ppPrev, const WhereLoop * pTemplate) && (pTemplate->nSkip) == 0 && (pTemplate->wsFlags & WHERE_INDEXED) != 0 && (pTemplate->wsFlags & WHERE_COLUMN_EQ) != 0 - && (p->prereq & pTemplate->prereq) == pTemplate->prereq) { + && (p->prereq_mask & pTemplate->prereq_mask) == + pTemplate->prereq_mask) break; - } /* If existing WhereLoop p is better than pTemplate, pTemplate can be * discarded. WhereLoop p is better if: * (1) p has no more dependencies than pTemplate, and * (2) p has an equal or lower cost than pTemplate */ - if ((p->prereq & pTemplate->prereq) == p->prereq /* (1) */ - && p->rSetup <= pTemplate->rSetup /* (2a) */ - && p->rRun <= pTemplate->rRun /* (2b) */ - && p->nOut <= pTemplate->nOut /* (2c) */ - ) { - return 0; /* Discard pTemplate */ - } + if ((p->prereq_mask & pTemplate->prereq_mask) == + p->prereq_mask && p->rSetup <= pTemplate->rSetup && + p->rRun <= pTemplate->rRun && p->nOut <= pTemplate->nOut) + return 0; /* If pTemplate is always better than p, then cause p to be overwritten * with pTemplate. pTemplate is better than p if: * (1) pTemplate has no more dependences than p, and * (2) pTemplate has an equal or lower cost than p. */ - if ((p->prereq & pTemplate->prereq) == pTemplate->prereq /* (1) */ - && p->rRun >= pTemplate->rRun /* (2a) */ - && p->nOut >= pTemplate->nOut /* (2b) */ - ) { + if ((p->prereq_mask & pTemplate->prereq_mask) == + pTemplate->prereq_mask && p->rRun >= pTemplate->rRun && + p->nOut >= pTemplate->nOut) { assert(p->rSetup >= pTemplate->rSetup); /* SETUP-INVARIANT above */ break; /* Cause p to be overwritten by pTemplate */ } @@ -2060,9 +2029,10 @@ whereLoopInsert(WhereLoopBuilder * pBuilder, WhereLoop * pTemplate) u16 n = pBuilder->pOrSet->n; int x = #endif - whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, - pTemplate->rRun, - pTemplate->nOut); + sql_where_or_set_append(pBuilder->pOrSet, + pTemplate->prereq_mask, + pTemplate->rRun, + pTemplate->nOut); #ifdef WHERETRACE_ENABLED /* 0x8 */ if (sqlite3WhereTrace & 0x8) { sqlite3DebugPrintf(x ? " or-%d: " : @@ -2181,7 +2151,7 @@ whereLoopOutputAdjust(WhereClause * pWC, /* The WHERE clause */ LogEst nRow) /* Number of rows in the entire table */ { WhereTerm *pTerm, *pX; - Bitmask notAllowed = ~(pLoop->prereq | pLoop->maskSelf); + uint64_t not_allowed_mask = ~(pLoop->prereq_mask | pLoop->self_mask); int i, j, k; LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */ @@ -2189,9 +2159,9 @@ whereLoopOutputAdjust(WhereClause * pWC, /* The WHERE clause */ for (i = pWC->nTerm, pTerm = pWC->a; i > 0; i--, pTerm++) { if ((pTerm->wtFlags & TERM_VIRTUAL) != 0) break; - if ((pTerm->prereqAll & pLoop->maskSelf) == 0) + if ((pTerm->prereq_all_mask & pLoop->self_mask) == 0) continue; - if ((pTerm->prereqAll & notAllowed) != 0) + if ((pTerm->prereq_all_mask & not_allowed_mask) != 0) continue; for (j = pLoop->nLTerm - 1; j >= 0; j--) { pX = pLoop->aLTerm[j]; @@ -2326,7 +2296,6 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder, /* The WhereLoop factory */ WhereTerm *pTerm; /* A WhereTerm under consideration */ int opMask; /* Valid operators for constraints */ WhereScan scan; /* Iterator for WHERE terms */ - Bitmask saved_prereq; /* Original value of pNew->prereq */ u16 saved_nLTerm; /* Original value of pNew->nLTerm */ u16 saved_nEq; /* Original value of pNew->nEq */ u16 saved_nBtm; /* Original value of pNew->nBtm */ @@ -2380,7 +2349,8 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder, /* The WhereLoop factory */ saved_nSkip = pNew->nSkip; saved_nLTerm = pNew->nLTerm; saved_wsFlags = pNew->wsFlags; - saved_prereq = pNew->prereq; + /* Original value of pNew->prereq_mask. */ + uint64_t saved_prereq = pNew->prereq_mask; saved_nOut = pNew->nOut; pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, saved_nEq, opMask, pProbe); @@ -2402,7 +2372,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder, /* The WhereLoop factory */ */ continue; } - if (pTerm->prereqRight & pNew->maskSelf) + if (pTerm->prereq_right_mask & pNew->self_mask) continue; /* Do not allow the upper bound of a LIKE optimization range constraint @@ -2430,8 +2400,8 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder, /* The WhereLoop factory */ if (whereLoopResize(db, pNew, pNew->nLTerm + 1)) break; /* OOM */ pNew->aLTerm[pNew->nLTerm++] = pTerm; - pNew->prereq = - (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf; + pNew->prereq_mask = + (saved_prereq | pTerm->prereq_right_mask) & ~pNew->self_mask; assert(nInMul == 0 || (pNew->wsFlags & WHERE_COLUMN_NULL) != 0 @@ -2648,7 +2618,7 @@ whereLoopAddBtreeIndex(WhereLoopBuilder * pBuilder, /* The WhereLoop factory */ pNew->nOut = saved_nOut; pBuilder->nRecValid = nRecValid; } - pNew->prereq = saved_prereq; + pNew->prereq_mask = saved_prereq; pNew->nEq = saved_nEq; pNew->nBtm = saved_nBtm; pNew->nTop = saved_nTop; @@ -2756,74 +2726,86 @@ indexMightHelpWithOrderBy(WhereLoopBuilder * pBuilder, return 0; } -/* - * Add all WhereLoop objects for a single table of the join where the table - * is identified by pBuilder->pNew->iTab. +/** + * Add all WhereLoop objects for a single table of the join where + * the table is identified by loop_builder->pNew->iTab. * - * The costs (WhereLoop.rRun) of the b-tree loops added by this function - * are calculated as follows: + * The costs (WhereLoop.rRun) of the b-tree loops added by this + * function are calculated as follows: * rRun = log2(cost) * 10 * - * For a full scan, assuming the table (or index) contains nRow rows: + * For a full scan, assuming the table (or index) contains nRow + * rows: * - * cost = nRow * 3.0 // full-table scan - * cost = nRow * K -> 4.0 for Tarantool // scan of covering index - * cost = nRow * (K+3.0) -> 4.0 for Tarantool // scan of non-covering index + * full-table scan: + * cost = nRow * 3.0 + * scan of covering index: + * cost = nRow * K -> 4.0 for Tarantool + * scan of non-covering index: + * cost = nRow * (K+3.0) -> 4.0 for Tarantool * - * This formula forces usage of pk for full-table scan for Tarantool + * This formula forces usage of pk for full-table scan for + * Tarantool. * - * where K is a value between 1.1 and 3.0 set based on the relative - * estimated average size of the index and table records. + * where K is a value between 1.1 and 3.0 set based on the + * relative estimated average size of the index and table records. * - * For an index scan, where nVisit is the number of index rows visited - * by the scan, and nSeek is the number of seek operations required on - * the index b-tree: + * For an index scan, where nVisit is the number of index rows + * visited by the scan, and nSeek is the number of seek operations + * required on the index b-tree: * - * cost = nSeek * (log(nRow) + K * nVisit) // covering index - * cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index + * covering index: + * cost = nSeek * (log(nRow) + K * nVisit) + * non-covering index: + * cost = nSeek * (log(nRow) + (K+3.0) * nVisit) * - * Normally, nSeek is 1. nSeek values greater than 1 come about if the - * WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when - * implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans. + * Normally, nSeek is 1. nSeek values greater than 1 come about + * if the WHERE clause includes "x IN (....)" terms used in place + * of "x=?". Or when implicit "x IN (SELECT x FROM tbl)" terms are + * added for skip-scans. * - * The estimated values (nRow, nVisit, nSeek) often contain a large amount - * of uncertainty. For this reason, scoring is designed to pick plans that - * "do the least harm" if the estimates are inaccurate. For example, a - * log(nRow) factor is omitted from a non-covering index scan in order to - * bias the scoring in favor of using an index, since the worst-case - * performance of using an index is far better than the worst-case performance - * of a full table scan. + * The estimated values (nRow, nVisit, nSeek) often contain a + * large amount of uncertainty. For this reason, scoring is + * designed to pick plans that "do the least harm" if the + * estimates are inaccurate. For example, a log(nRow) factor is + * omitted from a non-covering index scan in order to bias the + * scoring in favor of using an index, since the worst-case + * performance of using an index is far better than the worst-case + * performance of a full table scan. + * + * @param loop_builder WHERE clause information. + * @param prereq_mask Extra prerequesites for using this table. + * @retval 0 On success, not 0 elsewhere. */ static int -whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ - Bitmask mPrereq) /* Extra prerequesites for using this table */ +sql_where_loop_add_btree(struct WhereLoopBuilder *loop_builder, + uint64_t prereq_mask) { - WhereInfo *pWInfo; /* WHERE analysis context */ - Index *pProbe; /* An index we are evaluating */ Index fake_index; /* A fake index object for the primary key */ - SrcList *pTabList; /* The FROM clause */ - struct SrcList_item *pSrc; /* The FROM clause btree term to add */ - WhereLoop *pNew; /* Template WhereLoop object */ int rc = SQLITE_OK; /* Return code */ int iSortIdx = 1; /* Index number */ - int b; /* A boolean value */ LogEst rSize; /* number of rows in the table */ - WhereClause *pWC; /* The parsed WHERE clause */ - Table *pTab; /* Table being queried */ - pNew = pBuilder->pNew; - pWInfo = pBuilder->pWInfo; - pTabList = pWInfo->pTabList; - pSrc = pTabList->a + pNew->iTab; - pTab = pSrc->pTab; - pWC = pBuilder->pWC; - - if (pSrc->pIBIndex) { - /* An INDEXED BY clause specifies a particular index to use */ - pProbe = pSrc->pIBIndex; + /* Template WhereLoop object. */ + struct WhereLoop *new_where_loop = loop_builder->pNew; + /* WHERE analysis context. */ + struct WhereInfo *where_info = loop_builder->pWInfo; + /* The FROM clause. */ + struct SrcList *tab_list = where_info->pTabList; + /* The FROM clause btree term to add. */ + struct SrcList_item *src_list_table = tab_list->a + new_where_loop->iTab; + /* Table being queried. */ + struct Table *table = src_list_table->pTab; + /* The parsed WHERE clause. */ + struct WhereClause *where_clause = loop_builder->pWC; + /* An index we are evaluating. */ + struct Index *index; + if (src_list_table->pIBIndex) { + /* An INDEXED BY clause specifies a particular index to use. */ + index = src_list_table->pIBIndex; fake_index.def = NULL; - } else if (pTab->pIndex) { - pProbe = pTab->pIndex; + } else if (table->pIndex) { + index = table->pIndex; fake_index.def = NULL; } else { /* There is no INDEXED BY clause. Create a fake Index object in local @@ -2833,16 +2815,16 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ */ Index *pFirst; /* First of real indices on the table */ memset(&fake_index, 0, sizeof(Index)); - fake_index.pTable = pTab; + fake_index.pTable = table; struct key_def *key_def = key_def_new(1); if (key_def == NULL) { - pWInfo->pParse->nErr++; - pWInfo->pParse->rc = SQL_TARANTOOL_ERROR; + where_info->pParse->nErr++; + where_info->pParse->rc = SQL_TARANTOOL_ERROR; return SQL_TARANTOOL_ERROR; } - key_def_set_part(key_def, 0, 0, pTab->def->fields[0].type, + key_def_set_part(key_def, 0, 0, table->def->fields[0].type, ON_CONFLICT_ACTION_ABORT, NULL, COLL_NONE, SORT_ORDER_ASC); @@ -2850,7 +2832,7 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ index_opts_create(&opts); opts.sql = "fake_autoindex"; fake_index.def = - index_def_new(pTab->def->id, 0,"fake_autoindex", + index_def_new(table->def->id, 0,"fake_autoindex", sizeof("fake_autoindex") - 1, TREE, &opts, key_def, NULL); key_def_delete(key_def); @@ -2858,8 +2840,8 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ fake_index.def->iid = UINT32_MAX; if (fake_index.def == NULL) { - pWInfo->pParse->nErr++; - pWInfo->pParse->rc = SQL_TARANTOOL_ERROR; + where_info->pParse->nErr++; + where_info->pParse->rc = SQL_TARANTOOL_ERROR; return SQL_TARANTOOL_ERROR; } @@ -2867,18 +2849,18 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ (struct index_stat *) malloc(sizeof(struct index_stat)); stat->tuple_log_est = (log_est_t *) malloc(sizeof(log_est_t) * 2); - stat->tuple_log_est[0] = sql_space_tuple_log_count(pTab); + stat->tuple_log_est[0] = sql_space_tuple_log_count(table); stat->tuple_log_est[1] = 0; fake_index.def->opts.stat = stat; - pFirst = pSrc->pTab->pIndex; - if (pSrc->fg.notIndexed == 0) { + pFirst = src_list_table->pTab->pIndex; + if (src_list_table->fg.notIndexed == 0) { /* The real indices of the table are only considered if the * NOT INDEXED qualifier is omitted from the FROM clause */ fake_index.pNext = pFirst; } - pProbe = &fake_index; + index = &fake_index; } #ifndef SQLITE_OMIT_AUTOMATIC_INDEX @@ -2939,39 +2921,42 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ /* Loop over all indices */ - for (; rc == SQLITE_OK && pProbe; pProbe = pProbe->pNext, iSortIdx++) { - rSize = index_field_tuple_est(pProbe, 0); - pNew->nEq = 0; - pNew->nBtm = 0; - pNew->nTop = 0; - pNew->nSkip = 0; - pNew->nLTerm = 0; - pNew->iSortIdx = 0; - pNew->rSetup = 0; - pNew->prereq = mPrereq; - pNew->nOut = rSize; - pNew->pIndex = pProbe; - b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor); + for (; rc == SQLITE_OK && index != NULL; + index = index->pNext, iSortIdx++) { + rSize = index_field_tuple_est(index, 0); + new_where_loop->nEq = 0; + new_where_loop->nBtm = 0; + new_where_loop->nTop = 0; + new_where_loop->nSkip = 0; + new_where_loop->nLTerm = 0; + new_where_loop->iSortIdx = 0; + new_where_loop->rSetup = 0; + new_where_loop->prereq_mask = prereq_mask; + new_where_loop->nOut = rSize; + new_where_loop->pIndex = index; + int b = indexMightHelpWithOrderBy(loop_builder, index, + src_list_table->iCursor); /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */ - assert((pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED) == 0 - || b == 0); - if (pProbe->def->iid == UINT32_MAX) { + assert((where_info->wctrlFlags & WHERE_ONEPASS_DESIRED) == 0 || + b == 0); + if (index->def->iid == UINT32_MAX) { /* Integer primary key index */ - pNew->wsFlags = WHERE_IPK; + new_where_loop->wsFlags = WHERE_IPK; /* Full table scan */ - pNew->iSortIdx = b ? iSortIdx : 0; + new_where_loop->iSortIdx = b != 0 ? iSortIdx : 0; /* TUNING: Cost of full table scan is (N*3.0). */ - pNew->rRun = rSize + 16; - whereLoopOutputAdjust(pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); - pNew->nOut = rSize; + new_where_loop->rRun = rSize + 16; + whereLoopOutputAdjust(where_clause, new_where_loop, + rSize); + rc = whereLoopInsert(loop_builder, new_where_loop); + new_where_loop->nOut = rSize; if (rc) break; } else { - pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; + new_where_loop->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; /* Full scan via index */ - pNew->iSortIdx = b ? iSortIdx : 0; + new_where_loop->iSortIdx = b != 0 ? iSortIdx : 0; /* The cost of visiting the index rows is N*K, where K is * between 1.1 and 3.0 (3.0 and 4.0 for tarantool), @@ -2982,24 +2967,27 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ * of secondary indexes, because secondary indexes * are not really store any data (only pointers to tuples). */ - int notPkPenalty = sql_index_is_primary(pProbe) ? 0 : 4; - pNew->rRun = rSize + 16 + notPkPenalty; - whereLoopOutputAdjust(pWC, pNew, rSize); - rc = whereLoopInsert(pBuilder, pNew); - pNew->nOut = rSize; + int not_pk_penalty = + sql_index_is_primary(index) ? 0 : 4; + new_where_loop->rRun = rSize + 16 + not_pk_penalty; + whereLoopOutputAdjust(where_clause, new_where_loop, + rSize); + rc = whereLoopInsert(loop_builder, new_where_loop); + new_where_loop->nOut = rSize; if (rc) break; } - rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0); - sqlite3Stat4ProbeFree(pBuilder->pRec); - pBuilder->nRecValid = 0; - pBuilder->pRec = 0; + rc = whereLoopAddBtreeIndex(loop_builder, src_list_table, + index, 0); + sqlite3Stat4ProbeFree(loop_builder->pRec); + loop_builder->nRecValid = 0; + loop_builder->pRec = 0; /* If there was an INDEXED BY clause, then only that one index is * considered. */ - if (pSrc->pIBIndex) + if (src_list_table->pIBIndex != NULL) break; } if (fake_index.def != NULL) @@ -3010,132 +2998,150 @@ whereLoopAddBtree(WhereLoopBuilder * pBuilder, /* WHERE clause information */ return rc; } -/* +/** * Add WhereLoop entries to handle OR terms. + * @param loop_builder WHERE clause information. + * @param prereq_mask Extra prerequesites for using this table. + * @retval 0 On success, not 0 elsewhere. */ static int -whereLoopAddOr(WhereLoopBuilder * pBuilder, Bitmask mPrereq, Bitmask mUnusable) +sql_where_loop_add_or(struct WhereLoopBuilder *loop_builder, + uint64_t prereq_mask) { - WhereInfo *pWInfo = pBuilder->pWInfo; - WhereClause *pWC; - WhereLoop *pNew; - WhereTerm *pTerm, *pWCEnd; int rc = SQLITE_OK; - int iCur; - WhereClause tempWC; - WhereLoopBuilder sSubBuild; - WhereOrSet sSum, sCur; - struct SrcList_item *pItem; - - pWC = pBuilder->pWC; - pWCEnd = pWC->a + pWC->nTerm; - pNew = pBuilder->pNew; - memset(&sSum, 0, sizeof(sSum)); - pItem = pWInfo->pTabList->a + pNew->iTab; - iCur = pItem->iCursor; - - for (pTerm = pWC->a; pTerm < pWCEnd && rc == SQLITE_OK; pTerm++) { - if ((pTerm->eOperator & WO_OR) != 0 - && (pTerm->u.pOrInfo->indexable & pNew->maskSelf) != 0) { - WhereClause *const pOrWC = &pTerm->u.pOrInfo->wc; - WhereTerm *const pOrWCEnd = &pOrWC->a[pOrWC->nTerm]; - WhereTerm *pOrTerm; - int once = 1; - int i, j; - - sSubBuild = *pBuilder; - sSubBuild.pOrderBy = 0; - sSubBuild.pOrSet = &sCur; + struct WhereClause *where_clause = loop_builder->pWC; + struct WhereLoop *where_loop = loop_builder->pNew; + struct WhereOrSet where_costs_set; + memset(&where_costs_set, 0, sizeof(where_costs_set)); + struct WhereInfo *where_info = loop_builder->pWInfo; + struct SrcList_item *src_list_table = + where_info->pTabList->a + where_loop->iTab; + int cursor = src_list_table->iCursor; + struct WhereTerm *where_term = where_clause->a; + struct WhereTerm *where_term_end = + where_clause->a + where_clause->nTerm; + for (; where_term < where_term_end && rc == SQLITE_OK; where_term++) { + if ((where_term->eOperator & WO_OR) == 0 + || (where_term->u.pOrInfo->indexable_mask & + where_loop->self_mask) == 0) + continue; - WHERETRACE(0x200, - ("Begin processing OR-clause %p\n", pTerm)); - for (pOrTerm = pOrWC->a; pOrTerm < pOrWCEnd; pOrTerm++) { - if ((pOrTerm->eOperator & WO_AND) != 0) { - sSubBuild.pWC = - &pOrTerm->u.pAndInfo->wc; - } else if (pOrTerm->leftCursor == iCur) { - tempWC.pWInfo = pWC->pWInfo; - tempWC.pOuter = pWC; - tempWC.op = TK_AND; - tempWC.nTerm = 1; - tempWC.a = pOrTerm; - sSubBuild.pWC = &tempWC; - } else { - continue; - } - sCur.n = 0; + WHERETRACE(0x200, + ("Begin processing OR-clause %p\n", where_term)); + int once = 1; + struct WhereLoopBuilder sub_loop_builder; + sub_loop_builder = *loop_builder; + sub_loop_builder.pOrderBy = 0; + struct WhereOrSet where_or_set; + sub_loop_builder.pOrSet = &where_or_set; + struct WhereClause temp_wc; + + struct WhereClause *const or_wc = &where_term->u.pOrInfo->wc; + struct WhereTerm *or_term = or_wc->a; + struct WhereTerm *const or_term_end = &or_wc->a[or_wc->nTerm]; + for (; or_term < or_term_end; or_term++) { + if ((or_term->eOperator & WO_AND) != 0) { + sub_loop_builder.pWC = + &or_term->u.pAndInfo->wc; + } else if (or_term->leftCursor == cursor) { + temp_wc.pWInfo = where_clause->pWInfo; + temp_wc.pOuter = where_clause; + temp_wc.op = TK_AND; + temp_wc.nTerm = 1; + temp_wc.a = or_term; + sub_loop_builder.pWC = &temp_wc; + } else { + continue; + } + where_or_set.n = 0; #ifdef WHERETRACE_ENABLED - WHERETRACE(0x200, - ("OR-term %d of %p has %d subterms:\n", - (int)(pOrTerm - pOrWC->a), pTerm, - sSubBuild.pWC->nTerm)); - if (sqlite3WhereTrace & 0x400) { - sqlite3WhereClausePrint(sSubBuild.pWC); - } + WHERETRACE(0x200, + ("OR-term %d of %p has %d subterms:\n", + (int)(or_term - or_wc->a), where_term, + sub_loop_builder.pWC->nTerm)); + if (sqlite3WhereTrace & 0x400) { + sqlite3WhereClausePrint(sub_loop_builder. + pWC); + } #endif - { - rc = whereLoopAddBtree(&sSubBuild, - mPrereq); - } - if (rc == SQLITE_OK) { - rc = whereLoopAddOr(&sSubBuild, mPrereq, - mUnusable); - } - assert(rc == SQLITE_OK || sCur.n == 0); - if (sCur.n == 0) { - sSum.n = 0; - break; - } else if (once) { - whereOrMove(&sSum, &sCur); - once = 0; - } else { - WhereOrSet sPrev; - whereOrMove(&sPrev, &sSum); - sSum.n = 0; - for (i = 0; i < sPrev.n; i++) { - for (j = 0; j < sCur.n; j++) { - whereOrInsert(&sSum, - sPrev.a[i].prereq - | sCur.a[j].prereq, - sqlite3LogEstAdd(sPrev.a[i].rRun, - sCur.a[j].rRun), - sqlite3LogEstAdd(sPrev.a[i].nOut, - sCur.a[j].nOut)); - } + rc = sql_where_loop_add_btree(&sub_loop_builder, + prereq_mask); + if (rc == SQLITE_OK) { + rc = sql_where_loop_add_or(&sub_loop_builder, + prereq_mask); + } + assert(rc == SQLITE_OK || where_or_set.n == 0); + if (where_or_set.n == 0) { + where_costs_set.n = 0; + break; + } else if (once) { + whereOrMove(&where_costs_set, + &where_or_set); + once = 0; + } else { + struct WhereOrSet prev_costs_set; + whereOrMove(&prev_costs_set, &where_costs_set); + where_costs_set.n = 0; + for (int i = 0; i < prev_costs_set.n; i++) { + for (int j = 0; j < where_or_set.n; + j++) { + uint64_t prereq = + prev_costs_set.a[i]. + prereq_mask | + where_or_set.a[j]. + prereq_mask; + LogEst run_cost = + sqlite3LogEstAdd(prev_costs_set.a[i].rRun, + where_or_set.a[j].rRun); + LogEst outputs_cnt = + sqlite3LogEstAdd(prev_costs_set.a[i].nOut, + where_or_set.a[j].nOut); + sql_where_or_set_append(&where_costs_set, + prereq, + run_cost, + outputs_cnt); } } } - pNew->nLTerm = 1; - pNew->aLTerm[0] = pTerm; - pNew->wsFlags = WHERE_MULTI_OR; - pNew->rSetup = 0; - pNew->iSortIdx = 0; - pNew->nEq = 0; - pNew->nBtm = 0; - pNew->nTop = 0; - pNew->pIndex = NULL; - for (i = 0; rc == SQLITE_OK && i < sSum.n; i++) { - /* TUNING: Currently sSum.a[i].rRun is set to the sum of the costs - * of all sub-scans required by the OR-scan. However, due to rounding - * errors, it may be that the cost of the OR-scan is equal to its - * most expensive sub-scan. Add the smallest possible penalty - * (equivalent to multiplying the cost by 1.07) to ensure that - * this does not happen. Otherwise, for WHERE clauses such as the - * following where there is an index on "y": - * - * WHERE likelihood(x=?, 0.99) OR y=? - * - * the planner may elect to "OR" together a full-table scan and an - * index lookup. And other similarly odd results. - */ - pNew->rRun = sSum.a[i].rRun + 1; - pNew->nOut = sSum.a[i].nOut; - pNew->prereq = sSum.a[i].prereq; - rc = whereLoopInsert(pBuilder, pNew); - } - WHERETRACE(0x200, - ("End processing OR-clause %p\n", pTerm)); } + where_loop->nLTerm = 1; + where_loop->aLTerm[0] = where_term; + where_loop->wsFlags = WHERE_MULTI_OR; + where_loop->rSetup = 0; + where_loop->iSortIdx = 0; + where_loop->nEq = 0; + where_loop->nBtm = 0; + where_loop->nTop = 0; + where_loop->pIndex = NULL; + for (int i = 0; rc == SQLITE_OK && i < where_costs_set.n; i++) { + /* + * TUNING: + * Currently where_costs_set.a[i].rRun + * is where_or_set to the sum of the costs + * of all sub-scans required by the + * OR-scan. However, due to rounding + * errors, it may be that the cost of the + * OR-scan is equal to its most expensive + * sub-scan. Add the smallest possible + * penalty (equivalent to multiplying the + * cost by 1.07) to ensure that this does + * not happen. Otherwise, for WHERE + * clauses such as the following where + * there is an index on "y": + * + * WHERE likelihood(x=?, 0.99) OR y=? + * + * the planner may elect to "OR" together + * a full-table scan and an index lookup. + * And other similarly odd results. + */ + where_loop->rRun = where_costs_set.a[i].rRun + 1; + where_loop->nOut = where_costs_set.a[i].nOut; + where_loop->prereq_mask = where_costs_set.a[i].prereq_mask; + rc = whereLoopInsert(loop_builder, where_loop); + } + WHERETRACE(0x200, + ("End processing OR-clause %p\n", where_term)); } return rc; } @@ -3147,8 +3153,8 @@ static int whereLoopAddAll(WhereLoopBuilder * pBuilder) { WhereInfo *pWInfo = pBuilder->pWInfo; - Bitmask mPrereq = 0; - Bitmask mPrior = 0; + uint64_t prereq_mask = 0; + uint64_t prior_mask = 0; int iTab; SrcList *pTabList = pWInfo->pTabList; struct SrcList_item *pItem; @@ -3162,25 +3168,23 @@ whereLoopAddAll(WhereLoopBuilder * pBuilder) pNew = pBuilder->pNew; whereLoopInit(pNew); for (iTab = 0, pItem = pTabList->a; pItem < pEnd; iTab++, pItem++) { - Bitmask mUnusable = 0; pNew->iTab = iTab; - pNew->maskSelf = - sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor); + pNew->self_mask = + sql_where_get_mask(&pWInfo->sMaskSet, pItem->iCursor); if (((pItem->fg. jointype | priorJointype) & (JT_LEFT | JT_CROSS)) != 0) { /* This condition is true when pItem is the FROM clause term on the * right-hand-side of a LEFT or CROSS JOIN. */ - mPrereq = mPrior; + prereq_mask = prior_mask; } priorJointype = pItem->fg.jointype; { - rc = whereLoopAddBtree(pBuilder, mPrereq); - } - if (rc == SQLITE_OK) { - rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable); + rc = sql_where_loop_add_btree(pBuilder, prereq_mask); } - mPrior |= pNew->maskSelf; + if (rc == SQLITE_OK) + rc = sql_where_loop_add_or(pBuilder, prereq_mask); + prior_mask |= pNew->self_mask; if (rc || db->mallocFailed) break; } @@ -3189,31 +3193,43 @@ whereLoopAddAll(WhereLoopBuilder * pBuilder) return rc; } -/* - * Examine a WherePath (with the addition of the extra WhereLoop of the 5th - * parameters) to see if it outputs rows in the requested ORDER BY - * (or GROUP BY) without requiring a separate sort operation. Return N: +/** + * Examine a WherePath (with the addition of the extra WhereLoop + * of the 5th parameters) to see if it outputs rows in the + * requested ORDER BY (or GROUP BY) without requiring a separate + * sort operation. * - * N>0: N terms of the ORDER BY clause are satisfied - * N==0: No terms of the ORDER BY clause are satisfied - * N<0: Unknown yet how many terms of ORDER BY might be satisfied. + * Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY + * is not as strict. With GROUP BY and DISTINCT the only + * requirement is that equivalent rows appear immediately adjacent + * to one another. GROUP BY + * and DISTINCT do not require rows to appear in any particular + * order as long as equivalent rows are grouped together. Thus + * for GROUP BY and DISTINCT the pOrderBy terms can be matched + * in any order. With ORDER BY, the pOrderBy terms must be + * matched in strict left-to-right order. + * @param where_info The WHERE clause. + * @param order_expr_list ORDER BY or GROUP BY or DISTINCT clause + * to check. + * @param where_path The WherePath to check. + * @param wctrl_flags WHERE_GROUPBY or _DISTINCTBY or + * _ORDERBY_LIMIT. + * @param loop_cnt Number of entries in pPath->aLoop[]. + * @param new_where_loop Add this WhereLoop to the end of + * pPath->aLoop[]. + * @param rev_mask Mask of WhereLoops to run in reverse order. * - * Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as - * strict. With GROUP BY and DISTINCT the only requirement is that - * equivalent rows appear immediately adjacent to one another. GROUP BY - * and DISTINCT do not require rows to appear in any particular order as long - * as equivalent rows are grouped together. Thus for GROUP BY and DISTINCT - * the pOrderBy terms can be matched in any order. With ORDER BY, the - * pOrderBy terms must be matched in strict left-to-right order. + * @retval N>0: On N terms of the ORDER BY clause are satisfied + * N==0: No terms of the ORDER BY clause are satisfied + * N<0: Unknown yet how many terms of ORDER BY might be + * satisfied. */ static i8 -wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ - ExprList * pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */ - WherePath * pPath, /* The WherePath to check */ - u16 wctrlFlags, /* WHERE_GROUPBY or _DISTINCTBY or _ORDERBY_LIMIT */ - u16 nLoop, /* Number of entries in pPath->aLoop[] */ - WhereLoop * pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */ - Bitmask * pRevMask) /* OUT: Mask of WhereLoops to run in reverse order */ +sql_path_match_order_by(struct WhereInfo *where_info, + struct ExprList *order_expr_list, + struct WherePath *where_path, u16 wctrl_flags, + u16 loop_cnt, struct WhereLoop *new_where_loop, + uint64_t *rev_mask) { u8 revSet; /* True if rev is known */ u8 rev; /* Composite sort order */ @@ -3224,7 +3240,6 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ u16 eqOpMask; /* Allowed equality operators */ u16 nColumn; /* Total number of ordered columns in the index */ u16 nOrderBy; /* Number terms in the ORDER BY clause */ - int iLoop; /* Index of WhereLoop in pPath being processed */ int i, j; /* Loop counters */ int iCur; /* Cursor number for current WhereLoop */ int iColumn; /* A column number within table iCur */ @@ -3232,11 +3247,16 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ WhereTerm *pTerm; /* A single term of the WHERE clause */ Expr *pOBExpr; /* An expression from the ORDER BY clause */ Index *pIndex; /* The index associated with pLoop */ - sqlite3 *db = pWInfo->pParse->db; /* Database connection */ - Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */ - Bitmask obDone; /* Mask of all ORDER BY terms */ - Bitmask orderDistinctMask; /* Mask of all well-ordered loops */ - Bitmask ready; /* Mask of inner loops */ + /* Database connection. */ + struct sqlite3 *db = where_info->pParse->db; + /* Mask of ORDER BY terms satisfied so far. */ + uint64_t ob_sat_mask = 0; + /* Mask of all ORDER BY terms. */ + uint64_t ob_all_mask = 0; + /* Mask of all well-ordered loops. */ + uint64_t ob_dist_mask; + /* Mask of inner loops */ + uint64_t ready_mask; /* * We say the WhereLoop is "one-row" if it generates no more than one @@ -3256,33 +3276,32 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ * To be order-distinct, the columns must be UNIQUE and NOT NULL. */ - assert(pOrderBy != 0); - if (nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin)) + assert(order_expr_list != 0); + if (loop_cnt && OptimizationDisabled(db, SQLITE_OrderByIdxJoin)) return 0; - nOrderBy = pOrderBy->nExpr; - testcase(nOrderBy == BMS - 1); - if (nOrderBy > BMS - 1) + nOrderBy = order_expr_list->nExpr; + if (nOrderBy > COLUMN_MASK_SIZE - 1) return 0; /* Cannot optimize overly large ORDER BYs */ isOrderDistinct = 1; - obDone = MASKBIT(nOrderBy) - 1; - orderDistinctMask = 0; - ready = 0; + ob_all_mask = COLUMN_MASK_BIT(nOrderBy) - 1; + ob_dist_mask = 0; + ready_mask = 0; eqOpMask = WO_EQ | WO_ISNULL; - if (wctrlFlags & WHERE_ORDERBY_LIMIT) + if (wctrl_flags & WHERE_ORDERBY_LIMIT) eqOpMask |= WO_IN; - for (iLoop = 0; isOrderDistinct && obSat < obDone && iLoop <= nLoop; - iLoop++) { - if (iLoop > 0) - ready |= pLoop->maskSelf; - if (iLoop < nLoop) { - pLoop = pPath->aLoop[iLoop]; - if (wctrlFlags & WHERE_ORDERBY_LIMIT) + for (int loops_done = 0; isOrderDistinct && ob_sat_mask < ob_all_mask && loops_done <= loop_cnt; + loops_done++) { + if (loops_done > 0) + ready_mask |= pLoop->self_mask; + if (loops_done < loop_cnt) { + pLoop = where_path->aLoop[loops_done]; + if (wctrl_flags & WHERE_ORDERBY_LIMIT) continue; } else { - pLoop = pLast; + pLoop = new_where_loop; } - iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor; + iCur = where_info->pTabList->a[pLoop->iTab].iCursor; /* Mark off any ORDER BY term X that is a column in the table of * the current loop for which there is term in the WHERE @@ -3290,25 +3309,24 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ * loops. */ for (i = 0; i < nOrderBy; i++) { - if (MASKBIT(i) & obSat) + if (COLUMN_MASK_BIT(i) & ob_sat_mask) continue; - pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr); + pOBExpr = sqlite3ExprSkipCollate(order_expr_list->a[i].pExpr); if (pOBExpr->op != TK_COLUMN) continue; if (pOBExpr->iTable != iCur) continue; - pTerm = - sqlite3WhereFindTerm(&pWInfo->sWC, iCur, - pOBExpr->iColumn, ~ready, - eqOpMask, 0); - if (pTerm == 0) + pTerm = sql_where_find_term(&where_info->sWC, iCur, + pOBExpr->iColumn, ~ready_mask, + eqOpMask, 0); + if (pTerm == NULL) continue; if (pTerm->eOperator == WO_IN) { /* IN terms are only valid for sorting in the ORDER BY LIMIT * optimization, and then only if they are actually used * by the query plan */ - assert(wctrlFlags & WHERE_ORDERBY_LIMIT); + assert(wctrl_flags & WHERE_ORDERBY_LIMIT); for (j = 0; j < pLoop->nLTerm && pTerm != pLoop->aLTerm[j]; j++) { @@ -3321,16 +3339,16 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ struct coll *coll1, *coll2; bool unused; uint32_t id; - coll1 = sql_expr_coll(pWInfo->pParse, - pOrderBy->a[i].pExpr, + coll1 = sql_expr_coll(where_info->pParse, + order_expr_list->a[i].pExpr, &unused, &id); - coll2 = sql_expr_coll(pWInfo->pParse, + coll2 = sql_expr_coll(where_info->pParse, pTerm->pExpr, &unused, &id); if (coll1 != coll2) continue; } - obSat |= MASKBIT(i); + column_mask_set_fieldno(&ob_sat_mask, i); } if ((pLoop->wsFlags & WHERE_ONEROW) == 0) { @@ -3428,13 +3446,11 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ */ isMatch = 0; for (i = 0; bOnce && i < nOrderBy; i++) { - if (MASKBIT(i) & obSat) + if (COLUMN_MASK_BIT(i) & ob_sat_mask) continue; pOBExpr = - sqlite3ExprSkipCollate(pOrderBy-> a[i].pExpr); - testcase(wctrlFlags & WHERE_GROUPBY); - testcase(wctrlFlags & WHERE_DISTINCTBY); - if ((wctrlFlags & (WHERE_GROUPBY | WHERE_DISTINCTBY)) == 0) + sqlite3ExprSkipCollate(order_expr_list-> a[i].pExpr); + if ((wctrl_flags & (WHERE_GROUPBY | WHERE_DISTINCTBY)) == 0) bOnce = 0; if (iColumn >= (-1)) { if (pOBExpr->op != TK_COLUMN) @@ -3450,8 +3466,8 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ bool is_found; uint32_t id; struct coll *coll = - sql_expr_coll(pWInfo->pParse, - pOrderBy->a[i].pExpr, + sql_expr_coll(where_info->pParse, + order_expr_list->a[i].pExpr, &is_found, &id); struct coll *idx_coll = pIndex->def->key_def->parts[j].coll; @@ -3463,26 +3479,25 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ break; } if (isMatch - && (wctrlFlags & WHERE_GROUPBY) == 0) { + && (wctrl_flags & WHERE_GROUPBY) == 0) { /* Make sure the sort order is compatible in an ORDER BY clause. * Sort order is irrelevant for a GROUP BY clause. */ if (revSet) { if ((rev ^ revIdx) != - pOrderBy->a[i].sort_order) + order_expr_list->a[i].sort_order) isMatch = 0; } else { rev = - revIdx ^ pOrderBy->a[i]. + revIdx ^ order_expr_list->a[i]. sort_order; if (rev) - *pRevMask |= - MASKBIT(iLoop); + column_mask_set_fieldno(rev_mask, loops_done); revSet = 1; } } if (isMatch) { - obSat |= MASKBIT(i); + column_mask_set_fieldno(&ob_sat_mask, i); } else { /* No match found */ if (j == 0 || j < nColumn) { @@ -3501,29 +3516,28 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ /* end-if not one-row */ /* Mark off any other ORDER BY terms that reference pLoop */ if (isOrderDistinct) { - orderDistinctMask |= pLoop->maskSelf; + ob_dist_mask |= pLoop->self_mask; for (i = 0; i < nOrderBy; i++) { Expr *p; - Bitmask mTerm; - if (MASKBIT(i) & obSat) + uint64_t mTerm; + if (COLUMN_MASK_BIT(i) & ob_sat_mask) continue; - p = pOrderBy->a[i].pExpr; + p = order_expr_list->a[i].pExpr; mTerm = - sqlite3WhereExprUsage(&pWInfo->sMaskSet, p); + sqlite3WhereExprUsage(&where_info->sMaskSet, p); if (mTerm == 0 && !sqlite3ExprIsConstant(p)) continue; - if ((mTerm & ~orderDistinctMask) == 0) { - obSat |= MASKBIT(i); - } + if ((mTerm & ~ob_dist_mask) == 0) + column_mask_set_fieldno(&ob_sat_mask, i); } } } /* End the loop over all WhereLoops from outer-most down to inner-most */ - if (obSat == obDone) + if (ob_sat_mask == ob_all_mask) return (i8) nOrderBy; if (!isOrderDistinct) { for (i = nOrderBy - 1; i > 0; i--) { - Bitmask m = MASKBIT(i) - 1; - if ((obSat & m) == m) + uint64_t m = COLUMN_MASK_BIT(i) - 1; + if ((ob_sat_mask & m) == m) return i; } return 0; @@ -3739,12 +3753,14 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) LogEst rCost; /* Cost of path (pFrom+pWLoop) */ LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */ i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */ - Bitmask maskNew; /* Mask of src visited by (..) */ - Bitmask revMask = 0; /* Mask of rev-order loops for (..) */ + /* Mask of src visited by (..) */ + uint64_t new_mask; + /* Mask of rev-order loops for (..) */ + uint64_t rev_mask = 0; - if ((pWLoop->prereq & ~pFrom->maskLoop) != 0) + if ((pWLoop->prereq_mask & ~pFrom->loop_mask) != 0) continue; - if ((pWLoop->maskSelf & pFrom->maskLoop) != 0) + if ((pWLoop->self_mask & pFrom->loop_mask) != 0) continue; if ((pWLoop->wsFlags & WHERE_AUTO_INDEX) != 0 && pFrom->nRow < 10) { @@ -3764,18 +3780,20 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted); nOut = pFrom->nRow + pWLoop->nOut; - maskNew = pFrom->maskLoop | pWLoop->maskSelf; + new_mask = pFrom->loop_mask | pWLoop->self_mask; if (isOrdered < 0) { isOrdered = - wherePathSatisfiesOrderBy(pWInfo, - pWInfo->pOrderBy, - pFrom, - pWInfo->wctrlFlags, - iLoop, - pWLoop, - &revMask); + sql_path_match_order_by(pWInfo, + pWInfo-> + pOrderBy, + pFrom, + pWInfo-> + wctrlFlags, + iLoop, + pWLoop, + &rev_mask); } else { - revMask = pFrom->revLoop; + rev_mask = pFrom->rev_loop_mask; } if (isOrdered >= 0 && isOrdered < nOrderBy) { if (aSortCost[isOrdered] == 0) { @@ -3812,7 +3830,7 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) * of legal values for isOrdered, -1..64. */ for (jj = 0, pTo = aTo; jj < nTo; jj++, pTo++) { - if (pTo->maskLoop == maskNew + if (pTo->loop_mask == new_mask && ((pTo->isOrdered ^ isOrdered) & 0x80) == 0) { testcase(jj == nTo - 1); @@ -3917,8 +3935,8 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) #endif } /* pWLoop is a winner. Add it to the set of best so far */ - pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf; - pTo->revLoop = revMask; + pTo->loop_mask = pFrom->loop_mask | pWLoop->self_mask; + pTo->rev_loop_mask = rev_mask; pTo->nRow = nOut; pTo->rCost = rCost; pTo->rUnsorted = rUnsorted; @@ -3958,7 +3976,7 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) 0 ? (pTo->isOrdered + '0') : '?'); if (pTo->isOrdered > 0) { sqlite3DebugPrintf(" rev=0x%llx\n", - pTo->revLoop); + pTo->rev_loop_mask); } else { sqlite3DebugPrintf("\n"); } @@ -3996,67 +4014,58 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) if ((pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) != 0 && (pWInfo->wctrlFlags & WHERE_DISTINCTBY) == 0 && pWInfo->eDistinct == WHERE_DISTINCT_NOOP && nRowEst) { - Bitmask notUsed; - int rc = - wherePathSatisfiesOrderBy(pWInfo, pWInfo->pDistinctSet, - pFrom, - WHERE_DISTINCTBY, nLoop - 1, - pFrom->aLoop[nLoop - 1], - ¬Used); + uint64_t notUsed; + int rc = sql_path_match_order_by(pWInfo, + pWInfo->pDistinctSet, pFrom, + WHERE_DISTINCTBY, nLoop - 1, + pFrom->aLoop[nLoop - 1], + ¬Used); if (rc == pWInfo->pDistinctSet->nExpr) { pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; } } if (pWInfo->pOrderBy) { if (pWInfo->wctrlFlags & WHERE_DISTINCTBY) { - if (pFrom->isOrdered == pWInfo->pOrderBy->nExpr) { + if (pFrom->isOrdered == pWInfo->pOrderBy->nExpr) pWInfo->eDistinct = WHERE_DISTINCT_ORDERED; - } } else { pWInfo->nOBSat = pFrom->isOrdered; - pWInfo->revMask = pFrom->revLoop; - if (pWInfo->nOBSat <= 0) { - pWInfo->nOBSat = 0; - if (nLoop > 0) { - u32 wsFlags = - pFrom->aLoop[nLoop - 1]->wsFlags; - if ((wsFlags & WHERE_ONEROW) == 0 - && (wsFlags & (WHERE_IPK | WHERE_COLUMN_IN)) != (WHERE_IPK | WHERE_COLUMN_IN)) { - Bitmask m = 0; - int rc = - wherePathSatisfiesOrderBy - (pWInfo, pWInfo->pOrderBy, - pFrom, - WHERE_ORDERBY_LIMIT, - nLoop - 1, - pFrom->aLoop[nLoop - 1], - &m); - testcase(wsFlags & WHERE_IPK); - testcase(wsFlags & - WHERE_COLUMN_IN); - if (rc == - pWInfo->pOrderBy->nExpr) { - pWInfo-> - bOrderedInnerLoop = - 1; - pWInfo->revMask = m; - } - } + pWInfo->rev_mask = pFrom->rev_loop_mask; + if (pWInfo->nOBSat <= 0) + pWInfo->nOBSat = 0;; + u32 wsFlags = pFrom->aLoop[nLoop - 1]->wsFlags; + if ((pWInfo->nOBSat <= 0 && nLoop > 0) && + ((wsFlags & WHERE_ONEROW) == 0 && + (wsFlags & (WHERE_IPK | WHERE_COLUMN_IN)) != + (WHERE_IPK | WHERE_COLUMN_IN))) { + uint64_t m = 0; + int rc; + rc = sql_path_match_order_by(pWInfo, + pWInfo->pOrderBy, + pFrom, + WHERE_ORDERBY_LIMIT, + nLoop - 1, + pFrom-> + aLoop[nLoop - 1], + &m); + if (rc == pWInfo->pOrderBy->nExpr) { + pWInfo-> bOrderedInnerLoop = 1; + pWInfo->rev_mask = m; } } } if ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP) && pWInfo->nOBSat == pWInfo->pOrderBy->nExpr && nLoop > 0) { - Bitmask revMask = 0; + uint64_t rev_mask = 0; int nOrder = - wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, - pFrom, 0, nLoop - 1, - pFrom->aLoop[nLoop - 1], - &revMask); + sql_path_match_order_by(pWInfo, pWInfo->pOrderBy, + pFrom, 0, nLoop - 1, + pFrom->aLoop[nLoop - 1], + &rev_mask); assert(pWInfo->sorted == 0); if (nOrder == pWInfo->pOrderBy->nExpr) { pWInfo->sorted = 1; - pWInfo->revMask = revMask; + pWInfo->rev_mask = rev_mask; } } } @@ -4142,8 +4151,8 @@ where_loop_builder_shortcut(struct WhereLoopBuilder *builder) loop->wsFlags = 0; loop->nSkip = 0; loop->pIndex = NULL; - struct WhereTerm *term = sqlite3WhereFindTerm(clause, cursor, -1, 0, - WO_EQ, 0); + struct WhereTerm *term = sql_where_find_term(clause, cursor, -1, 0, + WO_EQ, 0); if (term != NULL) { loop->wsFlags = WHERE_COLUMN_EQ | WHERE_IPK | WHERE_ONEROW; loop->aLTerm[0] = term; @@ -4176,8 +4185,8 @@ where_loop_builder_shortcut(struct WhereLoopBuilder *builder) if (loop->wsFlags) { loop->nOut = (LogEst) 1; where_info->a[0].pWLoop = loop; - loop->maskSelf = sqlite3WhereGetMask(&where_info->sMaskSet, - cursor); + loop->self_mask = sql_where_get_mask(&where_info->sMaskSet, + cursor); where_info->a[0].iTabCur = cursor; where_info->nRowOut = 1; if (where_info->pOrderBy) @@ -4296,7 +4305,6 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ int nTabList; /* Number of elements in pTabList */ WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ - Bitmask notReady; /* Cursors that are not yet positioned */ WhereLoopBuilder sWLB; /* The WhereLoop builder */ WhereMaskSet *pMaskSet; /* The expression mask set */ WhereLevel *pLevel; /* A single level in pWInfo->a[] */ @@ -4329,8 +4337,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ memset(&sWLB, 0, sizeof(sWLB)); /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */ - testcase(pOrderBy && pOrderBy->nExpr == BMS - 1); - if (pOrderBy && pOrderBy->nExpr >= BMS) + if (pOrderBy != 0 && pOrderBy->nExpr >= COLUMN_MASK_SIZE) pOrderBy = 0; sWLB.pOrderBy = pOrderBy; @@ -4342,11 +4349,11 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ } /* The number of tables in the FROM clause is limited by the number of - * bits in a Bitmask + * bits in a uint64_t */ - testcase(pTabList->nSrc == BMS); - if (pTabList->nSrc > BMS) { - sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS); + if (pTabList->nSrc > COLUMN_MASK_SIZE) { + sqlite3ErrorMsg(pParse, "at most %d tables in a join", + COLUMN_MASK_SIZE); return 0; } @@ -4361,7 +4368,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ * return value. A single allocation is used to store the WhereInfo * struct, the contents of WhereInfo.a[], the WhereClause structure * and the WhereMaskSet structure. Since WhereClause contains an 8-byte - * field (type Bitmask) it must be aligned on an 8-byte boundary on + * field (type uint64_t) it must be aligned on an 8-byte boundary on * some architectures. Hence the ROUND8() below. */ nByteWInfo = @@ -4446,9 +4453,9 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ } #ifdef SQLITE_DEBUG for (ii = 0; ii < pTabList->nSrc; ii++) { - Bitmask m = - sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor); - assert(m == MASKBIT(ii)); + uint64_t m = + sql_where_get_mask(pMaskSet, pTabList->a[ii].iCursor); + assert(m == COLUMN_MASK_BIT(ii)); } #endif @@ -4514,18 +4521,16 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ } } if (pWInfo->pOrderBy == 0 && - (user_session->sql_flags & SQLITE_ReverseOrder) != 0) { - pWInfo->revMask = ALLBITS; - } - if (pParse->nErr || NEVER(db->mallocFailed)) { + (user_session->sql_flags & SQLITE_ReverseOrder) != 0) + pWInfo->rev_mask = COLUMN_MASK_FULL; + if (pParse->nErr != 0 || NEVER(db->mallocFailed != 0)) goto whereBeginError; - } #ifdef WHERETRACE_ENABLED if (sqlite3WhereTrace) { sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut); if (pWInfo->nOBSat > 0) { sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, - pWInfo->revMask); + pWInfo->rev_mask); } switch (pWInfo->eDistinct) { case WHERE_DISTINCT_UNIQUE:{ @@ -4551,10 +4556,10 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ if (pWInfo->nLevel >= 2 && pDistinctSet != 0 && OptimizationEnabled(db, SQLITE_OmitNoopJoin) ) { - Bitmask tabUsed = + uint64_t tab_used_mask = sqlite3WhereExprListUsage(pMaskSet, pDistinctSet); if (sWLB.pOrderBy) { - tabUsed |= + tab_used_mask |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy); } while (pWInfo->nLevel >= 2) { @@ -4567,11 +4572,11 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ && (pLoop->wsFlags & WHERE_ONEROW) == 0) { break; } - if ((tabUsed & pLoop->maskSelf) != 0) + if ((tab_used_mask & pLoop->self_mask) != 0) break; pEnd = sWLB.pWC->a + sWLB.pWC->nTerm; for (pTerm = sWLB.pWC->a; pTerm < pEnd; pTerm++) { - if ((pTerm->prereqAll & pLoop->maskSelf) != 0 + if ((pTerm->prereq_all_mask & pLoop->self_mask) != 0 && !ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) { @@ -4621,15 +4626,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ }; sqlite3OpenTable(pParse, pTabItem->iCursor, pTab, op); assert(pTabItem->iCursor == pLevel->iTabCur); - testcase(pWInfo->eOnePass == ONEPASS_OFF - && pTab->nCol == BMS - 1); - testcase(pWInfo->eOnePass == ONEPASS_OFF - && pTab->nCol == BMS); sqlite3VdbeChangeP5(v, bFordelete); #ifdef SQLITE_ENABLE_COLUMN_USED_MASK sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0, - (const u8 *)&pTabItem->colUsed, + (const u8 *) + &pTabItem->column_used_mask, P4_INT64); #endif } @@ -4727,7 +4729,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ VdbeComment((v, "%s", idx_def->name)); #ifdef SQLITE_ENABLE_COLUMN_USED_MASK { - u64 colUsed = 0; + u64 column_used_mask = 0; int ii, jj; for (ii = 0; ii < pIx->nColumn; ii++) { jj = pIx->aiColumn[ii]; @@ -4736,16 +4738,16 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ if (jj > 63) jj = 63; if ((pTabItem-> - colUsed & MASKBIT(jj)) == + column_used_mask & COLUMN_MASK_BIT(jj)) == 0) continue; - colUsed |= + column_used_mask |= ((u64) 1) << (ii < 63 ? ii : 63); } sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, iIndexCur, 0, 0, - (u8 *) & colUsed, + (u8 *) & column_used_mask, P4_INT64); } #endif /* SQLITE_ENABLE_COLUMN_USED_MASK */ @@ -4760,7 +4762,8 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ * loop below generates code for a single nested loop of the VM * program. */ - notReady = ~(Bitmask) 0; + /* Cursors that are not yet positioned. */ + uint64_t not_ready_mask = COLUMN_MASK_FULL; for (ii = 0; ii < nTabList; ii++) { int addrExplain; int wsFlags; @@ -4770,7 +4773,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ if ((pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX) != 0) { constructAutomaticIndex(pParse, &pWInfo->sWC, &pTabList->a[pLevel->iFrom], - notReady, pLevel); + not_ready_mask, pLevel); if (db->mallocFailed) goto whereBeginError; } @@ -4779,7 +4782,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ sqlite3WhereExplainOneScan(pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags); pLevel->addrBody = sqlite3VdbeCurrentAddr(v); - notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady); + not_ready_mask = sql_where_code_one_loop(pWInfo, ii, not_ready_mask); pWInfo->iContinue = pLevel->addrCont; if ((wsFlags & WHERE_MULTI_OR) == 0 && (wctrlFlags & WHERE_OR_SUBCLAUSE) == 0) { diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h index 889a667..eed1482 100644 --- a/src/box/sql/whereInt.h +++ b/src/box/sql/whereInt.h @@ -105,7 +105,8 @@ struct WhereLevel { Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */ } u; struct WhereLoop *pWLoop; /* The selected WhereLoop object */ - Bitmask notReady; /* FROM entries not usable at this level */ + /** FROM entries not usable at this level. */ + uint64_t not_ready_mask; #ifdef SQLITE_ENABLE_STMT_SCANSTATUS int addrVisit; /* Address at which row is visited */ #endif @@ -126,8 +127,10 @@ struct WhereLevel { * and that minimize the overall cost. */ struct WhereLoop { - Bitmask prereq; /* Bitmask of other loops that must run first */ - Bitmask maskSelf; /* Bitmask identifying table iTab */ + /** Bitmask of other loops that must run first. */ + uint64_t prereq_mask; + /** Bitmask identifying table iTab. */ + uint64_t self_mask; #ifdef SQLITE_DEBUG char cId; /* Symbolic ID of this loop for debugging use */ #endif @@ -159,7 +162,8 @@ struct WhereLoop { * See WhereOrSet for additional information */ struct WhereOrCost { - Bitmask prereq; /* Prerequisites */ + /** Prerequisites mask. */ + uint64_t prereq_mask; LogEst rRun; /* Cost of running this subquery */ LogEst nOut; /* Number of outputs for this subquery */ }; @@ -193,8 +197,10 @@ struct WhereOrSet { * at the end is the chosen query plan. */ struct WherePath { - Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */ - Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */ + /** Bitmask of all WhereLoop objects in this path. */ + uint64_t loop_mask; + /** aLoop[]s that should be reversed for ORDER BY. */ + uint64_t rev_loop_mask; LogEst nRow; /* Estimated number of rows generated by this path */ LogEst rCost; /* Total cost of this path */ LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */ @@ -203,12 +209,14 @@ struct WherePath { }; /* - * The query generator uses an array of instances of this structure to - * help it analyze the subexpressions of the WHERE clause. Each WHERE - * clause subexpression is separated from the others by AND operators, - * usually, or sometimes subexpressions separated by OR. + * The query generator uses an array of instances of this + * structure to help it analyze the subexpressions of the WHERE + * clause. Each WHERE clause subexpression is separated from the + * others by AND operators, usually, or sometimes subexpressions + * separated by OR. * - * All WhereTerms are collected into a single WhereClause structure. + * All WhereTerms are collected into a single WhereClause + * structure. * The following identity holds: * * WhereTerm.pWC->a[WhereTerm.idx] == WhereTerm @@ -218,11 +226,12 @@ struct WherePath { * X <op> <expr> * * where X is a column name and <op> is one of certain operators, - * then WhereTerm.leftCursor and WhereTerm.u.leftColumn record the - * cursor number and column number for X. WhereTerm.eOperator records - * the <op> using a bitmask encoding defined by WO_xxx below. The - * use of a bitmask encoding for the operator allows us to search - * quickly for terms that match any of several different operators. + * then WhereTerm.leftCursor and WhereTerm.u.leftColumn record + * the cursor number and column number for X. WhereTerm.eOperator + * records the <op> using a bitmask encoding defined by + * WO_xxx below. The use of a bitmask encoding for the operator + * allows us to search quickly for terms that match any of several + * different operators. * * A WhereTerm might also be two or more subterms connected by OR: * @@ -237,20 +246,20 @@ struct WherePath { * to the original subexpression content and wtFlags is set up appropriately * but no other fields in the WhereTerm object are meaningful. * - * When eOperator!=0, prereqRight and prereqAll record sets of cursor numbers, + * When eOperator!=0, prereq_right_mask and prereq_all_mask record sets of cursor numbers, * but they do so indirectly. A single WhereMaskSet structure translates - * cursor number into bits and the translated bit is stored in the prereq + * cursor number into bits and the translated bit is stored in the prereq_mask * fields. The translation is used in order to maximize the number of - * bits that will fit in a Bitmask. The VDBE cursor numbers might be + * bits that will fit in a uint64_t. The VDBE cursor numbers might be * spread out over the non-negative integers. For example, the cursor * numbers might be 3, 8, 9, 10, 20, 23, 41, and 45. The WhereMaskSet * translates these sparse cursor numbers into consecutive integers * beginning with 0 in order to make the best possible use of the available - * bits in the Bitmask. So, in the example above, the cursor numbers + * bits in the uint64_t. So, in the example above, the cursor numbers * would be mapped into integers 0 through 7. * * The number of terms in a join is limited by the number of bits - * in prereqRight and prereqAll. The default is 64 bits, hence SQLite + * in prereq_right_mask and prereq_all_mask. The default is 64 bits, hence SQLite * is only able to process joins with 64 or fewer tables. */ struct WhereTerm { @@ -268,8 +277,10 @@ struct WhereTerm { WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */ WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */ } u; - Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */ - Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */ + /** Bitmask of tables used by pExpr->pRight. */ + uint64_t prereq_right_mask; + /** Bitmask of tables referenced by pExpr. */ + uint64_t prereq_all_mask; }; /* @@ -340,7 +351,8 @@ struct WhereClause { */ struct WhereOrInfo { WhereClause wc; /* Decomposition into subterms */ - Bitmask indexable; /* Bitmask of all indexable tables in the clause */ + /** Bitmask of all indexable_mask tables in the clause. */ + uint64_t indexable_mask; }; /* @@ -379,7 +391,8 @@ struct WhereAndInfo { */ struct WhereMaskSet { int n; /* Number of assigned cursor values */ - int ix[BMS]; /* Cursor assigned to each bit */ + /** Cursor assigned to each bit. */ + int ix[COLUMN_MASK_SIZE]; }; /* @@ -431,29 +444,67 @@ struct WhereInfo { u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */ int iTop; /* The very beginning of the WHERE loop */ WhereLoop *pLoops; /* List of all WhereLoop objects */ - Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ + /** Mask of ORDER BY terms that need reversing. */ + uint64_t rev_mask; LogEst nRowOut; /* Estimated number of output rows */ WhereClause sWC; /* Decomposition of the WHERE clause */ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ WhereLevel a[1]; /* Information about each nest loop in WHERE */ }; -/* - * Private interfaces - callable only by other where.c routines. - * - * where.c: +/** + * Return the bitmask for the given cursor number. + * @param mask_set Where masks assigned for cursors. + * @param cursor Cursor to get mask if any. + * @retval Return 0 if iCursor is not in the set. */ -Bitmask sqlite3WhereGetMask(WhereMaskSet *, int); +uint64_t +sql_where_get_mask(struct WhereMaskSet *mask_set, int cursor); + #ifdef WHERETRACE_ENABLED void sqlite3WhereClausePrint(WhereClause * pWC); #endif -WhereTerm *sqlite3WhereFindTerm(WhereClause * pWC, /* The WHERE clause to be searched */ - int iCur, /* Cursor number of LHS */ - int iColumn, /* Column number of LHS */ - Bitmask notReady, /* RHS must not overlap with this mask */ - u32 op, /* Mask of WO_xx values describing operator */ - Index * pIdx /* Must be compatible with this index, if not NULL */ - ); + +/** + * Search for a term in the WHERE clause that is of the form + * "X <op> <expr>" where X is a reference to the @column of table + * @cursor or of @index if @index != NULL and <op> is one of the + * WO_xx operator codes specified by the op parameter. Return a + * pointer to the term. Return 0 if not found. + * + * If @index != NULL then it must be one of the indexes of table + * @cursor. Search for terms matching the @column-th column of + * @index rather than the @column-th column of table @cursor. + * + * The term returned might by Y=<expr> if there is another + * constraint in the WHERE clause that specifies that X=Y. Any + * such constraints will be identified by the WO_EQUIV bit in the + * pTerm->eOperator field. The aiCur[]/iaColumn[] arrays hold X + * and all its equivalents. There are 11 slots in + * aiCur[]/aiColumn[] so that means we can look for X plus up to + * 10 other equivalent values. Hence a search for X will return + * <expr> if X=A1 and A1=A2 and A2=A3 and ... and A9=A10 and + * A10=<expr>. + * + * If there are multiple terms in the WHERE clause of the form + * "X <op> <expr>" then try for the one with no dependencies on + * <expr> - in other words where <expr> is a constant expression + * of some kind. Only return entries of the form "X <op> Y" + * where Y is a column in another table if no terms of the form + * "X <op> <const-expr>" exist. If no terms with a constant RHS + * exist, try to return a term that does not use WO_EQUIV. + * + * @param where The WHERE clause to be searched. + * @param cursor Cursor number of LHS. + * @param column Column number of LHS. + * @param is_not_ready RHS must not overlap with this mask. + * @param op Mask of WO_xx values describing operator. + * @param index Must be compatible with this index, if not NULL. + * @retval not NULL WhereTerm pointer on found, NULL otherwise. + */ +struct WhereTerm * +sql_where_find_term(struct WhereClause *where, int cursor, int column, + uint64_t is_not_ready, u32 op, struct Index *index); /* wherecode.c: */ int sqlite3WhereExplainOneScan(Parse * pParse, /* Parse context */ @@ -472,22 +523,30 @@ void sqlite3WhereAddScanStatus(Vdbe * v, /* Vdbe to add scanstatus entry to */ #else #define sqlite3WhereAddScanStatus(a, b, c, d) ((void)d) #endif -Bitmask sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the WHERE clause */ - int iLevel, /* Which level of pWInfo->a[] should be coded */ - Bitmask notReady /* Which tables are currently available */ - ); + +/** + * Generate code for the start of the iLevel-th loop in the WHERE clause + * implementation described by pWInfo. + * + * @param where_info Complete information about the WHERE clause. + * @param level Which level of pWInfo->a[] should be coded. + * @param not_ready_mask Which tables are currently available. + */ +uint64_t +sql_where_code_one_loop(struct WhereInfo *where_info, int level, + uint64_t not_ready_mask); /* whereexpr.c: */ void sqlite3WhereClauseInit(WhereClause *, WhereInfo *); void sqlite3WhereClauseClear(WhereClause *); void sqlite3WhereSplit(WhereClause *, Expr *, u8); -Bitmask sqlite3WhereExprUsage(WhereMaskSet *, Expr *); -Bitmask sqlite3WhereExprListUsage(WhereMaskSet *, ExprList *); +uint64_t sqlite3WhereExprUsage(WhereMaskSet *, Expr *); +uint64_t sqlite3WhereExprListUsage(WhereMaskSet *, ExprList *); void sqlite3WhereExprAnalyze(SrcList *, WhereClause *); void sqlite3WhereTabFuncArgs(Parse *, struct SrcList_item *, WhereClause *); /* - * Bitmasks for the operators on WhereTerm objects. These are all + * uint64_ts for the operators on WhereTerm objects. These are all * operators that are of interest to the query planner. An * OR-ed combination of these values can be used when searching for * particular WhereTerms within a WhereClause. diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index 13a045c..0e0ef89 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -360,7 +360,7 @@ disableTerm(WhereLevel * pLevel, WhereTerm * pTerm) && (pTerm->wtFlags & TERM_CODED) == 0 && (pLevel->iLeftJoin == 0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin)) - && (pLevel->notReady & pTerm->prereqAll) == 0) { + && (pLevel->not_ready_mask & pTerm->prereq_all_mask) == 0) { if (nLoop && (pTerm->wtFlags & TERM_LIKE) != 0) { pTerm->wtFlags |= TERM_LIKECOND; } else { @@ -883,46 +883,43 @@ codeExprOrVector(Parse * pParse, Expr * p, int iReg, int nReg) } } -/* - * Generate code for the start of the iLevel-th loop in the WHERE clause - * implementation described by pWInfo. - */ -Bitmask -sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the WHERE clause */ - int iLevel, /* Which level of pWInfo->a[] should be coded */ - Bitmask notReady) /* Which tables are currently available */ + +uint64_t +sql_where_code_one_loop(struct WhereInfo *where_info, int level, + uint64_t not_ready_mask) { int j, k; /* Loop counters */ - int iCur; /* The VDBE cursor for the table */ int addrNxt; /* Where to jump to continue with the next IN case */ int omitTable; /* True if we use the index only */ - int bRev; /* True if we need to scan in reverse order */ - WhereLevel *pLevel; /* The where level to be coded */ - WhereLoop *pLoop; /* The WhereLoop object being coded */ - WhereClause *pWC; /* Decomposition of the entire WHERE clause */ WhereTerm *pTerm; /* A WHERE clause term */ - Parse *pParse; /* Parsing context */ - sqlite3 *db; /* Database connection */ - Vdbe *v; /* The prepared stmt under constructions */ - struct SrcList_item *pTabItem; /* FROM clause term being coded */ int addrBrk; /* Jump here to break out of the loop */ int addrCont; /* Jump here to continue with next cycle */ - pParse = pWInfo->pParse; - v = pParse->pVdbe; - pWC = &pWInfo->sWC; - db = pParse->db; - pLevel = &pWInfo->a[iLevel]; - pLoop = pLevel->pWLoop; - pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; - iCur = pTabItem->iCursor; - pLevel->notReady = - notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); - bRev = (pWInfo->revMask >> iLevel) & 1; - omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY) != 0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) == 0; - VdbeModuleComment((v, "Begin WHERE-loop%d: %s", iLevel, - pTabItem->pTab->zName)); + /* Parsing context. */ + struct Parse *parser = where_info->pParse; + /* The prepared stmt under constructions. */ + struct Vdbe *v = parser->pVdbe; + /* Decomposition of the entire WHERE clause. */ + struct WhereClause *where_clause = &where_info->sWC; + /* Database connection. */ + struct sqlite3 *db = parser->db; + /* The where level to be coded. */ + struct WhereLevel *where_level = &where_info->a[level]; + /* The WhereLoop object being coded. */ + struct WhereLoop *where_loop = where_level->pWLoop; + /* FROM clause term being coded. */ + struct SrcList_item *src_list_table = + &where_info->pTabList->a[where_level->iFrom]; + /* The VDBE cursor for the table. */ + int cursor = src_list_table->iCursor; + where_level->not_ready_mask = + not_ready_mask & ~sql_where_get_mask(&where_info->sMaskSet, cursor); + /* True if we need to scan in reverse order. */ + int is_reversed = (where_info->rev_mask >> level) & 1; + omitTable = (where_loop->wsFlags & WHERE_IDX_ONLY) != 0 && + (where_info->wctrlFlags & WHERE_OR_SUBCLAUSE) == 0; + VdbeModuleComment((v, "Begin WHERE-loop%d: %s", level, + src_list_table->pTab->zName)); /* Create labels for the "break" and "continue" instructions * for the current loop. Jump to addrBrk to break out of a loop. @@ -934,29 +931,33 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * there are no IN operators in the constraints, the "addrNxt" label * is the same as "addrBrk". */ - addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v); - addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v); + addrBrk = where_level->addrBrk = where_level->addrNxt = + sqlite3VdbeMakeLabel(v); + addrCont = where_level->addrCont = sqlite3VdbeMakeLabel(v); /* If this is the right table of a LEFT OUTER JOIN, allocate and * initialize a memory cell that records if this table matches any * row of the left table of the join. */ - if (pLevel->iFrom > 0 && (pTabItem[0].fg.jointype & JT_LEFT) != 0) { - pLevel->iLeftJoin = ++pParse->nMem; - sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin); + if (where_level->iFrom > 0 && + (src_list_table[0].fg.jointype & JT_LEFT) != 0) { + where_level->iLeftJoin = ++parser->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 0, where_level->iLeftJoin); VdbeComment((v, "init LEFT JOIN no-match flag")); } /* Special case of a FROM clause subquery implemented as a co-routine */ - if (pTabItem->fg.viaCoroutine) { - int regYield = pTabItem->regReturn; + if (src_list_table->fg.viaCoroutine) { + int regYield = src_list_table->regReturn; sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, - pTabItem->addrFillSub); - pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); + src_list_table->addrFillSub); + where_level->p2 = + sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk); VdbeCoverage(v); - VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->def->name)); - pLevel->op = OP_Goto; - } else if (pLoop->wsFlags & WHERE_INDEXED) { + VdbeComment((v, "next row of \"%s\"", + src_list_table->pTab->def->name)); + where_level->op = OP_Goto; + } else if (where_loop->wsFlags & WHERE_INDEXED) { /* Case 4: A scan using an index. * * The WHERE clause may contain zero or more equality @@ -991,22 +992,25 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t static const u8 aStartOp[] = { 0, 0, - OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */ - OP_Last, /* 3: (!start_constraints && startEq && bRev) */ - OP_SeekGT, /* 4: (start_constraints && !startEq && !bRev) */ - OP_SeekLT, /* 5: (start_constraints && !startEq && bRev) */ - OP_SeekGE, /* 6: (start_constraints && startEq && !bRev) */ - OP_SeekLE /* 7: (start_constraints && startEq && bRev) */ + OP_Rewind, /* 2: (!start_constraints && startEq && !is_reversed) */ + OP_Last, /* 3: (!start_constraints && startEq && is_reversed) */ + OP_SeekGT, /* 4: (start_constraints && !startEq && !is_reversed) */ + OP_SeekLT, /* 5: (start_constraints && !startEq && is_reversed) */ + OP_SeekGE, /* 6: (start_constraints && startEq && !is_reversed) */ + OP_SeekLE /* 7: (start_constraints && startEq && is_reversed) */ }; static const u8 aEndOp[] = { - OP_IdxGE, /* 0: (end_constraints && !bRev && !endEq) */ - OP_IdxGT, /* 1: (end_constraints && !bRev && endEq) */ - OP_IdxLE, /* 2: (end_constraints && bRev && !endEq) */ - OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */ + OP_IdxGE, /* 0: (end_constraints && !is_reversed && !endEq) */ + OP_IdxGT, /* 1: (end_constraints && !is_reversed && endEq) */ + OP_IdxLE, /* 2: (end_constraints && is_reversed && !endEq) */ + OP_IdxLT, /* 3: (end_constraints && is_reversed && endEq) */ }; - u16 nEq = pLoop->nEq; /* Number of == or IN terms */ - u16 nBtm = pLoop->nBtm; /* Length of BTM vector */ - u16 nTop = pLoop->nTop; /* Length of TOP vector */ + /* Number of == or IN terms. */ + u16 eq_cnt = where_loop->nEq; + /* Length of BTM vector */ + u16 btm_cnt = where_loop->nBtm; + /* Length of TOP vector */ + u16 top_cnt = where_loop->nTop; int regBase; /* Base register holding constraint values */ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */ @@ -1026,31 +1030,31 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * to integer type, used for IPK. */ - struct Index *pIdx = pLoop->pIndex; - struct index_def *idx_def = pLoop->index_def; - assert(pIdx != NULL || idx_def != NULL); - iIdxCur = pLevel->iIdxCur; - assert(nEq >= pLoop->nSkip); + struct Index *index = where_loop->pIndex; + struct index_def *idx_def = where_loop->index_def; + assert(index != NULL || idx_def != NULL); + iIdxCur = where_level->iIdxCur; + assert(eq_cnt >= where_loop->nSkip); /* If this loop satisfies a sort order (pOrderBy) request that * was passed to this function to implement a "SELECT min(x) ..." * query, then the caller will only allow the loop to run for * a single iteration. This means that the first row returned * should not have a NULL value stored in 'x'. If column 'x' is - * the first one after the nEq equality constraints in the index, + * the first one after the eq_cnt equality constraints in the index, * this requires some special handling. */ - assert(pWInfo->pOrderBy == 0 - || pWInfo->pOrderBy->nExpr == 1 - || (pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) == 0); + assert(where_info->pOrderBy == 0 || + where_info->pOrderBy->nExpr == 1 || + (where_info->wctrlFlags & WHERE_ORDERBY_MIN) == 0); uint32_t part_count; - if (pIdx != NULL) - part_count = pIdx->def->key_def->part_count; + if (index != NULL) + part_count = index->def->key_def->part_count; else part_count = idx_def->key_def->part_count; - if ((pWInfo->wctrlFlags & WHERE_ORDERBY_MIN) != 0 && - pWInfo->nOBSat > 0 && part_count > nEq) { - j = pIdx->def->key_def->parts[nEq].fieldno; + if ((where_info->wctrlFlags & WHERE_ORDERBY_MIN) != 0 && + where_info->nOBSat > 0 && part_count > eq_cnt) { + j = index->def->key_def->parts[eq_cnt].fieldno; /* Allow seek for column with `NOT NULL` == false attribute. * If a column may contain NULL-s, the comparator installed * by Tarantool is prepared to seek using a NULL value. @@ -1061,8 +1065,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * FYI: entries in an index are ordered as follows: * NULL, ... NULL, min_value, ... */ - if (pIdx->pTable->def->fields[j].is_nullable) { - assert(pLoop->nSkip == 0); + if (index->pTable->def->fields[j].is_nullable) { + assert(where_loop->nSkip == 0); bSeekPastNull = 1; nExtraReg = 1; } @@ -1071,43 +1075,43 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t /* Find any inequality constraint terms for the start and end * of the range. */ - j = nEq; - if (pLoop->wsFlags & WHERE_BTM_LIMIT) { - pRangeStart = pLoop->aLTerm[j++]; - nExtraReg = MAX(nExtraReg, pLoop->nBtm); + j = eq_cnt; + if (where_loop->wsFlags & WHERE_BTM_LIMIT) { + pRangeStart = where_loop->aLTerm[j++]; + nExtraReg = MAX(nExtraReg, where_loop->nBtm); /* Like optimization range constraints always occur in pairs */ assert((pRangeStart->wtFlags & TERM_LIKEOPT) == 0 || - (pLoop->wsFlags & WHERE_TOP_LIMIT) != 0); + (where_loop->wsFlags & WHERE_TOP_LIMIT) != 0); } - if (pLoop->wsFlags & WHERE_TOP_LIMIT) { - pRangeEnd = pLoop->aLTerm[j++]; - nExtraReg = MAX(nExtraReg, pLoop->nTop); + if (where_loop->wsFlags & WHERE_TOP_LIMIT) { + pRangeEnd = where_loop->aLTerm[j++]; + nExtraReg = MAX(nExtraReg, where_loop->nTop); #ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS if ((pRangeEnd->wtFlags & TERM_LIKEOPT) != 0) { assert(pRangeStart != 0); /* LIKE opt constraints */ assert(pRangeStart->wtFlags & TERM_LIKEOPT); /* occur in pairs */ - pLevel->iLikeRepCntr = (u32)++ pParse->nMem; + where_level->iLikeRepCntr = (u32)++ parser->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 1, - (int)pLevel->iLikeRepCntr); + (int)where_level->iLikeRepCntr); VdbeComment((v, "LIKE loop counter")); - pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v); + where_level->addrLikeRep = sqlite3VdbeCurrentAddr(v); /* iLikeRepCntr actually stores 2x the counter register number. The * bottom bit indicates whether the search order is ASC or DESC. */ - testcase(bRev); - testcase(pIdx->aSortOrder[nEq] == + testcase(index->aSortOrder[eq_cnt] == SORT_ORDER_DESC); - assert((bRev & ~1) == 0); - struct key_def *def = pIdx->def->key_def; - pLevel->iLikeRepCntr <<= 1; - pLevel->iLikeRepCntr |= - bRev ^ (def->parts[nEq].sort_order == - SORT_ORDER_DESC); + assert((is_reversed & ~1) == 0); + struct key_def *def = index->def->key_def; + where_level->iLikeRepCntr <<= 1; + where_level->iLikeRepCntr |= + is_reversed ^ + (def->parts[eq_cnt].sort_order == + SORT_ORDER_DESC); } #endif if (pRangeStart == 0) { - j = pIdx->def->key_def->parts[nEq].fieldno; - if (pIdx->pTable->def->fields[j].is_nullable) + j = index->def->key_def->parts[eq_cnt].fieldno; + if (index->pTable->def->fields[j].is_nullable) bSeekPastNull = 1; } } @@ -1118,12 +1122,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * a forward order scan on a descending index, interchange the * start and end terms (pRangeStart and pRangeEnd). */ - if ((nEq < part_count && - bRev == (pIdx->def->key_def->parts[nEq].sort_order == - SORT_ORDER_ASC)) || (bRev && part_count == nEq)) { + if ((eq_cnt < part_count && is_reversed == + (index->def->key_def->parts[eq_cnt].sort_order == + SORT_ORDER_ASC)) || (is_reversed && part_count == eq_cnt)) { SWAP(pRangeEnd, pRangeStart); SWAP(bSeekPastNull, bStopAtNull); - SWAP(nBtm, nTop); + SWAP(btm_cnt, top_cnt); } /* Generate code to evaluate all constraint terms using == or IN @@ -1131,13 +1135,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * starting at regBase. */ regBase = - codeAllEqualityTerms(pParse, pLevel, bRev, nExtraReg, - &zStartAff); - assert(zStartAff == 0 || sqlite3Strlen30(zStartAff) >= nEq); - if (zStartAff && nTop) { - zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]); - } - addrNxt = pLevel->addrNxt; + codeAllEqualityTerms(parser, where_level, is_reversed, + nExtraReg, &zStartAff); + assert(zStartAff == 0 || sqlite3Strlen30(zStartAff) >= eq_cnt); + if (zStartAff && top_cnt) + zEndAff = sqlite3DbStrDup(db, &zStartAff[eq_cnt]); + addrNxt = where_level->addrNxt; testcase(pRangeStart && (pRangeStart->eOperator & WO_LE) != 0); testcase(pRangeStart && (pRangeStart->eOperator & WO_GE) != 0); @@ -1146,37 +1149,37 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE | WO_GE); endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE | WO_GE); - start_constraints = pRangeStart || nEq > 0; + start_constraints = pRangeStart || eq_cnt > 0; /* Seek the index cursor to the start of the range. */ - nConstraint = nEq; + nConstraint = eq_cnt; if (pRangeStart) { Expr *pRight = pRangeStart->pExpr->pRight; - codeExprOrVector(pParse, pRight, regBase + nEq, nBtm); + codeExprOrVector(parser, pRight, regBase + eq_cnt, btm_cnt); - whereLikeOptimizationStringFixup(v, pLevel, + whereLikeOptimizationStringFixup(v, where_level, pRangeStart); if ((pRangeStart->wtFlags & TERM_VNULL) == 0 && sqlite3ExprCanBeNull(pRight)) { - sqlite3VdbeAddOp2(v, OP_IsNull, regBase + nEq, + sqlite3VdbeAddOp2(v, OP_IsNull, regBase + eq_cnt, addrNxt); VdbeCoverage(v); } if (zStartAff) { - updateRangeAffinityStr(pRight, nBtm, - &zStartAff[nEq]); + updateRangeAffinityStr(pRight, btm_cnt, + &zStartAff[eq_cnt]); } - nConstraint += nBtm; + nConstraint += btm_cnt; testcase(pRangeStart->wtFlags & TERM_VIRTUAL); if (sqlite3ExprIsVector(pRight) == 0) { - disableTerm(pLevel, pRangeStart); + disableTerm(where_level, pRangeStart); } else { startEq = 1; } bSeekPastNull = 0; } else if (bSeekPastNull) { - sqlite3VdbeAddOp2(v, OP_Null, 0, regBase + nEq); + sqlite3VdbeAddOp2(v, OP_Null, 0, regBase + eq_cnt); nConstraint++; startEq = 0; start_constraints = 1; @@ -1184,7 +1187,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t struct Index *pk = NULL; struct index_def *idx_pk = NULL; char affinity; - if (pIdx == NULL) { + if (index == NULL) { struct space *space = space_cache_find(idx_def->space_id); assert(space != NULL); idx_pk = space->index[0]->def; @@ -1199,9 +1202,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t affinity = AFFINITY_BLOB; } } else { - pk = sqlite3PrimaryKeyIndex(pIdx->pTable); + pk = sqlite3PrimaryKeyIndex(index->pTable); uint32_t fieldno = pk->def->key_def->parts[0].fieldno; - affinity = pIdx->pTable->def->fields[fieldno].affinity; + affinity = index->pTable->def->fields[fieldno].affinity; } uint32_t pk_part_count; @@ -1215,10 +1218,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * here: try to loosely convert FLOAT to INT. If RHS type * is not INT or FLOAT - skip this ites, i.e. goto addrNxt. */ - int limit = pRangeStart == NULL ? nEq : nEq + 1; + int limit = pRangeStart == NULL ? eq_cnt : eq_cnt + 1; for (int i = 0; i < limit; i++) { - if ((pIdx != NULL && - pIdx->def->key_def->parts[i].fieldno == + if ((index != NULL && + index->def->key_def->parts[i].fieldno == pk->def->key_def->parts[0].fieldno) || (idx_pk != NULL && idx_def->key_def->parts[i].fieldno == @@ -1226,7 +1229,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t /* Here: we know for sure that table has INTEGER PRIMARY KEY, single column, and Index we're trying to use for scan contains this column. */ - if (i < nEq) + if (i < eq_cnt) sqlite3VdbeAddOp2(v, OP_MustBeInt, regBase + i, addrNxt); else force_integer_reg = regBase + i; @@ -1234,16 +1237,16 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t } } } - codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, + codeApplyAffinity(parser, regBase, nConstraint - bSeekPastNull, zStartAff); - if (pLoop->nSkip > 0 && nConstraint == pLoop->nSkip) { + if (where_loop->nSkip > 0 && nConstraint == where_loop->nSkip) { /* The skip-scan logic inside the call to codeAllEqualityConstraints() * above has already left the cursor sitting on the correct row, * so no further seeking is needed */ } else { op = aStartOp[(start_constraints << 2) + - (startEq << 1) + bRev]; + (startEq << 1) + is_reversed]; assert(op != 0); sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); @@ -1273,35 +1276,36 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t /* Load the value for the inequality constraint at the end of the * range (if any). */ - nConstraint = nEq; + nConstraint = eq_cnt; if (pRangeEnd) { Expr *pRight = pRangeEnd->pExpr->pRight; - sqlite3ExprCacheRemove(pParse, regBase + nEq, 1); - codeExprOrVector(pParse, pRight, regBase + nEq, nTop); - whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd); + sqlite3ExprCacheRemove(parser, regBase + eq_cnt, 1); + codeExprOrVector(parser, pRight, regBase + eq_cnt, top_cnt); + whereLikeOptimizationStringFixup(v, where_level, + pRangeEnd); if ((pRangeEnd->wtFlags & TERM_VNULL) == 0 && sqlite3ExprCanBeNull(pRight)) { - sqlite3VdbeAddOp2(v, OP_IsNull, regBase + nEq, + sqlite3VdbeAddOp2(v, OP_IsNull, regBase + eq_cnt, addrNxt); VdbeCoverage(v); } if (zEndAff) { - updateRangeAffinityStr(pRight, nTop, zEndAff); - codeApplyAffinity(pParse, regBase + nEq, nTop, + updateRangeAffinityStr(pRight, top_cnt, zEndAff); + codeApplyAffinity(parser, regBase + eq_cnt, top_cnt, zEndAff); } else { - assert(pParse->db->mallocFailed); + assert(parser->db->mallocFailed); } - nConstraint += nTop; + nConstraint += top_cnt; testcase(pRangeEnd->wtFlags & TERM_VIRTUAL); if (sqlite3ExprIsVector(pRight) == 0) { - disableTerm(pLevel, pRangeEnd); + disableTerm(where_level, pRangeEnd); } else { endEq = 1; } } else if (bStopAtNull) { - sqlite3VdbeAddOp2(v, OP_Null, 0, regBase + nEq); + sqlite3VdbeAddOp2(v, OP_Null, 0, regBase + eq_cnt); endEq = 0; nConstraint++; } @@ -1309,11 +1313,11 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t sqlite3DbFree(db, zEndAff); /* Top of the loop body */ - pLevel->p2 = sqlite3VdbeCurrentAddr(v); + where_level->p2 = sqlite3VdbeCurrentAddr(v); /* Check if the index cursor is past the end of the range. */ if (nConstraint) { - op = aEndOp[bRev * 2 + endEq]; + op = aEndOp[is_reversed * 2 + endEq]; sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); testcase(op == OP_IdxGT); @@ -1328,40 +1332,44 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t /* Seek the table cursor, if required */ if (omitTable) { - /* pIdx is a covering index. No need to access the main table. */ - } else if (iCur != iIdxCur) { - Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); + /* + * Index is a covering index. + * No need to access the main table. + */ + } else if (cursor != iIdxCur) { + Index *pPk = sqlite3PrimaryKeyIndex(index->pTable); int pk_part_count = pPk->def->key_def->part_count; - int iKeyReg = sqlite3GetTempRange(pParse, pk_part_count); + int iKeyReg = sqlite3GetTempRange(parser, pk_part_count); for (j = 0; j < pk_part_count; j++) { k = pPk->def->key_def->parts[j].fieldno; sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iKeyReg + j); } - sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont, + sqlite3VdbeAddOp4Int(v, OP_NotFound, cursor, addrCont, iKeyReg, pk_part_count); VdbeCoverage(v); - sqlite3ReleaseTempRange(pParse, iKeyReg, pk_part_count); + sqlite3ReleaseTempRange(parser, iKeyReg, pk_part_count); } /* Record the instruction used to terminate the loop. */ - if (pLoop->wsFlags & WHERE_ONEROW) { - pLevel->op = OP_Noop; - } else if (bRev) { - pLevel->op = OP_Prev; + if (where_loop->wsFlags & WHERE_ONEROW) { + where_level->op = OP_Noop; + } else if (is_reversed) { + where_level->op = OP_Prev; } else { - pLevel->op = OP_Next; + where_level->op = OP_Next; } - pLevel->p1 = iIdxCur; - pLevel->p3 = (pLoop->wsFlags & WHERE_UNQ_WANTED) != 0 ? 1 : 0; - if ((pLoop->wsFlags & WHERE_CONSTRAINT) == 0) { - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + where_level->p1 = iIdxCur; + where_level->p3 = + (where_loop->wsFlags & WHERE_UNQ_WANTED) != 0 ? 1 : 0; + if ((where_loop->wsFlags & WHERE_CONSTRAINT) == 0) { + where_level->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } else { - assert(pLevel->p5 == 0); + assert(where_level->p5 == 0); } } else #ifndef SQLITE_OMIT_OR_OPTIMIZATION - if (pLoop->wsFlags & WHERE_MULTI_OR) { + if (where_loop->wsFlags & WHERE_MULTI_OR) { /* Case 5: Two or more separately indexed terms connected by OR * * Example: @@ -1380,56 +1388,59 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t WhereClause *pOrWc; /* The OR-clause broken out into subterms */ SrcList *pOrTab; /* Shortened table list or OR-clause generation */ Index *pCov = 0; /* Potential covering index (or NULL) */ - int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */ + /* Cursor used for index scans (if any). */ + int iCovCur = parser->nTab++; - int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ + /* Register used with OP_Gosub. */ + int return_reg = ++parser->nMem; int regRowset = 0; /* Register for RowSet object */ int regPk = 0; /* Register holding PK */ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ - int iRetInit; /* Address of regReturn init */ + int iRetInit; /* Address of return_reg init */ int untestedTerms = 0; /* Some terms not completely tested */ int ii; /* Loop counter */ u16 wctrlFlags; /* Flags for sub-WHERE clause */ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */ - Table *pTab = pTabItem->pTab; + Table *pTab = src_list_table->pTab; - pTerm = pLoop->aLTerm[0]; + pTerm = where_loop->aLTerm[0]; assert(pTerm != 0); assert(pTerm->eOperator & WO_OR); assert((pTerm->wtFlags & TERM_ORINFO) != 0); pOrWc = &pTerm->u.pOrInfo->wc; - pLevel->op = OP_Return; - pLevel->p1 = regReturn; + where_level->op = OP_Return; + where_level->p1 = return_reg; /* Set up a new SrcList in pOrTab containing the table being scanned - * by this loop in the a[0] slot and all notReady tables in a[1..] slots. + * by this loop in the a[0] slot and all not_ready_mask tables in a[1..] slots. * This becomes the SrcList in the recursive call to sqlite3WhereBegin(). */ - if (pWInfo->nLevel > 1) { - int nNotReady; /* The number of notReady tables */ + if (where_info->nLevel > 1) { + int nNotReady; /* The number of not_ready_mask tables */ struct SrcList_item *origSrc; /* Original list of tables */ - nNotReady = pWInfo->nLevel - iLevel - 1; + nNotReady = where_info->nLevel - level - 1; pOrTab = sqlite3StackAllocRaw(db, sizeof(*pOrTab) + nNotReady * sizeof(pOrTab->a[0])); if (pOrTab == 0) - return notReady; + return not_ready_mask; pOrTab->nAlloc = (u8) (nNotReady + 1); pOrTab->nSrc = pOrTab->nAlloc; - memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); - origSrc = pWInfo->pTabList->a; + memcpy(pOrTab->a, src_list_table, sizeof(*src_list_table)); + origSrc = where_info->pTabList->a; for (k = 1; k <= nNotReady; k++) { - memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], + memcpy(&pOrTab->a[k], + &origSrc[where_level[k].iFrom], sizeof(pOrTab->a[k])); } } else { - pOrTab = pWInfo->pTabList; + pOrTab = where_info->pTabList; } /* Create an ephemeral index capable of holding primary keys. * - * Also initialize regReturn to contain the address of the instruction + * Also initialize return_reg to contain the address of the instruction * immediately following the OP_Return at the bottom of the loop. This * is required in a few obscure LEFT JOIN cases where control jumps * over the top of the loop into the body of it. In this case the @@ -1437,16 +1448,16 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * fall through to the next instruction, just as an OP_Next does if * called on an uninitialized cursor. */ - if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) { + if ((where_info->wctrlFlags & WHERE_DUPLICATES_OK) == 0) { Index *pPk = sqlite3PrimaryKeyIndex(pTab); int pk_part_count = pPk->def->key_def->part_count; - regRowset = pParse->nTab++; + regRowset = parser->nTab++; sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, regRowset, pk_part_count); - sql_vdbe_set_p4_key_def(pParse, pPk); - regPk = ++pParse->nMem; + sql_vdbe_set_p4_key_def(parser, pPk); + regPk = ++parser->nMem; } - iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn); + iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, return_reg); /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y * Then for every term xN, evaluate as the subexpression: xN AND z @@ -1462,29 +1473,29 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * is not contained in the ON clause of a LEFT JOIN. * See ticket http://www.sqlite.org/src/info/f2369304e4 */ - if (pWC->nTerm > 1) { + if (where_clause->nTerm > 1) { int iTerm; - for (iTerm = 0; iTerm < pWC->nTerm; iTerm++) { - Expr *pExpr = pWC->a[iTerm].pExpr; - if (&pWC->a[iTerm] == pTerm) + for (iTerm = 0; iTerm < where_clause->nTerm; iTerm++) { + Expr *pExpr = where_clause->a[iTerm].pExpr; + if (&where_clause->a[iTerm] == pTerm) continue; if (ExprHasProperty(pExpr, EP_FromJoin)) continue; - testcase(pWC->a[iTerm].wtFlags & TERM_VIRTUAL); - testcase(pWC->a[iTerm].wtFlags & TERM_CODED); - if ((pWC->a[iTerm]. + testcase(where_clause->a[iTerm].wtFlags & TERM_VIRTUAL); + testcase(where_clause->a[iTerm].wtFlags & TERM_CODED); + if ((where_clause->a[iTerm]. wtFlags & (TERM_VIRTUAL | TERM_CODED)) != 0) continue; - if ((pWC->a[iTerm].eOperator & WO_ALL) == 0) + if ((where_clause->a[iTerm].eOperator & WO_ALL) == 0) continue; - testcase(pWC->a[iTerm].wtFlags & TERM_ORINFO); + testcase(where_clause->a[iTerm].wtFlags & TERM_ORINFO); pExpr = sqlite3ExprDup(db, pExpr, 0); pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); } if (pAndExpr) { pAndExpr = - sqlite3PExpr(pParse, + sqlite3PExpr(parser, TK_AND | TKFLG_DONTFOLD, 0, pAndExpr); } @@ -1495,11 +1506,11 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * sub-WHERE clause is to to invoke the main loop body as a subroutine. */ wctrlFlags = - WHERE_OR_SUBCLAUSE | (pWInfo-> + WHERE_OR_SUBCLAUSE | (where_info-> wctrlFlags & WHERE_SEEK_TABLE); for (ii = 0; ii < pOrWc->nTerm; ii++) { WhereTerm *pOrTerm = &pOrWc->a[ii]; - if (pOrTerm->leftCursor == iCur + if (pOrTerm->leftCursor == cursor || (pOrTerm->eOperator & WO_AND) != 0) { WhereInfo *pSubWInfo; /* Info for single OR-term scan */ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */ @@ -1513,19 +1524,21 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t WHERETRACE(0xffff, ("Subplan for OR-clause:\n")); pSubWInfo = - sqlite3WhereBegin(pParse, pOrTab, pOrExpr, + sqlite3WhereBegin(parser, pOrTab, pOrExpr, 0, 0, wctrlFlags, iCovCur); - assert(pSubWInfo || pParse->nErr + assert(pSubWInfo || parser->nErr || db->mallocFailed); if (pSubWInfo) { WhereLoop *pSubLoop; int addrExplain = - sqlite3WhereExplainOneScan(pParse, + sqlite3WhereExplainOneScan(parser, pOrTab, - &pSubWInfo->a[0], - iLevel, - pLevel->iFrom, + &pSubWInfo-> + a[0], + level, + where_level-> + iFrom, 0); sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], @@ -1536,7 +1549,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * PRIMARY KEY for the current row so that the same * row will be skipped in subsequent sub-WHERE clauses. */ - if ((pWInfo-> + if ((where_info-> wctrlFlags & WHERE_DUPLICATES_OK) == 0) { int r; @@ -1547,7 +1560,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t pPk->def->key_def; /* Read the PK into an array of temp registers. */ - r = sqlite3GetTempRange(pParse, + r = sqlite3GetTempRange(parser, def->part_count); for (uint32_t iPk = 0; iPk < def->part_count; @@ -1556,10 +1569,10 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t def->parts[iPk]. fieldno; sqlite3ExprCodeGetColumnToReg - (pParse, + (parser, pTab->def, fieldno, - iCur, + cursor, r + iPk); } @@ -1593,12 +1606,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t } /* Release the array of temp registers */ - sqlite3ReleaseTempRange(pParse, r, def->part_count); + sqlite3ReleaseTempRange(parser, r, def->part_count); } /* Invoke the main loop body as a subroutine */ sqlite3VdbeAddOp2(v, OP_Gosub, - regReturn, iLoopBody); + return_reg, iLoopBody); /* Jump here (skipping the main loop body subroutine) if the * current sub-WHERE row is a duplicate from prior sub-WHEREs. @@ -1607,8 +1620,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t sqlite3VdbeJumpHere(v, jmp1); /* The pSubWInfo->untestedTerms flag means that this OR term - * contained one or more AND term from a notReady table. The - * terms from the notReady table could not be tested and will + * contained one or more AND term from a not_ready_mask table. The + * terms from the not_ready_mask table could not be tested and will * need to be tested later. */ if (pSubWInfo->untestedTerms) @@ -1643,21 +1656,21 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t } } } - pLevel->u.pCovidx = pCov; + where_level->u.pCovidx = pCov; if (pCov) - pLevel->iIdxCur = iCovCur; + where_level->iIdxCur = iCovCur; if (pAndExpr) { pAndExpr->pLeft = 0; sql_expr_delete(db, pAndExpr, false); } sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); - sqlite3VdbeGoto(v, pLevel->addrBrk); + sqlite3VdbeGoto(v, where_level->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); - if (pWInfo->nLevel > 1) + if (where_info->nLevel > 1) sqlite3StackFree(db, pOrTab); if (!untestedTerms) - disableTerm(pLevel, pTerm); + disableTerm(where_level, pTerm); } else #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -1667,21 +1680,21 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t */ static const u8 aStep[] = { OP_Next, OP_Prev }; static const u8 aStart[] = { OP_Rewind, OP_Last }; - assert(bRev == 0 || bRev == 1); - if (pTabItem->fg.isRecursive) { + assert(is_reversed == 0 || is_reversed == 1); + if (src_list_table->fg.isRecursive) { /* Tables marked isRecursive have only a single row that is stored in * a pseudo-cursor. No need to Rewind or Next such cursors. */ - pLevel->op = OP_Noop; + where_level->op = OP_Noop; } else { - pLevel->op = aStep[bRev]; - pLevel->p1 = iCur; - pLevel->p2 = - 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, - addrBrk); - VdbeCoverageIf(v, bRev == 0); - VdbeCoverageIf(v, bRev != 0); - pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; + where_level->op = aStep[is_reversed]; + where_level->p1 = cursor; + where_level->p2 = + 1 + sqlite3VdbeAddOp2(v, aStart[is_reversed], + cursor, addrBrk); + VdbeCoverageIf(v, is_reversed == 0); + VdbeCoverageIf(v, is_reversed != 0); + where_level->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP; } } @@ -1692,25 +1705,24 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t /* Insert code to test every subexpression that can be completely * computed using the current set of tables. */ - for (pTerm = pWC->a, j = pWC->nTerm; j > 0; j--, pTerm++) { + for (pTerm = where_clause->a, j = where_clause->nTerm; j > 0; + j--, pTerm++) { Expr *pE; int skipLikeAddr = 0; testcase(pTerm->wtFlags & TERM_VIRTUAL); testcase(pTerm->wtFlags & TERM_CODED); if (pTerm->wtFlags & (TERM_VIRTUAL | TERM_CODED)) continue; - if ((pTerm->prereqAll & pLevel->notReady) != 0) { - testcase(pWInfo->untestedTerms == 0 - && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) != - 0); - pWInfo->untestedTerms = 1; + if ((pTerm->prereq_all_mask & + where_level->not_ready_mask) != 0) { + where_info->untestedTerms = 1; continue; } pE = pTerm->pExpr; assert(pE != 0); - if (pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin)) { + if (where_level->iLeftJoin && + !ExprHasProperty(pE, EP_FromJoin)) continue; - } if (pTerm->wtFlags & TERM_LIKECOND) { /* If the TERM_LIKECOND flag is set, that means that the range search * is sufficient to guarantee that the LIKE operator is true, so we @@ -1721,7 +1733,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t #ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS continue; #else - u32 x = pLevel->iLikeRepCntr; + u32 x = where_level->iLikeRepCntr; assert(x > 0); skipLikeAddr = sqlite3VdbeAddOp1(v, (x & 1) ? OP_IfNot : OP_If, @@ -1729,7 +1741,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t VdbeCoverage(v); #endif } - sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL); + sqlite3ExprIfFalse(parser, pE, addrCont, SQLITE_JUMPIFNULL); if (skipLikeAddr) sqlite3VdbeJumpHere(v, skipLikeAddr); pTerm->wtFlags |= TERM_CODED; @@ -1743,7 +1755,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t * then we cannot use the "t1.a=t2.b" constraint, but we can code * the implied "t1.a=123" constraint. */ - for (pTerm = pWC->a, j = pWC->nTerm; j > 0; j--, pTerm++) { + for (pTerm = where_clause->a, j = where_clause->nTerm; j > 0; + j--, pTerm++) { Expr *pE, sEAlt; WhereTerm *pAlt; if (pTerm->wtFlags & (TERM_VIRTUAL | TERM_CODED)) @@ -1752,16 +1765,17 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t continue; if ((pTerm->eOperator & WO_EQUIV) == 0) continue; - if (pTerm->leftCursor != iCur) + if (pTerm->leftCursor != cursor) continue; - if (pLevel->iLeftJoin) + if (where_level->iLeftJoin) continue; pE = pTerm->pExpr; assert(!ExprHasProperty(pE, EP_FromJoin)); - assert((pTerm->prereqRight & pLevel->notReady) != 0); - pAlt = - sqlite3WhereFindTerm(pWC, iCur, pTerm->u.leftColumn, - notReady, WO_EQ | WO_IN, 0); + assert((pTerm->prereq_right_mask & + where_level->not_ready_mask) != 0); + pAlt = sql_where_find_term(where_clause, cursor, + pTerm->u.leftColumn, not_ready_mask, + WO_EQ | WO_IN, 0); if (pAlt == 0) continue; if (pAlt->wtFlags & (TERM_CODED)) @@ -1771,32 +1785,33 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t VdbeModuleComment((v, "begin transitive constraint")); sEAlt = *pAlt->pExpr; sEAlt.pLeft = pE->pLeft; - sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL); + sqlite3ExprIfFalse(parser, &sEAlt, addrCont, SQLITE_JUMPIFNULL); } /* For a LEFT OUTER JOIN, generate code that will record the fact that * at least one row of the right table has matched the left table. */ - if (pLevel->iLeftJoin) { - pLevel->addrFirst = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin); + if (where_level->iLeftJoin != 0) { + where_level->addrFirst = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_Integer, 1, where_level->iLeftJoin); VdbeComment((v, "record LEFT JOIN hit")); - sqlite3ExprCacheClear(pParse); - for (pTerm = pWC->a, j = 0; j < pWC->nTerm; j++, pTerm++) { + sqlite3ExprCacheClear(parser); + for (pTerm = where_clause->a, j = 0; j < where_clause->nTerm; j++, pTerm++) { testcase(pTerm->wtFlags & TERM_VIRTUAL); testcase(pTerm->wtFlags & TERM_CODED); if (pTerm->wtFlags & (TERM_VIRTUAL | TERM_CODED)) continue; - if ((pTerm->prereqAll & pLevel->notReady) != 0) { - assert(pWInfo->untestedTerms); + if ((pTerm->prereq_all_mask & + where_level->not_ready_mask) != 0) { + assert(where_info->untestedTerms); continue; } assert(pTerm->pExpr); - sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, + sqlite3ExprIfFalse(parser, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; } } - return pLevel->notReady; + return where_level->not_ready_mask; } diff --git a/src/box/sql/whereexpr.c b/src/box/sql/whereexpr.c index 6128686..2b208b1 100644 --- a/src/box/sql/whereexpr.c +++ b/src/box/sql/whereexpr.c @@ -126,7 +126,7 @@ whereClauseInsert(WhereClause * pWC, Expr * p, u16 wtFlags) /* * Return TRUE if the given operator is one of the operators that is - * allowed for an indexable WHERE clause term. The allowed operators are + * allowed for an indexable_mask WHERE clause term. The allowed operators are * "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL" */ static int @@ -481,20 +481,20 @@ whereCombineDisjuncts(SrcList * pSrc, /* the FROM clause */ * * CASE 3: * - * If all subterms are indexable by a single table T, then set + * If all subterms are indexable_mask by a single table T, then set * * WhereTerm.eOperator = WO_OR - * WhereTerm.u.pOrInfo->indexable |= the cursor number for table T + * WhereTerm.u.pOrInfo->indexable_mask |= the cursor number for table T * - * A subterm is "indexable" if it is of the form + * A subterm is "indexable_mask" if it is of the form * "T.C <op> <expr>" where C is any column of table T and * <op> is one of "=", "<", "<=", ">", ">=", "IS NULL", or "IN". - * A subterm is also indexable if it is an AND of two or more - * subsubterms at least one of which is indexable. Indexable AND + * A subterm is also indexable_mask if it is an AND of two or more + * subsubterms at least one of which is indexable_mask. Indexable AND * subterms have their eOperator set to WO_AND and they have * u.pAndInfo set to a dynamically allocated WhereAndTerm object. * - * From another point of view, "indexable" means that the subterm could + * From another point of view, "indexable_mask" means that the subterm could * potentially be used with an index if an appropriate index exists. * This analysis does not consider whether or not the index exists; that * is decided elsewhere. This analysis only looks at whether subterms @@ -505,8 +505,8 @@ whereCombineDisjuncts(SrcList * pSrc, /* the FROM clause */ * always prefer case 1, so in that case we pretend that case 3 is not * satisfied. * - * It might be the case that multiple tables are indexable. For example, - * (E) above is indexable on tables P, Q, and R. + * It might be the case that multiple tables are indexable_mask. For example, + * (E) above is indexable_mask on tables P, Q, and R. * * OTHERWISE: * @@ -528,8 +528,6 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ WhereClause *pOrWc; /* Breakup of pTerm into subterms */ WhereTerm *pOrTerm; /* A Sub-term within the pOrWc */ WhereOrInfo *pOrInfo; /* Additional information associated with pTerm */ - Bitmask chngToIN; /* Tables that might satisfy case 1 */ - Bitmask indexable; /* Tables that are indexable, satisfying case 2 */ /* * Break the OR clause into its separate subterms. The subterms are @@ -555,21 +553,23 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ /* * Compute the set of tables that might satisfy cases 1 or 3. */ - indexable = ~(Bitmask) 0; - chngToIN = ~(Bitmask) 0; - for (i = pOrWc->nTerm - 1, pOrTerm = pOrWc->a; i >= 0 && indexable; + /* Tables that might satisfy case 1. */ + uint64_t chng_to_in_mask = COLUMN_MASK_FULL; + /* Tables that are indexable_mask, satisfying case 2. */ + uint64_t indexable_mask = COLUMN_MASK_FULL; + for (i = pOrWc->nTerm - 1, pOrTerm = pOrWc->a; i >= 0 && indexable_mask; i--, pOrTerm++) { if ((pOrTerm->eOperator & WO_SINGLE) == 0) { WhereAndInfo *pAndInfo; assert((pOrTerm-> wtFlags & (TERM_ANDINFO | TERM_ORINFO)) == 0); - chngToIN = 0; + chng_to_in_mask = 0; pAndInfo = sqlite3DbMallocRawNN(db, sizeof(*pAndInfo)); if (pAndInfo) { WhereClause *pAndWC; WhereTerm *pAndTerm; int j; - Bitmask b = 0; + uint64_t b = 0; pOrTerm->u.pAndInfo = pAndInfo; pOrTerm->wtFlags |= TERM_ANDINFO; pOrTerm->eOperator = WO_AND; @@ -581,42 +581,39 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ TK_AND); sqlite3WhereExprAnalyze(pSrc, pAndWC); pAndWC->pOuter = pWC; - if (!db->mallocFailed) { - for (j = 0, pAndTerm = pAndWC->a; - j < pAndWC->nTerm; - j++, pAndTerm++) { - assert(pAndTerm->pExpr); - if (allowedOp - (pAndTerm->pExpr->op) - || pAndTerm->eOperator == - WO_MATCH) { - b |= sqlite3WhereGetMask - (&pWInfo->sMaskSet, - pAndTerm-> - leftCursor); - } - } + if (db->mallocFailed) + break; + for (j = 0, pAndTerm = pAndWC->a; + j < pAndWC->nTerm; j++, pAndTerm++) { + assert(pAndTerm->pExpr != NULL); + if (!allowedOp(pAndTerm->pExpr->op) && + pAndTerm->eOperator != WO_MATCH) + continue; + b |= sql_where_get_mask(&pWInfo-> + sMaskSet, + pAndTerm-> + leftCursor); } - indexable &= b; + indexable_mask &= b; } } else if (pOrTerm->wtFlags & TERM_COPIED) { /* Skip this term for now. We revisit it when we process the * corresponding TERM_VIRTUAL term */ } else { - Bitmask b; - b = sqlite3WhereGetMask(&pWInfo->sMaskSet, - pOrTerm->leftCursor); + uint64_t b; + b = sql_where_get_mask(&pWInfo->sMaskSet, + pOrTerm->leftCursor); if (pOrTerm->wtFlags & TERM_VIRTUAL) { WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent]; - b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, - pOther->leftCursor); + b |= sql_where_get_mask(&pWInfo->sMaskSet, + pOther->leftCursor); } - indexable &= b; + indexable_mask &= b; if ((pOrTerm->eOperator & WO_EQ) == 0) { - chngToIN = 0; + chng_to_in_mask = 0; } else { - chngToIN &= b; + chng_to_in_mask &= b; } } } @@ -625,12 +622,12 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ * Record the set of tables that satisfy case 3. The set might be * empty. */ - pOrInfo->indexable = indexable; - pTerm->eOperator = indexable == 0 ? 0 : WO_OR; + pOrInfo->indexable_mask = indexable_mask; + pTerm->eOperator = indexable_mask == 0 ? 0 : WO_OR; /* For a two-way OR, attempt to implementation case 2. */ - if (indexable && pOrWc->nTerm == 2) { + if (indexable_mask && pOrWc->nTerm == 2) { int iOne = 0; WhereTerm *pOne; while ((pOne = whereNthSubterm(&pOrWc->a[0], iOne++)) != 0) { @@ -644,11 +641,11 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ } /* - * chngToIN holds a set of tables that *might* satisfy case 1. But + * chng_to_in_mask holds a set of tables that *might* satisfy case 1. But * we have to do some additional checking to see if case 1 really * is satisfied. * - * chngToIN will hold either 0, 1, or 2 bits. The 0-bit case means + * chng_to_in_mask will hold either 0, 1, or 2 bits. The 0-bit case means * that there is no possibility of transforming the OR clause into an * IN operator because one or more terms in the OR clause contain * something other than == on a column in the single table. The 1-bit @@ -664,7 +661,7 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ * Note that terms of the form "table.column1=table.column2" (the * same table on both sizes of the ==) cannot be optimized. */ - if (chngToIN) { + if (chng_to_in_mask != 0) { int okToChngToIN = 0; /* True if the conversion to IN is valid */ int iColumn = -1; /* Column index on lhs of IN operator */ int iCursor = -1; /* Table cursor common to all terms */ @@ -688,14 +685,17 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ assert(j == 1); continue; } - if ((chngToIN & - sqlite3WhereGetMask(&pWInfo->sMaskSet, - pOrTerm-> - leftCursor)) == 0) { - /* This term must be of the form t1.a==t2.b where t2 is in the - * chngToIN set but t1 is not. This term will be either preceded - * or follwed by an inverted copy (t2.b==t1.a). Skip this term - * and use its inversion. + if ((sql_where_get_mask(&pWInfo->sMaskSet, + pOrTerm->leftCursor) & + chng_to_in_mask) ==0) { + /* + * This term must be of the form + * t1.a==t2.b where t2 is in the + * chng_to_in_mask set but t1 is not. + * This term will be either preceded + * or follwed by an inverted copy + * (t2.b==t1.a). Skip this term and + * use its inversion. */ testcase(pOrTerm-> wtFlags & TERM_COPIED); @@ -715,9 +715,9 @@ exprAnalyzeOrTerm(SrcList * pSrc, /* the FROM clause */ * on the second iteration */ assert(j == 1); - assert(IsPowerOfTwo(chngToIN)); - assert(chngToIN == - sqlite3WhereGetMask(&pWInfo->sMaskSet, + assert(IsPowerOfTwo(chng_to_in_mask)); + assert(chng_to_in_mask == + sql_where_get_mask(&pWInfo->sMaskSet, iCursor)); break; } @@ -854,10 +854,10 @@ termIsEquivalence(Parse * pParse, Expr * pExpr) * a bitmask indicating which tables are used in that expression * tree. */ -static Bitmask +static uint64_t exprSelectUsage(WhereMaskSet * pMaskSet, Select * pS) { - Bitmask mask = 0; + uint64_t mask = 0; while (pS) { SrcList *pSrc = pS->pSrc; mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pEList); @@ -947,9 +947,9 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ WhereTerm *pTerm; /* The term to be analyzed */ WhereMaskSet *pMaskSet; /* Set of table index masks */ Expr *pExpr; /* The expression to be analyzed */ - Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ - Bitmask prereqAll; /* Prerequesites of pExpr */ - Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */ + uint64_t prereqLeft; /* Prerequesites of the pExpr->pLeft */ + uint64_t prereqAll; /* Prerequesites of pExpr */ + uint64_t extraRight = 0; /* Extra dependencies on LEFT JOIN */ Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ int noCase = 0; /* uppercase equivalent to lowercase */ @@ -971,28 +971,28 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ if (sqlite3ExprCheckIN(pParse, pExpr)) return; if (ExprHasProperty(pExpr, EP_xIsSelect)) { - pTerm->prereqRight = + pTerm->prereq_right_mask = exprSelectUsage(pMaskSet, pExpr->x.pSelect); } else { - pTerm->prereqRight = + pTerm->prereq_right_mask = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList); } } else if (op == TK_ISNULL) { - pTerm->prereqRight = 0; + pTerm->prereq_right_mask = 0; } else { - pTerm->prereqRight = + pTerm->prereq_right_mask = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight); } prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr); if (ExprHasProperty(pExpr, EP_FromJoin)) { - Bitmask x = - sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable); + uint64_t x = + sql_where_get_mask(pMaskSet, pExpr->iRightJoinTable); prereqAll |= x; extraRight = x - 1; /* ON clause terms may not be used with an index * on left table of a LEFT JOIN. Ticket #3015 */ } - pTerm->prereqAll = prereqAll; + pTerm->prereq_all_mask = prereqAll; pTerm->leftCursor = -1; pTerm->iParent = -1; pTerm->eOperator = 0; @@ -1001,7 +1001,7 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft); Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight); u16 opMask = - (pTerm->prereqRight & prereqLeft) == 0 ? WO_ALL : WO_EQUIV; + (pTerm->prereq_right_mask & prereqLeft) == 0 ? WO_ALL : WO_EQUIV; if (pTerm->iField > 0) { assert(op == TK_IN); @@ -1050,8 +1050,8 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ pNew->leftCursor = iCur; pNew->u.leftColumn = iColumn; testcase((prereqLeft | extraRight) != prereqLeft); - pNew->prereqRight = prereqLeft | extraRight; - pNew->prereqAll = prereqAll; + pNew->prereq_right_mask = prereqLeft | extraRight; + pNew->prereq_all_mask = prereqAll; pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask; } @@ -1277,14 +1277,14 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ TERM_VNULL); if (idxNew) { pNewTerm = &pWC->a[idxNew]; - pNewTerm->prereqRight = 0; + pNewTerm->prereq_right_mask = 0; pNewTerm->leftCursor = pLeft->iTable; pNewTerm->u.leftColumn = pLeft->iColumn; pNewTerm->eOperator = WO_GT; markTermAsChild(pWC, idxNew, idxTerm); pTerm = &pWC->a[idxTerm]; pTerm->wtFlags |= TERM_COPIED; - pNewTerm->prereqAll = pTerm->prereqAll; + pNewTerm->prereq_all_mask = pTerm->prereq_all_mask; } } @@ -1293,7 +1293,7 @@ exprAnalyze(SrcList * pSrc, /* the FROM clause */ */ testcase(pTerm != &pWC->a[idxTerm]); pTerm = &pWC->a[idxTerm]; - pTerm->prereqRight |= extraRight; + pTerm->prereq_right_mask |= extraRight; } /*************************************************************************** @@ -1379,14 +1379,14 @@ sqlite3WhereClauseClear(WhereClause * pWC) * a bitmask indicating which tables are used in that expression * tree. */ -Bitmask +uint64_t sqlite3WhereExprUsage(WhereMaskSet * pMaskSet, Expr * p) { - Bitmask mask; + uint64_t mask; if (p == 0) return 0; if (p->op == TK_COLUMN) { - mask = sqlite3WhereGetMask(pMaskSet, p->iTable); + mask = sql_where_get_mask(pMaskSet, p->iTable); return mask; } assert(!ExprHasProperty(p, EP_TokenOnly)); @@ -1401,11 +1401,11 @@ sqlite3WhereExprUsage(WhereMaskSet * pMaskSet, Expr * p) return mask; } -Bitmask +uint64_t sqlite3WhereExprListUsage(WhereMaskSet * pMaskSet, ExprList * pList) { int i; - Bitmask mask = 0; + uint64_t mask = 0; if (pList) { for (i = 0; i < pList->nExpr; i++) { mask |= -- 2.7.4
reply other threads:[~2018-08-30 15:18 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=cc088d98c8b0cf564164542aac0a6d9a91c74838.1535642184.git.kshcherbatov@tarantool.org \ --to=kshcherbatov@tarantool.org \ --cc=korablev@tarantool.org \ --cc=tarantool-patches@freelists.org \ --subject='Re: [tarantool-patches] [PATCH v1 1/1] sql: replace sql column mask with core mask' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox