1
0
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:
dan
2019-03-09 20:49:17 +00:00
parent f7b846e4ed
commit 72b9fdcf20
9 changed files with 228 additions and 73 deletions

View File

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