[tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
Kirill Yukhin
kyukhin at tarantool.org
Thu Oct 4 10:16:37 MSK 2018
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 at 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 at 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 */
More information about the Tarantool-patches
mailing list