1
0
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:
drh
2023-10-18 18:11:11 +00:00
parent db19f48b69
commit 59a0d0bbf9
5 changed files with 173 additions and 35 deletions

View File

@@ -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);
}