Tarantool development patches archive
 help / color / mirror / Atom feed
From: Kirill Yukhin <kyukhin@tarantool.org>
To: "n.pettik" <korablev@tarantool.org>
Cc: tarantool-patches@freelists.org
Subject: [tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones
Date: Thu, 4 Oct 2018 10:16:37 +0300	[thread overview]
Message-ID: <20181004071637.jncwrcmp62r7zucn@tarantool.org> (raw)
In-Reply-To: <4A0EC663-775A-4D4B-8458-A27ADB72B71C@tarantool.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 <kyukhin@tarantool.org>
> > Date:   Tue Sep 25 19:01:10 2018 +0300
> > 
> >    sql: refactor SQL cursor to remove write ones
> > 
> >    In Tarantool opening and positioning cursor for writing
> >    have no sense. So refactor SQL code, to perform:
> >      - Creation of ephemeral table w/o any cursors machinery.
> >        No op-code returns register which contains plain pointer
> >        to the ephemeral space.
> >      - OpenRead/OpenWrite opcodes were replaced with single
> >        CursorOpen op-code, which establishes new cursor w/
> 
> IteratorOpen
> 
> >        intention to subsequent read from the space. This opcode
> >        accepts both plain pointer (in P4 operand) and register
> >        which contains pointer (inn P3) to the ephemeral space.
> 
> Typo: in P3
> 
> > diff --git a/src/box/sql.c b/src/box/sql.c
> > index ab4a587..f3836f2 100644
> > --- a/src/box/sql.c
> > +++ b/src/box/sql.c
> > @@ -355,31 +355,15 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
> > 	return SQLITE_OK;
> > }
> > 
> > -/**
> > - * Create ephemeral space and set cursor to the first entry. Features of
> > - * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
> > - * can be changed, but now only memtx engine is supported), primary index
> > - * which covers all fields and no secondary indexes, given field number and
> > - * collation sequence. All fields are scalar and nullable.
> > - *
> > - * @param pCur Cursor which will point to the new ephemeral space.
> > - * @param field_count Number of fields in ephemeral space.
> > - * @param key_info Keys description for new ephemeral space.
> > - *
> > - * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
> > - */
> > -int
> > -tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
> > -				struct sql_key_info *key_info)
> > +struct space *
> > +sql_ephemeral_space_create(uint32_t field_count,
> > +			   struct sql_key_info *key_info)
> 
> Nit: you can fit now args in one line.
Done.

> > diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
> > index 9aa7058..17e1c32 100644
> > --- a/src/box/sql/delete.c
> > +++ b/src/box/sql/delete.c
> > @@ -69,7 +69,7 @@ sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name)
> > 
> > void
> > sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
> > -		     int cursor)
> > +		     int cursor, int reg_eph)
> 
> You slightly misunderstood me: you don’t need to pass arg reg_eph:
> you already have struct Parse *parse, so can fetch value for register
> inside function.
Got it. Done.

> > @@ -4061,7 +4064,8 @@ sqlite3ExprCodeTarget(Parse * pParse, Expr * pExpr, int target)
> > 			int n;
> > 			if (pExpr->pLeft->iTable == 0) {
> > 				pExpr->pLeft->iTable =
> > -				    sqlite3CodeSubselect(pParse, pExpr->pLeft, 0);
> > +				    sqlite3CodeSubselect(pParse, pExpr->pLeft,
> > +							 0);
> 
> Noise diff.
Removed, although it was simple fix of line length.

> > diff --git a/src/box/sql/select.c b/src/box/sql/select.c
> > index 02c0a5d..2776044 100644
> > --- a/src/box/sql/select.c
> > +++ b/src/box/sql/select.c
> > @@ -64,8 +64,15 @@ typedef struct DistinctCtx DistinctCtx;
> > struct DistinctCtx {
> > 	u8 isTnct;		/* True if the DISTINCT keyword is present */
> > 	u8 eTnctType;		/* One of the WHERE_DISTINCT_* operators */
> > -	int tabTnct;		/* Ephemeral table used for DISTINCT processing */
> > -	int addrTnct;		/* Address of OP_OpenEphemeral opcode for tabTnct */
> > +	/* Ephemeral table's cursor used for DISTINCT processing. It is
> > +	 * used for readin from ephemeral space.
> > +	 */
> > +	int cur_eph;
> > +	/* Register, containing a pointer to ephemeral space. It
> > +	 * is used for insertions while procesing DISTINCT.
> > +	 */
> > +	int reg_eph;
> > +	int addrTnct;		/* Address of OP_OpenEphemeral opcode for cur_eph */
> > };
> 
> I fixed comment a little bit:
Okay.

> > @@ -1039,17 +1059,25 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
> > 			}
> > 
> > 		case WHERE_DISTINCT_UNIQUE:{
> > -				sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
> > -				break;
> > +			/* To handle DISTINCT two op-codes are
> > +			 * emitted: OpenTEphemeral & IteratorOpen.
> > +			 * addrTnct is address off first insn in
> > +			 * a couple. Two evict ephemral space,
> > +			 * need to noop both op-codes.
> > +			 */
> 
> I’ve fixed comment:
Okay.

> > @@ -5699,6 +5745,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
> > 			int retAddr;
> > 			assert(pItem->addrFillSub == 0);
> > 			pItem->regReturn = ++pParse->nMem;
> > +
> 
> Noise diff.
Removed.

> > @@ -5885,7 +5941,15 @@ sqlite3Select(Parse * pParse,		/* The parser context */
> > 		 * into an OP_Noop.
> > 		 */
> > 		if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
> > +			/* To handle ordering two op-codes are
> > +			 * emitted: OpenTEphemeral & IteratorOpen.
> > +			 * sSort.addrSortIndex is address off
> > +			 * first insn in a couple. Two evict
> > +			 * ephemral space, need to noop both
> > +			 * op-codes.
> > +			 */
> 
> I’ve fixed comment:
Okay.

> > diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
> > index 53188e7..29636a0 100644
> > --- a/src/box/sql/sqliteInt.h
> > +++ b/src/box/sql/sqliteInt.h
> > @@ -2000,6 +2000,8 @@ struct AggInfo {
> > 		FuncDef *pFunc;	/* The aggregate function implementation */
> > 		int iMem;	/* Memory location that acts as accumulator */
> > 		int iDistinct;	/* Ephemeral table used to enforce DISTINCT */
> > +		/* Register, holding ephemeral's spacepointer. */
> 
> Nit: missed space between ’space’ and ‘pointer’. And start comment from /**.
Fixed.

> > @@ -2591,6 +2593,8 @@ struct SelectDest {
> > 	u8 eDest;		/* How to dispose of the results.  On of SRT_* above. */
> > 	char *zAffSdst;		/* Affinity used when eDest==SRT_Set */
> > 	int iSDParm;		/* A parameter used by the eDest disposal method */
> > +	/* Register containing ephemeral's space pointer. */
> 
> Nit: start comment from /**.
Done.

> > diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
> > index 8017742..497d989 100644
> > --- a/src/box/sql/tarantoolInt.h
> > +++ b/src/box/sql/tarantoolInt.h
> > @@ -97,9 +97,22 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
> > int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
> > 				  const char *zOldName, const char *zNewName);
> > 
> > -/* Interface for ephemeral tables. */
> > -int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
> > -				    struct sql_key_info *key_info);
> > +/**
> > + * Create ephemeral space. Features of ephemeral spaces: id == 0,
> > + * name == "ephemeral", memtx engine (in future it can be changed,
> > + * but now only memtx engine is supported), primary index which
> > + * covers all fields and no secondary indexes, given field number
> > + * and collation sequence. All fields are scalar and nullable.
> > + *
> > + * @param field_count Number of fields in ephemeral space.
> > + * @param key_info Keys description for new ephemeral space.
> > + *
> > + * @retval Pointer to created space, NULL if error.
> > + */
> > +struct space *
> > +sql_ephemeral_space_create(uint32_t filed_count,
> > +			   struct sql_key_info *key_info);
> 
> Nit: you can fit it in one line.
Done.

> > @@ -276,9 +280,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
> > 								pPk->def);
> > 		sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
> > 				  regKey, zAff, pk_part_count);
> > -		sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
> > -		/* Set flag to save memory allocating one by malloc. */
> > -		sqlite3VdbeChangeP5(v, 1);
> 
> You misunderstood me: you should change P5 to 1 for OP_MakeRecord opcode.
> To be more precise see patch:
> https://github.com/tarantool/tarantool/commit/a5576aa77bf900d0ceae126ba46a80c56078df6c
Fixed.

> > diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> > index 00ffb0c..aa602df 100644
> > --- a/src/box/sql/vdbe.c
> > +++ b/src/box/sql/vdbe.c
> > @@ -3054,51 +3054,37 @@ case OP_TTransaction: {
> > 	break;
> > }
> > 
> > -/* Opcode: OpenRead P1 P2 P3 P4 P5
> > +/* Opcode: CursorReopen P1 P2 P3 P4 P5
> 
> IteratorReopen?
Fixed everywhere.

--
Regards, Kirill Yukhin

commit b41476c6c8a046a90882ddaac8fd157baf97f83a
Author: Kirill Yukhin <kyukhin@tarantool.org>
Date:   Tue Sep 25 19:01:10 2018 +0300

    sql: refactor SQL cursor to remove write ones
    
    In Tarantool opening and positioning cursor for writing
    have no sense. So refactor SQL code, to perform:
      - Creation of ephemeral table w/o any cursors machinery.
        No op-code returns register which contains plain pointer
        to the ephemeral space.
      - OpenRead/OpenWrite opcodes were replaced with single
        IteratorOpen op-code, which establishes new cursor w/
        intention to subsequent read from the space. This opcode
        accepts both plain pointer (in P4 operand) and register
        which contains pointer (in P3) to the ephemeral space.
      - Fix query scheduler and DML routines thoroughly.
    
    Closes #3182
    Part of #2362
---
 src/box/sql.c              |  66 ++++--------
 src/box/sql/analyze.c      |  64 ++++-------
 src/box/sql/build.c        |   4 +-
 src/box/sql/delete.c       |  19 ++--
 src/box/sql/expr.c         |  38 +++----
 src/box/sql/insert.c       |  52 ++++-----
 src/box/sql/parse.y        |   6 +-
 src/box/sql/select.c       | 236 +++++++++++++++++++++++++---------------
 src/box/sql/sqliteInt.h    |  15 ++-
 src/box/sql/tarantoolInt.h |  31 +++++-
 src/box/sql/trigger.c      |  12 +--
 src/box/sql/update.c       |  19 ++--
 src/box/sql/vdbe.c         | 262 +++++++++++++++++++++------------------------
 src/box/sql/where.c        |  16 +--
 src/box/sql/whereInt.h     |   2 +-
 src/box/sql/wherecode.c    |  14 ++-
 16 files changed, 438 insertions(+), 418 deletions(-)

diff --git a/src/box/sql.c b/src/box/sql.c
index ab4a587..3b2f3a9 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -355,31 +355,14 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
 	return SQLITE_OK;
 }
 
-/**
- * Create ephemeral space and set cursor to the first entry. Features of
- * ephemeral spaces: id == 0, name == "ephemeral", memtx engine (in future it
- * can be changed, but now only memtx engine is supported), primary index
- * which covers all fields and no secondary indexes, given field number and
- * collation sequence. All fields are scalar and nullable.
- *
- * @param pCur Cursor which will point to the new ephemeral space.
- * @param field_count Number of fields in ephemeral space.
- * @param key_info Keys description for new ephemeral space.
- *
- * @retval SQLITE_OK on success, SQLITE_TARANTOOL_ERROR otherwise.
- */
-int
-tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
-				struct sql_key_info *key_info)
+struct space *
+sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
 {
-	assert(pCur);
-	assert(pCur->curFlags & BTCF_TEphemCursor);
-
 	struct key_def *def = NULL;
 	if (key_info != NULL) {
 		def = sql_key_info_to_key_def(key_info);
 		if (def == NULL)
-			return SQL_TARANTOOL_ERROR;
+			return NULL;
 	}
 
 	struct key_part_def *ephemer_key_parts = region_alloc(&fiber()->gc,
@@ -387,7 +370,7 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
 	if (ephemer_key_parts == NULL) {
 		diag_set(OutOfMemory, sizeof(*ephemer_key_parts) * field_count,
 			 "region", "key parts");
-		return SQL_TARANTOOL_ERROR;
+		return NULL;
 	}
 	for (uint32_t i = 0; i < field_count; ++i) {
 		struct key_part_def *part = &ephemer_key_parts[i];
@@ -404,14 +387,14 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
 	struct key_def *ephemer_key_def = key_def_new(ephemer_key_parts,
 						      field_count);
 	if (ephemer_key_def == NULL)
-		return SQL_TARANTOOL_ERROR;
+		return NULL;
 
 	struct index_def *ephemer_index_def =
 		index_def_new(0, 0, "ephemer_idx", strlen("ephemer_idx"), TREE,
 			      &index_opts_default, ephemer_key_def, NULL);
 	key_def_delete(ephemer_key_def);
 	if (ephemer_index_def == NULL)
-		return SQL_TARANTOOL_ERROR;
+		return NULL;
 
 	struct rlist key_list;
 	rlist_create(&key_list);
@@ -425,24 +408,15 @@ tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count,
 			      0 /* length of field_def */);
 	if (ephemer_space_def == NULL) {
 		index_def_delete(ephemer_index_def);
-		return SQL_TARANTOOL_ERROR;
+		return NULL;
 	}
 
 	struct space *ephemer_new_space = space_new_ephemeral(ephemer_space_def,
 							      &key_list);
 	index_def_delete(ephemer_index_def);
 	space_def_delete(ephemer_space_def);
-	if (ephemer_new_space == NULL)
-		return SQL_TARANTOOL_ERROR;
-	if (key_alloc(pCur, field_count) != 0) {
-		space_delete(ephemer_new_space);
-		return SQL_TARANTOOL_ERROR;
-	}
-	pCur->space = ephemer_new_space;
-	pCur->index = *ephemer_new_space->index;
 
-	int unused;
-	return tarantoolSqlite3First(pCur, &unused);
+	return ephemer_new_space;
 }
 
 int tarantoolSqlite3EphemeralInsert(struct space *space, const char *tuple,
@@ -1461,35 +1435,31 @@ sql_debug_info(struct info_handler *h)
 	info_end(h);
 }
 
-/*
+/**
  * Extract maximum integer value from ephemeral space.
  * If index is empty - return 0 in max_id and success status.
  *
- * @param pCur Cursor pointing to ephemeral space.
+ * @param space Pointer to ephemeral space.
  * @param fieldno Number of field from fetching tuple.
  * @param[out] max_id Fetched max value.
  *
  * @retval 0 on success, -1 otherwise.
  */
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor *pCur, uint32_t fieldno,
-				       uint64_t *max_id)
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
+				      uint64_t *max_id)
 {
-	struct space *ephem_space = pCur->space;
-	assert(ephem_space);
-	struct index *primary_index = *ephem_space->index;
-
+	struct index *primary_index = *space->index;
 	struct tuple *tuple;
-	if (index_max(primary_index, NULL, 0, &tuple) != 0) {
-		return SQL_TARANTOOL_ERROR;
-	}
+	if (index_max(primary_index, NULL, 0, &tuple) != 0)
+		return -1;
 	if (tuple == NULL) {
 		*max_id = 0;
-		return SQLITE_OK;
+		return 0;
 	}
 	if (tuple_field_u64(tuple, fieldno, max_id) == -1)
-		return SQL_TARANTOOL_ERROR;
+		return -1;
 
-	return SQLITE_OK;
+	return 0;
 }
 
 int
diff --git a/src/box/sql/analyze.c b/src/box/sql/analyze.c
index 01e2ad1..674d53d 100644
--- a/src/box/sql/analyze.c
+++ b/src/box/sql/analyze.c
@@ -122,13 +122,10 @@
  * created.
  *
  * @param parse Parsing context.
- * @param stat_cursor Open the _sql_stat1 table on this cursor.
- *        you should allocate |stat_names| cursors before call.
  * @param table_name Delete records of this table if specified.
  */
 static void
-vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
-			  const char *table_name)
+vdbe_emit_stat_space_open(struct Parse *parse, const char *table_name)
 {
 	const char *stat_names[] = {"_sql_stat1", "_sql_stat4"};
 	const uint32_t stat_ids[] = {BOX_SQL_STAT1_ID, BOX_SQL_STAT4_ID};
@@ -144,14 +141,6 @@ vdbe_emit_stat_space_open(struct Parse *parse, int stat_cursor,
 			sqlite3VdbeAddOp1(v, OP_Clear, stat_ids[i]);
 		}
 	}
-
-	/* Open the sql_stat tables for writing. */
-	for (uint i = 0; i < lengthof(stat_names); ++i) {
-		uint32_t id = stat_ids[i];
-		vdbe_emit_open_cursor(parse, stat_cursor + i, 0,
-				      space_by_id(id));
-		VdbeComment((v, stat_names[i]));
-	}
 }
 
 /*
@@ -770,15 +759,16 @@ callStatGet(Vdbe * v, int regStat4, int iParam, int regOut)
  *
  * @param parse Current parsing context.
  * @param space Space to be analyzed.
- * @param stat_cursor Cursor pointing to spaces containing
- *        statistics: _sql_stat1 (stat_cursor) and
- *        _sql_stat4 (stat_cursor + 1).
  */
 static void
-vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
-			int stat_cursor)
+vdbe_emit_analyze_space(struct Parse *parse, struct space *space)
 {
 	assert(space != NULL);
+	struct space *stat1 = space_by_id(BOX_SQL_STAT1_ID);
+	assert(stat1 != NULL);
+	struct space *stat4 = space_by_id(BOX_SQL_STAT4_ID);
+	assert(stat4 != NULL);
+
 	/* Register to hold Stat4Accum object. */
 	int stat4_reg = ++parse->nMem;
 	/* Index of changed index field. */
@@ -808,7 +798,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
 	struct Vdbe *v = sqlite3GetVdbe(parse);
 	assert(v != NULL);
 	const char *tab_name = space_name(space);
-	sqlite3VdbeAddOp4(v, OP_OpenRead, tab_cursor, 0, 0, (void *) space,
+	sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0, (void *) space,
 			  P4_SPACEPTR);
 	sqlite3VdbeLoadString(v, tab_name_reg, space->def->name);
 	for (uint32_t j = 0; j < space->index_count; ++j) {
@@ -871,7 +861,7 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
 		int idx_cursor;
 		if (j != 0) {
 			idx_cursor = parse->nTab - 1;
-			sqlite3VdbeAddOp4(v, OP_OpenRead, idx_cursor,
+			sqlite3VdbeAddOp4(v, OP_IteratorOpen, idx_cursor,
 					  idx->def->iid, 0,
 					  (void *) space, P4_SPACEPTR);
 			VdbeComment((v, "%s", idx->def->name));
@@ -1003,7 +993,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
 		assert("BBB"[0] == AFFINITY_TEXT);
 		sqlite3VdbeAddOp4(v, OP_MakeRecord, tab_name_reg, 3, tmp_reg,
 				  "BBB", 0);
-		sqlite3VdbeAddOp2(v, OP_IdxInsert, stat_cursor, tmp_reg);
+		sqlite3VdbeAddOp4(v, OP_IdxInsert, tmp_reg, 0, 0,
+				  (char *)stat1, P4_SPACEPTR);
 		/* Add the entries to the stat4 table. */
 		int eq_reg = stat1_reg;
 		int lt_reg = stat1_reg + 1;
@@ -1036,7 +1027,8 @@ vdbe_emit_analyze_space(struct Parse *parse, struct space *space,
 		sqlite3VdbeAddOp3(v, OP_MakeRecord, col_reg, part_count,
 				  sample_reg);
 		sqlite3VdbeAddOp3(v, OP_MakeRecord, tab_name_reg, 6, tmp_reg);
-		sqlite3VdbeAddOp2(v, OP_IdxReplace, stat_cursor + 1, tmp_reg);
+		sqlite3VdbeAddOp4(v, OP_IdxReplace, tmp_reg, 0, 0,
+				  (char *)stat4, P4_SPACEPTR);
 		/* P1==1 for end-of-loop. */
 		sqlite3VdbeAddOp2(v, OP_Goto, 1, next_addr);
 		sqlite3VdbeJumpHere(v, is_null_addr);
@@ -1058,27 +1050,12 @@ loadAnalysis(Parse * pParse)
 	}
 }
 
-/**
- * Wrapper to pass args to space_foreach callback.
- */
-struct analyze_data {
-	struct Parse *parse_context;
-	/**
-	 * Cursor numbers pointing to stat spaces:
-	 * stat_cursor is opened on _sql_stat1 and
-	 * stat_cursor + 1 - on _sql_stat4.
-	 */
-	int stat_cursor;
-};
-
 static int
 sql_space_foreach_analyze(struct space *space, void *data)
 {
 	if (space->def->opts.sql == NULL || space->def->opts.is_view)
 		return 0;
-	struct analyze_data *anal_data = (struct analyze_data *) data;
-	vdbe_emit_analyze_space(anal_data->parse_context, space,
-				anal_data->stat_cursor);
+	vdbe_emit_analyze_space((struct Parse*)data, space);
 	return 0;
 }
 
@@ -1090,11 +1067,8 @@ static void
 sql_analyze_database(struct Parse *parser)
 {
 	sql_set_multi_write(parser, false);
-	int stat_cursor = parser->nTab;
-	parser->nTab += 2;
-	vdbe_emit_stat_space_open(parser, stat_cursor, NULL);
-	struct analyze_data anal_data = { parser, stat_cursor };
-	space_foreach(sql_space_foreach_analyze, (void *) &anal_data);
+	vdbe_emit_stat_space_open(parser, NULL);
+	space_foreach(sql_space_foreach_analyze, (void *)parser);
 	loadAnalysis(parser);
 }
 
@@ -1114,10 +1088,8 @@ vdbe_emit_analyze_table(struct Parse *parse, struct space *space)
 	 * There are two system spaces for statistics: _sql_stat1
 	 * and _sql_stat4.
 	 */
-	int stat_cursor = parse->nTab;
-	parse->nTab += 2;
-	vdbe_emit_stat_space_open(parse, stat_cursor, space->def->name);
-	vdbe_emit_analyze_space(parse, space, stat_cursor);
+	vdbe_emit_stat_space_open(parse, space->def->name);
+	vdbe_emit_analyze_space(parse, space);
 	loadAnalysis(parse);
 }
 
diff --git a/src/box/sql/build.c b/src/box/sql/build.c
index 43be777..7670d53 100644
--- a/src/box/sql/build.c
+++ b/src/box/sql/build.c
@@ -883,7 +883,7 @@ vdbe_emit_open_cursor(struct Parse *parse_context, int cursor, int index_id,
 		      struct space *space)
 {
 	assert(space != NULL);
-	return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_OpenWrite, cursor,
+	return sqlite3VdbeAddOp4(parse_context->pVdbe, OP_IteratorOpen, cursor,
 				 index_id, 0, (void *) space, P4_SPACEPTR);
 }
 
@@ -2693,7 +2693,7 @@ sql_create_index(struct Parse *parse, struct Token *token,
 			goto exit_create_index;
 
 		sql_set_multi_write(parse, true);
-		sqlite3VdbeAddOp4(vdbe, OP_OpenWrite, cursor, 0, 0,
+		sqlite3VdbeAddOp4(vdbe, OP_IteratorOpen, cursor, 0, 0,
 				  (void *)space_by_id(BOX_INDEX_ID),
 				  P4_SPACEPTR);
 		sqlite3VdbeChangeP5(vdbe, OPFLAG_SEEKEQ);
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 9aa7058..8f6c76f 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -83,7 +83,7 @@ sql_materialize_view(struct Parse *parse, const char *name, struct Expr *where,
 	struct Select *select = sqlite3SelectNew(parse, NULL, from, where, NULL,
 						 NULL, NULL, 0, NULL, NULL);
 	struct SelectDest dest;
-	sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor);
+	sqlite3SelectDestInit(&dest, SRT_EphemTab, cursor, ++parse->nMem);
 	sqlite3Select(parse, select, &dest);
 	sql_select_delete(db, select);
 }
@@ -241,6 +241,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		 * it, so columns should be loaded manually.
 		 */
 		struct sql_key_info *pk_info = NULL;
+		int reg_eph = ++parse->nMem;
 		int reg_pk = parse->nMem + 1;
 		int pk_len;
 		int eph_cursor = parse->nTab++;
@@ -248,8 +249,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 		if (is_view) {
 			pk_len = table->def->field_count;
 			parse->nMem += pk_len;
-			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
-					  eph_cursor, pk_len);
+			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+					  pk_len);
 		} else {
                         assert(space->index_count > 0);
                         pk_info = sql_key_info_new_from_key_def(db,
@@ -258,9 +259,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
                                 goto delete_from_cleanup;
                         pk_len = pk_info->part_count;
                         parse->nMem += pk_len;
-                        sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, eph_cursor,
-                                          pk_len, 0,
-                                          (char *)pk_info, P4_KEYINFO);
+			sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
+					  pk_len, 0,
+					  (char *)pk_info, P4_KEYINFO);
 		}
 
 		/* Construct a query to find the primary key for
@@ -345,7 +346,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 			 * by malloc.
 			 */
 			sqlite3VdbeChangeP5(v, 1);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, eph_cursor, reg_key);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, reg_key, reg_eph);
 		}
 
 		/* If this DELETE cannot use the ONEPASS strategy,
@@ -369,7 +370,7 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 				iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once);
 				VdbeCoverage(v);
 			}
-			sqlite3VdbeAddOp4(v, OP_OpenWrite, tab_cursor, 0, 0,
+			sqlite3VdbeAddOp4(v, OP_IteratorOpen, tab_cursor, 0, 0,
 					  (void *) space, P4_SPACEPTR);
 			VdbeComment((v, "%s", space->index[0]->def->name));
 
@@ -390,6 +391,8 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
 
 			VdbeCoverage(v);
 		} else {
+			sqlite3VdbeAddOp3(v, OP_IteratorOpen,
+					  eph_cursor, 0, reg_eph);
 			addr_loop = sqlite3VdbeAddOp1(v, OP_Rewind, eph_cursor);
 			VdbeCoverage(v);
 			sqlite3VdbeAddOp2(v, OP_RowData, eph_cursor, reg_key);
diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
index a13de4f..3602606 100644
--- a/src/box/sql/expr.c
+++ b/src/box/sql/expr.c
@@ -2714,8 +2714,11 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 			 */
 			pExpr->iTable = pParse->nTab++;
 			pExpr->is_ephemeral = 1;
