From e0ec7756542ce20800879520c2d1e8c310b443e8 Mon Sep 17 00:00:00 2001 From: Nikita Pettik Date: Mon, 18 Jun 2018 00:38:55 +0300 Subject: [PATCH] sql: review fixes for a40287051 --- src/box/sql/analyze.c | 6 +- src/box/sql/build.c | 27 +--- src/box/sql/delete.c | 48 +++--- src/box/sql/expr.c | 9 +- src/box/sql/fkey.c | 7 +- src/box/sql/insert.c | 42 ++---- src/box/sql/select.c | 5 +- src/box/sql/sqliteInt.h | 32 +--- src/box/sql/where.c | 324 +++++++++++++++++++++------------------- src/box/sql/wherecode.c | 5 +- 10 files changed, 239 insertions(+), 266 deletions(-) diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index 9509645c0ea1..5f73f026e6bf 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -176,9 +176,9 @@ openStatTable(Parse * pParse, /* Parsing context */ /* Open the sql_stat[134] tables for writing. */ for (i = 0; aTable[i]; i++) { - int addr = emit_open_cursor(pParse, iStatCur + i, aRoot[i]); - v->aOp[addr].p4.key_def = NULL; - v->aOp[addr].p4type = P4_KEYDEF; + struct space *space = + space_by_id(SQLITE_PAGENO_TO_SPACEID(aRoot[i])); + vdbe_emit_open_cursor(pParse, iStatCur + i, aRoot[i], space); sqlite3VdbeChangeP5(v, aCreateTbl[i]); VdbeComment((v, aTable[i])); } diff --git a/src/box/sql/build.c b/src/box/sql/build.c index f571eafaf41c..176352f3fd90 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -1177,35 +1177,17 @@ space_checks_expr_list(uint32_t space_id) } int -emit_open_cursor(struct Parse *parse_context, int cursor, int entity_id) +vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id, + struct space *space) { - assert(entity_id > 0); - struct space *space = space_by_id(SQLITE_PAGENO_TO_SPACEID(entity_id)); assert(space != NULL); struct Vdbe *vdbe = parse_context->pVdbe; int space_ptr_reg = ++parse_context->nMem; sqlite3VdbeAddOp4(vdbe, OP_LoadPtr, 0, space_ptr_reg, 0, (void*)space, P4_SPACEPTR); - return sqlite3VdbeAddOp3(vdbe, OP_OpenWrite, cursor, entity_id, + return sqlite3VdbeAddOp3(vdbe, OP_OpenWrite, cursor, index_id, space_ptr_reg); } - -int -sql_emit_open_cursor(struct Parse *parse, int cursor, int index_id, struct space *space) -{ - assert(space != NULL); - Vdbe *vdbe = parse->pVdbe; - int space_ptr_reg = ++parse->nMem; - sqlite3VdbeAddOp4(vdbe, OP_LoadPtr, 0, space_ptr_reg, 0, (void*)space, - P4_SPACEPTR); - struct key_def *def = key_def_dup(space->index[index_id]->def->key_def); - if (def == NULL) - return 0; - return sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, index_id, - space_ptr_reg, - (char*)def, - P4_KEYDEF); -} /* * Generate code that will increment the schema cookie. * @@ -2664,7 +2646,8 @@ sqlite3RefillIndex(Parse * pParse, Index * pIndex, int memRootPage) if (memRootPage < 0) sqlite3VdbeAddOp2(v, OP_Clear, SQLITE_PAGENO_TO_SPACEID(tnum), 0); - emit_open_cursor(pParse, iIdx, tnum); + struct space *space = space_by_id(SQLITE_PAGENO_TO_SPACEID(tnum)); + vdbe_emit_open_cursor(pParse, iIdx, tnum, space); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR | ((memRootPage >= 0) ? OPFLAG_P2ISREG : 0)); diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index 23e2bcc49b5c..f2e9fba8cd4c 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -88,7 +88,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list, * instead of just a Table* parameter. */ struct Table *table = NULL; - struct space *space; + struct space *space = NULL; uint32_t space_id; /* Figure out if we have any triggers and if the table * being deleted from is a view. @@ -99,22 +99,36 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list, */ bool is_complex = false; const char *tab_name = tab_list->a->zName; + struct Table tmp_tab; if (sqlite3LocateTable(parse, LOCATE_NOERR, tab_name) == NULL) { space_id = box_space_id_by_name(tab_name, strlen(tab_name)); if (space_id == BOX_ID_NIL) goto delete_from_cleanup; + /* + * In this case space has been created via Lua + * facilities, so there is no corresponding entry + * in table hash. Thus, lets create simple + * wrapper around space_def to support interface. + */ + space = space_by_id(space_id); + memset(&tmp_tab, 0, sizeof(tmp_tab)); + tmp_tab.def = space->def; + /* Prevent from freeing memory in DeleteTable. */ + tmp_tab.nTabRef = 2; + tab_list->a[0].pTab = &tmp_tab; } else { table = sql_list_lookup_table(parse, tab_list); if (table == NULL) goto delete_from_cleanup; space_id = SQLITE_PAGENO_TO_SPACEID(table->tnum); + space = space_by_id(space_id); + assert(space != NULL); trigger_list =sqlite3TriggersExist(table, TK_DELETE, NULL, NULL); is_complex = trigger_list != NULL || sqlite3FkRequired(table, NULL); } - space = space_by_id(space_id); assert(space != NULL); bool is_view = space->def->opts.is_view; @@ -183,16 +197,6 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list, struct NameContext nc; memset(&nc, 0, sizeof(nc)); nc.pParse = parse; - if (tab_list->a[0].pTab == NULL) { - struct Table *t = calloc(sizeof(struct Table), 1); - if (t == NULL) { - sqlite3OomFault(parse->db); - goto delete_from_cleanup; - } - assert(space != NULL); - t->def = space->def; - tab_list->a[0].pTab = t; - } nc.pSrcList = tab_list; if (sqlite3ResolveExprNames(&nc, where)) goto delete_from_cleanup; @@ -266,11 +270,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list, /* Extract the primary key for the current row */ if (!is_view) { for (int i = 0; i < pk_len; i++) { - struct space_def *def; - if (table != NULL) - def = table->def; - else - def = space->def; + struct space_def *def = space->def; sqlite3ExprCodeGetColumnOfTable(v, def, tab_cursor, pk_def->parts[i].fieldno, @@ -342,15 +342,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list, sqlite3VdbeAddOp4(v, OP_LoadPtr, 0, space_ptr_reg, 0, (void *)space, P4_SPACEPTR); - int tnum; - if (table != NULL) { - tnum = table->tnum; - } - else { - /* index id is 0 for PK. */ - tnum = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space->def->id, - 0); - } + int tnum = + SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space_id, + 0); sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor, tnum, space_ptr_reg); struct key_def *def = key_def_dup(pk_def); @@ -518,7 +512,7 @@ sql_generate_row_delete(struct Parse *parse, struct Table *table, * of the DELETE statement is to fire the INSTEAD OF * triggers). */ - if (table == NULL || table->pSelect == NULL) { + if (table == NULL || !table->def->opts.is_view) { uint8_t p5 = 0; sqlite3VdbeAddOp2(v, OP_Delete, cursor, (need_update_count ? OPFLAG_NCHANGE : 0)); diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c index a69d38bd0912..e2b577368a34 100644 --- a/src/box/sql/expr.c +++ b/src/box/sql/expr.c @@ -36,6 +36,8 @@ #include "box/coll_id_cache.h" #include "coll.h" #include "sqliteInt.h" +#include "tarantoolInt.h" +#include "box/schema.h" #include "box/session.h" /* Forward declarations */ @@ -2485,9 +2487,10 @@ sqlite3FindInIndex(Parse * pParse, /* Parsing context */ "USING INDEX %s FOR IN-OPERATOR", pIdx->zName), P4_DYNAMIC); - emit_open_cursor(pParse, iTab, - pIdx->tnum); - sql_vdbe_set_p4_key_def(pParse, pIdx); + struct space *space = + space_by_id(SQLITE_PAGENO_TO_SPACEID(pIdx->tnum)); + vdbe_emit_open_cursor(pParse, iTab, + pIdx->tnum, space); VdbeComment((v, "%s", pIdx->zName)); assert(IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC + 1); diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c index 6fd29200ce61..9c5d3cdfc5f9 100644 --- a/src/box/sql/fkey.c +++ b/src/box/sql/fkey.c @@ -35,6 +35,7 @@ */ #include "coll.h" #include "sqliteInt.h" +#include "box/schema.h" #include "box/session.h" #include "tarantoolInt.h" @@ -439,9 +440,9 @@ fkLookupParent(Parse * pParse, /* Parse context */ int nCol = pFKey->nCol; int regTemp = sqlite3GetTempRange(pParse, nCol); int regRec = sqlite3GetTempReg(pParse); - - emit_open_cursor(pParse, iCur, pIdx->tnum); - sql_vdbe_set_p4_key_def(pParse, pIdx); + struct space *space = + space_by_id(SQLITE_PAGENO_TO_SPACEID(pIdx->tnum)); + vdbe_emit_open_cursor(pParse, iCur, pIdx->tnum, space); for (i = 0; i < nCol; i++) { sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i] + 1 + regData, diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c index 7aba83529502..74b4331365d2 100644 --- a/src/box/sql/insert.c +++ b/src/box/sql/insert.c @@ -54,8 +54,8 @@ sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */ Index *pPk = sqlite3PrimaryKeyIndex(pTab); assert(pPk != 0); assert(pPk->tnum == pTab->tnum); - emit_open_cursor(pParse, iCur, pPk->tnum); - sql_vdbe_set_p4_key_def(pParse, pPk); + struct space *space = space_by_id(SQLITE_PAGENO_TO_SPACEID(pPk->tnum)); + vdbe_emit_open_cursor(pParse, iCur, pPk->tnum, space); VdbeComment((v, "%s", pTab->def->name)); } @@ -105,34 +105,22 @@ sqlite3IndexAffinityStr(sqlite3 *db, Index *index) } char * -sql_index_affinity_str(struct sqlite3 * db, struct index_def *def) +sql_index_affinity_str(struct sqlite3 *db, struct index_def *def) { - char *aff; - /* The first time a column affinity string for a particular index is - * required, it is allocated and populated here. It is then stored as - * a member of the Index structure for subsequent use. - * - * The column affinity string will eventually be deleted by - * sqliteDeleteIndex() when the Index structure itself is cleaned - * up. - */ - int nColumn = def->key_def->part_count; - aff = (char *)sqlite3DbMallocRaw(0, nColumn + 1); - if (aff == NULL) { - sqlite3OomFault(db); - return 0; - } - int i; - struct space *space = space_cache_find(def->space_id); + uint32_t column_count = def->key_def->part_count; + char *aff = (char *)sqlite3DbMallocRaw(db, column_count + 1); + if (aff == NULL) + return NULL; + struct space *space = space_by_id(def->space_id); assert(space != NULL); - for (i = 0; i < nColumn; i++) { + for (uint32_t i = 0; i < column_count; i++) { uint32_t x = def->key_def->parts[i].fieldno; aff[i] = space->def->fields[x].affinity; if (aff[i] == AFFINITY_UNDEFINED) aff[i] = 'A'; } - aff[i] = 0; + aff[column_count] = '\0'; return aff; } @@ -1951,11 +1939,13 @@ xferOptimization(Parse * pParse, /* Parser context */ break; } assert(pSrcIdx); - emit_open_cursor(pParse, iSrc, pSrcIdx->tnum); - sql_vdbe_set_p4_key_def(pParse, pSrcIdx); + struct space *src_space = + space_by_id(SQLITE_PAGENO_TO_SPACEID(pSrcIdx->tnum)); + vdbe_emit_open_cursor(pParse, iSrc, pSrcIdx->tnum, src_space); VdbeComment((v, "%s", pSrcIdx->zName)); - emit_open_cursor(pParse, iDest, pDestIdx->tnum); - sql_vdbe_set_p4_key_def(pParse, pDestIdx); + struct space *dest_space = + space_by_id(SQLITE_PAGENO_TO_SPACEID(pDestIdx->tnum)); + vdbe_emit_open_cursor(pParse, iDest, pDestIdx->tnum, dest_space); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 4b5ba4d3ee22..14a78e7180e1 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -6107,8 +6107,9 @@ sqlite3Select(Parse * pParse, /* The parser context */ * Open the cursor, execute the OP_Count, * close the cursor. */ - emit_open_cursor(pParse, cursor, - space->def->id << 10); + vdbe_emit_open_cursor(pParse, cursor, + space->def->id << 10, + space); sqlite3VdbeAddOp2(v, OP_Count, cursor, sAggInfo.aFunc[0].iMem); sqlite3VdbeAddOp1(v, OP_Close, cursor); diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h index 878daa8dfea7..268b4f295ebb 100644 --- a/src/box/sql/sqliteInt.h +++ b/src/box/sql/sqliteInt.h @@ -2536,8 +2536,6 @@ struct SrcList { char *zName; /* Name of the table */ char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */ Table *pTab; /* An SQL table corresponding to zName */ - /* A temporary hack: need to store eph. space. */ - struct space *space; Select *pSelect; /* A SELECT statement used in place of a table name */ int addrFillSub; /* Address of subroutine to manifest a subquery */ int regReturn; /* Register holding return address of addrFillSub */ @@ -3553,20 +3551,6 @@ sql_index_column_sort_order(Index *idx, uint32_t column); void sqlite3EndTable(Parse *, Token *, Token *, Select *); -/** - * DEPRECATED. All calls to be replaced w/ sql_emit_open_cursor. - * Create cursor which will be positioned to the space/index. - * It makes space lookup and loads pointer to it into register, - * which is passes to OP_OpenWrite as an argument. - * - * @param parse Parse context. - * @param cursor Number of cursor to be created. - * @param entity_id Encoded space and index ids. - * @retval address of last opcode. - */ -int -emit_open_cursor(struct Parse *parse, int cursor, int entity_id); - /** * Create cursor which will be positioned to the space/index. * It makes space lookup and loads pointer to it into register, @@ -3580,8 +3564,8 @@ emit_open_cursor(struct Parse *parse, int cursor, int entity_id); * @retval address of last opcode. */ int -sql_emit_open_cursor(struct Parse *parse, int cursor, int index_id, - struct space *space); +vdbe_emit_open_cursor(struct Parse *parse, int cursor, int index_id, + struct space *space); int sqlite3ParseUri(const char *, const char *, unsigned int *, sqlite3_vfs **, char **, char **); @@ -4125,9 +4109,10 @@ int sqlite3VarintLen(u64 v); const char *sqlite3IndexAffinityStr(sqlite3 *, Index *); /** - * Return a pointer to the column affinity string associated with index - * pIdx. A column affinity string has one character for each column in - * the table, according to the affinity of the column: + * Return a pointer to the column affinity string associated with + * given index. A column affinity string has one character for + * each column in the table, according to the affinity of the + * column: * * Character Column affinity * ------------------------------ @@ -4138,12 +4123,11 @@ const char *sqlite3IndexAffinityStr(sqlite3 *, Index *); * 'F' REAL * * Memory for the buffer containing the column index affinity string - * is managed along with the rest of the Index structure. It will be - * released when sqlite3DeleteIndex() is called. + * is allocated on heap. * * @param db Database handle. * @param def index_def where from affinity to be extracted. - * @retval Affinity string. + * @retval Allocated affinity string, or NULL on OOM. */ char * sql_index_affinity_str(struct sqlite3 *db, struct index_def *def); diff --git a/src/box/sql/where.c b/src/box/sql/where.c index f017384b5ee3..e6c34f34a7a9 100644 --- a/src/box/sql/where.c +++ b/src/box/sql/where.c @@ -42,6 +42,7 @@ #include "tarantoolInt.h" #include "vdbeInt.h" #include "whereInt.h" +#include "box/coll_id_cache.h" #include "box/session.h" #include "box/schema.h" @@ -371,10 +372,7 @@ whereScanInit(WhereScan * pScan, /* The WhereScan object being initialized */ pScan->is_column_seen = false; if (pIdx) { int j = iColumn; - if (j >= pIdx->nColumn) - iColumn = -1; - else - iColumn = pIdx->aiColumn[j]; + iColumn = pIdx->aiColumn[j]; if (iColumn >= 0) { char affinity = pIdx->pTable->def->fields[iColumn].affinity; @@ -393,12 +391,30 @@ whereScanInit(WhereScan * pScan, /* The WhereScan object being initialized */ return whereScanNext(pScan); } +/** + * Analogue of whereScanInit() but also can be called for spaces + * created via Lua interface. This function doesn't rely on + * regular SQLite structures representing data dictionary. + * + * @param scan The WhereScan object being initialized. + * @param clause The WHERE clause to be scanned. + * @param cursor Cursor to scan for. + * @param column Column to scan for. + * @param op_mask Operator(s) to scan for. + * @param space_def Def of the space related to WHERE clause. + * @param key_def Def of the index to be used to satisfy WHERE + * clause. May be NULL. + * + * @retval Return a pointer to the first match. Return NULL if + * there are no matches. + */ static WhereTerm * -sql_where_scan_init(struct WhereScan *scan, struct WhereClause *clause, +where_scan_init(struct WhereScan *scan, struct WhereClause *clause, int cursor, int column, uint32_t op_mask, struct space_def *space_def, struct key_def *key_def) { - scan->pOrigWC = scan->pWC = clause; + scan->pOrigWC = clause; + scan->pWC = clause; scan->pIdxExpr = NULL; scan->idxaff = 0; scan->coll = NULL; @@ -406,11 +422,11 @@ sql_where_scan_init(struct WhereScan *scan, struct WhereClause *clause, if (key_def != NULL) { int j = column; column = key_def->parts[j].fieldno; - if (column >= 0) { - scan->idxaff = space_def->fields[column].affinity; - scan->coll = key_def->parts[column].coll; - scan->is_column_seen = true; - } + scan->idxaff = space_def->fields[column].affinity; + uint32_t coll_id = space_def->fields[column].coll_id; + struct coll_id *coll = coll_by_id(coll_id); + scan->coll = coll != NULL ? coll->coll : NULL; + scan->is_column_seen = true; } scan->opMask = op_mask; scan->k = 0; @@ -472,34 +488,43 @@ sqlite3WhereFindTerm(WhereClause * pWC, /* The WHERE clause to be searched */ return pResult; } -WhereTerm * -sql_where_find_term(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 */ - struct space_def *space_def, - struct key_def *key_def) /* Must be compatible with this index, if not NULL */ +/** + * Analogue of sqlite3WhereFindTerm() but also can be called + * for spaces created via Lua interface. This function doesn't + * rely on regular SQLite structures representing data + * dictionary. + * + * @param where_clause The WHERE clause to be examined. + * @param cursor Cursor number of LHS. + * @param column Column number of LHS + * @param is_ready RHS must not overlap with this mask. + * @param op Mask of WO_xx values describing operator. + * @param space_def Def of the space related to WHERE clause. + * @param key_def Def of the index to be used to satisfy WHERE + * clause. May be NULL. + * + * @retval New struct describing WHERE term. + */ +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, + struct key_def *key_def) { - WhereTerm *pResult = 0; - WhereTerm *p; - WhereScan scan; - - p = sql_where_scan_init(&scan, pWC, iCur, iColumn, op, space_def, - key_def); + struct WhereTerm *result = NULL; + struct WhereScan scan; + struct WhereTerm *p = where_scan_init(&scan, where_clause, cursor, + column, op, space_def, key_def); op &= WO_EQ; - while (p) { - if ((p->prereqRight & notReady) == 0) { - if (p->prereqRight == 0 && (p->eOperator & op) != 0) { - testcase(p->eOperator & WO_IS); + while (p != NULL) { + if ((p->prereqRight & is_ready) == 0) { + if (p->prereqRight == 0 && (p->eOperator & op) != 0) return p; - } - if (pResult == 0) - pResult = p; + if (result == NULL) + result = p; } p = whereScanNext(&scan); } - return pResult; + return result; } /* @@ -3428,7 +3453,7 @@ wherePathSatisfiesOrderBy(WhereInfo * pWInfo, /* The WHERE clause */ /* Get the column number in the table (iColumn) and sort order * (revIdx) for the j-th column of the index. */ - if (pIndex && j < pIndex->nColumn) { + if (pIndex != NULL) { iColumn = pIndex->aiColumn[j]; revIdx = sql_index_column_sort_order(pIndex, j); @@ -4096,135 +4121,125 @@ wherePathSolver(WhereInfo * pWInfo, LogEst nRowEst) return SQLITE_OK; } -/* - * Most queries use only a single table (they are not joins) and have - * simple == constraints against indexed fields. This routine attempts - * to plan those simple cases using much less ceremony than the - * general-purpose query planner, and thereby yield faster sqlite3_prepare() - * times for the common case. +/** + * Attempt at finding appropriate terms in WHERE clause. * - * Return non-zero on success, if this query can be handled by this - * no-frills query planner. Return zero if this query needs the - * general-purpose query planner. + * @param loop The loop @where belongs to. + * @param where The WHERE clause to be examined.. + * @param cursor Cursor number of LHS. + * @param space_def Def of the space related to WHERE clause. + * @param index_def Def of the index to be used to satisfy WHERE + * clause. May be NULL. + * @retval 0 on success, -1 otherwise. */ static int -sql_where_shortcut(struct WhereLoopBuilder *builder) +where_loop_assign_terms(struct WhereLoop *loop, struct WhereClause *where, + int cursor, struct space_def *space_def, + struct index_def *idx_def) { - WhereInfo *pWInfo; - struct SrcList_item *pItem; - WhereClause *pWC; - WhereTerm *pTerm; - WhereLoop *pLoop; - int iCur; - int j; + uint32_t column_count = idx_def != NULL ? idx_def->key_def->part_count : + space_def->field_count; + if (column_count > ArraySize(loop->aLTermSpace)) + return -1; + uint32_t i; + for (i = 0; i < column_count; ++i) { + struct WhereTerm *term = + where_clause_find_term(where, cursor, i, 0, WO_EQ, + space_def, idx_def != NULL ? + idx_def->key_def : NULL); + if (term == NULL) + break; + testcase(pTerm->eOperator & WO_IS); + loop->aLTerm[i] = term; + } + if (i != column_count) + return -1; + loop->wsFlags = WHERE_COLUMN_EQ | WHERE_ONEROW | WHERE_INDEXED | + WHERE_IDX_ONLY; + loop->nLTerm = i; + loop->nEq = i; + loop->index_def = idx_def; + /* TUNING: Cost of a unique index lookup is 15. */ + assert(39 == sqlite3LogEst(15)); + loop->rRun = 39; + return 0; +} - pWInfo = builder->pWInfo; - if (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) +/** + * Most queries use only a single table (they are not joins) and + * have simple == constraints against indexed fields. This + * routine attempts to plan those simple cases using much less + * ceremony than the general-purpose query planner, and thereby + * yield faster sqlite3_prepare() times for the common case. + * + * @param builder Where-Loop Builder. + * @retval Return non-zero on success, i.e. if this query can be + * handled by this no-frills query planner. Return zero + * if this query needs the general-purpose query planner. + */ +static int +where_loop_builder_shortcut(struct WhereLoopBuilder *builder) +{ + struct WhereInfo *where_info = builder->pWInfo; + if (where_info->wctrlFlags & WHERE_OR_SUBCLAUSE) return 0; - assert(pWInfo->pTabList->nSrc >= 1); - pItem = pWInfo->pTabList->a; - struct space_def *space_def = pItem->pTab->def; + assert(where_info->pTabList->nSrc >= 1); + struct SrcList_item *item = where_info->pTabList->a; + struct space_def *space_def = item->pTab->def; assert(space_def != NULL); - - if (pItem->fg.isIndexedBy) + if (item->fg.isIndexedBy) return 0; - iCur = pItem->iCursor; - pWC = &pWInfo->sWC; - pLoop = builder->pNew; - pLoop->wsFlags = 0; - pLoop->nSkip = 0; - pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ, 0); - if (pTerm) { - pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IPK | WHERE_ONEROW; - pLoop->aLTerm[0] = pTerm; - pLoop->nLTerm = 1; - pLoop->nEq = 1; - /* TUNING: Cost of a PK lookup is 10 */ - pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */ + int cursor = item->iCursor; + struct WhereClause *clause = &where_info->sWC; + struct WhereLoop *loop = builder->pNew; + loop->wsFlags = 0; + loop->nSkip = 0; + loop->pIndex = NULL; + struct WhereTerm *term = sqlite3WhereFindTerm(clause, cursor, -1, 0, + WO_EQ, 0); + if (term != NULL) { + loop->wsFlags = WHERE_COLUMN_EQ | WHERE_IPK | WHERE_ONEROW; + loop->aLTerm[0] = term; + loop->nLTerm = 1; + loop->nEq = 1; + /* TUNING: Cost of a PK lookup is 10. */ + assert(33 == sqlite3LogEst(10)); + loop->rRun = 33; } else { - struct space *space = space_cache_find(space_def->id); + assert(loop->aLTermSpace == loop->aLTerm); + struct space *space = space_by_id(space_def->id); if (space != NULL) { for (uint32_t i = 0; i < space->index_count; ++i) { - struct index_def *idx_def; - idx_def = space->index[i]->def; - int opMask; - int nIdxCol = idx_def->key_def->part_count; - assert(pLoop->aLTermSpace == pLoop->aLTerm); - if (!idx_def->opts.is_unique - /* || pIdx->pPartIdxWhere != 0 */ - || nIdxCol > ArraySize(pLoop->aLTermSpace) - ) + struct index_def *idx_def = + space->index[i]->def; + if (!idx_def->opts.is_unique) continue; - opMask = WO_EQ; - for (j = 0; j < nIdxCol; j++) { - pTerm = sql_where_find_term(pWC, iCur, - j, 0, - opMask, - space_def, - idx_def-> - key_def); - if (pTerm == 0) - break; - testcase(pTerm->eOperator & WO_IS); - pLoop->aLTerm[j] = pTerm; - } - if (j != nIdxCol) - continue; - pLoop->wsFlags = WHERE_COLUMN_EQ | - WHERE_ONEROW | WHERE_INDEXED | - WHERE_IDX_ONLY; - pLoop->nLTerm = j; - pLoop->nEq = j; - pLoop->pIndex = NULL; - pLoop->index_def = idx_def; - /* TUNING: Cost of a unique index lookup is 15 */ - pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */ - break; + if (where_loop_assign_terms(loop, clause, + cursor, space_def, + idx_def) == 0) + break; } } else { - /* Space is ephemeral. */ + /* Space is ephemeral. */ assert(space_def->id == 0); - int opMask; - int nIdxCol = space_def->field_count; - assert(pLoop->aLTermSpace == pLoop->aLTerm); - if ( nIdxCol > ArraySize(pLoop->aLTermSpace)) - return 0; - opMask = WO_EQ; - for (j = 0; j < nIdxCol; j++) { - pTerm = sql_where_find_term(pWC, iCur, - j, 0, - opMask, - space_def, - NULL); - if (pTerm == NULL) - break; - pLoop->aLTerm[j] = pTerm; - } - if (j != nIdxCol) - return 0; - pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_ONEROW | - WHERE_INDEXED | WHERE_IDX_ONLY; - pLoop->nLTerm = j; - pLoop->nEq = j; - pLoop->pIndex = NULL; - pLoop->index_def = NULL; - /* TUNING: Cost of a unique index lookup is 15 */ - pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */ + where_loop_assign_terms(loop, clause, cursor, + space_def, NULL); } } - if (pLoop->wsFlags) { - pLoop->nOut = (LogEst) 1; - pWInfo->a[0].pWLoop = pLoop; - pLoop->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); - pWInfo->a[0].iTabCur = iCur; - pWInfo->nRowOut = 1; - if (pWInfo->pOrderBy) - pWInfo->nOBSat = pWInfo->pOrderBy->nExpr; - if (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) { - pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE; + if (loop->wsFlags) { + loop->nOut = (LogEst) 1; + where_info->a[0].pWLoop = loop; + loop->maskSelf = sqlite3WhereGetMask(&where_info->sMaskSet, + cursor); + where_info->a[0].iTabCur = cursor; + where_info->nRowOut = 1; + if (where_info->pOrderBy) + where_info->nOBSat = where_info->pOrderBy->nExpr; + if (where_info->wctrlFlags & WHERE_WANT_DISTINCT) { + where_info->eDistinct = WHERE_DISTINCT_UNIQUE; } #ifdef SQLITE_DEBUG - pLoop->cId = '0'; + loop->cId = '0'; #endif return 1; } @@ -4522,7 +4537,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ } #endif - if (nTabList != 1 || sql_where_shortcut(&sWLB) == 0) { + if (nTabList != 1 || where_loop_builder_shortcut(&sWLB) == 0) { rc = whereLoopAddAll(&sWLB); if (rc) goto whereBeginError; @@ -4702,10 +4717,10 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ * It is something w/ defined space_def * and nothing else. Skip such loops. */ - if (idx_def == NULL && pIx == NULL) continue; + if (idx_def == NULL && pIx == NULL) + continue; bool is_primary = (pIx != NULL && IsPrimaryKeyIndex(pIx)) || - (idx_def != NULL && (idx_def->iid == 0)) | - (idx_def == NULL && pIx == NULL); + (idx_def != NULL && (idx_def->iid == 0)); if (is_primary && (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) { /* This is one term of an OR-optimization using @@ -4752,13 +4767,14 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */ assert(iIndexCur >= 0); if (op) { if (pIx != NULL) { - emit_open_cursor(pParse, iIndexCur, - pIx->tnum); - sql_vdbe_set_p4_key_def(pParse, pIx); + struct space *space = + space_by_id(SQLITE_PAGENO_TO_SPACEID(pIx->tnum)); + vdbe_emit_open_cursor(pParse, iIndexCur, + pIx->tnum, space); } else { - sql_emit_open_cursor(pParse, iIndexCur, - idx_def->iid, - space); + vdbe_emit_open_cursor(pParse, iIndexCur, + idx_def->iid, + space); } if ((pLoop->wsFlags & WHERE_CONSTRAINT) != 0 && (pLoop-> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index 60e0ac7e4975..eaab0b65768a 100644 --- a/src/box/sql/wherecode.c +++ b/src/box/sql/wherecode.c @@ -87,7 +87,7 @@ explainAppendTerm(StrAccum * pStr, /* The text expression being built */ assert(def != NULL); struct space *space = space_cache_find(def->space_id); assert(space != NULL); - name = space->def->fields[i].name; + name = space->def->fields[i + iTerm].name; } sqlite3StrAccumAppendAll(pStr, name); } @@ -143,7 +143,8 @@ explainIndexRange(StrAccum * pStr, WhereLoop * pLoop) } else { struct space *space = space_cache_find(def->space_id); assert(space != NULL); - z = space->def->fields[i].name; + uint32_t fieldno = def->key_def->parts[i].fieldno; + z = space->def->fields[fieldno].name; } if (i) sqlite3StrAccumAppend(pStr, " AND ", 5);