From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 71AF027B67 for ; Thu, 4 Oct 2018 03:16:44 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id AQnBZMgYQAoX for ; Thu, 4 Oct 2018 03:16:44 -0400 (EDT) Received: from smtp55.i.mail.ru (smtp55.i.mail.ru [217.69.128.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 3C89C27324 for ; Thu, 4 Oct 2018 03:16:43 -0400 (EDT) Date: Thu, 4 Oct 2018 10:16:37 +0300 From: Kirill Yukhin Subject: [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones Message-ID: <20181004071637.jncwrcmp62r7zucn@tarantool.org> References: <4aec5b52bc28f47ad2af0ac6f4e64be0712f378b.1538389679.git.kyukhin@tarantool.org> <7816B3E4-321F-446F-B19A-ED3E241D5D86@tarantool.org> <20181003095759.2cdk54garm4na3f3@tarantool.org> <4A0EC663-775A-4D4B-8458-A27ADB72B71C@tarantool.org> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: <4A0EC663-775A-4D4B-8458-A27ADB72B71C@tarantool.org> Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-help: List-unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-subscribe: List-owner: List-post: List-archive: To: "n.pettik" Cc: tarantool-patches@freelists.org 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 > > 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 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->p1nCursor); - 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 */