Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c91cbc7389 | |||
| 79211e194d | |||
| c7f6c148d9 | |||
| 7a0fd192cc | |||
| fb0d6e56d6 | |||
| f690c14280 | |||
| 0ff435a782 | |||
| 73e7d8b2bb | |||
| 7c8e9c78c8 | |||
| 6971952c65 | |||
| face087212 | |||
| bfa463696b | |||
| e6c7b09df6 | |||
| ab1dcc1a4b | |||
| 78d5843245 | |||
| dd23c6bfb4 | |||
| fd0a2f9756 | |||
| 3f802ebce2 | |||
| 83553eefaf | |||
| 70f624c3a9 | |||
| d345a66428 | |||
| f45f2326a2 |
@@ -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
@@ -1 +1 @@
|
||||
116bed5af664899a73b46dca528ac0c021fc50c3
|
||||
8d8609d6c619e02e3e832e3b2256b8dccb8c21de
|
||||
+89
-60
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user