mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Add implementation of nth_value() window function.
FossilOrigin-Name: eb1fb420ac70857e7ca4cbad78be15a27bee8f915ba2da080c7efa1879c06c31
This commit is contained in:
139
src/window.c
139
src/window.c
@@ -618,11 +618,11 @@ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
|
||||
static void windowAggInit(Parse *pParse, Window *pMWin){
|
||||
Window *pWin;
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
int funcFlags = pWin->pFunc->funcFlags;
|
||||
if( (funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
FuncDef *p = pWin->pFunc;
|
||||
if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
|
||||
ExprList *pList = pWin->pOwner->x.pList;
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0);
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
pWin->csrApp = pParse->nTab++;
|
||||
pWin->regApp = pParse->nMem+1;
|
||||
pParse->nMem += 3;
|
||||
@@ -634,6 +634,17 @@ static void windowAggInit(Parse *pParse, Window *pMWin){
|
||||
sqlite3VdbeAppendP4(v, pKeyInfo, P4_KEYINFO);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
|
||||
}
|
||||
else if( p->xSFunc==nth_valueStepFunc ){
|
||||
/* Allocate two registers at pWin->regApp. These will be used to
|
||||
** store the start and end index of the current frame. */
|
||||
assert( pMWin->iEphCsr );
|
||||
pWin->regApp = pParse->nMem+1;
|
||||
pWin->csrApp = pParse->nTab++;
|
||||
pParse->nMem += 2;
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
|
||||
sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,7 +714,9 @@ static void windowAggStep(
|
||||
regArg = reg + pWin->iArgCol;
|
||||
}
|
||||
|
||||
if( pWin->csrApp ){
|
||||
if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
|
||||
&& pWin->eStart!=TK_UNBOUNDED
|
||||
){
|
||||
if( bInverse==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
|
||||
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
|
||||
@@ -714,6 +727,10 @@ static void windowAggStep(
|
||||
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
|
||||
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
|
||||
}
|
||||
}else if( pWin->regApp ){
|
||||
assert( pWin->pFunc->xSFunc==nth_valueStepFunc );
|
||||
assert( bInverse==0 || bInverse==1 );
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
|
||||
}else{
|
||||
if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
|
||||
CollSeq *pColl;
|
||||
@@ -732,7 +749,9 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
|
||||
Window *pWin;
|
||||
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
if( pWin->csrApp ){
|
||||
if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX)
|
||||
&& pWin->eStart!=TK_UNBOUNDED
|
||||
){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
|
||||
sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult);
|
||||
@@ -740,6 +759,11 @@ static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){
|
||||
if( bFinal ){
|
||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
|
||||
}
|
||||
}else if( pWin->regApp ){
|
||||
if( bFinal ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
|
||||
}
|
||||
}else{
|
||||
if( bFinal==0 ){
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
|
||||
@@ -811,6 +835,56 @@ static void windowPartitionCache(
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart);
|
||||
}
|
||||
|
||||
static void windowReturnOneRow(
|
||||
Parse *pParse,
|
||||
Window *pMWin,
|
||||
int regGosub,
|
||||
int addrGosub
|
||||
){
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
Window *pWin;
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
FuncDef *pFunc = pWin->pFunc;
|
||||
if( pFunc->xSFunc==nth_valueStepFunc ){
|
||||
int csr = pWin->csrApp;
|
||||
int lbl = sqlite3VdbeMakeLabel(v);
|
||||
int tmpReg = sqlite3GetTempReg(pParse);
|
||||
sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, pWin->iEphCsr, pWin->iArgCol+1, tmpReg);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg);
|
||||
sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg);
|
||||
sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg);
|
||||
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult);
|
||||
sqlite3VdbeResolveLabel(v, lbl);
|
||||
sqlite3ReleaseTempReg(pParse, tmpReg);
|
||||
}
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
}
|
||||
|
||||
static void windowReturnRows(
|
||||
Parse *pParse,
|
||||
Window *pMWin,
|
||||
int regCtr,
|
||||
int bFinal,
|
||||
int regGosub,
|
||||
int addrGosub,
|
||||
int regInvArg,
|
||||
int regInvSize
|
||||
){
|
||||
int addr;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
|
||||
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
||||
if( regInvArg ){
|
||||
windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr);
|
||||
sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */
|
||||
}
|
||||
|
||||
/*
|
||||
** ROWS BETWEEN <expr1> PRECEDING AND <expr2> FOLLOWING
|
||||
** ----------------------------------------------------
|
||||
@@ -1081,11 +1155,12 @@ static void windowCodeRowExprStep(
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, regPeer);
|
||||
}
|
||||
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
if( bRange ){
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regPeer, 1);
|
||||
}
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone);
|
||||
if( bRange ){
|
||||
@@ -1203,6 +1278,8 @@ static void windowCodeCacheStep(
|
||||
int nArg;
|
||||
|
||||
assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT)
|
||||
|| (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED)
|
||||
|| (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT)
|
||||
);
|
||||
|
||||
regNewPeer = pParse->nMem+1;
|
||||
@@ -1236,39 +1313,33 @@ static void windowCodeCacheStep(
|
||||
addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrLead);
|
||||
sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr);
|
||||
|
||||
if( pOrderBy ){
|
||||
int addrJump; /* Address of OP_Jump below */
|
||||
int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
|
||||
int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
||||
|
||||
for(k=0; k<nPeer; k++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
|
||||
if( pOrderBy && pMWin->eEnd==TK_CURRENT ){
|
||||
int bCurrent = (pMWin->eEnd==TK_CURRENT && pMWin->eStart==TK_CURRENT);
|
||||
int addrJump = 0; /* Address of OP_Jump below */
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0);
|
||||
int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0);
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
||||
for(k=0; k<nPeer; k++){
|
||||
sqlite3VdbeAddOp3(v, OP_Column, csrLead, iOff+k, regNewPeer+k);
|
||||
}
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
|
||||
}
|
||||
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, nPeer-1);
|
||||
windowAggFinal(pParse, pMWin, 0);
|
||||
sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 , 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-3);
|
||||
sqlite3VdbeJumpHere(v, addrJump);
|
||||
windowReturnRows(pParse, pMWin, regCtr, 0, regGosub, addrGosub,
|
||||
(bCurrent ? regArg : 0), (bCurrent ? regSize : 0)
|
||||
);
|
||||
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
||||
}
|
||||
|
||||
windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize);
|
||||
sqlite3VdbeAddOp2(v, OP_AddImm, regCtr, 1);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Next, csrLead, addrRewind+2);
|
||||
|
||||
windowAggFinal(pParse, pMWin, 1);
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 , 1);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+3);
|
||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-3);
|
||||
windowReturnRows(pParse, pMWin, regCtr, 1, regGosub, addrGosub, 0, 0);
|
||||
|
||||
sqlite3VdbeJumpHere(v, addrRewind);
|
||||
sqlite3VdbeJumpHere(v, addrRewind+1);
|
||||
@@ -1497,7 +1568,10 @@ void sqlite3WindowCodeStep(
|
||||
}
|
||||
|
||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||
if( pWin->pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE ){
|
||||
FuncDef *pFunc = pWin->pFunc;
|
||||
if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
|
||||
|| (pFunc->xSFunc==nth_valueStepFunc)
|
||||
){
|
||||
windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
return;
|
||||
}
|
||||
@@ -1507,4 +1581,3 @@ void sqlite3WindowCodeStep(
|
||||
windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user