mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
Get ORDER BY working for recursive queries.
FossilOrigin-Name: 37b343b01841b338954ddfa9b76d92aa50037aec
This commit is contained in:
16
manifest
16
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Add\snew\sSelectDest\scodes,\sSRT_Queue\sand\sSRT_DistQueue\sin\santicipation\sof\sadding\nORDER\sBY\ssupport\son\srecursive\squeries.\s\sFactor\sout\sthe\srecursive\squery\ncode\sgenerator\sinto\sa\sseparate\sprocedure.
|
C Get\sORDER\sBY\sworking\sfor\srecursive\squeries.
|
||||||
D 2014-01-22T13:35:53.476
|
D 2014-01-22T17:28:35.279
|
||||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||||
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
F Makefile.in 2ef13430cd359f7b361bb863504e227b25cc7f81
|
||||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||||
@@ -219,12 +219,12 @@ F src/printf.c 85d07756e45d7496d19439dcae3e6e9e0090f269
|
|||||||
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
F src/random.c d10c1f85b6709ca97278428fd5db5bbb9c74eece
|
||||||
F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6
|
F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6
|
||||||
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
|
||||||
F src/select.c 11c02c82a6f3cb8a491452fc7474a568a48c64ef
|
F src/select.c b5430b99c0339dcfe9d06a4c251548e650e386e0
|
||||||
F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca
|
F src/shell.c 24722d24d4ea8ca93db35e44db7308de786767ca
|
||||||
F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
|
F src/sqlite.h.in eed7f7d66a60daaa7b4a597dcd9bad87aad9611b
|
||||||
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
|
||||||
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
|
||||||
F src/sqliteInt.h 16c73326604e5603c3c10c97ac1e63473590d4ff
|
F src/sqliteInt.h 87a90ad4818ac5d68d3463eb7fe3ed96e5209b25
|
||||||
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
|
||||||
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
F src/status.c 7ac05a5c7017d0b9f0b4bcd701228b784f987158
|
||||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||||
@@ -1092,7 +1092,7 @@ F test/wild001.test bca33f499866f04c24510d74baf1e578d4e44b1c
|
|||||||
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
F test/win32heap.test ea19770974795cff26e11575e12d422dbd16893c
|
||||||
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
F test/win32lock.test 7a6bd73a5dcdee39b5bb93e92395e1773a194361
|
||||||
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
||||||
F test/with1.test cec63b56797a70842afa8929c241dfdb3d864283
|
F test/with1.test f4aa699a3712207e02f13945cd2b67d01a9d9f57
|
||||||
F test/with2.test 2fe78fcd8deef2a0f9cfc49bfc755911d0b3fd64
|
F test/with2.test 2fe78fcd8deef2a0f9cfc49bfc755911d0b3fd64
|
||||||
F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
|
F test/withM.test e97f2a8c506ab3ea9eab94e6f6072f6cc924c991
|
||||||
F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8
|
F test/without_rowid1.test aaa26da19d543cd8d3d2d0e686dfa255556c15c8
|
||||||
@@ -1152,7 +1152,7 @@ F tool/vdbe-compress.tcl 0cf56e9263a152b84da86e75a5c0cdcdb7a47891
|
|||||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||||
P cceacc0e79c4e54682daddf2056c6bb8e88d9484
|
P 3eb5f9f8d6ac1ee145cb4119087c516f66fe1456
|
||||||
R 3e53ba84b5d9e416a6e5f351b0fd4313
|
R a615c5dcec438a7db4f9b5a3e0983f82
|
||||||
U drh
|
U drh
|
||||||
Z aa06559c4dc503939fd80ca5aa152df9
|
Z 4ad026d26a172a85d439a21b0ddb47e4
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
3eb5f9f8d6ac1ee145cb4119087c516f66fe1456
|
37b343b01841b338954ddfa9b76d92aa50037aec
|
||||||
131
src/select.c
131
src/select.c
@@ -581,10 +581,8 @@ static void selectInnerLoop(
|
|||||||
pDest->iSdst = pParse->nMem+1;
|
pDest->iSdst = pParse->nMem+1;
|
||||||
pDest->nSdst = nResultCol;
|
pDest->nSdst = nResultCol;
|
||||||
pParse->nMem += nResultCol;
|
pParse->nMem += nResultCol;
|
||||||
if( eDest==SRT_Queue ) pParse->nMem++;
|
|
||||||
}else{
|
}else{
|
||||||
assert( pDest->nSdst==nResultCol );
|
assert( pDest->nSdst==nResultCol );
|
||||||
assert( eDest!=SRT_Queue );
|
|
||||||
}
|
}
|
||||||
regResult = pDest->iSdst;
|
regResult = pDest->iSdst;
|
||||||
if( srcTab>=0 ){
|
if( srcTab>=0 ){
|
||||||
@@ -664,30 +662,10 @@ static void selectInnerLoop(
|
|||||||
** table iParm.
|
** table iParm.
|
||||||
*/
|
*/
|
||||||
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
#ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||||
#ifndef SQLITE_OMIT_CTE
|
|
||||||
case SRT_Queue: {
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Sequence, iParm, regResult+nResultCol);
|
|
||||||
nResultCol++;
|
|
||||||
/* Fall through into SRT_Union */
|
|
||||||
}
|
|
||||||
case SRT_DistQueue:
|
|
||||||
#endif /* SQLITE_OMIT_CTE */
|
|
||||||
case SRT_Union: {
|
case SRT_Union: {
|
||||||
int r1;
|
int r1;
|
||||||
r1 = sqlite3GetTempReg(pParse);
|
r1 = sqlite3GetTempReg(pParse);
|
||||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
|
||||||
#ifndef SQLITE_OMIT_CTE
|
|
||||||
if( eDest==SRT_DistQueue ){
|
|
||||||
/* If the destination is DistQueue, then cursor (iParm+1) is open
|
|
||||||
** on a second ephemeral index that holds all values every previously
|
|
||||||
** added to the queue. Only add this new value if it has never before
|
|
||||||
** been added */
|
|
||||||
int addr = sqlite3VdbeCurrentAddr(v) + 3;
|
|
||||||
sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0);
|
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1);
|
|
||||||
assert( pOrderBy==0 );
|
|
||||||
}
|
|
||||||
#endif /* SQLITE_OMIT_CTE */
|
|
||||||
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||||
sqlite3ReleaseTempReg(pParse, r1);
|
sqlite3ReleaseTempReg(pParse, r1);
|
||||||
break;
|
break;
|
||||||
@@ -809,6 +787,51 @@ static void selectInnerLoop(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef SQLITE_OMIT_CTE
|
||||||
|
/* Write the results into a priority queue that is order according to
|
||||||
|
** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an
|
||||||
|
** index with pSO->nExpr+2 columns. Build a key using pSO for the first
|
||||||
|
** pSO->nExpr columns, then make sure all keys are unique by adding a
|
||||||
|
** final OP_Sequence column. The last column is the record as a blob.
|
||||||
|
*/
|
||||||
|
case SRT_DistQueue:
|
||||||
|
case SRT_Queue: {
|
||||||
|
int nKey;
|
||||||
|
int r1, r2, r3;
|
||||||
|
int addrTest = 0;
|
||||||
|
ExprList *pSO;
|
||||||
|
pSO = pDest->pOrderBy;
|
||||||
|
assert( pSO );
|
||||||
|
nKey = pSO->nExpr;
|
||||||
|
r1 = sqlite3GetTempReg(pParse);
|
||||||
|
r2 = sqlite3GetTempRange(pParse, nKey+2);
|
||||||
|
r3 = r2+nKey+1;
|
||||||
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3);
|
||||||
|
if( eDest==SRT_DistQueue ){
|
||||||
|
/* If the destination is DistQueue, then cursor (iParm+1) is open
|
||||||
|
** on a second ephemeral index that holds all values every previously
|
||||||
|
** added to the queue. Only add this new value if it has never before
|
||||||
|
** been added */
|
||||||
|
addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0, r3, 0);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3);
|
||||||
|
}
|
||||||
|
for(i=0; i<nKey; i++){
|
||||||
|
sqlite3VdbeAddOp2(v, OP_SCopy,
|
||||||
|
regResult + pSO->a[i].u.x.iOrderByCol - 1,
|
||||||
|
r2+i);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
|
||||||
|
if( addrTest ) sqlite3VdbeJumpHere(v, addrTest);
|
||||||
|
sqlite3ReleaseTempReg(pParse, r1);
|
||||||
|
sqlite3ReleaseTempRange(pParse, r2, nKey+2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif /* SQLITE_OMIT_CTE */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if !defined(SQLITE_OMIT_TRIGGER)
|
#if !defined(SQLITE_OMIT_TRIGGER)
|
||||||
/* Discard the results. This is used for SELECT statements inside
|
/* Discard the results. This is used for SELECT statements inside
|
||||||
** the body of a TRIGGER. The purpose of such selects is to call
|
** the body of a TRIGGER. The purpose of such selects is to call
|
||||||
@@ -897,7 +920,7 @@ int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
|
|||||||
** function is responsible for seeing that this structure is eventually
|
** function is responsible for seeing that this structure is eventually
|
||||||
** freed.
|
** freed.
|
||||||
*/
|
*/
|
||||||
static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
|
static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList, int nExtra){
|
||||||
int nExpr;
|
int nExpr;
|
||||||
KeyInfo *pInfo;
|
KeyInfo *pInfo;
|
||||||
struct ExprList_item *pItem;
|
struct ExprList_item *pItem;
|
||||||
@@ -905,7 +928,7 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
nExpr = pList->nExpr;
|
nExpr = pList->nExpr;
|
||||||
pInfo = sqlite3KeyInfoAlloc(db, nExpr, 1);
|
pInfo = sqlite3KeyInfoAlloc(db, nExpr+nExtra, 1);
|
||||||
if( pInfo ){
|
if( pInfo ){
|
||||||
assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
assert( sqlite3KeyInfoIsWriteable(pInfo) );
|
||||||
for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
|
for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
|
||||||
@@ -1713,9 +1736,9 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
|
|||||||
**
|
**
|
||||||
** The setup-query runs once to generate an initial set of rows that go
|
** The setup-query runs once to generate an initial set of rows that go
|
||||||
** into a Queue table. Rows are extracted from the Queue table one by
|
** into a Queue table. Rows are extracted from the Queue table one by
|
||||||
** one. Each row extracted from iQueue is output to pDest. Then the single
|
** one. Each row extracted from Queue is output to pDest. Then the single
|
||||||
** extracted row (now the iCurrent table) becomes the content of the
|
** extracted row (now in the iCurrent table) becomes the content of the
|
||||||
** recursive-table and recursive-query is run. The output of the recursive-query
|
** recursive-table for a recursive-query run. The output of the recursive-query
|
||||||
** is added back into the Queue table. Then another row is extracted from Queue
|
** is added back into the Queue table. Then another row is extracted from Queue
|
||||||
** and the iteration continues until the Queue table is empty.
|
** and the iteration continues until the Queue table is empty.
|
||||||
**
|
**
|
||||||
@@ -1754,18 +1777,17 @@ static void generateWithRecursiveQuery(
|
|||||||
SelectDest destQueue; /* SelectDest targetting the Queue table */
|
SelectDest destQueue; /* SelectDest targetting the Queue table */
|
||||||
int i; /* Loop counter */
|
int i; /* Loop counter */
|
||||||
int rc; /* Result code */
|
int rc; /* Result code */
|
||||||
|
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||||
|
|
||||||
/* Obtain authorization to do a recursive query */
|
/* Obtain authorization to do a recursive query */
|
||||||
if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
|
if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
|
||||||
addrBreak = sqlite3VdbeMakeLabel(v);
|
addrBreak = sqlite3VdbeMakeLabel(v);
|
||||||
addrCont = sqlite3VdbeMakeLabel(v);
|
|
||||||
|
|
||||||
|
|
||||||
/* Check that there is no ORDER BY or LIMIT clause. Neither of these
|
/* Check that there is no ORDER BY or LIMIT clause. Neither of these
|
||||||
** are currently supported on recursive queries.
|
** are currently supported on recursive queries.
|
||||||
*/
|
*/
|
||||||
assert( p->pOffset==0 || p->pLimit );
|
assert( p->pOffset==0 || p->pLimit );
|
||||||
if( p->pOrderBy || p->pLimit ){
|
if( /*p->pOrderBy ||*/ p->pLimit ){
|
||||||
sqlite3ErrorMsg(pParse, "%s in a recursive query",
|
sqlite3ErrorMsg(pParse, "%s in a recursive query",
|
||||||
p->pOrderBy ? "ORDER BY" : "LIMIT"
|
p->pOrderBy ? "ORDER BY" : "LIMIT"
|
||||||
);
|
);
|
||||||
@@ -1780,22 +1802,34 @@ static void generateWithRecursiveQuery(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate cursors for Queue and Distinct. The cursor number for
|
/* Detach the ORDER BY clause from the compound SELECT */
|
||||||
|
pOrderBy = p->pOrderBy;
|
||||||
|
p->pOrderBy = 0;
|
||||||
|
|
||||||
|
/* Allocate cursors numbers for Queue and Distinct. The cursor number for
|
||||||
** the Distinct table must be exactly one greater than Queue in order
|
** the Distinct table must be exactly one greater than Queue in order
|
||||||
** for the SRT_DistTable destination to work. */
|
** for the SRT_DistTable and SRT_DistQueue destinations to work. */
|
||||||
iQueue = pParse->nTab++;
|
iQueue = pParse->nTab++;
|
||||||
if( p->op==TK_UNION ){
|
if( p->op==TK_UNION ){
|
||||||
assert( SRT_Table+1==SRT_DistTable );
|
eDest = pOrderBy ? SRT_DistQueue : SRT_DistTable;
|
||||||
assert( SRT_Queue+1==SRT_DistQueue );
|
|
||||||
eDest++;
|
|
||||||
iDistinct = pParse->nTab++;
|
iDistinct = pParse->nTab++;
|
||||||
|
}else{
|
||||||
|
eDest = pOrderBy ? SRT_Queue : SRT_Table;
|
||||||
}
|
}
|
||||||
sqlite3SelectDestInit(&destQueue, eDest, iQueue);
|
sqlite3SelectDestInit(&destQueue, eDest, iQueue);
|
||||||
|
|
||||||
/* Allocate cursors for Current, Queue, and Distinct. */
|
/* Allocate cursors for Current, Queue, and Distinct. */
|
||||||
regCurrent = ++pParse->nMem;
|
regCurrent = ++pParse->nMem;
|
||||||
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol);
|
sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol);
|
||||||
|
if( pOrderBy ){
|
||||||
|
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 1);
|
||||||
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0,
|
||||||
|
(char*)pKeyInfo, P4_KEYINFO);
|
||||||
|
destQueue.pOrderBy = pOrderBy;
|
||||||
|
}else{
|
||||||
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol);
|
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol);
|
||||||
|
}
|
||||||
|
VdbeComment((v, "Queue table"));
|
||||||
if( iDistinct ){
|
if( iDistinct ){
|
||||||
p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0);
|
p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0);
|
||||||
p->selFlags |= SF_UsesEphemeral;
|
p->selFlags |= SF_UsesEphemeral;
|
||||||
@@ -1803,19 +1837,26 @@ static void generateWithRecursiveQuery(
|
|||||||
|
|
||||||
/* Store the results of the setup-query in Queue. */
|
/* Store the results of the setup-query in Queue. */
|
||||||
rc = sqlite3Select(pParse, pSetup, &destQueue);
|
rc = sqlite3Select(pParse, pSetup, &destQueue);
|
||||||
if( rc ) return;
|
if( rc ) goto end_of_recursive_query;
|
||||||
|
|
||||||
/* Find the next row in the Queue and output that row */
|
/* Find the next row in the Queue and output that row */
|
||||||
addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak);
|
addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak);
|
||||||
selectInnerLoop(pParse, p, p->pEList, iQueue,
|
|
||||||
0, 0, pDest, addrCont, addrBreak);
|
|
||||||
sqlite3VdbeResolveLabel(v, addrCont);
|
|
||||||
|
|
||||||
/* Transfer the next row in Queue over to Current */
|
/* Transfer the next row in Queue over to Current */
|
||||||
sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */
|
sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */
|
||||||
|
if( pOrderBy ){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, iQueue, pOrderBy->nExpr+1, regCurrent);
|
||||||
|
}else{
|
||||||
sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent);
|
sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent);
|
||||||
|
}
|
||||||
sqlite3VdbeAddOp1(v, OP_Delete, iQueue);
|
sqlite3VdbeAddOp1(v, OP_Delete, iQueue);
|
||||||
|
|
||||||
|
/* Output the single row in Current */
|
||||||
|
addrCont = sqlite3VdbeMakeLabel(v);
|
||||||
|
selectInnerLoop(pParse, p, p->pEList, iCurrent,
|
||||||
|
0, 0, pDest, addrCont, addrBreak);
|
||||||
|
sqlite3VdbeResolveLabel(v, addrCont);
|
||||||
|
|
||||||
/* Execute the recursive SELECT taking the single row in Current as
|
/* Execute the recursive SELECT taking the single row in Current as
|
||||||
** the value for the recursive-table. Store the results in the Queue.
|
** the value for the recursive-table. Store the results in the Queue.
|
||||||
*/
|
*/
|
||||||
@@ -1827,6 +1868,10 @@ static void generateWithRecursiveQuery(
|
|||||||
/* Keep running the loop until the Queue is empty */
|
/* Keep running the loop until the Queue is empty */
|
||||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
|
||||||
sqlite3VdbeResolveLabel(v, addrBreak);
|
sqlite3VdbeResolveLabel(v, addrBreak);
|
||||||
|
|
||||||
|
end_of_recursive_query:
|
||||||
|
p->pOrderBy = pOrderBy;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -4227,7 +4272,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|||||||
"argument");
|
"argument");
|
||||||
pFunc->iDistinct = -1;
|
pFunc->iDistinct = -1;
|
||||||
}else{
|
}else{
|
||||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
|
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0);
|
||||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||||
(char*)pKeyInfo, P4_KEYINFO);
|
(char*)pKeyInfo, P4_KEYINFO);
|
||||||
}
|
}
|
||||||
@@ -4636,7 +4681,7 @@ int sqlite3Select(
|
|||||||
*/
|
*/
|
||||||
if( pOrderBy ){
|
if( pOrderBy ){
|
||||||
KeyInfo *pKeyInfo;
|
KeyInfo *pKeyInfo;
|
||||||
pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
|
pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0);
|
||||||
pOrderBy->iECursor = pParse->nTab++;
|
pOrderBy->iECursor = pParse->nTab++;
|
||||||
p->addrOpenEphm[2] = addrSortIndex =
|
p->addrOpenEphm[2] = addrSortIndex =
|
||||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||||
@@ -4668,7 +4713,7 @@ int sqlite3Select(
|
|||||||
sDistinct.tabTnct = pParse->nTab++;
|
sDistinct.tabTnct = pParse->nTab++;
|
||||||
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||||
sDistinct.tabTnct, 0, 0,
|
sDistinct.tabTnct, 0, 0,
|
||||||
(char*)keyInfoFromExprList(pParse, p->pEList),
|
(char*)keyInfoFromExprList(pParse, p->pEList, 0),
|
||||||
P4_KEYINFO);
|
P4_KEYINFO);
|
||||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||||
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
||||||
@@ -4792,7 +4837,7 @@ int sqlite3Select(
|
|||||||
** will be converted into a Noop.
|
** will be converted into a Noop.
|
||||||
*/
|
*/
|
||||||
sAggInfo.sortingIdx = pParse->nTab++;
|
sAggInfo.sortingIdx = pParse->nTab++;
|
||||||
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
|
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0);
|
||||||
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
||||||
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
||||||
0, (char*)pKeyInfo, P4_KEYINFO);
|
0, (char*)pKeyInfo, P4_KEYINFO);
|
||||||
|
|||||||
@@ -2256,6 +2256,7 @@ struct SelectDest {
|
|||||||
int iSDParm; /* A parameter used by the eDest disposal method */
|
int iSDParm; /* A parameter used by the eDest disposal method */
|
||||||
int iSdst; /* Base register where results are written */
|
int iSdst; /* Base register where results are written */
|
||||||
int nSdst; /* Number of registers allocated */
|
int nSdst; /* Number of registers allocated */
|
||||||
|
ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -152,7 +152,44 @@ do_execsql_test 5.1 {
|
|||||||
do_catchsql_test 5.2 {
|
do_catchsql_test 5.2 {
|
||||||
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i ORDER BY 1)
|
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i ORDER BY 1)
|
||||||
SELECT x FROM i LIMIT 10;
|
SELECT x FROM i LIMIT 10;
|
||||||
} {1 {ORDER BY in a recursive query}}
|
} {0 {1 2 3 4 5 6 7 8 9 10}}
|
||||||
|
|
||||||
|
do_execsql_test 5.2.1 {
|
||||||
|
CREATE TABLE edge(xfrom, xto, seq, PRIMARY KEY(xfrom, xto)) WITHOUT ROWID;
|
||||||
|
INSERT INTO edge VALUES(0, 1, 10);
|
||||||
|
INSERT INTO edge VALUES(1, 2, 20);
|
||||||
|
INSERT INTO edge VALUES(0, 3, 30);
|
||||||
|
INSERT INTO edge VALUES(2, 4, 40);
|
||||||
|
INSERT INTO edge VALUES(3, 4, 40);
|
||||||
|
INSERT INTO edge VALUES(2, 5, 50);
|
||||||
|
INSERT INTO edge VALUES(3, 6, 60);
|
||||||
|
INSERT INTO edge VALUES(5, 7, 70);
|
||||||
|
INSERT INTO edge VALUES(3, 7, 70);
|
||||||
|
INSERT INTO edge VALUES(4, 8, 80);
|
||||||
|
INSERT INTO edge VALUES(7, 8, 80);
|
||||||
|
INSERT INTO edge VALUES(8, 9, 90);
|
||||||
|
|
||||||
|
WITH RECURSIVE
|
||||||
|
ancest(id, mtime) AS
|
||||||
|
(VALUES(0, 0)
|
||||||
|
UNION
|
||||||
|
SELECT edge.xto, edge.seq FROM edge, ancest
|
||||||
|
WHERE edge.xfrom=ancest.id
|
||||||
|
ORDER BY 2
|
||||||
|
)
|
||||||
|
SELECT * FROM ancest;
|
||||||
|
} {0 0 1 10 2 20 3 30 4 40 5 50 6 60 7 70 8 80 9 90}
|
||||||
|
do_execsql_test 5.2.2 {
|
||||||
|
WITH RECURSIVE
|
||||||
|
ancest(id, mtime) AS
|
||||||
|
(VALUES(0, 0)
|
||||||
|
UNION ALL
|
||||||
|
SELECT edge.xto, edge.seq FROM edge, ancest
|
||||||
|
WHERE edge.xfrom=ancest.id
|
||||||
|
ORDER BY 2
|
||||||
|
)
|
||||||
|
SELECT * FROM ancest;
|
||||||
|
} {0 0 1 10 2 20 3 30 4 40 4 40 5 50 6 60 7 70 7 70 8 80 8 80 8 80 8 80 9 90 9 90 9 90 9 90}
|
||||||
|
|
||||||
do_catchsql_test 5.3 {
|
do_catchsql_test 5.3 {
|
||||||
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i LIMIT 10 )
|
WITH i(x) AS ( VALUES(1) UNION ALL SELECT x+1 FROM i LIMIT 10 )
|
||||||
|
|||||||
Reference in New Issue
Block a user