From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 3534020A44 for ; Sun, 17 Jun 2018 22:11:45 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id rlNFeU5ckTu0 for ; Sun, 17 Jun 2018 22:11:45 -0400 (EDT) Received: from smtp5.mail.ru (smtp5.mail.ru [94.100.179.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 2EA402086C for ; Sun, 17 Jun 2018 22:11:44 -0400 (EDT) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 10.3 \(3273\)) Subject: [tarantool-patches] Re: [PATCH 3/3] sql: implement point where for DELETE stmts From: "n.pettik" In-Reply-To: <16d25107-35e6-d6db-d0d5-01a47f1d3909@tarantool.org> Date: Mon, 18 Jun 2018 05:11:32 +0300 Content-Transfer-Encoding: quoted-printable Message-Id: <90D2A28D-188B-45EB-89A9-43462A3ECF36@tarantool.org> References: <16d25107-35e6-d6db-d0d5-01a47f1d3909@tarantool.org> Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: tarantool-patches@freelists.org Cc: Kirill Yukhin , Vladislav Shpilevoy For some reason this patch got to the trunk without review fixes. Raw version of this patch contains several bugs, so they led to test = fails (sql-tap/analyze6.test.lua and few sql-tap/tkt*). I suggest my own fixes. The whole patch is at the end of letter. Branch: = https://github.com/tarantool/tarantool/commits/np/gh-3463-review-fixed-for= -point-delete Issue: https://github.com/tarantool/tarantool/issues/3463 >=20 >> } >> struct ExprList * >> @@ -1221,6 +1216,22 @@ emit_open_cursor(Parse *parse_context, int = cursor, int entity_id) >> space_ptr_reg); >> } >> +int >> +sql_emit_open_cursor(struct Parse *parse, int cursor, int index_id, = struct space *space) >=20 > 2. Few lines above I see emit_open_cursor. Please, try to remove one = of them. > At the worst case you can just inline emit_open_cursor in the single = place of > its usage, and rename sql_emit_open_cursor to emit_open_cursor. Ok, I just replaced all calls of emit_open_cursor() with = sql_emit_open_cursor(). The only difference is in doing space lookup outside emit_open_cursor(). diff --git a/src/box/sql/build.c b/src/box/sql/build.c index 552578048..8c83a797e 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -1181,35 +1181,17 @@ space_checks_expr_list(uint32_t space_id) } =20 int -emit_open_cursor(struct Parse *parse_context, int cursor, int = entity_id) +sql_emit_open_cursor(struct Parse *parse_context, int cursor, int = index_id, + struct space *space) { - assert(entity_id > 0); - struct space *space =3D = space_by_id(SQLITE_PAGENO_TO_SPACEID(entity_id)); assert(space !=3D NULL); struct Vdbe *vdbe =3D parse_context->pVdbe; int space_ptr_reg =3D ++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 !=3D NULL); - Vdbe *vdbe =3D parse->pVdbe; - int space_ptr_reg =3D ++parse->nMem; - sqlite3VdbeAddOp4(vdbe, OP_LoadPtr, 0, space_ptr_reg, 0, = (void*)space, - P4_SPACEPTR); - struct key_def *def =3D = key_def_dup(space->index[index_id]->def->key_def); - if (def =3D=3D NULL) - return 0; - return sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, index_id, - space_ptr_reg, - (char*)def, - P4_KEYDEF); -} >=20 >> +{ >> + assert(space !=3D NULL); >> + Vdbe *vdbe =3D parse->pVdbe; >> + int space_ptr_reg =3D ++parse->nMem; >> + sqlite3VdbeAddOp4(vdbe, OP_LoadPtr, 0, space_ptr_reg, 0, = (void*)space, >> + P4_SPACEPTR); >> + struct key_def *def =3D = key_def_dup(space->index[index_id]->def->key_def); >> + if (def =3D=3D NULL) >> + return 0; >> + return sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, index_id, >> + space_ptr_reg, >> + (char*)def, >> + P4_KEYDEF); >=20 > 3. Looks like the arguments can fit in 2 lines instead of 4. Fixed. See code above. >=20 >> +} >> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c >> index 2b59130..de4e0c1 100644 >> --- a/src/box/sql/delete.c >> +++ b/src/box/sql/delete.c >> @@ -184,7 +184,7 @@ sql_table_delete_from(struct Parse *parse, struct = SrcList *tab_list, >> memset(&nc, 0, sizeof(nc)); >> nc.pParse =3D parse; >> if (tab_list->a[0].pTab =3D=3D NULL) { >> - struct Table *t =3D malloc(sizeof(struct = Table)); >> + struct Table *t =3D calloc(sizeof(struct Table), = 1); >=20 > 4. It must the part of the previous patch. Refactored this way: +++ 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 =3D NULL; - struct space *space; + struct space *space =3D NULL; uint32_t space_id; /* Figure out if we have any triggers and if the table * being deleted from is a view. @@ -104,17 +104,28 @@ sql_table_delete_from(struct Parse *parse, struct = SrcList *tab_list, strlen(tab_name)); if (space_id =3D=3D 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 =3D space_by_id(space_id); + tab_list->a[0].pTab =3D = sql_table_construct_from_space(space); + if (tab_list->a[0].pTab =3D=3D NULL) + goto delete_from_cleanup; } else { table =3D sql_list_lookup_table(parse, tab_list); if (table =3D=3D NULL) goto delete_from_cleanup; space_id =3D SQLITE_PAGENO_TO_SPACEID(table->tnum); + space =3D space_by_id(space_id); + assert(space !=3D NULL); @@ -183,16 +194,6 @@ sql_table_delete_from(struct Parse *parse, struct = SrcList *tab_list, struct NameContext nc; memset(&nc, 0, sizeof(nc)); nc.pParse =3D parse; - if (tab_list->a[0].pTab =3D=3D NULL) { - struct Table *t =3D calloc(sizeof(struct Table), = 1); - if (t =3D=3D NULL) { - sqlite3OomFault(parse->db); - goto delete_from_cleanup; - } - assert(space !=3D NULL); - t->def =3D space->def; - tab_list->a[0].pTab =3D t; - } +++ b/src/box/sql/resolve.c @@ -39,6 +39,9 @@ #include #include =20 +#include "box/box.h" +#include "box/schema.h" + /* * Walk the expression tree pExpr and increase the aggregate function * depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. @@ -1432,6 +1435,21 @@ resolveSelectStep(Walker * pWalker, Select * p) return WRC_Prune; } =20 +struct Table * +sql_table_construct_from_space(const struct space *space) +{ + assert(space !=3D NULL); + struct Table *table =3D calloc(1, sizeof(*table)); + if (table =3D=3D NULL) { + diag_set(OutOfMemory, sizeof(table), "calloc", "table"); + return NULL; + } + table->def =3D space_def_dup(space->def); + if (table->def =3D=3D NULL) + return NULL; + return table; +} + +/** + * Create wrapper around space_def of the given space. + * This routine is required to support. + * Both table and space def are allocated on heap. + * + * @param space Space to be prototype. + * @retval New instance of struct Table allocated on malloc, + * or NULL in case of OOM. + */ +struct Table * +sql_table_construct_from_space(const struct space *space); >=20 >> if (t =3D=3D NULL) { >> sqlite3OomFault(parse->db); >> goto delete_from_cleanup; >> @@ -266,7 +266,12 @@ 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 =3D 0; i < pk_len; i++) { >> - sqlite3ExprCodeGetColumnOfTable(v, = table->def, >> + struct space_def *def; >> + if (table !=3D NULL) >> + def =3D table->def; >> + else >> + def =3D space->def; >=20 > 5. Why not always space->def? Idk, indeed lets always use space->def: @@ -266,11 +267,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 =3D 0; i < pk_len; i++) { - struct space_def *def; - if (table !=3D NULL) - def =3D table->def; - else - def =3D space->def; + struct space_def *def =3D space->def; >=20 >> + sqlite3ExprCodeGetColumnOfTable(v, def, >> = tab_cursor, >> = pk_def->parts[i].fieldno, >> reg_pk + = i); >> @@ -339,8 +344,18 @@ sql_table_delete_from(struct Parse *parse, = struct SrcList *tab_list, >> int space_ptr_reg =3D ++parse->nMem; >> sqlite3VdbeAddOp4(v, OP_LoadPtr, 0, = space_ptr_reg, 0, >> (void *)space, P4_SPACEPTR); >> + >> + int tnum; >> + if (table !=3D NULL) { >> + tnum =3D table->tnum; >> + } >> + else { >> + /* index id is 0 for PK. */ >> + tnum =3D = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space->def->id, >> + = 0); >> + } >=20 > 6. Why not always the second version? Idk as well, lets always use conversion from space->def->id. @@ -342,15 +339,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); =20 - int tnum; - if (table !=3D NULL) { - tnum =3D table->tnum; - } - else { - /* index id is 0 for PK. */ - tnum =3D = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space->def->id, - = 0); - } + int tnum =3D + = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space->def->id, + = 0); >=20 >> sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor, >> - table->tnum, space_ptr_reg); >> + tnum, space_ptr_reg); >> struct key_def *def =3D key_def_dup(pk_def); >> if (def =3D=3D NULL) { >> sqlite3OomFault(parse->db); >> @@ -505,7 +521,7 @@ sql_generate_row_delete(struct Parse *parse, = struct Table *table, >> * of the DELETE statement is to fire the INSTEAD OF >> * triggers). >> */ >> - if (table->pSelect =3D=3D NULL) { >> + if (table =3D=3D NULL || table->pSelect =3D=3D NULL) { >=20 > 7. Is the same as space->def->opts.is_view. Ok: @@ -518,7 +509,7 @@ sql_generate_row_delete(struct Parse *parse, struct = Table *table, * of the DELETE statement is to fire the INSTEAD OF * triggers). */ - if (table =3D=3D NULL || table->pSelect =3D=3D NULL) { + if (table =3D=3D NULL || !table->def->opts.is_view) { >=20 >> uint8_t p5 =3D 0; >> sqlite3VdbeAddOp2(v, OP_Delete, cursor, >> (need_update_count ? OPFLAG_NCHANGE : = 0)); >> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c >> index 3dbf855..8d0ab3b 100644 >> --- a/src/box/sql/insert.c >> +++ b/src/box/sql/insert.c >> @@ -108,6 +108,39 @@ sqlite3IndexAffinityStr(sqlite3 * db, Index * = pIdx) >> return pIdx->zColAff; >> } >> +char * >> +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. >=20 > 8. No, it is not populated. Deleted the whole comment - it doesn=E2=80=99t seem to be relevant. And slightly refactored function: =20 @@ -107,32 +107,22 @@ sqlite3IndexAffinityStr(sqlite3 *db, Index *index) char * 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 =3D def->key_def->part_count; - aff =3D (char *)sqlite3DbMallocRaw(0, nColumn + 1); + uint32_t column_count =3D def->key_def->part_count; + char *aff =3D (char *)sqlite3DbMallocRaw(0, column_count + 1); if (aff =3D=3D NULL) { sqlite3OomFault(db); - return 0; + return NULL; } - int i; - struct space *space =3D space_cache_find(def->space_id); + struct space *space =3D space_by_id(def->space_id); assert(space !=3D NULL); =20 - for (i =3D 0; i < nColumn; i++) { + for (uint32_t i =3D 0; i < column_count; i++) { uint32_t x =3D def->key_def->parts[i].fieldno; aff[i] =3D space->def->fields[x].affinity; if (aff[i] =3D=3D AFFINITY_UNDEFINED) aff[i] =3D 'A'; } - aff[i] =3D 0; + aff[column_count] =3D '\0'; =20 return aff; } >=20 >> + * >> + * The column affinity string will eventually be deleted by >> + * sqliteDeleteIndex() when the Index structure itself is = cleaned >> + * up. >> + */ >> + int nColumn =3D def->key_def->part_count; >> + aff =3D (char *)sqlite3DbMallocRaw(0, nColumn + 1); >=20 > 9. Why do you need to declare 'char *aff;' separately above? Fixed, see diff above. >=20 >> + if (aff =3D=3D NULL) { >> + sqlite3OomFault(db); >> + return 0; >> + } >> + int i; >=20 > 10. Can be part of 'for=E2=80=99. Fixed, see diff above. >=20 >> + struct space *space =3D space_cache_find(def->space_id); >> + assert(space !=3D NULL); >> + >> + for (i =3D 0; i < nColumn; i++) { >> + uint32_t x =3D def->key_def->parts[i].fieldno; >> + aff[i] =3D space->def->fields[x].affinity; >> + if (aff[i] =3D=3D AFFINITY_UNDEFINED) >> + aff[i] =3D 'A'; >> + } >> + aff[i] =3D 0; >> + >> + return aff; >> +} >> + >> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h >> index 943fda9..6ea956d 100644 >> --- a/src/box/sql/sqliteInt.h >> +++ b/src/box/sql/sqliteInt.h >> @@ -2536,6 +2536,8 @@ 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; >=20 > 11. ??? It is unused. Indeed, removed: +++ 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; >=20 >> 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 */ >> diff --git a/src/box/sql/where.c b/src/box/sql/where.c >> index d312587..14cb23d 100644 >> --- a/src/box/sql/where.c >> +++ b/src/box/sql/where.c >> @@ -371,7 +371,10 @@ whereScanInit(WhereScan * pScan, /* The = WhereScan object being initialized */ >> pScan->is_column_seen =3D false; >> if (pIdx) { >> int j =3D iColumn; >> - iColumn =3D pIdx->aiColumn[j]; >> + if (j >=3D pIdx->nColumn) >> + iColumn =3D -1; >> + else >> + iColumn =3D pIdx->aiColumn[j]; >=20 > 12. How can j can be > nColumn? No way: @@ -371,10 +372,7 @@ whereScanInit(WhereScan * pScan, /* The WhereScan = object being initialized */ pScan->is_column_seen =3D false; if (pIdx) { int j =3D iColumn; - if (j >=3D pIdx->nColumn) - iColumn =3D -1; - else - iColumn =3D pIdx->aiColumn[j]; + iColumn =3D pIdx->aiColumn[j]; >=20 >> if (iColumn >=3D 0) { >> char affinity =3D >> = pIdx->pTable->def->fields[iColumn].affinity; >> @@ -441,6 +472,36 @@ 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 */ >> +{ >> + WhereTerm *pResult =3D 0; >> + WhereTerm *p; >> + WhereScan scan; >> + >> + p =3D sql_where_scan_init(&scan, pWC, iCur, iColumn, op, = space_def, >> + key_def); >=20 > 13. Why do you need to declare 'WhereTerm *p;' above? Please, make the = new functions > obey Tarantool code style. I will not mention it again below. Ok, I have refactored this function. See diff at the end of letter. >=20 >> + op &=3D WO_EQ; >> + while (p) { >> + if ((p->prereqRight & notReady) =3D=3D 0) { >> + if (p->prereqRight =3D=3D 0 && (p->eOperator & = op) !=3D 0) { >> + testcase(p->eOperator & WO_IS); >> + return p; >> + } >> + if (pResult =3D=3D 0) >> + pResult =3D p; >> + } >> + p =3D whereScanNext(&scan); >> + } >> + return pResult; >> +} >> + >> /* >> * This function searches pList for an entry that matches the = iCol-th column >> * of index pIdx. >> @@ -1703,6 +1764,7 @@ whereLoopInit(WhereLoop * p) >> p->nLTerm =3D 0; >> p->nLSlot =3D ArraySize(p->aLTermSpace); >> p->wsFlags =3D 0; >> + p->index_def =3D NULL; >=20 > 14. Why for non-existing struct Table you have created dummy one, but = for > non-existing struct Index you can not do the same? You can add > struct index_def to struct Index for this, it is not? Anyways = index_def > is going to be part of struct Index. AFAIK Ivan works on this issue, lets wait for his patch. >=20 >> } >> /* >> @@ -3366,7 +3428,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) { >> + if (pIndex && j < pIndex->nColumn) { >=20 > 15. pIndex !=3D NULL >=20 > 16. How j can be > pIndex->nColumn? @@ -2668,7 +2650,8 @@ sqlite3RefillIndex(Parse * pParse, Index * pIndex, = int memRootPage) - if (pIndex && j < pIndex->nColumn) { + if (pIndex !=3D NULL) { >=20 >> iColumn =3D pIndex->aiColumn[j]; >> revIdx =3D = sql_index_column_sort_order(pIndex, >> = j); >> @@ -4080,34 +4142,74 @@ whereShortCut(WhereLoopBuilder * pBuilder) >> /* TUNING: Cost of a PK lookup is 10 */ >> pLoop->rRun =3D 33; /* 33=3D=3Dsqlite3LogEst(10) */ >> } else { >> - for (pIdx =3D pTab->pIndex; pIdx; pIdx =3D pIdx->pNext) = { >> + struct space *space =3D space_cache_find(space_def->id); >> + if (space !=3D NULL) { >> + for (uint32_t i =3D 0; i < space->index_count; = ++i) { >> + struct index_def *idx_def; >> + idx_def =3D space->index[i]->def; >> + int opMask; >> + int nIdxCol =3D = idx_def->key_def->part_count; >> + assert(pLoop->aLTermSpace =3D=3D = pLoop->aLTerm); >> + if (!idx_def->opts.is_unique >> + /* || pIdx->pPartIdxWhere !=3D 0 */ >> + || nIdxCol > = ArraySize(pLoop->aLTermSpace) >> + ) >> + continue; >> + opMask =3D WO_EQ; >> + for (j =3D 0; j < nIdxCol; j++) { >> + pTerm =3D = sql_where_find_term(pWC, iCur, >> + j, = 0, >> + = opMask, >> + = space_def, >> + = idx_def-> >> + = key_def); >> + if (pTerm =3D=3D 0) >> + break; >> + testcase(pTerm->eOperator & = WO_IS); >> + pLoop->aLTerm[j] =3D pTerm; >> + } >> + if (j !=3D nIdxCol) >> + continue; >> + pLoop->wsFlags =3D WHERE_COLUMN_EQ | >> + WHERE_ONEROW | WHERE_INDEXED | >> + WHERE_IDX_ONLY; >> + pLoop->nLTerm =3D j; >> + pLoop->nEq =3D j; >> + pLoop->pIndex =3D NULL; >> + pLoop->index_def =3D idx_def; >> + /* TUNING: Cost of a unique index lookup = is 15 */ >> + pLoop->rRun =3D 39; /* = 39=3D=3Dsqlite3LogEst(15) */ >> + break; >=20 > 17. Too much copypaste. Can you please reduce duplicating? See patch below. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =46rom 9ced467a2f33ce02ac48f875db06d3eec7d5561d 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 | 45 +++---- src/box/sql/expr.c | 9 +- src/box/sql/fkey.c | 7 +- src/box/sql/insert.c | 36 +++--- src/box/sql/resolve.c | 18 +++ src/box/sql/select.c | 5 +- src/box/sql/sqliteInt.h | 42 ++++--- src/box/sql/where.c | 309 = +++++++++++++++++++++++++----------------------- src/box/sql/wherecode.c | 5 +- 11 files changed, 256 insertions(+), 253 deletions(-) diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c index 9509645c0..28d68a55b 100644 --- a/src/box/sql/analyze.c +++ b/src/box/sql/analyze.c @@ -176,9 +176,9 @@ openStatTable(Parse * pParse, /* Parsing = context */ =20 /* Open the sql_stat[134] tables for writing. */ for (i =3D 0; aTable[i]; i++) { - int addr =3D emit_open_cursor(pParse, iStatCur + i, = aRoot[i]); - v->aOp[addr].p4.key_def =3D NULL; - v->aOp[addr].p4type =3D P4_KEYDEF; + struct space *space =3D + space_by_id(SQLITE_PAGENO_TO_SPACEID(aRoot[i])); + sql_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 552578048..8c83a797e 100644 --- a/src/box/sql/build.c +++ b/src/box/sql/build.c @@ -1181,35 +1181,17 @@ space_checks_expr_list(uint32_t space_id) } =20 int -emit_open_cursor(struct Parse *parse_context, int cursor, int = entity_id) +sql_emit_open_cursor(struct Parse *parse_context, int cursor, int = index_id, + struct space *space) { - assert(entity_id > 0); - struct space *space =3D = space_by_id(SQLITE_PAGENO_TO_SPACEID(entity_id)); assert(space !=3D NULL); struct Vdbe *vdbe =3D parse_context->pVdbe; int space_ptr_reg =3D ++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 !=3D NULL); - Vdbe *vdbe =3D parse->pVdbe; - int space_ptr_reg =3D ++parse->nMem; - sqlite3VdbeAddOp4(vdbe, OP_LoadPtr, 0, space_ptr_reg, 0, = (void*)space, - P4_SPACEPTR); - struct key_def *def =3D = key_def_dup(space->index[index_id]->def->key_def); - if (def =3D=3D 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. * @@ -2668,7 +2650,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 =3D = space_by_id(SQLITE_PAGENO_TO_SPACEID(tnum)); + sql_emit_open_cursor(pParse, iIdx, tnum, space); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR | ((memRootPage >=3D 0) ? OPFLAG_P2ISREG : 0)); diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index 23e2bcc49..1beacec48 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 =3D NULL; - struct space *space; + struct space *space =3D NULL; uint32_t space_id; /* Figure out if we have any triggers and if the table * being deleted from is a view. @@ -104,17 +104,28 @@ sql_table_delete_from(struct Parse *parse, struct = SrcList *tab_list, strlen(tab_name)); if (space_id =3D=3D 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 =3D space_by_id(space_id); + tab_list->a[0].pTab =3D = sql_table_construct_from_space(space); + if (tab_list->a[0].pTab =3D=3D NULL) + goto delete_from_cleanup; } else { table =3D sql_list_lookup_table(parse, tab_list); if (table =3D=3D NULL) goto delete_from_cleanup; space_id =3D SQLITE_PAGENO_TO_SPACEID(table->tnum); + space =3D space_by_id(space_id); + assert(space !=3D NULL); trigger_list =3Dsqlite3TriggersExist(table, TK_DELETE, NULL, NULL); is_complex =3D trigger_list !=3D NULL || sqlite3FkRequired(table, NULL); } - space =3D space_by_id(space_id); assert(space !=3D NULL); =20 bool is_view =3D space->def->opts.is_view; @@ -183,16 +194,6 @@ sql_table_delete_from(struct Parse *parse, struct = SrcList *tab_list, struct NameContext nc; memset(&nc, 0, sizeof(nc)); nc.pParse =3D parse; - if (tab_list->a[0].pTab =3D=3D NULL) { - struct Table *t =3D calloc(sizeof(struct Table), = 1); - if (t =3D=3D NULL) { - sqlite3OomFault(parse->db); - goto delete_from_cleanup; - } - assert(space !=3D NULL); - t->def =3D space->def; - tab_list->a[0].pTab =3D t; - } nc.pSrcList =3D tab_list; if (sqlite3ResolveExprNames(&nc, where)) goto delete_from_cleanup; @@ -266,11 +267,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 =3D 0; i < pk_len; i++) { - struct space_def *def; - if (table !=3D NULL) - def =3D table->def; - else - def =3D space->def; + struct space_def *def =3D space->def; sqlite3ExprCodeGetColumnOfTable(v, def, = tab_cursor, = pk_def->parts[i].fieldno, @@ -342,15 +339,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); =20 - int tnum; - if (table !=3D NULL) { - tnum =3D table->tnum; - } - else { - /* index id is 0 for PK. */ - tnum =3D = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space->def->id, - = 0); - } + int tnum =3D + = SQLITE_PAGENO_FROM_SPACEID_AND_INDEXID(space->def->id, + = 0); sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor, tnum, space_ptr_reg); struct key_def *def =3D key_def_dup(pk_def); @@ -518,7 +509,7 @@ sql_generate_row_delete(struct Parse *parse, struct = Table *table, * of the DELETE statement is to fire the INSTEAD OF * triggers). */ - if (table =3D=3D NULL || table->pSelect =3D=3D NULL) { + if (table =3D=3D NULL || !table->def->opts.is_view) { uint8_t p5 =3D 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 a69d38bd0..1a5e130d0 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" =20 /* 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 =3D + = space_by_id(SQLITE_PAGENO_TO_SPACEID(pIdx->tnum)); + sql_emit_open_cursor(pParse, = iTab, + pIdx->tnum, = space); VdbeComment((v, "%s", = pIdx->zName)); assert(IN_INDEX_INDEX_DESC =3D=3D IN_INDEX_INDEX_ASC + 1); diff --git a/src/box/sql/fkey.c b/src/box/sql/fkey.c index 6fd29200c..74cf51302 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" =20 @@ -439,9 +440,9 @@ fkLookupParent(Parse * pParse, /* Parse context = */ int nCol =3D pFKey->nCol; int regTemp =3D sqlite3GetTempRange(pParse, = nCol); int regRec =3D sqlite3GetTempReg(pParse); - - emit_open_cursor(pParse, iCur, pIdx->tnum); - sql_vdbe_set_p4_key_def(pParse, pIdx); + struct space *space =3D + = space_by_id(SQLITE_PAGENO_TO_SPACEID(pIdx->tnum)); + sql_emit_open_cursor(pParse, iCur, pIdx->tnum, = space); for (i =3D 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 7aba83529..0a0b3fc21 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 =3D sqlite3PrimaryKeyIndex(pTab); assert(pPk !=3D 0); assert(pPk->tnum =3D=3D pTab->tnum); - emit_open_cursor(pParse, iCur, pPk->tnum); - sql_vdbe_set_p4_key_def(pParse, pPk); + struct space *space =3D = space_by_id(SQLITE_PAGENO_TO_SPACEID(pPk->tnum)); + sql_emit_open_cursor(pParse, iCur, pPk->tnum, space); VdbeComment((v, "%s", pTab->def->name)); } =20 @@ -107,32 +107,22 @@ sqlite3IndexAffinityStr(sqlite3 *db, Index *index) char * 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 =3D def->key_def->part_count; - aff =3D (char *)sqlite3DbMallocRaw(0, nColumn + 1); + uint32_t column_count =3D def->key_def->part_count; + char *aff =3D (char *)sqlite3DbMallocRaw(0, column_count + 1); if (aff =3D=3D NULL) { sqlite3OomFault(db); - return 0; + return NULL; } - int i; - struct space *space =3D space_cache_find(def->space_id); + struct space *space =3D space_by_id(def->space_id); assert(space !=3D NULL); =20 - for (i =3D 0; i < nColumn; i++) { + for (uint32_t i =3D 0; i < column_count; i++) { uint32_t x =3D def->key_def->parts[i].fieldno; aff[i] =3D space->def->fields[x].affinity; if (aff[i] =3D=3D AFFINITY_UNDEFINED) aff[i] =3D 'A'; } - aff[i] =3D 0; + aff[column_count] =3D '\0'; =20 return aff; } @@ -1951,11 +1941,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 =3D + = space_by_id(SQLITE_PAGENO_TO_SPACEID(pSrcIdx->tnum)); + sql_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 =3D + = space_by_id(SQLITE_PAGENO_TO_SPACEID(pDestIdx->tnum)); + sql_emit_open_cursor(pParse, iDest, pDestIdx->tnum, = dest_space); sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR); VdbeComment((v, "%s", pDestIdx->zName)); addr1 =3D sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); diff --git a/src/box/sql/resolve.c b/src/box/sql/resolve.c index 23e16189a..e7d9723fa 100644 --- a/src/box/sql/resolve.c +++ b/src/box/sql/resolve.c @@ -39,6 +39,9 @@ #include #include =20 +#include "box/box.h" +#include "box/schema.h" + /* * Walk the expression tree pExpr and increase the aggregate function * depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. @@ -1432,6 +1435,21 @@ resolveSelectStep(Walker * pWalker, Select * p) return WRC_Prune; } =20 +struct Table * +sql_table_construct_from_space(const struct space *space) +{ + assert(space !=3D NULL); + struct Table *table =3D calloc(1, sizeof(*table)); + if (table =3D=3D NULL) { + diag_set(OutOfMemory, sizeof(table), "calloc", "table"); + return NULL; + } + table->def =3D space_def_dup(space->def); + if (table->def =3D=3D NULL) + return NULL; + return table; +} + /* * This routine walks an expression tree and resolves references to * table columns and result-set columns. At the same time, do error diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 4b5ba4d3e..9e8ed97e7 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); + sql_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 878daa8df..b7e7eafa8 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); =20 void sqlite3EndTable(Parse *, Token *, Token *, Select *); =20 -/** - * 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, @@ -4125,9 +4109,10 @@ int sqlite3VarintLen(u64 v); const char *sqlite3IndexAffinityStr(sqlite3 *, Index *); =20 /** - * 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); @@ -4262,6 +4246,20 @@ void sqlite3SelectWrongNumTermsError(struct Parse *parse, struct Select *p); =20 int sqlite3MatchSpanName(const char *, const char *, const char *); + +/** + * Create wrapper around space_def of the given space. + * This routine is required to support original SQLite interface, + * which accepts struct Table and struct Index DD arguments. + * Both table and space def are allocated on heap. + * + * @param space Space to be prototype. + * @retval New instance of struct Table allocated on malloc, + * or NULL in case of OOM. + */ +struct Table * +sql_table_construct_from_space(const struct space *space); + int sqlite3ResolveExprNames(NameContext *, Expr *); int sqlite3ResolveExprListNames(NameContext *, ExprList *); void sqlite3ResolveSelectNames(Parse *, Select *, NameContext *); diff --git a/src/box/sql/where.c b/src/box/sql/where.c index f017384b5..a3a5bec81 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" =20 @@ -371,10 +372,7 @@ whereScanInit(WhereScan * pScan, /* The WhereScan = object being initialized */ pScan->is_column_seen =3D false; if (pIdx) { int j =3D iColumn; - if (j >=3D pIdx->nColumn) - iColumn =3D -1; - else - iColumn =3D pIdx->aiColumn[j]; + iColumn =3D pIdx->aiColumn[j]; if (iColumn >=3D 0) { char affinity =3D = pIdx->pTable->def->fields[iColumn].affinity; @@ -393,12 +391,30 @@ whereScanInit(WhereScan * pScan, /* The WhereScan = object being initialized */ return whereScanNext(pScan); } =20 +/** + * 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 =3D scan->pWC =3D clause; + scan->pOrigWC =3D clause; + scan->pWC =3D clause; scan->pIdxExpr =3D NULL; scan->idxaff =3D 0; scan->coll =3D NULL; @@ -406,11 +422,11 @@ sql_where_scan_init(struct WhereScan *scan, struct = WhereClause *clause, if (key_def !=3D NULL) { int j =3D column; column =3D key_def->parts[j].fieldno; - if (column >=3D 0) { - scan->idxaff =3D = space_def->fields[column].affinity; - scan->coll =3D key_def->parts[column].coll; - scan->is_column_seen =3D true; - } + scan->idxaff =3D space_def->fields[column].affinity; + uint32_t coll_id =3D space_def->fields[column].coll_id; + struct coll_id *coll =3D coll_by_id(coll_id); + scan->coll =3D coll !=3D NULL ? coll->coll : NULL; + scan->is_column_seen =3D true; } scan->opMask =3D op_mask; scan->k =3D 0; @@ -472,34 +488,43 @@ sqlite3WhereFindTerm(WhereClause * pWC, /* The = WHERE clause to be searched */ return pResult; } =20 +/** + * 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. + */ 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 */ +sql_where_find_term(WhereClause *where_clause, int cursor, int column, + Bitmask is_ready, u32 op, struct space_def = *space_def, + struct key_def *key_def) { - WhereTerm *pResult =3D 0; - WhereTerm *p; + WhereTerm *result =3D NULL; WhereScan scan; - - p =3D sql_where_scan_init(&scan, pWC, iCur, iColumn, op, = space_def, - key_def); + WhereTerm *p =3D where_scan_init(&scan, where_clause, cursor, = column, + op, space_def, key_def); op &=3D WO_EQ; - while (p) { - if ((p->prereqRight & notReady) =3D=3D 0) { - if (p->prereqRight =3D=3D 0 && (p->eOperator & = op) !=3D 0) { - testcase(p->eOperator & WO_IS); + while (p !=3D NULL) { + if ((p->prereqRight & is_ready) =3D=3D 0) { + if (p->prereqRight =3D=3D 0 && (p->eOperator & = op) !=3D 0) return p; - } - if (pResult =3D=3D 0) - pResult =3D p; + if (result =3D=3D NULL) + result =3D p; } p =3D whereScanNext(&scan); } - return pResult; + return result; } =20 /* @@ -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 !=3D NULL) { iColumn =3D pIndex->aiColumn[j]; revIdx =3D = sql_index_column_sort_order(pIndex, = j); @@ -4096,135 +4121,125 @@ wherePathSolver(WhereInfo * pWInfo, LogEst = nRowEst) return SQLITE_OK; } =20 -/* - * Most queries use only a single table (they are not joins) and have - * simple =3D=3D 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_assign_loop_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 =3D idx_def !=3D NULL ? = idx_def->key_def->part_count : + space_def->field_count; + if (column_count > ArraySize(loop->aLTermSpace)) + return -1; + uint32_t i; + for (i =3D 0; i < column_count; ++i) { + struct WhereTerm *term =3D + sql_where_find_term(where, cursor, i, 0, WO_EQ, + space_def, idx_def !=3D NULL = ? + idx_def->key_def : NULL); + if (term =3D=3D NULL) + break; + testcase(pTerm->eOperator & WO_IS); + loop->aLTerm[i] =3D term; + } + if (i !=3D column_count) + return -1; + loop->wsFlags =3D WHERE_COLUMN_EQ | WHERE_ONEROW | WHERE_INDEXED = | + WHERE_IDX_ONLY; + loop->nLTerm =3D i; + loop->nEq =3D i; + loop->index_def =3D idx_def; + /* TUNING: Cost of a unique index lookup is 15. */ + assert(39 =3D=3D sqlite3LogEst(15)); + loop->rRun =3D 39; + return 0; +} =20 - pWInfo =3D builder->pWInfo; - if (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE) +/** + * Most queries use only a single table (they are not joins) and + * have simple =3D=3D 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 +sql_where_shortcut(struct WhereLoopBuilder *builder) +{ + struct WhereInfo *where_info =3D builder->pWInfo; + if (where_info->wctrlFlags & WHERE_OR_SUBCLAUSE) return 0; - assert(pWInfo->pTabList->nSrc >=3D 1); - pItem =3D pWInfo->pTabList->a; - struct space_def *space_def =3D pItem->pTab->def; + assert(where_info->pTabList->nSrc >=3D 1); + struct SrcList_item *item =3D where_info->pTabList->a; + struct space_def *space_def =3D item->pTab->def; assert(space_def !=3D NULL); - - if (pItem->fg.isIndexedBy) + if (item->fg.isIndexedBy) return 0; - iCur =3D pItem->iCursor; - pWC =3D &pWInfo->sWC; - pLoop =3D builder->pNew; - pLoop->wsFlags =3D 0; - pLoop->nSkip =3D 0; - pTerm =3D sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ, 0); - if (pTerm) { - pLoop->wsFlags =3D WHERE_COLUMN_EQ | WHERE_IPK | = WHERE_ONEROW; - pLoop->aLTerm[0] =3D pTerm; - pLoop->nLTerm =3D 1; - pLoop->nEq =3D 1; - /* TUNING: Cost of a PK lookup is 10 */ - pLoop->rRun =3D 33; /* 33=3D=3Dsqlite3LogEst(10) */ + int cursor =3D item->iCursor; + struct WhereClause *clause =3D &where_info->sWC; + struct WhereLoop *loop =3D builder->pNew; + loop->wsFlags =3D 0; + loop->nSkip =3D 0; + loop->pIndex =3D NULL; + struct WhereTerm *term =3D sqlite3WhereFindTerm(clause, cursor, = -1, 0, + WO_EQ, 0); + if (term !=3D NULL) { + loop->wsFlags =3D WHERE_COLUMN_EQ | WHERE_IPK | = WHERE_ONEROW; + loop->aLTerm[0] =3D term; + loop->nLTerm =3D 1; + loop->nEq =3D 1; + /* TUNING: Cost of a PK lookup is 10. */ + assert(33 =3D=3D sqlite3LogEst(10)); + loop->rRun =3D 33; } else { - struct space *space =3D space_cache_find(space_def->id); + assert(loop->aLTermSpace =3D=3D loop->aLTerm); + struct space *space =3D space_by_id(space_def->id); if (space !=3D NULL) { for (uint32_t i =3D 0; i < space->index_count; = ++i) { - struct index_def *idx_def; - idx_def =3D space->index[i]->def; - int opMask; - int nIdxCol =3D = idx_def->key_def->part_count; - assert(pLoop->aLTermSpace =3D=3D = pLoop->aLTerm); - if (!idx_def->opts.is_unique - /* || pIdx->pPartIdxWhere !=3D 0 */ - || nIdxCol > = ArraySize(pLoop->aLTermSpace) - ) + struct index_def *idx_def =3D + space->index[i]->def; + if (!idx_def->opts.is_unique) continue; - opMask =3D WO_EQ; - for (j =3D 0; j < nIdxCol; j++) { - pTerm =3D = sql_where_find_term(pWC, iCur, - j, = 0, - = opMask, - = space_def, - = idx_def-> - = key_def); - if (pTerm =3D=3D 0) - break; - testcase(pTerm->eOperator & = WO_IS); - pLoop->aLTerm[j] =3D pTerm; - } - if (j !=3D nIdxCol) - continue; - pLoop->wsFlags =3D WHERE_COLUMN_EQ | - WHERE_ONEROW | WHERE_INDEXED | - WHERE_IDX_ONLY; - pLoop->nLTerm =3D j; - pLoop->nEq =3D j; - pLoop->pIndex =3D NULL; - pLoop->index_def =3D idx_def; - /* TUNING: Cost of a unique index lookup = is 15 */ - pLoop->rRun =3D 39; /* = 39=3D=3Dsqlite3LogEst(15) */ - break; + if (where_assign_loop_terms(loop, = clause, + cursor, = space_def, + idx_def) =3D=3D= 0) + break; } } else { - /* Space is ephemeral. */ + /* Space is ephemeral. */ assert(space_def->id =3D=3D 0); - int opMask; - int nIdxCol =3D space_def->field_count; - assert(pLoop->aLTermSpace =3D=3D pLoop->aLTerm); - if ( nIdxCol > ArraySize(pLoop->aLTermSpace)) - return 0; - opMask =3D WO_EQ; - for (j =3D 0; j < nIdxCol; j++) { - pTerm =3D sql_where_find_term(pWC, iCur, - j, 0, - opMask, - space_def, - NULL); - if (pTerm =3D=3D NULL) - break; - pLoop->aLTerm[j] =3D pTerm; - } - if (j !=3D nIdxCol) - return 0; - pLoop->wsFlags =3D WHERE_COLUMN_EQ | = WHERE_ONEROW | - WHERE_INDEXED | WHERE_IDX_ONLY; - pLoop->nLTerm =3D j; - pLoop->nEq =3D j; - pLoop->pIndex =3D NULL; - pLoop->index_def =3D NULL; - /* TUNING: Cost of a unique index lookup is 15 = */ - pLoop->rRun =3D 39; /* 39=3D=3Dsqlite3LogEst(1= 5) */ + where_assign_loop_terms(loop, clause, cursor, + space_def, NULL); } } - if (pLoop->wsFlags) { - pLoop->nOut =3D (LogEst) 1; - pWInfo->a[0].pWLoop =3D pLoop; - pLoop->maskSelf =3D = sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur); - pWInfo->a[0].iTabCur =3D iCur; - pWInfo->nRowOut =3D 1; - if (pWInfo->pOrderBy) - pWInfo->nOBSat =3D pWInfo->pOrderBy->nExpr; - if (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT) { - pWInfo->eDistinct =3D WHERE_DISTINCT_UNIQUE; + if (loop->wsFlags) { + loop->nOut =3D (LogEst) 1; + where_info->a[0].pWLoop =3D loop; + loop->maskSelf =3D = sqlite3WhereGetMask(&where_info->sMaskSet, + cursor); + where_info->a[0].iTabCur =3D cursor; + where_info->nRowOut =3D 1; + if (where_info->pOrderBy) + where_info->nOBSat =3D = where_info->pOrderBy->nExpr; + if (where_info->wctrlFlags & WHERE_WANT_DISTINCT) { + where_info->eDistinct =3D WHERE_DISTINCT_UNIQUE; } #ifdef SQLITE_DEBUG - pLoop->cId =3D '0'; + loop->cId =3D '0'; #endif return 1; } @@ -4704,8 +4719,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser = context */ */ if (idx_def =3D=3D NULL && pIx =3D=3D NULL) = continue; bool is_primary =3D (pIx !=3D NULL && = IsPrimaryKeyIndex(pIx)) || - (idx_def !=3D NULL && = (idx_def->iid =3D=3D 0)) | - (idx_def =3D=3D NULL && pIx =3D=3D = NULL); + (idx_def !=3D NULL && = (idx_def->iid =3D=3D 0)); if (is_primary && (wctrlFlags & WHERE_OR_SUBCLAUSE) !=3D 0) = { /* This is one term of an = OR-optimization using @@ -4752,9 +4766,10 @@ sqlite3WhereBegin(Parse * pParse, /* The = parser context */ assert(iIndexCur >=3D 0); if (op) { if (pIx !=3D NULL) { - emit_open_cursor(pParse, = iIndexCur, - pIx->tnum); - sql_vdbe_set_p4_key_def(pParse, = pIx); + struct space *space =3D + = space_by_id(SQLITE_PAGENO_TO_SPACEID(pIx->tnum)); + sql_emit_open_cursor(pParse, = iIndexCur, + pIx->tnum, = space); } else { sql_emit_open_cursor(pParse, = iIndexCur, = idx_def->iid, diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c index 60e0ac7e4..eaab0b657 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 !=3D NULL); struct space *space =3D = space_cache_find(def->space_id); assert(space !=3D NULL); - name =3D space->def->fields[i].name; + name =3D space->def->fields[i + iTerm].name; } sqlite3StrAccumAppendAll(pStr, name); } @@ -143,7 +143,8 @@ explainIndexRange(StrAccum * pStr, WhereLoop * = pLoop) } else { struct space *space =3D = space_cache_find(def->space_id); assert(space !=3D NULL); - z =3D space->def->fields[i].name; + uint32_t fieldno =3D = def->key_def->parts[i].fieldno; + z =3D space->def->fields[fieldno].name; } if (i) sqlite3StrAccumAppend(pStr, " AND ", 5); --=20 2.15.1