From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id 58BFB30537 for ; Fri, 31 May 2019 09:45:34 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id KTKudzzDWyoo for ; Fri, 31 May 2019 09:45:34 -0400 (EDT) Received: from smtp51.i.mail.ru (smtp51.i.mail.ru [94.100.177.111]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id DB9E930535 for ; Fri, 31 May 2019 09:45:33 -0400 (EDT) Subject: [tarantool-patches] Re: [PATCH v5 3/6] sql: introduce tuple_fetcher class References: From: Kirill Shcherbatov Message-ID: <5672cd4b-0c46-b668-efa6-a6a465e601fa@tarantool.org> Date: Fri, 31 May 2019 16:45:31 +0300 MIME-Version: 1.0 In-Reply-To: Content-Type: text/plain; charset=utf-8 Content-Language: en-US Content-Transfer-Encoding: 7bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tarantool-patches@freelists.org, Vladislav Shpilevoy 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->p1nCursor); assert(pC!=0); assert(p2nField); - 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->szMallocz = 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@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