<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body text="#000000" bgcolor="#FFFFFF">
<p>Hello! Thank you for review! New patch below.<br>
</p>
<br>
<div class="moz-cite-prefix">On 09/29/2018 07:36 AM, Konstantin
Osipov wrote:<br>
</div>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">* <a class="moz-txt-link-abbreviated" href="mailto:imeevma@tarantool.org">imeevma@tarantool.org</a> <a class="moz-txt-link-rfc2396E" href="mailto:imeevma@tarantool.org"><imeevma@tarantool.org></a> [18/09/28 19:53]:
</pre>
<blockquote type="cite">
<pre wrap="">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"
</pre>
</blockquote>
<pre wrap="">Ugh. You're including a header which ends with Int. Have you
thought what this Int means? It's not Integer.</pre>
</blockquote>
Fixed.<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap="">
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;
</pre>
</blockquote>
<pre wrap="">The name is very general. autoinc_id_list would be more specific.
Why not simply call it generated_ids here and everywhere? This is
what these values are called in the standard.</pre>
</blockquote>
Fixed.<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap=""> int changes = sqlite3_changes(db);
int size = mp_sizeof_uint(SQL_INFO_ROW_COUNT) +
mp_sizeof_uint(changes);
+ if (stailq_empty(id_list) == 0) {
</pre>
</blockquote>
<pre wrap="">What is the point of this == 0? Why are you comparing with an
integer here, not boolean? </pre>
</blockquote>
Fixed.<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap="">+ 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);
</pre>
</blockquote>
<pre wrap="">How can you get a negative id?</pre>
</blockquote>
It is possible:<br>
<br>
box.sql.execute('create table a (id int primary key autoincrement)')<br>
box.schema.sequence.alter('A', {min=-10000, step=-1})<br>
box.sql.execute('insert into a values (null)')<br>
box.sql.execute('insert into a values (null)')<br>
box.sql.execute('insert into a values (null)')<br>
box.sql.execute('insert into a values (null)')<br>
box.sql.execute('insert into a values (null)')<br>
box.sql.execute('select * from a')<br>
---<br>
- - [-3]<br>
- [-2]<br>
- [-1]<br>
- [0]<br>
- [1]<br>
...<br>
<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<blockquote type="cite">
<pre wrap="">+ id_count++;
+ }
+ size += mp_sizeof_uint(SQL_INFO_GENERATED_IDS) +
+ mp_sizeof_array(id_count);
+ }
</pre>
</blockquote>
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap=""> char *buf = obuf_alloc(out, size);
if (buf == NULL) {
diag_set(OutOfMemory, size, "obuf_alloc", "buf");
goto err;
}
+ if (id_count > 0) {
+ 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/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..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);
(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)
</pre>
</blockquote>
<pre wrap="">subject-verb-object</pre>
</blockquote>
Fixed.<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap="">+{
+ 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));
</pre>
</blockquote>
<pre wrap="">Why malloc?</pre>
</blockquote>
Fixed. To save ids in region fiber_gc() was moved from<br>
txn_commit() to sqlite3VdbeDelete().<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap="">+ 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);
+ return 0;
+}
+
int
sequence_next(struct sequence *seq, int64_t *result)
{
@@ -194,6 +219,8 @@ sequence_next(struct sequence *seq, int64_t *result)
new_data) == light_sequence_end)
return -1;
*result = def->start;
+ if(add_new_id_in_vdbe(*result) != 0)
+ return -1;
return 0;
}
old_data = light_sequence_get(&sequence_data_index, pos);
@@ -228,6 +255,8 @@ done:
new_data, &old_data) == light_sequence_end)
unreachable();
*result = value;
+ if(add_new_id_in_vdbe(*result) != 0)
+ return -1;
</pre>
</blockquote>
<pre wrap="">You continue to miss a space after if.</pre>
</blockquote>
Fixed.<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<blockquote type="cite">
<pre wrap=""> return 0;
overflow:
if (!def->cycle) {
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index 00ffb0c..cbec9a2 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -3040,7 +3040,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..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);
/**
* Prepare given VDBE to execution: initialize structs connected
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h
index ce97f49..ca1794c 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 id_list;
+
/* 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 54edf1b..98ddef7 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->id_list);
if (db->pVdbe) {
db->pVdbe->pPrev = p;
}
@@ -75,7 +77,7 @@ sqlite3VdbeCreate(Parse * pParse)
}
struct sql_txn *
-sql_alloc_txn()
+sql_alloc_txn(struct Vdbe *vdbe)
{
struct sql_txn *txn = region_alloc_object(&fiber()->gc,
struct sql_txn);
@@ -86,6 +88,8 @@ sql_alloc_txn()
}
txn->pSavepoint = NULL;
txn->fk_deferred_count = 0;
+ txn->vdbe = vdbe;
+ txn->id_list = &vdbe->id_list;
</pre>
</blockquote>
<pre wrap="">Why do you need both?</pre>
</blockquote>
Fixed.<br>
<blockquote type="cite" cite="mid:20180929043617.GD32712@chai">
<pre wrap="">
</pre>
<blockquote type="cite">
<pre wrap=""> return txn;
}
@@ -103,7 +107,7 @@ sql_vdbe_prepare(struct Vdbe *vdbe)
* check FK violations, at least now.
*/
if (txn->psql_txn == NULL) {
- txn->psql_txn = sql_alloc_txn();
+ txn->psql_txn = sql_alloc_txn(vdbe);
if (txn->psql_txn == NULL)
return -1;
}
@@ -2261,7 +2265,7 @@ sql_txn_begin(Vdbe *p)
struct txn *ptxn = txn_begin(false);
if (ptxn == NULL)
return -1;
- ptxn->psql_txn = sql_alloc_txn();
+ ptxn->psql_txn = sql_alloc_txn(p);
if (ptxn->psql_txn == NULL) {
box_txn_rollback();
return -1;
@@ -2746,6 +2750,11 @@ sqlite3VdbeClearObject(sqlite3 * db, Vdbe * p)
vdbeFreeOpArray(db, p->aOp, p->nOp);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
+ while (stailq_empty(&p->id_list) == 0) {
+ struct id_entry *id_entry =
+ stailq_shift_entry(&p->id_list, struct id_entry, link);
+ free(id_entry);
+ }
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
{
</pre>
</blockquote>
</blockquote>
<br>
<b> New patch:</b><br>
<br>
commit 9dd5e9833aec54c819e6dae07d62624538f76246<br>
Author: Mergen Imeev <a class="moz-txt-link-rfc2396E" href="mailto:imeevma@gmail.com"><imeevma@gmail.com></a><br>
Date: Wed Sep 26 22:07:54 2018 +0300<br>
<br>
sql: return all generated ids via IPROTO<br>
<br>
According to documentation some JDBC functions have an ability
to<br>
return all ids that were generated in executed INSERT statement.<br>
This patch gives a way to implement such functionality.<br>
<br>
Closes #2618<br>
<br>
diff --git a/src/box/execute.c b/src/box/execute.c<br>
index 24459b4..ebf3962 100644<br>
--- a/src/box/execute.c<br>
+++ b/src/box/execute.c<br>
@@ -42,6 +42,7 @@<br>
#include "schema.h"<br>
#include "port.h"<br>
#include "tuple.h"<br>
+#include "sql/vdbe.h"<br>
<br>
const char *sql_type_strs[] = {<br>
NULL,<br>
@@ -637,11 +638,26 @@ err:<br>
} else {<br>
keys = 1;<br>
assert(port_tuple->size == 0);<br>
- if (iproto_reply_map_key(out, 1, IPROTO_SQL_INFO) != 0)<br>
+ struct stailq *generated_ids =<br>
+ vdbe_generated_ids((struct Vdbe *)stmt);<br>
+ uint32_t map_size = stailq_empty(generated_ids) ? 1 : 2;<br>
+ if (iproto_reply_map_key(out, map_size, IPROTO_SQL_INFO) !=
0)<br>
goto err;<br>
+ uint64_t id_count = 0;<br>
int changes = sqlite3_changes(db);<br>
int size = mp_sizeof_uint(SQL_INFO_ROW_COUNT) +<br>
mp_sizeof_uint(changes);<br>
+ if (!stailq_empty(generated_ids)) {<br>
+ struct id_entry *id_entry;<br>
+ stailq_foreach_entry(id_entry, generated_ids, link) {<br>
+ size += id_entry->id >= 0 ?<br>
+ mp_sizeof_uint(id_entry->id) :<br>
+ mp_sizeof_int(id_entry->id);<br>
+ id_count++;<br>
+ }<br>
+ size += mp_sizeof_uint(SQL_INFO_GENERATED_IDS) +<br>
+ mp_sizeof_array(id_count);<br>
+ }<br>
char *buf = obuf_alloc(out, size);<br>
if (buf == NULL) {<br>
diag_set(OutOfMemory, size, "obuf_alloc", "buf");<br>
@@ -649,6 +665,16 @@ err:<br>
}<br>
buf = mp_encode_uint(buf, SQL_INFO_ROW_COUNT);<br>
buf = mp_encode_uint(buf, changes);<br>
+ if (!stailq_empty(generated_ids)) {<br>
+ buf = mp_encode_uint(buf, SQL_INFO_GENERATED_IDS);<br>
+ buf = mp_encode_array(buf, id_count);<br>
+ struct id_entry *id_entry;<br>
+ stailq_foreach_entry(id_entry, generated_ids, link) {<br>
+ buf = id_entry->id >= 0 ?<br>
+ mp_encode_uint(buf, id_entry->id) :<br>
+ mp_encode_int(buf, id_entry->id);<br>
+ }<br>
+ }<br>
}<br>
iproto_reply_sql(out, &header_svp, response->sync,
schema_version,<br>
keys);<br>
diff --git a/src/box/execute.h b/src/box/execute.h<br>
index f21393b..614d3d0 100644<br>
--- a/src/box/execute.h<br>
+++ b/src/box/execute.h<br>
@@ -42,6 +42,7 @@ extern "C" {<br>
/** Keys of IPROTO_SQL_INFO map. */<br>
enum sql_info_key {<br>
SQL_INFO_ROW_COUNT = 0,<br>
+ SQL_INFO_GENERATED_IDS = 1,<br>
sql_info_key_MAX,<br>
};<br>
<br>
diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c<br>
index a928a4c..1a9b3f7 100644<br>
--- a/src/box/lua/net_box.c<br>
+++ b/src/box/lua/net_box.c<br>
@@ -668,16 +668,37 @@ static void<br>
netbox_decode_sql_info(struct lua_State *L, const char **data)<br>
{<br>
uint32_t map_size = mp_decode_map(data);<br>
- /* Only SQL_INFO_ROW_COUNT is available. */<br>
- assert(map_size == 1);<br>
- (void) map_size;<br>
+ assert(map_size == 1 || map_size == 2);<br>
+ lua_newtable(L);<br>
+ /*<br>
+ * First element in data is SQL_INFO_ROW_COUNT.<br>
+ */<br>
uint32_t key = mp_decode_uint(data);<br>
assert(key == SQL_INFO_ROW_COUNT);<br>
- (void) key;<br>
uint32_t row_count = mp_decode_uint(data);<br>
- lua_createtable(L, 0, 1);<br>
lua_pushinteger(L, row_count);<br>
lua_setfield(L, -2, "rowcount");<br>
+ /*<br>
+ * If data have two elements then second is<br>
+ * SQL_INFO_GENERATED_IDS.<br>
+ */<br>
+ if (map_size == 2) {<br>
+ key = mp_decode_uint(data);<br>
+ assert(key == SQL_INFO_GENERATED_IDS);<br>
+ (void)key;<br>
+ uint64_t count = mp_decode_array(data);<br>
+ assert (count > 0);<br>
+ lua_createtable(L, 0, count);<br>
+ lua_setfield(L, -2, "generated_ids");<br>
+ lua_getfield(L, -1, "generated_ids");<br>
+ for (uint32_t j = 0; j < count; ++j) {<br>
+ int64_t id;<br>
+ mp_read_int64(data, &id);<br>
+ lua_pushinteger(L, id);<br>
+ lua_rawseti(L, -2, j + 1);<br>
+ }<br>
+ lua_pop(L, 1);<br>
+ }<br>
}<br>
<br>
static int<br>
diff --git a/src/box/sequence.c b/src/box/sequence.c<br>
index 35b7605..b1c68e9 100644<br>
--- a/src/box/sequence.c<br>
+++ b/src/box/sequence.c<br>
@@ -38,6 +38,7 @@<br>
#include <small/mempool.h><br>
#include <msgpuck/msgpuck.h><br>
<br>
+#include "txn.h"<br>
#include "diag.h"<br>
#include "error.h"<br>
#include "errcode.h"<br>
@@ -194,6 +195,9 @@ sequence_next(struct sequence *seq, int64_t
*result)<br>
new_data) == light_sequence_end)<br>
return -1;<br>
*result = def->start;<br>
+ if (txn_vdbe() != NULL &&<br>
+ vdbe_add_new_generated_id(txn_vdbe(), *result) != 0)<br>
+ return -1;<br>
return 0;<br>
}<br>
old_data = light_sequence_get(&sequence_data_index, pos);<br>
@@ -228,6 +232,9 @@ done:<br>
new_data, &old_data) == light_sequence_end)<br>
unreachable();<br>
*result = value;<br>
+ if (txn_vdbe() != NULL &&<br>
+ vdbe_add_new_generated_id(txn_vdbe(), *result) != 0)<br>
+ return -1;<br>
return 0;<br>
overflow:<br>
if (!def->cycle) {<br>
diff --git a/src/box/sequence.h b/src/box/sequence.h<br>
index 0f6c8da..d38fb2f 100644<br>
--- a/src/box/sequence.h<br>
+++ b/src/box/sequence.h<br>
@@ -43,6 +43,7 @@ extern "C" {<br>
#endif /* defined(__cplusplus) */<br>
<br>
struct iterator;<br>
+struct Vdbe;<br>
<br>
/** Sequence metadata. */<br>
struct sequence_def {<br>
@@ -140,6 +141,17 @@ int<br>
access_check_sequence(struct sequence *seq);<br>
<br>
/**<br>
+ * Add new generated id in VDBE.<br>
+ *<br>
+ * @param Vdbe VDBE to save id in.<br>
+ * @param id ID to save in VDBE.<br>
+ * @retval 0 Success.<br>
+ * @retval -1 Error.<br>
+ */<br>
+int<br>
+vdbe_add_new_generated_id(struct Vdbe *vdbe, int64_t id);<br>
+<br>
+/**<br>
* Create an iterator over sequence data.<br>
*<br>
* The iterator creates a snapshot of sequence data and walks<br>
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c<br>
index 7c1015c..7a3748e 100644<br>
--- a/src/box/sql/vdbe.c<br>
+++ b/src/box/sql/vdbe.c<br>
@@ -578,6 +578,26 @@ out2Prerelease(Vdbe *p, VdbeOp *pOp)<br>
}<br>
}<br>
<br>
+struct stailq *<br>
+vdbe_generated_ids(struct Vdbe *vdbe)<br>
+{<br>
+ return &vdbe->generated_ids;<br>
+}<br>
+<br>
+int<br>
+vdbe_add_new_generated_id(struct Vdbe *vdbe, int64_t id)<br>
+{<br>
+ assert(vdbe != NULL);<br>
+ struct id_entry *id_entry = region_alloc(&fiber()->gc,
sizeof(*id_entry));<br>
+ if (id_entry == NULL) {<br>
+ diag_set(OutOfMemory, sizeof(*id_entry), "region_alloc",
"id_entry");<br>
+ return -1;<br>
+ }<br>
+ id_entry->id = id;<br>
+ stailq_add_tail_entry(vdbe_generated_ids(vdbe), id_entry,
link);<br>
+ return 0;<br>
+}<br>
+<br>
/*<br>
* Execute as much of a VDBE program as we can.<br>
* This is the core of sqlite3_step().<br>
@@ -2996,7 +3016,7 @@ case OP_TransactionBegin: {<br>
*/<br>
case OP_TransactionCommit: {<br>
if (box_txn()) {<br>
- if (box_txn_commit() != 0) {<br>
+ if (txn_commit(in_txn()) != 0) {<br>
rc = SQL_TARANTOOL_ERROR;<br>
goto abort_due_to_error;<br>
}<br>
@@ -3040,7 +3060,7 @@ case OP_TransactionRollback: {<br>
*/<br>
case OP_TTransaction: {<br>
if (!box_txn()) {<br>
- if (box_txn_begin() != 0) {<br>
+ if (sql_txn_begin(p) != 0) {<br>
rc = SQL_TARANTOOL_ERROR;<br>
goto abort_due_to_error;<br>
}<br>
diff --git a/src/box/sql/vdbe.h b/src/box/sql/vdbe.h<br>
index d5da5d5..8f877b3 100644<br>
--- a/src/box/sql/vdbe.h<br>
+++ b/src/box/sql/vdbe.h<br>
@@ -197,6 +197,15 @@ sql_alloc_txn();<br>
int<br>
sql_vdbe_prepare(struct Vdbe *vdbe);<br>
<br>
+/**<br>
+ * Return pointer to list of generated ids.<br>
+ *<br>
+ * @param vdbe VDBE to get list of generated ids from.<br>
+ * @retval List of generated ids.<br>
+ */<br>
+struct stailq *<br>
+vdbe_generated_ids(struct Vdbe *vdbe);<br>
+<br>
int sqlite3VdbeAddOp0(Vdbe *, int);<br>
int sqlite3VdbeAddOp1(Vdbe *, int, int);<br>
int sqlite3VdbeAddOp2(Vdbe *, int, int, int);<br>
diff --git a/src/box/sql/vdbeInt.h b/src/box/sql/vdbeInt.h<br>
index ce97f49..37df5e2 100644<br>
--- a/src/box/sql/vdbeInt.h<br>
+++ b/src/box/sql/vdbeInt.h<br>
@@ -368,6 +368,9 @@ struct Vdbe {<br>
/** The auto-commit flag. */<br>
bool auto_commit;<br>
<br>
+ /** List of id generated in current VDBE. */<br>
+ struct stailq generated_ids;<br>
+<br>
/* When allocating a new Vdbe object, all of the fields below
should be<br>
* initialized to zero or NULL<br>
*/<br>
diff --git a/src/box/sql/vdbeaux.c b/src/box/sql/vdbeaux.c<br>
index 6e42d05..0023dc6 100644<br>
--- a/src/box/sql/vdbeaux.c<br>
+++ b/src/box/sql/vdbeaux.c<br>
@@ -57,6 +57,8 @@ sqlite3VdbeCreate(Parse * pParse)<br>
return 0;<br>
memset(p, 0, sizeof(Vdbe));<br>
p->db = db;<br>
+ /* Init list of generated ids. */<br>
+ stailq_create(&p->generated_ids);<br>
if (db->pVdbe) {<br>
db->pVdbe->pPrev = p;<br>
}<br>
@@ -106,6 +108,7 @@ sql_vdbe_prepare(struct Vdbe *vdbe)<br>
txn->psql_txn = sql_alloc_txn();<br>
if (txn->psql_txn == NULL)<br>
return -1;<br>
+ txn->psql_txn->vdbe = vdbe;<br>
}<br>
vdbe->psql_txn = txn->psql_txn;<br>
} else {<br>
@@ -2266,6 +2269,7 @@ sql_txn_begin(Vdbe *p)<br>
box_txn_rollback();<br>
return -1;<br>
}<br>
+ ptxn->psql_txn->vdbe = p;<br>
p->psql_txn = ptxn->psql_txn;<br>
return 0;<br>
}<br>
@@ -2414,9 +2418,9 @@ sqlite3VdbeHalt(Vdbe * p)<br>
* key constraints to hold up the transaction.
This means a commit<br>
* is required.<br>
*/<br>
- rc = box_txn_commit() ==<br>
- 0 ? SQLITE_OK :<br>
- SQL_TARANTOOL_ERROR;<br>
+ rc = (in_txn() == NULL ||<br>
+ txn_commit(in_txn()) == 0) ?<br>
+ SQLITE_OK : SQL_TARANTOOL_ERROR;<br>
closeCursorsAndFree(p);<br>
}<br>
if (rc == SQLITE_BUSY && !p->pDelFrame)
{<br>
@@ -2780,6 +2784,8 @@ sqlite3VdbeDelete(Vdbe * p)<br>
}<br>
p->magic = VDBE_MAGIC_DEAD;<br>
p->db = 0;<br>
+ if (in_txn() == NULL)<br>
+ fiber_gc();<br>
sqlite3DbFree(db, p);<br>
}<br>
<br>
diff --git a/src/box/txn.h b/src/box/txn.h<br>
index e74c14d..2acba9f 100644<br>
--- a/src/box/txn.h<br>
+++ b/src/box/txn.h<br>
@@ -117,6 +117,17 @@ struct sql_txn {<br>
* VDBE to the next in the same transaction.<br>
*/<br>
uint32_t fk_deferred_count;<br>
+ /** Current VDBE. */<br>
+ struct Vdbe *vdbe;<br>
+};<br>
+<br>
+/** An element of list of generated ids. */<br>
+struct id_entry<br>
+{<br>
+ /** A link in a generated id list. */<br>
+ struct stailq_entry link;<br>
+ /** Generated id. */<br>
+ int64_t id;<br>
};<br>
<br>
struct txn {<br>
@@ -337,6 +348,20 @@ txn_last_stmt(struct txn *txn)<br>
void<br>
txn_on_stop(struct trigger *trigger, void *event);<br>
<br>
+/**<br>
+ * Return VDBE that is currently executed.<br>
+ *<br>
+ * @retval VDBE that is currently executed.<br>
+ * @retval NULL Either txn or ptxn_sql or vdbe is NULL;<br>
+ */<br>
+static inline struct Vdbe *<br>
+txn_vdbe()<br>
+{<br>
+ struct txn *txn = in_txn();<br>
+ if (txn == NULL || txn->psql_txn == NULL)<br>
+ return NULL;<br>
+ return txn->psql_txn->vdbe;<br>
+}<br>
<br>
/**<br>
* FFI bindings: do not throw exceptions, do not accept extra<br>
diff --git a/test/sql/iproto.result b/test/sql/iproto.result<br>
index af474bc..163fb6e 100644<br>
--- a/test/sql/iproto.result<br>
+++ b/test/sql/iproto.result<br>
@@ -580,6 +580,77 @@ cn:close()<br>
box.sql.execute('drop table test')<br>
---<br>
...<br>
+-- gh-2618 Return generated columns after INSERT in IPROTO.<br>
+-- Return all ids generated in current INSERT statement.<br>
+box.sql.execute('create table test (id integer primary key
autoincrement, a integer)')<br>
+---<br>
+...<br>
+cn = remote.connect(box.cfg.listen)<br>
+---<br>
+...<br>
+cn:execute('insert into test values (1, 1)')<br>
+---<br>
+- rowcount: 1<br>
+...<br>
+cn:execute('insert into test values (null, 2)')<br>
+---<br>
+- generated_ids:<br>
+ - 2<br>
+ rowcount: 1<br>
+...<br>
+cn:execute('update test set a = 11 where id == 1')<br>
+---<br>
+- rowcount: 1<br>
+...<br>
+cn:execute('insert into test values (100, 1), (null, 1), (120, 1),
(null, 1)')<br>
+---<br>
+- generated_ids:<br>
+ - 101<br>
+ - 121<br>
+ rowcount: 4<br>
+...<br>
+cn:execute('insert into test values (null, 1), (null, 1), (null,
1), (null, 1), (null, 1)')<br>
+---<br>
+- generated_ids:<br>
+ - 122<br>
+ - 123<br>
+ - 124<br>
+ - 125<br>
+ - 126<br>
+ rowcount: 5<br>
+...<br>
+cn:execute('select * from test')<br>
+---<br>
+- metadata:<br>
+ - name: ID<br>
+ - name: A<br>
+ rows:<br>
+ - [1, 11]<br>
+ - [2, 2]<br>
+ - [100, 1]<br>
+ - [101, 1]<br>
+ - [120, 1]<br>
+ - [121, 1]<br>
+ - [122, 1]<br>
+ - [123, 1]<br>
+ - [124, 1]<br>
+ - [125, 1]<br>
+ - [126, 1]<br>
+...<br>
+cn:execute('create table test2 (id integer primary key, a
integer)')<br>
+---<br>
+- rowcount: 1<br>
+...<br>
+cn:execute('drop table test2')<br>
+---<br>
+- rowcount: 1<br>
+...<br>
+cn:close()<br>
+---<br>
+...<br>
+box.sql.execute('drop table test')<br>
+---<br>
+...<br>
box.schema.user.revoke('guest', 'read,write,execute', 'universe')<br>
---<br>
...<br>
diff --git a/test/sql/iproto.test.lua b/test/sql/iproto.test.lua<br>
index 220331b..6517cde 100644<br>
--- a/test/sql/iproto.test.lua<br>
+++ b/test/sql/iproto.test.lua<br>
@@ -201,6 +201,21 @@ future4:wait_result()<br>
cn:close()<br>
box.sql.execute('drop table test')<br>
<br>
+-- gh-2618 Return generated columns after INSERT in IPROTO.<br>
+-- Return all ids generated in current INSERT statement.<br>
+box.sql.execute('create table test (id integer primary key
autoincrement, a integer)')<br>
+cn = remote.connect(box.cfg.listen)<br>
+cn:execute('insert into test values (1, 1)')<br>
+cn:execute('insert into test values (null, 2)')<br>
+cn:execute('update test set a = 11 where id == 1')<br>
+cn:execute('insert into test values (100, 1), (null, 1), (120, 1),
(null, 1)')<br>
+cn:execute('insert into test values (null, 1), (null, 1), (null,
1), (null, 1), (null, 1)')<br>
+cn:execute('select * from test')<br>
+cn:execute('create table test2 (id integer primary key, a
integer)')<br>
+cn:execute('drop table test2')<br>
+cn:close()<br>
+box.sql.execute('drop table test')<br>
+<br>
box.schema.user.revoke('guest', 'read,write,execute', 'universe')<br>
space = nil<br>
<br>
<br>
</body>
</html>