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 EF294286B1 for ; Wed, 1 Aug 2018 13:24:03 -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 hXzXFEFHwDuG for ; Wed, 1 Aug 2018 13:24:03 -0400 (EDT) Received: from smtp3.mail.ru (smtp3.mail.ru [94.100.179.58]) (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 2B80E27509 for ; Wed, 1 Aug 2018 13:24:03 -0400 (EDT) Subject: [tarantool-patches] Re: [PATCH v2] sql: fix ephemeral table next rowid generation References: <20180718154718.3103-1-avkhatskevich@tarantool.org> <1368F28D-35EB-4BFA-B672-2BD8BD37963D@tarantool.org> From: Alex Khatskevich Message-ID: Date: Wed, 1 Aug 2018 20:23:54 +0300 MIME-Version: 1.0 In-Reply-To: <1368F28D-35EB-4BFA-B672-2BD8BD37963D@tarantool.org> Content-Type: text/plain; charset="utf-8"; format="flowed" Content-Transfer-Encoding: 8bit Content-Language: en-US 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: "n.pettik" , tarantool-patches@freelists.org >> diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c >> index f2d512f21..5a80d48ab 100644 >> --- a/src/box/memtx_space.c >> +++ b/src/box/memtx_space.c >> @@ -843,6 +843,8 @@ static void >> memtx_init_ephemeral_space(struct space *space) >> { >> memtx_space_add_primary_key(space); >> + struct memtx_space * ephem_space = (struct memtx_space *) space; >> + ephem_space->next_rowid = 0; >> } >> >> static int >> diff --git a/src/box/memtx_space.h b/src/box/memtx_space.h >> index 7dc341079..26181166a 100644 >> --- a/src/box/memtx_space.h >> +++ b/src/box/memtx_space.h >> @@ -48,6 +48,11 @@ struct memtx_space { >> */ >> int (*replace)(struct space *, struct tuple *, struct tuple *, >> enum dup_replace_mode, struct tuple **); >> + /** >> + * Next rowid. This number is added to the end of pk to > In fact, to the end of tuple to be inserted. Now ofc PK of ephemeral > spaces consists of all fields, but it may change someday, I guess. > > Moreover, ephemeral spaces aren’t supposed to be feature exclusively > of memtx engine. Vinyl ephemeral spaces also have right to exist. 1. Deleted those details. 2. Created vtab entity `ephemeral_next_rowid`. >> + * allow to store non-unique rows in ephemeral tables. >> + */ >> + uint64_t next_rowid; >> }; >> >> /** >> diff --git a/src/box/sql.c b/src/box/sql.c >> index 6104a6d0f..fdf0313d3 100644 >> --- a/src/box/sql.c >> +++ b/src/box/sql.c >> @@ -47,6 +47,7 @@ >> #include "box.h" >> #include "txn.h" >> #include "space.h" >> +#include "memtx_space.h" >> #include "space_def.h" >> #include "index_def.h" >> #include "tuple.h" >> @@ -448,6 +449,18 @@ int tarantoolSqlite3EphemeralDrop(BtCursor *pCur) >> return SQLITE_OK; >> } >> >> +/** >> + * Get next rowid for an ephemeral table. >> + */ >> +uint64_t >> +tarantool_ephemeral_next_rowid(BtCursor *bt_cur) > Firstly, we don’t use ’tarantool_’ prefix anymore. Also we use ’struct’ prefix > for compound types: struct BtCursor *bt_cur. Moreover, I don’t see comment > for this function. fixed. Added comment to `tarantoolInt.h`. > Secondly, I see your message within issue discussion: > " > Discussed verbally with > @kyukhin , @Korablev77 , @kostja and decided, that the unique rowid should be encapsulated into > vtab method of ephemeral space. > “ > > But this function doesn’t seem to be method of vtab… Created a dedicated vtab function. >> +{ >> + assert(bt_cur); >> + assert(bt_cur->curFlags & BTCF_TEphemCursor); >> + struct memtx_space * ephem_space = (struct memtx_space *) bt_cur->space; >> + return ephem_space->next_rowid++; >> +} >> + >> static inline int >> insertOrReplace(struct space *space, const char *tuple, const char *tuple_end, >> enum iproto_type type) >> @@ -1080,7 +1093,6 @@ key_alloc(BtCursor *cur, size_t key_size) >> } >> cur->key = new_key; >> } >> - cur->nKey = key_size; >> return 0; >> } >> >> @@ -1621,37 +1633,6 @@ sql_debug_info(struct info_handler *h) >> info_end(h); >> } >> >> >> diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h >> index 94f94cb44..369af77ff 100644 >> --- a/src/box/sql/tarantoolInt.h >> +++ b/src/box/sql/tarantoolInt.h >> @@ -116,8 +116,8 @@ int tarantoolSqlite3EphemeralDelete(BtCursor * pCur); >> int tarantoolSqlite3EphemeralCount(BtCursor * pCur, i64 * pnEntry); >> int tarantoolSqlite3EphemeralDrop(BtCursor * pCur); >> int tarantoolSqlite3EphemeralClearTable(BtCursor * pCur); >> -int tarantoolSqlite3EphemeralGetMaxId(BtCursor * pCur, uint32_t fieldno, >> - uint64_t * max_id); >> +uint64_t >> +tarantool_ephemeral_next_rowid(BtCursor *bt_cur); >> >> /** >> * Performs exactly as extract_key + sqlite3VdbeCompareMsgpack, >> diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c >> index 89c35d218..532c98e54 100644 >> --- a/src/box/sql/vdbe.c >> +++ b/src/box/sql/vdbe.c >> @@ -3790,27 +3790,19 @@ case OP_NextSequenceId: { >> break; >> } >> >> -/* Opcode: NextIdEphemeral P1 P2 P3 * * >> - * Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]}) >> +/* Opcode: NextIdEphemeral P1 P2 * * * >> + * Synopsis: r[P2]=get_next_rowid(space_index[P1]}) >> * >> - * This opcode works in the same way as OP_NextId does, except it is >> - * only applied for ephemeral tables. The difference is in the fact that >> - * all ephemeral tables don't have space_id (to be more precise it equals to zero). >> + * This opcode stores next `rowid` for the ephemeral space to P2 register. >> + * `rowid` is necessary, because inserted to ephemeral space tuples may be not >> + * unique, while Tarantool`s spaces can contain only unique tuples. > Nitpicking: generally speaking, ephemeral spaces can contain duplicates > when it comes for NULLs. You may investigate how this request works: > > SELECT (SELECT NULL UNION SELECT NULL) EXCEPT SELECT NULL; > > :) > >> */ >> case OP_NextIdEphemeral: { >> VdbeCursor *pC; >> - int p2; >> pC = p->apCsr[pOp->p1]; >> - p2 = pOp->p2; >> - pOut = &aMem[pOp->p3]; >> - >> - assert(pC->uc.pCursor->curFlags & BTCF_TEphemCursor); >> - >> - rc = tarantoolSqlite3EphemeralGetMaxId(pC->uc.pCursor, p2, >> - (uint64_t *) &pOut->u.i); >> - if (rc) goto abort_due_to_error; >> - >> - pOut->u.i += 1; >> + pOut = &aMem[pOp->p2]; >> + struct BtCursor * bt_cur = pC->uc.pCursor; >> + pOut->u.i = tarantool_ephemeral_next_rowid(bt_cur); > Should we add some checks on rowid overflow? Ephemeral spaces can be extensively > used for JOIN’s where number of inserted to ephemeral space tuples is O(n^2) in > worst case (for two tables). On the other hand, even simple check can slow down execution > especially when it concerns millions of tuples... In rowid is increasing 10^9 times per second, it would take 1017 years to overflow the counter. So, no. >> pOut->flags = MEM_Int; >> break; >> } >> diff --git a/test/sql-tap/gh-3297_ephemeral_rowid.test.lua b/test/sql-tap/gh-3297_ephemeral_rowid.test.lua >> new file mode 100755 >> index 000000000..ed596e7ad >> --- /dev/null >> +++ b/test/sql-tap/gh-3297_ephemeral_rowid.test.lua >> @@ -0,0 +1,30 @@ >> +#!/usr/bin/env tarantool >> +test = require("sqltester") >> +test:plan(1) >> + >> +-- Check that OP_NextIdEphemeral generates unique ids. >> + >> +test:execsql [[ >> + CREATE TABLE t1(a primary key); >> + CREATE TABLE t2(a primary key, b); >> + insert into t1 values(12); >> + insert into t2 values(1, 5); >> + insert into t2 values(2, 2); >> + insert into t2 values(3, 2); >> + insert into t2 values(4, 2); >> +]] >> + >> +test:do_execsql_test( >> + "gh-3297-1", >> + [[ >> + select * from ( select a from t1 limit 1), (select b from t2 limit 10); > Use upper-case for all SQL statements pls. Fixed. Full diff commit 3b30cd4ee27ccf9046a4623d6455479bf980cd1a Author: AKhatskevich Date:   Tue May 15 22:00:28 2018 +0300     sql: fix ephemeral table next rowid generation     Before this commit, next rowid was retrieved from an ephemeral table     using `index_max`. That led to wrong behavior because the index is not     sorted by rowid and `index_max` doesn`t return a tuple with the greatest     rowid.     New implementation stores next `rowid` value in `struct memtx_space`,     and increments it after each insert to the ephemeral space.     Extra refactoring:      * `nKey` attribute is deleted from BtCursor, because it is not used     anymore.     Closes #3297 diff --git a/src/box/memtx_space.c b/src/box/memtx_space.c index f2d512f21..9015c56ca 100644 --- a/src/box/memtx_space.c +++ b/src/box/memtx_space.c @@ -603,6 +603,20 @@ memtx_space_ephemeral_delete(struct space *space, const char *key)      return 0;  } +/** + * Generate unique number. + * This rowid may be used to store non-unique values in an + * ephemeral space. + * + * This function isn't involved in any transaction routine. + */ +static uint64_t +memtx_space_ephemeral_next_rowid(struct space *space) +{ +    struct memtx_space *memtx_space = (struct memtx_space *)space; +    return memtx_space->next_rowid++; +} +  /* }}} DML */  /* {{{ DDL */ @@ -843,6 +857,8 @@ static void  memtx_init_ephemeral_space(struct space *space)  {      memtx_space_add_primary_key(space); +    struct memtx_space * ephem_space = (struct memtx_space *) space; +    ephem_space->next_rowid = 0;  }  static int @@ -949,6 +965,7 @@ static const struct space_vtab memtx_space_vtab = {      /* .execute_upsert = */ memtx_space_execute_upsert,      /* .ephemeral_replace = */ memtx_space_ephemeral_replace,      /* .ephemeral_delete = */ memtx_space_ephemeral_delete, +    /* .ephemeral_next_rowid = */ memtx_space_ephemeral_next_rowid,      /* .ephemeral_cleanup = */ memtx_space_ephemeral_cleanup,      /* .init_system_space = */ memtx_init_system_space,      /* .init_ephemeral_space = */ memtx_init_ephemeral_space, diff --git a/src/box/memtx_space.h b/src/box/memtx_space.h index 7dc341079..bb1d8b74b 100644 --- a/src/box/memtx_space.h +++ b/src/box/memtx_space.h @@ -48,6 +48,11 @@ struct memtx_space {       */      int (*replace)(struct space *, struct tuple *, struct tuple *,                 enum dup_replace_mode, struct tuple **); +    /** +     * Next rowid. This number is used to allow to store +     * non-unique rows in ephemeral tables. +     */ +    uint64_t next_rowid;  };  /** diff --git a/src/box/space.h b/src/box/space.h index 8f675dfe1..e4ae29747 100644 --- a/src/box/space.h +++ b/src/box/space.h @@ -70,6 +70,10 @@ struct space_vtab {      int (*ephemeral_replace)(struct space *, const char *, const char *);      int (*ephemeral_delete)(struct space *, const char *); +    /** +     * Generate unique number. +     */ +    uint64_t (*ephemeral_next_rowid)(struct space *);      void (*ephemeral_cleanup)(struct space *); diff --git a/src/box/sql.c b/src/box/sql.c index 6104a6d0f..c27595afd 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -47,6 +47,7 @@  #include "box.h"  #include "txn.h"  #include "space.h" +#include "memtx_space.h"  #include "space_def.h"  #include "index_def.h"  #include "tuple.h" @@ -448,6 +449,15 @@ int tarantoolSqlite3EphemeralDrop(BtCursor *pCur)      return SQLITE_OK;  } +uint64_t +ephemeral_next_rowid(struct BtCursor *bt_cur) +{ +    assert(bt_cur); +    assert(bt_cur->curFlags & BTCF_TEphemCursor); +    struct space * ephem_space = (struct space *) bt_cur->space; +    return ephem_space->vtab->ephemeral_next_rowid(ephem_space); +} +  static inline int  insertOrReplace(struct space *space, const char *tuple, const char *tuple_end,          enum iproto_type type) @@ -1080,7 +1090,6 @@ key_alloc(BtCursor *cur, size_t key_size)          }          cur->key = new_key;      } -    cur->nKey = key_size;      return 0;  } @@ -1621,37 +1630,6 @@ sql_debug_info(struct info_handler *h)      info_end(h);  } -/* - * Extract maximum integer value from ephemeral space. - * If index is empty - return 0 in max_id and success status. - * - * @param pCur Cursor pointing to ephemeral space. - * @param fieldno Number of field from fetching tuple. - * @param[out] max_id Fetched max value. - * - * @retval 0 on success, -1 otherwise. - */ -int tarantoolSqlite3EphemeralGetMaxId(BtCursor *pCur, uint32_t fieldno, -                       uint64_t *max_id) -{ -    struct space *ephem_space = pCur->space; -    assert(ephem_space); -    struct index *primary_index = *ephem_space->index; - -    struct tuple *tuple; -    if (index_max(primary_index, NULL, 0, &tuple) != 0) { -        return SQL_TARANTOOL_ERROR; -    } -    if (tuple == NULL) { -        *max_id = 0; -        return SQLITE_OK; -    } -    if (tuple_field_u64(tuple, fieldno, max_id) == -1) -        return SQL_TARANTOOL_ERROR; - -    return SQLITE_OK; -} -  int  tarantoolSqlNextSeqId(uint64_t *max_id)  { diff --git a/src/box/sql/cursor.h b/src/box/sql/cursor.h index c55941784..ab379f01e 100644 --- a/src/box/sql/cursor.h +++ b/src/box/sql/cursor.h @@ -57,7 +57,6 @@ typedef struct BtCursor BtCursor;   * for ordinary space, or to TEphemCursor for ephemeral space.   */  struct BtCursor { -    i64 nKey;        /* Size of pKey, or last integer key */      u8 curFlags;        /* zero or more BTCF_* flags defined below */      u8 eState;        /* One of the CURSOR_XXX constants (see below) */      u8 hints;        /* As configured by CursorSetHints() */ diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c index 0b9e75a93..bddfdc50c 100644 --- a/src/box/sql/insert.c +++ b/src/box/sql/insert.c @@ -557,15 +557,12 @@ sqlite3Insert(Parse * pParse,    /* Parser context */               *      M: ...               */              int regRec;    /* Register to hold packed record */ -            int regTempId;    /* Register to hold temp table ID */              int regCopy;    /* Register to keep copy of registers from select */              int addrL;    /* Label "L" */ -            int64_t initial_pk = 0;              srcTab = pParse->nTab++;              regRec = sqlite3GetTempReg(pParse); -            regCopy = sqlite3GetTempRange(pParse, nColumn); -            regTempId = sqlite3GetTempReg(pParse); +            regCopy = sqlite3GetTempRange(pParse, nColumn + 1);              struct key_def *def = key_def_new(nColumn + 1);              if (def == NULL) {                  sqlite3OomFault(db); @@ -573,16 +570,10 @@ sqlite3Insert(Parse * pParse,    /* Parser context */              }              sqlite3VdbeAddOp4(v, OP_OpenTEphemeral, srcTab, nColumn+1,                        0, (char*)def, P4_KEYDEF); -            /* Create counter for rowid */ -            sqlite3VdbeAddOp4Dup8(v, OP_Int64, -                          0 /* unused */, -                          regTempId, -                          0 /* unused */, -                          (const unsigned char*) &initial_pk, -                          P4_INT64);              addrL = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);              VdbeCoverage(v); -            sqlite3VdbeAddOp2(v, OP_AddImm, regTempId, 1); +            sqlite3VdbeAddOp2(v, OP_NextIdEphemeral, srcTab, +                      regCopy + nColumn);              sqlite3VdbeAddOp3(v, OP_Copy, regFromSelect, regCopy, nColumn-1);              sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy,                        nColumn + 1, regRec); @@ -593,7 +584,6 @@ sqlite3Insert(Parse * pParse,    /* Parser context */              sqlite3VdbeGoto(v, addrL);              sqlite3VdbeJumpHere(v, addrL);              sqlite3ReleaseTempReg(pParse, regRec); -            sqlite3ReleaseTempReg(pParse, regTempId);              sqlite3ReleaseTempRange(pParse, regCopy, nColumn);          }      } else { diff --git a/src/box/sql/select.c b/src/box/sql/select.c index 1f27efdb5..5cd289ccc 100644 --- a/src/box/sql/select.c +++ b/src/box/sql/select.c @@ -1047,8 +1047,8 @@ selectInnerLoop(Parse * pParse,     /* The parser context */                  int regRec = sqlite3GetTempReg(pParse);                  /* Last column is required for ID. */                  int regCopy = sqlite3GetTempRange(pParse, nResultCol + 1); -                sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm, -                          nResultCol, regCopy + nResultCol); +                sqlite3VdbeAddOp2(v, OP_NextIdEphemeral, iParm, +                          regCopy + nResultCol);                  /* Positioning ID column to be last in inserted tuple.                   * NextId -> regCopy + n + 1                   * Copy [regResult, regResult + n] -> [regCopy, regCopy + n] @@ -1418,7 +1418,7 @@ generateSortTail(Parse * pParse, /* Parsing context */      case SRT_Table:      case SRT_EphemTab: {              int regCopy = sqlite3GetTempRange(pParse, nColumn); -            sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, iParm, nColumn, regTupleid); +            sqlite3VdbeAddOp2(v, OP_NextIdEphemeral, iParm, regTupleid);              sqlite3VdbeAddOp3(v, OP_Copy, regRow, regCopy, nSortData - 1);              sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, nColumn + 1, regRow);              sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRow); @@ -2898,8 +2898,8 @@ generateOutputSubroutine(struct Parse *parse, struct Select *p,      case SRT_EphemTab:{              int regRec = sqlite3GetTempReg(parse);              int regCopy = sqlite3GetTempRange(parse, in->nSdst + 1); -            sqlite3VdbeAddOp3(v, OP_NextIdEphemeral, dest->iSDParm, -                      in->nSdst, regCopy + in->nSdst); +            sqlite3VdbeAddOp2(v, OP_NextIdEphemeral, dest->iSDParm, +                      regCopy + in->nSdst);              sqlite3VdbeAddOp3(v, OP_Copy, in->iSdst, regCopy,                        in->nSdst - 1);              sqlite3VdbeAddOp3(v, OP_MakeRecord, regCopy, diff --git a/src/box/sql/tarantoolInt.h b/src/box/sql/tarantoolInt.h index 94f94cb44..38f6a56c1 100644 --- a/src/box/sql/tarantoolInt.h +++ b/src/box/sql/tarantoolInt.h @@ -116,8 +116,14 @@ int tarantoolSqlite3EphemeralDelete(BtCursor * pCur);  int tarantoolSqlite3EphemeralCount(BtCursor * pCur, i64 * pnEntry);  int tarantoolSqlite3EphemeralDrop(BtCursor * pCur);  int tarantoolSqlite3EphemeralClearTable(BtCursor * pCur); -int tarantoolSqlite3EphemeralGetMaxId(BtCursor * pCur, uint32_t fieldno, -                       uint64_t * max_id); +/** + * Get next rowid for a cursor which points to an ephemeral table. + * @param bt_cur Cursor which will point to the new ephemeral space. + * + * @retval New unique rowid. + */ +uint64_t +ephemeral_next_rowid(struct BtCursor *bt_cur);  /**   * Performs exactly as extract_key + sqlite3VdbeCompareMsgpack, diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c index 89c35d218..6ac12dec0 100644 --- a/src/box/sql/vdbe.c +++ b/src/box/sql/vdbe.c @@ -3790,27 +3790,19 @@ case OP_NextSequenceId: {      break;  } -/* Opcode: NextIdEphemeral P1 P2 P3 * * - * Synopsis: r[P3]=get_max(space_index[P1]{Column[P2]}) +/* Opcode: NextIdEphemeral P1 P2 * * * + * Synopsis: r[P2]=get_next_rowid(space_index[P1]})   * - * This opcode works in the same way as OP_NextId does, except it is - * only applied for ephemeral tables. The difference is in the fact that - * all ephemeral tables don't have space_id (to be more precise it equals to zero). + * This opcode stores next `rowid` for the ephemeral space to P2 register. + * `rowid` is necessary, because inserted to ephemeral space tuples may be not + * unique, while Tarantool`s spaces can contain only unique tuples.   */  case OP_NextIdEphemeral: {      VdbeCursor *pC; -    int p2;      pC = p->apCsr[pOp->p1]; -    p2 = pOp->p2; -    pOut = &aMem[pOp->p3]; - -    assert(pC->uc.pCursor->curFlags & BTCF_TEphemCursor); - -    rc = tarantoolSqlite3EphemeralGetMaxId(pC->uc.pCursor, p2, -                           (uint64_t *) &pOut->u.i); -    if (rc) goto abort_due_to_error; - -    pOut->u.i += 1; +    pOut = &aMem[pOp->p2]; +    struct BtCursor * bt_cur = pC->uc.pCursor; +    pOut->u.i = ephemeral_next_rowid(bt_cur);      pOut->flags = MEM_Int;      break;  } diff --git a/src/box/sysview_engine.c b/src/box/sysview_engine.c index 308a3328a..64fa6ee23 100644 --- a/src/box/sysview_engine.c +++ b/src/box/sysview_engine.c @@ -119,6 +119,14 @@ sysview_space_ephemeral_delete(struct space *space, const char *key)      return -1;  } +static uint64_t +sysview_space_ephemeral_next_rowid(struct space *space) +{ +    (void)space; +    unreachable(); +    return 0; +} +  static void  sysview_space_ephemeral_cleanup(struct space *space)  { @@ -206,6 +214,7 @@ static const struct space_vtab sysview_space_vtab = {      /* .execute_upsert = */ sysview_space_execute_upsert,      /* .ephemeral_replace = */ sysview_space_ephemeral_replace,      /* .ephemeral_delete = */ sysview_space_ephemeral_delete, +    /* .ephemeral_next_rowid = */ sysview_space_ephemeral_next_rowid,      /* .ephemeral_cleanup = */ sysview_space_ephemeral_cleanup,      /* .init_system_space = */ sysview_init_system_space,      /* .init_ephemeral_space = */ sysview_init_ephemeral_space, diff --git a/src/box/vinyl.c b/src/box/vinyl.c index b7238ae4c..b13b21dfb 100644 --- a/src/box/vinyl.c +++ b/src/box/vinyl.c @@ -2379,6 +2379,14 @@ vinyl_space_ephemeral_delete(struct space *space, const char *key)      return -1;  } +static uint64_t +vinyl_space_ephemeral_next_rowid(struct space *space) +{ +    (void)space; +    unreachable(); +    return 0; +} +  static void  vinyl_space_ephemeral_cleanup(struct space *space)  { @@ -4053,6 +4061,7 @@ static const struct space_vtab vinyl_space_vtab = {      /* .execute_upsert = */ vinyl_space_execute_upsert,      /* .ephemeral_replace = */ vinyl_space_ephemeral_replace,      /* .ephemeral_delete = */ vinyl_space_ephemeral_delete, +    /* .ephemeral_next_rowid = */ vinyl_space_ephemeral_next_rowid,      /* .ephemeral_cleanup = */ vinyl_space_ephemeral_cleanup,      /* .init_system_space = */ vinyl_init_system_space,      /* .init_ephemeral_space = */ vinyl_init_ephemeral_space, diff --git a/test/sql-tap/gh-3297_ephemeral_rowid.test.lua b/test/sql-tap/gh-3297_ephemeral_rowid.test.lua new file mode 100755 index 000000000..d49baf300 --- /dev/null +++ b/test/sql-tap/gh-3297_ephemeral_rowid.test.lua @@ -0,0 +1,30 @@ +#!/usr/bin/env tarantool +test = require("sqltester") +test:plan(1) + +-- Check that OP_NextIdEphemeral generates unique ids. + +test:execsql [[ +    CREATE TABLE T1(A PRIMARY KEY); +    CREATE TABLE T2(A PRIMARY KEY, B); +    INSERT INTO T1 VALUES(12); +    INSERT INTO T2 VALUES(1, 5); +    INSERT INTO T2 VALUES(2, 2); +    INSERT INTO T2 VALUES(3, 2); +    INSERT INTO T2 VALUES(4, 2); +]] + +test:do_execsql_test( +    "gh-3297-1", +    [[ +        SELECT * FROM ( SELECT A FROM T1 LIMIT 1), (SELECT B FROM T2 LIMIT 10); +    ]], +    { +        12, 2, +        12, 2, +        12, 2, +        12, 5 +    } +) + +test:finish_test()