+			int reg_eph = ++pParse->nMem;
 			addr = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
-						 pExpr->iTable, nVal);
+						 reg_eph, nVal);
+			sqlite3VdbeAddOp3(v, OP_IteratorOpen, pExpr->iTable, 0,
+					  reg_eph);
 			struct sql_key_info *key_info = sql_key_info_new(pParse->db, nVal);
 			if (key_info == NULL)
 				return 0;
@@ -2736,7 +2739,7 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 					SelectDest dest;
 					int i;
 					sqlite3SelectDestInit(&dest, SRT_Set,
-							      pExpr->iTable);
+							      pExpr->iTable, reg_eph);
 					dest.zAffSdst =
 					    exprINAffinity(pParse, pExpr);
 					assert((pExpr->iTable & 0x0000FFFF) ==
@@ -2808,8 +2811,8 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 							  1, r2, &affinity, 1);
 					sqlite3ExprCacheAffinityChange(pParse,
 								       r3, 1);
-					sqlite3VdbeAddOp2(v, OP_IdxInsert,
-							  pExpr->iTable, r2);
+					sqlite3VdbeAddOp2(v, OP_IdxInsert, r2,
+							  reg_eph);
 				}
 				sqlite3ReleaseTempReg(pParse, r1);
 				sqlite3ReleaseTempReg(pParse, r2);
@@ -2847,7 +2850,7 @@ sqlite3CodeSubselect(Parse * pParse,	/* Parsing context */
 
 			pSel = pExpr->x.pSelect;
 			nReg = pExpr->op == TK_SELECT ? pSel->pEList->nExpr : 1;
-			sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1);
+			sqlite3SelectDestInit(&dest, 0, pParse->nMem + 1, -1);
 			pParse->nMem += nReg;
 			if (pExpr->op == TK_SELECT) {
 				dest.eDest = SRT_Mem;
@@ -5361,24 +5364,17 @@ analyzeAggregate(Walker * pWalker, Expr * pExpr)
 						pItem->iMem = ++pParse->nMem;
 						assert(!ExprHasProperty
 						       (pExpr, EP_IntValue));
-						pItem->pFunc =
-						    sqlite3FindFunction(pParse->
-									db,
-									pExpr->
-									u.
-									zToken,
-									pExpr->
-									x.
-									pList ?
-									pExpr->
-									x.
-									pList->
-									nExpr :
-									0,
-									0);
+						pItem->pFunc = sqlite3FindFunction(
+							pParse->db,
+							pExpr->u.zToken,
+							pExpr->x.pList ?
+							pExpr->x.pList->nExpr : 0,
+							0);
 						if (pExpr->flags & EP_Distinct) {
 							pItem->iDistinct =
-							    pParse->nTab++;
+								pParse->nTab++;
+							pItem->reg_eph =
+								++pParse->nMem;
 						} else {
 							pItem->iDistinct = -1;
 						}
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 03f4e1b..287c6c9 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -40,23 +40,6 @@
 #include "bit/bit.h"
 #include "box/box.h"
 
-/*
- * Generate code that will open pTab as cursor iCur.
- */
-void
-sqlite3OpenTable(Parse * pParse,	/* Generate code into this VDBE */
-		 int iCur,	/* The cursor number of the table */
-		 struct space *space,	/* The table to be opened */
-		 int opcode)	/* OP_OpenRead or OP_OpenWrite */
-{
-	Vdbe *v;
-	v = sqlite3GetVdbe(pParse);
-	assert(opcode == OP_OpenWrite || opcode == OP_OpenRead);
-	assert(space->index_count > 0);
-	vdbe_emit_open_cursor(pParse, iCur, 0, space);
-	VdbeComment((v, "%s", space->def->name));
-}
-
 char *
 sql_space_index_affinity_str(struct sqlite3 *db, struct space_def *space_def,
 			     struct index_def *idx_def)
@@ -139,9 +122,12 @@ vdbe_has_table_read(struct Parse *parser, const struct Table *table)
 		 * Currently, there is no difference between Read
 		 * and Write cursors.
 		 */
-		if (op->opcode == OP_OpenRead || op->opcode == OP_OpenWrite) {
-			assert(op->p4type == P4_SPACEPTR);
-			struct space *space = op->p4.space;
+		if (op->opcode == OP_IteratorOpen) {
+			struct space *space = NULL;
+			if (op->p4type == P4_SPACEPTR)
+				space = op->p4.space;
+			else
+				continue;
 			if (space->def->id == table->def->id)
 				return true;
 		}
@@ -418,6 +404,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		}
 	}
 
+	int reg_eph;
 	/* Figure out how many columns of data are supplied.  If the data
 	 * is coming from a SELECT statement, then generate a co-routine that
 	 * produces a single row of the SELECT on each invocation.  The
@@ -434,7 +421,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		regYield = ++pParse->nMem;
 		addrTop = sqlite3VdbeCurrentAddr(v) + 1;
 		sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
-		sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+		sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield, -1);
 		dest.iSdst = bIdListInOrder ? regData : 0;
 		dest.nSdst = def->field_count;
 		rc = sqlite3Select(pParse, pSelect, &dest);
@@ -480,10 +467,13 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 			int64_t initial_pk = 0;
 
 			srcTab = pParse->nTab++;
+			reg_eph = ++pParse->nMem;
 			regRec = sqlite3GetTempReg(pParse);
 			regCopy = sqlite3GetTempRange(pParse, nColumn);
 			regTempId = sqlite3GetTempReg(pParse);
-			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, srcTab, nColumn+1);
+			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
+					  nColumn + 1);
+
 			/* Create counter for rowid */
 			sqlite3VdbeAddOp4Dup8(v, OP_Int64,
 					      0 /* unused */,
@@ -499,7 +489,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 					  nColumn + 1, regRec);
 			/* Set flag to save memory allocating one by malloc. */
 			sqlite3VdbeChangeP5(v, 1);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, srcTab, regRec);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, reg_eph);
 
 			sqlite3VdbeGoto(v, addrL);
 			sqlite3VdbeJumpHere(v, addrL);
@@ -515,6 +505,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		memset(&sNC, 0, sizeof(sNC));
 		sNC.pParse = pParse;
 		srcTab = -1;
+		reg_eph = -1;
 		assert(useTempTable == 0);
 		if (pList) {
 			nColumn = pList->nExpr;
@@ -555,6 +546,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		 *         end loop
 		 *      D: ...
 		 */
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, srcTab, 0, reg_eph);
 		addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
 		VdbeCoverage(v);
 		addrCont = sqlite3VdbeCurrentAddr(v);
@@ -746,9 +738,7 @@ sqlite3Insert(Parse * pParse,	/* Parser context */
 		vdbe_emit_constraint_checks(pParse, pTab, regIns + 1,
 					    on_error, endOfLoop, 0);
 		fkey_emit_check(pParse, pTab, 0, regIns, 0);
-		int pk_cursor = pParse->nTab++;
-		vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
-		vdbe_emit_insertion_completion(v, pk_cursor, regIns + 1,
+		vdbe_emit_insertion_completion(v, space, regIns + 1,
 					       pTab->def->field_count,
 					       on_error);
 	}
@@ -1063,8 +1053,8 @@ process_index:  ;
 }
 
 void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
-			       uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+			       int raw_data_reg, uint32_t tuple_len,
 			       enum on_conflict_action on_conflict)
 {
 	assert(v != NULL);
@@ -1077,7 +1067,8 @@ vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
 		pik_flags |= OPFLAG_OE_ROLLBACK;
 	sqlite3VdbeAddOp3(v, OP_MakeRecord, raw_data_reg, tuple_len,
 			  raw_data_reg + tuple_len);
-	sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor_id, raw_data_reg + tuple_len);
+	sqlite3VdbeAddOp1(v, OP_IdxInsert, raw_data_reg + tuple_len);
+	sqlite3VdbeChangeP4(v, -1, (char *)space, P4_SPACEPTR);
 	sqlite3VdbeChangeP5(v, pik_flags);
 }
 
