[tarantool-patches] Re: [PATCH v5 1/1] sql: return all generated ids via IPROTO
Imeev Mergen
imeevma at tarantool.org
Thu Oct 18 14:25:49 MSK 2018
Hello! Thank you for review! New patch below. There won't be diff
between versions as I started this version before decided to save
this diff.
On 10/03/2018 10:19 PM, Vladislav Shpilevoy wrote:
> Hi! Thanks for the patch!
>
> I see that Kostja slightly reviewed it and you did not
> answer him, so I inlined here some of his comments to
> agree/disagree with them.
>
> See 8 comments below.
>
> On 28/09/2018 19:53, imeevma at tarantool.org wrote:
>> According to documentation some JDBC functions have an ability to
>> return all ids that were generated in executed INSERT statement.
>> This patch gives a way to implement such functionality.
>>
>> Closes #2618
>> ---
>> Branch:
>> https://github.com/tarantool/tarantool/tree/imeevma/gh-2618-return-all-generated-ids
>> Issue: https://github.com/tarantool/tarantool/issues/2618
>>
>> src/box/execute.c | 24 ++++++++++++++++
>> src/box/execute.h | 1 +
>> src/box/lua/net_box.c | 21 ++++++++++++--
>> src/box/sequence.c | 29 ++++++++++++++++++++
>> src/box/sql/vdbe.c | 2 +-
>> src/box/sql/vdbe.h | 3 +-
>> src/box/sql/vdbeInt.h | 3 ++
>> src/box/sql/vdbeaux.c | 15 ++++++++--
>> src/box/txn.h | 13 +++++++++
>> test/sql/iproto.result | 71
>> ++++++++++++++++++++++++++++++++++++++++++++++++
>> test/sql/iproto.test.lua | 15 ++++++++++
>> 11 files changed, 190 insertions(+), 7 deletions(-)
>>
>> diff --git a/src/box/execute.c b/src/box/execute.c
>> index 24459b4..d9fe33f 100644
>> --- a/src/box/execute.c
>> +++ b/src/box/execute.c
>> @@ -42,6 +42,7 @@
>> #include "schema.h"
>> #include "port.h"
>> #include "tuple.h"
>> +#include "sql/vdbeInt.h"
>
> 1. Kostja said:
>
>>> Ugh. You're including a header which ends with Int. Have you
>>> thought what this Int means? It's not Integer.
>
> Strictly speaking, I agree with him. Src/box/execute is not
> an SQL internal file. It even uses SQLite public API to
> fetch names and types. I guess, you should implement a getter
> for ids list like
>
> const struct stailq *
> vdbe_auto_ids_list(const struct Vdbe *v);
Fixed.
>
>> const char *sql_type_strs[] = {
>> NULL,
>> @@ -639,14 +640,37 @@ err:
>> assert(port_tuple->size == 0);
>> if (iproto_reply_map_key(out, 1, IPROTO_SQL_INFO) != 0)
>> goto err;
>> + struct stailq *id_list = &((struct Vdbe *)stmt)->id_list;
>> + uint64_t id_count = 0;
>> int changes = sqlite3_changes(db);
>> int size = mp_sizeof_uint(SQL_INFO_ROW_COUNT) +
>> mp_sizeof_uint(changes);
>> + if (stailq_empty(id_list) == 0) {
>
> 2. Kostja said:
>
>>> What is the point of this == 0? Why are you comparing with an
>>> integer here, not boolean?
>
> I partially agree and I guess it is my fault that I am too harsh
> about code style. You used here == 0 because stailq_empty()
> returns an integer, but its name looks like a flag getter, so you
> can use it as a boolean here.
Fixed.
>
>> + struct id_entry *id_entry;
>> + stailq_foreach_entry(id_entry, id_list, link) {
>> + size += id_entry->id >= 0 ?
>> + mp_sizeof_uint(id_entry->id) :
>> + mp_sizeof_int(id_entry->id);
>> + id_count++;
>> + }
>> + size += mp_sizeof_uint(SQL_INFO_GENERATED_IDS) +
>> + mp_sizeof_array(id_count);
>> + }> char *buf = obuf_alloc(out, size);
>> if (buf == NULL) {
>> diag_set(OutOfMemory, size, "obuf_alloc", "buf");
>> goto err;
>> }
>> + if (id_count > 0) {
>
> 3. Lets be consistent: either you use !stailq_empty() in both
> places, or id_count > 0.
Fixed.
>
>> + buf = mp_encode_uint(buf, SQL_INFO_GENERATED_IDS);
>> + buf = mp_encode_array(buf, id_count);
>> + struct id_entry *id_entry;
>> + stailq_foreach_entry(id_entry, id_list, link) {
>> + buf = id_entry->id >= 0 ?
>> + mp_encode_uint(buf, id_entry->id) :
>> + mp_encode_int(buf, id_entry->id);
>> + }
>> + }
>> buf = mp_encode_uint(buf, SQL_INFO_ROW_COUNT);
>> buf = mp_encode_uint(buf, changes);
>> }
>> diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c
>> index a928a4c..5d2ac78 100644
>> --- a/src/box/lua/net_box.c
>> +++ b/src/box/lua/net_box.c
>> @@ -671,11 +671,28 @@ netbox_decode_sql_info(struct lua_State *L,
>> const char **data)
>> /* Only SQL_INFO_ROW_COUNT is available. */
>> assert(map_size == 1);
>
> 4. How does it work? map_size can be 2 now, and the comment is
> outdated - not only SQL_INFO_ROW_COUNT is available.
Fixed.
>
>> (void) map_size;
>> + lua_newtable(L);
>> uint32_t key = mp_decode_uint(data);
>> + /*
>> + * If SQL_INFO_GENERATED_IDS in data then it should be
>> + * just before SQL_INFO_ROW_COUNT.
>> + */
>> + if (key == SQL_INFO_GENERATED_IDS) {
>> + uint64_t count = mp_decode_array(data);
>> + assert (count > 0);
>> + lua_createtable(L, 0, count);
>> + lua_setfield(L, -2, "generated_ids");
>> + lua_getfield(L, -1, "generated_ids");
>> + for (uint32_t j = 0; j < count; ++j) {
>> + int64_t value = mp_decode_uint(data);
>> + lua_pushinteger(L, value);
>> + lua_rawseti(L, -2, j + 1);
>> + }
>> + lua_pop(L, 1);
>> + key = mp_decode_uint(data);
>> + }
>> assert(key == SQL_INFO_ROW_COUNT);
>> - (void) key;
>> uint32_t row_count = mp_decode_uint(data);
>> - lua_createtable(L, 0, 1);
>> lua_pushinteger(L, row_count);
>> lua_setfield(L, -2, "rowcount");
>> }
>> diff --git a/src/box/sequence.c b/src/box/sequence.c
>> index 35b7605..ed6b2da 100644
>> --- a/src/box/sequence.c
>> +++ b/src/box/sequence.c
>> @@ -38,6 +38,7 @@
>> #include <small/mempool.h>
>> #include <msgpuck/msgpuck.h>
>> +#include "txn.h"
>> #include "diag.h"
>> #include "error.h"
>> #include "errcode.h"
>> @@ -178,6 +179,30 @@ sequence_update(struct sequence *seq, int64_t
>> value)
>> return 0;
>> }
>> +/**
>> + * Save generated id in VDBE.
>> + *
>> + * @param value ID to save in VDBE.
>> + * @retval 0 Success.
>> + * @retval -1 Error.
>> + */
>> +static inline int
>> +add_new_id_in_vdbe(int64_t value)
>
> 5. Kostja said:
>
>>> subject-verb-object
> I agree. He means 'vdbe_add_new_id'. And in such a
> case it is evidently a part of Vdbe API, so put it into
> vdbe.h with this signature:
>
> int
> vdbe_add_new_auto_id(struct Vdbe *vdbe, int64_t id);
Fixed.
>
>> +{
>> + struct txn *txn = in_txn();
>> + if (txn == NULL || txn->psql_txn == NULL)
>> + return 0;
>> + assert(txn->psql_txn->vdbe != NULL);
>> + struct id_entry *id_entry = malloc(sizeof(*id_entry));
>> + if (id_entry == NULL) {
>> + diag_set(OutOfMemory, sizeof(*id_entry), "malloc", "id_entry");
>> + return -1;
>> + }
>> + id_entry->id = value;
>> + stailq_add_tail_entry(txn->psql_txn->id_list, id_entry, link);
>
> 6. If you have Vdbe, you do not need to store the list both in
> Vdbe and sql_txn. Store it in Vdbe only.
Fixed.
>
> 7. Kostja said:
>
>>> Why malloc?
> It is understandable why malloc - txn after commit invalidates
> the region memory. We neither can save ids on obuf directly
> since while getting next id a yield is possible and the obuf
> will be messed with another request. But guess I thought up a
> solution. It is a bit tricky, so maybe it should be accounted
> as a separate issue and this solved with malloc.
>
> I propose to split txn_commit() into commit and region reset.
> Txn_commit() goal is to write data to disk, not to destroy a
> region. It saves us when a transaction is started and committed
> by Vdbe. But what about autocommit transactions? I think, we
> should get rid of autocommit transactions for Vdbe. Vdbe always
> starts a transaction for DML and commits it, but without region
> reset - it is a part of vdbe finalize routine (which already
> exists actually).
>
> This split of txn_commit logic allows to store anything on
> a region during Vdbe work and not being afraid about a reset.
>
>> + return 0;
>> +}
>> +
>> diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
>> index d5da5d5..9b94183 100644
>> --- a/src/box/sql/vdbe.h
>> +++ b/src/box/sql/vdbe.h
>> @@ -179,10 +179,11 @@ Vdbe *sqlite3VdbeCreate(Parse *);
>> * Allocate and initialize SQL-specific struct which completes
>> * original Tarantool's txn struct using region allocator.
>> *
>> + * @param vdbe VDBE that is being prepared or executed.
>> * @retval NULL on OOM, new psql_txn struct on success.
>> **/
>> struct sql_txn *
>> -sql_alloc_txn();
>> +sql_alloc_txn(struct Vdbe *vdbe);
>
> 8. I think, alloc should only alloc. sql_txn_begin should
> put Vdbe into sql_txn.
Done.
*New patch:*
commit 9dd5e9833aec54c819e6dae07d62624538f76246
Author: Mergen Imeev <imeevma at gmail.com>
Date: Wed Sep 26 22:07:54 2018 +0300
sql: return all generated ids via IPROTO
According to documentation some JDBC functions have an ability to
return all ids that were generated in executed INSERT statement.
This patch gives a way to implement such functionality.
Closes #2618
diff --git a/src/box/execute.c b/src/box/execute.c
index 24459b4..ebf3962 100644
--- a/src/box/execute.c
+++ b/src/box/execute.c
@@ -42,6 +42,7 @@
#include "schema.h"
#include "port.h"
#include "tuple.h"
+#include "sql/vdbe.h"
const char *sql_type_strs[] = {
NULL,
@@ -637,11 +638,26 @@ err:
} else {
keys = 1;
assert(port_tuple->size == 0);
- if (iproto_reply_map_key(out, 1, IPROTO_SQL_INFO) != 0)
+ struct stailq *generated_ids =
+ vdbe_generated_ids((struct Vdbe *)stmt);
+ uint32_t map_size = stailq_empty(generated_ids) ? 1 : 2;
+ if (iproto_reply_map_key(out, map_size, IPROTO_SQL_INFO) != 0)
goto err;
+ uint64_t id_count = 0;
int changes = sqlite3_changes(db);
int size = mp_sizeof_uint(SQL_INFO_ROW_COUNT) +
mp_sizeof_uint(changes);
+ if (!stailq_empty(generated_ids)) {
+ struct id_entry *id_entry;
+ stailq_foreach_entry(id_entry, generated_ids, link) {
+ size += id_entry->id >= 0 ?
+ mp_sizeof_uint(id_entry->id) :
+ mp_sizeof_int(id_entry->id);
+ id_count++;
+ }
+ size += mp_sizeof_uint(SQL_INFO_GENERATED_IDS) +
+ mp_sizeof_array(id_count);
+ }
char *buf = obuf_alloc(out, size);
if (buf == NULL) {
diag_set(OutOfMemory, size, "obuf_alloc", "buf");
@@ -649,6 +665,16 @@ err:
}
buf = mp_encode_uint(buf, SQL_INFO_ROW_COUNT);
buf = mp_encode_uint(buf, changes);
+ if (!stailq_empty(generated_ids)) {
+ buf = mp_encode_uint(buf, SQL_INFO_GENERATED_IDS);
+ buf = mp_encode_array(buf, id_count);
+ struct id_entry *id_entry;
+ stailq_foreach_entry(id_entry, generated_ids, link) {
+ buf = id_entry->id >= 0 ?
+ mp_encode_uint(buf, id_entry->id) :
+ mp_encode_int(buf, id_entry->id);
+ }
+ }
}
iproto_reply_sql(out, &header_svp, response->sync, schema_version,
keys);
diff --git a/src/box/execute.h b/src/box/execute.h
index f21393b..614d3d0 100644
--- a/src/box/execute.h
+++ b/src/box/execute.h
@@ -42,6 +42,7 @@ extern "C" {
/** Keys of IPROTO_SQL_INFO map. */
enum sql_info_key {
SQL_INFO_ROW_COUNT = 0,
+ SQL_INFO_GENERATED_IDS = 1,
sql_info_key_MAX,
};
diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c
index a928a4c..1a9b3f7 100644
--- a/src/box/lua/net_box.c
+++ b/src/box/lua/net_box.c
@@ -668,16 +668,37 @@ static void
netbox_decode_sql_info(struct lua_State *L, const char **data)
{
uint32_t map_size = mp_decode_map(data);
- /* Only SQL_INFO_ROW_COUNT is available. */
- assert(map_size == 1);
- (void) map_size;
+ assert(map_size == 1 || map_size == 2);
+ lua_newtable(L);
+ /*
+ * First element in data is SQL_INFO_ROW_COUNT.
+ */
uint32_t key = mp_decode_uint(data);
assert(key == SQL_INFO_ROW_COUNT);
- (void) key;
uint32_t row_count = mp_decode_uint(data);
- lua_createtable(L, 0, 1);
lua_pushinteger(L, row_count);
lua_setfield(L, -2, "rowcount");
+ /*
+ * If data have two elements then second is
+ * SQL_INFO_GENERATED_IDS.
+ */
+ if (map_size == 2) {
+ key = mp_decode_uint(data);
+ assert(key == SQL_INFO_GENERATED_IDS);
+ (void)key;
+ uint64_t count = mp_decode_array(data);
+ assert (count > 0);
+ lua_createtable(L, 0, count);
+ lua_setfield(L, -2, "generated_ids");
+ lua_getfield(L, -1, "generated_ids");
+ for (uint32_t j = 0; j < count; ++j) {
+ int64_t id;
+ mp_read_int64(data, &id);
+ lua_pushinteger(L, id);
+ lua_rawseti(L, -2, j + 1);
+ }
+ lua_pop(L, 1);
+ }
}
static int
diff --git a/src/box/sequence.c b/src/box/sequence.c
index 35b7605..b1c68e9 100644
--- a/src/box/sequence.c
+++ b/src/box/sequence.c
@@ -38,6 +38,7 @@
#include <small/mempool.h>
#include <msgpuck/msgpuck.h>
+#include "txn.h"
#include "diag.h"
#include "error.h"
#include "errcode.h"
@@ -194,6 +195,9 @@ sequence_next(struct sequence *seq, int64_t *result)
new_data) == light_sequence_end)
return -1;
*result = def->start;
+ if (txn_vdbe() != NULL &&
+ vdbe_add_new_generated_id(txn_vdbe(), *result) != 0)
+ return -1;
return 0;
}
old_data = light_sequence_get(&sequence_data_index, pos);
@@ -228,6 +232,9 @@ done:
new_data, &old_data) == light_sequence_end)
unreachable();
*result = value;
+ if (txn_vdbe() != NULL &&
+ vdbe_add_new_generated_id(txn_vdbe(), *result) != 0)
+ return -1;
return 0;
overflow:
if (!def->cycle) {
diff --git a/src/box/sequence.h b/src/box/sequence.h
index 0f6c8da..d38fb2f 100644
--- a/src/box/sequence.h
+++ b/src/box/sequence.h
@@ -43,6 +43,7 @@ extern "C" {
#endif /* defined(__cplusplus) */
struct iterator;
+struct Vdbe;
/** Sequence metadata. */
struct sequence_def {
@@ -140,6 +141,17 @@ int
access_check_sequence(struct sequence *seq);
/**
+ * Add new generated id in VDBE.
+ *
+ * @param Vdbe VDBE to save id in.
+ * @param id ID to save in VDBE.
+ * @retval 0 Success.
+ * @retval -1 Error.
+ */
+int
+vdbe_add_new_generated_id(struct Vdbe *vdbe, int64_t id);
+
+/**
* Create an iterator over sequence data.
*
* The iterator creates a snapshot of sequence data and walks
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 7c1015c..7a3748e 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -578,6 +578,26 @@ out2Prerelease(Vdbe *p, VdbeOp *pOp)
}
}
+struct stailq *
+vdbe_generated_ids(struct Vdbe *vdbe)
+{
+ return &vdbe->generated_ids;
+}
+
+int
+vdbe_add_new_generated_id(struct Vdbe *vdbe, int64_t id)
+{
+ assert(vdbe != NULL);
+ struct id_entry *id_entry = region_alloc(&fiber()->gc,
sizeof(*id_entry));
+ if (id_entry == NULL) {
+ diag_set(OutOfMemory, sizeof(*id_entry), "region_alloc",
"id_entry");
+ return -1;
+ }
+ id_entry->id = id;
+ stailq_add_tail_entry(vdbe_generated_ids(vdbe), id_entry, link);
+ return 0;
+}
+
/*
* Execute as much of a VDBE program as we can.
* This is the core of sqlite3_step().
@@ -2996,7 +3016,7 @@ case OP_TransactionBegin: {
*/
case OP_TransactionCommit: {
if (box_txn()) {
- if (box_txn_commit() != 0) {
+ if (txn_commit(in_txn()) != 0) {
rc = SQL_TARANTOOL_ERROR;
goto abort_due_to_error;
}
@@ -3040,7 +3060,7 @@ case OP_TransactionRollback: {
*/
case OP_TTransaction: {
if (!box_txn()) {
- if (box_txn_begin() != 0) {
+ if (sql_txn_begin(p) != 0) {
rc = SQL_TARANTOOL_ERROR;
goto abort_due_to_error;
}
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h
index d5da5d5..8f877b3 100644
--- a/src/box/sql/vdbe.h
+++ b/src/box/sql/vdbe.h
@@ -197,6 +197,15 @@ sql_alloc_txn();
int
sql_vdbe_prepare(struct Vdbe *vdbe);
+/**
+ * Return pointer to list of generated ids.
+ *
+ * @param vdbe VDBE to get list of generated ids from.
+ * @retval List of generated ids.
+ */
+struct stailq *
+vdbe_generated_ids(struct Vdbe *vdbe);
+
int sqlite3VdbeAddOp0(Vdbe *, int);
int sqlite3VdbeAddOp1(Vdbe *, int, int);
int sqlite3VdbeAddOp2(Vdbe *, int, int, int);
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index ce97f49..37df5e2 100644
--- a/src/box/sql/vdbeInt.h
+++ b/src/box/sql/vdbeInt.h
@@ -368,6 +368,9 @@ struct Vdbe {
/** The auto-commit flag. */
bool auto_commit;
+ /** List of id generated in current VDBE. */
+ struct stailq generated_ids;
+
/* When allocating a new Vdbe object, all of the fields below
should be
* initialized to zero or NULL
*/
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c
index 6e42d05..0023dc6 100644
--- a/src/box/sql/vdbeaux.c
+++ b/src/box/sql/vdbeaux.c
@@ -57,6 +57,8 @@ sqlite3VdbeCreate(Parse * pParse)
return 0;
memset(p, 0, sizeof(Vdbe));
p->db = db;
+ /* Init list of generated ids. */
+ stailq_create(&p->generated_ids);
if (db->pVdbe) {
db->pVdbe->pPrev = p;
}
@@ -106,6 +108,7 @@ sql_vdbe_prepare(struct Vdbe *vdbe)
txn->psql_txn = sql_alloc_txn();
if (txn->psql_txn == NULL)
return -1;
+ txn->psql_txn->vdbe = vdbe;
}
vdbe->psql_txn = txn->psql_txn;
} else {
@@ -2266,6 +2269,7 @@ sql_txn_begin(Vdbe *p)
box_txn_rollback();
return -1;
}
+ ptxn->psql_txn->vdbe = p;
p->psql_txn = ptxn->psql_txn;
return 0;
}
@@ -2414,9 +2418,9 @@ sqlite3VdbeHalt(Vdbe * p)
* key constraints to hold up the transaction.
This means a commit
* is required.
*/
- rc = box_txn_commit() ==
- 0 ? SQLITE_OK :
- SQL_TARANTOOL_ERROR;
+ rc = (in_txn() == NULL ||
+ txn_commit(in_txn()) == 0) ?
+ SQLITE_OK : SQL_TARANTOOL_ERROR;
closeCursorsAndFree(p);
}
if (rc == SQLITE_BUSY && !p->pDelFrame) {
@@ -2780,6 +2784,8 @@ sqlite3VdbeDelete(Vdbe * p)
}
p->magic = VDBE_MAGIC_DEAD;
p->db = 0;
+ if (in_txn() == NULL)
+ fiber_gc();
sqlite3DbFree(db, p);
}
diff --git a/src/box/txn.h b/src/box/txn.h
index e74c14d..2acba9f 100644
--- a/src/box/txn.h
+++ b/src/box/txn.h
@@ -117,6 +117,17 @@ struct sql_txn {
* VDBE to the next in the same transaction.
*/
uint32_t fk_deferred_count;
+ /** Current VDBE. */
+ struct Vdbe *vdbe;
+};
+
+/** An element of list of generated ids. */
+struct id_entry
+{
+ /** A link in a generated id list. */
+ struct stailq_entry link;
+ /** Generated id. */
+ int64_t id;
};
struct txn {
@@ -337,6 +348,20 @@ txn_last_stmt(struct txn *txn)
void
txn_on_stop(struct trigger *trigger, void *event);
+/**
+ * Return VDBE that is currently executed.
+ *
+ * @retval VDBE that is currently executed.
+ * @retval NULL Either txn or ptxn_sql or vdbe is NULL;
+ */
+static inline struct Vdbe *
+txn_vdbe()
+{
+ struct txn *txn = in_txn();
+ if (txn == NULL || txn->psql_txn == NULL)
+ return NULL;
+ return txn->psql_txn->vdbe;
+}
/**
* FFI bindings: do not throw exceptions, do not accept extra
diff --git a/test/sql/iproto.result b/test/sql/iproto.result
index af474bc..163fb6e 100644
--- a/test/sql/iproto.result
+++ b/test/sql/iproto.result
@@ -580,6 +580,77 @@ cn:close()
box.sql.execute('drop table test')
---
...
+-- gh-2618 Return generated columns after INSERT in IPROTO.
+-- Return all ids generated in current INSERT statement.
+box.sql.execute('create table test (id integer primary key
autoincrement, a integer)')
+---
+...
+cn = remote.connect(box.cfg.listen)
+---
+...
+cn:execute('insert into test values (1, 1)')
+---
+- rowcount: 1
+...
+cn:execute('insert into test values (null, 2)')
+---
+- generated_ids:
+ - 2
+ rowcount: 1
+...
+cn:execute('update test set a = 11 where id == 1')
+---
+- rowcount: 1
+...
+cn:execute('insert into test values (100, 1), (null, 1), (120, 1),
(null, 1)')
+---
+- generated_ids:
+ - 101
+ - 121
+ rowcount: 4
+...
+cn:execute('insert into test values (null, 1), (null, 1), (null, 1),
(null, 1), (null, 1)')
+---
+- generated_ids:
+ - 122
+ - 123
+ - 124
+ - 125
+ - 126
+ rowcount: 5
+...
+cn:execute('select * from test')
+---
+- metadata:
+ - name: ID
+ - name: A
+ rows:
+ - [1, 11]
+ - [2, 2]
+ - [100, 1]
+ - [101, 1]
+ - [120, 1]
+ - [121, 1]
+ - [122, 1]
+ - [123, 1]
+ - [124, 1]
+ - [125, 1]
+ - [126, 1]
+...
+cn:execute('create table test2 (id integer primary key, a integer)')
+---
+- rowcount: 1
+...
+cn:execute('drop table test2')
+---
+- rowcount: 1
+...
+cn:close()
+---
+...
+box.sql.execute('drop table test')
+---
+...
box.schema.user.revoke('guest', 'read,write,execute', 'universe')
---
...
diff --git a/test/sql/iproto.test.lua b/test/sql/iproto.test.lua
index 220331b..6517cde 100644
--- a/test/sql/iproto.test.lua
+++ b/test/sql/iproto.test.lua
@@ -201,6 +201,21 @@ future4:wait_result()
cn:close()
box.sql.execute('drop table test')
+-- gh-2618 Return generated columns after INSERT in IPROTO.
+-- Return all ids generated in current INSERT statement.
+box.sql.execute('create table test (id integer primary key
autoincrement, a integer)')
+cn = remote.connect(box.cfg.listen)
+cn:execute('insert into test values (1, 1)')
+cn:execute('insert into test values (null, 2)')
+cn:execute('update test set a = 11 where id == 1')
+cn:execute('insert into test values (100, 1), (null, 1), (120, 1),
(null, 1)')
+cn:execute('insert into test values (null, 1), (null, 1), (null, 1),
(null, 1), (null, 1)')
+cn:execute('select * from test')
+cn:execute('create table test2 (id integer primary key, a integer)')
+cn:execute('drop table test2')
+cn:close()
+box.sql.execute('drop table test')
+
box.schema.user.revoke('guest', 'read,write,execute', 'universe')
space = nil
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.tarantool.org/pipermail/tarantool-patches/attachments/20181018/81387fb2/attachment.html>
More information about the Tarantool-patches
mailing list