[patches] [sql 4/9] sql: introduced Tarantool's ephemeral tables

Nikita Pettik korablev at tarantool.org
Tue Jan 23 23:19:15 MSK 2018


- Added intermediate functions between SQL and Tarantool,
  tarantoolSqlite3Ephemeral* in order to create and use Tarantool's ephemeral
  tables. Also, for this reason added BTCF_TEphemeral flag to BtCursor
  instead of BTCF_TaCursor to call appropriate functions.
- Added struct space* to SQL cursor, which allows to delete ephemeral tables.
  (since ephemeral tables don't have space_id and it is impossible to find them
  by traditional space_id lookup). Moreover, it enables us to avoid using
  box API inasmuch as we can directly call space_execute_* functions having
  struct space*.
- As the first stage of implemenation, ephemeral tables were successfully
  replaced for DELETE FROM ... WHERE statement. It utilized ephemeral space
  as intermediate holder where all tuples to be removed are placed before
  actual deletion.

Part of #2680

Signed-off-by: Nikita Pettik <korablev at tarantool.org>
---
 src/box/index.cc           |   3 +
 src/box/sql.c              | 187 ++++++++++++++++++++++++++++++++++++++++++++-
 src/box/sql/btree.c        |  33 +++++++-
 src/box/sql/btree.h        |   1 +
 src/box/sql/btreeInt.h     |   1 +
 src/box/sql/delete.c       |   2 +-
 src/box/sql/opcodes.c      | 103 ++++++++++++-------------
 src/box/sql/opcodes.h      | 117 ++++++++++++++--------------
 src/box/sql/tarantoolInt.h |   7 ++
 src/box/sql/vdbe.c         |  49 ++++++++++++
 10 files changed, 389 insertions(+), 114 deletions(-)

