/ Check-in [e8eee566df]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Improvements to the way built-in window functions are handled.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | window-functions
Files: files | file ages | folders
SHA3-256: e8eee566dfca6f4c8af074731dfe91f7fbcd9ca72f0303235b52e4e2e80d5b71
User & Date: dan 2019-03-06 17:12:32
Wiki:window-functions
Context
2019-03-06
21:04
Simplify the window function code generator some more. check-in: 45cbd3b449 user: dan tags: window-functions
17:12
Improvements to the way built-in window functions are handled. check-in: e8eee566df user: dan tags: window-functions
2019-03-05
19:29
Extend windowCodeStep() to handle any ROWS PRECEDING/FOLLOWING frame specification. check-in: af0ea13635 user: dan tags: window-functions
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/sqliteInt.h.

  3575   3575     int regPart;            /* First in a set of registers holding PARTITION BY
  3576   3576                             ** and ORDER BY values for the window */
  3577   3577     Expr *pOwner;           /* Expression object this window is attached to */
  3578   3578     int nBufferCol;         /* Number of columns in buffer table */
  3579   3579     int iArgCol;            /* Offset of first argument for this function */
  3580   3580   
  3581   3581     int regFirst;
         3582  +  int regSize;
  3582   3583   };
  3583   3584   
  3584   3585   #ifndef SQLITE_OMIT_WINDOWFUNC
  3585   3586   void sqlite3WindowDelete(sqlite3*, Window*);
  3586   3587   void sqlite3WindowListDelete(sqlite3 *db, Window *p);
  3587   3588   Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*);
  3588   3589   void sqlite3WindowAttach(Parse*, Expr*, Window*);

Changes to src/window.c.

  1088   1088       pMWin->regPart = pParse->nMem+1;
  1089   1089       pParse->nMem += nPart;
  1090   1090       sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1);
  1091   1091     }
  1092   1092   
  1093   1093     pMWin->regFirst = ++pParse->nMem;
  1094   1094     sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst);
         1095  +  pMWin->regSize = ++pParse->nMem;
         1096  +  sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regSize);
  1095   1097   
  1096   1098     for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
  1097   1099       FuncDef *p = pWin->pFunc;
  1098   1100       if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){
  1099   1101         /* The inline versions of min() and max() require a single ephemeral
  1100   1102         ** table and 3 registers. The registers are used as follows:
  1101   1103         **
................................................................................
  1836   1838     sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr);
  1837   1839     sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
  1838   1840     VdbeComment((v, "end flush_partition subroutine"));
  1839   1841   
  1840   1842     /* Jump to here to skip over flush_partition */
  1841   1843     sqlite3VdbeJumpHere(v, addrGoto);
  1842   1844   }
         1845  +
         1846  +/* 
         1847  +** Return true if the entire partition should be cached in the temp
         1848  +** table before processing.
         1849  +*/
         1850  +static int windowCachePartition(Window *pMWin){
         1851  +  Window *pWin;
         1852  +  for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
         1853  +    FuncDef *pFunc = pWin->pFunc;
         1854  +    if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
         1855  +     || (pFunc->zName==nth_valueName)
         1856  +     || (pFunc->zName==first_valueName)
         1857  +     || (pFunc->zName==leadName)
         1858  +     || (pFunc->zName==lagName)
         1859  +    ){
         1860  +      return 1;
         1861  +    }
         1862  +  }
         1863  +  return 0;
         1864  +}
  1843   1865   
  1844   1866   static void windowCodeStep(
  1845   1867     Parse *pParse, 
  1846   1868     Select *p,
  1847   1869     WhereInfo *pWInfo,
  1848   1870     int regGosub, 
  1849   1871     int addrGosub
................................................................................
  1867   1889   
  1868   1890     int addrGoto;
  1869   1891     int addrIf;
  1870   1892     int addrIfEnd;
  1871   1893     int addrIfStart;
  1872   1894     int addrGosubFlush;
  1873   1895     int addrInteger;
         1896  +  int addrCacheRewind;
         1897  +  int addrCacheNext;
  1874   1898   
  1875   1899     int addrShortcut = 0;
         1900  +
         1901  +  int bCache = windowCachePartition(pMWin);
  1876   1902   
  1877   1903     int reg = pParse->nMem+1;
  1878   1904     int regRecord = reg+nSub;
  1879   1905     int regRowid = regRecord+1;
         1906  +
         1907  +  bCache = 1;
  1880   1908   
  1881   1909     pParse->nMem += 1 + nSub + 1;
  1882   1910   
  1883   1911     regFlushPart = ++pParse->nMem;
  1884   1912     regStart = ++pParse->nMem;
  1885   1913     regEnd = ++pParse->nMem;
  1886   1914   
................................................................................
  1899   1927     ** into an array of registers starting at reg. Assemble them into
  1900   1928     ** a record in register regRecord. TODO: An optimization here? */
  1901   1929     for(k=0; k<nSub; k++){
  1902   1930       sqlite3VdbeAddOp3(v, OP_Column, iSubCsr, k, reg+k);
  1903   1931     }
  1904   1932     sqlite3VdbeAddOp3(v, OP_MakeRecord, reg, nSub, regRecord);
  1905   1933   
  1906         -  /* Check if the current iteration is the first row of a new partition */
         1934  +  /* An input row has just been read into an array of registers starting
         1935  +  ** at reg. If the window has a PARTITION clause, this block generates 
         1936  +  ** VM code to check if the input row is the start of a new partition.
         1937  +  ** If so, it does an OP_Gosub to an address to be filled in later. The
         1938  +  ** address of the OP_Gosub is stored in local variable addrGosubFlush.
         1939  +  */
  1907   1940     if( pMWin->pPartition ){
  1908   1941       int addr;
  1909   1942       ExprList *pPart = pMWin->pPartition;
  1910   1943       int nPart = pPart->nExpr;
  1911   1944       int regNewPart = reg + pMWin->nBufferCol;
  1912   1945       KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0);
  1913   1946   
  1914   1947       addrIf = sqlite3VdbeAddOp1(v, OP_If, pMWin->regFirst);
  1915   1948       addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart);
  1916   1949       sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO);
  1917         -    sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+3, addr+2);
         1950  +    sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2);
  1918   1951       VdbeCoverageEqNe(v);
  1919   1952       addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart);
  1920   1953       VdbeComment((v, "call flush_partition"));
  1921   1954       sqlite3VdbeJumpHere(v, addrIf);
         1955  +    sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1);
  1922   1956     }
  1923   1957   
  1924   1958     /* Insert the new row into the ephemeral table */
  1925   1959     sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid);
  1926   1960     sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid);
         1961  +  sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regSize, 1);
         1962  +
         1963  +  if( bCache ){
         1964  +    sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst);
         1965  +    sqlite3WhereEnd(pWInfo);
         1966  +    addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart);
         1967  +    if( pMWin->pPartition ){
         1968  +      sqlite3VdbeJumpHere(v, addrGosubFlush);
         1969  +    }
         1970  +    addrCacheRewind = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite);
         1971  +  }else{
         1972  +    addrIf = sqlite3VdbeAddOp1(v, OP_IfNot, pMWin->regFirst);
         1973  +  }
  1927   1974   
  1928   1975     /* This block is run for the first row of each partition */
  1929         -  addrIf = sqlite3VdbeAddOp1(v, OP_IfNot, pMWin->regFirst);
  1930         -  if( pMWin->pPartition ){
  1931         -    sqlite3VdbeAddOp3(v, OP_Copy, 
  1932         -        reg+pMWin->nBufferCol, pMWin->regPart, pMWin->pPartition->nExpr-1
  1933         -    );
  1934         -  }
  1935         -
  1936   1976     regArg = windowInitAccum(pParse, pMWin);
  1937   1977   
  1938   1978     sqlite3ExprCode(pParse, pMWin->pStart, regStart);
  1939   1979     windowCheckIntValue(pParse, regStart, 0);
  1940   1980     sqlite3ExprCode(pParse, pMWin->pEnd, regEnd);
  1941   1981     windowCheckIntValue(pParse, regEnd, 1);
  1942   1982   
  1943   1983     if( pMWin->eStart==TK_FOLLOWING || pMWin->eEnd==TK_PRECEDING ){
  1944   1984       int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le);
  1945   1985       int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd);
  1946   1986       windowAggFinal(pParse, pMWin, 0);
  1947         -    sqlite3VdbeAddOp2(v, OP_Rewind, csrCurrent, 1);
  1948         -    windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  1949         -    sqlite3VdbeAddOp1(v, OP_ResetSorter, csrCurrent);
         1987  +    if( bCache ){
         1988  +      sqlite3VdbeAddOp2(v, OP_Rowid, csrWrite, regRowid);
         1989  +      sqlite3VdbeAddOp3(v, OP_NotExists, csrCurrent, 0, regRowid);
         1990  +      windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
         1991  +      sqlite3VdbeAddOp2(v, OP_Next, csrWrite, addrCacheRewind+1);
         1992  +    }else{
         1993  +      sqlite3VdbeAddOp2(v, OP_Rewind, csrCurrent, 1);
         1994  +      windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
         1995  +      sqlite3VdbeAddOp1(v, OP_ResetSorter, csrCurrent);
         1996  +    }
  1950   1997       addrShortcut = sqlite3VdbeAddOp0(v, OP_Goto);
  1951   1998       sqlite3VdbeJumpHere(v, addrGe);
  1952   1999     }
  1953   2000     if( pMWin->eStart==TK_FOLLOWING ){
  1954   2001       sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart);
  1955   2002     }
  1956   2003   
................................................................................
  1958   2005     sqlite3VdbeAddOp2(v, OP_Rewind, csrCurrent, 1); sqlite3VdbeChangeP5(v, 1);
  1959   2006     sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, 1); sqlite3VdbeChangeP5(v, 1);
  1960   2007   
  1961   2008     sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regFirst);
  1962   2009     addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
  1963   2010   
  1964   2011     /* This block is run for the second and subsequent rows of each partition */
  1965         -  sqlite3VdbeJumpHere(v, addrIf);
         2012  +  if( bCache ){
         2013  +    addrCacheNext = sqlite3VdbeCurrentAddr(v);
         2014  +  }else{
         2015  +    sqlite3VdbeJumpHere(v, addrIf);
         2016  +  }
  1966   2017   
  1967   2018     if( pMWin->eStart==TK_FOLLOWING ){
  1968   2019       addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
  1969   2020       windowAggFinal(pParse, pMWin, 0);
  1970   2021       sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+1);
  1971   2022       windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  1972   2023       sqlite3VdbeJumpHere(v, addrIfEnd);
  1973   2024   
  1974   2025       addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
  1975   2026       sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
  1976         -    windowAggStep(pParse, pMWin, csrStart, 1, regArg, 0);
         2027  +    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
  1977   2028       sqlite3VdbeJumpHere(v, addrIfStart);
  1978   2029     }else
  1979   2030     if( pMWin->eEnd==TK_PRECEDING ){
  1980   2031       addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
  1981   2032       sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+1);
  1982         -    windowAggStep(pParse, pMWin, csrEnd, 0, regArg, 0);
         2033  +    windowAggStep(pParse, pMWin, csrEnd, 0, regArg, pMWin->regSize);
  1983   2034       sqlite3VdbeJumpHere(v, addrIfEnd);
  1984   2035   
  1985   2036       windowAggFinal(pParse, pMWin, 0);
  1986   2037       sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+1);
  1987   2038       windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  1988   2039   
  1989   2040       addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
  1990   2041       sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
  1991         -    windowAggStep(pParse, pMWin, csrStart, 1, regArg, 0);
         2042  +    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
  1992   2043       sqlite3VdbeJumpHere(v, addrIfStart);
  1993   2044     }else{
  1994   2045       addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
  1995   2046       windowAggFinal(pParse, pMWin, 0);
  1996   2047       sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+1);
  1997   2048       windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  1998   2049       addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
  1999   2050       sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
  2000         -    windowAggStep(pParse, pMWin, csrStart, 1, regArg, 0);
         2051  +    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
  2001   2052       sqlite3VdbeJumpHere(v, addrIfStart);
  2002   2053       sqlite3VdbeJumpHere(v, addrIfEnd);
  2003   2054     }
  2004   2055   
  2005   2056     sqlite3VdbeJumpHere(v, addrGoto);
  2006   2057     if( pMWin->eEnd!=TK_PRECEDING ){
  2007   2058       sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+1);
  2008         -    windowAggStep(pParse, pMWin, csrEnd, 0, regArg, 0);
         2059  +    windowAggStep(pParse, pMWin, csrEnd, 0, regArg, pMWin->regSize);
  2009   2060     }
  2010   2061   
  2011   2062     /* End of the main input loop */
  2012         -  if( addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut);
  2013         -  sqlite3WhereEnd(pWInfo);
         2063  +  if( bCache ){
         2064  +    sqlite3VdbeAddOp2(v, OP_Next, csrWrite, addrCacheNext);
         2065  +    sqlite3VdbeJumpHere(v, addrCacheRewind); 
         2066  +  }else{
         2067  +    if( addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut);
         2068  +    sqlite3WhereEnd(pWInfo);
         2069  +  }
  2014   2070   
  2015   2071     /* Fall through */
  2016   2072   
  2017         -  if( pMWin->pPartition ){
         2073  +  if( pMWin->pPartition && bCache==0 ){
  2018   2074       addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart);
  2019   2075       sqlite3VdbeJumpHere(v, addrGosubFlush);
  2020   2076     }
  2021   2077   
  2022   2078     if( pMWin->eStart==TK_FOLLOWING ){
  2023   2079       int addrBreak;
  2024   2080       addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
................................................................................
  2027   2083       windowAggFinal(pParse, pMWin, 0);
  2028   2084       windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  2029   2085       sqlite3VdbeJumpHere(v, addrIfEnd);
  2030   2086   
  2031   2087       addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
  2032   2088       sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2);
  2033   2089       sqlite3VdbeAddOp0(v, OP_Goto);
  2034         -    windowAggStep(pParse, pMWin, csrStart, 1, regArg, 0);
         2090  +    windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
  2035   2091       sqlite3VdbeJumpHere(v, addrIfStart);
  2036   2092       sqlite3VdbeJumpHere(v, addrIfStart+2);
  2037   2093   
  2038   2094       sqlite3VdbeAddOp2(v, OP_Goto, 0, addrIfEnd);
  2039   2095       sqlite3VdbeJumpHere(v, addrBreak);
  2040   2096     }else{
  2041   2097       sqlite3VdbeAddOp2(v, OP_Next, csrCurrent, sqlite3VdbeCurrentAddr(v)+2);
  2042   2098       addrGoto = sqlite3VdbeAddOp0(v, OP_Goto);
  2043   2099       if( pMWin->eEnd==TK_PRECEDING ){
  2044   2100         addrIfEnd = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1);
  2045   2101         sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+1);
  2046         -      windowAggStep(pParse, pMWin, csrEnd, 0, regArg, 0);
         2102  +      windowAggStep(pParse, pMWin, csrEnd, 0, regArg, pMWin->regSize);
  2047   2103         sqlite3VdbeJumpHere(v, addrIfEnd);
  2048   2104         windowAggFinal(pParse, pMWin, 0);
  2049   2105         windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  2050   2106       }else{
  2051   2107         windowAggFinal(pParse, pMWin, 0);
  2052   2108         windowReturnOneRow(pParse, pMWin, regGosub, addrGosub);
  2053   2109         addrIfStart = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0, 1);
  2054   2110         sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1);
  2055         -      windowAggStep(pParse, pMWin, csrStart, 1, regArg, 0);
         2111  +      windowAggStep(pParse, pMWin, csrStart, 1, regArg, pMWin->regSize);
  2056   2112         sqlite3VdbeJumpHere(v, addrIfStart);
  2057   2113         sqlite3VdbeAddOp2(v, OP_Goto, 0, addrGoto-1);
  2058   2114       }
  2059   2115       sqlite3VdbeJumpHere(v, addrGoto);
  2060   2116     }
  2061   2117   
  2062   2118   
         2119  +  if( bCache && addrShortcut>0 ) sqlite3VdbeJumpHere(v, addrShortcut);
  2063   2120     sqlite3VdbeAddOp1(v, OP_ResetSorter, csrCurrent);
  2064         -  sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst);
         2121  +  sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regSize);
         2122  +  if( bCache==0 ) sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regFirst);
  2065   2123     if( pMWin->pPartition ){
  2066   2124       sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v));
  2067   2125       sqlite3VdbeAddOp1(v, OP_Return, regFlushPart);
  2068   2126     }
  2069   2127   }
  2070   2128   
  2071   2129   /*
................................................................................
  2526   2584     ** windowCodeDefaultStep() is the only one of the three functions that
  2527   2585     ** does not cache each partition in a temp table before beginning to
  2528   2586     ** return rows.
  2529   2587     */
  2530   2588     if( pMWin->eType==TK_ROWS 
  2531   2589      && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy)
  2532   2590     ){
  2533         -    Window *pWin;
  2534         -    int bCache = 0;               /* True to use CacheStep() */
  2535         -    for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
  2536         -      FuncDef *pFunc = pWin->pFunc;
  2537         -      if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE)
  2538         -        || (pFunc->zName==nth_valueName)
  2539         -        || (pFunc->zName==first_valueName)
  2540         -        || (pFunc->zName==leadName)
  2541         -        || (pFunc->zName==lagName)
  2542         -      ){
  2543         -        bCache = 1;
  2544         -        break;
  2545         -      }
  2546         -    }
  2547         -    if( bCache 
  2548         -    || (pMWin->eStart!=TK_PRECEDING && pMWin->eStart!=TK_FOLLOWING)
  2549         -    || (pMWin->eEnd!=TK_FOLLOWING && pMWin->eEnd!=TK_PRECEDING) 
         2591  +    if( (pMWin->eStart!=TK_PRECEDING && pMWin->eStart!=TK_FOLLOWING)
         2592  +     || (pMWin->eEnd!=TK_FOLLOWING && pMWin->eEnd!=TK_PRECEDING) 
  2550   2593       ){
  2551   2594         VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()"));
  2552   2595         windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub);
  2553   2596         VdbeModuleComment((pParse->pVdbe, "End RowExprStep()"));
  2554   2597       }else{
  2555   2598         VdbeModuleComment((pParse->pVdbe, "Begin windowCodeStep()"));
  2556   2599         windowCodeStep(pParse, p, pWInfo, regGosub, addrGosub);

Changes to test/window4.tcl.

     1         -# 2018 May 19
            1  +## 2018 May 19
     2      2   #
     3      3   # The author disclaims copyright to this source code.  In place of
     4      4   # a legal notice, here is a blessing:
     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.