mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Add support for RANGE window frames. Some cases still do not work.
FossilOrigin-Name: ffc32b246d92d53c66094afe11950b53ffab6a1c230c602eebbfedafb2eb57f4
This commit is contained in:
164
src/window.c
164
src/window.c
@@ -559,6 +559,14 @@ void sqlite3WindowUpdate(
|
||||
}else{
|
||||
sqlite3WindowChain(pParse, pWin, pList);
|
||||
}
|
||||
if( (pWin->eType==TK_RANGE)
|
||||
&& (pWin->pStart || pWin->pEnd)
|
||||
&& (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1)
|
||||
){
|
||||
sqlite3ErrorMsg(pParse,
|
||||
"RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression"
|
||||
);
|
||||
}else
|
||||
if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){
|
||||
sqlite3 *db = pParse->db;
|
||||
if( pWin->pFilter ){
|
||||
@@ -927,14 +935,6 @@ Window *sqlite3WindowAlloc(
|
||||
eType = TK_RANGE;
|
||||
}
|
||||
|
||||
/* If a frame is declared "RANGE" (not "ROWS"), then it may not use
|
||||
** either "<expr> PRECEDING" or "<expr> FOLLOWING".
|
||||
*/
|
||||
if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){
|
||||
sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW");
|
||||
goto windowAllocErr;
|
||||
}
|
||||
|
||||
/* Additionally, the
|
||||
** starting boundary type may not occur earlier in the following list than
|
||||
** the ending boundary type:
|
||||
@@ -952,7 +952,7 @@ Window *sqlite3WindowAlloc(
|
||||
if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING)
|
||||
|| (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT))
|
||||
){
|
||||
sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS");
|
||||
sqlite3ErrorMsg(pParse, "unsupported frame specification");
|
||||
goto windowAllocErr;
|
||||
}
|
||||
|
||||
@@ -1485,7 +1485,7 @@ static int windowIfNewPeer(
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
addr = sqlite3VdbeAddOp3(
|
||||
v, OP_Jump, sqlite3VdbeCurrentAddr(v)+1, 0, sqlite3VdbeCurrentAddr(v)+1
|
||||
);
|
||||
);
|
||||
VdbeCoverageEqNe(v);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1);
|
||||
}else{
|
||||
@@ -1539,6 +1539,39 @@ static void windowReadPeerValues(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** This function is called as part of generating VM programs for RANGE
|
||||
** offset PRECEDING/FOLLOWING frame boundaries. It generates code equivalent
|
||||
** to:
|
||||
**
|
||||
** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl;
|
||||
** if( csr1.rowid >= csr2.rowid ) goto lbl;
|
||||
*/
|
||||
static void windowCodeRangeTest(
|
||||
WindowCodeArg *p,
|
||||
int op, /* OP_Ge or OP_Gt */
|
||||
int csr1,
|
||||
int regVal,
|
||||
int csr2,
|
||||
int lbl
|
||||
){
|
||||
Parse *pParse = p->pParse;
|
||||
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||
int reg1 = sqlite3GetTempReg(pParse);
|
||||
int reg2 = sqlite3GetTempReg(pParse);
|
||||
windowReadPeerValues(p, csr1, reg1);
|
||||
windowReadPeerValues(p, csr2, reg2);
|
||||
sqlite3VdbeAddOp3(v, OP_Add, reg1, regVal, reg1);
|
||||
sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, csr1, reg1);
|
||||
sqlite3VdbeAddOp2(v, OP_Rowid, csr2, reg2);
|
||||
sqlite3VdbeAddOp3(v, OP_Gt, reg2, lbl, reg1);
|
||||
sqlite3ReleaseTempReg(pParse, reg1);
|
||||
sqlite3ReleaseTempReg(pParse, reg2);
|
||||
|
||||
assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le );
|
||||
}
|
||||
|
||||
static int windowCodeOp(
|
||||
WindowCodeArg *p,
|
||||
int op,
|
||||
@@ -1555,6 +1588,9 @@ static int windowCodeOp(
|
||||
int addrGoto = 0;
|
||||
int bPeer = (pMWin->eType!=TK_ROWS);
|
||||
|
||||
int lblDone = sqlite3VdbeMakeLabel(pParse);
|
||||
int addrNextRange = 0;
|
||||
|
||||
/* Special case - WINDOW_AGGINVERSE is always a no-op if the frame
|
||||
** starts with UNBOUNDED PRECEDING. */
|
||||
if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){
|
||||
@@ -1563,7 +1599,39 @@ static int windowCodeOp(
|
||||
}
|
||||
|
||||
if( regCountdown>0 ){
|
||||
addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
addrNextRange = sqlite3VdbeCurrentAddr(v);
|
||||
|
||||
switch( op ){
|
||||
case WINDOW_RETURN_ROW: {
|
||||
assert( 0 );
|
||||
break;
|
||||
}
|
||||
|
||||
case WINDOW_AGGINVERSE: {
|
||||
if( pMWin->eStart==TK_FOLLOWING ){
|
||||
windowCodeRangeTest(
|
||||
p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone
|
||||
);
|
||||
}else{
|
||||
windowCodeRangeTest(
|
||||
p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case WINDOW_AGGSTEP: {
|
||||
windowCodeRangeTest(
|
||||
p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}else{
|
||||
addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if( op==WINDOW_RETURN_ROW ){
|
||||
@@ -1610,6 +1678,10 @@ static int windowCodeOp(
|
||||
sqlite3ReleaseTempRange(pParse, regTmp, nReg);
|
||||
}
|
||||
|
||||
if( addrNextRange ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange);
|
||||
}
|
||||
sqlite3VdbeResolveLabel(v, lblDone);
|
||||
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
||||
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
|
||||
return ret;
|
||||
@@ -1724,6 +1796,13 @@ static void windowCodeStep(
|
||||
int regNewPeer = 0;
|
||||
WindowCodeArg s;
|
||||
|
||||
assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT
|
||||
|| pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED
|
||||
);
|
||||
assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT
|
||||
|| pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING
|
||||
);
|
||||
|
||||
memset(&s, 0, sizeof(WindowCodeArg));
|
||||
s.pParse = pParse;
|
||||
s.pMWin = pMWin;
|
||||
@@ -1745,8 +1824,9 @@ static void windowCodeStep(
|
||||
regEnd = ++pParse->nMem;
|
||||
}
|
||||
|
||||
/* If this is not a "ROWS BETWEEN ..." frame, then allocate registers to
|
||||
** store a copy of the current ORDER BY expressions. */
|
||||
/* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of
|
||||
** registers to store a copies of the ORDER BY expressions for the
|
||||
** main loop, and for each cursor (start, current and end). */
|
||||
if( pMWin->eType!=TK_ROWS ){
|
||||
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
|
||||
regNewPeer = reg + pMWin->nBufferCol;
|
||||
@@ -1758,18 +1838,6 @@ static void windowCodeStep(
|
||||
s.end.reg = pParse->nMem+1; pParse->nMem += nPeer;
|
||||
}
|
||||
|
||||
assert( pMWin->eStart==TK_PRECEDING
|
||||
|| pMWin->eStart==TK_CURRENT
|
||||
|| pMWin->eStart==TK_FOLLOWING
|
||||
|| pMWin->eStart==TK_UNBOUNDED
|
||||
);
|
||||
assert( pMWin->eEnd==TK_FOLLOWING
|
||||
|| pMWin->eEnd==TK_CURRENT
|
||||
|| pMWin->eEnd==TK_UNBOUNDED
|
||||
|| pMWin->eEnd==TK_PRECEDING
|
||||
);
|
||||
|
||||
|
||||
/* Load the column values for the row returned by the sub-select
|
||||
** into an array of registers starting at reg. Assemble them into
|
||||
** a record in register regRecord. TODO: An optimization here? */
|
||||
@@ -1791,14 +1859,12 @@ static void windowCodeStep(
|
||||
int regNewPart = reg + pMWin->nBufferCol;
|
||||
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
|
||||
|
||||
addrIf = sqlite3VdbeAddOp1(v, OP_If, pMWin->regFirst);
|
||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart);
|
||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||
sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
|
||||
VdbeCoverageEqNe(v);
|
||||
addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart);
|
||||
VdbeComment((v, "call flush_partition"));
|
||||
sqlite3VdbeJumpHere(v, addrIf);
|
||||
sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
|
||||
}
|
||||
|
||||
@@ -1848,7 +1914,7 @@ static void windowCodeStep(
|
||||
addrShortcut = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||
sqlite3VdbeJumpHere(v, addrGe);
|
||||
}
|
||||
if( pMWin->eStart==TK_FOLLOWING && regEnd ){
|
||||
if( pMWin->eStart==TK_FOLLOWING && pMWin->eType!=TK_RANGE && regEnd ){
|
||||
assert( pMWin->eEnd==TK_FOLLOWING );
|
||||
sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart);
|
||||
}
|
||||
@@ -1888,8 +1954,18 @@ static void windowCodeStep(
|
||||
if( pMWin->eStart==TK_FOLLOWING ){
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd!=TK_UNBOUNDED ){
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
int lbl = sqlite3VdbeMakeLabel(pParse);
|
||||
int addrNext = sqlite3VdbeCurrentAddr(v);
|
||||
windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext);
|
||||
sqlite3VdbeResolveLabel(v, lbl);
|
||||
}else{
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
}
|
||||
}
|
||||
}else
|
||||
if( pMWin->eEnd==TK_PRECEDING ){
|
||||
@@ -1900,10 +1976,25 @@ static void windowCodeStep(
|
||||
int addr;
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eEnd!=TK_UNBOUNDED ){
|
||||
if( regEnd ) addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
if( regEnd ) sqlite3VdbeJumpHere(v, addr);
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
int lbl;
|
||||
addr = sqlite3VdbeCurrentAddr(v);
|
||||
if( regEnd ){
|
||||
lbl = sqlite3VdbeMakeLabel(pParse);
|
||||
windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl);
|
||||
}
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
if( regEnd ){
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
|
||||
sqlite3VdbeResolveLabel(v, lbl);
|
||||
}
|
||||
}else{
|
||||
if( regEnd ) addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
|
||||
windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0);
|
||||
windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0);
|
||||
if( regEnd ) sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
if( addrPeerJump ){
|
||||
@@ -1938,6 +2029,11 @@ static void windowCodeStep(
|
||||
int addrBreak2;
|
||||
int addrBreak3;
|
||||
windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0);
|
||||
if( pMWin->eType==TK_RANGE ){
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
|
||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1);
|
||||
}else
|
||||
if( pMWin->eEnd==TK_UNBOUNDED ){
|
||||
addrStart = sqlite3VdbeCurrentAddr(v);
|
||||
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
|
||||
|
Reference in New Issue
Block a user