]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix problem with window functions min() and max() when used with a PARTITION
authordan <dan@noemail.net>
Thu, 14 Jun 2018 19:06:36 +0000 (19:06 +0000)
committerdan <dan@noemail.net>
Thu, 14 Jun 2018 19:06:36 +0000 (19:06 +0000)
clause and a frame starting point other than "UNBOUNDED PRECEDING".

FossilOrigin-Name: 43eb1e75a4d7ac0973ed8589bbaf379c24cdc8eacc4e613610d2d4c24d385dc1

12 files changed:
manifest
manifest.uuid
src/attach.c
src/select.c
src/sqliteInt.h
src/vdbe.c
src/vdbemem.c
src/window.c
test/permutations.test
test/window1.test
test/window4.tcl
test/window4.test

index f68fad8115355891a8330a2829fabe12962ad4de..fa813d09b48021ad6ccccb48b83a88fd38f9327c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
-D 2018-06-14T14:30:51.987
+C Fix\sproblem\swith\swindow\sfunctions\smin()\sand\smax()\swhen\sused\swith\sa\sPARTITION\nclause\sand\sa\sframe\sstarting\spoint\sother\sthan\s"UNBOUNDED\sPRECEDING".
+D 2018-06-14T19:06:36.904
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F Makefile.in 498b77b89a8cb42f2ee20fcd6317f279a45c0d6ff40d27825f94b69884c09bbe
@@ -431,7 +431,7 @@ F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
 F src/alter.c 819b14b58e71565f8da505a9c1d5d9d904605f85cd64179cf9c7d1edcdad6c25
 F src/analyze.c 41f0b8d638fc2a7309477904ac38e535f2aabea3256da3251e529730e099df77
-F src/attach.c 3af6abc40733d10014b401c89a4e8ecfa7c3855517c62004461875220a3af453
+F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a
 F src/auth.c a38f3c63c974787ecf75e3213f8cac6568b9a7af7591fb0372ec0517dd16dca8
 F src/backup.c 78d3cecfbe28230a3a9a1793e2ead609f469be43e8f486ca996006be551857ab
 F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
@@ -495,12 +495,12 @@ F src/printf.c 7f6f3cba8e0c49c19e30a1ff4e9aeda6e06814dcbad4b664a69e1b6cb6e7e365
 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
 F src/resolve.c a8cf3d6144f6a821f002dad72f80659691e827a96e6da6dedf8b263edefe3a80
 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
-F src/select.c 224312eb28c1170117c8cef29abc8f6a420b2a60df26543df75632b731ecac8d
+F src/select.c 7e8e439bf8bf732860566ccceebd57d934bf1aceca213c394d825dde60473f8e
 F src/shell.c.in 8578421c5fb2a972461b2a996f7173646e55e0dbd2a2eee30c8f5dc7d3dbadfd
 F src/sqlite.h.in 19de593baa0667854730e7b8bc2e3039c20ee80a4d537e9b5ec2038947fe3daf
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7
-F src/sqliteInt.h f6c6b3b9690c6e3cb35b482deb4873dddb16fc127d97ce99b103ba5793b78640
+F src/sqliteInt.h 97525ef265cfca3cf39c87b73dd1e39f9260ee2f25fb0cee64bbbe26eb9f3888
 F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
@@ -565,13 +565,13 @@ F src/upsert.c 47edd408cc73f8d3c00a140550d1ad180b407c146285947969dd09874802bf88
 F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
 F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
 F src/vacuum.c 36e7d21a20c0bf6ef4ef7c399d192b5239410b7c4d3c1070fba4e30810d0b855
-F src/vdbe.c f68624da05fefc48eb663c3ceaba4fd9a999b8f9a15b957312e42ceb687a3622
+F src/vdbe.c 55bc870dcab52f7eac5a84d84e13e68122308997975d066f450a42c24d80df32
 F src/vdbe.h 9c8c245fa3785266c269ab02c135c836ff49a307612186686bcdae500f409945
 F src/vdbeInt.h d99f1c3da17b4ed271efc2f52898dd9a577dee077da47c2a014bc128f3cdba2a
 F src/vdbeapi.c af4a3de00d1851bcbc55b85dfbe52849aa2b1e17b4a5a1f3d9c257df7af361ff
 F src/vdbeaux.c c2d65c763b0811afe409e02e9d0d1f300c6e97892474fc94eec3da71955cd418
 F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
