<div dir="ltr"><br>Hello, Nikita! Here's newer version of the patch.<div>Diff can be found at the end.</div><div><br><div class="gmail_quote"><div dir="ltr">ср, 18 июл. 2018 г. в 18:13, n.pettik <<a href="mailto:korablev@tarantool.org" target="_blank">korablev@tarantool.org</a>>:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Please, add to commit message results of benchmark to indicate<br>
that this optimisation really matters.<br>
<br></blockquote><div><br></div><div>Added.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
> On 17 Jul 2018, at 00:27, Nikita Tatunov <<a href="mailto:hollow653@gmail.com" target="_blank">hollow653@gmail.com</a>> wrote:<br>
> <br>
> diff --git a/src/box/sql.c b/src/box/sql.c<br>
> index fdce224..398b2a6 100644<br>
> --- a/src/box/sql.c<br>
> +++ b/src/box/sql.c<br>
> @@ -1636,10 +1636,12 @@ sql_debug_info(struct info_handler *h)<br>
>       extern int sql_search_count;<br>
>       extern int sql_sort_count;<br>
>       extern int sql_found_count;<br>
> +     extern int sql_xferOpt_count;<br>
<br>
Don’t use camel notation. Lets call it simply ’sql_xfer_count’.<br>
<br></blockquote><div><br></div><div>Fixed.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
>       info_begin(h);<br>
>       info_append_int(h, "sql_search_count", sql_search_count);<br>
>       info_append_int(h, "sql_sort_count", sql_sort_count);<br>
>       info_append_int(h, "sql_found_count", sql_found_count);<br>
> +     info_append_int(h, "sql_xferOpt_count", sql_xferOpt_count);<br>
>       info_end(h);<br>
>  }<br>
>  <br>
> diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c<br>
> index 2c9188e..9a99bab 100644<br>
> --- a/src/box/sql/insert.c<br>
> +++ b/src/box/sql/insert.c<br>
> @@ -1635,7 +1635,7 @@ sqlite3OpenTableAndIndices(Parse * pParse,      /* Parsing context */<br>
>   * purposes only - to make sure the transfer optimization really<br>
>   * is happening when it is supposed to.<br>
>   */<br>
> -int sqlite3_xferopt_count;<br>
> +int sql_xferOpt_count = 0;<br>
>  #endif                               /* SQLITE_TEST */<br>
>  <br>
>  #ifndef SQLITE_OMIT_XFER_OPT<br>
> @@ -1658,6 +1658,8 @@ xferCompatibleIndex(Index * pDest, Index * pSrc)<br>
>       assert(pDest->pTable != pSrc->pTable);<br>
>       uint32_t nDestCol = index_column_count(pDest);<br>
>       uint32_t nSrcCol = index_column_count(pSrc);<br>
> +     if ((pDest->idxType != pSrc->idxType))<br>
> +             return 0;<br>
>       if (nDestCol != nSrcCol) {<br>
>               return 0;       /* Different number of columns */<br>
>       }<br>
> @@ -1725,9 +1727,9 @@ xferOptimization(Parse * pParse,        /* Parser context */<br>
>       int emptyDestTest = 0;  /* Address of test for empty pDest */<br>
>       int emptySrcTest = 0;   /* Address of test for empty pSrc */<br>
>       Vdbe *v;                /* The VDBE we are building */<br>
> -     int destHasUniqueIdx = 0;       /* True if pDest has a UNIQUE index */<br>
>       int regData, regTupleid;        /* Registers holding data and tupleid */<br>
>       struct session *user_session = current_session();<br>
> +     bool is_err_action_default = false;<br>
<br>
Again: why do you need this flag? Default action is just synonym for ABORT,<br>
so why should we care about it?<br>
<br></blockquote><div><br></div><div>It's all about conflict action priorities as I said before.</div><div>Consider the following example:<br>```<br><div><span style="white-space:pre">          </span>CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);</div><div><span style="white-space:pre">                </span>CREATE TABLE t2(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);</div><div><span style="white-space:pre">                </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div><span style="white-space:pre">         </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div><span style="white-space:pre">         </span>BEGIN;</div><div><span style="white-space:pre">                        </span>INSERT INTO t2 VALUES (4, 4);</div><div><span style="white-space:pre">                 </span>INSERT INTO t2 SELECT * FROM t1;</div></div><div><div><span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (10, 10);</div><div><span style="white-space:pre">               </span>COMMIT;</div></div><div>```</div><div>As we understand *_REPLACE should work in this case but</div><div>onError == *_ABORT (as it was converted from the default one). </div><div>It leads to a situation where an error will occur if xferOptimization is used.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
> +     struct space *src_space =<br>
> +             space_by_id(SQLITE_PAGENO_TO_SPACEID(pSrc->tnum));<br>
> +     struct space *dest_space =<br>
> +             space_by_id(SQLITE_PAGENO_TO_SPACEID(pDest->tnum));<br>
<br>
You don’t need to again proceed lookup: space is found few lines above.<br>
Moreover, I see those lookups are executed inside ‘for' loop. Lets move<br>
them outside it.<br></blockquote><div><br></div><div>Fixed it.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> +     struct index *src_idx = space_index(src_space, 0);<br>
> +     struct index *dest_idx = space_index(dest_space, 0);<br>
> +<br>
> +     /* Xfer optimization is unable to correctly insert data<br>
> +      * in case there's a conflict action other than *_ABORT.<br>
> +      * This is the reason we want to only run it if the<br>
> +      * destination table is initially empty.<br>
> +      * That block generates code to make that determination.<br>
> +      */<br>
<br>
Multi-line comment should be formatted as following:<br>
<br>
/*<br>
 * Comment starts here.<br>
 * …<br>
 */<br></blockquote><div><br></div><div>Fixed.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> +<br>
> +     if (!(onError == ON_CONFLICT_ACTION_ABORT &&<br>
> +         is_err_action_default == false)) {<br>
>               addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);<br>
>               VdbeCoverage(v);<br>
>               emptyDestTest = sqlite3VdbeAddOp0(v, OP_Goto);<br>
>               sqlite3VdbeJumpHere(v, addr1);<br>
> +#ifdef SQLITE_TEST<br>
> +             if (dest_idx->vtab->count(dest_idx, ITER_ALL, NULL, 0) == 0)<br>
> +                     sql_xferOpt_count++;<br>
<br>
Actually, I don’t like this approach.<br>
Look, query may be compiled and saved into cache (even thought it is still<br>
not implemented yet). So it might be executed later and it might be not empty.<br>
Moreover, we are going to avoid doing space lookups and use only def.<br>
With only def you can’t execute count.<br>
<br>
Personally, I wanted you to defer incrementing sql_xfer_count till<br>
VDBE execution. For instance, you may add special flag and pass it<br>
to OP_RowData indicating that xFer is currently processing.<br>
<br>
> +#endif<br>
> +     vdbe_emit_open_cursor(pParse, iSrc, 0, src_space);<br>
> +     VdbeComment((v, "%s", src_idx->def->name));<br>
> +     vdbe_emit_open_cursor(pParse, iDest, 0, dest_space);<br>
<br>
I see few lines above:<br>
<br>
sqlite3OpenTable(pParse, iDest, pDest, OP_OpenWrite);<br>
<br>
So, basically you don’t need to open it again.<br></blockquote><div><br></div><div>Fixed it.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
> +     VdbeComment((v, "%s", dest_idx->def->name));<br>
> +     addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);<br>
> +     VdbeCoverage(v);<br>
> +     sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);<br>
> +     sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);<br>
> +     sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);<br>
> +     sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1 + 1);<br>
> +     VdbeCoverage(v);<br>
> +     sqlite3VdbeJumpHere(v, addr1);<br>
> +     sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);<br>
> +     sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);<br>
> +<br>
>       if (emptySrcTest)<br>
>               sqlite3VdbeJumpHere(v, emptySrcTest);<br>
>       sqlite3ReleaseTempReg(pParse, regTupleid);<br>
> diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua<br>
> new file mode 100755<br>
> index 0000000..e75fabc<br>
> --- /dev/null<br>
> +test:do_catchsql_test(<br>
> +     "xfer-optimization-1.15",<br>
> +     [[<br>
> +             DROP TABLE t1;<br>
> +             DROP TABLE t2;<br>
> +             CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);<br>
> +             CREATE TABLE t2(a INTEGER PRIMARY KEY, b UNIQUE);<br>
> +             INSERT INTO t1 VALUES (2, 2), (3, 3), (5, 5);<br>
> +             INSERT INTO t2 VALUES (1, 1), (4, 4);<br>
> +             INSERT OR ROLLBACK INTO t2 SELECT * FROM t1;<br>
<br>
INSERT OT ROLLBACK outside transaction works the same as ABORT and DEFAULT.<br>
So, surround it with transaction and check that it really rollbacks.<br>
<br></blockquote><div><br></div><div>There are basically almost the same tests surrounded by transactions (1.30 - 1.35).</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
> +     ]], {<br>
> +             -- <xfer-optimization-1.15><br>
> +             0<br>
> +             -- <xfer-optimization-1.15><br>
> +     })<br>
> +<br>
> +test:do_execsql_test(<br>
> +     "xfer-optimization-1.16",<br>
> +     [[<br>
> +             SELECT * FROM t2;<br>
> +     ]], {<br>
> +             -- <xfer-optimization-1.16><br>
> +             1, 1, 2, 2, 3, 3, 4, 4, 5, 5<br>
> +             -- <xfer-optimization-1.16><br>
> +     })<br>
> +<br>
> +-- The following tests are supposed to test if xfer-optimization is actually<br>
> +-- used in the given cases (if the conflict actually occurs):<br>
> +--   1.0) insert w/o explicit confl. action & w/o index replace action<br>
> +--   1.1) insert w/o explicit confl. action & w/ index replace action & empty dest_table<br>
> +--   1.2) insert w/o explicit confl. action & w/ index replace action & non-empty dest_table<br>
> +--   2) insert with abort<br>
> +--   3.0) insert with rollback (into empty table)<br>
> +--   3.1) insert with rollback (into non-empty table)<br>
> +--   4) insert with replace<br>
> +--   5) insert with fail<br>
> +--   6) insert with ignore<br>
> +<br>
> +<br>
> +-- 1.0) insert w/o explicit confl. action & w/o index replace action<br>
> +-------------------------------------------------------------------------------------------<br>
> +<br>
> +bfr = box.sql.debug().sql_xferOpt_count<br>
> +<br>
> +test:do_catchsql_test(<br>
> +     "xfer-optimization-1.17",<br>
> +     [[<br>
> +             DROP TABLE t1;<br>
> +             DROP TABLE t2;<br>
> +             CREATE TABLE t1(a INTEGER PRIMARY KEY, b);<br>
> +             CREATE TABLE t2(a INTEGER PRIMARY KEY, b);<br>
> +             INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);<br>
> +             INSERT INTO t2 VALUES (2, 2), (3, 4);<br>
> +             BEGIN;<br>
> +                     INSERT INTO t2 VALUES (4, 4);<br>
> +                     INSERT INTO t2 SELECT * FROM t1;<br>
> +     ]], {<br>
> +             -- <xfer-optimization-1.17><br>
> +             1, "Duplicate key exists in unique index 'sqlite_autoindex_T2_1' in space 'T2'"<br>
> +             -- <xfer-optimization-1.17><br>
> +     })<br>
> +<br>
> +test:do_execsql_test(<br>
> +     "xfer-optimization-1.18",<br>
> +     [[<br>
> +                     INSERT INTO t2 VALUES (10, 10);<br>
> +             COMMIT;<br>
> +             SELECT * FROM t2;<br>
> +     ]], {<br>
> +             -- <xfer-optimization-1.18><br>
> +             2, 2, 3, 4, 4, 4, 10, 10<br>
> +             -- <xfer-optimization-1.18><br>
> +     })<br>
> +<br>
> +aftr = box.sql.debug().sql_xferOpt_count<br>
> +<br>
> +test:do_test(<br>
> +     "xfer-optimization-1.19",<br>
> +     function()<br>
> +             if (aftr - bfr == 1) then<br>
> +                     return {1}<br>
> +             end<br>
> +             if (aftr == bfr) then<br>
> +                     return {0}<br>
> +             end<br>
> +             return {2}<br>
<br>
Why do you repeat this snippet each time? You can declare it as named<br>
function once and use it everywhere.<br>
<br>
> +     end, {<br>
> +             -- <xfer-optimization-1.19><br>
> +             0<br>
> +             -- <xfer-optimization-1.19><br>
> +     })<br>
> +<br>
> +-- 1.1) insert w/o explicit confl. action & w/ index replace action & empty dest_table<br>
<br>
Even in tests lets not exceed 80 chars (here and in other places).<br></blockquote><div><br></div><div>diff --git a/src/box/sql.c b/src/box/sql.c</div><div>index fdce224..656ba17 100644</div><div>--- a/src/box/sql.c</div><div>+++ b/src/box/sql.c</div><div>@@ -1636,10 +1636,12 @@ sql_debug_info(struct info_handler *h)</div><div> <span style="white-space:pre">       </span>extern int sql_search_count;</div><div> <span style="white-space:pre">        </span>extern int sql_sort_count;</div><div> <span style="white-space:pre">  </span>extern int sql_found_count;</div><div>+<span style="white-space:pre">  </span>extern int sql_xfer_count;</div><div> <span style="white-space:pre">  </span>info_begin(h);</div><div> <span style="white-space:pre">      </span>info_append_int(h, "sql_search_count", sql_search_count);</div><div> <span style="white-space:pre"> </span>info_append_int(h, "sql_sort_count", sql_sort_count);</div><div> <span style="white-space:pre">     </span>info_append_int(h, "sql_found_count", sql_found_count);</div><div>+<span style="white-space:pre">    </span>info_append_int(h, "sql_xfer_count", sql_xfer_count);</div><div> <span style="white-space:pre">     </span>info_end(h);</div><div> }</div><div> </div><div>diff --git a/src/box/sql/insert.c b/src/box/sql/insert.c</div><div>index 2c9188e..c2df1c2 100644</div><div>--- a/src/box/sql/insert.c</div><div>+++ b/src/box/sql/insert.c</div><div>@@ -1628,16 +1628,6 @@ sqlite3OpenTableAndIndices(Parse * pParse,<span style="white-space:pre"> </span>/* Parsing context */</div><div> <span style="white-space:pre">       </span>return i;</div><div> }</div><div> </div><div>-#ifdef SQLITE_TEST</div><div>-/*</div><div>- * The following global variable is incremented whenever the</div><div>- * transfer optimization is used.  This is used for testing</div><div>- * purposes only - to make sure the transfer optimization really</div><div>- * is happening when it is supposed to.</div><div>- */</div><div>-int sqlite3_xferopt_count;</div><div>-#endif<span style="white-space:pre">                           </span>/* SQLITE_TEST */</div><div>-</div><div> #ifndef SQLITE_OMIT_XFER_OPT</div><div> /*</div><div>  * Check to see if index pSrc is compatible as a source of data</div><div>@@ -1658,6 +1648,8 @@ xferCompatibleIndex(Index * pDest, Index * pSrc)</div><div> <span style="white-space:pre">      </span>assert(pDest->pTable != pSrc->pTable);</div><div> <span style="white-space:pre">        </span>uint32_t nDestCol = index_column_count(pDest);</div><div> <span style="white-space:pre">      </span>uint32_t nSrcCol = index_column_count(pSrc);</div><div>+<span style="white-space:pre"> </span>if ((pDest->idxType != pSrc->idxType))</div><div>+<span style="white-space:pre">         </span>return 0;</div><div> <span style="white-space:pre">   </span>if (nDestCol != nSrcCol) {</div><div> <span style="white-space:pre">          </span>return 0;<span style="white-space:pre">    </span>/* Different number of columns */</div><div> <span style="white-space:pre">   </span>}</div><div>@@ -1724,10 +1716,9 @@ xferOptimization(Parse * pParse,<span style="white-space:pre">      </span>/* Parser context */</div><div> <span style="white-space:pre">        </span>int addr1;<span style="white-space:pre">           </span>/* Loop addresses */</div><div> <span style="white-space:pre">        </span>int emptyDestTest = 0;<span style="white-space:pre">       </span>/* Address of test for empty pDest */</div><div> <span style="white-space:pre">       </span>int emptySrcTest = 0;<span style="white-space:pre">        </span>/* Address of test for empty pSrc */</div><div>-<span style="white-space:pre"> </span>Vdbe *v;<span style="white-space:pre">             </span>/* The VDBE we are building */</div><div>-<span style="white-space:pre">       </span>int destHasUniqueIdx = 0;<span style="white-space:pre">    </span>/* True if pDest has a UNIQUE index */</div><div> <span style="white-space:pre">      </span>int regData, regTupleid;<span style="white-space:pre">     </span>/* Registers holding data and tupleid */</div><div> <span style="white-space:pre">    </span>struct session *user_session = current_session();</div><div>+<span style="white-space:pre">    </span>bool is_err_action_default = false;</div><div> </div><div> <span style="white-space:pre">        </span>if (pSelect == NULL)</div><div> <span style="white-space:pre">                </span>return 0;<span style="white-space:pre">    </span>/* Must be of the form  INSERT INTO ... SELECT ... */</div><div>@@ -1744,8 +1735,10 @@ xferOptimization(Parse * pParse,<span style="white-space:pre"> </span>/* Parser context */</div><div> <span style="white-space:pre">        </span>if (onError == ON_CONFLICT_ACTION_DEFAULT) {</div><div> <span style="white-space:pre">                </span>if (pDest->iPKey >= 0)</div><div> <span style="white-space:pre">                        </span>onError = pDest->keyConf;</div><div>-<span style="white-space:pre">         </span>if (onError == ON_CONFLICT_ACTION_DEFAULT)</div><div>+<span style="white-space:pre">           </span>if (onError == ON_CONFLICT_ACTION_DEFAULT) {</div><div> <span style="white-space:pre">                        </span>onError = ON_CONFLICT_ACTION_ABORT;</div><div>+<span style="white-space:pre">                  </span>is_err_action_default = true;</div><div>+<span style="white-space:pre">                </span>}</div><div> <span style="white-space:pre">   </span>}</div><div> <span style="white-space:pre">   </span>assert(pSelect->pSrc);<span style="white-space:pre">    </span>/* allocated even if there is no FROM clause */</div><div> <span style="white-space:pre">     </span>if (pSelect->pSrc->nSrc != 1) {</div><div>@@ -1807,6 +1800,10 @@ xferOptimization(Parse * pParse,<span style="white-space:pre">  </span>/* Parser context */</div><div> <span style="white-space:pre">        </span>/* Both tables must have the same INTEGER PRIMARY KEY. */</div><div> <span style="white-space:pre">   </span>if (pDest->iPKey != pSrc->iPKey)</div><div> <span style="white-space:pre">              </span>return 0;</div><div>+<span style="white-space:pre">    </span>uint32_t src_space_id = SQLITE_PAGENO_TO_SPACEID(pSrc->tnum);</div><div>+<span style="white-space:pre">     </span>struct space *src_space = space_by_id(src_space_id);</div><div>+<span style="white-space:pre"> </span>uint32_t dest_space_id = SQLITE_PAGENO_TO_SPACEID(pDest->tnum);</div><div>+<span style="white-space:pre">   </span>struct space *dest_space = space_by_id(dest_space_id);</div><div> <span style="white-space:pre">      </span>for (i = 0; i < (int)pDest->def->field_count; i++) {</div><div> <span style="white-space:pre">               </span>enum affinity_type dest_affinity =</div><div> <span style="white-space:pre">                  </span>pDest->def->fields[i].affinity;</div><div>@@ -1826,14 +1823,6 @@ xferOptimization(Parse * pParse,<span style="white-space:pre">  </span>/* Parser context */</div><div> <span style="white-space:pre">                </span>}</div><div> <span style="white-space:pre">           </span>/* Default values for second and subsequent columns need to match. */</div><div> <span style="white-space:pre">               </span>if (i > 0) {</div><div>-<span style="white-space:pre">                      </span>uint32_t src_space_id =</div><div>-<span style="white-space:pre">                              </span>SQLITE_PAGENO_TO_SPACEID(pSrc->tnum);</div><div>-<span style="white-space:pre">                     </span>struct space *src_space =</div><div>-<span style="white-space:pre">                            </span>space_cache_find(src_space_id);</div><div>-<span style="white-space:pre">                      </span>uint32_t dest_space_id =</div><div>-<span style="white-space:pre">                             </span>SQLITE_PAGENO_TO_SPACEID(pDest->tnum);</div><div>-<span style="white-space:pre">                    </span>struct space *dest_space =</div><div>-<span style="white-space:pre">                           </span>space_cache_find(dest_space_id);</div><div> <span style="white-space:pre">                    </span>assert(src_space != NULL && dest_space != NULL);</div><div> <span style="white-space:pre">                    </span>char *src_expr_str =</div><div> <span style="white-space:pre">                                </span>src_space->def->fields[i].default_value;</div><div>@@ -1848,9 +1837,6 @@ xferOptimization(Parse * pParse,<span style="white-space:pre">  </span>/* Parser context */</div><div> <span style="white-space:pre">                </span>}</div><div> <span style="white-space:pre">   </span>}</div><div> <span style="white-space:pre">   </span>for (pDestIdx = pDest->pIndex; pDestIdx; pDestIdx = pDestIdx->pNext) {</div><div>-<span style="white-space:pre">         </span>if (index_is_unique(pDestIdx)) {</div><div>-<span style="white-space:pre">                     </span>destHasUniqueIdx = 1;</div><div>-<span style="white-space:pre">                </span>}</div><div> <span style="white-space:pre">           </span>for (pSrcIdx = pSrc->pIndex; pSrcIdx; pSrcIdx = pSrcIdx->pNext) {</div><div> <span style="white-space:pre">                     </span>if (xferCompatibleIndex(pDestIdx, pSrcIdx))</div><div> <span style="white-space:pre">                         </span>break;</div><div>@@ -1860,10 +1846,8 @@ xferOptimization(Parse * pParse,<span style="white-space:pre"> </span>/* Parser context */</div><div> <span style="white-space:pre">                </span>}</div><div> <span style="white-space:pre">   </span>}</div><div> <span style="white-space:pre">   </span>/* Get server checks. */</div><div>-<span style="white-space:pre">     </span>ExprList *pCheck_src = space_checks_expr_list(</div><div>-<span style="white-space:pre">               </span>SQLITE_PAGENO_TO_SPACEID(pSrc->tnum));</div><div>-<span style="white-space:pre">    </span>ExprList *pCheck_dest = space_checks_expr_list(</div><div>-<span style="white-space:pre">              </span>SQLITE_PAGENO_TO_SPACEID(pDest->tnum));</div><div>+<span style="white-space:pre">   </span>ExprList *pCheck_src = space_checks_expr_list(src_space_id);</div><div>+<span style="white-space:pre"> </span>ExprList *pCheck_dest = space_checks_expr_list(dest_space_id);</div><div> <span style="white-space:pre">      </span>if (pCheck_dest != NULL &&</div><div> <span style="white-space:pre">  </span>    sqlite3ExprListCompare(pCheck_src, pCheck_dest, -1) != 0) {</div><div> <span style="white-space:pre">           </span>/* Tables have different CHECK constraints.  Ticket #2252 */</div><div>@@ -1888,72 +1872,51 @@ xferOptimization(Parse * pParse,<span style="white-space:pre"> </span>/* Parser context */</div><div> <span style="white-space:pre">        </span> * least a possibility, though it might only work if the destination</div><div> <span style="white-space:pre">        </span> * table (tab1) is initially empty.</div><div> <span style="white-space:pre"> </span> */</div><div>-#ifdef SQLITE_TEST</div><div>-<span style="white-space:pre">        </span>sqlite3_xferopt_count++;</div><div>-#endif</div><div>-<span style="white-space:pre">       </span>v = sqlite3GetVdbe(pParse);</div><div>+</div><div>+<span style="white-space:pre">  </span>/* The Vdbe we're building*/</div><div>+<span style="white-space:pre">     </span>Vdbe *v = sqlite3GetVdbe(pParse);</div><div> <span style="white-space:pre">   </span>iSrc = pParse->nTab++;</div><div> <span style="white-space:pre">   </span>iDest = pParse->nTab++;</div><div> <span style="white-space:pre">  </span>regData = sqlite3GetTempReg(pParse);</div><div> <span style="white-space:pre">        </span>regTupleid = sqlite3GetTempReg(pParse);</div><div>-<span style="white-space:pre">      </span>sqlite3OpenTable(pParse, iDest, pDest, OP_OpenWrite);</div><div>-<span style="white-space:pre">        </span>assert(destHasUniqueIdx);</div><div>-<span style="white-space:pre">    </span>if ((pDest->iPKey < 0 && pDest->pIndex != 0)<span style="white-space:pre">        </span>/* (1) */</div><div>-<span style="white-space:pre">    </span>    ||destHasUniqueIdx<span style="white-space:pre">     </span>/* (2) */</div><div>-<span style="white-space:pre">    </span>    || (onError != ON_CONFLICT_ACTION_ABORT</div><div>-<span style="white-space:pre">                </span>&& onError != ON_CONFLICT_ACTION_ROLLBACK)<span style="white-space:pre">   </span>/* (3) */</div><div>-<span style="white-space:pre">    </span>    ) {</div><div>-<span style="white-space:pre">            </span>/* In some circumstances, we are able to run the xfer optimization</div><div>-<span style="white-space:pre">           </span> * only if the destination table is initially empty.</div><div>-<span style="white-space:pre">         </span> * This block generates code to make</div><div>-<span style="white-space:pre">         </span> * that determination.</div><div>-<span style="white-space:pre">               </span> *</div><div>-<span style="white-space:pre">           </span> * Conditions under which the destination must be empty:</div><div>-<span style="white-space:pre">             </span> *</div><div>-<span style="white-space:pre">           </span> * (1) There is no INTEGER PRIMARY KEY but there are indices.</div><div>-<span style="white-space:pre">                </span> *</div><div>-<span style="white-space:pre">           </span> * (2) The destination has a unique index.  (The xfer optimization</div><div>-<span style="white-space:pre">          </span> *     is unable to test uniqueness.)</div><div>-<span style="white-space:pre">             </span> *</div><div>-<span style="white-space:pre">           </span> * (3) onError is something other than ON_CONFLICT_ACTION_ABORT and _ROLLBACK.</div><div>-<span style="white-space:pre">               </span> */</div><div>+</div><div>+<span style="white-space:pre">  </span>vdbe_emit_open_cursor(pParse, iDest, 0, dest_space);</div><div>+<span style="white-space:pre"> </span>VdbeComment((v, "%s", pDest->def->name));</div><div>+</div><div>+<span style="white-space:pre">    </span>/*</div><div>+<span style="white-space:pre">   </span> * Xfer optimization is unable to correctly insert data</div><div>+<span style="white-space:pre">      </span> * in case there's a conflict action other than *_ABORT.</div><div>+<span style="white-space:pre"> </span> * This is the reason we want to only run it if the</div><div>+<span style="white-space:pre">  </span> * destination table is initially empty.</div><div>+<span style="white-space:pre">     </span> * That block generates code to make that determination.</div><div>+<span style="white-space:pre">     </span> */</div><div>+</div><div>+<span style="white-space:pre">  </span>if (!(onError == ON_CONFLICT_ACTION_ABORT &&</div><div>+<span style="white-space:pre"> </span>    is_err_action_default == false)) {</div><div> <span style="white-space:pre">            </span>addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);</div><div> <span style="white-space:pre">          </span>VdbeCoverage(v);</div><div> <span style="white-space:pre">            </span>emptyDestTest = sqlite3VdbeAddOp0(v, OP_Goto);</div><div> <span style="white-space:pre">              </span>sqlite3VdbeJumpHere(v, addr1);</div><div> <span style="white-space:pre">      </span>}</div><div> </div><div>-<span style="white-space:pre">   </span>for (pDestIdx = pDest->pIndex; pDestIdx; pDestIdx = pDestIdx->pNext) {</div><div>-<span style="white-space:pre">         </span>for (pSrcIdx = pSrc->pIndex; ALWAYS(pSrcIdx);</div><div>-<span style="white-space:pre">             </span>     pSrcIdx = pSrcIdx->pNext) {</div><div>-<span style="white-space:pre">                       </span>if (xferCompatibleIndex(pDestIdx, pSrcIdx))</div><div>-<span style="white-space:pre">                          </span>break;</div><div>-<span style="white-space:pre">               </span>}</div><div>-<span style="white-space:pre">            </span>assert(pSrcIdx);</div><div>-<span style="white-space:pre">             </span>struct space *src_space =</div><div>-<span style="white-space:pre">                    </span>space_by_id(SQLITE_PAGENO_TO_SPACEID(pSrcIdx->tnum));</div><div>-<span style="white-space:pre">             </span>vdbe_emit_open_cursor(pParse, iSrc,</div><div>-<span style="white-space:pre">                          </span>      SQLITE_PAGENO_TO_INDEXID(pSrcIdx->tnum),</div><div>-<span style="white-space:pre">                         </span>      src_space);</div><div>-<span style="white-space:pre">         </span>VdbeComment((v, "%s", pSrcIdx->zName));</div><div>-<span style="white-space:pre">         </span>struct space *dest_space =</div><div>-<span style="white-space:pre">                   </span>space_by_id(SQLITE_PAGENO_TO_SPACEID(pDestIdx->tnum));</div><div>-<span style="white-space:pre">            </span>vdbe_emit_open_cursor(pParse, iDest,</div><div>-<span style="white-space:pre">                         </span>      SQLITE_PAGENO_TO_INDEXID(pDestIdx->tnum),</div><div>-<span style="white-space:pre">                                </span>      dest_space);</div><div>-<span style="white-space:pre">                </span>VdbeComment((v, "%s", pDestIdx->zName));</div><div>-<span style="white-space:pre">                </span>addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);</div><div>-<span style="white-space:pre">            </span>VdbeCoverage(v);</div><div>-<span style="white-space:pre">             </span>sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);</div><div>-<span style="white-space:pre">             </span>sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);</div><div>-<span style="white-space:pre">          </span>if (pDestIdx->idxType == SQLITE_IDXTYPE_PRIMARYKEY)</div><div>-<span style="white-space:pre">                       </span>sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);</div><div>-<span style="white-space:pre">              </span>sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1 + 1);</div><div>-<span style="white-space:pre">              </span>VdbeCoverage(v);</div><div>-<span style="white-space:pre">             </span>sqlite3VdbeJumpHere(v, addr1);</div><div>-<span style="white-space:pre">               </span>sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);</div><div>-<span style="white-space:pre">             </span>sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);</div><div>-<span style="white-space:pre">    </span>}</div><div>+<span style="white-space:pre">    </span>vdbe_emit_open_cursor(pParse, iSrc, 0, src_space);</div><div>+<span style="white-space:pre">   </span>VdbeComment((v, "%s", pSrc->def->name));</div><div>+<span style="white-space:pre">     </span>addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);</div><div>+<span style="white-space:pre">    </span>VdbeCoverage(v);</div><div>+<span style="white-space:pre">     </span>sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);</div><div>+</div><div>+#ifdef SQLITE_TEST</div><div>+<span style="white-space:pre">   </span>sqlite3VdbeChangeP5(v, OPFLAG_XFER_OPT);</div><div>+#endif</div><div>+</div><div>+<span style="white-space:pre">       </span>sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);</div><div>+<span style="white-space:pre">  </span>sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE);</div><div>+<span style="white-space:pre">      </span>sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1 + 1);</div><div>+<span style="white-space:pre">      </span>VdbeCoverage(v);</div><div>+<span style="white-space:pre">     </span>sqlite3VdbeJumpHere(v, addr1);</div><div>+<span style="white-space:pre">       </span>sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);</div><div>+<span style="white-space:pre">     </span>sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);</div><div>+</div><div> <span style="white-space:pre">   </span>if (emptySrcTest)</div><div> <span style="white-space:pre">           </span>sqlite3VdbeJumpHere(v, emptySrcTest);</div><div> <span style="white-space:pre">       </span>sqlite3ReleaseTempReg(pParse, regTupleid);</div><div>diff --git a/src/box/sql/sqliteInt.h b/src/box/sql/sqliteInt.h</div><div>index 18bf949..4b84695 100644</div><div>--- a/src/box/sql/sqliteInt.h</div><div>+++ b/src/box/sql/sqliteInt.h</div><div>@@ -3008,6 +3008,10 @@ struct Parse {</div><div> #define OPFLAG_NOOP_IF_NULL  0x02<span style="white-space:pre">   </span>/* OP_FCopy: if source register is NULL</div><div> <span style="white-space:pre">                                     </span> * then do nothing</div><div> <span style="white-space:pre">                                  </span> */</div><div>+/* OP_RowData: xferOptimization started processing */</div><div>+#ifdef SQLITE_TEST</div><div>+#define OPFLAG_XFER_OPT      0x01</div><div>+#endif</div><div> </div><div> /*</div><div>  * Each trigger present in the database schema is stored as an</div><div>diff --git a/src/box/sql/vdbe.c b/src/box/sql/vdbe.c</div><div>index f50e389..5f9bc13 100644</div><div>--- a/src/box/sql/vdbe.c</div><div>+++ b/src/box/sql/vdbe.c</div><div>@@ -77,6 +77,16 @@</div><div> int sql_search_count = 0;</div><div> #endif</div><div> </div><div>+#ifdef SQLITE_TEST</div><div>+/*</div><div>+ * The following global variable is incremented whenever the</div><div>+ * transfer optimization is used.  This is used for testing</div><div>+ * purposes only - to make sure the transfer optimization really</div><div>+ * is happening when it is supposed to.</div><div>+ */</div><div>+int sql_xfer_count = 0;</div><div>+#endif</div><div>+</div><div> /*</div><div>  * When this global variable is positive, it gets decremented once before</div><div>  * each instruction in the VDBE.  When it reaches zero, the u1.isInterrupted</div><div>@@ -3976,7 +3986,7 @@ case OP_SorterData: {</div><div> <span style="white-space:pre">     </span>break;</div><div> }</div><div> </div><div>-/* Opcode: RowData P1 P2 * * *</div><div>+/* Opcode: RowData P1 P2 * * P5</div><div>  * Synopsis: r[P2]=data</div><div>  *</div><div>  * Write into register P2 the complete row content for the row at</div><div>@@ -3984,6 +3994,8 @@ case OP_SorterData: {</div><div>  * There is no interpretation of the data.</div><div>  * It is just copied onto the P2 register exactly as</div><div>  * it is found in the database file.</div><div>+ * P5 can be used in debug mode to check if xferOptimization has</div><div>+ * actually started processing.</div><div>  *</div><div>  * If cursor P1 is an index, then the content is the key of the row.</div><div>  * If cursor P2 is a table, then the content extracted is the data.</div><div>@@ -3996,6 +4008,13 @@ case OP_RowData: {</div><div> <span style="white-space:pre">       </span>BtCursor *pCrsr;</div><div> <span style="white-space:pre">    </span>u32 n;</div><div> </div><div>+#ifdef SQLITE_TEST</div><div>+<span style="white-space:pre">    </span>if (pOp->p5 == 1) {</div><div>+<span style="white-space:pre">               </span>pOp->p5 = 0;</div><div>+<span style="white-space:pre">              </span>sql_xfer_count++;</div><div>+<span style="white-space:pre">    </span>}</div><div>+#endif</div><div>+</div><div> <span style="white-space:pre">     </span>pOut = &aMem[pOp->p2];</div><div> <span style="white-space:pre">       </span>memAboutToChange(p, pOut);</div><div> </div><div>diff --git a/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua</div><div>new file mode 100755</div><div>index 0000000..34f603f</div><div>--- /dev/null</div><div>+++ b/test/sql-tap/gh-3307-xfer-optimization-issue.test.lua</div><div>@@ -0,0 +1,601 @@</div><div>+#!/usr/bin/env tarantool</div><div>+test = require("sqltester")</div><div>+test:plan(44)</div><div>+</div><div>+local bfr, aftr</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">    </span>"xfer-optimization-1.1",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER UNIQUE);</div><div>+<span style="white-space:pre">            </span>INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3);</div><div>+<span style="white-space:pre">                </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER UNIQUE);</div><div>+<span style="white-space:pre">            </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.1></div><div>+<span style="white-space:pre">             </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.1></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.2",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.2></div><div>+<span style="white-space:pre">             </span>1, 1, 2, 2, 3, 3</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.2></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.3",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(id INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">          </span>CREATE TABLE t2(id INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">          </span>CREATE INDEX i1 ON t1(b);</div><div>+<span style="white-space:pre">            </span>CREATE INDEX i2 ON t2(b);</div><div>+<span style="white-space:pre">            </span>INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.3></div><div>+<span style="white-space:pre">             </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.3></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.4",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.4></div><div>+<span style="white-space:pre">             </span>1, 1, 2, 2, 3, 3</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.4></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.5",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t1 VALUES (1, 1, 2), (2, 2, 3), (3, 3, 4);</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.5></div><div>+<span style="white-space:pre">             </span>1, "table T2 has 2 columns but 3 values were supplied"</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.5></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.6",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.6></div><div>+</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.6></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.7",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 3);</div><div>+<span style="white-space:pre">                </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.7></div><div>+<span style="white-space:pre">             </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.7></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.8",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.6></div><div>+<span style="white-space:pre">             </span>1, 1, 2, 2, 3, 3</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.6></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.9",</div><div>+<span style="white-space:pre">   </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 2);</div><div>+<span style="white-space:pre">                </span>CREATE TABLE t2(b INTEGER, a INTEGER PRIMARY KEY);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.9></div><div>+<span style="white-space:pre">             </span>1, "Duplicate key exists in unique index 'sqlite_autoindex_T2_1' in space 'T2'"</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.9></div><div>+<span style="white-space:pre">     </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.10",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.10></div><div>+</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.10></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.11",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (2, 2), (3, 2);</div><div>+<span style="white-space:pre">                </span>CREATE TABLE t2(b INTEGER PRIMARY KEY, a INTEGER);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.11></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.11></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.12",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.12></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 2</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.12></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.13",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (3, 3), (4, 4), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (1, 1), (2, 2);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.13></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.13></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.14",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.14></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 3, 4, 4, 5, 5</div><div>+<span style="white-space:pre">         </span>-- <xfer-optimization-1.14></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.15",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);</div><div>+<span style="white-space:pre">            </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b UNIQUE);</div><div>+<span style="white-space:pre">            </span>INSERT INTO t1 VALUES (2, 2), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (1, 1), (4, 4);</div><div>+<span style="white-space:pre">                </span>INSERT OR ROLLBACK INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre"> </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.15></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.15></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.16",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.16></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 3, 4, 4, 5, 5</div><div>+<span style="white-space:pre">         </span>-- <xfer-optimization-1.16></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+-- The following tests are supposed to test if xfer-optimization is actually</div><div>+-- used in the given cases (if the conflict actually occurs):</div><div>+-- <span style="white-space:pre">       </span>1.0) insert w/o explicit confl. action & w/o index replace action</div><div>+-- <span style="white-space:pre">     </span>1.1) insert w/o explicit confl. action & w/ index replace action &</div><div>+--<span style="white-space:pre">         </span>empty dest_table</div><div>+-- <span style="white-space:pre">  </span>1.2) insert w/o explicit confl. action & w/ index replace action &</div><div>+--<span style="white-space:pre">         </span>non-empty dest_table</div><div>+-- <span style="white-space:pre">      </span>2) insert with abort</div><div>+-- <span style="white-space:pre">      </span>3.0) insert with rollback (into empty table)</div><div>+-- <span style="white-space:pre">      </span>3.1) insert with rollback (into non-empty table)</div><div>+-- <span style="white-space:pre">  </span>4) insert with replace</div><div>+-- <span style="white-space:pre">    </span>5) insert with fail</div><div>+-- <span style="white-space:pre">       </span>6) insert with ignore</div><div>+</div><div>+local function do_xfer_test(test_number, return_code)</div><div>+<span style="white-space:pre">   </span>test_name = string.format("xfer-optimization-1.%d", test_number)</div><div>+<span style="white-space:pre">   </span>test:do_test(</div><div>+<span style="white-space:pre">                </span>test_name,</div><div>+<span style="white-space:pre">           </span>function()</div><div>+<span style="white-space:pre">                   </span>if (aftr - bfr == 1) then</div><div>+<span style="white-space:pre">                            </span>return {1}</div><div>+<span style="white-space:pre">                   </span>end</div><div>+<span style="white-space:pre">                  </span>if (aftr == bfr) then</div><div>+<span style="white-space:pre">                                </span>return {0}</div><div>+<span style="white-space:pre">                   </span>end</div><div>+<span style="white-space:pre">          </span>end, {</div><div>+<span style="white-space:pre">                       </span>-- <test_name></div><div>+<span style="white-space:pre">                 </span>return_code</div><div>+<span style="white-space:pre">                  </span>-- <test_name></div><div>+<span style="white-space:pre">         </span>})</div><div>+end</div><div>+</div><div>+-- 1.0) insert w/o explicit confl. action & w/o index replace action</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">        </span>"xfer-optimization-1.17",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.17></div><div>+<span style="white-space:pre">            </span>1, "Duplicate key exists in unique index 'sqlite_autoindex_T2_1' in space 'T2'"</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.17></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.18",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.18></div><div>+<span style="white-space:pre">            </span>2, 2, 3, 4, 4, 4, 10, 10</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.18></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(19, 0)</div><div>+</div><div>+-- 1.1) insert w/o explicit confl. action & w/</div><div>+--      index replace action & empty dest_table</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">   </span>"xfer-optimization-1.20",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t2(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t3(id INT PRIMARY KEY);</div><div>+<span style="white-space:pre">         </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t3 VALUES (1);</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.20></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.20></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.21",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.21></div><div>+<span style="white-space:pre">            </span>1, 1, 3, 3, 5, 5, 10, 10</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.21></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre"> </span>"xfer-optimization-1.22",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t3;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.22></div><div>+<span style="white-space:pre">            </span>1</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.22></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+do_xfer_test(23, 1)</div><div>+</div><div>+-- 1.2) insert w/o explicit confl. action & w/</div><div>+-- index replace action & non-empty dest_table</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">    </span>"xfer-optimization-1.24",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t3;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t2(a INTEGER PRIMARY KEY ON CONFLICT REPLACE, b);</div><div>+<span style="white-space:pre">               </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.24></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.24></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.25",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.25></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 10, 10</div><div>+<span style="white-space:pre">         </span>-- <xfer-optimization-1.25></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(26, 0)</div><div>+</div><div>+-- 2) insert with abort</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">    </span>"xfer-optimization-1.27",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT OR ABORT INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.27></div><div>+<span style="white-space:pre">            </span>1, "Duplicate key exists in unique index 'sqlite_autoindex_T2_1' in space 'T2'"</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.27></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.28",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.28></div><div>+<span style="white-space:pre">            </span>2, 2, 3, 4, 4, 4, 10, 10</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.28></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(29, 1)</div><div>+</div><div>+-- 3.0) insert with rollback (into empty table)</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">    </span>"xfer-optimization-1.30",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT OR ROLLBACK INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre"> </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.30></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.30></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.31",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.31></div><div>+<span style="white-space:pre">            </span>1, 1, 3, 3, 5, 5, 10, 10</div><div>+<span style="white-space:pre">             </span>-- <xfer-optimization-1.31></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(32, 1)</div><div>+</div><div>+-- 3.1) insert with rollback (into non-empty table)</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">        </span>"xfer-optimization-1.33",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT OR ROLLBACK INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre"> </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.33></div><div>+<span style="white-space:pre">            </span>1, "UNIQUE constraint failed: T2.A"</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.33></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.34",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.34></div><div>+<span style="white-space:pre">            </span>2, 2, 3, 4</div><div>+<span style="white-space:pre">           </span>-- <xfer-optimization-1.34></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(35, 0)</div><div>+</div><div>+-- 4) insert with replace</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">  </span>"xfer-optimization-1.36",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT OR REPLACE INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">  </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.36></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.36></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.37",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.37></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 10, 10</div><div>+<span style="white-space:pre">         </span>-- <xfer-optimization-1.37></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(38, 0)</div><div>+</div><div>+-- 5) insert with fail</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">     </span>"xfer-optimization-1.39",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT OR FAIL INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">     </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.39></div><div>+<span style="white-space:pre">            </span>1, "Duplicate key exists in unique index 'sqlite_autoindex_T2_1' in space 'T2'"</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.39></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.40",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.40></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 4, 4, 4, 10, 10</div><div>+<span style="white-space:pre">               </span>-- <xfer-optimization-1.40></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(41, 0)</div><div>+</div><div>+-- 6) insert with ignore</div><div>+------------------------------------------------------------------------------</div><div>+</div><div>+bfr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+test:do_catchsql_test(</div><div>+<span style="white-space:pre">   </span>"xfer-optimization-1.42",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">           </span>DROP TABLE t1;</div><div>+<span style="white-space:pre">               </span>DROP TABLE t2;</div><div>+<span style="white-space:pre">               </span>CREATE TABLE t1(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>CREATE TABLE t2(a INTEGER PRIMARY KEY, b);</div><div>+<span style="white-space:pre">           </span>INSERT INTO t1 VALUES (1, 1), (3, 3), (5, 5);</div><div>+<span style="white-space:pre">                </span>INSERT INTO t2 VALUES (2, 2), (3, 4);</div><div>+<span style="white-space:pre">                </span>BEGIN;</div><div>+<span style="white-space:pre">                       </span>INSERT INTO t2 VALUES (4, 4);</div><div>+<span style="white-space:pre">                        </span>INSERT OR IGNORE INTO t2 SELECT * FROM t1;</div><div>+<span style="white-space:pre">   </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.42></div><div>+<span style="white-space:pre">            </span>0</div><div>+<span style="white-space:pre">            </span>-- <xfer-optimization-1.42></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+test:do_execsql_test(</div><div>+<span style="white-space:pre">      </span>"xfer-optimization-1.43",</div><div>+<span style="white-space:pre">  </span>[[</div><div>+<span style="white-space:pre">                   </span>INSERT INTO t2 VALUES (10, 10);</div><div>+<span style="white-space:pre">              </span>COMMIT;</div><div>+<span style="white-space:pre">              </span>SELECT * FROM t2;</div><div>+<span style="white-space:pre">    </span>]], {</div><div>+<span style="white-space:pre">                </span>-- <xfer-optimization-1.43></div><div>+<span style="white-space:pre">            </span>1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 10, 10</div><div>+<span style="white-space:pre">         </span>-- <xfer-optimization-1.43></div><div>+<span style="white-space:pre">    </span>})</div><div>+</div><div>+aftr = box.sql.debug().sql_xfer_count</div><div>+</div><div>+do_xfer_test(44, 0)</div><div>+</div><div>+test:finish_test()</div><div> </div></div></div></div>