From 31f5639fb71840d83eb7bb18691471f198632a48 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 24 May 2018 21:10:57 +0000 Subject: [PATCH] Allow " PRECEDING" to be used to specify the end of a window frame. FossilOrigin-Name: 7b709a989c188dbcf429989a0785294b36c8a7e89b5de8cefc25decf1f5b7f5a --- manifest | 16 ++-- manifest.uuid | 2 +- src/window.c | 205 ++++++++++++++++++++++++++++------------------ test/window2.tcl | 39 +++++++++ test/window2.test | 39 +++++++++ 5 files changed, 211 insertions(+), 90 deletions(-) diff --git a/manifest b/manifest index 3a161e7fea..5ebfde392c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Support\sother\sframe\stypes\sthat\suse\s"\sPRECEDING"\sor\s"\sFOLLOWING"\sas\nstart\sor\send\sconditions. -D 2018-05-24T17:49:14.994 +C Allow\s"\sPRECEDING"\sto\sbe\sused\sto\sspecify\sthe\send\sof\sa\swindow\sframe. +D 2018-05-24T21:10:57.618 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da @@ -582,7 +582,7 @@ F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1 F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53 F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8 F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a -F src/window.c dc58ad62f2bb06d2e289ce65375b7d0047646b73c11a09fb0325febac0aebba7 +F src/window.c e4441e8ee3f83df85ff542048d3eed1c199ce6f90f3edcfeb1c4c1f53991b125 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d @@ -1615,8 +1615,8 @@ F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc F test/window1.test 5705337783d220b47f6fb4432264543b7557a05be8013d772f57d71f2fded271 -F test/window2.tcl 798cfc8bef0f08a27a0ba64e147d8c72e9409c1673cc4ccff2ee7f150aa084e4 -F test/window2.test ca65b0818ddc948a7b0b07ee16a3c489dafcb958203bf8b75356eacd4696a206 +F test/window2.tcl 6df00293e3691a0d17624de339ce91632252e160701d9be663c21e3706f1dfd0 +F test/window2.test ee542c30bf8502f6df6f1a7304c2ca1d44143dfdd82dfad331f0b3b287de414c F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d @@ -1733,7 +1733,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 3a203660f1e4da3b8d2d605c494f4843f6e00752f28042b49e11d7d6550dd406 -R 91a6e47e9aa2b2bca4e7ec8da35f9bee +P ec7b648c7f0ee266653561bbb9daa45b9be0d8a1a14f11dc93bce467c35154e6 +R cae23b80a7b926c27766da973f4ae9e5 U dan -Z 468858d850092ff66af7324bc283099f +Z 6786b4d33cda910bcdf7493ef1afdc20 diff --git a/manifest.uuid b/manifest.uuid index 09eb988a9c..c007a10b23 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ec7b648c7f0ee266653561bbb9daa45b9be0d8a1a14f11dc93bce467c35154e6 \ No newline at end of file +7b709a989c188dbcf429989a0785294b36c8a7e89b5de8cefc25decf1f5b7f5a \ No newline at end of file diff --git a/src/window.c b/src/window.c index 47e4181915..433c6e3876 100644 --- a/src/window.c +++ b/src/window.c @@ -93,6 +93,26 @@ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){ sqlite3VdbeAppendP4(v, (void*)azErr[bEnd], P4_STATIC); } +static void windowAggStep( + Parse *pParse, + Window *pMWin, + int csr, + int bInverse, + int reg +){ + Vdbe *v = sqlite3GetVdbe(pParse); + Window *pWin; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + int i; + for(i=0; inArg; i++){ + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i); + } + sqlite3VdbeAddOp3(v, OP_AggStep0, bInverse, reg, pWin->regAccum); + sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeChangeP5(v, (u8)pWin->nArg); + } +} + /* ** ROWS BETWEEN PRECEDING AND FOLLOWING ** @@ -105,26 +125,26 @@ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){ ** Gosub flush_partition ** ** flush_partition: -** OpenDup (csr -> csr2) -** OpenDup (csr -> csr3) -** regPrec = // PRECEDING expression -** regFollow = // FOLLOWING expression -** if( regPrec<0 || regFollow<0 ) throw exception! -** Rewind (csr,csr2,csr3) // if EOF goto flush_partition_done -** Aggstep (csr3) -** Next(csr3) // if EOF fall-through -** if( (regFollow--)<=0 ){ +** Once { +** OpenDup (iEphCsr -> csrStart) +** OpenDup (iEphCsr -> csrEnd) +** } +** regStart = // PRECEDING expression +** regEnd = // FOLLOWING expression +** if( regStart<0 || regEnd<0 ) throw exception! +** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done +** Aggstep (csrEnd) +** Next(csrEnd) // if EOF fall-through +** if( (regEnd--)<=0 ){ ** AggFinal (xValue) ** Gosub addrGosub ** Next(csr) // if EOF goto flush_partition_done -** if( (regPrec--)<=0 ){ -** AggStep (csr2, xInverse) -** Next(csr2) +** if( (regStart--)<=0 ){ +** AggStep (csrStart, xInverse) +** Next(csrStart) ** } ** } ** flush_partition_done: -** Close (csr2) -** Close (csr3) ** ResetSorter (csr) ** Return ** @@ -136,6 +156,38 @@ static void windowCheckFrameValue(Parse *pParse, int reg, int bEnd){ ** These are similar to the above. For "CURRENT ROW", intialize the ** register to 0. For "UNBOUNDED ..." to infinity. ** +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** regEnd = regEnd - regStart +** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done +** Aggstep (csrEnd) +** Next(csrEnd) // if EOF fall-through +** if( (regEnd--)<=0 ){ +** AggStep (csrStart, xInverse) +** Next (csrStart) +** if( (regStart--)<=0 ){ +** AggFinal (xValue) +** Gosub addrGosub +** Next(csr) // if EOF goto flush_partition_done +** } +** } +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** +** Replace the bit after "Rewind" in the above with: +** +** if( (regEnd--)<=0 ){ +** AggStep (csrEnd) +** Next (csrEnd) +** } +** AggFinal (xValue) +** Gosub addrGosub +** Next(csr) // if EOF goto flush_partition_done +** if( (regStart--)<=0 ){ +** AggStep (csr2, xInverse) +** Next (csr2) +** } +** */ static void windowCodeRowExprStep( Parse *pParse, @@ -151,19 +203,20 @@ static void windowCodeRowExprStep( int iSubCsr = p->pSrc->a[0].iCursor; int nSub = p->pSrc->a[0].pTab->nCol; int regFlushPart; /* Register for "Gosub flush_partition" */ - int addrFlushPart; /* Label for "Gosub flush_partition" */ - int addrDone; /* Label for "Gosub flush_partition_done" */ + int lblFlushPart; /* Label for "Gosub flush_partition" */ + int lblFlushDone; /* Label for "Gosub flush_partition_done" */ int reg = pParse->nMem+1; int regRecord = reg+nSub; int regRowid = regRecord+1; int addr; - int csrPrec = pParse->nTab++; - int csrFollow = pParse->nTab++; - int regPrec; /* Value of PRECEDING */ - int regFollow; /* Value of FOLLOWING */ + int csrStart = pParse->nTab++; + int csrEnd = pParse->nTab++; + int regStart; /* Value of PRECEDING */ + int regEnd; /* Value of FOLLOWING */ int addrNext; int addrGoto; + int addrTop; int addrIfPos1; int addrIfPos2; @@ -174,17 +227,18 @@ static void windowCodeRowExprStep( assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT || pMWin->eEnd==TK_UNBOUNDED + || pMWin->eEnd==TK_PRECEDING ); pParse->nMem += nSub + 2; /* Allocate register and label for the "flush_partition" sub-routine. */ regFlushPart = ++pParse->nMem; - addrFlushPart = sqlite3VdbeMakeLabel(v); - addrDone = sqlite3VdbeMakeLabel(v); + lblFlushPart = sqlite3VdbeMakeLabel(v); + lblFlushDone = sqlite3VdbeMakeLabel(v); - regPrec = ++pParse->nMem; - regFollow = ++pParse->nMem; + regStart = ++pParse->nMem; + regEnd = ++pParse->nMem; /* Martial the row returned by the sub-select into an array of ** registers. */ @@ -205,7 +259,7 @@ static void windowCodeRowExprStep( addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart,nPart); sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, addrFlushPart); + sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart); } @@ -217,62 +271,61 @@ static void windowCodeRowExprStep( sqlite3WhereEnd(pWInfo); /* Invoke "flush_partition" to deal with the final (or only) partition */ - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, addrFlushPart); + sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); /* flush_partition: */ - sqlite3VdbeResolveLabel(v, addrFlushPart); + sqlite3VdbeResolveLabel(v, lblFlushPart); sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrPrec, pMWin->iEphCsr); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrFollow, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr); - /* If either regPrec or regFollow are not non-negative integers, throw + /* If either regStart or regEnd are not non-negative integers, throw ** an exception. */ if( pMWin->pStart ){ assert( pMWin->eStart==TK_PRECEDING ); - sqlite3ExprCode(pParse, pMWin->pStart, regPrec); - windowCheckFrameValue(pParse, regPrec, 0); + sqlite3ExprCode(pParse, pMWin->pStart, regStart); + windowCheckFrameValue(pParse, regStart, 0); } if( pMWin->pEnd ){ - assert( pMWin->eEnd==TK_FOLLOWING ); - sqlite3ExprCode(pParse, pMWin->pEnd, regFollow); - windowCheckFrameValue(pParse, regFollow, 1); + sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); + windowCheckFrameValue(pParse, regEnd, 1); } - sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regResult); - sqlite3VdbeAddOp2(v, OP_Null, 0, pMWin->regAccum); + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + } - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, addrDone); - sqlite3VdbeAddOp2(v, OP_Rewind, csrPrec, addrDone); + sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone); + sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone); sqlite3VdbeChangeP5(v, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, csrFollow, addrDone); + sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone); sqlite3VdbeChangeP5(v, 1); /* Invoke AggStep function for each window function using the row that - ** csrFollow currently points to. Or, if csrFollow is already at EOF, + ** csrEnd currently points to. Or, if csrEnd is already at EOF, ** do nothing. */ - addrNext = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp2(v, OP_Next, csrFollow, addrNext+2); - sqlite3VdbeAddOp0(v, OP_Goto); - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - int i; - for(i=0; inArg; i++){ - sqlite3VdbeAddOp3(v, OP_Column, csrFollow, pWin->iArgCol+i, reg+i); - } - sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg, pWin->regAccum); - sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, (u8)pWin->nArg); + addrTop = sqlite3VdbeCurrentAddr(v); + if( pMWin->eEnd==TK_PRECEDING ){ + addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); } + sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2); + addr = sqlite3VdbeAddOp0(v, OP_Goto); + windowAggStep(pParse, pMWin, csrEnd, 0, reg); if( pMWin->eEnd==TK_UNBOUNDED ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); - sqlite3VdbeJumpHere(v, addrNext+1); - addrNext = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); + sqlite3VdbeJumpHere(v, addr); + addrTop = sqlite3VdbeCurrentAddr(v); }else{ - sqlite3VdbeJumpHere(v, addrNext+1); + sqlite3VdbeJumpHere(v, addr); + if( pMWin->eEnd==TK_PRECEDING ){ + sqlite3VdbeJumpHere(v, addrIfPos1); + } } if( pMWin->eEnd==TK_FOLLOWING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regFollow, 0 , 1); + addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ sqlite3VdbeAddOp3(v, @@ -282,22 +335,14 @@ static void windowCodeRowExprStep( } sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone); if( pMWin->eStart==TK_CURRENT || pMWin->eStart==TK_PRECEDING ){ if( pMWin->eStart==TK_PRECEDING ){ - addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regPrec, 0 , 1); - } - sqlite3VdbeAddOp2(v, OP_Next, csrPrec, sqlite3VdbeCurrentAddr(v)+1); - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - int i; - for(i=0; inArg; i++){ - sqlite3VdbeAddOp3(v, OP_Column, csrPrec, pWin->iArgCol+i, reg+i); - } - sqlite3VdbeAddOp3(v, OP_AggStep0, 1, reg, pWin->regAccum); - sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); - sqlite3VdbeChangeP5(v, (u8)pWin->nArg); + addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); } + sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1); + windowAggStep(pParse, pMWin, csrStart, 1, reg); if( pMWin->eStart==TK_PRECEDING ){ sqlite3VdbeJumpHere(v, addrIfPos2); } @@ -305,10 +350,10 @@ static void windowCodeRowExprStep( if( pMWin->eEnd==TK_FOLLOWING ){ sqlite3VdbeJumpHere(v, addrIfPos1); } - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); /* flush_partition_done: */ - sqlite3VdbeResolveLabel(v, addrDone); + sqlite3VdbeResolveLabel(v, lblFlushDone); sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); @@ -505,28 +550,28 @@ static void windowCodeDefaultStep( ** ** Replace the bit after "Rewind" in the above with: ** -** if( (regFollow--)<=0 ){ +** if( (regEnd--)<=0 ){ ** AggStep (csr3) ** Next (csr3) ** } ** AggFinal (xValue) ** Gosub addrGosub ** Next(csr) // if EOF goto flush_partition_done -** if( (regPrec--)<=0 ){ +** if( (regStart--)<=0 ){ ** AggStep (csr2, xInverse) ** Next (csr2) ** } ** ** ROWS BETWEEN FOLLOWING AND FOLLOWING ** -** regFollow = regFollow - regPrec +** regEnd = regEnd - regStart ** Rewind (csr,csr2,csr3) // if EOF goto flush_partition_done ** Aggstep (csr3) ** Next(csr3) // if EOF fall-through -** if( (regFollow--)<=0 ){ +** if( (regEnd--)<=0 ){ ** AggStep (csr2, xInverse) ** Next (csr2) -** if( (regPrec--)<=0 ){ +** if( (regStart--)<=0 ){ ** AggFinal (xValue) ** Gosub addrGosub ** Next(csr) // if EOF goto flush_partition_done @@ -536,7 +581,7 @@ static void windowCodeDefaultStep( ** ROWS BETWEEN UNBOUNDED PRECEDING AND PRECEDING ** ROWS BETWEEN FOLLOWING AND UNBOUNDED FOLLOWING ** -** Similar to the above, except with regPrec or regFollow set to infinity, +** Similar to the above, except with regStart or regEnd set to infinity, ** as appropriate. ** ** @@ -552,10 +597,8 @@ void sqlite3WindowCodeStep( ){ Window *pMWin = p->pWin; - if( pMWin->eType==TK_ROWS - && (pMWin->eStart==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING) - && (pMWin->eStart!=TK_FOLLOWING || pMWin->eEnd==TK_PRECEDING) - ){ + if( pMWin->pStart || pMWin->pEnd ){ + assert( pMWin->eType==TK_ROWS ); *pbLoop = 0; windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); return; diff --git a/test/window2.tcl b/test/window2.tcl index 44a13cbf54..c32249cc8b 100644 --- a/test/window2.tcl +++ b/test/window2.tcl @@ -219,6 +219,45 @@ execsql_test 2.13 { ) FROM t1 } +execsql_test 2.14 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING + ) FROM t1 +} + +execsql_test 2.15 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN 1 PRECEDING AND 0 PRECEDING + ) FROM t1 +} + +execsql_test 2.16 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING + ) FROM t1 +} + +execsql_test 2.17 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN 1 PRECEDING AND 2 PRECEDING + ) FROM t1 +} + +execsql_test 2.18 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND 2 PRECEDING + ) FROM t1 +} + ========== diff --git a/test/window2.test b/test/window2.test index a5d4ee853e..bc6c449235 100644 --- a/test/window2.test +++ b/test/window2.test @@ -130,6 +130,45 @@ do_execsql_test 2.13 { ) FROM t1 } {1 21 2 21 3 21 4 20 5 18 6 15} +do_execsql_test 2.14 { + SELECT a, sum(d) OVER ( + ORDER BY d + ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING + ) FROM t1 +} {1 {} 2 1 3 3 4 6 5 9 6 12} + +do_execsql_test 2.15 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN 1 PRECEDING AND 0 PRECEDING + ) FROM t1 +} {2 2 4 6 6 10 1 1 3 4 5 8} + +do_execsql_test 2.16 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING + ) FROM t1 +} {2 {} 4 2 6 4 1 {} 3 1 5 3} + +do_execsql_test 2.17 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN 1 PRECEDING AND 2 PRECEDING + ) FROM t1 +} {2 {} 4 {} 6 {} 1 {} 3 {} 5 {}} + +do_execsql_test 2.18 { + SELECT a, sum(d) OVER ( + PARTITION BY b + ORDER BY d + ROWS BETWEEN UNBOUNDED PRECEDING AND 2 PRECEDING + ) FROM t1 +} {2 {} 4 {} 6 2 1 {} 3 {} 5 1} + #========================================================================== finish_test