-F src/vdbemem.c 7301d5c4f98069aead603c9a5b28e0a8dd793b1c57570d67e45aab6b87bf5fec
+F src/vdbemem.c b8f3bb3bed82774ee352fa3405c83de2fc3e528638adfd3d15fa9a0da5c03d07
 F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
 F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392
 F src/vtab.c 10ea07dec111de6fb0a4fc87a7ffa4c65fdc088a19dbfaf7d6f2b128f2e8eb7b
@@ -583,7 +583,7 @@ F src/where.c 0bcbf9e191ca07f9ea2008aa80e70ded46bcdffd26560c83397da501f00aece6
 F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4
 F src/wherecode.c 3317f2b083a66d3e65a03edf316ade4ccb0a99c9956273282ebb579b95d4ba96
 F src/whereexpr.c 19cf35cdd9bf6d5589d8a5c960d99259761136187a2319a6e14d11cf1abe14c2
-F src/window.c 0a6b366a3301c68172fcdbebd5b9ddf0466cdc6533ff92ad859b4acd06aaa29b
+F src/window.c 3fc03f5ac20516d218933bc9eaf37863a511d9b9fffb3e37a9409e7a25efca99
 F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
 F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@ -1149,7 +1149,7 @@ F test/parser1.test 391b9bf9a229547a129c61ac345ed1a6f5eb1854
 F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
 F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
 F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
-F test/permutations.test 10793f1de89a226fa22dde9ba9398de22571fee1bfb53a935a11be4aa014704f
+F test/permutations.test 5c2167e03dc55ff697e11bb3abf10c66ba452e4afb2dbd85a2b144048355300e
 F test/pg_common.tcl b50727fe1ee3369d0421eadea2c2fd247bfb9d89a8e06ececf2f6866de17abd8
 F test/pragma.test 7c8cfc328a1717a95663cf8edb06c52ddfeaf97bb0aee69ae7457132e8d39e7d
 F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
@@ -1617,13 +1617,13 @@ F test/win32heap.test 10fd891266bd00af68671e702317726375e5407561d859be1aa04696f2
 F test/win32lock.test fbf107c91d8f5512be5a5b87c4c42ab9fdd54972
 F test/win32longpath.test 169c75a3b2e43481f4a62122510210c67b08f26d
 F test/win32nolock.test ac4f08811a562e45a5755e661f45ca85892bdbbc
-F test/window1.test 94c626fe8d9eced3e3d5ef0a2106209904daba77d549aafde09eba1db8e98c3e
+F test/window1.test 68607fd4cfa24f5cdd6029a63a2a7c062042ff5787fa054aa5c0b006154fe183
 F test/window2.tcl 0983de5eade5eeda49469244799d5331bfe3199fca3f6c6d2a836aa08f4fba1b
 F test/window2.test 79747b2edde4ad424e0752b27529aedc86e91f3d8d88846fa17ff0cb67f65086
 F test/window3.tcl 654d61d73e10db089b22514d498bb23ec310f720c0f4b5f69f67fda83d672048
 F test/window3.test 41727668ee31d2ba50f78efcb5bf1bda2c5cffd889aa65243511004669d1ac25
-F test/window4.tcl 09167855f695ef94312da965532bc13f8027411de8ce442664fa74949f9df011
-F test/window4.test eb0cf5740de803a4a9373b2c30b73986a4fb1662149260ccf05458abba312ba5
+F test/window4.tcl 3388b29669144bccd012bf5e237d165cf8eff2a310c26cc43a70279775f2fb01
+F test/window4.test f14ecc20c004a2a0d5fd8530266ea4255e3c0a55681bce1fea24af04ea82005f
 F test/with1.test 58475190cd8caaeebea8cfeb2a264ec97a0c492b8ffe9ad20cefbb23df462f96
 F test/with2.test e0030e2f0267a910d6c0e4f46f2dfe941c1cc0d4f659ba69b3597728e7e8f1ab
 F test/with3.test 5e8ce2c585170bbbc0544e2a01a4941fa0be173ba5265e5c92eb588cd99a232d