@@ -1327,7 +1318,8 @@ xferOptimization(Parse * pParse,	/* Parser context */
 	sqlite3VdbeChangeP5(v, OPFLAG_XFER_OPT);
 #endif
 
-	sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
+	sqlite3VdbeAddOp4(v, OP_IdxInsert, regData, 0, 0,
+			  (char *)dest, P4_SPACEPTR);
 	switch (onError) {
 	case ON_CONFLICT_ACTION_IGNORE:
 		sqlite3VdbeChangeP5(v, OPFLAG_OE_IGNORE | OPFLAG_NCHANGE);
diff --git a/src/box/sql/parse.y b/src/box/sql/parse.y
index 473cf89..92e3a93 100644
--- a/src/box/sql/parse.y
+++ b/src/box/sql/parse.y
@@ -390,11 +390,11 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
 //////////////////////// The SELECT statement /////////////////////////////////
 //
 cmd ::= select(X).  {
-  SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
+  SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
   if(!pParse->parse_only)
-	  sqlite3Select(pParse, X, &dest);
+          sqlite3Select(pParse, X, &dest);
   else
-	  sql_expr_extract_select(pParse, X);
+          sql_expr_extract_select(pParse, X);
   sql_select_delete(pParse->db, X);
 }
 
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 02c0a5d..929d3c8 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -64,8 +64,17 @@ typedef struct DistinctCtx DistinctCtx;
 struct DistinctCtx {
 	u8 isTnct;		/* True if the DISTINCT keyword is present */
 	u8 eTnctType;		/* One of the WHERE_DISTINCT_* operators */
-	int tabTnct;		/* Ephemeral table used for DISTINCT processing */
-	int addrTnct;		/* Address of OP_OpenEphemeral opcode for tabTnct */
+	/**
+	 * Ephemeral table's cursor used for DISTINCT processing.
+	 * It is used for reading from ephemeral space.
+	 */
+	int cur_eph;
+	/**
+	 * Register, containing a pointer to ephemeral space.
+	 * It is used for insertions while procesing DISTINCT.
+	 */
+	int reg_eph;
+	int addrTnct;		/* Address of OP_OpenEphemeral opcode for cur_eph */
 };
 
 /*
@@ -77,6 +86,8 @@ struct SortCtx {
 	ExprList *pOrderBy;	/* The ORDER BY (or GROUP BY clause) */
 	int nOBSat;		/* Number of ORDER BY terms satisfied by indices */
 	int iECursor;		/* Cursor number for the sorter */
+	/* Register, containing pointer to ephemeral space. */
+	int reg_eph;
 	int regReturn;		/* Register holding block-output return address */
 	int labelBkOut;		/* Start label for the block-output subroutine */
 	int addrSortIndex;	/* Address of the OP_SorterOpen or OP_OpenEphemeral */
@@ -117,10 +128,11 @@ clearSelect(sqlite3 * db, Select * p, int bFree)
  * Initialize a SelectDest structure.
  */
 void
-sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm)
+sqlite3SelectDestInit(SelectDest * pDest, int eDest, int iParm, int reg_eph)
 {
 	pDest->eDest = (u8) eDest;
 	pDest->iSDParm = iParm;
+	pDest->reg_eph = reg_eph;
 	pDest->zAffSdst = 0;
 	pDest->iSdst = 0;
 	pDest->nSdst = 0;
@@ -687,7 +699,6 @@ pushOntoSorter(Parse * pParse,		/* Parser context */
 	int regBase;		/* Regs for sorter record */
 	int regRecord = ++pParse->nMem;	/* Assembled sorter record */
 	int nOBSat = pSort->nOBSat;	/* ORDER BY terms to skip */
-	int op;			/* Opcode to add sorter record to sorter */
 	int iLimit;		/* LIMIT counter */
 
 	assert(bSeq == 0 || bSeq == 1);
@@ -764,11 +775,13 @@ pushOntoSorter(Parse * pParse,		/* Parser context */
 		sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
 		sqlite3VdbeJumpHere(v, addrJmp);
 	}
-	if (pSort->sortFlags & SORTFLAG_UseSorter)
-		op = OP_SorterInsert;
-	else
-		op = OP_IdxInsert;
-	sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
+	if (pSort->sortFlags & SORTFLAG_UseSorter) {
+		sqlite3VdbeAddOp2(v, OP_SorterInsert, pSort->iECursor,
+				  regRecord);
+	} else {
+		sqlite3VdbeAddOp2(v, OP_IdxInsert, regRecord, pSort->reg_eph);
+	}
+
 	if (iLimit) {
 		int addr;
 		int r1 = 0;
@@ -823,32 +836,33 @@ codeOffset(Vdbe * v,		/* Generate code into this VM */
 	}
 }
 
-/*
- * Add code that will check to make sure the N registers starting at iMem
- * form a distinct entry.  iTab is a sorting index that holds previously
- * seen combinations of the N values.  A new entry is made in iTab
- * if the current N values are new.
- *
- * A jump to addrRepeat is made and the N+1 values are popped from the
- * stack if the top N elements are not distinct.
+/**
+ * Add code that will check to make sure the @n registers starting
+ * at @reg_data form a distinct entry. @cursor is a sorting index
+ * that holds previously seen combinations of the @n values.
+ * A new entry is made in @cursor if the current n values are new.
+ *
+ * A jump to @addr_repeat is made and the @n+1 values are popped
+ * from the stack if the top n elements are not distinct.
+ *
+ * @param parse Parsing and code generating context.
+ * @param cursor A sorting index cursor used to test for
+ *               distinctness.
+ * @param reg_eph Register holding ephemeral space's pointer.
+ * @param addr_repeat Jump here if not distinct.
+ * @param n Number of elements in record.
+ * @param reg_data First register holding the data.
  */
 static void
-codeDistinct(Parse * pParse,	/* Parsing and code generating context */
-	     int iTab,		/* A sorting index used to test for distinctness */
-	     int addrRepeat,	/* Jump to here if not distinct */
-	     int N,		/* Number of elements */
-	     int iMem)		/* First element */
+vdbe_insert_distinct(struct Parse *parse, int cursor, int reg_eph,
+		     int addr_repeat, int n, int reg_data)
 {
-	Vdbe *v;
-	int r1;
-
-	v = pParse->pVdbe;
-	r1 = sqlite3GetTempReg(pParse);
-	sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
-	VdbeCoverage(v);
-	sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
-	sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
-	sqlite3ReleaseTempReg(pParse, r1);
+	struct Vdbe *v = parse->pVdbe;
+	int r1 = sqlite3GetTempReg(parse);
+	sqlite3VdbeAddOp4Int(v, OP_Found, cursor, addr_repeat, reg_data, n);
+	sqlite3VdbeAddOp3(v, OP_MakeRecord, reg_data, n, r1);
+	sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, reg_eph);
+	sqlite3ReleaseTempReg(parse, r1);
 }
 
 /*
@@ -991,14 +1005,22 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 				regPrev = pParse->nMem + 1;
 				pParse->nMem += nResultCol;
 
-				/* Change the OP_OpenEphemeral coded earlier to an OP_Null
-				 * sets the MEM_Cleared bit on the first register of the
-				 * previous value.  This will cause the OP_Ne below to always
-				 * fail on the first iteration of the loop even if the first
-				 * row is all NULLs.
+				/* Actually, for DISTINCT handling
+				 * two op-codes were emitted here:
+				 * OpenTEphemeral & IteratorOpen.
+				 * So, we need to Noop one and
+				 * re-use second for Null op-code.
+				 *
+				 * Change to an OP_Null sets the
+				 * MEM_Cleared bit on the first
+				 * register of the previous value. 
+				 * This will cause the OP_Ne below
+				 * to always fail on the first
+				 * iteration of the loop even if
+				 * the first row is all NULLs.
 				 */
 				sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
-				pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
+				pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct + 1);
 				pOp->opcode = OP_Null;
 				pOp->p1 = 1;
 				pOp->p2 = regPrev;
@@ -1039,17 +1061,26 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 			}
 
 		case WHERE_DISTINCT_UNIQUE:{
-				sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
-				break;
+			/**
+			 * To handle DISTINCT two op-codes are
+			 * emitted: OpenTEphemeral & IteratorOpen.
+			 * addrTnct is address of first insn in
+			 * a couple. To evict ephemral space,
+			 * need to noop both op-codes.
+			 */
+			sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
+			sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct + 1);
+			break;
 			}
 
 		default:{
-				assert(pDistinct->eTnctType ==
-				       WHERE_DISTINCT_UNORDERED);
-				codeDistinct(pParse, pDistinct->tabTnct,
-					     iContinue, nResultCol, regResult);
-				break;
-			}
+			assert(pDistinct->eTnctType ==
+			       WHERE_DISTINCT_UNORDERED);
+			vdbe_insert_distinct(pParse, pDistinct->cur_eph,
+					     pDistinct->reg_eph, iContinue,
+					     nResultCol, regResult);
+			break;
+		}
 		}
 		if (pSort == 0) {
 			codeOffset(v, p->iOffset, iContinue);
@@ -1066,7 +1097,7 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 			r1 = sqlite3GetTempReg(pParse);
 			sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
 					  nResultCol, r1);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert,  r1, pDest->reg_eph);
 			sqlite3ReleaseTempReg(pParse, r1);
 			break;
 		}
@@ -1109,8 +1140,8 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 				sqlite3VdbeAddOp4Int(v, OP_Found, iParm + 1,
 						     addr, r1, 0);
 				VdbeCoverage(v);
-				sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
-						  r1);
+				sqlite3VdbeAddOp2(v, OP_IdxInsert, r1,
+						  pDest->reg_eph + 1);
 				assert(pSort == 0);
 			}
 #endif
@@ -1122,7 +1153,7 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 				int regRec = sqlite3GetTempReg(pParse);
 				/* Last column is required for ID. */
 				int regCopy = sqlite3GetTempRange(pParse, nResultCol + 1);
-				sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm,
+				sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
 						  nResultCol, regCopy + nResultCol);
 				/* Positioning ID column to be last in inserted tuple.
 				 * NextId -> regCopy + n + 1
@@ -1134,7 +1165,7 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 				sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nResultCol + 1, regRec);
 				/* Set flag to save memory allocating one by malloc. */
 				sqlite3VdbeChangeP5(v, 1);
-				sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRec);
+				sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, pDest->reg_eph);
 				sqlite3ReleaseTempReg(pParse, regRec);
 				sqlite3ReleaseTempRange(pParse, regCopy, nResultCol + 1);
 			}
@@ -1164,7 +1195,7 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 				sqlite3ExprCacheAffinityChange(pParse,
 							       regResult,
 							       nResultCol);
-				sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+				sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
 				sqlite3ReleaseTempReg(pParse, r1);
 			}
 			break;
@@ -1247,8 +1278,8 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 			sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult,
 					  nResultCol, r3);
 			if (eDest == SRT_DistQueue) {
-				sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm + 1,
-						  r3);
+				sqlite3VdbeAddOp2(v, OP_IdxInsert, r3,
+						  pDest->reg_eph + 1);
 			}
 			for (i = 0; i < nKey; i++) {
 				sqlite3VdbeAddOp2(v, OP_SCopy,
@@ -1258,7 +1289,7 @@ selectInnerLoop(Parse * pParse,		/* The parser context */
 			}
 			sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2 + nKey);
 			sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey + 2, r1);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, pDest->reg_eph);
 			if (addrTest)
 				sqlite3VdbeJumpHere(v, addrTest);
 			sqlite3ReleaseTempReg(pParse, r1);
