Compare commits

...

22 Commits

Author SHA1 Message Date
drh c91cbc7389 Merge recent trunk changes into the orderby-planning branch.
FossilOrigin-Name: 8d8609d6c619e02e3e832e3b2256b8dccb8c21de
2014-05-09 17:17:37 +00:00
drh 79211e194d Simplify assert() statements used to verify correct operation of
record comparison routines.

FossilOrigin-Name: 3300d62dcbe74842cf86ca436959fe4e77a89f84
2014-05-02 17:33:16 +00:00
drh c7f6c148d9 Failure to extend a temp file for use with mmap() in vdbesort.c is benign.
FossilOrigin-Name: d4d396387d373bd1e82eda2c7c2e7ca35ec099c4
2014-05-02 16:22:55 +00:00
drh 7a0fd192cc Fix a faulty assert() statement.
FossilOrigin-Name: 9196ce407379ca3b151b601b98848771e5cb4e8f
2014-05-02 15:25:24 +00:00
drh fb0d6e56d6 Merge latest trunk enhancements and fixes into the orderby-planning branch.
FossilOrigin-Name: 84862d3a095629d20c8e7b8a16f4dc26cd41ab6d
2014-05-02 13:09:06 +00:00
drh f690c14280 Improved header comment on the vdbesort.c module. No changes to code.
FossilOrigin-Name: bf09ce24d054bc68c226064f5f28d97e0e648a13
2014-04-24 16:25:25 +00:00
drh 0ff435a782 Reopen the orderby-planning branch and merge in the latest trunk enhancements
and fixes.

FossilOrigin-Name: 6077ddcd93318e24b9756adaaf293ba9fb3cedf7
2014-04-24 15:06:25 +00:00
drh 73e7d8b2bb Fix a compiler warning and an after-OOM memory leak.
FossilOrigin-Name: 58f7ca29303c280229f86d01f418e1de5f5aebba
2014-03-28 19:47:19 +00:00
drh 7c8e9c78c8 Merge the latest changes from trunk.
FossilOrigin-Name: 3047a25f1c41e83f0b4772f7c36fbfec0f12dc7e
2014-03-28 18:35:39 +00:00
dan 6971952c65 Instead of allocating a single large buffer at the beginning of each sort operation, start with a small buffer and extend it using realloc() as required.
FossilOrigin-Name: 81987c8ceb64f051528a6ca42673821d9ab7c0ff
2014-03-27 19:25:02 +00:00
dan face087212 Use xFetch() to access temporary files in vdbesort.c. Use a single large allocation instead of many small allocations when accumulating records in vdbesort.c. This is an interim commit - it allocates a buffer the size of the page-cache every time data is sorted.
FossilOrigin-Name: f4ac1bf28c4ba395ccab8f1c9df72614a61095a7
2014-03-27 17:23:41 +00:00
drh bfa463696b Merge the latest trunk changes and the fix for the crash on a corrupt
database.

FossilOrigin-Name: 0b35346c32dba14963c85ec178f2b46aa2bbf6dc
2014-03-27 00:09:00 +00:00
drh e6c7b09df6 Merge enhancements and fixes from trunk.
FossilOrigin-Name: e005f2d6dd9faf38cc8fdb9428b5aa6192a6adae
2014-03-25 18:29:12 +00:00
dan ab1dcc1a4b Fix a problem in the code added by [707ea170b3] causing vdbesort.c to sort unstably.
FossilOrigin-Name: d3e640afe611b6ae0b7f2cff5b00900d7e4d5ee3
2014-03-25 17:07:48 +00:00
dan 78d5843245 Remove the sequence values from sorter records used by ORDER BY as well.
FossilOrigin-Name: c3ae3697832a00d4d5758988a8766bdbb691e6b8
2014-03-25 15:04:07 +00:00
dan dd23c6bfb4 Omit the sequence value from sorter records used by GROUP BY queries that cannot use an index.
FossilOrigin-Name: 3f90abddc31ac20739778c235a834c33f7057997
2014-03-24 20:19:07 +00:00
drh fd0a2f9756 Avoid unnecessary moving of content between registers during an ORDER BY.
FossilOrigin-Name: 4f472accf072d9cb64f209923924b26f21b13d27
2014-03-24 18:08:15 +00:00
dan 3f802ebce2 Remove an unnecessary temporary register allocation.
FossilOrigin-Name: 5d506743f541b022cde04a9606baa4680cdfd70b
2014-03-24 09:34:58 +00:00
drh 83553eefaf Remove a pointless OP_Once operation in ORDER BY clauses with LIMIT.
FossilOrigin-Name: e6c59d23316c83b318b1a94d9b28a5d321737fa5
2014-03-24 02:20:53 +00:00
drh 70f624c3a9 Further enhancements to geneverated VDBE code for ORDER BY.
FossilOrigin-Name: e7188fad87ec82d36a39b80ccaf8006bf45a9bcd
2014-03-24 01:43:50 +00:00
drh d345a66428 merge fixes from trunk
FossilOrigin-Name: faf7f9caf526ab33a6fdb9c89b45a0483510db21
2014-03-23 18:47:00 +00:00
drh f45f2326a2 Use only a single OP_MakeRecord instead of two when constructing entries
to go onto a sorter.

FossilOrigin-Name: d696cdedacd39075aa7fc407ab7c7e50f01d9f39
2014-03-23 17:45:03 +00:00
8 changed files with 466 additions and 181 deletions
+11 -11
View File
@@ -1,5 +1,5 @@
C Include\ssqlite3rtree.h\sin\sthe\stsrc/\spile\sof\ssource\sfiles\sduring\ntarget_source\sin\sthe\smain.mk\smakefile.
D 2014-05-07T21:16:56.524
C Merge\srecent\strunk\schanges\sinto\sthe\sorderby-planning\sbranch.
D 2014-05-09T17:17:37.740
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in dd2b1aba364ff9b05de41086f74407f285c57670
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -220,7 +220,7 @@ F src/printf.c e5a0005f8b3de21f85da6a709d2fbee76775bf4b
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
F src/resolve.c 273d5f47c4e2c05b2d3d2bffeda939551ab59e66
F src/rowset.c a9c9aae3234b44a6d7c6f5a3cadf90dce1e627be
F src/select.c 089c4d46f067a5cccae93524c6377f981ba99bd9
F src/select.c a5ed3fdc82ebab5b9b095ea1971515a7f8a303d2
F src/shell.c 2afe7a7154e97be0c74c5feacf09626bda8493be
F src/sqlite.h.in 564fc23db33870b5096b20d72df7491ce0b8b74f
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
@@ -281,14 +281,14 @@ F src/update.c 5b3e74a03b3811e586b4f2b4cbd7c49f01c93115
F src/utf.c 6dc9ec9f1b3db43ae8ba0365377f11df1ee4c01c
F src/util.c 2b5fb283a190aacdb286f7835a447c45b345b83c
F src/vacuum.c 3728d74919d4fb1356f9e9a13e27773db60b7179
F src/vdbe.c 7f359193bf2366cc914a9ece093ebf284e56acdc
F src/vdbe.c b3510cc71f706beffc66e2aa4bbda54bcd5e9668
F src/vdbe.h 394464909ed682334aa3d5831aae0c2fe2abef94
F src/vdbeInt.h e6d83e5bfd62fc6685ba1ed6153f7099f82de9f7
F src/vdbeapi.c 0ed6053f947edd0b30f64ce5aeb811872a3450a4
F src/vdbeaux.c e493f38758c4b8f4ca2007cf6a700bd405d192f3
F src/vdbeaux.c c9a8c917776c941af99285594d5c30d99e21c99a
F src/vdbeblob.c 9205ce9d3b064d9600f8418a897fc88b5687d9ac
F src/vdbemem.c 6fc77594c60f6155404f3f8d71bf36d1fdeb4447
F src/vdbesort.c 4abb7c0f8f19b7d7d82f4558d5da1a30fdf9ea38
F src/vdbesort.c 6bcf73fb160ee5bb8ce8a8ec61fda268b081dbb7
F src/vdbetrace.c 6f52bc0c51e144b7efdcfb2a8f771167a8816767
F src/vtab.c 21b932841e51ebd7d075e2d0ad1415dce8d2d5fd
F src/wal.c 76e7fc6de229bea8b30bb2539110f03a494dc3a8
@@ -824,7 +824,7 @@ F test/skipscan1.test bed8cbe9d554c8c27afb6c88500f704c86a9196f
F test/skipscan2.test d77f79cdbba25f0f6f35298136cff21a7d7a553a
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
F test/softheap1.test 40562fe6cac6d9827b7b42b86d45aedf12c15e24
F test/sort.test 0e4456e729e5a92a625907c63dcdedfbe72c5dc5
F test/sort.test cb76a6e9db897b6871ef4dbc206ebc6dbc033bf4
F test/speed1.test f2974a91d79f58507ada01864c0e323093065452
F test/speed1p.explain d841e650a04728b39e6740296b852dccdca9b2cb
F test/speed1p.test b180e98609c7677382cf618c0ec9b69f789033a8
@@ -853,7 +853,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
F test/temptrigger.test 8ec228b0db5d7ebc4ee9b458fc28cb9e7873f5e1
F test/tester.tcl f31bea1483ea1d39620f982130026e76f872d744
F test/tester.tcl bc0889a2f86d9c17307992ca1e70391794780265
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -1170,7 +1170,7 @@ F tool/vdbe_profile.tcl 67746953071a9f8f2f668b73fe899074e2c6d8c1
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
F tool/win/sqlite.vsix a94fb9b1b1ef06efc2898975cdfcfa9643731f5e
P 68766f837491cb89c2103f2627eb9e23ab326a68
R 1b75d6e5e9a19ccc14f8e59e21c749f6
P 3300d62dcbe74842cf86ca436959fe4e77a89f84 116bed5af664899a73b46dca528ac0c021fc50c3
R 5413162d0868a0f16aff12e41234e8bf
U drh
Z 3528e73d8f87faaf66e6658213b6d573
Z d2a1dc17f4e656ec9e031956ee80b95a
+1 -1
View File
@@ -1 +1 @@
116bed5af664899a73b46dca528ac0c021fc50c3
8d8609d6c619e02e3e832e3b2256b8dccb8c21de
+89 -60
View File
@@ -455,28 +455,43 @@ static KeyInfo *keyInfoFromExprList(
);
/*
** Insert code into "v" that will push the record in register regData
** into the sorter.
** Generate code that will push the record in registers regData
** through regData+nData-1 onto the sorter.
*/
static void pushOntoSorter(
Parse *pParse, /* Parser context */
SortCtx *pSort, /* Information about the ORDER BY clause */
Select *pSelect, /* The whole SELECT statement */
int regData /* Register holding data to be sorted */
int regData, /* First register holding data to be sorted */
int nData, /* Number of elements in the data array */
int nPrefixReg /* No. of reg prior to regData available for use */
){
Vdbe *v = pParse->pVdbe;
int nExpr = pSort->pOrderBy->nExpr;
int regRecord = ++pParse->nMem;
int regBase = pParse->nMem+1;
int nOBSat = pSort->nOBSat;
int op;
Vdbe *v = pParse->pVdbe; /* Stmt under construction */
int bSeq = ((pSort->sortFlags & SORTFLAG_UseSorter)==0);
int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
int regBase; /* Regs for sorter record */
int regRecord = ++pParse->nMem; /* Assembled sorter record */
int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
int op; /* Opcode to add sorter record to sorter */
pParse->nMem += nExpr+2; /* nExpr+2 registers allocated at regBase */
sqlite3ExprCacheClear(pParse);
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, 0);
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nExpr+2-nOBSat,regRecord);
assert( bSeq==0 || bSeq==1 );
if( nPrefixReg ){
assert( nPrefixReg==nExpr+bSeq );
regBase = regData - nExpr - bSeq;
}else{
regBase = pParse->nMem + 1;
pParse->nMem += nBase;
}
sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, SQLITE_ECEL_DUP);
if( bSeq ){
sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
}
if( nPrefixReg==0 ){
sqlite3VdbeAddOp3(v, OP_Move, regData, regBase+nExpr+bSeq, nData);
}
sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
if( nOBSat>0 ){
int regPrevKey; /* The first nOBSat columns of the previous row */
int addrFirst; /* Address of the OP_IfNot opcode */
@@ -487,12 +502,17 @@ static void pushOntoSorter(
regPrevKey = pParse->nMem+1;
pParse->nMem += pSort->nOBSat;
nKey = nExpr - pSort->nOBSat + 1;
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr); VdbeCoverage(v);
nKey = nExpr - pSort->nOBSat + bSeq;
if( bSeq ){
addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr);
}else{
addrFirst = sqlite3VdbeAddOp1(v, OP_SequenceTest, pSort->iECursor);
}
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
if( pParse->db->mallocFailed ) return;
pOp->p2 = nKey + 1;
pOp->p2 = nKey + nData;
pKI = pOp->p4.pKeyInfo;
memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
@@ -627,6 +647,7 @@ static void selectInnerLoop(
int eDest = pDest->eDest; /* How to dispose of results */
int iParm = pDest->iSDParm; /* First argument to disposal method */
int nResultCol; /* Number of result columns */
int nPrefixReg = 0; /* Number of extra registers before regResult */
assert( v );
assert( pEList!=0 );
@@ -642,6 +663,11 @@ static void selectInnerLoop(
nResultCol = pEList->nExpr;
if( pDest->iSdst==0 ){
if( pSort ){
nPrefixReg = pSort->pOrderBy->nExpr;
if( !(pSort->sortFlags & SORTFLAG_UseSorter) ) nPrefixReg++;
pParse->nMem += nPrefixReg;
}
pDest->iSdst = pParse->nMem+1;
pParse->nMem += nResultCol;
}else if( pDest->iSdst+nResultCol > pParse->nMem ){
@@ -758,10 +784,10 @@ static void selectInnerLoop(
case SRT_DistFifo:
case SRT_Table:
case SRT_EphemTab: {
int r1 = sqlite3GetTempReg(pParse);
int r1 = sqlite3GetTempRange(pParse, nPrefixReg+1);
testcase( eDest==SRT_Table );
testcase( eDest==SRT_EphemTab );
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
#ifndef SQLITE_OMIT_CTE
if( eDest==SRT_DistFifo ){
/* If the destination is DistFifo, then cursor (iParm+1) is open
@@ -776,7 +802,7 @@ static void selectInnerLoop(
}
#endif
if( pSort ){
pushOntoSorter(pParse, pSort, p, r1);
pushOntoSorter(pParse, pSort, p, r1+nPrefixReg, 1, nPrefixReg);
}else{
int r2 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
@@ -784,7 +810,7 @@ static void selectInnerLoop(
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3ReleaseTempReg(pParse, r2);
}
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1);
break;
}
@@ -802,7 +828,7 @@ static void selectInnerLoop(
** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */
pushOntoSorter(pParse, pSort, p, regResult);
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
}else{
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult,1,r1, &pDest->affSdst, 1);
@@ -828,7 +854,7 @@ static void selectInnerLoop(
case SRT_Mem: {
assert( nResultCol==1 );
if( pSort ){
pushOntoSorter(pParse, pSort, p, regResult);
pushOntoSorter(pParse, pSort, p, regResult, 1, nPrefixReg);
}else{
sqlite3ExprCodeMove(pParse, regResult, iParm, 1);
/* The LIMIT clause will jump out of the loop for us */
@@ -842,10 +868,7 @@ static void selectInnerLoop(
testcase( eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
if( pSort ){
int r1 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
pushOntoSorter(pParse, pSort, p, r1);
sqlite3ReleaseTempReg(pParse, r1);
pushOntoSorter(pParse, pSort, p, regResult, nResultCol, nPrefixReg);
}else if( eDest==SRT_Coroutine ){
sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}else{
@@ -1125,46 +1148,62 @@ static void generateSortTail(
int addr;
int addrOnce = 0;
int iTab;
int pseudoTab = 0;
ExprList *pOrderBy = pSort->pOrderBy;
int eDest = pDest->eDest;
int iParm = pDest->iSDParm;
int regRow;
int regRowid;
int nKey;
int iSortTab; /* Sorter cursor to read from */
int nSortData; /* Trailing values to read from sorter */
u8 p5; /* p5 parameter for 1st OP_Column */
int i;
int bSeq; /* True if sorter record includes seq. no. */
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
struct ExprList_item *aOutEx = p->pEList->a;
#endif
if( pSort->labelBkOut ){
sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrBreak);
sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
}
iTab = pSort->iECursor;
regRow = sqlite3GetTempReg(pParse);
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
pseudoTab = pParse->nTab++;
sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn);
regRowid = 0;
regRow = pDest->iSdst;
nSortData = nColumn;
}else{
regRowid = sqlite3GetTempReg(pParse);
regRow = sqlite3GetTempReg(pParse);
nSortData = 1;
}
nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
int regSortOut = ++pParse->nMem;
int ptab2 = pParse->nTab++;
sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, nKey+2);
iSortTab = pParse->nTab++;
if( pSort->labelBkOut ){
addrOnce = sqlite3CodeOnce(pParse); VdbeCoverage(v);
}
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
VdbeCoverage(v);
codeOffset(v, p->iOffset, addrContinue);
sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
sqlite3VdbeAddOp3(v, OP_Column, ptab2, nKey+1, regRow);
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
p5 = OPFLAG_CLEARCACHE;
bSeq = 0;
}else{
if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
codeOffset(v, p->iOffset, addrContinue);
sqlite3VdbeAddOp3(v, OP_Column, iTab, nKey+1, regRow);
iSortTab = iTab;
p5 = 0;
bSeq = 1;
}
for(i=0; i<nSortData; i++){
sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
if( i==0 ) sqlite3VdbeChangeP5(v, p5);
VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
case SRT_Table:
@@ -1193,17 +1232,9 @@ static void generateSortTail(
}
#endif
default: {
int i;
assert( eDest==SRT_Output || eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
testcase( eDest==SRT_Coroutine );
for(i=0; i<nColumn; i++){
assert( regRow!=pDest->iSdst+i );
sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iSdst+i);
if( i==0 ){
sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}
}
if( eDest==SRT_Output ){
sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn);
sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn);
@@ -1213,9 +1244,10 @@ static void generateSortTail(
break;
}
}
sqlite3ReleaseTempReg(pParse, regRow);
sqlite3ReleaseTempReg(pParse, regRowid);
if( regRowid ){
sqlite3ReleaseTempReg(pParse, regRow);
sqlite3ReleaseTempReg(pParse, regRowid);
}
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, addrContinue);
@@ -1226,9 +1258,6 @@ static void generateSortTail(
}
if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
sqlite3VdbeResolveLabel(v, addrBreak);
if( eDest==SRT_Output || eDest==SRT_Coroutine ){
sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
}
}
/*
@@ -4758,8 +4787,9 @@ int sqlite3Select(
sSort.iECursor = pParse->nTab++;
sSort.addrSortIndex =
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
sSort.iECursor, sSort.pOrderBy->nExpr+2, 0,
(char*)pKeyInfo, P4_KEYINFO);
sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0,
(char*)pKeyInfo, P4_KEYINFO
);
}else{
sSort.addrSortIndex = -1;
}
@@ -4890,7 +4920,7 @@ int sqlite3Select(
sNC.pSrcList = pTabList;
sNC.pAggInfo = &sAggInfo;
sAggInfo.mnReg = pParse->nMem+1;
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
sAggInfo.pGroupBy = pGroupBy;
sqlite3ExprAnalyzeAggList(&sNC, pEList);
sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
@@ -4983,8 +5013,8 @@ int sqlite3Select(
groupBySort = 1;
nGroupBy = pGroupBy->nExpr;
nCol = nGroupBy + 1;
j = nGroupBy+1;
nCol = nGroupBy;
j = nGroupBy;
for(i=0; i<sAggInfo.nColumn; i++){
if( sAggInfo.aCol[i].iSorterColumn>=j ){
nCol++;
@@ -4994,8 +5024,7 @@ int sqlite3Select(
regBase = sqlite3GetTempRange(pParse, nCol);
sqlite3ExprCacheClear(pParse);
sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy);
j = nGroupBy+1;
j = nGroupBy;
for(i=0; i<sAggInfo.nColumn; i++){
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
if( pCol->iSorterColumn>=j ){
+18
View File
@@ -3410,6 +3410,24 @@ case OP_SorterOpen: {
break;
}
/* Opcode: SequenceTest P1 P2 * * *
** Synopsis: if( cursor[P1].ctr++ ) pc = P2
**
** P1 is a sorter cursor. If the sequence counter is currently zero, jump
** to P2. Regardless of whether or not the jump is taken, increment the
** the sequence value.
*/
case OP_SequenceTest: {
VdbeCursor *pC;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
pC = p->apCsr[pOp->p1];
assert( pC->pSorter );
if( (pC->seqCount++)==0 ){
pc = pOp->p2 - 1;
}
break;
}
/* Opcode: OpenPseudo P1 P2 P3 * *
** Synopsis: P3 columns in r[P2]
**
+19 -21
View File
@@ -3137,10 +3137,14 @@ void sqlite3VdbeRecordUnpack(
** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
** in assert() statements to ensure that the optimized code in
** sqlite3VdbeRecordCompare() returns results with these two primitives.
**
** Return true if the result of comparison is equivalent to desiredResult.
** Return false if there is a disagreement.
*/
static int vdbeRecordCompareDebug(
int nKey1, const void *pKey1, /* Left key */
const UnpackedRecord *pPKey2 /* Right key */
const UnpackedRecord *pPKey2, /* Right key */
int desiredResult /* Correct answer */
){
u32 d1; /* Offset into aKey[] of next data element */
u32 idx1; /* Offset into aKey[] of next header element */
@@ -3202,7 +3206,7 @@ static int vdbeRecordCompareDebug(
if( pKeyInfo->aSortOrder[i] ){
rc = -rc; /* Invert the result for DESC sort order. */
}
return rc;
goto debugCompareEnd;
}
i++;
}while( idx1<szHdr1 && i<pPKey2->nField );
@@ -3216,7 +3220,15 @@ static int vdbeRecordCompareDebug(
/* rc==0 here means that one of the keys ran out of fields and
** all the fields up to that point were equal. Return the the default_rc
** value. */
return pPKey2->default_rc;
rc = pPKey2->default_rc;
debugCompareEnd:
if( desiredResult==0 && rc==0 ) return 1;
if( desiredResult<0 && rc<0 ) return 1;
if( desiredResult>0 && rc>0 ) return 1;
if( CORRUPT_DB ) return 1;
if( pKeyInfo->db->mallocFailed ) return 1;
return 0;
}
#endif
@@ -3564,11 +3576,7 @@ int sqlite3VdbeRecordCompare(
if( pKeyInfo->aSortOrder[i] ){
rc = -rc;
}
assert( CORRUPT_DB
|| (rc<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|| (rc>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|| pKeyInfo->db->mallocFailed
);
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
assert( mem1.zMalloc==0 ); /* See comment below */
return rc;
}
@@ -3587,9 +3595,7 @@ int sqlite3VdbeRecordCompare(
/* rc==0 here means that one or both of the keys ran out of fields and
** all the fields up to that point were equal. Return the the default_rc
** value. */
assert( CORRUPT_DB
|| pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)
);
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc) );
return pPKey2->default_rc;
}
@@ -3686,11 +3692,7 @@ static int vdbeRecordCompareInt(
res = pPKey2->default_rc;
}
assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|| CORRUPT_DB
);
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
return res;
}
@@ -3750,11 +3752,7 @@ static int vdbeRecordCompareString(
}
}
assert( (res==0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)==0)
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|| CORRUPT_DB
);
assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
return res;
}
+303 -88
View File
@@ -1,5 +1,5 @@
/*
** 2011 July 9
** 2011-07-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -10,9 +10,55 @@
**
*************************************************************************
** This file contains code for the VdbeSorter object, used in concert with
** a VdbeCursor to sort large numbers of keys (as may be required, for
** example, by CREATE INDEX statements on tables too large to fit in main
** memory).
** a VdbeCursor to sort large numbers of keys for CREATE TABLE statements
** or by SELECT statements with ORDER BY clauses that cannot be satisfied
** using indexes and without LIMIT clauses.
**
** The VdbeSorter object implements a external merge sort
** algorithm that is efficient even if the aggregate size of
** the elements being sorted exceeds the available memory.
**
** Here is the (internal, non-API) interface between this module and the
** rest of the SQLite system:
**
** sqlite3VdbeSorterInit() Create a new VdbeSorter object.
**
** sqlite3VdbeSorterWrite() Add a single new row to the VdbeSorter
** object. The row is a binary blob in the
** OP_MakeRecord format that contains both
** the ORDER BY key columns and result columns
** in the case of a SELECT w/ ORDER BY, or
** the complete record for an index entry
** in the case of a CREATE INDEX.
**
** sqlite3VdbeSorterRewind() Sort all content previously added.
** Position the read cursor on the
** first sorted element.
**
** sqlite3VdbeSorterNext() Advance the read cursor to the next sorted
** element.
**
** sqlite3VdbeSorterRowkey() Return the complete binary blob for the
** row currently under the read cursor.
**
** sqlite3VdbeSorterCompare() Compare the binary blob for the row
** currently under the read cursor against
** another binary blob X and report if
** X is strictly less than the read cursor.
** Used to enforce uniqueness in a
** CREATE UNIQUE INDEX statement.
**
** sqlite3VdbeSorterClose() Close the VdbeSorter object and reclaim
** all resources.
**
** sqlite3VdbeSorterReset() Refurbish the VdbeSorter for reuse. This
** is like Close() followed by Init() only
** much faster.
**
** The interfaces above must be called in a particular order. Write() can
** only occur in between Init()/Reset() and Rewind(). Next(), Rowkey(), and
** Compare() can only occur in between Rewind() and Close()/Reset().
**
*/
#include "sqliteInt.h"
@@ -105,6 +151,9 @@ struct VdbeSorter {
sqlite3_file *pTemp1; /* PMA file 1 */
SorterRecord *pRecord; /* Head of in-memory record list */
UnpackedRecord *pUnpacked; /* Used to unpack keys */
u8* aMemory; /* Block to allocate records from */
int iMemory; /* Offset of free space in aMemory */
int nMemory; /* Current size of allocation at aMemory */
};
/*
@@ -121,6 +170,7 @@ struct VdbeSorterIter {
u8 *aKey; /* Pointer to current key */
u8 *aBuffer; /* Current read buffer */
int nBuffer; /* Size of read buffer in bytes */
u8 *aMap; /* Pointer to mapping of pFile */
};
/*
@@ -141,15 +191,37 @@ struct FileWriter {
/*
** A structure to store a single record. All in-memory records are connected
** together into a linked list headed at VdbeSorter.pRecord using the
** SorterRecord.pNext pointer.
** together into a linked list headed at VdbeSorter.pRecord.
**
** How the linked list is connected depends on how memory is being managed
** by this module. If using a separate allocation for each in-memory record
** (VdbeSorter.aMemory==0), then the list is always connected using the
** SorterRecord.u.pNext pointers.
**
** Or, if using the single large allocation method (VdbeSorter.aMemory!=0),
** then while records are being accumulated the list is linked using the
** SorterRecord.u.iNext offset. This is because the aMemory[] array may
** be sqlite3Realloc()ed while records are being accumulated. Once the VM
** has finished passing records to the sorter, or when the in-memory buffer
** is full, the list is sorted. As part of the sorting process, it is
** converted to use the SorterRecord.u.pNext pointers. See function
** vdbeSorterSort() for details.
*/
struct SorterRecord {
void *pVal;
int nVal;
SorterRecord *pNext;
union {
SorterRecord *pNext; /* Pointer to next record in list */
int iNext; /* Offset within aMemory of next record */
} u;
};
/* Return a pointer to the buffer containing the record data for SorterRecord
** object p. Should be used as if:
**
** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; }
*/
#define SRVAL(p) ((void*)((SorterRecord*)(p) + 1))
/* Minimum allowable value for the VdbeSorter.nWorking variable */
#define SORTER_MIN_WORKING 10
@@ -163,6 +235,7 @@ struct SorterRecord {
static void vdbeSorterIterZero(sqlite3 *db, VdbeSorterIter *pIter){
sqlite3DbFree(db, pIter->aAlloc);
sqlite3DbFree(db, pIter->aBuffer);
if( pIter->aMap ) sqlite3OsUnfetch(pIter->pFile, 0, pIter->aMap);
memset(pIter, 0, sizeof(VdbeSorterIter));
}
@@ -183,6 +256,13 @@ static int vdbeSorterIterRead(
){
int iBuf; /* Offset within buffer to read from */
int nAvail; /* Bytes of data available in buffer */
if( p->aMap ){
*ppOut = &p->aMap[p->iReadOff];
p->iReadOff += nByte;
return SQLITE_OK;
}
assert( p->aBuffer );
/* If there is no more data to be read from the buffer, read the next
@@ -264,18 +344,22 @@ static int vdbeSorterIterRead(
static int vdbeSorterIterVarint(sqlite3 *db, VdbeSorterIter *p, u64 *pnOut){
int iBuf;
iBuf = p->iReadOff % p->nBuffer;
if( iBuf && (p->nBuffer-iBuf)>=9 ){
p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut);
if( p->aMap ){
p->iReadOff += sqlite3GetVarint(&p->aMap[p->iReadOff], pnOut);
}else{
u8 aVarint[16], *a;
int i = 0, rc;
do{
rc = vdbeSorterIterRead(db, p, 1, &a);
if( rc ) return rc;
aVarint[(i++)&0xf] = a[0];
}while( (a[0]&0x80)!=0 );
sqlite3GetVarint(aVarint, pnOut);
iBuf = p->iReadOff % p->nBuffer;
if( iBuf && (p->nBuffer-iBuf)>=9 ){
p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut);
}else{
u8 aVarint[16], *a;
int i = 0, rc;
do{
rc = vdbeSorterIterRead(db, p, 1, &a);
if( rc ) return rc;
aVarint[(i++)&0xf] = a[0];
}while( (a[0]&0x80)!=0 );
sqlite3GetVarint(aVarint, pnOut);
}
}
return SQLITE_OK;
@@ -323,6 +407,7 @@ static int vdbeSorterIterInit(
){
int rc = SQLITE_OK;
int nBuf;
void *pMap;
nBuf = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
@@ -333,33 +418,41 @@ static int vdbeSorterIterInit(
pIter->iReadOff = iStart;
pIter->nAlloc = 128;
pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc);
pIter->nBuffer = nBuf;
pIter->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf);
if( !pIter->aBuffer ){
rc = SQLITE_NOMEM;
/* See if this PMA can be read using xFetch. */
rc = sqlite3OsFetch(pIter->pFile, 0, pSorter->iWriteOff, &pMap);
if( rc!=SQLITE_OK ) return rc;
if( pMap ){
pIter->aMap = (u8*)pMap;
}else{
int iBuf;
pIter->nBuffer = nBuf;
pIter->aBuffer = (u8 *)sqlite3DbMallocRaw(db, nBuf);
iBuf = iStart % nBuf;
if( iBuf ){
int nRead = nBuf - iBuf;
if( (iStart + nRead) > pSorter->iWriteOff ){
nRead = (int)(pSorter->iWriteOff - iStart);
if( !pIter->aBuffer ){
rc = SQLITE_NOMEM;
}else{
int iBuf;
iBuf = iStart % nBuf;
if( iBuf ){
int nRead = nBuf - iBuf;
if( (iStart + nRead) > pSorter->iWriteOff ){
nRead = (int)(pSorter->iWriteOff - iStart);
}
rc = sqlite3OsRead(
pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart
);
assert( rc!=SQLITE_IOERR_SHORT_READ );
}
rc = sqlite3OsRead(
pSorter->pTemp1, &pIter->aBuffer[iBuf], nRead, iStart
);
assert( rc!=SQLITE_IOERR_SHORT_READ );
}
}
if( rc==SQLITE_OK ){
u64 nByte; /* Size of PMA in bytes */
pIter->iEof = pSorter->iWriteOff;
rc = vdbeSorterIterVarint(db, pIter, &nByte);
pIter->iEof = pIter->iReadOff + nByte;
*pnByte += nByte;
}
if( rc==SQLITE_OK ){
u64 nByte; /* Size of PMA in bytes */
pIter->iEof = pSorter->iWriteOff;
rc = vdbeSorterIterVarint(db, pIter, &nByte);
pIter->iEof = pIter->iReadOff + nByte;
*pnByte += nByte;
}
if( rc==SQLITE_OK ){
@@ -480,6 +573,7 @@ int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d);
if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM;
assert( pSorter->pUnpacked==(UnpackedRecord *)d );
pSorter->pUnpacked->nField = pCsr->pKeyInfo->nField;
if( !sqlite3TempInMemory(db) ){
pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
@@ -487,6 +581,16 @@ int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
mxCache = db->aDb[0].pSchema->cache_size;
if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
pSorter->mxPmaSize = mxCache * pgsz;
/* If the application is using memsys3 or memsys5, use a separate
** allocation for each sort-key in memory. Otherwise, use a single big
** allocation at pSorter->aMemory for all sort-keys. */
if( sqlite3GlobalConfig.pHeap==0 ){
assert( pSorter->iMemory==0 );
pSorter->nMemory = pgsz;
pSorter->aMemory = (u8*)sqlite3Malloc(pSorter->nMemory);
if( !pSorter->aMemory ) return SQLITE_NOMEM;
}
}
return SQLITE_OK;
@@ -499,7 +603,7 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
SorterRecord *p;
SorterRecord *pNext;
for(p=pRecord; p; p=pNext){
pNext = p->pNext;
pNext = p->u.pNext;
sqlite3DbFree(db, p);
}
}
@@ -520,7 +624,9 @@ void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
sqlite3OsCloseFree(pSorter->pTemp1);
pSorter->pTemp1 = 0;
}
vdbeSorterRecordFree(db, pSorter->pRecord);
if( pSorter->aMemory==0 ){
vdbeSorterRecordFree(db, pSorter->pRecord);
}
pSorter->pRecord = 0;
pSorter->iWriteOff = 0;
pSorter->iReadOff = 0;
@@ -528,6 +634,7 @@ void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
pSorter->nTree = 0;
pSorter->nPMA = 0;
pSorter->aTree = 0;
pSorter->iMemory = 0;
}
@@ -539,6 +646,7 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
if( pSorter ){
sqlite3VdbeSorterReset(db, pSorter);
sqlite3DbFree(db, pSorter->pUnpacked);
sqlite3DbFree(db, pSorter->aMemory);
sqlite3DbFree(db, pSorter);
pCsr->pSorter = 0;
}
@@ -550,17 +658,25 @@ void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
** Otherwise, set *ppFile to 0 and return an SQLite error code.
*/
static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
int dummy;
return sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
int rc;
rc = sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
SQLITE_OPEN_TEMP_JOURNAL |
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &dummy
SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &rc
);
if( rc==SQLITE_OK ){
i64 max = SQLITE_MAX_MMAP_SIZE;
sqlite3OsFileControlHint( *ppFile, SQLITE_FCNTL_MMAP_SIZE, (void*)&max);
}
return rc;
}
/*
** Merge the two sorted lists p1 and p2 into a single list.
** Set *ppOut to the head of the new list.
**
** In cases where key values are equal, keys from list p1 are considered
** to be smaller than list p2.
*/
static void vdbeSorterMerge(
const VdbeCursor *pCsr, /* For pKeyInfo */
@@ -570,22 +686,22 @@ static void vdbeSorterMerge(
){
SorterRecord *pFinal = 0;
SorterRecord **pp = &pFinal;
void *pVal2 = p2 ? p2->pVal : 0;
void *pVal2 = p2 ? SRVAL(p2) : 0;
while( p1 && p2 ){
int res;
vdbeSorterCompare(pCsr, 0, p1->pVal, p1->nVal, pVal2, p2->nVal, &res);
vdbeSorterCompare(pCsr, 0, SRVAL(p1), p1->nVal, pVal2, p2->nVal, &res);
if( res<=0 ){
*pp = p1;
pp = &p1->pNext;
p1 = p1->pNext;
pp = &p1->u.pNext;
p1 = p1->u.pNext;
pVal2 = 0;
}else{
*pp = p2;
pp = &p2->pNext;
p2 = p2->pNext;
pp = &p2->u.pNext;
p2 = p2->u.pNext;
if( p2==0 ) break;
pVal2 = p2->pVal;
pVal2 = SRVAL(p2);
}
}
*pp = p1 ? p1 : p2;
@@ -596,6 +712,11 @@ static void vdbeSorterMerge(
** Sort the linked list of records headed at pCsr->pRecord. Return SQLITE_OK
** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error
** occurs.
**
** The sort is required to be stable - if two elements compare as equal
** then the one added to the sorter first is considered the smaller.
** Currently, the list is sorted from newest to oldest - pSorter->pRecord
** points to the most recently added sort key.
*/
static int vdbeSorterSort(const VdbeCursor *pCsr){
int i;
@@ -610,8 +731,18 @@ static int vdbeSorterSort(const VdbeCursor *pCsr){
p = pSorter->pRecord;
while( p ){
SorterRecord *pNext = p->pNext;
p->pNext = 0;
SorterRecord *pNext;
if( pSorter->aMemory ){
if( (u8*)p==pSorter->aMemory ){
pNext = 0;
}else{
assert( p->u.iNext<pSorter->nMemory );
pNext = (SorterRecord*)&pSorter->aMemory[p->u.iNext];
}
}else{
pNext = p->u.pNext;
}
p->u.pNext = 0;
for(i=0; aSlot[i]; i++){
vdbeSorterMerge(pCsr, p, aSlot[i], &p);
aSlot[i] = 0;
@@ -716,6 +847,28 @@ static void fileWriterWriteVarint(FileWriter *p, u64 iVal){
fileWriterWrite(p, aByte, nByte);
}
#if SQLITE_MAX_MMAP_SIZE>0
/*
** The first argument is a file-handle open on a temporary file. The file
** is guaranteed to be nByte bytes or smaller in size. This function
** attempts to extend the file to nByte bytes in size and to ensure that
** the VFS has memory mapped it.
**
** Whether or not the file does end up memory mapped of course depends on
** the specific VFS implementation.
*/
static void vdbeSorterExtendFile(sqlite3_file *pFile, i64 nByte){
int rc = sqlite3OsTruncate(pFile, nByte);
if( rc==SQLITE_OK ){
void *p = 0;
sqlite3OsFetch(pFile, 0, nByte, &p);
sqlite3OsUnfetch(pFile, 0, p);
}
}
#else
# define vdbeSorterExtendFile(x,y)
#endif
/*
** Write the current contents of the in-memory linked-list to a PMA. Return
** SQLITE_OK if successful, or an SQLite error code otherwise.
@@ -751,6 +904,13 @@ static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){
assert( pSorter->nPMA==0 );
}
/* Try to get the file to memory map */
if( rc==SQLITE_OK ){
vdbeSorterExtendFile(
pSorter->pTemp1, pSorter->iWriteOff + pSorter->nInMemory + 9
);
}
if( rc==SQLITE_OK ){
SorterRecord *p;
SorterRecord *pNext = 0;
@@ -759,15 +919,17 @@ static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){
pSorter->nPMA++;
fileWriterWriteVarint(&writer, pSorter->nInMemory);
for(p=pSorter->pRecord; p; p=pNext){
pNext = p->pNext;
pNext = p->u.pNext;
fileWriterWriteVarint(&writer, p->nVal);
fileWriterWrite(&writer, p->pVal, p->nVal);
sqlite3DbFree(db, p);
fileWriterWrite(&writer, SRVAL(p), p->nVal);
if( pSorter->aMemory==0 ) sqlite3DbFree(db, p);
}
pSorter->pRecord = p;
rc = fileWriterFinish(db, &writer, &pSorter->iWriteOff);
}
if( pSorter->aMemory ) pSorter->pRecord = 0;
assert( pSorter->pRecord==0 || rc!=SQLITE_OK );
return rc;
}
@@ -776,29 +938,28 @@ static int vdbeSorterListToPMA(sqlite3 *db, const VdbeCursor *pCsr){
*/
int sqlite3VdbeSorterWrite(
sqlite3 *db, /* Database handle */
const VdbeCursor *pCsr, /* Sorter cursor */
const VdbeCursor *pCsr, /* Sorter cursor */
Mem *pVal /* Memory cell containing record */
){
VdbeSorter *pSorter = pCsr->pSorter;
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
int bFlush; /* True to flush contents of memory to PMA */
int nReq; /* Bytes of memory required */
int nPMA; /* Bytes of PMA space required */
assert( pSorter );
pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n;
pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord));
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
pNew->pVal = (void *)&pNew[1];
memcpy(pNew->pVal, pVal->z, pVal->n);
pNew->nVal = pVal->n;
pNew->pNext = pSorter->pRecord;
pSorter->pRecord = pNew;
}
/* See if the contents of the sorter should now be written out. They
** are written out when either of the following are true:
/* Figure out whether or not the current contents of memory should be
** flushed to a PMA before continuing. If so, do so.
**
** If using the single large allocation mode (pSorter->aMemory!=0), then
** flush the contents of memory to a new PMA if (a) at least one value is
** already in memory and (b) the new value will not fit in memory.
**
** Or, if using separate allocations for each record, flush the contents
** of memory to a PMA if either of the following are true:
**
** * The total memory allocated for the in-memory list is greater
** than (page-size * cache-size), or
@@ -806,20 +967,64 @@ int sqlite3VdbeSorterWrite(
** * The total memory allocated for the in-memory list is greater
** than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
*/
if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && (
(pSorter->nInMemory>pSorter->mxPmaSize)
|| (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
)){
nReq = pVal->n + sizeof(SorterRecord);
nPMA = pVal->n + sqlite3VarintLen(pVal->n);
if( pSorter->aMemory ){
bFlush = pSorter->iMemory && (pSorter->iMemory+nReq) > pSorter->mxPmaSize;
}else{
bFlush = (
(pSorter->nInMemory > pSorter->mxPmaSize)
|| (pSorter->nInMemory > pSorter->mnPmaSize && sqlite3HeapNearlyFull())
);
}
if( bFlush ){
#ifdef SQLITE_DEBUG
i64 nExpect = pSorter->iWriteOff
+ sqlite3VarintLen(pSorter->nInMemory)
+ pSorter->nInMemory;
+ sqlite3VarintLen(pSorter->nInMemory)
+ pSorter->nInMemory;
#endif
rc = vdbeSorterListToPMA(db, pCsr);
pSorter->nInMemory = 0;
pSorter->iMemory = 0;
assert( rc!=SQLITE_OK || (nExpect==pSorter->iWriteOff) );
assert( rc!=SQLITE_OK || pSorter->pRecord==0 );
}
pSorter->nInMemory += nPMA;
if( pSorter->aMemory ){
int nMin = pSorter->iMemory + nReq;
if( nMin>pSorter->nMemory ){
u8 *aNew;
int nNew = pSorter->nMemory * 2;
while( nNew < nMin ) nNew = nNew*2;
if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize;
if( nNew < nMin ) nNew = nMin;
aNew = sqlite3Realloc(pSorter->aMemory, nNew);
if( !aNew ) return SQLITE_NOMEM;
pSorter->pRecord = (SorterRecord*)
(aNew + ((u8*)pSorter->pRecord - pSorter->aMemory));
pSorter->aMemory = aNew;
pSorter->nMemory = nNew;
}
pNew = (SorterRecord*)&pSorter->aMemory[pSorter->iMemory];
pSorter->iMemory += ROUND8(nReq);
pNew->u.iNext = (u8*)(pSorter->pRecord) - pSorter->aMemory;
}else{
pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n+sizeof(SorterRecord));
if( pNew==0 ){
return SQLITE_NOMEM;
}
pNew->u.pNext = pSorter->pRecord;
}
memcpy(SRVAL(pNew), pVal->z, pVal->n);
pNew->nVal = pVal->n;
pSorter->pRecord = pNew;
return rc;
}
@@ -836,7 +1041,7 @@ static int vdbeSorterInitMerge(
int i; /* Used to iterator through aIter[] */
i64 nByte = 0; /* Total bytes in all opened PMAs */
/* Initialize the iterators. */
/* Initialize the iterators. Iterator 0 contains the oldest data. */
for(i=0; i<SORTER_MAX_MERGE_COUNT; i++){
VdbeSorterIter *pIter = &pSorter->aIter[i];
rc = vdbeSorterIterInit(db, pSorter, pSorter->iReadOff, pIter, &nByte);
@@ -893,7 +1098,7 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
pSorter->aTree = (int *)&pSorter->aIter[N];
pSorter->nTree = N;
do {
while(1){
int iNew; /* Index of new, merged, PMA */
for(iNew=0;
@@ -925,6 +1130,9 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
if( pTemp2==0 ){
assert( iWrite2==0 );
rc = vdbeSorterOpenTempFile(db, &pTemp2);
if( rc==SQLITE_OK ){
vdbeSorterExtendFile(pTemp2, pSorter->iWriteOff);
}
}
if( rc==SQLITE_OK ){
@@ -943,6 +1151,7 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
if( rc==SQLITE_OK ) rc = rc2;
}
}
if( rc ) break;
if( pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
break;
@@ -955,7 +1164,7 @@ int sqlite3VdbeSorterRewind(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
pSorter->iReadOff = 0;
iWrite2 = 0;
}
}while( rc==SQLITE_OK );
}
if( pTemp2 ){
sqlite3OsCloseFree(pTemp2);
@@ -1008,8 +1217,13 @@ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
** set aTree[i] to its index and update pIter1. If vdbeSorterCompare()
** was actually called above, then pSorter->pUnpacked now contains
** a value equivalent to pIter2. So set pKey2 to NULL to prevent
** vdbeSorterCompare() from decoding pIter2 again. */
if( iRes<=0 ){
** vdbeSorterCompare() from decoding pIter2 again.
**
** If the two values were equal, then the value from the oldest
** PMA should be considered smaller. The VdbeSorter.aIter[] array
** is sorted from oldest to newest, so pIter1 contains older values
** than pIter2 iff (pIter1<pIter2). */
if( iRes<0 || (iRes==0 && pIter1<pIter2) ){
pSorter->aTree[i] = (int)(pIter1 - pSorter->aIter);
pIter2 = &pSorter->aIter[ pSorter->aTree[i ^ 0x0001] ];
pKey2 = pIter2->aKey;
@@ -1018,15 +1232,16 @@ int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
pSorter->aTree[i] = (int)(pIter2 - pSorter->aIter);
pIter1 = &pSorter->aIter[ pSorter->aTree[i ^ 0x0001] ];
}
}
*pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
}
}else{
SorterRecord *pFree = pSorter->pRecord;
pSorter->pRecord = pFree->pNext;
pFree->pNext = 0;
vdbeSorterRecordFree(db, pFree);
pSorter->pRecord = pFree->u.pNext;
pFree->u.pNext = 0;
if( pSorter->aMemory==0 ){
vdbeSorterRecordFree(db, pFree);
}
*pbEof = !pSorter->pRecord;
rc = SQLITE_OK;
}
@@ -1049,7 +1264,7 @@ static void *vdbeSorterRowkey(
pKey = pIter->aKey;
}else{
*pnKey = pSorter->pRecord->nVal;
pKey = pSorter->pRecord->pVal;
pKey = SRVAL(pSorter->pRecord);
}
return pKey;
}
+23
View File
@@ -464,4 +464,27 @@ do_test sort-12.1 {
}
} {1 2 xxx 1 3 yyy 1 1 zzz}
#-------------------------------------------------------------------------
# Check that the sorter in vdbesort.c sorts in a stable fashion.
#
do_execsql_test sort-13.0 {
CREATE TABLE t10(a, b);
}
do_test sort-13.1 {
db transaction {
for {set i 0} {$i < 100000} {incr i} {
execsql { INSERT INTO t10 VALUES( $i/10, $i%10 ) }
}
}
} {}
do_execsql_test sort-13.2 {
SELECT a, b FROM t10 ORDER BY a;
} [db eval {SELECT a, b FROM t10 ORDER BY a, b}]
do_execsql_test sort-13.3 {
PRAGMA cache_size = 5;
SELECT a, b FROM t10 ORDER BY a;
} [db eval {SELECT a, b FROM t10 ORDER BY a, b}]
finish_test
+2
View File
@@ -1076,6 +1076,7 @@ proc explain_i {sql {db db}} {
foreach opcode {
Seek SeekGe SeekGt SeekLe SeekLt NotFound Last Rewind
NoConflict Next Prev VNext VPrev VFilter
SorterSort SorterNext
} {
set color($opcode) $B
}
@@ -1098,6 +1099,7 @@ proc explain_i {sql {db db}} {
if {$opcode=="Next" || $opcode=="Prev"
|| $opcode=="VNext" || $opcode=="VPrev"
|| $opcode=="SorterNext"
} {
for {set i $p2} {$i<$addr} {incr i} {
incr x($i) 2