@@ -1740,7 +1740,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 bb915854d435bdd78f141d70e23527e97922ec176acd3ed8060c78dffc96bab8 ee431d55eba618cfba414c3946b3162bc205a70dd4e43d74a7623be332b94c92
-R 1cc4d33069ce5c96e4421646ef097697
+P 5cf5f1808a51f9c2cfc98dd49b4f1ce860b53e935287f89868ce2fdbace8eb06
+R ef2b7f57cdc1146bf754373b3823f53c
 U dan
-Z c65e558489cd4a1cde706d8fb1c8cdef
+Z e015141b8a297bc169625a565b97966a
index 7b50e2ba90b96889fb1c3469776f2d89aad46b49..fe46aaa1666272a340d9f67d4fe22c8e79e3d976 100644 (file)
@@ -1 +1 @@
-5cf5f1808a51f9c2cfc98dd49b4f1ce860b53e935287f89868ce2fdbace8eb06
\ No newline at end of file
+43eb1e75a4d7ac0973ed8589bbaf379c24cdc8eacc4e613610d2d4c24d385dc1
\ No newline at end of file
index 1f276156b8713de0a17825de2cd611f374b6788b..42ae53694269c128cb74cf7153b628edbf3524da 100644 (file)
@@ -414,7 +414,7 @@ void sqlite3Detach(Parse *pParse, Expr *pDbname){
     0,                /* pNext */
     detachFunc,       /* xSFunc */
     0,                /* xFinalize */
-    0, 0,
+    0, 0,             /* xValue, xInverse */
     "sqlite_detach",  /* zName */
     {0}
   };
@@ -434,7 +434,7 @@ void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
     0,                /* pNext */
     attachFunc,       /* xSFunc */
     0,                /* xFinalize */
-    0, 0,
+    0, 0,             /* xValue, xInverse */
     "sqlite_attach",  /* zName */
     {0}
   };
index c90a978d1ade2e43fb063f62f3166c63f227b584..a3a85503b5c9f8b925878dff84ec761e1ce2f55a 100644 (file)
@@ -3673,6 +3673,10 @@ static void substSelect(
 **        "SELECT x FROM (SELECT max(y), x FROM t1)" would not necessarily
 **        return the value X for which Y was maximal.)
 **
+**  (25)  If either the subquery or the parent query contains a window
+**        function in the select list or ORDER BY clause, flattening
+**        is not attempted.
+**
 **
 ** In this routine, the "p" parameter is a pointer to the outer query.
 ** The subquery is p->pSrc->a[iFrom].  isAgg is true if the outer query
@@ -3716,7 +3720,7 @@ static int flattenSubquery(
   pSub = pSubitem->pSelect;
   assert( pSub!=0 );
 
-  if( p->pWin || pSub->pWin ) return 0;
+  if( p->pWin || pSub->pWin ) return 0;                  /* Restriction (25) */
 
   pSubSrc = pSub->pSrc;
   assert( pSubSrc );
@@ -4587,12 +4591,20 @@ static void selectPopWith(Walker *pWalker, Select *p){
 #define selectPopWith 0
 #endif
 
+/*
+** The SrcList_item structure passed as the second argument represents a
+** sub-query in the FROM clause of a SELECT statement. This function
+** allocates and populates the SrcList_item.pTab object. If successful,
+** SQLITE_OK is returned. Otherwise, if an OOM error is encountered,
+** SQLITE_NOMEM.
+*/
 int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
   Select *pSel = pFrom->pSelect;
   Table *pTab;
 
+  assert( pSel );
   pFrom->pTab = pTab = sqlite3DbMallocZero(pParse->db, sizeof(Table));
-  if( pTab==0 ) return WRC_Abort;
+  if( pTab==0 ) return SQLITE_NOMEM;
   pTab->nTabRef = 1;
   if( pFrom->zAlias ){
     pTab->zName = sqlite3DbStrDup(pParse->db, pFrom->zAlias);
@@ -4605,7 +4617,7 @@ int sqlite3ExpandSubquery(Parse *pParse, struct SrcList_item *pFrom){
   pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
   pTab->tabFlags |= TF_Ephemeral;
 
-  return WRC_Continue;
+  return SQLITE_OK;
 }
 
 /*
index e63cc468908b17dc0cbdd691159c927470bdfcff..ce84441a131669d0951fc620615c6771d9159f98 100644 (file)
@@ -1685,7 +1685,7 @@ struct FuncDestructor {
 #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */
 #define SQLITE_FUNC_OFFSET   0x8000 /* Built-in sqlite_offset() function */
 #define SQLITE_FUNC_WINDOW  0x10000 /* Built-in window-only function */
-#define SQLITE_FUNC_WINDOW_SIZE  0x20000
+#define SQLITE_FUNC_WINDOW_SIZE  0x20000  /* Requires partition size as arg. */
 
 /*
 ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
@@ -3485,6 +3485,10 @@ struct TreeView {
 };
 #endif /* SQLITE_DEBUG */
 
+/*
+** Object used to encode the OVER() clause attached to a window-function
+** invocation. And some fields used while generating VM code for the same.
+*/
 struct Window {
   char *zName;            /* Name of window (may be NULL) */
   ExprList *pPartition;   /* PARTITION BY clause */
@@ -3524,6 +3528,7 @@ int sqlite3WindowRewrite(Parse*, Select*);
 int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
 void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
 Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
+void sqlite3WindowFunctions(void);
 
 /*
 ** Assuming zIn points to the first byte of a UTF-8 character,
@@ -4198,7 +4203,6 @@ extern sqlite3_uint64 sqlite3NProfileCnt;
 void sqlite3RootPageMoved(sqlite3*, int, int, int);
 void sqlite3Reindex(Parse*, Token*, Token*);
 void sqlite3AlterFunctions(void);
-void sqlite3WindowFunctions(void);
 void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
 int sqlite3GetToken(const unsigned char *, int *);
 void sqlite3NestedParse(Parse*, const char*, ...);
index b3f5ffbafd787177fd489a0270136ff00c62bc1f..e2db54dc13d11442dc8ebeb4b5daa566dcfbd774 100644 (file)
@@ -5082,7 +5082,7 @@ case OP_Sort: {        /* jump */
   p->aCounter[SQLITE_STMTSTATUS_SORT]++;
   /* Fall through into OP_Rewind */
 }
-/* Opcode: Rewind P1 P2 * * *
+/* Opcode: Rewind P1 P2 * * P5
 **
 ** The next use of the Rowid or Column or Next instruction for P1 
 ** will refer to the first entry in the database table or index.
@@ -5090,6 +5090,10 @@ case OP_Sort: {        /* jump */
 ** If the table or index is not empty, fall through to the following 
 ** instruction.
 **
+** If P5 is non-zero and the table is not empty, then the "skip-next"
+** flag is set on the cursor so that the next OP_Next instruction 
+** executed on it is a no-op.
+**
 ** This opcode leaves the cursor configured to move in forward order,
 ** from the beginning toward the end.  In other words, the cursor is
 ** configured to use Next, not Prev.
@@ -6282,24 +6286,24 @@ case OP_DecrJumpZero: {      /* jump, in1 */
 }
 
 
-/* Opcode: AggStep0 * P2 P3 P4 P5
+/* Opcode: AggStep0 P1 P2 P3 P4 P5
 ** Synopsis: accum=r[P3] step(r[P2@P5])
 **
-** Execute the step function for an aggregate.  The
-** function has P5 arguments.   P4 is a pointer to the FuncDef
-** structure that specifies the function.  Register P3 is the
+** Execute the xStep (if P1==0) or xInverse (if P1!=0) function for an
+** aggregate.  The function has P5 arguments.  P4 is a pointer to the 
+** FuncDef structure that specifies the function.  Register P3 is the
 ** accumulator.
 **
 ** The P5 arguments are taken from register P2 and its
 ** successors.
 */
-/* Opcode: AggStep * P2 P3 P4 P5
+/* Opcode: AggStep P1 P2 P3 P4 P5
 ** Synopsis: accum=r[P3] step(r[P2@P5])
 **
-** Execute the step function for an aggregate.  The
-** function has P5 arguments.   P4 is a pointer to an sqlite3_context
-** object that is used to run the function.  Register P3 is
-** as the accumulator.
+** Execute the xStep (if P1==0) or xInverse (if P1!=0) function for an
+** aggregate.  The function has P5 arguments.  P4 is a pointer to the 
+** FuncDef structure that specifies the function.  Register P3 is the
+** accumulator.
 **
 ** The P5 arguments are taken from register P2 and its
 ** successors.
@@ -6388,11 +6392,13 @@ case OP_AggStep: {
   break;
 }
 
-/* Opcode: AggFinal P1 P2 * P4 *
+/* Opcode: AggFinal P1 P2 P3 P4 *
 ** Synopsis: accum=r[P1] N=P2
 **
-** Execute the finalizer function for an aggregate.  P1 is
-** the memory location that is the accumulator for the aggregate.
+** P1 is the memory location that is the accumulator for an aggregate
+** or window function. If P3 is zero, then execute the finalizer function 
+** for an aggregate and store the result in P1. Or, if P3 is non-zero,
+** invoke the xValue() function and store the result in register P3.
 **
 ** P2 is the number of arguments that the step function takes and
 ** P4 is a pointer to the FuncDef for this function.  The P2
index b89396d0b74608edd632dbcc31c641b80d760986..6e0f8d6e07beafc1084413f9698c1db16a25f8b2 100644 (file)
@@ -415,6 +415,14 @@ int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
   return ctx.isError;
 }
 
+/*
+** Memory cell pAccum contains the context of an aggregate function.
+** This routine calls the xValue method for that function and stores
+** the results in memory cell pMem.
+**
+** SQLITE_ERROR is returned if xValue() reports an error. SQLITE_OK 
+** otherwise.
+*/
 int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){
   sqlite3_context ctx;
   Mem t;
@@ -432,6 +440,7 @@ int sqlite3VdbeMemAggValue(Mem *pAccum, Mem *pOut, FuncDef *pFunc){
   pFunc->xValue(&ctx);
   return ctx.isError;
 }
+
 /*
 ** If the memory cell contains a value that must be freed by
 ** invoking the external callback in Mem.xDel, then this routine
index 459d05e29e19cbb136cae08e22901c0493e45a45..0ec6bd34f8f3599deb8ce5517e85e1f55898ca13 100644 (file)
@@ -859,6 +859,13 @@ void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){
   for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
     FuncDef *p = pWin->pFunc;
     if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
+      /* The inline versions of min() and max() require a single ephemeral
+      ** table and 3 registers. The registers are used as follows:
+      **
+      **   regApp+0: slot to copy min()/max() argument to for MakeRecord
+      **   regApp+1: integer value used to ensure keys are unique
+      **   regApp+2: output of MakeRecord
+      */
       ExprList *pList = pWin->pOwner->x.pList;
       KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pList, 0, 0);
       pWin->csrApp = pParse->nTab++;
@@ -1248,14 +1255,21 @@ static int windowInitAccum(Parse *pParse, Window *pMWin){
   int nArg = 0;
   Window *pWin;
   for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
+    FuncDef *pFunc = pWin->pFunc;
     sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum);
     nArg = MAX(nArg, windowArgCount(pWin));
-    if( pWin->pFunc->xSFunc==nth_valueStepFunc
-     || pWin->pFunc->xSFunc==first_valueStepFunc 
+    if( pFunc->xSFunc==nth_valueStepFunc
+     || pFunc->xSFunc==first_valueStepFunc 
     ){
       sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp);
       sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
     }
+
+    if( (pFunc->funcFlags & SQLITE_FUNC_MINMAX) && pWin->csrApp ){
+      assert( pWin->eStart!=TK_UNBOUNDED );
+      sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp);
+      sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1);
+    }
   }
   regArg = pParse->nMem+1;
   pParse->nMem += nArg;
index 52e2509fc1508c3f2bc4e45c22907d248edb3a83..475ad10a6592153344e5f82a908eae6a8755a74c 100644 (file)
@@ -283,6 +283,12 @@ test_suite "fts5-light" -prefix "" -description {
       -exclude *corrupt* *fault* *big* *fts5aj*
 ]
 
+test_suite "window" -prefix "" -description {
+  All window function related tests .
+} -files [
+  test_set [glob -nocomplain $::testdir/window*.test]
+]
+
 test_suite "lsm1" -prefix "" -description {
   All LSM1 tests.
 } -files [glob -nocomplain $::testdir/../ext/lsm1/test/*.test]
index 18d547963c3a21240b3934748437dc057caa4f0a..14edd0e0315efca7d7ab215503267a91185e4185 100644 (file)
@@ -251,6 +251,10 @@ do_catchsql_test 7.1.6 {
   SELECT trim(x) OVER (ORDER BY y) FROM t1;
 } {1 {trim() may not be used as a window function}}
 
+do_catchsql_test 7.1.7 {
+  SELECT max(x) OVER abc FROM t1 WINDOW def AS (ORDER BY y);
+} {1 {no such window: abc}}
+
 
 finish_test
 
index 5c7466e75da7664540cbe6ea2cdd271ff50fd6ce..e96782afdc41b387335a0b5ff2d74666c9470b31 100644 (file)
@@ -128,7 +128,8 @@ execsql_test 3.6.3 {
   FROM t5
 }
 
-#=========================================================================
+==========
+
 execsql_test 4.0 {
   DROP TABLE IF EXISTS ttt;
   CREATE TABLE ttt(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER);
@@ -163,6 +164,57 @@ execsql_test 4.4 {
   ) FROM ttt;
 }
 
+set lPart  [list "PARTITION BY b" "PARTITION BY b, a" "" "PARTITION BY a"]
+set lOrder [list "ORDER BY a" "ORDER BY a DESC" "" "ORDER BY b, a"]
+set lRange {
+    "BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW"
+    "BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"
+    "BETWEEN CURRENT ROW AND CURRENT ROW"
+    "BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING"
+}
+
+set tn 1
+set SQL {
+  SELECT max(c) OVER ($p1 $o1 RANGE $r1), 
+  min(c) OVER ($p2 $o2 RANGE $r2)
+  FROM ttt ORDER BY a
+}
+set SQL2 {
+  SELECT avg(c) OVER ($p1 $o1 RANGE $r1), 
+         avg(c) OVER ($p2 $o2 RANGE $r2)
+  FROM ttt ORDER BY a
+}
+
+set o1 [lindex $lOrder 0]
+set o2 [lindex $lOrder 0]
+set r1 [lindex $lRange 0]
+set r2 [lindex $lRange 0]
+foreach p1 $lPart { foreach p2 $lPart { 
+  execsql_test 4.5.$tn.1 [subst $SQL]
+  execsql_float_test 4.5.$tn.2 [subst $SQL2]
+  incr tn
+}}
+
+set o1 [lindex $lOrder 0]
+set o2 [lindex $lOrder 0]
+set p1 [lindex $lPart 0]
+set p2 [lindex $lPart 0]
+foreach r1 $lRange { foreach r2 $lRange { 
+  execsql_test 4.5.$tn.1 [subst $SQL]
+  execsql_float_test 4.5.$tn.2 [subst $SQL2]
+  incr tn
+}}
+
+set r1 [lindex $lRange 0]
+set r2 [lindex $lRange 0]
+set p1 [lindex $lPart 0]
+set p2 [lindex $lPart 0]
+foreach o1 $lOrder { foreach o2 $lOrder { 
+  execsql_test 4.5.$tn.1 [subst $SQL]
+  execsql_float_test 4.5.$tn.2 [subst $SQL2]
+  incr tn
+}}
+
 
 finish_test
 
index 20b8a114b081d81c6498969166ac1bc34b75b7a2..a57fc16b74af40915a0d66bda20e62447aa8b3e5 100644 (file)
@@ -211,6 +211,8 @@ do_execsql_test 3.6.3 {
   FROM t5
 } {1 one   2 two   3 three   4 four   5 five}
 
+#==========================================================================
+
 do_execsql_test 4.0 {
   DROP TABLE IF EXISTS ttt;
   CREATE TABLE ttt(a INTEGER PRIMARY KEY, b INTEGER, c INTEGER);
@@ -245,4 +247,772 @@ do_execsql_test 4.4 {
   ) FROM ttt;
 } {18   17   15   12   11   9   6   5   3}
 
+do_execsql_test 4.5.1.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.1.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 1.50 2.50 2.50 3.50 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.2.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.2.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 2.00 2.50 3.00 3.50 4.00 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.3.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 1   3 1   2 1   3 1   4 1   3 1   4 1   5 1}
+
+do_test 4.5.3.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 1.50 3.00 2.00 1.50 2.00 2.50 2.20 3.50 2.50 2.00 2.57 3.00 2.75 4.00 3.00}
+
+do_execsql_test 4.5.4.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.4.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 2.00 2.50 3.00 3.50 4.00 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.5.1 {
+  SELECT max(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.5.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 1.50 3.00 2.50 4.00 3.50 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.6.1 {
+  SELECT max(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.6.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 2.00 3.00 3.00 4.00 4.00 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.7.1 {
+  SELECT max(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 1   3 1   2 1   3 1   4 1   3 1   4 1   5 1}
+
+do_test 4.5.7.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 1.50 3.00 2.00 2.00 2.00 3.00 2.20 4.00 2.50 3.00 2.57 4.00 2.75 5.00 3.00}
+
+do_execsql_test 4.5.8.1 {
+  SELECT max(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.8.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 2.00 3.00 3.00 4.00 4.00 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.9.1 {
+  SELECT max(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   3 1   3 2   4 3   4 1   4 2   5 3}
+
+do_test 4.5.9.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 1.50 2.00 2.00 3.00 2.00 1.50 2.20 2.50 2.50 3.50 2.57 2.00 2.75 3.00 3.00 4.00}
+
+do_execsql_test 4.5.10.1 {
+  SELECT max(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   3 2   3 3   4 4   4 3   4 4   5 5}
+
+do_test 4.5.10.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 1.50 2.00 2.00 3.00 2.00 2.00 2.20 3.00 2.50 4.00 2.57 3.00 2.75 4.00 3.00 5.00}
+
+do_execsql_test 4.5.11.1 {
+  SELECT max(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 1   3 1   3 1   3 1   4 1   4 1   4 1   5 1}
+
+do_test 4.5.11.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 1.50 1.50 2.00 2.00 2.00 2.00 2.20 2.20 2.50 2.50 2.57 2.57 2.75 2.75 3.00 3.00}
+
+do_execsql_test 4.5.12.1 {
+  SELECT max(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   3 2   3 3   4 4   4 3   4 4   5 5}
+
+do_test 4.5.12.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 1.50 2.00 2.00 3.00 2.00 2.00 2.20 3.00 2.50 4.00 2.57 3.00 2.75 4.00 3.00 5.00}
+
+do_execsql_test 4.5.13.1 {
+  SELECT max(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.13.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 1.50 3.00 2.50 4.00 3.50 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.14.1 {
+  SELECT max(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.14.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b, a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 2.00 3.00 3.00 4.00 4.00 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.15.1 {
+  SELECT max(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 1   3 1   2 1   3 1   4 1   3 1   4 1   5 1}
+
+do_test 4.5.15.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER ( ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 1.50 3.00 2.00 2.00 2.00 3.00 2.20 4.00 2.50 3.00 2.57 4.00 2.75 5.00 3.00}
+
+do_execsql_test 4.5.16.1 {
+  SELECT max(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.16.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY a ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 2.00 3.00 3.00 4.00 4.00 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.17.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.17.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 1.50 2.50 2.50 3.50 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.18.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.18.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 1.50 2.00 2.50 3.00 3.50 4.00 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.19.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.19.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 2.00 2.50 3.00 3.50 4.00 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.20.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.20.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 1.50 2.50 2.50 3.50 3.50 4.50 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.21.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.21.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.00 1.50 3.00 2.50 4.00 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.22.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.22.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.00 2.00 3.00 3.00 4.00 4.00 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.23.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 2   4 3   5 4   3 3   4 4   5 5}
+
+do_test 4.5.23.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.00 2.00 3.00 3.00 4.00 4.00 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.24.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 2   4 3   5 4   3 3   4 4   5 5}
+
+do_test 4.5.24.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.00 2.50 3.00 3.50 4.00 4.50 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.25.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.25.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 1.50 3.00 2.50 4.00 3.50 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.26.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.26.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 2.00 2.00 3.00 3.00 4.00 4.00 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.27.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.27.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 2.00 2.00 3.00 3.00 4.00 4.00 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.28.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.28.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 2.00 2.50 3.00 3.50 4.00 4.50 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.29.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.29.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.50 1.50 3.50 2.50 4.50 3.50 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.30.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.30.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.50 2.00 3.50 3.00 4.50 4.00 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.31.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 2   4 3   5 4   3 3   4 4   5 5}
+
+do_test 4.5.31.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.50 2.00 3.50 3.00 4.50 4.00 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.32.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 2   4 3   5 4   3 3   4 4   5 5}
+
+do_test 4.5.32.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.50 2.50 3.50 3.50 4.50 4.50 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.33.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.33.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 1.50 2.50 2.50 3.50 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.34.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.34.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 1.50 2.50 2.50 3.50 3.50 4.50 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.35.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.35.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 1.50 2.00 2.50 3.00 3.50 4.00 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.36.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.36.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 1.50 2.50 2.50 3.50 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.37.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.37.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.50 1.50 3.50 2.50 4.50 3.50 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.38.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 2   4 3   5 4   3 3   4 4   5 5}
+
+do_test 4.5.38.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.50 2.50 3.50 3.50 4.50 4.50 3.00 3.00 4.00 4.00 5.00 5.00}
+
+do_execsql_test 4.5.39.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.39.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.50 2.00 3.50 3.00 4.50 4.00 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.40.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.40.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.50 1.50 3.50 2.50 4.50 3.50 3.00 2.00 4.00 3.00 5.00 4.00}
+
+do_execsql_test 4.5.41.1 {
+  SELECT max(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.41.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.00 1.50 3.00 2.50 4.00 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.42.1 {
+  SELECT max(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 2   4 3   5 4   3 3   4 4   5 5}
+
+do_test 4.5.42.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.00 2.50 3.00 3.50 4.00 4.50 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.43.1 {
+  SELECT max(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.43.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 2.00 3.00 3.00 4.00 4.00 2.00 2.00 3.00 3.00 4.00 4.00 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.44.1 {
+  SELECT max(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {3 1   4 2   5 3   3 1   4 2   5 3   3 1   4 2   5 3}
+
+do_test 4.5.44.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {2.00 1.00 3.00 2.00 4.00 3.00 2.00 1.50 3.00 2.50 4.00 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.45.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.45.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 1.50 2.50 2.50 3.50 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.46.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 2   3 3   4 4   3 3   4 4   5 5}
+
+do_test 4.5.46.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 1.50 2.50 2.50 3.50 3.50 4.50 2.00 3.00 3.00 4.00 4.00 5.00}
+
+do_execsql_test 4.5.47.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.47.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b  RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 2.00 2.00 3.00 3.00 4.00 1.50 2.00 2.50 3.00 3.50 4.00 2.00 2.00 3.00 3.00 4.00 4.00}
+
+do_execsql_test 4.5.48.1 {
+  SELECT max(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+  min(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a
+} {1 1   2 2   3 3   2 1   3 2   4 3   3 1   4 2   5 3}
+
+do_test 4.5.48.2 {
+  set myres {}
+  foreach r [db eval {SELECT avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), 
+         avg(c) OVER (PARTITION BY b ORDER BY b, a RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
+  FROM ttt ORDER BY a}] {
+    lappend myres [format %.2f [set r]]
+  }
+  set myres
+} {1.00 1.00 2.00 2.00 3.00 3.00 1.50 1.50 2.50 2.50 3.50 3.50 2.00 2.00 3.00 3.00 4.00 4.00}
+
 finish_test