@@ -1498,7 +1529,6 @@ generateSortTail(Parse * pParse,	/* Parsing context */
 	int iTab;
 	ExprList *pOrderBy = pSort->pOrderBy;
 	int eDest = pDest->eDest;
-	int iParm = pDest->iSDParm;
 	int regRow;
 	int regTupleid;
 	int iCol;
@@ -1570,10 +1600,11 @@ generateSortTail(Parse * pParse,	/* Parsing context */
 	case SRT_Table:
 	case SRT_EphemTab: {
 			int regCopy = sqlite3GetTempRange(pParse,  nColumn);
-			sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm, nColumn, regTupleid);
+			sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, pDest->reg_eph,
+					  nColumn, regTupleid);
 			sqlite3VdbeAddOp3(v, OP_Copy, regRow, regCopy, nSortData - 1);
 			sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nColumn + 1, regRow);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRow);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, regRow, pDest->reg_eph);
 			sqlite3ReleaseTempReg(pParse, regCopy);
 			break;
 		}
@@ -1583,7 +1614,7 @@ generateSortTail(Parse * pParse,	/* Parsing context */
 			sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn,
 					  regTupleid, pDest->zAffSdst, nColumn);
 			sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regTupleid);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, regTupleid, pDest->reg_eph);
 			break;
 		}
 	case SRT_Mem:{
@@ -2409,13 +2440,16 @@ generateWithRecursiveQuery(Parse * pParse,	/* Parsing context */
 	 * for the SRT_DistFifo and SRT_DistQueue destinations to work.
 	 */
 	iQueue = pParse->nTab++;
+	int reg_queue = ++pParse->nMem;
+	int reg_dist = 0;
 	if (p->op == TK_UNION) {
 		eDest = pOrderBy ? SRT_DistQueue : SRT_DistFifo;
 		iDistinct = pParse->nTab++;
+		reg_dist = ++pParse->nMem;
 	} else {
 		eDest = pOrderBy ? SRT_Queue : SRT_Fifo;
 	}
-	sqlite3SelectDestInit(&destQueue, eDest, iQueue);
+	sqlite3SelectDestInit(&destQueue, eDest, iQueue, reg_queue);
 
 	/* Allocate cursors for Current, Queue, and Distinct. */
 	regCurrent = ++pParse->nMem;
@@ -2423,18 +2457,20 @@ generateWithRecursiveQuery(Parse * pParse,	/* Parsing context */
 	if (pOrderBy) {
 		struct sql_key_info *key_info =
 			sql_multiselect_orderby_to_key_info(pParse, p, 1);
-		sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, iQueue,
+		sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, reg_queue,
 				  pOrderBy->nExpr + 2, 0, (char *)key_info,
 				  P4_KEYINFO);
 		VdbeComment((v, "Orderby table"));
 		destQueue.pOrderBy = pOrderBy;
 	} else {
-		sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iQueue, nCol + 1);
+		sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_queue, nCol + 1);
 		VdbeComment((v, "Queue table"));
 	}
+	sqlite3VdbeAddOp3(v, OP_IteratorOpen, iQueue, 0, reg_queue);
 	if (iDistinct) {
 		p->addrOpenEphm[0] =
-		    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iDistinct, 1);
+		    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_dist, 1);
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, iDistinct, 0, reg_dist);
 		p->selFlags |= SF_UsesEphemeral;
 		VdbeComment((v, "Distinct table"));
 	}
@@ -2630,7 +2666,8 @@ multiSelect(Parse * pParse,	/* Parsing context */
 	if (dest.eDest == SRT_EphemTab) {
 		assert(p->pEList);
 		int nCols = p->pEList->nExpr;
-		sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.iSDParm, nCols + 1);
+		sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, dest.reg_eph, nCols + 1);
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, dest.iSDParm, 0, dest.reg_eph);
 		VdbeComment((v, "Destination temp"));
 		dest.eDest = SRT_Table;
 	}
@@ -2721,6 +2758,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 		case TK_EXCEPT:
 		case TK_UNION:{
 				int unionTab;	/* Cursor number of the temporary table holding result */
+				int reg_union;
 				u8 op = 0;	/* One of the SRT_ operations to apply to self */
 				int priorOp;	/* The SRT_ operation to apply to prior selects */
 				Expr *pLimit, *pOffset;	/* Saved values of p->nLimit and p->nOffset */
@@ -2737,16 +2775,19 @@ multiSelect(Parse * pParse,	/* Parsing context */
 					assert(p->pLimit == 0);	/* Not allowed on leftward elements */
 					assert(p->pOffset == 0);	/* Not allowed on leftward elements */
 					unionTab = dest.iSDParm;
+					reg_union = dest.reg_eph;
 				} else {
 					/* We will need to create our own temporary table to hold the
 					 * intermediate results.
 					 */
 					unionTab = pParse->nTab++;
+					reg_union = ++pParse->nMem;
 					assert(p->pOrderBy == 0);
 					addr =
 					    sqlite3VdbeAddOp2(v,
 							      OP_OpenTEphemeral,
-							      unionTab, 0);
+							      reg_union, 0);
+					sqlite3VdbeAddOp3(v, OP_IteratorOpen, unionTab, 0, reg_union);
 					assert(p->addrOpenEphm[0] == -1);
 					p->addrOpenEphm[0] = addr;
 					findRightmost(p)->selFlags |=
@@ -2758,7 +2799,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 				 */
 				assert(!pPrior->pOrderBy);
 				sqlite3SelectDestInit(&uniondest, priorOp,
-						      unionTab);
+						      unionTab, reg_union);
 				iSub1 = pParse->iNextSelectId;
 				rc = sqlite3Select(pParse, pPrior, &uniondest);
 				if (rc) {
@@ -2841,6 +2882,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 		default:
 			assert(p->op == TK_INTERSECT); {
 				int tab1, tab2;
+				int reg_eph1, reg_eph2;
 				int iCont, iBreak, iStart;
 				Expr *pLimit, *pOffset;
 				int addr;
@@ -2852,12 +2894,15 @@ multiSelect(Parse * pParse,	/* Parsing context */
 				 * by allocating the tables we will need.
 				 */
 				tab1 = pParse->nTab++;
+				reg_eph1 = ++pParse->nMem;
 				tab2 = pParse->nTab++;
+				reg_eph2 = ++pParse->nMem;
 				assert(p->pOrderBy == 0);
 
 				addr =
-				    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab1,
+				    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph1,
 						      0);
+				sqlite3VdbeAddOp3(v, OP_IteratorOpen, tab1, 0, reg_eph1);
 				assert(p->addrOpenEphm[0] == -1);
 				p->addrOpenEphm[0] = addr;
 				findRightmost(p)->selFlags |= SF_UsesEphemeral;
@@ -2866,7 +2911,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 				/* Code the SELECTs to our left into temporary table "tab1".
 				 */
 				sqlite3SelectDestInit(&intersectdest, SRT_Union,
-						      tab1);
+						      tab1, reg_eph1);
 				iSub1 = pParse->iNextSelectId;
 				rc = sqlite3Select(pParse, pPrior,
 						   &intersectdest);
@@ -2877,8 +2922,9 @@ multiSelect(Parse * pParse,	/* Parsing context */
 				/* Code the current SELECT into temporary table "tab2"
 				 */
 				addr =
-				    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, tab2,
+				    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph2,
 						      0);
+				sqlite3VdbeAddOp3(v, OP_IteratorOpen, tab2, 0, reg_eph2);
 				assert(p->addrOpenEphm[1] == -1);
 				p->addrOpenEphm[1] = addr;
 				p->pPrior = 0;
@@ -2887,6 +2933,7 @@ multiSelect(Parse * pParse,	/* Parsing context */
 				pOffset = p->pOffset;
 				p->pOffset = 0;
 				intersectdest.iSDParm = tab2;
+				intersectdest.reg_eph = reg_eph2;
 				iSub2 = pParse->iNextSelectId;
 				rc = sqlite3Select(pParse, p, &intersectdest);
 				testcase(rc != SQLITE_OK);
@@ -3075,7 +3122,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
 	case SRT_EphemTab:{
 			int regRec = sqlite3GetTempReg(parse);
 			int regCopy = sqlite3GetTempRange(parse, in->nSdst + 1);
-			sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->iSDParm,
+			sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->reg_eph,
 					  in->nSdst, regCopy + in->nSdst);
 			sqlite3VdbeAddOp3(v, OP_Copy, in->iSdst, regCopy,
 					  in->nSdst - 1);
@@ -3083,7 +3130,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
 					  in->nSdst + 1, regRec);
 			/* Set flag to save memory allocating one by malloc. */
 			sqlite3VdbeChangeP5(v, 1);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, regRec);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, regRec, dest->reg_eph);
 			sqlite3ReleaseTempRange(parse, regCopy, in->nSdst + 1);
 			sqlite3ReleaseTempReg(parse, regRec);
 			break;
@@ -3099,7 +3146,7 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,
 					  in->nSdst);
 			sqlite3ExprCacheAffinityChange(parse, in->iSdst,
 						       in->nSdst);
-			sqlite3VdbeAddOp2(v, OP_IdxInsert, dest->iSDParm, r1);
+			sqlite3VdbeAddOp2(v, OP_IdxInsert, r1, dest->reg_eph);
 			sqlite3ReleaseTempReg(parse, r1);
 			break;
 		}
@@ -3417,8 +3464,8 @@ multiSelectOrderBy(Parse * pParse,	/* Parsing context */
 	regAddrB = ++pParse->nMem;
 	regOutA = ++pParse->nMem;
 	regOutB = ++pParse->nMem;
-	sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
-	sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
+	sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA, -1);
+	sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB, -1);
 
 	/* Generate a coroutine to evaluate the SELECT statement to the
 	 * left of the compound operator - the "A" select.
@@ -5261,7 +5308,7 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
 	/* Verify that all AggInfo registers are within the range specified by
 	 * AggInfo.mnReg..AggInfo.mxReg
 	 */
-	assert(nReg == pAggInfo->mxReg - pAggInfo->mnReg + 1);
+	assert(nReg <= pAggInfo->mxReg - pAggInfo->mnReg + 1);
 	for (i = 0; i < pAggInfo->nColumn; i++) {
 		assert(pAggInfo->aCol[i].iMem >= pAggInfo->mnReg
 		       && pAggInfo->aCol[i].iMem <= pAggInfo->mxReg);
@@ -5287,8 +5334,10 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
 								  pE->x.pList,
 								  0);
 				sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
-						  pFunc->iDistinct, 1, 0,
+						  pFunc->reg_eph, 1, 0,
 						  (char *)key_info, P4_KEYINFO);
+				sqlite3VdbeAddOp3(v, OP_IteratorOpen,
+						  pFunc->iDistinct, 0, pFunc->reg_eph);
 			}
 		}
 	}
@@ -5347,8 +5396,8 @@ updateAccumulator(Parse * pParse, AggInfo * pAggInfo)
 			addrNext = sqlite3VdbeMakeLabel(v);
 			testcase(nArg == 0);	/* Error condition */
 			testcase(nArg > 1);	/* Also an error */
