[Tarantool-patches] [PATCH v2 2/2] sql: define ephemeral space fields

Vitaliia Ioffe v.ioffe at tarantool.org
Wed Aug 18 13:31:15 MSK 2021


QA LGTM
 
 
--
Vitaliia Ioffe
 
  
>Вторник, 17 августа 2021, 14:56 +03:00 от Mergen Imeev via Tarantool-patches <tarantool-patches at dev.tarantool.org>:
> 
>Prior to this patch, most ephemeral space fields were defined using the
>SCALAR type. After this patch, almost all fields will be properly
>defined. However, there are still cases where SCALAR will be set by
>default. For example, in IN, where rules is still not defined (#4692).
>Another example is when a field is not resolved because of a too complex
>query.
>
>Part of #6213
>---
> src/box/sql.c | 213 ++++++++++++--------
> src/box/sql/delete.c | 35 ++--
> src/box/sql/expr.c | 19 +-
> src/box/sql/insert.c | 57 ++++--
> src/box/sql/select.c | 400 ++++++++++++++++++++++++++++---------
> src/box/sql/sqlInt.h | 66 +++++-
> src/box/sql/tarantoolInt.h | 15 --
> src/box/sql/update.c | 15 +-
> src/box/sql/vdbe.c | 17 +-
> src/box/sql/vdbe.h | 13 +-
> src/box/sql/vdbeaux.c | 12 --
> src/box/sql/where.c | 10 +-
> src/box/sql/wherecode.c | 14 +-
> 13 files changed, 595 insertions(+), 291 deletions(-)
>
>diff --git a/src/box/sql.c b/src/box/sql.c
>index 0e93aa7bb..d15159d6e 100644
>--- a/src/box/sql.c
>+++ b/src/box/sql.c
>@@ -243,114 +243,161 @@ tarantoolsqlCount(struct BtCursor *pCur)
>  return index_count(pCur->index, pCur->iter_type, NULL, 0);
> }
> 
>-struct space *
>-sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
>+struct sql_space_info *
>+sql_space_info_new(uint32_t field_count, uint32_t part_count)
> {
>- struct key_def *def = NULL;
>- uint32_t part_count = field_count;
>- if (key_info != NULL) {
>- def = sql_key_info_to_key_def(key_info);
>- if (def == NULL)
>- return NULL;
>- /*
>- * In case is_pk_rowid is true we can use rowid
>- * as the only part of the key.
>- */
>- if (key_info->is_pk_rowid)
>- part_count = 1;
>+ assert(field_count > 0);
>+ uint32_t info_size = sizeof(struct sql_space_info);
>+ uint32_t field_size = field_count * sizeof(enum field_type);
>+ uint32_t colls_size = field_count * sizeof(uint32_t);
>+ uint32_t parts_size = part_count * sizeof(uint32_t);
>+ uint32_t sort_orders_size = part_count * sizeof(enum sort_order);
>+ uint32_t size = info_size + field_size + colls_size + parts_size +
>+ sort_orders_size;
>+
>+ struct sql_space_info *info = sqlDbMallocRawNN(sql_get(), size);
>+ if (info == NULL) {
>+ diag_set(OutOfMemory, size, "sqlDbMallocRawNN", "info");
>+ return NULL;
>  }
>+ info->types = (enum field_type *)((char *)info + info_size);
>+ info->coll_ids = (uint32_t *)((char *)info->types + field_size);
>+ info->parts = part_count == 0 ? NULL :
>+ (uint32_t *)((char *)info->coll_ids + colls_size);
>+ info->sort_orders = part_count == 0 ? NULL :
>+ (uint32_t *)((char *)info->parts + parts_size);
>+ info->field_count = field_count;
>+ info->part_count = part_count;
> 
>- struct region *region = &fiber()->gc;
>+ for (uint32_t i = 0; i < field_count; ++i) {
>+ info->types[i] = FIELD_TYPE_SCALAR;
>+ info->coll_ids[i] = COLL_NONE;
>+ }
>+ for (uint32_t i = 0; i < part_count; ++i) {
>+ info->parts[i] = i;
>+ info->sort_orders[i] = SORT_ORDER_ASC;
>+
>+ }
>+ return info;
>+}
>+
>+struct sql_space_info *
>+sql_space_info_new_from_space_def(const struct space_def *def)
>+{
>+ uint32_t field_count = def->field_count + 1;
>+ struct sql_space_info *info = sql_space_info_new(field_count, 0);
>+ if (info == NULL)
>+ return NULL;
>+ for (uint32_t i = 0; i < def->field_count; ++i) {
>+ info->types[i] = def->fields[i].type;
>+ info->coll_ids[i] = def->fields[i].coll_id;
>+ }
>+ /* Add one more field for rowid. */
>+ info->types[def->field_count] = FIELD_TYPE_INTEGER;
>+ return info;
>+}
>+
>+struct sql_space_info *
>+sql_space_info_new_from_index_def(const struct index_def *def, bool has_rowid)
>+{
>+ uint32_t field_count = def->key_def->part_count;
>+ if (has_rowid)
>+ ++field_count;
>+ struct sql_space_info *info = sql_space_info_new(field_count, 0);
>+ if (info == NULL)
>+ return NULL;
>+ for (uint32_t i = 0; i < def->key_def->part_count; ++i) {
>+ info->types[i] = def->key_def->parts[i].type;
>+ info->coll_ids[i] = def->key_def->parts[i].coll_id;
>+ }
>+ if (has_rowid)
>+ info->types[def->key_def->part_count] = FIELD_TYPE_INTEGER;
>+ return info;
>+}
>+
>+struct space *
>+sql_ephemeral_space_new(const struct sql_space_info *info)
>+{
>+ uint32_t field_count = info->field_count;
>+ uint32_t part_count = info->parts == NULL ? field_count :
>+ info->part_count;
>+ uint32_t parts_indent = field_count * sizeof(struct field_def);
>+ uint32_t names_indent = part_count * sizeof(struct key_part_def) +
>+ parts_indent;
>  /*
>- * Name of the fields will be "_COLUMN_1", "_COLUMN_2"
>- * and so on. Due to this, length of each name is no more
>- * than strlen("_COLUMN_") plus length of UINT32_MAX
>- * turned to string, which is 10 and plus 1 for \0.
>+ * Name of the fields will be "_COLUMN_1", "_COLUMN_2" and so on. Due to
>+ * this, length of each name is no more than 19 == strlen("_COLUMN_")
>+ * plus length of UINT32_MAX turned to string, which is 10 and plus 1
>+ * for '\0'.
>  */
>- uint32_t name_len = strlen("_COLUMN_") + 11;
>- uint32_t size = field_count * (sizeof(struct field_def) + name_len) +
>- part_count * sizeof(struct key_part_def);
>+ uint32_t size = names_indent + field_count * 19;
>+
>+ struct region *region = &fiber()->gc;
>+ size_t svp = region_used(region);
>  struct field_def *fields = region_aligned_alloc(region, size,
>  alignof(fields[0]));
>  if (fields == NULL) {
>  diag_set(OutOfMemory, size, "region_aligned_alloc", "fields");
>  return NULL;
>  }
>- struct key_part_def *ephemer_key_parts =
>- (void *)fields + field_count * sizeof(struct field_def);
>- static_assert(alignof(*fields) == alignof(*ephemer_key_parts),
>- "allocated in one block, and should have the same "
>- "alignment");
>- char *names = (char *)ephemer_key_parts +
>- part_count * sizeof(struct key_part_def);
>- for (uint32_t i = 0; i < field_count; ++i) {
>- struct field_def *field = &fields[i];
>- field->name = names;
>- names += name_len;
>- sprintf(field->name, "_COLUMN_%d", i);
>- field->is_nullable = true;
>- field->nullable_action = ON_CONFLICT_ACTION_NONE;
>- field->default_value = NULL;
>- field->default_value_expr = NULL;
>- if (def != NULL && i < def->part_count) {
>- assert(def->parts[i].type < field_type_MAX);
>- field->type = def->parts[i].type;
>- field->coll_id = def->parts[i].coll_id;
>- } else {
>- field->coll_id = COLL_NONE;
>- field->type = FIELD_TYPE_SCALAR;
>- }
>+ struct key_part_def *parts = (struct key_part_def *)((char *)fields +
>+ parts_indent);
>+ static_assert(alignof(*fields) == alignof(*parts), "allocated in one "
>+ "block, and should have the same alignment");
>+ char *names = (char *)fields + names_indent;
>+
>+ for (uint32_t i = 0; i < info->field_count; ++i) {
>+ fields[i].name = names;
>+ sprintf(names, "_COLUMN_%d", i);
>+ names += strlen(fields[i].name) + 1;
>+ fields[i].is_nullable = true;
>+ fields[i].nullable_action = ON_CONFLICT_ACTION_NONE;
>+ fields[i].default_value = NULL;
>+ fields[i].default_value_expr = NULL;
>+ fields[i].type = info->types[i];
>+ fields[i].coll_id = info->coll_ids[i];
>  }
>-
>  for (uint32_t i = 0; i < part_count; ++i) {
>- struct key_part_def *part = &ephemer_key_parts[i];
>- /*
>- * In case we need to save initial order of
>- * inserted into ephemeral space rows we use rowid
>- * as the only part of PK. If ephemeral space has
>- * a rowid, it is always the last column.
>- */
>- uint32_t j = i;
>- if (key_info != NULL && key_info->is_pk_rowid)
>- j = field_count - 1;
>- part->fieldno = j;
>- part->nullable_action = ON_CONFLICT_ACTION_NONE;
>- part->is_nullable = true;
>- part->exclude_null = false;
>- part->sort_order = SORT_ORDER_ASC;
>- part->path = NULL;
>- part->type = fields[j].type;
>- part->coll_id = fields[j].coll_id;
>+ uint32_t j = info->parts == NULL ? i : info->parts[i];
>+ parts[i].fieldno = j;
>+ parts[i].nullable_action = ON_CONFLICT_ACTION_NONE;
>+ parts[i].is_nullable = true;
>+ parts[i].exclude_null = false;
>+ parts[i].sort_order = SORT_ORDER_ASC;
>+ parts[i].path = NULL;
>+ parts[i].type = info->types[j];
>+ parts[i].coll_id = info->coll_ids[j];
>  }
>- struct key_def *ephemer_key_def = key_def_new(ephemer_key_parts,
>- part_count, false);
>- if (ephemer_key_def == NULL)
>+
>+ struct key_def *key_def = key_def_new(parts, part_count, false);
>+ if (key_def == NULL)
>  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)
>+ const char *name = "ephemer_idx";
>+ struct index_def *index_def = index_def_new(0, 0, name, strlen(name),
>+ TREE, &index_opts_default,
>+ key_def, NULL);
>+ key_def_delete(key_def);
>+ if (index_def == NULL)
>  return NULL;
> 
>  struct rlist key_list;
>  rlist_create(&key_list);
>- rlist_add_entry(&key_list, ephemer_index_def, link);
>+ rlist_add_entry(&key_list, index_def, link);
> 
>- struct space_def *ephemer_space_def =
>- space_def_new_ephemeral(field_count, fields);
>- if (ephemer_space_def == NULL) {
>- index_def_delete(ephemer_index_def);
>+ struct space_def *space_def = space_def_new_ephemeral(field_count,
>+ fields);
>+ if (space_def == NULL) {
>+ index_def_delete(index_def);
>  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);
>+ struct space *space = space_new_ephemeral(space_def, &key_list);
>+ index_def_delete(index_def);
>+ space_def_delete(space_def);
>+ region_truncate(region, svp);
> 
>- return ephemer_new_space;
>+ return space;
> }
> 
> int tarantoolsqlEphemeralInsert(struct space *space, const char *tuple,
>diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c
>index 5226dd6ea..b82a6874e 100644
>--- a/src/box/sql/delete.c
>+++ b/src/box/sql/delete.c
>@@ -224,12 +224,13 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>  * is held in ephemeral table, there is no PK for
>  * 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 pk_len = is_view ? space->def->field_count + 1 :
>+ space->index[0]->def->key_def->part_count;
>  int eph_cursor = parse->nTab++;
>  int addr_eph_open = sqlVdbeCurrentAddr(v);
>+ struct sql_space_info *info;
>  if (is_view) {
>  /*
>  * At this stage SELECT is already materialized
>@@ -249,22 +250,20 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>  * account that id field as well. That's why pk_len
>  * has one field more than view format.
>  */
>- pk_len = space->def->field_count + 1;
>- parse->nMem += pk_len;
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
>- pk_len);
>+ info = sql_space_info_new_from_space_def(space->def);
>  } else {
>                         assert(space->index_count > 0);
>- pk_info = sql_key_info_new_from_key_def(db,
>- space->index[0]->def->key_def);
>- if (pk_info == NULL)
>- goto delete_from_cleanup;
>- pk_len = pk_info->part_count;
>- parse->nMem += pk_len;
>- sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
>- pk_len, 0,
>- (char *)pk_info, P4_KEYINFO);
>+ struct index_def *index_def = space->index[0]->def;
>+ info = sql_space_info_new_from_index_def(index_def,
>+ false);
>  }
>+ if (info == NULL) {
>+ parse->is_aborted = true;
>+ goto delete_from_cleanup;
>+ }
>+ parse->nMem += pk_len;
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, 0, 0, (char *)info,
>+ P4_DYNAMIC);
> 
>  /* Construct a query to find the primary key for
>  * every row to be deleted, based on the WHERE
>@@ -295,8 +294,9 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
> 
>  /* Extract the primary key for the current row */
>  if (!is_view) {
>- struct key_part_def *part = pk_info->parts;
>- for (int i = 0; i < pk_len; i++, part++) {
>+ struct key_def *def = space->index[0]->def->key_def;
>+ for (int i = 0; i < pk_len; i++) {
>+ struct key_part *part = &def->parts[i];
>  sqlVdbeAddOp3(v, OP_Column, tab_cursor,
>  part->fieldno, reg_pk + i);
>  }
>@@ -373,7 +373,6 @@ sql_table_delete_from(struct Parse *parse, struct SrcList *tab_list,
>  if (one_pass != ONEPASS_OFF) {
>  /* OP_Found will use an unpacked key. */
>  assert(key_len == pk_len);
>- assert(pk_info != NULL || space->def->opts.is_view);
>  sqlVdbeAddOp4Int(v, OP_NotFound, tab_cursor,
>  addr_bypass, reg_key, key_len);
> 
>diff --git a/src/box/sql/expr.c b/src/box/sql/expr.c
>index 6f40183ac..8902c648f 100644
>--- a/src/box/sql/expr.c
>+++ b/src/box/sql/expr.c
>@@ -2740,7 +2740,6 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */
> 
>  switch (pExpr->op) {
>  case TK_IN:{
>- int addr; /* Address of OP_OpenEphemeral instruction */
>  Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
>  int nVal; /* Size of vector pLeft */
> 
>@@ -2761,13 +2760,14 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */
>  */
>  pExpr->iTable = pParse->nTab++;
>  int reg_eph = ++pParse->nMem;
>- addr = sqlVdbeAddOp2(v, OP_OpenTEphemeral,
>- reg_eph, nVal);
>+ struct sql_space_info *info =
>+ sql_space_info_new(nVal, 0);
>+ if (info == NULL)
>+ return 0;
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>  sqlVdbeAddOp3(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;
> 
>  if (ExprHasProperty(pExpr, EP_xIsSelect)) {
>  /* Case 1: expr IN (SELECT ...)
>@@ -2797,7 +2797,6 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */
>  (pParse, pSelect, &dest)) {
>  sqlDbFree(pParse->db,
>  dest.dest_type);
>- sql_key_info_unref(key_info);
>  return 0;
>  }
>  sqlDbFree(pParse->db,
>@@ -2809,7 +2808,7 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */
>  sqlVectorFieldSubexpr
>  (pLeft, i);
>  if (sql_binary_compare_coll_seq(pParse, p, pEList->a[i].pExpr,
>- &key_info->parts[i].coll_id) != 0)
>+ &info->coll_ids[i]) != 0)
>  return 0;
>  }
>  }
>@@ -2829,7 +2828,7 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */
>  bool unused;
>  struct coll *unused_coll;
>  if (sql_expr_coll(pParse, pExpr->pLeft, &unused,
>- &key_info->parts[0].coll_id,
>+ &info->coll_ids[0],
>  &unused_coll) != 0)
>  return 0;
> 
>@@ -2861,8 +2860,6 @@ sqlCodeSubselect(Parse * pParse, /* Parsing context */
>  sqlReleaseTempReg(pParse, r1);
>  sqlReleaseTempReg(pParse, r2);
>  }
>- sqlVdbeChangeP4(v, addr, (void *)key_info,
>- P4_KEYINFO);
>  break;
>  }
> 
>diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
>index 21b4f2407..341c202a2 100644
>--- a/src/box/sql/insert.c
>+++ b/src/box/sql/insert.c
>@@ -60,6 +60,27 @@ sql_emit_table_types(struct Vdbe *v, struct space_def *def, int reg)
>  (char *)colls_type, P4_DYNAMIC);
> }
> 
>+static struct sql_space_info *
>+sql_space_info_new_from_id_list(const struct space_def *def, int len,
>+ const struct IdList *list)
>+{
>+ struct sql_space_info *info = sql_space_info_new(len + 1, 1);
>+ if (info == NULL)
>+ return NULL;
>+ assert(len > 0 && len <= (int)def->field_count);
>+ struct field_def *fields = def->fields;
>+ for (int i = 0; i < len; ++i) {
>+ int j = list == NULL ? i : list->a[i].idx;
>+ info->types[i] = fields[j].type;
>+ info->coll_ids[i] = fields[j].coll_id;
>+ }
>+ /* Add one more field for rowid. */
>+ info->types[len] = FIELD_TYPE_INTEGER;
>+ /* Set rowid as the only part of primary index. */
>+ info->parts[0] = len;
>+ return info;
>+}
>+
> /**
>  * In SQL table can be created with AUTOINCREMENT.
>  * In Tarantool it can be detected as primary key which consists
>@@ -442,34 +463,30 @@ sqlInsert(Parse * pParse, /* Parser context */
>  reg_eph = ++pParse->nMem;
>  regRec = sqlGetTempReg(pParse);
>  regCopy = sqlGetTempRange(pParse, nColumn + 1);
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
>- nColumn + 1);
>  /*
>- * This key_info is used to show that
>- * rowid should be the first part of PK in
>- * case we used AUTOINCREMENT feature.
>- * This way we will save initial order of
>- * the inserted values. The order is
>- * important if we use the AUTOINCREMENT
>- * feature, since changing the order can
>- * change the number inserted instead of
>- * NULL.
>+ * Order of inserted values is important since it is
>+ * possible, that NULL will be inserted in field with
>+ * AUTOINCREMENT. So, the first part of key should be
>+ * rowid. Since each rowid is unique, we do not need any
>+ * other parts.
>  */
>- if (space->sequence != NULL) {
>- struct sql_key_info *key_info =
>- sql_key_info_new(pParse->db,
>- nColumn + 1);
>- key_info->parts[nColumn].type =
>- FIELD_TYPE_UNSIGNED;
>- key_info->is_pk_rowid = true;
>- sqlVdbeChangeP4(v, -1, (void *)key_info,
>- P4_KEYINFO);
>+ struct sql_space_info *info =
>+ sql_space_info_new_from_id_list(space_def,
>+ nColumn,
>+ pColumn);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ goto insert_cleanup;
>  }
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>  addrL = sqlVdbeAddOp1(v, OP_Yield, dest.iSDParm);
>  VdbeCoverage(v);
>  sqlVdbeAddOp2(v, OP_NextIdEphemeral, reg_eph,
>  regCopy + nColumn);
>  sqlVdbeAddOp3(v, OP_Copy, regFromSelect, regCopy, nColumn-1);
>+ sqlVdbeAddOp4(v, OP_ApplyType, regCopy, nColumn + 1, 0,
>+ (char *)info->types, P4_STATIC);
>  sqlVdbeAddOp3(v, OP_MakeRecord, regCopy,
>  nColumn + 1, regRec);
>  /* Set flag to save memory allocating one by malloc. */
>diff --git a/src/box/sql/select.c b/src/box/sql/select.c
>index 7cfe60db6..021e0ebd5 100644
>--- a/src/box/sql/select.c
>+++ b/src/box/sql/select.c
>@@ -99,6 +99,177 @@ struct SortCtx {
> #define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
> #define SORTFLAG_DESC 0xF0
> 
>+static inline uint32_t
>+multi_select_coll_seq(struct Parse *parser, struct Select *p, int n);
>+
>+static struct sql_space_info *
>+sql_space_info_new_from_expr_list(struct Parse *parser, struct ExprList *list,
>+ bool has_rowid)
>+{
>+ int n = list->nExpr;
>+ if (has_rowid)
>+ ++n;
>+ struct sql_space_info *info = sql_space_info_new(n, 0);
>+ if (info == NULL)
>+ return NULL;
>+ for (int i = 0; i < list->nExpr; ++i) {
>+ bool b;
>+ struct coll *coll;
>+ struct Expr *expr = list->a[i].pExpr;
>+ enum field_type type = sql_expr_type(expr);
>+ /*
>+ * Type ANY could mean that field was unresolved. We have no way
>+ * but to set it as SCALAR, however this could lead to
>+ * unexpected change of type.
>+ */
>+ if (type == FIELD_TYPE_ANY)
>+ type = FIELD_TYPE_SCALAR;
>+ uint32_t coll_id;
>+ if (sql_expr_coll(parser, expr, &b, &coll_id, &coll) != 0)
>+ return NULL;
>+ info->types[i] = type;
>+ info->coll_ids[i] = coll_id;
>+ }
>+ if (has_rowid)
>+ info->types[list->nExpr] = FIELD_TYPE_INTEGER;
>+ return info;
>+}
>+
>+static struct sql_space_info *
>+sql_space_info_new_from_order_by(struct Parse *parser, struct Select *select,
>+ struct ExprList *order_by)
>+{
>+ int n = order_by->nExpr + 2;
>+ struct sql_space_info *info = sql_space_info_new(n, 0);
>+ if (info == NULL)
>+ return NULL;
>+ for (int i = 0; i < order_by->nExpr; ++i) {
>+ struct Expr *expr = order_by->a[i].pExpr;
>+ enum field_type type = sql_expr_type(expr);
>+ /*
>+ * Type ANY could mean that field was unresolved. We have no way
>+ * but to set it as SCALAR, however this could lead to
>+ * unexpected change of type.
>+ */
>+ if (type == FIELD_TYPE_ANY)
>+ type = FIELD_TYPE_SCALAR;
>+ info->types[i] = type;
>+ uint32_t *id = &info->coll_ids[i];
>+ if ((expr->flags & EP_Collate) != 0) {
>+ bool b;
>+ struct coll *coll;
>+ if (sql_expr_coll(parser, expr, &b, id, &coll) != 0)
>+ return NULL;
>+ continue;
>+ }
>+ uint32_t fieldno = order_by->a[i].u.x.iOrderByCol - 1;
>+ info->coll_ids[i] = multi_select_coll_seq(parser, select,
>+ fieldno);
>+ if (info->coll_ids[i] != COLL_NONE) {
>+ const char *name = coll_by_id(info->coll_ids[i])->name;
>+ order_by->a[i].pExpr =
>+ sqlExprAddCollateString(parser, expr, name);
>+ }
>+ }
>+ info->types[order_by->nExpr] = FIELD_TYPE_INTEGER;
>+ info->types[order_by->nExpr + 1] = FIELD_TYPE_VARBINARY;
>+ return info;
>+}
>+
>+static struct sql_space_info *
>+sql_space_info_new_for_sorting(struct Parse *parser, struct ExprList *order_by,
>+ struct ExprList *list, int start, bool has_rowid)
>+{
>+ /*
>+ * Index consist of fields that were not included into index plus rowid,
>+ * is has_rowid is TRUE.
>+ */
>+ uint32_t part_count = order_by->nExpr - start;
>+ if (has_rowid)
>+ ++part_count;
>+ /*
>+ * Number of fields is number of parts in index plus number of fields
>+ * that is not appear in ORDER BY, but were in SELECT.
>+ */
>+ uint32_t field_count = part_count;
>+ /* If iOrderByCol != 0 than fields appear in ORDER BY. */
>+ for (int i = 0; i < list->nExpr; ++i)
>+ field_count += list->a[i].u.x.iOrderByCol == 0 ? 1 : 0;
>+
>+ struct sql_space_info *info =
>+ sql_space_info_new(field_count, part_count);
>+ if (info == NULL)
>+ return NULL;
>+ int k;
>+ for (k = 0; k < order_by->nExpr - start; ++k) {
>+ bool b;
>+ struct coll *coll;
>+ struct Expr *expr = order_by->a[k + start].pExpr;
>+ enum field_type type = sql_expr_type(expr);
>+ /*
>+ * Type ANY could mean that field was unresolved. We have no way
>+ * but to set it as SCALAR, however this could lead to
>+ * unexpected change of type.
>+ */
>+ if (type == FIELD_TYPE_ANY)
>+ type = FIELD_TYPE_SCALAR;
>+ uint32_t coll_id;
>+ if (sql_expr_coll(parser, expr, &b, &coll_id, &coll) != 0)
>+ return NULL;
>+ info->types[k] = type;
>+ info->coll_ids[k] = coll_id;
>+ info->sort_orders[k] = order_by->a[k + start].sort_order;
>+ }
>+ if (has_rowid)
>+ info->types[k++] = FIELD_TYPE_INTEGER;
>+ assert(k == (int)part_count);
>+ for (int i = 0; i < list->nExpr; ++i) {
>+ if (list->a[i].u.x.iOrderByCol != 0)
>+ continue;
>+ bool b;
>+ struct Expr *expr = list->a[i].pExpr;
>+ enum field_type type = sql_expr_type(expr);
>+ if (type == FIELD_TYPE_ANY)
>+ type = FIELD_TYPE_SCALAR;
>+ uint32_t id;
>+ struct coll *coll;
>+ if (sql_expr_coll(parser, expr, &b, &id, &coll) != 0)
>+ return NULL;
>+ info->types[k] = type;
>+ info->coll_ids[k] = id;
>+ ++k;
>+ }
>+ assert(k == (int)field_count);
>+ return info;
>+}
>+
>+static struct sql_key_info *
>+sql_key_info_new_from_space_info(const struct sql_space_info *info)
>+{
>+ uint32_t part_count = info->part_count - 1;
>+ assert(part_count > 0);
>+ uint32_t size = sizeof(struct sql_key_info) +
>+ part_count * sizeof(struct key_part_def);
>+ struct sql_key_info *key_info = sqlDbMallocRawNN(sql_get(), size);
>+ if (key_info == NULL)
>+ return NULL;
>+ key_info->db = sql_get();
>+ key_info->key_def = NULL;
>+ key_info->refs = 1;
>+ key_info->part_count = part_count;
>+ for (uint32_t i = 0; i < part_count; ++i) {
>+ key_info->parts[i].fieldno = i;
>+ key_info->parts[i].nullable_action = ON_CONFLICT_ACTION_ABORT;
>+ key_info->parts[i].is_nullable = false;
>+ key_info->parts[i].exclude_null = false;
>+ key_info->parts[i].sort_order = info->sort_orders[i];
>+ key_info->parts[i].path = NULL;
>+ key_info->parts[i].type = info->types[i];
>+ key_info->parts[i].coll_id = info->coll_ids[i];
>+ }
>+ return key_info;
>+}
>+
> /*
>  * Delete all the content of a Select structure. Deallocate the structure
>  * itself only if bFree is true.
>@@ -819,13 +990,32 @@ pushOntoSorter(Parse * pParse, /* Parser context */
>  if (pParse->db->mallocFailed)
>  return;
>  pOp->p2 = nKey + nData;
>- struct sql_key_info *key_info = pOp->p4.key_info;
>+ assert(pOp->opcode == OP_OpenTEphemeral ||
>+ pOp->opcode == OP_SorterOpen);
>+ struct sql_key_info *key_info =
>+ pOp->opcode == OP_SorterOpen ? pOp->p4.key_info :
>+ sql_key_info_new_from_space_info(pOp->p4.space_info);
>  for (uint32_t i = 0; i < key_info->part_count; i++)
>  key_info->parts[i].sort_order = SORT_ORDER_ASC;
>  sqlVdbeChangeP4(v, -1, (char *)key_info, P4_KEYINFO);
>- pOp->p4.key_info = sql_expr_list_to_key_info(pParse,
>- pSort->pOrderBy,
>- nOBSat);
>+ if (pOp->opcode == OP_SorterOpen) {
>+ pOp->p4.key_info =
>+ sql_expr_list_to_key_info(pParse,
>+ pSort->pOrderBy,
>+ nOBSat);
>+ } else {
>+ struct sql_space_info *info =
>+ sql_space_info_new_for_sorting(pParse,
>+ pSort->pOrderBy,
>+ pSelect->pEList,
>+ nOBSat, bSeq);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ return;
>+ }
>+ sqlVdbeChangeP4(v, pSort->addrSortIndex, (char *)info,
>+ P4_DYNAMIC);
>+ }
>  addrJmp = sqlVdbeCurrentAddr(v);
>  sqlVdbeAddOp3(v, OP_Jump, addrJmp + 1, 0, addrJmp + 1);
>  VdbeCoverage(v);
>@@ -1044,21 +1234,33 @@ selectInnerLoop(Parse * pParse, /* The parser context */
>  * space format.
>  */
>  uint32_t excess_field_count = 0;
>+ struct VdbeOp *op = sqlVdbeGetOp(v,
>+ pSort->addrSortIndex);
>  for (i = pSort->nOBSat; i < pSort->pOrderBy->nExpr;
>  i++) {
>  int j = pSort->pOrderBy->a[i].u.x.iOrderByCol;
>- if (j > 0) {
>- excess_field_count++;
>- pEList->a[j - 1].u.x.iOrderByCol =
>- (u16) (i + 1 - pSort->nOBSat);
>+ if (j == 0)
>+ continue;
>+ assert(j > 0);
>+ excess_field_count++;
>+ pEList->a[j - 1].u.x.iOrderByCol =
>+ (uint16_t)(i + 1 - pSort->nOBSat);
>+ if (op->opcode != OP_OpenTEphemeral)
>+ continue;
>+ struct sql_space_info *info = op->p4.space_info;
>+ --info->field_count;
>+ for (int k = j; k < pEList->nExpr; ++k) {
>+ int n = k + pSort->pOrderBy->nExpr + 1;
>+ info->types[n - 1] = info->types[n];
>+ info->coll_ids[n - 1] =
>+ info->coll_ids[n];
>  }
>  }
>- struct VdbeOp *open_eph_op =
>- sqlVdbeGetOp(v, pSort->addrSortIndex);
>- assert(open_eph_op->p2 - excess_field_count > 0);
>- sqlVdbeChangeP2(v, pSort->addrSortIndex,
>- open_eph_op->p2 -
>- excess_field_count);
>+ if (op->opcode != OP_OpenTEphemeral) {
>+ assert(op->p2 - excess_field_count > 0);
>+ sqlVdbeChangeP2(v, pSort->addrSortIndex,
>+ op->p2 - excess_field_count);
>+ }
>  regOrig = 0;
>  assert(eDest == SRT_Set || eDest == SRT_Mem
>  || eDest == SRT_Coroutine
>@@ -1419,7 +1621,6 @@ sql_key_info_new(sql *db, uint32_t part_count)
>  key_info->key_def = NULL;
>  key_info->refs = 1;
>  key_info->part_count = part_count;
>- key_info->is_pk_rowid = false;
>  for (uint32_t i = 0; i < part_count; i++) {
>  struct key_part_def *part = &key_info->parts[i];
>  part->fieldno = i;
>@@ -1434,24 +1635,6 @@ sql_key_info_new(sql *db, uint32_t part_count)
>  return key_info;
> }
> 
>-struct sql_key_info *
>-sql_key_info_new_from_key_def(sql *db, const struct key_def *key_def)
>-{
>- struct sql_key_info *key_info = sqlDbMallocRawNN(db,
>- sql_key_info_sizeof(key_def->part_count));
>- if (key_info == NULL) {
>- sqlOomFault(db);
>- return NULL;
>- }
>- key_info->db = db;
>- key_info->key_def = NULL;
>- key_info->refs = 1;
>- key_info->part_count = key_def->part_count;
>- key_info->is_pk_rowid = false;
>- key_def_dump_parts(key_def, key_info->parts, NULL);
>- return key_info;
>-}
>-
> struct sql_key_info *
> sql_key_info_ref(struct sql_key_info *key_info)
> {
>@@ -2453,22 +2636,26 @@ generateWithRecursiveQuery(Parse * pParse, /* Parsing context */
>  /* Allocate cursors for Current, Queue, and Distinct. */
>  regCurrent = ++pParse->nMem;
>  sqlVdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol);
>+ struct sql_space_info *info;
>  if (pOrderBy) {
>- struct sql_key_info *key_info =
>- sql_multiselect_orderby_to_key_info(pParse, p, 1);
>- sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_queue,
>- pOrderBy->nExpr + 2, 0, (char *)key_info,
>- P4_KEYINFO);
>  VdbeComment((v, "Orderby table"));
>+ info = sql_space_info_new_from_order_by(pParse, p, pOrderBy);
>  destQueue.pOrderBy = pOrderBy;
>  } else {
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_queue, nCol + 1);
>  VdbeComment((v, "Queue table"));
>+ info = sql_space_info_new_from_expr_list(pParse, p->pEList,
>+ true);
>+ }
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ goto end_of_recursive_query;
>  }
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_queue, 0, 0, (char *)info,
>+ P4_DYNAMIC);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, iQueue, 0, reg_queue);
>  if (iDistinct) {
>  p->addrOpenEphm[0] =
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_dist, 1);
>+ sqlVdbeAddOp1(v, OP_OpenTEphemeral, reg_dist);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, iDistinct, 0, reg_dist);
>  p->selFlags |= SF_UsesEphemeral;
>  VdbeComment((v, "Distinct table"));
>@@ -2672,8 +2859,16 @@ multiSelect(Parse * pParse, /* Parsing context */
>  */
>  if (dest.eDest == SRT_EphemTab) {
>  assert(p->pEList);
>- int nCols = p->pEList->nExpr;
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, dest.reg_eph, nCols + 1);
>+ struct sql_space_info *info =
>+ sql_space_info_new_from_expr_list(pParse, p->pEList,
>+ true);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ rc = 1;
>+ goto multi_select_end;
>+ }
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, dest.reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, dest.iSDParm, 0, dest.reg_eph);
>  VdbeComment((v, "Destination temp"));
>  dest.eDest = SRT_Table;
>@@ -2789,10 +2984,9 @@ multiSelect(Parse * pParse, /* Parsing context */
>  unionTab = pParse->nTab++;
>  reg_union = ++pParse->nMem;
>  assert(p->pOrderBy == 0);
>- addr =
>- sqlVdbeAddOp2(v,
>- OP_OpenTEphemeral,
>- reg_union, 0);
>+ addr = sqlVdbeAddOp1(v,
>+ OP_OpenTEphemeral,
>+ reg_union);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, unionTab, 0, reg_union);
>  assert(p->addrOpenEphm[0] == -1);
>  p->addrOpenEphm[0] = addr;
>@@ -2905,9 +3099,8 @@ multiSelect(Parse * pParse, /* Parsing context */
>  reg_eph2 = ++pParse->nMem;
>  assert(p->pOrderBy == 0);
> 
>- addr =
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph1,
>- 0);
>+ addr = sqlVdbeAddOp1(v, OP_OpenTEphemeral,
>+ reg_eph1);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, tab1, 0, reg_eph1);
>  assert(p->addrOpenEphm[0] == -1);
>  p->addrOpenEphm[0] = addr;
>@@ -2927,9 +3120,8 @@ multiSelect(Parse * pParse, /* Parsing context */
> 
>  /* Code the current SELECT into temporary table "tab2"
>  */
>- addr =
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph2,
>- 0);
>+ addr = sqlVdbeAddOp1(v, OP_OpenTEphemeral,
>+ reg_eph2);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, tab2, 0, reg_eph2);
>  assert(p->addrOpenEphm[1] == -1);
>  p->addrOpenEphm[1] = addr;
>@@ -3001,14 +3193,14 @@ multiSelect(Parse * pParse, /* Parsing context */
>  if (p->selFlags & SF_UsesEphemeral) {
>  assert(p->pNext == NULL);
>  int nCol = p->pEList->nExpr;
>- struct sql_key_info *key_info = sql_key_info_new(db, nCol);
>- if (key_info == NULL)
>+ struct sql_space_info *info = sql_space_info_new(nCol, 0);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>  goto multi_select_end;
>- for (int i = 0; i < nCol; i++) {
>- key_info->parts[i].coll_id =
>- multi_select_coll_seq(pParse, p, i);
>  }
>-
>+ for (int i = 0; i < nCol; ++i)
>+ info->coll_ids[i] = multi_select_coll_seq(pParse, p, i);
>+ bool is_info_used = false;
>  for (struct Select *pLoop = p; pLoop; pLoop = pLoop->pPrior) {
>  for (int i = 0; i < 2; i++) {
>  int addr = pLoop->addrOpenEphm[i];
>@@ -3020,13 +3212,15 @@ multiSelect(Parse * pParse, /* Parsing context */
>  break;
>  }
>  sqlVdbeChangeP2(v, addr, nCol);
>- sqlVdbeChangeP4(v, addr,
>- (char *)sql_key_info_ref(key_info),
>- P4_KEYINFO);
>+ sqlVdbeChangeP4(v, addr, (char *)info,
>+ is_info_used ?
>+ P4_STATIC : P4_DYNAMIC);
>+ is_info_used = true;
>  pLoop->addrOpenEphm[i] = -1;
>  }
>  }
>- sql_key_info_unref(key_info);
>+ if (!is_info_used)
>+ sqlDbFree(pParse->db, info);
>  }
> 
>  multi_select_end:
>@@ -5347,17 +5541,22 @@ resetAccumulator(Parse * pParse, AggInfo * pAggInfo)
>  "exactly one argument");
>  pParse->is_aborted = true;
>  pFunc->iDistinct = -1;
>- } else {
>- struct sql_key_info *key_info =
>- sql_expr_list_to_key_info(pParse,
>+ return;
>+ }
>+ assert(pE->x.pList->nExpr == 1);
>+ struct sql_space_info *info =
>+ sql_space_info_new_from_expr_list(pParse,
>  pE->x.pList,
>- 0);
>- sqlVdbeAddOp4(v, OP_OpenTEphemeral,
>- pFunc->reg_eph, 1, 0,
>- (char *)key_info, P4_KEYINFO);
>- sqlVdbeAddOp3(v, OP_IteratorOpen,
>- pFunc->iDistinct, 0, pFunc->reg_eph);
>+ false);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ pFunc->iDistinct = -1;
>+ return;
>  }
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, pFunc->reg_eph, 0,
>+ 0, (char *)info, P4_DYNAMIC);
>+ sqlVdbeAddOp3(v, OP_IteratorOpen, pFunc->iDistinct, 0,
>+ pFunc->reg_eph);
>  }
>  }
> }
>@@ -5862,23 +6061,21 @@ sqlSelect(Parse * pParse, /* The parser context */
>  * that change.
>  */
>  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
>- * and plus one column for ID.
>- */
>- int nCols = pEList->nExpr + sSort.pOrderBy->nExpr + 1;
>- if (key_info->parts[0].sort_order == SORT_ORDER_DESC) {
>- sSort.sortFlags |= SORTFLAG_DESC;
>+ struct sql_space_info *info =
>+ sql_space_info_new_for_sorting(pParse, sSort.pOrderBy,
>+ pEList, 0, true);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ goto select_end;
>  }
>+ if (info->sort_orders[0] == SORT_ORDER_DESC)
>+ sSort.sortFlags |= SORTFLAG_DESC;
>  sSort.addrSortIndex =
>- sqlVdbeAddOp4(v, OP_OpenTEphemeral,
>- sSort.reg_eph,
>- nCols,
>- 0, (char *)key_info, P4_KEYINFO);
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, sSort.reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>+
>  sqlVdbeAddOp3(v, OP_IteratorOpen, sSort.iECursor, 0, sSort.reg_eph);
>  VdbeComment((v, "Sort table"));
>  } else {
>@@ -5888,11 +6085,14 @@ sqlSelect(Parse * pParse, /* The parser context */
>  /* If the output is destined for a temporary table, open that table.
>  */
>  if (pDest->eDest == SRT_EphemTab) {
>- struct sql_key_info *key_info =
>- sql_expr_list_to_key_info(pParse, pEList, 0);
>- sqlVdbeAddOp4(v, OP_OpenTEphemeral, pDest->reg_eph,
>- pEList->nExpr + 1, 0, (char *)key_info,
>- P4_KEYINFO);
>+ struct sql_space_info *info =
>+ sql_space_info_new_from_expr_list(pParse, pEList, true);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ goto select_end;
>+ }
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, pDest->reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, pDest->iSDParm, 0,
>  pDest->reg_eph);
> 
>@@ -5907,8 +6107,15 @@ sqlSelect(Parse * pParse, /* The parser context */
>  }
>  computeLimitRegisters(pParse, p, iEnd);
>  if (p->iLimit == 0 && sSort.addrSortIndex >= 0) {
>+ struct VdbeOp *op = sqlVdbeGetOp(v, sSort.addrSortIndex);
>+ struct sql_key_info *key_info =
>+ sql_key_info_new_from_space_info(op->p4.space_info);
>  sqlVdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
>  sqlVdbeChangeP1(v, sSort.addrSortIndex, sSort.iECursor);
>+ sqlVdbeChangeP2(v, sSort.addrSortIndex,
>+ op->p4.space_info->field_count);
>+ sqlVdbeChangeP4(v, sSort.addrSortIndex, (char *)key_info,
>+ P4_KEYINFO);
>  sqlVdbeChangeToNoop(v, sSort.addrSortIndex + 1);
>  sSort.sortFlags |= SORTFLAG_UseSorter;
>  }
>@@ -5918,13 +6125,16 @@ sqlSelect(Parse * pParse, /* The parser context */
>  if (p->selFlags & SF_Distinct) {
>  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);
>+ struct sql_space_info *info =
>+ sql_space_info_new_from_expr_list(pParse, pEList,
>+ false);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ goto select_end;
>+ }
>  sDistinct.addrTnct = sqlVdbeAddOp4(v, OP_OpenTEphemeral,
>- sDistinct.reg_eph,
>- key_info->part_count,
>- 0, (char *)key_info,
>- P4_KEYINFO);
>+ sDistinct.reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, sDistinct.cur_eph, 0,
>  sDistinct.reg_eph);
>  VdbeComment((v, "Distinct table"));
>diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
>index 1f87e6823..540c3a2ff 100644
>--- a/src/box/sql/sqlInt.h
>+++ b/src/box/sql/sqlInt.h
>@@ -4013,8 +4013,6 @@ struct sql_key_info {
>  struct key_def *key_def;
>  /** Reference counter. */
>  uint32_t refs;
>- /** Rowid should be the only part of PK, if true. */
>- bool is_pk_rowid;
>  /** Number of parts in the key. */
>  uint32_t part_count;
>  /** Definition of the key parts. */
>@@ -4028,12 +4026,6 @@ struct sql_key_info {
> struct sql_key_info *
> sql_key_info_new(sql *db, uint32_t part_count);
> 
>-/**
>- * Allocate a key_info object from the given key definition.
>- */
>-struct sql_key_info *
>-sql_key_info_new_from_key_def(sql *db, const struct key_def *key_def);
>-
> /**
>  * Increment the reference counter of a key_info object.
>  */
>@@ -4055,6 +4047,64 @@ sql_key_info_unref(struct sql_key_info *key_info);
> struct key_def *
> sql_key_info_to_key_def(struct sql_key_info *key_info);
> 
>+/**
>+ * Structure that is used to store information about ephemeral space field types
>+ * and fieldno of key parts.
>+ */
>+struct sql_space_info {
>+ /** Field types of all fields of ephemeral space. */
>+ enum field_type *types;
>+ /** Collation ids of all fields of ephemeral space. */
>+ uint32_t *coll_ids;
>+ /**
>+ * Fieldno key parts of the ephemeral space. If NULL, then the index
>+ * consists of all fields in sequential order.
>+ */
>+ uint32_t *parts;
>+ /** Sort order of index. */
>+ enum sort_order *sort_orders;
>+ /** Number of fields of ephemetal space. */
>+ uint32_t field_count;
>+ /**
>+ * Number of parts in primary index of ephemetal space. If 0 then parts
>+ * is also NULL.
>+ */
>+ uint32_t part_count;
>+};
>+
>+/**
>+ * Allocate and initialize with default values a structure that will be used to
>+ * store information about ephemeral space field types and key parts.
>+ */
>+struct sql_space_info *
>+sql_space_info_new(uint32_t field_count, uint32_t part_count);
>+
>+/**
>+ * Initialize the field types and key parts of space_info with space_def.
>+ * Additionally added one more field type and key part for rowid. Rowid is
>+ * always INTEGER. Key parts will be initialized with the same values as the
>+ * field types. The number of initialized field types and key parts will be the
>+ * same as the field_count in space_def plus one.
>+ */
>+struct sql_space_info *
>+sql_space_info_new_from_space_def(const struct space_def *def);
>+
>+/**
>+ * Initialize the field types and key parts of space_info with index_def.
>+ * Key parts will be initialized with the same values as the field types. The
>+ * number of initialized field types and key parts will be the same as the
>+ * part_count in index_def.
>+ */
>+struct sql_space_info *
>+sql_space_info_new_from_index_def(const struct index_def *def, bool has_rowid);
>+
>+/**
>+ * Allocate and initialize an ephemeral space. Information about field types and
>+ * key parts is taken from the space_info structure.
>+ */
>+struct space *
>+sql_ephemeral_space_new(const struct sql_space_info *info);
>+
> /**
>  * Check if the function implements LIKE-style comparison & if it
>  * is appropriate to apply a LIKE query optimization.
>diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
>index 8fdc50432..adb7b5f9f 100644
>--- a/src/box/sql/tarantoolInt.h
>+++ b/src/box/sql/tarantoolInt.h
>@@ -61,21 +61,6 @@ sql_rename_table(uint32_t space_id, const char *new_name);
> int tarantoolsqlRenameTrigger(const char *zTriggerName,
>  const char *zOldName, const char *zNewName);
> 
>-/**
>- * 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
>diff --git a/src/box/sql/update.c b/src/box/sql/update.c
>index 22f82390c..b4827f242 100644
>--- a/src/box/sql/update.c
>+++ b/src/box/sql/update.c
>@@ -226,9 +226,20 @@ sqlUpdate(Parse * pParse, /* The parser context */
>  iEph = pParse->nTab++;
>  sqlVdbeAddOp2(v, OP_Null, 0, iPk);
> 
>+ struct sql_space_info *info;
>+ assert(space->index_count > 0 || is_view);
>+ if (is_view)
>+ info = sql_space_info_new_from_space_def(def);
>+ else
>+ info = sql_space_info_new_from_index_def(pPk->def, false);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ goto update_cleanup;
>+ }
>  /* Address of the OpenEphemeral instruction. */
>- int addrOpen = sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
>- pk_part_count);
>+ int addrOpen = sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, 0, 0,
>+ (char *)info, P4_DYNAMIC);
>+
>  pWInfo = sqlWhereBegin(pParse, pTabList, pWhere, 0, 0,
>  WHERE_ONEPASS_DESIRED, pk_cursor);
>  if (pWInfo == 0)
>diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
>index fcea9eefe..ce30afc5a 100644
>--- a/src/box/sql/vdbe.c
>+++ b/src/box/sql/vdbe.c
>@@ -2033,10 +2033,9 @@ case OP_Fetch: {
> case OP_ApplyType: {
>  enum field_type *types = pOp->p4.types;
>  assert(types != NULL);
>- assert(types[pOp->p2] == field_type_MAX);
>  pIn1 = &aMem[pOp->p1];
>- enum field_type type;
>- while((type = *(types++)) != field_type_MAX) {
>+ for (int i = 0; i < pOp->p2; ++i, ++pIn1) {
>+ enum field_type type = types[i];
>  assert(pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)]);
>  assert(memIsValid(pIn1));
>  if (mem_cast_implicit(pIn1, type) != 0) {
>@@ -2044,7 +2043,6 @@ case OP_ApplyType: {
>  mem_str(pIn1), field_type_strs[type]);
>  goto abort_due_to_error;
>  }
>- pIn1++;
>  }
>  break;
> }
>@@ -2380,10 +2378,9 @@ open_cursor_set_hints:
> }
> 
> /**
>- * Opcode: OpenTEphemeral P1 P2 * P4 *
>+ * Opcode: OpenTEphemeral P1 * * P4 *
>  * Synopsis:
>  * @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 stores pointer
>@@ -2391,11 +2388,11 @@ open_cursor_set_hints:
>  */
> case OP_OpenTEphemeral: {
>  assert(pOp->p1 >= 0);
>- assert(pOp->p2 > 0);
>- assert(pOp->p4type != P4_KEYINFO || pOp->p4.key_info != NULL);
> 
>- struct space *space = sql_ephemeral_space_create(pOp->p2,
>- pOp->p4.key_info);
>+ assert(pOp->p4type == P4_DYNAMIC || pOp->p4type == P4_STATIC);
>+ struct sql_space_info *info = pOp->p4.space_info;
>+ assert(info != NULL);
>+ struct space *space = sql_ephemeral_space_new(info);
> 
>  if (space == NULL)
>  goto abort_due_to_error;
>diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
>index be112c72d..e40a1a0b3 100644
>--- a/src/box/sql/vdbe.h
>+++ b/src/box/sql/vdbe.h
>@@ -93,6 +93,10 @@ struct VdbeOp {
>  * doing a cast.
>  */
>  enum field_type *types;
>+ /**
>+ * Information about ephemeral space field types and key parts.
>+ */
>+ struct sql_space_info *space_info;
>  } p4;
> #ifdef SQL_ENABLE_EXPLAIN_COMMENTS
>  char *zComment; /* Comment to improve readability */
>@@ -210,15 +214,6 @@ int sqlVdbeDeletePriorOpcode(Vdbe *, u8 op);
> void sqlVdbeChangeP4(Vdbe *, int addr, const char *zP4, int N);
> void sqlVdbeAppendP4(Vdbe *, void *pP4, int p4type);
> 
>-/**
>- * Set the P4 on the most recently added opcode to the key_def for the
>- * index given.
>- * @param Parse context, for error reporting.
>- * @param key_def Definition of a key to set.
>- */
>-void
>-sql_vdbe_set_p4_key_def(struct Parse *parse, struct key_def *key_def);
>-
> VdbeOp *sqlVdbeGetOp(Vdbe *, int);
> int sqlVdbeMakeLabel(Vdbe *);
> void sqlVdbeRunOnlyOnce(Vdbe *);
>diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
>index 61be7b489..2d7800b17 100644
>--- a/src/box/sql/vdbeaux.c
>+++ b/src/box/sql/vdbeaux.c
>@@ -787,18 +787,6 @@ sqlVdbeAppendP4(Vdbe * p, void *pP4, int n)
>  }
> }
> 
>-void
>-sql_vdbe_set_p4_key_def(struct Parse *parse, struct key_def *key_def)
>-{
>- struct Vdbe *v = parse->pVdbe;
>- assert(v != NULL);
>- assert(key_def != NULL);
>- struct sql_key_info *key_info =
>- sql_key_info_new_from_key_def(parse->db, key_def);
>- if (key_info != NULL)
>- sqlVdbeAppendP4(v, key_info, P4_KEYINFO);
>-}
>-
> #ifdef SQL_ENABLE_EXPLAIN_COMMENTS
> /*
>  * Change the comment on the most recently coded instruction. Or
>diff --git a/src/box/sql/where.c b/src/box/sql/where.c
>index 16766f2f8..d8d23161b 100644
>--- a/src/box/sql/where.c
>+++ b/src/box/sql/where.c
>@@ -919,15 +919,15 @@ constructAutomaticIndex(Parse * pParse, /* The parsing context */
>  /* Create the automatic index */
>  assert(pLevel->iIdxCur >= 0);
>  pLevel->iIdxCur = pParse->nTab++;
>- struct sql_key_info *pk_info =
>- sql_key_info_new_from_key_def(pParse->db, idx_def->key_def);
>- if (pk_info == NULL) {
>+ struct sql_space_info *info = sql_space_info_new_from_index_def(idx_def,
>+ true);
>+ if (info == NULL) {
>  pParse->is_aborted = true;
>  return;
>  }
>  int reg_eph = sqlGetTempReg(pParse);
>- sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, nKeyCol + 1, 0,
>- (char *)pk_info, P4_KEYINFO);
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph, 0, 0, (char *)info,
>+ P4_DYNAMIC);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, pLevel->iIdxCur, 0, reg_eph);
>  VdbeComment((v, "for %s", space->def->name));
> 
>diff --git a/src/box/sql/wherecode.c b/src/box/sql/wherecode.c
>index df6cc92e1..0d0cb054d 100644
>--- a/src/box/sql/wherecode.c
>+++ b/src/box/sql/wherecode.c
>@@ -1134,11 +1134,19 @@ sqlWhereCodeOneLoopStart(WhereInfo * pWInfo, /* Complete information about the W
>  if ((pWInfo->wctrlFlags & WHERE_DUPLICATES_OK) == 0) {
>  cur_row_set = pParse->nTab++;
>  reg_row_set = ++pParse->nMem;
>- sqlVdbeAddOp2(v, OP_OpenTEphemeral,
>- reg_row_set, pk_part_count);
>+ struct index_def *index_def = space->index[0]->def;
>+ struct sql_space_info *info =
>+ sql_space_info_new_from_index_def(index_def,
>+ false);
>+ if (info == NULL) {
>+ pParse->is_aborted = true;
>+ return notReady;
>+ }
>+ sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_row_set,
>+ pk_part_count, 0, (char *)info,
>+ P4_DYNAMIC);
>  sqlVdbeAddOp3(v, OP_IteratorOpen, cur_row_set, 0,
>  reg_row_set);
>- sql_vdbe_set_p4_key_def(pParse, pk_key_def);
>  regPk = ++pParse->nMem;
>  }
>  iRetInit = sqlVdbeAddOp2(v, OP_Integer, 0, regReturn);
>--
>2.25.1
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.tarantool.org/pipermail/tarantool-patches/attachments/20210818/3f08e2e7/attachment.htm>


More information about the Tarantool-patches mailing list