mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-11 01:42:22 +03:00
ORDER BY on aggregates seem to work, at least for simple smoke tests. Lots
more testing is needed though. Surely there are many bugs. FossilOrigin-Name: 64c12a835b6f1df8f2f5f4a41de083f6b3fc7f8030042c6aac0082382cd9cc4d
This commit is contained in:
146
src/select.c
146
src/select.c
@@ -6646,6 +6646,32 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
||||
pFunc->pFunc->zName));
|
||||
}
|
||||
}
|
||||
if( pFunc->iOBTab>=0 ){
|
||||
ExprList *pOBList;
|
||||
KeyInfo *pKeyInfo;
|
||||
int nExtra = 0;
|
||||
assert( pFunc->pFExpr->pLeft!=0 );
|
||||
assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
|
||||
assert( ExprUseXList(pFunc->pFExpr->pLeft) );
|
||||
pOBList = pFunc->pFExpr->pLeft->x.pList;
|
||||
if( !pFunc->bOBUnique ){
|
||||
nExtra++; /* One extra column for the OP_Sequence */
|
||||
}
|
||||
if( pFunc->bOBPayload ){
|
||||
/* extra columns for the function arguments */
|
||||
assert( ExprUseXList(pFunc->pFExpr) );
|
||||
nExtra += pFunc->pFExpr->x.pList->nExpr;
|
||||
}
|
||||
pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
|
||||
if( !pFunc->bOBUnique ){
|
||||
pKeyInfo->nKeyField++;
|
||||
}
|
||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||
pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
|
||||
(char*)pKeyInfo, P4_KEYINFO);
|
||||
ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)",
|
||||
pFunc->pFunc->zName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6661,13 +6687,46 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
|
||||
ExprList *pList;
|
||||
assert( ExprUseXList(pF->pFExpr) );
|
||||
pList = pF->pFExpr->x.pList;
|
||||
if( pF->iOBTab ){
|
||||
/* For an ORDER BY aggregate, calls to OP_AggStep where deferred and
|
||||
** all content was stored in emphermal table pF->iOBTab. Extract that
|
||||
** content now (in ORDER BY order) and make all calls to OP_AggStep
|
||||
** before doing the OP_AggFinal call. */
|
||||
int iTop; /* Start of loop for extracting columns */
|
||||
int nArg; /* Number of columns to extract */
|
||||
int nKey; /* Key columns to be skipped */
|
||||
int regAgg; /* Extract into this array */
|
||||
int j; /* Loop counter */
|
||||
|
||||
nArg = pList->nExpr;
|
||||
regAgg = sqlite3GetTempRange(pParse, nArg);
|
||||
|
||||
if( pF->bOBPayload==0 ){
|
||||
nKey = 0;
|
||||
}else{
|
||||
assert( pF->pFExpr->pLeft!=0 );
|
||||
assert( ExprUseXList(pF->pFExpr->pLeft) );
|
||||
assert( pF->pFExpr->pLeft->x.pList!=0 );
|
||||
nKey = pF->pFExpr->pLeft->x.pList->nExpr;
|
||||
if( !pF->bOBUnique ) nKey++;
|
||||
}
|
||||
iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab);
|
||||
for(j=nArg-1; j>=0; j--){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
|
||||
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, (u8)nArg);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1);
|
||||
sqlite3VdbeJumpHere(v, iTop);
|
||||
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
|
||||
pList ? pList->nExpr : 0);
|
||||
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Generate code that will update the accumulator memory cells for an
|
||||
** aggregate based on the current cursor position.
|
||||
@@ -6676,6 +6735,13 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
|
||||
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
|
||||
** registers if register regAcc contains 0. The caller will take care
|
||||
** of setting and clearing regAcc.
|
||||
**
|
||||
** For an ORDER BY aggregate, the actually accumulator memory cell update
|
||||
** is deferred until after all input rows have been received, so that they
|
||||
** can be run in the requested order. In that case, instead of invoking
|
||||
** OP_AggStep to update accumulator, just add the arguments that would
|
||||
** have been passed into OP_AggStep into the sorting ephemeral table
|
||||
** (along with the appropriate sort key).
|
||||
*/
|
||||
static void updateAccumulator(
|
||||
Parse *pParse,
|
||||
@@ -6697,6 +6763,7 @@ static void updateAccumulator(
|
||||
int nArg;
|
||||
int addrNext = 0;
|
||||
int regAgg;
|
||||
int regAggSz = 0;
|
||||
ExprList *pList;
|
||||
assert( ExprUseXList(pF->pFExpr) );
|
||||
assert( !IsWindowFunc(pF->pFExpr) );
|
||||
@@ -6723,7 +6790,39 @@ static void updateAccumulator(
|
||||
addrNext = sqlite3VdbeMakeLabel(pParse);
|
||||
sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
|
||||
}
|
||||
if( pList ){
|
||||
if( pF->iOBTab>=0 ){
|
||||
/* Instead of invoking AggStep, we must push the arguments that would
|
||||
** have been passed to AggStep onto the sorting table. */
|
||||
int jj; /* Registered used so far in building the record */
|
||||
ExprList *pOBList; /* The ORDER BY clause */
|
||||
assert( pList!=0 );
|
||||
nArg = pList->nExpr;
|
||||
assert( nArg>0 );
|
||||
assert( pF->pFExpr->pLeft!=0 );
|
||||
assert( pF->pFExpr->pLeft->op==TK_ORDER );
|
||||
assert( ExprUseXList(pF->pFExpr->pLeft) );
|
||||
pOBList = pF->pFExpr->pLeft->x.pList;
|
||||
assert( pOBList!=0 );
|
||||
assert( pOBList->nExpr>0 );
|
||||
regAggSz = pOBList->nExpr;
|
||||
if( !pF->bOBUnique ){
|
||||
regAggSz++; /* One register for OP_Sequence */
|
||||
}
|
||||
if( pF->bOBPayload ){
|
||||
regAggSz += nArg;
|
||||
}
|
||||
regAggSz++; /* One extra register to hold result of MakeRecord */
|
||||
regAgg = sqlite3GetTempRange(pParse, regAggSz);
|
||||
sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
|
||||
jj = pOBList->nExpr;
|
||||
if( !pF->bOBUnique ){
|
||||
sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
|
||||
jj++;
|
||||
}
|
||||
if( pF->bOBPayload ){
|
||||
sqlite3ExprCodeExprList(pParse, pList, regAgg+jj, 0, SQLITE_ECEL_DUP);
|
||||
}
|
||||
}else if( pList ){
|
||||
nArg = pList->nExpr;
|
||||
regAgg = sqlite3GetTempRange(pParse, nArg);
|
||||
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
|
||||
@@ -6738,24 +6837,35 @@ static void updateAccumulator(
|
||||
pF->iDistinct = codeDistinct(pParse, eDistinctType,
|
||||
pF->iDistinct, addrNext, pList, regAgg);
|
||||
}
|
||||
if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
||||
CollSeq *pColl = 0;
|
||||
struct ExprList_item *pItem;
|
||||
int j;
|
||||
assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
|
||||
for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
|
||||
if( pF->iOBTab>=0 ){
|
||||
/* Insert a new record into the ORDER BY table */
|
||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1,
|
||||
regAgg+regAggSz-1);
|
||||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1,
|
||||
regAgg, regAggSz-1);
|
||||
sqlite3ReleaseTempRange(pParse, regAgg, regAggSz);
|
||||
}else{
|
||||
/* Invoke the AggStep function */
|
||||
if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
||||
CollSeq *pColl = 0;
|
||||
struct ExprList_item *pItem;
|
||||
int j;
|
||||
assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
|
||||
for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
|
||||
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
|
||||
}
|
||||
if( !pColl ){
|
||||
pColl = pParse->db->pDfltColl;
|
||||
}
|
||||
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
|
||||
(char *)pColl, P4_COLLSEQ);
|
||||
}
|
||||
if( !pColl ){
|
||||
pColl = pParse->db->pDfltColl;
|
||||
}
|
||||
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
|
||||
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
|
||||
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
|
||||
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, (u8)nArg);
|
||||
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
|
||||
}
|
||||
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
|
||||
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
||||
sqlite3VdbeChangeP5(v, (u8)nArg);
|
||||
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
|
||||
if( addrNext ){
|
||||
sqlite3VdbeResolveLabel(v, addrNext);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user