[tarantool-patches] Re: [PATCH v5 3/6] sql: introduce tuple_fetcher class
Kirill Shcherbatov
kshcherbatov at tarantool.org
Fri May 31 16:45:31 MSK 2019
Hi! Thank you for review.
On 26.05.2019 15:05, Vladislav Shpilevoy wrote:
> Hi! Thanks for the fixes! See 10 comments below.
>
>> diff --git a/src/box/sql.h b/src/box/sql.h
>> index 15ef74b19..3aaeb2274 100644
>> --- a/src/box/sql.h
>> +++ b/src/box/sql.h
>> @@ -386,6 +386,52 @@ sql_src_list_entry_name(const struct SrcList *list, int i);
>> void
>> sqlSrcListDelete(struct sql *db, struct SrcList *list);
>>
>> +/**
>> + * Auxilary VDBE structure to speed-up tuple data field access.
>
> 1. 'Auxilary' -> 'Auxiliary'. Looks like you still did not install
> spell checker, as I asked in some previous patchsets. Please, do it.
Done. I use spell checker. It is unobtrusive. Moreover, this word exists in
this meaning. Doesn't matter. Fixed.
>> +void
>> +tuple_fetcher_create(struct tuple_fetcher *fetcher, struct tuple *tuple,
>> + const char *data, uint32_t data_sz);
>
> 2.>...
> Please, expose two public functions: tuple_fetcher_prepare_data and
> tuple_fetcher_prepare_tuple. Each takes either const char *data or
> struct tuple *tuple. Internally they call tuple_fetcher_create with
> both tuple and data.
>...
Done
>
>>
>> #if defined(__cplusplus)
>> } /* extern "C" { */
>> diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
>> index 2b04d961e..bb0f8f1a4 100644
>> --- a/src/box/sql/tarantoolInt.h
>> +++ b/src/box/sql/tarantoolInt.h
>> @@ -28,7 +28,8 @@ const void *tarantoolsqlPayloadFetch(BtCursor * pCur, u32 * pAmt);
>> * offset to @a fieldno.
>> */
>> const void *
>> -tarantoolsqlTupleColumnFast(BtCursor *pCur, u32 fieldno, u32 *field_size);
>> +tarantool_tuple_field_fast(struct tuple *tuple, uint32_t fieldno,
>> + uint32_t *field_size);
>
> 3. I suggest you to make this function method of the fetcher and static inline
> right above tuple_fetcher_fetch(). It is not needed in any other place.
Done.
> 4. The only reason to make tuple_fetcher_fetch() a separate function
> was to get rid of this parameter and related code, keeping it in
> OP_Column. Please, do it. Remove the parameter and keep it in
> OP_Column.
> 5. Why would ever need that parameter for OP_Fetch? It is not a
> task of a fetcher to return default fields IMO. It should only fetch
> a field, not replace it with a default value.
static int
tuple_fetcher_fetch(struct tuple_fetcher *fetcher, uint32_t field_idx,
struct Mem *dest_mem)
rc = tuple_fetcher_fetch(&pC->fetcher, p2, pDest);
if (rc != SQL_OK)
goto abort_due_to_error;
if ((pDest->flags & MEM_Null) &&
(uint32_t) p2 >= pC->fetcher.field_count &&
default_val_mem != NULL) {
sqlVdbeMemShallowCopy(pDest, default_val_mem, MEM_Static);
}
if ((pDest->flags & MEM_Int) != 0) {
if (field_type == FIELD_TYPE_NUMBER)
sqlVdbeMemSetDouble(pDest, pDest->u.i);
}
>> + * Interpret the data that P1 points as an initialized
>
> 6. "Interpret data P1 points at as an initialized ...".
Fixed.
>> + * Fetch the P2-th column from their tuple. The value extracted
>
> 7. 'Their' is for plural. Use 'its'.
Fixed.
> 8. You never generate OP_Fetch with a default value. Please,
> drop P4 argument. As well as P5. They are never used, even in the
> last patch.
>
>> + *
>> + * Value of P5 register is an 'expected' destination value type.
>
> 9. It is not 'expected'. I can't pass here FIELD_TYPE_STRING and get
> a string value despite an actually stored type. It is rather a flag,
> that you need to transform 'int' into 'number'. Anyway, it should be
> dropped.
Done.
> 10. Why is not this command a part of tuple_fetcher_fetch()?
Don't mind. Done.
=====================================================
Refactored OP_Column instruction with a new tuple_fetcher class.
The tuple_fetcher is a reusable object that speed-up field access
for given tuple.
Introduced OP_Fetch opcode that uses tuple_fetcher given as a
first argument. This opcode makes possible to perform binding
of a new tuple to an existent VDBE without decoding it's fields.
Needed for #3691
---
src/box/sql.c | 54 ++++---
src/box/sql.h | 54 +++++++
src/box/sql/tarantoolInt.h | 12 --
src/box/sql/vdbe.c | 296 ++++++++++++++++++++++---------------
src/box/sql/vdbeInt.h | 11 +-
5 files changed, 270 insertions(+), 157 deletions(-)
diff --git a/src/box/sql.c b/src/box/sql.c
index 385676055..07ceec2d2 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -181,25 +181,6 @@ const void *tarantoolsqlPayloadFetch(BtCursor *pCur, u32 *pAmt)
return tuple_data(pCur->last_tuple);
}
-const void *
-tarantoolsqlTupleColumnFast(BtCursor *pCur, u32 fieldno, u32 *field_size)
-{
- assert(pCur->curFlags & BTCF_TaCursor ||
- pCur->curFlags & BTCF_TEphemCursor);
- assert(pCur->last_tuple != NULL);
-
- struct tuple_format *format = tuple_format(pCur->last_tuple);
- if (fieldno >= tuple_format_field_count(format) ||
- tuple_format_field(format, fieldno)->offset_slot ==
- TUPLE_OFFSET_SLOT_NIL)
- return NULL;
- const char *field = tuple_field(pCur->last_tuple, fieldno);
- const char *end = field;
- mp_next(&end);
- *field_size = end - field;
- return field;
-}
-
/*
* Set cursor to the first tuple in given space.
* It is a simple wrapper around cursor_seek().
@@ -1362,3 +1343,38 @@ sql_checks_resolve_space_def_reference(ExprList *expr_list,
sql_parser_destroy(&parser);
return rc;
}
+
+/**
+ * Initialize a new tuple_fetcher instance with given tuple
+ * data.
+ * @param fetcher The tuple_fetcher instance to initialize.
+ * @param tuple The tuple object pointer or NULL when undefined.
+ * @param data The tuple data (is always defined).
+ * @param data_sz The size of tuple data (is always defined).
+ */
+static void
+tuple_fetcher_create(struct tuple_fetcher *fetcher, struct tuple *tuple,
+ const char *data, uint32_t data_sz)
+{
+ fetcher->tuple = tuple;
+ fetcher->data = data;
+ fetcher->data_sz = data_sz;
+
+ const char *field0 = data;
+ fetcher->field_count = mp_decode_array((const char **) &field0);
+ fetcher->slots[0] = (uint32_t)(field0 - data);
+ fetcher->rightmost_slot = 0;
+}
+
+void
+tuple_fetcher_prepare_data(struct tuple_fetcher *fetcher, const char *data,
+ uint32_t data_sz)
+{
+ tuple_fetcher_create(fetcher, NULL, data, data_sz);
+}
+
+void
+tuple_fetcher_prepare_tuple(struct tuple_fetcher *fetcher, struct tuple *tuple)
+{
+ tuple_fetcher_create(fetcher, tuple, tuple_data(tuple), tuple->bsize);
+}
diff --git a/src/box/sql.h b/src/box/sql.h
index 15ef74b19..a13f78ae8 100644
--- a/src/box/sql.h
+++ b/src/box/sql.h
@@ -386,6 +386,60 @@ sql_src_list_entry_name(const struct SrcList *list, int i);
void
sqlSrcListDelete(struct sql *db, struct SrcList *list);
+/**
+ * Auxiliary VDBE structure to speed-up tuple data field access.
+ * A memory allocation that manage this structure must have
+ * trailing unused bytes that extends the last 'slots' array.
+ * The amount of reserved memory should correspond to the problem
+ * to be solved and is usually equal to the greatest number of
+ * fields in the tuple.
+ *
+ * +------------------------+
+ * | struct tuple_fetcher |
+ * +------------------------+
+ * | RESERVED MEMORY |
+ * +------------------------+
+ */
+struct tuple_fetcher {
+ /** Tuple pointer or NULL when undefined. */
+ struct tuple *tuple;
+ /** Tuple data pointer. */
+ const char *data;
+ /** Tuple data size. */
+ uint32_t data_sz;
+ /** Count of fields in tuple. */
+ uint32_t field_count;
+ /**
+ * Index of the rightmost initialized slot in slots
+ * array.
+ */
+ uint32_t rightmost_slot;
+ /**
+ * Array of offsets of tuple fields.
+ * Only values <= rightmost_slot are valid.
+ */
+ uint32_t slots[1];
+};
+
+/**
+ * Initialize a new tuple_fetcher instance with given tuple
+ * data.
+ * @param fetcher The tuple_fetcher instance to initialize.
+ * @param data The tuple data.
+ * @param data_sz The size of tuple data.
+ */
+void
+tuple_fetcher_prepare_data(struct tuple_fetcher *fetcher, const char *data,
+ uint32_t data_sz);
+
+/**
+ * Initialize a new tuple_fetcher instance with given tuple
+ * data.
+ * @param fetcher The tuple_fetcher instance to initialize.
+ * @param tuple The tuple object pointer.
+ */
+void
+tuple_fetcher_prepare_tuple(struct tuple_fetcher *fetcher, struct tuple *tuple);
#if defined(__cplusplus)
} /* extern "C" { */
diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h
index 2b04d961e..2dd7eef20 100644
--- a/src/box/sql/tarantoolInt.h
+++ b/src/box/sql/tarantoolInt.h
@@ -18,18 +18,6 @@ int is_tarantool_error(int rc);
/* Storage interface. */
const void *tarantoolsqlPayloadFetch(BtCursor * pCur, u32 * pAmt);
-/**
- * Try to get a current tuple field using its field map.
- * @param pCur Btree cursor holding a tuple.
- * @param fieldno Number of a field to get.
- * @param[out] field_size Result field size.
- * @retval not NULL MessagePack field.
- * @retval NULL Can not use field_map - it does not contain
- * offset to @a fieldno.
- */
-const void *
-tarantoolsqlTupleColumnFast(BtCursor *pCur, u32 fieldno, u32 *field_size);
-
int tarantoolsqlFirst(BtCursor * pCur, int *pRes);
int tarantoolsqlLast(BtCursor * pCur, int *pRes);
int tarantoolsqlNext(BtCursor * pCur, int *pRes);
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 91830813d..250646c9b 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -43,6 +43,7 @@
#include "box/error.h"
#include "box/fk_constraint.h"
#include "box/txn.h"
+#include "box/tuple.h"
#include "sqlInt.h"
#include "vdbeInt.h"
#include "tarantoolInt.h"
@@ -612,6 +613,133 @@ mem_type_to_str(const struct Mem *p)
}
}
+/**
+ * Try to get a current tuple field using its field map.
+ * @param tuple Tuple to process.
+ * @param fieldno Number of a field to get.
+ * @param[out] field_size Result field size.
+ * @retval not NULL MessagePack field.
+ * @retval NULL Can not use field_map - it does not contain
+ * offset to @a fieldno.
+ */
+static const void *
+tuple_fetcher_tuple_field_fast(struct tuple_fetcher *fetcher, uint32_t fieldno,
+ uint32_t *field_size)
+{
+ if (fetcher->tuple == NULL)
+ return NULL;
+ struct tuple_format *format = tuple_format(fetcher->tuple);
+ if (fieldno >= tuple_format_field_count(format) ||
+ tuple_format_field(format, fieldno)->offset_slot ==
+ TUPLE_OFFSET_SLOT_NIL)
+ return NULL;
+ const char *field = tuple_field(fetcher->tuple, fieldno);
+ const char *end = field;
+ mp_next(&end);
+ *field_size = end - field;
+ return field;
+}
+
+/**
+ * Fetch field by field_idx using tuple_fetcher and store result
+ * in dest_mem.
+ * @param fetcher The initialized tuple_fetcher instance to use.
+ * @param field_idx The id of the field to fetch.
+ * @param[out] dest_mem The memory variable to store result.
+ * @retval SQL_OK Status code in case of success.
+ * @retval sql_ret_code Error code otherwise.
+ */
+static int
+tuple_fetcher_fetch(struct tuple_fetcher *fetcher, uint32_t field_idx,
+ struct Mem *dest_mem)
+{
+ sqlVdbeMemSetNull(dest_mem);
+ uint32_t *slots = fetcher->slots;
+ if (field_idx >= fetcher->field_count) {
+ UPDATE_MAX_BLOBSIZE(dest_mem);
+ return SQL_OK;
+ }
+
+ /*
+ * Make sure at least the first field_idx + 1 entries
+ * of the header have been parsed and valid information
+ * is in field_map[].
+ * If there is more header available for parsing in the
+ * record, try to extract additional fields up through the
+ * field_map+1-th field.
+ */
+ const char *data;
+ if (fetcher->rightmost_slot <= field_idx) {
+ uint32_t field_sz;
+ data = tuple_fetcher_tuple_field_fast(fetcher, field_idx,
+ &field_sz);
+ if (data != NULL) {
+ /*
+ * Special case for tarantool spaces: for
+ * indexed fields a tuple field map can be
+ * used. Else there is no sense in
+ * tuple_field usage, because it makes
+ * foreach field { mp_next(); } to find
+ * a field. In such a case sql is
+ * better - it saves offsets to all fields
+ * visited in mp_next() cycle.
+ */
+ uint32_t offset = (uint32_t)(data - fetcher->data);
+ slots[field_idx] = offset;
+ slots[field_idx + 1] = offset + field_sz;
+ } else {
+ uint32_t i = fetcher->rightmost_slot;
+ data = fetcher->data + slots[i];
+ do {
+ mp_next(&data);
+ slots[++i] = (uint32_t)(data - fetcher->data);
+ } while (i <= field_idx);
+ fetcher->rightmost_slot = i;
+ }
+ }
+
+ /*
+ * Extract the content for the p2+1-th column. Control
+ * can only reach this point if field_map[field_idx],
+ * field_map[field_idx+1] are valid.
+ */
+ assert(sqlVdbeCheckMemInvariants(dest_mem) != 0);
+ uint32_t dummy;
+ data = fetcher->data + slots[field_idx];
+ if (vdbe_decode_msgpack_into_mem(data, dest_mem, &dummy) != 0)
+ return SQL_TARANTOOL_ERROR;
+
+ /*
+ * MsgPack map, array or extension (unsupported in sql).
+ * Wrap it in a blob verbatim.
+ */
+ if (dest_mem->flags == 0) {
+ dest_mem->n = slots[field_idx + 1] - slots[field_idx];
+ dest_mem->z = (char *) data;
+ dest_mem->flags = MEM_Blob | MEM_Ephem | MEM_Subtype;
+ dest_mem->subtype = SQL_SUBTYPE_MSGPACK;
+ }
+ /*
+ * Add 0 termination (at most for strings)
+ * Not sure why do we check MEM_Ephem
+ */
+ if ((dest_mem->flags & (MEM_Ephem | MEM_Str)) == (MEM_Ephem | MEM_Str)) {
+ int len = dest_mem->n;
+ if (dest_mem->szMalloc < len + 1) {
+ if (sqlVdbeMemGrow(dest_mem, len + 1, 1) != 0)
+ return SQL_NOMEM;
+ } else {
+ dest_mem->z =
+ memcpy(dest_mem->zMalloc, dest_mem->z, len);
+ dest_mem->flags &= ~MEM_Ephem;
+ }
+ dest_mem->z[len] = 0;
+ dest_mem->flags |= MEM_Term;
+ }
+ UPDATE_MAX_BLOBSIZE(dest_mem);
+ return SQL_OK;
+}
+
/*
* Execute as much of a VDBE program as we can.
* This is the core of sql_step().
@@ -2597,12 +2725,7 @@ case OP_Column: {
int p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
BtCursor *pCrsr = NULL; /* The BTree cursor */
- u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
- int i; /* Loop counter */
Mem *pDest; /* Where to write the extracted value */
- const u8 *zData; /* Part of the record being decoded */
- const u8 MAYBE_UNUSED *zEnd; /* Data end */
- const u8 *zParse; /* Next unparsed byte of the row */
Mem *pReg; /* PseudoTable input register */
pC = p->apCsr[pOp->p1];
@@ -2614,7 +2737,6 @@ case OP_Column: {
assert(pOp->p1>=0 && pOp->p1<p->nCursor);
assert(pC!=0);
assert(p2<pC->nField);
- aOffset = pC->aOffset;
assert(pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow);
assert(pC->eCurType!=CURTYPE_SORTER);
@@ -2625,8 +2747,8 @@ case OP_Column: {
pReg = &aMem[pC->uc.pseudoTableReg];
assert(pReg->flags & MEM_Blob);
assert(memIsValid(pReg));
- pC->szRow = pReg->n;
- pC->aRow = (u8*)pReg->z;
+ tuple_fetcher_prepare_data(&pC->fetcher,
+ pReg->z, pReg->n);
} else {
sqlVdbeMemSetNull(pDest);
goto op_column_out;
@@ -2638,134 +2760,70 @@ case OP_Column: {
assert(sqlCursorIsValid(pCrsr));
assert(pCrsr->curFlags & BTCF_TaCursor ||
pCrsr->curFlags & BTCF_TEphemCursor);
- pC->aRow = tarantoolsqlPayloadFetch(pCrsr, &pC->szRow);
-
+ tuple_fetcher_prepare_tuple(&pC->fetcher,
+ pCrsr->last_tuple);
}
pC->cacheStatus = p->cacheCtr;
- zParse = pC->aRow;
- pC->nRowField = mp_decode_array((const char **)&zParse); /* # of fields */
- aOffset[0] = (u32)(zParse - pC->aRow);
- pC->nHdrParsed = 0;
- }
-
- if ( (u32)p2>=pC->nRowField) {
- if (pOp->p4type==P4_MEM) {
- sqlVdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
- } else {
- sqlVdbeMemSetNull(pDest);
- }
- goto op_column_out;
}
- zData = pC->aRow;
- zEnd = zData + pC->szRow;
-
- /*
- * Make sure at least the first p2+1 entries of the header
- * have been parsed and valid information is in aOffset[].
- * If there is more header available for parsing in the
- * record, try to extract additional fields up through the
- * p2+1-th field.
- */
- if (pC->nHdrParsed <= p2) {
- u32 size;
- if (pC->eCurType == CURTYPE_TARANTOOL &&
- pCrsr != NULL && ((pCrsr->curFlags & BTCF_TaCursor) != 0 ||
- (pCrsr->curFlags & BTCF_TEphemCursor)) &&
- (zParse = tarantoolsqlTupleColumnFast(pCrsr, p2,
- &size)) != NULL) {
- /*
- * Special case for tarantool spaces: for
- * indexed fields a tuple field map can be
- * used. Else there is no sense in
- * tuple_field usage, because it makes
- * foreach field { mp_next(); } to find
- * a field. In such a case sql is
- * better - it saves offsets to all fields
- * visited in mp_next() cycle.
- */
- aOffset[p2] = zParse - zData;
- aOffset[p2 + 1] = aOffset[p2] + size;
- } else {
- i = pC->nHdrParsed;
- zParse = zData+aOffset[i];
-
- /*
- * Fill in aOffset[i] values through the
- * p2-th field.
- */
- do{
- mp_next((const char **) &zParse);
- aOffset[++i] = (u32)(zParse-zData);
- }while( i<=p2);
- assert((u32)p2 != pC->nRowField || zParse == zEnd);
- pC->nHdrParsed = i;
- }
- }
-
- /* Extract the content for the p2+1-th column. Control can only
- * reach this point if aOffset[p2], aOffset[p2+1] are
- * all valid.
- */
- assert(rc==SQL_OK);
- assert(sqlVdbeCheckMemInvariants(pDest));
- if (VdbeMemDynamic(pDest)) {
- sqlVdbeMemSetNull(pDest);
- }
- uint32_t unused;
- if (vdbe_decode_msgpack_into_mem((const char *)(zData + aOffset[p2]),
- pDest, &unused) != 0) {
- rc = SQL_TARANTOOL_ERROR;
- goto abort_due_to_error;
- }
- /* MsgPack map, array or extension (unsupported in sql).
- * Wrap it in a blob verbatim.
- */
-
- if (pDest->flags == 0) {
- pDest->n = aOffset[p2+1]-aOffset[p2];
- pDest->z = (char *)zData+aOffset[p2];
- pDest->flags = MEM_Blob|MEM_Ephem|MEM_Subtype;
- pDest->subtype = SQL_SUBTYPE_MSGPACK;
- }
- if ((pDest->flags & MEM_Int) != 0 &&
- pC->eCurType == CURTYPE_TARANTOOL) {
- enum field_type f = FIELD_TYPE_ANY;
+ enum field_type field_type = FIELD_TYPE_ANY;
+ if (pC->eCurType == CURTYPE_TARANTOOL) {
/*
* Ephemeral spaces feature only one index
* covering all fields, but ephemeral spaces
* lack format. So, we can fetch type from
* key parts.
*/
- if (pC->uc.pCursor->curFlags & BTCF_TEphemCursor)
- f = pC->uc.pCursor->index->def->key_def->parts[p2].type;
- else if (pC->uc.pCursor->curFlags & BTCF_TaCursor)
- f = pC->uc.pCursor->space->def->fields[p2].type;
- if (f == FIELD_TYPE_NUMBER)
- sqlVdbeMemSetDouble(pDest, pDest->u.i);
- }
- /*
- * Add 0 termination (at most for strings)
- * Not sure why do we check MEM_Ephem
- */
- if ((pDest->flags & (MEM_Ephem | MEM_Str)) == (MEM_Ephem | MEM_Str)) {
- int len = pDest->n;
- if (pDest->szMalloc<len+1) {
- if (sqlVdbeMemGrow(pDest, len+1, 1))
- goto abort_due_to_error;
- } else {
- pDest->z = memcpy(pDest->zMalloc, pDest->z, len);
- pDest->flags &= ~MEM_Ephem;
+ if (pC->uc.pCursor->curFlags & BTCF_TEphemCursor) {
+ field_type = pC->uc.pCursor->index->def->
+ key_def->parts[p2].type;
+ } else if (pC->uc.pCursor->curFlags & BTCF_TaCursor) {
+ field_type = pC->uc.pCursor->space->def->
+ fields[p2].type;
}
- pDest->z[len] = 0;
- pDest->flags |= MEM_Term;
}
+ struct Mem *default_val_mem =
+ pOp->p4type == P4_MEM ? pOp->p4.pMem : NULL;
+ rc = tuple_fetcher_fetch(&pC->fetcher, p2, pDest);
+ if (rc != SQL_OK)
+ goto abort_due_to_error;
+ if ((pDest->flags & MEM_Null) &&
+ (uint32_t) p2 >= pC->fetcher.field_count &&
+ default_val_mem != NULL) {
+ sqlVdbeMemShallowCopy(pDest, default_val_mem, MEM_Static);
+ }
+ if ((pDest->flags & MEM_Int) != 0) {
+ if (field_type == FIELD_TYPE_NUMBER)
+ sqlVdbeMemSetDouble(pDest, pDest->u.i);
+ }
op_column_out:
- UPDATE_MAX_BLOBSIZE(pDest);
REGISTER_TRACE(p, pOp->p3, pDest);
break;
}
+/* Opcode: Fetch P1 P2 P3 P4 P5
+ * Synopsis: r[P3]=PX
+ *
+ * Interpret data P1 points at as an initialized tuple_fetcher
+ * object.
+ *
+ * Fetch the P2-th column from its tuple. The value extracted
+ * is stored in register P3. If the column contains fewer than
+ * P2 fields, then extract a NULL.
+ */
+case OP_Fetch: {
+ struct tuple_fetcher *fetcher =
+ (struct tuple_fetcher *) p->aMem[pOp->p1].u.p;
+ uint32_t field_idx = pOp->p2;
+ struct Mem *dest_mem = &aMem[pOp->p3];
+ memAboutToChange(p, dest_mem);
+ rc = tuple_fetcher_fetch(fetcher, field_idx, dest_mem);
+ if (rc != SQL_OK)
+ goto abort_due_to_error;
+ REGISTER_TRACE(p, pOp->p3, dest_mem);
+ break;
+}
+
/* Opcode: ApplyType P1 P2 * P4 *
* Synopsis: type(r[P1 at P2])
*
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index d58e3df7b..1a428afa9 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -112,17 +112,14 @@ struct VdbeCursor {
/** Info about keys needed by index cursors. */
struct key_def *key_def;
i16 nField; /* Number of fields in the header */
- u16 nHdrParsed; /* Number of header fields parsed so far */
- const u8 *aRow; /* Data for the current row, if all on one page */
- u32 szRow; /* Byte available in aRow */
#ifdef SQL_ENABLE_COLUMN_USED_MASK
u64 maskUsed; /* Mask of columns used by this cursor */
#endif
- u32 nRowField; /* Number of fields in the current row */
- /* Offsets for all fields in the record [nField+1]
- * Order of fields is the same as it was passes to create table statement
+ /**
+ * Auxiliary VDBE structure to speed-up tuple data
+ * field access.
*/
- u32 aOffset[1];
+ struct tuple_fetcher fetcher;
};
/*
--
2.21.0
More information about the Tarantool-patches
mailing list