* [tarantool-patches] [PATCH] sql: refactor SQL cursor to remove write ones
@ 2018-10-01 10:31 Kirill Yukhin
2018-10-02 11:56 ` [tarantool-patches] " n.pettik
2018-10-04 12:00 ` Kirill Yukhin
0 siblings, 2 replies; 9+ messages in thread
From: Kirill Yukhin @ 2018-10-01 10:31 UTC (permalink / raw)
To: korablev; +Cc: tarantool-patches, Kirill Yukhin
In Tarantool opening and positioning cursor for writing
have no sense. So refactor SQL code, to perform:
- Creation of ephemeral table w/o any cursors machinery.
No op-code returns register which contains plain pointer
to the ephemeral space.
- OpenRead/OpenWrite opcodes were replaced with single
CursorOpen op-code, which establishes new cursor w/
intention to sebsequent read from the space. This opcode
accepts both plain pointer (in P4 operand) and register
which contains pointer (inn P3) to the ephemeral space.
This query scheduler and DML routines thoroughly.
Closes #3182
Part of #2362
---
Branch: https://github.com/tarantool/tarantool/commits/kyukhin/gh-3182-repair-cursors-2
Issue: https://github.com/tarantool/tarantool/issues/3182
src/box/sql.c | 65 ++++-------
src/box/sql/analyze.c | 64 ++++-------
src/box/sql/build.c | 4 +-
src/box/sql/delete.c | 25 +++--
src/box/sql/expr.c | 39 ++++---
src/box/sql/insert.c | 40 ++++---
src/box/sql/parse.y | 2 +-
src/box/sql/select.c | 174 ++++++++++++++++++++----------
src/box/sql/sqliteInt.h | 21 ++--
src/box/sql/tarantoolInt.h | 32 +++++-
src/box/sql/trigger.c | 12 +--
src/box/sql/update.c | 19 ++--
src/box/sql/vdbe.c | 264 ++++++++++++++++++++++-----------------------
src/box/sql/where.c | 13 +--
src/box/sql/whereInt.h | 2 +-
src/box/sql/wherecode.c | 12 ++-
16 files changed, 411 insertions(+), 377 deletions(-)
diff --git a/src/box/sql.c b/src/box/sql.c
index ab4a587..c5461ad 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
return SQLITE_OK;
}
-/**
- * Create ephemeral space and set cursor to the first entry. Features of
- * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
- * can be changed, but now only memtx engine is supported), primary index
- * which covers all fields and no secondary indexes, given field number and
- * collation sequence. All fields are scalar and nullable.
- *
- * @param pCur Cursor which will point to the new ephemeral space.
- * @param field_count Number of fields in ephemeral space.
- * @param key_info Keys description for new ephemeral space.
- *
- * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
- */
-int
-tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
+struct space *
+tarantoolSqlite3EphemeralCreate(uint32_t field_count,
struct sql_key_info *key_info)
{
- assert(pCur);
- assert(pCur->curFlags & BTCF_TEphemCursor);
-
struct key_def *def = NULL;
if (key_info != NULL) {
def = sql_key_info_to_key_def(key_info);
if (def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
struct key_part_def *ephemer_key_parts = region_alloc(&fiber()->gc,
@@ -387,7 +371,7 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
if (ephemer_key_parts == NULL) {
diag_set(OutOfMemory, sizeof(*ephemer_key_parts) * field_count,
"region", "key parts");
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
for (uint32_t i = 0; i < field_count; ++i) {
struct key_part_def *part = &ephemer_key_parts[i];
@@ -404,14 +388,14 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
struct key_def *ephemer_key_def = key_def_new(ephemer_key_parts,
field_count);
if (ephemer_key_def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
struct index_def *ephemer_index_def =
index_def_new(0, 0, "ephemer_idx", strlen("ephemer_idx"), TREE,
&index_opts_default, ephemer_key_def, NULL);
key_def_delete(ephemer_key_def);
if (ephemer_index_def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
struct rlist key_list;
rlist_create(&key_list);
@@ -425,24 +409,15 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
0 /* length of field_def */);
if (ephemer_space_def == NULL) {
index_def_delete(ephemer_index_def);
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
struct space *ephemer_new_space = space_new_ephemeral(ephemer_space_def,
&key_list);
index_def_delete(ephemer_index_def);
space_def_delete(ephemer_space_def);
- if (ephemer_new_space == NULL)
- return SQL_TARANTOOL_ERROR;
- if (key_alloc(pCur, field_count) != 0) {
- space_delete(ephemer_new_space);
- return SQL_TARANTOOL_ERROR;
- }
- pCur->space = ephemer_new_space;
- pCur->index = *ephemer_new_space->index;
- int unused;
- return tarantoolSqlite3First(pCur, &unused);
+ return ephemer_new_space;
}
int tarantoolSqlite3EphemeralInsert(struct space *space, const char *tuple,
@@ -1461,35 +1436,31 @@ sql_debug_info(struct info_handler *h)
info_end(h);
}
-/*
+/**
* Extract maximum integer value from ephemeral space.
* If index is empty - return 0 in max_id and success status.
*
- * @param pCur Cursor pointing to ephemeral space.
+ * @param space Pointer to ephemeral space.
* @param fieldno Number of field from fetching tuple.
* @param[out] max_id Fetched max value.
*
* @retval 0 on success, -1 otherwise.
*/
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor *pCur, uint32_t fieldno,
- uint64_t *max_id)
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
+ uint64_t *max_id)
{
- struct space *ephem_space = pCur->space;
- assert(ephem_space);
- struct index *primary_index = *ephem_space->index;
-
+ struct index *primary_index = *space->index;
struct tuple *tuple;
- if (index_max(primary_index, NULL, 0, &tuple) != 0) {
- return SQL_TARANTOOL_ERROR;
- }
+ if (index_max(primary_index, NULL, 0, &tuple) != 0)
+ return -1;
if (tuple == NULL) {
*max_id = 0;
- return SQLITE_OK;
+ return 0;
}
if (tuple_field_u64(tuple, fieldno, max_id) == -1)
- return SQL_TARANTOOL_ERROR;
+ return -1;
- return SQLITE_OK;
+ return 0;
}
int
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 01e2ad1..36bbcff 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -122,13 +122,10 @@
* created.
*
* @param parse Parsing context.
- * @param stat_cursor Open the _sql_stat1 table on this cursor.
- * you should allocate |stat_names| cursors before call.
* @param table_name Delete records of this table if specified.
*/
static void
-vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
- const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
{
const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -144,14 +141,6 @@ vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
}
}
-
- /* Open the sql_stat tables for writing. */
- for (uint i = 0; i < lengthof(stat_names); ++i) {
- uint32_t id = stat_ids[i];
- vdbe_emit_open_cursor(parse, stat_cursor + i, 0,
- space_by_id(id));
- VdbeComment((v, stat_names[i]));
- }
}
/*
@@ -770,15 +759,16 @@ callStatGet(Vdbe * v, int regStat4, int iParam, int regOut)
*
* @param parse Current parsing context.
* @param space Space to be analyzed.
- * @param stat_cursor Cursor pointing to spaces containing
- * statistics: _sql_stat1 (stat_cursor) and
- * _sql_stat4 (stat_cursor + 1).
*/
static void
-vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
- int stat_cursor)
+vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
{
assert(space != NULL);
+ struct space *stat1 = space_by_id(BOX_SQL_STAT1_ID);
+ assert(stat1 != NULL);
+ struct space *stat4 = space_by_id(BOX_SQL_STAT4_ID);
+ assert(stat4 != NULL);
+
/* Register to hold Stat4Accum object. */
int stat4_reg = ++parse->nMem;
/* Index of changed index field. */
@@ -808,7 +798,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
struct Vdbe *v = sqlite3GetVdbe(parse);
assert(v != NULL);
const char *tab_name = space_name(space);
- sqlite3VdbeAddOp4(v, OP_OpenRead, tab_cursor, 0, 0, (void *) space,
+ sqlite3VdbeAddOp4(v, OP_CursorOpen, tab_cursor, 0, 0, (void *) space,
P4_SPACEPTR);
sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
for (uint32_t j = 0; j < space->index_count; ++j) {
@@ -871,7 +861,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
int idx_cursor;
if (j != 0) {
idx_cursor = parse->nTab - 1;
- sqlite3VdbeAddOp4(v, OP_OpenRead, idx_cursor,
+ sqlite3VdbeAddOp4(v, OP_CursorOpen, idx_cursor,
idx->def->iid, 0,
(void *) space, P4_SPACEPTR);
VdbeComment((v, "%s", idx->def->name));
@@ -1003,7 +993,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
assert("BBB"[0] == AFFINITY_TEXT);
sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
"BBB", 0);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, stat_cursor, tmp_reg);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
+ (char *)stat1, P4_SPACEPTR);
/* Add the entries to the stat4 table. */
int eq_reg = stat1_reg;
int lt_reg = stat1_reg + 1;
@@ -1036,7 +1027,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
sample_reg);
sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
- sqlite3VdbeAddOp2(v, OP_IdxReplace, stat_cursor + 1, tmp_reg);
+ sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
+ (char *)stat4, P4_SPACEPTR);
/* P1==1 for end-of-loop. */
sqlite3VdbeAddOp2(v, OP_Goto, 1, next_addr);
sqlite3VdbeJumpHere(v, is_null_addr);
@@ -1058,27 +1050,12 @@ loadAnalysis(Parse * pParse)
}
}
-/**
- * Wrapper to pass args to space_foreach callback.
- */
-struct analyze_data {
- struct Parse *parse_context;
- /**
- * Cursor numbers pointing to stat spaces:
- * stat_cursor is opened on _sql_stat1 and
- * stat_cursor + 1 - on _sql_stat4.
- */
- int stat_cursor;
-};
-
static int
sql_space_foreach_analyze(struct space *space, void *data)
{
if (space->def->opts.sql == NULL || space->def->opts.is_view)
return 0;
- struct analyze_data *anal_data = (struct analyze_data *) data;
- vdbe_emit_analyze_space(anal_data->parse_context, space,
- anal_data->stat_cursor);
+ vdbe_emit_analyze_space((struct Parse*)data, space);
return 0;
}
@@ -1090,11 +1067,8 @@ static void
sql_analyze_database(struct Parse *parser)
{
sql_set_multi_write(parser, false);
- int stat_cursor = parser->nTab;
- parser->nTab += 2;
- vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
- struct analyze_data anal_data = { parser, stat_cursor };
- space_foreach(sql_space_foreach_analyze, (void *) &anal_data);
+ vdbe_emit_stat_space_open(parser, NULL);
+ space_foreach(sql_space_foreach_analyze, (void *)parser);
loadAnalysis(parser);
}
@@ -1114,10 +1088,8 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space)
* There are two system spaces for statistics: _sql_stat1
* and _sql_stat4.
*/
- int stat_cursor = parse->nTab;
- parse->nTab += 2;
- vdbe_emit_stat_space_open(parse, stat_cursor, space->def->name);
- vdbe_emit_analyze_space(parse, space, stat_cursor);
+ vdbe_emit_stat_space_open(parse, space->def->name);
+ vdbe_emit_analyze_space(parse, space);
loadAnalysis(parse);
}
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 43be777..0ae4c7d 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -883,7 +883,7 @@ vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id,
struct space *space)
{
assert(space != NULL);
- return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_OpenWrite, cursor,
+ return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_CursorOpen, cursor,
index_id, 0, (void *) space, P4_SPACEPTR);
}
@@ -2693,7 +2693,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
goto exit_create_index;
sql_set_multi_write(parse, true);
- sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, 0, 0,
+ sqlite3VdbeAddOp4(vdbe, OP_CursorOpen, cursor, 0, 0,
(void *)space_by_id(BOX_INDEX_ID),
P4_SPACEPTR);
sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 9aa7058..ebde698 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
void
sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
- int cursor)
+ int cursor, int reg_eph)
{
struct sqlite3 *db = parse->db;
where = sqlite3ExprDup(db, where, 0);
@@ -83,7 +83,7 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
struct Select *select = sqlite3SelectNew(parse, NULL, from, where, NULL,
NULL, NULL, 0, NULL, NULL);
struct SelectDest dest;
- sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor);
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor, reg_eph);
sqlite3Select(parse, select, &dest);
sql_select_delete(db, select);
}
@@ -194,9 +194,10 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
/* If we are trying to delete from a view, realize that
* view into an ephemeral table.
*/
+ int reg_eph = ++parse->nMem;
if (is_view) {
sql_materialize_view(parse, space->def->name, where,
- tab_cursor);
+ tab_cursor, reg_eph);
}
/* Initialize the counter of the number of rows deleted,
@@ -241,6 +242,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
* it, so columns should be loaded manually.
*/
struct sql_key_info *pk_info = NULL;
+ int reg_eph = ++parse->nMem;
int reg_pk = parse->nMem + 1;
int pk_len;
int eph_cursor = parse->nTab++;
@@ -248,8 +250,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
if (is_view) {
pk_len = table->def->field_count;
parse->nMem += pk_len;
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- eph_cursor, pk_len);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+ pk_len);
} else {
assert(space->index_count > 0);
pk_info = sql_key_info_new_from_key_def(db,
@@ -258,9 +260,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
goto delete_from_cleanup;
pk_len = pk_info->part_count;
parse->nMem += pk_len;
- sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, eph_cursor,
- pk_len, 0,
- (char *)pk_info, P4_KEYINFO);
+ sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
+ pk_len, 0,
+ (char *)pk_info, P4_KEYINFO);
}
/* Construct a query to find the primary key for
@@ -345,7 +347,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
* by malloc.
*/
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, eph_cursor, reg_key);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, reg_key, reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
}
/* If this DELETE cannot use the ONEPASS strategy,
@@ -369,7 +372,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once);
VdbeCoverage(v);
}
- sqlite3VdbeAddOp4(v, OP_OpenWrite, tab_cursor, 0, 0,
+ sqlite3VdbeAddOp4(v, OP_CursorOpen, tab_cursor, 0, 0,
(void *) space, P4_SPACEPTR);
VdbeComment((v, "%s", space->index[0]->def->name));
@@ -390,6 +393,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
VdbeCoverage(v);
} else {
+ sqlite3VdbeAddOp3(v, OP_CursorOpen,
+ eph_cursor, 0, reg_eph);
addr_loop = sqlite3VdbeAddOp1(v, OP_Rewind, eph_cursor);
VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowData, eph_cursor, reg_key);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index a13de4f..aa26342 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2714,8 +2714,10 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
*/
pExpr->iTable = pParse->nTab++;
pExpr->is_ephemeral = 1;
+ int reg_eph = ++pParse->nMem;
addr = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- pExpr->iTable, nVal);
+ reg_eph, nVal);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, pExpr->iTable, 0, reg_eph);
struct sql_key_info *key_info = sql_key_info_new(pParse->db, nVal);
if (key_info == NULL)
return 0;
@@ -2736,7 +2738,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
SelectDest dest;
int i;
sqlite3SelectDestInit(&dest, SRT_Set,
- pExpr->iTable);
+ pExpr->iTable, reg_eph);
dest.zAffSdst =
exprINAffinity(pParse, pExpr);
assert((pExpr->iTable & 0x0000FFFF) ==
@@ -2808,8 +2810,10 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
1, r2, &affinity, 1);
sqlite3ExprCacheAffinityChange(pParse,
r3, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert,
- pExpr->iTable, r2);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r2,
+ reg_eph);
+ sqlite3VdbeChangeP5(v,
+ OPFLAG_EPH_INSERT);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -2847,7 +2851,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
pSel = pExpr->x.pSelect;
nReg = pExpr->op == TK_SELECT ? pSel->pEList->nExpr : 1;
- sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1);
+ sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1, 0);
pParse->nMem += nReg;
if (pExpr->op == TK_SELECT) {
dest.eDest = SRT_Mem;
@@ -5361,24 +5365,17 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr)
pItem->iMem = ++pParse->nMem;
assert(!ExprHasProperty
(pExpr, EP_IntValue));
- pItem->pFunc =
- sqlite3FindFunction(pParse->
- db,
- pExpr->
- u.
- zToken,
- pExpr->
- x.
- pList ?
- pExpr->
- x.
- pList->
- nExpr :
- 0,
- 0);
+ pItem->pFunc = sqlite3FindFunction(
+ pParse->db,
+ pExpr->u.zToken,
+ pExpr->x.pList ?
+ pExpr->x.pList->nExpr : 0,
+ 0);
if (pExpr->flags & EP_Distinct) {
pItem->iDistinct =
- pParse->nTab++;
+ pParse->nTab++;
+ pItem->reg_eph =
+ ++pParse->nMem;
} else {
pItem->iDistinct = -1;
}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 03f4e1b..c9b4846 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -46,12 +46,10 @@
void
sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */
int iCur, /* The cursor number of the table */
- struct space *space, /* The table to be opened */
- int opcode) /* OP_OpenRead or OP_OpenWrite */
+ struct space *space) /* The table to be opened */
{
Vdbe *v;
v = sqlite3GetVdbe(pParse);
- assert(opcode == OP_OpenWrite || opcode == OP_OpenRead);
assert(space->index_count > 0);
vdbe_emit_open_cursor(pParse, iCur, 0, space);
VdbeComment((v, "%s", space->def->name));
@@ -139,9 +137,12 @@ vdbe_has_table_read(struct Parse *parser, const struct Table *table)
* Currently, there is no difference between Read
* and Write cursors.
*/
- if (op->opcode == OP_OpenRead || op->opcode == OP_OpenWrite) {
- assert(op->p4type == P4_SPACEPTR);
- struct space *space = op->p4.space;
+ if (op->opcode == OP_CursorOpen) {
+ struct space *space = NULL;
+ if (op->p4type == P4_SPACEPTR)
+ space = op->p4.space;
+ else
+ continue;
if (space->def->id == table->def->id)
return true;
}
@@ -418,6 +419,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
}
}
+ int reg_eph;
/* Figure out how many columns of data are supplied. If the data
* is coming from a SELECT statement, then generate a co-routine that
* produces a single row of the SELECT on each invocation. The
@@ -434,7 +436,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
regYield = ++pParse->nMem;
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield, -1);
dest.iSdst = bIdListInOrder ? regData : 0;
dest.nSdst = def->field_count;
rc = sqlite3Select(pParse, pSelect, &dest);
@@ -480,10 +482,13 @@ sqlite3Insert(Parse * pParse, /* Parser context */
int64_t initial_pk = 0;
srcTab = pParse->nTab++;
+ reg_eph = ++pParse->nMem;
regRec = sqlite3GetTempReg(pParse);
regCopy = sqlite3GetTempRange(pParse, nColumn);
regTempId = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, srcTab, nColumn+1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+ nColumn + 1);
+
/* Create counter for rowid */
sqlite3VdbeAddOp4Dup8(v, OP_Int64,
0 /* unused */,
@@ -499,7 +504,8 @@ sqlite3Insert(Parse * pParse, /* Parser context */
nColumn + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, srcTab, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3VdbeGoto(v, addrL);
sqlite3VdbeJumpHere(v, addrL);
@@ -515,6 +521,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
srcTab = -1;
+ reg_eph = -1;
assert(useTempTable == 0);
if (pList) {
nColumn = pList->nExpr;
@@ -555,6 +562,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
* end loop
* D: ...
*/
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, srcTab, 0, reg_eph);
addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
VdbeCoverage(v);
addrCont = sqlite3VdbeCurrentAddr(v);
@@ -746,9 +754,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
vdbe_emit_constraint_checks(pParse, pTab, regIns + 1,
on_error, endOfLoop, 0);
fkey_emit_check(pParse, pTab, 0, regIns, 0);
- int pk_cursor = pParse->nTab++;
- vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
- vdbe_emit_insertion_completion(v, pk_cursor, regIns + 1,
+ vdbe_emit_insertion_completion(v, space, regIns + 1,
pTab->def->field_count,
on_error);
}
@@ -1063,8 +1069,8 @@ process_index: ;
}
void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
- uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+ int raw_data_reg, uint32_t tuple_len,
enum on_conflict_action on_conflict)
{
assert(v != NULL);
@@ -1077,7 +1083,8 @@ vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
pik_flags |= OPFLAG_OE_ROLLBACK;
sqlite3VdbeAddOp3(v, OP_MakeRecord, raw_data_reg, tuple_len,
raw_data_reg + tuple_len);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor_id, raw_data_reg + tuple_len);
+ sqlite3VdbeAddOp1(v, OP_IdxInsert, raw_data_reg + tuple_len);
+ sqlite3VdbeChangeP4(v, -1, (char *)space, P4_SPACEPTR);
sqlite3VdbeChangeP5(v, pik_flags);
}
@@ -1327,7 +1334,8 @@ xferOptimization(Parse * pParse, /* Parser context */
sqlite3VdbeChangeP5(v, OPFLAG_XFER_OPT);
#endif
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, regData, 0, 0,
+ (char *)dest, P4_SPACEPTR);
switch (onError) {
case ON_CONFLICT_ACTION_IGNORE:
sqlite3VdbeChangeP5(v, OPFLAG_OE_IGNORE | OPFLAG_NCHANGE);
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 473cf89..6b5f19d 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -390,7 +390,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
//////////////////////// The SELECT statement /////////////////////////////////
//
cmd ::= select(X). {
- SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
if(!pParse->parse_only)
sqlite3Select(pParse, X, &dest);
else
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 02c0a5d..0370327 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -66,6 +66,8 @@ struct DistinctCtx {
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
int tabTnct; /* Ephemeral table used for DISTINCT processing */
int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
+ /* Register, containing a pointer to ephemeral space. */
+ int reg_eph;
};
/*
@@ -77,6 +79,8 @@ struct SortCtx {
ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
int nOBSat; /* Number of ORDER BY terms satisfied by indices */
int iECursor; /* Cursor number for the sorter */
+ /* Register, containing pointer to ephemeral space. */
+ int reg_eph;
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
@@ -117,10 +121,11 @@ clearSelect(sqlite3 * db, Select * p, int bFree)
* Initialize a SelectDest structure.
*/
void
-sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm)
+sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm, int reg_eph)
{
pDest->eDest = (u8) eDest;
pDest->iSDParm = iParm;
+ pDest->reg_eph = reg_eph;
pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
@@ -687,7 +692,6 @@ pushOntoSorter(Parse * pParse, /* Parser context */
int regBase; /* Regs for sorter record */
int regRecord = ++pParse->nMem; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
- int op; /* Opcode to add sorter record to sorter */
int iLimit; /* LIMIT counter */
assert(bSeq == 0 || bSeq == 1);
@@ -764,11 +768,14 @@ pushOntoSorter(Parse * pParse, /* Parser context */
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
sqlite3VdbeJumpHere(v, addrJmp);
}
- if (pSort->sortFlags & SORTFLAG_UseSorter)
- op = OP_SorterInsert;
- else
- op = OP_IdxInsert;
- sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
+ if (pSort->sortFlags & SORTFLAG_UseSorter) {
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, pSort->iECursor,
+ regRecord);
+ } else {
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRecord, pSort->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
+ }
+
if (iLimit) {
int addr;
int r1 = 0;
@@ -823,7 +830,7 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
}
}
-/*
+/**
* Add code that will check to make sure the N registers starting at iMem
* form a distinct entry. iTab is a sorting index that holds previously
* seen combinations of the N values. A new entry is made in iTab
@@ -831,24 +838,32 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
*
* A jump to addrRepeat is made and the N+1 values are popped from the
* stack if the top N elements are not distinct.
+ *
+ * @param parse Parsing and code generating context.
+ * @param cursor A sorting index cursor used to test for distinctness.
+ * @param reg_eph Register holding ephemeral's space pointer.
+ * @param addr_repeat Jump to here if not distinct.
+ * @param n Number of elements.
+ * @param reg_data First register holding the data.
*/
static void
-codeDistinct(Parse * pParse, /* Parsing and code generating context */
- int iTab, /* A sorting index used to test for distinctness */
- int addrRepeat, /* Jump to here if not distinct */
- int N, /* Number of elements */
- int iMem) /* First element */
+codeDistinct(struct Parse * parse,
+ int cursor,
+ int reg_eph,
+ int addr_repeat,
+ int n,
+ int reg_data)
{
- Vdbe *v;
+ struct Vdbe *v = parse->pVdbe;
int r1;
- v = pParse->pVdbe;
- r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
+ r1 = sqlite3GetTempReg(parse);
+ sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
+ sqlite3ReleaseTempReg(parse, r1);
}
/*
@@ -998,7 +1013,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
* row is all NULLs.
*/
sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
+ pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct + 1);
pOp->opcode = OP_Null;
pOp->p1 = 1;
pOp->p2 = regPrev;
@@ -1040,13 +1056,14 @@ selectInnerLoop(Parse * pParse, /* The parser context */
case WHERE_DISTINCT_UNIQUE:{
sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
break;
}
default:{
assert(pDistinct->eTnctType ==
WHERE_DISTINCT_UNORDERED);
- codeDistinct(pParse, pDistinct->tabTnct,
+ codeDistinct(pParse, pDistinct->tabTnct, pDistinct->reg_eph,
iContinue, nResultCol, regResult);
break;
}
@@ -1066,7 +1083,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
nResultCol, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3ReleaseTempReg(pParse, r1);
break;
}
@@ -1109,8 +1127,9 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp4Int(v, OP_Found, iParm + 1,
addr, r1, 0);
VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
- r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1,
+ pDest->reg_eph + 1);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
assert(pSort == 0);
}
#endif
@@ -1122,7 +1141,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
int regRec = sqlite3GetTempReg(pParse);
/* Last column is required for ID. */
int regCopy = sqlite3GetTempRange(pParse, nResultCol + 1);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm,
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
nResultCol, regCopy + nResultCol);
/* Positioning ID column to be last in inserted tuple.
* NextId -> regCopy + n + 1
@@ -1134,7 +1153,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nResultCol + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, pDest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempRange(pParse, regCopy, nResultCol + 1);
}
@@ -1164,7 +1184,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3ExprCacheAffinityChange(pParse,
regResult,
nResultCol);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3ReleaseTempReg(pParse, r1);
}
break;
@@ -1247,8 +1268,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
nResultCol, r3);
if (eDest == SRT_DistQueue) {
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
- r3);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r3,
+ pDest->reg_eph + 1);
}
for (i = 0; i < nKey; i++) {
sqlite3VdbeAddOp2(v, OP_SCopy,
@@ -1258,7 +1279,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2 + nKey);
sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey + 2, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
if (addrTest)
sqlite3VdbeJumpHere(v, addrTest);
sqlite3ReleaseTempReg(pParse, r1);
@@ -1498,7 +1520,6 @@ generateSortTail(Parse * pParse, /* Parsing context */
int iTab;
ExprList *pOrderBy = pSort->pOrderBy;
int eDest = pDest->eDest;
- int iParm = pDest->iSDParm;
int regRow;
int regTupleid;
int iCol;
@@ -1570,10 +1591,12 @@ generateSortTail(Parse * pParse, /* Parsing context */
case SRT_Table:
case SRT_EphemTab: {
int regCopy = sqlite3GetTempRange(pParse, nColumn);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm, nColumn, regTupleid);
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
+ nColumn, regTupleid);
sqlite3VdbeAddOp3(v, OP_Copy, regRow, regCopy, nSortData - 1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nColumn + 1, regRow);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRow);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRow, pDest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3ReleaseTempReg(pParse, regCopy);
break;
}
@@ -1583,7 +1606,8 @@ generateSortTail(Parse * pParse, /* Parsing context */
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn,
regTupleid, pDest->zAffSdst, nColumn);
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regTupleid);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regTupleid, pDest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
break;
}
case SRT_Mem:{
@@ -2409,13 +2433,16 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
* for the SRT_DistFifo and SRT_DistQueue destinations to work.
*/
iQueue = pParse->nTab++;
+ int reg_queue = ++pParse->nMem;
+ int reg_dist = 0;
if (p->op == TK_UNION) {
eDest = pOrderBy ? SRT_DistQueue : SRT_DistFifo;
iDistinct = pParse->nTab++;
+ reg_dist = ++pParse->nMem;
} else {
eDest = pOrderBy ? SRT_Queue : SRT_Fifo;
}
- sqlite3SelectDestInit(&destQueue, eDest, iQueue);
+ sqlite3SelectDestInit(&destQueue, eDest, iQueue, reg_queue);
/* Allocate cursors for Current, Queue, and Distinct. */
regCurrent = ++pParse->nMem;
@@ -2423,18 +2450,20 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
if (pOrderBy) {
struct sql_key_info *key_info =
sql_multiselect_orderby_to_key_info(pParse, p, 1);
- sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, iQueue,
+ sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_queue,
pOrderBy->nExpr + 2, 0, (char *)key_info,
P4_KEYINFO);
VdbeComment((v, "Orderby table"));
destQueue.pOrderBy = pOrderBy;
} else {
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iQueue, nCol + 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_queue, nCol + 1);
VdbeComment((v, "Queue table"));
}
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, iQueue, 0, reg_queue);
if (iDistinct) {
p->addrOpenEphm[0] =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iDistinct, 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_dist, 1);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, iDistinct, 0, reg_dist);
p->selFlags |= SF_UsesEphemeral;
VdbeComment((v, "Distinct table"));
}
@@ -2630,7 +2659,8 @@ multiSelect(Parse * pParse, /* Parsing context */
if (dest.eDest == SRT_EphemTab) {
assert(p->pEList);
int nCols = p->pEList->nExpr;
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.iSDParm, nCols + 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.reg_eph, nCols + 1);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, dest.iSDParm, 0, dest.reg_eph);
VdbeComment((v, "Destination temp"));
dest.eDest = SRT_Table;
}
@@ -2721,6 +2751,7 @@ multiSelect(Parse * pParse, /* Parsing context */
case TK_EXCEPT:
case TK_UNION:{
int unionTab; /* Cursor number of the temporary table holding result */
+ int reg_union;
u8 op = 0; /* One of the SRT_ operations to apply to self */
int priorOp; /* The SRT_ operation to apply to prior selects */
Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
@@ -2737,16 +2768,19 @@ multiSelect(Parse * pParse, /* Parsing context */
assert(p->pLimit == 0); /* Not allowed on leftward elements */
assert(p->pOffset == 0); /* Not allowed on leftward elements */
unionTab = dest.iSDParm;
+ reg_union = dest.reg_eph;
} else {
/* We will need to create our own temporary table to hold the
* intermediate results.
*/
unionTab = pParse->nTab++;
+ reg_union = ++pParse->nMem;
assert(p->pOrderBy == 0);
addr =
sqlite3VdbeAddOp2(v,
OP_OpenTEphemeral,
- unionTab, 0);
+ reg_union, 0);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, unionTab, 0, reg_union);
assert(p->addrOpenEphm[0] == -1);
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |=
@@ -2758,7 +2792,7 @@ multiSelect(Parse * pParse, /* Parsing context */
*/
assert(!pPrior->pOrderBy);
sqlite3SelectDestInit(&uniondest, priorOp,
- unionTab);
+ unionTab, reg_union);
iSub1 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, pPrior, &uniondest);
if (rc) {
@@ -2841,6 +2875,7 @@ multiSelect(Parse * pParse, /* Parsing context */
default:
assert(p->op == TK_INTERSECT); {
int tab1, tab2;
+ int reg_eph1, reg_eph2;
int iCont, iBreak, iStart;
Expr *pLimit, *pOffset;
int addr;
@@ -2852,12 +2887,15 @@ multiSelect(Parse * pParse, /* Parsing context */
* by allocating the tables we will need.
*/
tab1 = pParse->nTab++;
+ reg_eph1 = ++pParse->nMem;
tab2 = pParse->nTab++;
+ reg_eph2 = ++pParse->nMem;
assert(p->pOrderBy == 0);
addr =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab1,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph1,
0);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, tab1, 0, reg_eph1);
assert(p->addrOpenEphm[0] == -1);
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |= SF_UsesEphemeral;
@@ -2866,7 +2904,7 @@ multiSelect(Parse * pParse, /* Parsing context */
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union,
- tab1);
+ tab1, reg_eph1);
iSub1 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, pPrior,
&intersectdest);
@@ -2877,8 +2915,9 @@ multiSelect(Parse * pParse, /* Parsing context */
/* Code the current SELECT into temporary table "tab2"
*/
addr =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab2,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph2,
0);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, tab2, 0, reg_eph2);
assert(p->addrOpenEphm[1] == -1);
p->addrOpenEphm[1] = addr;
p->pPrior = 0;
@@ -2887,6 +2926,7 @@ multiSelect(Parse * pParse, /* Parsing context */
pOffset = p->pOffset;
p->pOffset = 0;
intersectdest.iSDParm = tab2;
+ intersectdest.reg_eph = reg_eph2;
iSub2 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, p, &intersectdest);
testcase(rc != SQLITE_OK);
@@ -3075,7 +3115,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
case SRT_EphemTab:{
int regRec = sqlite3GetTempReg(parse);
int regCopy = sqlite3GetTempRange(parse, in->nSdst + 1);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->iSDParm,
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->reg_eph,
in->nSdst, regCopy + in->nSdst);
sqlite3VdbeAddOp3(v, OP_Copy, in->iSdst, regCopy,
in->nSdst - 1);
@@ -3083,7 +3123,8 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
in->nSdst + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, dest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3ReleaseTempRange(parse, regCopy, in->nSdst + 1);
sqlite3ReleaseTempReg(parse, regRec);
break;
@@ -3099,7 +3140,8 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
in->nSdst);
sqlite3ExprCacheAffinityChange(parse, in->iSdst,
in->nSdst);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, dest->reg_eph);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
sqlite3ReleaseTempReg(parse, r1);
break;
}
@@ -3417,8 +3459,8 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */
regAddrB = ++pParse->nMem;
regOutA = ++pParse->nMem;
regOutB = ++pParse->nMem;
- sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
- sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
+ sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA, 0);
+ sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB, 0);
/* Generate a coroutine to evaluate the SELECT statement to the
* left of the compound operator - the "A" select.
@@ -5261,7 +5303,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
/* Verify that all AggInfo registers are within the range specified by
* AggInfo.mnReg..AggInfo.mxReg
*/
- assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
+ assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
for (i = 0; i < pAggInfo->nColumn; i++) {
assert(pAggInfo->aCol[i].iMem >= pAggInfo->mnReg
&& pAggInfo->aCol[i].iMem <= pAggInfo->mxReg);
@@ -5287,8 +5329,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
pE->x.pList,
0);
sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- pFunc->iDistinct, 1, 0,
+ pFunc->reg_eph, 1, 0,
(char *)key_info, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen,
+ pFunc->iDistinct, 0, pFunc->reg_eph);
}
}
}
@@ -5347,8 +5391,8 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
addrNext = sqlite3VdbeMakeLabel(v);
testcase(nArg == 0); /* Error condition */
testcase(nArg > 1); /* Also an error */
- codeDistinct(pParse, pF->iDistinct, addrNext, 1,
- regAgg);
+ codeDistinct(pParse, pF->iDistinct, pF->reg_eph,
+ addrNext, 1, regAgg);
}
if (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) {
struct coll *coll = NULL;
@@ -5679,7 +5723,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
VdbeComment((v, "%s", pItem->pTab->def->name));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine,
- pItem->regReturn);
+ pItem->regReturn, 0);
pItem->iSelectId = pParse->iNextSelectId;
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5699,6 +5743,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
int retAddr;
assert(pItem->addrFillSub == 0);
pItem->regReturn = ++pParse->nMem;
+ pItem->reg = ++pParse->nMem;
topAddr =
sqlite3VdbeAddOp2(v, OP_Integer, 0,
pItem->regReturn);
@@ -5717,7 +5762,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
pItem->pTab->def->name));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab,
- pItem->iCursor);
+ pItem->iCursor, pItem->reg);
pItem->iSelectId = pParse->iNextSelectId;
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5796,6 +5841,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
if (sSort.pOrderBy) {
struct sql_key_info *key_info =
sql_expr_list_to_key_info(pParse, sSort.pOrderBy, 0);
+ sSort.reg_eph = ++pParse->nMem;
sSort.iECursor = pParse->nTab++;
/* Number of columns in transient table equals to number of columns in
* SELECT statement plus number of columns in ORDER BY statement
@@ -5807,9 +5853,10 @@ sqlite3Select(Parse * pParse, /* The parser context */
}
sSort.addrSortIndex =
sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- sSort.iECursor,
+ sSort.reg_eph,
nCols,
0, (char *)key_info, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, sSort.iECursor, 0, sSort.reg_eph);
VdbeComment((v, "Sort table"));
} else {
sSort.addrSortIndex = -1;
@@ -5818,8 +5865,10 @@ sqlite3Select(Parse * pParse, /* The parser context */
/* If the output is destined for a temporary table, open that table.
*/
if (pDest->eDest == SRT_EphemTab) {
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->iSDParm,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->reg_eph,
pEList->nExpr + 1);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, pDest->iSDParm, 0,
+ pDest->reg_eph);
VdbeComment((v, "Output table"));
}
@@ -5833,6 +5882,8 @@ sqlite3Select(Parse * pParse, /* The parser context */
computeLimitRegisters(pParse, p, iEnd);
if (p->iLimit == 0 && sSort.addrSortIndex >= 0) {
sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
+ sqlite3VdbeChangeP1(v, sSort.addrSortIndex, sSort.iECursor);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
sSort.sortFlags |= SORTFLAG_UseSorter;
}
@@ -5840,13 +5891,16 @@ sqlite3Select(Parse * pParse, /* The parser context */
*/
if (p->selFlags & SF_Distinct) {
sDistinct.tabTnct = pParse->nTab++;
+ sDistinct.reg_eph = ++pParse->nMem;
struct sql_key_info *key_info =
sql_expr_list_to_key_info(pParse, p->pEList, 0);
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- sDistinct.tabTnct,
+ sDistinct.reg_eph,
key_info->part_count,
0, (char *)key_info,
P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, sDistinct.tabTnct, 0,
+ sDistinct.reg_eph);
VdbeComment((v, "Distinct table"));
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
} else {
@@ -5886,6 +5940,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
*/
if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
}
/* Use the standard inner loop. */
@@ -6130,6 +6185,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
) {
sSort.pOrderBy = 0;
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
}
/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 53188e7..51110aa 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2000,6 +2000,8 @@ struct AggInfo {
FuncDef *pFunc; /* The aggregate function implementation */
int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
+ /* Register, holding ephemeral's spacepointer. */
+ int reg_eph;
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
};
@@ -2327,6 +2329,8 @@ struct SrcList {
} fg;
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
int iCursor; /* The VDBE cursor number used to access this table */
+ /* Register, containing pointer to the space. */
+ int reg;
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
@@ -2591,6 +2595,8 @@ struct SelectDest {
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
int iSDParm; /* A parameter used by the eDest disposal method */
+ /* Register containing ephemeral's space pointer. */
+ int reg_eph;
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
@@ -2806,6 +2812,8 @@ struct Parse {
#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */
#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
+#define OPFLAG_EPH_INSERT 0x02 /* OP_IdxInsert2: index's space is
+ ephemeral */
#define OPFLAG_SAME_FRAME 0x01 /* OP_FCopy: use same frame for source
* register
@@ -3508,7 +3516,7 @@ Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
struct Table *
sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name);
-void sqlite3OpenTable(Parse *, int iCur, struct space *, int);
+void sqlite3OpenTable(Parse *, int iCur, struct space *);
/**
* Generate code for a DELETE FROM statement.
*
@@ -3828,14 +3836,14 @@ vdbe_emit_constraint_checks(struct Parse *parse_context,
* @cursor_id.
*
* @param v Virtual database engine.
- * @param cursor_id Primary index cursor.
+ * @param space Pointer to space object.
* @param raw_data_reg Register with raw data to insert.
* @param tuple_len Number of registers to hold the tuple.
* @param on_conflict On conflict action.
*/
void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
- uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+ int raw_data_reg, uint32_t tuple_len,
enum on_conflict_action on_conflict);
void
@@ -3870,10 +3878,11 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *);
* @param name View name.
* @param where Option WHERE clause to be added.
* @param cursor Cursor number for ephemeral table.
+ * @param reg_eph Register holding pointer to ephemeral table.
*/
void
sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
- int cursor);
+ int cursor, int reg_eph);
/**
* This is called by the parser when it sees a CREATE TRIGGER
@@ -4529,7 +4538,7 @@ void sqlite3StrAccumAppendAll(StrAccum *, const char *);
void sqlite3AppendChar(StrAccum *, int, char);
char *sqlite3StrAccumFinish(StrAccum *);
void sqlite3StrAccumReset(StrAccum *);
-void sqlite3SelectDestInit(SelectDest *, int, int);
+void sqlite3SelectDestInit(SelectDest *, int, int, int);
Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
int sqlite3ExprCheckIN(Parse *, Expr *);
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index 8017742..50a989a 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -97,9 +97,22 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
const char *zOldName, const char *zNewName);
-/* Interface for ephemeral tables. */
-int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
- struct sql_key_info *key_info);
+/**
+ * Create ephemeral space. Features of ephemeral spaces: id == 0,
+ * name == "ephemeral", memtx engine (in future it can be changed,
+ * but now only memtx engine is supported), primary index which
+ * covers all fields and no secondary indexes, given field number
+ * and collation sequence. All fields are scalar and nullable.
+ *
+ * @param field_count Number of fields in ephemeral space.
+ * @param key_info Keys description for new ephemeral space.
+ *
+ * @retval Pointer to created space, NULL if error.
+ */
+struct space *
+tarantoolSqlite3EphemeralCreate(uint32_t filed_count,
+ struct sql_key_info *key_info);
+
/**
* Insert tuple into ephemeral space.
* In contrast to ordinary spaces, there is no need to create and
@@ -117,7 +130,18 @@ int tarantoolSqlite3EphemeralDelete(BtCursor * pCur);
int tarantoolSqlite3EphemeralCount(BtCursor * pCur, i64 * pnEntry);
int tarantoolSqlite3EphemeralDrop(BtCursor * pCur);
int tarantoolSqlite3EphemeralClearTable(BtCursor * pCur);
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor * pCur, uint32_t fieldno,
+
+/**
+ * Extract maximum integer value from ephemeral space.
+ * If index is empty - return 0 in max_id and success status.
+ *
+ * @param space Pointer to ephemeral space.
+ * @param fieldno Number of field from fetching tuple.
+ * @param[out] max_id Fetched max value.
+ *
+ * @retval 0 on success, -1 otherwise.
+ */
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
uint64_t * max_id);
/**
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index c6bd15b..271f40a 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -205,15 +205,9 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
if (db->mallocFailed)
goto cleanup;
- int cursor = parse->nTab++;
struct space *_trigger = space_by_id(BOX_TRIGGER_ID);
assert(_trigger != NULL);
- vdbe_emit_open_cursor(parse, cursor, 0, _trigger);
- /*
- * makerecord(cursor(iRecord),
- * [reg(first_col), reg(first_col+1)]).
- */
int first_col = parse->nMem + 1;
parse->nMem += 3;
int record = ++parse->nMem;
@@ -245,10 +239,10 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
sqlite3VdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
sqlite3VdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor, record);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, record, 0, 0,
+ (char *)_trigger, P4_SPACEPTR);
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
- sqlite3VdbeAddOp1(v, OP_Close, cursor);
sql_set_multi_write(parse, false);
} else {
@@ -694,7 +688,7 @@ codeTriggerProgram(Parse * pParse, /* The parser context */
SelectDest sDest;
Select *pSelect =
sqlite3SelectDup(db, pStep->pSelect, 0);
- sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
+ sqlite3SelectDestInit(&sDest, SRT_Discard, 0, 0);
sqlite3Select(pParse, pSelect, &sDest);
sql_select_delete(db, pSelect);
break;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 730872f..71bd71d 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -222,13 +222,16 @@ sqlite3Update(Parse * pParse, /* The parser context */
* an ephemeral table.
*/
uint32_t pk_part_count;
+ struct space *space;
if (is_view) {
- sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
+ int reg_eph = ++pParse->nMem;
+ sql_materialize_view(pParse, def->name, pWhere, pk_cursor, reg_eph);
/* Number of columns from SELECT plus ID.*/
pk_part_count = nKey = def->field_count + 1;
} else {
- vdbe_emit_open_cursor(pParse, pk_cursor, 0,
- space_by_id(pTab->def->id));
+ space = space_by_id(pTab->def->id);
+ assert(space != NULL);
+ vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
pk_part_count = pPk->def->key_def->part_count;
}
@@ -242,11 +245,12 @@ sqlite3Update(Parse * pParse, /* The parser context */
int iPk = pParse->nMem + 1;
pParse->nMem += pk_part_count;
regKey = ++pParse->nMem;
+ int reg_eph = ++pParse->nMem;
iEph = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
/* Address of the OpenEphemeral instruction. */
- int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iEph,
+ int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
pk_part_count);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
WHERE_ONEPASS_DESIRED, pk_cursor);
@@ -276,9 +280,9 @@ sqlite3Update(Parse * pParse, /* The parser context */
pPk->def);
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
regKey, zAff, pk_part_count);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
/* Set flag to save memory allocating one by malloc. */
- sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE | OPFLAG_EPH_INSERT);
}
/* End the database scan loop.
*/
@@ -304,6 +308,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
}
} else {
labelContinue = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, iEph, 0, reg_eph);
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
sqlite3VdbeAddOp4Int(v, OP_NotFound, pk_cursor, labelContinue,
@@ -437,7 +442,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
sqlite3VdbeJumpHere(v, addr1);
if (hasFK)
fkey_emit_check(pParse, pTab, 0, regNewPk, aXRef);
- vdbe_emit_insertion_completion(v, pk_cursor, regNew,
+ vdbe_emit_insertion_completion(v, space, regNew,
pTab->def->field_count,
on_error);
/*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 00ffb0c..7df491b 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -3054,51 +3054,37 @@ case OP_TTransaction: {
break;
}
-/* Opcode: OpenRead P1 P2 P3 P4 P5
+/* Opcode: CursorReopen P1 P2 P3 P4 P5
* Synopsis: index id = P2, space ptr = P4
*
- * Open a cursor for a space specified by pointer in P4 and index
- * id in P2. Give the new cursor an identifier of P1. The P1
- * values need not be contiguous but all P1 values should be
- * small integers. It is an error for P1 to be negative.
- */
-/* Opcode: ReopenIdx P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
- *
- * The ReopenIdx opcode works exactly like OpenRead except that
- * it first checks to see if the cursor on P1 is already open
+ * The CursorReopen opcode works exactly like CursorOpen except
+ * that it first checks to see if the cursor on P1 is already open
* with the same index and if it is this opcode becomes a no-op.
- * In other words, if the cursor is already open, do not reopen it.
+ * In other words, if the cursor is already open, do not reopen
+ * it.
*
- * The ReopenIdx opcode may only be used with P5 == 0.
+ * The CursorReopen opcode may only be used with P5 == 0.
*/
-/* Opcode: OpenWrite P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
+/* Opcode: CursorOpen P1 P2 P3 P4 P5
+ * Synopsis: index id = P2, space ptr = P4 or reg[P3]
*
- * For now, OpenWrite is an alias for OpenRead.
- * It exists just due legacy reasons and should be removed:
- * it isn't neccessary to open cursor to make insertion or
- * deletion.
- */
-case OP_ReopenIdx: {
- int nField;
- int p2;
- VdbeCursor *pCur;
- BtCursor *pBtCur;
-
- assert(pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
- pCur = p->apCsr[pOp->p1];
- p2 = pOp->p2;
- if (pCur && pCur->uc.pCursor->space == pOp->p4.space &&
- pCur->uc.pCursor->index->def->iid == (uint32_t)p2)
+ * Open a cursor for a space specified by pointer in P4 and index
+ * id in P2. Give the new cursor an identifier of P1. The P1
+ * values need not be contiguous but all P1 values should be
+ * small integers. It is an error for P1 to be negative.
+ * If P4 was not set, then P3 supposed to be the register
+ * containing space pointer.
+ */
+case OP_CursorReopen: {
+ assert(pOp->p5 == 0);
+ struct VdbeCursor *cur = p->apCsr[pOp->p1];
+ if (cur != NULL && cur->uc.pCursor->space == pOp->p4.space &&
+ cur->uc.pCursor->index->def->iid == (uint32_t)pOp->p2)
goto open_cursor_set_hints;
/* If the cursor is not currently open or is open on a different
- * index, then fall through into OP_OpenRead to force a reopen
+ * index, then fall through into OP_OpenCursor to force a reopen
*/
-case OP_OpenRead:
-case OP_OpenWrite:
-
- assert(pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
+case OP_CursorOpen:
if (box_schema_version() != p->schema_ver &&
(pOp->p5 & OPFLAG_SYSTEMSP) == 0) {
p->expired = 1;
@@ -3107,66 +3093,60 @@ case OP_OpenWrite:
"need to re-compile SQL statement");
goto abort_due_to_error;
}
- p2 = pOp->p2;
- struct space *space = pOp->p4.space;
+ struct space *space;
+ if (pOp->p4type == P4_SPACEPTR)
+ space = pOp->p4.space;
+ else
+ space = aMem[pOp->p3].u.p;
assert(space != NULL);
- struct index *index = space_index(space, p2);
+ struct index *index = space_index(space, pOp->p2);
assert(index != NULL);
- /*
- * Since Tarantool iterator provides the full tuple,
- * we need a number of fields as wide as the table itself.
- * Otherwise, not enough slots for row parser cache are
- * allocated in VdbeCursor object.
- */
- nField = space->def->field_count;
- assert(pOp->p1>=0);
- assert(nField>=0);
- pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_TARANTOOL);
- if (pCur==0) goto no_mem;
- pCur->nullRow = 1;
- pBtCur = pCur->uc.pCursor;
- pBtCur->curFlags |= BTCF_TaCursor;
- pBtCur->space = space;
- pBtCur->index = index;
- pBtCur->eState = CURSOR_INVALID;
+ assert(pOp->p1 >= 0);
+ cur = allocateCursor(p, pOp->p1,
+ space->def->exact_field_count == 0 ?
+ space->def->field_count :
+ space->def->exact_field_count,
+ CURTYPE_TARANTOOL);
+ if (cur == NULL)
+ goto no_mem;
+ struct BtCursor *bt_cur = cur->uc.pCursor;
+ bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
+ BTCF_TaCursor;
+ bt_cur->space = space;
+ bt_cur->index = index;
+ bt_cur->eState = CURSOR_INVALID;
/* Key info still contains sorter order and collation. */
- pCur->key_def = index->def->key_def;
-
+ cur->key_def = index->def->key_def;
+ cur->nullRow = 1;
open_cursor_set_hints:
- pCur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
- if (rc) goto abort_due_to_error;
+ cur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
+ if (rc != 0)
+ goto abort_due_to_error;
break;
}
/**
* Opcode: OpenTEphemeral P1 P2 * P4 *
* Synopsis:
- * @param P1 index of new cursor to be created.
+ * @param P1 register, where pointer to new space is stored.
* @param P2 number of columns in a new table.
* @param P4 key def for new table, NULL is allowed.
*
- * This opcode creates Tarantool's ephemeral table and sets cursor P1 to it.
+ * This opcode creates Tarantool's ephemeral table and stores pointer
+ * to it into P1 register.
*/
case OP_OpenTEphemeral: {
- VdbeCursor *pCx;
- BtCursor *pBtCur;
assert(pOp->p1 >= 0);
assert(pOp->p2 > 0);
assert(pOp->p4type != P4_KEYINFO || pOp->p4.key_info != NULL);
- pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_TARANTOOL);
- if (pCx == 0) goto no_mem;
- pCx->nullRow = 1;
+ struct space *space = tarantoolSqlite3EphemeralCreate(pOp->p2,
+ pOp->p4.key_info);
- pBtCur = pCx->uc.pCursor;
- /* Ephemeral spaces don't have space_id */
- pBtCur->eState = CURSOR_INVALID;
- pBtCur->curFlags = BTCF_TEphemCursor;
-
- rc = tarantoolSqlite3EphemeralCreate(pCx->uc.pCursor, pOp->p2,
- pOp->p4.key_info);
- pCx->key_def = pCx->uc.pCursor->index->def->key_def;
- if (rc) goto abort_due_to_error;
+ if (space == NULL)
+ goto abort_due_to_error;
+ aMem[pOp->p1].u.p = space;
+ aMem[pOp->p1].flags = MEM_Ptr;
break;
}
@@ -3708,23 +3688,21 @@ case OP_NextSequenceId: {
/* Opcode: NextIdEphemeral P1 P2 P3 * *
* Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]})
*
- * This opcode works in the same way as OP_NextId does, except it is
- * only applied for ephemeral tables. The difference is in the fact that
- * all ephemeral tables don't have space_id (to be more precise it equals to zero).
+ * This opcode works in the same way as OP_NextId does, except it
+ * is only applied for ephemeral tables. The difference is in the
+ * fact that all ephemeral tables don't have space_id (to be more
+ * precise it equals to zero). This opcode uses register P1 to
+ * fetch pointer to epehemeral space.
*/
case OP_NextIdEphemeral: {
- VdbeCursor *pC;
- int p2;
- pC = p->apCsr[pOp->p1];
- p2 = pOp->p2;
+ struct space *space = (struct space*)p->aMem[pOp->p1].u.p;
+ int p2 = pOp->p2;
+ assert(space != NULL);
pOut = &aMem[pOp->p3];
-
- assert(pC->uc.pCursor->curFlags & BTCF_TEphemCursor);
-
- rc = tarantoolSqlite3EphemeralGetMaxId(pC->uc.pCursor, p2,
+ rc = tarantoolSqlite3EphemeralGetMaxId(space, p2,
(uint64_t *) &pOut->u.i);
- if (rc) goto abort_due_to_error;
-
+ if (rc != 0)
+ goto abort_due_to_error;
pOut->u.i += 1;
pOut->flags = MEM_Int;
break;
@@ -4242,83 +4220,95 @@ case OP_Next: /* jump */
goto check_for_interrupt;
}
-/* Opcode: IdxInsert P1 P2 * * P5
+/* Opcode: SorterInsert P1 P2 * * *
* Synopsis: key=r[P2]
*
- * @param P1 Index of a space cursor.
- * @param P2 Index of a register with MessagePack data to insert.
+ * Register P2 holds an SQL index key made using the
+ * MakeRecord instructions. This opcode writes that key
+ * into the sorter P1. Data for the entry is nil.
+ */
+case OP_SorterInsert: { /* in2 */
+ assert(pOp->p1 >= 0 && pOp->p1 < p->nCursor);
+ struct VdbeCursor *cursor = p->apCsr[pOp->p1];
+ assert(cursor != NULL);
+ assert(isSorter(cursor));
+ pIn2 = &aMem[pOp->p2];
+ assert((pIn2->flags & MEM_Blob) != 0);
+ rc = ExpandBlob(pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ rc = sqlite3VdbeSorterWrite(cursor, pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ break;
+}
+
+/* Opcode: IdxInsert2 P1 * * P4 P5
+ * Synopsis: key=r[P1]
+ *
+ * @param P1 Index of a register with MessagePack data to insert.
+ * @param P2 If P4 is not set, then P2 is register containing pointer
+ * to space to insert into.
+ * @param P4 Pointer to the struct space to insert to.
* @param P5 Flags. If P5 contains OPFLAG_NCHANGE, then VDBE
* accounts the change in a case of successful insertion in
* nChange counter. If P5 contains OPFLAG_OE_IGNORE, then
* we are processing INSERT OR INGORE statement. Thus, in
* case of conflict we don't raise an error.
*/
-/* Opcode: IdxReplace P1 P2 * * P5
- * Synopsis: key=r[P2]
+/* Opcode: IdxReplace2 P1 * * P4 P5
+ * Synopsis: key=r[P1]
*
* This opcode works exactly as IdxInsert does, but in Tarantool
* internals it invokes box_replace() instead of box_insert().
*/
-/* Opcode: SorterInsert P1 P2 * * *
- * Synopsis: key=r[P2]
- *
- * Register P2 holds an SQL index key made using the
- * MakeRecord instructions. This opcode writes that key
- * into the sorter P1. Data for the entry is nil.
- */
-case OP_SorterInsert: /* in2 */
case OP_IdxReplace:
-case OP_IdxInsert: { /* in2 */
- VdbeCursor *pC;
-
- assert(pOp->p1>=0 && pOp->p1<p->nCursor);
- pC = p->apCsr[pOp->p1];
- assert(pC!=0);
- assert(isSorter(pC)==(pOp->opcode==OP_SorterInsert));
- pIn2 = &aMem[pOp->p2];
- assert(pIn2->flags & MEM_Blob);
- if (pOp->p5 & OPFLAG_NCHANGE) p->nChange++;
- assert(pC->eCurType==CURTYPE_TARANTOOL || pOp->opcode==OP_SorterInsert);
+case OP_IdxInsert: {
+ pIn2 = &aMem[pOp->p1];
+ assert((pIn2->flags & MEM_Blob) != 0);
+ if (pOp->p5 & OPFLAG_NCHANGE)
+ p->nChange++;
rc = ExpandBlob(pIn2);
- if (rc) goto abort_due_to_error;
- if (pOp->opcode==OP_SorterInsert) {
- rc = sqlite3VdbeSorterWrite(pC, pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ struct space *space;
+ if (pOp->p4type == P4_SPACEPTR) {
+ space = pOp->p4.space;
} else {
- BtCursor *pBtCur = pC->uc.pCursor;
- if (pBtCur->curFlags & BTCF_TaCursor) {
- /* Make sure that memory has been allocated on region. */
- assert(aMem[pOp->p2].flags & MEM_Ephem);
- if (pOp->opcode == OP_IdxInsert)
- rc = tarantoolSqlite3Insert(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
- else
- rc = tarantoolSqlite3Replace(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
- } else if (pBtCur->curFlags & BTCF_TEphemCursor) {
- rc = tarantoolSqlite3EphemeralInsert(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
+ space = aMem[pOp->p2].u.p;
+ }
+ assert(space != NULL);
+ if ((pOp->p5 & OPFLAG_EPH_INSERT) == 0) {
+ /* Make sure that memory has been allocated on region. */
+ assert(aMem[pOp->p1].flags & MEM_Ephem);
+ if (pOp->opcode == OP_IdxInsert) {
+ rc = tarantoolSqlite3Insert(space,
+ pIn2->z,
+ pIn2->z + pIn2->n);
} else {
- unreachable();
+ rc = tarantoolSqlite3Replace(space,
+ pIn2->z,
+ pIn2->z + pIn2->n);
}
- pC->cacheStatus = CACHE_STALE;
+ } else {
+ rc = tarantoolSqlite3EphemeralInsert(space,
+ pIn2->z,
+ pIn2->z + pIn2->n);
}
if (pOp->p5 & OPFLAG_OE_IGNORE) {
/* Ignore any kind of failes and do not raise error message */
rc = SQLITE_OK;
/* If we are in trigger, increment ignore raised counter */
- if (p->pFrame) {
+ if (p->pFrame)
p->ignoreRaised++;
- }
} else if (pOp->p5 & OPFLAG_OE_FAIL) {
p->errorAction = ON_CONFLICT_ACTION_FAIL;
} else if (pOp->p5 & OPFLAG_OE_ROLLBACK) {
p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
}
- if (rc) goto abort_due_to_error;
+ if (rc != 0)
+ goto abort_due_to_error;
break;
}
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 01dcc49..237a549 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4569,12 +4569,9 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
/* Do nothing */
} else if ((pLoop->wsFlags & WHERE_IDX_ONLY) == 0 &&
(wctrlFlags & WHERE_OR_SUBCLAUSE) == 0) {
- int op = OP_OpenRead;
- if (pWInfo->eOnePass != ONEPASS_OFF) {
- op = OP_OpenWrite;
+ if (pWInfo->eOnePass != ONEPASS_OFF)
pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
- };
- sqlite3OpenTable(pParse, pTabItem->iCursor, space, op);
+ sqlite3OpenTable(pParse, pTabItem->iCursor, space);
assert(pTabItem->iCursor == pLevel->iTabCur);
testcase(pWInfo->eOnePass == ONEPASS_OFF
&& pTab->nCol == BMS - 1);
@@ -4591,7 +4588,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
if (pLoop->wsFlags & WHERE_INDEXED) {
struct index_def *idx_def = pLoop->index_def;
int iIndexCur;
- int op = OP_OpenRead;
+ int op = OP_CursorOpen;
/* Check if index is primary. Either of
* points should be true:
* 1. struct Index is non-NULL and is
@@ -4636,12 +4633,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
}
}
assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
- op = OP_OpenWrite;
+ op = OP_CursorOpen;
pWInfo->aiCurOnePass[1] = iIndexCur;
} else if (iAuxArg
&& (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
iIndexCur = iAuxArg;
- op = OP_ReopenIdx;
+ op = OP_CursorReopen;
} else {
iIndexCur = pParse->nTab++;
}
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 8a3f2ac..1aeb360 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -416,7 +416,7 @@ struct WhereInfo {
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
ExprList *pDistinctSet; /* DISTINCT over all these values */
LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
- int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
+ int aiCurOnePass[2]; /* OP_CursorOpen cursors for the ONEPASS opt */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index e2aaca3..14278f0 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1341,7 +1341,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
- int regRowset = 0; /* Register for RowSet object */
+ int regRowset = 0; /* Cursor for RowSet object */
+ int reg_row_set = 0;
int regPk = 0; /* Register holding PK */
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
int iRetInit; /* Address of regReturn init */
@@ -1400,8 +1401,11 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
*/
if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
regRowset = pParse->nTab++;
+ reg_row_set = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- regRowset, pk_part_count);
+ reg_row_set, pk_part_count);
+ sqlite3VdbeAddOp3(v, OP_CursorOpen, regRowset, 0,
+ reg_row_set);
sql_vdbe_set_p4_key_def(pParse, pk_key_def);
regPk = ++pParse->nMem;
}
@@ -1545,7 +1549,9 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
r, pk_part_count, regPk);
sqlite3VdbeAddOp2
(v, OP_IdxInsert,
- regRowset, regPk);
+ regPk, reg_row_set);
+ sqlite3VdbeChangeP5(v,
+ OPFLAG_EPH_INSERT);
}
/* Release the array of temp registers */
--
2.16.2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-01 10:31 [tarantool-patches] [PATCH] sql: refactor SQL cursor to remove write ones Kirill Yukhin
@ 2018-10-02 11:56 ` n.pettik
2018-10-03 9:57 ` Kirill Yukhin
2018-10-04 12:00 ` Kirill Yukhin
1 sibling, 1 reply; 9+ messages in thread
From: n.pettik @ 2018-10-02 11:56 UTC (permalink / raw)
To: tarantool-patches; +Cc: Kirill Yukhin
Firstly, I see that code on branch differs (at the moment of writing review) from that
you send to review (in this letter). Outdated version contains a lot of artefacts
(tarantoolSqlite3EphemeralCreate2(), OP_IdxReplace2 etc).
Please, push your ready-for-review version.
In general patch is OK, I have spotted only several nits.
Also, I support your intention (which we discussed outside mailing list) to make
system spaces be ‘pre-loaded’. It means that all required system spaces will be
stored in predefined (at the start of program) registers. Moreover, I suggest to
attempt at involving not only system spaces. Idea is following:
each OP_CursorOpen during compilation stage skips register where space
should be stored. At the end of compilation (sql_finish_coding) we know that
totally program uses n registers. Then, we can allocate n + m (where m is number
of distinct spaces which are used in out query) registers to hold pointers to required
spaces. Finally, we patch each OP_CursorOpen and substitute void arg with
number of register where required space is stored. This approach for instance allows
to update all space pointers after schema changes with ease.
Finally, now I think about renaming CursorOpen to IteratorOpen: in fact after your
patch cursor has turned almost in wrapper around index iterator. Such naming
would underline the fact that ‘cursor’ is used only for read iterations over the space.
> On 1 Oct 2018, at 13:31, Kirill Yukhin <kyukhin@tarantool.org> wrote:
>
> In Tarantool opening and positioning cursor for writing
> have no sense. So refactor SQL code, to perform:
> - Creation of ephemeral table w/o any cursors machinery.
> No op-code returns register which contains plain pointer
> to the ephemeral space.
> - OpenRead/OpenWrite opcodes were replaced with single
> CursorOpen op-code, which establishes new cursor w/
> intention to sebsequent read from the space. This opcode
Nit: subsequent
> accepts both plain pointer (in P4 operand) and register
> which contains pointer (inn P3) to the ephemeral space.
> This query scheduler and DML routines thoroughly.
Nit: probably you missed verb in this sentence...
> diff --git a/src/box/sql.c b/src/box/sql.c
> index ab4a587..c5461ad 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
> return SQLITE_OK;
> }
>
> -/**
> - * Create ephemeral space and set cursor to the first entry. Features of
> - * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
> - * can be changed, but now only memtx engine is supported), primary index
> - * which covers all fields and no secondary indexes, given field number and
> - * collation sequence. All fields are scalar and nullable.
> - *
> - * @param pCur Cursor which will point to the new ephemeral space.
> - * @param field_count Number of fields in ephemeral space.
> - * @param key_info Keys description for new ephemeral space.
> - *
> - * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
> - */
> -int
> -tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
> +struct space *
> +tarantoolSqlite3EphemeralCreate(uint32_t field_count,
> struct sql_key_info *key_info)
> {
Would you mind to rename this function according to original Tarantool codestyle?
> int tarantoolSqlite3EphemeralInsert(struct space *space, const char *tuple,
> @@ -1461,35 +1436,31 @@ sql_debug_info(struct info_handler *h)
> info_end(h);
> }
>
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 43be777..0ae4c7d 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -883,7 +883,7 @@ vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id,
> struct space *space)
> {
> assert(space != NULL);
> - return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_OpenWrite, cursor,
> + return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_CursorOpen, cursor,
> index_id, 0, (void *) space, P4_SPACEPTR);
> }
>
> @@ -2693,7 +2693,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
> goto exit_create_index;
>
> sql_set_multi_write(parse, true);
> - sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, 0, 0,
> + sqlite3VdbeAddOp4(vdbe, OP_CursorOpen, cursor, 0, 0,
> (void *)space_by_id(BOX_INDEX_ID),
> P4_SPACEPTR);
> sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> index 9aa7058..ebde698 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
>
> void
> sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> - int cursor)
> + int cursor, int reg_eph)
This function is used twice: to materialize view during firing INSTEAD OF DELETE
and INSTEAD OF UPDATE triggers. In both cases, you pass reg_eph which is
not used after this function invocation. So I guess you can remove reg_eph arg.
> {
> struct sqlite3 *db = parse->db;
> where = sqlite3ExprDup(db, where, 0);
> @@ -83,7 +83,7 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> struct Select *select = sqlite3SelectNew(parse, NULL, from, where, NULL,
> NULL, NULL, 0, NULL, NULL);
> struct SelectDest dest;
> - sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor);
> + sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor, reg_eph);
> sqlite3Select(parse, select, &dest);
> sql_select_delete(db, select);
> }
> @@ -194,9 +194,10 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
> /* If we are trying to delete from a view, realize that
> * view into an ephemeral table.
> */
> + int reg_eph = ++parse->nMem;
This is what I am talking above: this variable is used once (right below).
> if (is_view) {
> sql_materialize_view(parse, space->def->name, where,
> - tab_cursor);
> + tab_cursor, reg_eph);
> }
>
> /* Initialize the counter of the number of rows deleted,
> @@ -390,6 +393,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>
> VdbeCoverage(v);
> } else {
> + sqlite3VdbeAddOp3(v, OP_CursorOpen,
> + eph_cursor, 0, reg_eph);
Broken indentation.
> addr_loop = sqlite3VdbeAddOp1(v, OP_Rewind, eph_cursor);
> VdbeCoverage(v);
> sqlite3VdbeAddOp2(v, OP_RowData, eph_cursor, reg_key);
> diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> index a13de4f..aa26342 100644
> --- a/src/box/sql/expr.c
> +++ b/src/box/sql/expr.c
> @@ -2714,8 +2714,10 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
> */
> pExpr->iTable = pParse->nTab++;
> pExpr->is_ephemeral = 1;
> + int reg_eph = ++pParse->nMem;
> addr = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
> - pExpr->iTable, nVal);
> + reg_eph, nVal);
> + sqlite3VdbeAddOp3(v, OP_CursorOpen, pExpr->iTable, 0, reg_eph);
Out of 80 chars.
> struct sql_key_info *key_info = sql_key_info_new(pParse->db, nVal);
> if (key_info == NULL)
> return 0;
> @@ -2736,7 +2738,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
> SelectDest dest;
> int i;
> sqlite3SelectDestInit(&dest, SRT_Set,
> - pExpr->iTable);
> + pExpr->iTable, reg_eph);
Out of 80.
> dest.zAffSdst =
> exprINAffinity(pParse, pExpr);
> assert((pExpr->iTable & 0x0000FFFF) ==
> @@ -2808,8 +2810,10 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
> 1, r2, &affinity, 1);
> sqlite3ExprCacheAffinityChange(pParse,
> r3, 1);
> - sqlite3VdbeAddOp2(v, OP_IdxInsert,
> - pExpr->iTable, r2);
> + sqlite3VdbeAddOp2(v, OP_IdxInsert, r2,
> + reg_eph);
> + sqlite3VdbeChangeP5(v,
> + OPFLAG_EPH_INSERT);
> }
> sqlite3ReleaseTempReg(pParse, r1);
> sqlite3ReleaseTempReg(pParse, r2);
> @@ -2847,7 +2851,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
>
> pSel = pExpr->x.pSelect;
> nReg = pExpr->op == TK_SELECT ? pSel->pEList->nExpr : 1;
> - sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1);
> + sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1, 0);
If you don’t use ephemeral register, mb it is worth initialising it with -1?
0 is still valid register number.
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> index 03f4e1b..c9b4846 100644
> --- a/src/box/sql/insert.c
> +++ b/src/box/sql/insert.c
> @@ -46,12 +46,10 @@
> void
> sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */
> int iCur, /* The cursor number of the table */
> - struct space *space, /* The table to be opened */
> - int opcode) /* OP_OpenRead or OP_OpenWrite */
> + struct space *space) /* The table to be opened */
> {
This function appears only once in source code. Lets simply inline it,
instead of refactoring.
>
> @@ -1327,7 +1334,8 @@ xferOptimization(Parse * pParse, /* Parser context */
> sqlite3VdbeChangeP5(v, OPFLAG_XFER_OPT);
> #endif
>
> - sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
> + sqlite3VdbeAddOp4(v, OP_IdxInsert, regData, 0, 0,
> + (char *)dest, P4_SPACEPTR);
> switch (onError) {
> case ON_CONFLICT_ACTION_IGNORE:
> sqlite3VdbeChangeP5(v, OPFLAG_OE_IGNORE | OPFLAG_NCHANGE);
> diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> index 473cf89..6b5f19d 100644
> --- a/src/box/sql/parse.y
> +++ b/src/box/sql/parse.y
> @@ -390,7 +390,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
> //////////////////////// The SELECT statement /////////////////////////////////
> //
> cmd ::= select(X). {
> - SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
> + SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
Nit: don’t mess tabs with spaces: in parse.y spaces are used.
Yep, it contradicts our codestyle, but lets better make code
look uniformly, than messing code styles...
> if(!pParse->parse_only)
> sqlite3Select(pParse, X, &dest);
> else
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 02c0a5d..0370327 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -66,6 +66,8 @@ struct DistinctCtx {
> u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
> int tabTnct; /* Ephemeral table used for DISTINCT processing */
> int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
> + /* Register, containing a pointer to ephemeral space. */
> + int reg_eph;
Please, leave explanation comment; otherwise it seems quite easy to
mess tabTnct (which is really cursor used to iterate over ephemeral space)
with reg_eph which in turn is register containing space *.
You may explain it one place and refer to it in other. Such schema
(I mean ephemeral space * + ephemeral cursor) appears quite
frequently.
> @@ -831,24 +838,32 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
> *
> * A jump to addrRepeat is made and the N+1 values are popped from the
> * stack if the top N elements are not distinct.
> + *
> + * @param parse Parsing and code generating context.
> + * @param cursor A sorting index cursor used to test for distinctness.
> + * @param reg_eph Register holding ephemeral's space pointer.
> + * @param addr_repeat Jump to here if not distinct.
> + * @param n Number of elements.
> + * @param reg_data First register holding the data.
> */
> static void
> -codeDistinct(Parse * pParse, /* Parsing and code generating context */
> - int iTab, /* A sorting index used to test for distinctness */
> - int addrRepeat, /* Jump to here if not distinct */
> - int N, /* Number of elements */
> - int iMem) /* First element */
> +codeDistinct(struct Parse * parse,
> + int cursor,
> + int reg_eph,
> + int addr_repeat,
> + int n,
> + int reg_data)
> {
> - Vdbe *v;
> + struct Vdbe *v = parse->pVdbe;
> int r1;
>
> - v = pParse->pVdbe;
> - r1 = sqlite3GetTempReg(pParse);
> - sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
> + r1 = sqlite3GetTempReg(parse);
> + sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
> VdbeCoverage(v);
> - sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
> - sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
> - sqlite3ReleaseTempReg(pParse, r1);
> + sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
> + sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
> + sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
> + sqlite3ReleaseTempReg(parse, r1);
> }
I slightly refactored this function (you forgot to format arguments, update comment etc).
It is only cosmetic change tho:
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index b70b85f54..e6b8b3864 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -831,35 +831,29 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
}
/**
- * Add code that will check to make sure the N registers starting at iMem
- * form a distinct entry. iTab is a sorting index that holds previously
- * seen combinations of the N values. A new entry is made in iTab
- * if the current N values are new.
+ * Add code that will check to make sure the @n registers starting
+ * at @reg_data form a distinct entry. @cursor is a sorting index
+ * that holds previously seen combinations of the @n values.
+ * A new entry is made in @cursor if the current n values are new.
*
- * A jump to addrRepeat is made and the N+1 values are popped from the
- * stack if the top N elements are not distinct.
+ * A jump to @addr_repeat is made and the @n+1 values are popped
+ * from the stack if the top n elements are not distinct.
*
* @param parse Parsing and code generating context.
- * @param cursor A sorting index cursor used to test for distinctness.
- * @param reg_eph Register holding ephemeral's space pointer.
- * @param addr_repeat Jump to here if not distinct.
- * @param n Number of elements.
+ * @param cursor A sorting index cursor used to test for
+ * distinctness.
+ * @param reg_eph Register holding ephemeral space's pointer.
+ * @param addr_repeat Jump here if not distinct.
+ * @param n Number of elements in record.
* @param reg_data First register holding the data.
*/
static void
-codeDistinct(struct Parse * parse,
- int cursor,
- int reg_eph,
- int addr_repeat,
- int n,
- int reg_data)
+vdbe_insert_distinct(struct Parse *parse, int cursor, int reg_eph,
+ int addr_repeat, int n, int reg_data)
{
struct Vdbe *v = parse->pVdbe;
- int r1;
-
- r1 = sqlite3GetTempReg(parse);
+ int r1 = sqlite3GetTempReg(parse);
sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
- VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
@@ -1061,12 +1055,13 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
default:{
- assert(pDistinct->eTnctType ==
- WHERE_DISTINCT_UNORDERED);
- codeDistinct(pParse, pDistinct->tabTnct, pDistinct->reg_eph,
- iContinue, nResultCol, regResult);
- break;
- }
+ assert(pDistinct->eTnctType ==
+ WHERE_DISTINCT_UNORDERED);
+ vdbe_insert_distinct(pParse, pDistinct->tabTnct,
+ pDistinct->reg_eph, iContinue,
+ nResultCol, regResult);
+ break;
+ }
}
if (pSort == 0) {
codeOffset(v, p->iOffset, iContinue);
@@ -5391,8 +5386,8 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
addrNext = sqlite3VdbeMakeLabel(v);
testcase(nArg == 0); /* Error condition */
testcase(nArg > 1); /* Also an error */
- codeDistinct(pParse, pF->iDistinct, pF->reg_eph,
- addrNext, 1, regAgg);
+ vdbe_insert_distinct(pParse, pF->iDistinct, pF->reg_eph,
+ addrNext, 1, regAgg);
>
> /*
> @@ -998,7 +1013,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
> * row is all NULLs.
> */
> sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> - pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
> + sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
> + pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct + 1);
Actually, I don’t understand why you need manipulations with addrTnct + 1.
If they are really vital, then leave explanation comment pls.
(I guess it is required since two opcodes (OpenEphemeral + OpenCursor)
turn into one (OpenCursor), but I didn’t dive into code).
> pOp->opcode = OP_Null;
> pOp->p1 = 1;
> pOp->p2 = regPrev;
> @@ -1040,13 +1056,14 @@ selectInnerLoop(Parse * pParse, /* The parser context */
>
> case WHERE_DISTINCT_UNIQUE:{
> sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> + sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
The same is here. Also, out of 80.
>
> @@ -3417,8 +3459,8 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */
> regAddrB = ++pParse->nMem;
> regOutA = ++pParse->nMem;
> regOutB = ++pParse->nMem;
> - sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
> - sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
> + sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA, 0);
> + sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB, 0);
Lets initialise unused ephemeral register with -1 here and in other
places as well.
>
> /* Generate a coroutine to evaluate the SELECT statement to the
> * left of the compound operator - the "A" select.
> @@ -5261,7 +5303,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
> /* Verify that all AggInfo registers are within the range specified by
> * AggInfo.mnReg..AggInfo.mxReg
> */
> - assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
> + assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
Hm? Explain pls this change.
> for (i = 0; i < pAggInfo->nColumn; i++) {
> assert(pAggInfo->aCol[i].iMem >= pAggInfo->mnReg
> && pAggInfo->aCol[i].iMem <= pAggInfo->mxReg);
>
> @@ -5840,13 +5891,16 @@ sqlite3Select(Parse * pParse, /* The parser context */
> */
> if (p->selFlags & SF_Distinct) {
> sDistinct.tabTnct = pParse->nTab++;
> + sDistinct.reg_eph = ++pParse->nMem;
> struct sql_key_info *key_info =
> sql_expr_list_to_key_info(pParse, p->pEList, 0);
> sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
> - sDistinct.tabTnct,
> + sDistinct.reg_eph,
> key_info->part_count,
> 0, (char *)key_info,
> P4_KEYINFO);
> + sqlite3VdbeAddOp3(v, OP_CursorOpen, sDistinct.tabTnct, 0,
> + sDistinct.reg_eph);
> VdbeComment((v, "Distinct table"));
> sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
> } else {
> @@ -5886,6 +5940,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> */
> if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
> sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
> + sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
Explain pls why do you need this.
> }
>
> /* Use the standard inner loop. */
> @@ -6130,6 +6185,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> ) {
> sSort.pOrderBy = 0;
> sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
> + sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
The same.
> }
>
> /* Evaluate the current GROUP BY terms and store in b0, b1, b2...
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 53188e7..51110aa 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -2000,6 +2000,8 @@ struct AggInfo {
> FuncDef *pFunc; /* The aggregate function implementation */
> int iMem; /* Memory location that acts as accumulator */
> int iDistinct; /* Ephemeral table used to enforce DISTINCT */
> + /* Register, holding ephemeral's spacepointer. */
> + int reg_eph;
> } *aFunc;
> int nFunc; /* Number of entries in aFunc[] */
> };
> @@ -2327,6 +2329,8 @@ struct SrcList {
> } fg;
> u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
> int iCursor; /* The VDBE cursor number used to access this table */
> + /* Register, containing pointer to the space. */
> + int reg;
Do you really need there this reg? AFAIS it used only in select.c:5741:11 and
select.c:5760:35. Could you suggest workaround to avoid adding member to
SrcList? I am asking cause SrcList is widely used through source code and
extra member is unlikely to be suitable.
> Expr *pOn; /* The ON clause of a join */
> IdList *pUsing; /* The USING clause of a join */
> Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
> @@ -2591,6 +2595,8 @@ struct SelectDest {
> u8 eDest; /* How to dispose of the results. On of SRT_* above. */
> char *zAffSdst; /* Affinity used when eDest==SRT_Set */
> int iSDParm; /* A parameter used by the eDest disposal method */
> + /* Register containing ephemeral's space pointer. */
> + int reg_eph;
> int iSdst; /* Base register where results are written */
> int nSdst; /* Number of registers allocated */
> ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
>
> /**
> diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
> index c6bd15b..271f40a 100644
> --- a/src/box/sql/trigger.c
> +++ b/src/box/sql/trigger.c
> @@ -694,7 +688,7 @@ codeTriggerProgram(Parse * pParse, /* The parser context */
> SelectDest sDest;
> Select *pSelect =
> sqlite3SelectDup(db, pStep->pSelect, 0);
> - sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
> + sqlite3SelectDestInit(&sDest, SRT_Discard, 0, 0);
Lets initialise unused ephemeral register with -1.
> sqlite3Select(pParse, pSelect, &sDest);
> sql_select_delete(db, pSelect);
> break;
> diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> index 730872f..71bd71d 100644
> --- a/src/box/sql/update.c
> +++ b/src/box/sql/update.c
> @@ -222,13 +222,16 @@ sqlite3Update(Parse * pParse, /* The parser context */
> * an ephemeral table.
> */
> uint32_t pk_part_count;
> + struct space *space;
> if (is_view) {
> - sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
> + int reg_eph = ++pParse->nMem;
> + sql_materialize_view(pParse, def->name, pWhere, pk_cursor, reg_eph);
> /* Number of columns from SELECT plus ID.*/
> pk_part_count = nKey = def->field_count + 1;
> } else {
> - vdbe_emit_open_cursor(pParse, pk_cursor, 0,
> - space_by_id(pTab->def->id));
> + space = space_by_id(pTab->def->id);
You don’t need to fetch space from cache: now real space is
stored in pTab->space (see sql_lookup_table()).
> + assert(space != NULL);
> + vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
> pk_part_count = pPk->def->key_def->part_count;
> }
>
> @@ -242,11 +245,12 @@ sqlite3Update(Parse * pParse, /* The parser context */
> int iPk = pParse->nMem + 1;
> pParse->nMem += pk_part_count;
> regKey = ++pParse->nMem;
> + int reg_eph = ++pParse->nMem;
> iEph = pParse->nTab++;
> sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
>
> /* Address of the OpenEphemeral instruction. */
> - int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iEph,
> + int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
> pk_part_count);
> pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
> WHERE_ONEPASS_DESIRED, pk_cursor);
> @@ -276,9 +280,9 @@ sqlite3Update(Parse * pParse, /* The parser context */
> pPk->def);
> sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
> regKey, zAff, pk_part_count);
> - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
> + sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
> /* Set flag to save memory allocating one by malloc. */
> - sqlite3VdbeChangeP5(v, 1);
> + sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE | OPFLAG_EPH_INSERT);
These flags are unlikely to be compatible: NCHANGE refers to changes
occurred to db. Insertion to ephemeral space can’t change anything in db.
This snippet should look like (yep it initially contains bug: ChangeP5(v, 1)
should refer to OP_MakeRecord):
+++ b/src/box/sql/update.c
@@ -280,9 +280,10 @@ sqlite3Update(Parse * pParse, /* The parser context */
pPk->def);
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
regKey, zAff, pk_part_count);
+ sqlite3VdbeChangeP5(v, 1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
/* Set flag to save memory allocating one by malloc. */
- sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE | OPFLAG_EPH_INSERT);
+ sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
> }
> /* End the database scan loop.
> */
>
> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index 00ffb0c..7df491b 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -3054,51 +3054,37 @@ case OP_TTransaction: {
> break;
> }
>
> @@ -3107,66 +3093,60 @@ case OP_OpenWrite:
> "need to re-compile SQL statement");
> goto abort_due_to_error;
> }
> - p2 = pOp->p2;
> - struct space *space = pOp->p4.space;
> + struct space *space;
> + if (pOp->p4type == P4_SPACEPTR)
> + space = pOp->p4.space;
> + else
> + space = aMem[pOp->p3].u.p;
> assert(space != NULL);
> - struct index *index = space_index(space, p2);
> + struct index *index = space_index(space, pOp->p2);
> assert(index != NULL);
> - /*
> - * Since Tarantool iterator provides the full tuple,
> - * we need a number of fields as wide as the table itself.
> - * Otherwise, not enough slots for row parser cache are
> - * allocated in VdbeCursor object.
> - */
> - nField = space->def->field_count;
> - assert(pOp->p1>=0);
> - assert(nField>=0);
> - pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_TARANTOOL);
> - if (pCur==0) goto no_mem;
> - pCur->nullRow = 1;
> - pBtCur = pCur->uc.pCursor;
> - pBtCur->curFlags |= BTCF_TaCursor;
> - pBtCur->space = space;
> - pBtCur->index = index;
> - pBtCur->eState = CURSOR_INVALID;
> + assert(pOp->p1 >= 0);
Trailing tab.
> + cur = allocateCursor(p, pOp->p1,
> + space->def->exact_field_count == 0 ?
> + space->def->field_count :
> + space->def->exact_field_count,
> + CURTYPE_TARANTOOL);
> + if (cur == NULL)
> + goto no_mem;
> + struct BtCursor *bt_cur = cur->uc.pCursor;
> + bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
> + BTCF_TaCursor;
> + bt_cur->space = space;
> + bt_cur->index = index;
> + bt_cur->eState = CURSOR_INVALID;
Grep CURSOR_INVALID - it is not used anywhere else.
Mb it is worth to remove it?
> /* Key info still contains sorter order and collation. */
> - pCur->key_def = index->def->key_def;
> -
> + cur->key_def = index->def->key_def;
> + cur->nullRow = 1;
> open_cursor_set_hints:
> - pCur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
> - if (rc) goto abort_due_to_error;
> + cur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
> + if (rc != 0)
> + goto abort_due_to_error;
> break;
> }
>
> -/* Opcode: IdxInsert P1 P2 * * P5
> +/* Opcode: SorterInsert P1 P2 * * *
> * Synopsis: key=r[P2]
> *
> - * @param P1 Index of a space cursor.
> - * @param P2 Index of a register with MessagePack data to insert.
> + * Register P2 holds an SQL index key made using the
> + * MakeRecord instructions. This opcode writes that key
> + * into the sorter P1. Data for the entry is nil.
> + */
> +case OP_SorterInsert: { /* in2 */
> + assert(pOp->p1 >= 0 && pOp->p1 < p->nCursor);
> + struct VdbeCursor *cursor = p->apCsr[pOp->p1];
> + assert(cursor != NULL);
> + assert(isSorter(cursor));
> + pIn2 = &aMem[pOp->p2];
> + assert((pIn2->flags & MEM_Blob) != 0);
> + rc = ExpandBlob(pIn2);
> + if (rc != 0)
> + goto abort_due_to_error;
> + rc = sqlite3VdbeSorterWrite(cursor, pIn2);
> + if (rc != 0)
> + goto abort_due_to_error;
> + break;
> +}
> +
> +/* Opcode: IdxInsert2 P1 * * P4 P5
P2 and P3 are used, so remove stars for clarity's sake.
> + * Synopsis: key=r[P1]
> + *
> + * @param P1 Index of a register with MessagePack data to insert.
> + * @param P2 If P4 is not set, then P2 is register containing pointer
> + * to space to insert into.
> + * @param P4 Pointer to the struct space to insert to.
> * @param P5 Flags. If P5 contains OPFLAG_NCHANGE, then VDBE
> * accounts the change in a case of successful insertion in
> * nChange counter. If P5 contains OPFLAG_OE_IGNORE, then
> * we are processing INSERT OR INGORE statement. Thus, in
> * case of conflict we don't raise an error.
> */
> -/* Opcode: IdxReplace P1 P2 * * P5
> - * Synopsis: key=r[P2]
> +/* Opcode: IdxReplace2 P1 * * P4 P5
> + * Synopsis: key=r[P1]
> *
> * This opcode works exactly as IdxInsert does, but in Tarantool
> * internals it invokes box_replace() instead of box_insert().
> */
> -/* Opcode: SorterInsert P1 P2 * * *
> - * Synopsis: key=r[P2]
> - *
> - * Register P2 holds an SQL index key made using the
> - * MakeRecord instructions. This opcode writes that key
> - * into the sorter P1. Data for the entry is nil.
> - */
> -case OP_SorterInsert: /* in2 */
> case OP_IdxReplace:
> -case OP_IdxInsert: { /* in2 */
> - VdbeCursor *pC;
> -
> - assert(pOp->p1>=0 && pOp->p1<p->nCursor);
> - pC = p->apCsr[pOp->p1];
> - assert(pC!=0);
> - assert(isSorter(pC)==(pOp->opcode==OP_SorterInsert));
> - pIn2 = &aMem[pOp->p2];
> - assert(pIn2->flags & MEM_Blob);
> - if (pOp->p5 & OPFLAG_NCHANGE) p->nChange++;
> - assert(pC->eCurType==CURTYPE_TARANTOOL || pOp->opcode==OP_SorterInsert);
> +case OP_IdxInsert: {
> + pIn2 = &aMem[pOp->p1];
> + assert((pIn2->flags & MEM_Blob) != 0);
> + if (pOp->p5 & OPFLAG_NCHANGE)
> + p->nChange++;
Broken indentation.
> rc = ExpandBlob(pIn2);
> - if (rc) goto abort_due_to_error;
> - if (pOp->opcode==OP_SorterInsert) {
> - rc = sqlite3VdbeSorterWrite(pC, pIn2);
> + if (rc != 0)
> + goto abort_due_to_error;
Broken indentation.
> + struct space *space;
> + if (pOp->p4type == P4_SPACEPTR) {
> + space = pOp->p4.space;
> } else {
> - BtCursor *pBtCur = pC->uc.pCursor;
> - if (pBtCur->curFlags & BTCF_TaCursor) {
> - /* Make sure that memory has been allocated on region. */
> - assert(aMem[pOp->p2].flags & MEM_Ephem);
> - if (pOp->opcode == OP_IdxInsert)
> - rc = tarantoolSqlite3Insert(pBtCur->space,
> - pIn2->z,
> - pIn2->z + pIn2->n);
> - else
> - rc = tarantoolSqlite3Replace(pBtCur->space,
> - pIn2->z,
> - pIn2->z + pIn2->n);
> - } else if (pBtCur->curFlags & BTCF_TEphemCursor) {
> - rc = tarantoolSqlite3EphemeralInsert(pBtCur->space,
> - pIn2->z,
> - pIn2->z + pIn2->n);
> + space = aMem[pOp->p2].u.p;
> + }
> + assert(space != NULL);
> + if ((pOp->p5 & OPFLAG_EPH_INSERT) == 0) {
Why do you need this flag at all? You have space here and you can
simply check its id to point out whether space is ephemeral or not.
Throwing out this flag would allow you drop typical
sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT); usage which looks
quite annoying.
> + /* Make sure that memory has been allocated on region. */
> + assert(aMem[pOp->p1].flags & MEM_Ephem);
> + if (pOp->opcode == OP_IdxInsert) {
> + rc = tarantoolSqlite3Insert(space,
> + pIn2->z,
> + pIn2->z + pIn2->n);
> } else {
> - unreachable();
> + rc = tarantoolSqlite3Replace(space,
> + pIn2->z,
> + pIn2->z + pIn2->n);
> }
> - pC->cacheStatus = CACHE_STALE;
> + } else {
> + rc = tarantoolSqlite3EphemeralInsert(space,
> + pIn2->z,
> + pIn2->z + pIn2->n);
> }
>
> if (pOp->p5 & OPFLAG_OE_IGNORE) {
> /* Ignore any kind of failes and do not raise error message */
> rc = SQLITE_OK;
> /* If we are in trigger, increment ignore raised counter */
> - if (p->pFrame) {
> + if (p->pFrame)
> p->ignoreRaised++;
> - }
> } else if (pOp->p5 & OPFLAG_OE_FAIL) {
> p->errorAction = ON_CONFLICT_ACTION_FAIL;
> } else if (pOp->p5 & OPFLAG_OE_ROLLBACK) {
> p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
> }
> - if (rc) goto abort_due_to_error;
> + if (rc != 0)
> + goto abort_due_to_error;
> break;
> }
I suggest cosmetic refactoring:
+++ b/src/box/sql/vdbe.c
@@ -4289,32 +4289,28 @@ case OP_IdxInsert: {
pIn2 = &aMem[pOp->p1];
assert((pIn2->flags & MEM_Blob) != 0);
if (pOp->p5 & OPFLAG_NCHANGE)
- p->nChange++;
+ p->nChange++;
rc = ExpandBlob(pIn2);
if (rc != 0)
- goto abort_due_to_error;
+ goto abort_due_to_error;
struct space *space;
- if (pOp->p4type == P4_SPACEPTR) {
+ if (pOp->p4type == P4_SPACEPTR)
space = pOp->p4.space;
- } else {
+ else
space = aMem[pOp->p2].u.p;
- }
assert(space != NULL);
if ((pOp->p5 & OPFLAG_EPH_INSERT) == 0) {
/* Make sure that memory has been allocated on region. */
assert(aMem[pOp->p1].flags & MEM_Ephem);
if (pOp->opcode == OP_IdxInsert) {
- rc = tarantoolSqlite3Insert(space,
- pIn2->z,
+ rc = tarantoolSqlite3Insert(space, pIn2->z,
pIn2->z + pIn2->n);
} else {
- rc = tarantoolSqlite3Replace(space,
- pIn2->z,
+ rc = tarantoolSqlite3Replace(space, pIn2->z,
pIn2->z + pIn2->n);
}
} else {
- rc = tarantoolSqlite3EphemeralInsert(space,
- pIn2->z,
+ rc = tarantoolSqlite3EphemeralInsert(space, pIn2->z,
pIn2->z + pIn2->n);
}
@@ -4330,7 +4326,7 @@ case OP_IdxInsert: {
p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
}
if (rc != 0)
- goto abort_due_to_error;
+ goto abort_due_to_error;
break;
}
>
> @@ -4591,7 +4588,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
> if (pLoop->wsFlags & WHERE_INDEXED) {
> struct index_def *idx_def = pLoop->index_def;
> int iIndexCur;
> - int op = OP_OpenRead;
> + int op = OP_CursorOpen;
> /* Check if index is primary. Either of
> * points should be true:
> * 1. struct Index is non-NULL and is
> @@ -4636,12 +4633,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
> }
> }
> assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
> - op = OP_OpenWrite;
> + op = OP_CursorOpen;
It is redundant assignment: you initialise this variable with OP_CursorOpen value.
> diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
> index e2aaca3..14278f0 100644
> --- a/src/box/sql/wherecode.c
> +++ b/src/box/sql/wherecode.c
> @@ -1341,7 +1341,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
> int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
>
> int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
> - int regRowset = 0; /* Register for RowSet object */
> + int regRowset = 0; /* Cursor for RowSet object */
regRowset now is really cursor, so lets better call like ‘cursor_row_set’.
Otherwise, it seems to be very confusing to operate on two
similar variables: regRowset and reg_row_set...
> + int reg_row_set = 0;
> int regPk = 0; /* Register holding PK */
> int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
> int iRetInit; /* Address of regReturn init */
> @@ -1400,8 +1401,11 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
> */
> if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
> regRowset = pParse->nTab++;
> + reg_row_set = ++pParse->nMem;
> sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
> - regRowset, pk_part_count);
> + reg_row_set, pk_part_count);
> + sqlite3VdbeAddOp3(v, OP_CursorOpen, regRowset, 0,
> + reg_row_set);
> sql_vdbe_set_p4_key_def(pParse, pk_key_def);
> regPk = ++pParse->nMem;
> }
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-02 11:56 ` [tarantool-patches] " n.pettik
@ 2018-10-03 9:57 ` Kirill Yukhin
2018-10-04 2:21 ` n.pettik
0 siblings, 1 reply; 9+ messages in thread
From: Kirill Yukhin @ 2018-10-03 9:57 UTC (permalink / raw)
To: n.pettik; +Cc: tarantool-patches
Hello Nikita!
Thanks a lot for review. My answers inlined. Updated patch in the
bottom of the message, branch force pushed to GH.
On 02 окт 14:56, n.pettik wrote:
> Firstly, I see that code on branch differs (at the moment of writing review) from that
> you send to review (in this letter). Outdated version contains a lot of artefacts
> (tarantoolSqlite3EphemeralCreate2(), OP_IdxReplace2 etc).
> Please, push your ready-for-review version.
Done.
> In general patch is OK, I have spotted only several nits.
> Also, I support your intention (which we discussed outside mailing list) to make
> system spaces be ‘pre-loaded’. It means that all required system spaces will be
> stored in predefined (at the start of program) registers. Moreover, I suggest to
> attempt at involving not only system spaces. Idea is following:
> each OP_CursorOpen during compilation stage skips register where space
> should be stored. At the end of compilation (sql_finish_coding) we know that
> totally program uses n registers. Then, we can allocate n + m (where m is number
> of distinct spaces which are used in out query) registers to hold pointers to required
> spaces. Finally, we patch each OP_CursorOpen and substitute void arg with
> number of register where required space is stored. This approach for instance allows
> to update all space pointers after schema changes with ease.
Great! I'll do that in follow up patches.
> Finally, now I think about renaming CursorOpen to IteratorOpen: in fact after your
> patch cursor has turned almost in wrapper around index iterator. Such naming
> would underline the fact that ‘cursor’ is used only for read iterations over the space.
Yes, sure. Renamed.
> > On 1 Oct 2018, at 13:31, Kirill Yukhin <kyukhin@tarantool.org> wrote:
> >
> > In Tarantool opening and positioning cursor for writing
> > have no sense. So refactor SQL code, to perform:
> > - Creation of ephemeral table w/o any cursors machinery.
> > No op-code returns register which contains plain pointer
> > to the ephemeral space.
> > - OpenRead/OpenWrite opcodes were replaced with single
> > CursorOpen op-code, which establishes new cursor w/
> > intention to sebsequent read from the space. This opcode
>
> Nit: subsequent
Fixed.
> > accepts both plain pointer (in P4 operand) and register
> > which contains pointer (inn P3) to the ephemeral space.
> > This query scheduler and DML routines thoroughly.
>
> Nit: probably you missed verb in this sentence...
Fixed.
> > diff --git a/src/box/sql.c b/src/box/sql.c
> > index ab4a587..c5461ad 100644
> > --- a/src/box/sql.c
> > +++ b/src/box/sql.c
> > @@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
> > return SQLITE_OK;
> > }
> >
> > -/**
> > - * Create ephemeral space and set cursor to the first entry. Features of
> > - * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
> > - * can be changed, but now only memtx engine is supported), primary index
> > - * which covers all fields and no secondary indexes, given field number and
> > - * collation sequence. All fields are scalar and nullable.
> > - *
> > - * @param pCur Cursor which will point to the new ephemeral space.
> > - * @param field_count Number of fields in ephemeral space.
> > - * @param key_info Keys description for new ephemeral space.
> > - *
> > - * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
> > - */
> > -int
> > -tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
> > +struct space *
> > +tarantoolSqlite3EphemeralCreate(uint32_t field_count,
> > struct sql_key_info *key_info)
> > {
>
> Would you mind to rename this function according to original Tarantool codestyle?
Done.
> > diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> > index 9aa7058..ebde698 100644
> > --- a/src/box/sql/delete.c
> > +++ b/src/box/sql/delete.c
> > @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
> >
> > void
> > sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> > - int cursor)
> > + int cursor, int reg_eph)
>
> This function is used twice: to materialize view during firing INSTEAD OF DELETE
> and INSTEAD OF UPDATE triggers. In both cases, you pass reg_eph which is
> not used after this function invocation. So I guess you can remove reg_eph arg.
IMHO, allocating a register w/o assigning it to local variable is less
straightforward, but OK, done.
> > @@ -390,6 +393,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
> >
> > VdbeCoverage(v);
> > } else {
> > + sqlite3VdbeAddOp3(v, OP_CursorOpen,
> > + eph_cursor, 0, reg_eph);
>
> Broken indentation.
Fixed.
> > diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
> > index a13de4f..aa26342 100644
> > --- a/src/box/sql/expr.c
> > +++ b/src/box/sql/expr.c
> > @@ -2714,8 +2714,10 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
> > */
> > pExpr->iTable = pParse->nTab++;
> > pExpr->is_ephemeral = 1;
> > + int reg_eph = ++pParse->nMem;
> > addr = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
> > - pExpr->iTable, nVal);
> > + reg_eph, nVal);
> > + sqlite3VdbeAddOp3(v, OP_CursorOpen, pExpr->iTable, 0, reg_eph);
>
> Out of 80 chars.
Fixed.
> > @@ -2736,7 +2738,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
> > SelectDest dest;
> > int i;
> > sqlite3SelectDestInit(&dest, SRT_Set,
> > - pExpr->iTable);
> > + pExpr->iTable, reg_eph);
>
> Out of 80.
Fixed.
> > @@ -2847,7 +2851,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
> >
> > pSel = pExpr->x.pSelect;
> > nReg = pExpr->op == TK_SELECT ? pSel->pEList->nExpr : 1;
> > - sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1);
> > + sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1, 0);
>
> If you don’t use ephemeral register, mb it is worth initialising it with -1?
> 0 is still valid register number.
Yes, sure. Fixed thoroughly.
> > diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> > index 03f4e1b..c9b4846 100644
> > --- a/src/box/sql/insert.c
> > +++ b/src/box/sql/insert.c
> > @@ -46,12 +46,10 @@
> > void
> > sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */
> > int iCur, /* The cursor number of the table */
> > - struct space *space, /* The table to be opened */
> > - int opcode) /* OP_OpenRead or OP_OpenWrite */
> > + struct space *space) /* The table to be opened */
> > {
>
> This function appears only once in source code. Lets simply inline it,
> instead of refactoring.
Done.
> > diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
> > index 473cf89..6b5f19d 100644
> > --- a/src/box/sql/parse.y
> > +++ b/src/box/sql/parse.y
> > @@ -390,7 +390,7 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
> > //////////////////////// The SELECT statement /////////////////////////////////
> > //
> > cmd ::= select(X). {
> > - SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
> > + SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
>
> Nit: don’t mess tabs with spaces: in parse.y spaces are used.
> Yep, it contradicts our codestyle, but lets better make code
> look uniformly, than messing code styles...
Fixed.
> > index 02c0a5d..0370327 100644
> > --- a/src/box/sql/select.c
> > +++ b/src/box/sql/select.c
> > @@ -66,6 +66,8 @@ struct DistinctCtx {
> > u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
> > int tabTnct; /* Ephemeral table used for DISTINCT processing */
> > int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
> > + /* Register, containing a pointer to ephemeral space. */
> > + int reg_eph;
>
> Please, leave explanation comment; otherwise it seems quite easy to
> mess tabTnct (which is really cursor used to iterate over ephemeral space)
> with reg_eph which in turn is register containing space *.
> You may explain it one place and refer to it in other. Such schema
> (I mean ephemeral space * + ephemeral cursor) appears quite
> frequently.
I've groupped those two fields together and extended comment.
> > @@ -831,24 +838,32 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
> > *
> > * A jump to addrRepeat is made and the N+1 values are popped from the
> > * stack if the top N elements are not distinct.
> > + *
> > + * @param parse Parsing and code generating context.
> > + * @param cursor A sorting index cursor used to test for distinctness.
> > + * @param reg_eph Register holding ephemeral's space pointer.
> > + * @param addr_repeat Jump to here if not distinct.
> > + * @param n Number of elements.
> > + * @param reg_data First register holding the data.
> > */
> > static void
> > -codeDistinct(Parse * pParse, /* Parsing and code generating context */
> > - int iTab, /* A sorting index used to test for distinctness */
> > - int addrRepeat, /* Jump to here if not distinct */
> > - int N, /* Number of elements */
> > - int iMem) /* First element */
> > +codeDistinct(struct Parse * parse,
> > + int cursor,
> > + int reg_eph,
> > + int addr_repeat,
> > + int n,
> > + int reg_data)
> > {
> > - Vdbe *v;
> > + struct Vdbe *v = parse->pVdbe;
> > int r1;
> >
> > - v = pParse->pVdbe;
> > - r1 = sqlite3GetTempReg(pParse);
> > - sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
> > + r1 = sqlite3GetTempReg(parse);
> > + sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
> > VdbeCoverage(v);
> > - sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
> > - sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
> > - sqlite3ReleaseTempReg(pParse, r1);
> > + sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
> > + sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
> > + sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT);
> > + sqlite3ReleaseTempReg(parse, r1);
> > }
>
> I slightly refactored this function (you forgot to format arguments, update comment etc).
Thanks, patch applied.
> >
> > /*
> > @@ -998,7 +1013,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
> > * row is all NULLs.
> > */
> > sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> > - pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
> > + sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
> > + pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct + 1);
>
> Actually, I don’t understand why you need manipulations with addrTnct + 1.
> If they are really vital, then leave explanation comment pls.
> (I guess it is required since two opcodes (OpenEphemeral + OpenCursor)
> turn into one (OpenCursor), but I didn’t dive into code).
I've extended a comment. Hope that'll help.
> > pOp->opcode = OP_Null;
> > pOp->p1 = 1;
> > pOp->p2 = regPrev;
> > @@ -1040,13 +1056,14 @@ selectInnerLoop(Parse * pParse, /* The parser context */
> >
> > case WHERE_DISTINCT_UNIQUE:{
> > sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> > + sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
>
> The same is here. Also, out of 80.
Added a comment, re-indented.
> > @@ -3417,8 +3459,8 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */
> > regAddrB = ++pParse->nMem;
> > regOutA = ++pParse->nMem;
> > regOutB = ++pParse->nMem;
> > - sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
> > - sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
> > + sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA, 0);
> > + sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB, 0);
>
> Lets initialise unused ephemeral register with -1 here and in other
> places as well.
Done.
> > @@ -5261,7 +5303,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
> > /* Verify that all AggInfo registers are within the range specified by
> > * AggInfo.mnReg..AggInfo.mxReg
> > */
> > - assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
> > + assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
>
> Hm? Explain pls this change.
I'd avoid explaining it in the code. So, will do it here.
Issue is that aggregate function may or may not contain distinct clause.
If so - one extra reg per such func will be used. Right hand size of the
assert doesn't take that into account and I see no reason to do that.
That's why, new assertion is that number of allocated registers for
aggregate is greater or even than sum of number of aggregate columns and
functions.
> > @@ -5886,6 +5940,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> > */
> > if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
> > sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
> > + sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
>
> Explain pls why do you need this.
That is pretty much the same as w/ DISTINCT. Comment added.
> > @@ -6130,6 +6185,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> > ) {
> > sSort.pOrderBy = 0;
> > sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
> > + sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
>
> The same.
Ditto.
> > diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> > index 53188e7..51110aa 100644
> > --- a/src/box/sql/sqliteInt.h
> > +++ b/src/box/sql/sqliteInt.h
> > @@ -2000,6 +2000,8 @@ struct AggInfo {
> > FuncDef *pFunc; /* The aggregate function implementation */
> > int iMem; /* Memory location that acts as accumulator */
> > int iDistinct; /* Ephemeral table used to enforce DISTINCT */
> > + /* Register, holding ephemeral's spacepointer. */
> > + int reg_eph;
> > } *aFunc;
> > int nFunc; /* Number of entries in aFunc[] */
> > };
> > @@ -2327,6 +2329,8 @@ struct SrcList {
> > } fg;
> > u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
> > int iCursor; /* The VDBE cursor number used to access this table */
> > + /* Register, containing pointer to the space. */
> > + int reg;
>
> Do you really need there this reg? AFAIS it used only in select.c:5741:11 and
> select.c:5760:35. Could you suggest workaround to avoid adding member to
> SrcList? I am asking cause SrcList is widely used through source code and
> extra member is unlikely to be suitable.
Good catch. Adding new field to overloaded struct Expr is definetely BS.
New register used locally. So, I've remvoed it.
> > diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
> > index c6bd15b..271f40a 100644
> > --- a/src/box/sql/trigger.c
> > +++ b/src/box/sql/trigger.c
> > @@ -694,7 +688,7 @@ codeTriggerProgram(Parse * pParse, /* The parser context */
> > SelectDest sDest;
> > Select *pSelect =
> > sqlite3SelectDup(db, pStep->pSelect, 0);
> > - sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
> > + sqlite3SelectDestInit(&sDest, SRT_Discard, 0, 0);
>
> Lets initialise unused ephemeral register with -1.
Done.
> > diff --git a/src/box/sql/update.c b/src/box/sql/update.c
> > index 730872f..71bd71d 100644
> > --- a/src/box/sql/update.c
> > +++ b/src/box/sql/update.c
> > @@ -222,13 +222,16 @@ sqlite3Update(Parse * pParse, /* The parser context */
> > * an ephemeral table.
> > */
> > uint32_t pk_part_count;
> > + struct space *space;
> > if (is_view) {
> > - sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
> > + int reg_eph = ++pParse->nMem;
> > + sql_materialize_view(pParse, def->name, pWhere, pk_cursor, reg_eph);
> > /* Number of columns from SELECT plus ID.*/
> > pk_part_count = nKey = def->field_count + 1;
> > } else {
> > - vdbe_emit_open_cursor(pParse, pk_cursor, 0,
> > - space_by_id(pTab->def->id));
> > + space = space_by_id(pTab->def->id);
>
> You don’t need to fetch space from cache: now real space is
> stored in pTab->space (see sql_lookup_table()).
Good. Fixed.
> > @@ -276,9 +280,9 @@ sqlite3Update(Parse * pParse, /* The parser context */
> > pPk->def);
> > sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
> > regKey, zAff, pk_part_count);
> > - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
> > + sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
> > /* Set flag to save memory allocating one by malloc. */
> > - sqlite3VdbeChangeP5(v, 1);
> > + sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE | OPFLAG_EPH_INSERT);
>
> These flags are unlikely to be compatible: NCHANGE refers to changes
> occurred to db. Insertion to ephemeral space can’t change anything in db.
> This snippet should look like (yep it initially contains bug: ChangeP5(v, 1)
> should refer to OP_MakeRecord):
Removed NCHANGE, testing pass :) So, OK.
> > diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> > index 00ffb0c..7df491b 100644
> > --- a/src/box/sql/vdbe.c
> > +++ b/src/box/sql/vdbe.c
> > @@ -3107,66 +3093,60 @@ case OP_OpenWrite:
> > "need to re-compile SQL statement");
> > goto abort_due_to_error;
> > }
> > - p2 = pOp->p2;
> > - struct space *space = pOp->p4.space;
> > + struct space *space;
> > + if (pOp->p4type == P4_SPACEPTR)
> > + space = pOp->p4.space;
> > + else
> > + space = aMem[pOp->p3].u.p;
> > assert(space != NULL);
> > - struct index *index = space_index(space, p2);
> > + struct index *index = space_index(space, pOp->p2);
> > assert(index != NULL);
> > - /*
> > - * Since Tarantool iterator provides the full tuple,
> > - * we need a number of fields as wide as the table itself.
> > - * Otherwise, not enough slots for row parser cache are
> > - * allocated in VdbeCursor object.
> > - */
> > - nField = space->def->field_count;
> > - assert(pOp->p1>=0);
> > - assert(nField>=0);
> > - pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_TARANTOOL);
> > - if (pCur==0) goto no_mem;
> > - pCur->nullRow = 1;
> > - pBtCur = pCur->uc.pCursor;
> > - pBtCur->curFlags |= BTCF_TaCursor;
> > - pBtCur->space = space;
> > - pBtCur->index = index;
> > - pBtCur->eState = CURSOR_INVALID;
> > + assert(pOp->p1 >= 0);
>
> Trailing tab.
Fixed.
> > + cur = allocateCursor(p, pOp->p1,
> > + space->def->exact_field_count == 0 ?
> > + space->def->field_count :
> > + space->def->exact_field_count,
> > + CURTYPE_TARANTOOL);
> > + if (cur == NULL)
> > + goto no_mem;
> > + struct BtCursor *bt_cur = cur->uc.pCursor;
> > + bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
> > + BTCF_TaCursor;
> > + bt_cur->space = space;
> > + bt_cur->index = index;
> > + bt_cur->eState = CURSOR_INVALID;
>
> Grep CURSOR_INVALID - it is not used anywhere else.
> Mb it is worth to remove it?
eState flag is heavily used by cursors machinery. I don't
think we can evict it right now.
> > +/* Opcode: IdxInsert2 P1 * * P4 P5
>
> P2 and P3 are used, so remove stars for clarity's sake.
Fixed for P2, P3 still unused.
> > +case OP_IdxInsert: {
> > + pIn2 = &aMem[pOp->p1];
> > + assert((pIn2->flags & MEM_Blob) != 0);
> > + if (pOp->p5 & OPFLAG_NCHANGE)
> > + p->nChange++;
>
> Broken indentation.
Fixed.
> > rc = ExpandBlob(pIn2);
> > - if (rc) goto abort_due_to_error;
> > - if (pOp->opcode==OP_SorterInsert) {
> > - rc = sqlite3VdbeSorterWrite(pC, pIn2);
> > + if (rc != 0)
> > + goto abort_due_to_error;
>
> Broken indentation.
Fixed.
> > + struct space *space;
> > + if (pOp->p4type == P4_SPACEPTR) {
> > + space = pOp->p4.space;
> > } else {
> > - BtCursor *pBtCur = pC->uc.pCursor;
> > - if (pBtCur->curFlags & BTCF_TaCursor) {
> > - /* Make sure that memory has been allocated on region. */
> > - assert(aMem[pOp->p2].flags & MEM_Ephem);
> > - if (pOp->opcode == OP_IdxInsert)
> > - rc = tarantoolSqlite3Insert(pBtCur->space,
> > - pIn2->z,
> > - pIn2->z + pIn2->n);
> > - else
> > - rc = tarantoolSqlite3Replace(pBtCur->space,
> > - pIn2->z,
> > - pIn2->z + pIn2->n);
> > - } else if (pBtCur->curFlags & BTCF_TEphemCursor) {
> > - rc = tarantoolSqlite3EphemeralInsert(pBtCur->space,
> > - pIn2->z,
> > - pIn2->z + pIn2->n);
> > + space = aMem[pOp->p2].u.p;
> > + }
> > + assert(space != NULL);
> > + if ((pOp->p5 & OPFLAG_EPH_INSERT) == 0) {
>
> Why do you need this flag at all? You have space here and you can
> simply check its id to point out whether space is ephemeral or not.
> Throwing out this flag would allow you drop typical
> sqlite3VdbeChangeP5(v, OPFLAG_EPH_INSERT); usage which looks
> quite annoying.
Removed.
> > + /* Make sure that memory has been allocated on region. */
> > + assert(aMem[pOp->p1].flags & MEM_Ephem);
> > + if (pOp->opcode == OP_IdxInsert) {
> > + rc = tarantoolSqlite3Insert(space,
> > + pIn2->z,
> > + pIn2->z + pIn2->n);
> > } else {
> > - unreachable();
> > + rc = tarantoolSqlite3Replace(space,
> > + pIn2->z,
> > + pIn2->z + pIn2->n);
> > }
> > - pC->cacheStatus = CACHE_STALE;
> > + } else {
> > + rc = tarantoolSqlite3EphemeralInsert(space,
> > + pIn2->z,
> > + pIn2->z + pIn2->n);
> > }
> >
> > if (pOp->p5 & OPFLAG_OE_IGNORE) {
> > /* Ignore any kind of failes and do not raise error message */
> > rc = SQLITE_OK;
> > /* If we are in trigger, increment ignore raised counter */
> > - if (p->pFrame) {
> > + if (p->pFrame)
> > p->ignoreRaised++;
> > - }
> > } else if (pOp->p5 & OPFLAG_OE_FAIL) {
> > p->errorAction = ON_CONFLICT_ACTION_FAIL;
> > } else if (pOp->p5 & OPFLAG_OE_ROLLBACK) {
> > p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
> > }
> > - if (rc) goto abort_due_to_error;
> > + if (rc != 0)
> > + goto abort_due_to_error;
> > break;
> > }
>
> I suggest cosmetic refactoring
Fixed mostly, few parts applied.
> > @@ -4636,12 +4633,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
> > }
> > }
> > assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
> > - op = OP_OpenWrite;
> > + op = OP_CursorOpen;
>
> It is redundant assignment: you initialise this variable with OP_CursorOpen value.
Fixed.
> > diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
> > index e2aaca3..14278f0 100644
> > --- a/src/box/sql/wherecode.c
> > +++ b/src/box/sql/wherecode.c
> > @@ -1341,7 +1341,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
> > int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
> >
> > int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
> > - int regRowset = 0; /* Register for RowSet object */
> > + int regRowset = 0; /* Cursor for RowSet object */
>
> regRowset now is really cursor, so lets better call like ‘cursor_row_set’.
> Otherwise, it seems to be very confusing to operate on two
> similar variables: regRowset and reg_row_set...
Done.
--
Regards, Kirill Yukhin
commit 5bd0cf10ac94cfbaf5d076a3f5a347811fcf4c11
Author: Kirill Yukhin <kyukhin@tarantool.org>
Date: Tue Sep 25 19:01:10 2018 +0300
sql: refactor SQL cursor to remove write ones
In Tarantool opening and positioning cursor for writing
have no sense. So refactor SQL code, to perform:
- Creation of ephemeral table w/o any cursors machinery.
No op-code returns register which contains plain pointer
to the ephemeral space.
- OpenRead/OpenWrite opcodes were replaced with single
CursorOpen op-code, which establishes new cursor w/
intention to subsequent read from the space. This opcode
accepts both plain pointer (in P4 operand) and register
which contains pointer (inn P3) to the ephemeral space.
- Fix query scheduler and DML routines thoroughly.
Closes #3182
Part of #2362
---
src/box/sql.c | 67 ++++--------
src/box/sql/analyze.c | 64 ++++-------
src/box/sql/build.c | 4 +-
src/box/sql/delete.c | 23 ++--
src/box/sql/expr.c | 41 ++++---
src/box/sql/insert.c | 52 ++++-----
src/box/sql/parse.y | 6 +-
src/box/sql/select.c | 233 +++++++++++++++++++++++++---------------
src/box/sql/sqliteInt.h | 16 +--
src/box/sql/tarantoolInt.h | 32 +++++-
src/box/sql/trigger.c | 12 +--
src/box/sql/update.c | 19 ++--
src/box/sql/vdbe.c | 262 +++++++++++++++++++++------------------------
src/box/sql/where.c | 16 +--
src/box/sql/whereInt.h | 2 +-
src/box/sql/wherecode.c | 14 ++-
16 files changed, 439 insertions(+), 424 deletions(-)
diff --git a/src/box/sql.c b/src/box/sql.c
index ab4a587..f3836f2 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
return SQLITE_OK;
}
-/**
- * Create ephemeral space and set cursor to the first entry. Features of
- * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
- * can be changed, but now only memtx engine is supported), primary index
- * which covers all fields and no secondary indexes, given field number and
- * collation sequence. All fields are scalar and nullable.
- *
- * @param pCur Cursor which will point to the new ephemeral space.
- * @param field_count Number of fields in ephemeral space.
- * @param key_info Keys description for new ephemeral space.
- *
- * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
- */
-int
-tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
- struct sql_key_info *key_info)
+struct space *
+sql_ephemeral_space_create(uint32_t field_count,
+ struct sql_key_info *key_info)
{
- assert(pCur);
- assert(pCur->curFlags & BTCF_TEphemCursor);
-
struct key_def *def = NULL;
if (key_info != NULL) {
def = sql_key_info_to_key_def(key_info);
if (def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
struct key_part_def *ephemer_key_parts = region_alloc(&fiber()->gc,
@@ -387,7 +371,7 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
if (ephemer_key_parts == NULL) {
diag_set(OutOfMemory, sizeof(*ephemer_key_parts) * field_count,
"region", "key parts");
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
for (uint32_t i = 0; i < field_count; ++i) {
struct key_part_def *part = &ephemer_key_parts[i];
@@ -404,14 +388,14 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
struct key_def *ephemer_key_def = key_def_new(ephemer_key_parts,
field_count);
if (ephemer_key_def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
struct index_def *ephemer_index_def =
index_def_new(0, 0, "ephemer_idx", strlen("ephemer_idx"), TREE,
&index_opts_default, ephemer_key_def, NULL);
key_def_delete(ephemer_key_def);
if (ephemer_index_def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
struct rlist key_list;
rlist_create(&key_list);
@@ -425,24 +409,15 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
0 /* length of field_def */);
if (ephemer_space_def == NULL) {
index_def_delete(ephemer_index_def);
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
struct space *ephemer_new_space = space_new_ephemeral(ephemer_space_def,
&key_list);
index_def_delete(ephemer_index_def);
space_def_delete(ephemer_space_def);
- if (ephemer_new_space == NULL)
- return SQL_TARANTOOL_ERROR;
- if (key_alloc(pCur, field_count) != 0) {
- space_delete(ephemer_new_space);
- return SQL_TARANTOOL_ERROR;
- }
- pCur->space = ephemer_new_space;
- pCur->index = *ephemer_new_space->index;
- int unused;
- return tarantoolSqlite3First(pCur, &unused);
+ return ephemer_new_space;
}
int tarantoolSqlite3EphemeralInsert(struct space *space, const char *tuple,
@@ -1461,35 +1436,31 @@ sql_debug_info(struct info_handler *h)
info_end(h);
}
-/*
+/**
* Extract maximum integer value from ephemeral space.
* If index is empty - return 0 in max_id and success status.
*
- * @param pCur Cursor pointing to ephemeral space.
+ * @param space Pointer to ephemeral space.
* @param fieldno Number of field from fetching tuple.
* @param[out] max_id Fetched max value.
*
* @retval 0 on success, -1 otherwise.
*/
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor *pCur, uint32_t fieldno,
- uint64_t *max_id)
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
+ uint64_t *max_id)
{
- struct space *ephem_space = pCur->space;
- assert(ephem_space);
- struct index *primary_index = *ephem_space->index;
-
+ struct index *primary_index = *space->index;
struct tuple *tuple;
- if (index_max(primary_index, NULL, 0, &tuple) != 0) {
- return SQL_TARANTOOL_ERROR;
- }
+ if (index_max(primary_index, NULL, 0, &tuple) != 0)
+ return -1;
if (tuple == NULL) {
*max_id = 0;
- return SQLITE_OK;
+ return 0;
}
if (tuple_field_u64(tuple, fieldno, max_id) == -1)
- return SQL_TARANTOOL_ERROR;
+ return -1;
- return SQLITE_OK;
+ return 0;
}
int
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 01e2ad1..674d53d 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -122,13 +122,10 @@
* created.
*
* @param parse Parsing context.
- * @param stat_cursor Open the _sql_stat1 table on this cursor.
- * you should allocate |stat_names| cursors before call.
* @param table_name Delete records of this table if specified.
*/
static void
-vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
- const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
{
const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -144,14 +141,6 @@ vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
}
}
-
- /* Open the sql_stat tables for writing. */
- for (uint i = 0; i < lengthof(stat_names); ++i) {
- uint32_t id = stat_ids[i];
- vdbe_emit_open_cursor(parse, stat_cursor + i, 0,
- space_by_id(id));
- VdbeComment((v, stat_names[i]));
- }
}
/*
@@ -770,15 +759,16 @@ callStatGet(Vdbe * v, int regStat4, int iParam, int regOut)
*
* @param parse Current parsing context.
* @param space Space to be analyzed.
- * @param stat_cursor Cursor pointing to spaces containing
- * statistics: _sql_stat1 (stat_cursor) and
- * _sql_stat4 (stat_cursor + 1).
*/
static void
-vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
- int stat_cursor)
+vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
{
assert(space != NULL);
+ struct space *stat1 = space_by_id(BOX_SQL_STAT1_ID);
+ assert(stat1 != NULL);
+ struct space *stat4 = space_by_id(BOX_SQL_STAT4_ID);
+ assert(stat4 != NULL);
+
/* Register to hold Stat4Accum object. */
int stat4_reg = ++parse->nMem;
/* Index of changed index field. */
@@ -808,7 +798,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
struct Vdbe *v = sqlite3GetVdbe(parse);
assert(v != NULL);
const char *tab_name = space_name(space);
- sqlite3VdbeAddOp4(v, OP_OpenRead, tab_cursor, 0, 0, (void *) space,
+ sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) space,
P4_SPACEPTR);
sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
for (uint32_t j = 0; j < space->index_count; ++j) {
@@ -871,7 +861,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
int idx_cursor;
if (j != 0) {
idx_cursor = parse->nTab - 1;
- sqlite3VdbeAddOp4(v, OP_OpenRead, idx_cursor,
+ sqlite3VdbeAddOp4(v, OP_IteratorOpen, idx_cursor,
idx->def->iid, 0,
(void *) space, P4_SPACEPTR);
VdbeComment((v, "%s", idx->def->name));
@@ -1003,7 +993,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
assert("BBB"[0] == AFFINITY_TEXT);
sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
"BBB", 0);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, stat_cursor, tmp_reg);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
+ (char *)stat1, P4_SPACEPTR);
/* Add the entries to the stat4 table. */
int eq_reg = stat1_reg;
int lt_reg = stat1_reg + 1;
@@ -1036,7 +1027,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
sample_reg);
sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
- sqlite3VdbeAddOp2(v, OP_IdxReplace, stat_cursor + 1, tmp_reg);
+ sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
+ (char *)stat4, P4_SPACEPTR);
/* P1==1 for end-of-loop. */
sqlite3VdbeAddOp2(v, OP_Goto, 1, next_addr);
sqlite3VdbeJumpHere(v, is_null_addr);
@@ -1058,27 +1050,12 @@ loadAnalysis(Parse * pParse)
}
}
-/**
- * Wrapper to pass args to space_foreach callback.
- */
-struct analyze_data {
- struct Parse *parse_context;
- /**
- * Cursor numbers pointing to stat spaces:
- * stat_cursor is opened on _sql_stat1 and
- * stat_cursor + 1 - on _sql_stat4.
- */
- int stat_cursor;
-};
-
static int
sql_space_foreach_analyze(struct space *space, void *data)
{
if (space->def->opts.sql == NULL || space->def->opts.is_view)
return 0;
- struct analyze_data *anal_data = (struct analyze_data *) data;
- vdbe_emit_analyze_space(anal_data->parse_context, space,
- anal_data->stat_cursor);
+ vdbe_emit_analyze_space((struct Parse*)data, space);
return 0;
}
@@ -1090,11 +1067,8 @@ static void
sql_analyze_database(struct Parse *parser)
{
sql_set_multi_write(parser, false);
- int stat_cursor = parser->nTab;
- parser->nTab += 2;
- vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
- struct analyze_data anal_data = { parser, stat_cursor };
- space_foreach(sql_space_foreach_analyze, (void *) &anal_data);
+ vdbe_emit_stat_space_open(parser, NULL);
+ space_foreach(sql_space_foreach_analyze, (void *)parser);
loadAnalysis(parser);
}
@@ -1114,10 +1088,8 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space)
* There are two system spaces for statistics: _sql_stat1
* and _sql_stat4.
*/
- int stat_cursor = parse->nTab;
- parse->nTab += 2;
- vdbe_emit_stat_space_open(parse, stat_cursor, space->def->name);
- vdbe_emit_analyze_space(parse, space, stat_cursor);
+ vdbe_emit_stat_space_open(parse, space->def->name);
+ vdbe_emit_analyze_space(parse, space);
loadAnalysis(parse);
}
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 43be777..7670d53 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -883,7 +883,7 @@ vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id,
struct space *space)
{
assert(space != NULL);
- return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_OpenWrite, cursor,
+ return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_IteratorOpen, cursor,
index_id, 0, (void *) space, P4_SPACEPTR);
}
@@ -2693,7 +2693,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
goto exit_create_index;
sql_set_multi_write(parse, true);
- sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, 0, 0,
+ sqlite3VdbeAddOp4(vdbe, OP_IteratorOpen, cursor, 0, 0,
(void *)space_by_id(BOX_INDEX_ID),
P4_SPACEPTR);
sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 9aa7058..17e1c32 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
void
sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
- int cursor)
+ int cursor, int reg_eph)
{
struct sqlite3 *db = parse->db;
where = sqlite3ExprDup(db, where, 0);
@@ -83,7 +83,7 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
struct Select *select = sqlite3SelectNew(parse, NULL, from, where, NULL,
NULL, NULL, 0, NULL, NULL);
struct SelectDest dest;
- sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor);
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor, reg_eph);
sqlite3Select(parse, select, &dest);
sql_select_delete(db, select);
}
@@ -196,7 +196,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
*/
if (is_view) {
sql_materialize_view(parse, space->def->name, where,
- tab_cursor);
+ tab_cursor, ++parse->nMem);
}
/* Initialize the counter of the number of rows deleted,
@@ -241,6 +241,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
* it, so columns should be loaded manually.
*/
struct sql_key_info *pk_info = NULL;
+ int reg_eph = ++parse->nMem;
int reg_pk = parse->nMem + 1;
int pk_len;
int eph_cursor = parse->nTab++;
@@ -248,8 +249,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
if (is_view) {
pk_len = table->def->field_count;
parse->nMem += pk_len;
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- eph_cursor, pk_len);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+ pk_len);
} else {
assert(space->index_count > 0);
pk_info = sql_key_info_new_from_key_def(db,
@@ -258,9 +259,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
goto delete_from_cleanup;
pk_len = pk_info->part_count;
parse->nMem += pk_len;
- sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, eph_cursor,
- pk_len, 0,
- (char *)pk_info, P4_KEYINFO);
+ sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
+ pk_len, 0,
+ (char *)pk_info, P4_KEYINFO);
}
/* Construct a query to find the primary key for
@@ -345,7 +346,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
* by malloc.
*/
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, eph_cursor, reg_key);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, reg_key, reg_eph);
}
/* If this DELETE cannot use the ONEPASS strategy,
@@ -369,7 +370,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once);
VdbeCoverage(v);
}
- sqlite3VdbeAddOp4(v, OP_OpenWrite, tab_cursor, 0, 0,
+ sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0,
(void *) space, P4_SPACEPTR);
VdbeComment((v, "%s", space->index[0]->def->name));
@@ -390,6 +391,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
VdbeCoverage(v);
} else {
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen,
+ eph_cursor, 0, reg_eph);
addr_loop = sqlite3VdbeAddOp1(v, OP_Rewind, eph_cursor);
VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowData, eph_cursor, reg_key);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index a13de4f..c5d4fb2 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2714,8 +2714,11 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
*/
pExpr->iTable = pParse->nTab++;
pExpr->is_ephemeral = 1;
+ int reg_eph = ++pParse->nMem;
addr = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- pExpr->iTable, nVal);
+ reg_eph, nVal);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, pExpr->iTable, 0,
+ reg_eph);
struct sql_key_info *key_info = sql_key_info_new(pParse->db, nVal);
if (key_info == NULL)
return 0;
@@ -2736,7 +2739,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
SelectDest dest;
int i;
sqlite3SelectDestInit(&dest, SRT_Set,
- pExpr->iTable);
+ pExpr->iTable, reg_eph);
dest.zAffSdst =
exprINAffinity(pParse, pExpr);
assert((pExpr->iTable & 0x0000FFFF) ==
@@ -2808,8 +2811,8 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
1, r2, &affinity, 1);
sqlite3ExprCacheAffinityChange(pParse,
r3, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert,
- pExpr->iTable, r2);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r2,
+ reg_eph);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -2847,7 +2850,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
pSel = pExpr->x.pSelect;
nReg = pExpr->op == TK_SELECT ? pSel->pEList->nExpr : 1;
- sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1);
+ sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1, -1);
pParse->nMem += nReg;
if (pExpr->op == TK_SELECT) {
dest.eDest = SRT_Mem;
@@ -4061,7 +4064,8 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
int n;
if (pExpr->pLeft->iTable == 0) {
pExpr->pLeft->iTable =
- sqlite3CodeSubselect(pParse, pExpr->pLeft, 0);
+ sqlite3CodeSubselect(pParse, pExpr->pLeft,
+ 0);
}
assert(pExpr->iTable == 0
|| pExpr->pLeft->op == TK_SELECT);
@@ -5361,24 +5365,17 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr)
pItem->iMem = ++pParse->nMem;
assert(!ExprHasProperty
(pExpr, EP_IntValue));
- pItem->pFunc =
- sqlite3FindFunction(pParse->
- db,
- pExpr->
- u.
- zToken,
- pExpr->
- x.
- pList ?
- pExpr->
- x.
- pList->
- nExpr :
- 0,
- 0);
+ pItem->pFunc = sqlite3FindFunction(
+ pParse->db,
+ pExpr->u.zToken,
+ pExpr->x.pList ?
+ pExpr->x.pList->nExpr : 0,
+ 0);
if (pExpr->flags & EP_Distinct) {
pItem->iDistinct =
- pParse->nTab++;
+ pParse->nTab++;
+ pItem->reg_eph =
+ ++pParse->nMem;
} else {
pItem->iDistinct = -1;
}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 03f4e1b..287c6c9 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -40,23 +40,6 @@
#include "bit/bit.h"
#include "box/box.h"
-/*
- * Generate code that will open pTab as cursor iCur.
- */
-void
-sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */
- int iCur, /* The cursor number of the table */
- struct space *space, /* The table to be opened */
- int opcode) /* OP_OpenRead or OP_OpenWrite */
-{
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- assert(opcode == OP_OpenWrite || opcode == OP_OpenRead);
- assert(space->index_count > 0);
- vdbe_emit_open_cursor(pParse, iCur, 0, space);
- VdbeComment((v, "%s", space->def->name));
-}
-
char *
sql_space_index_affinity_str(struct sqlite3 *db, struct space_def *space_def,
struct index_def *idx_def)
@@ -139,9 +122,12 @@ vdbe_has_table_read(struct Parse *parser, const struct Table *table)
* Currently, there is no difference between Read
* and Write cursors.
*/
- if (op->opcode == OP_OpenRead || op->opcode == OP_OpenWrite) {
- assert(op->p4type == P4_SPACEPTR);
- struct space *space = op->p4.space;
+ if (op->opcode == OP_IteratorOpen) {
+ struct space *space = NULL;
+ if (op->p4type == P4_SPACEPTR)
+ space = op->p4.space;
+ else
+ continue;
if (space->def->id == table->def->id)
return true;
}
@@ -418,6 +404,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
}
}
+ int reg_eph;
/* Figure out how many columns of data are supplied. If the data
* is coming from a SELECT statement, then generate a co-routine that
* produces a single row of the SELECT on each invocation. The
@@ -434,7 +421,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
regYield = ++pParse->nMem;
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield, -1);
dest.iSdst = bIdListInOrder ? regData : 0;
dest.nSdst = def->field_count;
rc = sqlite3Select(pParse, pSelect, &dest);
@@ -480,10 +467,13 @@ sqlite3Insert(Parse * pParse, /* Parser context */
int64_t initial_pk = 0;
srcTab = pParse->nTab++;
+ reg_eph = ++pParse->nMem;
regRec = sqlite3GetTempReg(pParse);
regCopy = sqlite3GetTempRange(pParse, nColumn);
regTempId = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, srcTab, nColumn+1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+ nColumn + 1);
+
/* Create counter for rowid */
sqlite3VdbeAddOp4Dup8(v, OP_Int64,
0 /* unused */,
@@ -499,7 +489,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
nColumn + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, srcTab, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, reg_eph);
sqlite3VdbeGoto(v, addrL);
sqlite3VdbeJumpHere(v, addrL);
@@ -515,6 +505,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
srcTab = -1;
+ reg_eph = -1;
assert(useTempTable == 0);
if (pList) {
nColumn = pList->nExpr;
@@ -555,6 +546,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
* end loop
* D: ...
*/
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, srcTab, 0, reg_eph);
addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
VdbeCoverage(v);
addrCont = sqlite3VdbeCurrentAddr(v);
@@ -746,9 +738,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
vdbe_emit_constraint_checks(pParse, pTab, regIns + 1,
on_error, endOfLoop, 0);
fkey_emit_check(pParse, pTab, 0, regIns, 0);
- int pk_cursor = pParse->nTab++;
- vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
- vdbe_emit_insertion_completion(v, pk_cursor, regIns + 1,
+ vdbe_emit_insertion_completion(v, space, regIns + 1,
pTab->def->field_count,
on_error);
}
@@ -1063,8 +1053,8 @@ process_index: ;
}
void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
- uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+ int raw_data_reg, uint32_t tuple_len,
enum on_conflict_action on_conflict)
{
assert(v != NULL);
@@ -1077,7 +1067,8 @@ vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
pik_flags |= OPFLAG_OE_ROLLBACK;
sqlite3VdbeAddOp3(v, OP_MakeRecord, raw_data_reg, tuple_len,
raw_data_reg + tuple_len);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor_id, raw_data_reg + tuple_len);
+ sqlite3VdbeAddOp1(v, OP_IdxInsert, raw_data_reg + tuple_len);
+ sqlite3VdbeChangeP4(v, -1, (char *)space, P4_SPACEPTR);
sqlite3VdbeChangeP5(v, pik_flags);
}
@@ -1327,7 +1318,8 @@ xferOptimization(Parse * pParse, /* Parser context */
sqlite3VdbeChangeP5(v, OPFLAG_XFER_OPT);
#endif
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, regData, 0, 0,
+ (char *)dest, P4_SPACEPTR);
switch (onError) {
case ON_CONFLICT_ACTION_IGNORE:
sqlite3VdbeChangeP5(v, OPFLAG_OE_IGNORE | OPFLAG_NCHANGE);
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 473cf89..92e3a93 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -390,11 +390,11 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
//////////////////////// The SELECT statement /////////////////////////////////
//
cmd ::= select(X). {
- SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
if(!pParse->parse_only)
- sqlite3Select(pParse, X, &dest);
+ sqlite3Select(pParse, X, &dest);
else
- sql_expr_extract_select(pParse, X);
+ sql_expr_extract_select(pParse, X);
sql_select_delete(pParse->db, X);
}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 02c0a5d..2776044 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -64,8 +64,15 @@ typedef struct DistinctCtx DistinctCtx;
struct DistinctCtx {
u8 isTnct; /* True if the DISTINCT keyword is present */
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
- int tabTnct; /* Ephemeral table used for DISTINCT processing */
- int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
+ /* Ephemeral table's cursor used for DISTINCT processing. It is
+ * used for readin from ephemeral space.
+ */
+ int cur_eph;
+ /* Register, containing a pointer to ephemeral space. It
+ * is used for insertions while procesing DISTINCT.
+ */
+ int reg_eph;
+ int addrTnct; /* Address of OP_OpenEphemeral opcode for cur_eph */
};
/*
@@ -77,6 +84,8 @@ struct SortCtx {
ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
int nOBSat; /* Number of ORDER BY terms satisfied by indices */
int iECursor; /* Cursor number for the sorter */
+ /* Register, containing pointer to ephemeral space. */
+ int reg_eph;
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
@@ -117,10 +126,11 @@ clearSelect(sqlite3 * db, Select * p, int bFree)
* Initialize a SelectDest structure.
*/
void
-sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm)
+sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm, int reg_eph)
{
pDest->eDest = (u8) eDest;
pDest->iSDParm = iParm;
+ pDest->reg_eph = reg_eph;
pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
@@ -687,7 +697,6 @@ pushOntoSorter(Parse * pParse, /* Parser context */
int regBase; /* Regs for sorter record */
int regRecord = ++pParse->nMem; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
- int op; /* Opcode to add sorter record to sorter */
int iLimit; /* LIMIT counter */
assert(bSeq == 0 || bSeq == 1);
@@ -764,11 +773,13 @@ pushOntoSorter(Parse * pParse, /* Parser context */
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
sqlite3VdbeJumpHere(v, addrJmp);
}
- if (pSort->sortFlags & SORTFLAG_UseSorter)
- op = OP_SorterInsert;
- else
- op = OP_IdxInsert;
- sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
+ if (pSort->sortFlags & SORTFLAG_UseSorter) {
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, pSort->iECursor,
+ regRecord);
+ } else {
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRecord, pSort->reg_eph);
+ }
+
if (iLimit) {
int addr;
int r1 = 0;
@@ -823,32 +834,33 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
}
}
-/*
- * Add code that will check to make sure the N registers starting at iMem
- * form a distinct entry. iTab is a sorting index that holds previously
- * seen combinations of the N values. A new entry is made in iTab
- * if the current N values are new.
- *
- * A jump to addrRepeat is made and the N+1 values are popped from the
- * stack if the top N elements are not distinct.
+/**
+ * Add code that will check to make sure the @n registers starting
+ * at @reg_data form a distinct entry. @cursor is a sorting index
+ * that holds previously seen combinations of the @n values.
+ * A new entry is made in @cursor if the current n values are new.
+ *
+ * A jump to @addr_repeat is made and the @n+1 values are popped
+ * from the stack if the top n elements are not distinct.
+ *
+ * @param parse Parsing and code generating context.
+ * @param cursor A sorting index cursor used to test for
+ * distinctness.
+ * @param reg_eph Register holding ephemeral space's pointer.
+ * @param addr_repeat Jump here if not distinct.
+ * @param n Number of elements in record.
+ * @param reg_data First register holding the data.
*/
static void
-codeDistinct(Parse * pParse, /* Parsing and code generating context */
- int iTab, /* A sorting index used to test for distinctness */
- int addrRepeat, /* Jump to here if not distinct */
- int N, /* Number of elements */
- int iMem) /* First element */
+vdbe_insert_distinct(struct Parse *parse, int cursor, int reg_eph,
+ int addr_repeat, int n, int reg_data)
{
- Vdbe *v;
- int r1;
-
- v = pParse->pVdbe;
- r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ struct Vdbe *v = parse->pVdbe;
+ int r1 = sqlite3GetTempReg(parse);
+ sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
+ sqlite3ReleaseTempReg(parse, r1);
}
/*
@@ -991,14 +1003,22 @@ selectInnerLoop(Parse * pParse, /* The parser context */
regPrev = pParse->nMem + 1;
pParse->nMem += nResultCol;
- /* Change the OP_OpenEphemeral coded earlier to an OP_Null
- * sets the MEM_Cleared bit on the first register of the
- * previous value. This will cause the OP_Ne below to always
- * fail on the first iteration of the loop even if the first
- * row is all NULLs.
+ /* Actually, for DISTINCT handling
+ * two op-codes were emitted here:
+ * OpenTEphemeral & IteratorOpen.
+ * So, we need to Noop one and
+ * re-use second for Null op-code.
+ *
+ * Change to an OP_Null sets the
+ * MEM_Cleared bit on the first
+ * register of the previous value.
+ * This will cause the OP_Ne below
+ * to always fail on the first
+ * iteration of the loop even if
+ * the first row is all NULLs.
*/
sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
+ pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct + 1);
pOp->opcode = OP_Null;
pOp->p1 = 1;
pOp->p2 = regPrev;
@@ -1039,17 +1059,25 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
case WHERE_DISTINCT_UNIQUE:{
- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- break;
+ /* To handle DISTINCT two op-codes are
+ * emitted: OpenTEphemeral & IteratorOpen.
+ * addrTnct is address off first insn in
+ * a couple. Two evict ephemral space,
+ * need to noop both op-codes.
+ */
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
+ break;
}
default:{
- assert(pDistinct->eTnctType ==
- WHERE_DISTINCT_UNORDERED);
- codeDistinct(pParse, pDistinct->tabTnct,
- iContinue, nResultCol, regResult);
- break;
- }
+ assert(pDistinct->eTnctType ==
+ WHERE_DISTINCT_UNORDERED);
+ vdbe_insert_distinct(pParse, pDistinct->cur_eph,
+ pDistinct->reg_eph, iContinue,
+ nResultCol, regResult);
+ break;
+ }
}
if (pSort == 0) {
codeOffset(v, p->iOffset, iContinue);
@@ -1066,7 +1094,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
nResultCol, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, r1);
break;
}
@@ -1109,8 +1137,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp4Int(v, OP_Found, iParm + 1,
addr, r1, 0);
VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
- r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1,
+ pDest->reg_eph + 1);
assert(pSort == 0);
}
#endif
@@ -1122,7 +1150,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
int regRec = sqlite3GetTempReg(pParse);
/* Last column is required for ID. */
int regCopy = sqlite3GetTempRange(pParse, nResultCol + 1);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm,
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
nResultCol, regCopy + nResultCol);
/* Positioning ID column to be last in inserted tuple.
* NextId -> regCopy + n + 1
@@ -1134,7 +1162,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nResultCol + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempRange(pParse, regCopy, nResultCol + 1);
}
@@ -1164,7 +1192,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3ExprCacheAffinityChange(pParse,
regResult,
nResultCol);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, r1);
}
break;
@@ -1247,8 +1275,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
nResultCol, r3);
if (eDest == SRT_DistQueue) {
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
- r3);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r3,
+ pDest->reg_eph + 1);
}
for (i = 0; i < nKey; i++) {
sqlite3VdbeAddOp2(v, OP_SCopy,
@@ -1258,7 +1286,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2 + nKey);
sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey + 2, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
if (addrTest)
sqlite3VdbeJumpHere(v, addrTest);
sqlite3ReleaseTempReg(pParse, r1);
@@ -1498,7 +1526,6 @@ generateSortTail(Parse * pParse, /* Parsing context */
int iTab;
ExprList *pOrderBy = pSort->pOrderBy;
int eDest = pDest->eDest;
- int iParm = pDest->iSDParm;
int regRow;
int regTupleid;
int iCol;
@@ -1570,10 +1597,11 @@ generateSortTail(Parse * pParse, /* Parsing context */
case SRT_Table:
case SRT_EphemTab: {
int regCopy = sqlite3GetTempRange(pParse, nColumn);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm, nColumn, regTupleid);
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
+ nColumn, regTupleid);
sqlite3VdbeAddOp3(v, OP_Copy, regRow, regCopy, nSortData - 1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nColumn + 1, regRow);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRow);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRow, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, regCopy);
break;
}
@@ -1583,7 +1611,7 @@ generateSortTail(Parse * pParse, /* Parsing context */
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn,
regTupleid, pDest->zAffSdst, nColumn);
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regTupleid);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regTupleid, pDest->reg_eph);
break;
}
case SRT_Mem:{
@@ -2409,13 +2437,16 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
* for the SRT_DistFifo and SRT_DistQueue destinations to work.
*/
iQueue = pParse->nTab++;
+ int reg_queue = ++pParse->nMem;
+ int reg_dist = 0;
if (p->op == TK_UNION) {
eDest = pOrderBy ? SRT_DistQueue : SRT_DistFifo;
iDistinct = pParse->nTab++;
+ reg_dist = ++pParse->nMem;
} else {
eDest = pOrderBy ? SRT_Queue : SRT_Fifo;
}
- sqlite3SelectDestInit(&destQueue, eDest, iQueue);
+ sqlite3SelectDestInit(&destQueue, eDest, iQueue, reg_queue);
/* Allocate cursors for Current, Queue, and Distinct. */
regCurrent = ++pParse->nMem;
@@ -2423,18 +2454,20 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
if (pOrderBy) {
struct sql_key_info *key_info =
sql_multiselect_orderby_to_key_info(pParse, p, 1);
- sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, iQueue,
+ sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_queue,
pOrderBy->nExpr + 2, 0, (char *)key_info,
P4_KEYINFO);
VdbeComment((v, "Orderby table"));
destQueue.pOrderBy = pOrderBy;
} else {
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iQueue, nCol + 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_queue, nCol + 1);
VdbeComment((v, "Queue table"));
}
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, iQueue, 0, reg_queue);
if (iDistinct) {
p->addrOpenEphm[0] =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iDistinct, 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_dist, 1);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, iDistinct, 0, reg_dist);
p->selFlags |= SF_UsesEphemeral;
VdbeComment((v, "Distinct table"));
}
@@ -2630,7 +2663,8 @@ multiSelect(Parse * pParse, /* Parsing context */
if (dest.eDest == SRT_EphemTab) {
assert(p->pEList);
int nCols = p->pEList->nExpr;
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.iSDParm, nCols + 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.reg_eph, nCols + 1);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, dest.iSDParm, 0, dest.reg_eph);
VdbeComment((v, "Destination temp"));
dest.eDest = SRT_Table;
}
@@ -2721,6 +2755,7 @@ multiSelect(Parse * pParse, /* Parsing context */
case TK_EXCEPT:
case TK_UNION:{
int unionTab; /* Cursor number of the temporary table holding result */
+ int reg_union;
u8 op = 0; /* One of the SRT_ operations to apply to self */
int priorOp; /* The SRT_ operation to apply to prior selects */
Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
@@ -2737,16 +2772,19 @@ multiSelect(Parse * pParse, /* Parsing context */
assert(p->pLimit == 0); /* Not allowed on leftward elements */
assert(p->pOffset == 0); /* Not allowed on leftward elements */
unionTab = dest.iSDParm;
+ reg_union = dest.reg_eph;
} else {
/* We will need to create our own temporary table to hold the
* intermediate results.
*/
unionTab = pParse->nTab++;
+ reg_union = ++pParse->nMem;
assert(p->pOrderBy == 0);
addr =
sqlite3VdbeAddOp2(v,
OP_OpenTEphemeral,
- unionTab, 0);
+ reg_union, 0);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, unionTab, 0, reg_union);
assert(p->addrOpenEphm[0] == -1);
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |=
@@ -2758,7 +2796,7 @@ multiSelect(Parse * pParse, /* Parsing context */
*/
assert(!pPrior->pOrderBy);
sqlite3SelectDestInit(&uniondest, priorOp,
- unionTab);
+ unionTab, reg_union);
iSub1 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, pPrior, &uniondest);
if (rc) {
@@ -2841,6 +2879,7 @@ multiSelect(Parse * pParse, /* Parsing context */
default:
assert(p->op == TK_INTERSECT); {
int tab1, tab2;
+ int reg_eph1, reg_eph2;
int iCont, iBreak, iStart;
Expr *pLimit, *pOffset;
int addr;
@@ -2852,12 +2891,15 @@ multiSelect(Parse * pParse, /* Parsing context */
* by allocating the tables we will need.
*/
tab1 = pParse->nTab++;
+ reg_eph1 = ++pParse->nMem;
tab2 = pParse->nTab++;
+ reg_eph2 = ++pParse->nMem;
assert(p->pOrderBy == 0);
addr =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab1,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph1,
0);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, tab1, 0, reg_eph1);
assert(p->addrOpenEphm[0] == -1);
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |= SF_UsesEphemeral;
@@ -2866,7 +2908,7 @@ multiSelect(Parse * pParse, /* Parsing context */
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union,
- tab1);
+ tab1, reg_eph1);
iSub1 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, pPrior,
&intersectdest);
@@ -2877,8 +2919,9 @@ multiSelect(Parse * pParse, /* Parsing context */
/* Code the current SELECT into temporary table "tab2"
*/
addr =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab2,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph2,
0);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, tab2, 0, reg_eph2);
assert(p->addrOpenEphm[1] == -1);
p->addrOpenEphm[1] = addr;
p->pPrior = 0;
@@ -2887,6 +2930,7 @@ multiSelect(Parse * pParse, /* Parsing context */
pOffset = p->pOffset;
p->pOffset = 0;
intersectdest.iSDParm = tab2;
+ intersectdest.reg_eph = reg_eph2;
iSub2 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, p, &intersectdest);
testcase(rc != SQLITE_OK);
@@ -3075,7 +3119,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
case SRT_EphemTab:{
int regRec = sqlite3GetTempReg(parse);
int regCopy = sqlite3GetTempRange(parse, in->nSdst + 1);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->iSDParm,
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->reg_eph,
in->nSdst, regCopy + in->nSdst);
sqlite3VdbeAddOp3(v, OP_Copy, in->iSdst, regCopy,
in->nSdst - 1);
@@ -3083,7 +3127,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
in->nSdst + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, dest->reg_eph);
sqlite3ReleaseTempRange(parse, regCopy, in->nSdst + 1);
sqlite3ReleaseTempReg(parse, regRec);
break;
@@ -3099,7 +3143,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
in->nSdst);
sqlite3ExprCacheAffinityChange(parse, in->iSdst,
in->nSdst);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, dest->reg_eph);
sqlite3ReleaseTempReg(parse, r1);
break;
}
@@ -3417,8 +3461,8 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */
regAddrB = ++pParse->nMem;
regOutA = ++pParse->nMem;
regOutB = ++pParse->nMem;
- sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
- sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
+ sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA, -1);
+ sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB, -1);
/* Generate a coroutine to evaluate the SELECT statement to the
* left of the compound operator - the "A" select.
@@ -5261,7 +5305,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
/* Verify that all AggInfo registers are within the range specified by
* AggInfo.mnReg..AggInfo.mxReg
*/
- assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
+ assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
for (i = 0; i < pAggInfo->nColumn; i++) {
assert(pAggInfo->aCol[i].iMem >= pAggInfo->mnReg
&& pAggInfo->aCol[i].iMem <= pAggInfo->mxReg);
@@ -5287,8 +5331,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
pE->x.pList,
0);
sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- pFunc->iDistinct, 1, 0,
+ pFunc->reg_eph, 1, 0,
(char *)key_info, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen,
+ pFunc->iDistinct, 0, pFunc->reg_eph);
}
}
}
@@ -5347,8 +5393,8 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
addrNext = sqlite3VdbeMakeLabel(v);
testcase(nArg == 0); /* Error condition */
testcase(nArg > 1); /* Also an error */
- codeDistinct(pParse, pF->iDistinct, addrNext, 1,
- regAgg);
+ vdbe_insert_distinct(pParse, pF->iDistinct, pF->reg_eph,
+ addrNext, 1, regAgg);
}
if (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) {
struct coll *coll = NULL;
@@ -5679,7 +5725,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
VdbeComment((v, "%s", pItem->pTab->def->name));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine,
- pItem->regReturn);
+ pItem->regReturn, -1);
pItem->iSelectId = pParse->iNextSelectId;
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5699,6 +5745,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
int retAddr;
assert(pItem->addrFillSub == 0);
pItem->regReturn = ++pParse->nMem;
+
topAddr =
sqlite3VdbeAddOp2(v, OP_Integer, 0,
pItem->regReturn);
@@ -5717,7 +5764,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
pItem->pTab->def->name));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab,
- pItem->iCursor);
+ pItem->iCursor, ++pParse->nMem);
pItem->iSelectId = pParse->iNextSelectId;
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5796,6 +5843,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
if (sSort.pOrderBy) {
struct sql_key_info *key_info =
sql_expr_list_to_key_info(pParse, sSort.pOrderBy, 0);
+ sSort.reg_eph = ++pParse->nMem;
sSort.iECursor = pParse->nTab++;
/* Number of columns in transient table equals to number of columns in
* SELECT statement plus number of columns in ORDER BY statement
@@ -5807,9 +5855,10 @@ sqlite3Select(Parse * pParse, /* The parser context */
}
sSort.addrSortIndex =
sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- sSort.iECursor,
+ sSort.reg_eph,
nCols,
0, (char *)key_info, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, sSort.iECursor, 0, sSort.reg_eph);
VdbeComment((v, "Sort table"));
} else {
sSort.addrSortIndex = -1;
@@ -5818,8 +5867,10 @@ sqlite3Select(Parse * pParse, /* The parser context */
/* If the output is destined for a temporary table, open that table.
*/
if (pDest->eDest == SRT_EphemTab) {
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->iSDParm,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->reg_eph,
pEList->nExpr + 1);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, pDest->iSDParm, 0,
+ pDest->reg_eph);
VdbeComment((v, "Output table"));
}
@@ -5833,20 +5884,25 @@ sqlite3Select(Parse * pParse, /* The parser context */
computeLimitRegisters(pParse, p, iEnd);
if (p->iLimit == 0 && sSort.addrSortIndex >= 0) {
sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
+ sqlite3VdbeChangeP1(v, sSort.addrSortIndex, sSort.iECursor);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
sSort.sortFlags |= SORTFLAG_UseSorter;
}
/* Open an ephemeral index to use for the distinct set.
*/
if (p->selFlags & SF_Distinct) {
- sDistinct.tabTnct = pParse->nTab++;
+ sDistinct.cur_eph = pParse->nTab++;
+ sDistinct.reg_eph = ++pParse->nMem;
struct sql_key_info *key_info =
sql_expr_list_to_key_info(pParse, p->pEList, 0);
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- sDistinct.tabTnct,
+ sDistinct.reg_eph,
key_info->part_count,
0, (char *)key_info,
P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, sDistinct.cur_eph, 0,
+ sDistinct.reg_eph);
VdbeComment((v, "Distinct table"));
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
} else {
@@ -5885,7 +5941,15 @@ sqlite3Select(Parse * pParse, /* The parser context */
* into an OP_Noop.
*/
if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
+ /* To handle ordering two op-codes are
+ * emitted: OpenTEphemeral & IteratorOpen.
+ * sSort.addrSortIndex is address off
+ * first insn in a couple. Two evict
+ * ephemral space, need to noop both
+ * op-codes.
+ */
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
}
/* Use the standard inner loop. */
@@ -6130,6 +6194,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
) {
sSort.pOrderBy = 0;
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
}
/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 53188e7..29636a0 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2000,6 +2000,8 @@ struct AggInfo {
FuncDef *pFunc; /* The aggregate function implementation */
int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
+ /* Register, holding ephemeral's spacepointer. */
+ int reg_eph;
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
};
@@ -2591,6 +2593,8 @@ struct SelectDest {
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
int iSDParm; /* A parameter used by the eDest disposal method */
+ /* Register containing ephemeral's space pointer. */
+ int reg_eph;
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
@@ -3508,7 +3512,6 @@ Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
struct Table *
sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name);
-void sqlite3OpenTable(Parse *, int iCur, struct space *, int);
/**
* Generate code for a DELETE FROM statement.
*
@@ -3828,14 +3831,14 @@ vdbe_emit_constraint_checks(struct Parse *parse_context,
* @cursor_id.
*
* @param v Virtual database engine.
- * @param cursor_id Primary index cursor.
+ * @param space Pointer to space object.
* @param raw_data_reg Register with raw data to insert.
* @param tuple_len Number of registers to hold the tuple.
* @param on_conflict On conflict action.
*/
void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
- uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+ int raw_data_reg, uint32_t tuple_len,
enum on_conflict_action on_conflict);
void
@@ -3870,10 +3873,11 @@ int sqlite3SafetyCheckSickOrOk(sqlite3 *);
* @param name View name.
* @param where Option WHERE clause to be added.
* @param cursor Cursor number for ephemeral table.
+ * @param reg_eph Register holding pointer to ephemeral table.
*/
void
sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
- int cursor);
+ int cursor, int reg_eph);
/**
* This is called by the parser when it sees a CREATE TRIGGER
@@ -4529,7 +4533,7 @@ void sqlite3StrAccumAppendAll(StrAccum *, const char *);
void sqlite3AppendChar(StrAccum *, int, char);
char *sqlite3StrAccumFinish(StrAccum *);
void sqlite3StrAccumReset(StrAccum *);
-void sqlite3SelectDestInit(SelectDest *, int, int);
+void sqlite3SelectDestInit(SelectDest *, int, int, int);
Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
int sqlite3ExprCheckIN(Parse *, Expr *);
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index 8017742..497d989 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -97,9 +97,22 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
const char *zOldName, const char *zNewName);
-/* Interface for ephemeral tables. */
-int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
- struct sql_key_info *key_info);
+/**
+ * Create ephemeral space. Features of ephemeral spaces: id == 0,
+ * name == "ephemeral", memtx engine (in future it can be changed,
+ * but now only memtx engine is supported), primary index which
+ * covers all fields and no secondary indexes, given field number
+ * and collation sequence. All fields are scalar and nullable.
+ *
+ * @param field_count Number of fields in ephemeral space.
+ * @param key_info Keys description for new ephemeral space.
+ *
+ * @retval Pointer to created space, NULL if error.
+ */
+struct space *
+sql_ephemeral_space_create(uint32_t filed_count,
+ struct sql_key_info *key_info);
+
/**
* Insert tuple into ephemeral space.
* In contrast to ordinary spaces, there is no need to create and
@@ -117,7 +130,18 @@ int tarantoolSqlite3EphemeralDelete(BtCursor * pCur);
int tarantoolSqlite3EphemeralCount(BtCursor * pCur, i64 * pnEntry);
int tarantoolSqlite3EphemeralDrop(BtCursor * pCur);
int tarantoolSqlite3EphemeralClearTable(BtCursor * pCur);
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor * pCur, uint32_t fieldno,
+
+/**
+ * Extract maximum integer value from ephemeral space.
+ * If index is empty - return 0 in max_id and success status.
+ *
+ * @param space Pointer to ephemeral space.
+ * @param fieldno Number of field from fetching tuple.
+ * @param[out] max_id Fetched max value.
+ *
+ * @retval 0 on success, -1 otherwise.
+ */
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
uint64_t * max_id);
/**
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index c6bd15b..c38f9cd 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -205,15 +205,9 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
if (db->mallocFailed)
goto cleanup;
- int cursor = parse->nTab++;
struct space *_trigger = space_by_id(BOX_TRIGGER_ID);
assert(_trigger != NULL);
- vdbe_emit_open_cursor(parse, cursor, 0, _trigger);
- /*
- * makerecord(cursor(iRecord),
- * [reg(first_col), reg(first_col+1)]).
- */
int first_col = parse->nMem + 1;
parse->nMem += 3;
int record = ++parse->nMem;
@@ -245,10 +239,10 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
sqlite3VdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
sqlite3VdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor, record);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, record, 0, 0,
+ (char *)_trigger, P4_SPACEPTR);
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
- sqlite3VdbeAddOp1(v, OP_Close, cursor);
sql_set_multi_write(parse, false);
} else {
@@ -694,7 +688,7 @@ codeTriggerProgram(Parse * pParse, /* The parser context */
SelectDest sDest;
Select *pSelect =
sqlite3SelectDup(db, pStep->pSelect, 0);
- sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
+ sqlite3SelectDestInit(&sDest, SRT_Discard, 0, -1);
sqlite3Select(pParse, pSelect, &sDest);
sql_select_delete(db, pSelect);
break;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 730872f..8673f68 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -222,13 +222,16 @@ sqlite3Update(Parse * pParse, /* The parser context */
* an ephemeral table.
*/
uint32_t pk_part_count;
+ struct space *space;
if (is_view) {
- sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
+ sql_materialize_view(pParse, def->name, pWhere, pk_cursor,
+ ++pParse->nMem);
/* Number of columns from SELECT plus ID.*/
pk_part_count = nKey = def->field_count + 1;
} else {
- vdbe_emit_open_cursor(pParse, pk_cursor, 0,
- space_by_id(pTab->def->id));
+ space = pTab->space;
+ assert(space != NULL);
+ vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
pk_part_count = pPk->def->key_def->part_count;
}
@@ -242,11 +245,12 @@ sqlite3Update(Parse * pParse, /* The parser context */
int iPk = pParse->nMem + 1;
pParse->nMem += pk_part_count;
regKey = ++pParse->nMem;
+ int reg_eph = ++pParse->nMem;
iEph = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
/* Address of the OpenEphemeral instruction. */
- int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iEph,
+ int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
pk_part_count);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
WHERE_ONEPASS_DESIRED, pk_cursor);
@@ -276,9 +280,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
pPk->def);
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
regKey, zAff, pk_part_count);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
- /* Set flag to save memory allocating one by malloc. */
- sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
}
/* End the database scan loop.
*/
@@ -304,6 +306,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
}
} else {
labelContinue = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, iEph, 0, reg_eph);
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
sqlite3VdbeAddOp4Int(v, OP_NotFound, pk_cursor, labelContinue,
@@ -437,7 +440,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
sqlite3VdbeJumpHere(v, addr1);
if (hasFK)
fkey_emit_check(pParse, pTab, 0, regNewPk, aXRef);
- vdbe_emit_insertion_completion(v, pk_cursor, regNew,
+ vdbe_emit_insertion_completion(v, space, regNew,
pTab->def->field_count,
on_error);
/*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 00ffb0c..aa602df 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -3054,51 +3054,37 @@ case OP_TTransaction: {
break;
}
-/* Opcode: OpenRead P1 P2 P3 P4 P5
+/* Opcode: CursorReopen P1 P2 P3 P4 P5
* Synopsis: index id = P2, space ptr = P4
*
- * Open a cursor for a space specified by pointer in P4 and index
- * id in P2. Give the new cursor an identifier of P1. The P1
- * values need not be contiguous but all P1 values should be
- * small integers. It is an error for P1 to be negative.
- */
-/* Opcode: ReopenIdx P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
- *
- * The ReopenIdx opcode works exactly like OpenRead except that
- * it first checks to see if the cursor on P1 is already open
+ * The CursorReopen opcode works exactly like CursorOpen except
+ * that it first checks to see if the cursor on P1 is already open
* with the same index and if it is this opcode becomes a no-op.
- * In other words, if the cursor is already open, do not reopen it.
+ * In other words, if the cursor is already open, do not reopen
+ * it.
*
- * The ReopenIdx opcode may only be used with P5 == 0.
+ * The CursorReopen opcode may only be used with P5 == 0.
*/
-/* Opcode: OpenWrite P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
+/* Opcode: CursorOpen P1 P2 P3 P4 P5
+ * Synopsis: index id = P2, space ptr = P4 or reg[P3]
*
- * For now, OpenWrite is an alias for OpenRead.
- * It exists just due legacy reasons and should be removed:
- * it isn't neccessary to open cursor to make insertion or
- * deletion.
- */
-case OP_ReopenIdx: {
- int nField;
- int p2;
- VdbeCursor *pCur;
- BtCursor *pBtCur;
-
- assert(pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
- pCur = p->apCsr[pOp->p1];
- p2 = pOp->p2;
- if (pCur && pCur->uc.pCursor->space == pOp->p4.space &&
- pCur->uc.pCursor->index->def->iid == (uint32_t)p2)
+ * Open a cursor for a space specified by pointer in P4 and index
+ * id in P2. Give the new cursor an identifier of P1. The P1
+ * values need not be contiguous but all P1 values should be
+ * small integers. It is an error for P1 to be negative.
+ * If P4 was not set, then P3 supposed to be the register
+ * containing space pointer.
+ */
+case OP_CursorReopen: {
+ assert(pOp->p5 == 0);
+ struct VdbeCursor *cur = p->apCsr[pOp->p1];
+ if (cur != NULL && cur->uc.pCursor->space == pOp->p4.space &&
+ cur->uc.pCursor->index->def->iid == (uint32_t)pOp->p2)
goto open_cursor_set_hints;
/* If the cursor is not currently open or is open on a different
- * index, then fall through into OP_OpenRead to force a reopen
+ * index, then fall through into OP_OpenCursor to force a reopen
*/
-case OP_OpenRead:
-case OP_OpenWrite:
-
- assert(pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
+case OP_IteratorOpen:
if (box_schema_version() != p->schema_ver &&
(pOp->p5 & OPFLAG_SYSTEMSP) == 0) {
p->expired = 1;
@@ -3107,66 +3093,60 @@ case OP_OpenWrite:
"need to re-compile SQL statement");
goto abort_due_to_error;
}
- p2 = pOp->p2;
- struct space *space = pOp->p4.space;
+ struct space *space;
+ if (pOp->p4type == P4_SPACEPTR)
+ space = pOp->p4.space;
+ else
+ space = aMem[pOp->p3].u.p;
assert(space != NULL);
- struct index *index = space_index(space, p2);
+ struct index *index = space_index(space, pOp->p2);
assert(index != NULL);
- /*
- * Since Tarantool iterator provides the full tuple,
- * we need a number of fields as wide as the table itself.
- * Otherwise, not enough slots for row parser cache are
- * allocated in VdbeCursor object.
- */
- nField = space->def->field_count;
- assert(pOp->p1>=0);
- assert(nField>=0);
- pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_TARANTOOL);
- if (pCur==0) goto no_mem;
- pCur->nullRow = 1;
- pBtCur = pCur->uc.pCursor;
- pBtCur->curFlags |= BTCF_TaCursor;
- pBtCur->space = space;
- pBtCur->index = index;
- pBtCur->eState = CURSOR_INVALID;
+ assert(pOp->p1 >= 0);
+ cur = allocateCursor(p, pOp->p1,
+ space->def->exact_field_count == 0 ?
+ space->def->field_count :
+ space->def->exact_field_count,
+ CURTYPE_TARANTOOL);
+ if (cur == NULL)
+ goto no_mem;
+ struct BtCursor *bt_cur = cur->uc.pCursor;
+ bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
+ BTCF_TaCursor;
+ bt_cur->space = space;
+ bt_cur->index = index;
+ bt_cur->eState = CURSOR_INVALID;
/* Key info still contains sorter order and collation. */
- pCur->key_def = index->def->key_def;
-
+ cur->key_def = index->def->key_def;
+ cur->nullRow = 1;
open_cursor_set_hints:
- pCur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
- if (rc) goto abort_due_to_error;
+ cur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
+ if (rc != 0)
+ goto abort_due_to_error;
break;
}
/**
* Opcode: OpenTEphemeral P1 P2 * P4 *
* Synopsis:
- * @param P1 index of new cursor to be created.
+ * @param P1 register, where pointer to new space is stored.
* @param P2 number of columns in a new table.
* @param P4 key def for new table, NULL is allowed.
*
- * This opcode creates Tarantool's ephemeral table and sets cursor P1 to it.
+ * This opcode creates Tarantool's ephemeral table and stores pointer
+ * to it into P1 register.
*/
case OP_OpenTEphemeral: {
- VdbeCursor *pCx;
- BtCursor *pBtCur;
assert(pOp->p1 >= 0);
assert(pOp->p2 > 0);
assert(pOp->p4type != P4_KEYINFO || pOp->p4.key_info != NULL);
- pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_TARANTOOL);
- if (pCx == 0) goto no_mem;
- pCx->nullRow = 1;
+ struct space *space = sql_ephemeral_space_create(pOp->p2,
+ pOp->p4.key_info);
- pBtCur = pCx->uc.pCursor;
- /* Ephemeral spaces don't have space_id */
- pBtCur->eState = CURSOR_INVALID;
- pBtCur->curFlags = BTCF_TEphemCursor;
-
- rc = tarantoolSqlite3EphemeralCreate(pCx->uc.pCursor, pOp->p2,
- pOp->p4.key_info);
- pCx->key_def = pCx->uc.pCursor->index->def->key_def;
- if (rc) goto abort_due_to_error;
+ if (space == NULL)
+ goto abort_due_to_error;
+ aMem[pOp->p1].u.p = space;
+ aMem[pOp->p1].flags = MEM_Ptr;
break;
}
@@ -3708,23 +3688,21 @@ case OP_NextSequenceId: {
/* Opcode: NextIdEphemeral P1 P2 P3 * *
* Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]})
*
- * This opcode works in the same way as OP_NextId does, except it is
- * only applied for ephemeral tables. The difference is in the fact that
- * all ephemeral tables don't have space_id (to be more precise it equals to zero).
+ * This opcode works in the same way as OP_NextId does, except it
+ * is only applied for ephemeral tables. The difference is in the
+ * fact that all ephemeral tables don't have space_id (to be more
+ * precise it equals to zero). This opcode uses register P1 to
+ * fetch pointer to epehemeral space.
*/
case OP_NextIdEphemeral: {
- VdbeCursor *pC;
- int p2;
- pC = p->apCsr[pOp->p1];
- p2 = pOp->p2;
+ struct space *space = (struct space*)p->aMem[pOp->p1].u.p;
+ int p2 = pOp->p2;
+ assert(space != NULL);
pOut = &aMem[pOp->p3];
-
- assert(pC->uc.pCursor->curFlags & BTCF_TEphemCursor);
-
- rc = tarantoolSqlite3EphemeralGetMaxId(pC->uc.pCursor, p2,
+ rc = tarantoolSqlite3EphemeralGetMaxId(space, p2,
(uint64_t *) &pOut->u.i);
- if (rc) goto abort_due_to_error;
-
+ if (rc != 0)
+ goto abort_due_to_error;
pOut->u.i += 1;
pOut->flags = MEM_Int;
break;
@@ -4242,83 +4220,91 @@ case OP_Next: /* jump */
goto check_for_interrupt;
}
-/* Opcode: IdxInsert P1 P2 * * P5
+/* Opcode: SorterInsert P1 P2 * * *
* Synopsis: key=r[P2]
*
- * @param P1 Index of a space cursor.
- * @param P2 Index of a register with MessagePack data to insert.
+ * Register P2 holds an SQL index key made using the
+ * MakeRecord instructions. This opcode writes that key
+ * into the sorter P1. Data for the entry is nil.
+ */
+case OP_SorterInsert: { /* in2 */
+ assert(pOp->p1 >= 0 && pOp->p1 < p->nCursor);
+ struct VdbeCursor *cursor = p->apCsr[pOp->p1];
+ assert(cursor != NULL);
+ assert(isSorter(cursor));
+ pIn2 = &aMem[pOp->p2];
+ assert((pIn2->flags & MEM_Blob) != 0);
+ rc = ExpandBlob(pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ rc = sqlite3VdbeSorterWrite(cursor, pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ break;
+}
+
+/* Opcode: IdxInsert P1 P2 * P4 P5
+ * Synopsis: key=r[P1]
+ *
+ * @param P1 Index of a register with MessagePack data to insert.
+ * @param P2 If P4 is not set, then P2 is register containing pointer
+ * to space to insert into.
+ * @param P4 Pointer to the struct space to insert to.
* @param P5 Flags. If P5 contains OPFLAG_NCHANGE, then VDBE
* accounts the change in a case of successful insertion in
* nChange counter. If P5 contains OPFLAG_OE_IGNORE, then
* we are processing INSERT OR INGORE statement. Thus, in
* case of conflict we don't raise an error.
*/
-/* Opcode: IdxReplace P1 P2 * * P5
- * Synopsis: key=r[P2]
+/* Opcode: IdxReplace2 P1 * * P4 P5
+ * Synopsis: key=r[P1]
*
* This opcode works exactly as IdxInsert does, but in Tarantool
* internals it invokes box_replace() instead of box_insert().
*/
-/* Opcode: SorterInsert P1 P2 * * *
- * Synopsis: key=r[P2]
- *
- * Register P2 holds an SQL index key made using the
- * MakeRecord instructions. This opcode writes that key
- * into the sorter P1. Data for the entry is nil.
- */
-case OP_SorterInsert: /* in2 */
case OP_IdxReplace:
-case OP_IdxInsert: { /* in2 */
- VdbeCursor *pC;
-
- assert(pOp->p1>=0 && pOp->p1<p->nCursor);
- pC = p->apCsr[pOp->p1];
- assert(pC!=0);
- assert(isSorter(pC)==(pOp->opcode==OP_SorterInsert));
- pIn2 = &aMem[pOp->p2];
- assert(pIn2->flags & MEM_Blob);
- if (pOp->p5 & OPFLAG_NCHANGE) p->nChange++;
- assert(pC->eCurType==CURTYPE_TARANTOOL || pOp->opcode==OP_SorterInsert);
+case OP_IdxInsert: {
+ pIn2 = &aMem[pOp->p1];
+ assert((pIn2->flags & MEM_Blob) != 0);
+ if (pOp->p5 & OPFLAG_NCHANGE)
+ p->nChange++;
rc = ExpandBlob(pIn2);
- if (rc) goto abort_due_to_error;
- if (pOp->opcode==OP_SorterInsert) {
- rc = sqlite3VdbeSorterWrite(pC, pIn2);
- } else {
- BtCursor *pBtCur = pC->uc.pCursor;
- if (pBtCur->curFlags & BTCF_TaCursor) {
- /* Make sure that memory has been allocated on region. */
- assert(aMem[pOp->p2].flags & MEM_Ephem);
- if (pOp->opcode == OP_IdxInsert)
- rc = tarantoolSqlite3Insert(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
- else
- rc = tarantoolSqlite3Replace(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
- } else if (pBtCur->curFlags & BTCF_TEphemCursor) {
- rc = tarantoolSqlite3EphemeralInsert(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
+ if (rc != 0)
+ goto abort_due_to_error;
+ struct space *space;
+ if (pOp->p4type == P4_SPACEPTR)
+ space = pOp->p4.space;
+ else
+ space = aMem[pOp->p2].u.p;
+ assert(space != NULL);
+ if (space->def->id != 0) {
+ /* Make sure that memory has been allocated on region. */
+ assert(aMem[pOp->p1].flags & MEM_Ephem);
+ if (pOp->opcode == OP_IdxInsert) {
+ rc = tarantoolSqlite3Insert(space, pIn2->z,
+ pIn2->z + pIn2->n);
} else {
- unreachable();
+ rc = tarantoolSqlite3Replace(space, pIn2->z,
+ pIn2->z + pIn2->n);
}
- pC->cacheStatus = CACHE_STALE;
+ } else {
+ rc = tarantoolSqlite3EphemeralInsert(space, pIn2->z,
+ pIn2->z + pIn2->n);
}
if (pOp->p5 & OPFLAG_OE_IGNORE) {
/* Ignore any kind of failes and do not raise error message */
rc = SQLITE_OK;
/* If we are in trigger, increment ignore raised counter */
- if (p->pFrame) {
+ if (p->pFrame)
p->ignoreRaised++;
- }
} else if (pOp->p5 & OPFLAG_OE_FAIL) {
p->errorAction = ON_CONFLICT_ACTION_FAIL;
} else if (pOp->p5 & OPFLAG_OE_ROLLBACK) {
p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
}
- if (rc) goto abort_due_to_error;
+ if (rc != 0)
+ goto abort_due_to_error;
break;
}
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 01dcc49..3e00e6b 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4569,12 +4569,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
/* Do nothing */
} else if ((pLoop->wsFlags & WHERE_IDX_ONLY) == 0 &&
(wctrlFlags & WHERE_OR_SUBCLAUSE) == 0) {
- int op = OP_OpenRead;
- if (pWInfo->eOnePass != ONEPASS_OFF) {
- op = OP_OpenWrite;
+ if (pWInfo->eOnePass != ONEPASS_OFF)
pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
- };
- sqlite3OpenTable(pParse, pTabItem->iCursor, space, op);
+ assert(space->index_count > 0);
+ vdbe_emit_open_cursor(pParse, pTabItem->iCursor, 0,
+ space);
+ VdbeComment((v, "%s", space->def->name));
assert(pTabItem->iCursor == pLevel->iTabCur);
testcase(pWInfo->eOnePass == ONEPASS_OFF
&& pTab->nCol == BMS - 1);
@@ -4591,7 +4591,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
if (pLoop->wsFlags & WHERE_INDEXED) {
struct index_def *idx_def = pLoop->index_def;
int iIndexCur;
- int op = OP_OpenRead;
+ int op;
/* Check if index is primary. Either of
* points should be true:
* 1. struct Index is non-NULL and is
@@ -4636,12 +4636,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
}
}
assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
- op = OP_OpenWrite;
+ op = OP_IteratorOpen;
pWInfo->aiCurOnePass[1] = iIndexCur;
} else if (iAuxArg
&& (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
iIndexCur = iAuxArg;
- op = OP_ReopenIdx;
+ op = OP_CursorReopen;
} else {
iIndexCur = pParse->nTab++;
}
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 8a3f2ac..1aeb360 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -416,7 +416,7 @@ struct WhereInfo {
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
ExprList *pDistinctSet; /* DISTINCT over all these values */
LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
- int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
+ int aiCurOnePass[2]; /* OP_CursorOpen cursors for the ONEPASS opt */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index e2aaca3..8b16119 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1341,7 +1341,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
- int regRowset = 0; /* Register for RowSet object */
+ int cur_row_set = 0;
+ int reg_row_set = 0;
int regPk = 0; /* Register holding PK */
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
int iRetInit; /* Address of regReturn init */
@@ -1399,9 +1400,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
* called on an uninitialized cursor.
*/
if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
- regRowset = pParse->nTab++;
+ cur_row_set = pParse->nTab++;
+ reg_row_set = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- regRowset, pk_part_count);
+ reg_row_set, pk_part_count);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, cur_row_set, 0,
+ reg_row_set);
sql_vdbe_set_p4_key_def(pParse, pk_key_def);
regPk = ++pParse->nMem;
}
@@ -1534,7 +1538,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
if (iSet) {
jmp1 = sqlite3VdbeAddOp4Int
(v, OP_Found,
- regRowset, 0,
+ cur_row_set, 0,
r,
pk_part_count);
VdbeCoverage(v);
@@ -1545,7 +1549,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
r, pk_part_count, regPk);
sqlite3VdbeAddOp2
(v, OP_IdxInsert,
- regRowset, regPk);
+ regPk, reg_row_set);
}
/* Release the array of temp registers */
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-03 9:57 ` Kirill Yukhin
@ 2018-10-04 2:21 ` n.pettik
2018-10-04 7:16 ` Kirill Yukhin
0 siblings, 1 reply; 9+ messages in thread
From: n.pettik @ 2018-10-04 2:21 UTC (permalink / raw)
To: tarantool-patches; +Cc: Kirill Yukhin
>> In general patch is OK, I have spotted only several nits.
>> Also, I support your intention (which we discussed outside mailing list) to make
>> system spaces be ‘pre-loaded’. It means that all required system spaces will be
>> stored in predefined (at the start of program) registers. Moreover, I suggest to
>> attempt at involving not only system spaces. Idea is following:
>> each OP_CursorOpen during compilation stage skips register where space
>> should be stored. At the end of compilation (sql_finish_coding) we know that
>> totally program uses n registers. Then, we can allocate n + m (where m is number
>> of distinct spaces which are used in out query) registers to hold pointers to required
>> spaces. Finally, we patch each OP_CursorOpen and substitute void arg with
>> number of register where required space is stored. This approach for instance allows
>> to update all space pointers after schema changes with ease.
> Great! I'll do that in follow up patches.
Ok, then register it as an (set of) issues(es)
(in case you are going to implement them separately).
>> Finally, now I think about renaming CursorOpen to IteratorOpen: in fact after your
>> patch cursor has turned almost in wrapper around index iterator. Such naming
>> would underline the fact that ‘cursor’ is used only for read iterations over the space.
> Yes, sure. Renamed.
You forgot to update name of opcode in commit message.
Also, you deleted not all mentions of OP_Cursor(Re)Open - just grep it
>>> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
>>> index 9aa7058..ebde698 100644
>>> --- a/src/box/sql/delete.c
>>> +++ b/src/box/sql/delete.c
>>> @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
>>>
>>> void
>>> sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
>>> - int cursor)
>>> + int cursor, int reg_eph)
>>
>> This function is used twice: to materialize view during firing INSTEAD OF DELETE
>> and INSTEAD OF UPDATE triggers. In both cases, you pass reg_eph which is
>> not used after this function invocation. So I guess you can remove reg_eph arg.
> IMHO, allocating a register w/o assigning it to local variable is less
> straightforward, but OK, done.
It still can be simplified - see comments below.
>>> @@ -5261,7 +5303,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
>>> /* Verify that all AggInfo registers are within the range specified by
>>> * AggInfo.mnReg..AggInfo.mxReg
>>> */
>>> - assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
>>> + assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
>>
>> Hm? Explain pls this change.
> I'd avoid explaining it in the code. So, will do it here.
> Issue is that aggregate function may or may not contain distinct clause.
> If so - one extra reg per such func will be used. Right hand size of the
> assert doesn't take that into account and I see no reason to do that.
> That's why, new assertion is that number of allocated registers for
> aggregate is greater or even than sum of number of aggregate columns and
> functions.
>
>>> + cur = allocateCursor(p, pOp->p1,
>>> + space->def->exact_field_count == 0 ?
>>> + space->def->field_count :
>>> + space->def->exact_field_count,
>>> + CURTYPE_TARANTOOL);
>>> + if (cur == NULL)
>>> + goto no_mem;
>>> + struct BtCursor *bt_cur = cur->uc.pCursor;
>>> + bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
>>> + BTCF_TaCursor;
>>> + bt_cur->space = space;
>>> + bt_cur->index = index;
>>> + bt_cur->eState = CURSOR_INVALID;
>>
>> Grep CURSOR_INVALID - it is not used anywhere else.
>> Mb it is worth to remove it?
> eState flag is heavily used by cursors machinery. I don't
> think we can evict it right now.
I meant CURSOR_INVALID is not used (you can grep its usages).
So basically we can make eState to be boolean value:
cursor is whether broken (invalid) or not. I don’t insist on this change tho.
> --
> Regards, Kirill Yukhin
>
> commit 5bd0cf10ac94cfbaf5d076a3f5a347811fcf4c11
> Author: Kirill Yukhin <kyukhin@tarantool.org>
> Date: Tue Sep 25 19:01:10 2018 +0300
>
> sql: refactor SQL cursor to remove write ones
>
> In Tarantool opening and positioning cursor for writing
> have no sense. So refactor SQL code, to perform:
> - Creation of ephemeral table w/o any cursors machinery.
> No op-code returns register which contains plain pointer
> to the ephemeral space.
> - OpenRead/OpenWrite opcodes were replaced with single
> CursorOpen op-code, which establishes new cursor w/
IteratorOpen
> intention to subsequent read from the space. This opcode
> accepts both plain pointer (in P4 operand) and register
> which contains pointer (inn P3) to the ephemeral space.
Typo: in P3
> diff --git a/src/box/sql.c b/src/box/sql.c
> index ab4a587..f3836f2 100644
> --- a/src/box/sql.c
> +++ b/src/box/sql.c
> @@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
> return SQLITE_OK;
> }
>
> -/**
> - * Create ephemeral space and set cursor to the first entry. Features of
> - * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
> - * can be changed, but now only memtx engine is supported), primary index
> - * which covers all fields and no secondary indexes, given field number and
> - * collation sequence. All fields are scalar and nullable.
> - *
> - * @param pCur Cursor which will point to the new ephemeral space.
> - * @param field_count Number of fields in ephemeral space.
> - * @param key_info Keys description for new ephemeral space.
> - *
> - * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
> - */
> -int
> -tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
> - struct sql_key_info *key_info)
> +struct space *
> +sql_ephemeral_space_create(uint32_t field_count,
> + struct sql_key_info *key_info)
Nit: you can fit now args in one line.
> diff --git a/src/box/sql/build.c b/src/box/sql/build.c
> index 43be777..7670d53 100644
> --- a/src/box/sql/build.c
> +++ b/src/box/sql/build.c
> @@ -883,7 +883,7 @@ vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id,
> struct space *space)
> {
> assert(space != NULL);
> - return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_OpenWrite, cursor,
> + return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_IteratorOpen, cursor,
> index_id, 0, (void *) space, P4_SPACEPTR);
> }
>
> @@ -2693,7 +2693,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
> goto exit_create_index;
>
> sql_set_multi_write(parse, true);
> - sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, 0, 0,
> + sqlite3VdbeAddOp4(vdbe, OP_IteratorOpen, cursor, 0, 0,
> (void *)space_by_id(BOX_INDEX_ID),
> P4_SPACEPTR);
> sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> index 9aa7058..17e1c32 100644
> --- a/src/box/sql/delete.c
> +++ b/src/box/sql/delete.c
> @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
>
> void
> sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> - int cursor)
> + int cursor, int reg_eph)
You slightly misunderstood me: you don’t need to pass arg reg_eph:
you already have struct Parse *parse, so can fetch value for register
inside function.
> @@ -4061,7 +4064,8 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> int n;
> if (pExpr->pLeft->iTable == 0) {
> pExpr->pLeft->iTable =
> - sqlite3CodeSubselect(pParse, pExpr->pLeft, 0);
> + sqlite3CodeSubselect(pParse, pExpr->pLeft,
> + 0);
Noise diff.
> diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> index 02c0a5d..2776044 100644
> --- a/src/box/sql/select.c
> +++ b/src/box/sql/select.c
> @@ -64,8 +64,15 @@ typedef struct DistinctCtx DistinctCtx;
> struct DistinctCtx {
> u8 isTnct; /* True if the DISTINCT keyword is present */
> u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
> - int tabTnct; /* Ephemeral table used for DISTINCT processing */
> - int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
> + /* Ephemeral table's cursor used for DISTINCT processing. It is
> + * used for readin from ephemeral space.
> + */
> + int cur_eph;
> + /* Register, containing a pointer to ephemeral space. It
> + * is used for insertions while procesing DISTINCT.
> + */
> + int reg_eph;
> + int addrTnct; /* Address of OP_OpenEphemeral opcode for cur_eph */
> };
I fixed comment a little bit:
@@ -64,12 +64,14 @@ typedef struct DistinctCtx DistinctCtx;
struct DistinctCtx {
u8 isTnct; /* True if the DISTINCT keyword is present */
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
- /* Ephemeral table's cursor used for DISTINCT processing. It is
- * used for readin from ephemeral space.
+ /**
+ * Ephemeral table's cursor used for DISTINCT processing.
+ * It is used for reading from ephemeral space.
*/
int cur_eph;
- /* Register, containing a pointer to ephemeral space. It
- * is used for insertions while procesing DISTINCT.
+ /**
+ * Register, containing a pointer to ephemeral space.
+ * It is used for insertions while processing DISTINCT.
*/
> @@ -1039,17 +1059,25 @@ selectInnerLoop(Parse * pParse, /* The parser context */
> }
>
> case WHERE_DISTINCT_UNIQUE:{
> - sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> - break;
> + /* To handle DISTINCT two op-codes are
> + * emitted: OpenTEphemeral & IteratorOpen.
> + * addrTnct is address off first insn in
> + * a couple. Two evict ephemral space,
> + * need to noop both op-codes.
> + */
I’ve fixed comment:
- /* To handle DISTINCT two op-codes are
+ /*
+ * To handle DISTINCT two op-codes are
* emitted: OpenTEphemeral & IteratorOpen.
- * addrTnct is address off first insn in
- * a couple. Two evict ephemral space,
- * need to noop both op-codes.
+ * addrTnct is address of first opcode in
+ * a couple. To evict ephemeral space,
+ * we need to noop both op-codes.
*/
> + sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> + sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
> + break;
> }
>
>
> @@ -5679,7 +5725,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> VdbeComment((v, "%s", pItem->pTab->def->name));
> pItem->addrFillSub = addrTop;
> sqlite3SelectDestInit(&dest, SRT_Coroutine,
> - pItem->regReturn);
> + pItem->regReturn, -1);
> pItem->iSelectId = pParse->iNextSelectId;
> sqlite3Select(pParse, pSub, &dest);
> pItem->pTab->tuple_log_count = pSub->nSelectRow;
> @@ -5699,6 +5745,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> int retAddr;
> assert(pItem->addrFillSub == 0);
> pItem->regReturn = ++pParse->nMem;
> +
Noise diff.
> @@ -5885,7 +5941,15 @@ sqlite3Select(Parse * pParse, /* The parser context */
> * into an OP_Noop.
> */
> if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
> + /* To handle ordering two op-codes are
> + * emitted: OpenTEphemeral & IteratorOpen.
> + * sSort.addrSortIndex is address off
> + * first insn in a couple. Two evict
> + * ephemral space, need to noop both
> + * op-codes.
> + */
I’ve fixed comment:
- /* To handle ordering two op-codes are
+ /*
+ * To handle ordering two op-codes are
* emitted: OpenTEphemeral & IteratorOpen.
- * sSort.addrSortIndex is address off
- * first insn in a couple. Two evict
- * ephemral space, need to noop both
+ * sSort.addrSortIndex is address of
+ * first opcode in a couple. To evict
+ * ephemeral space, we need to noop both
* op-codes.
*/
> diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> index 53188e7..29636a0 100644
> --- a/src/box/sql/sqliteInt.h
> +++ b/src/box/sql/sqliteInt.h
> @@ -2000,6 +2000,8 @@ struct AggInfo {
> FuncDef *pFunc; /* The aggregate function implementation */
> int iMem; /* Memory location that acts as accumulator */
> int iDistinct; /* Ephemeral table used to enforce DISTINCT */
> + /* Register, holding ephemeral's spacepointer. */
Nit: missed space between ’space’ and ‘pointer’. And start comment from /**.
> + int reg_eph;
> } *aFunc;
> int nFunc; /* Number of entries in aFunc[] */
> };
> @@ -2591,6 +2593,8 @@ struct SelectDest {
> u8 eDest; /* How to dispose of the results. On of SRT_* above. */
> char *zAffSdst; /* Affinity used when eDest==SRT_Set */
> int iSDParm; /* A parameter used by the eDest disposal method */
> + /* Register containing ephemeral's space pointer. */
Nit: start comment from /**.
> diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
> index 8017742..497d989 100644
> --- a/src/box/sql/tarantoolInt.h
> +++ b/src/box/sql/tarantoolInt.h
> @@ -97,9 +97,22 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
> int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
> const char *zOldName, const char *zNewName);
>
> -/* Interface for ephemeral tables. */
> -int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
> - struct sql_key_info *key_info);
> +/**
> + * Create ephemeral space. Features of ephemeral spaces: id == 0,
> + * name == "ephemeral", memtx engine (in future it can be changed,
> + * but now only memtx engine is supported), primary index which
> + * covers all fields and no secondary indexes, given field number
> + * and collation sequence. All fields are scalar and nullable.
> + *
> + * @param field_count Number of fields in ephemeral space.
> + * @param key_info Keys description for new ephemeral space.
> + *
> + * @retval Pointer to created space, NULL if error.
> + */
> +struct space *
> +sql_ephemeral_space_create(uint32_t filed_count,
> + struct sql_key_info *key_info);
Nit: you can fit it in one line.
> @@ -276,9 +280,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
> pPk->def);
> sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
> regKey, zAff, pk_part_count);
> - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
> - /* Set flag to save memory allocating one by malloc. */
> - sqlite3VdbeChangeP5(v, 1);
You misunderstood me: you should change P5 to 1 for OP_MakeRecord opcode.
To be more precise see patch:
https://github.com/tarantool/tarantool/commit/a5576aa77bf900d0ceae126ba46a80c56078df6c
> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> index 00ffb0c..aa602df 100644
> --- a/src/box/sql/vdbe.c
> +++ b/src/box/sql/vdbe.c
> @@ -3054,51 +3054,37 @@ case OP_TTransaction: {
> break;
> }
>
> -/* Opcode: OpenRead P1 P2 P3 P4 P5
> +/* Opcode: CursorReopen P1 P2 P3 P4 P5
IteratorReopen?
> * Synopsis: index id = P2, space ptr = P4
> *
> - * Open a cursor for a space specified by pointer in P4 and index
> - * id in P2. Give the new cursor an identifier of P1. The P1
> - * values need not be contiguous but all P1 values should be
> - * small integers. It is an error for P1 to be negative.
> - */
> -/* Opcode: ReopenIdx P1 P2 P3 P4 P5
> - * Synopsis: index id = P2, space ptr = P4
> - *
> - * The ReopenIdx opcode works exactly like OpenRead except that
> - * it first checks to see if the cursor on P1 is already open
> + * The CursorReopen opcode works exactly like CursorOpen except
> + * that it first checks to see if the cursor on P1 is already open
> * with the same index and if it is this opcode becomes a no-op.
> - * In other words, if the cursor is already open, do not reopen it.
> + * In other words, if the cursor is already open, do not reopen
> + * it.
> *
> - * The ReopenIdx opcode may only be used with P5 == 0.
> + * The CursorReopen opcode may only be used with P5 == 0.
> */
> -/* Opcode: OpenWrite P1 P2 P3 P4 P5
> - * Synopsis: index id = P2, space ptr = P4
> +/* Opcode: CursorOpen P1 P2 P3 P4 P5
Don’t forget to rename opcode even in comments.
> diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
> index 8a3f2ac..1aeb360 100644
> --- a/src/box/sql/whereInt.h
> +++ b/src/box/sql/whereInt.h
> @@ -416,7 +416,7 @@ struct WhereInfo {
> ExprList *pOrderBy; /* The ORDER BY clause or NULL */
> ExprList *pDistinctSet; /* DISTINCT over all these values */
> LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
> - int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
> + int aiCurOnePass[2]; /* OP_CursorOpen cursors for the ONEPASS opt */
IteratorOpen?
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-04 2:21 ` n.pettik
@ 2018-10-04 7:16 ` Kirill Yukhin
2018-10-04 10:45 ` n.pettik
0 siblings, 1 reply; 9+ messages in thread
From: Kirill Yukhin @ 2018-10-04 7:16 UTC (permalink / raw)
To: n.pettik; +Cc: tarantool-patches
Hello Nikita!
Thanks for review. All nits applied.
Updated patch in the bottom. Branch force-pushed.
On 04 окт 05:21, n.pettik wrote:
>
> >> In general patch is OK, I have spotted only several nits.
> >> Also, I support your intention (which we discussed outside mailing list) to make
> >> system spaces be ‘pre-loaded’. It means that all required system spaces will be
> >> stored in predefined (at the start of program) registers. Moreover, I suggest to
> >> attempt at involving not only system spaces. Idea is following:
> >> each OP_CursorOpen during compilation stage skips register where space
> >> should be stored. At the end of compilation (sql_finish_coding) we know that
> >> totally program uses n registers. Then, we can allocate n + m (where m is number
> >> of distinct spaces which are used in out query) registers to hold pointers to required
> >> spaces. Finally, we patch each OP_CursorOpen and substitute void arg with
> >> number of register where required space is stored. This approach for instance allows
> >> to update all space pointers after schema changes with ease.
> > Great! I'll do that in follow up patches.
>
> Ok, then register it as an (set of) issues(es)
> (in case you are going to implement them separately).
I've submitted https://github.com/tarantool/tarantool/issues/3713
> >> Finally, now I think about renaming CursorOpen to IteratorOpen: in fact after your
> >> patch cursor has turned almost in wrapper around index iterator. Such naming
> >> would underline the fact that ‘cursor’ is used only for read iterations over the space.
> > Yes, sure. Renamed.
>
> You forgot to update name of opcode in commit message.
> Also, you deleted not all mentions of OP_Cursor(Re)Open - just grep it
>
> >>> diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> >>> index 9aa7058..ebde698 100644
> >>> --- a/src/box/sql/delete.c
> >>> +++ b/src/box/sql/delete.c
> >>> @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
> >>>
> >>> void
> >>> sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> >>> - int cursor)
> >>> + int cursor, int reg_eph)
> >>
> >> This function is used twice: to materialize view during firing INSTEAD OF DELETE
> >> and INSTEAD OF UPDATE triggers. In both cases, you pass reg_eph which is
> >> not used after this function invocation. So I guess you can remove reg_eph arg.
> > IMHO, allocating a register w/o assigning it to local variable is less
> > straightforward, but OK, done.
>
> It still can be simplified - see comments below.
>
> >>> @@ -5261,7 +5303,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
> >>> /* Verify that all AggInfo registers are within the range specified by
> >>> * AggInfo.mnReg..AggInfo.mxReg
> >>> */
> >>> - assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
> >>> + assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
> >>
> >> Hm? Explain pls this change.
> > I'd avoid explaining it in the code. So, will do it here.
> > Issue is that aggregate function may or may not contain distinct clause.
> > If so - one extra reg per such func will be used. Right hand size of the
> > assert doesn't take that into account and I see no reason to do that.
> > That's why, new assertion is that number of allocated registers for
> > aggregate is greater or even than sum of number of aggregate columns and
> > functions.
> >
> >>> + cur = allocateCursor(p, pOp->p1,
> >>> + space->def->exact_field_count == 0 ?
> >>> + space->def->field_count :
> >>> + space->def->exact_field_count,
> >>> + CURTYPE_TARANTOOL);
> >>> + if (cur == NULL)
> >>> + goto no_mem;
> >>> + struct BtCursor *bt_cur = cur->uc.pCursor;
> >>> + bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
> >>> + BTCF_TaCursor;
> >>> + bt_cur->space = space;
> >>> + bt_cur->index = index;
> >>> + bt_cur->eState = CURSOR_INVALID;
> >>
> >> Grep CURSOR_INVALID - it is not used anywhere else.
> >> Mb it is worth to remove it?
> > eState flag is heavily used by cursors machinery. I don't
> > think we can evict it right now.
>
> I meant CURSOR_INVALID is not used (you can grep its usages).
> So basically we can make eState to be boolean value:
> cursor is whether broken (invalid) or not. I don’t insist on this change tho.
>
> > --
> > Regards, Kirill Yukhin
> >
> > commit 5bd0cf10ac94cfbaf5d076a3f5a347811fcf4c11
> > Author: Kirill Yukhin <kyukhin@tarantool.org>
> > Date: Tue Sep 25 19:01:10 2018 +0300
> >
> > sql: refactor SQL cursor to remove write ones
> >
> > In Tarantool opening and positioning cursor for writing
> > have no sense. So refactor SQL code, to perform:
> > - Creation of ephemeral table w/o any cursors machinery.
> > No op-code returns register which contains plain pointer
> > to the ephemeral space.
> > - OpenRead/OpenWrite opcodes were replaced with single
> > CursorOpen op-code, which establishes new cursor w/
>
> IteratorOpen
>
> > intention to subsequent read from the space. This opcode
> > accepts both plain pointer (in P4 operand) and register
> > which contains pointer (inn P3) to the ephemeral space.
>
> Typo: in P3
>
> > diff --git a/src/box/sql.c b/src/box/sql.c
> > index ab4a587..f3836f2 100644
> > --- a/src/box/sql.c
> > +++ b/src/box/sql.c
> > @@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
> > return SQLITE_OK;
> > }
> >
> > -/**
> > - * Create ephemeral space and set cursor to the first entry. Features of
> > - * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
> > - * can be changed, but now only memtx engine is supported), primary index
> > - * which covers all fields and no secondary indexes, given field number and
> > - * collation sequence. All fields are scalar and nullable.
> > - *
> > - * @param pCur Cursor which will point to the new ephemeral space.
> > - * @param field_count Number of fields in ephemeral space.
> > - * @param key_info Keys description for new ephemeral space.
> > - *
> > - * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
> > - */
> > -int
> > -tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
> > - struct sql_key_info *key_info)
> > +struct space *
> > +sql_ephemeral_space_create(uint32_t field_count,
> > + struct sql_key_info *key_info)
>
> Nit: you can fit now args in one line.
Done.
> > diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> > index 9aa7058..17e1c32 100644
> > --- a/src/box/sql/delete.c
> > +++ b/src/box/sql/delete.c
> > @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
> >
> > void
> > sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> > - int cursor)
> > + int cursor, int reg_eph)
>
> You slightly misunderstood me: you don’t need to pass arg reg_eph:
> you already have struct Parse *parse, so can fetch value for register
> inside function.
Got it. Done.
> > @@ -4061,7 +4064,8 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> > int n;
> > if (pExpr->pLeft->iTable == 0) {
> > pExpr->pLeft->iTable =
> > - sqlite3CodeSubselect(pParse, pExpr->pLeft, 0);
> > + sqlite3CodeSubselect(pParse, pExpr->pLeft,
> > + 0);
>
> Noise diff.
Removed, although it was simple fix of line length.
> > diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> > index 02c0a5d..2776044 100644
> > --- a/src/box/sql/select.c
> > +++ b/src/box/sql/select.c
> > @@ -64,8 +64,15 @@ typedef struct DistinctCtx DistinctCtx;
> > struct DistinctCtx {
> > u8 isTnct; /* True if the DISTINCT keyword is present */
> > u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
> > - int tabTnct; /* Ephemeral table used for DISTINCT processing */
> > - int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
> > + /* Ephemeral table's cursor used for DISTINCT processing. It is
> > + * used for readin from ephemeral space.
> > + */
> > + int cur_eph;
> > + /* Register, containing a pointer to ephemeral space. It
> > + * is used for insertions while procesing DISTINCT.
> > + */
> > + int reg_eph;
> > + int addrTnct; /* Address of OP_OpenEphemeral opcode for cur_eph */
> > };
>
> I fixed comment a little bit:
Okay.
> > @@ -1039,17 +1059,25 @@ selectInnerLoop(Parse * pParse, /* The parser context */
> > }
> >
> > case WHERE_DISTINCT_UNIQUE:{
> > - sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> > - break;
> > + /* To handle DISTINCT two op-codes are
> > + * emitted: OpenTEphemeral & IteratorOpen.
> > + * addrTnct is address off first insn in
> > + * a couple. Two evict ephemral space,
> > + * need to noop both op-codes.
> > + */
>
> I’ve fixed comment:
Okay.
> > @@ -5699,6 +5745,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
> > int retAddr;
> > assert(pItem->addrFillSub == 0);
> > pItem->regReturn = ++pParse->nMem;
> > +
>
> Noise diff.
Removed.
> > @@ -5885,7 +5941,15 @@ sqlite3Select(Parse * pParse, /* The parser context */
> > * into an OP_Noop.
> > */
> > if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
> > + /* To handle ordering two op-codes are
> > + * emitted: OpenTEphemeral & IteratorOpen.
> > + * sSort.addrSortIndex is address off
> > + * first insn in a couple. Two evict
> > + * ephemral space, need to noop both
> > + * op-codes.
> > + */
>
> I’ve fixed comment:
Okay.
> > diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> > index 53188e7..29636a0 100644
> > --- a/src/box/sql/sqliteInt.h
> > +++ b/src/box/sql/sqliteInt.h
> > @@ -2000,6 +2000,8 @@ struct AggInfo {
> > FuncDef *pFunc; /* The aggregate function implementation */
> > int iMem; /* Memory location that acts as accumulator */
> > int iDistinct; /* Ephemeral table used to enforce DISTINCT */
> > + /* Register, holding ephemeral's spacepointer. */
>
> Nit: missed space between ’space’ and ‘pointer’. And start comment from /**.
Fixed.
> > @@ -2591,6 +2593,8 @@ struct SelectDest {
> > u8 eDest; /* How to dispose of the results. On of SRT_* above. */
> > char *zAffSdst; /* Affinity used when eDest==SRT_Set */
> > int iSDParm; /* A parameter used by the eDest disposal method */
> > + /* Register containing ephemeral's space pointer. */
>
> Nit: start comment from /**.
Done.
> > diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
> > index 8017742..497d989 100644
> > --- a/src/box/sql/tarantoolInt.h
> > +++ b/src/box/sql/tarantoolInt.h
> > @@ -97,9 +97,22 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
> > int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
> > const char *zOldName, const char *zNewName);
> >
> > -/* Interface for ephemeral tables. */
> > -int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
> > - struct sql_key_info *key_info);
> > +/**
> > + * Create ephemeral space. Features of ephemeral spaces: id == 0,
> > + * name == "ephemeral", memtx engine (in future it can be changed,
> > + * but now only memtx engine is supported), primary index which
> > + * covers all fields and no secondary indexes, given field number
> > + * and collation sequence. All fields are scalar and nullable.
> > + *
> > + * @param field_count Number of fields in ephemeral space.
> > + * @param key_info Keys description for new ephemeral space.
> > + *
> > + * @retval Pointer to created space, NULL if error.
> > + */
> > +struct space *
> > +sql_ephemeral_space_create(uint32_t filed_count,
> > + struct sql_key_info *key_info);
>
> Nit: you can fit it in one line.
Done.
> > @@ -276,9 +280,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
> > pPk->def);
> > sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
> > regKey, zAff, pk_part_count);
> > - sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
> > - /* Set flag to save memory allocating one by malloc. */
> > - sqlite3VdbeChangeP5(v, 1);
>
> You misunderstood me: you should change P5 to 1 for OP_MakeRecord opcode.
> To be more precise see patch:
> https://github.com/tarantool/tarantool/commit/a5576aa77bf900d0ceae126ba46a80c56078df6c
Fixed.
> > diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> > index 00ffb0c..aa602df 100644
> > --- a/src/box/sql/vdbe.c
> > +++ b/src/box/sql/vdbe.c
> > @@ -3054,51 +3054,37 @@ case OP_TTransaction: {
> > break;
> > }
> >
> > -/* Opcode: OpenRead P1 P2 P3 P4 P5
> > +/* Opcode: CursorReopen P1 P2 P3 P4 P5
>
> IteratorReopen?
Fixed everywhere.
--
Regards, Kirill Yukhin
commit b41476c6c8a046a90882ddaac8fd157baf97f83a
Author: Kirill Yukhin <kyukhin@tarantool.org>
Date: Tue Sep 25 19:01:10 2018 +0300
sql: refactor SQL cursor to remove write ones
In Tarantool opening and positioning cursor for writing
have no sense. So refactor SQL code, to perform:
- Creation of ephemeral table w/o any cursors machinery.
No op-code returns register which contains plain pointer
to the ephemeral space.
- OpenRead/OpenWrite opcodes were replaced with single
IteratorOpen op-code, which establishes new cursor w/
intention to subsequent read from the space. This opcode
accepts both plain pointer (in P4 operand) and register
which contains pointer (in P3) to the ephemeral space.
- Fix query scheduler and DML routines thoroughly.
Closes #3182
Part of #2362
---
src/box/sql.c | 66 ++++--------
src/box/sql/analyze.c | 64 ++++-------
src/box/sql/build.c | 4 +-
src/box/sql/delete.c | 19 ++--
src/box/sql/expr.c | 38 +++----
src/box/sql/insert.c | 52 ++++-----
src/box/sql/parse.y | 6 +-
src/box/sql/select.c | 236 +++++++++++++++++++++++++---------------
src/box/sql/sqliteInt.h | 15 ++-
src/box/sql/tarantoolInt.h | 31 +++++-
src/box/sql/trigger.c | 12 +--
src/box/sql/update.c | 19 ++--
src/box/sql/vdbe.c | 262 +++++++++++++++++++++------------------------
src/box/sql/where.c | 16 +--
src/box/sql/whereInt.h | 2 +-
src/box/sql/wherecode.c | 14 ++-
16 files changed, 438 insertions(+), 418 deletions(-)
diff --git a/src/box/sql.c b/src/box/sql.c
index ab4a587..3b2f3a9 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -355,31 +355,14 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
return SQLITE_OK;
}
-/**
- * Create ephemeral space and set cursor to the first entry. Features of
- * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
- * can be changed, but now only memtx engine is supported), primary index
- * which covers all fields and no secondary indexes, given field number and
- * collation sequence. All fields are scalar and nullable.
- *
- * @param pCur Cursor which will point to the new ephemeral space.
- * @param field_count Number of fields in ephemeral space.
- * @param key_info Keys description for new ephemeral space.
- *
- * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
- */
-int
-tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
- struct sql_key_info *key_info)
+struct space *
+sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
{
- assert(pCur);
- assert(pCur->curFlags & BTCF_TEphemCursor);
-
struct key_def *def = NULL;
if (key_info != NULL) {
def = sql_key_info_to_key_def(key_info);
if (def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
struct key_part_def *ephemer_key_parts = region_alloc(&fiber()->gc,
@@ -387,7 +370,7 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
if (ephemer_key_parts == NULL) {
diag_set(OutOfMemory, sizeof(*ephemer_key_parts) * field_count,
"region", "key parts");
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
for (uint32_t i = 0; i < field_count; ++i) {
struct key_part_def *part = &ephemer_key_parts[i];
@@ -404,14 +387,14 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
struct key_def *ephemer_key_def = key_def_new(ephemer_key_parts,
field_count);
if (ephemer_key_def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
struct index_def *ephemer_index_def =
index_def_new(0, 0, "ephemer_idx", strlen("ephemer_idx"), TREE,
&index_opts_default, ephemer_key_def, NULL);
key_def_delete(ephemer_key_def);
if (ephemer_index_def == NULL)
- return SQL_TARANTOOL_ERROR;
+ return NULL;
struct rlist key_list;
rlist_create(&key_list);
@@ -425,24 +408,15 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
0 /* length of field_def */);
if (ephemer_space_def == NULL) {
index_def_delete(ephemer_index_def);
- return SQL_TARANTOOL_ERROR;
+ return NULL;
}
struct space *ephemer_new_space = space_new_ephemeral(ephemer_space_def,
&key_list);
index_def_delete(ephemer_index_def);
space_def_delete(ephemer_space_def);
- if (ephemer_new_space == NULL)
- return SQL_TARANTOOL_ERROR;
- if (key_alloc(pCur, field_count) != 0) {
- space_delete(ephemer_new_space);
- return SQL_TARANTOOL_ERROR;
- }
- pCur->space = ephemer_new_space;
- pCur->index = *ephemer_new_space->index;
- int unused;
- return tarantoolSqlite3First(pCur, &unused);
+ return ephemer_new_space;
}
int tarantoolSqlite3EphemeralInsert(struct space *space, const char *tuple,
@@ -1461,35 +1435,31 @@ sql_debug_info(struct info_handler *h)
info_end(h);
}
-/*
+/**
* Extract maximum integer value from ephemeral space.
* If index is empty - return 0 in max_id and success status.
*
- * @param pCur Cursor pointing to ephemeral space.
+ * @param space Pointer to ephemeral space.
* @param fieldno Number of field from fetching tuple.
* @param[out] max_id Fetched max value.
*
* @retval 0 on success, -1 otherwise.
*/
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor *pCur, uint32_t fieldno,
- uint64_t *max_id)
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
+ uint64_t *max_id)
{
- struct space *ephem_space = pCur->space;
- assert(ephem_space);
- struct index *primary_index = *ephem_space->index;
-
+ struct index *primary_index = *space->index;
struct tuple *tuple;
- if (index_max(primary_index, NULL, 0, &tuple) != 0) {
- return SQL_TARANTOOL_ERROR;
- }
+ if (index_max(primary_index, NULL, 0, &tuple) != 0)
+ return -1;
if (tuple == NULL) {
*max_id = 0;
- return SQLITE_OK;
+ return 0;
}
if (tuple_field_u64(tuple, fieldno, max_id) == -1)
- return SQL_TARANTOOL_ERROR;
+ return -1;
- return SQLITE_OK;
+ return 0;
}
int
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 01e2ad1..674d53d 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -122,13 +122,10 @@
* created.
*
* @param parse Parsing context.
- * @param stat_cursor Open the _sql_stat1 table on this cursor.
- * you should allocate |stat_names| cursors before call.
* @param table_name Delete records of this table if specified.
*/
static void
-vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
- const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
{
const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -144,14 +141,6 @@ vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
}
}
-
- /* Open the sql_stat tables for writing. */
- for (uint i = 0; i < lengthof(stat_names); ++i) {
- uint32_t id = stat_ids[i];
- vdbe_emit_open_cursor(parse, stat_cursor + i, 0,
- space_by_id(id));
- VdbeComment((v, stat_names[i]));
- }
}
/*
@@ -770,15 +759,16 @@ callStatGet(Vdbe * v, int regStat4, int iParam, int regOut)
*
* @param parse Current parsing context.
* @param space Space to be analyzed.
- * @param stat_cursor Cursor pointing to spaces containing
- * statistics: _sql_stat1 (stat_cursor) and
- * _sql_stat4 (stat_cursor + 1).
*/
static void
-vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
- int stat_cursor)
+vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
{
assert(space != NULL);
+ struct space *stat1 = space_by_id(BOX_SQL_STAT1_ID);
+ assert(stat1 != NULL);
+ struct space *stat4 = space_by_id(BOX_SQL_STAT4_ID);
+ assert(stat4 != NULL);
+
/* Register to hold Stat4Accum object. */
int stat4_reg = ++parse->nMem;
/* Index of changed index field. */
@@ -808,7 +798,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
struct Vdbe *v = sqlite3GetVdbe(parse);
assert(v != NULL);
const char *tab_name = space_name(space);
- sqlite3VdbeAddOp4(v, OP_OpenRead, tab_cursor, 0, 0, (void *) space,
+ sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) space,
P4_SPACEPTR);
sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
for (uint32_t j = 0; j < space->index_count; ++j) {
@@ -871,7 +861,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
int idx_cursor;
if (j != 0) {
idx_cursor = parse->nTab - 1;
- sqlite3VdbeAddOp4(v, OP_OpenRead, idx_cursor,
+ sqlite3VdbeAddOp4(v, OP_IteratorOpen, idx_cursor,
idx->def->iid, 0,
(void *) space, P4_SPACEPTR);
VdbeComment((v, "%s", idx->def->name));
@@ -1003,7 +993,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
assert("BBB"[0] == AFFINITY_TEXT);
sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
"BBB", 0);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, stat_cursor, tmp_reg);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
+ (char *)stat1, P4_SPACEPTR);
/* Add the entries to the stat4 table. */
int eq_reg = stat1_reg;
int lt_reg = stat1_reg + 1;
@@ -1036,7 +1027,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
sample_reg);
sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
- sqlite3VdbeAddOp2(v, OP_IdxReplace, stat_cursor + 1, tmp_reg);
+ sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
+ (char *)stat4, P4_SPACEPTR);
/* P1==1 for end-of-loop. */
sqlite3VdbeAddOp2(v, OP_Goto, 1, next_addr);
sqlite3VdbeJumpHere(v, is_null_addr);
@@ -1058,27 +1050,12 @@ loadAnalysis(Parse * pParse)
}
}
-/**
- * Wrapper to pass args to space_foreach callback.
- */
-struct analyze_data {
- struct Parse *parse_context;
- /**
- * Cursor numbers pointing to stat spaces:
- * stat_cursor is opened on _sql_stat1 and
- * stat_cursor + 1 - on _sql_stat4.
- */
- int stat_cursor;
-};
-
static int
sql_space_foreach_analyze(struct space *space, void *data)
{
if (space->def->opts.sql == NULL || space->def->opts.is_view)
return 0;
- struct analyze_data *anal_data = (struct analyze_data *) data;
- vdbe_emit_analyze_space(anal_data->parse_context, space,
- anal_data->stat_cursor);
+ vdbe_emit_analyze_space((struct Parse*)data, space);
return 0;
}
@@ -1090,11 +1067,8 @@ static void
sql_analyze_database(struct Parse *parser)
{
sql_set_multi_write(parser, false);
- int stat_cursor = parser->nTab;
- parser->nTab += 2;
- vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
- struct analyze_data anal_data = { parser, stat_cursor };
- space_foreach(sql_space_foreach_analyze, (void *) &anal_data);
+ vdbe_emit_stat_space_open(parser, NULL);
+ space_foreach(sql_space_foreach_analyze, (void *)parser);
loadAnalysis(parser);
}
@@ -1114,10 +1088,8 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space)
* There are two system spaces for statistics: _sql_stat1
* and _sql_stat4.
*/
- int stat_cursor = parse->nTab;
- parse->nTab += 2;
- vdbe_emit_stat_space_open(parse, stat_cursor, space->def->name);
- vdbe_emit_analyze_space(parse, space, stat_cursor);
+ vdbe_emit_stat_space_open(parse, space->def->name);
+ vdbe_emit_analyze_space(parse, space);
loadAnalysis(parse);
}
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 43be777..7670d53 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -883,7 +883,7 @@ vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id,
struct space *space)
{
assert(space != NULL);
- return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_OpenWrite, cursor,
+ return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_IteratorOpen, cursor,
index_id, 0, (void *) space, P4_SPACEPTR);
}
@@ -2693,7 +2693,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
goto exit_create_index;
sql_set_multi_write(parse, true);
- sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, 0, 0,
+ sqlite3VdbeAddOp4(vdbe, OP_IteratorOpen, cursor, 0, 0,
(void *)space_by_id(BOX_INDEX_ID),
P4_SPACEPTR);
sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 9aa7058..8f6c76f 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -83,7 +83,7 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
struct Select *select = sqlite3SelectNew(parse, NULL, from, where, NULL,
NULL, NULL, 0, NULL, NULL);
struct SelectDest dest;
- sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor);
+ sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor, ++parse->nMem);
sqlite3Select(parse, select, &dest);
sql_select_delete(db, select);
}
@@ -241,6 +241,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
* it, so columns should be loaded manually.
*/
struct sql_key_info *pk_info = NULL;
+ int reg_eph = ++parse->nMem;
int reg_pk = parse->nMem + 1;
int pk_len;
int eph_cursor = parse->nTab++;
@@ -248,8 +249,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
if (is_view) {
pk_len = table->def->field_count;
parse->nMem += pk_len;
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- eph_cursor, pk_len);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+ pk_len);
} else {
assert(space->index_count > 0);
pk_info = sql_key_info_new_from_key_def(db,
@@ -258,9 +259,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
goto delete_from_cleanup;
pk_len = pk_info->part_count;
parse->nMem += pk_len;
- sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, eph_cursor,
- pk_len, 0,
- (char *)pk_info, P4_KEYINFO);
+ sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
+ pk_len, 0,
+ (char *)pk_info, P4_KEYINFO);
}
/* Construct a query to find the primary key for
@@ -345,7 +346,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
* by malloc.
*/
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, eph_cursor, reg_key);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, reg_key, reg_eph);
}
/* If this DELETE cannot use the ONEPASS strategy,
@@ -369,7 +370,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once);
VdbeCoverage(v);
}
- sqlite3VdbeAddOp4(v, OP_OpenWrite, tab_cursor, 0, 0,
+ sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0,
(void *) space, P4_SPACEPTR);
VdbeComment((v, "%s", space->index[0]->def->name));
@@ -390,6 +391,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
VdbeCoverage(v);
} else {
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen,
+ eph_cursor, 0, reg_eph);
addr_loop = sqlite3VdbeAddOp1(v, OP_Rewind, eph_cursor);
VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowData, eph_cursor, reg_key);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index a13de4f..3602606 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2714,8 +2714,11 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
*/
pExpr->iTable = pParse->nTab++;
pExpr->is_ephemeral = 1;
+ int reg_eph = ++pParse->nMem;
addr = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- pExpr->iTable, nVal);
+ reg_eph, nVal);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, pExpr->iTable, 0,
+ reg_eph);
struct sql_key_info *key_info = sql_key_info_new(pParse->db, nVal);
if (key_info == NULL)
return 0;
@@ -2736,7 +2739,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
SelectDest dest;
int i;
sqlite3SelectDestInit(&dest, SRT_Set,
- pExpr->iTable);
+ pExpr->iTable, reg_eph);
dest.zAffSdst =
exprINAffinity(pParse, pExpr);
assert((pExpr->iTable & 0x0000FFFF) ==
@@ -2808,8 +2811,8 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
1, r2, &affinity, 1);
sqlite3ExprCacheAffinityChange(pParse,
r3, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert,
- pExpr->iTable, r2);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r2,
+ reg_eph);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
@@ -2847,7 +2850,7 @@ sqlite3CodeSubselect(Parse * pParse, /* Parsing context */
pSel = pExpr->x.pSelect;
nReg = pExpr->op == TK_SELECT ? pSel->pEList->nExpr : 1;
- sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1);
+ sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1, -1);
pParse->nMem += nReg;
if (pExpr->op == TK_SELECT) {
dest.eDest = SRT_Mem;
@@ -5361,24 +5364,17 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr)
pItem->iMem = ++pParse->nMem;
assert(!ExprHasProperty
(pExpr, EP_IntValue));
- pItem->pFunc =
- sqlite3FindFunction(pParse->
- db,
- pExpr->
- u.
- zToken,
- pExpr->
- x.
- pList ?
- pExpr->
- x.
- pList->
- nExpr :
- 0,
- 0);
+ pItem->pFunc = sqlite3FindFunction(
+ pParse->db,
+ pExpr->u.zToken,
+ pExpr->x.pList ?
+ pExpr->x.pList->nExpr : 0,
+ 0);
if (pExpr->flags & EP_Distinct) {
pItem->iDistinct =
- pParse->nTab++;
+ pParse->nTab++;
+ pItem->reg_eph =
+ ++pParse->nMem;
} else {
pItem->iDistinct = -1;
}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 03f4e1b..287c6c9 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -40,23 +40,6 @@
#include "bit/bit.h"
#include "box/box.h"
-/*
- * Generate code that will open pTab as cursor iCur.
- */
-void
-sqlite3OpenTable(Parse * pParse, /* Generate code into this VDBE */
- int iCur, /* The cursor number of the table */
- struct space *space, /* The table to be opened */
- int opcode) /* OP_OpenRead or OP_OpenWrite */
-{
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- assert(opcode == OP_OpenWrite || opcode == OP_OpenRead);
- assert(space->index_count > 0);
- vdbe_emit_open_cursor(pParse, iCur, 0, space);
- VdbeComment((v, "%s", space->def->name));
-}
-
char *
sql_space_index_affinity_str(struct sqlite3 *db, struct space_def *space_def,
struct index_def *idx_def)
@@ -139,9 +122,12 @@ vdbe_has_table_read(struct Parse *parser, const struct Table *table)
* Currently, there is no difference between Read
* and Write cursors.
*/
- if (op->opcode == OP_OpenRead || op->opcode == OP_OpenWrite) {
- assert(op->p4type == P4_SPACEPTR);
- struct space *space = op->p4.space;
+ if (op->opcode == OP_IteratorOpen) {
+ struct space *space = NULL;
+ if (op->p4type == P4_SPACEPTR)
+ space = op->p4.space;
+ else
+ continue;
if (space->def->id == table->def->id)
return true;
}
@@ -418,6 +404,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
}
}
+ int reg_eph;
/* Figure out how many columns of data are supplied. If the data
* is coming from a SELECT statement, then generate a co-routine that
* produces a single row of the SELECT on each invocation. The
@@ -434,7 +421,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
regYield = ++pParse->nMem;
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield, -1);
dest.iSdst = bIdListInOrder ? regData : 0;
dest.nSdst = def->field_count;
rc = sqlite3Select(pParse, pSelect, &dest);
@@ -480,10 +467,13 @@ sqlite3Insert(Parse * pParse, /* Parser context */
int64_t initial_pk = 0;
srcTab = pParse->nTab++;
+ reg_eph = ++pParse->nMem;
regRec = sqlite3GetTempReg(pParse);
regCopy = sqlite3GetTempRange(pParse, nColumn);
regTempId = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, srcTab, nColumn+1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+ nColumn + 1);
+
/* Create counter for rowid */
sqlite3VdbeAddOp4Dup8(v, OP_Int64,
0 /* unused */,
@@ -499,7 +489,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
nColumn + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, srcTab, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, reg_eph);
sqlite3VdbeGoto(v, addrL);
sqlite3VdbeJumpHere(v, addrL);
@@ -515,6 +505,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
srcTab = -1;
+ reg_eph = -1;
assert(useTempTable == 0);
if (pList) {
nColumn = pList->nExpr;
@@ -555,6 +546,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
* end loop
* D: ...
*/
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, srcTab, 0, reg_eph);
addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
VdbeCoverage(v);
addrCont = sqlite3VdbeCurrentAddr(v);
@@ -746,9 +738,7 @@ sqlite3Insert(Parse * pParse, /* Parser context */
vdbe_emit_constraint_checks(pParse, pTab, regIns + 1,
on_error, endOfLoop, 0);
fkey_emit_check(pParse, pTab, 0, regIns, 0);
- int pk_cursor = pParse->nTab++;
- vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
- vdbe_emit_insertion_completion(v, pk_cursor, regIns + 1,
+ vdbe_emit_insertion_completion(v, space, regIns + 1,
pTab->def->field_count,
on_error);
}
@@ -1063,8 +1053,8 @@ process_index: ;
}
void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
- uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+ int raw_data_reg, uint32_t tuple_len,
enum on_conflict_action on_conflict)
{
assert(v != NULL);
@@ -1077,7 +1067,8 @@ vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
pik_flags |= OPFLAG_OE_ROLLBACK;
sqlite3VdbeAddOp3(v, OP_MakeRecord, raw_data_reg, tuple_len,
raw_data_reg + tuple_len);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor_id, raw_data_reg + tuple_len);
+ sqlite3VdbeAddOp1(v, OP_IdxInsert, raw_data_reg + tuple_len);
+ sqlite3VdbeChangeP4(v, -1, (char *)space, P4_SPACEPTR);
sqlite3VdbeChangeP5(v, pik_flags);
}
@@ -1327,7 +1318,8 @@ xferOptimization(Parse * pParse, /* Parser context */
sqlite3VdbeChangeP5(v, OPFLAG_XFER_OPT);
#endif
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, regData, 0, 0,
+ (char *)dest, P4_SPACEPTR);
switch (onError) {
case ON_CONFLICT_ACTION_IGNORE:
sqlite3VdbeChangeP5(v, OPFLAG_OE_IGNORE | OPFLAG_NCHANGE);
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 473cf89..92e3a93 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -390,11 +390,11 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
//////////////////////// The SELECT statement /////////////////////////////////
//
cmd ::= select(X). {
- SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
if(!pParse->parse_only)
- sqlite3Select(pParse, X, &dest);
+ sqlite3Select(pParse, X, &dest);
else
- sql_expr_extract_select(pParse, X);
+ sql_expr_extract_select(pParse, X);
sql_select_delete(pParse->db, X);
}
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 02c0a5d..929d3c8 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -64,8 +64,17 @@ typedef struct DistinctCtx DistinctCtx;
struct DistinctCtx {
u8 isTnct; /* True if the DISTINCT keyword is present */
u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
- int tabTnct; /* Ephemeral table used for DISTINCT processing */
- int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
+ /**
+ * Ephemeral table's cursor used for DISTINCT processing.
+ * It is used for reading from ephemeral space.
+ */
+ int cur_eph;
+ /**
+ * Register, containing a pointer to ephemeral space.
+ * It is used for insertions while procesing DISTINCT.
+ */
+ int reg_eph;
+ int addrTnct; /* Address of OP_OpenEphemeral opcode for cur_eph */
};
/*
@@ -77,6 +86,8 @@ struct SortCtx {
ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
int nOBSat; /* Number of ORDER BY terms satisfied by indices */
int iECursor; /* Cursor number for the sorter */
+ /* Register, containing pointer to ephemeral space. */
+ int reg_eph;
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
@@ -117,10 +128,11 @@ clearSelect(sqlite3 * db, Select * p, int bFree)
* Initialize a SelectDest structure.
*/
void
-sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm)
+sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm, int reg_eph)
{
pDest->eDest = (u8) eDest;
pDest->iSDParm = iParm;
+ pDest->reg_eph = reg_eph;
pDest->zAffSdst = 0;
pDest->iSdst = 0;
pDest->nSdst = 0;
@@ -687,7 +699,6 @@ pushOntoSorter(Parse * pParse, /* Parser context */
int regBase; /* Regs for sorter record */
int regRecord = ++pParse->nMem; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
- int op; /* Opcode to add sorter record to sorter */
int iLimit; /* LIMIT counter */
assert(bSeq == 0 || bSeq == 1);
@@ -764,11 +775,13 @@ pushOntoSorter(Parse * pParse, /* Parser context */
sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
sqlite3VdbeJumpHere(v, addrJmp);
}
- if (pSort->sortFlags & SORTFLAG_UseSorter)
- op = OP_SorterInsert;
- else
- op = OP_IdxInsert;
- sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
+ if (pSort->sortFlags & SORTFLAG_UseSorter) {
+ sqlite3VdbeAddOp2(v, OP_SorterInsert, pSort->iECursor,
+ regRecord);
+ } else {
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRecord, pSort->reg_eph);
+ }
+
if (iLimit) {
int addr;
int r1 = 0;
@@ -823,32 +836,33 @@ codeOffset(Vdbe * v, /* Generate code into this VM */
}
}
-/*
- * Add code that will check to make sure the N registers starting at iMem
- * form a distinct entry. iTab is a sorting index that holds previously
- * seen combinations of the N values. A new entry is made in iTab
- * if the current N values are new.
- *
- * A jump to addrRepeat is made and the N+1 values are popped from the
- * stack if the top N elements are not distinct.
+/**
+ * Add code that will check to make sure the @n registers starting
+ * at @reg_data form a distinct entry. @cursor is a sorting index
+ * that holds previously seen combinations of the @n values.
+ * A new entry is made in @cursor if the current n values are new.
+ *
+ * A jump to @addr_repeat is made and the @n+1 values are popped
+ * from the stack if the top n elements are not distinct.
+ *
+ * @param parse Parsing and code generating context.
+ * @param cursor A sorting index cursor used to test for
+ * distinctness.
+ * @param reg_eph Register holding ephemeral space's pointer.
+ * @param addr_repeat Jump here if not distinct.
+ * @param n Number of elements in record.
+ * @param reg_data First register holding the data.
*/
static void
-codeDistinct(Parse * pParse, /* Parsing and code generating context */
- int iTab, /* A sorting index used to test for distinctness */
- int addrRepeat, /* Jump to here if not distinct */
- int N, /* Number of elements */
- int iMem) /* First element */
+vdbe_insert_distinct(struct Parse *parse, int cursor, int reg_eph,
+ int addr_repeat, int n, int reg_data)
{
- Vdbe *v;
- int r1;
-
- v = pParse->pVdbe;
- r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ struct Vdbe *v = parse->pVdbe;
+ int r1 = sqlite3GetTempReg(parse);
+ sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
+ sqlite3ReleaseTempReg(parse, r1);
}
/*
@@ -991,14 +1005,22 @@ selectInnerLoop(Parse * pParse, /* The parser context */
regPrev = pParse->nMem + 1;
pParse->nMem += nResultCol;
- /* Change the OP_OpenEphemeral coded earlier to an OP_Null
- * sets the MEM_Cleared bit on the first register of the
- * previous value. This will cause the OP_Ne below to always
- * fail on the first iteration of the loop even if the first
- * row is all NULLs.
+ /* Actually, for DISTINCT handling
+ * two op-codes were emitted here:
+ * OpenTEphemeral & IteratorOpen.
+ * So, we need to Noop one and
+ * re-use second for Null op-code.
+ *
+ * Change to an OP_Null sets the
+ * MEM_Cleared bit on the first
+ * register of the previous value.
+ * This will cause the OP_Ne below
+ * to always fail on the first
+ * iteration of the loop even if
+ * the first row is all NULLs.
*/
sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
+ pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct + 1);
pOp->opcode = OP_Null;
pOp->p1 = 1;
pOp->p2 = regPrev;
@@ -1039,17 +1061,26 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
case WHERE_DISTINCT_UNIQUE:{
- sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
- break;
+ /**
+ * To handle DISTINCT two op-codes are
+ * emitted: OpenTEphemeral & IteratorOpen.
+ * addrTnct is address of first insn in
+ * a couple. To evict ephemral space,
+ * need to noop both op-codes.
+ */
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
+ break;
}
default:{
- assert(pDistinct->eTnctType ==
- WHERE_DISTINCT_UNORDERED);
- codeDistinct(pParse, pDistinct->tabTnct,
- iContinue, nResultCol, regResult);
- break;
- }
+ assert(pDistinct->eTnctType ==
+ WHERE_DISTINCT_UNORDERED);
+ vdbe_insert_distinct(pParse, pDistinct->cur_eph,
+ pDistinct->reg_eph, iContinue,
+ nResultCol, regResult);
+ break;
+ }
}
if (pSort == 0) {
codeOffset(v, p->iOffset, iContinue);
@@ -1066,7 +1097,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
nResultCol, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, r1);
break;
}
@@ -1109,8 +1140,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp4Int(v, OP_Found, iParm + 1,
addr, r1, 0);
VdbeCoverage(v);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
- r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1,
+ pDest->reg_eph + 1);
assert(pSort == 0);
}
#endif
@@ -1122,7 +1153,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
int regRec = sqlite3GetTempReg(pParse);
/* Last column is required for ID. */
int regCopy = sqlite3GetTempRange(pParse, nResultCol + 1);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm,
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
nResultCol, regCopy + nResultCol);
/* Positioning ID column to be last in inserted tuple.
* NextId -> regCopy + n + 1
@@ -1134,7 +1165,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nResultCol + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempRange(pParse, regCopy, nResultCol + 1);
}
@@ -1164,7 +1195,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3ExprCacheAffinityChange(pParse,
regResult,
nResultCol);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, r1);
}
break;
@@ -1247,8 +1278,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
nResultCol, r3);
if (eDest == SRT_DistQueue) {
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
- r3);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r3,
+ pDest->reg_eph + 1);
}
for (i = 0; i < nKey; i++) {
sqlite3VdbeAddOp2(v, OP_SCopy,
@@ -1258,7 +1289,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2 + nKey);
sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey + 2, r1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
if (addrTest)
sqlite3VdbeJumpHere(v, addrTest);
sqlite3ReleaseTempReg(pParse, r1);
@@ -1498,7 +1529,6 @@ generateSortTail(Parse * pParse, /* Parsing context */
int iTab;
ExprList *pOrderBy = pSort->pOrderBy;
int eDest = pDest->eDest;
- int iParm = pDest->iSDParm;
int regRow;
int regTupleid;
int iCol;
@@ -1570,10 +1600,11 @@ generateSortTail(Parse * pParse, /* Parsing context */
case SRT_Table:
case SRT_EphemTab: {
int regCopy = sqlite3GetTempRange(pParse, nColumn);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm, nColumn, regTupleid);
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
+ nColumn, regTupleid);
sqlite3VdbeAddOp3(v, OP_Copy, regRow, regCopy, nSortData - 1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nColumn + 1, regRow);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRow);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRow, pDest->reg_eph);
sqlite3ReleaseTempReg(pParse, regCopy);
break;
}
@@ -1583,7 +1614,7 @@ generateSortTail(Parse * pParse, /* Parsing context */
sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn,
regTupleid, pDest->zAffSdst, nColumn);
sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regTupleid);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regTupleid, pDest->reg_eph);
break;
}
case SRT_Mem:{
@@ -2409,13 +2440,16 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
* for the SRT_DistFifo and SRT_DistQueue destinations to work.
*/
iQueue = pParse->nTab++;
+ int reg_queue = ++pParse->nMem;
+ int reg_dist = 0;
if (p->op == TK_UNION) {
eDest = pOrderBy ? SRT_DistQueue : SRT_DistFifo;
iDistinct = pParse->nTab++;
+ reg_dist = ++pParse->nMem;
} else {
eDest = pOrderBy ? SRT_Queue : SRT_Fifo;
}
- sqlite3SelectDestInit(&destQueue, eDest, iQueue);
+ sqlite3SelectDestInit(&destQueue, eDest, iQueue, reg_queue);
/* Allocate cursors for Current, Queue, and Distinct. */
regCurrent = ++pParse->nMem;
@@ -2423,18 +2457,20 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
if (pOrderBy) {
struct sql_key_info *key_info =
sql_multiselect_orderby_to_key_info(pParse, p, 1);
- sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, iQueue,
+ sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_queue,
pOrderBy->nExpr + 2, 0, (char *)key_info,
P4_KEYINFO);
VdbeComment((v, "Orderby table"));
destQueue.pOrderBy = pOrderBy;
} else {
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iQueue, nCol + 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_queue, nCol + 1);
VdbeComment((v, "Queue table"));
}
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, iQueue, 0, reg_queue);
if (iDistinct) {
p->addrOpenEphm[0] =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iDistinct, 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_dist, 1);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, iDistinct, 0, reg_dist);
p->selFlags |= SF_UsesEphemeral;
VdbeComment((v, "Distinct table"));
}
@@ -2630,7 +2666,8 @@ multiSelect(Parse * pParse, /* Parsing context */
if (dest.eDest == SRT_EphemTab) {
assert(p->pEList);
int nCols = p->pEList->nExpr;
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.iSDParm, nCols + 1);
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.reg_eph, nCols + 1);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, dest.iSDParm, 0, dest.reg_eph);
VdbeComment((v, "Destination temp"));
dest.eDest = SRT_Table;
}
@@ -2721,6 +2758,7 @@ multiSelect(Parse * pParse, /* Parsing context */
case TK_EXCEPT:
case TK_UNION:{
int unionTab; /* Cursor number of the temporary table holding result */
+ int reg_union;
u8 op = 0; /* One of the SRT_ operations to apply to self */
int priorOp; /* The SRT_ operation to apply to prior selects */
Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
@@ -2737,16 +2775,19 @@ multiSelect(Parse * pParse, /* Parsing context */
assert(p->pLimit == 0); /* Not allowed on leftward elements */
assert(p->pOffset == 0); /* Not allowed on leftward elements */
unionTab = dest.iSDParm;
+ reg_union = dest.reg_eph;
} else {
/* We will need to create our own temporary table to hold the
* intermediate results.
*/
unionTab = pParse->nTab++;
+ reg_union = ++pParse->nMem;
assert(p->pOrderBy == 0);
addr =
sqlite3VdbeAddOp2(v,
OP_OpenTEphemeral,
- unionTab, 0);
+ reg_union, 0);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, unionTab, 0, reg_union);
assert(p->addrOpenEphm[0] == -1);
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |=
@@ -2758,7 +2799,7 @@ multiSelect(Parse * pParse, /* Parsing context */
*/
assert(!pPrior->pOrderBy);
sqlite3SelectDestInit(&uniondest, priorOp,
- unionTab);
+ unionTab, reg_union);
iSub1 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, pPrior, &uniondest);
if (rc) {
@@ -2841,6 +2882,7 @@ multiSelect(Parse * pParse, /* Parsing context */
default:
assert(p->op == TK_INTERSECT); {
int tab1, tab2;
+ int reg_eph1, reg_eph2;
int iCont, iBreak, iStart;
Expr *pLimit, *pOffset;
int addr;
@@ -2852,12 +2894,15 @@ multiSelect(Parse * pParse, /* Parsing context */
* by allocating the tables we will need.
*/
tab1 = pParse->nTab++;
+ reg_eph1 = ++pParse->nMem;
tab2 = pParse->nTab++;
+ reg_eph2 = ++pParse->nMem;
assert(p->pOrderBy == 0);
addr =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab1,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph1,
0);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, tab1, 0, reg_eph1);
assert(p->addrOpenEphm[0] == -1);
p->addrOpenEphm[0] = addr;
findRightmost(p)->selFlags |= SF_UsesEphemeral;
@@ -2866,7 +2911,7 @@ multiSelect(Parse * pParse, /* Parsing context */
/* Code the SELECTs to our left into temporary table "tab1".
*/
sqlite3SelectDestInit(&intersectdest, SRT_Union,
- tab1);
+ tab1, reg_eph1);
iSub1 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, pPrior,
&intersectdest);
@@ -2877,8 +2922,9 @@ multiSelect(Parse * pParse, /* Parsing context */
/* Code the current SELECT into temporary table "tab2"
*/
addr =
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab2,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph2,
0);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, tab2, 0, reg_eph2);
assert(p->addrOpenEphm[1] == -1);
p->addrOpenEphm[1] = addr;
p->pPrior = 0;
@@ -2887,6 +2933,7 @@ multiSelect(Parse * pParse, /* Parsing context */
pOffset = p->pOffset;
p->pOffset = 0;
intersectdest.iSDParm = tab2;
+ intersectdest.reg_eph = reg_eph2;
iSub2 = pParse->iNextSelectId;
rc = sqlite3Select(pParse, p, &intersectdest);
testcase(rc != SQLITE_OK);
@@ -3075,7 +3122,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
case SRT_EphemTab:{
int regRec = sqlite3GetTempReg(parse);
int regCopy = sqlite3GetTempRange(parse, in->nSdst + 1);
- sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->iSDParm,
+ sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->reg_eph,
in->nSdst, regCopy + in->nSdst);
sqlite3VdbeAddOp3(v, OP_Copy, in->iSdst, regCopy,
in->nSdst - 1);
@@ -3083,7 +3130,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
in->nSdst + 1, regRec);
/* Set flag to save memory allocating one by malloc. */
sqlite3VdbeChangeP5(v, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, regRec);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, dest->reg_eph);
sqlite3ReleaseTempRange(parse, regCopy, in->nSdst + 1);
sqlite3ReleaseTempReg(parse, regRec);
break;
@@ -3099,7 +3146,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
in->nSdst);
sqlite3ExprCacheAffinityChange(parse, in->iSdst,
in->nSdst);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, dest->reg_eph);
sqlite3ReleaseTempReg(parse, r1);
break;
}
@@ -3417,8 +3464,8 @@ multiSelectOrderBy(Parse * pParse, /* Parsing context */
regAddrB = ++pParse->nMem;
regOutA = ++pParse->nMem;
regOutB = ++pParse->nMem;
- sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
- sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
+ sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA, -1);
+ sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB, -1);
/* Generate a coroutine to evaluate the SELECT statement to the
* left of the compound operator - the "A" select.
@@ -5261,7 +5308,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
/* Verify that all AggInfo registers are within the range specified by
* AggInfo.mnReg..AggInfo.mxReg
*/
- assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
+ assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
for (i = 0; i < pAggInfo->nColumn; i++) {
assert(pAggInfo->aCol[i].iMem >= pAggInfo->mnReg
&& pAggInfo->aCol[i].iMem <= pAggInfo->mxReg);
@@ -5287,8 +5334,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
pE->x.pList,
0);
sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- pFunc->iDistinct, 1, 0,
+ pFunc->reg_eph, 1, 0,
(char *)key_info, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen,
+ pFunc->iDistinct, 0, pFunc->reg_eph);
}
}
}
@@ -5347,8 +5396,8 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
addrNext = sqlite3VdbeMakeLabel(v);
testcase(nArg == 0); /* Error condition */
testcase(nArg > 1); /* Also an error */
- codeDistinct(pParse, pF->iDistinct, addrNext, 1,
- regAgg);
+ vdbe_insert_distinct(pParse, pF->iDistinct, pF->reg_eph,
+ addrNext, 1, regAgg);
}
if (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) {
struct coll *coll = NULL;
@@ -5679,7 +5728,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
VdbeComment((v, "%s", pItem->pTab->def->name));
pItem->addrFillSub = addrTop;
sqlite3SelectDestInit(&dest, SRT_Coroutine,
- pItem->regReturn);
+ pItem->regReturn, -1);
pItem->iSelectId = pParse->iNextSelectId;
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5717,7 +5766,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
pItem->pTab->def->name));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab,
- pItem->iCursor);
+ pItem->iCursor, ++pParse->nMem);
pItem->iSelectId = pParse->iNextSelectId;
sqlite3Select(pParse, pSub, &dest);
pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5796,6 +5845,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
if (sSort.pOrderBy) {
struct sql_key_info *key_info =
sql_expr_list_to_key_info(pParse, sSort.pOrderBy, 0);
+ sSort.reg_eph = ++pParse->nMem;
sSort.iECursor = pParse->nTab++;
/* Number of columns in transient table equals to number of columns in
* SELECT statement plus number of columns in ORDER BY statement
@@ -5807,9 +5857,10 @@ sqlite3Select(Parse * pParse, /* The parser context */
}
sSort.addrSortIndex =
sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- sSort.iECursor,
+ sSort.reg_eph,
nCols,
0, (char *)key_info, P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, sSort.iECursor, 0, sSort.reg_eph);
VdbeComment((v, "Sort table"));
} else {
sSort.addrSortIndex = -1;
@@ -5818,8 +5869,10 @@ sqlite3Select(Parse * pParse, /* The parser context */
/* If the output is destined for a temporary table, open that table.
*/
if (pDest->eDest == SRT_EphemTab) {
- sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->iSDParm,
+ sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->reg_eph,
pEList->nExpr + 1);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, pDest->iSDParm, 0,
+ pDest->reg_eph);
VdbeComment((v, "Output table"));
}
@@ -5833,20 +5886,25 @@ sqlite3Select(Parse * pParse, /* The parser context */
computeLimitRegisters(pParse, p, iEnd);
if (p->iLimit == 0 && sSort.addrSortIndex >= 0) {
sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
+ sqlite3VdbeChangeP1(v, sSort.addrSortIndex, sSort.iECursor);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
sSort.sortFlags |= SORTFLAG_UseSorter;
}
/* Open an ephemeral index to use for the distinct set.
*/
if (p->selFlags & SF_Distinct) {
- sDistinct.tabTnct = pParse->nTab++;
+ sDistinct.cur_eph = pParse->nTab++;
+ sDistinct.reg_eph = ++pParse->nMem;
struct sql_key_info *key_info =
sql_expr_list_to_key_info(pParse, p->pEList, 0);
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
- sDistinct.tabTnct,
+ sDistinct.reg_eph,
key_info->part_count,
0, (char *)key_info,
P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, sDistinct.cur_eph, 0,
+ sDistinct.reg_eph);
VdbeComment((v, "Distinct table"));
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
} else {
@@ -5885,7 +5943,16 @@ sqlite3Select(Parse * pParse, /* The parser context */
* into an OP_Noop.
*/
if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
+ /*
+ * To handle ordering two op-codes are
+ * emitted: OpenTEphemeral & IteratorOpen.
+ * sSort.addrSortIndex is address of
+ * first insn in a couple. To evict
+ * ephemral space, need to noop both
+ * op-codes.
+ */
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
}
/* Use the standard inner loop. */
@@ -6130,6 +6197,7 @@ sqlite3Select(Parse * pParse, /* The parser context */
) {
sSort.pOrderBy = 0;
sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
}
/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 53188e7..352f2ae 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2000,6 +2000,10 @@ struct AggInfo {
FuncDef *pFunc; /* The aggregate function implementation */
int iMem; /* Memory location that acts as accumulator */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
+ /**
+ * Register, holding ephemeral's space pointer.
+ */
+ int reg_eph;
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
};
@@ -2591,6 +2595,8 @@ struct SelectDest {
u8 eDest; /* How to dispose of the results. On of SRT_* above. */
char *zAffSdst; /* Affinity used when eDest==SRT_Set */
int iSDParm; /* A parameter used by the eDest disposal method */
+ /** Register containing ephemeral's space pointer. */
+ int reg_eph;
int iSdst; /* Base register where results are written */
int nSdst; /* Number of registers allocated */
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
@@ -3508,7 +3514,6 @@ Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
struct Table *
sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name);
-void sqlite3OpenTable(Parse *, int iCur, struct space *, int);
/**
* Generate code for a DELETE FROM statement.
*
@@ -3828,14 +3833,14 @@ vdbe_emit_constraint_checks(struct Parse *parse_context,
* @cursor_id.
*
* @param v Virtual database engine.
- * @param cursor_id Primary index cursor.
+ * @param space Pointer to space object.
* @param raw_data_reg Register with raw data to insert.
* @param tuple_len Number of registers to hold the tuple.
* @param on_conflict On conflict action.
*/
void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
- uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+ int raw_data_reg, uint32_t tuple_len,
enum on_conflict_action on_conflict);
void
@@ -4529,7 +4534,7 @@ void sqlite3StrAccumAppendAll(StrAccum *, const char *);
void sqlite3AppendChar(StrAccum *, int, char);
char *sqlite3StrAccumFinish(StrAccum *);
void sqlite3StrAccumReset(StrAccum *);
-void sqlite3SelectDestInit(SelectDest *, int, int);
+void sqlite3SelectDestInit(SelectDest *, int, int, int);
Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
int sqlite3ExprCheckIN(Parse *, Expr *);
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index 8017742..daab3c8 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -97,9 +97,21 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
const char *zOldName, const char *zNewName);
-/* Interface for ephemeral tables. */
-int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
- struct sql_key_info *key_info);
+/**
+ * Create ephemeral space. Features of ephemeral spaces: id == 0,
+ * name == "ephemeral", memtx engine (in future it can be changed,
+ * but now only memtx engine is supported), primary index which
+ * covers all fields and no secondary indexes, given field number
+ * and collation sequence. All fields are scalar and nullable.
+ *
+ * @param field_count Number of fields in ephemeral space.
+ * @param key_info Keys description for new ephemeral space.
+ *
+ * @retval Pointer to created space, NULL if error.
+ */
+struct space *
+sql_ephemeral_space_create(uint32_t filed_count, struct sql_key_info *key_info);
+
/**
* Insert tuple into ephemeral space.
* In contrast to ordinary spaces, there is no need to create and
@@ -117,7 +129,18 @@ int tarantoolSqlite3EphemeralDelete(BtCursor * pCur);
int tarantoolSqlite3EphemeralCount(BtCursor * pCur, i64 * pnEntry);
int tarantoolSqlite3EphemeralDrop(BtCursor * pCur);
int tarantoolSqlite3EphemeralClearTable(BtCursor * pCur);
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor * pCur, uint32_t fieldno,
+
+/**
+ * Extract maximum integer value from ephemeral space.
+ * If index is empty - return 0 in max_id and success status.
+ *
+ * @param space Pointer to ephemeral space.
+ * @param fieldno Number of field from fetching tuple.
+ * @param[out] max_id Fetched max value.
+ *
+ * @retval 0 on success, -1 otherwise.
+ */
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
uint64_t * max_id);
/**
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index c6bd15b..c38f9cd 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -205,15 +205,9 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
if (db->mallocFailed)
goto cleanup;
- int cursor = parse->nTab++;
struct space *_trigger = space_by_id(BOX_TRIGGER_ID);
assert(_trigger != NULL);
- vdbe_emit_open_cursor(parse, cursor, 0, _trigger);
- /*
- * makerecord(cursor(iRecord),
- * [reg(first_col), reg(first_col+1)]).
- */
int first_col = parse->nMem + 1;
parse->nMem += 3;
int record = ++parse->nMem;
@@ -245,10 +239,10 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
sqlite3VdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
sqlite3VdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor, record);
+ sqlite3VdbeAddOp4(v, OP_IdxInsert, record, 0, 0,
+ (char *)_trigger, P4_SPACEPTR);
sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
- sqlite3VdbeAddOp1(v, OP_Close, cursor);
sql_set_multi_write(parse, false);
} else {
@@ -694,7 +688,7 @@ codeTriggerProgram(Parse * pParse, /* The parser context */
SelectDest sDest;
Select *pSelect =
sqlite3SelectDup(db, pStep->pSelect, 0);
- sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
+ sqlite3SelectDestInit(&sDest, SRT_Discard, 0, -1);
sqlite3Select(pParse, pSelect, &sDest);
sql_select_delete(db, pSelect);
break;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 730872f..0e2d0fd 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -222,13 +222,15 @@ sqlite3Update(Parse * pParse, /* The parser context */
* an ephemeral table.
*/
uint32_t pk_part_count;
+ struct space *space;
if (is_view) {
sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
/* Number of columns from SELECT plus ID.*/
pk_part_count = nKey = def->field_count + 1;
} else {
- vdbe_emit_open_cursor(pParse, pk_cursor, 0,
- space_by_id(pTab->def->id));
+ space = pTab->space;
+ assert(space != NULL);
+ vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
pk_part_count = pPk->def->key_def->part_count;
}
@@ -242,11 +244,12 @@ sqlite3Update(Parse * pParse, /* The parser context */
int iPk = pParse->nMem + 1;
pParse->nMem += pk_part_count;
regKey = ++pParse->nMem;
+ int reg_eph = ++pParse->nMem;
iEph = pParse->nTab++;
sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
/* Address of the OpenEphemeral instruction. */
- int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iEph,
+ int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
pk_part_count);
pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
WHERE_ONEPASS_DESIRED, pk_cursor);
@@ -276,9 +279,12 @@ sqlite3Update(Parse * pParse, /* The parser context */
pPk->def);
sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
regKey, zAff, pk_part_count);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
- /* Set flag to save memory allocating one by malloc. */
+ /*
+ * Set flag to save memory allocating one by
+ * malloc.
+ */
sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
}
/* End the database scan loop.
*/
@@ -304,6 +310,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
}
} else {
labelContinue = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, iEph, 0, reg_eph);
sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
sqlite3VdbeAddOp4Int(v, OP_NotFound, pk_cursor, labelContinue,
@@ -437,7 +444,7 @@ sqlite3Update(Parse * pParse, /* The parser context */
sqlite3VdbeJumpHere(v, addr1);
if (hasFK)
fkey_emit_check(pParse, pTab, 0, regNewPk, aXRef);
- vdbe_emit_insertion_completion(v, pk_cursor, regNew,
+ vdbe_emit_insertion_completion(v, space, regNew,
pTab->def->field_count,
on_error);
/*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 00ffb0c..7c1015c 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -3054,51 +3054,37 @@ case OP_TTransaction: {
break;
}
-/* Opcode: OpenRead P1 P2 P3 P4 P5
+/* Opcode: IteratorReopen P1 P2 P3 P4 P5
* Synopsis: index id = P2, space ptr = P4
*
- * Open a cursor for a space specified by pointer in P4 and index
- * id in P2. Give the new cursor an identifier of P1. The P1
- * values need not be contiguous but all P1 values should be
- * small integers. It is an error for P1 to be negative.
- */
-/* Opcode: ReopenIdx P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
- *
- * The ReopenIdx opcode works exactly like OpenRead except that
- * it first checks to see if the cursor on P1 is already open
+ * The IteratorReopen opcode works exactly like IteratorOpen except
+ * that it first checks to see if the cursor on P1 is already open
* with the same index and if it is this opcode becomes a no-op.
- * In other words, if the cursor is already open, do not reopen it.
+ * In other words, if the cursor is already open, do not reopen
+ * it.
*
- * The ReopenIdx opcode may only be used with P5 == 0.
+ * The IteratorReopen opcode may only be used with P5 == 0.
*/
-/* Opcode: OpenWrite P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
+/* Opcode: IteratorOpen P1 P2 P3 P4 P5
+ * Synopsis: index id = P2, space ptr = P4 or reg[P3]
*
- * For now, OpenWrite is an alias for OpenRead.
- * It exists just due legacy reasons and should be removed:
- * it isn't neccessary to open cursor to make insertion or
- * deletion.
- */
-case OP_ReopenIdx: {
- int nField;
- int p2;
- VdbeCursor *pCur;
- BtCursor *pBtCur;
-
- assert(pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
- pCur = p->apCsr[pOp->p1];
- p2 = pOp->p2;
- if (pCur && pCur->uc.pCursor->space == pOp->p4.space &&
- pCur->uc.pCursor->index->def->iid == (uint32_t)p2)
+ * Open a cursor for a space specified by pointer in P4 and index
+ * id in P2. Give the new cursor an identifier of P1. The P1
+ * values need not be contiguous but all P1 values should be
+ * small integers. It is an error for P1 to be negative.
+ * If P4 was not set, then P3 supposed to be the register
+ * containing space pointer.
+ */
+case OP_IteratorReopen: {
+ assert(pOp->p5 == 0);
+ struct VdbeCursor *cur = p->apCsr[pOp->p1];
+ if (cur != NULL && cur->uc.pCursor->space == pOp->p4.space &&
+ cur->uc.pCursor->index->def->iid == (uint32_t)pOp->p2)
goto open_cursor_set_hints;
/* If the cursor is not currently open or is open on a different
- * index, then fall through into OP_OpenRead to force a reopen
+ * index, then fall through into OP_OpenCursor to force a reopen
*/
-case OP_OpenRead:
-case OP_OpenWrite:
-
- assert(pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
+case OP_IteratorOpen:
if (box_schema_version() != p->schema_ver &&
(pOp->p5 & OPFLAG_SYSTEMSP) == 0) {
p->expired = 1;
@@ -3107,66 +3093,60 @@ case OP_OpenWrite:
"need to re-compile SQL statement");
goto abort_due_to_error;
}
- p2 = pOp->p2;
- struct space *space = pOp->p4.space;
+ struct space *space;
+ if (pOp->p4type == P4_SPACEPTR)
+ space = pOp->p4.space;
+ else
+ space = aMem[pOp->p3].u.p;
assert(space != NULL);
- struct index *index = space_index(space, p2);
+ struct index *index = space_index(space, pOp->p2);
assert(index != NULL);
- /*
- * Since Tarantool iterator provides the full tuple,
- * we need a number of fields as wide as the table itself.
- * Otherwise, not enough slots for row parser cache are
- * allocated in VdbeCursor object.
- */
- nField = space->def->field_count;
- assert(pOp->p1>=0);
- assert(nField>=0);
- pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_TARANTOOL);
- if (pCur==0) goto no_mem;
- pCur->nullRow = 1;
- pBtCur = pCur->uc.pCursor;
- pBtCur->curFlags |= BTCF_TaCursor;
- pBtCur->space = space;
- pBtCur->index = index;
- pBtCur->eState = CURSOR_INVALID;
+ assert(pOp->p1 >= 0);
+ cur = allocateCursor(p, pOp->p1,
+ space->def->exact_field_count == 0 ?
+ space->def->field_count :
+ space->def->exact_field_count,
+ CURTYPE_TARANTOOL);
+ if (cur == NULL)
+ goto no_mem;
+ struct BtCursor *bt_cur = cur->uc.pCursor;
+ bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
+ BTCF_TaCursor;
+ bt_cur->space = space;
+ bt_cur->index = index;
+ bt_cur->eState = CURSOR_INVALID;
/* Key info still contains sorter order and collation. */
- pCur->key_def = index->def->key_def;
-
+ cur->key_def = index->def->key_def;
+ cur->nullRow = 1;
open_cursor_set_hints:
- pCur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
- if (rc) goto abort_due_to_error;
+ cur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
+ if (rc != 0)
+ goto abort_due_to_error;
break;
}
/**
* Opcode: OpenTEphemeral P1 P2 * P4 *
* Synopsis:
- * @param P1 index of new cursor to be created.
+ * @param P1 register, where pointer to new space is stored.
* @param P2 number of columns in a new table.
* @param P4 key def for new table, NULL is allowed.
*
- * This opcode creates Tarantool's ephemeral table and sets cursor P1 to it.
+ * This opcode creates Tarantool's ephemeral table and stores pointer
+ * to it into P1 register.
*/
case OP_OpenTEphemeral: {
- VdbeCursor *pCx;
- BtCursor *pBtCur;
assert(pOp->p1 >= 0);
assert(pOp->p2 > 0);
assert(pOp->p4type != P4_KEYINFO || pOp->p4.key_info != NULL);
- pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_TARANTOOL);
- if (pCx == 0) goto no_mem;
- pCx->nullRow = 1;
+ struct space *space = sql_ephemeral_space_create(pOp->p2,
+ pOp->p4.key_info);
- pBtCur = pCx->uc.pCursor;
- /* Ephemeral spaces don't have space_id */
- pBtCur->eState = CURSOR_INVALID;
- pBtCur->curFlags = BTCF_TEphemCursor;
-
- rc = tarantoolSqlite3EphemeralCreate(pCx->uc.pCursor, pOp->p2,
- pOp->p4.key_info);
- pCx->key_def = pCx->uc.pCursor->index->def->key_def;
- if (rc) goto abort_due_to_error;
+ if (space == NULL)
+ goto abort_due_to_error;
+ aMem[pOp->p1].u.p = space;
+ aMem[pOp->p1].flags = MEM_Ptr;
break;
}
@@ -3708,23 +3688,21 @@ case OP_NextSequenceId: {
/* Opcode: NextIdEphemeral P1 P2 P3 * *
* Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]})
*
- * This opcode works in the same way as OP_NextId does, except it is
- * only applied for ephemeral tables. The difference is in the fact that
- * all ephemeral tables don't have space_id (to be more precise it equals to zero).
+ * This opcode works in the same way as OP_NextId does, except it
+ * is only applied for ephemeral tables. The difference is in the
+ * fact that all ephemeral tables don't have space_id (to be more
+ * precise it equals to zero). This opcode uses register P1 to
+ * fetch pointer to epehemeral space.
*/
case OP_NextIdEphemeral: {
- VdbeCursor *pC;
- int p2;
- pC = p->apCsr[pOp->p1];
- p2 = pOp->p2;
+ struct space *space = (struct space*)p->aMem[pOp->p1].u.p;
+ int p2 = pOp->p2;
+ assert(space != NULL);
pOut = &aMem[pOp->p3];
-
- assert(pC->uc.pCursor->curFlags & BTCF_TEphemCursor);
-
- rc = tarantoolSqlite3EphemeralGetMaxId(pC->uc.pCursor, p2,
+ rc = tarantoolSqlite3EphemeralGetMaxId(space, p2,
(uint64_t *) &pOut->u.i);
- if (rc) goto abort_due_to_error;
-
+ if (rc != 0)
+ goto abort_due_to_error;
pOut->u.i += 1;
pOut->flags = MEM_Int;
break;
@@ -4242,83 +4220,91 @@ case OP_Next: /* jump */
goto check_for_interrupt;
}
-/* Opcode: IdxInsert P1 P2 * * P5
+/* Opcode: SorterInsert P1 P2 * * *
* Synopsis: key=r[P2]
*
- * @param P1 Index of a space cursor.
- * @param P2 Index of a register with MessagePack data to insert.
+ * Register P2 holds an SQL index key made using the
+ * MakeRecord instructions. This opcode writes that key
+ * into the sorter P1. Data for the entry is nil.
+ */
+case OP_SorterInsert: { /* in2 */
+ assert(pOp->p1 >= 0 && pOp->p1 < p->nCursor);
+ struct VdbeCursor *cursor = p->apCsr[pOp->p1];
+ assert(cursor != NULL);
+ assert(isSorter(cursor));
+ pIn2 = &aMem[pOp->p2];
+ assert((pIn2->flags & MEM_Blob) != 0);
+ rc = ExpandBlob(pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ rc = sqlite3VdbeSorterWrite(cursor, pIn2);
+ if (rc != 0)
+ goto abort_due_to_error;
+ break;
+}
+
+/* Opcode: IdxInsert P1 P2 * P4 P5
+ * Synopsis: key=r[P1]
+ *
+ * @param P1 Index of a register with MessagePack data to insert.
+ * @param P2 If P4 is not set, then P2 is register containing pointer
+ * to space to insert into.
+ * @param P4 Pointer to the struct space to insert to.
* @param P5 Flags. If P5 contains OPFLAG_NCHANGE, then VDBE
* accounts the change in a case of successful insertion in
* nChange counter. If P5 contains OPFLAG_OE_IGNORE, then
* we are processing INSERT OR INGORE statement. Thus, in
* case of conflict we don't raise an error.
*/
-/* Opcode: IdxReplace P1 P2 * * P5
- * Synopsis: key=r[P2]
+/* Opcode: IdxReplace2 P1 * * P4 P5
+ * Synopsis: key=r[P1]
*
* This opcode works exactly as IdxInsert does, but in Tarantool
* internals it invokes box_replace() instead of box_insert().
*/
-/* Opcode: SorterInsert P1 P2 * * *
- * Synopsis: key=r[P2]
- *
- * Register P2 holds an SQL index key made using the
- * MakeRecord instructions. This opcode writes that key
- * into the sorter P1. Data for the entry is nil.
- */
-case OP_SorterInsert: /* in2 */
case OP_IdxReplace:
-case OP_IdxInsert: { /* in2 */
- VdbeCursor *pC;
-
- assert(pOp->p1>=0 && pOp->p1<p->nCursor);
- pC = p->apCsr[pOp->p1];
- assert(pC!=0);
- assert(isSorter(pC)==(pOp->opcode==OP_SorterInsert));
- pIn2 = &aMem[pOp->p2];
- assert(pIn2->flags & MEM_Blob);
- if (pOp->p5 & OPFLAG_NCHANGE) p->nChange++;
- assert(pC->eCurType==CURTYPE_TARANTOOL || pOp->opcode==OP_SorterInsert);
+case OP_IdxInsert: {
+ pIn2 = &aMem[pOp->p1];
+ assert((pIn2->flags & MEM_Blob) != 0);
+ if (pOp->p5 & OPFLAG_NCHANGE)
+ p->nChange++;
rc = ExpandBlob(pIn2);
- if (rc) goto abort_due_to_error;
- if (pOp->opcode==OP_SorterInsert) {
- rc = sqlite3VdbeSorterWrite(pC, pIn2);
- } else {
- BtCursor *pBtCur = pC->uc.pCursor;
- if (pBtCur->curFlags & BTCF_TaCursor) {
- /* Make sure that memory has been allocated on region. */
- assert(aMem[pOp->p2].flags & MEM_Ephem);
- if (pOp->opcode == OP_IdxInsert)
- rc = tarantoolSqlite3Insert(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
- else
- rc = tarantoolSqlite3Replace(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
- } else if (pBtCur->curFlags & BTCF_TEphemCursor) {
- rc = tarantoolSqlite3EphemeralInsert(pBtCur->space,
- pIn2->z,
- pIn2->z + pIn2->n);
+ if (rc != 0)
+ goto abort_due_to_error;
+ struct space *space;
+ if (pOp->p4type == P4_SPACEPTR)
+ space = pOp->p4.space;
+ else
+ space = aMem[pOp->p2].u.p;
+ assert(space != NULL);
+ if (space->def->id != 0) {
+ /* Make sure that memory has been allocated on region. */
+ assert(aMem[pOp->p1].flags & MEM_Ephem);
+ if (pOp->opcode == OP_IdxInsert) {
+ rc = tarantoolSqlite3Insert(space, pIn2->z,
+ pIn2->z + pIn2->n);
} else {
- unreachable();
+ rc = tarantoolSqlite3Replace(space, pIn2->z,
+ pIn2->z + pIn2->n);
}
- pC->cacheStatus = CACHE_STALE;
+ } else {
+ rc = tarantoolSqlite3EphemeralInsert(space, pIn2->z,
+ pIn2->z + pIn2->n);
}
if (pOp->p5 & OPFLAG_OE_IGNORE) {
/* Ignore any kind of failes and do not raise error message */
rc = SQLITE_OK;
/* If we are in trigger, increment ignore raised counter */
- if (p->pFrame) {
+ if (p->pFrame)
p->ignoreRaised++;
- }
} else if (pOp->p5 & OPFLAG_OE_FAIL) {
p->errorAction = ON_CONFLICT_ACTION_FAIL;
} else if (pOp->p5 & OPFLAG_OE_ROLLBACK) {
p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
}
- if (rc) goto abort_due_to_error;
+ if (rc != 0)
+ goto abort_due_to_error;
break;
}
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 01dcc49..6a1bea0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4569,12 +4569,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
/* Do nothing */
} else if ((pLoop->wsFlags & WHERE_IDX_ONLY) == 0 &&
(wctrlFlags & WHERE_OR_SUBCLAUSE) == 0) {
- int op = OP_OpenRead;
- if (pWInfo->eOnePass != ONEPASS_OFF) {
- op = OP_OpenWrite;
+ if (pWInfo->eOnePass != ONEPASS_OFF)
pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
- };
- sqlite3OpenTable(pParse, pTabItem->iCursor, space, op);
+ assert(space->index_count > 0);
+ vdbe_emit_open_cursor(pParse, pTabItem->iCursor, 0,
+ space);
+ VdbeComment((v, "%s", space->def->name));
assert(pTabItem->iCursor == pLevel->iTabCur);
testcase(pWInfo->eOnePass == ONEPASS_OFF
&& pTab->nCol == BMS - 1);
@@ -4591,7 +4591,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
if (pLoop->wsFlags & WHERE_INDEXED) {
struct index_def *idx_def = pLoop->index_def;
int iIndexCur;
- int op = OP_OpenRead;
+ int op;
/* Check if index is primary. Either of
* points should be true:
* 1. struct Index is non-NULL and is
@@ -4636,12 +4636,12 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
}
}
assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
- op = OP_OpenWrite;
+ op = OP_IteratorOpen;
pWInfo->aiCurOnePass[1] = iIndexCur;
} else if (iAuxArg
&& (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
iIndexCur = iAuxArg;
- op = OP_ReopenIdx;
+ op = OP_IteratorReopen;
} else {
iIndexCur = pParse->nTab++;
}
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 8a3f2ac..4657055 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -416,7 +416,7 @@ struct WhereInfo {
ExprList *pOrderBy; /* The ORDER BY clause or NULL */
ExprList *pDistinctSet; /* DISTINCT over all these values */
LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
- int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
+ int aiCurOnePass[2]; /* OP_IteratorOpen cursors for the ONEPASS opt */
int iContinue; /* Jump here to continue with next record */
int iBreak; /* Jump here to break out of the loop */
int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index e2aaca3..8b16119 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1341,7 +1341,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
- int regRowset = 0; /* Register for RowSet object */
+ int cur_row_set = 0;
+ int reg_row_set = 0;
int regPk = 0; /* Register holding PK */
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
int iRetInit; /* Address of regReturn init */
@@ -1399,9 +1400,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
* called on an uninitialized cursor.
*/
if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
- regRowset = pParse->nTab++;
+ cur_row_set = pParse->nTab++;
+ reg_row_set = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
- regRowset, pk_part_count);
+ reg_row_set, pk_part_count);
+ sqlite3VdbeAddOp3(v, OP_IteratorOpen, cur_row_set, 0,
+ reg_row_set);
sql_vdbe_set_p4_key_def(pParse, pk_key_def);
regPk = ++pParse->nMem;
}
@@ -1534,7 +1538,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
if (iSet) {
jmp1 = sqlite3VdbeAddOp4Int
(v, OP_Found,
- regRowset, 0,
+ cur_row_set, 0,
r,
pk_part_count);
VdbeCoverage(v);
@@ -1545,7 +1549,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about t
r, pk_part_count, regPk);
sqlite3VdbeAddOp2
(v, OP_IdxInsert,
- regRowset, regPk);
+ regPk, reg_row_set);
}
/* Release the array of temp registers */
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-04 7:16 ` Kirill Yukhin
@ 2018-10-04 10:45 ` n.pettik
2018-10-04 11:48 ` Kirill Yukhin
0 siblings, 1 reply; 9+ messages in thread
From: n.pettik @ 2018-10-04 10:45 UTC (permalink / raw)
To: tarantool-patches; +Cc: Kirill Yukhin
> Hello Nikita!
> Thanks for review. All nits applied.
> Updated patch in the bottom. Branch force-pushed.
IDK whether it is outlined in our review policy or not, but it is quite
hard to review each time the whole patch again. So, it would be nice
if you attached intermediate diff (i.e. diff between two versions of patch)
in order to review only parts which have changed. Or, inline diff for
each comment/remark.
>>>> Finally, now I think about renaming CursorOpen to IteratorOpen: in fact after your
>>>> patch cursor has turned almost in wrapper around index iterator. Such naming
>>>> would underline the fact that ‘cursor’ is used only for read iterations over the space.
>>> Yes, sure. Renamed.
>>
>> You forgot to update name of opcode in commit message.
>> Also, you deleted not all mentions of OP_Cursor(Re)Open - just grep it
Still, I can grep OP_OpenWrite. Eliminate it completely pls.
Consider code style fixes:
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 929d3c814..353b0af9f 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -86,7 +86,7 @@ struct SortCtx {
ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
int nOBSat; /* Number of ORDER BY terms satisfied by indices */
int iECursor; /* Cursor number for the sorter */
- /* Register, containing pointer to ephemeral space. */
+ /** Register, containing pointer to ephemeral space. */
int reg_eph;
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
@@ -1005,7 +1005,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
regPrev = pParse->nMem + 1;
pParse->nMem += nResultCol;
- /* Actually, for DISTINCT handling
+ /*
+ * Actually, for DISTINCT handling
* two op-codes were emitted here:
* OpenTEphemeral & IteratorOpen.
* So, we need to Noop one and
@@ -1061,7 +1062,7 @@ selectInnerLoop(Parse * pParse, /* The parser context */
}
case WHERE_DISTINCT_UNIQUE:{
- /**
+ /*
* To handle DISTINCT two op-codes are
* emitted: OpenTEphemeral & IteratorOpen.
* addrTnct is address of first insn in
Except for these nits, patch LGTM.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-04 10:45 ` n.pettik
@ 2018-10-04 11:48 ` Kirill Yukhin
0 siblings, 0 replies; 9+ messages in thread
From: Kirill Yukhin @ 2018-10-04 11:48 UTC (permalink / raw)
To: n.pettik; +Cc: tarantool-patches
Hello,
On 04 окт 13:45, n.pettik wrote:
> > Hello Nikita!
> > Thanks for review. All nits applied.
> > Updated patch in the bottom. Branch force-pushed.
>
> IDK whether it is outlined in our review policy or not, but it is quite
> hard to review each time the whole patch again. So, it would be nice
> if you attached intermediate diff (i.e. diff between two versions of patch)
> in order to review only parts which have changed. Or, inline diff for
> each comment/remark.
Here it is:
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 929d3c8..d28b3f1 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1005,7 +1005,8 @@ selectInnerLoop(Parse * pParse, /* The parser context */
regPrev = pParse->nMem + 1;
pParse->nMem += nResultCol;
- /* Actually, for DISTINCT handling
+ /*
+ * Actually, for DISTINCT handling
* two op-codes were emitted here:
* OpenTEphemeral & IteratorOpen.
* So, we need to Noop one and
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index d28b3f1..b4f7a2b 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -86,7 +86,7 @@ struct SortCtx {
ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
int nOBSat; /* Number of ORDER BY terms satisfied by indices */
int iECursor; /* Cursor number for the sorter */
- /* Register, containing pointer to ephemeral space. */
+ /** Register, containing pointer to ephemeral space. */
int reg_eph;
int regReturn; /* Register holding block-output return address */
int labelBkOut; /* Start label for the block-output subroutine */
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 352f2ae..49aef33 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -3367,7 +3367,7 @@ void sqlite3EndTable(Parse *, Token *, Select *);
/**
* 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.
+ * which is passes to OP_IteratorOpen as an argument.
*
* @param parse_context Parse context.
* @param cursor Number of cursor to be created.
> >>>> Finally, now I think about renaming CursorOpen to IteratorOpen: in fact after your
> >>>> patch cursor has turned almost in wrapper around index iterator. Such naming
> >>>> would underline the fact that ‘cursor’ is used only for read iterations over the space.
> >>> Yes, sure. Renamed.
> >>
> >> You forgot to update name of opcode in commit message.
> >> Also, you deleted not all mentions of OP_Cursor(Re)Open - just grep it
>
> Still, I can grep OP_OpenWrite. Eliminate it completely pls.
Fixed.
> Consider code style fixes:
Added.
I'll commit it to 2.0 branch today, thanks!
--
Regards, Kirill Yukhin
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-01 10:31 [tarantool-patches] [PATCH] sql: refactor SQL cursor to remove write ones Kirill Yukhin
2018-10-02 11:56 ` [tarantool-patches] " n.pettik
@ 2018-10-04 12:00 ` Kirill Yukhin
2018-10-05 4:39 ` Kirill Yukhin
1 sibling, 1 reply; 9+ messages in thread
From: Kirill Yukhin @ 2018-10-04 12:00 UTC (permalink / raw)
To: korablev; +Cc: tarantool-patches
Hello,
On 01 окт 13:31, Kirill Yukhin wrote:
> In Tarantool opening and positioning cursor for writing
> have no sense. So refactor SQL code, to perform:
> - Creation of ephemeral table w/o any cursors machinery.
> No op-code returns register which contains plain pointer
> to the ephemeral space.
> - OpenRead/OpenWrite opcodes were replaced with single
> CursorOpen op-code, which establishes new cursor w/
> intention to sebsequent read from the space. This opcode
> accepts both plain pointer (in P4 operand) and register
> which contains pointer (inn P3) to the ephemeral space.
> This query scheduler and DML routines thoroughly.
>
> Closes #3182
> Part of #2362
> ---
> Branch: https://github.com/tarantool/tarantool/commits/kyukhin/gh-3182-repair-cursors-2
> Issue: https://github.com/tarantool/tarantool/issues/3182
Patch was checked into 2.0 branch.
--
Regards, Kirill Yukhin
^ permalink raw reply [flat|nested] 9+ messages in thread
* [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
2018-10-04 12:00 ` Kirill Yukhin
@ 2018-10-05 4:39 ` Kirill Yukhin
0 siblings, 0 replies; 9+ messages in thread
From: Kirill Yukhin @ 2018-10-05 4:39 UTC (permalink / raw)
To: korablev; +Cc: tarantool-patches
Hello,
On 04 окт 15:00, Kirill Yukhin wrote:
> Hello,
> On 01 окт 13:31, Kirill Yukhin wrote:
> > In Tarantool opening and positioning cursor for writing
> > have no sense. So refactor SQL code, to perform:
> > - Creation of ephemeral table w/o any cursors machinery.
> > No op-code returns register which contains plain pointer
> > to the ephemeral space.
> > - OpenRead/OpenWrite opcodes were replaced with single
> > CursorOpen op-code, which establishes new cursor w/
> > intention to sebsequent read from the space. This opcode
> > accepts both plain pointer (in P4 operand) and register
> > which contains pointer (inn P3) to the ephemeral space.
> > This query scheduler and DML routines thoroughly.
> >
> > Closes #3182
> > Part of #2362
> > ---
> > Branch: https://github.com/tarantool/tarantool/commits/kyukhin/gh-3182-repair-cursors-2
> > Issue: https://github.com/tarantool/tarantool/issues/3182
> Patch was checked into 2.0 branch.
Seems like the patch broken testing in release mode. This is
due to non-initialized local variable.
Patch in the bottom fixes that. Comitted as obvious into 2.0
branch.
--
Regard, Kirill Yukhin
commit cebcf21114f50141e8aa0a72f30ea47dfbfef16c
Author: Kirill Yukhin <kyukhin@tarantool.org>
Date: Fri Oct 5 07:19:56 2018 +0300
sql: fix local var initialization in select planner
Not initialized var led to crashes in non-debug
modes.
---
src/box/sql/where.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 6a1bea0..520a847 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4591,7 +4591,7 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
if (pLoop->wsFlags & WHERE_INDEXED) {
struct index_def *idx_def = pLoop->index_def;
int iIndexCur;
- int op;
+ int op = OP_IteratorOpen;
/* Check if index is primary. Either of
* points should be true:
* 1. struct Index is non-NULL and is
@@ -4636,7 +4636,6 @@ sqlite3WhereBegin(Parse * pParse, /* The parser context */
}
}
assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
- op = OP_IteratorOpen;
pWInfo->aiCurOnePass[1] = iIndexCur;
} else if (iAuxArg
&& (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2018-10-05 4:39 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-10-01 10:31 [tarantool-patches] [PATCH] sql: refactor SQL cursor to remove write ones Kirill Yukhin
2018-10-02 11:56 ` [tarantool-patches] " n.pettik
2018-10-03 9:57 ` Kirill Yukhin
2018-10-04 2:21 ` n.pettik
2018-10-04 7:16 ` Kirill Yukhin
2018-10-04 10:45 ` n.pettik
2018-10-04 11:48 ` Kirill Yukhin
2018-10-04 12:00 ` Kirill Yukhin
2018-10-05 4:39 ` Kirill Yukhin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox