[Tarantool-patches] [PATCH v1 1/1] sql: use rowid as PK of ephemeral space on INSERT

Mergen Imeev imeevma at tarantool.org
Mon Mar 23 14:40:52 MSK 2020


Hi! Thank you for the patch. My answers, diff and new
version below.

On Wed, Mar 11, 2020 at 03:27:59PM +0000, Nikita Pettik wrote:
> On 25 Feb 13:17, Mergen Imeev wrote:
> > Hi! Thank you for review. My answers and new patch beliw.
> > Date: Mon, 24 Feb 2020 16:44:07 +0300
> > Subject: [PATCH] sql: do not change order of inserted values on INSERT
> > 
> > Prior to this patch, if ephemeral space was used during INSERT,
> 
> Does fix affect only insert, but not replace? Or both?
It affects both, but the problem appears only during INSERT
since we cannot use REPLACE to insert NULL in field with
AUTOINCREMENT. Changed the commit-message.

> 
> > the inserted values were sorted by the first column.
> 
> Are inserted values are always sorted during insertion op?
No. It says "if ephemeral space was used", is this not
enough?

> 
> > This can lead
> > to an error when using the autoincrement feature.
> 
> Why? Could you please provide details? Not personally for me,
> but in commit message.
Extended the commit message.

> 
> > To avoid this,
> > the patch makes it possible to make the rowid of the value
> > inserted into the ephemeral space be the first part of
> > ephemeral space index.
> 
> So, before patch rowid could be only last column of table?
Now it is also the last column of the table.

> And you achieve it extending sql_key_info with additional field?
Yes.

> 
> All these tip-questions should help you to make commit message
> more informative.
Thanks.

> 
> > Closes #4256
> 
> Please attach a changelog to the patch.
Done.

> 
> > 
> > diff --git a/src/box/sql.c b/src/box/sql.c
> > index 1256df8..47cf235 100644
> > --- a/src/box/sql.c
> > +++ b/src/box/sql.c
> > @@ -338,7 +338,10 @@ sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
> >  		return NULL;
> >  	}
> >  	for (uint32_t i = 0; i < field_count; ++i) {
> > -		struct key_part_def *part = &ephemer_key_parts[i];
> > +		uint32_t j = i;
> > +		if (key_info != NULL && key_info->is_rowid_first)
> > +			j = (j + 1) % field_count;
> 
> What is going on here??
Cyclic shift.

> 
> > +		struct key_part_def *part = &ephemer_key_parts[j];
> >  		part->fieldno = i;
> >  		part->nullable_action = ON_CONFLICT_ACTION_NONE;
> >  		part->is_nullable = true;
> > diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
> > index 43a0de5..6ee728a 100644
> > --- a/src/box/sql/insert.c
> > +++ b/src/box/sql/insert.c
> > @@ -455,8 +455,17 @@ sqlInsert(Parse * pParse,	/* Parser context */
> >  			reg_eph = ++pParse->nMem;
> >  			regRec = sqlGetTempReg(pParse);
> >  			regCopy = sqlGetTempRange(pParse, nColumn + 1);
> > -			sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
> > -					  nColumn + 1);
> > +			/*
> > +			 * This key_info is used to show that
> > +			 * rowid should be the first part of PK.
> > +			 */
> 
> Please, explain here why rowid should come as first column.
> See reference example in sql_table_delete_from():
> 
>    233			if (is_view) {
>    234				/*
>    235				 * At this stage SELECT is already materialized
>    236				 * into ephemeral table, which has one additional
>    237				 * tail field (except for ones specified in view
>    238				 * format) which contains sequential ids. These ids
>    239				 * are required since selected values may turn out to
>    240				 * be non-unique. For instance:
>    241				 * CREATE TABLE t (id INT PRIMARY KEY, a INT);
>    242				 * INSERT INTO t VALUES (1, 1), (2, 1), (3, 1);
>    243				 * CREATE VIEW v AS SELECT a FROM t;
> 
> ...
> 
Done.

> > +			struct sql_key_info *key_info =
> > +				sql_key_info_new(pParse->db, nColumn + 1);
> > +			key_info->parts[nColumn].type = FIELD_TYPE_UNSIGNED;
> > +			key_info->is_rowid_first = true;
> > +			sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
> > +				      nColumn + 1, 0, (char *)key_info,
> > +				      P4_KEYINFO);
> >  			addrL = sqlVdbeAddOp1(v, OP_Yield, dest.iSDParm);
> >  			VdbeCoverage(v);
> >  			sqlVdbeAddOp2(v, OP_NextIdEphemeral, reg_eph,
> > diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
> > index d1fcf47..d9da921 100644
> > --- a/src/box/sql/sqlInt.h
> > +++ b/src/box/sql/sqlInt.h
> > @@ -4111,6 +4111,8 @@ struct sql_key_info {
> >  	struct key_def *key_def;
> >  	/** Reference counter. */
> >  	uint32_t refs;
> > +	/** Rowid should be the first part of PK if true. */
> > +	bool is_rowid_first;
> 
> Otherwise it is last or not presented at all?
Yes. I changed the comment, but I am not sure if this is
right. The only porpose of this flag is to show that if
the rowid the first column. If this flag is false we may
say that rowid may be not the first column.

> 
> > diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
> > index 620d74e..db782a7 100644
> > --- a/src/box/sql/vdbe.c
> > +++ b/src/box/sql/vdbe.c
> > @@ -2702,8 +2702,25 @@ case OP_Column: {
> >  		 * key parts.
> >  		 */
> >  		if (pC->uc.pCursor->curFlags & BTCF_TEphemCursor) {
> > -			field_type = pC->uc.pCursor->index->def->
> > -					key_def->parts[p2].type;
> > +			struct key_def *key_def =
> > +				pC->uc.pCursor->index->def->key_def;
> > +			/*
> > +			 * There are three options:
> > +			 * |Rowid|First field|...|Last field|
> > +			 * Or:
> > +			 * |First field|...|Last field|
> > +			 * Or:
> > +			 * |First field|...|Last field|Rowid|
> > +			 *
> > +			 * If ephemeral space has a rowid column,
> 
> But it contradicts schema above.
How?

> 
> > +			 * it is always the last column. Due to
> > +			 * this, the field number of the rowid
> > +			 * column cannot be 0.
> > +			 */
> > +			int partno = p2;
> > +			if (key_def->parts[0].fieldno != 0)
> > +				partno = (partno + 1) % key_def->part_count;
> 
> See this for the second time, still don't understand what it is
> supposed to mean.
Simplified a bit and extended the comment.

> 
> > +			field_type = key_def->parts[partno].type;
> >  		} else if (pC->uc.pCursor->curFlags & BTCF_TaCursor) {
> >  			field_type = pC->uc.pCursor->space->def->
> >  					fields[p2].type;
> > diff --git a/test/sql/autoincrement.test.lua b/test/sql/autoincrement.test.lua
> > index 63a902a..99f4813 100644
> > --- a/test/sql/autoincrement.test.lua
> > +++ b/test/sql/autoincrement.test.lua
> > @@ -31,3 +31,13 @@ seqs = box.space._sequence:select{}
> >  #seqs == 0 or seqs
> >  
> >  box.schema.user.drop('user1')
> > +
> > +--
> > +-- gh-4256: make sure that when inserting, values are inserted in
> > +-- the given order when ephemeral space is used.
> > +--
> > +box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
> > +box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
> > +box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
> 
> Leave explanation why no-op trigger is required. Otherwise it
> looks redundant.
Done.

> Also, Kirill forces new convention saying that
> each bug fix should be placed at separate file named gh-xxxx-description.
Done.

> 

> > +box.execute('SELECT * FROM t;')
> > +box.execute('DROP TABLE t;')


Diff:


diff --git a/src/box/sql.c b/src/box/sql.c
index 47cf235..8c25095 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -339,6 +339,12 @@ sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
 	}
 	for (uint32_t i = 0; i < field_count; ++i) {
 		uint32_t j = i;
+		/*
+		 * In case we know that the last column is rowid,
+		 * and we want to make it the first part of the
+		 * index, we will do a cyclic shift. Thus, we will
+		 * not disrupt the order of other columns.
+		 */
 		if (key_info != NULL && key_info->is_rowid_first)
 			j = (j + 1) % field_count;
 		struct key_part_def *part = &ephemer_key_parts[j];
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 6ee728a..28e3096 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -458,6 +458,12 @@ sqlInsert(Parse * pParse,	/* Parser context */
 			/*
 			 * This key_info is used to show that
 			 * rowid should be the first part of PK.
+			 * This way we will save initial order of
+			 * the inserted values. The order is
+			 * important if we use the AUTOINCREMENT
+			 * feature, since changing the order can
+			 * change the number inserted instead of
+			 * NULL.
 			 */
 			struct sql_key_info *key_info =
 				sql_key_info_new(pParse->db, nColumn + 1);
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 9c00a7b..a5f0249 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4114,7 +4114,11 @@ struct sql_key_info {
 	struct key_def *key_def;
 	/** Reference counter. */
 	uint32_t refs;
-	/** Rowid should be the first part of PK if true. */
+	/**
+	 * Rowid should be the first part of PK, if true. If this
+	 * flag is false, rowid may be any part of the index or
+	 * may be absent.
+	 */
 	bool is_rowid_first;
 	/** Number of parts in the key. */
 	uint32_t part_count;
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index a200063..e5cfa5e 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2715,11 +2715,21 @@ case OP_Column: {
 			 * If ephemeral space has a rowid column,
 			 * it is always the last column. Due to
 			 * this, the field number of the rowid
-			 * column cannot be 0.
+			 * column cannot be 0. So, if the first
+			 * part of the index have something other
+			 * than 0 as a fieldno, we know that this
+			 * is fieldno of rowid, since in all other
+			 * cases fieldno of the first part is 0.
+			 *
+			 * If rowid is the first part of the
+			 * index, we know that all the other parts
+			 * are columns sorted in the required
+			 * order. So, we should increment partno
+			 * by 1.
 			 */
-			int partno = p2;
+			uint32_t partno = p2;
 			if (key_def->parts[0].fieldno != 0)
-				partno = (partno + 1) % key_def->part_count;
+				++partno;
 			field_type = key_def->parts[partno].type;
 		} else if (pC->uc.pCursor->curFlags & BTCF_TaCursor) {
 			field_type = pC->uc.pCursor->space->def->
diff --git a/test/sql/autoincrement.result b/test/sql/autoincrement.result
index 7d3e902..ca3d6f3 100644
--- a/test/sql/autoincrement.result
+++ b/test/sql/autoincrement.result
@@ -78,43 +78,3 @@ seqs = box.space._sequence:select{}
 box.schema.user.drop('user1')
  | ---
  | ...
-
---
--- gh-4256: make sure that when inserting, values are inserted in
--- the given order when ephemeral space is used.
---
-box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
- | ---
- | - row_count: 1
- | ...
-box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
- | ---
- | - row_count: 1
- | ...
-box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
- | ---
- | - autoincrement_ids:
- |   - 2
- |   - 11
- |   - 12
- |   - 13
- |   row_count: 7
- | ...
-box.execute('SELECT * FROM t;')
- | ---
- | - metadata:
- |   - name: I
- |     type: integer
- |   rows:
- |   - [1]
- |   - [2]
- |   - [3]
- |   - [10]
- |   - [11]
- |   - [12]
- |   - [13]
- | ...
-box.execute('DROP TABLE t;')
- | ---
- | - row_count: 1
- | ...
diff --git a/test/sql/autoincrement.test.lua b/test/sql/autoincrement.test.lua
index 99f4813..63a902a 100644
--- a/test/sql/autoincrement.test.lua
+++ b/test/sql/autoincrement.test.lua
@@ -31,13 +31,3 @@ seqs = box.space._sequence:select{}
 #seqs == 0 or seqs
 
 box.schema.user.drop('user1')
-
---
--- gh-4256: make sure that when inserting, values are inserted in
--- the given order when ephemeral space is used.
---
-box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
-box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
-box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
-box.execute('SELECT * FROM t;')
-box.execute('DROP TABLE t;')
diff --git a/test/sql/gh-4256-do-not-change-order-during-insertion.result b/test/sql/gh-4256-do-not-change-order-during-insertion.result
new file mode 100644
index 0000000..56e28b5
--- /dev/null
+++ b/test/sql/gh-4256-do-not-change-order-during-insertion.result
@@ -0,0 +1,50 @@
+-- test-run result file version 2
+test_run = require('test_run').new()
+ | ---
+ | ...
+engine = test_run:get_cfg('engine')
+ | ---
+ | ...
+--
+-- Make sure that when inserting, values are inserted in the given
+-- order when ephemeral space is used.
+--
+box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
+ | ---
+ | - row_count: 1
+ | ...
+--
+-- In order for this INSERT to use the ephemeral space, we created
+-- this trigger.
+--
+box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
+ | ---
+ | - row_count: 1
+ | ...
+box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
+ | ---
+ | - autoincrement_ids:
+ |   - 2
+ |   - 11
+ |   - 12
+ |   - 13
+ |   row_count: 7
+ | ...
+box.execute('SELECT * FROM t;')
+ | ---
+ | - metadata:
+ |   - name: I
+ |     type: integer
+ |   rows:
+ |   - [1]
+ |   - [2]
+ |   - [3]
+ |   - [10]
+ |   - [11]
+ |   - [12]
+ |   - [13]
+ | ...
+box.execute('DROP TABLE t;')
+ | ---
+ | - row_count: 1
+ | ...
diff --git a/test/sql/gh-4256-do-not-change-order-during-insertion.test.lua b/test/sql/gh-4256-do-not-change-order-during-insertion.test.lua
new file mode 100644
index 0000000..4d8367c
--- /dev/null
+++ b/test/sql/gh-4256-do-not-change-order-during-insertion.test.lua
@@ -0,0 +1,15 @@
+test_run = require('test_run').new()
+engine = test_run:get_cfg('engine')
+--
+-- Make sure that when inserting, values are inserted in the given
+-- order when ephemeral space is used.
+--
+box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
+--
+-- In order for this INSERT to use the ephemeral space, we created
+-- this trigger.
+--
+box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
+box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
+box.execute('SELECT * FROM t;')
+box.execute('DROP TABLE t;')


New version:


>From 4244c170e0b22fa1084d8d9e1fac9c252b9b73cd Mon Sep 17 00:00:00 2001
Message-Id: <4244c170e0b22fa1084d8d9e1fac9c252b9b73cd.1584962777.git.imeevma at gmail.com>
From: Mergen Imeev <imeevma at gmail.com>
Date: Mon, 24 Feb 2020 16:44:07 +0300
Subject: [PATCH v2 1/1] sql: do not change order of inserted values

Before this patch, if an ephemeral space was used during INSERT or
REPLACE, the inserted values were sorted by the first column,
since this was the first part of the index. This can lead to an
error when using the AUTOINCREMENT feature, since changing the
order of the inserted value can change the value inserted instead
of NULL. To avoid this, the patch makes the rowid of the inserted
row in the ephemeral space the first part of the ephemeral space
index. Currently, if the ephemeral space has a rowid, it is always
the last column of the ephemeral space. So, to make rowid the
first part of the index, a cyclic shift is used. Thus, we will not
change the order of all other columns.

Closes #4256
---
https://github.com/tarantool/tarantool/issues/4256
https://github.com/tarantool/tarantool/tree/imeevma/gh-4256-fix-order-during-insertion

@ChangeLog
 - During INSERT and REPLACE the order of inserted rows will not 
   be changed (gh-4256).

 src/box/sql.c                                      | 11 ++++-
 src/box/sql/insert.c                               | 19 +++++++-
 src/box/sql/select.c                               |  2 +
 src/box/sql/sqlInt.h                               |  6 +++
 src/box/sql/vdbe.c                                 | 31 +++++++++++++-
 ...256-do-not-change-order-during-insertion.result | 50 ++++++++++++++++++++++
 ...6-do-not-change-order-during-insertion.test.lua | 15 +++++++
 7 files changed, 129 insertions(+), 5 deletions(-)
 create mode 100644 test/sql/gh-4256-do-not-change-order-during-insertion.result
 create mode 100644 test/sql/gh-4256-do-not-change-order-during-insertion.test.lua

diff --git a/src/box/sql.c b/src/box/sql.c
index 1256df8..8c25095 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -338,7 +338,16 @@ sql_ephemeral_space_create(uint32_t field_count, struct sql_key_info *key_info)
 		return NULL;
 	}
 	for (uint32_t i = 0; i < field_count; ++i) {
-		struct key_part_def *part = &ephemer_key_parts[i];
+		uint32_t j = i;
+		/*
+		 * In case we know that the last column is rowid,
+		 * and we want to make it the first part of the
+		 * index, we will do a cyclic shift. Thus, we will
+		 * not disrupt the order of other columns.
+		 */
+		if (key_info != NULL && key_info->is_rowid_first)
+			j = (j + 1) % field_count;
+		struct key_part_def *part = &ephemer_key_parts[j];
 		part->fieldno = i;
 		part->nullable_action = ON_CONFLICT_ACTION_NONE;
 		part->is_nullable = true;
diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c
index 43a0de5..28e3096 100644
--- a/src/box/sql/insert.c
+++ b/src/box/sql/insert.c
@@ -455,8 +455,23 @@ sqlInsert(Parse * pParse,	/* Parser context */
 			reg_eph = ++pParse->nMem;
 			regRec = sqlGetTempReg(pParse);
 			regCopy = sqlGetTempRange(pParse, nColumn + 1);
-			sqlVdbeAddOp2(v, OP_OpenTEphemeral, reg_eph,
-					  nColumn + 1);
+			/*
+			 * This key_info is used to show that
+			 * rowid should be the first part of PK.
+			 * This way we will save initial order of
+			 * the inserted values. The order is
+			 * important if we use the AUTOINCREMENT
+			 * feature, since changing the order can
+			 * change the number inserted instead of
+			 * NULL.
+			 */
+			struct sql_key_info *key_info =
+				sql_key_info_new(pParse->db, nColumn + 1);
+			key_info->parts[nColumn].type = FIELD_TYPE_UNSIGNED;
+			key_info->is_rowid_first = true;
+			sqlVdbeAddOp4(v, OP_OpenTEphemeral, reg_eph,
+				      nColumn + 1, 0, (char *)key_info,
+				      P4_KEYINFO);
 			addrL = sqlVdbeAddOp1(v, OP_Yield, dest.iSDParm);
 			VdbeCoverage(v);
 			sqlVdbeAddOp2(v, OP_NextIdEphemeral, reg_eph,
diff --git a/src/box/sql/select.c b/src/box/sql/select.c
index 65e41f2..105a4a3 100644
--- a/src/box/sql/select.c
+++ b/src/box/sql/select.c
@@ -1422,6 +1422,7 @@ sql_key_info_new(sql *db, uint32_t part_count)
 	key_info->key_def = NULL;
 	key_info->refs = 1;
 	key_info->part_count = part_count;
+	key_info->is_rowid_first = false;
 	for (uint32_t i = 0; i < part_count; i++) {
 		struct key_part_def *part = &key_info->parts[i];
 		part->fieldno = i;
@@ -1448,6 +1449,7 @@ sql_key_info_new_from_key_def(sql *db, const struct key_def *key_def)
 	key_info->key_def = NULL;
 	key_info->refs = 1;
 	key_info->part_count = key_def->part_count;
+	key_info->is_rowid_first = false;
 	key_def_dump_parts(key_def, key_info->parts, NULL);
 	return key_info;
 }
diff --git a/src/box/sql/sqlInt.h b/src/box/sql/sqlInt.h
index 1579cc9..a5f0249 100644
--- a/src/box/sql/sqlInt.h
+++ b/src/box/sql/sqlInt.h
@@ -4114,6 +4114,12 @@ struct sql_key_info {
 	struct key_def *key_def;
 	/** Reference counter. */
 	uint32_t refs;
+	/**
+	 * Rowid should be the first part of PK, if true. If this
+	 * flag is false, rowid may be any part of the index or
+	 * may be absent.
+	 */
+	bool is_rowid_first;
 	/** Number of parts in the key. */
 	uint32_t part_count;
 	/** Definition of the key parts. */
diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c
index e8a029a..e5cfa5e 100644
--- a/src/box/sql/vdbe.c
+++ b/src/box/sql/vdbe.c
@@ -2702,8 +2702,35 @@ case OP_Column: {
 		 * key parts.
 		 */
 		if (pC->uc.pCursor->curFlags & BTCF_TEphemCursor) {
-			field_type = pC->uc.pCursor->index->def->
-					key_def->parts[p2].type;
+			struct key_def *key_def =
+				pC->uc.pCursor->index->def->key_def;
+			/*
+			 * There are three options:
+			 * |Rowid|First field|...|Last field|
+			 * Or:
+			 * |First field|...|Last field|
+			 * Or:
+			 * |First field|...|Last field|Rowid|
+			 *
+			 * If ephemeral space has a rowid column,
+			 * it is always the last column. Due to
+			 * this, the field number of the rowid
+			 * column cannot be 0. So, if the first
+			 * part of the index have something other
+			 * than 0 as a fieldno, we know that this
+			 * is fieldno of rowid, since in all other
+			 * cases fieldno of the first part is 0.
+			 *
+			 * If rowid is the first part of the
+			 * index, we know that all the other parts
+			 * are columns sorted in the required
+			 * order. So, we should increment partno
+			 * by 1.
+			 */
+			uint32_t partno = p2;
+			if (key_def->parts[0].fieldno != 0)
+				++partno;
+			field_type = key_def->parts[partno].type;
 		} else if (pC->uc.pCursor->curFlags & BTCF_TaCursor) {
 			field_type = pC->uc.pCursor->space->def->
 					fields[p2].type;
diff --git a/test/sql/gh-4256-do-not-change-order-during-insertion.result b/test/sql/gh-4256-do-not-change-order-during-insertion.result
new file mode 100644
index 0000000..56e28b5
--- /dev/null
+++ b/test/sql/gh-4256-do-not-change-order-during-insertion.result
@@ -0,0 +1,50 @@
+-- test-run result file version 2
+test_run = require('test_run').new()
+ | ---
+ | ...
+engine = test_run:get_cfg('engine')
+ | ---
+ | ...
+--
+-- Make sure that when inserting, values are inserted in the given
+-- order when ephemeral space is used.
+--
+box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
+ | ---
+ | - row_count: 1
+ | ...
+--
+-- In order for this INSERT to use the ephemeral space, we created
+-- this trigger.
+--
+box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
+ | ---
+ | - row_count: 1
+ | ...
+box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
+ | ---
+ | - autoincrement_ids:
+ |   - 2
+ |   - 11
+ |   - 12
+ |   - 13
+ |   row_count: 7
+ | ...
+box.execute('SELECT * FROM t;')
+ | ---
+ | - metadata:
+ |   - name: I
+ |     type: integer
+ |   rows:
+ |   - [1]
+ |   - [2]
+ |   - [3]
+ |   - [10]
+ |   - [11]
+ |   - [12]
+ |   - [13]
+ | ...
+box.execute('DROP TABLE t;')
+ | ---
+ | - row_count: 1
+ | ...
diff --git a/test/sql/gh-4256-do-not-change-order-during-insertion.test.lua b/test/sql/gh-4256-do-not-change-order-during-insertion.test.lua
new file mode 100644
index 0000000..4d8367c
--- /dev/null
+++ b/test/sql/gh-4256-do-not-change-order-during-insertion.test.lua
@@ -0,0 +1,15 @@
+test_run = require('test_run').new()
+engine = test_run:get_cfg('engine')
+--
+-- Make sure that when inserting, values are inserted in the given
+-- order when ephemeral space is used.
+--
+box.execute('CREATE TABLE t (i INT PRIMARY KEY AUTOINCREMENT);')
+--
+-- In order for this INSERT to use the ephemeral space, we created
+-- this trigger.
+--
+box.execute('CREATE TRIGGER r AFTER INSERT ON t FOR EACH ROW BEGIN SELECT 1; END')
+box.execute('INSERT INTO t VALUES (1), (NULL), (10), (NULL), (NULL), (3), (NULL);')
+box.execute('SELECT * FROM t;')
+box.execute('DROP TABLE t;')
-- 
2.7.4



More information about the Tarantool-patches mailing list