-			codeDistinct(pParse, pF->iDistinct, addrNext, 1,
-				     regAgg);
+			vdbe_insert_distinct(pParse, pF->iDistinct, pF->reg_eph,
+					     addrNext, 1, regAgg);
 		}
 		if (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL) {
 			struct coll *coll = NULL;
@@ -5679,7 +5728,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 			VdbeComment((v, "%s", pItem->pTab->def->name));
 			pItem->addrFillSub = addrTop;
 			sqlite3SelectDestInit(&dest, SRT_Coroutine,
-					      pItem->regReturn);
+					      pItem->regReturn, -1);
 			pItem->iSelectId = pParse->iNextSelectId;
 			sqlite3Select(pParse, pSub, &dest);
 			pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5717,7 +5766,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 						 pItem->pTab->def->name));
 			}
 			sqlite3SelectDestInit(&dest, SRT_EphemTab,
-					      pItem->iCursor);
+					      pItem->iCursor, ++pParse->nMem);
 			pItem->iSelectId = pParse->iNextSelectId;
 			sqlite3Select(pParse, pSub, &dest);
 			pItem->pTab->tuple_log_count = pSub->nSelectRow;
@@ -5796,6 +5845,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 	if (sSort.pOrderBy) {
 		struct sql_key_info *key_info =
 			sql_expr_list_to_key_info(pParse, sSort.pOrderBy, 0);
+		sSort.reg_eph = ++pParse->nMem;
 		sSort.iECursor = pParse->nTab++;
 		/* Number of columns in transient table equals to number of columns in
 		 * SELECT statement plus number of columns in ORDER BY statement
@@ -5807,9 +5857,10 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 		}
 		sSort.addrSortIndex =
 		    sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
-				      sSort.iECursor,
+				      sSort.reg_eph,
 				      nCols,
 				      0, (char *)key_info, P4_KEYINFO);
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, sSort.iECursor, 0, sSort.reg_eph);
 		VdbeComment((v, "Sort table"));
 	} else {
 		sSort.addrSortIndex = -1;
@@ -5818,8 +5869,10 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 	/* If the output is destined for a temporary table, open that table.
 	 */
 	if (pDest->eDest == SRT_EphemTab) {
-		sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->iSDParm,
+		sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, pDest->reg_eph,
 				  pEList->nExpr + 1);
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, pDest->iSDParm, 0,
+				  pDest->reg_eph);
 
 		VdbeComment((v, "Output table"));
 	}
@@ -5833,20 +5886,25 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 	computeLimitRegisters(pParse, p, iEnd);
 	if (p->iLimit == 0 && sSort.addrSortIndex >= 0) {
 		sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
+		sqlite3VdbeChangeP1(v, sSort.addrSortIndex, sSort.iECursor);
+		sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
 		sSort.sortFlags |= SORTFLAG_UseSorter;
 	}
 
 	/* Open an ephemeral index to use for the distinct set.
 	 */
 	if (p->selFlags & SF_Distinct) {
-		sDistinct.tabTnct = pParse->nTab++;
+		sDistinct.cur_eph = pParse->nTab++;
+		sDistinct.reg_eph = ++pParse->nMem;
 		struct sql_key_info *key_info =
 			sql_expr_list_to_key_info(pParse, p->pEList, 0);
 		sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenTEphemeral,
-						       sDistinct.tabTnct,
+						       sDistinct.reg_eph,
 						       key_info->part_count,
 						       0, (char *)key_info,
 						       P4_KEYINFO);
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, sDistinct.cur_eph, 0,
+				  sDistinct.reg_eph);
 		VdbeComment((v, "Distinct table"));
 		sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
 	} else {
@@ -5885,7 +5943,16 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 		 * into an OP_Noop.
 		 */
 		if (sSort.addrSortIndex >= 0 && sSort.pOrderBy == 0) {
+			/*
+			 * To handle ordering two op-codes are
+			 * emitted: OpenTEphemeral & IteratorOpen.
+			 * sSort.addrSortIndex is address of
+			 * first insn in a couple. To evict
+			 * ephemral space, need to noop both
+			 * op-codes.
+			 */
 			sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+			sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
 		}
 
 		/* Use the standard inner loop. */
@@ -6130,6 +6197,7 @@ sqlite3Select(Parse * pParse,		/* The parser context */
 			    ) {
 				sSort.pOrderBy = 0;
 				sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
+				sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex + 1);
 			}
 
 			/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h
index 53188e7..352f2ae 100644
--- a/src/box/sql/sqliteInt.h
+++ b/src/box/sql/sqliteInt.h
@@ -2000,6 +2000,10 @@ struct AggInfo {
 		FuncDef *pFunc;	/* The aggregate function implementation */
 		int iMem;	/* Memory location that acts as accumulator */
 		int iDistinct;	/* Ephemeral table used to enforce DISTINCT */
+		/**
+		 * Register, holding ephemeral's space pointer.
+		 */
+		int reg_eph;
 	} *aFunc;
 	int nFunc;		/* Number of entries in aFunc[] */
 };
@@ -2591,6 +2595,8 @@ struct SelectDest {
 	u8 eDest;		/* How to dispose of the results.  On of SRT_* above. */
 	char *zAffSdst;		/* Affinity used when eDest==SRT_Set */
 	int iSDParm;		/* A parameter used by the eDest disposal method */
+	/** Register containing ephemeral's space pointer. */
+	int reg_eph;
 	int iSdst;		/* Base register where results are written */
 	int nSdst;		/* Number of registers allocated */
 	ExprList *pOrderBy;	/* Key columns for SRT_Queue and SRT_DistQueue */
@@ -3508,7 +3514,6 @@ Select *sqlite3SelectNew(Parse *, ExprList *, SrcList *, Expr *, ExprList *,
 struct Table *
 sql_lookup_table(struct Parse *parse, struct SrcList_item *tbl_name);
 
-void sqlite3OpenTable(Parse *, int iCur, struct space *, int);
 /**
  * Generate code for a DELETE FROM statement.
  *
@@ -3828,14 +3833,14 @@ vdbe_emit_constraint_checks(struct Parse *parse_context,
  * @cursor_id.
  *
  * @param v Virtual database engine.
- * @param cursor_id Primary index cursor.
+ * @param space Pointer to space object.
  * @param raw_data_reg Register with raw data to insert.
  * @param tuple_len Number of registers to hold the tuple.
  * @param on_conflict On conflict action.
  */
 void
-vdbe_emit_insertion_completion(struct Vdbe *v, int cursor_id, int raw_data_reg,
-			       uint32_t tuple_len,
+vdbe_emit_insertion_completion(struct Vdbe *v, struct space *space,
+			       int raw_data_reg, uint32_t tuple_len,
 			       enum on_conflict_action on_conflict);
 
 void
@@ -4529,7 +4534,7 @@ void sqlite3StrAccumAppendAll(StrAccum *, const char *);
 void sqlite3AppendChar(StrAccum *, int, char);
 char *sqlite3StrAccumFinish(StrAccum *);
 void sqlite3StrAccumReset(StrAccum *);
-void sqlite3SelectDestInit(SelectDest *, int, int);
+void sqlite3SelectDestInit(SelectDest *, int, int, int);
 Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
 
 int sqlite3ExprCheckIN(Parse *, Expr *);
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index 8017742..daab3c8 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -97,9 +97,21 @@ sql_index_update_table_name(struct index_def *idef, const char *new_tbl_name,
 int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
 				  const char *zOldName, const char *zNewName);
 
-/* Interface for ephemeral tables. */
-int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count,
-				    struct sql_key_info *key_info);
+/**
+ * Create ephemeral space. Features of ephemeral spaces: id == 0,
+ * name == "ephemeral", memtx engine (in future it can be changed,
+ * but now only memtx engine is supported), primary index which
+ * covers all fields and no secondary indexes, given field number
+ * and collation sequence. All fields are scalar and nullable.
+ *
+ * @param field_count Number of fields in ephemeral space.
+ * @param key_info Keys description for new ephemeral space.
+ *
+ * @retval Pointer to created space, NULL if error.
+ */
+struct space *
+sql_ephemeral_space_create(uint32_t filed_count, struct sql_key_info *key_info);
+
 /**
  * Insert tuple into ephemeral space.
  * In contrast to ordinary spaces, there is no need to create and
@@ -117,7 +129,18 @@ int tarantoolSqlite3EphemeralDelete(BtCursor * pCur);
 int tarantoolSqlite3EphemeralCount(BtCursor * pCur, i64 * pnEntry);
 int tarantoolSqlite3EphemeralDrop(BtCursor * pCur);
 int tarantoolSqlite3EphemeralClearTable(BtCursor * pCur);
-int tarantoolSqlite3EphemeralGetMaxId(BtCursor * pCur, uint32_t fieldno,
+
+/**
+ * Extract maximum integer value from ephemeral space.
+ * If index is empty - return 0 in max_id and success status.
+ *
+ * @param space Pointer to ephemeral space.
+ * @param fieldno Number of field from fetching tuple.
+ * @param[out] max_id Fetched max value.
+ *
+ * @retval 0 on success, -1 otherwise.
+ */
+int tarantoolSqlite3EphemeralGetMaxId(struct space *space, uint32_t fieldno,
 				       uint64_t * max_id);
 
 /**
diff --git a/src/box/sql/trigger.c b/src/box/sql/trigger.c
index c6bd15b..c38f9cd 100644
--- a/src/box/sql/trigger.c
+++ b/src/box/sql/trigger.c
@@ -205,15 +205,9 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
 		if (db->mallocFailed)
 			goto cleanup;
 
-		int cursor = parse->nTab++;
 		struct space *_trigger = space_by_id(BOX_TRIGGER_ID);
 		assert(_trigger != NULL);
-		vdbe_emit_open_cursor(parse, cursor, 0, _trigger);
 
-		/*
-		 * makerecord(cursor(iRecord),
-		 * [reg(first_col), reg(first_col+1)]).
-		 */
 		int first_col = parse->nMem + 1;
 		parse->nMem += 3;
 		int record = ++parse->nMem;
@@ -245,10 +239,10 @@ sql_trigger_finish(struct Parse *parse, struct TriggerStep *step_list,
 		sqlite3VdbeAddOp4(v, OP_Blob, opts_buff_sz, first_col + 2,
 				  SQL_SUBTYPE_MSGPACK, opts_buff, P4_DYNAMIC);
 		sqlite3VdbeAddOp3(v, OP_MakeRecord, first_col, 3, record);
-		sqlite3VdbeAddOp2(v, OP_IdxInsert, cursor, record);
+		sqlite3VdbeAddOp4(v, OP_IdxInsert, record, 0, 0,
+				  (char *)_trigger, P4_SPACEPTR);
 
 		sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);
-		sqlite3VdbeAddOp1(v, OP_Close, cursor);
 
 		sql_set_multi_write(parse, false);
 	} else {
@@ -694,7 +688,7 @@ codeTriggerProgram(Parse * pParse,	/* The parser context */
 				SelectDest sDest;
 				Select *pSelect =
 				    sqlite3SelectDup(db, pStep->pSelect, 0);
-				sqlite3SelectDestInit(&sDest, SRT_Discard, 0);
+				sqlite3SelectDestInit(&sDest, SRT_Discard, 0, -1);
 				sqlite3Select(pParse, pSelect, &sDest);
 				sql_select_delete(db, pSelect);
 				break;
diff --git a/src/box/sql/update.c b/src/box/sql/update.c
index 730872f..0e2d0fd 100644
--- a/src/box/sql/update.c
+++ b/src/box/sql/update.c
@@ -222,13 +222,15 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	 * an ephemeral table.
 	 */
 	uint32_t pk_part_count;
+	struct space *space;
 	if (is_view) {
 		sql_materialize_view(pParse, def->name, pWhere, pk_cursor);
 		/* Number of columns from SELECT plus ID.*/
 		pk_part_count = nKey = def->field_count + 1;
 	} else {
-		vdbe_emit_open_cursor(pParse, pk_cursor, 0,
-				      space_by_id(pTab->def->id));
+		space = pTab->space;
+		assert(space != NULL);
+		vdbe_emit_open_cursor(pParse, pk_cursor, 0, space);
 		pk_part_count = pPk->def->key_def->part_count;
 	}
 
@@ -242,11 +244,12 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 	int iPk = pParse->nMem + 1;
 	pParse->nMem += pk_part_count;
 	regKey = ++pParse->nMem;
+	int reg_eph = ++pParse->nMem;
 	iEph = pParse->nTab++;
 	sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
 
 	/* Address of the OpenEphemeral instruction. */
-	int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iEph,
+	int addrOpen = sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
 					 pk_part_count);
 	pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
 				   WHERE_ONEPASS_DESIRED, pk_cursor);
@@ -276,9 +279,12 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 								pPk->def);
 		sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, pk_part_count,
 				  regKey, zAff, pk_part_count);
