[tarantool-patches] Re: [PATCH 3/3] sql: implement point where for DELETE stmts
n.pettik
korablev at tarantool.org
Mon Jun 18 05:11:32 MSK 2018
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
>
>> }
>> 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)
>
> 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)
}
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 = 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);
-}
>
>> +{
>> + 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);
>
> 3. Looks like the arguments can fit in 2 lines instead of 4.
Fixed. See code above.
>
>> +}
>> 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 = parse;
>> if (tab_list->a[0].pTab == NULL) {
>> - struct Table *t = malloc(sizeof(struct Table));
>> + struct Table *t = calloc(sizeof(struct Table), 1);
>
> 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 = 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.
@@ -104,17 +104,28 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
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);
+ tab_list->a[0].pTab = sql_table_construct_from_space(space);
+ if (tab_list->a[0].pTab == NULL)
+ goto delete_from_cleanup;
} 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);
@@ -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 = 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;
- }
+++ b/src/box/sql/resolve.c
@@ -39,6 +39,9 @@
#include <stdlib.h>
#include <string.h>
+#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;
}
+struct Table *
+sql_table_construct_from_space(const struct space *space)
+{
+ assert(space != NULL);
+ struct Table *table = calloc(1, sizeof(*table));
+ if (table == NULL) {
+ diag_set(OutOfMemory, sizeof(table), "calloc", "table");
+ return NULL;
+ }
+ table->def = space_def_dup(space->def);
+ if (table->def == 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);
>
>> if (t == 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 = 0; i < pk_len; i++) {
>> - sqlite3ExprCodeGetColumnOfTable(v, table->def,
>> + struct space_def *def;
>> + if (table != NULL)
>> + def = table->def;
>> + else
>> + def = space->def;
>
> 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 = 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,
>> reg_pk + i);
>> @@ -339,8 +344,18 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>> int space_ptr_reg = ++parse->nMem;
>> 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);
>> + }
>
> 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);
- 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->def->id,
+ 0);
>
>> sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor,
>> - table->tnum, space_ptr_reg);
>> + tnum, space_ptr_reg);
>> struct key_def *def = key_def_dup(pk_def);
>> if (def == 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 == NULL) {
>> + if (table == NULL || table->pSelect == NULL) {
>
> 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 == 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/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.
>
> 8. No, it is not populated.
Deleted the whole comment - it doesn’t seem to be relevant.
And slightly refactored function:
@@ -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 = def->key_def->part_count;
- aff = (char *)sqlite3DbMallocRaw(0, nColumn + 1);
+ uint32_t column_count = def->key_def->part_count;
+ char *aff = (char *)sqlite3DbMallocRaw(0, column_count + 1);
if (aff == NULL) {
sqlite3OomFault(db);
- return 0;
+ return NULL;
}
- int i;
- struct space *space = space_cache_find(def->space_id);
+ 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;
}
>
>> + *
>> + * 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);
>
> 9. Why do you need to declare 'char *aff;' separately above?
Fixed, see diff above.
>
>> + if (aff == NULL) {
>> + sqlite3OomFault(db);
>> + return 0;
>> + }
>> + int i;
>
> 10. Can be part of 'for’.
Fixed, see diff above.
>
>> + struct space *space = space_cache_find(def->space_id);
>> + assert(space != NULL);
>> +
>> + for (i = 0; i < nColumn; 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;
>> +
>> + 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;
>
> 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;
>
>> 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 = false;
>> if (pIdx) {
>> int j = iColumn;
>> - iColumn = pIdx->aiColumn[j];
>> + if (j >= pIdx->nColumn)
>> + iColumn = -1;
>> + else
>> + iColumn = pIdx->aiColumn[j];
>
> 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 = 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;
>> @@ -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 = 0;
>> + WhereTerm *p;
>> + WhereScan scan;
>> +
>> + p = sql_where_scan_init(&scan, pWC, iCur, iColumn, op, space_def,
>> + key_def);
>
> 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.
>
>> + op &= WO_EQ;
>> + while (p) {
>> + if ((p->prereqRight & notReady) == 0) {
>> + if (p->prereqRight == 0 && (p->eOperator & op) != 0) {
>> + testcase(p->eOperator & WO_IS);
>> + return p;
>> + }
>> + if (pResult == 0)
>> + pResult = p;
>> + }
>> + p = 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 = 0;
>> p->nLSlot = ArraySize(p->aLTermSpace);
>> p->wsFlags = 0;
>> + p->index_def = NULL;
>
> 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.
>
>> }
>> /*
>> @@ -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) {
>
> 15. pIndex != NULL
>
> 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 != NULL) {
>
>> iColumn = pIndex->aiColumn[j];
>> revIdx = sql_index_column_sort_order(pIndex,
>> j);
>> @@ -4080,34 +4142,74 @@ whereShortCut(WhereLoopBuilder * pBuilder)
>> /* TUNING: Cost of a PK lookup is 10 */
>> pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
>> } else {
>> - for (pIdx = pTab->pIndex; pIdx; pIdx = pIdx->pNext) {
>> + struct space *space = space_cache_find(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)
>> + )
>> + 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;
>
> 17. Too much copypaste. Can you please reduce duplicating?
See patch below.
=======================================================================
From 9ced467a2f33ce02ac48f875db06d3eec7d5561d Mon Sep 17 00:00:00 2001
From: Nikita Pettik <korablev at tarantool.org>
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 */
/* 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]));
+ 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)
}
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 = 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.
*
@@ -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 = space_by_id(SQLITE_PAGENO_TO_SPACEID(tnum));
+ sql_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 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 = 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.
@@ -104,17 +104,28 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
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);
+ tab_list->a[0].pTab = sql_table_construct_from_space(space);
+ if (tab_list->a[0].pTab == NULL)
+ goto delete_from_cleanup;
} 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 +194,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 +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 = 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 +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);
- 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->def->id,
+ 0);
sqlite3VdbeAddOp3(v, OP_OpenWrite, tab_cursor,
tnum, space_ptr_reg);
struct key_def *def = 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 == 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 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"
/* 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));
+ sql_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 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"
@@ -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));
+ sql_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 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 = 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));
+ sql_emit_open_cursor(pParse, iCur, pPk->tnum, space);
VdbeComment((v, "%s", pTab->def->name));
}
@@ -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 = def->key_def->part_count;
- aff = (char *)sqlite3DbMallocRaw(0, nColumn + 1);
+ uint32_t column_count = def->key_def->part_count;
+ char *aff = (char *)sqlite3DbMallocRaw(0, column_count + 1);
if (aff == NULL) {
sqlite3OomFault(db);
- return 0;
+ return NULL;
}
- int i;
- struct space *space = space_cache_find(def->space_id);
+ 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 +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 =
+ 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 =
+ 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 = 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 <stdlib.h>
#include <string.h>
+#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;
}
+struct Table *
+sql_table_construct_from_space(const struct space *space)
+{
+ assert(space != NULL);
+ struct Table *table = calloc(1, sizeof(*table));
+ if (table == NULL) {
+ diag_set(OutOfMemory, sizeof(table), "calloc", "table");
+ return NULL;
+ }
+ table->def = space_def_dup(space->def);
+ if (table->def == 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);
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,
@@ -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);
@@ -4262,6 +4246,20 @@ void
sqlite3SelectWrongNumTermsError(struct Parse *parse, struct Select *p);
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"
@@ -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;
}
+/**
+ * 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 = 0;
- WhereTerm *p;
+ WhereTerm *result = NULL;
WhereScan scan;
-
- p = sql_where_scan_init(&scan, pWC, iCur, iColumn, op, space_def,
- key_def);
+ 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_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 = 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 =
+ sql_where_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
+sql_where_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_assign_loop_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_assign_loop_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;
}
@@ -4704,8 +4719,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
*/
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,9 +4766,10 @@ 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));
+ 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 != 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);
--
2.15.1
More information about the Tarantool-patches
mailing list