mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-05 04:30:38 +03:00
Sorting is now done using a sorting index rather than loading the entire
result set into memory and doing a merge sort. The old merge sort technique was a carry-over from SQLite version 1. The new method uses a bounded amount of memory and scales to much larger result sets. There are still errors: some 39 regression tests fail. (CVS 2653) FossilOrigin-Name: 09db0a24241f9248584250d1728117b8a3159626
This commit is contained in:
24
manifest
24
manifest
@@ -1,5 +1,5 @@
|
|||||||
C {quote:\sKeyInfo}\sgeneration\smoved\sto\sa\scommon\ssubroutine.\s(CVS\s2652)
|
C Sorting\sis\snow\sdone\susing\sa\ssorting\sindex\srather\sthan\sloading\sthe\sentire\nresult\sset\sinto\smemory\sand\sdoing\sa\smerge\ssort.\s\sThe\sold\smerge\ssort\stechnique\nwas\sa\scarry-over\sfrom\sSQLite\sversion\s1.\s\sThe\snew\smethod\suses\sa\sbounded\samount\nof\smemory\sand\sscales\sto\smuch\slarger\sresult\ssets.\s\sThere\sare\sstill\serrors:\nsome\s39\sregression\stests\sfail.\s(CVS\s2653)
|
||||||
D 2005-08-31T18:20:00
|
D 2005-09-01T03:07:44
|
||||||
F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1
|
F Makefile.in 12784cdce5ffc8dfb707300c34e4f1eb3b8a14f1
|
||||||
F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
|
F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7
|
||||||
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
|
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
|
||||||
@@ -40,7 +40,7 @@ F src/complete.c 4de937dfdd4c79a501772ab2035b26082f337a79
|
|||||||
F src/date.c 7444b0900a28da77e57e3337a636873cff0ae940
|
F src/date.c 7444b0900a28da77e57e3337a636873cff0ae940
|
||||||
F src/delete.c be1fc25c9e109cd8cbab42a43ee696263da7c04b
|
F src/delete.c be1fc25c9e109cd8cbab42a43ee696263da7c04b
|
||||||
F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
|
F src/experimental.c 50c1e3b34f752f4ac10c36f287db095c2b61766d
|
||||||
F src/expr.c aef4a3901a5bea6625b9613be9d9ddaccd575bc4
|
F src/expr.c e9d0401bed1fa61cce356c2da4b53dae769c4fc3
|
||||||
F src/func.c 9da04a6241309a612cf610715944c6a2aaf0f297
|
F src/func.c 9da04a6241309a612cf610715944c6a2aaf0f297
|
||||||
F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
|
F src/hash.c 2b1b13f7400e179631c83a1be0c664608c8f021f
|
||||||
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
|
F src/hash.h 1b0c445e1c89ff2aaad9b4605ba61375af001e84
|
||||||
@@ -63,10 +63,10 @@ F src/pragma.c 69413fbdc0c6aaa493a776ea52c1b3e6cf35dfb2
|
|||||||
F src/prepare.c 86f0d8e744b8d956eff6bc40e29049efee017610
|
F src/prepare.c 86f0d8e744b8d956eff6bc40e29049efee017610
|
||||||
F src/printf.c d2678b06cfa07be9b14c330a42310f62340e34ce
|
F src/printf.c d2678b06cfa07be9b14c330a42310f62340e34ce
|
||||||
F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4
|
F src/random.c 90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4
|
||||||
F src/select.c cf566f995358f728288f0be481f12d20305117c0
|
F src/select.c a0b10feee29d4e86731c6e381e0ff0ecf9d5eac8
|
||||||
F src/shell.c b21daba017b8feef2fdc65ecde57f70209494217
|
F src/shell.c b21daba017b8feef2fdc65ecde57f70209494217
|
||||||
F src/sqlite.h.in d6561d51025d08de4f455607f3f9f9aa76e855d5
|
F src/sqlite.h.in d6561d51025d08de4f455607f3f9f9aa76e855d5
|
||||||
F src/sqliteInt.h 207b63f9782d7faf1f19e694e8052e60841fb377
|
F src/sqliteInt.h 845ff6f8019f80baafb1bdbb8ef80fcd04d9d0f9
|
||||||
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9
|
||||||
F src/tclsqlite.c ac94682f9e601dd373912c46414a5a842db2089a
|
F src/tclsqlite.c ac94682f9e601dd373912c46414a5a842db2089a
|
||||||
F src/test1.c 97425910c6adf87b5dc867ae7704f0430441663c
|
F src/test1.c 97425910c6adf87b5dc867ae7704f0430441663c
|
||||||
@@ -80,11 +80,11 @@ F src/update.c a9d2c5f504212d62da1b094476f1389c0e02f83f
|
|||||||
F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
|
F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
|
||||||
F src/util.c 5650f6fe5ee30e0678985ad7b94da91e3f85752b
|
F src/util.c 5650f6fe5ee30e0678985ad7b94da91e3f85752b
|
||||||
F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c
|
F src/vacuum.c 829d9e1a6d7c094b80e0899686670932eafd768c
|
||||||
F src/vdbe.c 69f33e22c7d0a64b23fbb69e6da95a1bb6869032
|
F src/vdbe.c 70e2dd078b0dfa15b70c0b9d31e7127da7408f15
|
||||||
F src/vdbe.h 3b29a9af6c7a64ed692bef1fc5f61338f40d2f67
|
F src/vdbe.h 3b29a9af6c7a64ed692bef1fc5f61338f40d2f67
|
||||||
F src/vdbeInt.h e5f2855b0f0b120d870e0459816061b88b603774
|
F src/vdbeInt.h c9dcaec8b411384da8b01c362cc856b6560b04f1
|
||||||
F src/vdbeapi.c f0d36ff0f06bb5315efac5645b62e99db2c175b8
|
F src/vdbeapi.c f0d36ff0f06bb5315efac5645b62e99db2c175b8
|
||||||
F src/vdbeaux.c 192e0dbeaaa0bfa652b0c2579c19894e5e5626fc
|
F src/vdbeaux.c 68d5d0881ded9867db1521fa2c0ae5ac8007a9d5
|
||||||
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
|
F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
|
||||||
F src/vdbemem.c 4732fd4d1a75dc38549493d7f9a81d02bf7c59b5
|
F src/vdbemem.c 4732fd4d1a75dc38549493d7f9a81d02bf7c59b5
|
||||||
F src/where.c bbb973cbbd862b6b872faac39716a3fe13adfb44
|
F src/where.c bbb973cbbd862b6b872faac39716a3fe13adfb44
|
||||||
@@ -129,7 +129,7 @@ F test/collate3.test 51362bdfb43a72bd2b087d90b2623b0695538e7a
|
|||||||
F test/collate4.test daf498e294dcd596b961d425c3f2dda117e4717e
|
F test/collate4.test daf498e294dcd596b961d425c3f2dda117e4717e
|
||||||
F test/collate5.test 5a49cd169e7565e4f92b42695667d6d5db25670d
|
F test/collate5.test 5a49cd169e7565e4f92b42695667d6d5db25670d
|
||||||
F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638
|
F test/collate6.test 6c9470d1606ee3e564675b229653e320c49ec638
|
||||||
F test/conflict.test 3e7beba8c253095330c6853c00aaf6356e84cc68
|
F test/conflict.test 774e10709f5e9a9a7352dc929b856929ec805e58
|
||||||
F test/corrupt.test 18c7a995b1af76a8c8600b996257f2c7b7bff083
|
F test/corrupt.test 18c7a995b1af76a8c8600b996257f2c7b7bff083
|
||||||
F test/corrupt2.test 88342570828f2b8cbbd8369eff3891f5c0bdd5ba
|
F test/corrupt2.test 88342570828f2b8cbbd8369eff3891f5c0bdd5ba
|
||||||
F test/crash.test f38b980a0508655d08c957a6dd27d66bca776504
|
F test/crash.test f38b980a0508655d08c957a6dd27d66bca776504
|
||||||
@@ -306,7 +306,7 @@ F www/tclsqlite.tcl 3df553505b6efcad08f91e9b975deb2e6c9bb955
|
|||||||
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
|
||||||
F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
|
F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
|
||||||
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
|
F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
|
||||||
P b1dceef0508ffe20ab2ff8fa5e5b5a44f4f224aa
|
P a25801df06e218e70570a6b9eae71603d590fe3a
|
||||||
R 22b6fa7b5f0b031707b250894ea2c1df
|
R 7dae09495a78580995d7ecb2f7ce6601
|
||||||
U drh
|
U drh
|
||||||
Z 24532867563e79fa05da569ea322e240
|
Z 29fbd68d397ac0605a1223b60872d370
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
a25801df06e218e70570a6b9eae71603d590fe3a
|
09db0a24241f9248584250d1728117b8a3159626
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
** This file contains routines used for analyzing expressions and
|
** This file contains routines used for analyzing expressions and
|
||||||
** for generating VDBE code that evaluates expressions in SQLite.
|
** for generating VDBE code that evaluates expressions in SQLite.
|
||||||
**
|
**
|
||||||
** $Id: expr.c,v 1.222 2005/08/30 00:54:02 drh Exp $
|
** $Id: expr.c,v 1.223 2005/09/01 03:07:44 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@@ -546,9 +546,12 @@ Select *sqlite3SelectDup(Select *p){
|
|||||||
pNew->pOffset = sqlite3ExprDup(p->pOffset);
|
pNew->pOffset = sqlite3ExprDup(p->pOffset);
|
||||||
pNew->iLimit = -1;
|
pNew->iLimit = -1;
|
||||||
pNew->iOffset = -1;
|
pNew->iOffset = -1;
|
||||||
pNew->ppOpenVirtual = 0;
|
|
||||||
pNew->isResolved = p->isResolved;
|
pNew->isResolved = p->isResolved;
|
||||||
pNew->isAgg = p->isAgg;
|
pNew->isAgg = p->isAgg;
|
||||||
|
pNew->pRightmost = 0;
|
||||||
|
pNew->addrOpenVirt[0] = -1;
|
||||||
|
pNew->addrOpenVirt[1] = -1;
|
||||||
|
pNew->addrOpenVirt[2] = -1;
|
||||||
return pNew;
|
return pNew;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|||||||
257
src/select.c
257
src/select.c
@@ -12,7 +12,7 @@
|
|||||||
** This file contains C code routines that are called by the parser
|
** This file contains C code routines that are called by the parser
|
||||||
** to handle SELECT statements in SQLite.
|
** to handle SELECT statements in SQLite.
|
||||||
**
|
**
|
||||||
** $Id: select.c,v 1.257 2005/08/31 18:20:00 drh Exp $
|
** $Id: select.c,v 1.258 2005/09/01 03:07:44 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
|
|
||||||
@@ -60,6 +60,9 @@ Select *sqlite3SelectNew(
|
|||||||
pNew->pOffset = pOffset;
|
pNew->pOffset = pOffset;
|
||||||
pNew->iLimit = -1;
|
pNew->iLimit = -1;
|
||||||
pNew->iOffset = -1;
|
pNew->iOffset = -1;
|
||||||
|
pNew->addrOpenVirt[0] = -1;
|
||||||
|
pNew->addrOpenVirt[1] = -1;
|
||||||
|
pNew->addrOpenVirt[2] = -1;
|
||||||
}
|
}
|
||||||
return pNew;
|
return pNew;
|
||||||
}
|
}
|
||||||
@@ -327,8 +330,9 @@ void sqlite3SelectDelete(Select *p){
|
|||||||
*/
|
*/
|
||||||
static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){
|
static void pushOntoSorter(Parse *pParse, Vdbe *v, ExprList *pOrderBy){
|
||||||
sqlite3ExprCodeExprList(pParse, pOrderBy);
|
sqlite3ExprCodeExprList(pParse, pOrderBy);
|
||||||
sqlite3VdbeAddOp(v, OP_MakeRecord, pOrderBy->nExpr, 0);
|
sqlite3VdbeAddOp(v, OP_Pull, pOrderBy->nExpr, 0);
|
||||||
sqlite3VdbeAddOp(v, OP_SortInsert, 0, 0);
|
sqlite3VdbeAddOp(v, OP_MakeRecord, pOrderBy->nExpr + 1, 0);
|
||||||
|
sqlite3VdbeAddOp(v, OP_IdxInsert, pOrderBy->iTab, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -555,6 +559,12 @@ static int selectInnerLoop(
|
|||||||
** Given an expression list, generate a KeyInfo structure that records
|
** Given an expression list, generate a KeyInfo structure that records
|
||||||
** the collating sequence for each expression in that expression list.
|
** the collating sequence for each expression in that expression list.
|
||||||
**
|
**
|
||||||
|
** If the ExprList is an ORDER BY or GROUP BY clause then the resulting
|
||||||
|
** KeyInfo structure is appropriate for initializing a virtual index to
|
||||||
|
** implement that clause. If the ExprList is the result set of a SELECT
|
||||||
|
** then the KeyInfo structure is appropriate for initializing a virtual
|
||||||
|
** index to implement a DISTINCT test.
|
||||||
|
**
|
||||||
** Space to hold the KeyInfo structure is obtain from malloc. The calling
|
** Space to hold the KeyInfo structure is obtain from malloc. The calling
|
||||||
** function is responsible for seeing that this structure is eventually
|
** function is responsible for seeing that this structure is eventually
|
||||||
** freed. Add the KeyInfo structure to the P3 field of an opcode using
|
** freed. Add the KeyInfo structure to the P3 field of an opcode using
|
||||||
@@ -601,17 +611,17 @@ static void generateSortTail(
|
|||||||
int eDest, /* Write the sorted results here */
|
int eDest, /* Write the sorted results here */
|
||||||
int iParm /* Optional parameter associated with eDest */
|
int iParm /* Optional parameter associated with eDest */
|
||||||
){
|
){
|
||||||
int end1 = sqlite3VdbeMakeLabel(v);
|
int brk = sqlite3VdbeMakeLabel(v);
|
||||||
int end2 = sqlite3VdbeMakeLabel(v);
|
int cont = sqlite3VdbeMakeLabel(v);
|
||||||
int addr;
|
int addr;
|
||||||
KeyInfo *pInfo;
|
int iTab;
|
||||||
|
ExprList *pOrderBy = p->pOrderBy;
|
||||||
|
|
||||||
if( eDest==SRT_Sorter ) return;
|
if( eDest==SRT_Sorter ) return;
|
||||||
pInfo = keyInfoFromExprList(pParse, p->pOrderBy);
|
iTab = pOrderBy->iTab;
|
||||||
if( pInfo==0 ) return;
|
addr = 1 + sqlite3VdbeAddOp(v, OP_Sort, iTab, brk);
|
||||||
sqlite3VdbeOp3(v, OP_Sort, 0, 0, (char*)pInfo, P3_KEYINFO_HANDOFF);
|
codeLimiter(v, p, cont, brk, 0);
|
||||||
addr = sqlite3VdbeAddOp(v, OP_SortNext, 0, end1);
|
sqlite3VdbeAddOp(v, OP_Column, iTab, pOrderBy->nExpr);
|
||||||
codeLimiter(v, p, addr, end2, 1);
|
|
||||||
switch( eDest ){
|
switch( eDest ){
|
||||||
case SRT_Table:
|
case SRT_Table:
|
||||||
case SRT_TempTable: {
|
case SRT_TempTable: {
|
||||||
@@ -634,7 +644,7 @@ static void generateSortTail(
|
|||||||
case SRT_Mem: {
|
case SRT_Mem: {
|
||||||
assert( nColumn==1 );
|
assert( nColumn==1 );
|
||||||
sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
|
sqlite3VdbeAddOp(v, OP_MemStore, iParm, 1);
|
||||||
sqlite3VdbeAddOp(v, OP_Goto, 0, end1);
|
sqlite3VdbeAddOp(v, OP_Goto, 0, brk);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -659,11 +669,9 @@ static void generateSortTail(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sqlite3VdbeAddOp(v, OP_Goto, 0, addr);
|
sqlite3VdbeResolveLabel(v, cont);
|
||||||
sqlite3VdbeResolveLabel(v, end2);
|
sqlite3VdbeAddOp(v, OP_Next, iTab, addr);
|
||||||
sqlite3VdbeAddOp(v, OP_Pop, 1, 0);
|
sqlite3VdbeResolveLabel(v, brk);
|
||||||
sqlite3VdbeResolveLabel(v, end1);
|
|
||||||
sqlite3VdbeAddOp(v, OP_SortReset, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1327,48 +1335,20 @@ static void computeLimitRegisters(Parse *pParse, Select *p){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Generate VDBE instructions that will open a transient table that
|
** Allocate a virtual index to use for sorting.
|
||||||
** will be used for an index or to store keyed results for a compound
|
|
||||||
** select. In other words, open a transient table that needs a
|
|
||||||
** KeyInfo structure. The number of columns in the KeyInfo is determined
|
|
||||||
** by the result set of the SELECT statement in the second argument.
|
|
||||||
**
|
|
||||||
** Specifically, this routine is called to open an index table for
|
|
||||||
** DISTINCT, UNION, INTERSECT and EXCEPT select statements (but not
|
|
||||||
** UNION ALL).
|
|
||||||
**
|
|
||||||
** The value returned is the address of the OP_OpenVirtual instruction.
|
|
||||||
*/
|
*/
|
||||||
static int openVirtualIndex(Parse *pParse, Select *p, int iTab){
|
static createSortingIndex(Parse *pParse, Select *p, ExprList *pOrderBy){
|
||||||
KeyInfo *pKeyInfo;
|
if( pOrderBy ){
|
||||||
Vdbe *v = pParse->pVdbe;
|
int addr;
|
||||||
int addr;
|
assert( pOrderBy->iTab==0 );
|
||||||
|
pOrderBy->iTab = pParse->nTab++;
|
||||||
if( prepSelectStmt(pParse, p) ){
|
addr = sqlite3VdbeAddOp(pParse->pVdbe, OP_OpenVirtual,
|
||||||
return 0;
|
pOrderBy->iTab, pOrderBy->nExpr+1);
|
||||||
|
assert( p->addrOpenVirt[2] == -1 );
|
||||||
|
p->addrOpenVirt[2] = addr;
|
||||||
}
|
}
|
||||||
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
|
|
||||||
if( pKeyInfo==0 ) return 0;
|
|
||||||
addr = sqlite3VdbeOp3(v, OP_OpenVirtual, iTab, 0,
|
|
||||||
(char*)pKeyInfo, P3_KEYINFO_HANDOFF);
|
|
||||||
return addr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
|
||||||
/*
|
|
||||||
** Add the address "addr" to the set of all OpenVirtual opcode addresses
|
|
||||||
** that are being accumulated in p->ppOpenVirtual.
|
|
||||||
*/
|
|
||||||
static int multiSelectOpenVirtualAddr(Select *p, int addr){
|
|
||||||
IdList *pList = *p->ppOpenVirtual = sqlite3IdListAppend(*p->ppOpenVirtual, 0);
|
|
||||||
if( pList==0 ){
|
|
||||||
return SQLITE_NOMEM;
|
|
||||||
}
|
|
||||||
pList->a[pList->nId-1].idx = addr;
|
|
||||||
return SQLITE_OK;
|
|
||||||
}
|
|
||||||
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||||
/*
|
/*
|
||||||
** Return the appropriate collating sequence for the iCol-th column of
|
** Return the appropriate collating sequence for the iCol-th column of
|
||||||
@@ -1433,10 +1413,10 @@ static int multiSelect(
|
|||||||
int rc = SQLITE_OK; /* Success code from a subroutine */
|
int rc = SQLITE_OK; /* Success code from a subroutine */
|
||||||
Select *pPrior; /* Another SELECT immediately to our left */
|
Select *pPrior; /* Another SELECT immediately to our left */
|
||||||
Vdbe *v; /* Generate code to this VDBE */
|
Vdbe *v; /* Generate code to this VDBE */
|
||||||
IdList *pOpenVirtual = 0;/* OP_OpenVirtual opcodes that need a KeyInfo */
|
|
||||||
int aAddr[5]; /* Addresses of SetNumColumns operators */
|
|
||||||
int nAddr = 0; /* Number used */
|
|
||||||
int nCol; /* Number of columns in the result set */
|
int nCol; /* Number of columns in the result set */
|
||||||
|
ExprList *pOrderBy; /* The ORDER BY clause on p */
|
||||||
|
int aSetP2[2]; /* Set P2 value of these op to number of columns */
|
||||||
|
int nSetP2 = 0; /* Number of slots in aSetP2[] used */
|
||||||
|
|
||||||
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
|
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
|
||||||
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
|
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
|
||||||
@@ -1446,6 +1426,8 @@ static int multiSelect(
|
|||||||
goto multi_select_end;
|
goto multi_select_end;
|
||||||
}
|
}
|
||||||
pPrior = p->pPrior;
|
pPrior = p->pPrior;
|
||||||
|
assert( pPrior->pRightmost!=pPrior );
|
||||||
|
assert( pPrior->pRightmost==p->pRightmost );
|
||||||
if( pPrior->pOrderBy ){
|
if( pPrior->pOrderBy ){
|
||||||
sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
|
sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
|
||||||
selectOpName(p->op));
|
selectOpName(p->op));
|
||||||
@@ -1467,32 +1449,21 @@ static int multiSelect(
|
|||||||
goto multi_select_end;
|
goto multi_select_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If *p this is the right-most select statement, then initialize
|
|
||||||
** p->ppOpenVirtual to point to pOpenVirtual. If *p is not the right most
|
|
||||||
** statement then p->ppOpenVirtual will have already been initialized
|
|
||||||
** by a prior call to this same procedure. Pass along the pOpenVirtual
|
|
||||||
** pointer to pPrior, the next statement to our left.
|
|
||||||
*/
|
|
||||||
if( p->ppOpenVirtual==0 ){
|
|
||||||
p->ppOpenVirtual = &pOpenVirtual;
|
|
||||||
}
|
|
||||||
pPrior->ppOpenVirtual = p->ppOpenVirtual;
|
|
||||||
|
|
||||||
/* Create the destination temporary table if necessary
|
/* Create the destination temporary table if necessary
|
||||||
*/
|
*/
|
||||||
if( eDest==SRT_TempTable ){
|
if( eDest==SRT_TempTable ){
|
||||||
assert( p->pEList );
|
assert( p->pEList );
|
||||||
sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, 0);
|
assert( nSetP2<sizeof(aSetP2)/sizeof(aSetP2[0]) );
|
||||||
assert( nAddr==0 );
|
aSetP2[nSetP2++] = sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, 0);
|
||||||
aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, 0);
|
|
||||||
eDest = SRT_Table;
|
eDest = SRT_Table;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Generate code for the left and right SELECT statements.
|
/* Generate code for the left and right SELECT statements.
|
||||||
*/
|
*/
|
||||||
|
pOrderBy = p->pOrderBy;
|
||||||
switch( p->op ){
|
switch( p->op ){
|
||||||
case TK_ALL: {
|
case TK_ALL: {
|
||||||
if( p->pOrderBy==0 ){
|
if( pOrderBy==0 ){
|
||||||
assert( !pPrior->pLimit );
|
assert( !pPrior->pLimit );
|
||||||
pPrior->pLimit = p->pLimit;
|
pPrior->pLimit = p->pLimit;
|
||||||
pPrior->pOffset = p->pOffset;
|
pPrior->pOffset = p->pOffset;
|
||||||
@@ -1520,11 +1491,10 @@ static int multiSelect(
|
|||||||
int op = 0; /* One of the SRT_ operations to apply to self */
|
int op = 0; /* One of the SRT_ operations to apply to self */
|
||||||
int priorOp; /* The SRT_ operation to apply to prior selects */
|
int priorOp; /* The SRT_ operation to apply to prior selects */
|
||||||
Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
|
Expr *pLimit, *pOffset; /* Saved values of p->nLimit and p->nOffset */
|
||||||
ExprList *pOrderBy; /* The ORDER BY clause for the right SELECT */
|
|
||||||
int addr;
|
int addr;
|
||||||
|
|
||||||
priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
|
priorOp = p->op==TK_ALL ? SRT_Table : SRT_Union;
|
||||||
if( eDest==priorOp && p->pOrderBy==0 && !p->pLimit && !p->pOffset ){
|
if( eDest==priorOp && pOrderBy==0 && !p->pLimit && !p->pOffset ){
|
||||||
/* We can reuse a temporary table generated by a SELECT to our
|
/* We can reuse a temporary table generated by a SELECT to our
|
||||||
** right.
|
** right.
|
||||||
*/
|
*/
|
||||||
@@ -1534,20 +1504,20 @@ static int multiSelect(
|
|||||||
** intermediate results.
|
** intermediate results.
|
||||||
*/
|
*/
|
||||||
unionTab = pParse->nTab++;
|
unionTab = pParse->nTab++;
|
||||||
if( p->pOrderBy
|
if( pOrderBy && matchOrderbyToColumn(pParse, p, pOrderBy, unionTab,1) ){
|
||||||
&& matchOrderbyToColumn(pParse, p, p->pOrderBy, unionTab, 1) ){
|
|
||||||
rc = 1;
|
rc = 1;
|
||||||
goto multi_select_end;
|
goto multi_select_end;
|
||||||
}
|
}
|
||||||
addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, unionTab, 0);
|
addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, unionTab, 0);
|
||||||
if( p->op!=TK_ALL ){
|
if( priorOp==SRT_Table ){
|
||||||
rc = multiSelectOpenVirtualAddr(p, addr);
|
assert( nSetP2<sizeof(aSetP2)/sizeof(aSetP2[0]) );
|
||||||
if( rc!=SQLITE_OK ){
|
aSetP2[nSetP2++] = addr;
|
||||||
goto multi_select_end;
|
}else{
|
||||||
}
|
assert( p->addrOpenVirt[0] == -1 );
|
||||||
|
p->addrOpenVirt[0] = addr;
|
||||||
|
p->pRightmost->usesVirt = 1;
|
||||||
}
|
}
|
||||||
assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
|
createSortingIndex(pParse, p, pOrderBy);
|
||||||
aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, unionTab, 0);
|
|
||||||
assert( p->pEList );
|
assert( p->pEList );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1567,7 +1537,6 @@ static int multiSelect(
|
|||||||
case TK_ALL: op = SRT_Table; break;
|
case TK_ALL: op = SRT_Table; break;
|
||||||
}
|
}
|
||||||
p->pPrior = 0;
|
p->pPrior = 0;
|
||||||
pOrderBy = p->pOrderBy;
|
|
||||||
p->pOrderBy = 0;
|
p->pOrderBy = 0;
|
||||||
pLimit = p->pLimit;
|
pLimit = p->pLimit;
|
||||||
p->pLimit = 0;
|
p->pLimit = 0;
|
||||||
@@ -1601,7 +1570,7 @@ static int multiSelect(
|
|||||||
computeLimitRegisters(pParse, p);
|
computeLimitRegisters(pParse, p);
|
||||||
iStart = sqlite3VdbeCurrentAddr(v);
|
iStart = sqlite3VdbeCurrentAddr(v);
|
||||||
rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
|
rc = selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
|
||||||
p->pOrderBy, -1, eDest, iParm,
|
pOrderBy, -1, eDest, iParm,
|
||||||
iCont, iBreak, 0);
|
iCont, iBreak, 0);
|
||||||
if( rc ){
|
if( rc ){
|
||||||
rc = 1;
|
rc = 1;
|
||||||
@@ -1626,18 +1595,16 @@ static int multiSelect(
|
|||||||
*/
|
*/
|
||||||
tab1 = pParse->nTab++;
|
tab1 = pParse->nTab++;
|
||||||
tab2 = pParse->nTab++;
|
tab2 = pParse->nTab++;
|
||||||
if( p->pOrderBy && matchOrderbyToColumn(pParse,p,p->pOrderBy,tab1,1) ){
|
if( pOrderBy && matchOrderbyToColumn(pParse,p,pOrderBy,tab1,1) ){
|
||||||
rc = 1;
|
rc = 1;
|
||||||
goto multi_select_end;
|
goto multi_select_end;
|
||||||
}
|
}
|
||||||
|
createSortingIndex(pParse, p, pOrderBy);
|
||||||
|
|
||||||
addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, tab1, 0);
|
addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, tab1, 0);
|
||||||
rc = multiSelectOpenVirtualAddr(p, addr);
|
assert( p->addrOpenVirt[0] == -1 );
|
||||||
if( rc!=SQLITE_OK ){
|
p->addrOpenVirt[0] = addr;
|
||||||
goto multi_select_end;
|
p->pRightmost->usesVirt = 1;
|
||||||
}
|
|
||||||
assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
|
|
||||||
aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab1, 0);
|
|
||||||
assert( p->pEList );
|
assert( p->pEList );
|
||||||
|
|
||||||
/* Code the SELECTs to our left into temporary table "tab1".
|
/* Code the SELECTs to our left into temporary table "tab1".
|
||||||
@@ -1650,12 +1617,8 @@ static int multiSelect(
|
|||||||
/* Code the current SELECT into temporary table "tab2"
|
/* Code the current SELECT into temporary table "tab2"
|
||||||
*/
|
*/
|
||||||
addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, tab2, 0);
|
addr = sqlite3VdbeAddOp(v, OP_OpenVirtual, tab2, 0);
|
||||||
rc = multiSelectOpenVirtualAddr(p, addr);
|
assert( p->addrOpenVirt[1] == -1 );
|
||||||
if( rc!=SQLITE_OK ){
|
p->addrOpenVirt[1] = addr;
|
||||||
goto multi_select_end;
|
|
||||||
}
|
|
||||||
assert( nAddr<sizeof(aAddr)/sizeof(aAddr[0]) );
|
|
||||||
aAddr[nAddr++] = sqlite3VdbeAddOp(v, OP_SetNumColumns, tab2, 0);
|
|
||||||
p->pPrior = 0;
|
p->pPrior = 0;
|
||||||
pLimit = p->pLimit;
|
pLimit = p->pLimit;
|
||||||
p->pLimit = 0;
|
p->pLimit = 0;
|
||||||
@@ -1684,7 +1647,7 @@ static int multiSelect(
|
|||||||
iStart = sqlite3VdbeAddOp(v, OP_RowKey, tab1, 0);
|
iStart = sqlite3VdbeAddOp(v, OP_RowKey, tab1, 0);
|
||||||
sqlite3VdbeAddOp(v, OP_NotFound, tab2, iCont);
|
sqlite3VdbeAddOp(v, OP_NotFound, tab2, iCont);
|
||||||
rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
|
rc = selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
|
||||||
p->pOrderBy, -1, eDest, iParm,
|
pOrderBy, -1, eDest, iParm,
|
||||||
iCont, iBreak, 0);
|
iCont, iBreak, 0);
|
||||||
if( rc ){
|
if( rc ){
|
||||||
rc = 1;
|
rc = 1;
|
||||||
@@ -1713,9 +1676,8 @@ static int multiSelect(
|
|||||||
/* Set the number of columns in temporary tables
|
/* Set the number of columns in temporary tables
|
||||||
*/
|
*/
|
||||||
nCol = p->pEList->nExpr;
|
nCol = p->pEList->nExpr;
|
||||||
while( nAddr>0 ){
|
while( nSetP2 ){
|
||||||
nAddr--;
|
sqlite3VdbeChangeP2(v, aSetP2[--nSetP2], nCol);
|
||||||
sqlite3VdbeChangeP2(v, aAddr[nAddr], nCol);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compute collating sequences used by either the ORDER BY clause or
|
/* Compute collating sequences used by either the ORDER BY clause or
|
||||||
@@ -1728,12 +1690,15 @@ static int multiSelect(
|
|||||||
** SELECT might also skip this part if it has no ORDER BY clause and
|
** SELECT might also skip this part if it has no ORDER BY clause and
|
||||||
** no temp tables are required.
|
** no temp tables are required.
|
||||||
*/
|
*/
|
||||||
if( p->pOrderBy || (pOpenVirtual && pOpenVirtual->nId>0) ){
|
if( pOrderBy || p->usesVirt ){
|
||||||
int i; /* Loop counter */
|
int i; /* Loop counter */
|
||||||
KeyInfo *pKeyInfo; /* Collating sequence for the result set */
|
KeyInfo *pKeyInfo; /* Collating sequence for the result set */
|
||||||
|
Select *pLoop; /* For looping through SELECT statements */
|
||||||
|
CollSeq **apColl;
|
||||||
|
CollSeq **aCopy;
|
||||||
|
|
||||||
assert( p->ppOpenVirtual == &pOpenVirtual );
|
assert( p->pRightmost==p );
|
||||||
pKeyInfo = sqliteMalloc(sizeof(*pKeyInfo)+nCol*sizeof(CollSeq*));
|
pKeyInfo = sqliteMalloc(sizeof(*pKeyInfo)+nCol*2*sizeof(CollSeq*));
|
||||||
if( !pKeyInfo ){
|
if( !pKeyInfo ){
|
||||||
rc = SQLITE_NOMEM;
|
rc = SQLITE_NOMEM;
|
||||||
goto multi_select_end;
|
goto multi_select_end;
|
||||||
@@ -1742,46 +1707,57 @@ static int multiSelect(
|
|||||||
pKeyInfo->enc = pParse->db->enc;
|
pKeyInfo->enc = pParse->db->enc;
|
||||||
pKeyInfo->nField = nCol;
|
pKeyInfo->nField = nCol;
|
||||||
|
|
||||||
for(i=0; i<nCol; i++){
|
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
|
||||||
pKeyInfo->aColl[i] = multiSelectCollSeq(pParse, p, i);
|
*apColl = multiSelectCollSeq(pParse, p, i);
|
||||||
if( !pKeyInfo->aColl[i] ){
|
if( 0==*apColl ){
|
||||||
pKeyInfo->aColl[i] = pParse->db->pDfltColl;
|
*apColl = pParse->db->pDfltColl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i=0; pOpenVirtual && i<pOpenVirtual->nId; i++){
|
for(pLoop=p; pLoop; pLoop=pLoop->pPrior){
|
||||||
int p3type = (i==0?P3_KEYINFO_HANDOFF:P3_KEYINFO);
|
for(i=0; i<2; i++){
|
||||||
int addr = pOpenVirtual->a[i].idx;
|
int addr = pLoop->addrOpenVirt[i];
|
||||||
sqlite3VdbeChangeP3(v, addr, (char *)pKeyInfo, p3type);
|
if( addr<0 ){
|
||||||
|
/* If [0] is unused then [1] is also unused. So we can
|
||||||
|
** always safely abort as soon as the first unused slot is found */
|
||||||
|
assert( pLoop->addrOpenVirt[1]<0 );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sqlite3VdbeChangeP2(v, addr, nCol);
|
||||||
|
sqlite3VdbeChangeP3(v, addr, (char*)pKeyInfo, P3_KEYINFO);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( p->pOrderBy ){
|
if( pOrderBy ){
|
||||||
struct ExprList_item *pOrderByTerm = p->pOrderBy->a;
|
struct ExprList_item *pOTerm = pOrderBy->a;
|
||||||
for(i=0; i<p->pOrderBy->nExpr; i++, pOrderByTerm++){
|
int nExpr = pOrderBy->nExpr;
|
||||||
Expr *pExpr = pOrderByTerm->pExpr;
|
int addr;
|
||||||
char *zName = pOrderByTerm->zName;
|
|
||||||
|
aCopy = (CollSeq**)&pKeyInfo[1];
|
||||||
|
memcpy(aCopy, pKeyInfo->aColl, nCol*sizeof(CollSeq*));
|
||||||
|
apColl = pKeyInfo->aColl;
|
||||||
|
for(i=0; i<pOrderBy->nExpr; i++, pOTerm++, apColl++){
|
||||||
|
Expr *pExpr = pOTerm->pExpr;
|
||||||
|
char *zName = pOTerm->zName;
|
||||||
assert( pExpr->op==TK_COLUMN && pExpr->iColumn<nCol );
|
assert( pExpr->op==TK_COLUMN && pExpr->iColumn<nCol );
|
||||||
/* assert( !pExpr->pColl ); */
|
|
||||||
if( zName ){
|
if( zName ){
|
||||||
pExpr->pColl = sqlite3LocateCollSeq(pParse, zName, -1);
|
*apColl = sqlite3LocateCollSeq(pParse, zName, -1);
|
||||||
}else{
|
}else{
|
||||||
pExpr->pColl = pKeyInfo->aColl[pExpr->iColumn];
|
*apColl = aCopy[pExpr->iColumn];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert( p->pRightmost==p );
|
||||||
|
assert( p->addrOpenVirt[2]>=0 );
|
||||||
|
addr = p->addrOpenVirt[2];
|
||||||
|
sqlite3VdbeChangeP2(v, addr, p->pEList->nExpr+1);
|
||||||
|
sqlite3VdbeChangeP3(v, addr, (char*)pKeyInfo, P3_KEYINFO);
|
||||||
generateSortTail(pParse, p, v, p->pEList->nExpr, eDest, iParm);
|
generateSortTail(pParse, p, v, p->pEList->nExpr, eDest, iParm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !pOpenVirtual ){
|
sqliteFree(pKeyInfo);
|
||||||
/* This happens for UNION ALL ... ORDER BY */
|
|
||||||
sqliteFree(pKeyInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
multi_select_end:
|
multi_select_end:
|
||||||
if( pOpenVirtual ){
|
|
||||||
sqlite3IdListDelete(pOpenVirtual);
|
|
||||||
}
|
|
||||||
p->ppOpenVirtual = 0;
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
|
||||||
@@ -2517,6 +2493,12 @@ int sqlite3Select(
|
|||||||
/* If there is are a sequence of queries, do the earlier ones first.
|
/* If there is are a sequence of queries, do the earlier ones first.
|
||||||
*/
|
*/
|
||||||
if( p->pPrior ){
|
if( p->pPrior ){
|
||||||
|
if( p->pRightmost==0 ){
|
||||||
|
Select *pLoop;
|
||||||
|
for(pLoop=p; pLoop; pLoop=pLoop->pPrior){
|
||||||
|
pLoop->pRightmost = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
return multiSelect(pParse, p, eDest, iParm, aff);
|
return multiSelect(pParse, p, eDest, iParm, aff);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2639,6 +2621,8 @@ int sqlite3Select(
|
|||||||
*/
|
*/
|
||||||
if( pOrderBy ){
|
if( pOrderBy ){
|
||||||
struct ExprList_item *pTerm;
|
struct ExprList_item *pTerm;
|
||||||
|
KeyInfo *pKeyInfo;
|
||||||
|
int addr;
|
||||||
for(i=0, pTerm=pOrderBy->a; i<pOrderBy->nExpr; i++, pTerm++){
|
for(i=0, pTerm=pOrderBy->a; i<pOrderBy->nExpr; i++, pTerm++){
|
||||||
if( pTerm->zName ){
|
if( pTerm->zName ){
|
||||||
pTerm->pExpr->pColl = sqlite3LocateCollSeq(pParse, pTerm->zName, -1);
|
pTerm->pExpr->pColl = sqlite3LocateCollSeq(pParse, pTerm->zName, -1);
|
||||||
@@ -2647,6 +2631,11 @@ int sqlite3Select(
|
|||||||
if( pParse->nErr ){
|
if( pParse->nErr ){
|
||||||
goto select_end;
|
goto select_end;
|
||||||
}
|
}
|
||||||
|
pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
|
||||||
|
pOrderBy->iTab = pParse->nTab++;
|
||||||
|
addr = sqlite3VdbeOp3(v, OP_OpenVirtual, pOrderBy->iTab, pOrderBy->nExpr+1,
|
||||||
|
(char*)pKeyInfo, P3_KEYINFO_HANDOFF);
|
||||||
|
p->addrOpenVirt[2] = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set the limiter.
|
/* Set the limiter.
|
||||||
@@ -2656,8 +2645,7 @@ int sqlite3Select(
|
|||||||
/* If the output is destined for a temporary table, open that table.
|
/* If the output is destined for a temporary table, open that table.
|
||||||
*/
|
*/
|
||||||
if( eDest==SRT_TempTable ){
|
if( eDest==SRT_TempTable ){
|
||||||
sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, 0);
|
sqlite3VdbeAddOp(v, OP_OpenVirtual, iParm, pEList->nExpr);
|
||||||
sqlite3VdbeAddOp(v, OP_SetNumColumns, iParm, pEList->nExpr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Do an analysis of aggregate expressions.
|
/* Do an analysis of aggregate expressions.
|
||||||
@@ -2720,8 +2708,11 @@ int sqlite3Select(
|
|||||||
/* Open a virtual index to use for the distinct set.
|
/* Open a virtual index to use for the distinct set.
|
||||||
*/
|
*/
|
||||||
if( isDistinct ){
|
if( isDistinct ){
|
||||||
|
KeyInfo *pKeyInfo;
|
||||||
distinct = pParse->nTab++;
|
distinct = pParse->nTab++;
|
||||||
openVirtualIndex(pParse, p, distinct);
|
pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
|
||||||
|
sqlite3VdbeOp3(v, OP_OpenVirtual, distinct, 0,
|
||||||
|
(char*)pKeyInfo, P3_KEYINFO_HANDOFF);
|
||||||
}else{
|
}else{
|
||||||
distinct = -1;
|
distinct = -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
*************************************************************************
|
*************************************************************************
|
||||||
** Internal interface definitions for SQLite.
|
** Internal interface definitions for SQLite.
|
||||||
**
|
**
|
||||||
** @(#) $Id: sqliteInt.h,v 1.406 2005/08/30 00:54:03 drh Exp $
|
** @(#) $Id: sqliteInt.h,v 1.407 2005/09/01 03:07:44 drh Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef _SQLITEINT_H_
|
#ifndef _SQLITEINT_H_
|
||||||
#define _SQLITEINT_H_
|
#define _SQLITEINT_H_
|
||||||
@@ -886,13 +886,13 @@ struct Expr {
|
|||||||
struct ExprList {
|
struct ExprList {
|
||||||
int nExpr; /* Number of expressions on the list */
|
int nExpr; /* Number of expressions on the list */
|
||||||
int nAlloc; /* Number of entries allocated below */
|
int nAlloc; /* Number of entries allocated below */
|
||||||
|
int iTab; /* VDBE Cursor associated with this ExprList */
|
||||||
struct ExprList_item {
|
struct ExprList_item {
|
||||||
Expr *pExpr; /* The list of expressions */
|
Expr *pExpr; /* The list of expressions */
|
||||||
char *zName; /* Token associated with this expression */
|
char *zName; /* Token associated with this expression */
|
||||||
u8 sortOrder; /* 1 for DESC or 0 for ASC */
|
u8 sortOrder; /* 1 for DESC or 0 for ASC */
|
||||||
u8 isAgg; /* True if this is an aggregate like count(*) */
|
u8 isAgg; /* True if this is an aggregate like count(*) */
|
||||||
u8 done; /* A flag to indicate when processing is finished */
|
u8 done; /* A flag to indicate when processing is finished */
|
||||||
u8 orderByDup[2]; /* Corresponding term in OrderBy/GroupBy clause */
|
|
||||||
} *a; /* One entry for each expression */
|
} *a; /* One entry for each expression */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1045,23 +1045,35 @@ struct NameContext {
|
|||||||
** limit and nOffset to the value of the offset (or 0 if there is not
|
** limit and nOffset to the value of the offset (or 0 if there is not
|
||||||
** offset). But later on, nLimit and nOffset become the memory locations
|
** offset). But later on, nLimit and nOffset become the memory locations
|
||||||
** in the VDBE that record the limit and offset counters.
|
** in the VDBE that record the limit and offset counters.
|
||||||
|
**
|
||||||
|
** addrOpenVirt[] entries contain the address of OP_OpenVirtual opcodes.
|
||||||
|
** These addresses must be stored so that we can go back and fill in
|
||||||
|
** the P3_KEYINFO and P2 parameters later. Neither the KeyInfo nor
|
||||||
|
** the number of columns in P2 can be computed at the same time
|
||||||
|
** as the OP_OpenVirtual instruction is coded because not
|
||||||
|
** enough information about the compound query is known at that point.
|
||||||
|
** The KeyInfo for addrOpenVirt[0] and [1] contains collating sequences
|
||||||
|
** for the result set. The KeyInfo for addrOpenVirt[2] contains collating
|
||||||
|
** sequences for the ORDER BY clause.
|
||||||
*/
|
*/
|
||||||
struct Select {
|
struct Select {
|
||||||
ExprList *pEList; /* The fields of the result */
|
ExprList *pEList; /* The fields of the result */
|
||||||
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
|
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
|
||||||
u8 isDistinct; /* True if the DISTINCT keyword is present */
|
u8 isDistinct; /* True if the DISTINCT keyword is present */
|
||||||
|
u8 isResolved; /* True once sqlite3SelectResolve() has run. */
|
||||||
|
u8 isAgg; /* True if this is an aggregate query */
|
||||||
|
u8 usesVirt; /* True if uses an OpenVirtual opcode */
|
||||||
SrcList *pSrc; /* The FROM clause */
|
SrcList *pSrc; /* The FROM clause */
|
||||||
Expr *pWhere; /* The WHERE clause */
|
Expr *pWhere; /* The WHERE clause */
|
||||||
ExprList *pGroupBy; /* The GROUP BY clause */
|
ExprList *pGroupBy; /* The GROUP BY clause */
|
||||||
Expr *pHaving; /* The HAVING clause */
|
Expr *pHaving; /* The HAVING clause */
|
||||||
ExprList *pOrderBy; /* The ORDER BY clause */
|
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||||
Select *pPrior; /* Prior select in a compound select statement */
|
Select *pPrior; /* Prior select in a compound select statement */
|
||||||
|
Select *pRightmost; /* Right-most select in a compound select statement */
|
||||||
Expr *pLimit; /* LIMIT expression. NULL means not used. */
|
Expr *pLimit; /* LIMIT expression. NULL means not used. */
|
||||||
Expr *pOffset; /* OFFSET expression. NULL means not used. */
|
Expr *pOffset; /* OFFSET expression. NULL means not used. */
|
||||||
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
||||||
IdList **ppOpenVirtual;/* OP_OpenVirtual addresses used by multi-selects */
|
int addrOpenVirt[3]; /* OP_OpenVirtual opcodes related to this select */
|
||||||
u8 isResolved; /* True once sqlite3SelectResolve() has run. */
|
|
||||||
u8 isAgg; /* True if this is an aggregate query */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
162
src/vdbe.c
162
src/vdbe.c
@@ -43,7 +43,7 @@
|
|||||||
** in this file for details. If in doubt, do not deviate from existing
|
** in this file for details. If in doubt, do not deviate from existing
|
||||||
** commenting and indentation practices when changing or adding code.
|
** commenting and indentation practices when changing or adding code.
|
||||||
**
|
**
|
||||||
** $Id: vdbe.c,v 1.478 2005/07/28 20:51:19 drh Exp $
|
** $Id: vdbe.c,v 1.479 2005/09/01 03:07:44 drh Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
@@ -205,39 +205,6 @@ static void popStack(Mem **ppTos, int N){
|
|||||||
*ppTos = pTos;
|
*ppTos = pTos;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** The parameters are pointers to the head of two sorted lists
|
|
||||||
** of Sorter structures. Merge these two lists together and return
|
|
||||||
** a single sorted list. This routine forms the core of the merge-sort
|
|
||||||
** algorithm.
|
|
||||||
**
|
|
||||||
** In the case of a tie, left sorts in front of right.
|
|
||||||
*/
|
|
||||||
static Sorter *Merge(Sorter *pLeft, Sorter *pRight, KeyInfo *pKeyInfo){
|
|
||||||
Sorter sHead;
|
|
||||||
Sorter *pTail;
|
|
||||||
pTail = &sHead;
|
|
||||||
pTail->pNext = 0;
|
|
||||||
while( pLeft && pRight ){
|
|
||||||
int c = sqlite3VdbeRecordCompare(pKeyInfo, pLeft->nKey, pLeft->zKey,
|
|
||||||
pRight->nKey, pRight->zKey);
|
|
||||||
if( c<=0 ){
|
|
||||||
pTail->pNext = pLeft;
|
|
||||||
pLeft = pLeft->pNext;
|
|
||||||
}else{
|
|
||||||
pTail->pNext = pRight;
|
|
||||||
pRight = pRight->pNext;
|
|
||||||
}
|
|
||||||
pTail = pTail->pNext;
|
|
||||||
}
|
|
||||||
if( pLeft ){
|
|
||||||
pTail->pNext = pLeft;
|
|
||||||
}else if( pRight ){
|
|
||||||
pTail->pNext = pRight;
|
|
||||||
}
|
|
||||||
return sHead.pNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Allocate cursor number iCur. Return a pointer to it. Return NULL
|
** Allocate cursor number iCur. Return a pointer to it. Return NULL
|
||||||
** if we run out of memory.
|
** if we run out of memory.
|
||||||
@@ -635,7 +602,7 @@ case OP_Return: { /* no-push */
|
|||||||
|
|
||||||
/* Opcode: Halt P1 P2 P3
|
/* Opcode: Halt P1 P2 P3
|
||||||
**
|
**
|
||||||
** Exit immediately. All open cursors, Lists, Sorts, etc are closed
|
** Exit immediately. All open cursors, Fifos, etc are closed
|
||||||
** automatically.
|
** automatically.
|
||||||
**
|
**
|
||||||
** P1 is the result code returned by sqlite3_exec(), sqlite3_reset(),
|
** P1 is the result code returned by sqlite3_exec(), sqlite3_reset(),
|
||||||
@@ -2603,13 +2570,14 @@ case OP_OpenWrite: { /* no-push */
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opcode: OpenVirtual P1 * P3
|
/* Opcode: OpenVirtual P1 P2 P3
|
||||||
**
|
**
|
||||||
** Open a new cursor to a transient or virtual table.
|
** Open a new cursor P1 to a transient or virtual table.
|
||||||
** The cursor is always opened read/write even if
|
** The cursor is always opened read/write even if
|
||||||
** the main database is read-only. The transient or virtual
|
** the main database is read-only. The transient or virtual
|
||||||
** table is deleted automatically when the cursor is closed.
|
** table is deleted automatically when the cursor is closed.
|
||||||
**
|
**
|
||||||
|
** P2 is the number of columns in the virtual table.
|
||||||
** The cursor points to a BTree table if P3==0 and to a BTree index
|
** The cursor points to a BTree table if P3==0 and to a BTree index
|
||||||
** if P3 is not 0. If P3 is not NULL, it points to a KeyInfo structure
|
** if P3 is not 0. If P3 is not NULL, it points to a KeyInfo structure
|
||||||
** that defines the format of keys in the index.
|
** that defines the format of keys in the index.
|
||||||
@@ -2650,6 +2618,7 @@ case OP_OpenVirtual: { /* no-push */
|
|||||||
pCx->pIncrKey = &pCx->bogusIncrKey;
|
pCx->pIncrKey = &pCx->bogusIncrKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pCx->nField = pOp->p2;
|
||||||
pCx->isIndex = !pCx->isTable;
|
pCx->isIndex = !pCx->isTable;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3464,6 +3433,23 @@ case OP_Last: { /* no-push */
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Opcode: Sort P1 P2 *
|
||||||
|
**
|
||||||
|
** This opcode does exactly the same thing as OP_Rewind except that
|
||||||
|
** it increments an undocumented global variable used for testing.
|
||||||
|
**
|
||||||
|
** Sorting is accomplished by writing records into a sorting index,
|
||||||
|
** then rewinding that index and playing it back from beginning to
|
||||||
|
** end. We use the OP_Sort opcode instead of OP_Rewind to do the
|
||||||
|
** rewinding so that the global variable will be incremented and
|
||||||
|
** regression tests can determine whether or not the optimizer is
|
||||||
|
** correctly optimizing out sorts.
|
||||||
|
*/
|
||||||
|
case OP_Sort: { /* no-push */
|
||||||
|
sqlite3_sort_count++;
|
||||||
|
/* Fall through into OP_Rewind */
|
||||||
|
}
|
||||||
/* Opcode: Rewind P1 P2 *
|
/* Opcode: Rewind P1 P2 *
|
||||||
**
|
**
|
||||||
** The next use of the Rowid or Column or Next instruction for P1
|
** The next use of the Rowid or Column or Next instruction for P1
|
||||||
@@ -4091,108 +4077,6 @@ case OP_ContextPop: { /* no-push */
|
|||||||
}
|
}
|
||||||
#endif /* #ifndef SQLITE_OMIT_TRIGGER */
|
#endif /* #ifndef SQLITE_OMIT_TRIGGER */
|
||||||
|
|
||||||
/* Opcode: SortInsert * * *
|
|
||||||
**
|
|
||||||
** The TOS is the key and the NOS is the data. Pop both from the stack
|
|
||||||
** and put them on the sorter. The key and data should have been
|
|
||||||
** made using the MakeRecord opcode.
|
|
||||||
*/
|
|
||||||
case OP_SortInsert: { /* no-push */
|
|
||||||
Mem *pNos = &pTos[-1];
|
|
||||||
Sorter *pSorter;
|
|
||||||
assert( pNos>=p->aStack );
|
|
||||||
if( Dynamicify(pTos, db->enc) ) goto no_mem;
|
|
||||||
pSorter = sqliteMallocRaw( sizeof(Sorter) );
|
|
||||||
if( pSorter==0 ) goto no_mem;
|
|
||||||
pSorter->pNext = 0;
|
|
||||||
if( p->pSortTail ){
|
|
||||||
p->pSortTail->pNext = pSorter;
|
|
||||||
}else{
|
|
||||||
p->pSort = pSorter;
|
|
||||||
}
|
|
||||||
p->pSortTail = pSorter;
|
|
||||||
assert( pTos->flags & MEM_Dyn );
|
|
||||||
pSorter->nKey = pTos->n;
|
|
||||||
pSorter->zKey = pTos->z;
|
|
||||||
pSorter->data.flags = MEM_Null;
|
|
||||||
rc = sqlite3VdbeMemMove(&pSorter->data, pNos);
|
|
||||||
pTos -= 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Opcode: Sort * * P3
|
|
||||||
**
|
|
||||||
** Sort all elements on the sorter. The algorithm is a
|
|
||||||
** mergesort. The P3 argument is a pointer to a KeyInfo structure
|
|
||||||
** that describes the keys to be sorted.
|
|
||||||
*/
|
|
||||||
case OP_Sort: { /* no-push */
|
|
||||||
int i;
|
|
||||||
KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3;
|
|
||||||
Sorter *pElem;
|
|
||||||
Sorter *apSorter[NSORT];
|
|
||||||
sqlite3_sort_count++;
|
|
||||||
pKeyInfo->enc = p->db->enc;
|
|
||||||
for(i=0; i<NSORT; i++){
|
|
||||||
apSorter[i] = 0;
|
|
||||||
}
|
|
||||||
while( p->pSort ){
|
|
||||||
pElem = p->pSort;
|
|
||||||
p->pSort = pElem->pNext;
|
|
||||||
pElem->pNext = 0;
|
|
||||||
for(i=0; i<NSORT-1; i++){
|
|
||||||
if( apSorter[i]==0 ){
|
|
||||||
apSorter[i] = pElem;
|
|
||||||
break;
|
|
||||||
}else{
|
|
||||||
pElem = Merge(apSorter[i], pElem, pKeyInfo);
|
|
||||||
apSorter[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( i>=NSORT-1 ){
|
|
||||||
apSorter[NSORT-1] = Merge(apSorter[NSORT-1],pElem, pKeyInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pElem = 0;
|
|
||||||
for(i=0; i<NSORT; i++){
|
|
||||||
pElem = Merge(apSorter[i], pElem, pKeyInfo);
|
|
||||||
}
|
|
||||||
p->pSort = pElem;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Opcode: SortNext * P2 *
|
|
||||||
**
|
|
||||||
** Push the data for the topmost element in the sorter onto the
|
|
||||||
** stack, then remove the element from the sorter. If the sorter
|
|
||||||
** is empty, push nothing on the stack and instead jump immediately
|
|
||||||
** to instruction P2.
|
|
||||||
*/
|
|
||||||
case OP_SortNext: {
|
|
||||||
Sorter *pSorter = p->pSort;
|
|
||||||
CHECK_FOR_INTERRUPT;
|
|
||||||
if( pSorter!=0 ){
|
|
||||||
p->pSort = pSorter->pNext;
|
|
||||||
pTos++;
|
|
||||||
pTos->flags = MEM_Null;
|
|
||||||
rc = sqlite3VdbeMemMove(pTos, &pSorter->data);
|
|
||||||
sqliteFree(pSorter->zKey);
|
|
||||||
sqliteFree(pSorter);
|
|
||||||
}else{
|
|
||||||
pc = pOp->p2 - 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Opcode: SortReset * * *
|
|
||||||
**
|
|
||||||
** Remove any elements that remain on the sorter.
|
|
||||||
*/
|
|
||||||
case OP_SortReset: { /* no-push */
|
|
||||||
sqlite3VdbeSorterReset(p);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Opcode: MemStore P1 P2 *
|
/* Opcode: MemStore P1 P2 *
|
||||||
**
|
**
|
||||||
** Write the top of the stack into memory location P1.
|
** Write the top of the stack into memory location P1.
|
||||||
|
|||||||
@@ -125,23 +125,6 @@ struct Mem {
|
|||||||
};
|
};
|
||||||
typedef struct Mem Mem;
|
typedef struct Mem Mem;
|
||||||
|
|
||||||
/*
|
|
||||||
** A sorter builds a list of elements to be sorted. Each element of
|
|
||||||
** the list is an instance of the following structure.
|
|
||||||
*/
|
|
||||||
typedef struct Sorter Sorter;
|
|
||||||
struct Sorter {
|
|
||||||
int nKey; /* Number of bytes in the key */
|
|
||||||
char *zKey; /* The key by which we will sort */
|
|
||||||
Mem data;
|
|
||||||
Sorter *pNext; /* Next in the list */
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Number of buckets used for merge-sort.
|
|
||||||
*/
|
|
||||||
#define NSORT 30
|
|
||||||
|
|
||||||
/* One or more of the following flags are set to indicate the validOK
|
/* One or more of the following flags are set to indicate the validOK
|
||||||
** representations of the value stored in the Mem struct.
|
** representations of the value stored in the Mem struct.
|
||||||
**
|
**
|
||||||
@@ -324,8 +307,6 @@ struct Vdbe {
|
|||||||
Mem *aColName; /* Column names to return */
|
Mem *aColName; /* Column names to return */
|
||||||
int nCursor; /* Number of slots in apCsr[] */
|
int nCursor; /* Number of slots in apCsr[] */
|
||||||
Cursor **apCsr; /* One element of this array for each open cursor */
|
Cursor **apCsr; /* One element of this array for each open cursor */
|
||||||
Sorter *pSort; /* A linked list of objects to be sorted */
|
|
||||||
Sorter *pSortTail; /* Last element on the pSort list */
|
|
||||||
int nVar; /* Number of entries in aVar[] */
|
int nVar; /* Number of entries in aVar[] */
|
||||||
Mem *aVar; /* Values for the OP_Variable opcode. */
|
Mem *aVar; /* Values for the OP_Variable opcode. */
|
||||||
char **azVar; /* Name of variables */
|
char **azVar; /* Name of variables */
|
||||||
@@ -373,7 +354,6 @@ struct Vdbe {
|
|||||||
** Function prototypes
|
** Function prototypes
|
||||||
*/
|
*/
|
||||||
void sqlite3VdbeFreeCursor(Cursor*);
|
void sqlite3VdbeFreeCursor(Cursor*);
|
||||||
void sqlite3VdbeSorterReset(Vdbe*);
|
|
||||||
int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *);
|
int sqlite3VdbeAggReset(sqlite3*, Agg *, KeyInfo *);
|
||||||
void sqliteVdbePopStack(Vdbe*,int);
|
void sqliteVdbePopStack(Vdbe*,int);
|
||||||
int sqlite3VdbeCursorMoveto(Cursor*);
|
int sqlite3VdbeCursorMoveto(Cursor*);
|
||||||
|
|||||||
@@ -763,21 +763,6 @@ void sqlite3VdbeMakeReady(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Remove any elements that remain on the sorter for the VDBE given.
|
|
||||||
*/
|
|
||||||
void sqlite3VdbeSorterReset(Vdbe *p){
|
|
||||||
while( p->pSort ){
|
|
||||||
Sorter *pSorter = p->pSort;
|
|
||||||
p->pSort = pSorter->pNext;
|
|
||||||
sqliteFree(pSorter->zKey);
|
|
||||||
sqlite3VdbeMemRelease(&pSorter->data);
|
|
||||||
sqliteFree(pSorter);
|
|
||||||
}
|
|
||||||
p->pSortTail = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Free all resources allociated with AggElem pElem, an element of
|
** Free all resources allociated with AggElem pElem, an element of
|
||||||
** aggregate pAgg.
|
** aggregate pAgg.
|
||||||
@@ -965,7 +950,6 @@ static void Cleanup(Vdbe *p){
|
|||||||
}
|
}
|
||||||
sqliteFree(p->contextStack);
|
sqliteFree(p->contextStack);
|
||||||
}
|
}
|
||||||
sqlite3VdbeSorterReset(p);
|
|
||||||
for(i=0; i<p->nAgg; i++){
|
for(i=0; i<p->nAgg; i++){
|
||||||
sqlite3VdbeAggReset(0, &p->apAgg[i], 0);
|
sqlite3VdbeAggReset(0, &p->apAgg[i], 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
# This file implements tests for the conflict resolution extension
|
# This file implements tests for the conflict resolution extension
|
||||||
# to SQLite.
|
# to SQLite.
|
||||||
#
|
#
|
||||||
# $Id: conflict.test,v 1.24 2005/06/07 02:12:30 drh Exp $
|
# $Id: conflict.test,v 1.25 2005/09/01 03:07:45 drh Exp $
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -281,18 +281,18 @@ do_test conflict-6.0 {
|
|||||||
#
|
#
|
||||||
foreach {i conf1 cmd t0 t1 t2 t3} {
|
foreach {i conf1 cmd t0 t1 t2 t3} {
|
||||||
1 {} UPDATE 1 {6 7 8 9} 1 1
|
1 {} UPDATE 1 {6 7 8 9} 1 1
|
||||||
2 REPLACE UPDATE 0 {7 6 9} 1 0
|
2 REPLACE UPDATE 0 {7 6 9} 1 1
|
||||||
3 IGNORE UPDATE 0 {6 7 3 9} 1 0
|
3 IGNORE UPDATE 0 {6 7 3 9} 1 1
|
||||||
4 FAIL UPDATE 1 {6 7 3 4} 1 0
|
4 FAIL UPDATE 1 {6 7 3 4} 1 0
|
||||||
5 ABORT UPDATE 1 {1 2 3 4} 1 1
|
5 ABORT UPDATE 1 {1 2 3 4} 1 1
|
||||||
6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0
|
6 ROLLBACK UPDATE 1 {1 2 3 4} 0 0
|
||||||
7 REPLACE {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0
|
7 REPLACE {UPDATE OR IGNORE} 0 {6 7 3 9} 1 1
|
||||||
8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 0
|
8 IGNORE {UPDATE OR REPLACE} 0 {7 6 9} 1 1
|
||||||
9 FAIL {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0
|
9 FAIL {UPDATE OR IGNORE} 0 {6 7 3 9} 1 1
|
||||||
10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 0
|
10 ABORT {UPDATE OR REPLACE} 0 {7 6 9} 1 1
|
||||||
11 ROLLBACK {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0
|
11 ROLLBACK {UPDATE OR IGNORE} 0 {6 7 3 9} 1 1
|
||||||
12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 0
|
12 {} {UPDATE OR IGNORE} 0 {6 7 3 9} 1 1
|
||||||
13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 0
|
13 {} {UPDATE OR REPLACE} 0 {7 6 9} 1 1
|
||||||
14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0
|
14 {} {UPDATE OR FAIL} 1 {6 7 3 4} 1 0
|
||||||
15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 1
|
15 {} {UPDATE OR ABORT} 1 {1 2 3 4} 1 1
|
||||||
16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0
|
16 {} {UPDATE OR ROLLBACK} 1 {1 2 3 4} 0 0
|
||||||
|
|||||||
Reference in New Issue
Block a user