-		sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
-		/* Set flag to save memory allocating one by malloc. */
+		/*
+		 * Set flag to save memory allocating one by
+		 * malloc.
+		 */
 		sqlite3VdbeChangeP5(v, 1);
+		sqlite3VdbeAddOp2(v, OP_IdxInsert, regKey, reg_eph);
 	}
 	/* End the database scan loop.
 	 */
@@ -304,6 +310,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		}
 	} else {
 		labelContinue = sqlite3VdbeMakeLabel(v);
+		sqlite3VdbeAddOp3(v, OP_IteratorOpen, iEph, 0, reg_eph);
 		sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak);
 		addrTop = sqlite3VdbeAddOp2(v, OP_RowData, iEph, regKey);
 		sqlite3VdbeAddOp4Int(v, OP_NotFound, pk_cursor, labelContinue,
@@ -437,7 +444,7 @@ sqlite3Update(Parse * pParse,		/* The parser context */
 		sqlite3VdbeJumpHere(v, addr1);
 		if (hasFK)
 			fkey_emit_check(pParse, pTab, 0, regNewPk, aXRef);
-		vdbe_emit_insertion_completion(v, pk_cursor, regNew,
+		vdbe_emit_insertion_completion(v, space, regNew,
 					       pTab->def->field_count,
 					       on_error);
 		/*
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 00ffb0c..7c1015c 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -3054,51 +3054,37 @@ case OP_TTransaction: {
 	break;
 }
 
-/* Opcode: OpenRead P1 P2 P3 P4 P5
+/* Opcode: IteratorReopen P1 P2 P3 P4 P5
  * Synopsis: index id = P2, space ptr = P4
  *
- * Open a cursor for a space specified by pointer in P4 and index
- * id in P2. Give the new cursor an identifier of P1. The P1
- * values need not be contiguous but all P1 values should be
- * small integers. It is an error for P1 to be negative.
- */
-/* Opcode: ReopenIdx P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
- *
- * The ReopenIdx opcode works exactly like OpenRead except that
- * it first checks to see if the cursor on P1 is already open
+ * The IteratorReopen opcode works exactly like IteratorOpen except
+ * that it first checks to see if the cursor on P1 is already open
  * with the same index and if it is this opcode becomes a no-op.
- * In other words, if the cursor is already open, do not reopen it.
+ * In other words, if the cursor is already open, do not reopen
+ * it.
  *
- * The ReopenIdx opcode may only be used with P5 == 0.
+ * The IteratorReopen opcode may only be used with P5 == 0.
  */
-/* Opcode: OpenWrite P1 P2 P3 P4 P5
- * Synopsis: index id = P2, space ptr = P4
+/* Opcode: IteratorOpen P1 P2 P3 P4 P5
+ * Synopsis: index id = P2, space ptr = P4 or reg[P3]
  *
- * For now, OpenWrite is an alias for OpenRead.
- * It exists just due legacy reasons and should be removed:
- * it isn't neccessary to open cursor to make insertion or
- * deletion.
- */
-case OP_ReopenIdx: {
-	int nField;
-	int p2;
-	VdbeCursor *pCur;
-	BtCursor *pBtCur;
-
-	assert(pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
-	pCur = p->apCsr[pOp->p1];
-	p2 = pOp->p2;
-	if (pCur && pCur->uc.pCursor->space == pOp->p4.space &&
-	    pCur->uc.pCursor->index->def->iid == (uint32_t)p2)
+ * Open a cursor for a space specified by pointer in P4 and index
+ * id in P2. Give the new cursor an identifier of P1. The P1
+ * values need not be contiguous but all P1 values should be
+ * small integers. It is an error for P1 to be negative.
+ * If P4 was not set, then P3 supposed to be the register
+ * containing space pointer.
+ */
+case OP_IteratorReopen: {
+	assert(pOp->p5 == 0);
+	struct VdbeCursor *cur = p->apCsr[pOp->p1];
+	if (cur != NULL && cur->uc.pCursor->space == pOp->p4.space &&
+	    cur->uc.pCursor->index->def->iid == (uint32_t)pOp->p2)
 		goto open_cursor_set_hints;
 	/* If the cursor is not currently open or is open on a different
-	 * index, then fall through into OP_OpenRead to force a reopen
+	 * index, then fall through into OP_OpenCursor to force a reopen
 	 */
-case OP_OpenRead:
-case OP_OpenWrite:
-
-	assert(pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ);
+case OP_IteratorOpen:
 	if (box_schema_version() != p->schema_ver &&
 	    (pOp->p5 & OPFLAG_SYSTEMSP) == 0) {
 		p->expired = 1;
@@ -3107,66 +3093,60 @@ case OP_OpenWrite:
 				    "need to re-compile SQL statement");
 		goto abort_due_to_error;
 	}
-	p2 = pOp->p2;
-	struct space *space = pOp->p4.space;
+	struct space *space;
+	if (pOp->p4type == P4_SPACEPTR)
+		space = pOp->p4.space;
+	else
+		space = aMem[pOp->p3].u.p;
 	assert(space != NULL);
-	struct index *index = space_index(space, p2);
+	struct index *index = space_index(space, pOp->p2);
 	assert(index != NULL);
-	/*
-	 * Since Tarantool iterator provides the full tuple,
-	 * we need a number of fields as wide as the table itself.
-	 * Otherwise, not enough slots for row parser cache are
-	 * allocated in VdbeCursor object.
-	 */
-	nField = space->def->field_count;
-	assert(pOp->p1>=0);
-	assert(nField>=0);
-	pCur = allocateCursor(p, pOp->p1, nField, CURTYPE_TARANTOOL);
-	if (pCur==0) goto no_mem;
-	pCur->nullRow = 1;
-	pBtCur = pCur->uc.pCursor;
-	pBtCur->curFlags |= BTCF_TaCursor;
-	pBtCur->space = space;
-	pBtCur->index = index;
-	pBtCur->eState = CURSOR_INVALID;
+	assert(pOp->p1 >= 0);
+	cur = allocateCursor(p, pOp->p1,
+			     space->def->exact_field_count == 0 ?
+			     space->def->field_count :
+			     space->def->exact_field_count,
+			     CURTYPE_TARANTOOL);
+	if (cur == NULL)
+		goto no_mem;
+	struct BtCursor *bt_cur = cur->uc.pCursor;
+	bt_cur->curFlags |= space->def->id == 0 ? BTCF_TEphemCursor :
+				BTCF_TaCursor;
+	bt_cur->space = space;
+	bt_cur->index = index;
+	bt_cur->eState = CURSOR_INVALID;
 	/* Key info still contains sorter order and collation. */
-	pCur->key_def = index->def->key_def;
-
+	cur->key_def = index->def->key_def;
+	cur->nullRow = 1;
 open_cursor_set_hints:
-	pCur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
-	if (rc) goto abort_due_to_error;
+	cur->uc.pCursor->hints = pOp->p5 & OPFLAG_SEEKEQ;
+	if (rc != 0)
+		goto abort_due_to_error;
 	break;
 }
 
 /**
  * Opcode: OpenTEphemeral P1 P2 * P4 *
  * Synopsis:
- * @param P1 index of new cursor to be created.
+ * @param P1 register, where pointer to new space is stored.
  * @param P2 number of columns in a new table.
  * @param P4 key def for new table, NULL is allowed.
  *
- * This opcode creates Tarantool's ephemeral table and sets cursor P1 to it.
+ * This opcode creates Tarantool's ephemeral table and stores pointer
+ * to it into P1 register.
  */
 case OP_OpenTEphemeral: {
-	VdbeCursor *pCx;
-	BtCursor *pBtCur;
 	assert(pOp->p1 >= 0);
 	assert(pOp->p2 > 0);
 	assert(pOp->p4type != P4_KEYINFO || pOp->p4.key_info != NULL);
 
-	pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_TARANTOOL);
-	if (pCx == 0) goto no_mem;
-	pCx->nullRow = 1;
+	struct space *space = sql_ephemeral_space_create(pOp->p2,
+							 pOp->p4.key_info);
 
-	pBtCur = pCx->uc.pCursor;
-	/* Ephemeral spaces don't have space_id */
-	pBtCur->eState = CURSOR_INVALID;
-	pBtCur->curFlags = BTCF_TEphemCursor;
-
-	rc = tarantoolSqlite3EphemeralCreate(pCx->uc.pCursor, pOp->p2,
-					     pOp->p4.key_info);
-	pCx->key_def = pCx->uc.pCursor->index->def->key_def;
-	if (rc) goto abort_due_to_error;
+	if (space == NULL)
+		goto abort_due_to_error;
+	aMem[pOp->p1].u.p = space;
+	aMem[pOp->p1].flags = MEM_Ptr;
 	break;
 }
 
@@ -3708,23 +3688,21 @@ case OP_NextSequenceId: {
 /* Opcode: NextIdEphemeral P1 P2 P3 * *
  * Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]})
  *
- * This opcode works in the same way as OP_NextId does, except it is
- * only applied for ephemeral tables. The difference is in the fact that
- * all ephemeral tables don't have space_id (to be more precise it equals to zero).
+ * This opcode works in the same way as OP_NextId does, except it
+ * is only applied for ephemeral tables. The difference is in the
+ * fact that all ephemeral tables don't have space_id (to be more
+ * precise it equals to zero). This opcode uses register P1 to
+ * fetch pointer to epehemeral space.
  */
 case OP_NextIdEphemeral: {
-	VdbeCursor *pC;
-	int p2;
-	pC = p->apCsr[pOp->p1];
-	p2 = pOp->p2;
+	struct space *space = (struct space*)p->aMem[pOp->p1].u.p;
+	int p2 = pOp->p2;
+	assert(space != NULL);
 	pOut = &aMem[pOp->p3];
-
-	assert(pC->uc.pCursor->curFlags & BTCF_TEphemCursor);
-
-	rc = tarantoolSqlite3EphemeralGetMaxId(pC->uc.pCursor, p2,
+	rc = tarantoolSqlite3EphemeralGetMaxId(space, p2,
 					       (uint64_t *) &pOut->u.i);
-	if (rc) goto abort_due_to_error;
-
+	if (rc != 0)
+		goto abort_due_to_error;
 	pOut->u.i += 1;
 	pOut->flags = MEM_Int;
 	break;
@@ -4242,83 +4220,91 @@ case OP_Next:          /* jump */
 	goto check_for_interrupt;
 }
 
-/* Opcode: IdxInsert P1 P2 * * P5
+/* Opcode: SorterInsert P1 P2 * * *
  * Synopsis: key=r[P2]
  *
- * @param P1 Index of a space cursor.
- * @param P2 Index of a register with MessagePack data to insert.
+ * Register P2 holds an SQL index key made using the
+ * MakeRecord instructions.  This opcode writes that key
+ * into the sorter P1.  Data for the entry is nil.
+ */
+case OP_SorterInsert: {      /* in2 */
+	assert(pOp->p1 >= 0 && pOp->p1 < p->nCursor);
+	struct VdbeCursor *cursor = p->apCsr[pOp->p1];
+	assert(cursor != NULL);
+	assert(isSorter(cursor));
+	pIn2 = &aMem[pOp->p2];
+	assert((pIn2->flags & MEM_Blob) != 0);
+	rc = ExpandBlob(pIn2);
+	if (rc != 0)
+		goto abort_due_to_error;
+	rc = sqlite3VdbeSorterWrite(cursor, pIn2);
+	if (rc != 0)
+		goto abort_due_to_error;
+	break;
+}
+
+/* Opcode: IdxInsert P1 P2 * P4 P5
+ * Synopsis: key=r[P1]
+ *
+ * @param P1 Index of a register with MessagePack data to insert.
+ * @param P2 If P4 is not set, then P2 is register containing pointer
+ *           to space to insert into.
+ * @param P4 Pointer to the struct space to insert to.
  * @param P5 Flags. If P5 contains OPFLAG_NCHANGE, then VDBE
  *        accounts the change in a case of successful insertion in
  *        nChange counter. If P5 contains OPFLAG_OE_IGNORE, then
  *        we are processing INSERT OR INGORE statement. Thus, in
  *        case of conflict we don't raise an error.
  */
-/* Opcode: IdxReplace P1 P2 * * P5
- * Synopsis: key=r[P2]
+/* Opcode: IdxReplace2 P1 * * P4 P5
+ * Synopsis: key=r[P1]
  *
  * This opcode works exactly as IdxInsert does, but in Tarantool
  * internals it invokes box_replace() instead of box_insert().
  */
-/* Opcode: SorterInsert P1 P2 * * *
- * Synopsis: key=r[P2]
- *
- * Register P2 holds an SQL index key made using the
- * MakeRecord instructions.  This opcode writes that key
- * into the sorter P1.  Data for the entry is nil.
- */
-case OP_SorterInsert:       /* in2 */
 case OP_IdxReplace:
-case OP_IdxInsert: {        /* in2 */
-	VdbeCursor *pC;
-
-	assert(pOp->p1>=0 && pOp->p1<p->nCursor);
-	pC = p->apCsr[pOp->p1];
-	assert(pC!=0);
-	assert(isSorter(pC)==(pOp->opcode==OP_SorterInsert));
-	pIn2 = &aMem[pOp->p2];
-	assert(pIn2->flags & MEM_Blob);
-	if (pOp->p5 & OPFLAG_NCHANGE) p->nChange++;
-	assert(pC->eCurType==CURTYPE_TARANTOOL || pOp->opcode==OP_SorterInsert);
+case OP_IdxInsert: {
+	pIn2 = &aMem[pOp->p1];
+	assert((pIn2->flags & MEM_Blob) != 0);
+	if (pOp->p5 & OPFLAG_NCHANGE)
+		p->nChange++;
 	rc = ExpandBlob(pIn2);
-	if (rc) goto abort_due_to_error;
-	if (pOp->opcode==OP_SorterInsert) {
-		rc = sqlite3VdbeSorterWrite(pC, pIn2);
-	} else {
-		BtCursor *pBtCur = pC->uc.pCursor;
-		if (pBtCur->curFlags & BTCF_TaCursor) {
-			/* Make sure that memory has been allocated on region. */
-			assert(aMem[pOp->p2].flags & MEM_Ephem);
-			if (pOp->opcode == OP_IdxInsert)
-				rc = tarantoolSqlite3Insert(pBtCur->space,
-							    pIn2->z,
-							    pIn2->z + pIn2->n);
-			else
-				rc = tarantoolSqlite3Replace(pBtCur->space,
-							     pIn2->z,
-							     pIn2->z + pIn2->n);
-		} else if (pBtCur->curFlags & BTCF_TEphemCursor) {
-			rc = tarantoolSqlite3EphemeralInsert(pBtCur->space,
-							     pIn2->z,
-							     pIn2->z + pIn2->n);
+	if (rc != 0)
+		goto abort_due_to_error;
+	struct space *space;
+	if (pOp->p4type == P4_SPACEPTR)
+		space = pOp->p4.space;
+	else
+		space = aMem[pOp->p2].u.p;
+	assert(space != NULL);
+	if (space->def->id != 0) {
+		/* Make sure that memory has been allocated on region. */
+		assert(aMem[pOp->p1].flags & MEM_Ephem);
+		if (pOp->opcode == OP_IdxInsert) {
+			rc = tarantoolSqlite3Insert(space, pIn2->z,
+						    pIn2->z + pIn2->n);
 		} else {
-			unreachable();
+			rc = tarantoolSqlite3Replace(space, pIn2->z,
+						     pIn2->z + pIn2->n);
 		}
-		pC->cacheStatus = CACHE_STALE;
+	} else {
+		rc = tarantoolSqlite3EphemeralInsert(space, pIn2->z,
+						     pIn2->z + pIn2->n);
 	}
 
 	if (pOp->p5 & OPFLAG_OE_IGNORE) {
 		/* Ignore any kind of failes and do not raise error message */
 		rc = SQLITE_OK;
 		/* If we are in trigger, increment ignore raised counter */
-		if (p->pFrame) {
+		if (p->pFrame)
 			p->ignoreRaised++;
-		}
 	} else if (pOp->p5 & OPFLAG_OE_FAIL) {
 		p->errorAction = ON_CONFLICT_ACTION_FAIL;
 	} else if (pOp->p5 & OPFLAG_OE_ROLLBACK) {
 		p->errorAction = ON_CONFLICT_ACTION_ROLLBACK;
 	}
-	if (rc) goto abort_due_to_error;
+	if (rc != 0)
+		goto abort_due_to_error;
 	break;
 }
 
diff --git a/src/box/sql/where.c b/src/box/sql/where.c
index 01dcc49..6a1bea0 100644
--- a/src/box/sql/where.c
+++ b/src/box/sql/where.c
@@ -4569,12 +4569,12 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 			/* Do nothing */
 		} else if ((pLoop->wsFlags & WHERE_IDX_ONLY) == 0 &&
 			   (wctrlFlags & WHERE_OR_SUBCLAUSE) == 0) {
-			int op = OP_OpenRead;
-			if (pWInfo->eOnePass != ONEPASS_OFF) {
-				op = OP_OpenWrite;
+			if (pWInfo->eOnePass != ONEPASS_OFF)
 				pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
-			};
-			sqlite3OpenTable(pParse, pTabItem->iCursor, space, op);
+			assert(space->index_count > 0);
+			vdbe_emit_open_cursor(pParse, pTabItem->iCursor, 0,
+					      space);
+			VdbeComment((v, "%s", space->def->name));
 			assert(pTabItem->iCursor == pLevel->iTabCur);
 			testcase(pWInfo->eOnePass == ONEPASS_OFF
 				 && pTab->nCol == BMS - 1);
@@ -4591,7 +4591,7 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 		if (pLoop->wsFlags & WHERE_INDEXED) {
 			struct index_def *idx_def = pLoop->index_def;
 			int iIndexCur;
-			int op = OP_OpenRead;
+			int op;
 			/* Check if index is primary. Either of
 			 * points should be true:
 			 * 1. struct Index is non-NULL and is
@@ -4636,12 +4636,12 @@ sqlite3WhereBegin(Parse * pParse,	/* The parser context */
 					}
 				}
 				assert(wctrlFlags & WHERE_ONEPASS_DESIRED);
-				op = OP_OpenWrite;
+				op = OP_IteratorOpen;
 				pWInfo->aiCurOnePass[1] = iIndexCur;
 			} else if (iAuxArg
 				   && (wctrlFlags & WHERE_OR_SUBCLAUSE) != 0) {
 				iIndexCur = iAuxArg;
-				op = OP_ReopenIdx;
+				op = OP_IteratorReopen;
 			} else {
 				iIndexCur = pParse->nTab++;
 			}
diff --git a/src/box/sql/whereInt.h b/src/box/sql/whereInt.h
index 8a3f2ac..4657055 100644
--- a/src/box/sql/whereInt.h
+++ b/src/box/sql/whereInt.h
@@ -416,7 +416,7 @@ struct WhereInfo {
 	ExprList *pOrderBy;	/* The ORDER BY clause or NULL */
 	ExprList *pDistinctSet;	/* DISTINCT over all these values */
 	LogEst iLimit;		/* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
-	int aiCurOnePass[2];	/* OP_OpenWrite cursors for the ONEPASS opt */
+	int aiCurOnePass[2];	/* OP_IteratorOpen cursors for the ONEPASS opt */
 	int iContinue;		/* Jump here to continue with next record */
 	int iBreak;		/* Jump here to break out of the loop */
 	int savedNQueryLoop;	/* pParse->nQueryLoop outside the WHERE loop */
diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
index e2aaca3..8b16119 100644
--- a/src/box/sql/wherecode.c
+++ b/src/box/sql/wherecode.c
@@ -1341,7 +1341,8 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 		int iCovCur = pParse->nTab++;	/* Cursor used for index scans (if any) */
 
 		int regReturn = ++pParse->nMem;	/* Register used with OP_Gosub */
-		int regRowset = 0;	/* Register for RowSet object */
+		int cur_row_set = 0;
+		int reg_row_set = 0;
 		int regPk = 0;	/* Register holding PK */
 		int iLoopBody = sqlite3VdbeMakeLabel(v);	/* Start of loop body */
 		int iRetInit;	/* Address of regReturn init */
@@ -1399,9 +1400,12 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 		 * called on an uninitialized cursor.
 		 */
 		if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
-			regRowset = pParse->nTab++;
+			cur_row_set = pParse->nTab++;
+			reg_row_set = ++pParse->nMem;
 			sqlite3VdbeAddOp2(v, OP_OpenTEphemeral,
-					  regRowset, pk_part_count);
+					  reg_row_set, pk_part_count);
+			sqlite3VdbeAddOp3(v, OP_IteratorOpen, cur_row_set, 0,
+					  reg_row_set);
 			sql_vdbe_set_p4_key_def(pParse, pk_key_def);
 			regPk = ++pParse->nMem;
 		}
@@ -1534,7 +1538,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 						if (iSet) {
 							jmp1 = sqlite3VdbeAddOp4Int
 								(v, OP_Found,
-								 regRowset, 0,
+								 cur_row_set, 0,
 								 r,
 								 pk_part_count);
 							VdbeCoverage(v);
@@ -1545,7 +1549,7 @@ sqlite3WhereCodeOneLoopStart(WhereInfo * pWInfo,	/* Complete information about t
 								 r, pk_part_count, regPk);
 							sqlite3VdbeAddOp2
 								(v, OP_IdxInsert,
-								 regRowset, regPk);
+								 regPk, reg_row_set);
 						}
 
 						/* Release the array of temp registers */

  reply	other threads:[~2018-10-04  7:16 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-01 10:31 [tarantool-patches] " Kirill Yukhin
2018-10-02 11:56 ` [tarantool-patches] " n.pettik
2018-10-03  9:57   ` Kirill Yukhin
2018-10-04  2:21     ` n.pettik
2018-10-04  7:16       ` Kirill Yukhin [this message]
2018-10-04 10:45         ` n.pettik
2018-10-04 11:48           ` Kirill Yukhin
2018-10-04 12:00 ` Kirill Yukhin
2018-10-05  4:39   ` Kirill Yukhin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181004071637.jncwrcmp62r7zucn@tarantool.org \
    --to=kyukhin@tarantool.org \
    --cc=korablev@tarantool.org \
    --cc=tarantool-patches@freelists.org \
    --subject='[tarantool-patches] Re: [PATCH] sql: refactor SQL cursor to remove write ones' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox