mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Begin adding support for more esoteric window frames.
FossilOrigin-Name: bc4b81d60d40583de0f929730159011c1a7696802532ebd02220de3ace94a60d
This commit is contained in:
20
manifest
20
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Fix\sminor\sproblems\son\sthis\sbranch.
|
C Begin\sadding\ssupport\sfor\smore\sesoteric\swindow\sframes.
|
||||||
D 2018-05-19T14:15:29.031
|
D 2018-05-21T19:45:11.880
|
||||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||||
F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
|
F Makefile.in bfc40f350586923e0419d2ea4b559c37ec10ee4b6e210e08c14401f8e340f0da
|
||||||
@@ -493,12 +493,12 @@ F src/printf.c 1d1b4a568a58d0f32a5ff26c6b98db8a6e1883467f343a6406263cacd2e60c21
|
|||||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||||
F src/resolve.c 446f60b2e0d2440bb233d6a69a4ed0f2ad030a4e63ac4b3cfc0e98cf73d9c5a3
|
F src/resolve.c 446f60b2e0d2440bb233d6a69a4ed0f2ad030a4e63ac4b3cfc0e98cf73d9c5a3
|
||||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||||
F src/select.c 34a5cb9e6a37d4f7e25160ba0caeb02bccc1abee462805ba5f3fde9abc266873
|
F src/select.c 1ebe775c0651bf357ab83b4c9b9139194149cfe1277dfa3e16f3ed73669b6b04
|
||||||
F src/shell.c.in 53affa90711f280342f7238f9c9aa9dcaed321ec6218a18043cf92154ef8a704
|
F src/shell.c.in 53affa90711f280342f7238f9c9aa9dcaed321ec6218a18043cf92154ef8a704
|
||||||
F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4
|
F src/sqlite.h.in 34be2d0d18bf4726538793bdc9854cd87f689fda4b3789515134cdbd68188cf4
|
||||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||||
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
|
F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
|
||||||
F src/sqliteInt.h 6b3400a90f179542045ed318c780bf73675b19b3de437506859adf998a41e125
|
F src/sqliteInt.h a3f0edb26ebfee6107fa99f3c300e71018ad23addeeba0746a4ac62425e36f3f
|
||||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||||
@@ -581,7 +581,7 @@ F src/where.c 60ec752fcbe9f9e0271ac60548d159a540a1ee47a4f9fedc85e88a3d0e392dd1
|
|||||||
F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53
|
F src/whereInt.h cbae2bcd37cfebdb7812a8b188cdb19634ced2b9346470d1c270556b0c33ea53
|
||||||
F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8
|
F src/wherecode.c 728c7f70731430ccdac807a79969873e1af6968bf1c4745dff3f9dd35f636cc8
|
||||||
F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
|
F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
|
||||||
F src/window.c 33d3751eb1442ce8a7e428e028cbc6c220359446a7ab2a9514244c3edfea1b63
|
F src/window.c da24f2e57a704dd8e0ce96df18e7442145582c65b4eb1c3176367e530d665928
|
||||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||||
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
|
||||||
@@ -1614,8 +1614,8 @@ F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
|
|||||||
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
|
||||||
F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
|
F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
|
||||||
F test/window1.test 5705337783d220b47f6fb4432264543b7557a05be8013d772f57d71f2fded271
|
F test/window1.test 5705337783d220b47f6fb4432264543b7557a05be8013d772f57d71f2fded271
|
||||||
F test/window2.tcl f9df2fded8de70fc4252030775351b42440bc10aa2eb07d2f625e5d14a666775
|
F test/window2.tcl 19a7d45c4502f00917649a171a4eb1da3670657f5cb102bce7a813e15a112b3f
|
||||||
F test/window2.test 57c0500f98a08fd372f7fecf60f0d2ade58ad3373dad5031063d0a278fd79b18
|
F test/window2.test 23daf252647d780f046a2e612d7f0492d4fe589b3302fd6c062b0abfff3743bf
|
||||||
F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
|
F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
|
||||||
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
|
F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
|
||||||
F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
|
F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
|
||||||
@@ -1732,7 +1732,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
|||||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||||
P c9f0f140941660ff368e5bb5752d54feb1964b7a9eac986d4bfb8f24a1c20d86
|
P 19c2e4b2f164521eab84cb0a0e12984be9431eaedd001dd3671e9ea1a6212353
|
||||||
R 6dddf8e93774e8d8cc7600c6ceb6a6b5
|
R 977b49583a548e1f3f37fbf1de8b5833
|
||||||
U dan
|
U dan
|
||||||
Z 12927ac87a28c4acd3b751d346125a3b
|
Z 314fa758fa6aadc36a0f111ad4e55744
|
||||||
|
@@ -1 +1 @@
|
|||||||
19c2e4b2f164521eab84cb0a0e12984be9431eaedd001dd3671e9ea1a6212353
|
bc4b81d60d40583de0f929730159011c1a7696802532ebd02220de3ace94a60d
|
146
src/select.c
146
src/select.c
@@ -530,14 +530,6 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Forward reference */
|
|
||||||
static KeyInfo *keyInfoFromExprList(
|
|
||||||
Parse *pParse, /* Parsing context */
|
|
||||||
ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
|
||||||
int iStart, /* Begin with this column of pList */
|
|
||||||
int nExtra /* Add this many extra columns to the end */
|
|
||||||
);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** An instance of this object holds information (beyond pParse and pSelect)
|
** An instance of this object holds information (beyond pParse and pSelect)
|
||||||
** needed to load the next result row that is to be added to the sorter.
|
** needed to load the next result row that is to be added to the sorter.
|
||||||
@@ -679,7 +671,7 @@ static void pushOntoSorter(
|
|||||||
memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
|
memset(pKI->aSortOrder, 0, pKI->nKeyField); /* Makes OP_Jump testable */
|
||||||
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
|
||||||
testcase( pKI->nAllField > pKI->nKeyField+2 );
|
testcase( pKI->nAllField > pKI->nKeyField+2 );
|
||||||
pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
|
pOp->p4.pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pSort->pOrderBy,nOBSat,
|
||||||
pKI->nAllField-pKI->nKeyField-1);
|
pKI->nAllField-pKI->nKeyField-1);
|
||||||
addrJmp = sqlite3VdbeCurrentAddr(v);
|
addrJmp = sqlite3VdbeCurrentAddr(v);
|
||||||
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
|
sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
|
||||||
@@ -1349,7 +1341,7 @@ int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
|
|||||||
** function is responsible for seeing that this structure is eventually
|
** function is responsible for seeing that this structure is eventually
|
||||||
** freed.
|
** freed.
|
||||||
*/
|
*/
|
||||||
static KeyInfo *keyInfoFromExprList(
|
KeyInfo *sqlite3KeyInfoFromExprList(
|
||||||
Parse *pParse, /* Parsing context */
|
Parse *pParse, /* Parsing context */
|
||||||
ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
ExprList *pList, /* Form the KeyInfo object from this ExprList */
|
||||||
int iStart, /* Begin with this column of pList */
|
int iStart, /* Begin with this column of pList */
|
||||||
@@ -5088,7 +5080,7 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
|
|||||||
"argument");
|
"argument");
|
||||||
pFunc->iDistinct = -1;
|
pFunc->iDistinct = -1;
|
||||||
}else{
|
}else{
|
||||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0);
|
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pE->x.pList,0,0);
|
||||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
|
||||||
(char*)pKeyInfo, P4_KEYINFO);
|
(char*)pKeyInfo, P4_KEYINFO);
|
||||||
}
|
}
|
||||||
@@ -6008,7 +6000,8 @@ int sqlite3Select(
|
|||||||
*/
|
*/
|
||||||
if( sSort.pOrderBy ){
|
if( sSort.pOrderBy ){
|
||||||
KeyInfo *pKeyInfo;
|
KeyInfo *pKeyInfo;
|
||||||
pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr);
|
pKeyInfo = sqlite3KeyInfoFromExprList(
|
||||||
|
pParse, sSort.pOrderBy, 0, pEList->nExpr);
|
||||||
sSort.iECursor = pParse->nTab++;
|
sSort.iECursor = pParse->nTab++;
|
||||||
sSort.addrSortIndex =
|
sSort.addrSortIndex =
|
||||||
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||||
@@ -6042,9 +6035,9 @@ int sqlite3Select(
|
|||||||
if( p->selFlags & SF_Distinct ){
|
if( p->selFlags & SF_Distinct ){
|
||||||
sDistinct.tabTnct = pParse->nTab++;
|
sDistinct.tabTnct = pParse->nTab++;
|
||||||
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
|
||||||
sDistinct.tabTnct, 0, 0,
|
sDistinct.tabTnct, 0, 0,
|
||||||
(char*)keyInfoFromExprList(pParse, p->pEList,0,0),
|
(char*)sqlite3KeyInfoFromExprList(pParse, p->pEList,0,0),
|
||||||
P4_KEYINFO);
|
P4_KEYINFO);
|
||||||
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
|
||||||
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
|
||||||
}else{
|
}else{
|
||||||
@@ -6052,22 +6045,15 @@ int sqlite3Select(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if( !isAgg && pGroupBy==0 ){
|
if( !isAgg && pGroupBy==0 ){
|
||||||
Window *pMWin = p->pWin; /* Master window object (or NULL) */
|
Window *pWin = p->pWin; /* Master window object (or NULL) */
|
||||||
int regPart = 0;
|
|
||||||
|
|
||||||
/* No aggregate functions and no GROUP BY clause */
|
/* No aggregate functions and no GROUP BY clause */
|
||||||
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
|
u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
|
||||||
assert( WHERE_USE_LIMIT==SF_FixedLimit );
|
assert( WHERE_USE_LIMIT==SF_FixedLimit );
|
||||||
wctrlFlags |= p->selFlags & SF_FixedLimit;
|
wctrlFlags |= p->selFlags & SF_FixedLimit;
|
||||||
|
|
||||||
if( pMWin ){
|
if( pWin ){
|
||||||
int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0);
|
sqlite3WindowCodeInit(pParse, pWin);
|
||||||
nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0);
|
|
||||||
if( nPart ){
|
|
||||||
regPart = pParse->nMem+1;
|
|
||||||
pParse->nMem += nPart;
|
|
||||||
sqlite3VdbeAddOp3(v, OP_Null, 0, regPart, regPart+nPart-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Begin the database scan. */
|
/* Begin the database scan. */
|
||||||
@@ -6098,112 +6084,18 @@ int sqlite3Select(
|
|||||||
}
|
}
|
||||||
|
|
||||||
assert( p->pEList==pEList );
|
assert( p->pEList==pEList );
|
||||||
if( pMWin ){
|
if( pWin ){
|
||||||
Window *pWin;
|
int addrGosub = sqlite3VdbeMakeLabel(v);
|
||||||
int k;
|
int regGosub = ++pParse->nMem;
|
||||||
int iSubCsr = p->pSrc->a[0].iCursor;
|
|
||||||
int nSub = p->pSrc->a[0].pTab->nCol;
|
|
||||||
int reg = pParse->nMem+1;
|
|
||||||
int regRecord = reg+nSub;
|
|
||||||
int regRowid = regRecord+1;
|
|
||||||
int regGosub = regRowid+1;
|
|
||||||
int addr;
|
int addr;
|
||||||
int addrGosub;
|
|
||||||
|
|
||||||
pParse->nMem += nSub + 3;
|
sqlite3WindowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);
|
||||||
|
|
||||||
/* Martial the row returned by the sub-select into an array of
|
|
||||||
** registers. */
|
|
||||||
for(k=0; k<nSub; k++){
|
|
||||||
sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if this is the start of a new partition or peer group. */
|
|
||||||
if( regPart ){
|
|
||||||
ExprList *pPart = pMWin->pPartition;
|
|
||||||
int nPart = (pPart ? pPart->nExpr : 0);
|
|
||||||
ExprList *pOrderBy = pMWin->pOrderBy;
|
|
||||||
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
|
|
||||||
int addrGoto = 0;
|
|
||||||
int addrJump = 0;
|
|
||||||
|
|
||||||
if( pPart ){
|
|
||||||
int regNewPart = reg + pMWin->nBufferCol;
|
|
||||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pPart, 0, 0);
|
|
||||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, regPart, nPart);
|
|
||||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
|
||||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
|
||||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
||||||
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
|
|
||||||
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
|
|
||||||
}
|
|
||||||
if( pOrderBy ){
|
|
||||||
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pOrderBy ){
|
|
||||||
int regNewPeer = reg + pMWin->nBufferCol + nPart;
|
|
||||||
int regPeer = regPart + nPart;
|
|
||||||
|
|
||||||
KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
|
||||||
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
|
||||||
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
|
||||||
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
|
||||||
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
|
||||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
||||||
sqlite3VdbeAddOp3(v,
|
|
||||||
OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
|
|
||||||
);
|
|
||||||
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
||||||
}
|
|
||||||
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
|
||||||
}
|
|
||||||
|
|
||||||
addrGosub = sqlite3VdbeAddOp1(v, OP_Gosub, regGosub);
|
|
||||||
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
|
||||||
sqlite3VdbeAddOp3(
|
|
||||||
v, OP_Copy, reg+pMWin->nBufferCol, regPart, nPart+nPeer-1
|
|
||||||
);
|
|
||||||
|
|
||||||
sqlite3VdbeJumpHere(v, addrJump);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Invoke step function for window functions */
|
|
||||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
||||||
sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
|
|
||||||
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
||||||
sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buffer the current row in the ephemeral table. */
|
|
||||||
if( pMWin->nBufferCol>0 ){
|
|
||||||
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
|
|
||||||
}else{
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
|
|
||||||
sqlite3VdbeAppendP4(v, (void*)"", 0);
|
|
||||||
}
|
|
||||||
sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
|
|
||||||
sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
|
|
||||||
|
|
||||||
/* End the database scan loop. */
|
|
||||||
sqlite3WhereEnd(pWInfo);
|
|
||||||
|
|
||||||
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
|
||||||
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
|
|
||||||
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
|
|
||||||
}
|
|
||||||
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, sqlite3VdbeCurrentAddr(v)+2);
|
|
||||||
|
|
||||||
sqlite3VdbeAddOp0(v, OP_Goto);
|
sqlite3VdbeAddOp0(v, OP_Goto);
|
||||||
if( regPart ){
|
sqlite3VdbeResolveLabel(v, addrGosub);
|
||||||
sqlite3VdbeJumpHere(v, addrGosub);
|
addr = sqlite3VdbeAddOp1(v, OP_Rewind, pWin->iEphCsr);
|
||||||
}
|
|
||||||
addr = sqlite3VdbeAddOp1(v, OP_Rewind, pMWin->iEphCsr);
|
|
||||||
selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0);
|
selectInnerLoop(pParse, p, -1, &sSort, &sDistinct, pDest, addr+1, 0);
|
||||||
sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr+1);
|
sqlite3VdbeAddOp2(v, OP_Next, pWin->iEphCsr, addr+1);
|
||||||
sqlite3VdbeJumpHere(v, addr);
|
sqlite3VdbeJumpHere(v, addr);
|
||||||
sqlite3VdbeAddOp1(v, OP_Return, regGosub);
|
sqlite3VdbeAddOp1(v, OP_Return, regGosub);
|
||||||
sqlite3VdbeJumpHere(v, addr-1); /* OP_Goto jumps here */
|
sqlite3VdbeJumpHere(v, addr-1); /* OP_Goto jumps here */
|
||||||
@@ -6346,7 +6238,7 @@ int sqlite3Select(
|
|||||||
** will be converted into a Noop.
|
** will be converted into a Noop.
|
||||||
*/
|
*/
|
||||||
sAggInfo.sortingIdx = pParse->nTab++;
|
sAggInfo.sortingIdx = pParse->nTab++;
|
||||||
pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn);
|
pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn);
|
||||||
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
|
||||||
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
|
||||||
0, (char*)pKeyInfo, P4_KEYINFO);
|
0, (char*)pKeyInfo, P4_KEYINFO);
|
||||||
|
@@ -3485,6 +3485,7 @@ struct Window {
|
|||||||
FuncDef *pFunc;
|
FuncDef *pFunc;
|
||||||
int nArg;
|
int nArg;
|
||||||
|
|
||||||
|
int regPart;
|
||||||
Expr *pOwner; /* Expression object this window is attached to */
|
Expr *pOwner; /* Expression object this window is attached to */
|
||||||
int nBufferCol; /* Number of columns in buffer table */
|
int nBufferCol; /* Number of columns in buffer table */
|
||||||
int iArgCol; /* Offset of first argument for this function */
|
int iArgCol; /* Offset of first argument for this function */
|
||||||
@@ -3494,6 +3495,8 @@ void sqlite3WindowDelete(sqlite3*, Window*);
|
|||||||
Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
|
Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
|
||||||
void sqlite3WindowAttach(Parse*, Expr*, Window*);
|
void sqlite3WindowAttach(Parse*, Expr*, Window*);
|
||||||
int sqlite3WindowCompare(Parse*, Window*, Window*);
|
int sqlite3WindowCompare(Parse*, Window*, Window*);
|
||||||
|
void sqlite3WindowCodeInit(Parse*, Window*);
|
||||||
|
void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Assuming zIn points to the first byte of a UTF-8 character,
|
** Assuming zIn points to the first byte of a UTF-8 character,
|
||||||
@@ -4202,6 +4205,8 @@ KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
|
|||||||
void sqlite3KeyInfoUnref(KeyInfo*);
|
void sqlite3KeyInfoUnref(KeyInfo*);
|
||||||
KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
|
KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
|
||||||
KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
|
KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
|
||||||
|
KeyInfo *sqlite3KeyInfoFromExprList(Parse*, ExprList*, int, int);
|
||||||
|
|
||||||
#ifdef SQLITE_DEBUG
|
#ifdef SQLITE_DEBUG
|
||||||
int sqlite3KeyInfoIsWriteable(KeyInfo*);
|
int sqlite3KeyInfoIsWriteable(KeyInfo*);
|
||||||
#endif
|
#endif
|
||||||
|
212
src/window.c
212
src/window.c
@@ -66,4 +66,216 @@ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sqlite3WindowCodeInit(Parse *pParse, Window *pWin){
|
||||||
|
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||||
|
int nPart = (pWin->pPartition ? pWin->pPartition->nExpr : 0);
|
||||||
|
nPart += (pWin->pOrderBy ? pWin->pOrderBy->nExpr : 0);
|
||||||
|
if( nPart ){
|
||||||
|
pWin->regPart = pParse->nMem+1;
|
||||||
|
pParse->nMem += nPart;
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Null, 0, pWin->regPart, pWin->regPart+nPart-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||||
|
**
|
||||||
|
** ...
|
||||||
|
** if( new partition ){
|
||||||
|
** AggFinal (xFinalize)
|
||||||
|
** Gosub addrGosub
|
||||||
|
** ResetSorter eph-table
|
||||||
|
** }
|
||||||
|
** else if( new peer ){
|
||||||
|
** AggFinal (xValue)
|
||||||
|
** Gosub addrGosub
|
||||||
|
** ResetSorter eph-table
|
||||||
|
** }
|
||||||
|
** AggStep
|
||||||
|
** Insert (record into eph-table)
|
||||||
|
** sqlite3WhereEnd()
|
||||||
|
** AggFinal (xFinalize)
|
||||||
|
** Gosub addrGosub
|
||||||
|
**
|
||||||
|
** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||||
|
**
|
||||||
|
** As above, except take no action for a "new peer". Invoke
|
||||||
|
** the sub-routine once only for each partition.
|
||||||
|
**
|
||||||
|
** RANGE BETWEEN CURRENT ROW AND CURRENT ROW
|
||||||
|
**
|
||||||
|
** As above, except that the "new peer" condition is handled in the
|
||||||
|
** same way as "new partition" (so there is no "else if" block).
|
||||||
|
**
|
||||||
|
** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||||
|
**
|
||||||
|
** One way is to just reverse the sort order and do as for BETWEEN
|
||||||
|
** UNBOUNDED PRECEDING AND CURRENT ROW. But that is not quite the same for
|
||||||
|
** things like group_concat(). And perhaps other user defined aggregates
|
||||||
|
** as well.
|
||||||
|
**
|
||||||
|
** ...
|
||||||
|
** if( new partition ){
|
||||||
|
** Gosub flush_partition;
|
||||||
|
** ResetSorter eph-table
|
||||||
|
** }
|
||||||
|
** AggStep
|
||||||
|
** Insert (record into eph-table)
|
||||||
|
** sqlite3WhereEnd()
|
||||||
|
** Gosub flush_partition
|
||||||
|
**
|
||||||
|
** flush_partition:
|
||||||
|
** OpenDup (csr -> csr2)
|
||||||
|
** foreach (record in eph-table) {
|
||||||
|
** if( new peer ){
|
||||||
|
** while( csr2!=csr ){
|
||||||
|
** AggStep (xInverse)
|
||||||
|
** Next (csr2)
|
||||||
|
** }
|
||||||
|
** }
|
||||||
|
** AggFinal (xValue)
|
||||||
|
** Gosub addrGosub
|
||||||
|
** }
|
||||||
|
**
|
||||||
|
**========================================================================
|
||||||
|
**
|
||||||
|
** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||||
|
** ...
|
||||||
|
** if( new partition ){
|
||||||
|
** AggFinal (xFinalize)
|
||||||
|
** }
|
||||||
|
** AggStep
|
||||||
|
** AggFinal (xValue)
|
||||||
|
** Gosub addrGosub
|
||||||
|
** sqlite3WhereEnd()
|
||||||
|
**
|
||||||
|
** ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||||
|
** ROWS BETWEEN CURRENT ROW AND CURRENT ROW
|
||||||
|
** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||||
|
**
|
||||||
|
**========================================================================
|
||||||
|
**
|
||||||
|
** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> PRECEDING
|
||||||
|
** ROWS BETWEEN <expr> PRECEDING AND <expr> PRECEDING
|
||||||
|
** ROWS BETWEEN <expr> PRECEDING AND CURRENT ROW
|
||||||
|
** ROWS BETWEEN UNBOUNDED PRECEDING AND <expr> FOLLOWING
|
||||||
|
** ROWS BETWEEN <expr> PRECEDING AND <expr> FOLLOWING
|
||||||
|
** ROWS BETWEEN CURRENT ROW AND <expr> FOLLOWING
|
||||||
|
** ROWS BETWEEN <expr> FOLLOWING AND <expr> FOLLOWING
|
||||||
|
** ROWS BETWEEN <expr> PRECEDING AND UNBOUNDED FOLLOWING
|
||||||
|
** ROWS BETWEEN <expr> FOLLOWING AND UNBOUNDED FOLLOWING
|
||||||
|
**
|
||||||
|
** Cases that involve <expr> PRECEDING or <expr> FOLLOWING.
|
||||||
|
**
|
||||||
|
** ...
|
||||||
|
** Insert (record in eph-table)
|
||||||
|
** sqlite3WhereEnd()
|
||||||
|
**
|
||||||
|
*/
|
||||||
|
void sqlite3WindowCodeStep(
|
||||||
|
Parse *pParse,
|
||||||
|
Select *p,
|
||||||
|
WhereInfo *pWInfo,
|
||||||
|
int regGosub,
|
||||||
|
int addrGosub
|
||||||
|
){
|
||||||
|
Vdbe *v = sqlite3GetVdbe(pParse);
|
||||||
|
Window *pWin;
|
||||||
|
Window *pMWin = p->pWin;
|
||||||
|
int k;
|
||||||
|
int iSubCsr = p->pSrc->a[0].iCursor;
|
||||||
|
int nSub = p->pSrc->a[0].pTab->nCol;
|
||||||
|
int reg = pParse->nMem+1;
|
||||||
|
int regRecord = reg+nSub;
|
||||||
|
int regRowid = regRecord+1;
|
||||||
|
int addr;
|
||||||
|
|
||||||
|
pParse->nMem += nSub + 2;
|
||||||
|
|
||||||
|
/* Martial the row returned by the sub-select into an array of
|
||||||
|
** registers. */
|
||||||
|
for(k=0; k<nSub; k++){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this is the start of a new partition or peer group. */
|
||||||
|
if( pMWin->regPart ){
|
||||||
|
ExprList *pPart = pMWin->pPartition;
|
||||||
|
int nPart = (pPart ? pPart->nExpr : 0);
|
||||||
|
ExprList *pOrderBy = pMWin->pOrderBy;
|
||||||
|
int nPeer = (pOrderBy ? pOrderBy->nExpr : 0);
|
||||||
|
int addrGoto = 0;
|
||||||
|
int addrJump = 0;
|
||||||
|
|
||||||
|
if( pPart ){
|
||||||
|
int regNewPart = reg + pMWin->nBufferCol;
|
||||||
|
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
|
||||||
|
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart);
|
||||||
|
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||||
|
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||||
|
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||||
|
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
|
||||||
|
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
|
||||||
|
}
|
||||||
|
if( pOrderBy ){
|
||||||
|
addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( pOrderBy ){
|
||||||
|
int regNewPeer = reg + pMWin->nBufferCol + nPart;
|
||||||
|
int regPeer = pMWin->regPart + nPart;
|
||||||
|
|
||||||
|
KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0);
|
||||||
|
if( addrJump ) sqlite3VdbeJumpHere(v, addrJump);
|
||||||
|
addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer);
|
||||||
|
sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
|
||||||
|
addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2);
|
||||||
|
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||||
|
sqlite3VdbeAddOp3(v,
|
||||||
|
OP_AggFinal, pWin->regAccum, pWin->nArg, pWin->regResult
|
||||||
|
);
|
||||||
|
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
||||||
|
}
|
||||||
|
if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||||
|
sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
|
||||||
|
sqlite3VdbeAddOp3(
|
||||||
|
v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1
|
||||||
|
);
|
||||||
|
|
||||||
|
sqlite3VdbeJumpHere(v, addrJump);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invoke step function for window functions */
|
||||||
|
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_AggStep0, 0, reg+pWin->iArgCol, pWin->regAccum);
|
||||||
|
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
||||||
|
sqlite3VdbeChangeP5(v, (u8)pWin->nArg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buffer the current row in the ephemeral table. */
|
||||||
|
if( pMWin->nBufferCol>0 ){
|
||||||
|
sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, pMWin->nBufferCol, regRecord);
|
||||||
|
}else{
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Blob, 0, regRecord);
|
||||||
|
sqlite3VdbeAppendP4(v, (void*)"", 0);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid);
|
||||||
|
sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid);
|
||||||
|
|
||||||
|
/* End the database scan loop. */
|
||||||
|
sqlite3WhereEnd(pWInfo);
|
||||||
|
|
||||||
|
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
|
||||||
|
sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, pWin->nArg);
|
||||||
|
sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF);
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult);
|
||||||
|
}
|
||||||
|
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -39,13 +39,18 @@ proc execsql {sql} {
|
|||||||
}
|
}
|
||||||
#puts $lSql
|
#puts $lSql
|
||||||
|
|
||||||
set ret [list]
|
set ret ""
|
||||||
foreach stmt $lSql {
|
foreach stmt $lSql {
|
||||||
set res [pg_exec $::db $stmt]
|
set res [pg_exec $::db $stmt]
|
||||||
set err [pg_result $res -error]
|
set err [pg_result $res -error]
|
||||||
if {$err!=""} { error $err }
|
if {$err!=""} { error $err }
|
||||||
for {set i 0} {$i < [pg_result $res -numTuples]} {incr i} {
|
for {set i 0} {$i < [pg_result $res -numTuples]} {incr i} {
|
||||||
lappend ret {*}[pg_result $res -getTuple $i]
|
if {$i==0} {
|
||||||
|
set ret [pg_result $res -getTuple 0]
|
||||||
|
} else {
|
||||||
|
append ret " [pg_result $res -getTuple $i]"
|
||||||
|
}
|
||||||
|
# lappend ret {*}[pg_result $res -getTuple $i]
|
||||||
}
|
}
|
||||||
pg_result $res -clear
|
pg_result $res -clear
|
||||||
}
|
}
|
||||||
@@ -89,6 +94,15 @@ puts $::fd [string trimleft "
|
|||||||
puts $::fd ""
|
puts $::fd ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proc -- {args} {
|
||||||
|
puts $::fd "# $args"
|
||||||
|
}
|
||||||
|
|
||||||
|
proc ========== {args} {
|
||||||
|
puts $::fd "#[string repeat = 74]"
|
||||||
|
puts $::fd ""
|
||||||
|
}
|
||||||
|
|
||||||
proc finish_test {} {
|
proc finish_test {} {
|
||||||
puts $::fd finish_test
|
puts $::fd finish_test
|
||||||
close $::fd
|
close $::fd
|
||||||
@@ -122,6 +136,30 @@ execsql_test 1.3 {
|
|||||||
SELECT sum(d) OVER (PARTITION BY b) FROM t1;
|
SELECT sum(d) OVER (PARTITION BY b) FROM t1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
puts $::fd finish_test
|
||||||
|
==========
|
||||||
|
|
||||||
|
execsql_test 2.1 {
|
||||||
|
SELECT a, sum(d) OVER (
|
||||||
|
PARTITION BY b ORDER BY d
|
||||||
|
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||||
|
) FROM t1
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 2.2 {
|
||||||
|
SELECT a, sum(d) OVER (
|
||||||
|
ORDER BY b
|
||||||
|
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||||
|
) FROM t1
|
||||||
|
}
|
||||||
|
|
||||||
|
execsql_test 2.3 {
|
||||||
|
SELECT a, sum(d) OVER (
|
||||||
|
ORDER BY d
|
||||||
|
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||||
|
) FROM t1
|
||||||
|
}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
|
||||||
|
|
||||||
|
@@ -32,14 +32,38 @@ do_execsql_test 1.0 {
|
|||||||
|
|
||||||
do_execsql_test 1.1 {
|
do_execsql_test 1.1 {
|
||||||
SELECT c, sum(d) OVER (PARTITION BY b ORDER BY c) FROM t1;
|
SELECT c, sum(d) OVER (PARTITION BY b ORDER BY c) FROM t1;
|
||||||
} {four 4 six 10 two 12 five 5 one 6 three 9}
|
} {four 4 six 10 two 12 five 5 one 6 three 9}
|
||||||
|
|
||||||
do_execsql_test 1.2 {
|
do_execsql_test 1.2 {
|
||||||
SELECT sum(d) OVER () FROM t1;
|
SELECT sum(d) OVER () FROM t1;
|
||||||
} {21 21 21 21 21 21}
|
} {21 21 21 21 21 21}
|
||||||
|
|
||||||
do_execsql_test 1.3 {
|
do_execsql_test 1.3 {
|
||||||
SELECT sum(d) OVER (PARTITION BY b) FROM t1;
|
SELECT sum(d) OVER (PARTITION BY b) FROM t1;
|
||||||
} {12 12 12 9 9 9}
|
} {12 12 12 9 9 9}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
#==========================================================================
|
||||||
|
|
||||||
|
do_execsql_test 2.1 {
|
||||||
|
SELECT a, sum(d) OVER (
|
||||||
|
PARTITION BY b ORDER BY d
|
||||||
|
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||||
|
) FROM t1
|
||||||
|
} {2 12 4 10 6 6 1 9 3 8 5 5}
|
||||||
|
|
||||||
|
do_execsql_test 2.2 {
|
||||||
|
SELECT a, sum(d) OVER (
|
||||||
|
ORDER BY b
|
||||||
|
RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
|
||||||
|
) FROM t1
|
||||||
|
} {2 21 4 21 6 21 1 9 3 9 5 9}
|
||||||
|
|
||||||
|
do_execsql_test 2.3 {
|
||||||
|
SELECT a, sum(d) OVER (
|
||||||
|
ORDER BY d
|
||||||
|
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
|
||||||
|
) FROM t1
|
||||||
|
} {1 21 2 21 3 21 4 21 5 21 6 21}
|
||||||
|
|
||||||
finish_test
|
finish_test
|
||||||
|
Reference in New Issue
Block a user