diff --git a/src/box/index.cc b/src/box/index.cc
index 3c644f0a8..dfddeb32b 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -449,6 +449,9 @@ int
 iterator_next(struct iterator *it, struct tuple **ret)
 {
 	assert(it->next != NULL);
+	/* In case of ephemeral space there is no need to check schema version */
+	if (it->space_id == 0)
+		return it->next(it, ret);
 	if (unlikely(it->schema_version != schema_version)) {
 		struct space *space = space_by_id(it->space_id);
 		if (space == NULL)
diff --git a/src/box/sql.c b/src/box/sql.c
index 3511690ef..01eb88419 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -48,12 +48,15 @@
 #include "schema.h"
 #include "box.h"
 #include "txn.h"
+#include "space.h"
 #include "space_def.h"
 #include "index_def.h"
 #include "tuple.h"
 #include "fiber.h"
 #include "small/region.h"
 #include "session.h"
+#include "xrow.h"
+#include "iproto_constants.h"
 
 static sqlite3 *db;
 
@@ -160,6 +163,8 @@ struct ta_cursor {
 	box_iterator_t    *iter;
 	struct tuple      *tuple_last;
 	enum iterator_type type;
+	/* Used only by ephemeral spaces, for ordinary space == NULL */
+	struct space      *ephem_space;
 	char               key[1];
 };
 
@@ -173,6 +178,13 @@ cursor_seek(BtCursor *pCur, int *pRes, enum iterator_type type,
 static int
 cursor_advance(BtCursor *pCur, int *pRes);
 
+static int
+cursor_ephemeral_seek(BtCursor *pCur, int *pRes, enum iterator_type type,
+		      const char *key, const char *key_end);
+
+static int
+cursor_ephemeral_advance(BtCursor *pCur, int *pRes);
+
 const char *tarantoolErrorMessage()
 {
 	return box_error_message(box_error_last());
@@ -196,7 +208,8 @@ int tarantoolSqlite3CloseCursor(BtCursor *pCur)
 
 const void *tarantoolSqlite3PayloadFetch(BtCursor *pCur, u32 *pAmt)
 {
-	assert(pCur->curFlags & BTCF_TaCursor);
+	assert(pCur->curFlags & BTCF_TaCursor ||
+	       pCur->curFlags & BTCF_TEphemCursor);
 
 	struct ta_cursor *c = pCur->pTaCursor;
 
@@ -225,6 +238,12 @@ tarantoolSqlite3TupleColumnFast(BtCursor *pCur, u32 fieldno, u32 *field_size)
 	return field;
 }
 
+int tarantoolSqlite3EphemeralFirst(BtCursor *pCur, int *pRes)
+{
+	return cursor_ephemeral_seek(pCur, pRes, ITER_GE,
+				     nil_key, nil_key + sizeof(nil_key));
+}
+
 int tarantoolSqlite3First(BtCursor *pCur, int *pRes)
 {
 	return cursor_seek(pCur, pRes, ITER_GE,
@@ -237,6 +256,19 @@ int tarantoolSqlite3Last(BtCursor *pCur, int *pRes)
 			   nil_key, nil_key + sizeof(nil_key));
 }
 
+int tarantoolSqlite3EphemeralNext(BtCursor *pCur, int *pRes)
+{
+	assert(pCur->curFlags & BTCF_TEphemCursor);
+	if (pCur->eState == CURSOR_INVALID) {
+		*pRes = 1;
+		return SQLITE_OK;
+	}
+	assert(pCur->pTaCursor);
+	assert(iterator_direction(
+		((struct ta_cursor *)pCur->pTaCursor)->type) > 0);
+	return cursor_ephemeral_advance(pCur, pRes);
+}
+
 int tarantoolSqlite3Next(BtCursor *pCur, int *pRes)
 {
 	assert(pCur->curFlags & BTCF_TaCursor);
@@ -345,6 +377,80 @@ int tarantoolSqlite3Count(BtCursor *pCur, i64 *pnEntry)
 	return SQLITE_OK;
 }
 
+int tarantoolSqlite3EphemeralCreate(BtCursor *pCur, uint32_t field_count)
+{
+	assert(pCur);
+	assert(pCur->curFlags & BTCF_TEphemCursor);
+
+	struct space_def *ephemer_space_def =
+		space_def_new(0 /* space id */, 0 /* user id */, field_count,
+			      "ephemeral", strlen("ephemeral"),
+			      "memtx", strlen("memtx"),
+			      &space_opts_default, &field_def_default,
+			      0 /* length of field_def */);
+
+	struct key_def *ephemer_key_def = key_def_new(field_count);
+	assert(key_def);
+	for (uint32_t part = 0; part < field_count; ++part) {
+		key_def_set_part(ephemer_key_def, part /* part no */,
+				 part /* filed no */,
+				 FIELD_TYPE_SCALAR, true /* is_nullable */,
+				 NULL /* coll */);
+	}
+
+	struct index_def *ephemer_index_def =
+		index_def_new(0 /*space id */, 0 /* index id */, "ephemer_idx",
+			      strlen("ephemer_idx"), TREE, &index_opts_default,
+			      ephemer_key_def, NULL /* pk def */);
+
+	struct rlist key_list;
+	rlist_create(&key_list);
+	rlist_add_entry(&key_list, ephemer_index_def, link);
+
+	struct space *ephemer_new_space = space_new_ephemeral(ephemer_space_def,
+							      &key_list);
+	struct ta_cursor *c = NULL;
+	c = cursor_create(c, field_count /* key size */);
+	if (!c)
+		return SQLITE_NOMEM;
+
+	c->ephem_space = ephemer_new_space;
+	pCur->pTaCursor = c;
+
+	int unused;
+	return tarantoolSqlite3EphemeralFirst(pCur, &unused);
+}
+
+int tarantoolSqlite3EphemeralInsert(BtCursor *pCur, const BtreePayload *pX)
+{
+	assert(pCur);
+	assert(pCur->curFlags & BTCF_TEphemCursor);
+	struct ta_cursor *c = pCur->pTaCursor;
+	assert(c);
+	assert(c->ephem_space);
+
+	mp_tuple_assert(pX->pKey, pX->pKey + pX->nKey);
+	struct space *space = c->ephem_space;
+	if (space_ephemeral_replace(space, pX->pKey,
+				    pX->pKey + pX->nKey) != 0) {
+		diag_log();
+		return SQLITE_TARANTOOL_ERROR;
+	}
+	return SQLITE_OK;
+}
+
+int tarantoolSqlite3EphemeralDrop(BtCursor *pCur)
+{
+	assert(pCur);
+	assert(pCur->curFlags & BTCF_TEphemCursor);
+
+	struct ta_cursor *c = pCur->pTaCursor;
+	assert(c->ephem_space);
+	space_delete(c->ephem_space);
+
+	return SQLITE_OK;
+}
+
 int tarantoolSqlite3Insert(BtCursor *pCur, const BtreePayload *pX)
 {
 	assert(pCur->curFlags & BTCF_TaCursor);
@@ -967,13 +1073,16 @@ cursor_create(struct ta_cursor *c, size_t key_size)
 {
 	size_t             size;
 	struct ta_cursor  *res;
+	struct space *ephem_space;
 
 	if (c) {
 		size = c->size;
+		ephem_space = c->ephem_space;
 		if (size - offsetof(struct ta_cursor, key) >= key_size)
 			return c;
 	} else {
 		size = sizeof(*c);
+		ephem_space = NULL;
 	}
 
 	while (size - offsetof(struct ta_cursor, key) < key_size)
@@ -982,6 +1091,7 @@ cursor_create(struct ta_cursor *c, size_t key_size)
 	res = realloc(c, size);
 	if (res) {
 		res->size = size;
+		res->ephem_space = ephem_space;
 		if (!c) {
 			res->iter = NULL;
 			res->tuple_last = NULL;
@@ -990,6 +1100,56 @@ cursor_create(struct ta_cursor *c, size_t key_size)
 	return res;
 }
 
+static int
+cursor_ephemeral_seek(BtCursor *pCur, int *pRes, enum iterator_type type,
+		      const char *key, const char *key_end)
+{
+	assert(pCur->curFlags & BTCF_TEphemCursor);
+	assert(key != NULL && key_end != NULL);
+	assert(type < 0 || type >= iterator_type_MAX);
+
+	struct ta_cursor *c = pCur->pTaCursor;
+	assert(c->ephem_space);
+
+	size_t key_size = 0;
+	if (c && c->iter) {
+		box_iterator_free(c->iter);
+		c->iter = NULL;
+	}
+
+	if (type == ITER_EQ || type == ITER_REQ) {
+		key_size = (size_t)(key_end - key);
+	}
+	c = cursor_create(c, key_size);
+	if (!c) {
+		*pRes = 1;
+		return SQLITE_NOMEM;
+	}
+	pCur->pTaCursor = c;
+
+	uint32_t part_count = mp_decode_array(&key);
+	struct space *ephem_space = c->ephem_space;
+	struct index *index = *ephem_space->index;
+	if (key_validate(index->def, type, key, part_count)) {
+		diag_log();
+		return SQLITE_TARANTOOL_ERROR;
+	}
+	mp_tuple_assert(key, key_end);
+
+	struct iterator *it = index_create_iterator(*ephem_space->index, type,
+						    key, part_count);
+	if (it == NULL) {
+		pCur->eState = CURSOR_INVALID;
+		return SQLITE_TARANTOOL_ERROR;
+	}
+	c->iter = it;
+	c->type = type;
+	pCur->eState = CURSOR_VALID;
+	pCur->curIntKey = 0;
+
+	return cursor_ephemeral_advance(pCur, pRes);
+}
+
 /* Cursor positioning. */
 static int
 cursor_seek(BtCursor *pCur, int *pRes, enum iterator_type type,
@@ -1037,6 +1197,31 @@ cursor_seek(BtCursor *pCur, int *pRes, enum iterator_type type,
 	return cursor_advance(pCur, pRes);
 }
 
+static int
+cursor_ephemeral_advance(BtCursor *pCur, int *pRes)
+{
+	assert(pCur->curFlags & BTCF_TEphemCursor);
+	struct ta_cursor *c = pCur->pTaCursor;
+	assert(c);
+	assert(c->iter);
+
+	struct tuple *tuple;
+	if (iterator_next(c->iter, &tuple) != 0)
+		return SQLITE_TARANTOOL_ERROR;
+	if (tuple != NULL && tuple_bless(tuple) == NULL)
+		return SQLITE_TARANTOOL_ERROR;
+	if (c->tuple_last) box_tuple_unref(c->tuple_last);
+	if (tuple) {
+		box_tuple_ref(tuple);
+		*pRes = 0;
+	} else {
+		pCur->eState = CURSOR_INVALID;
+		*pRes = 1;
+	}
+	c->tuple_last = tuple;
+	return SQLITE_OK;
+}
+
 static int
 cursor_advance(BtCursor *pCur, int *pRes)
 {
diff --git a/src/box/sql/btree.c b/src/box/sql/btree.c
index d9935d443..1a2d1f976 100644
--- a/src/box/sql/btree.c
+++ b/src/box/sql/btree.c
@@ -2965,6 +2965,17 @@ sqlite3BtreeCursor(Btree * p,	/* The btree */
 	return rc;
 }
 
+int
+sqlite3BtreeCursorEphemeral(Btree* p, int iTable, int wrFlag,
+			    struct KeyInfo *pKeyInfo, BtCursor * pCur)
+{
+	btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
+	pCur->curFlags |= BTCF_TEphemCursor;
+	pCur->pTaCursor = 0;
+
+	return SQLITE_OK;
+}
+
 /*
  * Return the size of a BtCursor object in bytes.
  *
@@ -3025,6 +3036,9 @@ sqlite3BtreeCloseCursor(BtCursor * pCur)
 		sqlite3_free(pCur->aOverflow);
 		if (pCur->curFlags & BTCF_TaCursor) {
 			tarantoolSqlite3CloseCursor(pCur);
+		} else if (pCur->curFlags & BTCF_TEphemCursor) {
+			tarantoolSqlite3EphemeralDrop(pCur);
+			tarantoolSqlite3CloseCursor(pCur);
 		}
 		/* sqlite3_free(pCur); */
 	}
@@ -3116,7 +3130,8 @@ sqlite3BtreePayloadSize(BtCursor * pCur)
 {
 	assert(cursorHoldsMutex(pCur));
 	assert(pCur->eState == CURSOR_VALID);
-	if (pCur->curFlags & BTCF_TaCursor) {
+	if (pCur->curFlags & BTCF_TaCursor ||
+	    pCur->curFlags & BTCF_TEphemCursor) {
 		u32 sz;
 		tarantoolSqlite3PayloadFetch(pCur, &sz);
 		return sz;
@@ -3244,7 +3259,8 @@ accessPayload(BtCursor * pCur,	/* Cursor pointing to entry to read from */
 	      int eOp		/* zero to read. non-zero to write. */
     )
 {
-	if (pCur->curFlags & BTCF_TaCursor) {
+	if (pCur->curFlags & BTCF_TaCursor ||
+	    pCur->curFlags & BTCF_TEphemCursor) {
 		const void *pPayload;
 		u32 sz;
 		pPayload = tarantoolSqlite3PayloadFetch(pCur, &sz);
@@ -3486,8 +3502,10 @@ sqlite3BtreePayload(BtCursor * pCur, u32 offset, u32 amt, void *pBuf)
 	assert(cursorHoldsMutex(pCur));
 	assert(pCur->eState == CURSOR_VALID);
 	assert((pCur->curFlags & BTCF_TaCursor) ||
+	       (pCur->curFlags & BTCF_TEphemCursor) ||
 	       (pCur->iPage >= 0 && pCur->apPage[pCur->iPage]));
 	assert((pCur->curFlags & BTCF_TaCursor) ||
+	       (pCur->curFlags & BTCF_TEphemCursor) ||
 	       pCur->aiIdx[pCur->iPage] < pCur->apPage[pCur->iPage]->nCell);
 	return accessPayload(pCur, offset, amt, (unsigned char *)pBuf, 0);
 }
@@ -3832,6 +3850,9 @@ sqlite3BtreeFirst(BtCursor * pCur, int *pRes)
 	if (pCur->curFlags & BTCF_TaCursor) {
 		return tarantoolSqlite3First(pCur, pRes);
 	}
+	if (pCur->curFlags & BTCF_TEphemCursor) {
+		return tarantoolSqlite3EphemeralFirst(pCur, pRes);
+	}
 	rc = moveToRoot(pCur);
 	if (rc == SQLITE_OK) {
 		if (pCur->eState == CURSOR_INVALID) {
@@ -4317,13 +4338,16 @@ sqlite3BtreeNext(BtCursor * pCur, int *pRes)
 	pCur->info.nSize = 0;
 	pCur->curFlags &= ~(BTCF_ValidNKey | BTCF_ValidOvfl);
 	*pRes = 0;
-	if (pCur->curFlags & BTCF_TaCursor) {
+	if (pCur->curFlags & BTCF_TaCursor ||
+	    pCur->curFlags & BTCF_TEphemCursor) {
 		if (pCur->eState != CURSOR_VALID) {
 			int rc = restoreCursorPosition(pCur);
 			if (rc != SQLITE_OK) {
 				return rc;
 			}
 		}
+		if (pCur->curFlags & BTCF_TEphemCursor)
+			return tarantoolSqlite3EphemeralNext(pCur, pRes);
 		return tarantoolSqlite3Next(pCur, pRes);
 	}
 	if (pCur->eState != CURSOR_VALID)
@@ -6795,6 +6819,9 @@ sqlite3BtreeInsert(BtCursor * pCur,	/* Insert data into the table of this cursor
 		return tarantoolSqlite3Insert(pCur, pX);
 	}
 
+	if (pCur->curFlags & BTCF_TEphemCursor) {
+		return tarantoolSqlite3EphemeralInsert(pCur, pX);
+	}
 	/* Save the positions of any other cursors open on this table.
 	 *
 	 * In some cases, the call to btreeMoveto() below is a no-op. For
diff --git a/src/box/sql/btree.h b/src/box/sql/btree.h
index 2b5b687cd..df0f24f06 100644
--- a/src/box/sql/btree.h
+++ b/src/box/sql/btree.h
@@ -221,6 +221,7 @@ int sqlite3BtreeCursor(Btree *,	/* BTree containing table to open */
 		       struct KeyInfo *,	/* First argument to compare function */
 		       BtCursor * pCursor	/* Space to write cursor structure */
     );
+int sqlite3BtreeCursorEphemeral(Btree *, int, int, struct KeyInfo *, BtCursor *);
 int sqlite3BtreeCursorSize(void);
 void sqlite3BtreeCursorZero(BtCursor *);
 void sqlite3BtreeCursorHintFlags(BtCursor *, unsigned);
diff --git a/src/box/sql/btreeInt.h b/src/box/sql/btreeInt.h
index fc4a6ca66..4e6331c3b 100644
--- a/src/box/sql/btreeInt.h
+++ b/src/box/sql/btreeInt.h
@@ -539,6 +539,7 @@ struct BtCursor {
 #define BTCF_Incrblob     0x10	/* True if an incremental I/O handle */
 #define BTCF_Multiple     0x20	/* Maybe another cursor on the same btree */
 #define BTCF_TaCursor     0x80	/* Tarantool cursor, pTaCursor valid */
+#define BTCF_TEphemCursor 0x40	/* Tarantool cursor to ephemeral table  */
 
 /*
  * Potential values for BtCursor.eState.
diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
index 4ee5ac1d6..172509b10 100644
--- a/src/box/sql/delete.c
+++ b/src/box/sql/delete.c
@@ -421,7 +421,7 @@ sqlite3DeleteFrom(Parse * pParse,	/* The parser context */
 			pParse->nMem += nPk;
 			iEphCur = pParse->nTab++;
 			addrEphOpen =
-			    sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur,
+			    sqlite3VdbeAddOp2(v, OP_OpenTEphemeral, iEphCur,
 					      nPk);
 			sqlite3VdbeSetP4KeyInfo(pParse, pPk);
 		}
diff --git a/src/box/sql/opcodes.c b/src/box/sql/opcodes.c
index 84fae8e70..f27568be3 100644
--- a/src/box/sql/opcodes.c
+++ b/src/box/sql/opcodes.c
@@ -117,58 +117,59 @@ const char *sqlite3OpcodeName(int i){
     /* 103 */ "OpenWrite"        OpHelp("root=P2"),
     /* 104 */ "OpenAutoindex"    OpHelp("nColumn=P2"),
     /* 105 */ "OpenEphemeral"    OpHelp("nColumn=P2"),
-    /* 106 */ "SorterOpen"       OpHelp(""),
-    /* 107 */ "SequenceTest"     OpHelp("if (cursor[P1].ctr++) pc = P2"),
-    /* 108 */ "OpenPseudo"       OpHelp("P3 columns in r[P2]"),
-    /* 109 */ "Close"            OpHelp(""),
-    /* 110 */ "ColumnsUsed"      OpHelp(""),
-    /* 111 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
-    /* 112 */ "NextId"           OpHelp("r[P3]=get_max(space_index[P1]{Column[P2]})"),
-    /* 113 */ "FCopy"            OpHelp("reg[P2 at cur_frame]= reg[P1 at root_frame(OPFLAG_SAME_FRAME)]"),
-    /* 114 */ "NewRowid"         OpHelp("r[P2]=rowid"),
+    /* 106 */ "OpenTEphemeral"   OpHelp("nColumn = P2"),
+    /* 107 */ "SorterOpen"       OpHelp(""),
+    /* 108 */ "SequenceTest"     OpHelp("if (cursor[P1].ctr++) pc = P2"),
+    /* 109 */ "OpenPseudo"       OpHelp("P3 columns in r[P2]"),
+    /* 110 */ "Close"            OpHelp(""),
+    /* 111 */ "ColumnsUsed"      OpHelp(""),
+    /* 112 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
+    /* 113 */ "NextId"           OpHelp("r[P3]=get_max(space_index[P1]{Column[P2]})"),
+    /* 114 */ "FCopy"            OpHelp("reg[P2 at cur_frame]= reg[P1 at root_frame(OPFLAG_SAME_FRAME)]"),
     /* 115 */ "Real"             OpHelp("r[P2]=P4"),
-    /* 116 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),
-    /* 117 */ "InsertInt"        OpHelp("intkey=P3 data=r[P2]"),
-    /* 118 */ "Delete"           OpHelp(""),
-    /* 119 */ "ResetCount"       OpHelp(""),
-    /* 120 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
-    /* 121 */ "SorterData"       OpHelp("r[P2]=data"),
-    /* 122 */ "RowData"          OpHelp("r[P2]=data"),
-    /* 123 */ "Rowid"            OpHelp("r[P2]=rowid"),
-    /* 124 */ "NullRow"          OpHelp(""),
-    /* 125 */ "SorterInsert"     OpHelp("key=r[P2]"),
-    /* 126 */ "IdxInsert"        OpHelp("key=r[P2]"),
-    /* 127 */ "IdxDelete"        OpHelp("key=r[P2 at P3]"),
-    /* 128 */ "Seek"             OpHelp("Move P3 to P1.rowid"),
-    /* 129 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
-    /* 130 */ "Destroy"          OpHelp(""),
-    /* 131 */ "Clear"            OpHelp(""),
-    /* 132 */ "ResetSorter"      OpHelp(""),
-    /* 133 */ "CreateIndex"      OpHelp("r[P2]=root"),
-    /* 134 */ "CreateTable"      OpHelp("r[P2]=root"),
-    /* 135 */ "ParseSchema2"     OpHelp("rows=r[P1 at P2]"),
-    /* 136 */ "ParseSchema3"     OpHelp("name=r[P1] sql=r[P1+1]"),
-    /* 137 */ "RenameTable"      OpHelp("P1 = root, P4 = name"),
-    /* 138 */ "LoadAnalysis"     OpHelp(""),
-    /* 139 */ "DropTable"        OpHelp(""),
-    /* 140 */ "DropIndex"        OpHelp(""),
-    /* 141 */ "DropTrigger"      OpHelp(""),
-    /* 142 */ "IntegrityCk"      OpHelp(""),
-    /* 143 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
-    /* 144 */ "Param"            OpHelp(""),
-    /* 145 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
-    /* 146 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
-    /* 147 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
-    /* 148 */ "AggStep0"         OpHelp("accum=r[P3] step(r[P2 at P5])"),
-    /* 149 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2 at P5])"),
-    /* 150 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
-    /* 151 */ "Expire"           OpHelp(""),
-    /* 152 */ "Pagecount"        OpHelp(""),
-    /* 153 */ "MaxPgcnt"         OpHelp(""),
-    /* 154 */ "CursorHint"       OpHelp(""),
-    /* 155 */ "IncMaxid"         OpHelp(""),
-    /* 156 */ "Noop"             OpHelp(""),
-    /* 157 */ "Explain"          OpHelp(""),
+    /* 116 */ "NewRowid"         OpHelp("r[P2]=rowid"),
+    /* 117 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),
+    /* 118 */ "InsertInt"        OpHelp("intkey=P3 data=r[P2]"),
+    /* 119 */ "Delete"           OpHelp(""),
+    /* 120 */ "ResetCount"       OpHelp(""),
+    /* 121 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+    /* 122 */ "SorterData"       OpHelp("r[P2]=data"),
+    /* 123 */ "RowData"          OpHelp("r[P2]=data"),
+    /* 124 */ "Rowid"            OpHelp("r[P2]=rowid"),
+    /* 125 */ "NullRow"          OpHelp(""),
+    /* 126 */ "SorterInsert"     OpHelp("key=r[P2]"),
+    /* 127 */ "IdxInsert"        OpHelp("key=r[P2]"),
+    /* 128 */ "IdxDelete"        OpHelp("key=r[P2 at P3]"),
+    /* 129 */ "Seek"             OpHelp("Move P3 to P1.rowid"),
+    /* 130 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
+    /* 131 */ "Destroy"          OpHelp(""),
+    /* 132 */ "Clear"            OpHelp(""),
+    /* 133 */ "ResetSorter"      OpHelp(""),
+    /* 134 */ "CreateIndex"      OpHelp("r[P2]=root"),
+    /* 135 */ "CreateTable"      OpHelp("r[P2]=root"),
+    /* 136 */ "ParseSchema2"     OpHelp("rows=r[P1 at P2]"),
+    /* 137 */ "ParseSchema3"     OpHelp("name=r[P1] sql=r[P1+1]"),
+    /* 138 */ "RenameTable"      OpHelp("P1 = root, P4 = name"),
+    /* 139 */ "LoadAnalysis"     OpHelp(""),
+    /* 140 */ "DropTable"        OpHelp(""),
+    /* 141 */ "DropIndex"        OpHelp(""),
+    /* 142 */ "DropTrigger"      OpHelp(""),
+    /* 143 */ "IntegrityCk"      OpHelp(""),
+    /* 144 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
+    /* 145 */ "Param"            OpHelp(""),
+    /* 146 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
+    /* 147 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
+    /* 148 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+    /* 149 */ "AggStep0"         OpHelp("accum=r[P3] step(r[P2 at P5])"),
+    /* 150 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2 at P5])"),
+    /* 151 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
+    /* 152 */ "Expire"           OpHelp(""),
+    /* 153 */ "Pagecount"        OpHelp(""),
+    /* 154 */ "MaxPgcnt"         OpHelp(""),
+    /* 155 */ "CursorHint"       OpHelp(""),
+    /* 156 */ "IncMaxid"         OpHelp(""),
+    /* 157 */ "Noop"             OpHelp(""),
+    /* 158 */ "Explain"          OpHelp(""),
   };
   return azName[i];
 }
diff --git a/src/box/sql/opcodes.h b/src/box/sql/opcodes.h
index dfa19b754..28cf70b7a 100644
--- a/src/box/sql/opcodes.h
+++ b/src/box/sql/opcodes.h
@@ -106,58 +106,59 @@
 #define OP_OpenWrite     103 /* synopsis: root=P2                          */
 #define OP_OpenAutoindex 104 /* synopsis: nColumn=P2                       */
 #define OP_OpenEphemeral 105 /* synopsis: nColumn=P2                       */
-#define OP_SorterOpen    106
-#define OP_SequenceTest  107 /* synopsis: if (cursor[P1].ctr++) pc = P2    */
-#define OP_OpenPseudo    108 /* synopsis: P3 columns in r[P2]              */
-#define OP_Close         109
-#define OP_ColumnsUsed   110
-#define OP_Sequence      111 /* synopsis: r[P2]=cursor[P1].ctr++           */
-#define OP_NextId        112 /* synopsis: r[P3]=get_max(space_index[P1]{Column[P2]}) */
-#define OP_FCopy         113 /* synopsis: reg[P2 at cur_frame]= reg[P1 at root_frame(OPFLAG_SAME_FRAME)] */
-#define OP_NewRowid      114 /* synopsis: r[P2]=rowid                      */
+#define OP_OpenTEphemeral 106 /* synopsis: nColumn = P2                     */
+#define OP_SorterOpen    107
+#define OP_SequenceTest  108 /* synopsis: if (cursor[P1].ctr++) pc = P2    */
+#define OP_OpenPseudo    109 /* synopsis: P3 columns in r[P2]              */
+#define OP_Close         110
+#define OP_ColumnsUsed   111
+#define OP_Sequence      112 /* synopsis: r[P2]=cursor[P1].ctr++           */
+#define OP_NextId        113 /* synopsis: r[P3]=get_max(space_index[P1]{Column[P2]}) */
+#define OP_FCopy         114 /* synopsis: reg[P2 at cur_frame]= reg[P1 at root_frame(OPFLAG_SAME_FRAME)] */
 #define OP_Real          115 /* same as TK_FLOAT, synopsis: r[P2]=P4       */
-#define OP_Insert        116 /* synopsis: intkey=r[P3] data=r[P2]          */
-#define OP_InsertInt     117 /* synopsis: intkey=P3 data=r[P2]             */
-#define OP_Delete        118
-#define OP_ResetCount    119
-#define OP_SorterCompare 120 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
-#define OP_SorterData    121 /* synopsis: r[P2]=data                       */
-#define OP_RowData       122 /* synopsis: r[P2]=data                       */
-#define OP_Rowid         123 /* synopsis: r[P2]=rowid                      */
-#define OP_NullRow       124
-#define OP_SorterInsert  125 /* synopsis: key=r[P2]                        */
-#define OP_IdxInsert     126 /* synopsis: key=r[P2]                        */
-#define OP_IdxDelete     127 /* synopsis: key=r[P2 at P3]                     */
-#define OP_Seek          128 /* synopsis: Move P3 to P1.rowid              */
-#define OP_IdxRowid      129 /* synopsis: r[P2]=rowid                      */
-#define OP_Destroy       130
-#define OP_Clear         131
-#define OP_ResetSorter   132
-#define OP_CreateIndex   133 /* synopsis: r[P2]=root                       */
-#define OP_CreateTable   134 /* synopsis: r[P2]=root                       */
-#define OP_ParseSchema2  135 /* synopsis: rows=r[P1 at P2]                    */
-#define OP_ParseSchema3  136 /* synopsis: name=r[P1] sql=r[P1+1]           */
-#define OP_RenameTable   137 /* synopsis: P1 = root, P4 = name             */
-#define OP_LoadAnalysis  138
-#define OP_DropTable     139
-#define OP_DropIndex     140
-#define OP_DropTrigger   141
-#define OP_IntegrityCk   142
-#define OP_RowSetAdd     143 /* synopsis: rowset(P1)=r[P2]                 */
-#define OP_Param         144
-#define OP_FkCounter     145 /* synopsis: fkctr[P1]+=P2                    */
-#define OP_MemMax        146 /* synopsis: r[P1]=max(r[P1],r[P2])           */
-#define OP_OffsetLimit   147 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
-#define OP_AggStep0      148 /* synopsis: accum=r[P3] step(r[P2 at P5])       */
-#define OP_AggStep       149 /* synopsis: accum=r[P3] step(r[P2 at P5])       */
-#define OP_AggFinal      150 /* synopsis: accum=r[P1] N=P2                 */
-#define OP_Expire        151
-#define OP_Pagecount     152
-#define OP_MaxPgcnt      153
-#define OP_CursorHint    154
-#define OP_IncMaxid      155
-#define OP_Noop          156
-#define OP_Explain       157
+#define OP_NewRowid      116 /* synopsis: r[P2]=rowid                      */
+#define OP_Insert        117 /* synopsis: intkey=r[P3] data=r[P2]          */
+#define OP_InsertInt     118 /* synopsis: intkey=P3 data=r[P2]             */
+#define OP_Delete        119
+#define OP_ResetCount    120
+#define OP_SorterCompare 121 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData    122 /* synopsis: r[P2]=data                       */
+#define OP_RowData       123 /* synopsis: r[P2]=data                       */
+#define OP_Rowid         124 /* synopsis: r[P2]=rowid                      */
+#define OP_NullRow       125
+#define OP_SorterInsert  126 /* synopsis: key=r[P2]                        */
+#define OP_IdxInsert     127 /* synopsis: key=r[P2]                        */
+#define OP_IdxDelete     128 /* synopsis: key=r[P2 at P3]                     */
+#define OP_Seek          129 /* synopsis: Move P3 to P1.rowid              */
+#define OP_IdxRowid      130 /* synopsis: r[P2]=rowid                      */
+#define OP_Destroy       131
+#define OP_Clear         132
+#define OP_ResetSorter   133
+#define OP_CreateIndex   134 /* synopsis: r[P2]=root                       */
+#define OP_CreateTable   135 /* synopsis: r[P2]=root                       */
+#define OP_ParseSchema2  136 /* synopsis: rows=r[P1 at P2]                    */
+#define OP_ParseSchema3  137 /* synopsis: name=r[P1] sql=r[P1+1]           */
+#define OP_RenameTable   138 /* synopsis: P1 = root, P4 = name             */
+#define OP_LoadAnalysis  139
+#define OP_DropTable     140
+#define OP_DropIndex     141
+#define OP_DropTrigger   142
+#define OP_IntegrityCk   143
+#define OP_RowSetAdd     144 /* synopsis: rowset(P1)=r[P2]                 */
+#define OP_Param         145
+#define OP_FkCounter     146 /* synopsis: fkctr[P1]+=P2                    */
+#define OP_MemMax        147 /* synopsis: r[P1]=max(r[P1],r[P2])           */
+#define OP_OffsetLimit   148 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggStep0      149 /* synopsis: accum=r[P3] step(r[P2 at P5])       */
+#define OP_AggStep       150 /* synopsis: accum=r[P3] step(r[P2 at P5])       */
+#define OP_AggFinal      151 /* synopsis: accum=r[P1] N=P2                 */
+#define OP_Expire        152
+#define OP_Pagecount     153
+#define OP_MaxPgcnt      154
+#define OP_CursorHint    155
+#define OP_IncMaxid      156
+#define OP_Noop          157
+#define OP_Explain       158
 
 /* Properties such as "out2" or "jump" that are specified in
 ** comments following the "case" for each opcode in the vdbe.c
@@ -183,13 +184,13 @@
 /*  80 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\
 /*  88 */ 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\
 /*  96 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
-/* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,\
-/* 112 */ 0x20, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\
-/* 120 */ 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x04, 0x00,\
-/* 128 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00,\
-/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,\
-/* 144 */ 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00,\
-/* 152 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,}
+/* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 112 */ 0x10, 0x20, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,\
+/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x04,\
+/* 128 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\
+/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 144 */ 0x06, 0x10, 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00,\
+/* 152 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,}
 
 /* The sqlite3P2Values() routine is able to run faster if it knows
 ** the value of the largest JUMP opcode.  The smaller the maximum
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index acbadc5a1..933bf25b8 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -85,6 +85,13 @@ int tarantoolSqlite3RenameTrigger(const char *zTriggerName,
 int tarantoolSqlite3RenameParentTable(int iTab, const char *zOldParentName,
 				      const char *zNewParentName);
 
+/* Interface for ephemeral tables. */
+int tarantoolSqlite3EphemeralCreate(BtCursor * pCur, uint32_t filed_count);
+int tarantoolSqlite3EphemeralInsert(BtCursor * pCur, const BtreePayload * pX);
+int tarantoolSqlite3EphemeralFirst(BtCursor * pCur, int * pRes);
+int tarantoolSqlite3EphemeralNext(BtCursor * pCur, int * pRes);
+int tarantoolSqlite3EphemeralDrop(BtCursor * pCur);
+
 /* Compare against the index key under a cursor -
  * the key may span non-adjacent fields in a random order,
  * ex: [4]-[1]-[2]
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index ea6e03041..2f22508c3 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -3450,6 +3450,55 @@ case OP_OpenEphemeral: {
 	break;
 }
 
+/* Opcode: OpenTEphemeral P1 P2 * * *
+ * Synopsis: nColumn = P2
+ *
+ * This opcode creates Tarantool's ephemeral table and sets cursor P1 to it.
+ */
+case OP_OpenTEphemeral: {
+	VdbeCursor *pCx;
+	KeyInfo *pKeyInfo;
+	static const int vfsFlags =
+		SQLITE_OPEN_READWRITE |
+		SQLITE_OPEN_CREATE |
+		SQLITE_OPEN_EXCLUSIVE |
+		SQLITE_OPEN_DELETEONCLOSE |
+		SQLITE_OPEN_TRANSIENT_DB |
+		SQLITE_OPEN_MEMORY;
+	assert(pOp->p1 >= 0);
+	assert(pOp->p2 > 0);
+
+	pCx = allocateCursor(p, pOp->p1, pOp->p2, CURTYPE_BTREE);
+	if (pCx == 0) goto no_mem;
+	pCx->isEphemeral = 1;
+	pCx->nullRow = 1;
+	rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBtx,
+			      BTREE_OMIT_JOURNAL | BTREE_SINGLE, vfsFlags);
+	if (rc) goto abort_due_to_error;
+	rc = sqlite3BtreeBeginTrans(pCx->pBtx, 0, 1);
+	if (rc) goto abort_due_to_error;
+	if ((pCx->pKeyInfo = pKeyInfo = pOp->p4.pKeyInfo) !=0) {
+		int pgno;
+		assert(pOp->p4type == P4_KEYINFO);
+		rc = sqlite3BtreeCreateTable(pCx->pBtx, &pgno, BTREE_BLOBKEY | pOp->p5);
+		if (rc == SQLITE_OK) {
+			assert(pgno == 2);
+			assert(pKeyInfo->db==db);
+			sqlite3BtreeCursorEphemeral(pCx->pBtx, pgno, BTREE_WRCSR, pKeyInfo,
+						    pCx->uc.pCursor);
+		}
+		pCx->isTable = 0;
+	} else {
+		sqlite3BtreeCursorEphemeral(pCx->pBtx, 1, BTREE_WRCSR, 0,
+					    pCx->uc.pCursor);
+		pCx->isTable = 1;
+	}
+
+	rc = tarantoolSqlite3EphemeralCreate(pCx->uc.pCursor, pOp->p2);
+	if (rc) goto abort_due_to_error;
+	break;
+}
+
 /* Opcode: SorterOpen P1 P2 P3 P4 *
  *
  * This opcode works like OP_OpenEphemeral except that it opens
-- 
2.15.1




More information about the Tarantool-patches mailing list