Index: Makefile.in ================================================================== --- Makefile.in +++ Makefile.in @@ -421,10 +421,11 @@ $(TOP)/src/test_superlock.c \ $(TOP)/src/test_syscall.c \ $(TOP)/src/test_tclsh.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ + $(TOP)/src/test_vdbecov.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_windirent.c \ $(TOP)/src/test_window.c \ $(TOP)/src/test_wsd.c \ $(TOP)/ext/fts3/fts3_term.c \ @@ -1050,16 +1051,13 @@ # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c -parse.c: $(TOP)/src/parse.y lemon$(BEXE) $(TOP)/tool/addopcodes.tcl +parse.c: $(TOP)/src/parse.y lemon$(BEXE) cp $(TOP)/src/parse.y . - rm -f parse.h ./lemon$(BEXE) $(OPT_FEATURE_FLAGS) $(OPTS) parse.y - mv parse.h parse.h.temp - $(TCLSH_CMD) $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid$(BEXE) $(TOP)/VERSION $(TCLSH_CMD) $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c @@ -1374,12 +1372,13 @@ # This target will fail if the SQLite amalgamation contains any exported # symbols that do not begin with "sqlite3_". It is run as part of the # releasetest.tcl script. # VALIDIDS=' sqlite3(changeset|changegroup|session)?_' -checksymbols: sqlite3.lo - nm -g --defined-only sqlite3.lo | egrep -v $(VALIDIDS); test $$? -ne 0 +checksymbols: sqlite3.o + nm -g --defined-only sqlite3.o + nm -g --defined-only sqlite3.o | egrep -v $(VALIDIDS); test $$? -ne 0 echo '0 errors out of 1 tests' # Build the amalgamation-autoconf package. The amalamgation-tarball target builds # a tarball named for the version number. Ex: sqlite-autoconf-3110000.tar.gz. # The snapshot-tarball target builds a tarball named by the SHA1 hash Index: Makefile.msc ================================================================== --- Makefile.msc +++ Makefile.msc @@ -317,10 +317,17 @@ !ENDIF !IFNDEF SQLITETCLDECLSH SQLITETCLDECLSH = sqlite_tclDecls.h !ENDIF + +# This is the name to use for the dynamic link library (DLL) containing the +# Tcl bindings for SQLite. +# +!IFNDEF SQLITE3TCLDLL +SQLITE3TCLDLL = tclsqlite3.dll +!ENDIF # These are the additional targets that the targets that integrate with the # Tcl library should depend on when compiling, etc. # !IFNDEF SQLITE_TCL_DEP @@ -493,13 +500,13 @@ # C compiler and options for use in building executables that # will run on the platform that is doing the build. # !IF $(USE_FULLWARN)!=0 -BCC = $(NCC) -nologo -W4 $(CCOPTS) $(BCCOPTS) +BCC = $(NCC) -nologo -W4 -Fd$*.pdb $(CCOPTS) $(BCCOPTS) !ELSE -BCC = $(NCC) -nologo -W3 $(CCOPTS) $(BCCOPTS) +BCC = $(NCC) -nologo -W3 -Fd$*.pdb $(CCOPTS) $(BCCOPTS) !ENDIF # Check if assembly code listings should be generated for the source # code files to be compiled. # @@ -851,10 +858,18 @@ # non-stubs enabled programs using Tcl must link against. These variables # (TCLINCDIR, TCLLIBDIR, and LIBTCL) may be overridden via the environment # prior to running nmake in order to match the actual installed location and # version on this machine. # +!IFNDEF TCLVERSION +TCLVERSION = 86 +!ENDIF + +!IFNDEF TCLSUFFIX +TCLSUFFIX = +!ENDIF + !IFNDEF TCLDIR TCLDIR = $(TOP)\compat\tcl !ENDIF !IFNDEF TCLINCDIR @@ -864,15 +879,15 @@ !IFNDEF TCLLIBDIR TCLLIBDIR = $(TCLDIR)\lib !ENDIF !IFNDEF LIBTCL -LIBTCL = tcl86.lib +LIBTCL = tcl$(TCLVERSION)$(TCLSUFFIX).lib !ENDIF !IFNDEF LIBTCLSTUB -LIBTCLSTUB = tclstub86.lib +LIBTCLSTUB = tclstub$(TCLVERSION)$(TCLSUFFIX).lib !ENDIF !IFNDEF LIBTCLPATH LIBTCLPATH = $(TCLDIR)\bin !ENDIF @@ -1061,11 +1076,11 @@ # <> # Command line prefixes for compiling code, compiling resources, # linking, etc. # -LTCOMPILE = $(TCC) -Fo$@ +LTCOMPILE = $(TCC) -Fo$@ -Fd$*.pdb LTRCOMPILE = $(RCC) -r LTLIB = lib.exe LTLINK = $(TCC) -Fe$@ # If requested, link to the RPCRT4 library. @@ -1079,10 +1094,15 @@ # set this for you. Otherwise, the linker will attempt # to deduce the binary type based on the object files. !IFDEF PLATFORM LTLINKOPTS = /NOLOGO /MACHINE:$(PLATFORM) LTLIBOPTS = /NOLOGO /MACHINE:$(PLATFORM) +!ELSEIF "$(VISUALSTUDIOVERSION)"=="12.0" || \ + "$(VISUALSTUDIOVERSION)"=="14.0" || \ + "$(VISUALSTUDIOVERSION)"=="15.0" +LTLINKOPTS = /NOLOGO /MACHINE:x86 +LTLIBOPTS = /NOLOGO /MACHINE:x86 !ELSE LTLINKOPTS = /NOLOGO LTLIBOPTS = /NOLOGO !ENDIF @@ -1496,10 +1516,11 @@ $(TOP)\src\test_superlock.c \ $(TOP)\src\test_syscall.c \ $(TOP)\src\test_tclsh.c \ $(TOP)\src\test_tclvar.c \ $(TOP)\src\test_thread.c \ + $(TOP)\src\test_vdbecov.c \ $(TOP)\src\test_vfs.c \ $(TOP)\src\test_windirent.c \ $(TOP)\src\test_window.c \ $(TOP)\src\test_wsd.c \ $(TOP)\ext\fts3\fts3_term.c \ @@ -1664,11 +1685,11 @@ TESTOPTS = --verbose=file --output=test-out.txt # Extra targets for the "all" target that require Tcl. # !IF $(NO_TCL)==0 -ALL_TCL_TARGETS = libtclsqlite3.lib +ALL_TCL_TARGETS = $(SQLITE3TCLDLL) !ELSE ALL_TCL_TARGETS = !ENDIF # <> @@ -1696,11 +1717,26 @@ # <> libsqlite3.lib: $(LIBOBJ) $(LTLIB) $(LTLIBOPTS) /OUT:$@ $(LIBOBJ) $(TLIBS) libtclsqlite3.lib: tclsqlite.lo libsqlite3.lib - $(LTLIB) $(LTLIBOPTS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS) + $(LTLIB) $(LTLIBOPTS) $(TCLLIBPATHS) $(LTLIBPATHS) /OUT:$@ tclsqlite.lo libsqlite3.lib $(LIBTCLSTUB) $(TLIBS) + +tclsqlite3.def: tclsqlite.lo + echo EXPORTS > tclsqlite3.def + dumpbin /all tclsqlite.lo \ + | $(TCLSH_CMD) $(TOP)\tool\replace.tcl include "^\s+/EXPORT:_?((?:Sqlite3|Tclsqlite3)_[^@]*)(?:@\d+)?$$" \1 \ + | sort >> tclsqlite3.def + +pkgIndex.tcl: $(TOP)\VERSION + for /F %%V in ('type "$(TOP)\VERSION"') do ( \ + echo package ifneeded sqlite3 @version@ [list load [file join $$dir $(SQLITE3TCLDLL)] sqlite3] \ + | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact @version@ %%V > pkgIndex.tcl \ + ) + +$(SQLITE3TCLDLL): libtclsqlite3.lib $(LIBRESOBJS) tclsqlite3.def pkgIndex.tcl + $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL /DEF:tclsqlite3.def /OUT:$@ libtclsqlite3.lib $(LIBRESOBJS) $(LTLIBS) $(TLIBS) # <> $(SQLITE3DLL): $(LIBOBJ) $(LIBRESOBJS) $(CORE_LINK_DEP) $(LD) $(LDFLAGS) $(LTLINKOPTS) $(LTLIBPATHS) /DLL $(CORE_LINK_OPTS) /OUT:$@ $(LIBOBJ) $(LIBRESOBJS) $(LTLIBS) $(TLIBS) @@ -1836,11 +1872,11 @@ # Rule to build the Win32 resources object file. # !IF $(USE_RC)!=0 # <> -$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(SQLITE3H) +$(LIBRESOBJS): $(TOP)\src\sqlite3.rc $(SQLITE3H) $(TOP)\VERSION echo #ifndef SQLITE_RESOURCE_VERSION > sqlite3rc.h for /F %%V in ('type "$(TOP)\VERSION"') do ( \ echo #define SQLITE_RESOURCE_VERSION %%V \ | $(TCLSH_CMD) $(TOP)\tool\replace.tcl exact . ^, >> sqlite3rc.h \ ) @@ -2102,16 +2138,14 @@ # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c -parse.c: $(TOP)\src\parse.y lemon.exe $(TOP)\tool\addopcodes.tcl +parse.c: $(TOP)\src\parse.y lemon.exe del /Q parse.y parse.h parse.h.temp 2>NUL copy $(TOP)\src\parse.y . .\lemon.exe $(REQ_FEATURE_FLAGS) $(OPT_FEATURE_FLAGS) $(EXT_FEATURE_FLAGS) $(OPTS) parse.y - move parse.h parse.h.temp - $(TCLSH_CMD) $(TOP)\tool\addopcodes.tcl parse.h.temp > parse.h $(SQLITE3H): $(TOP)\src\sqlite.h.in $(TOP)\manifest mksourceid.exe $(TOP)\VERSION $(TCLSH_CMD) $(TOP)\tool\mksqlite3h.tcl $(TOP:\=/) > $(SQLITE3H) $(MKSQLITE3H_ARGS) sqlite3ext.h: .target_source @@ -2513,10 +2547,11 @@ clean: del /Q *.exp *.lo *.ilk *.lib *.obj *.ncb *.pdb *.sdf *.suo 2>NUL del /Q *.bsc *.def *.cod *.da *.bb *.bbg *.vc gmon.out 2>NUL del /Q $(SQLITE3EXE) $(SQLITE3DLL) Replace.exe 2>NUL # <> + del /Q $(SQLITE3TCLDLL) pkgIndex.tcl 2>NUL del /Q opcodes.c opcodes.h 2>NUL del /Q lemon.* lempar.c parse.* 2>NUL del /Q mksourceid.* mkkeywordhash.* keywordhash.h 2>NUL del /Q notasharedlib.* 2>NUL -rmdir /Q/S .deps 2>NUL @@ -2523,10 +2558,11 @@ -rmdir /Q/S .libs 2>NUL -rmdir /Q/S tsrc 2>NUL del /Q .target_source 2>NUL del /Q tclsqlite3.exe $(SQLITETCLH) $(SQLITETCLDECLSH) 2>NUL del /Q lsm.dll lsmtest.exe 2>NUL + del /Q atrc.exe changesetfuzz.exe dbtotxt.exe index_usage.exe 2>NUL del /Q testloadext.dll 2>NUL del /Q testfixture.exe test.db 2>NUL del /Q LogEst.exe fts3view.exe rollback-test.exe showdb.exe dbdump.exe 2>NUL del /Q changeset.exe 2>NUL del /Q showjournal.exe showstat4.exe showwal.exe speedtest1.exe 2>NUL Index: README.md ================================================================== --- README.md +++ README.md @@ -39,11 +39,11 @@ [Tarball](https://www.sqlite.org/src/tarball/sqlite.tar.gz?r=release), [ZIP-archive](https://www.sqlite.org/src/zip/sqlite.zip?r=release), or [SQLite-archive](https://www.sqlite.org/src/sqlar/sqlite.sqlar?r=release). * For other check-ins, substitute an appropriate branch name or - tag or hash prefix for "release" in the URLs of the previous + tag or hash prefix in place of "release" in the URLs of the previous bullet. Or browse the [timeline](https://www.sqlite.org/src/timeline) to locate the check-in desired, click on its information page link, then click on the "Tarball" or "ZIP Archive" links on the information page. @@ -173,15 +173,12 @@ The SQL language parser is **parse.c** which is generate from a grammar in the src/parse.y file. The conversion of "parse.y" into "parse.c" is done by the [lemon](./doc/lemon.html) LALR(1) parser generator. The source code for lemon is at tool/lemon.c. Lemon uses the tool/lempar.c file as a template for generating its parser. - Lemon also generates the **parse.h** header file, at the same time it -generates parse.c. But the parse.h header file is -modified further (to add additional symbols) using the ./addopcodes.tcl -Tcl script. +generates parse.c. The **opcodes.h** header file contains macros that define the numbers corresponding to opcodes in the "VDBE" virtual machine. The opcodes.h file is generated by the scanning the src/vdbe.c source file. The Tcl script at ./mkopcodeh.tcl does this scan and generates opcodes.h. @@ -311,32 +308,37 @@ If you obtained an SQLite source tree from a secondary source, such as a GitHub mirror, and you want to verify that it has not been altered, there are a couple of ways to do that. -If you have an official release version of SQLite, and you are using the +If you have a release version of SQLite, and you are using the `sqlite3.c` amalgamation, then SHA3-256 hashes for the amalgamation are available in the [change log](https://www.sqlite.org/changes.html) on the official website. After building the `sqlite3.c` file, you can check -that is authentic by comparing the hash. This does not ensure that the +that it is authentic by comparing the hash. This does not ensure that the test scripts are unaltered, but it does validate the deliverable part of -the code and only involves computing and comparing a single hash. +the code and the verification process only involves computing and +comparing a single hash. For versions other than an official release, or if you are building the `sqlite3.c` amalgamation using non-standard build options, the verification process is a little more involved. The `manifest` file at the root directory -of the source tree ([example](https://sqlite.org/src/artifact/bd49a8271d650fa8)) +of the source tree contains either a SHA3-256 hash (for newer files) or a SHA1 hash (for older files) for every source file in the repository. You can write a script to extracts hashes from `manifest` and verifies the hashes against the corresponding files in the source tree. The SHA3-256 hash of the `manifest` file itself is the official name of the version of the source tree that you have. The `manifest.uuid` file should contain the SHA3-256 hash of the `manifest` file. If all of the above hash comparisons are correct, then you can be confident that your source tree is authentic and unadulterated. +The format of the `manifest` file should be mostly self-explanatory, but +if you want details, they are available +[here](https://fossil-scm.org/fossil/doc/trunk/www/fileformat.wiki#manifest). + ## Contacts -The main SQLite webpage is [http://www.sqlite.org/](http://www.sqlite.org/) +The main SQLite website is [http://www.sqlite.org/](http://www.sqlite.org/) with geographically distributed backups at [http://www2.sqlite.org/](http://www2.sqlite.org) and [http://www3.sqlite.org/](http://www3.sqlite.org). Index: autoconf/Makefile.msc ================================================================== --- autoconf/Makefile.msc +++ autoconf/Makefile.msc @@ -431,13 +431,13 @@ # C compiler and options for use in building executables that # will run on the platform that is doing the build. # !IF $(USE_FULLWARN)!=0 -BCC = $(NCC) -nologo -W4 $(CCOPTS) $(BCCOPTS) +BCC = $(NCC) -nologo -W4 -Fd$*.pdb $(CCOPTS) $(BCCOPTS) !ELSE -BCC = $(NCC) -nologo -W3 $(CCOPTS) $(BCCOPTS) +BCC = $(NCC) -nologo -W3 -Fd$*.pdb $(CCOPTS) $(BCCOPTS) !ENDIF # Check if assembly code listings should be generated for the source # code files to be compiled. # @@ -806,11 +806,11 @@ # Command line prefixes for compiling code, compiling resources, # linking, etc. # -LTCOMPILE = $(TCC) -Fo$@ +LTCOMPILE = $(TCC) -Fo$@ -Fd$*.pdb LTRCOMPILE = $(RCC) -r LTLIB = lib.exe LTLINK = $(TCC) -Fe$@ # If requested, link to the RPCRT4 library. @@ -824,10 +824,15 @@ # set this for you. Otherwise, the linker will attempt # to deduce the binary type based on the object files. !IFDEF PLATFORM LTLINKOPTS = /NOLOGO /MACHINE:$(PLATFORM) LTLIBOPTS = /NOLOGO /MACHINE:$(PLATFORM) +!ELSEIF "$(VISUALSTUDIOVERSION)"=="12.0" || \ + "$(VISUALSTUDIOVERSION)"=="14.0" || \ + "$(VISUALSTUDIOVERSION)"=="15.0" +LTLINKOPTS = /NOLOGO /MACHINE:x86 +LTLIBOPTS = /NOLOGO /MACHINE:x86 !ELSE LTLINKOPTS = /NOLOGO LTLIBOPTS = /NOLOGO !ENDIF Index: ext/fts3/fts3_snippet.c ================================================================== --- ext/fts3/fts3_snippet.c +++ ext/fts3/fts3_snippet.c @@ -126,21 +126,23 @@ */ /* ** Allocate a two-slot MatchinfoBuffer object. */ -static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){ +static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ MatchinfoBuffer *pRet; - int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer); - int nStr = (int)strlen(zMatchinfo); + sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) + + sizeof(MatchinfoBuffer); + sqlite3_int64 nStr = strlen(zMatchinfo); - pRet = sqlite3_malloc(nByte + nStr+1); + pRet = sqlite3_malloc64(nByte + nStr+1); if( pRet ){ memset(pRet, 0, nByte); pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; - pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1); - pRet->nElem = nElem; + pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + + sizeof(u32)*((int)nElem+1); + pRet->nElem = (int)nElem; pRet->zMatchinfo = ((char*)pRet) + nByte; memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); pRet->aRef[0] = 1; } @@ -997,12 +999,12 @@ } sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo request: %c", cArg); return SQLITE_ERROR; } -static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ - int nVal; /* Number of integers output by cArg */ +static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){ + size_t nVal; /* Number of integers output by cArg */ switch( cArg ){ case FTS3_MATCHINFO_NDOC: case FTS3_MATCHINFO_NPHRASE: case FTS3_MATCHINFO_NCOL: @@ -1282,11 +1284,11 @@ } break; case FTS3_MATCHINFO_LHITS_BM: case FTS3_MATCHINFO_LHITS: { - int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); + size_t nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32); memset(pInfo->aMatchinfo, 0, nZero); rc = fts3ExprLHitGather(pCsr->pExpr, pInfo); break; } @@ -1351,11 +1353,11 @@ ** matchinfo function has been called for this query. In this case ** allocate the array used to accumulate the matchinfo data and ** initialize those elements that are constant for every row. */ if( pCsr->pMIBuffer==0 ){ - int nMatchinfo = 0; /* Number of u32 elements in match-info */ + size_t nMatchinfo = 0; /* Number of u32 elements in match-info */ int i; /* Used to iterate through zArg */ /* Determine the number of phrases in the query */ pCsr->nPhrase = fts3ExprPhraseCount(pCsr->pExpr); sInfo.nPhrase = pCsr->nPhrase; Index: ext/fts3/fts3_test.c ================================================================== --- ext/fts3/fts3_test.c +++ ext/fts3/fts3_test.c @@ -446,18 +446,18 @@ if( p==pEnd ){ rc = SQLITE_DONE; }else{ /* Advance to the end of the token */ const char *pToken = p; - int nToken; + sqlite3_int64 nToken; while( ppCsr->nBuffer ){ sqlite3_free(pCsr->aBuffer); - pCsr->aBuffer = sqlite3_malloc(nToken); + pCsr->aBuffer = sqlite3_malloc64(nToken); } if( pCsr->aBuffer==0 ){ rc = SQLITE_NOMEM; }else{ int i; @@ -469,11 +469,11 @@ } pCsr->iToken++; pCsr->iInput = (int)(p - pCsr->aInput); *ppToken = pCsr->aBuffer; - *pnBytes = nToken; + *pnBytes = (int)nToken; *piStartOffset = (int)(pToken - pCsr->aInput); *piEndOffset = (int)(p - pCsr->aInput); *piPosition = pCsr->iToken; } } Index: ext/fts3/fts3_tokenize_vtab.c ================================================================== --- ext/fts3/fts3_tokenize_vtab.c +++ ext/fts3/fts3_tokenize_vtab.c @@ -344,11 +344,11 @@ fts3tokResetCursor(pCsr); if( idxNum==1 ){ const char *zByte = (const char *)sqlite3_value_text(apVal[0]); int nByte = sqlite3_value_bytes(apVal[0]); - pCsr->zInput = sqlite3_malloc(nByte+1); + pCsr->zInput = sqlite3_malloc64(nByte+1); if( pCsr->zInput==0 ){ rc = SQLITE_NOMEM; }else{ memcpy(pCsr->zInput, zByte, nByte); pCsr->zInput[nByte] = 0; Index: ext/fts3/fts3_tokenizer.c ================================================================== --- ext/fts3/fts3_tokenizer.c +++ ext/fts3/fts3_tokenizer.c @@ -77,11 +77,11 @@ zName = sqlite3_value_text(argv[0]); nName = sqlite3_value_bytes(argv[0])+1; if( argc==2 ){ - if( fts3TokenizerEnabled(context) ){ + if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[1]) ){ void *pOld; int n = sqlite3_value_bytes(argv[1]); if( zName==0 || n!=sizeof(pPtr) ){ sqlite3_result_error(context, "argument type mismatch", -1); return; @@ -104,11 +104,11 @@ sqlite3_result_error(context, zErr, -1); sqlite3_free(zErr); return; } } - if( fts3TokenizerEnabled(context) ){ + if( fts3TokenizerEnabled(context) || sqlite3_value_frombind(argv[0]) ){ sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } } int sqlite3Fts3IsIdChar(char c){ @@ -194,12 +194,12 @@ }else{ char const **aArg = 0; int iArg = 0; z = &z[n+1]; while( z0 ){ - int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); - pReader = (Fts3SegReader *)sqlite3_malloc(nByte); + sqlite3_int64 nByte; + nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); + pReader = (Fts3SegReader *)sqlite3_malloc64(nByte); if( !pReader ){ rc = SQLITE_NOMEM; }else{ memset(pReader, 0, nByte); pReader->iIdx = 0x7FFFFFFF; @@ -3365,11 +3366,11 @@ int nBlob; /* Number of bytes in the BLOB */ sqlite3_stmt *pStmt; /* Statement used to insert the encoding */ int rc; /* Result code from subfunctions */ if( *pRC ) return; - pBlob = sqlite3_malloc( 10*p->nColumn ); + pBlob = sqlite3_malloc64( 10*(sqlite3_int64)p->nColumn ); if( pBlob==0 ){ *pRC = SQLITE_NOMEM; return; } fts3EncodeIntArray(p->nColumn, aSz, pBlob, &nBlob); @@ -3415,11 +3416,11 @@ int rc; /* Result code from subfunctions */ const int nStat = p->nColumn+2; if( *pRC ) return; - a = sqlite3_malloc( (sizeof(u32)+10)*nStat ); + a = sqlite3_malloc64( (sizeof(u32)+10)*(sqlite3_int64)nStat ); if( a==0 ){ *pRC = SQLITE_NOMEM; return; } pBlob = (char*)&a[nStat]; @@ -3536,12 +3537,12 @@ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); sqlite3_free(zSql); } if( rc==SQLITE_OK ){ - int nByte = sizeof(u32) * (p->nColumn+1)*3; - aSz = (u32 *)sqlite3_malloc(nByte); + sqlite3_int64 nByte = sizeof(u32) * ((sqlite3_int64)p->nColumn+1)*3; + aSz = (u32 *)sqlite3_malloc64(nByte); if( aSz==0 ){ rc = SQLITE_NOMEM; }else{ memset(aSz, 0, nByte); aSzIns = &aSz[p->nColumn+1]; @@ -3603,16 +3604,16 @@ int nSeg, /* Number of segments to merge */ Fts3MultiSegReader *pCsr /* Cursor object to populate */ ){ int rc; /* Return Code */ sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */ - int nByte; /* Bytes allocated at pCsr->apSegment[] */ + sqlite3_int64 nByte; /* Bytes allocated at pCsr->apSegment[] */ /* Allocate space for the Fts3MultiSegReader.aCsr[] array */ memset(pCsr, 0, sizeof(*pCsr)); nByte = sizeof(Fts3SegReader *) * nSeg; - pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte); + pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc64(nByte); if( pCsr->apSegment==0 ){ rc = SQLITE_NOMEM; }else{ memset(pCsr->apSegment, 0, nByte); @@ -5588,11 +5589,11 @@ rc = SQLITE_CONSTRAINT; goto update_out; } /* Allocate space to hold the change in document sizes */ - aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 ); + aSzDel = sqlite3_malloc64(sizeof(aSzDel[0])*((sqlite3_int64)p->nColumn+1)*2); if( aSzDel==0 ){ rc = SQLITE_NOMEM; goto update_out; } aSzIns = &aSzDel[p->nColumn+1]; Index: ext/fts5/fts5_index.c ================================================================== --- ext/fts5/fts5_index.c +++ ext/fts5/fts5_index.c @@ -2641,12 +2641,12 @@ }else if( p2->pLeaf==0 ){ /* If p2 is at EOF */ iRes = i1; }else{ int res = fts5BufferCompare(&p1->term, &p2->term); if( res==0 ){ - assert( i2>i1 ); - assert( i2!=0 ); + assert_nc( i2>i1 ); + assert_nc( i2!=0 ); pRes->bTermEq = 1; if( p1->iRowid==p2->iRowid ){ p1->bDel = p2->bDel; return i2; } @@ -3689,11 +3689,11 @@ pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl ); if( aDlidx==0 ){ p->rc = SQLITE_NOMEM; }else{ - int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); + size_t nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx); memset(&aDlidx[pWriter->nDlidx], 0, nByte); pWriter->aDlidx = aDlidx; pWriter->nDlidx = nLvl; } } Index: ext/fts5/fts5_main.c ================================================================== --- ext/fts5/fts5_main.c +++ ext/fts5/fts5_main.c @@ -2466,18 +2466,18 @@ ){ Fts5Global *pGlobal = (Fts5Global*)pApi; int rc = sqlite3_overload_function(pGlobal->db, zName, -1); if( rc==SQLITE_OK ){ Fts5Auxiliary *pAux; - int nName; /* Size of zName in bytes, including \0 */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nName; /* Size of zName in bytes, including \0 */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ - nName = (int)strlen(zName) + 1; + nName = strlen(zName) + 1; nByte = sizeof(Fts5Auxiliary) + nName; - pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte); + pAux = (Fts5Auxiliary*)sqlite3_malloc64(nByte); if( pAux ){ - memset(pAux, 0, nByte); + memset(pAux, 0, (size_t)nByte); pAux->zFunc = (char*)&pAux[1]; memcpy(pAux->zFunc, zName, nName); pAux->pGlobal = pGlobal; pAux->pUserData = pUserData; pAux->xFunc = xFunc; @@ -2503,19 +2503,19 @@ fts5_tokenizer *pTokenizer, /* Tokenizer implementation */ void(*xDestroy)(void*) /* Destructor for pUserData */ ){ Fts5Global *pGlobal = (Fts5Global*)pApi; Fts5TokenizerModule *pNew; - int nName; /* Size of zName and its \0 terminator */ - int nByte; /* Bytes of space to allocate */ + sqlite3_int64 nName; /* Size of zName and its \0 terminator */ + sqlite3_int64 nByte; /* Bytes of space to allocate */ int rc = SQLITE_OK; - nName = (int)strlen(zName) + 1; + nName = strlen(zName) + 1; nByte = sizeof(Fts5TokenizerModule) + nName; - pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte); + pNew = (Fts5TokenizerModule*)sqlite3_malloc64(nByte); if( pNew ){ - memset(pNew, 0, nByte); + memset(pNew, 0, (size_t)nByte); pNew->zName = (char*)&pNew[1]; memcpy(pNew->zName, zName, nName); pNew->pUserData = pUserData; pNew->x = *pTokenizer; pNew->xDestroy = xDestroy; Index: ext/fts5/fts5_tokenize.c ================================================================== --- ext/fts5/fts5_tokenize.c +++ ext/fts5/fts5_tokenize.c @@ -367,11 +367,11 @@ int i; memset(p, 0, sizeof(Unicode61Tokenizer)); p->eRemoveDiacritic = FTS5_REMOVE_DIACRITICS_SIMPLE; p->nFold = 64; - p->aFold = sqlite3_malloc(p->nFold * sizeof(char)); + p->aFold = sqlite3_malloc64(p->nFold * sizeof(char)); if( p->aFold==0 ){ rc = SQLITE_NOMEM; } /* Search for a "categories" argument */ Index: ext/fts5/test/fts5corrupt3.test ================================================================== --- ext/fts5/test/fts5corrupt3.test +++ ext/fts5/test/fts5corrupt3.test @@ -7992,13 +7992,143 @@ | 0: 0d 00 00 00 03 0f f2 00 0f fc 0f f7 0f f2 00 00 ................ | 4080: 00 00 03 03 02 01 03 03 02 02 01 02 02 01 02 09 ................ | end crash-2acc487d09f033.db }]} {} -do_catchsql_test 56.1 { - INSERT INTO t1(b) VALUES(randomblob(250)); - INSERT INTO t1(b) VALUES(randomblob(250)); +do_test 56.1 { + set res [catchsql { + INSERT INTO t1(b) VALUES(randomblob(250)); + INSERT INTO t1(b) VALUES(randomblob(250)); + }] + + # For some permutations - those that use the page-cache - this test + # may return SQLITE_CONSTRAINT instead of SQLITE_CORRUPT. This is because + # the corrupt db in the test over-reads the page buffer slightly, with + # different results depending on whether or not the page-cache is in use. + if {$res=="1 {constraint failed}"} { + set res "1 {database disk image is malformed}" + } + set res +} {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +reset_db +do_test 57.0 { + sqlite3 db {} + db deserialize [decode_hexdb { +| size 28672 pagesize 4096 filename x.db +| page 1 offset 0 +| 0: 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 SQLite format 3. +| 16: 10 00 01 01 00 40 20 20 00 00 00 01 00 00 00 07 .....@ ........ +| 32: 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 ................ +| 80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ................ +| 96: 00 2e 34 20 0d 00 00 00 07 0d d2 00 0f c4 0f 6d ..4 ...........m +| 112: 0f 02 0e ab 0e 4e 0d f6 0d d2 00 00 00 00 00 00 .....N.......... +| 3536: 00 00 22 07 06 17 11 11 01 31 74 61 62 6c 65 74 .........1tablet +| 3552: 32 74 32 07 43 52 45 41 54 45 20 54 41 42 4c 45 2t2.CREATE TABLE +| 3568: 20 74 32 28 78 29 56 06 06 17 1f 1f 01 7d 74 61 t2(x)V.......ta +| 3584: 61 6b 65 74 31 5f 63 6f 6e 66 69 67 74 31 5f 63 aket1_configt1_c +| 3600: 6f 7e 66 69 67 06 43 52 45 41 54 45 20 54 41 42 o~fig.CREATE TAB +| 3616: 4c 45 20 27 74 31 5f 63 6f 6e 66 69 67 27 28 6b LE 't1_config'(k +| 3632: 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 76 29 PRIMARY KEY, v) +| 3648: 20 57 49 54 48 4f 55 54 20 52 4f 57 49 44 5b 05 WITHOUT ROWID[. +| 3664: 07 17 21 21 01 81 01 74 61 62 6c 65 74 31 5f 64 ..!!...tablet1_d +| 3680: 6f 63 73 69 7a 65 74 31 5f 64 6f 63 73 69 7a 65 ocsizet1_docsize +| 3696: 05 43 52 45 41 54 45 20 54 41 42 4c 45 20 27 74 .CREATE TABLE 't +| 3712: 31 5f 64 6f 63 73 69 7a 65 27 28 69 64 20 49 4e 1_docsize'(id IN +| 3728: 54 45 47 45 52 20 50 52 49 4d 41 52 59 20 4b 45 TEGER PRIMARY KE +| 3744: 59 2c 20 73 7a 20 42 4c 4f 42 29 55 04 06 17 21 Y, sz BLOB)U...! +| 3760: 21 01 77 74 61 62 6c 65 74 31 5f 63 6f 6e 74 1d !.wtablet1_cont. +| 3776: 6e 74 74 31 5f 63 6f 6e 74 65 6e 74 04 43 52 45 ntt1_content.CRE +| 3792: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 63 6f ATE TABLE 't1_co +| 3808: 6e 74 65 6e 74 27 28 69 64 20 49 4e 54 45 47 45 ntent'(id INTEGE +| 3824: 52 20 50 52 49 4d 41 52 59 20 4b 45 59 2c 20 63 R PRIMARY KEY, c +| 3840: 30 29 69 03 07 17 19 19 01 81 2d 74 61 62 6c 65 0)i.......-table +| 3856: 74 31 5f 69 64 78 74 31 5f 59 64 78 03 43 52 45 t1_idxt1_Ydx.CRE +| 3872: 41 54 45 20 54 41 42 4c 45 20 27 74 31 5f 69 64 ATE TABLE 't1_id +| 3888: 78 27 28 73 65 67 69 64 2c 20 74 65 72 6d 2c 20 x'(segid, term, +| 3904: 70 67 6e 6f 2c 20 50 52 49 4d 41 52 59 20 4b 45 pgno, PRIMARY KE +| 3920: 59 28 73 65 67 69 64 2c 20 74 65 72 6d 29 29 20 Y(segid, term)) +| 3936: 57 49 54 48 4f 55 54 20 52 4f 57 49 44 55 02 07 WITHOUT ROWIDU.. +| 3952: 17 1b 1b 01 81 01 74 61 62 6c 65 74 31 5f 64 61 ......tablet1_da +| 3968: 74 61 74 31 5f 64 61 64 61 02 43 52 45 41 54 45 tat1_dada.CREATE +| 3984: 20 54 41 42 4c 45 20 27 74 31 5f 64 61 74 61 27 TABLE 't1_data' +| 4000: 28 69 64 20 49 4e 54 45 47 45 52 20 50 52 49 4d (id INTEGER PRIM +| 4016: 41 52 59 20 4b 45 59 2c 20 62 6c 6f 63 6b 20 42 ARY KEY, block B +| 4032: 4c 4f 42 29 3a 01 06 17 11 11 08 63 74 61 62 6c LOB):......ctabl +| 4048: 65 74 31 74 31 43 52 45 41 54 45 20 56 49 52 54 et1t1CREATE VIRT +| 4064: 55 41 4c 20 54 41 42 4c 45 20 74 31 20 55 53 49 UAL TABLE t1 USI +| 4080: 4e 47 20 66 74 73 35 28 63 6f 6e 74 65 6e 74 29 NG fts5(content) +| page 2 offset 4096 +| 0: 0d 0e b4 00 06 0e 35 00 0f e8 0e 35 0f bd 0f 4e ......5....5...N +| 16: 0e cb 0e 4f 00 00 00 00 00 00 00 00 00 00 00 00 ...O............ +| 3632: 00 00 00 00 00 18 0a 03 00 36 00 00 00 00 01 04 .........6...... +| 3648: 04 00 04 01 01 01 02 01 01 03 01 01 04 01 01 5e ...............^ +| 3664: 90 80 80 80 80 01 04 00 81 40 00 00 00 51 06 30 .........@...Q.0 +| 3680: 61 62 61 63 6b 01 01 04 04 6e 64 6f 6e 01 01 02 aback....ndon... +| 3696: 04 63 69 76 65 01 01 02 04 6c 70 68 61 01 01 02 .cive....lpha... +| 3712: 03 74 6f 6d 01 01 01 06 62 61 63 6b 75 70 01 01 .tom....backup.. +| 3728: 02 05 6f 6f 6d 65 72 01 01 01 06 63 68 61 6e 6e ..oomer....chann +| 3744: 65 01 01 01 04 74 65 73 74 01 01 04 09 08 08 08 e....test....... +| 3760: 07 0a 09 0a 0f 3a 00 17 30 00 00 00 00 01 03 03 .....:..0....... +| 3776: 00 03 01 01 01 02 01 01 03 01 01 68 8c 80 80 80 ...........h.... +| 3792: 80 01 04 00 81 54 00 00 00 5b 06 30 61 62 61 63 .....T...[.0abac +| 3808: 6b 02 02 07 04 04 6e 64 6f 6e 02 02 05 02 04 63 k.....ndon.....c +| 3824: 69 76 65 02 02 0b 02 04 6c 70 68 61 02 04 02 0a ive.....lpha.... +| 3840: 02 03 74 6f 6d 02 02 09 01 06 62 61 63 6b 75 70 ..tom.....backup +| 3856: 02 02 04 02 05 6f 6f 6d 65 72 02 02 08 01 06 63 .....oomer.....c +| 3872: 68 61 6e 6e 65 02 02 03 01 04 74 65 73 74 02 02 hanne.....test.. +| 3888: 06 04 0a 09 09 0a 08 0b 0a 0b 0f ef 00 14 2a 00 ..............*. +| 3904: 00 00 00 01 02 02 00 02 01 01 01 02 01 01 68 88 ..............h. +| 3920: 80 80 80 80 01 04 00 81 54 00 00 00 5b 06 30 61 ........T...[.0a +| 3936: 62 61 63 6b 01 02 07 04 04 6e 64 6f 6e 01 02 05 back.....ndon... +| 3952: 02 04 63 69 76 65 01 02 0b 02 04 6c 70 68 61 01 ..cive.....lpha. +| 3968: 04 02 0a 02 03 74 6f 6d 01 02 09 01 06 62 61 63 .....tom.....bac +| 3984: 6b 75 70 01 02 04 02 05 6f 6f 6d 65 72 01 02 08 kup.....oomer... +| 4000: 01 06 63 68 61 6e 6e 65 01 02 03 01 04 74 65 73 ..channe.....tes +| 4016: 74 01 02 06 04 0a 09 09 0a 08 0b 0a 0b 24 84 80 t............$.. +| 4032: 80 80 80 01 03 00 4e 00 00 00 1e 06 30 61 62 61 ......N.....0aba +| 4048: 63 6b 01 02 02 05 42 66 74 02 02 02 04 04 6e 64 ck....Bft.....nd +| 4064: 6f 6e 03 02 02 04 0a 07 05 01 03 00 10 04 0d 00 on.............. +| 4080: 00 00 11 24 00 00 00 00 01 01 01 00 01 01 01 01 ...$............ +| page 3 offset 8192 +| 0: 0a 00 00 00 04 0f e5 00 00 00 0f f3 0f ec 0f e5 ................ +| 4064: 00 00 00 00 00 06 04 01 0c 01 04 02 06 04 01 0c ................ +| 4080: 01 03 02 06 04 01 0c 01 02 02 05 04 09 0d 01 02 ................ +| page 4 offset 12288 +| 0: 0d 0e bc 00 04 0e 78 00 00 00 00 00 00 00 0e 78 ......x........x +| 16: 0e 78 00 00 00 00 00 00 00 00 00 00 00 00 00 00 .x.............. +| 3696: 00 00 00 00 00 00 00 00 42 02 04 00 81 09 61 6c ........B.....al +| 3712: 70 68 61 20 63 68 61 6e 6e 65 20 62 61 63 6b 75 pha channe backu +| 3728: 70 20 61 62 61 6e 64 6f 6e 20 74 65 73 74 20 61 p abandon test a +| 3744: 62 61 63 6b 20 62 6f 6f 6d 65 72 20 61 74 6f 6d back boomer atom +| 3760: 20 61 6c 70 68 61 20 61 63 69 76 65 00 00 00 44 alpha acive...D +| 3776: 81 09 61 6c 70 68 61 20 63 68 61 6e 6e 65 20 62 ..alpha channe b +| 3792: 61 63 6b 75 70 20 61 62 61 6e 64 6f 6e 20 74 65 ackup abandon te +| 3808: 73 74 20 61 62 61 63 6b 20 62 6f 6f 6d 65 72 20 st aback boomer +| 3824: 61 74 6f 6d 20 61 6c 70 68 61 20 61 63 69 76 65 atom alpha acive +| 4064: 0a 03 03 00 1b 61 4e 61 6e 64 6f 6e 08 02 03 00 .....aNandon.... +| 4080: 17 61 62 61 66 74 08 01 03 00 17 61 62 71 63 6b .abaft.....abqck +| page 5 offset 16384 +| 0: 0d 0f e8 00 04 0f e2 00 00 00 00 00 00 00 0f e2 ................ +| 16: 0f e2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +| 4064: 00 00 04 02 03 00 0e 0a 00 00 00 06 0e 0a 04 03 ................ +| 4080: 03 00 0e 01 04 02 03 00 0e 01 04 01 03 10 0e 01 ................ +| page 6 offset 20480 +| 0: 0a 00 00 00 01 0f f4 00 0f f4 00 00 00 00 00 00 ................ +| 4080: 00 00 00 00 0b 03 1b 01 76 65 72 73 69 6f 6e 04 ........version. +| page 7 offset 24576 +| 0: 0d 00 00 00 03 0f d6 00 0f f4 00 00 00 00 00 00 ................ +| 4048: 00 00 00 00 00 00 09 03 02 1b 72 65 62 75 69 6c ..........rebuil +| 4064: 64 11 02 02 2b 69 6e 74 65 67 72 69 74 79 2d 63 d...+integrity-c +| 4080: 68 65 63 6b 0a 01 02 1d 6f 70 74 69 6d 69 7a 65 heck....optimize +| end x.db +}]} {} + +do_catchsql_test 57.1 { + INSERT INTO t1(t1) VALUES('optimize') } {1 {database disk image is malformed}} + sqlite3_fts5_may_be_corrupt 0 finish_test ADDED ext/misc/blobio.c Index: ext/misc/blobio.c ================================================================== --- /dev/null +++ ext/misc/blobio.c @@ -0,0 +1,152 @@ +/* +** 2019-03-30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** An SQL function that uses the incremental BLOB I/O mechanism of SQLite +** to read or write part of a blob. This is intended for debugging use +** in the CLI. +** +** readblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,N) +** +** Returns N bytes of the blob starting at OFFSET. +** +** writeblob(SCHEMA,TABLE,COLUMN,ROWID,OFFSET,NEWDATA) +** +** NEWDATA must be a blob. The content of NEWDATA overwrites the +** existing BLOB data at SCHEMA.TABLE.COLUMN for row ROWID beginning +** at OFFSET bytes into the blob. +*/ +#include "sqlite3ext.h" +SQLITE_EXTENSION_INIT1 +#include +#include + +static void readblobFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_blob *pBlob = 0; + const char *zSchema; + const char *zTable; + const char *zColumn; + sqlite3_int64 iRowid; + int iOfst; + unsigned char *aData; + int nData; + sqlite3 *db; + int rc; + + zSchema = (const char*)sqlite3_value_text(argv[0]); + zTable = (const char*)sqlite3_value_text(argv[1]); + if( zTable==0 ){ + sqlite3_result_error(context, "bad table name", -1); + return; + } + zColumn = (const char*)sqlite3_value_text(argv[2]); + if( zTable==0 ){ + sqlite3_result_error(context, "bad column name", -1); + return; + } + iRowid = sqlite3_value_int64(argv[3]); + iOfst = sqlite3_value_int(argv[4]); + nData = sqlite3_value_int(argv[5]); + if( nData<=0 ) return; + aData = sqlite3_malloc64( nData+1 ); + if( aData==0 ){ + sqlite3_result_error_nomem(context); + return; + } + db = sqlite3_context_db_handle(context); + rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 0, &pBlob); + if( rc ){ + sqlite3_free(aData); + sqlite3_result_error(context, "cannot open BLOB pointer", -1); + return; + } + rc = sqlite3_blob_read(pBlob, aData, nData, iOfst); + sqlite3_blob_close(pBlob); + if( rc ){ + sqlite3_free(aData); + sqlite3_result_error(context, "BLOB write failed", -1); + }else{ + sqlite3_result_blob(context, aData, nData, sqlite3_free); + } +} + +static void writeblobFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_blob *pBlob = 0; + const char *zSchema; + const char *zTable; + const char *zColumn; + sqlite3_int64 iRowid; + int iOfst; + unsigned char *aData; + int nData; + sqlite3 *db; + int rc; + + zSchema = (const char*)sqlite3_value_text(argv[0]); + zTable = (const char*)sqlite3_value_text(argv[1]); + if( zTable==0 ){ + sqlite3_result_error(context, "bad table name", -1); + return; + } + zColumn = (const char*)sqlite3_value_text(argv[2]); + if( zTable==0 ){ + sqlite3_result_error(context, "bad column name", -1); + return; + } + iRowid = sqlite3_value_int64(argv[3]); + iOfst = sqlite3_value_int(argv[4]); + if( sqlite3_value_type(argv[5])!=SQLITE_BLOB ){ + sqlite3_result_error(context, "6th argument must be a BLOB", -1); + return; + } + nData = sqlite3_value_bytes(argv[5]); + aData = (unsigned char *)sqlite3_value_blob(argv[5]); + db = sqlite3_context_db_handle(context); + rc = sqlite3_blob_open(db, zSchema, zTable, zColumn, iRowid, 1, &pBlob); + if( rc ){ + sqlite3_result_error(context, "cannot open BLOB pointer", -1); + return; + } + rc = sqlite3_blob_write(pBlob, aData, nData, iOfst); + sqlite3_blob_close(pBlob); + if( rc ){ + sqlite3_result_error(context, "BLOB write failed", -1); + } +} + + +#ifdef _WIN32 +__declspec(dllexport) +#endif +int sqlite3_blobio_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + int rc = SQLITE_OK; + SQLITE_EXTENSION_INIT2(pApi); + (void)pzErrMsg; /* Unused parameter */ + rc = sqlite3_create_function(db, "readblob", 6, SQLITE_UTF8, 0, + readblobFunc, 0, 0); + if( rc==SQLITE_OK ){ + rc = sqlite3_create_function(db, "writeblob", 6, SQLITE_UTF8, 0, + writeblobFunc, 0, 0); + } + return rc; +} Index: ext/misc/fileio.c ================================================================== --- ext/misc/fileio.c +++ ext/misc/fileio.c @@ -156,11 +156,11 @@ if( pBuf==0 ){ sqlite3_result_error_nomem(ctx); fclose(in); return; } - if( nIn==fread(pBuf, 1, (size_t)nIn, in) ){ + if( nIn==(sqlite3_int64)fread(pBuf, 1, (size_t)nIn, in) ){ sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free); }else{ sqlite3_result_error_code(ctx, SQLITE_IOERR); sqlite3_free(pBuf); } ADDED ext/rbu/rbupartial.test Index: ext/rbu/rbupartial.test ================================================================== --- /dev/null +++ ext/rbu/rbupartial.test @@ -0,0 +1,86 @@ +# 2019 April 11 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +source [file join [file dirname [info script]] rbu_common.tcl] +set ::testprefix rbupartial + +db close +sqlite3_shutdown +sqlite3_config_uri 1 + +foreach {tn without_rowid a b c d} { + 1 "" a b c d + 2 "WITHOUT ROWID" aaa bbb ccc ddd + 3 "WITHOUT ROWID" "\"hello\"" {"one'two"} {[c]} ddd + 4 "WITHOUT ROWID" {`a b`} {"one'two"} {[c c c]} ddd + 5 "" a b c {"d""d"} + 6 "" {'one''two'} b {"c""c"} {"d""d"} +} { + eval [string map [list \ + %WITHOUT_ROWID% $without_rowid %A% $a %B% $b %C% $c %D% $d + ] { + reset_db + do_execsql_test $tn.1.0 { + CREATE TABLE t1(%A% PRIMARY KEY, %B%, %C%, %D%) %WITHOUT_ROWID% ; + CREATE INDEX i1b ON t1(%B%); + CREATE INDEX i1b2 ON t1(%B%) WHERE %C%<5; + CREATE INDEX i1b3 ON t1(%B%) WHERE %C%>=5; + + CREATE INDEX i1c ON t1(%C%); + CREATE INDEX i1c2 ON t1(%C%) WHERE %C% IS NULL; + CREATE INDEX i1c3 ON t1(%C%) WHERE %C% IS NOT NULL; + + CREATE INDEX i1c4 ON t1(%C%) WHERE %D% < 'd'; + } + + do_execsql_test $tn.1.1 { + INSERT INTO t1 VALUES(0, NULL, NULL, 'a'); + INSERT INTO t1 VALUES(1, 2, 3, 'b'); + INSERT INTO t1 VALUES(4, 5, 6, 'c'); + INSERT INTO t1 VALUES(7, 8, 9, 'd'); + } + + forcedelete rbu.db + do_test $tn.1.2 { + sqlite3 rbu rbu.db + rbu eval { + CREATE TABLE data_t1(%A%, %B%, %C%, %D%, rbu_control); + + INSERT INTO data_t1 VALUES(10, 11, 12, 'e', 0); + INSERT INTO data_t1 VALUES(13, 14, NULL, 'f', 0); + + INSERT INTO data_t1 VALUES(0, NULL, NULL, NULL, 1); + INSERT INTO data_t1 VALUES(4, NULL, NULL, NULL, 1); + + INSERT INTO data_t1 VALUES(7, NULL, 4, NULL, '..x.'); + INSERT INTO data_t1 VALUES(1, 10, NULL, NULL, '.xx.'); + } + rbu close + } {} + + do_test $tn.1.3 { + run_rbu test.db rbu.db + execsql { PRAGMA integrity_check } + } {ok} + + do_execsql_test $tn.1.4 { + SELECT * FROM t1 ORDER BY %A%; + } { + 1 10 {} b 7 8 4 d 10 11 12 e 13 14 {} f + } + + set step 0 + do_rbu_vacuum_test $tn.1.5 0 + }] +} + +finish_test Index: ext/rbu/sqlite3rbu.c ================================================================== --- ext/rbu/sqlite3rbu.c +++ ext/rbu/sqlite3rbu.c @@ -238,10 +238,15 @@ ** abIndexed: ** If the table has no indexes on it, abIndexed is set to NULL. Otherwise, ** it points to an array of flags nTblCol elements in size. The flag is ** set for each column that is either a part of the PK or a part of an ** index. Or clear otherwise. +** +** If there are one or more partial indexes on the table, all fields of +** this array set set to 1. This is because in that case, the module has +** no way to tell which fields will be required to add and remove entries +** from the partial indexes. ** */ struct RbuObjIter { sqlite3_stmt *pTblIter; /* Iterate through tables */ sqlite3_stmt *pIdxIter; /* Index iterator */ @@ -1033,11 +1038,11 @@ ** error code in the rbu handle passed as the first argument. Or, if an ** error has already occurred when this function is called, return NULL ** immediately without attempting the allocation or modifying the stored ** error code. */ -static void *rbuMalloc(sqlite3rbu *p, int nByte){ +static void *rbuMalloc(sqlite3rbu *p, sqlite3_int64 nByte){ void *pRet = 0; if( p->rc==SQLITE_OK ){ assert( nByte>0 ); pRet = sqlite3_malloc64(nByte); if( pRet==0 ){ @@ -1054,11 +1059,11 @@ ** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that ** there is room for at least nCol elements. If an OOM occurs, store an ** error code in the RBU handle passed as the first argument. */ static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){ - int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol; + sqlite3_int64 nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol; char **azNew; azNew = (char**)rbuMalloc(p, nByte); if( azNew ){ pIter->azTblCol = azNew; @@ -1248,12 +1253,16 @@ } pIter->nIndex = 0; while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){ const char *zIdx = (const char*)sqlite3_column_text(pList, 1); + int bPartial = sqlite3_column_int(pList, 4); sqlite3_stmt *pXInfo = 0; if( zIdx==0 ) break; + if( bPartial ){ + memset(pIter->abIndexed, 0x01, sizeof(u8)*pIter->nTblCol); + } p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg, sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx) ); while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){ int iCid = sqlite3_column_int(pXInfo, 1); @@ -1694,11 +1703,11 @@ ** when this function is called, NULL is returned immediately, without ** attempting the allocation or modifying the stored error code. */ static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){ char *zRet = 0; - int nByte = nBind*2 + 1; + sqlite3_int64 nByte = 2*(sqlite3_int64)nBind + 1; zRet = (char*)rbuMalloc(p, nByte); if( zRet ){ int i; for(i=0; irc; + char *zRet = 0; + + if( rc==SQLITE_OK ){ + rc = prepareAndCollectError(p->dbMain, &pStmt, &p->zErrmsg, + "SELECT trim(sql) FROM sqlite_master WHERE type='index' AND name=?" + ); + } + if( rc==SQLITE_OK ){ + int rc2; + rc = sqlite3_bind_text(pStmt, 1, pIter->zIdx, -1, SQLITE_STATIC); + if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ + const char *zSql = (const char*)sqlite3_column_text(pStmt, 0); + if( zSql ){ + int nParen = 0; /* Number of open parenthesis */ + int i; + for(i=0; zSql[i]; i++){ + char c = zSql[i]; + if( c=='(' ){ + nParen++; + } + else if( c==')' ){ + nParen--; + if( nParen==0 ){ + i++; + break; + } + }else if( c=='"' || c=='\'' || c=='`' ){ + for(i++; 1; i++){ + if( zSql[i]==c ){ + if( zSql[i+1]!=c ) break; + i++; + } + } + }else if( c=='[' ){ + for(i++; 1; i++){ + if( zSql[i]==']' ) break; + } + } + } + if( zSql[i] ){ + zRet = rbuStrndup(&zSql[i], &rc); + } + } + } + + rc2 = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) rc = rc2; + } + + p->rc = rc; + return zRet; +} /* ** Ensure that the SQLite statement handles required to update the ** target database object currently indicated by the iterator passed ** as the second argument are available. @@ -1985,17 +2050,19 @@ const char *zTbl = pIter->zTbl; char *zImposterCols = 0; /* Columns for imposter table */ char *zImposterPK = 0; /* Primary key declaration for imposter */ char *zWhere = 0; /* WHERE clause on PK columns */ char *zBind = 0; + char *zPart = 0; int nBind = 0; assert( pIter->eType!=RBU_PK_VTAB ); zCollist = rbuObjIterGetIndexCols( p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind ); zBind = rbuObjIterGetBindlist(p, nBind); + zPart = rbuObjIterGetIndexWhere(p, pIter); /* Create the imposter table used to write to this index. */ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1); sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1,tnum); rbuMPrintfExec(p, p->dbMain, @@ -2024,32 +2091,34 @@ /* Create the SELECT statement to read keys in sorted order */ if( p->rc==SQLITE_OK ){ char *zSql; if( rbuIsVacuum(p) ){ zSql = sqlite3_mprintf( - "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s", + "SELECT %s, 0 AS rbu_control FROM '%q' %s ORDER BY %s%s", zCollist, pIter->zDataTbl, - zCollist, zLimit + zPart, zCollist, zLimit ); }else if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s", + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s ORDER BY %s%s", zCollist, p->zStateDb, pIter->zDataTbl, - zCollist, zLimit + zPart, zCollist, zLimit ); }else{ zSql = sqlite3_mprintf( - "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' " + "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' %s " "UNION ALL " "SELECT %s, rbu_control FROM '%q' " - "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 " + "%s %s typeof(rbu_control)='integer' AND rbu_control!=1 " "ORDER BY %s%s", - zCollist, p->zStateDb, pIter->zDataTbl, + zCollist, p->zStateDb, pIter->zDataTbl, zPart, zCollist, pIter->zDataTbl, + zPart, + (zPart ? "AND" : "WHERE"), zCollist, zLimit ); } p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql); } @@ -2056,10 +2125,11 @@ sqlite3_free(zImposterCols); sqlite3_free(zImposterPK); sqlite3_free(zWhere); sqlite3_free(zBind); + sqlite3_free(zPart); }else{ int bRbuRowid = (pIter->eType==RBU_PK_VTAB) ||(pIter->eType==RBU_PK_NONE) ||(pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p)); const char *zTbl = pIter->zTbl; /* Table this step applies to */ @@ -4489,11 +4559,11 @@ ** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space ** instead of a file on disk. */ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) ); if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){ if( iRegion<=p->nShm ){ - int nByte = (iRegion+1) * sizeof(char*); + sqlite3_int64 nByte = (iRegion+1) * sizeof(char*); char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte); if( apNew==0 ){ rc = SQLITE_NOMEM; }else{ memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm)); Index: ext/rtree/geopoly.c ================================================================== --- ext/rtree/geopoly.c +++ ext/rtree/geopoly.c @@ -267,11 +267,11 @@ && (s.z++, geopolySkipSpace(&s)==0) ){ GeoPoly *pOut; int x = 1; s.nVertex--; /* Remove the redundant vertex at the end */ - pOut = sqlite3_malloc64( GEOPOLY_SZ(s.nVertex) ); + pOut = sqlite3_malloc64( GEOPOLY_SZ((sqlite3_int64)s.nVertex) ); x = 1; if( pOut==0 ) goto parse_json_err; pOut->nVertex = s.nVertex; memcpy(pOut->a, s.a, s.nVertex*2*sizeof(GeoCoord)); pOut->hdr[0] = *(unsigned char*)&x; @@ -653,11 +653,11 @@ else if( r>mxY ) mxY = (float)r; } if( pRc ) *pRc = SQLITE_OK; if( aCoord==0 ){ geopolyBboxFill: - pOut = sqlite3_realloc(p, GEOPOLY_SZ(4)); + pOut = sqlite3_realloc64(p, GEOPOLY_SZ(4)); if( pOut==0 ){ sqlite3_free(p); if( context ) sqlite3_result_error_nomem(context); if( pRc ) *pRc = SQLITE_NOMEM; return 0; @@ -1049,13 +1049,13 @@ /* ** Determine the overlap between two polygons */ static int geopolyOverlap(GeoPoly *p1, GeoPoly *p2){ - int nVertex = p1->nVertex + p2->nVertex + 2; + sqlite3_int64 nVertex = p1->nVertex + p2->nVertex + 2; GeoOverlap *p; - int nByte; + sqlite3_int64 nByte; GeoEvent *pThisEvent; double rX; int rc = 0; int needSort = 0; GeoSegment *pActive = 0; @@ -1063,11 +1063,11 @@ unsigned char aOverlap[4]; nByte = sizeof(GeoEvent)*nVertex*2 + sizeof(GeoSegment)*nVertex + sizeof(GeoOverlap); - p = sqlite3_malloc( nByte ); + p = sqlite3_malloc64( nByte ); if( p==0 ) return -1; p->aEvent = (GeoEvent*)&p[1]; p->aSegment = (GeoSegment*)&p->aEvent[nVertex*2]; p->nEvent = p->nSegment = 0; geopolyAddSegments(p, p1, 1); @@ -1222,22 +1222,22 @@ char **pzErr, /* OUT: Error message, if any */ int isCreate /* True for xCreate, false for xConnect */ ){ int rc = SQLITE_OK; Rtree *pRtree; - int nDb; /* Length of string argv[1] */ - int nName; /* Length of string argv[2] */ + sqlite3_int64 nDb; /* Length of string argv[1] */ + sqlite3_int64 nName; /* Length of string argv[2] */ sqlite3_str *pSql; char *zSql; int ii; sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1); /* Allocate the sqlite3_vtab structure */ - nDb = (int)strlen(argv[1]); - nName = (int)strlen(argv[2]); - pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2); + nDb = strlen(argv[1]); + nName = strlen(argv[2]); + pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2); if( !pRtree ){ return SQLITE_NOMEM; } memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2); pRtree->nBusy = 1; Index: ext/session/changesetfuzz.c ================================================================== --- ext/session/changesetfuzz.c +++ ext/session/changesetfuzz.c @@ -152,21 +152,22 @@ sz = ftell(f); rewind(f); pBuf = sqlite3_malloc64( sz ? sz : 1 ); if( pBuf==0 ){ fprintf(stderr, "cannot allocate %d to hold content of \"%s\"\n", - sz, zFilename); + (int)sz, zFilename); exit(1); } if( sz>0 ){ - if( fread(pBuf, sz, 1, f)!=1 ){ - fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", sz, zFilename); + if( fread(pBuf, (size_t)sz, 1, f)!=1 ){ + fprintf(stderr, "cannot read all %d bytes of \"%s\"\n", + (int)sz, zFilename); exit(1); } fclose(f); } - *pSz = sz; + *pSz = (int)sz; *ppBuf = pBuf; } /* ** Write the contents of buffer pBuf, size nBuf bytes, into file zFilename @@ -341,11 +342,11 @@ ** Allocate and return nByte bytes of zeroed memory. */ static void *fuzzMalloc(sqlite3_int64 nByte){ void *pRet = sqlite3_malloc64(nByte); if( pRet ){ - memset(pRet, 0, nByte); + memset(pRet, 0, (size_t)nByte); } return pRet; } /* @@ -382,11 +383,11 @@ ** Return the number of bytes written to buffer p. */ static int fuzzPutVarint(u8 *p, int nVal){ assert( nVal>0 && nVal<2097152 ); if( nVal<128 ){ - p[0] = nVal; + p[0] = (u8)nVal; return 1; } if( nVal<16384 ){ p[0] = ((nVal >> 7) & 0x7F) | 0x80; p[1] = (nVal & 0x7F); @@ -457,11 +458,11 @@ p++; p += fuzzGetVarint(p, &pGrp->nCol); pGrp->aPK = p; p += pGrp->nCol; pGrp->zTab = (const char*)p; - p = &p[strlen(p)+1]; + p = &p[strlen((const char*)p)+1]; if( p>=pEnd ){ rc = fuzzCorrupt(); } } @@ -693,12 +694,10 @@ } case 0x03: /* text */ case 0x04: { /* blob */ int nTxt; - int sz; - int i; p += fuzzGetVarint(p, &nTxt); printf("%s%s", zPre, eType==0x03 ? "'" : "X'"); for(i=0; iaSub[1] = nByte; + pChange->aSub[1] = (u8)nByte; fuzzRandomBlob(nByte, &pChange->aSub[2]); if( pChange->aSub[0]==0x03 ){ int i; for(i=0; iaSub[2+i] &= 0x7F; @@ -1002,11 +1001,11 @@ if( p==pFuzz->pSub1 ){ pCopy = pFuzz->pSub2; }else if( p==pFuzz->pSub2 ){ pCopy = pFuzz->pSub1; }else if( i==iUndef ){ - pCopy = "\0"; + pCopy = (u8*)"\0"; } if( pCopy[0]==0x00 && eNew!=eType && eType==SQLITE_UPDATE && iRec==0 ){ while( pCopy[0]==0x00 ){ pCopy = pParse->apVal[fuzzRandomInt(pParse->nVal)]; @@ -1065,11 +1064,11 @@ int i; u8 *pCsr = (*ppOut) + 2; for(i=0; inCol; i++){ int sz; u8 *pCopy = pCsr; - if( pGrp->aPK[i] ) pCopy = "\0"; + if( pGrp->aPK[i] ) pCopy = (u8*)"\0"; fuzzChangeSize(pCopy, &sz); memcpy(pOut, pCopy, sz); pOut += sz; fuzzChangeSize(pCsr, &sz); pCsr += sz; Index: ext/session/sqlite3session.c ================================================================== --- ext/session/sqlite3session.c +++ ext/session/sqlite3session.c @@ -900,11 +900,11 @@ */ static int sessionGrowHash(int bPatchset, SessionTable *pTab){ if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){ int i; SessionChange **apNew; - int nNew = (pTab->nChange ? pTab->nChange : 128) * 2; + sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128); apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew); if( apNew==0 ){ if( pTab->nChange==0 ){ return SQLITE_ERROR; @@ -1827,11 +1827,11 @@ ** If not, use sqlite3_realloc() to grow the buffer so that there is. ** ** If successful, return zero. Otherwise, if an OOM condition is encountered, ** set *pRc to SQLITE_NOMEM and return non-zero. */ -static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){ +static int sessionBufferGrow(SessionBuffer *p, size_t nByte, int *pRc){ if( *pRc==SQLITE_OK && p->nAlloc-p->nBufnAlloc ? p->nAlloc : 128; do { nNew = nNew*2; @@ -2945,11 +2945,11 @@ rc = SQLITE_CORRUPT_BKPT; } } if( rc==SQLITE_OK ){ - int iPK = sizeof(sqlite3_value*)*p->nCol*2; + size_t iPK = sizeof(sqlite3_value*)*p->nCol*2; memset(p->tblhdr.aBuf, 0, iPK); memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy); p->in.iNext += nCopy; } @@ -3860,11 +3860,11 @@ return rc; } /* -** This function is called from within sqlite3changset_apply_v2() when +** This function is called from within sqlite3changeset_apply_v2() when ** a conflict is encountered and resolved using conflict resolution ** mode eType (either SQLITE_CHANGESET_OMIT or SQLITE_CHANGESET_REPLACE).. ** It adds a conflict resolution record to the buffer in ** SessionApplyCtx.rebase, which will eventually be returned to the caller ** of apply_v2() as the "rebase" buffer. @@ -4249,11 +4249,11 @@ SessionBuffer cons = pApply->constraints; memset(&pApply->constraints, 0, sizeof(SessionBuffer)); rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf, 0); if( rc==SQLITE_OK ){ - int nByte = 2*pApply->nCol*sizeof(sqlite3_value*); + size_t nByte = 2*pApply->nCol*sizeof(sqlite3_value*); int rc2; pIter2->bPatchset = bPatchset; pIter2->zTab = (char*)zTab; pIter2->nCol = pApply->nCol; pIter2->abPK = pApply->abPK; Index: ext/session/sqlite3session.h ================================================================== --- ext/session/sqlite3session.h +++ ext/session/sqlite3session.h @@ -1413,11 +1413,11 @@ ** ** Argument pIn must point to a buffer containing a changeset nIn bytes ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) -** is set to point to the new buffer containing the rebased changset and +** is set to point to the new buffer containing the rebased changeset and ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** responsibility of the caller to eventually free the new buffer using ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) ** are set to zero and an SQLite error code returned. */ Index: main.mk ================================================================== --- main.mk +++ main.mk @@ -347,10 +347,11 @@ $(TOP)/src/test_superlock.c \ $(TOP)/src/test_syscall.c \ $(TOP)/src/test_tclsh.c \ $(TOP)/src/test_tclvar.c \ $(TOP)/src/test_thread.c \ + $(TOP)/src/test_vdbecov.c \ $(TOP)/src/test_vfs.c \ $(TOP)/src/test_windirent.c \ $(TOP)/src/test_window.c \ $(TOP)/src/test_wsd.c @@ -376,11 +377,10 @@ $(TOP)/ext/misc/series.c \ $(TOP)/ext/misc/spellfix.c \ $(TOP)/ext/misc/totype.c \ $(TOP)/ext/misc/unionvtab.c \ $(TOP)/ext/misc/wholenumber.c \ - $(TOP)/ext/misc/vfslog.c \ $(TOP)/ext/misc/zipfile.c \ $(TOP)/ext/fts5/fts5_tcl.c \ $(TOP)/ext/fts5/fts5_test_mi.c \ $(TOP)/ext/fts5/fts5_test_tok.c @@ -715,16 +715,13 @@ # Rules to build parse.c and parse.h - the outputs of lemon. # parse.h: parse.c -parse.c: $(TOP)/src/parse.y lemon $(TOP)/tool/addopcodes.tcl +parse.c: $(TOP)/src/parse.y lemon cp $(TOP)/src/parse.y . - rm -f parse.h ./lemon -s $(OPTS) parse.y - mv parse.h parse.h.temp - tclsh $(TOP)/tool/addopcodes.tcl parse.h.temp >parse.h sqlite3.h: $(TOP)/src/sqlite.h.in $(TOP)/manifest mksourceid $(TOP)/VERSION $(TOP)/ext/rtree/sqlite3rtree.h tclsh $(TOP)/tool/mksqlite3h.tcl $(TOP) >sqlite3.h keywordhash.h: $(TOP)/tool/mkkeywordhash.c Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -831,18 +831,22 @@ ** at most one effective restoreCursorPosition() call after each ** saveCursorPosition(). */ static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; - int skipNext; + int skipNext = 0; assert( cursorOwnsBtShared(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + if( sqlite3FaultSim(410) ){ + rc = SQLITE_IOERR; + }else{ + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext); + } if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID ); if( skipNext ) pCur->skipNext = skipNext; @@ -1430,15 +1434,11 @@ ** two (or one) blocks of cells using memmove() and add the required ** offsets to each pointer in the cell-pointer array than it is to ** reconstruct the entire page. */ if( (int)data[hdr+7]<=nMaxFrag ){ int iFree = get2byte(&data[hdr+1]); - - /* If the initial freeblock offset were out of bounds, that would have - ** been detected by btreeComputeFreeSpace() when it was computing the - ** number of free bytes on the page. */ - assert( iFree<=usableSize-4 ); + if( iFree>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); if( iFree ){ int iFree2 = get2byte(&data[iFree]); if( iFree2>usableSize-4 ) return SQLITE_CORRUPT_PAGE(pPage); if( 0==iFree2 || (data[iFree2]==0 && data[iFree2+1]==0) ){ u8 *pEnd = &data[cellOffset + nCell*2]; @@ -5271,27 +5271,10 @@ rc = SQLITE_OK; } return rc; } -/* -** This function is a no-op if cursor pCur does not point to a valid row. -** Otherwise, if pCur is valid, configure it so that the next call to -** sqlite3BtreeNext() is a no-op. -*/ -#ifndef SQLITE_OMIT_WINDOWFUNC -void sqlite3BtreeSkipNext(BtCursor *pCur){ - /* We believe that the cursor must always be in the valid state when - ** this routine is called, but the proof is difficult, so we add an - ** ALWaYS() test just in case we are wrong. */ - if( ALWAYS(pCur->eState==CURSOR_VALID) ){ - pCur->eState = CURSOR_SKIPNEXT; - pCur->skipNext = 1; - } -} -#endif /* SQLITE_OMIT_WINDOWFUNC */ - /* Move the cursor to the last entry in the table. Return SQLITE_OK ** on success. Set *pRes to 0 if the cursor actually points to something ** or set *pRes to 1 if the table is empty. */ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){ @@ -6185,11 +6168,11 @@ MemPage *pTrunk = 0; /* Free-list trunk page */ Pgno iTrunk = 0; /* Page number of free-list trunk page */ MemPage *pPage1 = pBt->pPage1; /* Local reference to page 1 */ MemPage *pPage; /* Page being freed. May be NULL. */ int rc; /* Return Code */ - int nFree; /* Initial number of pages on free-list */ + u32 nFree; /* Initial number of pages on free-list */ assert( sqlite3_mutex_held(pBt->mutex) ); assert( CORRUPT_DB || iPage>1 ); assert( !pMemPage || pMemPage->pgno==iPage ); @@ -8832,13 +8815,16 @@ assert( pBt->inTransaction==TRANS_WRITE ); assert( (pBt->btsFlags & BTS_READ_ONLY)==0 ); assert( pCur->curFlags & BTCF_WriteFlag ); assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); assert( !hasReadConflicts(p, pCur->pgnoRoot) ); - assert( pCur->ixpPage->nCell ); - assert( pCur->eState==CURSOR_VALID ); assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 ); + if( pCur->eState==CURSOR_REQUIRESEEK ){ + rc = btreeRestoreCursorPosition(pCur); + if( rc ) return rc; + } + assert( pCur->eState==CURSOR_VALID ); iCellDepth = pCur->iPage; iCellIdx = pCur->ix; pPage = pCur->pPage; pCell = findCell(pPage, iCellIdx); Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -299,13 +299,10 @@ }; int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload, int flags, int seekResult); int sqlite3BtreeFirst(BtCursor*, int *pRes); -#ifndef SQLITE_OMIT_WINDOWFUNC -void sqlite3BtreeSkipNext(BtCursor*); -#endif int sqlite3BtreeLast(BtCursor*, int *pRes); int sqlite3BtreeNext(BtCursor*, int flags); int sqlite3BtreeEof(BtCursor*); int sqlite3BtreePrevious(BtCursor*, int flags); i64 sqlite3BtreeIntegerKey(BtCursor*); Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -262,10 +262,11 @@ if( zSql==0 ){ /* This can result either from an OOM or because the formatted string ** exceeds SQLITE_LIMIT_LENGTH. In the latter case, we need to set ** an error */ if( !db->mallocFailed ) pParse->rc = SQLITE_TOOBIG; + pParse->nErr++; return; } pParse->nested++; memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ); memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ); @@ -3318,10 +3319,11 @@ if( pList==0 ) goto exit_create_index; assert( pList->nExpr==1 ); sqlite3ExprListSetSortOrder(pList, sortOrder); }else{ sqlite3ExprListCheckLength(pParse, pList, "index"); + if( pParse->nErr ) goto exit_create_index; } /* Figure out how many bytes of space are required to store explicitly ** specified collation sequence names. */ @@ -3336,10 +3338,11 @@ /* ** Allocate the index structure. */ nName = sqlite3Strlen30(zName); nExtraCol = pPk ? pPk->nKeyCol : 1; + assert( pList->nExpr + nExtraCol <= 32767 /* Fits in i16 */ ); pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol, nName + nExtra + 1, &zExtra); if( db->mallocFailed ){ goto exit_create_index; } @@ -3820,23 +3823,22 @@ int szEntry, /* Size of each object in the array */ int *pnEntry, /* Number of objects currently in use */ int *pIdx /* Write the index of a new slot here */ ){ char *z; - int n = *pnEntry; + sqlite3_int64 n = *pIdx = *pnEntry; if( (n & (n-1))==0 ){ - int sz = (n==0) ? 1 : 2*n; + sqlite3_int64 sz = (n==0) ? 1 : 2*n; void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry); if( pNew==0 ){ *pIdx = -1; return pArray; } pArray = pNew; } z = (char*)pArray; memset(&z[n * szEntry], 0, szEntry); - *pIdx = n; ++*pnEntry; return pArray; } /* @@ -3943,11 +3945,11 @@ assert( iStart<=pSrc->nSrc ); /* Allocate additional space if needed */ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){ SrcList *pNew; - int nAlloc = pSrc->nSrc*2+nExtra; + sqlite3_int64 nAlloc = 2*(sqlite3_int64)pSrc->nSrc+nExtra; sqlite3 *db = pParse->db; if( pSrc->nSrc+nExtra>=SQLITE_MAX_SRCLIST ){ sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", SQLITE_MAX_SRCLIST); @@ -4450,11 +4452,12 @@ char *zErr; int j; StrAccum errMsg; Table *pTab = pIdx->pTable; - sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200); + sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, + pParse->db->aLimit[SQLITE_LIMIT_LENGTH]); if( pIdx->aColExpr ){ sqlite3_str_appendf(&errMsg, "index '%q'", pIdx->zName); }else{ for(j=0; jnKeyCol; j++){ char *zCol; @@ -4699,11 +4702,11 @@ } } } if( pWith ){ - int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); + sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); pNew = sqlite3DbRealloc(db, pWith, nByte); }else{ pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); } assert( (pNew!=0 && zName!=0) || db->mallocFailed ); Index: src/expr.c ================================================================== --- src/expr.c +++ src/expr.c @@ -855,11 +855,11 @@ p = sqlite3ExprAnd(pParse->db, pLeft, pRight); }else{ p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)); if( p ){ memset(p, 0, sizeof(Expr)); - p->op = op & TKFLG_MASK; + p->op = op & 0xff; p->iAgg = -1; } sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight); } if( p ) { @@ -1320,11 +1320,11 @@ */ #ifndef SQLITE_OMIT_CTE static With *withDup(sqlite3 *db, With *p){ With *pRet = 0; if( p ){ - int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); + sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); pRet = sqlite3DbMallocZero(db, nByte); if( pRet ){ int i; pRet->nCte = p->nCte; for(i=0; inCte; i++){ @@ -1585,11 +1585,11 @@ } pList->nExpr = 0; }else if( (pList->nExpr & (pList->nExpr-1))==0 ){ ExprList *pNew; pNew = sqlite3DbRealloc(db, pList, - sizeof(*pList)+(2*pList->nExpr - 1)*sizeof(pList->a[0])); + sizeof(*pList)+(2*(sqlite3_int64)pList->nExpr-1)*sizeof(pList->a[0])); if( pNew==0 ){ goto no_mem; } pList = pNew; } @@ -5030,10 +5030,21 @@ ** be non-NULL, then the LEFT JOIN can be safely converted into an ** ordinary join. */ int sqlite3ExprImpliesNonNullRow(Expr *p, int iTab){ Walker w; + p = sqlite3ExprSkipCollate(p); + while( p ){ + if( p->op==TK_NOTNULL ){ + p = p->pLeft; + }else if( p->op==TK_AND ){ + if( sqlite3ExprImpliesNonNullRow(p->pLeft, iTab) ) return 1; + p = p->pRight; + }else{ + break; + } + } w.xExprCallback = impliesNotNullRow; w.xSelectCallback = 0; w.xSelectCallback2 = 0; w.eCode = 0; w.u.iCur = iTab; Index: src/hash.c ================================================================== --- src/hash.c +++ src/hash.c @@ -148,11 +148,11 @@ const Hash *pH, /* The pH to be searched */ const char *pKey, /* The key we are searching for */ unsigned int *pHash /* Write the hash value here */ ){ HashElem *elem; /* Used to loop thru the element list */ - int count; /* Number of elements left to test */ + unsigned int count; /* Number of elements left to test */ unsigned int h; /* The computed hash */ static HashElem nullElement = { 0, 0, 0, 0 }; if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/ struct _ht *pEntry; @@ -196,12 +196,12 @@ if( pH->ht ){ pEntry = &pH->ht[h]; if( pEntry->chain==elem ){ pEntry->chain = elem->next; } + assert( pEntry->count>0 ); pEntry->count--; - assert( pEntry->count>=0 ); } sqlite3_free( elem ); pH->count--; if( pH->count==0 ){ assert( pH->first==0 ); Index: src/hash.h ================================================================== --- src/hash.h +++ src/hash.h @@ -43,11 +43,11 @@ struct Hash { unsigned int htsize; /* Number of buckets in the hash table */ unsigned int count; /* Number of entries in this table */ HashElem *first; /* The first element of the array */ struct _ht { /* the hash table */ - int count; /* Number of entries with this hash */ + unsigned int count; /* Number of entries with this hash */ HashElem *chain; /* Pointer to first entry with this hash */ } *ht; }; /* Each element in the hash table is an instance of the following Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -2273,10 +2273,17 @@ if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break; } if( pSrcIdx==0 ){ return 0; /* pDestIdx has no corresponding index in pSrc */ } + if( pSrcIdx->tnum==pDestIdx->tnum && pSrc->pSchema==pDest->pSchema + && sqlite3FaultSim(411)==SQLITE_OK ){ + /* The sqlite3FaultSim() call allows this corruption test to be + ** bypassed during testing, in order to exercise other corruption tests + ** further downstream. */ + return 0; /* Corrupt schema - two indexes on the same btree */ + } } #ifndef SQLITE_OMIT_CHECK if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){ return 0; /* Tables have different CHECK constraints. Ticket #2252 */ } @@ -2350,11 +2357,11 @@ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); VdbeCoverage(v); sqlite3RowidConstraint(pParse, onError, pDest); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); - }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_Vacuum) ){ + }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid); }else{ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); assert( (pDest->tabFlags & TF_Autoincrement)==0 ); } Index: src/loadext.c ================================================================== --- src/loadext.c +++ src/loadext.c @@ -453,14 +453,17 @@ sqlite3_str_value, /* Version 3.25.0 and later */ sqlite3_create_window_function, /* Version 3.26.0 and later */ #ifdef SQLITE_ENABLE_NORMALIZE - sqlite3_normalized_sql + sqlite3_normalized_sql, #else - 0 + 0, #endif + /* Version 3.28.0 and later */ + sqlite3_stmt_isexplain, + sqlite3_value_frombind }; /* ** Attempt to load an SQLite extension library contained in the file ** zFile. The entry point is zProc. zProc may be 0 in which case a Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -703,11 +703,11 @@ if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; }else if( pBuf==0 ){ sqlite3BeginBenignMalloc(); - pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */ + pStart = sqlite3Malloc( sz*(sqlite3_int64)cnt ); /* IMP: R-61949-35727 */ sqlite3EndBenignMalloc(); if( pStart ) cnt = sqlite3MallocSize(pStart)/sz; }else{ pStart = pBuf; } Index: src/parse.y ================================================================== --- src/parse.y +++ src/parse.y @@ -191,13 +191,11 @@ // Declare some tokens early in order to influence their values, to // improve performance and reduce the executable size. The goal here is // to get the "jump" operations in ISNULL through ESCAPE to have numeric // values that are early enough so that all jump operations are clustered -// at the beginning, but also so that the comparison tokens NE through GE -// are as large as possible so that they are near to FUNCTION, which is a -// token synthesized by addopcodes.tcl. +// at the beginning. // %token ABORT ACTION AFTER ANALYZE ASC ATTACH BEFORE BEGIN BY CASCADE CAST. %token CONFLICT DATABASE DEFERRED DESC DETACH EACH END EXCLUSIVE EXPLAIN FAIL. %token OR AND NOT IS MATCH LIKE_KW BETWEEN IN ISNULL NOTNULL NE EQ. %token GT LE LT GE ESCAPE. @@ -216,10 +214,11 @@ %ifdef SQLITE_OMIT_COMPOUND_SELECT EXCEPT INTERSECT UNION %endif SQLITE_OMIT_COMPOUND_SELECT %ifndef SQLITE_OMIT_WINDOWFUNC CURRENT FOLLOWING PARTITION PRECEDING RANGE UNBOUNDED + EXCLUDE GROUPS OTHERS TIES %endif SQLITE_OMIT_WINDOWFUNC REINDEX RENAME CTIME_KW IF . %wildcard ANY. @@ -1631,17 +1630,18 @@ %type windowdefn_list {Window*} %destructor windowdefn_list {sqlite3WindowListDelete(pParse->db, $$);} windowdefn_list(A) ::= windowdefn(Z). { A = Z; } windowdefn_list(A) ::= windowdefn_list(Y) COMMA windowdefn(Z). { assert( Z!=0 ); + sqlite3WindowChain(pParse, Z, Y); Z->pNextWin = Y; A = Z; } %type windowdefn {Window*} %destructor windowdefn {sqlite3WindowDelete(pParse->db, $$);} -windowdefn(A) ::= nm(X) AS window(Y). { +windowdefn(A) ::= nm(X) AS LP window(Y) RP. { if( ALWAYS(Y) ){ Y->zName = sqlite3DbStrNDup(pParse->db, X.z, X.n); } A = Y; } @@ -1665,51 +1665,68 @@ %type frame_bound_s {struct FrameBound} %destructor frame_bound_s {sqlite3ExprDelete(pParse->db, $$.pExpr);} %type frame_bound_e {struct FrameBound} %destructor frame_bound_e {sqlite3ExprDelete(pParse->db, $$.pExpr);} -window(A) ::= LP part_opt(X) orderby_opt(Y) frame_opt(Z) RP. { +window(A) ::= PARTITION BY nexprlist(X) orderby_opt(Y) frame_opt(Z). { + A = sqlite3WindowAssemble(pParse, Z, X, Y, 0); +} +window(A) ::= nm(W) PARTITION BY nexprlist(X) orderby_opt(Y) frame_opt(Z). { + A = sqlite3WindowAssemble(pParse, Z, X, Y, &W); +} +window(A) ::= ORDER BY sortlist(Y) frame_opt(Z). { + A = sqlite3WindowAssemble(pParse, Z, 0, Y, 0); +} +window(A) ::= nm(W) ORDER BY sortlist(Y) frame_opt(Z). { + A = sqlite3WindowAssemble(pParse, Z, 0, Y, &W); +} +window(A) ::= frame_opt(Z). { A = Z; - if( ALWAYS(A) ){ - A->pPartition = X; - A->pOrderBy = Y; - } -} - -part_opt(A) ::= PARTITION BY nexprlist(X). { A = X; } -part_opt(A) ::= . { A = 0; } +} +window(A) ::= nm(W) frame_opt(Z). { + A = sqlite3WindowAssemble(pParse, Z, 0, 0, &W); +} frame_opt(A) ::= . { - A = sqlite3WindowAlloc(pParse, TK_RANGE, TK_UNBOUNDED, 0, TK_CURRENT, 0); -} -frame_opt(A) ::= range_or_rows(X) frame_bound_s(Y). { - A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, TK_CURRENT, 0); -} -frame_opt(A) ::= range_or_rows(X) BETWEEN frame_bound_s(Y) AND frame_bound_e(Z). { - A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, Z.eType, Z.pExpr); -} - -range_or_rows(A) ::= RANGE. { A = TK_RANGE; } -range_or_rows(A) ::= ROWS. { A = TK_ROWS; } - - -frame_bound_s(A) ::= frame_bound(X). { A = X; } -frame_bound_s(A) ::= UNBOUNDED PRECEDING. {A.eType = TK_UNBOUNDED; A.pExpr = 0;} -frame_bound_e(A) ::= frame_bound(X). { A = X; } -frame_bound_e(A) ::= UNBOUNDED FOLLOWING. {A.eType = TK_UNBOUNDED; A.pExpr = 0;} - -frame_bound(A) ::= expr(X) PRECEDING. { A.eType = TK_PRECEDING; A.pExpr = X; } -frame_bound(A) ::= CURRENT ROW. { A.eType = TK_CURRENT ; A.pExpr = 0; } -frame_bound(A) ::= expr(X) FOLLOWING. { A.eType = TK_FOLLOWING; A.pExpr = X; } + A = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0); +} +frame_opt(A) ::= range_or_rows(X) frame_bound_s(Y) frame_exclude_opt(Z). { + A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, TK_CURRENT, 0, Z); +} +frame_opt(A) ::= range_or_rows(X) BETWEEN frame_bound_s(Y) AND + frame_bound_e(Z) frame_exclude_opt(W). { + A = sqlite3WindowAlloc(pParse, X, Y.eType, Y.pExpr, Z.eType, Z.pExpr, W); +} + +range_or_rows(A) ::= RANGE|ROWS|GROUPS(X). {A = @X; /*A-overwrites-X*/} + +frame_bound_s(A) ::= frame_bound(X). {A = X;} +frame_bound_s(A) ::= UNBOUNDED(X) PRECEDING. {A.eType = @X; A.pExpr = 0;} +frame_bound_e(A) ::= frame_bound(X). {A = X;} +frame_bound_e(A) ::= UNBOUNDED(X) FOLLOWING. {A.eType = @X; A.pExpr = 0;} + +frame_bound(A) ::= expr(X) PRECEDING|FOLLOWING(Y). + {A.eType = @Y; A.pExpr = X;} +frame_bound(A) ::= CURRENT(X) ROW. {A.eType = @X; A.pExpr = 0;} + +%type frame_exclude_opt {u8} +frame_exclude_opt(A) ::= . {A = 0;} +frame_exclude_opt(A) ::= EXCLUDE frame_exclude(X). {A = X;} + +%type frame_exclude {u8} +frame_exclude(A) ::= NO(X) OTHERS. {A = @X; /*A-overwrites-X*/} +frame_exclude(A) ::= CURRENT(X) ROW. {A = @X; /*A-overwrites-X*/} +frame_exclude(A) ::= GROUP|TIES(X). {A = @X; /*A-overwrites-X*/} + %type window_clause {Window*} %destructor window_clause {sqlite3WindowListDelete(pParse->db, $$);} window_clause(A) ::= WINDOW windowdefn_list(B). { A = B; } %type over_clause {Window*} %destructor over_clause {sqlite3WindowDelete(pParse->db, $$);} -over_clause(A) ::= filter_opt(W) OVER window(Z). { +over_clause(A) ::= filter_opt(W) OVER LP window(Z) RP. { A = Z; assert( A!=0 ); A->pFilter = W; } over_clause(A) ::= filter_opt(W) OVER nm(Z). { @@ -1723,5 +1740,45 @@ } filter_opt(A) ::= . { A = 0; } filter_opt(A) ::= FILTER LP WHERE expr(X) RP. { A = X; } %endif /* SQLITE_OMIT_WINDOWFUNC */ + +/* +** The code generator needs some extra TK_ token values for tokens that +** are synthesized and do not actually appear in the grammar: +*/ +%token + TRUEFALSE /* True or false keyword */ + ISNOT /* Combination of IS and NOT */ + FUNCTION /* A function invocation */ + COLUMN /* Reference to a table column */ + AGG_FUNCTION /* An aggregate function */ + AGG_COLUMN /* An aggregated column */ + UMINUS /* Unary minus */ + UPLUS /* Unary plus */ + TRUTH /* IS TRUE or IS FALSE or IS NOT TRUE or IS NOT FALSE */ + REGISTER /* Reference to a VDBE register */ + VECTOR /* Vector */ + SELECT_COLUMN /* Choose a single column from a multi-column SELECT */ + IF_NULL_ROW /* the if-null-row operator */ + ASTERISK /* The "*" in count(*) and similar */ + SPAN /* The span operator */ +. +/* There must be no more than 255 tokens defined above. If this grammar +** is extended with new rules and tokens, they must either be so few in +** number that TK_SPAN is no more than 255, or else the new tokens must +** appear after this line. +*/ +%include { +#if TK_SPAN>255 +# error too many tokens in the grammar +#endif +} + +/* +** The TK_SPACE and TK_ILLEGAL tokens must be the last two tokens. The +** parser depends on this. Those tokens are not used in any grammar rule. +** They are only used by the tokenizer. Declare them last so that they +** are guaranteed to be the last two tokens +*/ +%token SPACE ILLEGAL. Index: src/pcache1.c ================================================================== --- src/pcache1.c +++ src/pcache1.c @@ -487,13 +487,11 @@ ** Malloc function used by SQLite to obtain space from the buffer configured ** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer ** exists, this function falls back to sqlite3Malloc(). */ void *sqlite3PageMalloc(int sz){ - /* During rebalance operations on a corrupt database file, it is sometimes - ** (rarely) possible to overread the temporary page buffer by a few bytes. - ** Enlarge the allocation slightly so that this does not cause problems. */ + assert( sz<=65536+8 ); /* These allocations are never very large */ return pcache1Alloc(sz); } /* ** Free an allocated buffer obtained from sqlite3PageMalloc(). Index: src/printf.c ================================================================== --- src/printf.c +++ src/printf.c @@ -135,10 +135,11 @@ */ static void setStrAccumError(StrAccum *p, u8 eError){ assert( eError==SQLITE_NOMEM || eError==SQLITE_TOOBIG ); p->accError = eError; if( p->mxAlloc ) sqlite3_str_reset(p); + if( eError==SQLITE_TOOBIG ) sqlite3ErrorToParser(p->db, eError); } /* ** Extra argument values from a PrintfArguments object */ Index: src/resolve.c ================================================================== --- src/resolve.c +++ src/resolve.c @@ -432,10 +432,14 @@ assert( pExpr->x.pSelect==0 ); pOrig = pEList->a[j].pExpr; if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){ sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; + } + if( (pNC->ncFlags&NC_AllowWin)==0 && ExprHasProperty(pOrig, EP_Win) ){ + sqlite3ErrorMsg(pParse, "misuse of aliased window function %s",zAs); + return WRC_Abort; } if( sqlite3ExprVectorSize(pOrig)!=1 ){ sqlite3ErrorMsg(pParse, "row value misused"); return WRC_Abort; } @@ -723,10 +727,11 @@ int is_agg = 0; /* True if is an aggregate function */ int nId; /* Number of characters in function name */ const char *zId; /* The function name. */ FuncDef *pDef; /* Information about the function */ u8 enc = ENC(pParse->db); /* The database encoding */ + int savedAllowFlags = (pNC->ncFlags & (NC_AllowAgg | NC_AllowWin)); assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); zId = pExpr->u.zToken; nId = sqlite3Strlen30(zId); pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0); @@ -844,12 +849,15 @@ sqlite3ErrorMsg(pParse,"wrong number of arguments to function %.*s()", nId, zId); pNC->nErr++; } if( is_agg ){ + /* Window functions may not be arguments of aggregate functions. + ** Or arguments of other window functions. But aggregate functions + ** may be arguments for window functions. */ #ifndef SQLITE_OMIT_WINDOWFUNC - pNC->ncFlags &= ~(pExpr->y.pWin ? NC_AllowWin : NC_AllowAgg); + pNC->ncFlags &= ~(NC_AllowWin | (!pExpr->y.pWin ? NC_AllowAgg : 0)); #else pNC->ncFlags &= ~NC_AllowAgg; #endif } } @@ -866,11 +874,11 @@ || 0==sqlite3WindowCompare(pParse, pSel->pWin, pExpr->y.pWin) ){ pExpr->y.pWin->pNextWin = pSel->pWin; pSel->pWin = pExpr->y.pWin; } - pNC->ncFlags |= NC_AllowWin; + pNC->ncFlags |= NC_HasWin; }else #endif /* SQLITE_OMIT_WINDOWFUNC */ { NameContext *pNC2 = pNC; pExpr->op = TK_AGG_FUNCTION; @@ -884,12 +892,12 @@ assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg ); testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 ); pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX); } - pNC->ncFlags |= NC_AllowAgg; } + pNC->ncFlags |= savedAllowFlags; } /* FIX ME: Compute pExpr->affinity based on the expected return ** type of the function */ return WRC_Prune; @@ -1422,11 +1430,11 @@ /* Recursively resolve names in all subqueries */ for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; - if( pItem->pSelect ){ + if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){ NameContext *pNC; /* Used to iterate name contexts */ int nRef = 0; /* Refcount for pOuterNC and outer contexts */ const char *zSavedContext = pParse->zAuthContext; /* Count the total number of references to pOuterNC and all of its @@ -1646,12 +1654,12 @@ ){ u16 savedHasAgg; Walker w; if( pExpr==0 ) return SQLITE_OK; - savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg); - pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg); + savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin); + pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin); w.pParse = pNC->pParse; w.xExprCallback = resolveExprStep; w.xSelectCallback = resolveSelectStep; w.xSelectCallback2 = 0; w.u.pNC = pNC; @@ -1663,13 +1671,15 @@ #endif sqlite3WalkExpr(&w, pExpr); #if SQLITE_MAX_EXPR_DEPTH>0 w.pParse->nHeight -= pExpr->nHeight; #endif - if( pNC->ncFlags & NC_HasAgg ){ - ExprSetProperty(pExpr, EP_Agg); - } + assert( EP_Agg==NC_HasAgg ); + assert( EP_Win==NC_HasWin ); + testcase( pNC->ncFlags & NC_HasAgg ); + testcase( pNC->ncFlags & NC_HasWin ); + ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) ); pNC->ncFlags |= savedHasAgg; return pNC->nErr>0 || w.pParse->nErr>0; } /* Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -187,10 +187,13 @@ ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS int sqlite3_compileoption_used(const char *zOptName); const char *sqlite3_compileoption_get(int N); +#else +# define sqlite3_compileoption_used(X) 0 +# define sqlite3_compileoption_get(X) ((void*)0) #endif /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** @@ -4976,10 +4979,12 @@ ** sqlite3_value_numeric_type   ** →  Best numeric datatype of the value ** sqlite3_value_nochange   ** →  True if the column is unchanged in an UPDATE ** against a virtual table. +** sqlite3_value_frombind   +** →  True if value originated from a [bound parameter] ** ** ** Details: ** ** These routines extract type, size, and content information from @@ -5036,10 +5041,15 @@ ** was unchanging). ^Within an [xUpdate] method, any value for which ** sqlite3_value_nochange(X) is true will in all other respects appear ** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. +** +** ^The sqlite3_value_frombind(X) interface returns non-zero if the +** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] +** interfaces. ^If X comes from an SQL literal value, or a table column, +** and expression, then sqlite3_value_frombind(X) returns zero. ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], @@ -5082,10 +5092,11 @@ int sqlite3_value_bytes(sqlite3_value*); int sqlite3_value_bytes16(sqlite3_value*); int sqlite3_value_type(sqlite3_value*); int sqlite3_value_numeric_type(sqlite3_value*); int sqlite3_value_nochange(sqlite3_value*); +int sqlite3_value_frombind(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: sqlite3_value ** Index: src/sqlite3ext.h ================================================================== --- src/sqlite3ext.h +++ src/sqlite3ext.h @@ -317,10 +317,13 @@ void (*xValue)(sqlite3_context*), void (*xInv)(sqlite3_context*,int,sqlite3_value**), void(*xDestroy)(void*)); /* Version 3.26.0 and later */ const char *(*normalized_sql)(sqlite3_stmt*); + /* Version 3.28.0 and later */ + int (*stmt_isexplain)(sqlite3_stmt*); + int (*value_frombind)(sqlite3_value*); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". @@ -606,10 +609,13 @@ #define sqlite3_str_value sqlite3_api->str_value /* Version 3.25.0 and later */ #define sqlite3_create_window_function sqlite3_api->create_window_function /* Version 3.26.0 and later */ #define sqlite3_normalized_sql sqlite3_api->normalized_sql +/* Version 3.28.0 and later */ +#define sqlite3_stmt_isexplain sqlite3_api->isexplain +#define sqlite3_value_frombind sqlite3_api->frombind #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1577,11 +1577,12 @@ ** Allowed values for sqlite3.mDbFlags */ #define DBFLAG_SchemaChange 0x0001 /* Uncommitted Hash table changes */ #define DBFLAG_PreferBuiltin 0x0002 /* Preference to built-in funcs */ #define DBFLAG_Vacuum 0x0004 /* Currently in a VACUUM */ -#define DBFLAG_SchemaKnownOk 0x0008 /* Schema is known to be valid */ +#define DBFLAG_VacuumInto 0x0008 /* Currently running VACUUM INTO */ +#define DBFLAG_SchemaKnownOk 0x0010 /* Schema is known to be valid */ #define DBFLAG_SchemaInuse 0x0010 /* Do not release sharable schemas */ #define DBFLAG_FreeSchema 0x0020 /* Free extra shared schemas on release */ /* @@ -1588,11 +1589,11 @@ ** Bits of the sqlite3.dbOptFlags field that are used by the ** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to ** selectively disable various optimizations. */ #define SQLITE_QueryFlattener 0x0001 /* Query flattening */ - /* 0x0002 available for reuse */ +#define SQLITE_WindowFunc 0x0002 /* Use xInverse for window functions */ #define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */ #define SQLITE_FactorOutConst 0x0008 /* Constant factoring */ #define SQLITE_DistinctOpt 0x0010 /* DISTINCT using indexes */ #define SQLITE_CoverIdxScan 0x0020 /* Covering index scans */ #define SQLITE_OrderByIdxJoin 0x0040 /* ORDER BY of joins via index */ @@ -1706,11 +1707,10 @@ #define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a ** single query - might change over time */ #define SQLITE_FUNC_AFFINITY 0x4000 /* Built-in affinity() function */ #define SQLITE_FUNC_OFFSET 0x8000 /* Built-in sqlite_offset() function */ #define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */ -#define SQLITE_FUNC_WINDOW_SIZE 0x20000 /* Requires partition size as arg. */ #define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are ** used to create the initializers for the FuncDef structures. @@ -2515,16 +2515,20 @@ } y; }; /* ** The following are the meanings of bits in the Expr.flags field. +** Value restrictions: +** +** EP_Agg == NC_HasAgg == SF_HasAgg +** EP_Win == NC_HasWin */ #define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */ -#define EP_Agg 0x000002 /* Contains one or more aggregate functions */ +#define EP_Distinct 0x000002 /* Aggregate function with DISTINCT keyword */ #define EP_HasFunc 0x000004 /* Contains one or more functions of any kind */ #define EP_FixedCol 0x000008 /* TK_Column with a known fixed value */ -#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */ +#define EP_Agg 0x000010 /* Contains one or more aggregate functions */ #define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */ #define EP_DblQuoted 0x000040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */ #define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */ @@ -2531,11 +2535,11 @@ #define EP_IntValue 0x000400 /* Integer value contained in u.iValue */ #define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */ #define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */ #define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */ #define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */ -#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */ +#define EP_Win 0x008000 /* Contains window functions */ #define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */ #define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */ #define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */ #define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */ #define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */ @@ -2543,10 +2547,11 @@ #define EP_Alias 0x400000 /* Is an alias for a result set column */ #define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */ #define EP_WinFunc 0x1000000 /* TK_FUNCTION with Expr.y.pWin set */ #define EP_Subrtn 0x2000000 /* Uses Expr.y.sub. TK_IN, _SELECT, or _EXISTS */ #define EP_Quoted 0x4000000 /* TK_ID was originally quoted */ +#define EP_Static 0x8000000 /* Held in memory not obtained from malloc() */ /* ** The EP_Propagate mask is a set of properties that automatically propagate ** upwards into parent nodes. */ @@ -2782,12 +2787,13 @@ /* ** Allowed values for the NameContext, ncFlags field. ** ** Value constraints (all checked via assert()): -** NC_HasAgg == SF_HasAgg +** NC_HasAgg == SF_HasAgg == EP_Agg ** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX +** NC_HasWin == EP_Win ** */ #define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */ #define NC_PartIdx 0x0002 /* True if resolving a partial index WHERE */ #define NC_IsCheck 0x0004 /* True if resolving names in a CHECK constraint */ @@ -2799,10 +2805,11 @@ #define NC_UAggInfo 0x0100 /* True if uNC.pAggInfo is used */ #define NC_UUpsert 0x0200 /* True if uNC.pUpsert is used */ #define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */ #define NC_Complex 0x2000 /* True if a function or subquery seen */ #define NC_AllowWin 0x4000 /* Window functions are allowed here */ +#define NC_HasWin 0x8000 /* One or more window functions seen */ /* ** An instance of the following object describes a single ON CONFLICT ** clause in an upsert. ** @@ -3558,11 +3565,11 @@ u8 bLine[100]; /* Draw vertical in column i if bLine[i] is true */ }; #endif /* SQLITE_DEBUG */ /* -** This object is used in varioius ways, all related to window functions +** This object is used in various ways, all related to window functions ** ** (1) A single instance of this structure is attached to the ** the Expr.pWin field for each window function in an expression tree. ** This object holds the information contained in the OVER clause, ** plus additional fields used during code generation. @@ -3573,19 +3580,22 @@ ** ** (3) The terms of the WINDOW clause of a SELECT are instances of this ** object on a linked list attached to Select.pWinDefn. ** ** The uses (1) and (2) are really the same Window object that just happens -** to be accessible in two different ways. Use (3) is are separate objects. +** to be accessible in two different ways. Use case (3) are separate objects. */ struct Window { char *zName; /* Name of window (may be NULL) */ + char *zBase; /* Name of base window for chaining (may be NULL) */ ExprList *pPartition; /* PARTITION BY clause */ ExprList *pOrderBy; /* ORDER BY clause */ - u8 eType; /* TK_RANGE or TK_ROWS */ + u8 eFrmType; /* TK_RANGE, TK_GROUPS, TK_ROWS, or 0 */ u8 eStart; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ u8 eEnd; /* UNBOUNDED, CURRENT, PRECEDING or FOLLOWING */ + u8 bImplicitFrame; /* True if frame was implicitly specified */ + u8 eExclude; /* TK_NO, TK_CURRENT, TK_TIES, TK_GROUP, or 0 */ Expr *pStart; /* Expression for " PRECEDING" */ Expr *pEnd; /* Expression for " FOLLOWING" */ Window *pNextWin; /* Next window function belonging to this SELECT */ Expr *pFilter; /* The FILTER expression */ FuncDef *pFunc; /* The function */ @@ -3592,21 +3602,23 @@ int iEphCsr; /* Partition buffer or Peer buffer */ int regAccum; int regResult; int csrApp; /* Function cursor (used by min/max) */ int regApp; /* Function register (also used by min/max) */ - int regPart; /* First in a set of registers holding PARTITION BY - ** and ORDER BY values for the window */ + int regPart; /* Array of registers for PARTITION BY values */ Expr *pOwner; /* Expression object this window is attached to */ int nBufferCol; /* Number of columns in buffer table */ int iArgCol; /* Offset of first argument for this function */ + int regOne; /* Register containing constant value 1 */ + int regStartRowid; + int regEndRowid; }; #ifndef SQLITE_OMIT_WINDOWFUNC void sqlite3WindowDelete(sqlite3*, Window*); void sqlite3WindowListDelete(sqlite3 *db, Window *p); -Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*); +Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8); void sqlite3WindowAttach(Parse*, Expr*, Window*); int sqlite3WindowCompare(Parse*, Window*, Window*); void sqlite3WindowCodeInit(Parse*, Window*); void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int); int sqlite3WindowRewrite(Parse*, Select*); @@ -3613,10 +3625,12 @@ int sqlite3ExpandSubquery(Parse*, struct SrcList_item*); void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*); Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p); Window *sqlite3WindowListDup(sqlite3 *db, Window *p); void sqlite3WindowFunctions(void); +void sqlite3WindowChain(Parse*, Window*, Window*); +Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*); #else # define sqlite3WindowDelete(a,b) # define sqlite3WindowFunctions() # define sqlite3WindowAttach(a,b,c) #endif @@ -3842,10 +3856,11 @@ #endif void sqlite3SetString(char **, sqlite3*, const char*); void sqlite3ErrorMsg(Parse*, const char*, ...); +int sqlite3ErrorToParser(sqlite3*,int); void sqlite3Dequote(char*); void sqlite3DequoteExpr(Expr*); void sqlite3TokenInit(Token*,char*); int sqlite3KeywordCode(const unsigned char*, int); int sqlite3RunParser(Parse*, const char*, char **); Index: src/test4.c ================================================================== --- src/test4.c +++ src/test4.c @@ -62,11 +62,11 @@ /* ** The main loop for a thread. Threads use busy waiting. */ -static void *thread_main(void *pArg){ +static void *test_thread_main(void *pArg){ Thread *p = (Thread*)pArg; if( p->db ){ sqlite3_close(p->db); } sqlite3_open(p->zFilename, &p->db); @@ -149,11 +149,11 @@ threadset[i].busy = 1; sqlite3_free(threadset[i].zFilename); threadset[i].zFilename = sqlite3_mprintf("%s", argv[2]); threadset[i].opnum = 1; threadset[i].completed = 0; - rc = pthread_create(&x, 0, thread_main, &threadset[i]); + rc = pthread_create(&x, 0, test_thread_main, &threadset[i]); if( rc ){ Tcl_AppendResult(interp, "failed to create the thread", 0); sqlite3_free(threadset[i].zFilename); threadset[i].busy = 0; return TCL_ERROR; @@ -163,11 +163,11 @@ } /* ** Wait for a thread to reach its idle state. */ -static void thread_wait(Thread *p){ +static void test_thread_wait(Thread *p){ while( p->opnum>p->completed ) sched_yield(); } /* ** Usage: thread_wait ID @@ -191,22 +191,22 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); return TCL_OK; } /* ** Stop a thread. */ -static void stop_thread(Thread *p){ - thread_wait(p); +static void test_stop_thread(Thread *p){ + test_thread_wait(p); p->xOp = 0; p->opnum++; - thread_wait(p); + test_thread_wait(p); sqlite3_free(p->zArg); p->zArg = 0; sqlite3_free(p->zFilename); p->zFilename = 0; p->busy = 0; @@ -231,20 +231,20 @@ " ID", 0); return TCL_ERROR; } if( argv[1][0]=='*' && argv[1][1]==0 ){ for(i=0; i=threadset[i].argc ){ Tcl_AppendResult(interp, "column number out of range", 0); return TCL_ERROR; } Tcl_AppendResult(interp, threadset[i].argv[n], 0); @@ -340,11 +340,11 @@ if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[2], &n) ) return TCL_ERROR; - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); if( n<0 || n>=threadset[i].argc ){ Tcl_AppendResult(interp, "column number out of range", 0); return TCL_ERROR; } Tcl_AppendResult(interp, threadset[i].colv[n], 0); @@ -375,11 +375,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); zName = sqlite3ErrName(threadset[i].rc); Tcl_AppendResult(interp, zName, 0); return TCL_OK; } @@ -406,11 +406,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); Tcl_AppendResult(interp, threadset[i].zErr, 0); return TCL_OK; } /* @@ -450,11 +450,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); threadset[i].xOp = do_compile; sqlite3_free(threadset[i].zArg); threadset[i].zArg = sqlite3_mprintf("%s", argv[2]); threadset[i].opnum++; return TCL_OK; @@ -503,11 +503,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); threadset[i].xOp = do_step; threadset[i].opnum++; return TCL_OK; } @@ -545,11 +545,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); threadset[i].xOp = do_finalize; sqlite3_free(threadset[i].zArg); threadset[i].zArg = 0; threadset[i].opnum++; return TCL_OK; @@ -577,18 +577,18 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); j = parse_thread_id(interp, argv[2]); if( j<0 ) return TCL_ERROR; if( !threadset[j].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[j]); + test_thread_wait(&threadset[j]); temp = threadset[i].db; threadset[i].db = threadset[j].db; threadset[j].db = temp; return TCL_OK; } @@ -618,11 +618,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); sqlite3TestMakePointerStr(interp, zBuf, threadset[i].db); threadset[i].db = 0; Tcl_AppendResult(interp, zBuf, (char*)0); return TCL_OK; } @@ -649,11 +649,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); assert( !threadset[i].db ); threadset[i].db = (sqlite3*)sqlite3TestTextToPtr(argv[2]); return TCL_OK; } @@ -681,11 +681,11 @@ if( i<0 ) return TCL_ERROR; if( !threadset[i].busy ){ Tcl_AppendResult(interp, "no such thread", 0); return TCL_ERROR; } - thread_wait(&threadset[i]); + test_thread_wait(&threadset[i]); sqlite3TestMakePointerStr(interp, zBuf, threadset[i].pStmt); threadset[i].pStmt = 0; Tcl_AppendResult(interp, zBuf, (char*)0); return TCL_OK; } Index: src/test_fs.c ================================================================== --- src/test_fs.c +++ src/test_fs.c @@ -738,11 +738,11 @@ fd = open(zFile, O_RDONLY); if( fd<0 ) return SQLITE_IOERR; fstat(fd, &sbuf); if( sbuf.st_size>=pCur->nAlloc ){ - int nNew = sbuf.st_size*2; + sqlite3_int64 nNew = sbuf.st_size*2; char *zNew; if( nNew<1024 ) nNew = 1024; zNew = sqlite3Realloc(pCur->zBuf, nNew); if( zNew==0 ){ Index: src/test_func.c ================================================================== --- src/test_func.c +++ src/test_func.c @@ -627,10 +627,28 @@ int argc, sqlite3_value **argv ){ sqlite3_result_int(context, (int)sqlite3_value_subtype(argv[0])); } + +/* test_frombind(A,B,C,...) +** +** Return an integer bitmask that has a bit set for every argument +** (up to the first 63 arguments) that originates from a bind a parameter. +*/ +static void test_frombind( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + sqlite3_uint64 m = 0; + int i; + for(i=0; i> 4)==4); + if( b ){ + if( (b & 0x01)==0 ){ + appendToList(pRes, i, 0, bFlag ? "less than" : "falls through"); + } + if( (b & 0x02)==0 ){ + appendToList(pRes, i, 1, bFlag ? "equal" : "taken"); + } + if( (b & 0x04)==0 ){ + appendToList(pRes, i, 2, bFlag ? "greater-than" : "NULL"); + } + } + } + Tcl_SetObjResult(interp, pRes); + Tcl_DecrRefCount(pRes); + break; + }; + + default: /* stop */ + sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, 0, 0); + break; + } + + return TCL_OK; +} + +#endif /* SQLITE_VDBE_COVERAGE */ + +int Sqlitetestvdbecov_Init(Tcl_Interp *interp){ +#ifdef SQLITE_VDBE_COVERAGE + Tcl_CreateObjCommand(interp, "vdbe_coverage", test_vdbe_coverage, 0, 0); +#endif + return TCL_OK; +} + +#endif Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -233,10 +233,11 @@ { SQLITE_IOERR, "SQLITE_IOERR" }, { SQLITE_LOCKED, "SQLITE_LOCKED" }, { SQLITE_BUSY, "SQLITE_BUSY" }, { SQLITE_READONLY, "SQLITE_READONLY" }, { SQLITE_READONLY_CANTINIT, "SQLITE_READONLY_CANTINIT" }, + { -1, "SQLITE_OMIT" }, }; const char *z; int i; @@ -380,10 +381,11 @@ tvfsExecTcl(p, "xWrite", Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt) ); tvfsResultCode(p, &rc); + if( rc<0 ) return SQLITE_OK; } if( rc==SQLITE_OK && tvfsInjectFullerr(p) ){ rc = SQLITE_FULL; } Index: src/treeview.c ================================================================== --- src/treeview.c +++ src/treeview.c @@ -307,28 +307,66 @@ #ifndef SQLITE_OMIT_WINDOWFUNC /* ** Generate a human-readable explanation for a Window object */ void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u8 more){ + int nElement = 0; + if( pWin->pFilter ){ + sqlite3TreeViewItem(pView, "FILTER", 1); + sqlite3TreeViewExpr(pView, pWin->pFilter, 0); + sqlite3TreeViewPop(pView); + } pView = sqlite3TreeViewPush(pView, more); if( pWin->zName ){ - sqlite3TreeViewLine(pView, "OVER %s", pWin->zName); + sqlite3TreeViewLine(pView, "OVER %s (%p)", pWin->zName, pWin); }else{ - sqlite3TreeViewLine(pView, "OVER"); + sqlite3TreeViewLine(pView, "OVER (%p)", pWin); + } + if( pWin->zBase ) nElement++; + if( pWin->pOrderBy ) nElement++; + if( pWin->eFrmType ) nElement++; + if( pWin->eExclude ) nElement++; + if( pWin->zBase ){ + sqlite3TreeViewPush(pView, (--nElement)>0); + sqlite3TreeViewLine(pView, "window: %s", pWin->zBase); + sqlite3TreeViewPop(pView); } if( pWin->pPartition ){ - sqlite3TreeViewExprList(pView, pWin->pPartition, 1, "PARTITION-BY"); + sqlite3TreeViewExprList(pView, pWin->pPartition, nElement>0,"PARTITION-BY"); } if( pWin->pOrderBy ){ - sqlite3TreeViewExprList(pView, pWin->pOrderBy, 1, "ORDER-BY"); + sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY"); } - if( pWin->eType ){ - sqlite3TreeViewItem(pView, pWin->eType==TK_RANGE ? "RANGE" : "ROWS", 0); + if( pWin->eFrmType ){ + char zBuf[30]; + const char *zFrmType = "ROWS"; + if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE"; + if( pWin->eFrmType==TK_GROUPS ) zFrmType = "GROUPS"; + sqlite3_snprintf(sizeof(zBuf),zBuf,"%s%s",zFrmType, + pWin->bImplicitFrame ? " (implied)" : ""); + sqlite3TreeViewItem(pView, zBuf, (--nElement)>0); sqlite3TreeViewBound(pView, pWin->eStart, pWin->pStart, 1); sqlite3TreeViewBound(pView, pWin->eEnd, pWin->pEnd, 0); sqlite3TreeViewPop(pView); } + if( pWin->eExclude ){ + char zBuf[30]; + const char *zExclude; + switch( pWin->eExclude ){ + case TK_NO: zExclude = "NO OTHERS"; break; + case TK_CURRENT: zExclude = "CURRENT ROW"; break; + case TK_GROUP: zExclude = "GROUP"; break; + case TK_TIES: zExclude = "TIES"; break; + default: + sqlite3_snprintf(sizeof(zBuf),zBuf,"invalid(%d)", pWin->eExclude); + zExclude = zBuf; + break; + } + sqlite3TreeViewPush(pView, 0); + sqlite3TreeViewLine(pView, "EXCLUDE %s", zExclude); + sqlite3TreeViewPop(pView); + } sqlite3TreeViewPop(pView); } #endif /* SQLITE_OMIT_WINDOWFUNC */ #ifndef SQLITE_OMIT_WINDOWFUNC Index: src/utf.c ================================================================== --- src/utf.c +++ src/utf.c @@ -198,15 +198,15 @@ ** This routine transforms the internal text encoding used by pMem to ** desiredEnc. It is an error if the string is already of the desired ** encoding, or if *pMem does not contain a string value. */ SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ - int len; /* Maximum length of output string in bytes */ - unsigned char *zOut; /* Output buffer */ - unsigned char *zIn; /* Input iterator */ - unsigned char *zTerm; /* End of input */ - unsigned char *z; /* Output iterator */ + sqlite3_int64 len; /* Maximum length of output string in bytes */ + unsigned char *zOut; /* Output buffer */ + unsigned char *zIn; /* Input iterator */ + unsigned char *zTerm; /* End of input */ + unsigned char *z; /* Output iterator */ unsigned int c; assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); assert( pMem->flags&MEM_Str ); assert( pMem->enc!=desiredEnc ); @@ -251,18 +251,18 @@ ** translating a 2-byte character to a 4-byte UTF-8 character. ** A single byte is required for the output string ** nul-terminator. */ pMem->n &= ~1; - len = pMem->n * 2 + 1; + len = 2 * (sqlite3_int64)pMem->n + 1; }else{ /* When converting from UTF-8 to UTF-16 the maximum growth is caused ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 ** character. Two bytes are required in the output buffer for the ** nul-terminator. */ - len = pMem->n * 2 + 2; + len = 2 * (sqlite3_int64)pMem->n + 2; } /* Set zIn to point at the start of the input buffer and zTerm to point 1 ** byte past the end. ** Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -30,19 +30,27 @@ dummy += (unsigned)x; } #endif /* -** Give a callback to the test harness that can be used to simulate faults -** in places where it is difficult or expensive to do so purely by means -** of inputs. -** -** The intent of the integer argument is to let the fault simulator know -** which of multiple sqlite3FaultSim() calls has been hit. -** -** Return whatever integer value the test callback returns, or return -** SQLITE_OK if no test callback is installed. +** Calls to sqlite3FaultSim() are used to simulate a failure during testing, +** or to bypass normal error detection during testing in order to let +** execute proceed futher downstream. +** +** In deployment, sqlite3FaultSim() *always* return SQLITE_OK (0). The +** sqlite3FaultSim() function only returns non-zero during testing. +** +** During testing, if the test harness has set a fault-sim callback using +** a call to sqlite3_test_control(SQLITE_TESTCTRL_FAULT_INSTALL), then +** each call to sqlite3FaultSim() is relayed to that application-supplied +** callback and the integer return value form the application-supplied +** callback is returned by sqlite3FaultSim(). +** +** The integer argument to sqlite3FaultSim() is a code to identify which +** sqlite3FaultSim() instance is being invoked. Each call to sqlite3FaultSim() +** should have a unique code. To prevent legacy testing applications from +** breaking, the codes should not be changed or reused. */ #ifndef SQLITE_UNTESTABLE int sqlite3FaultSim(int iTest){ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback; return xCallback ? xCallback(iTest) : SQLITE_OK; @@ -222,10 +230,23 @@ sqlite3DbFree(db, pParse->zErrMsg); pParse->zErrMsg = zMsg; pParse->rc = SQLITE_ERROR; } } + +/* +** If database connection db is currently parsing SQL, then transfer +** error code errCode to that parser if the parser has not already +** encountered some other kind of error. +*/ +int sqlite3ErrorToParser(sqlite3 *db, int errCode){ + Parse *pParse; + if( db==0 || (pParse = db->pParse)==0 ) return errCode; + pParse->rc = errCode; + pParse->nErr++; + return errCode; +} /* ** Convert an SQL-style quoted string into a normal string by removing ** the quote characters. The conversion is done in-place. If the ** input does not begin with a quote character, then this routine @@ -1574,11 +1595,11 @@ nInt = nName/4 + 3; assert( pIn==0 || pIn[0]>=3 ); /* Verify ok to add new elements */ if( pIn==0 || pIn[1]+nInt > pIn[0] ){ /* Enlarge the allocation */ - int nAlloc = (pIn ? pIn[0]*2 : 10) + nInt; + sqlite3_int64 nAlloc = (pIn ? 2*(sqlite3_int64)pIn[0] : 10) + nInt; VList *pOut = sqlite3DbRealloc(db, pIn, nAlloc*sizeof(int)); if( pOut==0 ) return pIn; if( pIn==0 ) pOut[1] = 2; pIn = pOut; pIn[0] = nAlloc; Index: src/vacuum.c ================================================================== --- src/vacuum.c +++ src/vacuum.c @@ -138,11 +138,11 @@ } /* ** This routine implements the OP_Vacuum opcode of the VDBE. */ -int sqlite3RunVacuum( +SQLITE_NOINLINE int sqlite3RunVacuum( char **pzErrMsg, /* Write error message here */ sqlite3 *db, /* Database connection */ int iDb, /* Which attached DB to vacuum */ sqlite3_value *pOut /* Write results here, if not NULL. VACUUM INTO */ ){ @@ -162,15 +162,15 @@ const char *zDbMain; /* Schema name of database to vacuum */ const char *zOut; /* Name of output file */ if( !db->autoCommit ){ sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction"); - return SQLITE_ERROR; + return SQLITE_ERROR; /* IMP: R-12218-18073 */ } if( db->nVdbeActive>1 ){ sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress"); - return SQLITE_ERROR; + return SQLITE_ERROR; /* IMP: R-15610-35227 */ } saved_openFlags = db->openFlags; if( pOut ){ if( sqlite3_value_type(pOut)!=SQLITE_TEXT ){ sqlite3SetString(pzErrMsg, db, "non-text filename"); @@ -229,10 +229,11 @@ if( id->pMethods!=0 && (sqlite3OsFileSize(id, &sz)!=SQLITE_OK || sz>0) ){ rc = SQLITE_ERROR; sqlite3SetString(pzErrMsg, db, "output file already exists"); goto end_of_vacuum; } + db->mDbFlags |= DBFLAG_VacuumInto; } nRes = sqlite3BtreeGetOptimalReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ #ifdef SQLITE_HAS_CODEC Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -120,16 +120,24 @@ /* ** Invoke the VDBE coverage callback, if that callback is defined. This ** feature is used for test suite validation only and does not appear an ** production builds. ** -** M is an integer between 2 and 4. 2 indicates a ordinary two-way -** branch (I=0 means fall through and I=1 means taken). 3 indicates -** a 3-way branch where the third way is when one of the operands is -** NULL. 4 indicates the OP_Jump instruction which has three destinations -** depending on whether the first operand is less than, equal to, or greater -** than the second. +** M is the type of branch. I is the direction taken for this instance of +** the branch. +** +** M: 2 - two-way branch (I=0: fall-thru 1: jump ) +** 3 - two-way + NULL (I=0: fall-thru 1: jump 2: NULL ) +** 4 - OP_Jump (I=0: jump p1 1: jump p2 2: jump p3) +** +** In other words, if M is 2, then I is either 0 (for fall-through) or +** 1 (for when the branch is taken). If M is 3, the I is 0 for an +** ordinary fall-through, I is 1 if the branch was taken, and I is 2 +** if the result of comparison is NULL. For M=3, I=2 the jump may or +** may not be taken, depending on the SQLITE_JUMPIFNULL flags in p5. +** When M is 4, that means that an OP_Jump is being run. I is 0, 1, or 2 +** depending on if the operands are less than, equal, or greater than. ** ** iSrcLine is the source code line (from the __LINE__ macro) that ** generated the VDBE instruction combined with flag bits. The source ** code line number is in the lower 24 bits of iSrcLine and the upper ** 8 bytes are flags. The lower three bits of the flags indicate @@ -136,13 +144,13 @@ ** values for I that should never occur. For example, if the branch is ** always taken, the flags should be 0x05 since the fall-through and ** alternate branch are never taken. If a branch is never taken then ** flags should be 0x06 since only the fall-through approach is allowed. ** -** Bit 0x04 of the flags indicates an OP_Jump opcode that is only +** Bit 0x08 of the flags indicates an OP_Jump opcode that is only ** interested in equal or not-equal. In other words, I==0 and I==2 -** should be treated the same. +** should be treated as equivalent ** ** Since only a line number is retained, not the filename, this macro ** only works for amalgamation builds. But that is ok, since these macros ** should be no-ops except for special builds used to measure test coverage. */ @@ -162,10 +170,22 @@ ** a branch really does go in one of those directions, assert right ** away. */ mNever = iSrcLine >> 24; assert( (I & mNever)==0 ); if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/ + /* Invoke the branch coverage callback with three arguments: + ** iSrcLine - the line number of the VdbeCoverage() macro, with + ** flags removed. + ** I - Mask of bits 0x07 indicating which cases are are + ** fulfilled by this instance of the jump. 0x01 means + ** fall-thru, 0x02 means taken, 0x04 means NULL. Any + ** impossible cases (ex: if the comparison is never NULL) + ** are filled in automatically so that the coverage + ** measurement logic does not flag those impossible cases + ** as missed coverage. + ** M - Type of jump. Same as M argument above + */ I |= mNever; if( M==2 ) I |= 0x04; if( M==4 ){ I |= 0x08; if( (mNever&0x08)!=0 && (I&0x05)!=0) I |= 0x05; /*NO_TEST*/ @@ -1234,11 +1254,14 @@ pVar = &p->aVar[pOp->p1 - 1]; if( sqlite3VdbeMemTooBig(pVar) ){ goto too_big; } pOut = &aMem[pOp->p2]; - sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static); + if( VdbeMemDynamic(pOut) ) sqlite3VdbeMemSetNull(pOut); + memcpy(pOut, pVar, MEMCELLSIZE); + pOut->flags &= ~(MEM_Dyn|MEM_Ephem); + pOut->flags |= MEM_Static|MEM_FromBind; UPDATE_MAX_BLOBSIZE(pOut); break; } /* Opcode: Move P1 P2 P3 * * @@ -1732,20 +1755,21 @@ */ case OP_MustBeInt: { /* jump, in1 */ pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Int)==0 ){ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); - VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2); if( (pIn1->flags & MEM_Int)==0 ){ + VdbeBranchTaken(1, 2); if( pOp->p2==0 ){ rc = SQLITE_MISMATCH; goto abort_due_to_error; }else{ goto jump_to_p2; } } } + VdbeBranchTaken(0, 2); MemSetTypeFlag(pIn1, MEM_Int); break; } #ifndef SQLITE_OMIT_FLOATING_POINT @@ -1916,20 +1940,19 @@ if( pOp->p5 & SQLITE_NULLEQ ){ /* If SQLITE_NULLEQ is set (which will only happen if the operator is ** OP_Eq or OP_Ne) then take the jump or not depending on whether ** or not both operands are null. */ - assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); assert( (flags1 & MEM_Cleared)==0 ); assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 || CORRUPT_DB ); testcase( (pOp->p5 & SQLITE_JUMPIFNULL)!=0 ); if( (flags1&flags3&MEM_Null)!=0 && (flags3&MEM_Cleared)==0 ){ res = 0; /* Operands are equal */ }else{ - res = 1; /* Operands are not equal */ + res = ((flags3 & MEM_Null) ? -1 : +1); /* Operands are not equal */ } }else{ /* SQLITE_NULLEQ is clear and at least one operand is NULL, ** then the result is always NULL. ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. @@ -2043,11 +2066,11 @@ memAboutToChange(p, pOut); MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = res2; REGISTER_TRACE(pOp->p2, pOut); }else{ - VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); + VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3); if( res2 ){ goto jump_to_p2; } } break; @@ -3604,10 +3627,11 @@ pCx->nullRow = 1; pCx->isEphemeral = 1; pCx->pKeyInfo = pOrig->pKeyInfo; pCx->isTable = pOrig->isTable; pCx->pgnoRoot = pOrig->pgnoRoot; + pCx->isOrdered = pOrig->isOrdered; rc = sqlite3BtreeCursor(pOrig->pBtx, pCx->pgnoRoot, BTREE_WRCSR, pCx->pKeyInfo, pCx->uc.pCursor); /* The sqlite3BtreeCursor() routine can only fail for the first cursor ** opened for a database. Since there is already an open cursor when this ** opcode is run, the sqlite3BtreeCursor() cannot fail */ @@ -5112,22 +5136,18 @@ sqlite3_search_count--; #endif p->aCounter[SQLITE_STMTSTATUS_SORT]++; /* Fall through into OP_Rewind */ } -/* Opcode: Rewind P1 P2 * * P5 +/* Opcode: Rewind P1 P2 * * * ** ** 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. ** If the table or index is empty, jump immediately to P2. ** 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. */ case OP_Rewind: { /* jump */ @@ -5134,10 +5154,11 @@ VdbeCursor *pC; BtCursor *pCrsr; int res; assert( pOp->p1>=0 && pOp->p1nCursor ); + assert( pOp->p5==0 ); pC = p->apCsr[pOp->p1]; assert( pC!=0 ); assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) ); res = 1; #ifdef SQLITE_DEBUG @@ -5148,13 +5169,10 @@ }else{ assert( pC->eCurType==CURTYPE_BTREE ); pCrsr = pC->uc.pCursor; assert( pCrsr ); rc = sqlite3BtreeFirst(pCrsr, &res); -#ifndef SQLITE_OMIT_WINDOWFUNC - if( pOp->p5 ) sqlite3BtreeSkipNext(pCrsr); -#endif pC->deferredMoveto = 0; pC->cacheStatus = CACHE_STALE; } if( rc ) goto abort_due_to_error; pC->nullRow = (u8)res; @@ -6532,10 +6550,11 @@ assert( pOp->p3==0 || pOp->opcode==OP_AggValue ); pMem = &aMem[pOp->p1]; assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); #ifndef SQLITE_OMIT_WINDOWFUNC if( pOp->p3 ){ + memAboutToChange(p, &aMem[pOp->p3]); rc = sqlite3VdbeMemAggValue(pMem, &aMem[pOp->p3], pOp->p4.pFunc); pMem = &aMem[pOp->p3]; }else #endif { Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -244,15 +244,15 @@ #define MEM_Str 0x0002 /* Value is a string */ #define MEM_Int 0x0004 /* Value is an integer */ #define MEM_Real 0x0008 /* Value is a real number */ #define MEM_Blob 0x0010 /* Value is a BLOB */ #define MEM_AffMask 0x001f /* Mask of affinity bits */ -/* Available 0x0020 */ +#define MEM_FromBind 0x0020 /* Value originates from sqlite3_bind() */ /* Available 0x0040 */ #define MEM_Undefined 0x0080 /* Value is undefined */ #define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */ -#define MEM_TypeMask 0xc1ff /* Mask of type bits */ +#define MEM_TypeMask 0xc1df /* Mask of type bits */ /* Whenever Mem contains a valid string or blob representation, one of ** the following flags must be set to determine the memory management ** policy for Mem.z. The MEM_Term flag tells us whether or not the @@ -280,10 +280,16 @@ ** Clear any existing type flags from a Mem and replace them with f */ #define MemSetTypeFlag(p, f) \ ((p)->flags = ((p)->flags&~(MEM_TypeMask|MEM_Zero))|f) +/* +** True if Mem X is a NULL-nochng type. +*/ +#define MemNullNochng(X) \ + ((X)->flags==(MEM_Null|MEM_Zero) && (X)->n==0 && (X)->u.nZero==0) + /* ** Return true if a memory cell is not marked as invalid. This macro ** is for use inside assert() statements only. */ #ifdef SQLITE_DEBUG Index: src/vdbeapi.c ================================================================== --- src/vdbeapi.c +++ src/vdbeapi.c @@ -65,11 +65,11 @@ assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 ); assert( db->init.busy==0 ); assert( p->zSql!=0 ); sqlite3OsCurrentTimeInt64(db->pVfs, &iNow); iElapse = (iNow - p->startTime)*1000000; -#ifndef SQLITE_OMIT_DEPRECATED +#ifndef SQLITE_OMIT_DEPRECATED if( db->xProfile ){ db->xProfile(db->pProfileArg, p->zSql, iElapse); } #endif if( db->mTrace & SQLITE_TRACE_PROFILE ){ @@ -272,10 +272,15 @@ /* Return true if a parameter to xUpdate represents an unchanged column */ int sqlite3_value_nochange(sqlite3_value *pVal){ return (pVal->flags&(MEM_Null|MEM_Zero))==(MEM_Null|MEM_Zero); } + +/* Return true if a parameter value originated from an sqlite3_bind() */ +int sqlite3_value_frombind(sqlite3_value *pVal){ + return (pVal->flags&MEM_FromBind)!=0; +} /* Make a copy of an sqlite3_value object */ sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){ sqlite3_value *pNew; Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -153,13 +153,15 @@ ** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array ** by the minimum* amount required until the size reaches 512. Normal ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current ** size of the op array or add 1KB of space, whichever is smaller. */ #ifdef SQLITE_TEST_REALLOC_STRESS - int nNew = (v->nOpAlloc>=512 ? v->nOpAlloc*2 : v->nOpAlloc+nOp); + sqlite3_int64 nNew = (v->nOpAlloc>=512 ? 2*(sqlite3_int64)v->nOpAlloc + : (sqlite3_int64)v->nOpAlloc+nOp); #else - int nNew = (v->nOpAlloc ? v->nOpAlloc*2 : (int)(1024/sizeof(Op))); + sqlite3_int64 nNew = (v->nOpAlloc ? 2*(sqlite3_int64)v->nOpAlloc + : (sqlite3_int64)(1024/sizeof(Op))); UNUSED_PARAMETER(nOp); #endif /* Ensure that the size of a VDBE does not grow too large */ if( nNew > p->db->aLimit[SQLITE_LIMIT_VDBE_OP] ){ @@ -945,11 +947,11 @@ int addrLoop, /* Address of loop counter */ int addrVisit, /* Address of rows visited counter */ LogEst nEst, /* Estimated number of output rows */ const char *zName /* Name of table or index being scanned */ ){ - int nByte = (p->nScan+1) * sizeof(ScanStatus); + sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus); ScanStatus *aNew; aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte); if( aNew ){ ScanStatus *pNew = &aNew[p->nScan++]; pNew->addrExplain = addrExplain; @@ -2066,13 +2068,13 @@ /* An instance of this object describes bulk memory available for use ** by subcomponents of a prepared statement. Space is allocated out ** of a ReusableSpace object by the allocSpace() routine below. */ struct ReusableSpace { - u8 *pSpace; /* Available memory */ - int nFree; /* Bytes of available memory */ - int nNeeded; /* Total bytes that could not be allocated */ + u8 *pSpace; /* Available memory */ + sqlite3_int64 nFree; /* Bytes of available memory */ + sqlite3_int64 nNeeded; /* Total bytes that could not be allocated */ }; /* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf ** from the ReusableSpace object. Return a pointer to the allocated ** memory on success. If insufficient memory is available in the @@ -2088,11 +2090,11 @@ ** statement. */ static void *allocSpace( struct ReusableSpace *p, /* Bulk memory available for allocation */ void *pBuf, /* Pointer to a prior allocation */ - int nByte /* Bytes of memory needed */ + sqlite3_int64 nByte /* Bytes of memory needed */ ){ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) ); if( pBuf==0 ){ nByte = ROUND8(nByte); if( nByte <= p->nFree ){ Index: src/vdbemem.c ================================================================== --- src/vdbemem.c +++ src/vdbemem.c @@ -55,11 +55,11 @@ assert( ((p->flags&MEM_Dyn)!=0 ? 1 : 0) + ((p->flags&MEM_Ephem)!=0 ? 1 : 0) + ((p->flags&MEM_Static)!=0 ? 1 : 0) <= 1 ); /* No other bits set */ - assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype + assert( (p->flags & ~(MEM_Null|MEM_Term|MEM_Subtype|MEM_FromBind |MEM_Dyn|MEM_Ephem|MEM_Static))==0 ); }else{ /* A pure NULL might have other flags, such as MEM_Static, MEM_Dyn, ** MEM_Ephem, MEM_Cleared, or MEM_Subtype */ } @@ -296,17 +296,19 @@ */ #ifndef SQLITE_OMIT_INCRBLOB int sqlite3VdbeMemExpandBlob(Mem *pMem){ int nByte; assert( pMem->flags & MEM_Zero ); - assert( pMem->flags&MEM_Blob ); + assert( (pMem->flags&MEM_Blob)!=0 || MemNullNochng(pMem) ); + testcase( sqlite3_value_nochange(pMem) ); assert( !sqlite3VdbeMemIsRowSet(pMem) ); assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); /* Set nByte to the number of bytes required to store the expanded blob. */ nByte = pMem->n + pMem->u.nZero; if( nByte<=0 ){ + if( (pMem->flags & MEM_Blob)==0 ) return SQLITE_OK; nByte = 1; } if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){ return SQLITE_NOMEM_BKPT; } @@ -1059,11 +1061,11 @@ u32 nAlloc = nByte; if( flags&MEM_Term ){ nAlloc += (enc==SQLITE_UTF8?1:2); } if( nByte>iLimit ){ - return SQLITE_TOOBIG; + return sqlite3ErrorToParser(pMem->db, SQLITE_TOOBIG); } testcase( nAlloc==0 ); testcase( nAlloc==31 ); testcase( nAlloc==32 ); if( sqlite3VdbeMemClearAndResize(pMem, (int)MAX(nAlloc,32)) ){ Index: src/vdbesort.c ================================================================== --- src/vdbesort.c +++ src/vdbesort.c @@ -535,11 +535,11 @@ int nRem; /* Bytes remaining to copy */ /* Extend the p->aAlloc[] allocation if required. */ if( p->nAllocnAlloc*2); + sqlite3_int64 nNew = MAX(128, 2*(sqlite3_int64)p->nAlloc); while( nByte>nNew ) nNew = nNew*2; aNew = sqlite3Realloc(p->aAlloc, nNew); if( !aNew ) return SQLITE_NOMEM_BKPT; p->nAlloc = nNew; p->aAlloc = aNew; @@ -1827,11 +1827,11 @@ int nMin = pSorter->iMemory + nReq; if( nMin>pSorter->nMemory ){ u8 *aNew; int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory; - int nNew = pSorter->nMemory * 2; + sqlite3_int64 nNew = 2 * (sqlite3_int64)pSorter->nMemory; while( nNew < nMin ) nNew = nNew*2; if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize; if( nNew < nMin ) nNew = nMin; aNew = sqlite3Realloc(pSorter->list.aMemory, nNew); Index: src/vtab.c ================================================================== --- src/vtab.c +++ src/vtab.c @@ -311,13 +311,17 @@ ** Add a new module argument to pTable->azModuleArg[]. ** The string is not copied - the pointer is stored. The ** string will be freed automatically when the table is ** deleted. */ -static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){ - int nBytes = sizeof(char *)*(2+pTable->nModuleArg); +static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){ + sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg); char **azModuleArg; + sqlite3 *db = pParse->db; + if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){ + sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName); + } azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes); if( azModuleArg==0 ){ sqlite3DbFree(db, zArg); }else{ int i = pTable->nModuleArg++; @@ -348,13 +352,13 @@ assert( 0==pTable->pIndex ); db = pParse->db; assert( pTable->nModuleArg==0 ); - addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName)); - addModuleArgument(db, pTable, 0); - addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName)); + addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName)); + addModuleArgument(pParse, pTable, 0); + addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName)); assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0) || (pParse->sNameToken.z==pName1->z && pName2->z==0) ); pParse->sNameToken.n = (int)( &pModuleName->z[pModuleName->n] - pParse->sNameToken.z @@ -383,11 +387,11 @@ static void addArgumentToVtab(Parse *pParse){ if( pParse->sArg.z && pParse->pNewTable ){ const char *z = (const char*)pParse->sArg.z; int n = pParse->sArg.n; sqlite3 *db = pParse->db; - addModuleArgument(db, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); + addModuleArgument(pParse, pParse->pNewTable, sqlite3DbStrNDup(db, z, n)); } } /* ** The parser calls this routine after the CREATE VIRTUAL TABLE statement @@ -691,11 +695,12 @@ const int ARRAY_INCR = 5; /* Grow the sqlite3.aVTrans array if required */ if( (db->nVTrans%ARRAY_INCR)==0 ){ VTable **aVTrans; - int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); + sqlite3_int64 nBytes = sizeof(sqlite3_vtab*)* + ((sqlite3_int64)db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ return SQLITE_NOMEM_BKPT; } memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR); @@ -1187,13 +1192,13 @@ pMod->pEpoTab = pTab; pTab->nTabRef = 1; pTab->pSchema = db->aDb[0].pSchema; assert( pTab->nModuleArg==0 ); pTab->iPKey = -1; - addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); - addModuleArgument(db, pTab, 0); - addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); + addModuleArgument(pParse, pTab, 0); + addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName)); rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr); if( rc ){ sqlite3ErrorMsg(pParse, "%s", zErr); sqlite3DbFree(db, zErr); sqlite3VtabEponymousTableClear(db, pMod); Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -573,11 +573,11 @@ ){ int rc = SQLITE_OK; /* Enlarge the pWal->apWiData[] array if required */ if( pWal->nWiData<=iPage ){ - int nByte = sizeof(u32*)*(iPage+1); + sqlite3_int64 nByte = sizeof(u32*)*(iPage+1); volatile u32 **apNew; apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte); if( !apNew ){ *ppPage = 0; return SQLITE_NOMEM_BKPT; @@ -677,10 +677,11 @@ s1 = s2 = 0; } assert( nByte>=8 ); assert( (nByte&0x00000007)==0 ); + assert( nByte<=65536 ); if( nativeCksum ){ do { s1 += *aData++ + s2; s2 += *aData++ + s1; @@ -984,10 +985,11 @@ static void walCleanupHash(Wal *pWal){ WalHashLoc sLoc; /* Hash table location */ int iLimit = 0; /* Zero values greater than this */ int nByte; /* Number of bytes to zero in aPgno[] */ int i; /* Used to iterate through aHash[] */ + int rc; /* Return code form walHashGet() */ assert( pWal->writeLock ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE ); testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 ); @@ -994,15 +996,16 @@ if( pWal->hdr.mxFrame==0 ) return; /* Obtain pointers to the hash-table and page-number array containing ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed - ** that the page said hash-table and array reside on is already mapped. + ** that the page said hash-table and array reside on is already mapped.(1) */ assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) ); assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] ); - walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + rc = walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc); + if( NEVER(rc) ) return; /* Defense-in-depth, in case (1) above is wrong */ /* Zero all hash-table entries that correspond to frame numbers greater ** than pWal->hdr.mxFrame. */ iLimit = pWal->hdr.mxFrame - sLoc.iZero; @@ -1612,11 +1615,11 @@ */ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){ WalIterator *p; /* Return value */ int nSegment; /* Number of segments to merge */ u32 iLast; /* Last frame in log */ - int nByte; /* Number of bytes to allocate */ + sqlite3_int64 nByte; /* Number of bytes to allocate */ int i; /* Iterator variable */ ht_slot *aTmp; /* Temp space used by merge-sort */ int rc = SQLITE_OK; /* Return Code */ /* This routine only runs while holding the checkpoint lock. And Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -3333,15 +3333,15 @@ WHERETRACE(0x800, ("BEGIN %s.addVirtual()\n", pSrc->pTab->zName)); WHERETRACE(0x40, (" VirtualOne: all usable\n")); rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn); /* If the call to xBestIndex() with all terms enabled produced a plan - ** that does not require any source tables (IOW: a plan with mBest==0), - ** then there is no point in making any further calls to xBestIndex() - ** since they will all return the same result (if the xBestIndex() - ** implementation is sane). */ - if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){ + ** that does not require any source tables (IOW: a plan with mBest==0) + ** and does not use an IN(...) operator, then there is no point in making + ** any further calls to xBestIndex() since they will all return the same + ** result (if the xBestIndex() implementation is sane). */ + if( rc==SQLITE_OK && ((mBest = (pNew->prereq & ~mPrereq))!=0 || bIn) ){ int seenZero = 0; /* True if a plan with no prereqs seen */ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */ Bitmask mPrev = 0; Bitmask mBestNoIn = 0; Index: src/wherecode.c ================================================================== --- src/wherecode.c +++ src/wherecode.c @@ -1965,11 +1965,16 @@ testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO ); pExpr = sqlite3ExprDup(db, pExpr, 0); pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr); } if( pAndExpr ){ - pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr); + /* The extra 0x10000 bit on the opcode is masked off and does not + ** become part of the new Expr.op. However, it does make the + ** op==TK_AND comparison inside of sqlite3PExpr() false, and this + ** prevents sqlite3PExpr() from implementing AND short-circuit + ** optimization, which we do not want here. */ + pAndExpr = sqlite3PExpr(pParse, TK_AND|0x10000, 0, pAndExpr); } } /* Run a separate WHERE clause for each term of the OR clause. After ** eliminating duplicates from other WHERE clauses, the action for each @@ -2195,12 +2200,13 @@ continue; #else u32 x = pLevel->iLikeRepCntr; if( x>0 ){ skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)?OP_IfNot:OP_If,(int)(x>>1)); + VdbeCoverageIf(v, (x&1)==1); + VdbeCoverageIf(v, (x&1)==0); } - VdbeCoverage(v); #endif } #ifdef WHERETRACE_ENABLED /* 0xffff */ if( sqlite3WhereTrace ){ VdbeNoopComment((v, "WhereTerm[%d] (%p) priority=%d", Index: src/window.c ================================================================== --- src/window.c +++ src/window.c @@ -196,10 +196,100 @@ } sqlite3_result_int64(pCtx, p->nValue); } } +/* +** Implementation of built-in window function nth_value(). This +** implementation is used in "slow mode" only - when the EXCLUDE clause +** is not set to the default value "NO OTHERS". +*/ +struct NthValueCtx { + i64 nStep; + sqlite3_value *pValue; +}; +static void nth_valueStepFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p ){ + i64 iVal; + switch( sqlite3_value_numeric_type(apArg[1]) ){ + case SQLITE_INTEGER: + iVal = sqlite3_value_int64(apArg[1]); + break; + case SQLITE_FLOAT: { + double fVal = sqlite3_value_double(apArg[1]); + if( ((i64)fVal)!=fVal ) goto error_out; + iVal = (i64)fVal; + break; + } + default: + goto error_out; + } + if( iVal<=0 ) goto error_out; + + p->nStep++; + if( iVal==p->nStep ){ + p->pValue = sqlite3_value_dup(apArg[0]); + if( !p->pValue ){ + sqlite3_result_error_nomem(pCtx); + } + } + } + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); + return; + + error_out: + sqlite3_result_error( + pCtx, "second argument to nth_value must be a positive integer", -1 + ); +} +static void nth_valueFinalizeFunc(sqlite3_context *pCtx){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, 0); + if( p && p->pValue ){ + sqlite3_result_value(pCtx, p->pValue); + sqlite3_value_free(p->pValue); + p->pValue = 0; + } +} +#define nth_valueInvFunc noopStepFunc +#define nth_valueValueFunc noopValueFunc + +static void first_valueStepFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p && p->pValue==0 ){ + p->pValue = sqlite3_value_dup(apArg[0]); + if( !p->pValue ){ + sqlite3_result_error_nomem(pCtx); + } + } + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); +} +static void first_valueFinalizeFunc(sqlite3_context *pCtx){ + struct NthValueCtx *p; + p = (struct NthValueCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p && p->pValue ){ + sqlite3_result_value(pCtx, p->pValue); + sqlite3_value_free(p->pValue); + p->pValue = 0; + } +} +#define first_valueInvFunc noopStepFunc +#define first_valueValueFunc noopValueFunc + /* ** Implementation of built-in window function rank(). Assumes that ** the window frame has been set to: ** ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW @@ -231,75 +321,90 @@ /* ** Implementation of built-in window function percent_rank(). Assumes that ** the window frame has been set to: ** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING */ static void percent_rankStepFunc( sqlite3_context *pCtx, int nArg, sqlite3_value **apArg ){ struct CallCount *p; - UNUSED_PARAMETER(nArg); assert( nArg==1 ); - + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + if( p ){ + p->nTotal++; + } +} +static void percent_rankInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct CallCount *p; + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); - if( p ){ - if( p->nTotal==0 ){ - p->nTotal = sqlite3_value_int64(apArg[0]); - } - p->nStep++; - if( p->nValue==0 ){ - p->nValue = p->nStep; - } - } + p->nStep++; } static void percent_rankValueFunc(sqlite3_context *pCtx){ struct CallCount *p; p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ + p->nValue = p->nStep; if( p->nTotal>1 ){ - double r = (double)(p->nValue-1) / (double)(p->nTotal-1); + double r = (double)p->nValue / (double)(p->nTotal-1); sqlite3_result_double(pCtx, r); }else{ sqlite3_result_double(pCtx, 0.0); } - p->nValue = 0; } } +#define percent_rankFinalizeFunc percent_rankValueFunc /* ** Implementation of built-in window function cume_dist(). Assumes that ** the window frame has been set to: ** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW +** GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING */ static void cume_distStepFunc( sqlite3_context *pCtx, int nArg, sqlite3_value **apArg ){ struct CallCount *p; - assert( nArg==1 ); UNUSED_PARAMETER(nArg); - + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ - if( p->nTotal==0 ){ - p->nTotal = sqlite3_value_int64(apArg[0]); - } - p->nStep++; + p->nTotal++; } +} +static void cume_distInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct CallCount *p; + UNUSED_PARAMETER(nArg); assert( nArg==0 ); + UNUSED_PARAMETER(apArg); + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p->nStep++; } static void cume_distValueFunc(sqlite3_context *pCtx){ struct CallCount *p; - p = (struct CallCount*)sqlite3_aggregate_context(pCtx, sizeof(*p)); - if( p && p->nTotal ){ + p = (struct CallCount*)sqlite3_aggregate_context(pCtx, 0); + if( p ){ double r = (double)(p->nStep) / (double)(p->nTotal); sqlite3_result_double(pCtx, r); } } +#define cume_distFinalizeFunc cume_distValueFunc /* ** Context object for ntile() window function. */ struct NtileCtx { @@ -310,44 +415,54 @@ /* ** Implementation of ntile(). This assumes that the window frame has ** been coerced to: ** -** ROWS UNBOUNDED PRECEDING AND CURRENT ROW +** ROWS CURRENT ROW AND UNBOUNDED FOLLOWING */ static void ntileStepFunc( sqlite3_context *pCtx, int nArg, sqlite3_value **apArg ){ struct NtileCtx *p; - assert( nArg==2 ); UNUSED_PARAMETER(nArg); + assert( nArg==1 ); UNUSED_PARAMETER(nArg); p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p ){ if( p->nTotal==0 ){ p->nParam = sqlite3_value_int64(apArg[0]); - p->nTotal = sqlite3_value_int64(apArg[1]); if( p->nParam<=0 ){ sqlite3_result_error( pCtx, "argument of ntile must be a positive integer", -1 ); } } - p->iRow++; + p->nTotal++; } +} +static void ntileInvFunc( + sqlite3_context *pCtx, + int nArg, + sqlite3_value **apArg +){ + struct NtileCtx *p; + assert( nArg==1 ); UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(apArg); + p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); + p->iRow++; } static void ntileValueFunc(sqlite3_context *pCtx){ struct NtileCtx *p; p = (struct NtileCtx*)sqlite3_aggregate_context(pCtx, sizeof(*p)); if( p && p->nParam>0 ){ int nSize = (p->nTotal / p->nParam); if( nSize==0 ){ - sqlite3_result_int64(pCtx, p->iRow); + sqlite3_result_int64(pCtx, p->iRow+1); }else{ i64 nLarge = p->nTotal - p->nParam*nSize; i64 iSmall = nLarge*(nSize+1); - i64 iRow = p->iRow-1; + i64 iRow = p->iRow; assert( (nLarge*(nSize+1) + (p->nParam-nLarge)*nSize)==p->nTotal ); if( iRowpVal ){ sqlite3_result_value(pCtx, p->pVal); } } static void last_valueFinalizeFunc(sqlite3_context *pCtx){ @@ -494,25 +610,36 @@ void sqlite3WindowFunctions(void){ static FuncDef aWindowFuncs[] = { WINDOWFUNCX(row_number, 0, 0), WINDOWFUNCX(dense_rank, 0, 0), WINDOWFUNCX(rank, 0, 0), - WINDOWFUNCX(percent_rank, 0, SQLITE_FUNC_WINDOW_SIZE), - WINDOWFUNCX(cume_dist, 0, SQLITE_FUNC_WINDOW_SIZE), - WINDOWFUNCX(ntile, 1, SQLITE_FUNC_WINDOW_SIZE), + WINDOWFUNCALL(percent_rank, 0, 0), + WINDOWFUNCALL(cume_dist, 0, 0), + WINDOWFUNCALL(ntile, 1, 0), WINDOWFUNCALL(last_value, 1, 0), - WINDOWFUNCNOOP(nth_value, 2, 0), - WINDOWFUNCNOOP(first_value, 1, 0), + WINDOWFUNCALL(nth_value, 2, 0), + WINDOWFUNCALL(first_value, 1, 0), WINDOWFUNCNOOP(lead, 1, 0), WINDOWFUNCNOOP(lead, 2, 0), WINDOWFUNCNOOP(lead, 3, 0), WINDOWFUNCNOOP(lag, 1, 0), WINDOWFUNCNOOP(lag, 2, 0), WINDOWFUNCNOOP(lag, 3, 0), }; sqlite3InsertBuiltinFuncs(aWindowFuncs, ArraySize(aWindowFuncs)); } + +static Window *windowFind(Parse *pParse, Window *pList, const char *zName){ + Window *p; + for(p=pList; p; p=p->pNextWin){ + if( sqlite3StrICmp(p->zName, zName)==0 ) break; + } + if( p==0 ){ + sqlite3ErrorMsg(pParse, "no such window: %s", zName); + } + return p; +} /* ** This function is called immediately after resolving the function name ** for a window function within a SELECT statement. Argument pList is a ** linked list of WINDOW definitions for the current SELECT statement. @@ -533,52 +660,70 @@ Parse *pParse, Window *pList, /* List of named windows for this SELECT */ Window *pWin, /* Window frame to update */ FuncDef *pFunc /* Window function definition */ ){ - if( pWin->zName && pWin->eType==0 ){ - Window *p; - for(p=pList; p; p=p->pNextWin){ - if( sqlite3StrICmp(p->zName, pWin->zName)==0 ) break; - } - if( p==0 ){ - sqlite3ErrorMsg(pParse, "no such window: %s", pWin->zName); - return; - } + if( pWin->zName && pWin->eFrmType==0 ){ + Window *p = windowFind(pParse, pList, pWin->zName); + if( p==0 ) return; pWin->pPartition = sqlite3ExprListDup(pParse->db, p->pPartition, 0); pWin->pOrderBy = sqlite3ExprListDup(pParse->db, p->pOrderBy, 0); pWin->pStart = sqlite3ExprDup(pParse->db, p->pStart, 0); pWin->pEnd = sqlite3ExprDup(pParse->db, p->pEnd, 0); pWin->eStart = p->eStart; pWin->eEnd = p->eEnd; - pWin->eType = p->eType; + pWin->eFrmType = p->eFrmType; + pWin->eExclude = p->eExclude; + }else{ + sqlite3WindowChain(pParse, pWin, pList); } + if( (pWin->eFrmType==TK_RANGE) + && (pWin->pStart || pWin->pEnd) + && (pWin->pOrderBy==0 || pWin->pOrderBy->nExpr!=1) + ){ + sqlite3ErrorMsg(pParse, + "RANGE with offset PRECEDING/FOLLOWING requires one ORDER BY expression" + ); + }else if( pFunc->funcFlags & SQLITE_FUNC_WINDOW ){ sqlite3 *db = pParse->db; if( pWin->pFilter ){ sqlite3ErrorMsg(pParse, "FILTER clause may only be used with aggregate window functions" ); - }else - if( pFunc->zName==row_numberName || pFunc->zName==ntileName ){ - sqlite3ExprDelete(db, pWin->pStart); - sqlite3ExprDelete(db, pWin->pEnd); - pWin->pStart = pWin->pEnd = 0; - pWin->eType = TK_ROWS; - pWin->eStart = TK_UNBOUNDED; - pWin->eEnd = TK_CURRENT; - }else - - if( pFunc->zName==dense_rankName || pFunc->zName==rankName - || pFunc->zName==percent_rankName || pFunc->zName==cume_distName - ){ - sqlite3ExprDelete(db, pWin->pStart); - sqlite3ExprDelete(db, pWin->pEnd); - pWin->pStart = pWin->pEnd = 0; - pWin->eType = TK_RANGE; - pWin->eStart = TK_UNBOUNDED; - pWin->eEnd = TK_CURRENT; + }else{ + struct WindowUpdate { + const char *zFunc; + int eFrmType; + int eStart; + int eEnd; + } aUp[] = { + { row_numberName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT }, + { dense_rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT }, + { rankName, TK_RANGE, TK_UNBOUNDED, TK_CURRENT }, + { percent_rankName, TK_GROUPS, TK_CURRENT, TK_UNBOUNDED }, + { cume_distName, TK_GROUPS, TK_FOLLOWING, TK_UNBOUNDED }, + { ntileName, TK_ROWS, TK_CURRENT, TK_UNBOUNDED }, + { leadName, TK_ROWS, TK_UNBOUNDED, TK_UNBOUNDED }, + { lagName, TK_ROWS, TK_UNBOUNDED, TK_CURRENT }, + }; + int i; + for(i=0; izName==aUp[i].zFunc ){ + sqlite3ExprDelete(db, pWin->pStart); + sqlite3ExprDelete(db, pWin->pEnd); + pWin->pEnd = pWin->pStart = 0; + pWin->eFrmType = aUp[i].eFrmType; + pWin->eStart = aUp[i].eStart; + pWin->eEnd = aUp[i].eEnd; + pWin->eExclude = 0; + if( pWin->eStart==TK_FOLLOWING ){ + pWin->pStart = sqlite3Expr(db, TK_INTEGER, "1"); + } + break; + } + } } } pWin->pFunc = pFunc; } @@ -779,10 +924,11 @@ /* Assign a cursor number for the ephemeral table used to buffer rows. ** The OpenEphemeral instruction is coded later, after it is known how ** many columns the table will have. */ pMWin->iEphCsr = pParse->nTab++; + pParse->nTab += 3; selectWindowRewriteEList(pParse, pMWin, pSrc, p->pEList, &pSublist); selectWindowRewriteEList(pParse, pMWin, pSrc, p->pOrderBy, &pSublist); pMWin->nBufferCol = (pSublist ? pSublist->nExpr : 0); @@ -834,10 +980,13 @@ p->selFlags &= ~SF_Aggregate; sqlite3SelectPrep(pParse, pSub, 0); } sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pMWin->iEphCsr, pSublist->nExpr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+1, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+2, pMWin->iEphCsr); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->iEphCsr+3, pMWin->iEphCsr); }else{ sqlite3SelectDelete(db, pSub); } if( db->mallocFailed ) rc = SQLITE_NOMEM; } @@ -854,10 +1003,11 @@ sqlite3ExprListDelete(db, p->pPartition); sqlite3ExprListDelete(db, p->pOrderBy); sqlite3ExprDelete(db, p->pEnd); sqlite3ExprDelete(db, p->pStart); sqlite3DbFree(db, p->zName); + sqlite3DbFree(db, p->zBase); sqlite3DbFree(db, p); } } /* @@ -890,34 +1040,32 @@ /* ** Allocate and return a new Window object describing a Window Definition. */ Window *sqlite3WindowAlloc( Parse *pParse, /* Parsing context */ - int eType, /* Frame type. TK_RANGE or TK_ROWS */ + int eType, /* Frame type. TK_RANGE, TK_ROWS, TK_GROUPS, or 0 */ int eStart, /* Start type: CURRENT, PRECEDING, FOLLOWING, UNBOUNDED */ Expr *pStart, /* Start window size if TK_PRECEDING or FOLLOWING */ int eEnd, /* End type: CURRENT, FOLLOWING, TK_UNBOUNDED, PRECEDING */ - Expr *pEnd /* End window size if TK_FOLLOWING or PRECEDING */ + Expr *pEnd, /* End window size if TK_FOLLOWING or PRECEDING */ + u8 eExclude /* EXCLUDE clause */ ){ Window *pWin = 0; + int bImplicitFrame = 0; /* Parser assures the following: */ - assert( eType==TK_RANGE || eType==TK_ROWS ); + assert( eType==0 || eType==TK_RANGE || eType==TK_ROWS || eType==TK_GROUPS ); assert( eStart==TK_CURRENT || eStart==TK_PRECEDING || eStart==TK_UNBOUNDED || eStart==TK_FOLLOWING ); assert( eEnd==TK_CURRENT || eEnd==TK_FOLLOWING || eEnd==TK_UNBOUNDED || eEnd==TK_PRECEDING ); assert( (eStart==TK_PRECEDING || eStart==TK_FOLLOWING)==(pStart!=0) ); assert( (eEnd==TK_FOLLOWING || eEnd==TK_PRECEDING)==(pEnd!=0) ); - - /* If a frame is declared "RANGE" (not "ROWS"), then it may not use - ** either " PRECEDING" or " FOLLOWING". - */ - if( eType==TK_RANGE && (pStart!=0 || pEnd!=0) ){ - sqlite3ErrorMsg(pParse, "RANGE must use only UNBOUNDED or CURRENT ROW"); - goto windowAllocErr; + if( eType==0 ){ + bImplicitFrame = 1; + eType = TK_RANGE; } /* Additionally, the ** starting boundary type may not occur earlier in the following list than ** the ending boundary type: @@ -933,28 +1081,96 @@ ** frame boundary. */ if( (eStart==TK_CURRENT && eEnd==TK_PRECEDING) || (eStart==TK_FOLLOWING && (eEnd==TK_PRECEDING || eEnd==TK_CURRENT)) ){ - sqlite3ErrorMsg(pParse, "unsupported frame delimiter for ROWS"); + sqlite3ErrorMsg(pParse, "unsupported frame specification"); goto windowAllocErr; } pWin = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window)); if( pWin==0 ) goto windowAllocErr; - pWin->eType = eType; + pWin->eFrmType = eType; pWin->eStart = eStart; pWin->eEnd = eEnd; + if( eExclude==0 && OptimizationDisabled(pParse->db, SQLITE_WindowFunc) ){ + eExclude = TK_NO; + } + pWin->eExclude = eExclude; + pWin->bImplicitFrame = bImplicitFrame; pWin->pEnd = sqlite3WindowOffsetExpr(pParse, pEnd); pWin->pStart = sqlite3WindowOffsetExpr(pParse, pStart); return pWin; windowAllocErr: sqlite3ExprDelete(pParse->db, pEnd); sqlite3ExprDelete(pParse->db, pStart); return 0; } + +/* +** Attach PARTITION and ORDER BY clauses pPartition and pOrderBy to window +** pWin. Also, if parameter pBase is not NULL, set pWin->zBase to the +** equivalent nul-terminated string. +*/ +Window *sqlite3WindowAssemble( + Parse *pParse, + Window *pWin, + ExprList *pPartition, + ExprList *pOrderBy, + Token *pBase +){ + if( pWin ){ + pWin->pPartition = pPartition; + pWin->pOrderBy = pOrderBy; + if( pBase ){ + pWin->zBase = sqlite3DbStrNDup(pParse->db, pBase->z, pBase->n); + } + }else{ + sqlite3ExprListDelete(pParse->db, pPartition); + sqlite3ExprListDelete(pParse->db, pOrderBy); + } + return pWin; +} + +/* +** Window *pWin has just been created from a WINDOW clause. Tokne pBase +** is the base window. Earlier windows from the same WINDOW clause are +** stored in the linked list starting at pWin->pNextWin. This function +** either updates *pWin according to the base specification, or else +** leaves an error in pParse. +*/ +void sqlite3WindowChain(Parse *pParse, Window *pWin, Window *pList){ + if( pWin->zBase ){ + sqlite3 *db = pParse->db; + Window *pExist = windowFind(pParse, pList, pWin->zBase); + if( pExist ){ + const char *zErr = 0; + /* Check for errors */ + if( pWin->pPartition ){ + zErr = "PARTITION clause"; + }else if( pExist->pOrderBy && pWin->pOrderBy ){ + zErr = "ORDER BY clause"; + }else if( pExist->bImplicitFrame==0 ){ + zErr = "frame specification"; + } + if( zErr ){ + sqlite3ErrorMsg(pParse, + "cannot override %s of window: %s", zErr, pWin->zBase + ); + }else{ + pWin->pPartition = sqlite3ExprListDup(db, pExist->pPartition, 0); + if( pExist->pOrderBy ){ + assert( pWin->pOrderBy==0 ); + pWin->pOrderBy = sqlite3ExprListDup(db, pExist->pOrderBy, 0); + } + sqlite3DbFree(db, pWin->zBase); + pWin->zBase = 0; + } + } + } +} /* ** Attach window object pWin to expression p. */ void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){ @@ -980,13 +1196,14 @@ /* ** Return 0 if the two window objects are identical, or non-zero otherwise. ** Identical window objects can be processed in a single scan. */ int sqlite3WindowCompare(Parse *pParse, Window *p1, Window *p2){ - if( p1->eType!=p2->eType ) return 1; + if( p1->eFrmType!=p2->eFrmType ) return 1; if( p1->eStart!=p2->eStart ) return 1; if( p1->eEnd!=p2->eEnd ) return 1; + if( p1->eExclude!=p2->eExclude ) return 1; if( sqlite3ExprCompare(pParse, p1->pStart, p2->pStart, -1) ) return 1; if( sqlite3ExprCompare(pParse, p1->pEnd, p2->pEnd, -1) ) return 1; if( sqlite3ExprListCompare(p1->pPartition, p2->pPartition, -1) ) return 1; if( sqlite3ExprListCompare(p1->pOrderBy, p2->pOrderBy, -1) ) return 1; return 0; @@ -999,16 +1216,31 @@ ** and initialize registers and cursors used by sqlite3WindowCodeStep(). */ void sqlite3WindowCodeInit(Parse *pParse, Window *pMWin){ Window *pWin; Vdbe *v = sqlite3GetVdbe(pParse); - int nPart = (pMWin->pPartition ? pMWin->pPartition->nExpr : 0); - nPart += (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); - if( nPart ){ + + /* Allocate registers to use for PARTITION BY values, if any. Initialize + ** said registers to NULL. */ + if( pMWin->pPartition ){ + int nExpr = pMWin->pPartition->nExpr; pMWin->regPart = pParse->nMem+1; - pParse->nMem += nPart; - sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nPart-1); + pParse->nMem += nExpr; + sqlite3VdbeAddOp3(v, OP_Null, 0, pMWin->regPart, pMWin->regPart+nExpr-1); + } + + pMWin->regOne = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regOne); + + if( pMWin->eExclude ){ + pMWin->regStartRowid = ++pParse->nMem; + pMWin->regEndRowid = ++pParse->nMem; + pMWin->csrApp = pParse->nTab++; + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid); + sqlite3VdbeAddOp2(v, OP_OpenDup, pMWin->csrApp, pMWin->iEphCsr); + return; } for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ FuncDef *p = pWin->pFunc; if( (p->funcFlags & SQLITE_FUNC_MINMAX) && pWin->eStart!=TK_UNBOUNDED ){ @@ -1033,50 +1265,71 @@ sqlite3VdbeAddOp2(v, OP_Integer, 0, pWin->regApp+1); } else if( p->zName==nth_valueName || p->zName==first_valueName ){ /* Allocate two registers at pWin->regApp. These will be used to ** store the start and end index of the current frame. */ - assert( pMWin->iEphCsr ); pWin->regApp = pParse->nMem+1; pWin->csrApp = pParse->nTab++; pParse->nMem += 2; sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr); } else if( p->zName==leadName || p->zName==lagName ){ - assert( pMWin->iEphCsr ); pWin->csrApp = pParse->nTab++; sqlite3VdbeAddOp2(v, OP_OpenDup, pWin->csrApp, pMWin->iEphCsr); } } } + +#define WINDOW_STARTING_INT 0 +#define WINDOW_ENDING_INT 1 +#define WINDOW_NTH_VALUE_INT 2 +#define WINDOW_STARTING_NUM 3 +#define WINDOW_ENDING_NUM 4 /* ** A "PRECEDING " (eCond==0) or "FOLLOWING " (eCond==1) or the ** value of the second argument to nth_value() (eCond==2) has just been ** evaluated and the result left in register reg. This function generates VM ** code to check that the value is a non-negative integer and throws an ** exception if it is not. */ -static void windowCheckIntValue(Parse *pParse, int reg, int eCond){ +static void windowCheckValue(Parse *pParse, int reg, int eCond){ static const char *azErr[] = { "frame starting offset must be a non-negative integer", "frame ending offset must be a non-negative integer", - "second argument to nth_value must be a positive integer" + "second argument to nth_value must be a positive integer", + "frame starting offset must be a non-negative number", + "frame ending offset must be a non-negative number", }; - static int aOp[] = { OP_Ge, OP_Ge, OP_Gt }; + static int aOp[] = { OP_Ge, OP_Ge, OP_Gt, OP_Ge, OP_Ge }; Vdbe *v = sqlite3GetVdbe(pParse); int regZero = sqlite3GetTempReg(pParse); - assert( eCond==0 || eCond==1 || eCond==2 ); + assert( eCond>=0 && eCond=WINDOW_STARTING_NUM ){ + int regString = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + sqlite3VdbeAddOp3(v, OP_Ge, regString, sqlite3VdbeCurrentAddr(v)+2, reg); + sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC|SQLITE_JUMPIFNULL); + VdbeCoverage(v); + assert( eCond==3 || eCond==4 ); + VdbeCoverageIf(v, eCond==3); + VdbeCoverageIf(v, eCond==4); + }else{ + sqlite3VdbeAddOp2(v, OP_MustBeInt, reg, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + assert( eCond==0 || eCond==1 || eCond==2 ); + VdbeCoverageIf(v, eCond==0); + VdbeCoverageIf(v, eCond==1); + VdbeCoverageIf(v, eCond==2); + } sqlite3VdbeAddOp3(v, aOp[eCond], regZero, sqlite3VdbeCurrentAddr(v)+2, reg); - VdbeCoverageNeverNullIf(v, eCond==0); - VdbeCoverageNeverNullIf(v, eCond==1); + VdbeCoverageNeverNullIf(v, eCond==0); /* NULL case captured by */ + VdbeCoverageNeverNullIf(v, eCond==1); /* the OP_MustBeInt */ VdbeCoverageNeverNullIf(v, eCond==2); + VdbeCoverageNeverNullIf(v, eCond==3); /* NULL case caught by */ + VdbeCoverageNeverNullIf(v, eCond==4); /* the OP_Ge */ sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_ERROR, OE_Abort); sqlite3VdbeAppendP4(v, (void*)azErr[eCond], P4_STATIC); sqlite3ReleaseTempReg(pParse, regZero); } @@ -1112,41 +1365,32 @@ static void windowAggStep( Parse *pParse, Window *pMWin, /* Linked list of window functions */ int csr, /* Read arguments from this cursor */ int bInverse, /* True to invoke xInverse instead of xStep */ - int reg, /* Array of registers */ - int regPartSize /* Register containing size of partition */ + int reg /* Array of registers */ ){ Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - int flags = pWin->pFunc->funcFlags; + FuncDef *pFunc = pWin->pFunc; int regArg; int nArg = windowArgCount(pWin); + int i; - if( csr>=0 ){ - int i; - for(i=0; izName!=nth_valueName ){ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i); - } - regArg = reg; - if( flags & SQLITE_FUNC_WINDOW_SIZE ){ - if( nArg==0 ){ - regArg = regPartSize; - }else{ - sqlite3VdbeAddOp2(v, OP_SCopy, regPartSize, reg+nArg); - } - nArg++; - } - }else{ - assert( !(flags & SQLITE_FUNC_WINDOW_SIZE) ); - regArg = reg + pWin->iArgCol; - } - - if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && pWin->eStart!=TK_UNBOUNDED + }else{ + sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i); + } + } + regArg = reg; + + if( pMWin->regStartRowid==0 + && (pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) ){ int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg); VdbeCoverage(v); if( bInverse==0 ){ sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1); @@ -1159,151 +1403,223 @@ sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); } sqlite3VdbeJumpHere(v, addrIsNull); }else if( pWin->regApp ){ - assert( pWin->pFunc->zName==nth_valueName - || pWin->pFunc->zName==first_valueName + assert( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName ); assert( bInverse==0 || bInverse==1 ); sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1); - }else if( pWin->pFunc->zName==leadName - || pWin->pFunc->zName==lagName - ){ - /* no-op */ - }else{ + }else if( pFunc->xSFunc!=noopStepFunc ){ int addrIf = 0; if( pWin->pFilter ){ int regTmp; assert( nArg==0 || nArg==pWin->pOwner->x.pList->nExpr ); assert( nArg || pWin->pOwner->x.pList==0 ); - if( csr>0 ){ - regTmp = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); - }else{ - regTmp = regArg + nArg; - } + regTmp = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp); addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1); VdbeCoverage(v); - if( csr>0 ){ - sqlite3ReleaseTempReg(pParse, regTmp); - } + sqlite3ReleaseTempReg(pParse, regTmp); } - if( pWin->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ + if( pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){ CollSeq *pColl; assert( nArg>0 ); pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr); sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); } sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, bInverse, regArg, pWin->regAccum); - sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); + sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); } } } + +typedef struct WindowCodeArg WindowCodeArg; +typedef struct WindowCsrAndReg WindowCsrAndReg; +struct WindowCsrAndReg { + int csr; + int reg; +}; + +struct WindowCodeArg { + Parse *pParse; + Window *pMWin; + Vdbe *pVdbe; + int regGosub; + int addrGosub; + int regArg; + int eDelete; + + WindowCsrAndReg start; + WindowCsrAndReg current; + WindowCsrAndReg end; +}; + +/* +** Values that may be passed as the second argument to windowCodeOp(). +*/ +#define WINDOW_RETURN_ROW 1 +#define WINDOW_AGGINVERSE 2 +#define WINDOW_AGGSTEP 3 + +/* +** Generate VM code to read the window frames peer values from cursor csr into +** an array of registers starting at reg. +*/ +static void windowReadPeerValues( + WindowCodeArg *p, + int csr, + int reg +){ + Window *pMWin = p->pMWin; + ExprList *pOrderBy = pMWin->pOrderBy; + if( pOrderBy ){ + Vdbe *v = sqlite3GetVdbe(p->pParse); + ExprList *pPart = pMWin->pPartition; + int iColOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); + int i; + for(i=0; inExpr; i++){ + sqlite3VdbeAddOp3(v, OP_Column, csr, iColOff+i, reg+i); + } + } +} /* -** Generate VM code to invoke either xValue() (bFinal==0) or xFinalize() -** (bFinal==1) for each window function in the linked list starting at +** Generate VM code to invoke either xValue() (bFin==0) or xFinalize() +** (bFin==1) for each window function in the linked list starting at ** pMWin. Or, for built-in window-functions that do not use the standard ** API, generate the equivalent VM code. */ -static void windowAggFinal(Parse *pParse, Window *pMWin, int bFinal){ +static void windowAggFinal(WindowCodeArg *p, int bFin){ + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; Vdbe *v = sqlite3GetVdbe(pParse); Window *pWin; for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - if( (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) - && pWin->eStart!=TK_UNBOUNDED + if( pMWin->regStartRowid==0 + && (pWin->pFunc->funcFlags & SQLITE_FUNC_MINMAX) + && (pWin->eStart!=TK_UNBOUNDED) ){ sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); sqlite3VdbeAddOp1(v, OP_Last, pWin->csrApp); VdbeCoverage(v); sqlite3VdbeAddOp3(v, OP_Column, pWin->csrApp, 0, pWin->regResult); sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); - if( bFinal ){ - sqlite3VdbeAddOp1(v, OP_ResetSorter, pWin->csrApp); - } }else if( pWin->regApp ){ + assert( pMWin->regStartRowid==0 ); }else{ - if( bFinal ){ - sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, windowArgCount(pWin)); + int nArg = windowArgCount(pWin); + if( bFin ){ + sqlite3VdbeAddOp2(v, OP_AggFinal, pWin->regAccum, nArg); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); sqlite3VdbeAddOp2(v, OP_Copy, pWin->regAccum, pWin->regResult); sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); }else{ - sqlite3VdbeAddOp3(v, OP_AggValue, pWin->regAccum, windowArgCount(pWin), - pWin->regResult); + sqlite3VdbeAddOp3(v, OP_AggValue,pWin->regAccum,nArg,pWin->regResult); sqlite3VdbeAppendP4(v, pWin->pFunc, P4_FUNCDEF); } } } } /* -** This function generates VM code to invoke the sub-routine at address -** lblFlushPart once for each partition with the entire partition cached in -** the Window.iEphCsr temp table. +** Generate code to calculate the current values of all window functions in the +** p->pMWin list by doing a full scan of the current window frame. Store the +** results in the Window.regResult registers, ready to return the upper +** layer. */ -static void windowPartitionCache( - Parse *pParse, - Select *p, /* The rewritten SELECT statement */ - WhereInfo *pWInfo, /* WhereInfo to call WhereEnd() on */ - int regFlushPart, /* Register to use with Gosub lblFlushPart */ - int lblFlushPart, /* Subroutine to Gosub to */ - int *pRegSize /* OUT: Register containing partition size */ -){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int iSubCsr = p->pSrc->a[0].iCursor; - int nSub = p->pSrc->a[0].pTab->nCol; - int k; - - int reg = pParse->nMem+1; - int regRecord = reg+nSub; - int regRowid = regRecord+1; - - *pRegSize = regRowid; - pParse->nMem += nSub + 2; - - /* Load the column values for the row returned by the sub-select - ** into an array of registers starting at reg. */ - for(k=0; kpPartition ){ +static void windowFullScan(WindowCodeArg *p){ + Window *pWin; + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; + Vdbe *v = p->pVdbe; + + int regCRowid = 0; /* Current rowid value */ + int regCPeer = 0; /* Current peer values */ + int regRowid = 0; /* AggStep rowid value */ + int regPeer = 0; /* AggStep peer values */ + + int nPeer; + int lblNext; + int lblBrk; + int addrNext; + int csr = pMWin->csrApp; + + nPeer = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); + + lblNext = sqlite3VdbeMakeLabel(pParse); + lblBrk = sqlite3VdbeMakeLabel(pParse); + + regCRowid = sqlite3GetTempReg(pParse); + regRowid = sqlite3GetTempReg(pParse); + if( nPeer ){ + regCPeer = sqlite3GetTempRange(pParse, nPeer); + regPeer = sqlite3GetTempRange(pParse, nPeer); + } + + sqlite3VdbeAddOp2(v, OP_Rowid, pMWin->iEphCsr, regCRowid); + windowReadPeerValues(p, pMWin->iEphCsr, regCPeer); + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regAccum); + } + + sqlite3VdbeAddOp3(v, OP_SeekGE, csr, lblBrk, pMWin->regStartRowid); + VdbeCoverage(v); + addrNext = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp2(v, OP_Rowid, csr, regRowid); + sqlite3VdbeAddOp3(v, OP_Gt, pMWin->regEndRowid, lblBrk, regRowid); + VdbeCoverageNeverNull(v); + + if( pMWin->eExclude==TK_CURRENT ){ + sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, lblNext, regRowid); + VdbeCoverageNeverNull(v); + }else if( pMWin->eExclude!=TK_NO ){ int addr; - ExprList *pPart = pMWin->pPartition; - int nPart = pPart->nExpr; - 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); - sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); - VdbeCoverageEqNe(v); - sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - VdbeComment((v, "call flush_partition")); - } - - /* Buffer the current row in the ephemeral table. */ - sqlite3VdbeAddOp2(v, OP_NewRowid, pMWin->iEphCsr, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pMWin->iEphCsr, regRecord, regRowid); - - /* End of the input loop */ - sqlite3WhereEnd(pWInfo); - - /* Invoke "flush_partition" to deal with the final (or only) partition */ - sqlite3VdbeAddOp2(v, OP_Gosub, regFlushPart, lblFlushPart); - VdbeComment((v, "call flush_partition")); + int addrEq = 0; + KeyInfo *pKeyInfo = 0; + + if( pMWin->pOrderBy ){ + pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pMWin->pOrderBy, 0, 0); + } + if( pMWin->eExclude==TK_TIES ){ + addrEq = sqlite3VdbeAddOp3(v, OP_Eq, regCRowid, 0, regRowid); + VdbeCoverageNeverNull(v); + } + if( pKeyInfo ){ + windowReadPeerValues(p, csr, regPeer); + sqlite3VdbeAddOp3(v, OP_Compare, regPeer, regCPeer, nPeer); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + addr = sqlite3VdbeCurrentAddr(v)+1; + sqlite3VdbeAddOp3(v, OP_Jump, addr, lblNext, addr); + VdbeCoverageEqNe(v); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblNext); + } + if( addrEq ) sqlite3VdbeJumpHere(v, addrEq); + } + + windowAggStep(pParse, pMWin, csr, 0, p->regArg); + + sqlite3VdbeResolveLabel(v, lblNext); + sqlite3VdbeAddOp2(v, OP_Next, csr, addrNext); + VdbeCoverage(v); + sqlite3VdbeJumpHere(v, addrNext-1); + sqlite3VdbeJumpHere(v, addrNext+1); + sqlite3ReleaseTempReg(pParse, regRowid); + sqlite3ReleaseTempReg(pParse, regCRowid); + if( nPeer ){ + sqlite3ReleaseTempRange(pParse, regPeer, nPeer); + sqlite3ReleaseTempRange(pParse, regCPeer, nPeer); + } + + windowAggFinal(p, 1); } /* ** Invoke the sub-routine at regGosub (generated by code in select.c) to ** return the current row of Window.iEphCsr. If all window functions are @@ -1315,114 +1631,78 @@ ** nth_value() ** first_value() ** lag() ** lead() */ -static void windowReturnOneRow( - Parse *pParse, - Window *pMWin, - int regGosub, - int addrGosub -){ - Vdbe *v = sqlite3GetVdbe(pParse); - Window *pWin; - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ - int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(pParse); - int tmpReg = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); - - if( pFunc->zName==nth_valueName ){ - sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+1,tmpReg); - windowCheckIntValue(pParse, tmpReg, 2); - }else{ - sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg); - } - sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg); - sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg); - VdbeCoverageNeverTaken(v); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); - sqlite3VdbeResolveLabel(v, lbl); - sqlite3ReleaseTempReg(pParse, tmpReg); - } - else if( pFunc->zName==leadName || pFunc->zName==lagName ){ - int nArg = pWin->pOwner->x.pList->nExpr; - int iEph = pMWin->iEphCsr; - int csr = pWin->csrApp; - int lbl = sqlite3VdbeMakeLabel(pParse); - int tmpReg = sqlite3GetTempReg(pParse); - - if( nArg<3 ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); - }else{ - sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+2, pWin->regResult); - } - sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg); - if( nArg<2 ){ - int val = (pFunc->zName==leadName ? 1 : -1); - sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val); - }else{ - int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract); - int tmpReg2 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2); - sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg); - sqlite3ReleaseTempReg(pParse, tmpReg2); - } - - sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); - sqlite3VdbeResolveLabel(v, lbl); - sqlite3ReleaseTempReg(pParse, tmpReg); - } - } - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); -} - -/* -** Invoke the code generated by windowReturnOneRow() and, optionally, the -** xInverse() function for each window function, for one or more rows -** from the Window.iEphCsr temp table. This routine generates VM code -** similar to: -** -** while( regCtr>0 ){ -** regCtr--; -** windowReturnOneRow() -** if( bInverse ){ -** AggInverse -** } -** Next (Window.iEphCsr) -** } -*/ -static void windowReturnRows( - Parse *pParse, - Window *pMWin, /* List of window functions */ - int regCtr, /* Register containing number of rows */ - int regGosub, /* Register for Gosub addrGosub */ - int addrGosub, /* Address of sub-routine for ReturnOneRow */ - int regInvArg, /* Array of registers for xInverse args */ - int regInvSize /* Register containing size of partition */ -){ - int addr; - Vdbe *v = sqlite3GetVdbe(pParse); - windowAggFinal(pParse, pMWin, 0); - addr = sqlite3VdbeAddOp3(v, OP_IfPos, regCtr, sqlite3VdbeCurrentAddr(v)+2 ,1); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); - if( regInvArg ){ - windowAggStep(pParse, pMWin, pMWin->iEphCsr, 1, regInvArg, regInvSize); - } - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, addr); - VdbeCoverage(v); - sqlite3VdbeJumpHere(v, addr+1); /* The OP_Goto */ +static void windowReturnOneRow(WindowCodeArg *p){ + Window *pMWin = p->pMWin; + Vdbe *v = p->pVdbe; + + if( pMWin->regStartRowid ){ + windowFullScan(p); + }else{ + Parse *pParse = p->pParse; + Window *pWin; + + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( pFunc->zName==nth_valueName + || pFunc->zName==first_valueName + ){ + int csr = pWin->csrApp; + int lbl = sqlite3VdbeMakeLabel(pParse); + int tmpReg = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); + + if( pFunc->zName==nth_valueName ){ + sqlite3VdbeAddOp3(v, OP_Column,pMWin->iEphCsr,pWin->iArgCol+1,tmpReg); + windowCheckValue(pParse, tmpReg, 2); + }else{ + sqlite3VdbeAddOp2(v, OP_Integer, 1, tmpReg); + } + sqlite3VdbeAddOp3(v, OP_Add, tmpReg, pWin->regApp, tmpReg); + sqlite3VdbeAddOp3(v, OP_Gt, pWin->regApp+1, lbl, tmpReg); + VdbeCoverageNeverNull(v); + sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, 0, tmpReg); + VdbeCoverageNeverTaken(v); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); + sqlite3VdbeResolveLabel(v, lbl); + sqlite3ReleaseTempReg(pParse, tmpReg); + } + else if( pFunc->zName==leadName || pFunc->zName==lagName ){ + int nArg = pWin->pOwner->x.pList->nExpr; + int csr = pWin->csrApp; + int lbl = sqlite3VdbeMakeLabel(pParse); + int tmpReg = sqlite3GetTempReg(pParse); + int iEph = pMWin->iEphCsr; + + if( nArg<3 ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, pWin->regResult); + }else{ + sqlite3VdbeAddOp3(v, OP_Column, iEph,pWin->iArgCol+2,pWin->regResult); + } + sqlite3VdbeAddOp2(v, OP_Rowid, iEph, tmpReg); + if( nArg<2 ){ + int val = (pFunc->zName==leadName ? 1 : -1); + sqlite3VdbeAddOp2(v, OP_AddImm, tmpReg, val); + }else{ + int op = (pFunc->zName==leadName ? OP_Add : OP_Subtract); + int tmpReg2 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_Column, iEph, pWin->iArgCol+1, tmpReg2); + sqlite3VdbeAddOp3(v, op, tmpReg2, tmpReg, tmpReg); + sqlite3ReleaseTempReg(pParse, tmpReg2); + } + + sqlite3VdbeAddOp3(v, OP_SeekRowid, csr, lbl, tmpReg); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol, pWin->regResult); + sqlite3VdbeResolveLabel(v, lbl); + sqlite3ReleaseTempReg(pParse, tmpReg); + } + } + } + sqlite3VdbeAddOp2(v, OP_Gosub, p->regGosub, p->addrGosub); } /* ** Generate code to set the accumulator register for each window function ** in the linked list passed as the second argument to NULL. And perform @@ -1436,693 +1716,269 @@ 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( pFunc->zName==nth_valueName - || pFunc->zName==first_valueName - ){ - 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); + if( pMWin->regStartRowid==0 ){ + if( pFunc->zName==nth_valueName || pFunc->zName==first_valueName ){ + 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; return regArg; } - -/* -** This function does the work of sqlite3WindowCodeStep() for all "ROWS" -** window frame types except for "BETWEEN UNBOUNDED PRECEDING AND CURRENT -** ROW". Pseudo-code for each follows. -** -** ROWS BETWEEN PRECEDING AND FOLLOWING -** -** ... -** if( new partition ){ -** Gosub flush_partition -** } -** Insert (record in eph-table) -** sqlite3WhereEnd() -** Gosub flush_partition -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrStart) -** OpenDup (iEphCsr -> csrEnd) -** } -** regStart = // PRECEDING expression -** regEnd = // FOLLOWING expression -** if( regStart<0 || regEnd<0 ){ error! } -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Next(csrEnd) // if EOF skip Aggstep -** Aggstep (csrEnd) -** if( (regEnd--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** flush_partition_done: -** ResetSorter (csr) -** Return -** -** ROWS BETWEEN PRECEDING AND CURRENT ROW -** ROWS BETWEEN CURRENT ROW AND FOLLOWING -** ROWS BETWEEN UNBOUNDED PRECEDING AND FOLLOWING -** -** These are similar to the above. For "CURRENT ROW", intialize the -** register to 0. For "UNBOUNDED PRECEDING" to infinity. -** -** ROWS BETWEEN PRECEDING AND UNBOUNDED FOLLOWING -** ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** while( 1 ){ -** Next(csrEnd) // Exit while(1) at EOF -** Aggstep (csrEnd) -** } -** while( 1 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** -** For the "CURRENT ROW AND UNBOUNDED FOLLOWING" case, the final if() -** condition is always true (as if regStart were initialized to 0). -** -** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** This is the only RANGE case handled by this routine. It modifies the -** second while( 1 ) loop in "ROWS BETWEEN CURRENT ... UNBOUNDED..." to -** be: -** -** while( 1 ){ -** AggFinal (xValue) -** while( 1 ){ -** regPeer++ -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( new peer ) break; -** } -** while( (regPeer--)>0 ){ -** AggInverse (csrStart) -** Next(csrStart) -** } -** } -** -** ROWS BETWEEN FOLLOWING AND FOLLOWING -** -** regEnd = regEnd - regStart -** Rewind (csr,csrStart,csrEnd) // if EOF goto flush_partition_done -** Aggstep (csrEnd) -** Next(csrEnd) // if EOF fall-through -** if( (regEnd--)<=0 ){ -** if( (regStart--)<=0 ){ -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** } -** AggInverse (csrStart) -** Next (csrStart) -** } -** -** ROWS BETWEEN PRECEDING AND PRECEDING -** -** Replace the bit after "Rewind" in the above with: -** -** if( (regEnd--)<=0 ){ -** AggStep (csrEnd) -** Next (csrEnd) -** } -** AggFinal (xValue) -** Gosub addrGosub -** Next(csr) // if EOF goto flush_partition_done -** if( (regStart--)<=0 ){ -** AggInverse (csr2) -** Next (csr2) -** } -** -*/ -static void windowCodeRowExprStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub -){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int regFlushPart; /* Register for "Gosub flush_partition" */ - int lblFlushPart; /* Label for "Gosub flush_partition" */ - int lblFlushDone; /* Label for "Gosub flush_partition_done" */ - - int regArg; - int addr; - int csrStart = pParse->nTab++; - int csrEnd = pParse->nTab++; - int regStart; /* Value of PRECEDING */ - int regEnd; /* Value of FOLLOWING */ - int addrGoto; - int addrTop; - int addrIfPos1 = 0; - int addrIfPos2 = 0; - int regSize = 0; - - assert( pMWin->eStart==TK_PRECEDING - || pMWin->eStart==TK_CURRENT - || pMWin->eStart==TK_FOLLOWING - || pMWin->eStart==TK_UNBOUNDED - ); - assert( pMWin->eEnd==TK_FOLLOWING - || pMWin->eEnd==TK_CURRENT - || pMWin->eEnd==TK_UNBOUNDED - || pMWin->eEnd==TK_PRECEDING - ); - - /* Allocate register and label for the "flush_partition" sub-routine. */ - regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(pParse); - lblFlushDone = sqlite3VdbeMakeLabel(pParse); - - regStart = ++pParse->nMem; - regEnd = ++pParse->nMem; - - windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size); - - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - - /* Start of "flush_partition" */ - sqlite3VdbeResolveLabel(v, lblFlushPart); - sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - VdbeComment((v, "Flush_partition subroutine")); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrStart, pMWin->iEphCsr); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrEnd, pMWin->iEphCsr); - - /* If either regStart or regEnd are not non-negative integers, throw - ** an exception. */ - if( pMWin->pStart ){ - sqlite3ExprCode(pParse, pMWin->pStart, regStart); - windowCheckIntValue(pParse, regStart, 0); - } - if( pMWin->pEnd ){ - sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); - windowCheckIntValue(pParse, regEnd, 1); - } - - /* If this is "ROWS FOLLOWING AND ROWS FOLLOWING", do: - ** - ** if( regEndpEnd && pMWin->eStart==TK_FOLLOWING ){ - assert( pMWin->pStart!=0 ); - assert( pMWin->eEnd==TK_FOLLOWING ); - sqlite3VdbeAddOp3(v, OP_Ge, regStart, sqlite3VdbeCurrentAddr(v)+2, regEnd); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart); - sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd); - } - - if( pMWin->pStart && pMWin->eEnd==TK_PRECEDING ){ - assert( pMWin->pEnd!=0 ); - assert( pMWin->eStart==TK_PRECEDING ); - sqlite3VdbeAddOp3(v, OP_Le, regStart, sqlite3VdbeCurrentAddr(v)+3, regEnd); - VdbeCoverageNeverNull(v); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regStart); - sqlite3VdbeAddOp2(v, OP_Copy, regSize, regEnd); - } - - /* Initialize the accumulator register for each window function to NULL */ - regArg = windowInitAccum(pParse, pMWin); - - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblFlushDone); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, csrStart, lblFlushDone); - VdbeCoverageNeverTaken(v); - sqlite3VdbeChangeP5(v, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, csrEnd, lblFlushDone); - VdbeCoverageNeverTaken(v); - sqlite3VdbeChangeP5(v, 1); - - /* Invoke AggStep function for each window function using the row that - ** csrEnd currently points to. Or, if csrEnd is already at EOF, - ** do nothing. */ - addrTop = sqlite3VdbeCurrentAddr(v); - if( pMWin->eEnd==TK_PRECEDING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); - VdbeCoverage(v); - } - sqlite3VdbeAddOp2(v, OP_Next, csrEnd, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - addr = sqlite3VdbeAddOp0(v, OP_Goto); - windowAggStep(pParse, pMWin, csrEnd, 0, regArg, regSize); - if( pMWin->eEnd==TK_UNBOUNDED ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - sqlite3VdbeJumpHere(v, addr); - addrTop = sqlite3VdbeCurrentAddr(v); - }else{ - sqlite3VdbeJumpHere(v, addr); - if( pMWin->eEnd==TK_PRECEDING ){ - sqlite3VdbeJumpHere(v, addrIfPos1); - } - } - - if( pMWin->eEnd==TK_FOLLOWING ){ - addrIfPos1 = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0 , 1); - VdbeCoverage(v); - } - if( pMWin->eStart==TK_FOLLOWING ){ - addrIfPos2 = sqlite3VdbeAddOp3(v, OP_IfPos, regStart, 0 , 1); - VdbeCoverage(v); - } - windowAggFinal(pParse, pMWin, 0); - windowReturnOneRow(pParse, pMWin, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblFlushDone); - if( pMWin->eStart==TK_FOLLOWING ){ - sqlite3VdbeJumpHere(v, addrIfPos2); - } - - if( pMWin->eStart==TK_CURRENT - || pMWin->eStart==TK_PRECEDING - || pMWin->eStart==TK_FOLLOWING - ){ - int lblSkipInverse = sqlite3VdbeMakeLabel(pParse);; - if( pMWin->eStart==TK_PRECEDING ){ - sqlite3VdbeAddOp3(v, OP_IfPos, regStart, lblSkipInverse, 1); - VdbeCoverage(v); - } - if( pMWin->eStart==TK_FOLLOWING ){ - sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Goto, 0, lblSkipInverse); - }else{ - sqlite3VdbeAddOp2(v, OP_Next, csrStart, sqlite3VdbeCurrentAddr(v)+1); - VdbeCoverageAlwaysTaken(v); - } - windowAggStep(pParse, pMWin, csrStart, 1, regArg, regSize); - sqlite3VdbeResolveLabel(v, lblSkipInverse); - } - if( pMWin->eEnd==TK_FOLLOWING ){ - sqlite3VdbeJumpHere(v, addrIfPos1); - } - sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop); - - /* flush_partition_done: */ - sqlite3VdbeResolveLabel(v, lblFlushDone); - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); - VdbeComment((v, "end flush_partition subroutine")); - - /* Jump to here to skip over flush_partition */ - sqlite3VdbeJumpHere(v, addrGoto); -} - -/* -** This function does the work of sqlite3WindowCodeStep() for cases that -** would normally be handled by windowCodeDefaultStep() when there are -** one or more built-in window-functions that require the entire partition -** to be cached in a temp table before any rows can be returned. Additionally. -** "RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" is always handled by -** this function. -** -** Pseudo-code corresponding to the VM code generated by this function -** for each type of window follows. -** -** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrLead) -** } -** Integer ctr 0 -** foreach row (csrLead){ -** if( new peer ){ -** AggFinal (xValue) -** for(i=0; i csrLead) -** } -** foreach row (csrLead) { -** AggStep (csrLead) -** } -** foreach row (iEphCsr) { -** Gosub addrGosub -** } -** -** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING -** -** flush_partition: -** Once { -** OpenDup (iEphCsr -> csrLead) -** } -** foreach row (csrLead){ -** AggStep (csrLead) -** } -** Rewind (csrLead) -** Integer ctr 0 -** foreach row (csrLead){ -** if( new peer ){ -** AggFinal (xValue) -** for(i=0; ipWin; - Vdbe *v = sqlite3GetVdbe(pParse); - int k; - int addr; - ExprList *pPart = pMWin->pPartition; - ExprList *pOrderBy = pMWin->pOrderBy; - int nPeer = pOrderBy ? pOrderBy->nExpr : 0; - int regNewPeer; - - int addrGoto; /* Address of Goto used to jump flush_par.. */ - int addrNext; /* Jump here for next iteration of loop */ - int regFlushPart; - int lblFlushPart; - int csrLead; - int regCtr; - int regArg; /* Register array to martial function args */ - int regSize; - int lblEmpty; - int bReverse = pMWin->pOrderBy && pMWin->eStart==TK_CURRENT - && pMWin->eEnd==TK_UNBOUNDED; - - assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED) - ); - - lblEmpty = sqlite3VdbeMakeLabel(pParse); - regNewPeer = pParse->nMem+1; - pParse->nMem += nPeer; - - /* Allocate register and label for the "flush_partition" sub-routine. */ - regFlushPart = ++pParse->nMem; - lblFlushPart = sqlite3VdbeMakeLabel(pParse); - - csrLead = pParse->nTab++; - regCtr = ++pParse->nMem; - - windowPartitionCache(pParse, p, pWInfo, regFlushPart, lblFlushPart, ®Size); - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - - /* Start of "flush_partition" */ - sqlite3VdbeResolveLabel(v, lblFlushPart); - sqlite3VdbeAddOp2(v, OP_Once, 0, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_OpenDup, csrLead, pMWin->iEphCsr); - - /* Initialize the accumulator register for each window function to NULL */ - regArg = windowInitAccum(pParse, pMWin); - - sqlite3VdbeAddOp2(v, OP_Integer, 0, regCtr); - sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr, lblEmpty); - VdbeCoverageNeverTaken(v); - - if( bReverse ){ - int addr2 = sqlite3VdbeCurrentAddr(v); - windowAggStep(pParse, pMWin, csrLead, 0, regArg, regSize); - sqlite3VdbeAddOp2(v, OP_Next, csrLead, addr2); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Rewind, csrLead, lblEmpty); - VdbeCoverageNeverTaken(v); - } - addrNext = sqlite3VdbeCurrentAddr(v); - - if( pOrderBy && (pMWin->eEnd==TK_CURRENT || pMWin->eStart==TK_CURRENT) ){ - int bCurrent = (pMWin->eStart==TK_CURRENT); - int addrJump = 0; /* Address of OP_Jump below */ - if( pMWin->eType==TK_RANGE ){ - int iOff = pMWin->nBufferCol + (pPart ? pPart->nExpr : 0); - int regPeer = pMWin->regPart + (pPart ? pPart->nExpr : 0); - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); - for(k=0; kiEphCsr); - sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); - - /* Jump to here to skip over flush_partition */ - sqlite3VdbeJumpHere(v, addrGoto); -} - - -/* -** 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). -** -** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -** -** As above, except assume every row is a "new peer". -*/ -static void windowCodeDefaultStep( - Parse *pParse, - Select *p, - WhereInfo *pWInfo, - int regGosub, - int addrGosub -){ - Window *pMWin = p->pWin; - Vdbe *v = sqlite3GetVdbe(pParse); - 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; - ExprList *pPart = pMWin->pPartition; - ExprList *pOrderBy = pMWin->pOrderBy; - - assert( pMWin->eType==TK_RANGE - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - ); - - assert( (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_UNBOUNDED && pMWin->eEnd==TK_UNBOUNDED) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_CURRENT) - || (pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED && !pOrderBy) - ); - - if( pMWin->eEnd==TK_UNBOUNDED ){ - pOrderBy = 0; - } - - pParse->nMem += nSub + 2; - - /* Load the individual column values of the row returned by - ** the sub-select into an array of registers. */ - for(k=0; knExpr : 0); - int addrGoto = 0; - int addrJump = 0; - int nPeer = (pOrderBy ? pOrderBy->nExpr : 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); - VdbeCoverageEqNe(v); - windowAggFinal(pParse, pMWin, 1); - if( pOrderBy ){ - addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); - } - } - - if( pOrderBy ){ - int regNewPeer = reg + pMWin->nBufferCol + nPart; - int regPeer = pMWin->regPart + nPart; - - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); - if( pMWin->eType==TK_RANGE ){ - KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); - addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPeer, regPeer, nPeer); - sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); - addrJump = sqlite3VdbeAddOp3(v, OP_Jump, addr+2, 0, addr+2); - VdbeCoverage(v); - }else{ - addrJump = 0; - } - windowAggFinal(pParse, pMWin, pMWin->eStart==TK_CURRENT); - if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); - } - - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); - VdbeCoverage(v); - - sqlite3VdbeAddOp1(v, OP_ResetSorter, pMWin->iEphCsr); - sqlite3VdbeAddOp3( - v, OP_Copy, reg+pMWin->nBufferCol, pMWin->regPart, nPart+nPeer-1 - ); - - if( addrJump ) sqlite3VdbeJumpHere(v, addrJump); - } - - /* Invoke step function for window functions */ - windowAggStep(pParse, pMWin, -1, 0, reg, 0); - - /* 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); - - windowAggFinal(pParse, pMWin, 1); - sqlite3VdbeAddOp2(v, OP_Rewind, pMWin->iEphCsr,sqlite3VdbeCurrentAddr(v)+3); - VdbeCoverage(v); - sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrGosub); - sqlite3VdbeAddOp2(v, OP_Next, pMWin->iEphCsr, sqlite3VdbeCurrentAddr(v)-1); - VdbeCoverage(v); -} +/* +** Return true if the current frame should be cached in the ephemeral table, +** even if there are no xInverse() calls required. +*/ +static int windowCacheFrame(Window *pMWin){ + Window *pWin; + if( pMWin->regStartRowid ) return 1; + for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ + FuncDef *pFunc = pWin->pFunc; + if( (pFunc->zName==nth_valueName) + || (pFunc->zName==first_valueName) + || (pFunc->zName==leadName) + || (pFunc->zName==lagName) + ){ + return 1; + } + } + return 0; +} + +/* +** regOld and regNew are each the first register in an array of size +** pOrderBy->nExpr. This function generates code to compare the two +** arrays of registers using the collation sequences and other comparison +** parameters specified by pOrderBy. +** +** If the two arrays are not equal, the contents of regNew is copied to +** regOld and control falls through. Otherwise, if the contents of the arrays +** are equal, an OP_Goto is executed. The address of the OP_Goto is returned. +*/ +static void windowIfNewPeer( + Parse *pParse, + ExprList *pOrderBy, + int regNew, /* First in array of new values */ + int regOld, /* First in array of old values */ + int addr /* Jump here */ +){ + Vdbe *v = sqlite3GetVdbe(pParse); + if( pOrderBy ){ + int nVal = pOrderBy->nExpr; + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOrderBy, 0, 0); + sqlite3VdbeAddOp3(v, OP_Compare, regOld, regNew, nVal); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeAddOp3(v, OP_Jump, + sqlite3VdbeCurrentAddr(v)+1, addr, sqlite3VdbeCurrentAddr(v)+1 + ); + VdbeCoverageEqNe(v); + sqlite3VdbeAddOp3(v, OP_Copy, regNew, regOld, nVal-1); + }else{ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); + } +} + +/* +** This function is called as part of generating VM programs for RANGE +** offset PRECEDING/FOLLOWING frame boundaries. Assuming "ASC" order for +** the ORDER BY term in the window, it generates code equivalent to: +** +** if( csr1.peerVal + regVal >= csr2.peerVal ) goto lbl; +** +** A special type of arithmetic is used such that if csr.peerVal is not +** a numeric type (real or integer), then the result of the addition is +** a copy of csr1.peerVal. +*/ +static void windowCodeRangeTest( + WindowCodeArg *p, + int op, /* OP_Ge or OP_Gt */ + int csr1, + int regVal, + int csr2, + int lbl +){ + Parse *pParse = p->pParse; + Vdbe *v = sqlite3GetVdbe(pParse); + int reg1 = sqlite3GetTempReg(pParse); + int reg2 = sqlite3GetTempReg(pParse); + int arith = OP_Add; + int addrGe; + + int regString = ++pParse->nMem; + + assert( op==OP_Ge || op==OP_Gt || op==OP_Le ); + assert( p->pMWin->pOrderBy && p->pMWin->pOrderBy->nExpr==1 ); + if( p->pMWin->pOrderBy->a[0].sortOrder ){ + switch( op ){ + case OP_Ge: op = OP_Le; break; + case OP_Gt: op = OP_Lt; break; + default: assert( op==OP_Le ); op = OP_Ge; break; + } + arith = OP_Subtract; + } + + windowReadPeerValues(p, csr1, reg1); + windowReadPeerValues(p, csr2, reg2); + + /* Check if the peer value for csr1 value is a text or blob by comparing + ** it to the smallest possible string - ''. If it is, jump over the + ** OP_Add or OP_Subtract operation and proceed directly to the comparison. */ + sqlite3VdbeAddOp4(v, OP_String8, 0, regString, 0, "", P4_STATIC); + addrGe = sqlite3VdbeAddOp3(v, OP_Ge, regString, 0, reg1); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, arith, regVal, reg1, reg1); + sqlite3VdbeJumpHere(v, addrGe); + sqlite3VdbeAddOp3(v, op, reg2, lbl, reg1); VdbeCoverage(v); + sqlite3VdbeChangeP5(v, SQLITE_NULLEQ); + assert( op==OP_Ge || op==OP_Gt || op==OP_Lt || op==OP_Le ); + testcase(op==OP_Ge); VdbeCoverageIf(v, op==OP_Ge); + testcase(op==OP_Lt); VdbeCoverageIf(v, op==OP_Lt); + testcase(op==OP_Le); VdbeCoverageIf(v, op==OP_Le); + testcase(op==OP_Gt); VdbeCoverageIf(v, op==OP_Gt); + + sqlite3ReleaseTempReg(pParse, reg1); + sqlite3ReleaseTempReg(pParse, reg2); +} + +/* +** Helper function for sqlite3WindowCodeStep(). Each call to this function +** generates VM code for a single RETURN_ROW, AGGSTEP or AGGINVERSE +** operation. Refer to the header comment for sqlite3WindowCodeStep() for +** details. +*/ +static int windowCodeOp( + WindowCodeArg *p, /* Context object */ + int op, /* WINDOW_RETURN_ROW, AGGSTEP or AGGINVERSE */ + int regCountdown, /* Register for OP_IfPos countdown */ + int jumpOnEof /* Jump here if stepped cursor reaches EOF */ +){ + int csr, reg; + Parse *pParse = p->pParse; + Window *pMWin = p->pMWin; + int ret = 0; + Vdbe *v = p->pVdbe; + int addrIf = 0; + int addrContinue = 0; + int addrGoto = 0; + int bPeer = (pMWin->eFrmType!=TK_ROWS); + + int lblDone = sqlite3VdbeMakeLabel(pParse); + int addrNextRange = 0; + + /* Special case - WINDOW_AGGINVERSE is always a no-op if the frame + ** starts with UNBOUNDED PRECEDING. */ + if( op==WINDOW_AGGINVERSE && pMWin->eStart==TK_UNBOUNDED ){ + assert( regCountdown==0 && jumpOnEof==0 ); + return 0; + } + + if( regCountdown>0 ){ + if( pMWin->eFrmType==TK_RANGE ){ + addrNextRange = sqlite3VdbeCurrentAddr(v); + assert( op==WINDOW_AGGINVERSE || op==WINDOW_AGGSTEP ); + if( op==WINDOW_AGGINVERSE ){ + if( pMWin->eStart==TK_FOLLOWING ){ + windowCodeRangeTest( + p, OP_Le, p->current.csr, regCountdown, p->start.csr, lblDone + ); + }else{ + windowCodeRangeTest( + p, OP_Ge, p->start.csr, regCountdown, p->current.csr, lblDone + ); + } + }else{ + windowCodeRangeTest( + p, OP_Gt, p->end.csr, regCountdown, p->current.csr, lblDone + ); + } + }else{ + addrIf = sqlite3VdbeAddOp3(v, OP_IfPos, regCountdown, 0, 1); + VdbeCoverage(v); + } + } + + if( op==WINDOW_RETURN_ROW && pMWin->regStartRowid==0 ){ + windowAggFinal(p, 0); + } + addrContinue = sqlite3VdbeCurrentAddr(v); + switch( op ){ + case WINDOW_RETURN_ROW: + csr = p->current.csr; + reg = p->current.reg; + windowReturnOneRow(p); + break; + + case WINDOW_AGGINVERSE: + csr = p->start.csr; + reg = p->start.reg; + if( pMWin->regStartRowid ){ + assert( pMWin->regEndRowid ); + sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regStartRowid, 1); + }else{ + windowAggStep(pParse, pMWin, csr, 1, p->regArg); + } + break; + + default: + assert( op==WINDOW_AGGSTEP ); + csr = p->end.csr; + reg = p->end.reg; + if( pMWin->regStartRowid ){ + assert( pMWin->regEndRowid ); + sqlite3VdbeAddOp2(v, OP_AddImm, pMWin->regEndRowid, 1); + }else{ + windowAggStep(pParse, pMWin, csr, 0, p->regArg); + } + break; + } + + if( op==p->eDelete ){ + sqlite3VdbeAddOp1(v, OP_Delete, csr); + sqlite3VdbeChangeP5(v, OPFLAG_SAVEPOSITION); + } + + if( jumpOnEof ){ + sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + ret = sqlite3VdbeAddOp0(v, OP_Goto); + }else{ + sqlite3VdbeAddOp2(v, OP_Next, csr, sqlite3VdbeCurrentAddr(v)+1+bPeer); + VdbeCoverage(v); + if( bPeer ){ + addrGoto = sqlite3VdbeAddOp0(v, OP_Goto); + } + } + + if( bPeer ){ + int nReg = (pMWin->pOrderBy ? pMWin->pOrderBy->nExpr : 0); + int regTmp = (nReg ? sqlite3GetTempRange(pParse, nReg) : 0); + windowReadPeerValues(p, csr, regTmp); + windowIfNewPeer(pParse, pMWin->pOrderBy, regTmp, reg, addrContinue); + sqlite3ReleaseTempRange(pParse, regTmp, nReg); + } + + if( addrNextRange ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNextRange); + } + sqlite3VdbeResolveLabel(v, lblDone); + if( addrGoto ) sqlite3VdbeJumpHere(v, addrGoto); + if( addrIf ) sqlite3VdbeJumpHere(v, addrIf); + return ret; +} + /* ** Allocate and return a duplicate of the Window object indicated by the ** third argument. Set the Window.pOwner field of the new object to ** pOwner. @@ -2135,13 +1991,14 @@ pNew->zName = sqlite3DbStrDup(db, p->zName); pNew->pFilter = sqlite3ExprDup(db, p->pFilter, 0); pNew->pFunc = p->pFunc; pNew->pPartition = sqlite3ExprListDup(db, p->pPartition, 0); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, 0); - pNew->eType = p->eType; + pNew->eFrmType = p->eFrmType; pNew->eEnd = p->eEnd; pNew->eStart = p->eStart; + pNew->eExclude = p->eExclude; pNew->pStart = sqlite3ExprDup(db, p->pStart, 0); pNew->pEnd = sqlite3ExprDup(db, p->pEnd, 0); pNew->pOwner = pOwner; } } @@ -2163,94 +2020,688 @@ pp = &((*pp)->pNextWin); } return pRet; } + +/* +** Return true if it can be determined at compile time that expression +** pExpr evaluates to a value that, when cast to an integer, is greater +** than zero. False otherwise. +** +** If an OOM error occurs, this function sets the Parse.db.mallocFailed +** flag and returns zero. +*/ +static int windowExprGtZero(Parse *pParse, Expr *pExpr){ + int ret = 0; + sqlite3 *db = pParse->db; + sqlite3_value *pVal = 0; + sqlite3ValueFromExpr(db, pExpr, db->enc, SQLITE_AFF_NUMERIC, &pVal); + if( pVal && sqlite3_value_int(pVal)>0 ){ + ret = 1; + } + sqlite3ValueFree(pVal); + return ret; +} /* ** sqlite3WhereBegin() has already been called for the SELECT statement ** passed as the second argument when this function is invoked. It generates -** code to populate the Window.regResult register for each window function and -** invoke the sub-routine at instruction addrGosub once for each row. -** This function calls sqlite3WhereEnd() before returning. +** code to populate the Window.regResult register for each window function +** and invoke the sub-routine at instruction addrGosub once for each row. +** sqlite3WhereEnd() is always called before returning. +** +** This function handles several different types of window frames, which +** require slightly different processing. The following pseudo code is +** used to implement window frames of the form: +** +** ROWS BETWEEN PRECEDING AND FOLLOWING +** +** Other window frame types use variants of the following: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** +** if( first row of partition ){ +** // Rewind three cursors, all open on the eph table. +** Rewind(csrEnd); +** Rewind(csrStart); +** Rewind(csrCurrent); +** +** regEnd = // FOLLOWING expression +** regStart = // PRECEDING expression +** }else{ +** // First time this branch is taken, the eph table contains two +** // rows. The first row in the partition, which all three cursors +** // currently point to, and the following row. +** AGGSTEP +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** if( (regStart--)<=0 ){ +** AggInverse(csrStart) +** Next(csrStart) +** } +** } +** +** The pseudo-code above uses the following shorthand: +** +** AGGSTEP: invoke the aggregate xStep() function for each window function +** with arguments read from the current row of cursor csrEnd, then +** step cursor csrEnd forward one row (i.e. sqlite3BtreeNext()). +** +** RETURN_ROW: return a row to the caller based on the contents of the +** current row of csrCurrent and the current state of all +** aggregates. Then step cursor csrCurrent forward one row. +** +** AGGINVERSE: invoke the aggregate xInverse() function for each window +** functions with arguments read from the current row of cursor +** csrStart. Then step csrStart forward one row. +** +** There are two other ROWS window frames that are handled significantly +** differently from the above - "BETWEEN PRECEDING AND PRECEDING" +** and "BETWEEN FOLLOWING AND FOLLOWING". These are special +** cases because they change the order in which the three cursors (csrStart, +** csrCurrent and csrEnd) iterate through the ephemeral table. Cases that +** use UNBOUNDED or CURRENT ROW are much simpler variations on one of these +** three. +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** if( (regEnd--)<=0 ){ +** AGGSTEP +** } +** RETURN_ROW +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** flush: +** if( (regEnd--)<=0 ){ +** AGGSTEP +** } +** RETURN_ROW +** +** +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = regEnd - +** }else{ +** AGGSTEP +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** } +** if( (regStart--)<=0 ){ +** AGGINVERSE +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** if( (regEnd--)<=0 ){ +** RETURN_ROW +** if( eof ) break; +** } +** if( (regStart--)<=0 ){ +** AGGINVERSE +** if( eof ) break +** } +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** For the most part, the patterns above are adapted to support UNBOUNDED by +** assuming that it is equivalent to "infinity PRECEDING/FOLLOWING" and +** CURRENT ROW by assuming that it is equivilent to "0 PRECEDING/FOLLOWING". +** This is optimized of course - branches that will never be taken and +** conditions that are always true are omitted from the VM code. The only +** exceptional case is: +** +** ROWS BETWEEN FOLLOWING AND UNBOUNDED FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regStart = +** }else{ +** AGGSTEP +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** if( (regStart--)<=0 ){ +** AGGINVERSE +** if( eof ) break +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** Also requiring special handling are the cases: +** +** ROWS BETWEEN PRECEDING AND PRECEDING +** ROWS BETWEEN FOLLOWING AND FOLLOWING +** +** when (expr1 < expr2). This is detected at runtime, not by this function. +** To handle this case, the pseudo-code programs depicted above are modified +** slightly to be: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** if( regEnd < regStart ){ +** RETURN_ROW +** delete eph table contents +** continue +** } +** ... +** +** The new "continue" statement in the above jumps to the next iteration +** of the outer loop - the one started by sqlite3WhereBegin(). +** +** The various GROUPS cases are implemented using the same patterns as +** ROWS. The VM code is modified slightly so that: +** +** 1. The else branch in the main loop is only taken if the row just +** added to the ephemeral table is the start of a new group. In +** other words, it becomes: +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else if( new group ){ +** ... +** } +** } +** +** 2. Instead of processing a single row, each RETURN_ROW, AGGSTEP or +** AGGINVERSE step processes the current row of the relevant cursor and +** all subsequent rows belonging to the same group. +** +** RANGE window frames are a little different again. As for GROUPS, the +** main loop runs once per group only. And RETURN_ROW, AGGSTEP and AGGINVERSE +** deal in groups instead of rows. As for ROWS and GROUPS, there are three +** basic cases: +** +** RANGE BETWEEN PRECEDING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** RETURN_ROW +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** RETURN ROW +** if( csrCurrent is EOF ) break; +** while( csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** } +** } +** +** In the above notation, "csr.key" means the current value of the ORDER BY +** expression (there is only ever 1 for a RANGE that uses an FOLLOWING +** or PRECEDING AND PRECEDING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** if( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** flush: +** while( (csrEnd.key + regEnd) <= csrCurrent.key ){ +** AGGSTEP +** } +** while( (csrStart.key + regStart) < csrCurrent.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** +** RANGE BETWEEN FOLLOWING AND FOLLOWING +** +** ... loop started by sqlite3WhereBegin() ... +** if( new partition ){ +** Gosub flush +** } +** Insert new row into eph table. +** if( first row of partition ){ +** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent) +** regEnd = +** regStart = +** }else{ +** AGGSTEP +** while( (csrCurrent.key + regEnd) < csrEnd.key ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** } +** RETURN_ROW +** } +** } +** } +** flush: +** AGGSTEP +** while( 1 ){ +** while( (csrCurrent.key + regStart) > csrStart.key ){ +** AGGINVERSE +** if( eof ) break "while( 1 )" loop. +** } +** RETURN_ROW +** } +** while( !eof csrCurrent ){ +** RETURN_ROW +** } +** +** The text above leaves out many details. Refer to the code and comments +** below for a more complete picture. */ void sqlite3WindowCodeStep( Parse *pParse, /* Parse context */ Select *p, /* Rewritten SELECT statement */ WhereInfo *pWInfo, /* Context returned by sqlite3WhereBegin() */ int regGosub, /* Register for OP_Gosub */ int addrGosub /* OP_Gosub here to return each row */ ){ Window *pMWin = p->pWin; - - /* There are three different functions that may be used to do the work - ** of this one, depending on the window frame and the specific built-in - ** window functions used (if any). - ** - ** windowCodeRowExprStep() handles all "ROWS" window frames, except for: - ** - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** - ** The exception is because windowCodeRowExprStep() implements all window - ** frame types by caching the entire partition in a temp table, and - ** "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW" is easy enough to - ** implement without such a cache. - ** - ** windowCodeCacheStep() is used for: - ** - ** RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING - ** - ** It is also used for anything not handled by windowCodeRowExprStep() - ** that invokes a built-in window function that requires the entire - ** partition to be cached in a temp table before any rows are returned - ** (e.g. nth_value() or percent_rank()). - ** - ** Finally, assuming there is no built-in window function that requires - ** the partition to be cached, windowCodeDefaultStep() is used for: - ** - ** RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING - ** RANGE BETWEEN CURRENT ROW AND CURRENT ROW - ** ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW - ** - ** windowCodeDefaultStep() is the only one of the three functions that - ** does not cache each partition in a temp table before beginning to - ** return rows. - */ - if( pMWin->eType==TK_ROWS - && (pMWin->eStart!=TK_UNBOUNDED||pMWin->eEnd!=TK_CURRENT||!pMWin->pOrderBy) - ){ - VdbeModuleComment((pParse->pVdbe, "Begin RowExprStep()")); - windowCodeRowExprStep(pParse, p, pWInfo, regGosub, addrGosub); - }else{ - Window *pWin; - int bCache = 0; /* True to use CacheStep() */ - - if( pMWin->eStart==TK_CURRENT && pMWin->eEnd==TK_UNBOUNDED ){ - bCache = 1; - }else{ - for(pWin=pMWin; pWin; pWin=pWin->pNextWin){ - FuncDef *pFunc = pWin->pFunc; - if( (pFunc->funcFlags & SQLITE_FUNC_WINDOW_SIZE) - || (pFunc->zName==nth_valueName) - || (pFunc->zName==first_valueName) - || (pFunc->zName==leadName) - || (pFunc->zName==lagName) - ){ - bCache = 1; - break; - } - } - } - - /* Otherwise, call windowCodeDefaultStep(). */ - if( bCache ){ - VdbeModuleComment((pParse->pVdbe, "Begin CacheStep()")); - windowCodeCacheStep(pParse, p, pWInfo, regGosub, addrGosub); - }else{ - VdbeModuleComment((pParse->pVdbe, "Begin DefaultStep()")); - windowCodeDefaultStep(pParse, p, pWInfo, regGosub, addrGosub); - } + ExprList *pOrderBy = pMWin->pOrderBy; + Vdbe *v = sqlite3GetVdbe(pParse); + int csrWrite; /* Cursor used to write to eph. table */ + int csrInput = p->pSrc->a[0].iCursor; /* Cursor of sub-select */ + int nInput = p->pSrc->a[0].pTab->nCol; /* Number of cols returned by sub */ + int iInput; /* To iterate through sub cols */ + int addrNe; /* Address of OP_Ne */ + int addrGosubFlush = 0; /* Address of OP_Gosub to flush: */ + int addrInteger = 0; /* Address of OP_Integer */ + int addrEmpty; /* Address of OP_Rewind in flush: */ + int regStart = 0; /* Value of PRECEDING */ + int regEnd = 0; /* Value of FOLLOWING */ + int regNew; /* Array of registers holding new input row */ + int regRecord; /* regNew array in record form */ + int regRowid; /* Rowid for regRecord in eph table */ + int regNewPeer = 0; /* Peer values for new row (part of regNew) */ + int regPeer = 0; /* Peer values for current row */ + int regFlushPart = 0; /* Register for "Gosub flush_partition" */ + WindowCodeArg s; /* Context object for sub-routines */ + int lblWhereEnd; /* Label just before sqlite3WhereEnd() code */ + + assert( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_CURRENT + || pMWin->eStart==TK_FOLLOWING || pMWin->eStart==TK_UNBOUNDED + ); + assert( pMWin->eEnd==TK_FOLLOWING || pMWin->eEnd==TK_CURRENT + || pMWin->eEnd==TK_UNBOUNDED || pMWin->eEnd==TK_PRECEDING + ); + assert( pMWin->eExclude==0 || pMWin->eExclude==TK_CURRENT + || pMWin->eExclude==TK_GROUP || pMWin->eExclude==TK_TIES + || pMWin->eExclude==TK_NO + ); + + lblWhereEnd = sqlite3VdbeMakeLabel(pParse); + + /* Fill in the context object */ + memset(&s, 0, sizeof(WindowCodeArg)); + s.pParse = pParse; + s.pMWin = pMWin; + s.pVdbe = v; + s.regGosub = regGosub; + s.addrGosub = addrGosub; + s.current.csr = pMWin->iEphCsr; + csrWrite = s.current.csr+1; + s.start.csr = s.current.csr+2; + s.end.csr = s.current.csr+3; + + /* Figure out when rows may be deleted from the ephemeral table. There + ** are four options - they may never be deleted (eDelete==0), they may + ** be deleted as soon as they are no longer part of the window frame + ** (eDelete==WINDOW_AGGINVERSE), they may be deleted as after the row + ** has been returned to the caller (WINDOW_RETURN_ROW), or they may + ** be deleted after they enter the frame (WINDOW_AGGSTEP). */ + switch( pMWin->eStart ){ + case TK_FOLLOWING: + if( pMWin->eFrmType!=TK_RANGE + && windowExprGtZero(pParse, pMWin->pStart) + ){ + s.eDelete = WINDOW_RETURN_ROW; + } + break; + case TK_UNBOUNDED: + if( windowCacheFrame(pMWin)==0 ){ + if( pMWin->eEnd==TK_PRECEDING ){ + if( pMWin->eFrmType!=TK_RANGE + && windowExprGtZero(pParse, pMWin->pEnd) + ){ + s.eDelete = WINDOW_AGGSTEP; + } + }else{ + s.eDelete = WINDOW_RETURN_ROW; + } + } + break; + default: + s.eDelete = WINDOW_AGGINVERSE; + break; + } + + /* Allocate registers for the array of values from the sub-query, the + ** samve values in record form, and the rowid used to insert said record + ** into the ephemeral table. */ + regNew = pParse->nMem+1; + pParse->nMem += nInput; + regRecord = ++pParse->nMem; + regRowid = ++pParse->nMem; + + /* If the window frame contains an " PRECEDING" or " FOLLOWING" + ** clause, allocate registers to store the results of evaluating each + ** . */ + if( pMWin->eStart==TK_PRECEDING || pMWin->eStart==TK_FOLLOWING ){ + regStart = ++pParse->nMem; + } + if( pMWin->eEnd==TK_PRECEDING || pMWin->eEnd==TK_FOLLOWING ){ + regEnd = ++pParse->nMem; + } + + /* If this is not a "ROWS BETWEEN ..." frame, then allocate arrays of + ** registers to store copies of the ORDER BY expressions (peer values) + ** for the main loop, and for each cursor (start, current and end). */ + if( pMWin->eFrmType!=TK_ROWS ){ + int nPeer = (pOrderBy ? pOrderBy->nExpr : 0); + regNewPeer = regNew + pMWin->nBufferCol; + if( pMWin->pPartition ) regNewPeer += pMWin->pPartition->nExpr; + regPeer = pParse->nMem+1; pParse->nMem += nPeer; + s.start.reg = pParse->nMem+1; pParse->nMem += nPeer; + s.current.reg = pParse->nMem+1; pParse->nMem += nPeer; + s.end.reg = pParse->nMem+1; pParse->nMem += nPeer; + } + + /* Load the column values for the row returned by the sub-select + ** into an array of registers starting at regNew. Assemble them into + ** a record in register regRecord. */ + for(iInput=0; iInputpPartition ){ + int addr; + ExprList *pPart = pMWin->pPartition; + int nPart = pPart->nExpr; + int regNewPart = regNew + pMWin->nBufferCol; + KeyInfo *pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pPart, 0, 0); + + regFlushPart = ++pParse->nMem; + addr = sqlite3VdbeAddOp3(v, OP_Compare, regNewPart, pMWin->regPart, nPart); + sqlite3VdbeAppendP4(v, (void*)pKeyInfo, P4_KEYINFO); + sqlite3VdbeAddOp3(v, OP_Jump, addr+2, addr+4, addr+2); + VdbeCoverageEqNe(v); + addrGosubFlush = sqlite3VdbeAddOp1(v, OP_Gosub, regFlushPart); + VdbeComment((v, "call flush_partition")); + sqlite3VdbeAddOp3(v, OP_Copy, regNewPart, pMWin->regPart, nPart-1); + } + + /* Insert the new row into the ephemeral table */ + sqlite3VdbeAddOp2(v, OP_NewRowid, csrWrite, regRowid); + sqlite3VdbeAddOp3(v, OP_Insert, csrWrite, regRecord, regRowid); + addrNe = sqlite3VdbeAddOp3(v, OP_Ne, pMWin->regOne, 0, regRowid); + VdbeCoverageNeverNull(v); + + /* This block is run for the first row of each partition */ + s.regArg = windowInitAccum(pParse, pMWin); + + if( regStart ){ + sqlite3ExprCode(pParse, pMWin->pStart, regStart); + windowCheckValue(pParse, regStart, 0 + (pMWin->eFrmType==TK_RANGE ? 3 : 0)); + } + if( regEnd ){ + sqlite3ExprCode(pParse, pMWin->pEnd, regEnd); + windowCheckValue(pParse, regEnd, 1 + (pMWin->eFrmType==TK_RANGE ? 3 : 0)); + } + + if( pMWin->eStart==pMWin->eEnd && regStart ){ + int op = ((pMWin->eStart==TK_FOLLOWING) ? OP_Ge : OP_Le); + int addrGe = sqlite3VdbeAddOp3(v, op, regStart, 0, regEnd); + VdbeCoverageNeverNullIf(v, op==OP_Ge); /* NeverNull because bound */ + VdbeCoverageNeverNullIf(v, op==OP_Le); /* values previously checked */ + windowAggFinal(&s, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); + VdbeCoverageNeverTaken(v); + windowReturnOneRow(&s); + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); + sqlite3VdbeJumpHere(v, addrGe); + } + if( pMWin->eStart==TK_FOLLOWING && pMWin->eFrmType!=TK_RANGE && regEnd ){ + assert( pMWin->eEnd==TK_FOLLOWING ); + sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regStart); + } + + if( pMWin->eStart!=TK_UNBOUNDED ){ + sqlite3VdbeAddOp2(v, OP_Rewind, s.start.csr, 1); + VdbeCoverageNeverTaken(v); + } + sqlite3VdbeAddOp2(v, OP_Rewind, s.current.csr, 1); + VdbeCoverageNeverTaken(v); + sqlite3VdbeAddOp2(v, OP_Rewind, s.end.csr, 1); + VdbeCoverageNeverTaken(v); + if( regPeer && pOrderBy ){ + sqlite3VdbeAddOp3(v, OP_Copy, regNewPeer, regPeer, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.start.reg, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.current.reg, pOrderBy->nExpr-1); + sqlite3VdbeAddOp3(v, OP_Copy, regPeer, s.end.reg, pOrderBy->nExpr-1); + } + + sqlite3VdbeAddOp2(v, OP_Goto, 0, lblWhereEnd); + + sqlite3VdbeJumpHere(v, addrNe); + + /* Beginning of the block executed for the second and subsequent rows. */ + if( regPeer ){ + windowIfNewPeer(pParse, pOrderBy, regNewPeer, regPeer, lblWhereEnd); + } + if( pMWin->eStart==TK_FOLLOWING ){ + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eEnd!=TK_UNBOUNDED ){ + if( pMWin->eFrmType==TK_RANGE ){ + int lbl = sqlite3VdbeMakeLabel(pParse); + int addrNext = sqlite3VdbeCurrentAddr(v); + windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrNext); + sqlite3VdbeResolveLabel(v, lbl); + }else{ + windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + } + } + }else + if( pMWin->eEnd==TK_PRECEDING ){ + int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE); + windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); + if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + if( !bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + }else{ + int addr = 0; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eEnd!=TK_UNBOUNDED ){ + if( pMWin->eFrmType==TK_RANGE ){ + int lbl = 0; + addr = sqlite3VdbeCurrentAddr(v); + if( regEnd ){ + lbl = sqlite3VdbeMakeLabel(pParse); + windowCodeRangeTest(&s, OP_Ge, s.current.csr, regEnd, s.end.csr, lbl); + } + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + if( regEnd ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); + sqlite3VdbeResolveLabel(v, lbl); + } + }else{ + if( regEnd ){ + addr = sqlite3VdbeAddOp3(v, OP_IfPos, regEnd, 0, 1); + VdbeCoverage(v); + } + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + if( regEnd ) sqlite3VdbeJumpHere(v, addr); + } + } + } + + /* End of the main input loop */ + sqlite3VdbeResolveLabel(v, lblWhereEnd); + sqlite3WhereEnd(pWInfo); + + /* Fall through */ + if( pMWin->pPartition ){ + addrInteger = sqlite3VdbeAddOp2(v, OP_Integer, 0, regFlushPart); + sqlite3VdbeJumpHere(v, addrGosubFlush); + } + + addrEmpty = sqlite3VdbeAddOp1(v, OP_Rewind, csrWrite); + VdbeCoverage(v); + if( pMWin->eEnd==TK_PRECEDING ){ + int bRPS = (pMWin->eStart==TK_PRECEDING && pMWin->eFrmType==TK_RANGE); + windowCodeOp(&s, WINDOW_AGGSTEP, regEnd, 0); + if( bRPS ) windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 0); + }else if( pMWin->eStart==TK_FOLLOWING ){ + int addrStart; + int addrBreak1; + int addrBreak2; + int addrBreak3; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + if( pMWin->eFrmType==TK_RANGE ){ + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + }else + if( pMWin->eEnd==TK_UNBOUNDED ){ + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1); + }else{ + assert( pMWin->eEnd==TK_FOLLOWING ); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1); + addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1); + } + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak2); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak3 = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak1); + sqlite3VdbeJumpHere(v, addrBreak3); + }else{ + int addrBreak; + int addrStart; + windowCodeOp(&s, WINDOW_AGGSTEP, 0, 0); + addrStart = sqlite3VdbeCurrentAddr(v); + addrBreak = windowCodeOp(&s, WINDOW_RETURN_ROW, 0, 1); + windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 0); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart); + sqlite3VdbeJumpHere(v, addrBreak); + } + sqlite3VdbeJumpHere(v, addrEmpty); + + sqlite3VdbeAddOp1(v, OP_ResetSorter, s.current.csr); + if( pMWin->pPartition ){ + if( pMWin->regStartRowid ){ + sqlite3VdbeAddOp2(v, OP_Integer, 1, pMWin->regStartRowid); + sqlite3VdbeAddOp2(v, OP_Integer, 0, pMWin->regEndRowid); + } + sqlite3VdbeChangeP1(v, addrInteger, sqlite3VdbeCurrentAddr(v)); + sqlite3VdbeAddOp1(v, OP_Return, regFlushPart); } } #endif /* SQLITE_OMIT_WINDOWFUNC */ Index: test/altertab3.test ================================================================== --- test/altertab3.test +++ test/altertab3.test @@ -97,12 +97,13 @@ } {1 {error in trigger tr1: no such table: main.t2}} do_execsql_test 4.1.2 { COMMIT; } do_execsql_test 4.1.3 { - SELECT * FROM sqlite_master WHERE type='table' AND name!='t1'; -} {table t3 t3 3 {CREATE TABLE t3(e, f)}} + SELECT type, name, tbl_name, sql + FROM sqlite_master WHERE type='table' AND name!='t1'; +} {table t3 t3 {CREATE TABLE t3(e, f)}} do_catchsql_test 4.2.1 { BEGIN; ALTER TABLE t3 RENAME e TO eee; @@ -109,12 +110,13 @@ } {1 {error in trigger tr1: no such table: main.t2}} do_execsql_test 4.2.2 { COMMIT; } do_execsql_test 4.2.3 { - SELECT * FROM sqlite_master WHERE type='table' AND name!='t1'; -} {table t3 t3 3 {CREATE TABLE t3(e, f)}} + SELECT type, name, tbl_name, sql + FROM sqlite_master WHERE type='table' AND name!='t1'; +} {table t3 t3 {CREATE TABLE t3(e, f)}} #------------------------------------------------------------------------- reset_db do_execsql_test 5.0 { CREATE TABLE t1 ( Index: test/autoinc.test ================================================================== --- test/autoinc.test +++ test/autoinc.test @@ -14,10 +14,11 @@ # $Id: autoinc.test,v 1.14 2009/06/23 20:28:54 drh Exp $ # set testdir [file dirname $argv0] source $testdir/tester.tcl +set testprefix autoinc # If the library is not compiled with autoincrement support then # skip all tests in this file. # ifcapable {!autoinc} { @@ -853,7 +854,30 @@ INSERT INTO t1(b) VALUES('five'); PRAGMA integrity_check; }} msg] lappend res $msg } {0 ok} + +#-------------------------------------------------------------------------- +reset_db +do_execsql_test 13.0 { + CREATE TABLE t1(i INTEGER PRIMARY KEY AUTOINCREMENT, j); + CREATE TABLE t2(i INTEGER PRIMARY KEY AUTOINCREMENT, j); + CREATE TABLE t3(i INTEGER PRIMARY KEY AUTOINCREMENT, j); + + INSERT INTO t1 VALUES(NULL, 1); + INSERT INTO t2 VALUES(NULL, 2); + INSERT INTO t3 VALUES(NULL, 3); + + SELECT name FROM sqlite_sequence; +} {t1 t2 t3} + +do_execsql_test 13.1 { + UPDATE sqlite_sequence SET name=NULL WHERE name='t2'; + INSERT INTO t3 VALUES(NULL, 4); + DELETE FROM t3; + INSERT INTO t3 VALUES(NULL, 5); + SELECT * FROM t3; +} {3 5} + finish_test Index: test/badutf2.test ================================================================== --- test/badutf2.test +++ test/badutf2.test @@ -96,16 +96,22 @@ set res [ sqlite3_exec db $sql ] lindex [ lindex $res 1] 1 } $uval } - do_test badutf2-4.1.$i { - sqlite3_reset $S - sqlite3_bind_text $S 1 $xstr $len - sqlite3_step $S - utf8_to_ustr2 [ sqlite3_column_text $S 0 ] - } $ustr + # Tcl 8.7 and later do automatic bad-utf8 correction for + # characters 0x80 thru 0x9f so test case 5 does not work here. + if {$i==5 && $tcl_version>=8.7} { + # no-op + } else { + do_test badutf2-4.1.$i { + sqlite3_reset $S + sqlite3_bind_text $S 1 $xstr $len + sqlite3_step $S + utf8_to_ustr2 [ sqlite3_column_text $S 0 ] + } $ustr + } ifcapable debug { do_test badutf2-5.1.$i { utf8_to_utf8 $uval } $u2u Index: test/bestindex1.test ================================================================== --- test/bestindex1.test +++ test/bestindex1.test @@ -263,8 +263,65 @@ 1 0 ValueA 1 0 ValueA 2 0 ValueA 2 0 ValueA 3 0 ValueB 3 0 ValueB 4 0 ValueB 4 0 ValueB } + +#------------------------------------------------------------------------- +# If there is an IN(..) condition in the WHERE clause of a query on a +# virtual table, the xBestIndex method is first invoked with the IN(...) +# represented by a "usable" SQLITE_INDEX_CONSTRAINT_EQ constraint. If +# the virtual table elects to use the IN(...) constraint, then the +# xBestIndex method is invoked again, this time with the IN(...) marked +# as "not usable". Depending on the relative costs of the two plans as +# defined by the virtual table implementation, and the cardinality of the +# IN(...) operator, SQLite chooses the most efficient plan. +# +# At one point the second invocation of xBestIndex() was only being made +# for join queries. The following tests check that this problem has been +# fixed. +# +proc vtab_command {method args} { + switch -- $method { + xConnect { + return "CREATE TABLE t1(a, b, c, d)" + } + + xBestIndex { + set clist [lindex $args 0] + lappend ::bestindex_calls $clist + set ret "cost 1000000 idxnum 555" + for {set i 0} {$i < [llength $clist]} {incr i} { + array set C [lindex $clist $i] + if {$C(usable)} { lappend ret use $i } + } + return $ret + } + } + return {} +} + +do_execsql_test 4.0 { + CREATE VIRTUAL TABLE x1 USING tcl(vtab_command); +} {} + +do_test 4.1 { + set ::bestindex_calls [list] + execsql { + SELECT * FROM x1 WHERE a=? AND b BETWEEN ? AND ? AND c IN (1, 2, 3, 4); + } + set ::bestindex_calls +} [list \ + [list {op eq column 0 usable 1} \ + {op eq column 2 usable 1} \ + {op ge column 1 usable 1} \ + {op le column 1 usable 1} \ + ] \ + [list {op eq column 0 usable 1} \ + {op eq column 2 usable 0} \ + {op ge column 1 usable 1} \ + {op le column 1 usable 1} + ] +] finish_test Index: test/corruptC.test ================================================================== --- test/corruptC.test +++ test/corruptC.test @@ -95,11 +95,12 @@ # insert corrupt byte(s) hexio_write test.db 2053 [format %02x 0x04] sqlite3 db test.db catchsql {PRAGMA integrity_check} -} {1 {database disk image is malformed}} +} {0 {{*** in database main *** +Page 3: free space corruption}}} # test that a corrupt content offset size is handled (seed 5649) # # Update 2016-12-27: As of check-in [0b86fbca66] "In sqlite3BtreeInsert() when # replacing a re-existing row, try to overwrite the cell directly rather than Index: test/createtab.test ================================================================== --- test/createtab.test +++ test/createtab.test @@ -10,11 +10,10 @@ #*********************************************************************** # This file implements regression tests for SQLite library. The # focus of this file is testing that it is OK to create new tables # and indices while creating existing tables and indices. # -# $Id: createtab.test,v 1.3 2007/09/12 17:01:45 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl ifcapable autovacuum { @@ -140,7 +139,16 @@ } } {t1 t2 t3 t4} integrity_check createtab-$av.40 } + +# 2019-03-31 Ensure that a proper error is returned for an index +# with too many columns. +# +do_test createtab-3.1 { + db eval {DROP TABLE IF EXISTS t1;} + set sql "CREATE TABLE t1(x,UNIQUE(x[string repeat ,x 100000]))" + catchsql $sql +} {1 {too many columns in index}} finish_test Index: test/dbfuzz001.test ================================================================== --- test/dbfuzz001.test +++ test/dbfuzz001.test @@ -187,15 +187,13 @@ # Prior to a certain fix to sqlite3BtreeDelete() and because of the # corruption to the freeblock list on page 8, this would fail to # cause a rebalance operation, which would leave the btree in a weird # state that would lead to segfaults and or assertion faults. # -set res {0 {}} -ifcapable oversize_cell_check { set res {1 {database disk image is malformed}} } -do_catchsql_test dbfuzz001-110 { +do_execsql_test dbfuzz001-110 { DELETE FROM t3 WHERE x IS NOT NULL AND +rowid=6; -} $res +} {} # This is a dbfuzz2-generate test case that can cause a page with # pPage->nCell==0 to enter the balancer. # do_test dbfuzz001-200 { Index: test/delete4.test ================================================================== --- test/delete4.test +++ test/delete4.test @@ -180,8 +180,85 @@ CREATE TABLE t2(x INT); INSERT INTO t2(x) VALUES(1),(2),(3),(4),(5); DELETE FROM t2 WHERE EXISTS(SELECT 1 FROM t2 AS v WHERE v.x=t2.x+1); SELECT x FROM t2; } {5} + +#------------------------------------------------------------------------- +# Test the effect of failing to find a table row based on an index key +# within a DELETE. Either because the db is corrupt, or a trigger on another +# row already deleted the entry, or because a BEFORE trigger on the current +# row has already deleted it. +# +do_execsql_test 7.1.0 { + CREATE TABLE t3(id INT PRIMARY KEY, a, b) WITHOUT ROWID; + CREATE INDEX t3a ON t3(a); + CREATE INDEX t3b ON t3(b); + + INSERT INTO t3 VALUES(1, 1, 1); + INSERT INTO t3 VALUES(2, 2, 2); + INSERT INTO t3 VALUES(3, 3, 3); + INSERT INTO t3 VALUES(4, 4, 1); +} +do_execsql_test 7.1.1 { + DELETE FROM t3 WHERE a=4 OR b=1; +} +do_execsql_test 7.1.2 { + SELECT * FROM t3; +} { 2 2 2 3 3 3 } + +do_execsql_test 7.2.0 { + CREATE TABLE t4(a PRIMARY KEY, b) WITHOUT ROWID; + CREATE INDEX t4i ON t4(b); + INSERT INTO t4 VALUES(1, 'hello'); + INSERT INTO t4 VALUES(2, 'world'); + + CREATE TABLE t5(a PRIMARY KEY, b) WITHOUT ROWID; + CREATE INDEX t5i ON t5(b); + INSERT INTO t5 VALUES(1, 'hello'); + INSERT INTO t5 VALUES(3, 'world'); + + PRAGMA writable_schema = 1; + UPDATE sqlite_master SET rootpage = ( + SELECT rootpage FROM sqlite_master WHERE name = 't5' + ) WHERE name = 't4'; +} + +db close +sqlite3 db test.db +do_execsql_test 7.2.1 { + DELETE FROM t4 WHERE b='world' +} +reset_db + +do_execsql_test 7.3.0 { + CREATE TABLE t3(id INT PRIMARY KEY, a, b) WITHOUT ROWID; + INSERT INTO t3 VALUES(1, 2, 3); + INSERT INTO t3 VALUES(4, 5, 6); + INSERT INTO t3 VALUES(7, 8, 9); + CREATE TRIGGER t3t BEFORE DELETE ON t3 BEGIN + DELETE FROM t3 WHERE id=old.id+3; + END; +} + +do_execsql_test 7.3.1 { + DELETE FROM t3 WHERE a IN(2, 5, 8); + SELECT * FROM t3; +} {} + +do_execsql_test 7.3.2 { + DROP TRIGGER t3t; + INSERT INTO t3 VALUES(1, 2, 3); + INSERT INTO t3 VALUES(4, 5, 6); + INSERT INTO t3 VALUES(7, 8, 9); + CREATE TRIGGER t3t BEFORE DELETE ON t3 BEGIN + DELETE FROM t3 WHERE id=old.id; + END; +} + +do_execsql_test 7.3.3 { + DELETE FROM t3 WHERE a IN(2, 5, 8); + SELECT * FROM t3; +} {} finish_test Index: test/e_vacuum.test ================================================================== --- test/e_vacuum.test +++ test/e_vacuum.test @@ -230,13 +230,13 @@ SELECT rowid, x FROM t4; } {1 x 3 z} do_execsql_test e_vacuum-3.1.2 { VACUUM; SELECT rowid, x FROM t4; -} {1 x 3 z} -# Was: {1 x 2 z} +} {1 x 2 z} +# Rowids are preserved if an INTEGER PRIMARY KEY is used do_execsql_test e_vacuum-3.1.3 { CREATE TABLE t5(x, y INTEGER PRIMARY KEY); INSERT INTO t5(x) VALUES('x'); INSERT INTO t5(x) VALUES('y'); INSERT INTO t5(x) VALUES('z'); @@ -246,13 +246,51 @@ do_execsql_test e_vacuum-3.1.4 { VACUUM; SELECT rowid, x FROM t5; } {1 x 3 z} -# EVIDENCE-OF: R-49563-33883 A VACUUM will fail if there is an open -# transaction, or if there are one or more active SQL statements when it -# is run. +# Rowid is preserved for VACUUM INTO +do_execsql_test e_vacuum-3.1.5 { + DROP TABLE t5; + CREATE TABLE t5(x); + INSERT INTO t5(x) VALUES('x'); + INSERT INTO t5(x) VALUES('y'); + INSERT INTO t5(x) VALUES('z'); + DELETE FROM t5 WHERE x = 'y'; + SELECT rowid, x FROM t5; +} {1 x 3 z} +forcedelete test2.db +do_execsql_test e_vacuum-3.1.6 { + VACUUM INTO 'test2.db'; + ATTACH 'test2.db' AS aux1; + SELECT rowid, x FROM aux1.t5; + DETACH aux1; +} {1 x 3 z} + +# Rowids are not renumbered if the table being vacuumed +# has indexes. +do_execsql_test e_vacuum-3.1.7 { + DROP TABLE t5; + CREATE TABLE t5(x,y,z); + INSERT INTO t5(x) VALUES('x'); + INSERT INTO t5(x) VALUES('y'); + INSERT INTO t5(x) VALUES('z'); + UPDATE t5 SET y=x, z=random(); + DELETE FROM t5 WHERE x = 'y'; + CREATE INDEX t5x ON t5(x); + CREATE UNIQUE INDEX t5y ON t5(y); + CREATE INDEX t5zxy ON t5(z,x,y); + SELECT rowid, x FROM t5; +} {1 x 3 z} +do_execsql_test e_vacuum-3.1.8 { + VACUUM; + SELECT rowid, x FROM t5; +} {1 x 3 z} + +# EVIDENCE-OF: R-12218-18073 A VACUUM will fail if there is an open +# transaction on the database connection that is attempting to run the +# VACUUM. # do_execsql_test e_vacuum-3.2.1.1 { BEGIN } {} do_catchsql_test e_vacuum-3.2.1.2 { VACUUM } {1 {cannot VACUUM from within a transaction}} Index: test/fts3atoken.test ================================================================== --- test/fts3atoken.test +++ test/fts3atoken.test @@ -84,15 +84,52 @@ INSERT INTO t1(content) VALUES('That the colt from ol regret had got'); SELECT content FROM t1 WHERE content MATCH 'movement' } } {{There was movement at the station}} +unset -nocomplain simple blah2name simplename +set simplename "simple" +set blah2name "blah2" +set simple [db one {SELECT fts3_tokenizer('simple')}] sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 0 do_catchsql_test 1.6 { SELECT fts3_tokenizer('blah', fts3_tokenizer('simple')) IS NULL; } {1 {fts3tokenize disabled}} +do_test fts3atoken-1.7 { + execsql { + SELECT fts3_tokenizer('blah2', $simple) IS NULL; + } +} {1} + +# With ENABLE_FTS3_TOKENIZER off, the fts3_tokenzer(1) function +# returns NULL unless the first parameter is a bound parameter. +# If the first parameter is a bound parameter, then fts3_tokenizer(1) +# returns the actual pointer value as a BLOB. +# +do_test fts3atoken-1.8 { + execsql { + SELECT fts3_tokenizer($blah2name) == fts3_tokenizer($simplename), + typeof(fts3_tokenizer($blah2name)), + typeof(fts3_tokenizer('blah2')), + typeof(fts3_tokenizer($simplename)), + typeof(fts3_tokenizer('simple')); + } +} {1 blob null blob null} +# With ENABLE_FTS3_TOKENIZER on, fts3_tokenizer() always returns +# the BLOB pointer, regardless the parameter +# +sqlite3_db_config db SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1 +do_test fts3atoken-1.9 { + execsql { + SELECT fts3_tokenizer('blah2') == fts3_tokenizer('simple'), + typeof(fts3_tokenizer($blah2name)), + typeof(fts3_tokenizer('blah2')), + typeof(fts3_tokenizer($simplename)), + typeof(fts3_tokenizer('simple')); + } +} {1 blob blob blob blob} #-------------------------------------------------------------------------- # Test cases fts3atoken-2.* test error cases in the scalar function based # API for getting and setting tokenizers. # Index: test/fts3varint.test ================================================================== --- test/fts3varint.test +++ test/fts3varint.test @@ -108,11 +108,14 @@ 1152921504606846975 1152921504606846976 1152921504606846977 } do_fts3_varint_test 2.61 { 2305843009213693951 2305843009213693952 2305843009213693953 } do_fts3_varint_test 2.62 { 4611686018427387903 4611686018427387904 4611686018427387905 } -do_fts3_varint_test 2.63 { - 9223372036854775807 9223372036854775808 9223372036854775809 } + +if {![catch {fts3_test_varint 18446744073709551615}]} { + do_fts3_varint_test 2.63 { + 9223372036854775807 9223372036854775808 9223372036854775809 } -do_fts3_varint_test 3.0 { 18446744073709551615 -18446744073709551615 } + do_fts3_varint_test 3.0 { 18446744073709551615 -18446744073709551615 } +} finish_test Index: test/func.test ================================================================== --- test/func.test +++ test/func.test @@ -1389,6 +1389,34 @@ # Test char(). # do_execsql_test func-31.1 { SELECT char(), length(char()), typeof(char()) } {{} 0 text} + +# sqlite3_value_frombind() +# +do_execsql_test func-32.100 { + SELECT test_frombind(1,2,3,4); +} {0} +do_execsql_test func-32.110 { + SELECT test_frombind(1,2,?,4); +} {4} +do_execsql_test func-32.120 { + SELECT test_frombind(1,(?),4,?+7); +} {2} +do_execsql_test func-32.130 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a,b,c,e,f); + INSERT INTO t1 VALUES(1,2.5,'xyz',x'e0c1b2a3',null); + SELECT test_frombind(a,b,c,e,f,$xyz) FROM t1; +} {32} +do_execsql_test func-32.140 { + SELECT test_frombind(a,b,c,e,f,$xyz+f) FROM t1; +} {0} +do_execsql_test func-32.150 { + SELECT test_frombind(x.a,y.b,x.c,:123,y.e,x.f,$xyz+y.f) FROM t1 x, t1 y; +} {8} + + + + finish_test Index: test/fuzzdata8.db ================================================================== --- test/fuzzdata8.db +++ test/fuzzdata8.db cannot compute difference between binary files Index: test/in.test ================================================================== --- test/in.test +++ test/in.test @@ -711,8 +711,29 @@ WHERE x=1 ) AND t6.id IN (1,id) ); } {1 Alice} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test in-16.0 { + CREATE TABLE x1(a, b); + INSERT INTO x1(a) VALUES(1), (2), (3), (4), (5), (6); + CREATE INDEX x1i ON x1(a, b); +} + +do_execsql_test in-16.1 { + SELECT * FROM x1 + WHERE a IN (SELECT a FROM x1 WHERE (a%2)==0) + ORDER BY a DESC, b; +} {6 {} 4 {} 2 {}} + +do_execsql_test in-16.2 { + SELECT * FROM x1 + WHERE a IN (SELECT a FROM x1 WHERE (a%7)==0) + ORDER BY a DESC, b; +} {} + finish_test Index: test/insert4.test ================================================================== --- test/insert4.test +++ test/insert4.test @@ -596,7 +596,27 @@ set sqlite3_xferopt_count 0 execsql { INSERT INTO x SELECT * FROM t8 } set sqlite3_xferopt_count } {1} +#------------------------------------------------------------------------- +# xfer transfer between tables where the source has an empty partial index. +# +do_execsql_test 11.0 { + CREATE TABLE t9(a, b, c); + CREATE INDEX t9a ON t9(a); + CREATE INDEX t9b ON t9(b) WHERE c=0; + + INSERT INTO t9 VALUES(1, 1, 1); + INSERT INTO t9 VALUES(2, 2, 2); + INSERT INTO t9 VALUES(3, 3, 3); + + CREATE TABLE t10(a, b, c); + CREATE INDEX t10a ON t10(a); + CREATE INDEX t10b ON t10(b) WHERE c=0; + + INSERT INTO t10 SELECT * FROM t9; + SELECT * FROM t10; + PRAGMA integrity_check; +} {1 1 1 2 2 2 3 3 3 ok} finish_test Index: test/journal3.test ================================================================== --- test/journal3.test +++ test/journal3.test @@ -36,10 +36,13 @@ 3 00600 4 00755 } { db close #set effective [format %.5o [expr $permissions & ~$umask]] + if {$tcl_version>=8.7} { + regsub {^00} $permissions {0o} permissions + } set effective $permissions do_test journal3-1.2.$tn.1 { catch { forcedelete test.db-journal } file attributes test.db -permissions $permissions file attributes test.db -permissions Index: test/memdb1.test ================================================================== --- test/memdb1.test +++ test/memdb1.test @@ -184,21 +184,23 @@ set rc [catch {db serialize a b} msg] lappend rc $msg } {1 {wrong # args: should be "db serialize ?DATABASE?"}} #------------------------------------------------------------------------- -reset_db -do_execsql_test 700 { - CREATE TABLE t1(a, b); - PRAGMA schema_version = 0; -} -do_test 710 { - set ser [db serialize main] - db close - sqlite3 db - db deserialize main $ser - catchsql { - CREATE VIRTUAL TABLE t1 USING rtree(id, a, b, c, d); - } -} {1 {table t1 already exists}} +ifcapable vtab { + reset_db + do_execsql_test 700 { + CREATE TABLE t1(a, b); + PRAGMA schema_version = 0; + } + do_test 710 { + set ser [db serialize main] + db close + sqlite3 db + db deserialize main $ser + catchsql { + CREATE VIRTUAL TABLE t1 USING rtree(id, a, b, c, d); + } + } {1 {table t1 already exists}} +} finish_test Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -1030,17 +1030,19 @@ ] test_suite "no_optimization" -description { Run test scripts with optimizations disabled using the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) interface. -} -files { - where.test where2.test where3.test where4.test where5.test - where6.test where7.test where8.test where9.test - whereA.test whereB.test wherelimit.test - select1.test select2.test select3.test select4.test select5.test - select7.test select8.test selectA.test selectC.test -} -dbconfig { +} -files [ + test_set \ + [glob -nocomplain $::testdir/window*.test] \ + where.test where2.test where3.test where4.test where5.test \ + where6.test where7.test where8.test where9.test \ + whereA.test whereB.test wherelimit.test \ + select1.test select2.test select3.test select4.test select5.test \ + select7.test select8.test selectA.test selectC.test +] -dbconfig { optimization_control $::dbhandle all 0 } test_suite "prepare" -description { Run tests with the db connection using sqlite3_prepare() instead of _v2(). Index: test/pg_common.tcl ================================================================== --- test/pg_common.tcl +++ test/pg_common.tcl @@ -38,21 +38,30 @@ lappend lSql $frag } #puts $lSql set ret "" + set nChar 0 foreach stmt $lSql { set res [pg_exec $::db $stmt] set err [pg_result $res -error] if {$err!=""} { error $err } + for {set i 0} {$i < [pg_result $res -numTuples]} {incr i} { - if {$i==0} { - set ret [pg_result $res -getTuple 0] + set t [pg_result $res -getTuple $i] + set nNew [string length $t] + if {$nChar>0 && ($nChar+$nNew+3)>75} { + append ret "\n " + set nChar 0 } else { - append ret " [pg_result $res -getTuple $i]" + if {$nChar>0} { + append ret " " + incr nChar 3 + } } - # lappend ret {*}[pg_result $res -getTuple $i] + incr nChar $nNew + append ret $t } pg_result $res -clear } set ret @@ -59,15 +68,31 @@ } proc execsql_test {tn sql} { set res [execsql $sql] set sql [string map {string_agg group_concat} $sql] + set sql [string map [list {NULLS FIRST} {}] $sql] + set sql [string map [list {NULLS LAST} {}] $sql] puts $::fd "do_execsql_test $tn {" puts $::fd " [string trim $sql]" puts $::fd "} {$res}" puts $::fd "" } + +proc errorsql_test {tn sql} { + set rc [catch {execsql $sql} msg] + if {$rc==0} { + error "errorsql_test SQL did not cause an error!" + } + set msg [lindex [split [string trim $msg] "\n"] 0] + puts $::fd "# PG says $msg" + set sql [string map {string_agg group_concat} $sql] + puts $::fd "do_test $tn { catch { execsql {" + puts $::fd " [string trim $sql]" + puts $::fd "} } } 1" + puts $::fd "" +} # Same as [execsql_test], except coerce all results to floating point values # with two decimal points. # proc execsql_float_test {tn sql} { @@ -86,14 +111,16 @@ set myres {} foreach r [db eval {$sql}] { lappend myres [format $F [set r]] } set res2 {$res2} + set i 0 foreach r [set myres] r2 [set res2] { if {[set r]<([set r2]-$T) || [set r]>([set r2]+$T)} { error "list element [set i] does not match: got=[set r] expected=[set r2]" } + incr i } set {} {} } {} }] } Index: test/pragma.test ================================================================== --- test/pragma.test +++ test/pragma.test @@ -249,10 +249,35 @@ execsql { PRAGMA synchronous=10; PRAGMA synchronous; } } {2} + +do_execsql_test 1.15.1 { + PRAGMA default_cache_size = 0; +} +do_execsql_test 1.15.2 { + PRAGMA default_cache_size; +} $DFLT_CACHE_SZ +do_execsql_test 1.15.3 { + PRAGMA default_cache_size = -500; +} +do_execsql_test 1.15.4 { + PRAGMA default_cache_size; +} 500 +do_execsql_test 1.15.3 { + PRAGMA default_cache_size = 500; +} +do_execsql_test 1.15.4 { + PRAGMA default_cache_size; +} 500 +db close +hexio_write test.db 48 FFFFFF00 +sqlite3 db test.db +do_execsql_test 1.15.4 { + PRAGMA default_cache_size; +} 256 } ;# ifcapable pager_pragmas # Test turning "flag" pragmas on and off. # ifcapable debug { Index: test/pragma4.test ================================================================== --- test/pragma4.test +++ test/pragma4.test @@ -172,10 +172,11 @@ sqlite3 db3 test.db sqlite3 db2 test.db2 execsql { DROP INDEX i1 } db3 execsql { DROP INDEX i2 } db2 } {} +if {[permutation]=="prepare"} { catchsql { SELECT * FROM sqlite_master } } ifcapable vtab { do_execsql_test 4.3.5 { SELECT * FROM pragma_index_info('i1') } do_execsql_test 4.3.6 { SELECT * FROM pragma_index_info('i2') } } @@ -190,10 +191,13 @@ } do_test 4.4.3 { execsql { DROP INDEX i1 } db3 execsql { DROP INDEX i2 } db2 } {} +if {[permutation]=="prepare"} { + catchsql { SELECT * FROM sqlite_master, aux.sqlite_master } +} ifcapable vtab { do_execsql_test 4.4.5 { SELECT * FROM pragma_index_list('t1') } {} do_execsql_test 4.4.6 { SELECT * FROM pragma_index_list('t2') } {} } execsql {SELECT * FROM main.sqlite_master, aux.sqlite_master} @@ -214,10 +218,13 @@ } do_test 4.5.3 { execsql { DROP TABLE c1 } db3 execsql { DROP TABLE c2 } db2 } {} +if {[permutation]=="prepare"} { + catchsql { SELECT * FROM sqlite_master, aux.sqlite_master } +} ifcapable vtab { do_execsql_test 4.5.4 { SELECT * FROM pragma_foreign_key_list('c1') } do_execsql_test 4.5.5 { SELECT * FROM pragma_foreign_key_list('c2') } } execsql {SELECT * FROM main.sqlite_master, aux.sqlite_master} ADDED test/releasetest_data.tcl Index: test/releasetest_data.tcl ================================================================== --- /dev/null +++ test/releasetest_data.tcl @@ -0,0 +1,412 @@ + +# This file contains Configuration data used by "wapptest.tcl" and +# "releasetest.tcl". +# + +# Omit comments (text between # and \n) in a long multi-line string. +# +proc strip_comments {in} { + regsub -all {#[^\n]*\n} $in {} out + return $out +} + +array set ::Configs [strip_comments { + "Default" { + -O2 + --disable-amalgamation --disable-shared + --enable-session + -DSQLITE_ENABLE_DESERIALIZE + } + "Sanitize" { + CC=clang -fsanitize=undefined + -DSQLITE_ENABLE_STAT4 + --enable-session + } + "Stdcall" { + -DUSE_STDCALL=1 + -O2 + } + "Have-Not" { + # The "Have-Not" configuration sets all possible -UHAVE_feature options + # in order to verify that the code works even on platforms that lack + # these support services. + -DHAVE_FDATASYNC=0 + -DHAVE_GMTIME_R=0 + -DHAVE_ISNAN=0 + -DHAVE_LOCALTIME_R=0 + -DHAVE_LOCALTIME_S=0 + -DHAVE_MALLOC_USABLE_SIZE=0 + -DHAVE_STRCHRNUL=0 + -DHAVE_USLEEP=0 + -DHAVE_UTIME=0 + } + "Unlock-Notify" { + -O2 + -DSQLITE_ENABLE_UNLOCK_NOTIFY + -DSQLITE_THREADSAFE + -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + } + "User-Auth" { + -O2 + -DSQLITE_USER_AUTHENTICATION=1 + } + "Secure-Delete" { + -O2 + -DSQLITE_SECURE_DELETE=1 + -DSQLITE_SOUNDEX=1 + } + "Update-Delete-Limit" { + -O2 + -DSQLITE_DEFAULT_FILE_FORMAT=4 + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_ENABLE_STMT_SCANSTATUS + -DSQLITE_LIKE_DOESNT_MATCH_BLOBS + -DSQLITE_ENABLE_CURSOR_HINTS + --enable-json1 + } + "Check-Symbols" { + -DSQLITE_MEMDEBUG=1 + -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 + -DSQLITE_ENABLE_FTS3=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_MEMSYS5=1 + -DSQLITE_ENABLE_MEMSYS3=1 + -DSQLITE_ENABLE_COLUMN_METADATA=1 + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_SECURE_DELETE=1 + -DSQLITE_SOUNDEX=1 + -DSQLITE_ENABLE_ATOMIC_WRITE=1 + -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 + -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_STMT_SCANSTATUS + --enable-json1 --enable-fts5 --enable-session + } + "Debug-One" { + --disable-shared + -O2 -funsigned-char + -DSQLITE_DEBUG=1 + -DSQLITE_MEMDEBUG=1 + -DSQLITE_MUTEX_NOOP=1 + -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + -DSQLITE_ENABLE_FTS3=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_MEMSYS5=1 + -DSQLITE_ENABLE_COLUMN_METADATA=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_HIDDEN_COLUMNS + -DSQLITE_MAX_ATTACHED=125 + -DSQLITE_MUTATION_TEST + --enable-fts5 --enable-json1 + } + "Fast-One" { + -O6 + -DSQLITE_ENABLE_FTS4=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_RBU + -DSQLITE_MAX_ATTACHED=125 + -DLONGDOUBLE_TYPE=double + --enable-session + } + "Device-One" { + -O2 + -DSQLITE_DEBUG=1 + -DSQLITE_DEFAULT_AUTOVACUUM=1 + -DSQLITE_DEFAULT_CACHE_SIZE=64 + -DSQLITE_DEFAULT_PAGE_SIZE=1024 + -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=32 + -DSQLITE_DISABLE_LFS=1 + -DSQLITE_ENABLE_ATOMIC_WRITE=1 + -DSQLITE_ENABLE_IOTRACE=1 + -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 + -DSQLITE_MAX_PAGE_SIZE=4096 + -DSQLITE_OMIT_LOAD_EXTENSION=1 + -DSQLITE_OMIT_PROGRESS_CALLBACK=1 + -DSQLITE_OMIT_VIRTUALTABLE=1 + -DSQLITE_ENABLE_HIDDEN_COLUMNS + -DSQLITE_TEMP_STORE=3 + --enable-json1 + } + "Device-Two" { + -DSQLITE_4_BYTE_ALIGNED_MALLOC=1 + -DSQLITE_DEFAULT_AUTOVACUUM=1 + -DSQLITE_DEFAULT_CACHE_SIZE=1000 + -DSQLITE_DEFAULT_LOCKING_MODE=0 + -DSQLITE_DEFAULT_PAGE_SIZE=1024 + -DSQLITE_DEFAULT_TEMP_CACHE_SIZE=1000 + -DSQLITE_DISABLE_LFS=1 + -DSQLITE_ENABLE_FTS3=1 + -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_MAX_COMPOUND_SELECT=50 + -DSQLITE_MAX_PAGE_SIZE=32768 + -DSQLITE_OMIT_TRACE=1 + -DSQLITE_TEMP_STORE=3 + -DSQLITE_THREADSAFE=2 + -DSQLITE_ENABLE_DESERIALIZE=1 + --enable-json1 --enable-fts5 --enable-session + } + "Locking-Style" { + -O2 + -DSQLITE_ENABLE_LOCKING_STYLE=1 + } + "Apple" { + -Os + -DHAVE_GMTIME_R=1 + -DHAVE_ISNAN=1 + -DHAVE_LOCALTIME_R=1 + -DHAVE_PREAD=1 + -DHAVE_PWRITE=1 + -DHAVE_USLEEP=1 + -DHAVE_USLEEP=1 + -DHAVE_UTIME=1 + -DSQLITE_DEFAULT_CACHE_SIZE=1000 + -DSQLITE_DEFAULT_CKPTFULLFSYNC=1 + -DSQLITE_DEFAULT_MEMSTATUS=1 + -DSQLITE_DEFAULT_PAGE_SIZE=1024 + -DSQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS=1 + -DSQLITE_ENABLE_API_ARMOR=1 + -DSQLITE_ENABLE_AUTO_PROFILE=1 + -DSQLITE_ENABLE_FLOCKTIMEOUT=1 + -DSQLITE_ENABLE_FTS3=1 + -DSQLITE_ENABLE_FTS3_PARENTHESIS=1 + -DSQLITE_ENABLE_FTS3_TOKENIZER=1 + if:os=="Darwin" -DSQLITE_ENABLE_LOCKING_STYLE=1 + -DSQLITE_ENABLE_PERSIST_WAL=1 + -DSQLITE_ENABLE_PURGEABLE_PCACHE=1 + -DSQLITE_ENABLE_RTREE=1 + -DSQLITE_ENABLE_SNAPSHOT=1 + # -DSQLITE_ENABLE_SQLLOG=1 + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 + -DSQLITE_MAX_LENGTH=2147483645 + -DSQLITE_MAX_VARIABLE_NUMBER=500000 + # -DSQLITE_MEMDEBUG=1 + -DSQLITE_NO_SYNC=1 + -DSQLITE_OMIT_AUTORESET=1 + -DSQLITE_OMIT_LOAD_EXTENSION=1 + -DSQLITE_PREFER_PROXY_LOCKING=1 + -DSQLITE_SERIES_CONSTRAINT_VERIFY=1 + -DSQLITE_THREADSAFE=2 + -DSQLITE_USE_URI=1 + -DSQLITE_WRITE_WALFRAME_PREBUFFERED=1 + -DUSE_GUARDED_FD=1 + -DUSE_PREAD=1 + --enable-json1 --enable-fts5 + } + "Extra-Robustness" { + -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + -DSQLITE_MAX_ATTACHED=62 + } + "Devkit" { + -DSQLITE_DEFAULT_FILE_FORMAT=4 + -DSQLITE_MAX_ATTACHED=30 + -DSQLITE_ENABLE_COLUMN_METADATA + -DSQLITE_ENABLE_FTS4 + -DSQLITE_ENABLE_FTS5 + -DSQLITE_ENABLE_FTS4_PARENTHESIS + -DSQLITE_DISABLE_FTS4_DEFERRED + -DSQLITE_ENABLE_RTREE + --enable-json1 --enable-fts5 + } + "No-lookaside" { + -DSQLITE_TEST_REALLOC_STRESS=1 + -DSQLITE_OMIT_LOOKASIDE=1 + -DHAVE_USLEEP=1 + } + "Valgrind" { + -DSQLITE_ENABLE_STAT4 + -DSQLITE_ENABLE_FTS4 + -DSQLITE_ENABLE_RTREE + -DSQLITE_ENABLE_HIDDEN_COLUMNS + --enable-json1 + } + + # The next group of configurations are used only by the + # Failure-Detection platform. They are all the same, but we need + # different names for them all so that they results appear in separate + # subdirectories. + # + Fail0 {-O0} + Fail2 {-O0} + Fail3 {-O0} + Fail4 {-O0} + FuzzFail1 {-O0} + FuzzFail2 {-O0} +}] + +array set ::Platforms [strip_comments { + Linux-x86_64 { + "Check-Symbols" checksymbols + "Fast-One" "fuzztest test" + "Debug-One" "mptest test" + "Have-Not" test + "Secure-Delete" test + "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" + "User-Auth" tcltest + "Update-Delete-Limit" test + "Extra-Robustness" test + "Device-Two" test + "No-lookaside" test + "Devkit" test + "Apple" test + "Sanitize" {QUICKTEST_OMIT=func4.test,nan.test test} + "Device-One" fulltest + "Default" "threadtest fulltest" + "Valgrind" valgrindtest + } + Linux-i686 { + "Devkit" test + "Have-Not" test + "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" + "Device-One" test + "Device-Two" test + "Default" "threadtest fulltest" + } + Darwin-i386 { + "Locking-Style" "mptest test" + "Have-Not" test + "Apple" "threadtest fulltest" + } + Darwin-x86_64 { + "Locking-Style" "mptest test" + "Have-Not" test + "Apple" "threadtest fulltest" + } + "Windows NT-intel" { + "Stdcall" test + "Have-Not" test + "Default" "mptest fulltestonly" + } + "Windows NT-amd64" { + "Stdcall" test + "Have-Not" test + "Default" "mptest fulltestonly" + } + + # The Failure-Detection platform runs various tests that deliberately + # fail. This is used as a test of this script to verify that this script + # correctly identifies failures. + # + Failure-Detection { + Fail0 "TEST_FAILURE=0 test" + Sanitize "TEST_FAILURE=1 test" + Fail2 "TEST_FAILURE=2 valgrindtest" + Fail3 "TEST_FAILURE=3 valgrindtest" + Fail4 "TEST_FAILURE=4 test" + FuzzFail1 "TEST_FAILURE=5 test" + FuzzFail2 "TEST_FAILURE=5 valgrindtest" + } +}] + +proc make_test_suite {msvc withtcl name testtarget config} { + + # Tcl variable $opts is used to build up the value used to set the + # OPTS Makefile variable. Variable $cflags holds the value for + # CFLAGS. The makefile will pass OPTS to both gcc and lemon, but + # CFLAGS is only passed to gcc. + # + set makeOpts "" + set cflags [expr {$msvc ? "-Zi" : "-g"}] + set opts "" + set title ${name}($testtarget) + set configOpts $withtcl + set skip 0 + + regsub -all {#[^\n]*\n} $config \n config + foreach arg $config { + if {$skip} { + set skip 0 + continue + } + if {[regexp {^-[UD]} $arg]} { + lappend opts $arg + } elseif {[regexp {^[A-Z]+=} $arg]} { + lappend testtarget $arg + } elseif {[regexp {^if:([a-z]+)(.*)} $arg all key tail]} { + # Arguments of the form 'if:os=="Linux"' will cause the subsequent + # argument to be skipped if the $tcl_platform(os) is not "Linux", for + # example... + set skip [expr !(\$::tcl_platform($key)$tail)] + } elseif {[regexp {^--(enable|disable)-} $arg]} { + if {$msvc} { + if {$arg eq "--disable-amalgamation"} { + lappend makeOpts USE_AMALGAMATION=0 + continue + } + if {$arg eq "--disable-shared"} { + lappend makeOpts USE_CRT_DLL=0 DYNAMIC_SHELL=0 + continue + } + if {$arg eq "--enable-fts5"} { + lappend opts -DSQLITE_ENABLE_FTS5 + continue + } + if {$arg eq "--enable-json1"} { + lappend opts -DSQLITE_ENABLE_JSON1 + continue + } + if {$arg eq "--enable-shared"} { + lappend makeOpts USE_CRT_DLL=1 DYNAMIC_SHELL=1 + continue + } + } + lappend configOpts $arg + } else { + if {$msvc} { + if {$arg eq "-g"} { + lappend cflags -Zi + continue + } + if {[regexp -- {^-O(\d+)$} $arg all level]} then { + lappend makeOpts OPTIMIZATIONS=$level + continue + } + } + lappend cflags $arg + } + } + + # Disable sync to make testing faster. + # + lappend opts -DSQLITE_NO_SYNC=1 + + # Some configurations already set HAVE_USLEEP; in that case, skip it. + # + if {[lsearch -regexp $opts {^-DHAVE_USLEEP(?:=|$)}]==-1} { + lappend opts -DHAVE_USLEEP=1 + } + + # Add the define for this platform. + # + if {$::tcl_platform(platform)=="windows"} { + lappend opts -DSQLITE_OS_WIN=1 + } else { + lappend opts -DSQLITE_OS_UNIX=1 + } + + # Set the sub-directory to use. + # + set dir [string tolower [string map {- _ " " _ "(" _ ")" _} $name]] + + # Join option lists into strings, using space as delimiter. + # + set makeOpts [join $makeOpts " "] + set cflags [join $cflags " "] + set opts [join $opts " "] + + return [list $title $dir $configOpts $testtarget $makeOpts $cflags $opts] +} + +# Configuration verification: Check that each entry in the list of configs +# specified for each platforms exists. +# +foreach {key value} [array get ::Platforms] { + foreach {v t} $value { + if {0==[info exists ::Configs($v)]} { + puts stderr "No such configuration: \"$v\"" + exit -1 + } + } +} + Index: test/shell1.test ================================================================== --- test/shell1.test +++ test/shell1.test @@ -1020,10 +1020,12 @@ # cannot be used here. # if {$i==0x0D || ($tcl_platform(platform)=="windows" && $i==0x1A)} { continue } + # Tcl 8.7 maps 0x80 through 0x9f into valid UTF8. So skip those tests. + if {$i>=0x80 && $i<=0x9f} continue if {$i>=0xE0 && $tcl_platform(os)=="OpenBSD"} continue if {$i>=0xE0 && $i<=0xEF && $tcl_platform(os)=="Linux"} continue set hex [format %02X $i] set char [subst \\x$hex]; set oldChar $char set escapes [list] Index: test/skipscan1.test ================================================================== --- test/skipscan1.test +++ test/skipscan1.test @@ -342,7 +342,35 @@ do_execsql_test skipscan1-9.3 { EXPLAIN QUERY PLAN SELECT * FROM t9a WHERE b IN (SELECT x FROM t9b WHERE y!=5); } {/{SCAN TABLE t9a}/} optimization_control db skip-scan 1 + +do_execsql_test skipscan1-2.1 { + CREATE TABLE t6(a TEXT, b INT, c INT, d INT); + CREATE INDEX t6abc ON t6(a,b,c); + INSERT INTO t6 VALUES('abc',123,4,5); + + ANALYZE; + DELETE FROM sqlite_stat1; + INSERT INTO sqlite_stat1 VALUES('t6','t6abc','10000 5000 2000 10'); + ANALYZE sqlite_master; + DELETE FROM t6; +} {} + +do_execsql_test skipscan1-2.2eqp { + EXPLAIN QUERY PLAN + SELECT a,b,c,d,'|' FROM t6 WHERE d<>99 AND b=345 ORDER BY a; +} {/* USING INDEX t6abc (ANY(a) AND b=?)*/} +do_execsql_test skipscan1-2.2 { + SELECT a,b,c,d,'|' FROM t6 WHERE d<>99 AND b=345 ORDER BY a; +} {} + +do_execsql_test skipscan1-2.3eqp { + EXPLAIN QUERY PLAN + SELECT a,b,c,d,'|' FROM t6 WHERE d<>99 AND b=345 ORDER BY a DESC; +} {/* USING INDEX t6abc (ANY(a) AND b=?)*/} +do_execsql_test skipscan1-2.3 { + SELECT a,b,c,d,'|' FROM t6 WHERE d<>99 AND b=345 ORDER BY a DESC; +} {} finish_test Index: test/skipscan2.test ================================================================== --- test/skipscan2.test +++ test/skipscan2.test @@ -198,8 +198,9 @@ execsql { ANALYZE } } {} do_eqp_test skipscan2-3.3eqp { SELECT * FROM t3 WHERE b=42; } {SEARCH TABLE t3 USING PRIMARY KEY (ANY(a) AND b=?)} + finish_test Index: test/sqllimits1.test ================================================================== --- test/sqllimits1.test +++ test/sqllimits1.test @@ -887,6 +887,16 @@ foreach {key value} [array get saved] { catch {set $key $value} } + +#------------------------------------------------------------------------- +# At one point the following caused an assert() to fail. +# +sqlite3_limit db SQLITE_LIMIT_LENGTH 10000 +set nm [string repeat x 10000] +do_catchsql_test sqllimits1-17.1 " + CREATE TABLE $nm (x PRIMARY KEY) +" {1 {string or blob too big}} + finish_test Index: test/tester.tcl ================================================================== --- test/tester.tcl +++ test/tester.tcl @@ -574,10 +574,14 @@ } if {$cmdlinearg(verbose)==""} { set cmdlinearg(verbose) 1 } + + if {[info commands vdbe_coverage]!=""} { + vdbe_coverage start + } } # Update the soft-heap-limit each time this script is run. In that # way if an individual test file changes the soft-heap-limit, it # will be reset at the start of the next test file. @@ -1293,19 +1297,55 @@ if {[sqlite3_memory_used]>0} { output2 "Writing leaks.tcl..." sqlite3_memdebug_log sync memdebug_log_sql leaks.tcl } + } + if {[info commands vdbe_coverage]!=""} { + vdbe_coverage_report } foreach f [glob -nocomplain test.db-*-journal] { forcedelete $f } foreach f [glob -nocomplain test.db-mj*] { forcedelete $f } exit [expr {$nErr>0}] } + +proc vdbe_coverage_report {} { + puts "Writing vdbe coverage report to vdbe_coverage.txt" + set lSrc [list] + set iLine 0 + if {[file exists ../sqlite3.c]} { + set fd [open ../sqlite3.c] + set iLine + while { ![eof $fd] } { + set line [gets $fd] + incr iLine + if {[regexp {^/\** Begin file (.*\.c) \**/} $line -> file]} { + lappend lSrc [list $iLine $file] + } + } + close $fd + } + set fd [open vdbe_coverage.txt w] + foreach miss [vdbe_coverage report] { + foreach {line branch never} $miss {} + set nextfile "" + while {[llength $lSrc]>0 && [lindex $lSrc 0 0] < $line} { + set nextfile [lindex $lSrc 0 1] + set lSrc [lrange $lSrc 1 end] + } + if {$nextfile != ""} { + puts $fd "" + puts $fd "### $nextfile ###" + } + puts $fd "Vdbe branch $line: never $never (path $branch)" + } + close $fd +} # Display memory statistics for analysis and debugging purposes. # proc show_memstats {} { set x [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] Index: test/triggerC.test ================================================================== --- test/triggerC.test +++ test/triggerC.test @@ -1054,8 +1054,22 @@ do_catchsql_test 16.2 { SELECT count(*) FROM sqlite_master GROUP BY raise(IGNORE) HAVING raise(ABORT, 'msg'); } {1 {RAISE() may only be used within a trigger-program}} + +#------------------------------------------------------------------------- +# Datatype mismatch on IPK when there are BEFORE triggers. +# +do_execsql_test 17.0 { + CREATE TABLE xyz(x INTEGER PRIMARY KEY, y, z); + CREATE TRIGGER xyz_tr BEFORE INSERT ON xyz BEGIN + SELECT new.x; + END; +} +do_catchsql_test 17.1 { + INSERT INTO xyz VALUES('hello', 2, 3); +} {1 {datatype mismatch}} + finish_test Index: test/wal2.test ================================================================== --- test/wal2.test +++ test/wal2.test @@ -1083,11 +1083,11 @@ 4 00755 } { set effective [format %.5o [expr $permissions & ~$umask]] do_test wal2-12.2.$tn.1 { file attributes test.db -permissions $permissions - file attributes test.db -permissions + string map {o 0} [file attributes test.db -permissions] } $permissions do_test wal2-12.2.$tn.2 { list [file exists test.db-wal] [file exists test.db-shm] } {0 0} do_test wal2-12.2.$tn.3 { @@ -1094,11 +1094,12 @@ sqlite3 db test.db execsql { INSERT INTO tx DEFAULT VALUES } list [file exists test.db-wal] [file exists test.db-shm] } {1 1} do_test wal2-12.2.$tn.4 { - list [file attr test.db-wal -perm] [file attr test.db-shm -perm] + set x [list [file attr test.db-wal -perm] [file attr test.db-shm -perm]] + string map {o 0} $x } [list $effective $effective] do_test wal2-12.2.$tn.5 { db close list [file exists test.db-wal] [file exists test.db-shm] } {0 0} @@ -1152,10 +1153,11 @@ file attr test.db-shm -perm $shm_perm set L [file attr test.db -perm] lappend L [file attr test.db-wal -perm] lappend L [file attr test.db-shm -perm] + string map {o 0} $L } [list $db_perm $wal_perm $shm_perm] # If $can_open is true, then it should be possible to open a database # handle. Otherwise, if $can_open is 0, attempting to open the db # handle throws an "unable to open database file" exception. ADDED test/wapp.tcl Index: test/wapp.tcl ================================================================== --- /dev/null +++ test/wapp.tcl @@ -0,0 +1,987 @@ +# Copyright (c) 2017 D. Richard Hipp +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the Simplified BSD License (also +# known as the "2-Clause License" or "FreeBSD License".) +# +# This program is distributed in the hope that it will be useful, +# but without any warranty; without even the implied warranty of +# merchantability or fitness for a particular purpose. +# +#--------------------------------------------------------------------------- +# +# Design rules: +# +# (1) All identifiers in the global namespace begin with "wapp" +# +# (2) Indentifiers intended for internal use only begin with "wappInt" +# +package require Tcl 8.6 + +# Add text to the end of the HTTP reply. No interpretation or transformation +# of the text is performs. The argument should be enclosed within {...} +# +proc wapp {txt} { + global wapp + dict append wapp .reply $txt +} + +# Add text to the page under construction. Do no escaping on the text. +# +# Though "unsafe" in general, there are uses for this kind of thing. +# For example, if you want to return the complete, unmodified content of +# a file: +# +# set fd [open content.html rb] +# wapp-unsafe [read $fd] +# close $fd +# +# You could do the same thing using ordinary "wapp" instead of "wapp-unsafe". +# The difference is that wapp-safety-check will complain about the misuse +# of "wapp", but it assumes that the person who write "wapp-unsafe" understands +# the risks. +# +# Though occasionally necessary, the use of this interface should be minimized. +# +proc wapp-unsafe {txt} { + global wapp + dict append wapp .reply $txt +} + +# Add text to the end of the reply under construction. The following +# substitutions are made: +# +# %html(...) Escape text for inclusion in HTML +# %url(...) Escape text for use as a URL +# %qp(...) Escape text for use as a URI query parameter +# %string(...) Escape text for use within a JSON string +# %unsafe(...) No transformations of the text +# +# The substitutions above terminate at the first ")" character. If the +# text of the TCL string in ... contains ")" characters itself, use instead: +# +# %html%(...)% +# %url%(...)% +# %qp%(...)% +# %string%(...)% +# %unsafe%(...)% +# +# In other words, use "%(...)%" instead of "(...)" to include the TCL string +# to substitute. +# +# The %unsafe substitution should be avoided whenever possible, obviously. +# In addition to the substitutions above, the text also does backslash +# escapes. +# +# The wapp-trim proc works the same as wapp-subst except that it also removes +# whitespace from the left margin, so that the generated HTML/CSS/Javascript +# does not appear to be indented when delivered to the client web browser. +# +if {$tcl_version>=8.7} { + proc wapp-subst {txt} { + global wapp + regsub -all -command \ + {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt wappInt-enc txt + dict append wapp .reply [subst -novariables -nocommand $txt] + } + proc wapp-trim {txt} { + global wapp + regsub -all {\n\s+} [string trim $txt] \n txt + regsub -all -command \ + {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt wappInt-enc txt + dict append wapp .reply [subst -novariables -nocommand $txt] + } + proc wappInt-enc {all mode nu1 txt} { + return [uplevel 2 "wappInt-enc-$mode \"$txt\""] + } +} else { + proc wapp-subst {txt} { + global wapp + regsub -all {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt \ + {[wappInt-enc-\1 "\3"]} txt + dict append wapp .reply [uplevel 1 [list subst -novariables $txt]] + } + proc wapp-trim {txt} { + global wapp + regsub -all {\n\s+} [string trim $txt] \n txt + regsub -all {%(html|url|qp|string|unsafe){1,1}?(|%)\((.+)\)\2} $txt \ + {[wappInt-enc-\1 "\3"]} txt + dict append wapp .reply [uplevel 1 [list subst -novariables $txt]] + } +} + +# There must be a wappInt-enc-NAME routine for each possible substitution +# in wapp-subst. Thus there are routines for "html", "url", "qp", and "unsafe". +# +# wappInt-enc-html Escape text so that it is safe to use in the +# body of an HTML document. +# +# wappInt-enc-url Escape text so that it is safe to pass as an +# argument to href= and src= attributes in HTML. +# +# wappInt-enc-qp Escape text so that it is safe to use as the +# value of a query parameter in a URL or in +# post data or in a cookie. +# +# wappInt-enc-string Escape ", ', \, and < for using inside of a +# javascript string literal. The < character +# is escaped to prevent "" from causing +# problems in embedded javascript. +# +# wappInt-enc-unsafe Perform no encoding at all. Unsafe. +# +proc wappInt-enc-html {txt} { + return [string map {& & < < > > \" " \\ \} $txt] +} +proc wappInt-enc-unsafe {txt} { + return $txt +} +proc wappInt-enc-url {s} { + if {[regsub -all {[^-{}@~?=#_.:/a-zA-Z0-9]} $s {[wappInt-%HHchar {&}]} s]} { + set s [subst -novar -noback $s] + } + if {[regsub -all {[{}]} $s {[wappInt-%HHchar \\&]} s]} { + set s [subst -novar -noback $s] + } + return $s +} +proc wappInt-enc-qp {s} { + if {[regsub -all {[^-{}_.a-zA-Z0-9]} $s {[wappInt-%HHchar {&}]} s]} { + set s [subst -novar -noback $s] + } + if {[regsub -all {[{}]} $s {[wappInt-%HHchar \\&]} s]} { + set s [subst -novar -noback $s] + } + return $s +} +proc wappInt-enc-string {s} { + return [string map {\\ \\\\ \" \\\" ' \\' < \\u003c} $s] +} + +# This is a helper routine for wappInt-enc-url and wappInt-enc-qp. It returns +# an appropriate %HH encoding for the single character c. If c is a unicode +# character, then this routine might return multiple bytes: %HH%HH%HH +# +proc wappInt-%HHchar {c} { + if {$c==" "} {return +} + return [regsub -all .. [binary encode hex [encoding convertto utf-8 $c]] {%&}] +} + + +# Undo the www-url-encoded format. +# +# HT: This code stolen from ncgi.tcl +# +proc wappInt-decode-url {str} { + set str [string map [list + { } "\\" "\\\\" \[ \\\[ \] \\\]] $str] + regsub -all -- \ + {%([Ee][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])} \ + $str {[encoding convertfrom utf-8 [binary decode hex \1\2\3]]} str + regsub -all -- \ + {%([CDcd][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])} \ + $str {[encoding convertfrom utf-8 [binary decode hex \1\2]]} str + regsub -all -- {%([0-7][A-Fa-f0-9])} $str {\\u00\1} str + return [subst -novar $str] +} + +# Reset the document back to an empty string. +# +proc wapp-reset {} { + global wapp + dict set wapp .reply {} +} + +# Change the mime-type of the result document. +# +proc wapp-mimetype {x} { + global wapp + dict set wapp .mimetype $x +} + +# Change the reply code. +# +proc wapp-reply-code {x} { + global wapp + dict set wapp .reply-code $x +} + +# Set a cookie +# +proc wapp-set-cookie {name value} { + global wapp + dict lappend wapp .new-cookies $name $value +} + +# Unset a cookie +# +proc wapp-clear-cookie {name} { + wapp-set-cookie $name {} +} + +# Add extra entries to the reply header +# +proc wapp-reply-extra {name value} { + global wapp + dict lappend wapp .reply-extra $name $value +} + +# Specifies how the web-page under construction should be cached. +# The argument should be one of: +# +# no-cache +# max-age=N (for some integer number of seconds, N) +# private,max-age=N +# +proc wapp-cache-control {x} { + wapp-reply-extra Cache-Control $x +} + +# Redirect to a different web page +# +proc wapp-redirect {uri} { + wapp-reply-code {307 Redirect} + wapp-reply-extra Location $uri +} + +# Return the value of a wapp parameter +# +proc wapp-param {name {dflt {}}} { + global wapp + if {![dict exists $wapp $name]} {return $dflt} + return [dict get $wapp $name] +} + +# Return true if a and only if the wapp parameter $name exists +# +proc wapp-param-exists {name} { + global wapp + return [dict exists $wapp $name] +} + +# Set the value of a wapp parameter +# +proc wapp-set-param {name value} { + global wapp + dict set wapp $name $value +} + +# Return all parameter names that match the GLOB pattern, or all +# names if the GLOB pattern is omitted. +# +proc wapp-param-list {{glob {*}}} { + global wapp + return [dict keys $wapp $glob] +} + +# By default, Wapp does not decode query parameters and POST parameters +# for cross-origin requests. This is a security restriction, designed to +# help prevent cross-site request forgery (CSRF) attacks. +# +# As a consequence of this restriction, URLs for sites generated by Wapp +# that contain query parameters will not work as URLs found in other +# websites. You cannot create a link from a second website into a Wapp +# website if the link contains query planner, by default. +# +# Of course, it is sometimes desirable to allow query parameters on external +# links. For URLs for which this is safe, the application should invoke +# wapp-allow-xorigin-params. This procedure tells Wapp that it is safe to +# go ahead and decode the query parameters even for cross-site requests. +# +# In other words, for Wapp security is the default setting. Individual pages +# need to actively disable the cross-site request security if those pages +# are safe for cross-site access. +# +proc wapp-allow-xorigin-params {} { + global wapp + if {![dict exists $wapp .qp] && ![dict get $wapp SAME_ORIGIN]} { + wappInt-decode-query-params + } +} + +# Set the content-security-policy. +# +# The default content-security-policy is very strict: "default-src 'self'" +# The default policy prohibits the use of in-line javascript or CSS. +# +# Provide an alternative CSP as the argument. Or use "off" to disable +# the CSP completely. +# +proc wapp-content-security-policy {val} { + global wapp + if {$val=="off"} { + dict unset wapp .csp + } else { + dict set wapp .csp $val + } +} + +# Examine the bodys of all procedures in this program looking for +# unsafe calls to various Wapp interfaces. Return a text string +# containing warnings. Return an empty string if all is ok. +# +# This routine is advisory only. It misses some constructs that are +# dangerous and flags others that are safe. +# +proc wapp-safety-check {} { + set res {} + foreach p [info procs] { + set ln 0 + foreach x [split [info body $p] \n] { + incr ln + if {[regexp {^[ \t]*wapp[ \t]+([^\n]+)} $x all tail] + && [string index $tail 0]!="\173" + && [regexp {[[$]} $tail] + } { + append res "$p:$ln: unsafe \"wapp\" call: \"[string trim $x]\"\n" + } + if {[regexp {^[ \t]*wapp-(subst|trim)[ \t]+[^\173]} $x all cx]} { + append res "$p:$ln: unsafe \"wapp-$cx\" call: \"[string trim $x]\"\n" + } + } + } + return $res +} + +# Return a string that descripts the current environment. Applications +# might find this useful for debugging. +# +proc wapp-debug-env {} { + global wapp + set out {} + foreach var [lsort [dict keys $wapp]] { + if {[string index $var 0]=="."} continue + append out "$var = [list [dict get $wapp $var]]\n" + } + append out "\[pwd\] = [list [pwd]]\n" + return $out +} + +# Tracing function for each HTTP request. This is overridden by wapp-start +# if tracing is enabled. +# +proc wappInt-trace {} {} + +# Start up a listening socket. Arrange to invoke wappInt-new-connection +# for each inbound HTTP connection. +# +# port Listen on this TCP port. 0 means to select a port +# that is not currently in use +# +# wappmode One of "scgi", "remote-scgi", "server", or "local". +# +# fromip If not {}, then reject all requests from IP addresses +# other than $fromip +# +proc wappInt-start-listener {port wappmode fromip} { + if {[string match *scgi $wappmode]} { + set type SCGI + set server [list wappInt-new-connection \ + wappInt-scgi-readable $wappmode $fromip] + } else { + set type HTTP + set server [list wappInt-new-connection \ + wappInt-http-readable $wappmode $fromip] + } + if {$wappmode=="local" || $wappmode=="scgi"} { + set x [socket -server $server -myaddr 127.0.0.1 $port] + } else { + set x [socket -server $server $port] + } + set coninfo [chan configure $x -sockname] + set port [lindex $coninfo 2] + if {$wappmode=="local"} { + wappInt-start-browser http://127.0.0.1:$port/ + } elseif {$fromip!=""} { + puts "Listening for $type requests on TCP port $port from IP $fromip" + } else { + puts "Listening for $type requests on TCP port $port" + } +} + +# Start a web-browser and point it at $URL +# +proc wappInt-start-browser {url} { + global tcl_platform + if {$tcl_platform(platform)=="windows"} { + exec cmd /c start $url & + } elseif {$tcl_platform(os)=="Darwin"} { + exec open $url & + } elseif {[catch {exec xdg-open $url}]} { + exec firefox $url & + } +} + +# This routine is a "socket -server" callback. The $chan, $ip, and $port +# arguments are added by the socket command. +# +# Arrange to invoke $callback when content is available on the new socket. +# The $callback will process inbound HTTP or SCGI content. Reject the +# request if $fromip is not an empty string and does not match $ip. +# +proc wappInt-new-connection {callback wappmode fromip chan ip port} { + upvar #0 wappInt-$chan W + if {$fromip!="" && ![string match $fromip $ip]} { + close $chan + return + } + set W [dict create REMOTE_ADDR $ip REMOTE_PORT $port WAPP_MODE $wappmode \ + .header {}] + fconfigure $chan -blocking 0 -translation binary + fileevent $chan readable [list $callback $chan] +} + +# Close an input channel +# +proc wappInt-close-channel {chan} { + if {$chan=="stdout"} { + # This happens after completing a CGI request + exit 0 + } else { + unset ::wappInt-$chan + close $chan + } +} + +# Process new text received on an inbound HTTP request +# +proc wappInt-http-readable {chan} { + if {[catch [list wappInt-http-readable-unsafe $chan] msg]} { + puts stderr "$msg\n$::errorInfo" + wappInt-close-channel $chan + } +} +proc wappInt-http-readable-unsafe {chan} { + upvar #0 wappInt-$chan W wapp wapp + if {![dict exists $W .toread]} { + # If the .toread key is not set, that means we are still reading + # the header + set line [string trimright [gets $chan]] + set n [string length $line] + if {$n>0} { + if {[dict get $W .header]=="" || [regexp {^\s+} $line]} { + dict append W .header $line + } else { + dict append W .header \n$line + } + if {[string length [dict get $W .header]]>100000} { + error "HTTP request header too big - possible DOS attack" + } + } elseif {$n==0} { + # We have reached the blank line that terminates the header. + global argv0 + set a0 [file normalize $argv0] + dict set W SCRIPT_FILENAME $a0 + dict set W DOCUMENT_ROOT [file dir $a0] + if {[wappInt-parse-header $chan]} { + catch {close $chan} + return + } + set len 0 + if {[dict exists $W CONTENT_LENGTH]} { + set len [dict get $W CONTENT_LENGTH] + } + if {$len>0} { + # Still need to read the query content + dict set W .toread $len + } else { + # There is no query content, so handle the request immediately + set wapp $W + wappInt-handle-request $chan 0 + } + } + } else { + # If .toread is set, that means we are reading the query content. + # Continue reading until .toread reaches zero. + set got [read $chan [dict get $W .toread]] + dict append W CONTENT $got + dict set W .toread [expr {[dict get $W .toread]-[string length $got]}] + if {[dict get $W .toread]<=0} { + # Handle the request as soon as all the query content is received + set wapp $W + wappInt-handle-request $chan 0 + } + } +} + +# Decode the HTTP request header. +# +# This routine is always running inside of a [catch], so if +# any problems arise, simply raise an error. +# +proc wappInt-parse-header {chan} { + upvar #0 wappInt-$chan W + set hdr [split [dict get $W .header] \n] + if {$hdr==""} {return 1} + set req [lindex $hdr 0] + dict set W REQUEST_METHOD [set method [lindex $req 0]] + if {[lsearch {GET HEAD POST} $method]<0} { + error "unsupported request method: \"[dict get $W REQUEST_METHOD]\"" + } + set uri [lindex $req 1] + set split_uri [split $uri ?] + set uri0 [lindex $split_uri 0] + if {![regexp {^/[-.a-z0-9_/]*$} $uri0]} { + error "invalid request uri: \"$uri0\"" + } + dict set W REQUEST_URI $uri0 + dict set W PATH_INFO $uri0 + set uri1 [lindex $split_uri 1] + dict set W QUERY_STRING $uri1 + set n [llength $hdr] + for {set i 1} {$i<$n} {incr i} { + set x [lindex $hdr $i] + if {![regexp {^(.+): +(.*)$} $x all name value]} { + error "invalid header line: \"$x\"" + } + set name [string toupper $name] + switch -- $name { + REFERER {set name HTTP_REFERER} + USER-AGENT {set name HTTP_USER_AGENT} + CONTENT-LENGTH {set name CONTENT_LENGTH} + CONTENT-TYPE {set name CONTENT_TYPE} + HOST {set name HTTP_HOST} + COOKIE {set name HTTP_COOKIE} + ACCEPT-ENCODING {set name HTTP_ACCEPT_ENCODING} + default {set name .hdr:$name} + } + dict set W $name $value + } + return 0 +} + +# Decode the QUERY_STRING parameters from a GET request or the +# application/x-www-form-urlencoded CONTENT from a POST request. +# +# This routine sets the ".qp" element of the ::wapp dict as a signal +# that query parameters have already been decoded. +# +proc wappInt-decode-query-params {} { + global wapp + dict set wapp .qp 1 + if {[dict exists $wapp QUERY_STRING]} { + foreach qterm [split [dict get $wapp QUERY_STRING] &] { + set qsplit [split $qterm =] + set nm [lindex $qsplit 0] + if {[regexp {^[a-z][a-z0-9]*$} $nm]} { + dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] + } + } + } + if {[dict exists $wapp CONTENT_TYPE] && [dict exists $wapp CONTENT]} { + set ctype [dict get $wapp CONTENT_TYPE] + if {$ctype=="application/x-www-form-urlencoded"} { + foreach qterm [split [string trim [dict get $wapp CONTENT]] &] { + set qsplit [split $qterm =] + set nm [lindex $qsplit 0] + if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { + dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] + } + } + } elseif {[string match multipart/form-data* $ctype]} { + regexp {^(.*?)\r\n(.*)$} [dict get $wapp CONTENT] all divider body + set ndiv [string length $divider] + while {[string length $body]} { + set idx [string first $divider $body] + set unit [string range $body 0 [expr {$idx-3}]] + set body [string range $body [expr {$idx+$ndiv+2}] end] + if {[regexp {^Content-Disposition: form-data; (.*?)\r\n\r\n(.*)$} \ + $unit unit hdr content]} { + if {[regexp {name="(.*)"; filename="(.*)"\r\nContent-Type: (.*?)$}\ + $hdr hr name filename mimetype]} { + dict set wapp $name.filename \ + [string map [list \\\" \" \\\\ \\] $filename] + dict set wapp $name.mimetype $mimetype + dict set wapp $name.content $content + } elseif {[regexp {name="(.*)"} $hdr hr name]} { + dict set wapp $name $content + } + } + } + } + } +} + +# Invoke application-supplied methods to generate a reply to +# a single HTTP request. +# +# This routine always runs within [catch], so handle exceptions by +# invoking [error]. +# +proc wappInt-handle-request {chan useCgi} { + global wapp + dict set wapp .reply {} + dict set wapp .mimetype {text/html; charset=utf-8} + dict set wapp .reply-code {200 Ok} + dict set wapp .csp {default-src 'self'} + + # Set up additional CGI environment values + # + if {![dict exists $wapp HTTP_HOST]} { + dict set wapp BASE_URL {} + } elseif {[dict exists $wapp HTTPS]} { + dict set wapp BASE_URL https://[dict get $wapp HTTP_HOST] + } else { + dict set wapp BASE_URL http://[dict get $wapp HTTP_HOST] + } + if {![dict exists $wapp REQUEST_URI]} { + dict set wapp REQUEST_URI / + } elseif {[regsub {\?.*} [dict get $wapp REQUEST_URI] {} newR]} { + # Some servers (ex: nginx) append the query parameters to REQUEST_URI. + # These need to be stripped off + dict set wapp REQUEST_URI $newR + } + if {[dict exists $wapp SCRIPT_NAME]} { + dict append wapp BASE_URL [dict get $wapp SCRIPT_NAME] + } else { + dict set wapp SCRIPT_NAME {} + } + if {![dict exists $wapp PATH_INFO]} { + # If PATH_INFO is missing (ex: nginx) then construct it + set URI [dict get $wapp REQUEST_URI] + set skip [string length [dict get $wapp SCRIPT_NAME]] + dict set wapp PATH_INFO [string range $URI $skip end] + } + if {[regexp {^/([^/]+)(.*)$} [dict get $wapp PATH_INFO] all head tail]} { + dict set wapp PATH_HEAD $head + dict set wapp PATH_TAIL [string trimleft $tail /] + } else { + dict set wapp PATH_INFO {} + dict set wapp PATH_HEAD {} + dict set wapp PATH_TAIL {} + } + dict set wapp SELF_URL [dict get $wapp BASE_URL]/[dict get $wapp PATH_HEAD] + + # Parse query parameters from the query string, the cookies, and + # POST data + # + if {[dict exists $wapp HTTP_COOKIE]} { + foreach qterm [split [dict get $wapp HTTP_COOKIE] {;}] { + set qsplit [split [string trim $qterm] =] + set nm [lindex $qsplit 0] + if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { + dict set wapp $nm [wappInt-decode-url [lindex $qsplit 1]] + } + } + } + set same_origin 0 + if {[dict exists $wapp HTTP_REFERER]} { + set referer [dict get $wapp HTTP_REFERER] + set base [dict get $wapp BASE_URL] + if {$referer==$base || [string match $base/* $referer]} { + set same_origin 1 + } + } + dict set wapp SAME_ORIGIN $same_origin + if {$same_origin} { + wappInt-decode-query-params + } + + # Invoke the application-defined handler procedure for this page + # request. If an error occurs while running that procedure, generate + # an HTTP reply that contains the error message. + # + wapp-before-dispatch-hook + wappInt-trace + set mname [dict get $wapp PATH_HEAD] + if {[catch { + if {$mname!="" && [llength [info proc wapp-page-$mname]]>0} { + wapp-page-$mname + } else { + wapp-default + } + } msg]} { + if {[wapp-param WAPP_MODE]=="local" || [wapp-param WAPP_MODE]=="server"} { + puts "ERROR: $::errorInfo" + } + wapp-reset + wapp-reply-code "500 Internal Server Error" + wapp-mimetype text/html + wapp-trim { +

Wapp Application Error

+
%html($::errorInfo)
+ } + dict unset wapp .new-cookies + } + + # Transmit the HTTP reply + # + if {$chan=="stdout"} { + puts $chan "Status: [dict get $wapp .reply-code]\r" + } else { + puts $chan "HTTP/1.1 [dict get $wapp .reply-code]\r" + puts $chan "Server: wapp\r" + puts $chan "Connection: close\r" + } + if {[dict exists $wapp .reply-extra]} { + foreach {name value} [dict get $wapp .reply-extra] { + puts $chan "$name: $value\r" + } + } + if {[dict exists $wapp .csp]} { + puts $chan "Content-Security-Policy: [dict get $wapp .csp]\r" + } + set mimetype [dict get $wapp .mimetype] + puts $chan "Content-Type: $mimetype\r" + if {[dict exists $wapp .new-cookies]} { + foreach {nm val} [dict get $wapp .new-cookies] { + if {[regexp {^[a-z][-a-z0-9_]*$} $nm]} { + if {$val==""} { + puts $chan "Set-Cookie: $nm=; HttpOnly; Path=/; Max-Age=1\r" + } else { + set val [wappInt-enc-url $val] + puts $chan "Set-Cookie: $nm=$val; HttpOnly; Path=/\r" + } + } + } + } + if {[string match text/* $mimetype]} { + set reply [encoding convertto utf-8 [dict get $wapp .reply]] + if {[regexp {\ygzip\y} [wapp-param HTTP_ACCEPT_ENCODING]]} { + catch { + set x [zlib gzip $reply] + set reply $x + puts $chan "Content-Encoding: gzip\r" + } + } + } else { + set reply [dict get $wapp .reply] + } + puts $chan "Content-Length: [string length $reply]\r" + puts $chan \r + puts -nonewline $chan $reply + flush $chan + wappInt-close-channel $chan +} + +# This routine runs just prior to request-handler dispatch. The +# default implementation is a no-op, but applications can override +# to do additional transformations or checks. +# +proc wapp-before-dispatch-hook {} {return} + +# Process a single CGI request +# +proc wappInt-handle-cgi-request {} { + global wapp env + foreach key { + CONTENT_LENGTH + CONTENT_TYPE + DOCUMENT_ROOT + HTTP_ACCEPT_ENCODING + HTTP_COOKIE + HTTP_HOST + HTTP_REFERER + HTTP_USER_AGENT + HTTPS + PATH_INFO + QUERY_STRING + REMOTE_ADDR + REQUEST_METHOD + REQUEST_URI + REMOTE_USER + SCRIPT_FILENAME + SCRIPT_NAME + SERVER_NAME + SERVER_PORT + SERVER_PROTOCOL + } { + if {[info exists env($key)]} { + dict set wapp $key $env($key) + } + } + set len 0 + if {[dict exists $wapp CONTENT_LENGTH]} { + set len [dict get $wapp CONTENT_LENGTH] + } + if {$len>0} { + fconfigure stdin -translation binary + dict set wapp CONTENT [read stdin $len] + } + dict set wapp WAPP_MODE cgi + fconfigure stdout -translation binary + wappInt-handle-request stdout 1 +} + +# Process new text received on an inbound SCGI request +# +proc wappInt-scgi-readable {chan} { + if {[catch [list wappInt-scgi-readable-unsafe $chan] msg]} { + puts stderr "$msg\n$::errorInfo" + wappInt-close-channel $chan + } +} +proc wappInt-scgi-readable-unsafe {chan} { + upvar #0 wappInt-$chan W wapp wapp + if {![dict exists $W .toread]} { + # If the .toread key is not set, that means we are still reading + # the header. + # + # An SGI header is short. This implementation assumes the entire + # header is available all at once. + # + dict set W .remove_addr [dict get $W REMOTE_ADDR] + set req [read $chan 15] + set n [string length $req] + scan $req %d:%s len hdr + incr len [string length "$len:,"] + append hdr [read $chan [expr {$len-15}]] + foreach {nm val} [split $hdr \000] { + if {$nm==","} break + dict set W $nm $val + } + set len 0 + if {[dict exists $W CONTENT_LENGTH]} { + set len [dict get $W CONTENT_LENGTH] + } + if {$len>0} { + # Still need to read the query content + dict set W .toread $len + } else { + # There is no query content, so handle the request immediately + dict set W SERVER_ADDR [dict get $W .remove_addr] + set wapp $W + wappInt-handle-request $chan 0 + } + } else { + # If .toread is set, that means we are reading the query content. + # Continue reading until .toread reaches zero. + set got [read $chan [dict get $W .toread]] + dict append W CONTENT $got + dict set W .toread [expr {[dict get $W .toread]-[string length $got]}] + if {[dict get $W .toread]<=0} { + # Handle the request as soon as all the query content is received + dict set W SERVER_ADDR [dict get $W .remove_addr] + set wapp $W + wappInt-handle-request $chan 0 + } + } +} + +# Start up the wapp framework. Parameters are a list passed as the +# single argument. +# +# -server $PORT Listen for HTTP requests on this TCP port $PORT +# +# -local $PORT Listen for HTTP requests on 127.0.0.1:$PORT +# +# -scgi $PORT Listen for SCGI requests on 127.0.0.1:$PORT +# +# -remote-scgi $PORT Listen for SCGI requests on TCP port $PORT +# +# -cgi Handle a single CGI request +# +# With no arguments, the behavior is called "auto". In "auto" mode, +# if the GATEWAY_INTERFACE environment variable indicates CGI, then run +# as CGI. Otherwise, start an HTTP server bound to the loopback address +# only, on an arbitrary TCP port, and automatically launch a web browser +# on that TCP port. +# +# Additional options: +# +# -fromip GLOB Reject any incoming request where the remote +# IP address does not match the GLOB pattern. This +# value defaults to '127.0.0.1' for -local and -scgi. +# +# -nowait Do not wait in the event loop. Return immediately +# after all event handlers are established. +# +# -trace "puts" each request URL as it is handled, for +# debugging +# +# -lint Run wapp-safety-check on the application instead +# of running the application itself +# +# -Dvar=value Set TCL global variable "var" to "value" +# +# +proc wapp-start {arglist} { + global env + set mode auto + set port 0 + set nowait 0 + set fromip {} + set n [llength $arglist] + for {set i 0} {$i<$n} {incr i} { + set term [lindex $arglist $i] + if {[string match --* $term]} {set term [string range $term 1 end]} + switch -glob -- $term { + -server { + incr i; + set mode "server" + set port [lindex $arglist $i] + } + -local { + incr i; + set mode "local" + set fromip 127.0.0.1 + set port [lindex $arglist $i] + } + -scgi { + incr i; + set mode "scgi" + set fromip 127.0.0.1 + set port [lindex $arglist $i] + } + -remote-scgi { + incr i; + set mode "remote-scgi" + set port [lindex $arglist $i] + } + -cgi { + set mode "cgi" + } + -fromip { + incr i + set fromip [lindex $arglist $i] + } + -nowait { + set nowait 1 + } + -trace { + proc wappInt-trace {} { + set q [wapp-param QUERY_STRING] + set uri [wapp-param BASE_URL][wapp-param PATH_INFO] + if {$q!=""} {append uri ?$q} + puts $uri + } + } + -lint { + set res [wapp-safety-check] + if {$res!=""} { + puts "Potential problems in this code:" + puts $res + exit 1 + } else { + exit + } + } + -D*=* { + if {[regexp {^.D([^=]+)=(.*)$} $term all var val]} { + set ::$var $val + } + } + default { + error "unknown option: $term" + } + } + } + if {$mode=="auto"} { + if {[info exists env(GATEWAY_INTERFACE)] + && [string match CGI/1.* $env(GATEWAY_INTERFACE)]} { + set mode cgi + } else { + set mode local + } + } + if {$mode=="cgi"} { + wappInt-handle-cgi-request + } else { + wappInt-start-listener $port $mode $fromip + if {!$nowait} { + vwait ::forever + } + } +} + +# Call this version 1.0 +package provide wapp 1.0 ADDED test/wapptest.tcl Index: test/wapptest.tcl ================================================================== --- /dev/null +++ test/wapptest.tcl @@ -0,0 +1,674 @@ +#!/bin/sh +# \ +exec wapptclsh "$0" ${1+"$@"} + +# package required wapp +source [file join [file dirname [info script]] wapp.tcl] + +# Read the data from the releasetest_data.tcl script. +# +source [file join [file dirname [info script]] releasetest_data.tcl] + +# Variables set by the "control" form: +# +# G(platform) - User selected platform. +# G(test) - Set to "Normal", "Veryquick", "Smoketest" or "Build-Only". +# G(keep) - Boolean. True to delete no files after each test. +# G(msvc) - Boolean. True to use MSVC as the compiler. +# G(tcl) - Use Tcl from this directory for builds. +# G(jobs) - How many sub-processes to run simultaneously. +# +set G(platform) $::tcl_platform(os)-$::tcl_platform(machine) +set G(test) Normal +set G(keep) 0 +set G(msvc) 0 +set G(tcl) [::tcl::pkgconfig get libdir,install] +set G(jobs) 3 +set G(debug) 0 + +proc wapptest_init {} { + global G + + set lSave [list platform test keep msvc tcl jobs debug] + foreach k $lSave { set A($k) $G($k) } + array unset G + foreach k $lSave { set G($k) $A($k) } + + # The root of the SQLite source tree. + set G(srcdir) [file dirname [file dirname [info script]]] + + # releasetest.tcl script + set G(releaseTest) [file join [file dirname [info script]] releasetest.tcl] + + set G(sqlite_version) "unknown" + + # Either "config", "running" or "stopped": + set G(state) "config" + + set G(hostname) "(unknown host)" + catch { set G(hostname) [exec hostname] } + set G(host) $G(hostname) + append G(host) " $::tcl_platform(os) $::tcl_platform(osVersion)" + append G(host) " $::tcl_platform(machine) $::tcl_platform(byteOrder)" +} + +# Check to see if there are uncommitted changes in the SQLite source +# directory. Return true if there are, or false otherwise. +# +proc check_uncommitted {} { + global G + set ret 0 + set pwd [pwd] + cd $G(srcdir) + if {[catch {exec fossil changes} res]==0 && [string trim $res]!=""} { + set ret 1 + } + cd $pwd + return $ret +} + +proc generate_fossil_info {} { + global G + set pwd [pwd] + cd $G(srcdir) + if {[catch {exec fossil info} r1]} return + if {[catch {exec fossil changes} r2]} return + cd $pwd + + foreach line [split $r1 "\n"] { + if {[regexp {^checkout: *(.*)$} $line -> co]} { + wapp-trim {
%html($co) } + } + } + + if {[string trim $r2]!=""} { + wapp-trim { +
+ WARNING: Uncommitted changes in checkout + + } + } +} + +# If the application is in "config" state, set the contents of the +# ::G(test_array) global to reflect the tests that will be run. If the +# app is in some other state ("running" or "stopped"), this command +# is a no-op. +# +proc set_test_array {} { + global G + if { $G(state)=="config" } { + set G(test_array) [list] + foreach {config target} $::Platforms($G(platform)) { + + # If using MSVC, do not run sanitize or valgrind tests. Or the + # checksymbols test. + if {$G(msvc) && ( + "Sanitize" == $config + || "checksymbols" in $target + || "valgrindtest" in $target + )} { + continue + } + + # If the test mode is not "Normal", override the target. + # + if {$target!="checksymbols" && $G(platform)!="Failure-Detection"} { + switch -- $G(test) { + Veryquick { set target quicktest } + Smoketest { set target smoketest } + Build-Only { + set target testfixture + if {$::tcl_platform(platform)=="windows"} { + set target testfixture.exe + } + } + } + } + + lappend G(test_array) [dict create config $config target $target] + + set exclude [list checksymbols valgrindtest fuzzoomtest] + if {$G(debug) && !($target in $exclude)} { + set debug_idx [lsearch -glob $::Configs($config) -DSQLITE_DEBUG*] + set xtarget $target + regsub -all {fulltest[a-z]*} $xtarget test xtarget + if {$debug_idx<0} { + lappend G(test_array) [ + dict create config $config-(Debug) target $xtarget + ] + } else { + lappend G(test_array) [ + dict create config $config-(NDebug) target $xtarget + ] + } + } + } + } +} + +proc count_tests_and_errors {name logfile} { + global G + + set fd [open $logfile rb] + set seen 0 + while {![eof $fd]} { + set line [gets $fd] + if {[regexp {(\d+) errors out of (\d+) tests} $line all nerr ntest]} { + incr G(test.$name.nError) $nerr + incr G(test.$name.nTest) $ntest + set seen 1 + if {$nerr>0} { + set G(test.$name.errmsg) $line + } + } + if {[regexp {runtime error: +(.*)} $line all msg]} { + # skip over "value is outside range" errors + if {[regexp {value .* is outside the range of representable} $line]} { + # noop + } else { + incr G(test.$name.nError) + if {$G(test.$name.errmsg)==""} { + set G(test.$name.errmsg) $msg + } + } + } + if {[regexp {fatal error +(.*)} $line all msg]} { + incr G(test.$name.nError) + if {$G(test.$name.errmsg)==""} { + set G(test.$name.errmsg) $msg + } + } + if {[regexp {ERROR SUMMARY: (\d+) errors.*} $line all cnt] && $cnt>0} { + incr G(test.$name.nError) + if {$G(test.$name.errmsg)==""} { + set G(test.$name.errmsg) $all + } + } + if {[regexp {^VERSION: 3\.\d+.\d+} $line]} { + set v [string range $line 9 end] + if {$G(sqlite_version) eq "unknown"} { + set G(sqlite_version) $v + } elseif {$G(sqlite_version) ne $v} { + set G(test.$name.errmsg) "version conflict: {$G(sqlite_version)} vs. {$v}" + } + } + } + close $fd + if {$G(test) == "Build-Only"} { + incr G(test.$name.nTest) + if {$G(test.$name.nError)>0} { + set errmsg "Build failed" + } + } elseif {!$seen} { + set G(test.$name.errmsg) "Test did not complete" + if {[file readable core]} { + append G(test.$name.errmsg) " - core file exists" + } + } +} + +proc slave_test_done {name rc} { + global G + set G(test.$name.done) [clock seconds] + set G(test.$name.nError) 0 + set G(test.$name.nTest) 0 + set G(test.$name.errmsg) "" + if {$rc} { + incr G(test.$name.nError) + } + if {[file exists $G(test.$name.log)]} { + count_tests_and_errors $name $G(test.$name.log) + } +} + +proc slave_fileevent {name} { + global G + set fd $G(test.$name.channel) + + if {[eof $fd]} { + fconfigure $fd -blocking 1 + set rc [catch { close $fd }] + unset G(test.$name.channel) + slave_test_done $name $rc + } else { + set line [gets $fd] + if {[string trim $line] != ""} { puts "Trace : $name - \"$line\"" } + } + + do_some_stuff +} + +proc do_some_stuff {} { + global G + + # Count the number of running jobs. A running job has an entry named + # "channel" in its dictionary. + set nRunning 0 + set bFinished 1 + foreach j $G(test_array) { + set name [dict get $j config] + if { [info exists G(test.$name.channel)]} { incr nRunning } + if {![info exists G(test.$name.done)]} { set bFinished 0 } + } + + if {$bFinished} { + set nError 0 + set nTest 0 + set nConfig 0 + foreach j $G(test_array) { + set name [dict get $j config] + incr nError $G(test.$name.nError) + incr nTest $G(test.$name.nTest) + incr nConfig + } + set G(result) "$nError errors from $nTest tests in $nConfig configurations." + catch { + append G(result) " SQLite version $G(sqlite_version)" + } + set G(state) "stopped" + } else { + set nLaunch [expr $G(jobs) - $nRunning] + foreach j $G(test_array) { + if {$nLaunch<=0} break + set name [dict get $j config] + if { ![info exists G(test.$name.channel)] + && ![info exists G(test.$name.done)] + } { + set target [dict get $j target] + set G(test.$name.start) [clock seconds] + set fd [open "|[info nameofexecutable] $G(releaseTest) --slave" r+] + set G(test.$name.channel) $fd + fconfigure $fd -blocking 0 + fileevent $fd readable [list slave_fileevent $name] + + puts $fd [list 0 $G(msvc) 0 $G(keep)] + + set wtcl "" + if {$G(tcl)!=""} { set wtcl "--with-tcl=$G(tcl)" } + + # If this configuration is named -(Debug) or -(NDebug), + # then add or remove the SQLITE_DEBUG option from the base + # configuration before running the test. + if {[regexp -- {(.*)-(\(.*\))} $name -> head tail]} { + set opts $::Configs($head) + if {$tail=="(Debug)"} { + append opts " -DSQLITE_DEBUG=1 -DSQLITE_EXTRA_IFNULLROW=1" + } else { + regsub { *-DSQLITE_MEMDEBUG[^ ]* *} $opts { } opts + regsub { *-DSQLITE_DEBUG[^ ]* *} $opts { } opts + } + } else { + set opts $::Configs($name) + } + + set L [make_test_suite $G(msvc) $wtcl $name $target $opts] + puts $fd $L + flush $fd + set G(test.$name.log) [file join [lindex $L 1] test.log] + incr nLaunch -1 + } + } + } +} + +proc generate_select_widget {label id lOpt opt} { + wapp-trim { + + } +} + +proc generate_main_page {{extra {}}} { + global G + set_test_array + + set hostname $G(hostname) + wapp-trim { + + + %html($hostname): wapptest.tcl + + + + } + + set host $G(host) + wapp-trim { +
%string($host) + } + generate_fossil_info + wapp-trim { +
+
+
+ } + + # Build the "platform" select widget. + set lOpt [array names ::Platforms] + generate_select_widget Platform control_platform $lOpt $G(platform) + + # Build the "test" select widget. + set lOpt [list Normal Veryquick Smoketest Build-Only] + generate_select_widget Test control_test $lOpt $G(test) + + # Build the "jobs" select widget. Options are 1 to 8. + generate_select_widget Jobs control_jobs {1 2 3 4 5 6 7 8} $G(jobs) + + switch $G(state) { + config { + set txt "Run Tests!" + set id control_run + } + running { + set txt "STOP Tests!" + set id control_stop + } + stopped { + set txt "Reset!" + set id control_reset + } + } + wapp-trim { +
+ + +
+ } + + wapp-trim { +

+ + + + + + + + + + + } + wapp-trim { +
+ } + wapp-trim { +
+
+ } + wapp-page-tests + + set script "script/$G(state).js" + wapp-trim { +
+ + + + } +} + +proc wapp-default {} { + generate_main_page +} + +proc wapp-page-tests {} { + global G + wapp-trim { } + foreach t $G(test_array) { + set config [dict get $t config] + set target [dict get $t target] + + set class "testwait" + set seconds "" + + if {[info exists G(test.$config.log)]} { + if {[info exists G(test.$config.channel)]} { + set class "testrunning" + set seconds [expr [clock seconds] - $G(test.$config.start)] + } elseif {[info exists G(test.$config.done)]} { + if {$G(test.$config.nError)>0} { + set class "testfail" + } else { + set class "testdone" + } + set seconds [expr $G(test.$config.done) - $G(test.$config.start)] + } + + set min [format %.2d [expr ($seconds / 60) % 60]] + set hr [format %.2d [expr $seconds / 3600]] + set sec [format %.2d [expr $seconds % 60]] + set seconds "$hr:$min:$sec" + } + + wapp-trim { + + +
%html($config) + %html($target) + %html($seconds) + + } + if {[info exists G(test.$config.log)]} { + set log $G(test.$config.log) + set uri "log/$log" + wapp-trim { + %html($log) + } + } + if {[info exists G(test.$config.errmsg)] && $G(test.$config.errmsg)!=""} { + set errmsg $G(test.$config.errmsg) + wapp-trim { +
%html($errmsg) + } + } + } + + wapp-trim {
} + + if {[info exists G(result)]} { + set res $G(result) + wapp-trim { +
%string($res)
+ } + } +} + +# URI: /control +# +# Whenever the form at the top of the application page is submitted, it +# is submitted here. +# +proc wapp-page-control {} { + global G + if {$::G(state)=="config"} { + set lControls [list platform test tcl jobs keep msvc debug] + set G(msvc) 0 + set G(keep) 0 + set G(debug) 0 + } else { + set lControls [list jobs] + } + foreach v $lControls { + if {[wapp-param-exists control_$v]} { + set G($v) [wapp-param control_$v] + } + } + + if {[wapp-param-exists control_run]} { + # This is a "run test" command. + set_test_array + set ::G(state) "running" + } + + if {[wapp-param-exists control_stop]} { + # A "STOP tests" command. + set G(state) "stopped" + set G(result) "Test halted by user" + foreach j $G(test_array) { + set name [dict get $j config] + if { [info exists G(test.$name.channel)] } { + close $G(test.$name.channel) + unset G(test.$name.channel) + slave_test_done $name 1 + } + } + } + + if {[wapp-param-exists control_reset]} { + # A "reset app" command. + set G(state) "config" + wapptest_init + } + + if {$::G(state) == "running"} { + do_some_stuff + } + wapp-redirect / +} + +# URI: /style.css +# +# Return the stylesheet for the application main page. +# +proc wapp-page-style.css {} { + wapp-subst { + + /* The boxes with black borders use this class */ + .border { + border: 3px groove #444444; + padding: 1em; + margin-top: 1em; + margin-bottom: 1em; + } + + /* Float to the right (used for the Run/Stop/Reset button) */ + .right { float: right; } + + /* Style for the large red warning at the top of the page */ + .warning { + color: red; + font-weight: bold; + } + + /* Styles used by cells in the test table */ + .padleft { padding-left: 5ex; } + .nowrap { white-space: nowrap; } + + /* Styles for individual tests, depending on the outcome */ + .testwait { } + .testrunning { color: blue } + .testdone { color: green } + .testfail { color: red } + } +} + +# URI: /script/${state}.js +# +# The last part of this URI is always "config.js", "running.js" or +# "stopped.js", depending on the state of the application. It returns +# the javascript part of the front-end for the requested state to the +# browser. +# +proc wapp-page-script {} { + regexp {[^/]*$} [wapp-param REQUEST_URI] script + + set tcl $::G(tcl) + set keep $::G(keep) + set msvc $::G(msvc) + set debug $::G(debug) + + wapp-subst { + var lElem = \["control_platform", "control_test", "control_msvc", + "control_jobs", "control_debug" + \]; + lElem.forEach(function(e) { + var elem = document.getElementById(e); + elem.addEventListener("change", function() { control.submit() } ); + }) + + elem = document.getElementById("control_tcl"); + elem.value = "%string($tcl)" + + elem = document.getElementById("control_keep"); + elem.checked = %string($keep); + + elem = document.getElementById("control_msvc"); + elem.checked = %string($msvc); + + elem = document.getElementById("control_debug"); + elem.checked = %string($debug); + } + + if {$script != "config.js"} { + wapp-subst { + var lElem = \["control_platform", "control_test", + "control_tcl", "control_keep", "control_msvc", + "control_debug" + \]; + lElem.forEach(function(e) { + var elem = document.getElementById(e); + elem.disabled = true; + }) + } + } + + if {$script == "running.js"} { + wapp-subst { + function reload_tests() { + fetch('tests') + .then( data => data.text() ) + .then( data => { + document.getElementById("tests").innerHTML = data; + }) + .then( data => { + if( document.getElementById("result") ){ + document.location = document.location; + } else { + setTimeout(reload_tests, 1000) + } + }); + } + + setTimeout(reload_tests, 1000) + } + } +} + +# URI: /env +# +# This is for debugging only. Serves no other purpose. +# +proc wapp-page-env {} { + wapp-allow-xorigin-params + wapp-trim { +

Wapp Environment

\n
+    
%html([wapp-debug-env])
+ } +} + +# URI: /log/dirname/test.log +# +# This URI reads file "dirname/test.log" from disk, wraps it in a
+# block, and returns it to the browser. Use for viewing log files.
+#
+proc wapp-page-log {} {
+  set log [string range [wapp-param REQUEST_URI] 5 end]
+  set fd [open $log]
+  set data [read $fd]
+  close $fd
+  wapp-trim {
+    
+    %html($data)
+    
+ } +} + +wapptest_init +wapp-start $argv + Index: test/where.test ================================================================== --- test/where.test +++ test/where.test @@ -1436,6 +1436,107 @@ CREATE TABLE t2(x INTEGER PRIMARY KEY, y INT); INSERT INTO t2(y) VALUES(2),(3); SELECT * FROM t1, t2 WHERE a=y AND y=3; } {3 2 3} +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test where-24.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(1, 'one'); + INSERT INTO t1 VALUES(2, 'two'); + INSERT INTO t1 VALUES(3, 'three'); + INSERT INTO t1 VALUES(4, 'four'); +} + +foreach {tn sql res} { + 1 "SELECT b FROM t1" {one two three four} + 2 "SELECT b FROM t1 WHERE a<4" {one two three} + 3 "SELECT b FROM t1 WHERE a>1" {two three four} + 4 "SELECT b FROM t1 WHERE a>1 AND a<4" {two three} + + 5 "SELECT b FROM t1 WHERE a>? AND a<4" {} + 6 "SELECT b FROM t1 WHERE a>1 AND a? AND a=? AND a<=4" {} + 8 "SELECT b FROM t1 WHERE a>=1 AND a<=?" {} + 9 "SELECT b FROM t1 WHERE a>=? AND a<=?" {} +} { + set rev [list] + foreach r $res { set rev [concat $r $rev] } + + do_execsql_test where-24.$tn.1 "$sql" $res + do_execsql_test where-24.$tn.2 "$sql ORDER BY rowid" $res + do_execsql_test where-24.$tn.3 "$sql ORDER BY rowid DESC" $rev + + do_execsql_test where-24-$tn.4 " + BEGIN; + DELETE FROM t1; + $sql; + $sql ORDER BY rowid; + $sql ORDER BY rowid DESC; + ROLLBACK; + " +} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test where-25.0 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c); + CREATE UNIQUE INDEX i1 ON t1(c); + INSERT INTO t1 VALUES(1, 'one', 'i'); + INSERT INTO t1 VALUES(2, 'two', 'ii'); + + CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); + CREATE UNIQUE INDEX i2 ON t2(c); + INSERT INTO t2 VALUES(1, 'one', 'i'); + INSERT INTO t2 VALUES(2, 'two', 'ii'); + INSERT INTO t2 VALUES(3, 'three', 'iii'); + + PRAGMA writable_schema = 1; + UPDATE sqlite_master SET rootpage = ( + SELECT rootpage FROM sqlite_master WHERE name = 'i2' + ) WHERE name = 'i1'; +} +db close +sqlite3 db test.db +do_catchsql_test where-25.1 { + DELETE FROM t1 WHERE c='iii' +} {1 {database disk image is malformed}} +do_catchsql_test where-25.2 { + INSERT INTO t1 VALUES(4, 'four', 'iii') + ON CONFLICT(c) DO UPDATE SET b=NULL +} {1 {database disk image is malformed}} + +reset_db +do_execsql_test where-25.3 { + CREATE TABLE t1(a PRIMARY KEY, b, c) WITHOUT ROWID; + CREATE UNIQUE INDEX i1 ON t1(c); + INSERT INTO t1 VALUES(1, 'one', 'i'); + INSERT INTO t1 VALUES(2, 'two', 'ii'); + + CREATE TABLE t2(a INTEGER PRIMARY KEY, b, c); + CREATE UNIQUE INDEX i2 ON t2(c); + INSERT INTO t2 VALUES(1, 'one', 'i'); + INSERT INTO t2 VALUES(2, 'two', 'ii'); + INSERT INTO t2 VALUES(3, 'three', 'iii'); + + PRAGMA writable_schema = 1; + UPDATE sqlite_master SET rootpage = ( + SELECT rootpage FROM sqlite_master WHERE name = 'i2' + ) WHERE name = 'i1'; +} +db close +sqlite3 db test.db +do_catchsql_test where-25.4 { + SELECT * FROM t1 WHERE c='iii' +} {0 {}} +do_catchsql_test where-25.5 { + INSERT INTO t1 VALUES(4, 'four', 'iii') + ON CONFLICT(c) DO UPDATE SET b=NULL +} {1 {corrupt database}} + finish_test + Index: test/window1.test ================================================================== --- test/window1.test +++ test/window1.test @@ -717,35 +717,456 @@ do_execsql_test 17.3 { SELECT 10+sum(a) OVER (ORDER BY a) FROM t8 ORDER BY 10+sum(a) OVER (ORDER BY a) DESC; } {16 13 11} + #------------------------------------------------------------------------- +# Test error cases from chaining window definitions. # reset_db do_execsql_test 18.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c TEXT, d INTEGER); + INSERT INTO t1 VALUES(1, 'odd', 'one', 1); + INSERT INTO t1 VALUES(2, 'even', 'two', 2); + INSERT INTO t1 VALUES(3, 'odd', 'three', 3); + INSERT INTO t1 VALUES(4, 'even', 'four', 4); + INSERT INTO t1 VALUES(5, 'odd', 'five', 5); + INSERT INTO t1 VALUES(6, 'even', 'six', 6); +} + +foreach {tn sql error} { + 1 { + SELECT c, sum(d) OVER win2 FROM t1 + WINDOW win1 AS (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING), + win2 AS (win1 ORDER BY b) + } {cannot override frame specification of window: win1} + + 2 { + SELECT c, sum(d) OVER win2 FROM t1 + WINDOW win1 AS (), + win2 AS (win4 ORDER BY b) + } {no such window: win4} + + 3 { + SELECT c, sum(d) OVER win2 FROM t1 + WINDOW win1 AS (), + win2 AS (win1 PARTITION BY d) + } {cannot override PARTITION clause of window: win1} + + 4 { + SELECT c, sum(d) OVER win2 FROM t1 + WINDOW win1 AS (ORDER BY b), + win2 AS (win1 ORDER BY d) + } {cannot override ORDER BY clause of window: win1} +} { + do_catchsql_test 18.1.$tn $sql [list 1 $error] +} + +foreach {tn sql error} { + 1 { + SELECT c, sum(d) OVER (win1 ORDER BY b) FROM t1 + WINDOW win1 AS (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) + } {cannot override frame specification of window: win1} + + 2 { + SELECT c, sum(d) OVER (win4 ORDER BY b) FROM t1 + WINDOW win1 AS () + } {no such window: win4} + + 3 { + SELECT c, sum(d) OVER (win1 PARTITION BY d) FROM t1 + WINDOW win1 AS () + } {cannot override PARTITION clause of window: win1} + + 4 { + SELECT c, sum(d) OVER (win1 ORDER BY d) FROM t1 + WINDOW win1 AS (ORDER BY b) + } {cannot override ORDER BY clause of window: win1} +} { + do_catchsql_test 18.2.$tn $sql [list 1 $error] +} + +do_execsql_test 18.3.1 { + SELECT group_concat(c, '.') OVER (PARTITION BY b ORDER BY c) + FROM t1 +} {four four.six four.six.two five five.one five.one.three} + +do_execsql_test 18.3.2 { + SELECT group_concat(c, '.') OVER (win1 ORDER BY c) + FROM t1 + WINDOW win1 AS (PARTITION BY b) +} {four four.six four.six.two five five.one five.one.three} + +do_execsql_test 18.3.3 { + SELECT group_concat(c, '.') OVER win2 + FROM t1 + WINDOW win1 AS (PARTITION BY b), + win2 AS (win1 ORDER BY c) +} {four four.six four.six.two five five.one five.one.three} + +do_execsql_test 18.3.4 { + SELECT group_concat(c, '.') OVER (win2) + FROM t1 + WINDOW win1 AS (PARTITION BY b), + win2 AS (win1 ORDER BY c) +} {four four.six four.six.two five five.one five.one.three} + +do_execsql_test 18.3.5 { + SELECT group_concat(c, '.') OVER win5 + FROM t1 + WINDOW win1 AS (PARTITION BY b), + win2 AS (win1), + win3 AS (win2), + win4 AS (win3), + win5 AS (win4 ORDER BY c) +} {four four.six four.six.two five five.one five.one.three} + +#------------------------------------------------------------------------- +# Test RANGE PRECEDING/FOLLOWING when there are string, blob +# and NULL values in the dataset. +# +reset_db +do_execsql_test 19.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES + (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), + ('a', 6), ('b', 7), ('c', 8), ('d', 9), ('e', 10); +} +do_execsql_test 19.1 { + SELECT a, sum(b) OVER (ORDER BY a) FROM t1; +} {1 1 2 3 3 6 4 10 5 15 a 21 b 28 c 36 d 45 e 55} + +do_execsql_test 19.2.1 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1; +} {1 3 2 6 3 9 4 12 5 9 a 6 b 7 c 8 d 9 e 10} +do_execsql_test 19.2.2 { + SELECT a, sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY a ASC; +} {1 3 2 6 3 9 4 12 5 9 a 6 b 7 c 8 d 9 e 10} + +do_execsql_test 19.3.1 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING + ) FROM t1; +} {1 3 2 6 3 10 4 14 5 12 a 6 b 7 c 8 d 9 e 10} +do_execsql_test 19.3.2 { + SELECT a, sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND 2 FOLLOWING + ) FROM t1 ORDER BY a ASC; +} {1 3 2 6 3 10 4 14 5 12 a 6 b 7 c 8 d 9 e 10} + + +reset_db +do_execsql_test 20.0 { + CREATE TABLE t1(a, b); + INSERT INTO t1 VALUES + (NULL, 100), (NULL, 100), + (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), + ('a', 6), ('b', 7), ('c', 8), ('d', 9), ('e', 10); +} +do_execsql_test 20.1 { + SELECT a, sum(b) OVER (ORDER BY a) FROM t1; +} { + {} 200 {} 200 1 201 2 203 3 206 4 210 5 215 + a 221 b 228 c 236 d 245 e 255 +} + +do_execsql_test 20.2.1 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1; +} {{} 200 {} 200 1 3 2 6 3 9 4 12 5 9 a 6 b 7 c 8 d 9 e 10} +do_execsql_test 20.2.2 { + SELECT a, sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY a ASC; +} {{} 200 {} 200 1 3 2 6 3 9 4 12 5 9 a 6 b 7 c 8 d 9 e 10} + +do_execsql_test 20.3.1 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING + ) FROM t1; +} {{} 200 {} 200 1 3 2 6 3 10 4 14 5 12 a 6 b 7 c 8 d 9 e 10} +do_execsql_test 20.3.2 { + SELECT a, sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 1 PRECEDING AND 2 FOLLOWING + ) FROM t1 ORDER BY a ASC; +} {{} 200 {} 200 1 3 2 6 3 10 4 14 5 12 a 6 b 7 c 8 d 9 e 10} + +#------------------------------------------------------------------------- +do_execsql_test 21.0 { + CREATE TABLE keyword_tab( + current, exclude, filter, following, groups, no, others, over, + partition, preceding, range, ties, unbounded, window + ); +} +do_execsql_test 21.1 { + SELECT + current, exclude, filter, following, groups, no, others, over, + partition, preceding, range, ties, unbounded, window + FROM keyword_tab +} + +#------------------------------------------------------------------------- +foreach {tn expr err} { + 1 4.5 0 + 2 NULL 1 + 3 0.0 0 + 4 0.1 0 + 5 -0.1 1 + 6 '' 1 + 7 '2.0' 0 + 8 '2.0x' 1 + 9 x'1234' 1 + 10 '1.2' 0 +} { + set res {0 1} + if {$err} {set res {1 {frame starting offset must be a non-negative number}} } + do_catchsql_test 22.$tn.1 " + WITH a(x, y) AS ( VALUES(1, 2) ) + SELECT sum(x) OVER ( + ORDER BY y RANGE BETWEEN $expr PRECEDING AND UNBOUNDED FOLLOWING + ) FROM a + " $res + + set res {0 1} + if {$err} {set res {1 {frame ending offset must be a non-negative number}} } + do_catchsql_test 22.$tn.2 " + WITH a(x, y) AS ( VALUES(1, 2) ) + SELECT sum(x) OVER ( + ORDER BY y RANGE BETWEEN UNBOUNDED PRECEDING AND $expr FOLLOWING + ) FROM a + " $res +} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 23.0 { + CREATE TABLE t5(a, b, c); + CREATE INDEX t5ab ON t5(a, b); +} + +proc do_ordercount_test {tn sql nOrderBy} { + set plan [execsql "EXPLAIN QUERY PLAN $sql"] + uplevel [list do_test $tn [list regexp -all ORDER $plan] $nOrderBy] +} + +do_ordercount_test 23.1 { + SELECT + sum(c) OVER (ORDER BY a, b), + sum(c) OVER (PARTITION BY a ORDER BY b) + FROM t5 +} 0 + +do_ordercount_test 23.2 { + SELECT + sum(c) OVER (ORDER BY b, a), + sum(c) OVER (PARTITION BY b ORDER BY a) + FROM t5 +} 1 + +do_ordercount_test 23.3 { + SELECT + sum(c) OVER (ORDER BY b, a), + sum(c) OVER (ORDER BY c, b) + FROM t5 +} 2 + +do_ordercount_test 23.4 { + SELECT + sum(c) OVER (ORDER BY b ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), + sum(c) OVER (ORDER BY b RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW), + sum(c) OVER (ORDER BY b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) + FROM t5 +} 1 + +do_ordercount_test 23.5 { + SELECT + sum(c) OVER (ORDER BY b+1 ROWS UNBOUNDED PRECEDING), + sum(c) OVER (ORDER BY b+1 RANGE UNBOUNDED PRECEDING), + sum(c) OVER (ORDER BY b+1 GROUPS UNBOUNDED PRECEDING) + FROM t5 +} 1 + +do_ordercount_test 23.6 { + SELECT + sum(c) OVER (ORDER BY b+1 ROWS UNBOUNDED PRECEDING), + sum(c) OVER (ORDER BY b+2 RANGE UNBOUNDED PRECEDING), + sum(c) OVER (ORDER BY b+3 GROUPS UNBOUNDED PRECEDING) + FROM t5 +} 3 + +do_execsql_test 24.1 { + SELECT sum(44) OVER () +} {44} + +do_execsql_test 24.2 { + SELECT lead(44) OVER () +} {{}} + +#------------------------------------------------------------------------- +# +reset_db +do_execsql_test 25.0 { CREATE TABLE t1 ( t1_id INTEGER PRIMARY KEY ); CREATE TABLE t2 ( t2_id INTEGER PRIMARY KEY ); CREATE TABLE t3 ( t3_id INTEGER PRIMARY KEY ); INSERT INTO t1 VALUES(1), (3), (5); INSERT INTO t2 VALUES (3), (5); INSERT INTO t3 VALUES(10), (11), (12); } -do_execsql_test 18.1 { +do_execsql_test 25.1 { SELECT t1.* FROM t1, t2 WHERE t1_id=t2_id AND t1_id IN ( SELECT t1_id + row_number() OVER ( ORDER BY t1_id ) FROM t3 ) } -do_execsql_test 18.2 { +do_execsql_test 25.2 { SELECT t1.* FROM t1, t2 WHERE t1_id=t2_id AND t1_id IN ( SELECT row_number() OVER ( ORDER BY t1_id ) FROM t3 ) } {3} +#------------------------------------------------------------------------- +reset_db +do_execsql_test 26.0 { + CREATE TABLE t1(x); + CREATE TABLE t2(c); +} + +do_execsql_test 26.1 { + SELECT ( SELECT row_number() OVER () FROM ( SELECT c FROM t1 ) ) FROM t2 +} {} + +do_execsql_test 26.2 { + INSERT INTO t1 VALUES(1), (2), (3), (4); + INSERT INTO t2 VALUES(2), (6), (8), (4); + SELECT c, c IN ( + SELECT row_number() OVER () FROM ( SELECT c FROM t1 ) + ) FROM t2 +} {2 1 6 0 8 0 4 1} + +do_execsql_test 26.3 { + DELETE FROM t1; + DELETE FROM t2; + + INSERT INTO t2 VALUES(1), (2), (3), (4); + INSERT INTO t1 VALUES(1), (1), (2), (3), (3), (3), (3), (4), (4); + + SELECT c, c IN ( + SELECT row_number() OVER () FROM ( SELECT 1 FROM t1 WHERE x=c ) + ) FROM t2 +} {1 1 2 0 3 1 4 0} + +#------------------------------------------------------------------------- +reset_db +do_execsql_test 27.0 { + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(NULL), (1), (2), (3), (4), (5); +} +do_execsql_test 27.1 { + SELECT min(x) FROM t1; +} {1} +do_execsql_test 27.2 { + SELECT min(x) OVER win FROM t1 + WINDOW win AS (ORDER BY rowid ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING) +} {1 1 1 2 3 4} + +#------------------------------------------------------------------------- + +reset_db +do_execsql_test 28.1.1 { + CREATE TABLE t1(a INTEGER PRIMARY KEY, b CHAR(1), c CHAR(2), d ANY); + INSERT INTO t1 VALUES (3, 'C', 'cc', 1.0); + INSERT INTO t1 VALUES (13,'M', 'cc', NULL); +} + +do_execsql_test 28.1.2 { + SELECT group_concat(b,'') OVER w1 FROM t1 + WINDOW w1 AS (ORDER BY a RANGE BETWEEN 3 PRECEDING AND 1 PRECEDING) +} { + {} {} +} + +do_execsql_test 28.2.1 { + CREATE TABLE t2(a TEXT, b INTEGER); + INSERT INTO t2 VALUES('A', NULL); + INSERT INTO t2 VALUES('B', NULL); +} + +do_execsql_test 28.2.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b CHAR(1), c CHAR(2), d ANY); + INSERT INTO t1 VALUES + (10,'J', 'cc', NULL), + (11,'K', 'cc', 'xyz'), + (13,'M', 'cc', NULL); +} + +do_execsql_test 28.2.2 { + SELECT a, b, c, quote(d), group_concat(b,'') OVER w1, '|' FROM t1 + WINDOW w1 AS + (ORDER BY d DESC RANGE BETWEEN 7.0 PRECEDING AND 2.5 PRECEDING) + ORDER BY c, d, a; +} { + 10 J cc NULL JM | + 13 M cc NULL JM | + 11 K cc 'xyz' K | +} + +#------------------------------------------------------------------------- +reset_db + +do_execsql_test 29.1 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b CHAR(1), c CHAR(2), d ANY); + INSERT INTO t1 VALUES + (1, 'A', 'aa', 2.5), + (2, 'B', 'bb', 3.75), + (3, 'C', 'cc', 1.0), + (4, 'D', 'cc', 8.25), + (5, 'E', 'bb', 6.5), + (6, 'F', 'aa', 6.5), + (7, 'G', 'aa', 6.0), + (8, 'H', 'bb', 9.0), + (9, 'I', 'aa', 3.75), + (10,'J', 'cc', NULL), + (11,'K', 'cc', 'xyz'), + (12,'L', 'cc', 'xyZ'), + (13,'M', 'cc', NULL); +} + +do_execsql_test 29.2 { + SELECT a, b, c, quote(d), group_concat(b,'') OVER w1, '|' FROM t1 + WINDOW w1 AS + (PARTITION BY c ORDER BY d DESC + RANGE BETWEEN 7.0 PRECEDING AND 2.5 PRECEDING) + ORDER BY c, d, a; +} { + 1 A aa 2.5 FG | + 9 I aa 3.75 F | + 7 G aa 6 {} | + 6 F aa 6.5 {} | + 2 B bb 3.75 HE | + 5 E bb 6.5 H | + 8 H bb 9 {} | + 10 J cc NULL JM | + 13 M cc NULL JM | + 3 C cc 1 {} | + 4 D cc 8.25 {} | + 12 L cc 'xyZ' L | + 11 K cc 'xyz' K | +} finish_test + + Index: test/window3.tcl ================================================================== --- test/window3.tcl +++ test/window3.tcl @@ -19,11 +19,10 @@ execsql_test 1.0 { DROP TABLE IF EXISTS t2; CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER); INSERT INTO t2(a, b) VALUES - (1,0), (2,74), (3,41), (4,74), (5,23), (6,99), (7,26), (8,33), (9,2), (10,89), (11,81), (12,96), (13,59), (14,38), (15,68), (16,39), (17,62), (18,91), (19,46), (20,6), (21,99), (22,97), (23,27), (24,46), (25,78), (26,54), (27,97), (28,8), (29,67), (30,29), (31,93), (32,84), (33,77), (34,23), (35,16), (36,16), (37,93), (38,65), (39,35), (40,47), (41,7), (42,86), (43,74), (44,61), (45,91), (46,85), (47,24), (48,85), (49,43), @@ -71,10 +70,15 @@ 13 "ROWS BETWEEN 2 FOLLOWING AND 4 FOLLOWING" 14 "ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING" 15 "ROWS BETWEEN 4 PRECEDING AND UNBOUNDED FOLLOWING" 16 "ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING" 17 "ROWS BETWEEN 4 FOLLOWING AND UNBOUNDED FOLLOWING" + + 18 "ROWS BETWEEN 4 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW" + 19 "ROWS BETWEEN 4 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES" + 20 "ROWS BETWEEN 4 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP" + } { execsql_test 1.$tn.2.1 "SELECT max(b) OVER ( ORDER BY a $window ) FROM t2" execsql_test 1.$tn.2.2 "SELECT min(b) OVER ( ORDER BY a $window ) FROM t2" execsql_test 1.$tn.3.1 " @@ -304,10 +308,32 @@ SELECT string_agg(CAST(b AS TEXT), '.') OVER ( ORDER BY b%10,a $window ) FROM t2 " execsql_test 1.$tn.14.6 " SELECT string_agg(CAST(b AS TEXT), '.') OVER (PARTITION BY b%2,a ORDER BY b%10 $window) FROM t2 " + + execsql_test 1.$tn.14.7 " + SELECT string_agg(CAST(b AS TEXT), '.') OVER (win1 ORDER BY b%10 $window) + FROM t2 + WINDOW win1 AS (PARTITION BY b%2,a) + ORDER BY 1 + " + + execsql_test 1.$tn.14.8 " + SELECT string_agg(CAST(b AS TEXT), '.') OVER (win1 $window) + FROM t2 + WINDOW win1 AS (PARTITION BY b%2,a ORDER BY b%10) + ORDER BY 1 + " + + execsql_test 1.$tn.14.9 " + SELECT string_agg(CAST(b AS TEXT), '.') OVER win2 + FROM t2 + WINDOW win1 AS (PARTITION BY b%2,a ORDER BY b%10), + win2 AS (win1 $window) + ORDER BY 1 + " execsql_test 1.$tn.15.1 " SELECT count(*) OVER win, string_agg(CAST(b AS TEXT), '.') FILTER (WHERE a%2=0) OVER win FROM t2 WINDOW win AS (ORDER BY a $window) Index: test/window3.test ================================================================== --- test/window3.test +++ test/window3.test cannot compute difference between binary files Index: test/window4.tcl ================================================================== --- test/window4.tcl +++ test/window4.tcl @@ -1,6 +1,6 @@ -# 2018 May 19 +## 2018 May 19 # # The author disclaims copyright to this source code. In place of # a legal notice, here is a blessing: # # May you do good and not evil. Index: test/window4.test ================================================================== --- test/window4.test +++ test/window4.test @@ -148,11 +148,12 @@ do_execsql_test 2.4.1 { SELECT group_concat(b, '.') OVER ( ORDER BY a ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) FROM t4 -} {A.B.C.D.E.F.G.H.I.J B.C.D.E.F.G.H.I.J C.D.E.F.G.H.I.J D.E.F.G.H.I.J E.F.G.H.I.J F.G.H.I.J G.H.I.J H.I.J I.J J} +} {A.B.C.D.E.F.G.H.I.J B.C.D.E.F.G.H.I.J C.D.E.F.G.H.I.J D.E.F.G.H.I.J + E.F.G.H.I.J F.G.H.I.J G.H.I.J H.I.J I.J J} do_execsql_test 3.0 { DROP TABLE IF EXISTS t5; CREATE TABLE t5(a INTEGER PRIMARY KEY, b TEXT, c TEXT, d INTEGER); INSERT INTO t5 VALUES(1, 'A', 'one', 5); @@ -1222,14 +1223,16 @@ set myres {} foreach r [db eval {SELECT x, percent_rank() OVER (PARTITION BY x ORDER BY x) FROM t2}] { lappend myres [format %.4f [set r]] } set res2 {1.0000 0.0000 1.0000 0.0000 1.0000 0.0000 4.0000 0.0000 4.0000 0.0000 6.0000 0.0000 7.0000 0.0000} + set i 0 foreach r [set myres] r2 [set res2] { if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { error "list element [set i] does not match: got=[set r] expected=[set r2]" } + incr i } set {} {} } {} do_execsql_test 9.4 { @@ -1245,14 +1248,16 @@ set myres {} foreach r [db eval {SELECT percent_rank() OVER () FROM t1}] { lappend myres [format %.4f [set r]] } set res2 {0.0000 0.0000 0.0000} + set i 0 foreach r [set myres] r2 [set res2] { if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { error "list element [set i] does not match: got=[set r] expected=[set r2]" } + incr i } set {} {} } {} @@ -1260,14 +1265,16 @@ set myres {} foreach r [db eval {SELECT cume_dist() OVER () FROM t1}] { lappend myres [format %.4f [set r]] } set res2 {1.0000 1.0000 1.0000} + set i 0 foreach r [set myres] r2 [set res2] { if {[set r]<([set r2]-0.0001) || [set r]>([set r2]+0.0001)} { error "list element [set i] does not match: got=[set r] expected=[set r2]" } + incr i } set {} {} } {} do_execsql_test 10.0 { Index: test/window6.test ================================================================== --- test/window6.test +++ test/window6.test @@ -217,21 +217,21 @@ SELECT x, group_concat(x) OVER (ORDER BY x ROWS 2 PRECEDING) FROM c; } { 1 1 2 1,2 3 1,2,3 4 2,3,4 5 3,4,5 } -do_catchsql_test 9.1 { - WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) - SELECT x, group_concat(x) OVER (ORDER BY x RANGE 2 PRECEDING) - FROM c; -} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}} - -do_catchsql_test 9.2 { - WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) - SELECT x, group_concat(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING) - FROM c; -} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}} +#do_catchsql_test 9.1 { +# WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) +# SELECT x, group_concat(x) OVER (ORDER BY x RANGE 2 PRECEDING) +# FROM c; +#} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}} +# +#do_catchsql_test 9.2 { +# WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) +# SELECT x, group_concat(x) OVER (ORDER BY x RANGE BETWEEN UNBOUNDED PRECEDING AND 2 FOLLOWING) +# FROM c; +#} {1 {RANGE must use only UNBOUNDED or CURRENT ROW}} do_catchsql_test 9.3 { WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) SELECT count(DISTINCT x) OVER (ORDER BY x) FROM c; } {1 {DISTINCT is not supported for window functions}} @@ -260,11 +260,11 @@ do_catchsql_test 9.7.$tn " WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) SELECT count() OVER ( ORDER BY x ROWS $frame ) FROM c; - " {1 {unsupported frame delimiter for ROWS}} + " {1 {unsupported frame specification}} } do_catchsql_test 9.8.1 { WITH RECURSIVE c(x) AS (VALUES(1) UNION ALL SELECT x+1 FROM c WHERE x<5) SELECT count() OVER ( @@ -334,7 +334,38 @@ FROM t1 ORDER BY a; } { 10 ten 10 15 fifteen 25 20 {} 65 20 {} 65 25 {} 90 30 thirty 150 30 thirty 150 50 {} 200 } + +do_execsql_test 11.3.1 { + SELECT a, sum(a) OVER win FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) +} { + 10 10 15 25 20 45 20 65 25 90 30 120 30 150 50 200 +} +do_execsql_test 11.3.2 { + SELECT a, sum(a) OVER win FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN UNBOUNDED PRECEDING AND 0 FOLLOWING) +} { + 10 10 15 25 20 45 20 65 25 90 30 120 30 150 50 200 +} +do_execsql_test 11.3.3 { + SELECT a, sum(a) OVER win FROM t1 + WINDOW win AS (ORDER BY a ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) +} { + 10 10 15 25 20 45 20 65 25 90 30 120 30 150 50 200 +} + +do_execsql_test 11.4.1 { + SELECT y, group_concat(y, '.') OVER win FROM t3 + WINDOW win AS ( + ORDER BY y RANGE BETWEEN UNBOUNDED PRECEDING AND 10 PRECEDING + ); +} { + fifteen fifteen + ten fifteen.ten + thirty fifteen.ten.thirty +} finish_test + ADDED test/window7.tcl Index: test/window7.tcl ================================================================== --- /dev/null +++ test/window7.tcl @@ -0,0 +1,91 @@ +# 2018 May 19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +source [file join [file dirname $argv0] pg_common.tcl] + +#========================================================================= + +start_test window7 "2019 March 01" +ifcapable !windowfunc + +execsql_test 1.0 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a INTEGER, b INTEGER); + INSERT INTO t3 VALUES + (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), + (9, 9), (0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16), + (7, 17), (8, 18), (9, 19), (0, 20), (1, 21), (2, 22), (3, 23), (4, 24), + (5, 25), (6, 26), (7, 27), (8, 28), (9, 29), (0, 30), (1, 31), (2, 32), + (3, 33), (4, 34), (5, 35), (6, 36), (7, 37), (8, 38), (9, 39), (0, 40), + (1, 41), (2, 42), (3, 43), (4, 44), (5, 45), (6, 46), (7, 47), (8, 48), + (9, 49), (0, 50), (1, 51), (2, 52), (3, 53), (4, 54), (5, 55), (6, 56), + (7, 57), (8, 58), (9, 59), (0, 60), (1, 61), (2, 62), (3, 63), (4, 64), + (5, 65), (6, 66), (7, 67), (8, 68), (9, 69), (0, 70), (1, 71), (2, 72), + (3, 73), (4, 74), (5, 75), (6, 76), (7, 77), (8, 78), (9, 79), (0, 80), + (1, 81), (2, 82), (3, 83), (4, 84), (5, 85), (6, 86), (7, 87), (8, 88), + (9, 89), (0, 90), (1, 91), (2, 92), (3, 93), (4, 94), (5, 95), (6, 96), + (7, 97), (8, 98), (9, 99), (0, 100); +} + +execsql_test 1.1 { + SELECT a, sum(b) FROM t3 GROUP BY a ORDER BY 1; +} + +execsql_test 1.2 { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN CURRENT ROW AND CURRENT ROW + ) FROM t3 ORDER BY 1; +} + +execsql_test 1.3 { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING + ) FROM t3 ORDER BY 1; +} + +execsql_test 1.4 { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING + ) FROM t3 ORDER BY 1; +} + +execsql_test 1.5 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING + ) FROM t3 ORDER BY 1; +} + +execsql_test 1.6 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING + ) FROM t3 ORDER BY 1; +} + +execsql_test 1.7 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING + ) FROM t3 ORDER BY 1; +} + +execsql_test 1.8.1 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 0 PRECEDING AND 1 FOLLOWING + ) FROM t3 ORDER BY 1; +} +execsql_test 1.8.2 { + SELECT a, sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 0 PRECEDING AND 1 FOLLOWING + ) FROM t3 ORDER BY 1; +} + +finish_test + ADDED test/window7.test Index: test/window7.test ================================================================== --- /dev/null +++ test/window7.test @@ -0,0 +1,94 @@ +# 2019 March 01 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +#################################################### +# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED! +#################################################### + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix window7 + +ifcapable !windowfunc { finish_test ; return } +do_execsql_test 1.0 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a INTEGER, b INTEGER); + INSERT INTO t3 VALUES + (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8), + (9, 9), (0, 10), (1, 11), (2, 12), (3, 13), (4, 14), (5, 15), (6, 16), + (7, 17), (8, 18), (9, 19), (0, 20), (1, 21), (2, 22), (3, 23), (4, 24), + (5, 25), (6, 26), (7, 27), (8, 28), (9, 29), (0, 30), (1, 31), (2, 32), + (3, 33), (4, 34), (5, 35), (6, 36), (7, 37), (8, 38), (9, 39), (0, 40), + (1, 41), (2, 42), (3, 43), (4, 44), (5, 45), (6, 46), (7, 47), (8, 48), + (9, 49), (0, 50), (1, 51), (2, 52), (3, 53), (4, 54), (5, 55), (6, 56), + (7, 57), (8, 58), (9, 59), (0, 60), (1, 61), (2, 62), (3, 63), (4, 64), + (5, 65), (6, 66), (7, 67), (8, 68), (9, 69), (0, 70), (1, 71), (2, 72), + (3, 73), (4, 74), (5, 75), (6, 76), (7, 77), (8, 78), (9, 79), (0, 80), + (1, 81), (2, 82), (3, 83), (4, 84), (5, 85), (6, 86), (7, 87), (8, 88), + (9, 89), (0, 90), (1, 91), (2, 92), (3, 93), (4, 94), (5, 95), (6, 96), + (7, 97), (8, 98), (9, 99), (0, 100); +} {} + +do_execsql_test 1.1 { + SELECT a, sum(b) FROM t3 GROUP BY a ORDER BY 1; +} {0 550 1 460 2 470 3 480 4 490 5 500 6 510 7 520 8 530 9 540} + +do_execsql_test 1.2 { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN CURRENT ROW AND CURRENT ROW + ) FROM t3 ORDER BY 1; +} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540} + +do_execsql_test 1.3 { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN 0 PRECEDING AND 0 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540} + +do_execsql_test 1.4 { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 2 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590} + +do_execsql_test 1.5 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 1 460 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 2 470 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 3 480 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 4 490 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 5 500 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 6 510 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 7 520 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 8 530 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540} + +do_execsql_test 1.6 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 2 PRECEDING AND 2 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 0 1480 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 1 1960 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 2 2450 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 3 2400 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 4 2450 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 5 2500 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 6 2550 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 7 2600 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590} + +do_execsql_test 1.7 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 2 PRECEDING AND 1 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 1 1480 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 2 1960 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 3 1900 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 4 1940 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 5 1980 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 6 2020 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 7 2060 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 8 2100 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590 9 1590} + +do_execsql_test 1.8.1 { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 0 PRECEDING AND 1 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 0 1010 1 930 1 930 1 930 1 930 1 930 1 930 1 930 1 930 1 930 1 930 2 950 2 950 2 950 2 950 2 950 2 950 2 950 2 950 2 950 2 950 3 970 3 970 3 970 3 970 3 970 3 970 3 970 3 970 3 970 3 970 4 990 4 990 4 990 4 990 4 990 4 990 4 990 4 990 4 990 4 990 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 5 1010 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 6 1030 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 7 1050 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 8 1070 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540 9 540} + +do_execsql_test 1.8.2 { + SELECT a, sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 0 PRECEDING AND 1 FOLLOWING + ) FROM t3 ORDER BY 1; +} {0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 0 550 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 1 1010 2 930 2 930 2 930 2 930 2 930 2 930 2 930 2 930 2 930 2 930 3 950 3 950 3 950 3 950 3 950 3 950 3 950 3 950 3 950 3 950 4 970 4 970 4 970 4 970 4 970 4 970 4 970 4 970 4 970 4 970 5 990 5 990 5 990 5 990 5 990 5 990 5 990 5 990 5 990 5 990 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 6 1010 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 7 1030 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 8 1050 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070 9 1070} + +finish_test ADDED test/window8.tcl Index: test/window8.tcl ================================================================== --- /dev/null +++ test/window8.tcl @@ -0,0 +1,299 @@ +# 2018 May 19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +source [file join [file dirname $argv0] pg_common.tcl] + +#========================================================================= + +start_test window8 "2019 March 01" +ifcapable !windowfunc + +execsql_test 1.0 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a TEXT, b TEXT, c INTEGER); + INSERT INTO t3 VALUES + ('HH', 'bb', 355), ('CC', 'aa', 158), ('BB', 'aa', 399), + ('FF', 'bb', 938), ('HH', 'aa', 480), ('FF', 'bb', 870), + ('JJ', 'aa', 768), ('JJ', 'aa', 899), ('GG', 'bb', 929), + ('II', 'bb', 421), ('GG', 'bb', 844), ('FF', 'bb', 574), + ('CC', 'bb', 822), ('GG', 'bb', 938), ('BB', 'aa', 660), + ('HH', 'aa', 979), ('BB', 'bb', 792), ('DD', 'aa', 845), + ('JJ', 'bb', 354), ('FF', 'bb', 295), ('JJ', 'aa', 234), + ('BB', 'bb', 840), ('AA', 'aa', 934), ('EE', 'aa', 113), + ('AA', 'bb', 309), ('BB', 'aa', 412), ('AA', 'aa', 911), + ('AA', 'bb', 572), ('II', 'aa', 398), ('II', 'bb', 250), + ('II', 'aa', 652), ('BB', 'bb', 633), ('AA', 'aa', 239), + ('FF', 'aa', 670), ('BB', 'bb', 705), ('HH', 'bb', 963), + ('CC', 'bb', 346), ('II', 'bb', 671), ('BB', 'aa', 247), + ('AA', 'aa', 223), ('GG', 'aa', 480), ('HH', 'aa', 790), + ('FF', 'aa', 208), ('BB', 'bb', 711), ('EE', 'aa', 777), + ('DD', 'bb', 716), ('CC', 'aa', 759), ('CC', 'aa', 430), + ('CC', 'aa', 607), ('DD', 'bb', 794), ('GG', 'aa', 148), + ('GG', 'aa', 634), ('JJ', 'bb', 257), ('DD', 'bb', 959), + ('FF', 'bb', 726), ('BB', 'aa', 762), ('JJ', 'bb', 336), + ('GG', 'aa', 335), ('HH', 'bb', 330), ('GG', 'bb', 160), + ('JJ', 'bb', 839), ('FF', 'aa', 618), ('BB', 'aa', 393), + ('EE', 'bb', 629), ('FF', 'aa', 667), ('AA', 'bb', 870), + ('FF', 'bb', 102), ('JJ', 'aa', 113), ('DD', 'aa', 224), + ('AA', 'bb', 627), ('HH', 'bb', 730), ('II', 'bb', 443), + ('HH', 'bb', 133), ('EE', 'bb', 252), ('II', 'bb', 805), + ('BB', 'bb', 786), ('EE', 'bb', 768), ('HH', 'bb', 683), + ('DD', 'bb', 238), ('DD', 'aa', 256); +} + +foreach {tn frame} { + 1 { GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING } + 2 { GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW } + 3 { GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING } + 4 { GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING } + 5 { GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING } + 6 { GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING } + 7 { GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING } + 8 { GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING } + 9 { GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW } + 10 { GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING } + 11 { GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING } + 12 { GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING } + 13 { GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING } + 14 { GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING } + 15 { GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING } + 16 { GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING } + 17 { GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING } + 18 { GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING } + 19 { GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING } + +} { + execsql_test 1.$tn.1 " + SELECT a, b, sum(c) OVER (ORDER BY a $frame) FROM t3 ORDER BY 1, 2, 3; + " + execsql_test 1.$tn.2 " + SELECT a, b, sum(c) OVER (ORDER BY a,b $frame) FROM t3 ORDER BY 1, 2, 3; + " + execsql_test 1.$tn.3 " + SELECT a, b, rank() OVER (ORDER BY a $frame) FROM t3 ORDER BY 1, 2, 3; + " + execsql_test 1.$tn.4 " + SELECT a, b, max(c) OVER (ORDER BY a,b $frame) FROM t3 ORDER BY 1, 2, 3; + " + execsql_test 1.$tn.5 " + SELECT a, b, min(c) OVER (ORDER BY a,b $frame) FROM t3 ORDER BY 1, 2, 3; + " + + set f2 "$frame EXCLUDE CURRENT ROW" + + execsql_test 1.$tn.6 " + SELECT a, b, sum(c) OVER (ORDER BY a $f2) FROM t3 ORDER BY 1, 2, 3; + " + execsql_test 1.$tn.7 " + SELECT a, b, sum(c) OVER (ORDER BY a,b $f2) FROM t3 ORDER BY 1, 2, 3; + " + + execsql_test 1.$tn.8 " + SELECT a, b, + sum(c) OVER (ORDER BY a $f2), + sum(c) OVER (ORDER BY a $frame), + sum(c) OVER (ORDER BY a,b $f2), + sum(c) OVER (ORDER BY a,b $frame) + FROM t3 ORDER BY 1, 2, 3; + " +} + + +foreach {tn ex} { + 1 { EXCLUDE NO OTHERS } + 2 { EXCLUDE CURRENT ROW } + 3 { EXCLUDE GROUP } + 4 { EXCLUDE TIES } +} { + execsql_test 2.$tn.1 " + SELECT row_number() OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING $ex + ) + " + + execsql_test 2.$tn.2 " + SELECT nth_value(c, 14) OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING $ex + ) + " + + execsql_test 2.$tn.3 " + SELECT min(c) OVER win, max(c) OVER win, sum(c) OVER win FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW $ex + ) ORDER BY a, b, c; + " +} + +========== + +execsql_test 3.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a REAL, b INTEGER); + INSERT INTO t1 VALUES + (5, 10), (10, 20), (13, 26), (13, 26), + (15, 30), (20, 40), (22,80), (30, 90); +} + +foreach {tn frame} { + 1 { ORDER BY a RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING } + 2 { ORDER BY a RANGE BETWEEN 10 PRECEDING AND 5 PRECEDING } + 3 { ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 3 FOLLOWING } + 4 { ORDER BY a DESC RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING } + 5 { ORDER BY a DESC RANGE BETWEEN 10 PRECEDING AND 5 PRECEDING } + 6 { ORDER BY a DESC RANGE BETWEEN 2 FOLLOWING AND 3 FOLLOWING } + + 7 { ORDER BY a RANGE BETWEEN 5.1 PRECEDING AND 5.3 FOLLOWING } + 8 { ORDER BY a RANGE BETWEEN 10.2 PRECEDING AND 5.4 PRECEDING } + 9 { ORDER BY a RANGE BETWEEN 2.6 FOLLOWING AND 3.5 FOLLOWING } + 10 { ORDER BY a DESC RANGE BETWEEN 5.7 PRECEDING AND 5.8 FOLLOWING } + 11 { ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND 5.9 PRECEDING } + 12 { ORDER BY a DESC RANGE BETWEEN 2.1 FOLLOWING AND UNBOUNDED FOLLOWING } + 13 { ORDER BY a RANGE 5.1 PRECEDING } +} { + execsql_test 3.$tn " + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ($frame) + " +} + +========== + +execsql_test 4.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER, b INTEGER); + INSERT INTO t1 VALUES + (NULL, 1), (NULL, 2), (NULL, 3), (10, 4), (10, 5); +} + +execsql_test 4.1.1 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1; +} +execsql_test 4.1.2 { + SELECT sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1; +} + +execsql_test 4.2.1 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1 NULLS FIRST; +} + +execsql_test 4.2.2 { + SELECT sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1 NULLS FIRST; +} + +execsql_test 4.3.1 { + SELECT sum(b) OVER ( + ORDER BY a NULLS FIRST RANGE BETWEEN UNBOUNDED PRECEDING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1 NULLS FIRST; +} + +execsql_test 4.4.1 { + SELECT sum(b) OVER ( + ORDER BY a NULLS FIRST ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 NULLS FIRST; +} + +execsql_test 4.4.2 { + SELECT sum(b) OVER ( + ORDER BY a DESC NULLS LAST ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 NULLS FIRST; +} + +========== + +execsql_test 5.0 { + INSERT INTO t3 VALUES + (NULL, 'bb', 355), (NULL, 'cc', 158), (NULL, 'aa', 399), + ('JJ', NULL, 839), ('FF', NULL, 618), ('BB', NULL, 393), + (NULL, 'bb', 629), (NULL, NULL, 667), (NULL, NULL, 870); +} + +foreach {tn ex} { + 1 { EXCLUDE NO OTHERS } + 2 { EXCLUDE CURRENT ROW } + 3 { EXCLUDE GROUP } + 4 { EXCLUDE TIES } +} { + foreach {tn2 frame} { + 1 { RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING } + 2 { ORDER BY a NULLS FIRST + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING } + 3 { PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING } + 4 { ORDER BY a NULLS FIRST GROUPS 6 PRECEDING } + 5 { ORDER BY c NULLS FIRST RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING } + 6 { ORDER BY c NULLS FIRST RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING } + 7 { ORDER BY c NULLS FIRST, b NULLS FIRST, a NULLS FIRST + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING } + } { + execsql_test 5.$tn.$tn2.1 " + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( $frame $ex ) + ORDER BY 1 NULLS FIRST, 2 NULLS FIRST, 3 NULLS FIRST + " + + execsql_test 5.$tn.$tn2.2 " + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( $frame $ex ) + ORDER BY 1 NULLS FIRST, 2 NULLS FIRST, 3 NULLS FIRST + " + } +} + +========== + +execsql_test 6.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a TEXT, b INTEGER); + INSERT INTO t2 VALUES('A', NULL); + INSERT INTO t2 VALUES('B', NULL); + INSERT INTO t2 VALUES('C', 1); +} + +execsql_test 6.1 { + SELECT string_agg(a, '.') OVER ( + ORDER BY b NULLS FIRST RANGE BETWEEN 7 PRECEDING AND 2 PRECEDING + ) + FROM t2 +} + +execsql_test 6.2 { + SELECT string_agg(a, '.') OVER ( + ORDER BY b DESC NULLS LAST RANGE BETWEEN 7 PRECEDING AND 2 PRECEDING + ) + FROM t2 +} + + +finish_test + + ADDED test/window8.test Index: test/window8.test ================================================================== --- /dev/null +++ test/window8.test @@ -0,0 +1,4875 @@ +# 2019 March 01 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +#################################################### +# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED! +#################################################### + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix window8 + +ifcapable !windowfunc { finish_test ; return } +do_execsql_test 1.0 { + DROP TABLE IF EXISTS t3; + CREATE TABLE t3(a TEXT, b TEXT, c INTEGER); + INSERT INTO t3 VALUES + ('HH', 'bb', 355), ('CC', 'aa', 158), ('BB', 'aa', 399), + ('FF', 'bb', 938), ('HH', 'aa', 480), ('FF', 'bb', 870), + ('JJ', 'aa', 768), ('JJ', 'aa', 899), ('GG', 'bb', 929), + ('II', 'bb', 421), ('GG', 'bb', 844), ('FF', 'bb', 574), + ('CC', 'bb', 822), ('GG', 'bb', 938), ('BB', 'aa', 660), + ('HH', 'aa', 979), ('BB', 'bb', 792), ('DD', 'aa', 845), + ('JJ', 'bb', 354), ('FF', 'bb', 295), ('JJ', 'aa', 234), + ('BB', 'bb', 840), ('AA', 'aa', 934), ('EE', 'aa', 113), + ('AA', 'bb', 309), ('BB', 'aa', 412), ('AA', 'aa', 911), + ('AA', 'bb', 572), ('II', 'aa', 398), ('II', 'bb', 250), + ('II', 'aa', 652), ('BB', 'bb', 633), ('AA', 'aa', 239), + ('FF', 'aa', 670), ('BB', 'bb', 705), ('HH', 'bb', 963), + ('CC', 'bb', 346), ('II', 'bb', 671), ('BB', 'aa', 247), + ('AA', 'aa', 223), ('GG', 'aa', 480), ('HH', 'aa', 790), + ('FF', 'aa', 208), ('BB', 'bb', 711), ('EE', 'aa', 777), + ('DD', 'bb', 716), ('CC', 'aa', 759), ('CC', 'aa', 430), + ('CC', 'aa', 607), ('DD', 'bb', 794), ('GG', 'aa', 148), + ('GG', 'aa', 634), ('JJ', 'bb', 257), ('DD', 'bb', 959), + ('FF', 'bb', 726), ('BB', 'aa', 762), ('JJ', 'bb', 336), + ('GG', 'aa', 335), ('HH', 'bb', 330), ('GG', 'bb', 160), + ('JJ', 'bb', 839), ('FF', 'aa', 618), ('BB', 'aa', 393), + ('EE', 'bb', 629), ('FF', 'aa', 667), ('AA', 'bb', 870), + ('FF', 'bb', 102), ('JJ', 'aa', 113), ('DD', 'aa', 224), + ('AA', 'bb', 627), ('HH', 'bb', 730), ('II', 'bb', 443), + ('HH', 'bb', 133), ('EE', 'bb', 252), ('II', 'bb', 805), + ('BB', 'bb', 786), ('EE', 'bb', 768), ('HH', 'bb', 683), + ('DD', 'bb', 238), ('DD', 'aa', 256); +} {} + +do_execsql_test 1.1.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 + BB bb 4685 CC aa 12025 CC aa 12025 CC aa 12025 CC aa 12025 + CC bb 12025 CC bb 12025 DD aa 15147 DD aa 15147 DD aa 15147 + DD bb 15147 DD bb 15147 DD bb 15147 DD bb 15147 EE aa 19179 + EE aa 19179 EE bb 19179 EE bb 19179 EE bb 19179 FF aa 21718 + FF aa 21718 FF aa 21718 FF aa 21718 FF bb 21718 FF bb 21718 + FF bb 21718 FF bb 21718 FF bb 21718 FF bb 21718 GG aa 27386 + GG aa 27386 GG aa 27386 GG aa 27386 GG bb 27386 GG bb 27386 + GG bb 27386 GG bb 27386 HH aa 31854 HH aa 31854 HH aa 31854 + HH bb 31854 HH bb 31854 HH bb 31854 HH bb 31854 HH bb 31854 + HH bb 31854 II aa 37297 II aa 37297 II bb 37297 II bb 37297 + II bb 37297 II bb 37297 II bb 37297 JJ aa 40937 JJ aa 40937 + JJ aa 40937 JJ aa 40937 JJ bb 40937 JJ bb 40937 JJ bb 40937 + JJ bb 40937} + +do_execsql_test 1.1.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 2307 AA bb 2307 + AA bb 2307 AA bb 2307 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB aa 4685 BB bb 7558 BB bb 7558 BB bb 7558 BB bb 7558 + BB bb 7558 BB bb 7558 CC aa 12025 CC aa 12025 CC aa 12025 + CC aa 12025 CC bb 13979 CC bb 13979 DD aa 15147 DD aa 15147 + DD aa 15147 DD bb 16472 DD bb 16472 DD bb 16472 DD bb 16472 + EE aa 19179 EE aa 19179 EE bb 20069 EE bb 20069 EE bb 20069 + FF aa 21718 FF aa 21718 FF aa 21718 FF aa 21718 FF bb 23881 + FF bb 23881 FF bb 23881 FF bb 23881 FF bb 23881 FF bb 23881 + GG aa 27386 GG aa 27386 GG aa 27386 GG aa 27386 GG bb 28983 + GG bb 28983 GG bb 28983 GG bb 28983 HH aa 31854 HH aa 31854 + HH aa 31854 HH bb 34103 HH bb 34103 HH bb 34103 HH bb 34103 + HH bb 34103 HH bb 34103 II aa 37297 II aa 37297 II bb 38347 + II bb 38347 II bb 38347 II bb 38347 II bb 38347 JJ aa 40937 + JJ aa 40937 JJ aa 40937 JJ aa 40937 JJ bb 42951 JJ bb 42951 + JJ bb 42951 JJ bb 42951} + +do_execsql_test 1.1.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.1.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 934 BB bb 934 BB bb 934 BB bb 934 + BB bb 934 BB bb 934 CC aa 934 CC aa 934 CC aa 934 CC aa 934 + CC bb 934 CC bb 934 DD aa 934 DD aa 934 DD aa 934 DD bb 934 + DD bb 934 DD bb 934 DD bb 934 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 959 FF aa 959 FF aa 959 FF aa 959 + FF bb 959 FF bb 959 FF bb 959 FF bb 959 FF bb 959 FF bb 959 + GG aa 959 GG aa 959 GG aa 959 GG aa 959 GG bb 959 GG bb 959 + GG bb 959 GG bb 959 HH aa 959 HH aa 959 HH aa 959 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 979 JJ aa 979 JJ aa 979 JJ aa 979 JJ bb 979 JJ bb 979 + JJ bb 979 JJ bb 979} + +do_execsql_test 1.1.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 223 BB bb 223 BB bb 223 BB bb 223 + BB bb 223 BB bb 223 CC aa 223 CC aa 223 CC aa 223 CC aa 223 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 158 + DD bb 158 DD bb 158 DD bb 158 EE aa 158 EE aa 158 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 102 + HH bb 102 HH bb 102 HH bb 102 HH bb 102 HH bb 102 II aa 102 + II aa 102 II bb 102 II bb 102 II bb 102 II bb 102 II bb 102 + JJ aa 102 JJ aa 102 JJ aa 102 JJ aa 102 JJ bb 102 JJ bb 102 + JJ bb 102 JJ bb 102} + +do_execsql_test 1.1.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 + BB bb 4685 CC aa 12025 CC aa 12025 CC aa 12025 CC aa 12025 + CC bb 12025 CC bb 12025 DD aa 15147 DD aa 15147 DD aa 15147 + DD bb 15147 DD bb 15147 DD bb 15147 DD bb 15147 EE aa 19179 + EE aa 19179 EE bb 19179 EE bb 19179 EE bb 19179 FF aa 21718 + FF aa 21718 FF aa 21718 FF aa 21718 FF bb 21718 FF bb 21718 + FF bb 21718 FF bb 21718 FF bb 21718 FF bb 21718 GG aa 27386 + GG aa 27386 GG aa 27386 GG aa 27386 GG bb 27386 GG bb 27386 + GG bb 27386 GG bb 27386 HH aa 31854 HH aa 31854 HH aa 31854 + HH bb 31854 HH bb 31854 HH bb 31854 HH bb 31854 HH bb 31854 + HH bb 31854 II aa 37297 II aa 37297 II bb 37297 II bb 37297 + II bb 37297 II bb 37297 II bb 37297 JJ aa 40937 JJ aa 40937 + JJ aa 40937 JJ aa 40937 JJ bb 40937 JJ bb 40937 JJ bb 40937 + JJ bb 40937} + +do_execsql_test 1.1.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 2307 AA bb 2307 + AA bb 2307 AA bb 2307 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB aa 4685 BB bb 7558 BB bb 7558 BB bb 7558 BB bb 7558 + BB bb 7558 BB bb 7558 CC aa 12025 CC aa 12025 CC aa 12025 + CC aa 12025 CC bb 13979 CC bb 13979 DD aa 15147 DD aa 15147 + DD aa 15147 DD bb 16472 DD bb 16472 DD bb 16472 DD bb 16472 + EE aa 19179 EE aa 19179 EE bb 20069 EE bb 20069 EE bb 20069 + FF aa 21718 FF aa 21718 FF aa 21718 FF aa 21718 FF bb 23881 + FF bb 23881 FF bb 23881 FF bb 23881 FF bb 23881 FF bb 23881 + GG aa 27386 GG aa 27386 GG aa 27386 GG aa 27386 GG bb 28983 + GG bb 28983 GG bb 28983 GG bb 28983 HH aa 31854 HH aa 31854 + HH aa 31854 HH bb 34103 HH bb 34103 HH bb 34103 HH bb 34103 + HH bb 34103 HH bb 34103 II aa 37297 II aa 37297 II bb 38347 + II bb 38347 II bb 38347 II bb 38347 II bb 38347 JJ aa 40937 + JJ aa 40937 JJ aa 40937 JJ aa 40937 JJ bb 42951 JJ bb 42951 + JJ bb 42951 JJ bb 42951} + +do_execsql_test 1.1.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} {} {} {} AA aa {} {} {} {} AA aa {} {} {} {} + AA aa {} {} {} {} AA bb {} {} 2307 2307 AA bb {} {} 2307 2307 + AA bb {} {} 2307 2307 AA bb {} {} 2307 2307 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB bb 4685 4685 7558 7558 + BB bb 4685 4685 7558 7558 BB bb 4685 4685 7558 7558 + BB bb 4685 4685 7558 7558 BB bb 4685 4685 7558 7558 + BB bb 4685 4685 7558 7558 CC aa 12025 12025 12025 12025 + CC aa 12025 12025 12025 12025 CC aa 12025 12025 12025 12025 + CC aa 12025 12025 12025 12025 CC bb 12025 12025 13979 13979 + CC bb 12025 12025 13979 13979 DD aa 15147 15147 15147 15147 + DD aa 15147 15147 15147 15147 DD aa 15147 15147 15147 15147 + DD bb 15147 15147 16472 16472 DD bb 15147 15147 16472 16472 + DD bb 15147 15147 16472 16472 DD bb 15147 15147 16472 16472 + EE aa 19179 19179 19179 19179 EE aa 19179 19179 19179 19179 + EE bb 19179 19179 20069 20069 EE bb 19179 19179 20069 20069 + EE bb 19179 19179 20069 20069 FF aa 21718 21718 21718 21718 + FF aa 21718 21718 21718 21718 FF aa 21718 21718 21718 21718 + FF aa 21718 21718 21718 21718 FF bb 21718 21718 23881 23881 + FF bb 21718 21718 23881 23881 FF bb 21718 21718 23881 23881 + FF bb 21718 21718 23881 23881 FF bb 21718 21718 23881 23881 + FF bb 21718 21718 23881 23881 GG aa 27386 27386 27386 27386 + GG aa 27386 27386 27386 27386 GG aa 27386 27386 27386 27386 + GG aa 27386 27386 27386 27386 GG bb 27386 27386 28983 28983 + GG bb 27386 27386 28983 28983 GG bb 27386 27386 28983 28983 + GG bb 27386 27386 28983 28983 HH aa 31854 31854 31854 31854 + HH aa 31854 31854 31854 31854 HH aa 31854 31854 31854 31854 + HH bb 31854 31854 34103 34103 HH bb 31854 31854 34103 34103 + HH bb 31854 31854 34103 34103 HH bb 31854 31854 34103 34103 + HH bb 31854 31854 34103 34103 HH bb 31854 31854 34103 34103 + II aa 37297 37297 37297 37297 II aa 37297 37297 37297 37297 + II bb 37297 37297 38347 38347 II bb 37297 37297 38347 38347 + II bb 37297 37297 38347 38347 II bb 37297 37297 38347 38347 + II bb 37297 37297 38347 38347 JJ aa 40937 40937 40937 40937 + JJ aa 40937 40937 40937 40937 JJ aa 40937 40937 40937 40937 + JJ aa 40937 40937 40937 40937 JJ bb 40937 40937 42951 42951 + JJ bb 40937 40937 42951 42951 JJ bb 40937 40937 42951 42951 + JJ bb 40937 40937 42951 42951} + +do_execsql_test 1.2.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 12025 BB aa 12025 BB aa 12025 + BB aa 12025 BB aa 12025 BB aa 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 BB bb 12025 CC aa 15147 + CC aa 15147 CC aa 15147 CC aa 15147 CC bb 15147 CC bb 15147 + DD aa 19179 DD aa 19179 DD aa 19179 DD bb 19179 DD bb 19179 + DD bb 19179 DD bb 19179 EE aa 21718 EE aa 21718 EE bb 21718 + EE bb 21718 EE bb 21718 FF aa 27386 FF aa 27386 FF aa 27386 + FF aa 27386 FF bb 27386 FF bb 27386 FF bb 27386 FF bb 27386 + FF bb 27386 FF bb 27386 GG aa 31854 GG aa 31854 GG aa 31854 + GG aa 31854 GG bb 31854 GG bb 31854 GG bb 31854 GG bb 31854 + HH aa 37297 HH aa 37297 HH aa 37297 HH bb 37297 HH bb 37297 + HH bb 37297 HH bb 37297 HH bb 37297 HH bb 37297 II aa 40937 + II aa 40937 II bb 40937 II bb 40937 II bb 40937 II bb 40937 + II bb 40937 JJ aa 44737 JJ aa 44737 JJ aa 44737 JJ aa 44737 + JJ bb 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737} + +do_execsql_test 1.2.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 2307 AA aa 2307 AA aa 2307 AA aa 2307 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 7558 BB aa 7558 BB aa 7558 BB aa 7558 + BB aa 7558 BB aa 7558 BB bb 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 CC aa 13979 CC aa 13979 + CC aa 13979 CC aa 13979 CC bb 15147 CC bb 15147 DD aa 16472 + DD aa 16472 DD aa 16472 DD bb 19179 DD bb 19179 DD bb 19179 + DD bb 19179 EE aa 20069 EE aa 20069 EE bb 21718 EE bb 21718 + EE bb 21718 FF aa 23881 FF aa 23881 FF aa 23881 FF aa 23881 + FF bb 27386 FF bb 27386 FF bb 27386 FF bb 27386 FF bb 27386 + FF bb 27386 GG aa 28983 GG aa 28983 GG aa 28983 GG aa 28983 + GG bb 31854 GG bb 31854 GG bb 31854 GG bb 31854 HH aa 34103 + HH aa 34103 HH aa 34103 HH bb 37297 HH bb 37297 HH bb 37297 + HH bb 37297 HH bb 37297 HH bb 37297 II aa 38347 II aa 38347 + II bb 40937 II bb 40937 II bb 40937 II bb 40937 II bb 40937 + JJ aa 42951 JJ aa 42951 JJ aa 42951 JJ aa 42951 JJ bb 44737 + JJ bb 44737 JJ bb 44737 JJ bb 44737} + +do_execsql_test 1.2.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.2.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 934 BB bb 934 BB bb 934 BB bb 934 + BB bb 934 BB bb 934 CC aa 934 CC aa 934 CC aa 934 CC aa 934 + CC bb 934 CC bb 934 DD aa 934 DD aa 934 DD aa 934 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 959 FF aa 959 FF aa 959 FF aa 959 + FF bb 959 FF bb 959 FF bb 959 FF bb 959 FF bb 959 FF bb 959 + GG aa 959 GG aa 959 GG aa 959 GG aa 959 GG bb 959 GG bb 959 + GG bb 959 GG bb 959 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 979 JJ aa 979 JJ aa 979 JJ aa 979 JJ bb 979 JJ bb 979 + JJ bb 979 JJ bb 979} + +do_execsql_test 1.2.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 223 BB bb 223 BB bb 223 BB bb 223 + BB bb 223 BB bb 223 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 158 + DD bb 158 DD bb 158 DD bb 158 EE aa 113 EE aa 113 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 102 + HH bb 102 HH bb 102 HH bb 102 HH bb 102 HH bb 102 II aa 102 + II aa 102 II bb 102 II bb 102 II bb 102 II bb 102 II bb 102 + JJ aa 102 JJ aa 102 JJ aa 102 JJ aa 102 JJ bb 102 JJ bb 102 + JJ bb 102 JJ bb 102} + +do_execsql_test 1.2.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 11263 BB aa 11365 BB aa 11613 + BB aa 11626 BB aa 11632 BB aa 11778 BB bb 11185 BB bb 11233 + BB bb 11239 BB bb 11314 BB bb 11320 BB bb 11392 CC aa 14388 + CC aa 14540 CC aa 14717 CC aa 14989 CC bb 14325 CC bb 14801 + DD aa 18334 DD aa 18923 DD aa 18955 DD bb 18220 DD bb 18385 + DD bb 18463 DD bb 18941 EE aa 20941 EE aa 21605 EE bb 20950 + EE bb 21089 EE bb 21466 FF aa 26716 FF aa 26719 FF aa 26768 + FF aa 27178 FF bb 26448 FF bb 26516 FF bb 26660 FF bb 26812 + FF bb 27091 FF bb 27284 GG aa 31220 GG aa 31374 GG aa 31519 + GG aa 31706 GG bb 30916 GG bb 30925 GG bb 31010 GG bb 31694 + HH aa 36318 HH aa 36507 HH aa 36817 HH bb 36334 HH bb 36567 + HH bb 36614 HH bb 36942 HH bb 36967 HH bb 37164 II aa 40285 + II aa 40539 II bb 40132 II bb 40266 II bb 40494 II bb 40516 + II bb 40687 JJ aa 43838 JJ aa 43969 JJ aa 44503 JJ aa 44624 + JJ bb 43898 JJ bb 44383 JJ bb 44401 JJ bb 44480} + +do_execsql_test 1.2.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1373 AA aa 1396 AA aa 2068 AA aa 2084 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 6796 BB aa 6898 BB aa 7146 BB aa 7159 + BB aa 7165 BB aa 7311 BB bb 11185 BB bb 11233 BB bb 11239 + BB bb 11314 BB bb 11320 BB bb 11392 CC aa 13220 CC aa 13372 + CC aa 13549 CC aa 13821 CC bb 14325 CC bb 14801 DD aa 15627 + DD aa 16216 DD aa 16248 DD bb 18220 DD bb 18385 DD bb 18463 + DD bb 18941 EE aa 19292 EE aa 19956 EE bb 20950 EE bb 21089 + EE bb 21466 FF aa 23211 FF aa 23214 FF aa 23263 FF aa 23673 + FF bb 26448 FF bb 26516 FF bb 26660 FF bb 26812 FF bb 27091 + FF bb 27284 GG aa 28349 GG aa 28503 GG aa 28648 GG aa 28835 + GG bb 30916 GG bb 30925 GG bb 31010 GG bb 31694 HH aa 33124 + HH aa 33313 HH aa 33623 HH bb 36334 HH bb 36567 HH bb 36614 + HH bb 36942 HH bb 36967 HH bb 37164 II aa 37695 II aa 37949 + II bb 40132 II bb 40266 II bb 40494 II bb 40516 II bb 40687 + JJ aa 42052 JJ aa 42183 JJ aa 42717 JJ aa 42838 JJ bb 43898 + JJ bb 44383 JJ bb 44401 JJ bb 44480} + +do_execsql_test 1.2.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 4685 1373 2307 AA aa 3774 4685 1396 2307 + AA aa 4446 4685 2068 2307 AA aa 4462 4685 2084 2307 + AA bb 3815 4685 3815 4685 AA bb 4058 4685 4058 4685 + AA bb 4113 4685 4113 4685 AA bb 4376 4685 4376 4685 + BB aa 11263 12025 6796 7558 BB aa 11365 12025 6898 7558 + BB aa 11613 12025 7146 7558 BB aa 11626 12025 7159 7558 + BB aa 11632 12025 7165 7558 BB aa 11778 12025 7311 7558 + BB bb 11185 12025 11185 12025 BB bb 11233 12025 11233 12025 + BB bb 11239 12025 11239 12025 BB bb 11314 12025 11314 12025 + BB bb 11320 12025 11320 12025 BB bb 11392 12025 11392 12025 + CC aa 14388 15147 13220 13979 CC aa 14540 15147 13372 13979 + CC aa 14717 15147 13549 13979 CC aa 14989 15147 13821 13979 + CC bb 14325 15147 14325 15147 CC bb 14801 15147 14801 15147 + DD aa 18334 19179 15627 16472 DD aa 18923 19179 16216 16472 + DD aa 18955 19179 16248 16472 DD bb 18220 19179 18220 19179 + DD bb 18385 19179 18385 19179 DD bb 18463 19179 18463 19179 + DD bb 18941 19179 18941 19179 EE aa 20941 21718 19292 20069 + EE aa 21605 21718 19956 20069 EE bb 20950 21718 20950 21718 + EE bb 21089 21718 21089 21718 EE bb 21466 21718 21466 21718 + FF aa 26716 27386 23211 23881 FF aa 26719 27386 23214 23881 + FF aa 26768 27386 23263 23881 FF aa 27178 27386 23673 23881 + FF bb 26448 27386 26448 27386 FF bb 26516 27386 26516 27386 + FF bb 26660 27386 26660 27386 FF bb 26812 27386 26812 27386 + FF bb 27091 27386 27091 27386 FF bb 27284 27386 27284 27386 + GG aa 31220 31854 28349 28983 GG aa 31374 31854 28503 28983 + GG aa 31519 31854 28648 28983 GG aa 31706 31854 28835 28983 + GG bb 30916 31854 30916 31854 GG bb 30925 31854 30925 31854 + GG bb 31010 31854 31010 31854 GG bb 31694 31854 31694 31854 + HH aa 36318 37297 33124 34103 HH aa 36507 37297 33313 34103 + HH aa 36817 37297 33623 34103 HH bb 36334 37297 36334 37297 + HH bb 36567 37297 36567 37297 HH bb 36614 37297 36614 37297 + HH bb 36942 37297 36942 37297 HH bb 36967 37297 36967 37297 + HH bb 37164 37297 37164 37297 II aa 40285 40937 37695 38347 + II aa 40539 40937 37949 38347 II bb 40132 40937 40132 40937 + II bb 40266 40937 40266 40937 II bb 40494 40937 40494 40937 + II bb 40516 40937 40516 40937 II bb 40687 40937 40687 40937 + JJ aa 43838 44737 42052 42951 JJ aa 43969 44737 42183 42951 + JJ aa 44503 44737 42717 42951 JJ aa 44624 44737 42838 42951 + JJ bb 43898 44737 43898 44737 JJ bb 44383 44737 44383 44737 + JJ bb 44401 44737 44401 44737 JJ bb 44480 44737 44480 44737} + +do_execsql_test 1.3.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 12025 AA aa 12025 AA aa 12025 AA aa 12025 AA bb 12025 + AA bb 12025 AA bb 12025 AA bb 12025 BB aa 15147 BB aa 15147 + BB aa 15147 BB aa 15147 BB aa 15147 BB aa 15147 BB bb 15147 + BB bb 15147 BB bb 15147 BB bb 15147 BB bb 15147 BB bb 15147 + CC aa 19179 CC aa 19179 CC aa 19179 CC aa 19179 CC bb 19179 + CC bb 19179 DD aa 21718 DD aa 21718 DD aa 21718 DD bb 21718 + DD bb 21718 DD bb 21718 DD bb 21718 EE aa 27386 EE aa 27386 + EE bb 27386 EE bb 27386 EE bb 27386 FF aa 31854 FF aa 31854 + FF aa 31854 FF aa 31854 FF bb 31854 FF bb 31854 FF bb 31854 + FF bb 31854 FF bb 31854 FF bb 31854 GG aa 37297 GG aa 37297 + GG aa 37297 GG aa 37297 GG bb 37297 GG bb 37297 GG bb 37297 + GG bb 37297 HH aa 40937 HH aa 40937 HH aa 40937 HH bb 40937 + HH bb 40937 HH bb 40937 HH bb 40937 HH bb 40937 HH bb 40937 + II aa 44737 II aa 44737 II bb 44737 II bb 44737 II bb 44737 + II bb 44737 II bb 44737 JJ aa 44737 JJ aa 44737 JJ aa 44737 + JJ aa 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737} + +do_execsql_test 1.3.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 7558 AA bb 7558 + AA bb 7558 AA bb 7558 BB aa 12025 BB aa 12025 BB aa 12025 + BB aa 12025 BB aa 12025 BB aa 12025 BB bb 13979 BB bb 13979 + BB bb 13979 BB bb 13979 BB bb 13979 BB bb 13979 CC aa 15147 + CC aa 15147 CC aa 15147 CC aa 15147 CC bb 16472 CC bb 16472 + DD aa 19179 DD aa 19179 DD aa 19179 DD bb 20069 DD bb 20069 + DD bb 20069 DD bb 20069 EE aa 21718 EE aa 21718 EE bb 23881 + EE bb 23881 EE bb 23881 FF aa 27386 FF aa 27386 FF aa 27386 + FF aa 27386 FF bb 28983 FF bb 28983 FF bb 28983 FF bb 28983 + FF bb 28983 FF bb 28983 GG aa 31854 GG aa 31854 GG aa 31854 + GG aa 31854 GG bb 34103 GG bb 34103 GG bb 34103 GG bb 34103 + HH aa 37297 HH aa 37297 HH aa 37297 HH bb 38347 HH bb 38347 + HH bb 38347 HH bb 38347 HH bb 38347 HH bb 38347 II aa 40937 + II aa 40937 II bb 42951 II bb 42951 II bb 42951 II bb 42951 + II bb 42951 JJ aa 44737 JJ aa 44737 JJ aa 44737 JJ aa 44737 + JJ bb 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737} + +do_execsql_test 1.3.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.3.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 934 BB bb 934 BB bb 934 BB bb 934 + BB bb 934 BB bb 934 CC aa 934 CC aa 934 CC aa 934 CC aa 934 + CC bb 934 CC bb 934 DD aa 959 DD aa 959 DD aa 959 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 959 FF aa 959 FF aa 959 FF aa 959 + FF bb 959 FF bb 959 FF bb 959 FF bb 959 FF bb 959 FF bb 959 + GG aa 959 GG aa 959 GG aa 959 GG aa 959 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 979 JJ aa 979 JJ aa 979 JJ aa 979 JJ bb 979 JJ bb 979 + JJ bb 979 JJ bb 979} + +do_execsql_test 1.3.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 158 BB bb 158 BB bb 158 BB bb 158 + BB bb 158 BB bb 158 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 113 + DD bb 113 DD bb 113 DD bb 113 EE aa 113 EE aa 113 EE bb 113 + EE bb 113 EE bb 113 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 102 + HH bb 102 HH bb 102 HH bb 102 HH bb 102 HH bb 102 II aa 102 + II aa 102 II bb 102 II bb 102 II bb 102 II bb 102 II bb 102 + JJ aa 102 JJ aa 102 JJ aa 102 JJ aa 102 JJ bb 102 JJ bb 102 + JJ bb 102 JJ bb 102} + +do_execsql_test 1.3.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 11091 AA aa 11114 AA aa 11786 AA aa 11802 AA bb 11155 + AA bb 11398 AA bb 11453 AA bb 11716 BB aa 14385 BB aa 14487 + BB aa 14735 BB aa 14748 BB aa 14754 BB aa 14900 BB bb 14307 + BB bb 14355 BB bb 14361 BB bb 14436 BB bb 14442 BB bb 14514 + CC aa 18420 CC aa 18572 CC aa 18749 CC aa 19021 CC bb 18357 + CC bb 18833 DD aa 20873 DD aa 21462 DD aa 21494 DD bb 20759 + DD bb 20924 DD bb 21002 DD bb 21480 EE aa 26609 EE aa 27273 + EE bb 26618 EE bb 26757 EE bb 27134 FF aa 31184 FF aa 31187 + FF aa 31236 FF aa 31646 FF bb 30916 FF bb 30984 FF bb 31128 + FF bb 31280 FF bb 31559 FF bb 31752 GG aa 36663 GG aa 36817 + GG aa 36962 GG aa 37149 GG bb 36359 GG bb 36368 GG bb 36453 + GG bb 37137 HH aa 39958 HH aa 40147 HH aa 40457 HH bb 39974 + HH bb 40207 HH bb 40254 HH bb 40582 HH bb 40607 HH bb 40804 + II aa 44085 II aa 44339 II bb 43932 II bb 44066 II bb 44294 + II bb 44316 II bb 44487 JJ aa 43838 JJ aa 43969 JJ aa 44503 + JJ aa 44624 JJ bb 43898 JJ bb 44383 JJ bb 44401 JJ bb 44480} + +do_execsql_test 1.3.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 6688 AA bb 6931 + AA bb 6986 AA bb 7249 BB aa 11263 BB aa 11365 BB aa 11613 + BB aa 11626 BB aa 11632 BB aa 11778 BB bb 13139 BB bb 13187 + BB bb 13193 BB bb 13268 BB bb 13274 BB bb 13346 CC aa 14388 + CC aa 14540 CC aa 14717 CC aa 14989 CC bb 15650 CC bb 16126 + DD aa 18334 DD aa 18923 DD aa 18955 DD bb 19110 DD bb 19275 + DD bb 19353 DD bb 19831 EE aa 20941 EE aa 21605 EE bb 23113 + EE bb 23252 EE bb 23629 FF aa 26716 FF aa 26719 FF aa 26768 + FF aa 27178 FF bb 28045 FF bb 28113 FF bb 28257 FF bb 28409 + FF bb 28688 FF bb 28881 GG aa 31220 GG aa 31374 GG aa 31519 + GG aa 31706 GG bb 33165 GG bb 33174 GG bb 33259 GG bb 33943 + HH aa 36318 HH aa 36507 HH aa 36817 HH bb 37384 HH bb 37617 + HH bb 37664 HH bb 37992 HH bb 38017 HH bb 38214 II aa 40285 + II aa 40539 II bb 42146 II bb 42280 II bb 42508 II bb 42530 + II bb 42701 JJ aa 43838 JJ aa 43969 JJ aa 44503 JJ aa 44624 + JJ bb 43898 JJ bb 44383 JJ bb 44401 JJ bb 44480} + +do_execsql_test 1.3.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND 1 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 11091 12025 3751 4685 AA aa 11114 12025 3774 4685 + AA aa 11786 12025 4446 4685 AA aa 11802 12025 4462 4685 + AA bb 11155 12025 6688 7558 AA bb 11398 12025 6931 7558 + AA bb 11453 12025 6986 7558 AA bb 11716 12025 7249 7558 + BB aa 14385 15147 11263 12025 BB aa 14487 15147 11365 12025 + BB aa 14735 15147 11613 12025 BB aa 14748 15147 11626 12025 + BB aa 14754 15147 11632 12025 BB aa 14900 15147 11778 12025 + BB bb 14307 15147 13139 13979 BB bb 14355 15147 13187 13979 + BB bb 14361 15147 13193 13979 BB bb 14436 15147 13268 13979 + BB bb 14442 15147 13274 13979 BB bb 14514 15147 13346 13979 + CC aa 18420 19179 14388 15147 CC aa 18572 19179 14540 15147 + CC aa 18749 19179 14717 15147 CC aa 19021 19179 14989 15147 + CC bb 18357 19179 15650 16472 CC bb 18833 19179 16126 16472 + DD aa 20873 21718 18334 19179 DD aa 21462 21718 18923 19179 + DD aa 21494 21718 18955 19179 DD bb 20759 21718 19110 20069 + DD bb 20924 21718 19275 20069 DD bb 21002 21718 19353 20069 + DD bb 21480 21718 19831 20069 EE aa 26609 27386 20941 21718 + EE aa 27273 27386 21605 21718 EE bb 26618 27386 23113 23881 + EE bb 26757 27386 23252 23881 EE bb 27134 27386 23629 23881 + FF aa 31184 31854 26716 27386 FF aa 31187 31854 26719 27386 + FF aa 31236 31854 26768 27386 FF aa 31646 31854 27178 27386 + FF bb 30916 31854 28045 28983 FF bb 30984 31854 28113 28983 + FF bb 31128 31854 28257 28983 FF bb 31280 31854 28409 28983 + FF bb 31559 31854 28688 28983 FF bb 31752 31854 28881 28983 + GG aa 36663 37297 31220 31854 GG aa 36817 37297 31374 31854 + GG aa 36962 37297 31519 31854 GG aa 37149 37297 31706 31854 + GG bb 36359 37297 33165 34103 GG bb 36368 37297 33174 34103 + GG bb 36453 37297 33259 34103 GG bb 37137 37297 33943 34103 + HH aa 39958 40937 36318 37297 HH aa 40147 40937 36507 37297 + HH aa 40457 40937 36817 37297 HH bb 39974 40937 37384 38347 + HH bb 40207 40937 37617 38347 HH bb 40254 40937 37664 38347 + HH bb 40582 40937 37992 38347 HH bb 40607 40937 38017 38347 + HH bb 40804 40937 38214 38347 II aa 44085 44737 40285 40937 + II aa 44339 44737 40539 40937 II bb 43932 44737 42146 42951 + II bb 44066 44737 42280 42951 II bb 44294 44737 42508 42951 + II bb 44316 44737 42530 42951 II bb 44487 44737 42701 42951 + JJ aa 43838 44737 43838 44737 JJ aa 43969 44737 43969 44737 + JJ aa 44503 44737 44503 44737 JJ aa 44624 44737 44624 44737 + JJ bb 43898 44737 43898 44737 JJ bb 44383 44737 44383 44737 + JJ bb 44401 44737 44401 44737 JJ bb 44480 44737 44480 44737} + +do_execsql_test 1.4.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 44737 + AA bb 44737 AA bb 44737 AA bb 44737 BB aa 44737 BB aa 44737 + BB aa 44737 BB aa 44737 BB aa 44737 BB aa 44737 BB bb 44737 + BB bb 44737 BB bb 44737 BB bb 44737 BB bb 44737 BB bb 44737 + CC aa 44737 CC aa 44737 CC aa 44737 CC aa 44737 CC bb 44737 + CC bb 44737 DD aa 44737 DD aa 44737 DD aa 44737 DD bb 44737 + DD bb 44737 DD bb 44737 DD bb 44737 EE aa 44737 EE aa 44737 + EE bb 44737 EE bb 44737 EE bb 44737 FF aa 44737 FF aa 44737 + FF aa 44737 FF aa 44737 FF bb 44737 FF bb 44737 FF bb 44737 + FF bb 44737 FF bb 44737 FF bb 44737 GG aa 44737 GG aa 44737 + GG aa 44737 GG aa 44737 GG bb 44737 GG bb 44737 GG bb 44737 + GG bb 44737 HH aa 44737 HH aa 44737 HH aa 44737 HH bb 44737 + HH bb 44737 HH bb 44737 HH bb 44737 HH bb 44737 HH bb 44737 + II aa 44737 II aa 44737 II bb 44737 II bb 44737 II bb 44737 + II bb 44737 II bb 44737 JJ aa 44737 JJ aa 44737 JJ aa 44737 + JJ aa 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737} + +do_execsql_test 1.4.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 44737 + AA bb 44737 AA bb 44737 AA bb 44737 BB aa 44737 BB aa 44737 + BB aa 44737 BB aa 44737 BB aa 44737 BB aa 44737 BB bb 44737 + BB bb 44737 BB bb 44737 BB bb 44737 BB bb 44737 BB bb 44737 + CC aa 44737 CC aa 44737 CC aa 44737 CC aa 44737 CC bb 44737 + CC bb 44737 DD aa 44737 DD aa 44737 DD aa 44737 DD bb 44737 + DD bb 44737 DD bb 44737 DD bb 44737 EE aa 44737 EE aa 44737 + EE bb 44737 EE bb 44737 EE bb 44737 FF aa 44737 FF aa 44737 + FF aa 44737 FF aa 44737 FF bb 44737 FF bb 44737 FF bb 44737 + FF bb 44737 FF bb 44737 FF bb 44737 GG aa 44737 GG aa 44737 + GG aa 44737 GG aa 44737 GG bb 44737 GG bb 44737 GG bb 44737 + GG bb 44737 HH aa 44737 HH aa 44737 HH aa 44737 HH bb 44737 + HH bb 44737 HH bb 44737 HH bb 44737 HH bb 44737 HH bb 44737 + II aa 44737 II aa 44737 II bb 44737 II bb 44737 II bb 44737 + II bb 44737 II bb 44737 JJ aa 44737 JJ aa 44737 JJ aa 44737 + JJ aa 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737 JJ bb 44737} + +do_execsql_test 1.4.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.4.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 979 AA aa 979 AA aa 979 AA aa 979 AA bb 979 AA bb 979 + AA bb 979 AA bb 979 BB aa 979 BB aa 979 BB aa 979 BB aa 979 + BB aa 979 BB aa 979 BB bb 979 BB bb 979 BB bb 979 BB bb 979 + BB bb 979 BB bb 979 CC aa 979 CC aa 979 CC aa 979 CC aa 979 + CC bb 979 CC bb 979 DD aa 979 DD aa 979 DD aa 979 DD bb 979 + DD bb 979 DD bb 979 DD bb 979 EE aa 979 EE aa 979 EE bb 979 + EE bb 979 EE bb 979 FF aa 979 FF aa 979 FF aa 979 FF aa 979 + FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 + GG aa 979 GG aa 979 GG aa 979 GG aa 979 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 979 JJ aa 979 JJ aa 979 JJ aa 979 JJ bb 979 JJ bb 979 + JJ bb 979 JJ bb 979} + +do_execsql_test 1.4.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 102 AA aa 102 AA aa 102 AA aa 102 AA bb 102 AA bb 102 + AA bb 102 AA bb 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 + BB aa 102 BB aa 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 + BB bb 102 BB bb 102 CC aa 102 CC aa 102 CC aa 102 CC aa 102 + CC bb 102 CC bb 102 DD aa 102 DD aa 102 DD aa 102 DD bb 102 + DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 + EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 102 + HH bb 102 HH bb 102 HH bb 102 HH bb 102 HH bb 102 II aa 102 + II aa 102 II bb 102 II bb 102 II bb 102 II bb 102 II bb 102 + JJ aa 102 JJ aa 102 JJ aa 102 JJ aa 102 JJ bb 102 JJ bb 102 + JJ bb 102 JJ bb 102} + +do_execsql_test 1.4.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 43867 + AA bb 44110 AA bb 44165 AA bb 44428 BB aa 43975 BB aa 44077 + BB aa 44325 BB aa 44338 BB aa 44344 BB aa 44490 BB bb 43897 + BB bb 43945 BB bb 43951 BB bb 44026 BB bb 44032 BB bb 44104 + CC aa 43978 CC aa 44130 CC aa 44307 CC aa 44579 CC bb 43915 + CC bb 44391 DD aa 43892 DD aa 44481 DD aa 44513 DD bb 43778 + DD bb 43943 DD bb 44021 DD bb 44499 EE aa 43960 EE aa 44624 + EE bb 43969 EE bb 44108 EE bb 44485 FF aa 44067 FF aa 44070 + FF aa 44119 FF aa 44529 FF bb 43799 FF bb 43867 FF bb 44011 + FF bb 44163 FF bb 44442 FF bb 44635 GG aa 44103 GG aa 44257 + GG aa 44402 GG aa 44589 GG bb 43799 GG bb 43808 GG bb 43893 + GG bb 44577 HH aa 43758 HH aa 43947 HH aa 44257 HH bb 43774 + HH bb 44007 HH bb 44054 HH bb 44382 HH bb 44407 HH bb 44604 + II aa 44085 II aa 44339 II bb 43932 II bb 44066 II bb 44294 + II bb 44316 II bb 44487 JJ aa 43838 JJ aa 43969 JJ aa 44503 + JJ aa 44624 JJ bb 43898 JJ bb 44383 JJ bb 44401 JJ bb 44480} + +do_execsql_test 1.4.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 43867 + AA bb 44110 AA bb 44165 AA bb 44428 BB aa 43975 BB aa 44077 + BB aa 44325 BB aa 44338 BB aa 44344 BB aa 44490 BB bb 43897 + BB bb 43945 BB bb 43951 BB bb 44026 BB bb 44032 BB bb 44104 + CC aa 43978 CC aa 44130 CC aa 44307 CC aa 44579 CC bb 43915 + CC bb 44391 DD aa 43892 DD aa 44481 DD aa 44513 DD bb 43778 + DD bb 43943 DD bb 44021 DD bb 44499 EE aa 43960 EE aa 44624 + EE bb 43969 EE bb 44108 EE bb 44485 FF aa 44067 FF aa 44070 + FF aa 44119 FF aa 44529 FF bb 43799 FF bb 43867 FF bb 44011 + FF bb 44163 FF bb 44442 FF bb 44635 GG aa 44103 GG aa 44257 + GG aa 44402 GG aa 44589 GG bb 43799 GG bb 43808 GG bb 43893 + GG bb 44577 HH aa 43758 HH aa 43947 HH aa 44257 HH bb 43774 + HH bb 44007 HH bb 44054 HH bb 44382 HH bb 44407 HH bb 44604 + II aa 44085 II aa 44339 II bb 43932 II bb 44066 II bb 44294 + II bb 44316 II bb 44487 JJ aa 43838 JJ aa 43969 JJ aa 44503 + JJ aa 44624 JJ bb 43898 JJ bb 44383 JJ bb 44401 JJ bb 44480} + +do_execsql_test 1.4.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 44737 43803 44737 AA aa 43826 44737 43826 44737 + AA aa 44498 44737 44498 44737 AA aa 44514 44737 44514 44737 + AA bb 43867 44737 43867 44737 AA bb 44110 44737 44110 44737 + AA bb 44165 44737 44165 44737 AA bb 44428 44737 44428 44737 + BB aa 43975 44737 43975 44737 BB aa 44077 44737 44077 44737 + BB aa 44325 44737 44325 44737 BB aa 44338 44737 44338 44737 + BB aa 44344 44737 44344 44737 BB aa 44490 44737 44490 44737 + BB bb 43897 44737 43897 44737 BB bb 43945 44737 43945 44737 + BB bb 43951 44737 43951 44737 BB bb 44026 44737 44026 44737 + BB bb 44032 44737 44032 44737 BB bb 44104 44737 44104 44737 + CC aa 43978 44737 43978 44737 CC aa 44130 44737 44130 44737 + CC aa 44307 44737 44307 44737 CC aa 44579 44737 44579 44737 + CC bb 43915 44737 43915 44737 CC bb 44391 44737 44391 44737 + DD aa 43892 44737 43892 44737 DD aa 44481 44737 44481 44737 + DD aa 44513 44737 44513 44737 DD bb 43778 44737 43778 44737 + DD bb 43943 44737 43943 44737 DD bb 44021 44737 44021 44737 + DD bb 44499 44737 44499 44737 EE aa 43960 44737 43960 44737 + EE aa 44624 44737 44624 44737 EE bb 43969 44737 43969 44737 + EE bb 44108 44737 44108 44737 EE bb 44485 44737 44485 44737 + FF aa 44067 44737 44067 44737 FF aa 44070 44737 44070 44737 + FF aa 44119 44737 44119 44737 FF aa 44529 44737 44529 44737 + FF bb 43799 44737 43799 44737 FF bb 43867 44737 43867 44737 + FF bb 44011 44737 44011 44737 FF bb 44163 44737 44163 44737 + FF bb 44442 44737 44442 44737 FF bb 44635 44737 44635 44737 + GG aa 44103 44737 44103 44737 GG aa 44257 44737 44257 44737 + GG aa 44402 44737 44402 44737 GG aa 44589 44737 44589 44737 + GG bb 43799 44737 43799 44737 GG bb 43808 44737 43808 44737 + GG bb 43893 44737 43893 44737 GG bb 44577 44737 44577 44737 + HH aa 43758 44737 43758 44737 HH aa 43947 44737 43947 44737 + HH aa 44257 44737 44257 44737 HH bb 43774 44737 43774 44737 + HH bb 44007 44737 44007 44737 HH bb 44054 44737 44054 44737 + HH bb 44382 44737 44382 44737 HH bb 44407 44737 44407 44737 + HH bb 44604 44737 44604 44737 II aa 44085 44737 44085 44737 + II aa 44339 44737 44339 44737 II bb 43932 44737 43932 44737 + II bb 44066 44737 44066 44737 II bb 44294 44737 44294 44737 + II bb 44316 44737 44316 44737 II bb 44487 44737 44487 44737 + JJ aa 43838 44737 43838 44737 JJ aa 43969 44737 43969 44737 + JJ aa 44503 44737 44503 44737 JJ aa 44624 44737 44624 44737 + JJ bb 43898 44737 43898 44737 JJ bb 44383 44737 44383 44737 + JJ bb 44401 44737 44401 44737 JJ bb 44480 44737 44480 44737} + +do_execsql_test 1.5.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.5.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.5.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.5.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.5.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.5.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.5.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.5.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 PRECEDING AND 2 PRECEDING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} {} {} {} AA aa {} {} {} {} AA aa {} {} {} {} + AA aa {} {} {} {} AA bb {} {} {} {} AA bb {} {} {} {} + AA bb {} {} {} {} AA bb {} {} {} {} BB aa {} {} {} {} + BB aa {} {} {} {} BB aa {} {} {} {} BB aa {} {} {} {} + BB aa {} {} {} {} BB aa {} {} {} {} BB bb {} {} {} {} + BB bb {} {} {} {} BB bb {} {} {} {} BB bb {} {} {} {} + BB bb {} {} {} {} BB bb {} {} {} {} CC aa {} {} {} {} + CC aa {} {} {} {} CC aa {} {} {} {} CC aa {} {} {} {} + CC bb {} {} {} {} CC bb {} {} {} {} DD aa {} {} {} {} + DD aa {} {} {} {} DD aa {} {} {} {} DD bb {} {} {} {} + DD bb {} {} {} {} DD bb {} {} {} {} DD bb {} {} {} {} + EE aa {} {} {} {} EE aa {} {} {} {} EE bb {} {} {} {} + EE bb {} {} {} {} EE bb {} {} {} {} FF aa {} {} {} {} + FF aa {} {} {} {} FF aa {} {} {} {} FF aa {} {} {} {} + FF bb {} {} {} {} FF bb {} {} {} {} FF bb {} {} {} {} + FF bb {} {} {} {} FF bb {} {} {} {} FF bb {} {} {} {} + GG aa {} {} {} {} GG aa {} {} {} {} GG aa {} {} {} {} + GG aa {} {} {} {} GG bb {} {} {} {} GG bb {} {} {} {} + GG bb {} {} {} {} GG bb {} {} {} {} HH aa {} {} {} {} + HH aa {} {} {} {} HH aa {} {} {} {} HH bb {} {} {} {} + HH bb {} {} {} {} HH bb {} {} {} {} HH bb {} {} {} {} + HH bb {} {} {} {} HH bb {} {} {} {} II aa {} {} {} {} + II aa {} {} {} {} II bb {} {} {} {} II bb {} {} {} {} + II bb {} {} {} {} II bb {} {} {} {} II bb {} {} {} {} + JJ aa {} {} {} {} JJ aa {} {} {} {} JJ aa {} {} {} {} + JJ aa {} {} {} {} JJ bb {} {} {} {} JJ bb {} {} {} {} + JJ bb {} {} {} {} JJ bb {} {} {} {}} + +do_execsql_test 1.6.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 + BB bb 4685 CC aa 12025 CC aa 12025 CC aa 12025 CC aa 12025 + CC bb 12025 CC bb 12025 DD aa 10462 DD aa 10462 DD aa 10462 + DD bb 10462 DD bb 10462 DD bb 10462 DD bb 10462 EE aa 7154 + EE aa 7154 EE bb 7154 EE bb 7154 EE bb 7154 FF aa 6571 FF aa 6571 + FF aa 6571 FF aa 6571 FF bb 6571 FF bb 6571 FF bb 6571 FF bb 6571 + FF bb 6571 FF bb 6571 GG aa 8207 GG aa 8207 GG aa 8207 GG aa 8207 + GG bb 8207 GG bb 8207 GG bb 8207 GG bb 8207 HH aa 10136 + HH aa 10136 HH aa 10136 HH bb 10136 HH bb 10136 HH bb 10136 + HH bb 10136 HH bb 10136 HH bb 10136 II aa 9911 II aa 9911 + II bb 9911 II bb 9911 II bb 9911 II bb 9911 II bb 9911 JJ aa 9083 + JJ aa 9083 JJ aa 9083 JJ aa 9083 JJ bb 9083 JJ bb 9083 JJ bb 9083 + JJ bb 9083} + +do_execsql_test 1.6.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 2307 AA bb 2307 + AA bb 2307 AA bb 2307 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB aa 4685 BB bb 5251 BB bb 5251 BB bb 5251 BB bb 5251 + BB bb 5251 BB bb 5251 CC aa 7340 CC aa 7340 CC aa 7340 CC aa 7340 + CC bb 6421 CC bb 6421 DD aa 3122 DD aa 3122 DD aa 3122 DD bb 2493 + DD bb 2493 DD bb 2493 DD bb 2493 EE aa 4032 EE aa 4032 EE bb 3597 + EE bb 3597 EE bb 3597 FF aa 2539 FF aa 2539 FF aa 2539 FF aa 2539 + FF bb 3812 FF bb 3812 FF bb 3812 FF bb 3812 FF bb 3812 FF bb 3812 + GG aa 5668 GG aa 5668 GG aa 5668 GG aa 5668 GG bb 5102 GG bb 5102 + GG bb 5102 GG bb 5102 HH aa 4468 HH aa 4468 HH aa 4468 HH bb 5120 + HH bb 5120 HH bb 5120 HH bb 5120 HH bb 5120 HH bb 5120 II aa 5443 + II aa 5443 II bb 4244 II bb 4244 II bb 4244 II bb 4244 II bb 4244 + JJ aa 3640 JJ aa 3640 JJ aa 3640 JJ aa 3640 JJ bb 4604 JJ bb 4604 + JJ bb 4604 JJ bb 4604} + +do_execsql_test 1.6.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.6.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 870 BB bb 870 BB bb 870 BB bb 870 + BB bb 870 BB bb 870 CC aa 840 CC aa 840 CC aa 840 CC aa 840 + CC bb 840 CC bb 840 DD aa 822 DD aa 822 DD aa 822 DD bb 845 + DD bb 845 DD bb 845 DD bb 845 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 777 FF aa 777 FF aa 777 FF aa 777 + FF bb 768 FF bb 768 FF bb 768 FF bb 768 FF bb 768 FF bb 768 + GG aa 938 GG aa 938 GG aa 938 GG aa 938 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 938 HH aa 938 HH aa 938 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 963 II bb 963 II bb 963 II bb 963 II bb 963 + JJ aa 805 JJ aa 805 JJ aa 805 JJ aa 805 JJ bb 899 JJ bb 899 + JJ bb 899 JJ bb 899} + +do_execsql_test 1.6.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 247 BB bb 247 BB bb 247 BB bb 247 + BB bb 247 BB bb 247 CC aa 247 CC aa 247 CC aa 247 CC aa 247 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 224 + DD bb 224 DD bb 224 DD bb 224 EE aa 224 EE aa 224 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 208 FF bb 208 FF bb 208 FF bb 208 FF bb 208 FF bb 208 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 148 HH aa 148 HH aa 148 HH bb 160 + HH bb 160 HH bb 160 HH bb 160 HH bb 160 HH bb 160 II aa 133 + II aa 133 II bb 133 II bb 133 II bb 133 II bb 133 II bb 133 + JJ aa 250 JJ aa 250 JJ aa 250 JJ aa 250 JJ bb 113 JJ bb 113 + JJ bb 113 JJ bb 113} + +do_execsql_test 1.6.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 + BB bb 4685 CC aa 12025 CC aa 12025 CC aa 12025 CC aa 12025 + CC bb 12025 CC bb 12025 DD aa 10462 DD aa 10462 DD aa 10462 + DD bb 10462 DD bb 10462 DD bb 10462 DD bb 10462 EE aa 7154 + EE aa 7154 EE bb 7154 EE bb 7154 EE bb 7154 FF aa 6571 FF aa 6571 + FF aa 6571 FF aa 6571 FF bb 6571 FF bb 6571 FF bb 6571 FF bb 6571 + FF bb 6571 FF bb 6571 GG aa 8207 GG aa 8207 GG aa 8207 GG aa 8207 + GG bb 8207 GG bb 8207 GG bb 8207 GG bb 8207 HH aa 10136 + HH aa 10136 HH aa 10136 HH bb 10136 HH bb 10136 HH bb 10136 + HH bb 10136 HH bb 10136 HH bb 10136 II aa 9911 II aa 9911 + II bb 9911 II bb 9911 II bb 9911 II bb 9911 II bb 9911 JJ aa 9083 + JJ aa 9083 JJ aa 9083 JJ aa 9083 JJ bb 9083 JJ bb 9083 JJ bb 9083 + JJ bb 9083} + +do_execsql_test 1.6.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 2307 AA bb 2307 + AA bb 2307 AA bb 2307 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB aa 4685 BB bb 5251 BB bb 5251 BB bb 5251 BB bb 5251 + BB bb 5251 BB bb 5251 CC aa 7340 CC aa 7340 CC aa 7340 CC aa 7340 + CC bb 6421 CC bb 6421 DD aa 3122 DD aa 3122 DD aa 3122 DD bb 2493 + DD bb 2493 DD bb 2493 DD bb 2493 EE aa 4032 EE aa 4032 EE bb 3597 + EE bb 3597 EE bb 3597 FF aa 2539 FF aa 2539 FF aa 2539 FF aa 2539 + FF bb 3812 FF bb 3812 FF bb 3812 FF bb 3812 FF bb 3812 FF bb 3812 + GG aa 5668 GG aa 5668 GG aa 5668 GG aa 5668 GG bb 5102 GG bb 5102 + GG bb 5102 GG bb 5102 HH aa 4468 HH aa 4468 HH aa 4468 HH bb 5120 + HH bb 5120 HH bb 5120 HH bb 5120 HH bb 5120 HH bb 5120 II aa 5443 + II aa 5443 II bb 4244 II bb 4244 II bb 4244 II bb 4244 II bb 4244 + JJ aa 3640 JJ aa 3640 JJ aa 3640 JJ aa 3640 JJ bb 4604 JJ bb 4604 + JJ bb 4604 JJ bb 4604} + +do_execsql_test 1.6.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND 1 PRECEDING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} {} {} {} AA aa {} {} {} {} AA aa {} {} {} {} + AA aa {} {} {} {} AA bb {} {} 2307 2307 AA bb {} {} 2307 2307 + AA bb {} {} 2307 2307 AA bb {} {} 2307 2307 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB bb 4685 4685 5251 5251 + BB bb 4685 4685 5251 5251 BB bb 4685 4685 5251 5251 + BB bb 4685 4685 5251 5251 BB bb 4685 4685 5251 5251 + BB bb 4685 4685 5251 5251 CC aa 12025 12025 7340 7340 + CC aa 12025 12025 7340 7340 CC aa 12025 12025 7340 7340 + CC aa 12025 12025 7340 7340 CC bb 12025 12025 6421 6421 + CC bb 12025 12025 6421 6421 DD aa 10462 10462 3122 3122 + DD aa 10462 10462 3122 3122 DD aa 10462 10462 3122 3122 + DD bb 10462 10462 2493 2493 DD bb 10462 10462 2493 2493 + DD bb 10462 10462 2493 2493 DD bb 10462 10462 2493 2493 + EE aa 7154 7154 4032 4032 EE aa 7154 7154 4032 4032 + EE bb 7154 7154 3597 3597 EE bb 7154 7154 3597 3597 + EE bb 7154 7154 3597 3597 FF aa 6571 6571 2539 2539 + FF aa 6571 6571 2539 2539 FF aa 6571 6571 2539 2539 + FF aa 6571 6571 2539 2539 FF bb 6571 6571 3812 3812 + FF bb 6571 6571 3812 3812 FF bb 6571 6571 3812 3812 + FF bb 6571 6571 3812 3812 FF bb 6571 6571 3812 3812 + FF bb 6571 6571 3812 3812 GG aa 8207 8207 5668 5668 + GG aa 8207 8207 5668 5668 GG aa 8207 8207 5668 5668 + GG aa 8207 8207 5668 5668 GG bb 8207 8207 5102 5102 + GG bb 8207 8207 5102 5102 GG bb 8207 8207 5102 5102 + GG bb 8207 8207 5102 5102 HH aa 10136 10136 4468 4468 + HH aa 10136 10136 4468 4468 HH aa 10136 10136 4468 4468 + HH bb 10136 10136 5120 5120 HH bb 10136 10136 5120 5120 + HH bb 10136 10136 5120 5120 HH bb 10136 10136 5120 5120 + HH bb 10136 10136 5120 5120 HH bb 10136 10136 5120 5120 + II aa 9911 9911 5443 5443 II aa 9911 9911 5443 5443 + II bb 9911 9911 4244 4244 II bb 9911 9911 4244 4244 + II bb 9911 9911 4244 4244 II bb 9911 9911 4244 4244 + II bb 9911 9911 4244 4244 JJ aa 9083 9083 3640 3640 + JJ aa 9083 9083 3640 3640 JJ aa 9083 9083 3640 3640 + JJ aa 9083 9083 3640 3640 JJ bb 9083 9083 4604 4604 + JJ bb 9083 9083 4604 4604 JJ bb 9083 9083 4604 4604 + JJ bb 9083 9083 4604 4604} + +do_execsql_test 1.7.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 + BB bb 4685 CC aa 12025 CC aa 12025 CC aa 12025 CC aa 12025 + CC bb 12025 CC bb 12025 DD aa 15147 DD aa 15147 DD aa 15147 + DD bb 15147 DD bb 15147 DD bb 15147 DD bb 15147 EE aa 14494 + EE aa 14494 EE bb 14494 EE bb 14494 EE bb 14494 FF aa 9693 + FF aa 9693 FF aa 9693 FF aa 9693 FF bb 9693 FF bb 9693 FF bb 9693 + FF bb 9693 FF bb 9693 FF bb 9693 GG aa 12239 GG aa 12239 + GG aa 12239 GG aa 12239 GG bb 12239 GG bb 12239 GG bb 12239 + GG bb 12239 HH aa 12675 HH aa 12675 HH aa 12675 HH bb 12675 + HH bb 12675 HH bb 12675 HH bb 12675 HH bb 12675 HH bb 12675 + II aa 15579 II aa 15579 II bb 15579 II bb 15579 II bb 15579 + II bb 15579 II bb 15579 JJ aa 13551 JJ aa 13551 JJ aa 13551 + JJ aa 13551 JJ bb 13551 JJ bb 13551 JJ bb 13551 JJ bb 13551} + +do_execsql_test 1.7.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 2307 AA bb 2307 + AA bb 2307 AA bb 2307 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB aa 4685 BB bb 7558 BB bb 7558 BB bb 7558 BB bb 7558 + BB bb 7558 BB bb 7558 CC aa 9718 CC aa 9718 CC aa 9718 CC aa 9718 + CC bb 9294 CC bb 9294 DD aa 7589 DD aa 7589 DD aa 7589 DD bb 4447 + DD bb 4447 DD bb 4447 DD bb 4447 EE aa 5200 EE aa 5200 EE bb 4922 + EE bb 4922 EE bb 4922 FF aa 5246 FF aa 5246 FF aa 5246 FF aa 5246 + FF bb 4702 FF bb 4702 FF bb 4702 FF bb 4702 FF bb 4702 FF bb 4702 + GG aa 7317 GG aa 7317 GG aa 7317 GG aa 7317 GG bb 7265 GG bb 7265 + GG bb 7265 GG bb 7265 HH aa 7973 HH aa 7973 HH aa 7973 HH bb 6717 + HH bb 6717 HH bb 6717 HH bb 6717 HH bb 6717 HH bb 6717 II aa 8314 + II aa 8314 II bb 6493 II bb 6493 II bb 6493 II bb 6493 II bb 6493 + JJ aa 6834 JJ aa 6834 JJ aa 6834 JJ aa 6834 JJ bb 5654 JJ bb 5654 + JJ bb 5654 JJ bb 5654} + +do_execsql_test 1.7.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.7.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 934 BB bb 934 BB bb 934 BB bb 934 + BB bb 934 BB bb 934 CC aa 870 CC aa 870 CC aa 870 CC aa 870 + CC bb 840 CC bb 840 DD aa 840 DD aa 840 DD aa 840 DD bb 845 + DD bb 845 DD bb 845 DD bb 845 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 959 FF aa 959 FF aa 959 FF aa 959 + FF bb 777 FF bb 777 FF bb 777 FF bb 777 FF bb 777 FF bb 777 + GG aa 938 GG aa 938 GG aa 938 GG aa 938 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 938 HH aa 938 HH aa 938 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 963 JJ aa 963 JJ aa 963 JJ aa 963 JJ bb 899 JJ bb 899 + JJ bb 899 JJ bb 899} + +do_execsql_test 1.7.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 223 BB bb 223 BB bb 223 BB bb 223 + BB bb 223 BB bb 223 CC aa 247 CC aa 247 CC aa 247 CC aa 247 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 158 + DD bb 158 DD bb 158 DD bb 158 EE aa 224 EE aa 224 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 148 + HH bb 148 HH bb 148 HH bb 148 HH bb 148 HH bb 148 II aa 133 + II aa 133 II bb 133 II bb 133 II bb 133 II bb 133 II bb 133 + JJ aa 133 JJ aa 133 JJ aa 133 JJ aa 133 JJ bb 113 JJ bb 113 + JJ bb 113 JJ bb 113} + +do_execsql_test 1.7.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 BB bb 4685 + BB bb 4685 CC aa 12025 CC aa 12025 CC aa 12025 CC aa 12025 + CC bb 12025 CC bb 12025 DD aa 15147 DD aa 15147 DD aa 15147 + DD bb 15147 DD bb 15147 DD bb 15147 DD bb 15147 EE aa 14494 + EE aa 14494 EE bb 14494 EE bb 14494 EE bb 14494 FF aa 9693 + FF aa 9693 FF aa 9693 FF aa 9693 FF bb 9693 FF bb 9693 FF bb 9693 + FF bb 9693 FF bb 9693 FF bb 9693 GG aa 12239 GG aa 12239 + GG aa 12239 GG aa 12239 GG bb 12239 GG bb 12239 GG bb 12239 + GG bb 12239 HH aa 12675 HH aa 12675 HH aa 12675 HH bb 12675 + HH bb 12675 HH bb 12675 HH bb 12675 HH bb 12675 HH bb 12675 + II aa 15579 II aa 15579 II bb 15579 II bb 15579 II bb 15579 + II bb 15579 II bb 15579 JJ aa 13551 JJ aa 13551 JJ aa 13551 + JJ aa 13551 JJ bb 13551 JJ bb 13551 JJ bb 13551 JJ bb 13551} + +do_execsql_test 1.7.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb 2307 AA bb 2307 + AA bb 2307 AA bb 2307 BB aa 4685 BB aa 4685 BB aa 4685 BB aa 4685 + BB aa 4685 BB aa 4685 BB bb 7558 BB bb 7558 BB bb 7558 BB bb 7558 + BB bb 7558 BB bb 7558 CC aa 9718 CC aa 9718 CC aa 9718 CC aa 9718 + CC bb 9294 CC bb 9294 DD aa 7589 DD aa 7589 DD aa 7589 DD bb 4447 + DD bb 4447 DD bb 4447 DD bb 4447 EE aa 5200 EE aa 5200 EE bb 4922 + EE bb 4922 EE bb 4922 FF aa 5246 FF aa 5246 FF aa 5246 FF aa 5246 + FF bb 4702 FF bb 4702 FF bb 4702 FF bb 4702 FF bb 4702 FF bb 4702 + GG aa 7317 GG aa 7317 GG aa 7317 GG aa 7317 GG bb 7265 GG bb 7265 + GG bb 7265 GG bb 7265 HH aa 7973 HH aa 7973 HH aa 7973 HH bb 6717 + HH bb 6717 HH bb 6717 HH bb 6717 HH bb 6717 HH bb 6717 II aa 8314 + II aa 8314 II bb 6493 II bb 6493 II bb 6493 II bb 6493 II bb 6493 + JJ aa 6834 JJ aa 6834 JJ aa 6834 JJ aa 6834 JJ bb 5654 JJ bb 5654 + JJ bb 5654 JJ bb 5654} + +do_execsql_test 1.7.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 1 PRECEDING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} {} {} {} AA aa {} {} {} {} AA aa {} {} {} {} + AA aa {} {} {} {} AA bb {} {} 2307 2307 AA bb {} {} 2307 2307 + AA bb {} {} 2307 2307 AA bb {} {} 2307 2307 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB aa 4685 4685 4685 4685 + BB aa 4685 4685 4685 4685 BB bb 4685 4685 7558 7558 + BB bb 4685 4685 7558 7558 BB bb 4685 4685 7558 7558 + BB bb 4685 4685 7558 7558 BB bb 4685 4685 7558 7558 + BB bb 4685 4685 7558 7558 CC aa 12025 12025 9718 9718 + CC aa 12025 12025 9718 9718 CC aa 12025 12025 9718 9718 + CC aa 12025 12025 9718 9718 CC bb 12025 12025 9294 9294 + CC bb 12025 12025 9294 9294 DD aa 15147 15147 7589 7589 + DD aa 15147 15147 7589 7589 DD aa 15147 15147 7589 7589 + DD bb 15147 15147 4447 4447 DD bb 15147 15147 4447 4447 + DD bb 15147 15147 4447 4447 DD bb 15147 15147 4447 4447 + EE aa 14494 14494 5200 5200 EE aa 14494 14494 5200 5200 + EE bb 14494 14494 4922 4922 EE bb 14494 14494 4922 4922 + EE bb 14494 14494 4922 4922 FF aa 9693 9693 5246 5246 + FF aa 9693 9693 5246 5246 FF aa 9693 9693 5246 5246 + FF aa 9693 9693 5246 5246 FF bb 9693 9693 4702 4702 + FF bb 9693 9693 4702 4702 FF bb 9693 9693 4702 4702 + FF bb 9693 9693 4702 4702 FF bb 9693 9693 4702 4702 + FF bb 9693 9693 4702 4702 GG aa 12239 12239 7317 7317 + GG aa 12239 12239 7317 7317 GG aa 12239 12239 7317 7317 + GG aa 12239 12239 7317 7317 GG bb 12239 12239 7265 7265 + GG bb 12239 12239 7265 7265 GG bb 12239 12239 7265 7265 + GG bb 12239 12239 7265 7265 HH aa 12675 12675 7973 7973 + HH aa 12675 12675 7973 7973 HH aa 12675 12675 7973 7973 + HH bb 12675 12675 6717 6717 HH bb 12675 12675 6717 6717 + HH bb 12675 12675 6717 6717 HH bb 12675 12675 6717 6717 + HH bb 12675 12675 6717 6717 HH bb 12675 12675 6717 6717 + II aa 15579 15579 8314 8314 II aa 15579 15579 8314 8314 + II bb 15579 15579 6493 6493 II bb 15579 15579 6493 6493 + II bb 15579 15579 6493 6493 II bb 15579 15579 6493 6493 + II bb 15579 15579 6493 6493 JJ aa 13551 13551 6834 6834 + JJ aa 13551 13551 6834 6834 JJ aa 13551 13551 6834 6834 + JJ aa 13551 13551 6834 6834 JJ bb 13551 13551 5654 5654 + JJ bb 13551 13551 5654 5654 JJ bb 13551 13551 5654 5654 + JJ bb 13551 13551 5654 5654} + +do_execsql_test 1.8.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 12025 BB aa 12025 BB aa 12025 + BB aa 12025 BB aa 12025 BB aa 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 BB bb 12025 CC aa 15147 + CC aa 15147 CC aa 15147 CC aa 15147 CC bb 15147 CC bb 15147 + DD aa 19179 DD aa 19179 DD aa 19179 DD bb 19179 DD bb 19179 + DD bb 19179 DD bb 19179 EE aa 17033 EE aa 17033 EE bb 17033 + EE bb 17033 EE bb 17033 FF aa 15361 FF aa 15361 FF aa 15361 + FF aa 15361 FF bb 15361 FF bb 15361 FF bb 15361 FF bb 15361 + FF bb 15361 FF bb 15361 GG aa 16707 GG aa 16707 GG aa 16707 + GG aa 16707 GG bb 16707 GG bb 16707 GG bb 16707 GG bb 16707 + HH aa 18118 HH aa 18118 HH aa 18118 HH bb 18118 HH bb 18118 + HH bb 18118 HH bb 18118 HH bb 18118 HH bb 18118 II aa 19219 + II aa 19219 II bb 19219 II bb 19219 II bb 19219 II bb 19219 + II bb 19219 JJ aa 17351 JJ aa 17351 JJ aa 17351 JJ aa 17351 + JJ bb 17351 JJ bb 17351 JJ bb 17351 JJ bb 17351} + +do_execsql_test 1.8.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 2307 AA aa 2307 AA aa 2307 AA aa 2307 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 7558 BB aa 7558 BB aa 7558 BB aa 7558 + BB aa 7558 BB aa 7558 BB bb 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 CC aa 11672 CC aa 11672 + CC aa 11672 CC aa 11672 CC bb 10462 CC bb 10462 DD aa 8914 + DD aa 8914 DD aa 8914 DD bb 7154 DD bb 7154 DD bb 7154 DD bb 7154 + EE aa 6090 EE aa 6090 EE bb 6571 EE bb 6571 EE bb 6571 FF aa 7409 + FF aa 7409 FF aa 7409 FF aa 7409 FF bb 8207 FF bb 8207 FF bb 8207 + FF bb 8207 FF bb 8207 FF bb 8207 GG aa 8914 GG aa 8914 GG aa 8914 + GG aa 8914 GG bb 10136 GG bb 10136 GG bb 10136 GG bb 10136 + HH aa 10222 HH aa 10222 HH aa 10222 HH bb 9911 HH bb 9911 + HH bb 9911 HH bb 9911 HH bb 9911 HH bb 9911 II aa 9364 II aa 9364 + II bb 9083 II bb 9083 II bb 9083 II bb 9083 II bb 9083 JJ aa 8848 + JJ aa 8848 JJ aa 8848 JJ aa 8848 JJ bb 7440 JJ bb 7440 JJ bb 7440 + JJ bb 7440} + +do_execsql_test 1.8.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.8.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 934 BB bb 934 BB bb 934 BB bb 934 + BB bb 934 BB bb 934 CC aa 870 CC aa 870 CC aa 870 CC aa 870 + CC bb 840 CC bb 840 DD aa 845 DD aa 845 DD aa 845 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 959 FF aa 959 FF aa 959 FF aa 959 + FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 + GG aa 938 GG aa 938 GG aa 938 GG aa 938 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 963 JJ aa 963 JJ aa 963 JJ aa 963 JJ bb 899 JJ bb 899 + JJ bb 899 JJ bb 899} + +do_execsql_test 1.8.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 223 BB bb 223 BB bb 223 BB bb 223 + BB bb 223 BB bb 223 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 158 + DD bb 158 DD bb 158 DD bb 158 EE aa 113 EE aa 113 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 133 + HH bb 133 HH bb 133 HH bb 133 HH bb 133 HH bb 133 II aa 133 + II aa 133 II bb 133 II bb 133 II bb 133 II bb 133 II bb 133 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 113 JJ bb 113 + JJ bb 113 JJ bb 113} + +do_execsql_test 1.8.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 11263 BB aa 11365 BB aa 11613 + BB aa 11626 BB aa 11632 BB aa 11778 BB bb 11185 BB bb 11233 + BB bb 11239 BB bb 11314 BB bb 11320 BB bb 11392 CC aa 14388 + CC aa 14540 CC aa 14717 CC aa 14989 CC bb 14325 CC bb 14801 + DD aa 18334 DD aa 18923 DD aa 18955 DD bb 18220 DD bb 18385 + DD bb 18463 DD bb 18941 EE aa 16256 EE aa 16920 EE bb 16265 + EE bb 16404 EE bb 16781 FF aa 14691 FF aa 14694 FF aa 14743 + FF aa 15153 FF bb 14423 FF bb 14491 FF bb 14635 FF bb 14787 + FF bb 15066 FF bb 15259 GG aa 16073 GG aa 16227 GG aa 16372 + GG aa 16559 GG bb 15769 GG bb 15778 GG bb 15863 GG bb 16547 + HH aa 17139 HH aa 17328 HH aa 17638 HH bb 17155 HH bb 17388 + HH bb 17435 HH bb 17763 HH bb 17788 HH bb 17985 II aa 18567 + II aa 18821 II bb 18414 II bb 18548 II bb 18776 II bb 18798 + II bb 18969 JJ aa 16452 JJ aa 16583 JJ aa 17117 JJ aa 17238 + JJ bb 16512 JJ bb 16997 JJ bb 17015 JJ bb 17094} + +do_execsql_test 1.8.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1373 AA aa 1396 AA aa 2068 AA aa 2084 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 6796 BB aa 6898 BB aa 7146 BB aa 7159 + BB aa 7165 BB aa 7311 BB bb 11185 BB bb 11233 BB bb 11239 + BB bb 11314 BB bb 11320 BB bb 11392 CC aa 10913 CC aa 11065 + CC aa 11242 CC aa 11514 CC bb 9640 CC bb 10116 DD aa 8069 + DD aa 8658 DD aa 8690 DD bb 6195 DD bb 6360 DD bb 6438 DD bb 6916 + EE aa 5313 EE aa 5977 EE bb 5803 EE bb 5942 EE bb 6319 FF aa 6739 + FF aa 6742 FF aa 6791 FF aa 7201 FF bb 7269 FF bb 7337 FF bb 7481 + FF bb 7633 FF bb 7912 FF bb 8105 GG aa 8280 GG aa 8434 GG aa 8579 + GG aa 8766 GG bb 9198 GG bb 9207 GG bb 9292 GG bb 9976 HH aa 9243 + HH aa 9432 HH aa 9742 HH bb 8948 HH bb 9181 HH bb 9228 HH bb 9556 + HH bb 9581 HH bb 9778 II aa 8712 II aa 8966 II bb 8278 II bb 8412 + II bb 8640 II bb 8662 II bb 8833 JJ aa 7949 JJ aa 8080 JJ aa 8614 + JJ aa 8735 JJ bb 6601 JJ bb 7086 JJ bb 7104 JJ bb 7183} + +do_execsql_test 1.8.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 PRECEDING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 4685 1373 2307 AA aa 3774 4685 1396 2307 + AA aa 4446 4685 2068 2307 AA aa 4462 4685 2084 2307 + AA bb 3815 4685 3815 4685 AA bb 4058 4685 4058 4685 + AA bb 4113 4685 4113 4685 AA bb 4376 4685 4376 4685 + BB aa 11263 12025 6796 7558 BB aa 11365 12025 6898 7558 + BB aa 11613 12025 7146 7558 BB aa 11626 12025 7159 7558 + BB aa 11632 12025 7165 7558 BB aa 11778 12025 7311 7558 + BB bb 11185 12025 11185 12025 BB bb 11233 12025 11233 12025 + BB bb 11239 12025 11239 12025 BB bb 11314 12025 11314 12025 + BB bb 11320 12025 11320 12025 BB bb 11392 12025 11392 12025 + CC aa 14388 15147 10913 11672 CC aa 14540 15147 11065 11672 + CC aa 14717 15147 11242 11672 CC aa 14989 15147 11514 11672 + CC bb 14325 15147 9640 10462 CC bb 14801 15147 10116 10462 + DD aa 18334 19179 8069 8914 DD aa 18923 19179 8658 8914 + DD aa 18955 19179 8690 8914 DD bb 18220 19179 6195 7154 + DD bb 18385 19179 6360 7154 DD bb 18463 19179 6438 7154 + DD bb 18941 19179 6916 7154 EE aa 16256 17033 5313 6090 + EE aa 16920 17033 5977 6090 EE bb 16265 17033 5803 6571 + EE bb 16404 17033 5942 6571 EE bb 16781 17033 6319 6571 + FF aa 14691 15361 6739 7409 FF aa 14694 15361 6742 7409 + FF aa 14743 15361 6791 7409 FF aa 15153 15361 7201 7409 + FF bb 14423 15361 7269 8207 FF bb 14491 15361 7337 8207 + FF bb 14635 15361 7481 8207 FF bb 14787 15361 7633 8207 + FF bb 15066 15361 7912 8207 FF bb 15259 15361 8105 8207 + GG aa 16073 16707 8280 8914 GG aa 16227 16707 8434 8914 + GG aa 16372 16707 8579 8914 GG aa 16559 16707 8766 8914 + GG bb 15769 16707 9198 10136 GG bb 15778 16707 9207 10136 + GG bb 15863 16707 9292 10136 GG bb 16547 16707 9976 10136 + HH aa 17139 18118 9243 10222 HH aa 17328 18118 9432 10222 + HH aa 17638 18118 9742 10222 HH bb 17155 18118 8948 9911 + HH bb 17388 18118 9181 9911 HH bb 17435 18118 9228 9911 + HH bb 17763 18118 9556 9911 HH bb 17788 18118 9581 9911 + HH bb 17985 18118 9778 9911 II aa 18567 19219 8712 9364 + II aa 18821 19219 8966 9364 II bb 18414 19219 8278 9083 + II bb 18548 19219 8412 9083 II bb 18776 19219 8640 9083 + II bb 18798 19219 8662 9083 II bb 18969 19219 8833 9083 + JJ aa 16452 17351 7949 8848 JJ aa 16583 17351 8080 8848 + JJ aa 17117 17351 8614 8848 JJ aa 17238 17351 8735 8848 + JJ bb 16512 17351 6601 7440 JJ bb 16997 17351 7086 7440 + JJ bb 17015 17351 7104 7440 JJ bb 17094 17351 7183 7440} + +do_execsql_test 1.9.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 12025 BB aa 12025 BB aa 12025 + BB aa 12025 BB aa 12025 BB aa 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 BB bb 12025 CC aa 15147 + CC aa 15147 CC aa 15147 CC aa 15147 CC bb 15147 CC bb 15147 + DD aa 14494 DD aa 14494 DD aa 14494 DD bb 14494 DD bb 14494 + DD bb 14494 DD bb 14494 EE aa 9693 EE aa 9693 EE bb 9693 + EE bb 9693 EE bb 9693 FF aa 12239 FF aa 12239 FF aa 12239 + FF aa 12239 FF bb 12239 FF bb 12239 FF bb 12239 FF bb 12239 + FF bb 12239 FF bb 12239 GG aa 12675 GG aa 12675 GG aa 12675 + GG aa 12675 GG bb 12675 GG bb 12675 GG bb 12675 GG bb 12675 + HH aa 15579 HH aa 15579 HH aa 15579 HH bb 15579 HH bb 15579 + HH bb 15579 HH bb 15579 HH bb 15579 HH bb 15579 II aa 13551 + II aa 13551 II bb 13551 II bb 13551 II bb 13551 II bb 13551 + II bb 13551 JJ aa 12883 JJ aa 12883 JJ aa 12883 JJ aa 12883 + JJ bb 12883 JJ bb 12883 JJ bb 12883 JJ bb 12883} + +do_execsql_test 1.9.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 2307 AA aa 2307 AA aa 2307 AA aa 2307 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 7558 BB aa 7558 BB aa 7558 BB aa 7558 + BB aa 7558 BB aa 7558 BB bb 9718 BB bb 9718 BB bb 9718 BB bb 9718 + BB bb 9718 BB bb 9718 CC aa 9294 CC aa 9294 CC aa 9294 CC aa 9294 + CC bb 7589 CC bb 7589 DD aa 4447 DD aa 4447 DD aa 4447 DD bb 5200 + DD bb 5200 DD bb 5200 DD bb 5200 EE aa 4922 EE aa 4922 EE bb 5246 + EE bb 5246 EE bb 5246 FF aa 4702 FF aa 4702 FF aa 4702 FF aa 4702 + FF bb 7317 FF bb 7317 FF bb 7317 FF bb 7317 FF bb 7317 FF bb 7317 + GG aa 7265 GG aa 7265 GG aa 7265 GG aa 7265 GG bb 7973 GG bb 7973 + GG bb 7973 GG bb 7973 HH aa 6717 HH aa 6717 HH aa 6717 HH bb 8314 + HH bb 8314 HH bb 8314 HH bb 8314 HH bb 8314 HH bb 8314 II aa 6493 + II aa 6493 II bb 6834 II bb 6834 II bb 6834 II bb 6834 II bb 6834 + JJ aa 5654 JJ aa 5654 JJ aa 5654 JJ aa 5654 JJ bb 6390 JJ bb 6390 + JJ bb 6390 JJ bb 6390} + +do_execsql_test 1.9.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.9.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 870 BB bb 870 BB bb 870 BB bb 870 + BB bb 870 BB bb 870 CC aa 840 CC aa 840 CC aa 840 CC aa 840 + CC bb 840 CC bb 840 DD aa 845 DD aa 845 DD aa 845 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 777 FF aa 777 FF aa 777 FF aa 777 + FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 + GG aa 938 GG aa 938 GG aa 938 GG aa 938 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 963 II bb 963 II bb 963 II bb 963 II bb 963 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 899 JJ bb 899 + JJ bb 899 JJ bb 899} + +do_execsql_test 1.9.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 247 BB bb 247 BB bb 247 BB bb 247 + BB bb 247 BB bb 247 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 224 + DD bb 224 DD bb 224 DD bb 224 EE aa 113 EE aa 113 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 148 HH aa 148 HH aa 148 HH bb 133 + HH bb 133 HH bb 133 HH bb 133 HH bb 133 HH bb 133 II aa 133 + II aa 133 II bb 133 II bb 133 II bb 133 II bb 133 II bb 133 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 113 JJ bb 113 + JJ bb 113 JJ bb 113} + +do_execsql_test 1.9.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 11263 BB aa 11365 BB aa 11613 + BB aa 11626 BB aa 11632 BB aa 11778 BB bb 11185 BB bb 11233 + BB bb 11239 BB bb 11314 BB bb 11320 BB bb 11392 CC aa 14388 + CC aa 14540 CC aa 14717 CC aa 14989 CC bb 14325 CC bb 14801 + DD aa 13649 DD aa 14238 DD aa 14270 DD bb 13535 DD bb 13700 + DD bb 13778 DD bb 14256 EE aa 8916 EE aa 9580 EE bb 8925 + EE bb 9064 EE bb 9441 FF aa 11569 FF aa 11572 FF aa 11621 + FF aa 12031 FF bb 11301 FF bb 11369 FF bb 11513 FF bb 11665 + FF bb 11944 FF bb 12137 GG aa 12041 GG aa 12195 GG aa 12340 + GG aa 12527 GG bb 11737 GG bb 11746 GG bb 11831 GG bb 12515 + HH aa 14600 HH aa 14789 HH aa 15099 HH bb 14616 HH bb 14849 + HH bb 14896 HH bb 15224 HH bb 15249 HH bb 15446 II aa 12899 + II aa 13153 II bb 12746 II bb 12880 II bb 13108 II bb 13130 + II bb 13301 JJ aa 11984 JJ aa 12115 JJ aa 12649 JJ aa 12770 + JJ bb 12044 JJ bb 12529 JJ bb 12547 JJ bb 12626} + +do_execsql_test 1.9.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1373 AA aa 1396 AA aa 2068 AA aa 2084 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 6796 BB aa 6898 BB aa 7146 BB aa 7159 + BB aa 7165 BB aa 7311 BB bb 8878 BB bb 8926 BB bb 8932 BB bb 9007 + BB bb 9013 BB bb 9085 CC aa 8535 CC aa 8687 CC aa 8864 CC aa 9136 + CC bb 6767 CC bb 7243 DD aa 3602 DD aa 4191 DD aa 4223 DD bb 4241 + DD bb 4406 DD bb 4484 DD bb 4962 EE aa 4145 EE aa 4809 EE bb 4478 + EE bb 4617 EE bb 4994 FF aa 4032 FF aa 4035 FF aa 4084 FF aa 4494 + FF bb 6379 FF bb 6447 FF bb 6591 FF bb 6743 FF bb 7022 FF bb 7215 + GG aa 6631 GG aa 6785 GG aa 6930 GG aa 7117 GG bb 7035 GG bb 7044 + GG bb 7129 GG bb 7813 HH aa 5738 HH aa 5927 HH aa 6237 HH bb 7351 + HH bb 7584 HH bb 7631 HH bb 7959 HH bb 7984 HH bb 8181 II aa 5841 + II aa 6095 II bb 6029 II bb 6163 II bb 6391 II bb 6413 II bb 6584 + JJ aa 4755 JJ aa 4886 JJ aa 5420 JJ aa 5541 JJ bb 5551 JJ bb 6036 + JJ bb 6054 JJ bb 6133} + +do_execsql_test 1.9.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND CURRENT ROW ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 4685 1373 2307 AA aa 3774 4685 1396 2307 + AA aa 4446 4685 2068 2307 AA aa 4462 4685 2084 2307 + AA bb 3815 4685 3815 4685 AA bb 4058 4685 4058 4685 + AA bb 4113 4685 4113 4685 AA bb 4376 4685 4376 4685 + BB aa 11263 12025 6796 7558 BB aa 11365 12025 6898 7558 + BB aa 11613 12025 7146 7558 BB aa 11626 12025 7159 7558 + BB aa 11632 12025 7165 7558 BB aa 11778 12025 7311 7558 + BB bb 11185 12025 8878 9718 BB bb 11233 12025 8926 9718 + BB bb 11239 12025 8932 9718 BB bb 11314 12025 9007 9718 + BB bb 11320 12025 9013 9718 BB bb 11392 12025 9085 9718 + CC aa 14388 15147 8535 9294 CC aa 14540 15147 8687 9294 + CC aa 14717 15147 8864 9294 CC aa 14989 15147 9136 9294 + CC bb 14325 15147 6767 7589 CC bb 14801 15147 7243 7589 + DD aa 13649 14494 3602 4447 DD aa 14238 14494 4191 4447 + DD aa 14270 14494 4223 4447 DD bb 13535 14494 4241 5200 + DD bb 13700 14494 4406 5200 DD bb 13778 14494 4484 5200 + DD bb 14256 14494 4962 5200 EE aa 8916 9693 4145 4922 + EE aa 9580 9693 4809 4922 EE bb 8925 9693 4478 5246 + EE bb 9064 9693 4617 5246 EE bb 9441 9693 4994 5246 + FF aa 11569 12239 4032 4702 FF aa 11572 12239 4035 4702 + FF aa 11621 12239 4084 4702 FF aa 12031 12239 4494 4702 + FF bb 11301 12239 6379 7317 FF bb 11369 12239 6447 7317 + FF bb 11513 12239 6591 7317 FF bb 11665 12239 6743 7317 + FF bb 11944 12239 7022 7317 FF bb 12137 12239 7215 7317 + GG aa 12041 12675 6631 7265 GG aa 12195 12675 6785 7265 + GG aa 12340 12675 6930 7265 GG aa 12527 12675 7117 7265 + GG bb 11737 12675 7035 7973 GG bb 11746 12675 7044 7973 + GG bb 11831 12675 7129 7973 GG bb 12515 12675 7813 7973 + HH aa 14600 15579 5738 6717 HH aa 14789 15579 5927 6717 + HH aa 15099 15579 6237 6717 HH bb 14616 15579 7351 8314 + HH bb 14849 15579 7584 8314 HH bb 14896 15579 7631 8314 + HH bb 15224 15579 7959 8314 HH bb 15249 15579 7984 8314 + HH bb 15446 15579 8181 8314 II aa 12899 13551 5841 6493 + II aa 13153 13551 6095 6493 II bb 12746 13551 6029 6834 + II bb 12880 13551 6163 6834 II bb 13108 13551 6391 6834 + II bb 13130 13551 6413 6834 II bb 13301 13551 6584 6834 + JJ aa 11984 12883 4755 5654 JJ aa 12115 12883 4886 5654 + JJ aa 12649 12883 5420 5654 JJ aa 12770 12883 5541 5654 + JJ bb 12044 12883 5551 6390 JJ bb 12529 12883 6036 6390 + JJ bb 12547 12883 6054 6390 JJ bb 12626 12883 6133 6390} + +do_execsql_test 1.10.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 12025 BB aa 12025 BB aa 12025 + BB aa 12025 BB aa 12025 BB aa 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 BB bb 12025 CC aa 15147 + CC aa 15147 CC aa 15147 CC aa 15147 CC bb 15147 CC bb 15147 + DD aa 19179 DD aa 19179 DD aa 19179 DD bb 19179 DD bb 19179 + DD bb 19179 DD bb 19179 EE aa 17033 EE aa 17033 EE bb 17033 + EE bb 17033 EE bb 17033 FF aa 15361 FF aa 15361 FF aa 15361 + FF aa 15361 FF bb 15361 FF bb 15361 FF bb 15361 FF bb 15361 + FF bb 15361 FF bb 15361 GG aa 16707 GG aa 16707 GG aa 16707 + GG aa 16707 GG bb 16707 GG bb 16707 GG bb 16707 GG bb 16707 + HH aa 18118 HH aa 18118 HH aa 18118 HH bb 18118 HH bb 18118 + HH bb 18118 HH bb 18118 HH bb 18118 HH bb 18118 II aa 19219 + II aa 19219 II bb 19219 II bb 19219 II bb 19219 II bb 19219 + II bb 19219 JJ aa 17351 JJ aa 17351 JJ aa 17351 JJ aa 17351 + JJ bb 17351 JJ bb 17351 JJ bb 17351 JJ bb 17351} + +do_execsql_test 1.10.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 2307 AA aa 2307 AA aa 2307 AA aa 2307 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 7558 BB aa 7558 BB aa 7558 BB aa 7558 + BB aa 7558 BB aa 7558 BB bb 12025 BB bb 12025 BB bb 12025 + BB bb 12025 BB bb 12025 BB bb 12025 CC aa 11672 CC aa 11672 + CC aa 11672 CC aa 11672 CC bb 10462 CC bb 10462 DD aa 8914 + DD aa 8914 DD aa 8914 DD bb 7154 DD bb 7154 DD bb 7154 DD bb 7154 + EE aa 6090 EE aa 6090 EE bb 6571 EE bb 6571 EE bb 6571 FF aa 7409 + FF aa 7409 FF aa 7409 FF aa 7409 FF bb 8207 FF bb 8207 FF bb 8207 + FF bb 8207 FF bb 8207 FF bb 8207 GG aa 8914 GG aa 8914 GG aa 8914 + GG aa 8914 GG bb 10136 GG bb 10136 GG bb 10136 GG bb 10136 + HH aa 10222 HH aa 10222 HH aa 10222 HH bb 9911 HH bb 9911 + HH bb 9911 HH bb 9911 HH bb 9911 HH bb 9911 II aa 9364 II aa 9364 + II bb 9083 II bb 9083 II bb 9083 II bb 9083 II bb 9083 JJ aa 8848 + JJ aa 8848 JJ aa 8848 JJ aa 8848 JJ bb 7440 JJ bb 7440 JJ bb 7440 + JJ bb 7440} + +do_execsql_test 1.10.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.10.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 934 AA bb 934 + AA bb 934 AA bb 934 BB aa 934 BB aa 934 BB aa 934 BB aa 934 + BB aa 934 BB aa 934 BB bb 934 BB bb 934 BB bb 934 BB bb 934 + BB bb 934 BB bb 934 CC aa 870 CC aa 870 CC aa 870 CC aa 870 + CC bb 840 CC bb 840 DD aa 845 DD aa 845 DD aa 845 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 959 EE aa 959 EE bb 959 + EE bb 959 EE bb 959 FF aa 959 FF aa 959 FF aa 959 FF aa 959 + FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 + GG aa 938 GG aa 938 GG aa 938 GG aa 938 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 979 II bb 979 II bb 979 II bb 979 II bb 979 + JJ aa 963 JJ aa 963 JJ aa 963 JJ aa 963 JJ bb 899 JJ bb 899 + JJ bb 899 JJ bb 899} + +do_execsql_test 1.10.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 223 AA bb 223 + AA bb 223 AA bb 223 BB aa 223 BB aa 223 BB aa 223 BB aa 223 + BB aa 223 BB aa 223 BB bb 223 BB bb 223 BB bb 223 BB bb 223 + BB bb 223 BB bb 223 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 158 CC bb 158 DD aa 158 DD aa 158 DD aa 158 DD bb 158 + DD bb 158 DD bb 158 DD bb 158 EE aa 113 EE aa 113 EE bb 113 + EE bb 113 EE bb 113 FF aa 113 FF aa 113 FF aa 113 FF aa 113 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 102 HH aa 102 HH aa 102 HH bb 133 + HH bb 133 HH bb 133 HH bb 133 HH bb 133 HH bb 133 II aa 133 + II aa 133 II bb 133 II bb 133 II bb 133 II bb 133 II bb 133 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 113 JJ bb 113 + JJ bb 113 JJ bb 113} + +do_execsql_test 1.10.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 11263 BB aa 11365 BB aa 11613 + BB aa 11626 BB aa 11632 BB aa 11778 BB bb 11185 BB bb 11233 + BB bb 11239 BB bb 11314 BB bb 11320 BB bb 11392 CC aa 14388 + CC aa 14540 CC aa 14717 CC aa 14989 CC bb 14325 CC bb 14801 + DD aa 18334 DD aa 18923 DD aa 18955 DD bb 18220 DD bb 18385 + DD bb 18463 DD bb 18941 EE aa 16256 EE aa 16920 EE bb 16265 + EE bb 16404 EE bb 16781 FF aa 14691 FF aa 14694 FF aa 14743 + FF aa 15153 FF bb 14423 FF bb 14491 FF bb 14635 FF bb 14787 + FF bb 15066 FF bb 15259 GG aa 16073 GG aa 16227 GG aa 16372 + GG aa 16559 GG bb 15769 GG bb 15778 GG bb 15863 GG bb 16547 + HH aa 17139 HH aa 17328 HH aa 17638 HH bb 17155 HH bb 17388 + HH bb 17435 HH bb 17763 HH bb 17788 HH bb 17985 II aa 18567 + II aa 18821 II bb 18414 II bb 18548 II bb 18776 II bb 18798 + II bb 18969 JJ aa 16452 JJ aa 16583 JJ aa 17117 JJ aa 17238 + JJ bb 16512 JJ bb 16997 JJ bb 17015 JJ bb 17094} + +do_execsql_test 1.10.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1373 AA aa 1396 AA aa 2068 AA aa 2084 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 6796 BB aa 6898 BB aa 7146 BB aa 7159 + BB aa 7165 BB aa 7311 BB bb 11185 BB bb 11233 BB bb 11239 + BB bb 11314 BB bb 11320 BB bb 11392 CC aa 10913 CC aa 11065 + CC aa 11242 CC aa 11514 CC bb 9640 CC bb 10116 DD aa 8069 + DD aa 8658 DD aa 8690 DD bb 6195 DD bb 6360 DD bb 6438 DD bb 6916 + EE aa 5313 EE aa 5977 EE bb 5803 EE bb 5942 EE bb 6319 FF aa 6739 + FF aa 6742 FF aa 6791 FF aa 7201 FF bb 7269 FF bb 7337 FF bb 7481 + FF bb 7633 FF bb 7912 FF bb 8105 GG aa 8280 GG aa 8434 GG aa 8579 + GG aa 8766 GG bb 9198 GG bb 9207 GG bb 9292 GG bb 9976 HH aa 9243 + HH aa 9432 HH aa 9742 HH bb 8948 HH bb 9181 HH bb 9228 HH bb 9556 + HH bb 9581 HH bb 9778 II aa 8712 II aa 8966 II bb 8278 II bb 8412 + II bb 8640 II bb 8662 II bb 8833 JJ aa 7949 JJ aa 8080 JJ aa 8614 + JJ aa 8735 JJ bb 6601 JJ bb 7086 JJ bb 7104 JJ bb 7183} + +do_execsql_test 1.10.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 3 PRECEDING AND 0 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 4685 1373 2307 AA aa 3774 4685 1396 2307 + AA aa 4446 4685 2068 2307 AA aa 4462 4685 2084 2307 + AA bb 3815 4685 3815 4685 AA bb 4058 4685 4058 4685 + AA bb 4113 4685 4113 4685 AA bb 4376 4685 4376 4685 + BB aa 11263 12025 6796 7558 BB aa 11365 12025 6898 7558 + BB aa 11613 12025 7146 7558 BB aa 11626 12025 7159 7558 + BB aa 11632 12025 7165 7558 BB aa 11778 12025 7311 7558 + BB bb 11185 12025 11185 12025 BB bb 11233 12025 11233 12025 + BB bb 11239 12025 11239 12025 BB bb 11314 12025 11314 12025 + BB bb 11320 12025 11320 12025 BB bb 11392 12025 11392 12025 + CC aa 14388 15147 10913 11672 CC aa 14540 15147 11065 11672 + CC aa 14717 15147 11242 11672 CC aa 14989 15147 11514 11672 + CC bb 14325 15147 9640 10462 CC bb 14801 15147 10116 10462 + DD aa 18334 19179 8069 8914 DD aa 18923 19179 8658 8914 + DD aa 18955 19179 8690 8914 DD bb 18220 19179 6195 7154 + DD bb 18385 19179 6360 7154 DD bb 18463 19179 6438 7154 + DD bb 18941 19179 6916 7154 EE aa 16256 17033 5313 6090 + EE aa 16920 17033 5977 6090 EE bb 16265 17033 5803 6571 + EE bb 16404 17033 5942 6571 EE bb 16781 17033 6319 6571 + FF aa 14691 15361 6739 7409 FF aa 14694 15361 6742 7409 + FF aa 14743 15361 6791 7409 FF aa 15153 15361 7201 7409 + FF bb 14423 15361 7269 8207 FF bb 14491 15361 7337 8207 + FF bb 14635 15361 7481 8207 FF bb 14787 15361 7633 8207 + FF bb 15066 15361 7912 8207 FF bb 15259 15361 8105 8207 + GG aa 16073 16707 8280 8914 GG aa 16227 16707 8434 8914 + GG aa 16372 16707 8579 8914 GG aa 16559 16707 8766 8914 + GG bb 15769 16707 9198 10136 GG bb 15778 16707 9207 10136 + GG bb 15863 16707 9292 10136 GG bb 16547 16707 9976 10136 + HH aa 17139 18118 9243 10222 HH aa 17328 18118 9432 10222 + HH aa 17638 18118 9742 10222 HH bb 17155 18118 8948 9911 + HH bb 17388 18118 9181 9911 HH bb 17435 18118 9228 9911 + HH bb 17763 18118 9556 9911 HH bb 17788 18118 9581 9911 + HH bb 17985 18118 9778 9911 II aa 18567 19219 8712 9364 + II aa 18821 19219 8966 9364 II bb 18414 19219 8278 9083 + II bb 18548 19219 8412 9083 II bb 18776 19219 8640 9083 + II bb 18798 19219 8662 9083 II bb 18969 19219 8833 9083 + JJ aa 16452 17351 7949 8848 JJ aa 16583 17351 8080 8848 + JJ aa 17117 17351 8614 8848 JJ aa 17238 17351 8735 8848 + JJ bb 16512 17351 6601 7440 JJ bb 16997 17351 7086 7440 + JJ bb 17015 17351 7104 7440 JJ bb 17094 17351 7183 7440} + +do_execsql_test 1.11.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 44737 + AA bb 44737 AA bb 44737 AA bb 44737 BB aa 44737 BB aa 44737 + BB aa 44737 BB aa 44737 BB aa 44737 BB aa 44737 BB bb 44737 + BB bb 44737 BB bb 44737 BB bb 44737 BB bb 44737 BB bb 44737 + CC aa 44737 CC aa 44737 CC aa 44737 CC aa 44737 CC bb 44737 + CC bb 44737 DD aa 40052 DD aa 40052 DD aa 40052 DD bb 40052 + DD bb 40052 DD bb 40052 DD bb 40052 EE aa 32712 EE aa 32712 + EE bb 32712 EE bb 32712 EE bb 32712 FF aa 29590 FF aa 29590 + FF aa 29590 FF aa 29590 FF bb 29590 FF bb 29590 FF bb 29590 + FF bb 29590 FF bb 29590 FF bb 29590 GG aa 25558 GG aa 25558 + GG aa 25558 GG aa 25558 GG bb 25558 GG bb 25558 GG bb 25558 + GG bb 25558 HH aa 23019 HH aa 23019 HH aa 23019 HH bb 23019 + HH bb 23019 HH bb 23019 HH bb 23019 HH bb 23019 HH bb 23019 + II aa 17351 II aa 17351 II bb 17351 II bb 17351 II bb 17351 + II bb 17351 II bb 17351 JJ aa 12883 JJ aa 12883 JJ aa 12883 + JJ aa 12883 JJ bb 12883 JJ bb 12883 JJ bb 12883 JJ bb 12883} + +do_execsql_test 1.11.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 44737 + AA bb 44737 AA bb 44737 AA bb 44737 BB aa 44737 BB aa 44737 + BB aa 44737 BB aa 44737 BB aa 44737 BB aa 44737 BB bb 42430 + BB bb 42430 BB bb 42430 BB bb 42430 BB bb 42430 BB bb 42430 + CC aa 40052 CC aa 40052 CC aa 40052 CC aa 40052 CC bb 37179 + CC bb 37179 DD aa 32712 DD aa 32712 DD aa 32712 DD bb 30758 + DD bb 30758 DD bb 30758 DD bb 30758 EE aa 29590 EE aa 29590 + EE bb 28265 EE bb 28265 EE bb 28265 FF aa 25558 FF aa 25558 + FF aa 25558 FF aa 25558 FF bb 24668 FF bb 24668 FF bb 24668 + FF bb 24668 FF bb 24668 FF bb 24668 GG aa 23019 GG aa 23019 + GG aa 23019 GG aa 23019 GG bb 20856 GG bb 20856 GG bb 20856 + GG bb 20856 HH aa 17351 HH aa 17351 HH aa 17351 HH bb 15754 + HH bb 15754 HH bb 15754 HH bb 15754 HH bb 15754 HH bb 15754 + II aa 12883 II aa 12883 II bb 10634 II bb 10634 II bb 10634 + II bb 10634 II bb 10634 JJ aa 7440 JJ aa 7440 JJ aa 7440 + JJ aa 7440 JJ bb 6390 JJ bb 6390 JJ bb 6390 JJ bb 6390} + +do_execsql_test 1.11.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.11.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 979 AA aa 979 AA aa 979 AA aa 979 AA bb 979 AA bb 979 + AA bb 979 AA bb 979 BB aa 979 BB aa 979 BB aa 979 BB aa 979 + BB aa 979 BB aa 979 BB bb 979 BB bb 979 BB bb 979 BB bb 979 + BB bb 979 BB bb 979 CC aa 979 CC aa 979 CC aa 979 CC aa 979 + CC bb 979 CC bb 979 DD aa 979 DD aa 979 DD aa 979 DD bb 979 + DD bb 979 DD bb 979 DD bb 979 EE aa 979 EE aa 979 EE bb 979 + EE bb 979 EE bb 979 FF aa 979 FF aa 979 FF aa 979 FF aa 979 + FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 + GG aa 979 GG aa 979 GG aa 979 GG aa 979 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 979 HH aa 979 HH aa 979 HH bb 979 + HH bb 979 HH bb 979 HH bb 979 HH bb 979 HH bb 979 II aa 979 + II aa 979 II bb 963 II bb 963 II bb 963 II bb 963 II bb 963 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 899 JJ bb 899 + JJ bb 899 JJ bb 899} + +do_execsql_test 1.11.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 102 AA aa 102 AA aa 102 AA aa 102 AA bb 102 AA bb 102 + AA bb 102 AA bb 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 + BB aa 102 BB aa 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 + BB bb 102 BB bb 102 CC aa 102 CC aa 102 CC aa 102 CC aa 102 + CC bb 102 CC bb 102 DD aa 102 DD aa 102 DD aa 102 DD bb 102 + DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 + EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 102 GG aa 102 GG aa 102 GG aa 102 GG bb 102 GG bb 102 + GG bb 102 GG bb 102 HH aa 113 HH aa 113 HH aa 113 HH bb 113 + HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 II aa 113 + II aa 113 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 113 JJ bb 113 + JJ bb 113 JJ bb 113} + +do_execsql_test 1.11.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 43867 + AA bb 44110 AA bb 44165 AA bb 44428 BB aa 43975 BB aa 44077 + BB aa 44325 BB aa 44338 BB aa 44344 BB aa 44490 BB bb 43897 + BB bb 43945 BB bb 43951 BB bb 44026 BB bb 44032 BB bb 44104 + CC aa 43978 CC aa 44130 CC aa 44307 CC aa 44579 CC bb 43915 + CC bb 44391 DD aa 39207 DD aa 39796 DD aa 39828 DD bb 39093 + DD bb 39258 DD bb 39336 DD bb 39814 EE aa 31935 EE aa 32599 + EE bb 31944 EE bb 32083 EE bb 32460 FF aa 28920 FF aa 28923 + FF aa 28972 FF aa 29382 FF bb 28652 FF bb 28720 FF bb 28864 + FF bb 29016 FF bb 29295 FF bb 29488 GG aa 24924 GG aa 25078 + GG aa 25223 GG aa 25410 GG bb 24620 GG bb 24629 GG bb 24714 + GG bb 25398 HH aa 22040 HH aa 22229 HH aa 22539 HH bb 22056 + HH bb 22289 HH bb 22336 HH bb 22664 HH bb 22689 HH bb 22886 + II aa 16699 II aa 16953 II bb 16546 II bb 16680 II bb 16908 + II bb 16930 II bb 17101 JJ aa 11984 JJ aa 12115 JJ aa 12649 + JJ aa 12770 JJ bb 12044 JJ bb 12529 JJ bb 12547 JJ bb 12626} + +do_execsql_test 1.11.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 43867 + AA bb 44110 AA bb 44165 AA bb 44428 BB aa 43975 BB aa 44077 + BB aa 44325 BB aa 44338 BB aa 44344 BB aa 44490 BB bb 41590 + BB bb 41638 BB bb 41644 BB bb 41719 BB bb 41725 BB bb 41797 + CC aa 39293 CC aa 39445 CC aa 39622 CC aa 39894 CC bb 36357 + CC bb 36833 DD aa 31867 DD aa 32456 DD aa 32488 DD bb 29799 + DD bb 29964 DD bb 30042 DD bb 30520 EE aa 28813 EE aa 29477 + EE bb 27497 EE bb 27636 EE bb 28013 FF aa 24888 FF aa 24891 + FF aa 24940 FF aa 25350 FF bb 23730 FF bb 23798 FF bb 23942 + FF bb 24094 FF bb 24373 FF bb 24566 GG aa 22385 GG aa 22539 + GG aa 22684 GG aa 22871 GG bb 19918 GG bb 19927 GG bb 20012 + GG bb 20696 HH aa 16372 HH aa 16561 HH aa 16871 HH bb 14791 + HH bb 15024 HH bb 15071 HH bb 15399 HH bb 15424 HH bb 15621 + II aa 12231 II aa 12485 II bb 9829 II bb 9963 II bb 10191 + II bb 10213 II bb 10384 JJ aa 6541 JJ aa 6672 JJ aa 7206 + JJ aa 7327 JJ bb 5551 JJ bb 6036 JJ bb 6054 JJ bb 6133} + +do_execsql_test 1.11.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 2 PRECEDING AND UNBOUNDED FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 44737 43803 44737 AA aa 43826 44737 43826 44737 + AA aa 44498 44737 44498 44737 AA aa 44514 44737 44514 44737 + AA bb 43867 44737 43867 44737 AA bb 44110 44737 44110 44737 + AA bb 44165 44737 44165 44737 AA bb 44428 44737 44428 44737 + BB aa 43975 44737 43975 44737 BB aa 44077 44737 44077 44737 + BB aa 44325 44737 44325 44737 BB aa 44338 44737 44338 44737 + BB aa 44344 44737 44344 44737 BB aa 44490 44737 44490 44737 + BB bb 43897 44737 41590 42430 BB bb 43945 44737 41638 42430 + BB bb 43951 44737 41644 42430 BB bb 44026 44737 41719 42430 + BB bb 44032 44737 41725 42430 BB bb 44104 44737 41797 42430 + CC aa 43978 44737 39293 40052 CC aa 44130 44737 39445 40052 + CC aa 44307 44737 39622 40052 CC aa 44579 44737 39894 40052 + CC bb 43915 44737 36357 37179 CC bb 44391 44737 36833 37179 + DD aa 39207 40052 31867 32712 DD aa 39796 40052 32456 32712 + DD aa 39828 40052 32488 32712 DD bb 39093 40052 29799 30758 + DD bb 39258 40052 29964 30758 DD bb 39336 40052 30042 30758 + DD bb 39814 40052 30520 30758 EE aa 31935 32712 28813 29590 + EE aa 32599 32712 29477 29590 EE bb 31944 32712 27497 28265 + EE bb 32083 32712 27636 28265 EE bb 32460 32712 28013 28265 + FF aa 28920 29590 24888 25558 FF aa 28923 29590 24891 25558 + FF aa 28972 29590 24940 25558 FF aa 29382 29590 25350 25558 + FF bb 28652 29590 23730 24668 FF bb 28720 29590 23798 24668 + FF bb 28864 29590 23942 24668 FF bb 29016 29590 24094 24668 + FF bb 29295 29590 24373 24668 FF bb 29488 29590 24566 24668 + GG aa 24924 25558 22385 23019 GG aa 25078 25558 22539 23019 + GG aa 25223 25558 22684 23019 GG aa 25410 25558 22871 23019 + GG bb 24620 25558 19918 20856 GG bb 24629 25558 19927 20856 + GG bb 24714 25558 20012 20856 GG bb 25398 25558 20696 20856 + HH aa 22040 23019 16372 17351 HH aa 22229 23019 16561 17351 + HH aa 22539 23019 16871 17351 HH bb 22056 23019 14791 15754 + HH bb 22289 23019 15024 15754 HH bb 22336 23019 15071 15754 + HH bb 22664 23019 15399 15754 HH bb 22689 23019 15424 15754 + HH bb 22886 23019 15621 15754 II aa 16699 17351 12231 12883 + II aa 16953 17351 12485 12883 II bb 16546 17351 9829 10634 + II bb 16680 17351 9963 10634 II bb 16908 17351 10191 10634 + II bb 16930 17351 10213 10634 II bb 17101 17351 10384 10634 + JJ aa 11984 12883 6541 7440 JJ aa 12115 12883 6672 7440 + JJ aa 12649 12883 7206 7440 JJ aa 12770 12883 7327 7440 + JJ bb 12044 12883 5551 6390 JJ bb 12529 12883 6036 6390 + JJ bb 12547 12883 6054 6390 JJ bb 12626 12883 6133 6390} + +do_execsql_test 1.12.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 7340 BB aa 7340 BB aa 7340 BB aa 7340 + BB aa 7340 BB aa 7340 BB bb 7340 BB bb 7340 BB bb 7340 BB bb 7340 + BB bb 7340 BB bb 7340 CC aa 3122 CC aa 3122 CC aa 3122 CC aa 3122 + CC bb 3122 CC bb 3122 DD aa 4032 DD aa 4032 DD aa 4032 DD bb 4032 + DD bb 4032 DD bb 4032 DD bb 4032 EE aa 2539 EE aa 2539 EE bb 2539 + EE bb 2539 EE bb 2539 FF aa 5668 FF aa 5668 FF aa 5668 FF aa 5668 + FF bb 5668 FF bb 5668 FF bb 5668 FF bb 5668 FF bb 5668 FF bb 5668 + GG aa 4468 GG aa 4468 GG aa 4468 GG aa 4468 GG bb 4468 GG bb 4468 + GG bb 4468 GG bb 4468 HH aa 5443 HH aa 5443 HH aa 5443 HH bb 5443 + HH bb 5443 HH bb 5443 HH bb 5443 HH bb 5443 HH bb 5443 II aa 3640 + II aa 3640 II bb 3640 II bb 3640 II bb 3640 II bb 3640 II bb 3640 + JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 3800 JJ bb 3800 + JJ bb 3800 JJ bb 3800} + +do_execsql_test 1.12.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 2307 AA aa 2307 AA aa 2307 AA aa 2307 AA bb 2378 AA bb 2378 + AA bb 2378 AA bb 2378 BB aa 2873 BB aa 2873 BB aa 2873 BB aa 2873 + BB aa 2873 BB aa 2873 BB bb 4467 BB bb 4467 BB bb 4467 BB bb 4467 + BB bb 4467 BB bb 4467 CC aa 1954 CC aa 1954 CC aa 1954 CC aa 1954 + CC bb 1168 CC bb 1168 DD aa 1325 DD aa 1325 DD aa 1325 DD bb 2707 + DD bb 2707 DD bb 2707 DD bb 2707 EE aa 890 EE aa 890 EE bb 1649 + EE bb 1649 EE bb 1649 FF aa 2163 FF aa 2163 FF aa 2163 FF aa 2163 + FF bb 3505 FF bb 3505 FF bb 3505 FF bb 3505 FF bb 3505 FF bb 3505 + GG aa 1597 GG aa 1597 GG aa 1597 GG aa 1597 GG bb 2871 GG bb 2871 + GG bb 2871 GG bb 2871 HH aa 2249 HH aa 2249 HH aa 2249 HH bb 3194 + HH bb 3194 HH bb 3194 HH bb 3194 HH bb 3194 HH bb 3194 II aa 1050 + II aa 1050 II bb 2590 II bb 2590 II bb 2590 II bb 2590 II bb 2590 + JJ aa 2014 JJ aa 2014 JJ aa 2014 JJ aa 2014 JJ bb 1786 JJ bb 1786 + JJ bb 1786 JJ bb 1786} + +do_execsql_test 1.12.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.12.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 870 AA bb 870 + AA bb 870 AA bb 870 BB aa 762 BB aa 762 BB aa 762 BB aa 762 + BB aa 762 BB aa 762 BB bb 840 BB bb 840 BB bb 840 BB bb 840 + BB bb 840 BB bb 840 CC aa 759 CC aa 759 CC aa 759 CC aa 759 + CC bb 822 CC bb 822 DD aa 845 DD aa 845 DD aa 845 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 777 EE aa 777 EE bb 768 + EE bb 768 EE bb 768 FF aa 670 FF aa 670 FF aa 670 FF aa 670 + FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 + GG aa 634 GG aa 634 GG aa 634 GG aa 634 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 979 HH aa 979 HH aa 979 HH bb 963 + HH bb 963 HH bb 963 HH bb 963 HH bb 963 HH bb 963 II aa 652 + II aa 652 II bb 805 II bb 805 II bb 805 II bb 805 II bb 805 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 839 JJ bb 839 + JJ bb 839 JJ bb 839} + +do_execsql_test 1.12.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 309 AA bb 309 + AA bb 309 AA bb 309 BB aa 247 BB aa 247 BB aa 247 BB aa 247 + BB aa 247 BB aa 247 BB bb 633 BB bb 633 BB bb 633 BB bb 633 + BB bb 633 BB bb 633 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 346 CC bb 346 DD aa 224 DD aa 224 DD aa 224 DD bb 238 + DD bb 238 DD bb 238 DD bb 238 EE aa 113 EE aa 113 EE bb 252 + EE bb 252 EE bb 252 FF aa 208 FF aa 208 FF aa 208 FF aa 208 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 148 GG aa 148 GG aa 148 GG aa 148 GG bb 160 GG bb 160 + GG bb 160 GG bb 160 HH aa 480 HH aa 480 HH aa 480 HH bb 133 + HH bb 133 HH bb 133 HH bb 133 HH bb 133 HH bb 133 II aa 398 + II aa 398 II bb 250 II bb 250 II bb 250 II bb 250 II bb 250 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 257 JJ bb 257 + JJ bb 257 JJ bb 257} + +do_execsql_test 1.12.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 6578 BB aa 6680 BB aa 6928 BB aa 6941 + BB aa 6947 BB aa 7093 BB bb 6500 BB bb 6548 BB bb 6554 BB bb 6629 + BB bb 6635 BB bb 6707 CC aa 2363 CC aa 2515 CC aa 2692 CC aa 2964 + CC bb 2300 CC bb 2776 DD aa 3187 DD aa 3776 DD aa 3808 DD bb 3073 + DD bb 3238 DD bb 3316 DD bb 3794 EE aa 1762 EE aa 2426 EE bb 1771 + EE bb 1910 EE bb 2287 FF aa 4998 FF aa 5001 FF aa 5050 FF aa 5460 + FF bb 4730 FF bb 4798 FF bb 4942 FF bb 5094 FF bb 5373 FF bb 5566 + GG aa 3834 GG aa 3988 GG aa 4133 GG aa 4320 GG bb 3530 GG bb 3539 + GG bb 3624 GG bb 4308 HH aa 4464 HH aa 4653 HH aa 4963 HH bb 4480 + HH bb 4713 HH bb 4760 HH bb 5088 HH bb 5113 HH bb 5310 II aa 2988 + II aa 3242 II bb 2835 II bb 2969 II bb 3197 II bb 3219 II bb 3390 + JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 2961 JJ bb 3446 + JJ bb 3464 JJ bb 3543} + +do_execsql_test 1.12.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1373 AA aa 1396 AA aa 2068 AA aa 2084 AA bb 1508 AA bb 1751 + AA bb 1806 AA bb 2069 BB aa 2111 BB aa 2213 BB aa 2461 BB aa 2474 + BB aa 2480 BB aa 2626 BB bb 3627 BB bb 3675 BB bb 3681 BB bb 3756 + BB bb 3762 BB bb 3834 CC aa 1195 CC aa 1347 CC aa 1524 CC aa 1796 + CC bb 346 CC bb 822 DD aa 480 DD aa 1069 DD aa 1101 DD bb 1748 + DD bb 1913 DD bb 1991 DD bb 2469 EE aa 113 EE aa 777 EE bb 881 + EE bb 1020 EE bb 1397 FF aa 1493 FF aa 1496 FF aa 1545 FF aa 1955 + FF bb 2567 FF bb 2635 FF bb 2779 FF bb 2931 FF bb 3210 FF bb 3403 + GG aa 963 GG aa 1117 GG aa 1262 GG aa 1449 GG bb 1933 GG bb 1942 + GG bb 2027 GG bb 2711 HH aa 1270 HH aa 1459 HH aa 1769 HH bb 2231 + HH bb 2464 HH bb 2511 HH bb 2839 HH bb 2864 HH bb 3061 II aa 398 + II aa 652 II bb 1785 II bb 1919 II bb 2147 II bb 2169 II bb 2340 + JJ aa 1115 JJ aa 1246 JJ aa 1780 JJ aa 1901 JJ bb 947 JJ bb 1432 + JJ bb 1450 JJ bb 1529} + +do_execsql_test 1.12.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 0 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 4685 1373 2307 AA aa 3774 4685 1396 2307 + AA aa 4446 4685 2068 2307 AA aa 4462 4685 2084 2307 + AA bb 3815 4685 1508 2378 AA bb 4058 4685 1751 2378 + AA bb 4113 4685 1806 2378 AA bb 4376 4685 2069 2378 + BB aa 6578 7340 2111 2873 BB aa 6680 7340 2213 2873 + BB aa 6928 7340 2461 2873 BB aa 6941 7340 2474 2873 + BB aa 6947 7340 2480 2873 BB aa 7093 7340 2626 2873 + BB bb 6500 7340 3627 4467 BB bb 6548 7340 3675 4467 + BB bb 6554 7340 3681 4467 BB bb 6629 7340 3756 4467 + BB bb 6635 7340 3762 4467 BB bb 6707 7340 3834 4467 + CC aa 2363 3122 1195 1954 CC aa 2515 3122 1347 1954 + CC aa 2692 3122 1524 1954 CC aa 2964 3122 1796 1954 + CC bb 2300 3122 346 1168 CC bb 2776 3122 822 1168 + DD aa 3187 4032 480 1325 DD aa 3776 4032 1069 1325 + DD aa 3808 4032 1101 1325 DD bb 3073 4032 1748 2707 + DD bb 3238 4032 1913 2707 DD bb 3316 4032 1991 2707 + DD bb 3794 4032 2469 2707 EE aa 1762 2539 113 890 + EE aa 2426 2539 777 890 EE bb 1771 2539 881 1649 + EE bb 1910 2539 1020 1649 EE bb 2287 2539 1397 1649 + FF aa 4998 5668 1493 2163 FF aa 5001 5668 1496 2163 + FF aa 5050 5668 1545 2163 FF aa 5460 5668 1955 2163 + FF bb 4730 5668 2567 3505 FF bb 4798 5668 2635 3505 + FF bb 4942 5668 2779 3505 FF bb 5094 5668 2931 3505 + FF bb 5373 5668 3210 3505 FF bb 5566 5668 3403 3505 + GG aa 3834 4468 963 1597 GG aa 3988 4468 1117 1597 + GG aa 4133 4468 1262 1597 GG aa 4320 4468 1449 1597 + GG bb 3530 4468 1933 2871 GG bb 3539 4468 1942 2871 + GG bb 3624 4468 2027 2871 GG bb 4308 4468 2711 2871 + HH aa 4464 5443 1270 2249 HH aa 4653 5443 1459 2249 + HH aa 4963 5443 1769 2249 HH bb 4480 5443 2231 3194 + HH bb 4713 5443 2464 3194 HH bb 4760 5443 2511 3194 + HH bb 5088 5443 2839 3194 HH bb 5113 5443 2864 3194 + HH bb 5310 5443 3061 3194 II aa 2988 3640 398 1050 + II aa 3242 3640 652 1050 II bb 2835 3640 1785 2590 + II bb 2969 3640 1919 2590 II bb 3197 3640 2147 2590 + II bb 3219 3640 2169 2590 II bb 3390 3640 2340 2590 + JJ aa 2901 3800 1115 2014 JJ aa 3032 3800 1246 2014 + JJ aa 3566 3800 1780 2014 JJ aa 3687 3800 1901 2014 + JJ bb 2961 3800 947 1786 JJ bb 3446 3800 1432 1786 + JJ bb 3464 3800 1450 1786 JJ bb 3543 3800 1529 1786} + +do_execsql_test 1.13.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 12025 AA aa 12025 AA aa 12025 AA aa 12025 AA bb 12025 + AA bb 12025 AA bb 12025 AA bb 12025 BB aa 10462 BB aa 10462 + BB aa 10462 BB aa 10462 BB aa 10462 BB aa 10462 BB bb 10462 + BB bb 10462 BB bb 10462 BB bb 10462 BB bb 10462 BB bb 10462 + CC aa 7154 CC aa 7154 CC aa 7154 CC aa 7154 CC bb 7154 CC bb 7154 + DD aa 6571 DD aa 6571 DD aa 6571 DD bb 6571 DD bb 6571 DD bb 6571 + DD bb 6571 EE aa 8207 EE aa 8207 EE bb 8207 EE bb 8207 EE bb 8207 + FF aa 10136 FF aa 10136 FF aa 10136 FF aa 10136 FF bb 10136 + FF bb 10136 FF bb 10136 FF bb 10136 FF bb 10136 FF bb 10136 + GG aa 9911 GG aa 9911 GG aa 9911 GG aa 9911 GG bb 9911 GG bb 9911 + GG bb 9911 GG bb 9911 HH aa 9083 HH aa 9083 HH aa 9083 HH bb 9083 + HH bb 9083 HH bb 9083 HH bb 9083 HH bb 9083 HH bb 9083 II aa 7440 + II aa 7440 II bb 7440 II bb 7440 II bb 7440 II bb 7440 II bb 7440 + JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 3800 JJ bb 3800 + JJ bb 3800 JJ bb 3800} + +do_execsql_test 1.13.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 5251 AA bb 5251 + AA bb 5251 AA bb 5251 BB aa 7340 BB aa 7340 BB aa 7340 BB aa 7340 + BB aa 7340 BB aa 7340 BB bb 6421 BB bb 6421 BB bb 6421 BB bb 6421 + BB bb 6421 BB bb 6421 CC aa 3122 CC aa 3122 CC aa 3122 CC aa 3122 + CC bb 2493 CC bb 2493 DD aa 4032 DD aa 4032 DD aa 4032 DD bb 3597 + DD bb 3597 DD bb 3597 DD bb 3597 EE aa 2539 EE aa 2539 EE bb 3812 + EE bb 3812 EE bb 3812 FF aa 5668 FF aa 5668 FF aa 5668 FF aa 5668 + FF bb 5102 FF bb 5102 FF bb 5102 FF bb 5102 FF bb 5102 FF bb 5102 + GG aa 4468 GG aa 4468 GG aa 4468 GG aa 4468 GG bb 5120 GG bb 5120 + GG bb 5120 GG bb 5120 HH aa 5443 HH aa 5443 HH aa 5443 HH bb 4244 + HH bb 4244 HH bb 4244 HH bb 4244 HH bb 4244 HH bb 4244 II aa 3640 + II aa 3640 II bb 4604 II bb 4604 II bb 4604 II bb 4604 II bb 4604 + JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 1786 JJ bb 1786 + JJ bb 1786 JJ bb 1786} + +do_execsql_test 1.13.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.13.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 870 AA bb 870 + AA bb 870 AA bb 870 BB aa 840 BB aa 840 BB aa 840 BB aa 840 + BB aa 840 BB aa 840 BB bb 840 BB bb 840 BB bb 840 BB bb 840 + BB bb 840 BB bb 840 CC aa 822 CC aa 822 CC aa 822 CC aa 822 + CC bb 845 CC bb 845 DD aa 959 DD aa 959 DD aa 959 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 777 EE aa 777 EE bb 768 + EE bb 768 EE bb 768 FF aa 938 FF aa 938 FF aa 938 FF aa 938 + FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 + GG aa 938 GG aa 938 GG aa 938 GG aa 938 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 979 HH aa 979 HH aa 979 HH bb 963 + HH bb 963 HH bb 963 HH bb 963 HH bb 963 HH bb 963 II aa 805 + II aa 805 II bb 899 II bb 899 II bb 899 II bb 899 II bb 899 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 839 JJ bb 839 + JJ bb 839 JJ bb 839} + +do_execsql_test 1.13.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 247 AA bb 247 + AA bb 247 AA bb 247 BB aa 247 BB aa 247 BB aa 247 BB aa 247 + BB aa 247 BB aa 247 BB bb 158 BB bb 158 BB bb 158 BB bb 158 + BB bb 158 BB bb 158 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 224 CC bb 224 DD aa 224 DD aa 224 DD aa 224 DD bb 113 + DD bb 113 DD bb 113 DD bb 113 EE aa 113 EE aa 113 EE bb 208 + EE bb 208 EE bb 208 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 148 GG aa 148 GG aa 148 GG aa 148 GG bb 160 GG bb 160 + GG bb 160 GG bb 160 HH aa 133 HH aa 133 HH aa 133 HH bb 133 + HH bb 133 HH bb 133 HH bb 133 HH bb 133 HH bb 133 II aa 250 + II aa 250 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 257 JJ bb 257 + JJ bb 257 JJ bb 257} + +do_execsql_test 1.13.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 11091 AA aa 11114 AA aa 11786 AA aa 11802 AA bb 11155 + AA bb 11398 AA bb 11453 AA bb 11716 BB aa 9700 BB aa 9802 + BB aa 10050 BB aa 10063 BB aa 10069 BB aa 10215 BB bb 9622 + BB bb 9670 BB bb 9676 BB bb 9751 BB bb 9757 BB bb 9829 CC aa 6395 + CC aa 6547 CC aa 6724 CC aa 6996 CC bb 6332 CC bb 6808 DD aa 5726 + DD aa 6315 DD aa 6347 DD bb 5612 DD bb 5777 DD bb 5855 DD bb 6333 + EE aa 7430 EE aa 8094 EE bb 7439 EE bb 7578 EE bb 7955 FF aa 9466 + FF aa 9469 FF aa 9518 FF aa 9928 FF bb 9198 FF bb 9266 FF bb 9410 + FF bb 9562 FF bb 9841 FF bb 10034 GG aa 9277 GG aa 9431 + GG aa 9576 GG aa 9763 GG bb 8973 GG bb 8982 GG bb 9067 GG bb 9751 + HH aa 8104 HH aa 8293 HH aa 8603 HH bb 8120 HH bb 8353 HH bb 8400 + HH bb 8728 HH bb 8753 HH bb 8950 II aa 6788 II aa 7042 II bb 6635 + II bb 6769 II bb 6997 II bb 7019 II bb 7190 JJ aa 2901 JJ aa 3032 + JJ aa 3566 JJ aa 3687 JJ bb 2961 JJ bb 3446 JJ bb 3464 JJ bb 3543} + +do_execsql_test 1.13.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 4381 AA bb 4624 + AA bb 4679 AA bb 4942 BB aa 6578 BB aa 6680 BB aa 6928 BB aa 6941 + BB aa 6947 BB aa 7093 BB bb 5581 BB bb 5629 BB bb 5635 BB bb 5710 + BB bb 5716 BB bb 5788 CC aa 2363 CC aa 2515 CC aa 2692 CC aa 2964 + CC bb 1671 CC bb 2147 DD aa 3187 DD aa 3776 DD aa 3808 DD bb 2638 + DD bb 2803 DD bb 2881 DD bb 3359 EE aa 1762 EE aa 2426 EE bb 3044 + EE bb 3183 EE bb 3560 FF aa 4998 FF aa 5001 FF aa 5050 FF aa 5460 + FF bb 4164 FF bb 4232 FF bb 4376 FF bb 4528 FF bb 4807 FF bb 5000 + GG aa 3834 GG aa 3988 GG aa 4133 GG aa 4320 GG bb 4182 GG bb 4191 + GG bb 4276 GG bb 4960 HH aa 4464 HH aa 4653 HH aa 4963 HH bb 3281 + HH bb 3514 HH bb 3561 HH bb 3889 HH bb 3914 HH bb 4111 II aa 2988 + II aa 3242 II bb 3799 II bb 3933 II bb 4161 II bb 4183 II bb 4354 + JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 947 JJ bb 1432 + JJ bb 1450 JJ bb 1529} + +do_execsql_test 1.13.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 11091 12025 3751 4685 AA aa 11114 12025 3774 4685 + AA aa 11786 12025 4446 4685 AA aa 11802 12025 4462 4685 + AA bb 11155 12025 4381 5251 AA bb 11398 12025 4624 5251 + AA bb 11453 12025 4679 5251 AA bb 11716 12025 4942 5251 + BB aa 9700 10462 6578 7340 BB aa 9802 10462 6680 7340 + BB aa 10050 10462 6928 7340 BB aa 10063 10462 6941 7340 + BB aa 10069 10462 6947 7340 BB aa 10215 10462 7093 7340 + BB bb 9622 10462 5581 6421 BB bb 9670 10462 5629 6421 + BB bb 9676 10462 5635 6421 BB bb 9751 10462 5710 6421 + BB bb 9757 10462 5716 6421 BB bb 9829 10462 5788 6421 + CC aa 6395 7154 2363 3122 CC aa 6547 7154 2515 3122 + CC aa 6724 7154 2692 3122 CC aa 6996 7154 2964 3122 + CC bb 6332 7154 1671 2493 CC bb 6808 7154 2147 2493 + DD aa 5726 6571 3187 4032 DD aa 6315 6571 3776 4032 + DD aa 6347 6571 3808 4032 DD bb 5612 6571 2638 3597 + DD bb 5777 6571 2803 3597 DD bb 5855 6571 2881 3597 + DD bb 6333 6571 3359 3597 EE aa 7430 8207 1762 2539 + EE aa 8094 8207 2426 2539 EE bb 7439 8207 3044 3812 + EE bb 7578 8207 3183 3812 EE bb 7955 8207 3560 3812 + FF aa 9466 10136 4998 5668 FF aa 9469 10136 5001 5668 + FF aa 9518 10136 5050 5668 FF aa 9928 10136 5460 5668 + FF bb 9198 10136 4164 5102 FF bb 9266 10136 4232 5102 + FF bb 9410 10136 4376 5102 FF bb 9562 10136 4528 5102 + FF bb 9841 10136 4807 5102 FF bb 10034 10136 5000 5102 + GG aa 9277 9911 3834 4468 GG aa 9431 9911 3988 4468 + GG aa 9576 9911 4133 4468 GG aa 9763 9911 4320 4468 + GG bb 8973 9911 4182 5120 GG bb 8982 9911 4191 5120 + GG bb 9067 9911 4276 5120 GG bb 9751 9911 4960 5120 + HH aa 8104 9083 4464 5443 HH aa 8293 9083 4653 5443 + HH aa 8603 9083 4963 5443 HH bb 8120 9083 3281 4244 + HH bb 8353 9083 3514 4244 HH bb 8400 9083 3561 4244 + HH bb 8728 9083 3889 4244 HH bb 8753 9083 3914 4244 + HH bb 8950 9083 4111 4244 II aa 6788 7440 2988 3640 + II aa 7042 7440 3242 3640 II bb 6635 7440 3799 4604 + II bb 6769 7440 3933 4604 II bb 6997 7440 4161 4604 + II bb 7019 7440 4183 4604 II bb 7190 7440 4354 4604 + JJ aa 2901 3800 2901 3800 JJ aa 3032 3800 3032 3800 + JJ aa 3566 3800 3566 3800 JJ aa 3687 3800 3687 3800 + JJ bb 2961 3800 947 1786 JJ bb 3446 3800 1432 1786 + JJ bb 3464 3800 1450 1786 JJ bb 3543 3800 1529 1786} + +do_execsql_test 1.14.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 44737 + AA bb 44737 AA bb 44737 AA bb 44737 BB aa 40052 BB aa 40052 + BB aa 40052 BB aa 40052 BB aa 40052 BB aa 40052 BB bb 40052 + BB bb 40052 BB bb 40052 BB bb 40052 BB bb 40052 BB bb 40052 + CC aa 32712 CC aa 32712 CC aa 32712 CC aa 32712 CC bb 32712 + CC bb 32712 DD aa 29590 DD aa 29590 DD aa 29590 DD bb 29590 + DD bb 29590 DD bb 29590 DD bb 29590 EE aa 25558 EE aa 25558 + EE bb 25558 EE bb 25558 EE bb 25558 FF aa 23019 FF aa 23019 + FF aa 23019 FF aa 23019 FF bb 23019 FF bb 23019 FF bb 23019 + FF bb 23019 FF bb 23019 FF bb 23019 GG aa 17351 GG aa 17351 + GG aa 17351 GG aa 17351 GG bb 17351 GG bb 17351 GG bb 17351 + GG bb 17351 HH aa 12883 HH aa 12883 HH aa 12883 HH bb 12883 + HH bb 12883 HH bb 12883 HH bb 12883 HH bb 12883 HH bb 12883 + II aa 7440 II aa 7440 II bb 7440 II bb 7440 II bb 7440 II bb 7440 + II bb 7440 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 3800 + JJ bb 3800 JJ bb 3800 JJ bb 3800} + +do_execsql_test 1.14.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 42430 + AA bb 42430 AA bb 42430 AA bb 42430 BB aa 40052 BB aa 40052 + BB aa 40052 BB aa 40052 BB aa 40052 BB aa 40052 BB bb 37179 + BB bb 37179 BB bb 37179 BB bb 37179 BB bb 37179 BB bb 37179 + CC aa 32712 CC aa 32712 CC aa 32712 CC aa 32712 CC bb 30758 + CC bb 30758 DD aa 29590 DD aa 29590 DD aa 29590 DD bb 28265 + DD bb 28265 DD bb 28265 DD bb 28265 EE aa 25558 EE aa 25558 + EE bb 24668 EE bb 24668 EE bb 24668 FF aa 23019 FF aa 23019 + FF aa 23019 FF aa 23019 FF bb 20856 FF bb 20856 FF bb 20856 + FF bb 20856 FF bb 20856 FF bb 20856 GG aa 17351 GG aa 17351 + GG aa 17351 GG aa 17351 GG bb 15754 GG bb 15754 GG bb 15754 + GG bb 15754 HH aa 12883 HH aa 12883 HH aa 12883 HH bb 10634 + HH bb 10634 HH bb 10634 HH bb 10634 HH bb 10634 HH bb 10634 + II aa 7440 II aa 7440 II bb 6390 II bb 6390 II bb 6390 II bb 6390 + II bb 6390 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 1786 + JJ bb 1786 JJ bb 1786 JJ bb 1786} + +do_execsql_test 1.14.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.14.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 979 AA aa 979 AA aa 979 AA aa 979 AA bb 979 AA bb 979 + AA bb 979 AA bb 979 BB aa 979 BB aa 979 BB aa 979 BB aa 979 + BB aa 979 BB aa 979 BB bb 979 BB bb 979 BB bb 979 BB bb 979 + BB bb 979 BB bb 979 CC aa 979 CC aa 979 CC aa 979 CC aa 979 + CC bb 979 CC bb 979 DD aa 979 DD aa 979 DD aa 979 DD bb 979 + DD bb 979 DD bb 979 DD bb 979 EE aa 979 EE aa 979 EE bb 979 + EE bb 979 EE bb 979 FF aa 979 FF aa 979 FF aa 979 FF aa 979 + FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 + GG aa 979 GG aa 979 GG aa 979 GG aa 979 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 979 HH aa 979 HH aa 979 HH bb 963 + HH bb 963 HH bb 963 HH bb 963 HH bb 963 HH bb 963 II aa 899 + II aa 899 II bb 899 II bb 899 II bb 899 II bb 899 II bb 899 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 839 JJ bb 839 + JJ bb 839 JJ bb 839} + +do_execsql_test 1.14.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 102 AA aa 102 AA aa 102 AA aa 102 AA bb 102 AA bb 102 + AA bb 102 AA bb 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 + BB aa 102 BB aa 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 + BB bb 102 BB bb 102 CC aa 102 CC aa 102 CC aa 102 CC aa 102 + CC bb 102 CC bb 102 DD aa 102 DD aa 102 DD aa 102 DD bb 102 + DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 + EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 113 GG aa 113 GG aa 113 GG aa 113 GG bb 113 GG bb 113 + GG bb 113 GG bb 113 HH aa 113 HH aa 113 HH aa 113 HH bb 113 + HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 II aa 113 + II aa 113 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 257 JJ bb 257 + JJ bb 257 JJ bb 257} + +do_execsql_test 1.14.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 43867 + AA bb 44110 AA bb 44165 AA bb 44428 BB aa 39290 BB aa 39392 + BB aa 39640 BB aa 39653 BB aa 39659 BB aa 39805 BB bb 39212 + BB bb 39260 BB bb 39266 BB bb 39341 BB bb 39347 BB bb 39419 + CC aa 31953 CC aa 32105 CC aa 32282 CC aa 32554 CC bb 31890 + CC bb 32366 DD aa 28745 DD aa 29334 DD aa 29366 DD bb 28631 + DD bb 28796 DD bb 28874 DD bb 29352 EE aa 24781 EE aa 25445 + EE bb 24790 EE bb 24929 EE bb 25306 FF aa 22349 FF aa 22352 + FF aa 22401 FF aa 22811 FF bb 22081 FF bb 22149 FF bb 22293 + FF bb 22445 FF bb 22724 FF bb 22917 GG aa 16717 GG aa 16871 + GG aa 17016 GG aa 17203 GG bb 16413 GG bb 16422 GG bb 16507 + GG bb 17191 HH aa 11904 HH aa 12093 HH aa 12403 HH bb 11920 + HH bb 12153 HH bb 12200 HH bb 12528 HH bb 12553 HH bb 12750 + II aa 6788 II aa 7042 II bb 6635 II bb 6769 II bb 6997 II bb 7019 + II bb 7190 JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 2961 + JJ bb 3446 JJ bb 3464 JJ bb 3543} + +do_execsql_test 1.14.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 41560 + AA bb 41803 AA bb 41858 AA bb 42121 BB aa 39290 BB aa 39392 + BB aa 39640 BB aa 39653 BB aa 39659 BB aa 39805 BB bb 36339 + BB bb 36387 BB bb 36393 BB bb 36468 BB bb 36474 BB bb 36546 + CC aa 31953 CC aa 32105 CC aa 32282 CC aa 32554 CC bb 29936 + CC bb 30412 DD aa 28745 DD aa 29334 DD aa 29366 DD bb 27306 + DD bb 27471 DD bb 27549 DD bb 28027 EE aa 24781 EE aa 25445 + EE bb 23900 EE bb 24039 EE bb 24416 FF aa 22349 FF aa 22352 + FF aa 22401 FF aa 22811 FF bb 19918 FF bb 19986 FF bb 20130 + FF bb 20282 FF bb 20561 FF bb 20754 GG aa 16717 GG aa 16871 + GG aa 17016 GG aa 17203 GG bb 14816 GG bb 14825 GG bb 14910 + GG bb 15594 HH aa 11904 HH aa 12093 HH aa 12403 HH bb 9671 + HH bb 9904 HH bb 9951 HH bb 10279 HH bb 10304 HH bb 10501 + II aa 6788 II aa 7042 II bb 5585 II bb 5719 II bb 5947 II bb 5969 + II bb 6140 JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 947 + JJ bb 1432 JJ bb 1450 JJ bb 1529} + +do_execsql_test 1.14.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND 100 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 44737 43803 44737 AA aa 43826 44737 43826 44737 + AA aa 44498 44737 44498 44737 AA aa 44514 44737 44514 44737 + AA bb 43867 44737 41560 42430 AA bb 44110 44737 41803 42430 + AA bb 44165 44737 41858 42430 AA bb 44428 44737 42121 42430 + BB aa 39290 40052 39290 40052 BB aa 39392 40052 39392 40052 + BB aa 39640 40052 39640 40052 BB aa 39653 40052 39653 40052 + BB aa 39659 40052 39659 40052 BB aa 39805 40052 39805 40052 + BB bb 39212 40052 36339 37179 BB bb 39260 40052 36387 37179 + BB bb 39266 40052 36393 37179 BB bb 39341 40052 36468 37179 + BB bb 39347 40052 36474 37179 BB bb 39419 40052 36546 37179 + CC aa 31953 32712 31953 32712 CC aa 32105 32712 32105 32712 + CC aa 32282 32712 32282 32712 CC aa 32554 32712 32554 32712 + CC bb 31890 32712 29936 30758 CC bb 32366 32712 30412 30758 + DD aa 28745 29590 28745 29590 DD aa 29334 29590 29334 29590 + DD aa 29366 29590 29366 29590 DD bb 28631 29590 27306 28265 + DD bb 28796 29590 27471 28265 DD bb 28874 29590 27549 28265 + DD bb 29352 29590 28027 28265 EE aa 24781 25558 24781 25558 + EE aa 25445 25558 25445 25558 EE bb 24790 25558 23900 24668 + EE bb 24929 25558 24039 24668 EE bb 25306 25558 24416 24668 + FF aa 22349 23019 22349 23019 FF aa 22352 23019 22352 23019 + FF aa 22401 23019 22401 23019 FF aa 22811 23019 22811 23019 + FF bb 22081 23019 19918 20856 FF bb 22149 23019 19986 20856 + FF bb 22293 23019 20130 20856 FF bb 22445 23019 20282 20856 + FF bb 22724 23019 20561 20856 FF bb 22917 23019 20754 20856 + GG aa 16717 17351 16717 17351 GG aa 16871 17351 16871 17351 + GG aa 17016 17351 17016 17351 GG aa 17203 17351 17203 17351 + GG bb 16413 17351 14816 15754 GG bb 16422 17351 14825 15754 + GG bb 16507 17351 14910 15754 GG bb 17191 17351 15594 15754 + HH aa 11904 12883 11904 12883 HH aa 12093 12883 12093 12883 + HH aa 12403 12883 12403 12883 HH bb 11920 12883 9671 10634 + HH bb 12153 12883 9904 10634 HH bb 12200 12883 9951 10634 + HH bb 12528 12883 10279 10634 HH bb 12553 12883 10304 10634 + HH bb 12750 12883 10501 10634 II aa 6788 7440 6788 7440 + II aa 7042 7440 7042 7440 II bb 6635 7440 5585 6390 + II bb 6769 7440 5719 6390 II bb 6997 7440 5947 6390 + II bb 7019 7440 5969 6390 II bb 7190 7440 6140 6390 + JJ aa 2901 3800 2901 3800 JJ aa 3032 3800 3032 3800 + JJ aa 3566 3800 3566 3800 JJ aa 3687 3800 3687 3800 + JJ bb 2961 3800 947 1786 JJ bb 3446 3800 1432 1786 + JJ bb 3464 3800 1450 1786 JJ bb 3543 3800 1529 1786} + +do_execsql_test 1.15.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 44737 + AA bb 44737 AA bb 44737 AA bb 44737 BB aa 40052 BB aa 40052 + BB aa 40052 BB aa 40052 BB aa 40052 BB aa 40052 BB bb 40052 + BB bb 40052 BB bb 40052 BB bb 40052 BB bb 40052 BB bb 40052 + CC aa 32712 CC aa 32712 CC aa 32712 CC aa 32712 CC bb 32712 + CC bb 32712 DD aa 29590 DD aa 29590 DD aa 29590 DD bb 29590 + DD bb 29590 DD bb 29590 DD bb 29590 EE aa 25558 EE aa 25558 + EE bb 25558 EE bb 25558 EE bb 25558 FF aa 23019 FF aa 23019 + FF aa 23019 FF aa 23019 FF bb 23019 FF bb 23019 FF bb 23019 + FF bb 23019 FF bb 23019 FF bb 23019 GG aa 17351 GG aa 17351 + GG aa 17351 GG aa 17351 GG bb 17351 GG bb 17351 GG bb 17351 + GG bb 17351 HH aa 12883 HH aa 12883 HH aa 12883 HH bb 12883 + HH bb 12883 HH bb 12883 HH bb 12883 HH bb 12883 HH bb 12883 + II aa 7440 II aa 7440 II bb 7440 II bb 7440 II bb 7440 II bb 7440 + II bb 7440 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 3800 + JJ bb 3800 JJ bb 3800 JJ bb 3800} + +do_execsql_test 1.15.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 44737 AA aa 44737 AA aa 44737 AA aa 44737 AA bb 42430 + AA bb 42430 AA bb 42430 AA bb 42430 BB aa 40052 BB aa 40052 + BB aa 40052 BB aa 40052 BB aa 40052 BB aa 40052 BB bb 37179 + BB bb 37179 BB bb 37179 BB bb 37179 BB bb 37179 BB bb 37179 + CC aa 32712 CC aa 32712 CC aa 32712 CC aa 32712 CC bb 30758 + CC bb 30758 DD aa 29590 DD aa 29590 DD aa 29590 DD bb 28265 + DD bb 28265 DD bb 28265 DD bb 28265 EE aa 25558 EE aa 25558 + EE bb 24668 EE bb 24668 EE bb 24668 FF aa 23019 FF aa 23019 + FF aa 23019 FF aa 23019 FF bb 20856 FF bb 20856 FF bb 20856 + FF bb 20856 FF bb 20856 FF bb 20856 GG aa 17351 GG aa 17351 + GG aa 17351 GG aa 17351 GG bb 15754 GG bb 15754 GG bb 15754 + GG bb 15754 HH aa 12883 HH aa 12883 HH aa 12883 HH bb 10634 + HH bb 10634 HH bb 10634 HH bb 10634 HH bb 10634 HH bb 10634 + II aa 7440 II aa 7440 II bb 6390 II bb 6390 II bb 6390 II bb 6390 + II bb 6390 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 1786 + JJ bb 1786 JJ bb 1786 JJ bb 1786} + +do_execsql_test 1.15.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.15.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 979 AA aa 979 AA aa 979 AA aa 979 AA bb 979 AA bb 979 + AA bb 979 AA bb 979 BB aa 979 BB aa 979 BB aa 979 BB aa 979 + BB aa 979 BB aa 979 BB bb 979 BB bb 979 BB bb 979 BB bb 979 + BB bb 979 BB bb 979 CC aa 979 CC aa 979 CC aa 979 CC aa 979 + CC bb 979 CC bb 979 DD aa 979 DD aa 979 DD aa 979 DD bb 979 + DD bb 979 DD bb 979 DD bb 979 EE aa 979 EE aa 979 EE bb 979 + EE bb 979 EE bb 979 FF aa 979 FF aa 979 FF aa 979 FF aa 979 + FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 + GG aa 979 GG aa 979 GG aa 979 GG aa 979 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 979 HH aa 979 HH aa 979 HH bb 963 + HH bb 963 HH bb 963 HH bb 963 HH bb 963 HH bb 963 II aa 899 + II aa 899 II bb 899 II bb 899 II bb 899 II bb 899 II bb 899 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 839 JJ bb 839 + JJ bb 839 JJ bb 839} + +do_execsql_test 1.15.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 102 AA aa 102 AA aa 102 AA aa 102 AA bb 102 AA bb 102 + AA bb 102 AA bb 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 + BB aa 102 BB aa 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 + BB bb 102 BB bb 102 CC aa 102 CC aa 102 CC aa 102 CC aa 102 + CC bb 102 CC bb 102 DD aa 102 DD aa 102 DD aa 102 DD bb 102 + DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 + EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 113 GG aa 113 GG aa 113 GG aa 113 GG bb 113 GG bb 113 + GG bb 113 GG bb 113 HH aa 113 HH aa 113 HH aa 113 HH bb 113 + HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 II aa 113 + II aa 113 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 257 JJ bb 257 + JJ bb 257 JJ bb 257} + +do_execsql_test 1.15.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 43867 + AA bb 44110 AA bb 44165 AA bb 44428 BB aa 39290 BB aa 39392 + BB aa 39640 BB aa 39653 BB aa 39659 BB aa 39805 BB bb 39212 + BB bb 39260 BB bb 39266 BB bb 39341 BB bb 39347 BB bb 39419 + CC aa 31953 CC aa 32105 CC aa 32282 CC aa 32554 CC bb 31890 + CC bb 32366 DD aa 28745 DD aa 29334 DD aa 29366 DD bb 28631 + DD bb 28796 DD bb 28874 DD bb 29352 EE aa 24781 EE aa 25445 + EE bb 24790 EE bb 24929 EE bb 25306 FF aa 22349 FF aa 22352 + FF aa 22401 FF aa 22811 FF bb 22081 FF bb 22149 FF bb 22293 + FF bb 22445 FF bb 22724 FF bb 22917 GG aa 16717 GG aa 16871 + GG aa 17016 GG aa 17203 GG bb 16413 GG bb 16422 GG bb 16507 + GG bb 17191 HH aa 11904 HH aa 12093 HH aa 12403 HH bb 11920 + HH bb 12153 HH bb 12200 HH bb 12528 HH bb 12553 HH bb 12750 + II aa 6788 II aa 7042 II bb 6635 II bb 6769 II bb 6997 II bb 7019 + II bb 7190 JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 2961 + JJ bb 3446 JJ bb 3464 JJ bb 3543} + +do_execsql_test 1.15.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 AA aa 43826 AA aa 44498 AA aa 44514 AA bb 41560 + AA bb 41803 AA bb 41858 AA bb 42121 BB aa 39290 BB aa 39392 + BB aa 39640 BB aa 39653 BB aa 39659 BB aa 39805 BB bb 36339 + BB bb 36387 BB bb 36393 BB bb 36468 BB bb 36474 BB bb 36546 + CC aa 31953 CC aa 32105 CC aa 32282 CC aa 32554 CC bb 29936 + CC bb 30412 DD aa 28745 DD aa 29334 DD aa 29366 DD bb 27306 + DD bb 27471 DD bb 27549 DD bb 28027 EE aa 24781 EE aa 25445 + EE bb 23900 EE bb 24039 EE bb 24416 FF aa 22349 FF aa 22352 + FF aa 22401 FF aa 22811 FF bb 19918 FF bb 19986 FF bb 20130 + FF bb 20282 FF bb 20561 FF bb 20754 GG aa 16717 GG aa 16871 + GG aa 17016 GG aa 17203 GG bb 14816 GG bb 14825 GG bb 14910 + GG bb 15594 HH aa 11904 HH aa 12093 HH aa 12403 HH bb 9671 + HH bb 9904 HH bb 9951 HH bb 10279 HH bb 10304 HH bb 10501 + II aa 6788 II aa 7042 II bb 5585 II bb 5719 II bb 5947 II bb 5969 + II bb 6140 JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 947 + JJ bb 1432 JJ bb 1450 JJ bb 1529} + +do_execsql_test 1.15.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 43803 44737 43803 44737 AA aa 43826 44737 43826 44737 + AA aa 44498 44737 44498 44737 AA aa 44514 44737 44514 44737 + AA bb 43867 44737 41560 42430 AA bb 44110 44737 41803 42430 + AA bb 44165 44737 41858 42430 AA bb 44428 44737 42121 42430 + BB aa 39290 40052 39290 40052 BB aa 39392 40052 39392 40052 + BB aa 39640 40052 39640 40052 BB aa 39653 40052 39653 40052 + BB aa 39659 40052 39659 40052 BB aa 39805 40052 39805 40052 + BB bb 39212 40052 36339 37179 BB bb 39260 40052 36387 37179 + BB bb 39266 40052 36393 37179 BB bb 39341 40052 36468 37179 + BB bb 39347 40052 36474 37179 BB bb 39419 40052 36546 37179 + CC aa 31953 32712 31953 32712 CC aa 32105 32712 32105 32712 + CC aa 32282 32712 32282 32712 CC aa 32554 32712 32554 32712 + CC bb 31890 32712 29936 30758 CC bb 32366 32712 30412 30758 + DD aa 28745 29590 28745 29590 DD aa 29334 29590 29334 29590 + DD aa 29366 29590 29366 29590 DD bb 28631 29590 27306 28265 + DD bb 28796 29590 27471 28265 DD bb 28874 29590 27549 28265 + DD bb 29352 29590 28027 28265 EE aa 24781 25558 24781 25558 + EE aa 25445 25558 25445 25558 EE bb 24790 25558 23900 24668 + EE bb 24929 25558 24039 24668 EE bb 25306 25558 24416 24668 + FF aa 22349 23019 22349 23019 FF aa 22352 23019 22352 23019 + FF aa 22401 23019 22401 23019 FF aa 22811 23019 22811 23019 + FF bb 22081 23019 19918 20856 FF bb 22149 23019 19986 20856 + FF bb 22293 23019 20130 20856 FF bb 22445 23019 20282 20856 + FF bb 22724 23019 20561 20856 FF bb 22917 23019 20754 20856 + GG aa 16717 17351 16717 17351 GG aa 16871 17351 16871 17351 + GG aa 17016 17351 17016 17351 GG aa 17203 17351 17203 17351 + GG bb 16413 17351 14816 15754 GG bb 16422 17351 14825 15754 + GG bb 16507 17351 14910 15754 GG bb 17191 17351 15594 15754 + HH aa 11904 12883 11904 12883 HH aa 12093 12883 12093 12883 + HH aa 12403 12883 12403 12883 HH bb 11920 12883 9671 10634 + HH bb 12153 12883 9904 10634 HH bb 12200 12883 9951 10634 + HH bb 12528 12883 10279 10634 HH bb 12553 12883 10304 10634 + HH bb 12750 12883 10501 10634 II aa 6788 7440 6788 7440 + II aa 7042 7440 7042 7440 II bb 6635 7440 5585 6390 + II bb 6769 7440 5719 6390 II bb 6997 7440 5947 6390 + II bb 7019 7440 5969 6390 II bb 7190 7440 6140 6390 + JJ aa 2901 3800 2901 3800 JJ aa 3032 3800 3032 3800 + JJ aa 3566 3800 3566 3800 JJ aa 3687 3800 3687 3800 + JJ bb 2961 3800 947 1786 JJ bb 3446 3800 1432 1786 + JJ bb 3464 3800 1450 1786 JJ bb 3543 3800 1529 1786} + +do_execsql_test 1.16.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 4685 AA aa 4685 AA aa 4685 AA aa 4685 AA bb 4685 AA bb 4685 + AA bb 4685 AA bb 4685 BB aa 7340 BB aa 7340 BB aa 7340 BB aa 7340 + BB aa 7340 BB aa 7340 BB bb 7340 BB bb 7340 BB bb 7340 BB bb 7340 + BB bb 7340 BB bb 7340 CC aa 3122 CC aa 3122 CC aa 3122 CC aa 3122 + CC bb 3122 CC bb 3122 DD aa 4032 DD aa 4032 DD aa 4032 DD bb 4032 + DD bb 4032 DD bb 4032 DD bb 4032 EE aa 2539 EE aa 2539 EE bb 2539 + EE bb 2539 EE bb 2539 FF aa 5668 FF aa 5668 FF aa 5668 FF aa 5668 + FF bb 5668 FF bb 5668 FF bb 5668 FF bb 5668 FF bb 5668 FF bb 5668 + GG aa 4468 GG aa 4468 GG aa 4468 GG aa 4468 GG bb 4468 GG bb 4468 + GG bb 4468 GG bb 4468 HH aa 5443 HH aa 5443 HH aa 5443 HH bb 5443 + HH bb 5443 HH bb 5443 HH bb 5443 HH bb 5443 HH bb 5443 II aa 3640 + II aa 3640 II bb 3640 II bb 3640 II bb 3640 II bb 3640 II bb 3640 + JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ aa 3800 JJ bb 3800 JJ bb 3800 + JJ bb 3800 JJ bb 3800} + +do_execsql_test 1.16.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 2307 AA aa 2307 AA aa 2307 AA aa 2307 AA bb 2378 AA bb 2378 + AA bb 2378 AA bb 2378 BB aa 2873 BB aa 2873 BB aa 2873 BB aa 2873 + BB aa 2873 BB aa 2873 BB bb 4467 BB bb 4467 BB bb 4467 BB bb 4467 + BB bb 4467 BB bb 4467 CC aa 1954 CC aa 1954 CC aa 1954 CC aa 1954 + CC bb 1168 CC bb 1168 DD aa 1325 DD aa 1325 DD aa 1325 DD bb 2707 + DD bb 2707 DD bb 2707 DD bb 2707 EE aa 890 EE aa 890 EE bb 1649 + EE bb 1649 EE bb 1649 FF aa 2163 FF aa 2163 FF aa 2163 FF aa 2163 + FF bb 3505 FF bb 3505 FF bb 3505 FF bb 3505 FF bb 3505 FF bb 3505 + GG aa 1597 GG aa 1597 GG aa 1597 GG aa 1597 GG bb 2871 GG bb 2871 + GG bb 2871 GG bb 2871 HH aa 2249 HH aa 2249 HH aa 2249 HH bb 3194 + HH bb 3194 HH bb 3194 HH bb 3194 HH bb 3194 HH bb 3194 II aa 1050 + II aa 1050 II bb 2590 II bb 2590 II bb 2590 II bb 2590 II bb 2590 + JJ aa 2014 JJ aa 2014 JJ aa 2014 JJ aa 2014 JJ bb 1786 JJ bb 1786 + JJ bb 1786 JJ bb 1786} + +do_execsql_test 1.16.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.16.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 934 AA aa 934 AA aa 934 AA aa 934 AA bb 870 AA bb 870 + AA bb 870 AA bb 870 BB aa 762 BB aa 762 BB aa 762 BB aa 762 + BB aa 762 BB aa 762 BB bb 840 BB bb 840 BB bb 840 BB bb 840 + BB bb 840 BB bb 840 CC aa 759 CC aa 759 CC aa 759 CC aa 759 + CC bb 822 CC bb 822 DD aa 845 DD aa 845 DD aa 845 DD bb 959 + DD bb 959 DD bb 959 DD bb 959 EE aa 777 EE aa 777 EE bb 768 + EE bb 768 EE bb 768 FF aa 670 FF aa 670 FF aa 670 FF aa 670 + FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 FF bb 938 + GG aa 634 GG aa 634 GG aa 634 GG aa 634 GG bb 938 GG bb 938 + GG bb 938 GG bb 938 HH aa 979 HH aa 979 HH aa 979 HH bb 963 + HH bb 963 HH bb 963 HH bb 963 HH bb 963 HH bb 963 II aa 652 + II aa 652 II bb 805 II bb 805 II bb 805 II bb 805 II bb 805 + JJ aa 899 JJ aa 899 JJ aa 899 JJ aa 899 JJ bb 839 JJ bb 839 + JJ bb 839 JJ bb 839} + +do_execsql_test 1.16.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 223 AA aa 223 AA aa 223 AA aa 223 AA bb 309 AA bb 309 + AA bb 309 AA bb 309 BB aa 247 BB aa 247 BB aa 247 BB aa 247 + BB aa 247 BB aa 247 BB bb 633 BB bb 633 BB bb 633 BB bb 633 + BB bb 633 BB bb 633 CC aa 158 CC aa 158 CC aa 158 CC aa 158 + CC bb 346 CC bb 346 DD aa 224 DD aa 224 DD aa 224 DD bb 238 + DD bb 238 DD bb 238 DD bb 238 EE aa 113 EE aa 113 EE bb 252 + EE bb 252 EE bb 252 FF aa 208 FF aa 208 FF aa 208 FF aa 208 + FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 FF bb 102 + GG aa 148 GG aa 148 GG aa 148 GG aa 148 GG bb 160 GG bb 160 + GG bb 160 GG bb 160 HH aa 480 HH aa 480 HH aa 480 HH bb 133 + HH bb 133 HH bb 133 HH bb 133 HH bb 133 HH bb 133 II aa 398 + II aa 398 II bb 250 II bb 250 II bb 250 II bb 250 II bb 250 + JJ aa 113 JJ aa 113 JJ aa 113 JJ aa 113 JJ bb 257 JJ bb 257 + JJ bb 257 JJ bb 257} + +do_execsql_test 1.16.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 AA aa 3774 AA aa 4446 AA aa 4462 AA bb 3815 AA bb 4058 + AA bb 4113 AA bb 4376 BB aa 6578 BB aa 6680 BB aa 6928 BB aa 6941 + BB aa 6947 BB aa 7093 BB bb 6500 BB bb 6548 BB bb 6554 BB bb 6629 + BB bb 6635 BB bb 6707 CC aa 2363 CC aa 2515 CC aa 2692 CC aa 2964 + CC bb 2300 CC bb 2776 DD aa 3187 DD aa 3776 DD aa 3808 DD bb 3073 + DD bb 3238 DD bb 3316 DD bb 3794 EE aa 1762 EE aa 2426 EE bb 1771 + EE bb 1910 EE bb 2287 FF aa 4998 FF aa 5001 FF aa 5050 FF aa 5460 + FF bb 4730 FF bb 4798 FF bb 4942 FF bb 5094 FF bb 5373 FF bb 5566 + GG aa 3834 GG aa 3988 GG aa 4133 GG aa 4320 GG bb 3530 GG bb 3539 + GG bb 3624 GG bb 4308 HH aa 4464 HH aa 4653 HH aa 4963 HH bb 4480 + HH bb 4713 HH bb 4760 HH bb 5088 HH bb 5113 HH bb 5310 II aa 2988 + II aa 3242 II bb 2835 II bb 2969 II bb 3197 II bb 3219 II bb 3390 + JJ aa 2901 JJ aa 3032 JJ aa 3566 JJ aa 3687 JJ bb 2961 JJ bb 3446 + JJ bb 3464 JJ bb 3543} + +do_execsql_test 1.16.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1373 AA aa 1396 AA aa 2068 AA aa 2084 AA bb 1508 AA bb 1751 + AA bb 1806 AA bb 2069 BB aa 2111 BB aa 2213 BB aa 2461 BB aa 2474 + BB aa 2480 BB aa 2626 BB bb 3627 BB bb 3675 BB bb 3681 BB bb 3756 + BB bb 3762 BB bb 3834 CC aa 1195 CC aa 1347 CC aa 1524 CC aa 1796 + CC bb 346 CC bb 822 DD aa 480 DD aa 1069 DD aa 1101 DD bb 1748 + DD bb 1913 DD bb 1991 DD bb 2469 EE aa 113 EE aa 777 EE bb 881 + EE bb 1020 EE bb 1397 FF aa 1493 FF aa 1496 FF aa 1545 FF aa 1955 + FF bb 2567 FF bb 2635 FF bb 2779 FF bb 2931 FF bb 3210 FF bb 3403 + GG aa 963 GG aa 1117 GG aa 1262 GG aa 1449 GG bb 1933 GG bb 1942 + GG bb 2027 GG bb 2711 HH aa 1270 HH aa 1459 HH aa 1769 HH bb 2231 + HH bb 2464 HH bb 2511 HH bb 2839 HH bb 2864 HH bb 3061 II aa 398 + II aa 652 II bb 1785 II bb 1919 II bb 2147 II bb 2169 II bb 2340 + JJ aa 1115 JJ aa 1246 JJ aa 1780 JJ aa 1901 JJ bb 947 JJ bb 1432 + JJ bb 1450 JJ bb 1529} + +do_execsql_test 1.16.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 0 FOLLOWING AND 0 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 3751 4685 1373 2307 AA aa 3774 4685 1396 2307 + AA aa 4446 4685 2068 2307 AA aa 4462 4685 2084 2307 + AA bb 3815 4685 1508 2378 AA bb 4058 4685 1751 2378 + AA bb 4113 4685 1806 2378 AA bb 4376 4685 2069 2378 + BB aa 6578 7340 2111 2873 BB aa 6680 7340 2213 2873 + BB aa 6928 7340 2461 2873 BB aa 6941 7340 2474 2873 + BB aa 6947 7340 2480 2873 BB aa 7093 7340 2626 2873 + BB bb 6500 7340 3627 4467 BB bb 6548 7340 3675 4467 + BB bb 6554 7340 3681 4467 BB bb 6629 7340 3756 4467 + BB bb 6635 7340 3762 4467 BB bb 6707 7340 3834 4467 + CC aa 2363 3122 1195 1954 CC aa 2515 3122 1347 1954 + CC aa 2692 3122 1524 1954 CC aa 2964 3122 1796 1954 + CC bb 2300 3122 346 1168 CC bb 2776 3122 822 1168 + DD aa 3187 4032 480 1325 DD aa 3776 4032 1069 1325 + DD aa 3808 4032 1101 1325 DD bb 3073 4032 1748 2707 + DD bb 3238 4032 1913 2707 DD bb 3316 4032 1991 2707 + DD bb 3794 4032 2469 2707 EE aa 1762 2539 113 890 + EE aa 2426 2539 777 890 EE bb 1771 2539 881 1649 + EE bb 1910 2539 1020 1649 EE bb 2287 2539 1397 1649 + FF aa 4998 5668 1493 2163 FF aa 5001 5668 1496 2163 + FF aa 5050 5668 1545 2163 FF aa 5460 5668 1955 2163 + FF bb 4730 5668 2567 3505 FF bb 4798 5668 2635 3505 + FF bb 4942 5668 2779 3505 FF bb 5094 5668 2931 3505 + FF bb 5373 5668 3210 3505 FF bb 5566 5668 3403 3505 + GG aa 3834 4468 963 1597 GG aa 3988 4468 1117 1597 + GG aa 4133 4468 1262 1597 GG aa 4320 4468 1449 1597 + GG bb 3530 4468 1933 2871 GG bb 3539 4468 1942 2871 + GG bb 3624 4468 2027 2871 GG bb 4308 4468 2711 2871 + HH aa 4464 5443 1270 2249 HH aa 4653 5443 1459 2249 + HH aa 4963 5443 1769 2249 HH bb 4480 5443 2231 3194 + HH bb 4713 5443 2464 3194 HH bb 4760 5443 2511 3194 + HH bb 5088 5443 2839 3194 HH bb 5113 5443 2864 3194 + HH bb 5310 5443 3061 3194 II aa 2988 3640 398 1050 + II aa 3242 3640 652 1050 II bb 2835 3640 1785 2590 + II bb 2969 3640 1919 2590 II bb 3197 3640 2147 2590 + II bb 3219 3640 2169 2590 II bb 3390 3640 2340 2590 + JJ aa 2901 3800 1115 2014 JJ aa 3032 3800 1246 2014 + JJ aa 3566 3800 1780 2014 JJ aa 3687 3800 1901 2014 + JJ bb 2961 3800 947 1786 JJ bb 3446 3800 1432 1786 + JJ bb 3464 3800 1450 1786 JJ bb 3543 3800 1529 1786} + +do_execsql_test 1.17.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.17.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.17.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.17.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.17.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.17.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.17.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} AA aa {} AA aa {} AA aa {} AA bb {} AA bb {} AA bb {} + AA bb {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} BB aa {} + BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} BB bb {} CC aa {} + CC aa {} CC aa {} CC aa {} CC bb {} CC bb {} DD aa {} DD aa {} + DD aa {} DD bb {} DD bb {} DD bb {} DD bb {} EE aa {} EE aa {} + EE bb {} EE bb {} EE bb {} FF aa {} FF aa {} FF aa {} FF aa {} + FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} FF bb {} GG aa {} + GG aa {} GG aa {} GG aa {} GG bb {} GG bb {} GG bb {} GG bb {} + HH aa {} HH aa {} HH aa {} HH bb {} HH bb {} HH bb {} HH bb {} + HH bb {} HH bb {} II aa {} II aa {} II bb {} II bb {} II bb {} + II bb {} II bb {} JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} + JJ bb {} JJ bb {} JJ bb {}} + +do_execsql_test 1.17.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 0 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa {} {} {} {} AA aa {} {} {} {} AA aa {} {} {} {} + AA aa {} {} {} {} AA bb {} {} {} {} AA bb {} {} {} {} + AA bb {} {} {} {} AA bb {} {} {} {} BB aa {} {} {} {} + BB aa {} {} {} {} BB aa {} {} {} {} BB aa {} {} {} {} + BB aa {} {} {} {} BB aa {} {} {} {} BB bb {} {} {} {} + BB bb {} {} {} {} BB bb {} {} {} {} BB bb {} {} {} {} + BB bb {} {} {} {} BB bb {} {} {} {} CC aa {} {} {} {} + CC aa {} {} {} {} CC aa {} {} {} {} CC aa {} {} {} {} + CC bb {} {} {} {} CC bb {} {} {} {} DD aa {} {} {} {} + DD aa {} {} {} {} DD aa {} {} {} {} DD bb {} {} {} {} + DD bb {} {} {} {} DD bb {} {} {} {} DD bb {} {} {} {} + EE aa {} {} {} {} EE aa {} {} {} {} EE bb {} {} {} {} + EE bb {} {} {} {} EE bb {} {} {} {} FF aa {} {} {} {} + FF aa {} {} {} {} FF aa {} {} {} {} FF aa {} {} {} {} + FF bb {} {} {} {} FF bb {} {} {} {} FF bb {} {} {} {} + FF bb {} {} {} {} FF bb {} {} {} {} FF bb {} {} {} {} + GG aa {} {} {} {} GG aa {} {} {} {} GG aa {} {} {} {} + GG aa {} {} {} {} GG bb {} {} {} {} GG bb {} {} {} {} + GG bb {} {} {} {} GG bb {} {} {} {} HH aa {} {} {} {} + HH aa {} {} {} {} HH aa {} {} {} {} HH bb {} {} {} {} + HH bb {} {} {} {} HH bb {} {} {} {} HH bb {} {} {} {} + HH bb {} {} {} {} HH bb {} {} {} {} II aa {} {} {} {} + II aa {} {} {} {} II bb {} {} {} {} II bb {} {} {} {} + II bb {} {} {} {} II bb {} {} {} {} II bb {} {} {} {} + JJ aa {} {} {} {} JJ aa {} {} {} {} JJ aa {} {} {} {} + JJ aa {} {} {} {} JJ bb {} {} {} {} JJ bb {} {} {} {} + JJ bb {} {} {} {} JJ bb {} {} {} {}} + +do_execsql_test 1.18.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 22701 AA aa 22701 AA aa 22701 AA aa 22701 AA bb 22701 + AA bb 22701 AA bb 22701 AA bb 22701 BB aa 19829 BB aa 19829 + BB aa 19829 BB aa 19829 BB aa 19829 BB aa 19829 BB bb 19829 + BB bb 19829 BB bb 19829 BB bb 19829 BB bb 19829 BB bb 19829 + CC aa 22150 CC aa 22150 CC aa 22150 CC aa 22150 CC bb 22150 + CC bb 22150 DD aa 21758 DD aa 21758 DD aa 21758 DD bb 21758 + DD bb 21758 DD bb 21758 DD bb 21758 EE aa 23019 EE aa 23019 + EE bb 23019 EE bb 23019 EE bb 23019 FF aa 17351 FF aa 17351 + FF aa 17351 FF aa 17351 FF bb 17351 FF bb 17351 FF bb 17351 + FF bb 17351 FF bb 17351 FF bb 17351 GG aa 12883 GG aa 12883 + GG aa 12883 GG aa 12883 GG bb 12883 GG bb 12883 GG bb 12883 + GG bb 12883 HH aa 7440 HH aa 7440 HH aa 7440 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 3800 + II aa 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 + JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} JJ bb {} JJ bb {} + JJ bb {}} + +do_execsql_test 1.18.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 12840 AA aa 12840 AA aa 12840 AA aa 12840 AA bb 11787 + AA bb 11787 AA bb 11787 AA bb 11787 BB aa 11621 BB aa 11621 + BB aa 11621 BB aa 11621 BB aa 11621 BB aa 11621 BB bb 8044 + BB bb 8044 BB bb 8044 BB bb 8044 BB bb 8044 BB bb 8044 CC aa 7739 + CC aa 7739 CC aa 7739 CC aa 7739 CC bb 8734 CC bb 8734 + DD aa 10914 DD aa 10914 DD aa 10914 DD bb 9804 DD bb 9804 + DD bb 9804 DD bb 9804 EE aa 11785 EE aa 11785 EE bb 12385 + EE bb 12385 EE bb 12385 FF aa 13416 FF aa 13416 FF aa 13416 + FF aa 13416 FF bb 10961 FF bb 10961 FF bb 10961 FF bb 10961 + FF bb 10961 FF bb 10961 GG aa 11954 GG aa 11954 GG aa 11954 + GG aa 11954 GG bb 11097 GG bb 11097 GG bb 11097 GG bb 11097 + HH aa 10634 HH aa 10634 HH aa 10634 HH bb 7440 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 6390 II aa 6390 + II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 JJ aa 1786 + JJ aa 1786 JJ aa 1786 JJ aa 1786 JJ bb {} JJ bb {} JJ bb {} + JJ bb {}} + +do_execsql_test 1.18.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.18.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 870 AA aa 870 AA aa 870 AA aa 870 AA bb 845 AA bb 845 + AA bb 845 AA bb 845 BB aa 959 BB aa 959 BB aa 959 BB aa 959 + BB aa 959 BB aa 959 BB bb 959 BB bb 959 BB bb 959 BB bb 959 + BB bb 959 BB bb 959 CC aa 959 CC aa 959 CC aa 959 CC aa 959 + CC bb 959 CC bb 959 DD aa 959 DD aa 959 DD aa 959 DD bb 938 + DD bb 938 DD bb 938 DD bb 938 EE aa 938 EE aa 938 EE bb 979 + EE bb 979 EE bb 979 FF aa 979 FF aa 979 FF aa 979 FF aa 979 + FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 + GG aa 979 GG aa 979 GG aa 979 GG aa 979 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 963 HH aa 963 HH aa 963 HH bb 899 + HH bb 899 HH bb 899 HH bb 899 HH bb 899 HH bb 899 II aa 899 + II aa 899 II bb 899 II bb 899 II bb 899 II bb 899 II bb 899 + JJ aa 839 JJ aa 839 JJ aa 839 JJ aa 839 JJ bb {} JJ bb {} + JJ bb {} JJ bb {}} + +do_execsql_test 1.18.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 158 AA aa 158 AA aa 158 AA aa 158 AA bb 158 AA bb 158 + AA bb 158 AA bb 158 BB aa 158 BB aa 158 BB aa 158 BB aa 158 + BB aa 158 BB aa 158 BB bb 113 BB bb 113 BB bb 113 BB bb 113 + BB bb 113 BB bb 113 CC aa 113 CC aa 113 CC aa 113 CC aa 113 + CC bb 113 CC bb 113 DD aa 102 DD aa 102 DD aa 102 DD bb 102 + DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 + EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 133 FF bb 133 FF bb 133 FF bb 133 FF bb 133 FF bb 133 + GG aa 133 GG aa 133 GG aa 133 GG aa 133 GG bb 113 GG bb 113 + GG bb 113 GG bb 113 HH aa 113 HH aa 113 HH aa 113 HH bb 113 + HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 II aa 113 + II aa 113 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 + JJ aa 257 JJ aa 257 JJ aa 257 JJ aa 257 JJ bb {} JJ bb {} + JJ bb {} JJ bb {}} + +do_execsql_test 1.18.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 22701 AA aa 22701 AA aa 22701 AA aa 22701 AA bb 22701 + AA bb 22701 AA bb 22701 AA bb 22701 BB aa 19829 BB aa 19829 + BB aa 19829 BB aa 19829 BB aa 19829 BB aa 19829 BB bb 19829 + BB bb 19829 BB bb 19829 BB bb 19829 BB bb 19829 BB bb 19829 + CC aa 22150 CC aa 22150 CC aa 22150 CC aa 22150 CC bb 22150 + CC bb 22150 DD aa 21758 DD aa 21758 DD aa 21758 DD bb 21758 + DD bb 21758 DD bb 21758 DD bb 21758 EE aa 23019 EE aa 23019 + EE bb 23019 EE bb 23019 EE bb 23019 FF aa 17351 FF aa 17351 + FF aa 17351 FF aa 17351 FF bb 17351 FF bb 17351 FF bb 17351 + FF bb 17351 FF bb 17351 FF bb 17351 GG aa 12883 GG aa 12883 + GG aa 12883 GG aa 12883 GG bb 12883 GG bb 12883 GG bb 12883 + GG bb 12883 HH aa 7440 HH aa 7440 HH aa 7440 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 3800 + II aa 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 + JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} JJ bb {} JJ bb {} + JJ bb {}} + +do_execsql_test 1.18.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 12840 AA aa 12840 AA aa 12840 AA aa 12840 AA bb 11787 + AA bb 11787 AA bb 11787 AA bb 11787 BB aa 11621 BB aa 11621 + BB aa 11621 BB aa 11621 BB aa 11621 BB aa 11621 BB bb 8044 + BB bb 8044 BB bb 8044 BB bb 8044 BB bb 8044 BB bb 8044 CC aa 7739 + CC aa 7739 CC aa 7739 CC aa 7739 CC bb 8734 CC bb 8734 + DD aa 10914 DD aa 10914 DD aa 10914 DD bb 9804 DD bb 9804 + DD bb 9804 DD bb 9804 EE aa 11785 EE aa 11785 EE bb 12385 + EE bb 12385 EE bb 12385 FF aa 13416 FF aa 13416 FF aa 13416 + FF aa 13416 FF bb 10961 FF bb 10961 FF bb 10961 FF bb 10961 + FF bb 10961 FF bb 10961 GG aa 11954 GG aa 11954 GG aa 11954 + GG aa 11954 GG bb 11097 GG bb 11097 GG bb 11097 GG bb 11097 + HH aa 10634 HH aa 10634 HH aa 10634 HH bb 7440 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 6390 II aa 6390 + II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 JJ aa 1786 + JJ aa 1786 JJ aa 1786 JJ aa 1786 JJ bb {} JJ bb {} JJ bb {} + JJ bb {}} + +do_execsql_test 1.18.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND 5 FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 22701 22701 12840 12840 AA aa 22701 22701 12840 12840 + AA aa 22701 22701 12840 12840 AA aa 22701 22701 12840 12840 + AA bb 22701 22701 11787 11787 AA bb 22701 22701 11787 11787 + AA bb 22701 22701 11787 11787 AA bb 22701 22701 11787 11787 + BB aa 19829 19829 11621 11621 BB aa 19829 19829 11621 11621 + BB aa 19829 19829 11621 11621 BB aa 19829 19829 11621 11621 + BB aa 19829 19829 11621 11621 BB aa 19829 19829 11621 11621 + BB bb 19829 19829 8044 8044 BB bb 19829 19829 8044 8044 + BB bb 19829 19829 8044 8044 BB bb 19829 19829 8044 8044 + BB bb 19829 19829 8044 8044 BB bb 19829 19829 8044 8044 + CC aa 22150 22150 7739 7739 CC aa 22150 22150 7739 7739 + CC aa 22150 22150 7739 7739 CC aa 22150 22150 7739 7739 + CC bb 22150 22150 8734 8734 CC bb 22150 22150 8734 8734 + DD aa 21758 21758 10914 10914 DD aa 21758 21758 10914 10914 + DD aa 21758 21758 10914 10914 DD bb 21758 21758 9804 9804 + DD bb 21758 21758 9804 9804 DD bb 21758 21758 9804 9804 + DD bb 21758 21758 9804 9804 EE aa 23019 23019 11785 11785 + EE aa 23019 23019 11785 11785 EE bb 23019 23019 12385 12385 + EE bb 23019 23019 12385 12385 EE bb 23019 23019 12385 12385 + FF aa 17351 17351 13416 13416 FF aa 17351 17351 13416 13416 + FF aa 17351 17351 13416 13416 FF aa 17351 17351 13416 13416 + FF bb 17351 17351 10961 10961 FF bb 17351 17351 10961 10961 + FF bb 17351 17351 10961 10961 FF bb 17351 17351 10961 10961 + FF bb 17351 17351 10961 10961 FF bb 17351 17351 10961 10961 + GG aa 12883 12883 11954 11954 GG aa 12883 12883 11954 11954 + GG aa 12883 12883 11954 11954 GG aa 12883 12883 11954 11954 + GG bb 12883 12883 11097 11097 GG bb 12883 12883 11097 11097 + GG bb 12883 12883 11097 11097 GG bb 12883 12883 11097 11097 + HH aa 7440 7440 10634 10634 HH aa 7440 7440 10634 10634 + HH aa 7440 7440 10634 10634 HH bb 7440 7440 7440 7440 + HH bb 7440 7440 7440 7440 HH bb 7440 7440 7440 7440 + HH bb 7440 7440 7440 7440 HH bb 7440 7440 7440 7440 + HH bb 7440 7440 7440 7440 II aa 3800 3800 6390 6390 + II aa 3800 3800 6390 6390 II bb 3800 3800 3800 3800 + II bb 3800 3800 3800 3800 II bb 3800 3800 3800 3800 + II bb 3800 3800 3800 3800 II bb 3800 3800 3800 3800 + JJ aa {} {} 1786 1786 JJ aa {} {} 1786 1786 JJ aa {} {} 1786 1786 + JJ aa {} {} 1786 1786 JJ bb {} {} {} {} JJ bb {} {} {} {} + JJ bb {} {} {} {} JJ bb {} {} {} {}} + +do_execsql_test 1.19.1 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 40052 AA aa 40052 AA aa 40052 AA aa 40052 AA bb 40052 + AA bb 40052 AA bb 40052 AA bb 40052 BB aa 32712 BB aa 32712 + BB aa 32712 BB aa 32712 BB aa 32712 BB aa 32712 BB bb 32712 + BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 + CC aa 29590 CC aa 29590 CC aa 29590 CC aa 29590 CC bb 29590 + CC bb 29590 DD aa 25558 DD aa 25558 DD aa 25558 DD bb 25558 + DD bb 25558 DD bb 25558 DD bb 25558 EE aa 23019 EE aa 23019 + EE bb 23019 EE bb 23019 EE bb 23019 FF aa 17351 FF aa 17351 + FF aa 17351 FF aa 17351 FF bb 17351 FF bb 17351 FF bb 17351 + FF bb 17351 FF bb 17351 FF bb 17351 GG aa 12883 GG aa 12883 + GG aa 12883 GG aa 12883 GG bb 12883 GG bb 12883 GG bb 12883 + GG bb 12883 HH aa 7440 HH aa 7440 HH aa 7440 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 3800 + II aa 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 + JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} JJ bb {} JJ bb {} + JJ bb {}} + +do_execsql_test 1.19.2 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 42430 AA aa 42430 AA aa 42430 AA aa 42430 AA bb 40052 + AA bb 40052 AA bb 40052 AA bb 40052 BB aa 37179 BB aa 37179 + BB aa 37179 BB aa 37179 BB aa 37179 BB aa 37179 BB bb 32712 + BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 + CC aa 30758 CC aa 30758 CC aa 30758 CC aa 30758 CC bb 29590 + CC bb 29590 DD aa 28265 DD aa 28265 DD aa 28265 DD bb 25558 + DD bb 25558 DD bb 25558 DD bb 25558 EE aa 24668 EE aa 24668 + EE bb 23019 EE bb 23019 EE bb 23019 FF aa 20856 FF aa 20856 + FF aa 20856 FF aa 20856 FF bb 17351 FF bb 17351 FF bb 17351 + FF bb 17351 FF bb 17351 FF bb 17351 GG aa 15754 GG aa 15754 + GG aa 15754 GG aa 15754 GG bb 12883 GG bb 12883 GG bb 12883 + GG bb 12883 HH aa 10634 HH aa 10634 HH aa 10634 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 6390 + II aa 6390 II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 + JJ aa 1786 JJ aa 1786 JJ aa 1786 JJ aa 1786 JJ bb {} JJ bb {} + JJ bb {} JJ bb {}} + +do_execsql_test 1.19.3 { + SELECT a, b, rank() OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 1 AA aa 1 AA aa 1 AA aa 1 AA bb 1 AA bb 1 AA bb 1 + AA bb 1 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 BB aa 9 + BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 BB bb 9 CC aa 21 + CC aa 21 CC aa 21 CC aa 21 CC bb 21 CC bb 21 DD aa 27 DD aa 27 + DD aa 27 DD bb 27 DD bb 27 DD bb 27 DD bb 27 EE aa 34 EE aa 34 + EE bb 34 EE bb 34 EE bb 34 FF aa 39 FF aa 39 FF aa 39 FF aa 39 + FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 FF bb 39 GG aa 49 + GG aa 49 GG aa 49 GG aa 49 GG bb 49 GG bb 49 GG bb 49 GG bb 49 + HH aa 57 HH aa 57 HH aa 57 HH bb 57 HH bb 57 HH bb 57 HH bb 57 + HH bb 57 HH bb 57 II aa 66 II aa 66 II bb 66 II bb 66 II bb 66 + II bb 66 II bb 66 JJ aa 73 JJ aa 73 JJ aa 73 JJ aa 73 JJ bb 73 + JJ bb 73 JJ bb 73 JJ bb 73} + +do_execsql_test 1.19.4 { + SELECT a, b, max(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 979 AA aa 979 AA aa 979 AA aa 979 AA bb 979 AA bb 979 + AA bb 979 AA bb 979 BB aa 979 BB aa 979 BB aa 979 BB aa 979 + BB aa 979 BB aa 979 BB bb 979 BB bb 979 BB bb 979 BB bb 979 + BB bb 979 BB bb 979 CC aa 979 CC aa 979 CC aa 979 CC aa 979 + CC bb 979 CC bb 979 DD aa 979 DD aa 979 DD aa 979 DD bb 979 + DD bb 979 DD bb 979 DD bb 979 EE aa 979 EE aa 979 EE bb 979 + EE bb 979 EE bb 979 FF aa 979 FF aa 979 FF aa 979 FF aa 979 + FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 FF bb 979 + GG aa 979 GG aa 979 GG aa 979 GG aa 979 GG bb 979 GG bb 979 + GG bb 979 GG bb 979 HH aa 963 HH aa 963 HH aa 963 HH bb 899 + HH bb 899 HH bb 899 HH bb 899 HH bb 899 HH bb 899 II aa 899 + II aa 899 II bb 899 II bb 899 II bb 899 II bb 899 II bb 899 + JJ aa 839 JJ aa 839 JJ aa 839 JJ aa 839 JJ bb {} JJ bb {} + JJ bb {} JJ bb {}} + +do_execsql_test 1.19.5 { + SELECT a, b, min(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 102 AA aa 102 AA aa 102 AA aa 102 AA bb 102 AA bb 102 + AA bb 102 AA bb 102 BB aa 102 BB aa 102 BB aa 102 BB aa 102 + BB aa 102 BB aa 102 BB bb 102 BB bb 102 BB bb 102 BB bb 102 + BB bb 102 BB bb 102 CC aa 102 CC aa 102 CC aa 102 CC aa 102 + CC bb 102 CC bb 102 DD aa 102 DD aa 102 DD aa 102 DD bb 102 + DD bb 102 DD bb 102 DD bb 102 EE aa 102 EE aa 102 EE bb 102 + EE bb 102 EE bb 102 FF aa 102 FF aa 102 FF aa 102 FF aa 102 + FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 FF bb 113 + GG aa 113 GG aa 113 GG aa 113 GG aa 113 GG bb 113 GG bb 113 + GG bb 113 GG bb 113 HH aa 113 HH aa 113 HH aa 113 HH bb 113 + HH bb 113 HH bb 113 HH bb 113 HH bb 113 HH bb 113 II aa 113 + II aa 113 II bb 113 II bb 113 II bb 113 II bb 113 II bb 113 + JJ aa 257 JJ aa 257 JJ aa 257 JJ aa 257 JJ bb {} JJ bb {} + JJ bb {} JJ bb {}} + +do_execsql_test 1.19.6 { + SELECT a, b, sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 40052 AA aa 40052 AA aa 40052 AA aa 40052 AA bb 40052 + AA bb 40052 AA bb 40052 AA bb 40052 BB aa 32712 BB aa 32712 + BB aa 32712 BB aa 32712 BB aa 32712 BB aa 32712 BB bb 32712 + BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 + CC aa 29590 CC aa 29590 CC aa 29590 CC aa 29590 CC bb 29590 + CC bb 29590 DD aa 25558 DD aa 25558 DD aa 25558 DD bb 25558 + DD bb 25558 DD bb 25558 DD bb 25558 EE aa 23019 EE aa 23019 + EE bb 23019 EE bb 23019 EE bb 23019 FF aa 17351 FF aa 17351 + FF aa 17351 FF aa 17351 FF bb 17351 FF bb 17351 FF bb 17351 + FF bb 17351 FF bb 17351 FF bb 17351 GG aa 12883 GG aa 12883 + GG aa 12883 GG aa 12883 GG bb 12883 GG bb 12883 GG bb 12883 + GG bb 12883 HH aa 7440 HH aa 7440 HH aa 7440 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 3800 + II aa 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 + JJ aa {} JJ aa {} JJ aa {} JJ aa {} JJ bb {} JJ bb {} JJ bb {} + JJ bb {}} + +do_execsql_test 1.19.7 { + SELECT a, b, sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW) FROM t3 ORDER BY 1, 2, 3; +} {AA aa 42430 AA aa 42430 AA aa 42430 AA aa 42430 AA bb 40052 + AA bb 40052 AA bb 40052 AA bb 40052 BB aa 37179 BB aa 37179 + BB aa 37179 BB aa 37179 BB aa 37179 BB aa 37179 BB bb 32712 + BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 BB bb 32712 + CC aa 30758 CC aa 30758 CC aa 30758 CC aa 30758 CC bb 29590 + CC bb 29590 DD aa 28265 DD aa 28265 DD aa 28265 DD bb 25558 + DD bb 25558 DD bb 25558 DD bb 25558 EE aa 24668 EE aa 24668 + EE bb 23019 EE bb 23019 EE bb 23019 FF aa 20856 FF aa 20856 + FF aa 20856 FF aa 20856 FF bb 17351 FF bb 17351 FF bb 17351 + FF bb 17351 FF bb 17351 FF bb 17351 GG aa 15754 GG aa 15754 + GG aa 15754 GG aa 15754 GG bb 12883 GG bb 12883 GG bb 12883 + GG bb 12883 HH aa 10634 HH aa 10634 HH aa 10634 HH bb 7440 + HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 HH bb 7440 II aa 6390 + II aa 6390 II bb 3800 II bb 3800 II bb 3800 II bb 3800 II bb 3800 + JJ aa 1786 JJ aa 1786 JJ aa 1786 JJ aa 1786 JJ bb {} JJ bb {} + JJ bb {} JJ bb {}} + +do_execsql_test 1.19.8 { + SELECT a, b, + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW), + sum(c) OVER (ORDER BY a,b GROUPS BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING ) + FROM t3 ORDER BY 1, 2, 3; +} {AA aa 40052 40052 42430 42430 AA aa 40052 40052 42430 42430 + AA aa 40052 40052 42430 42430 AA aa 40052 40052 42430 42430 + AA bb 40052 40052 40052 40052 AA bb 40052 40052 40052 40052 + AA bb 40052 40052 40052 40052 AA bb 40052 40052 40052 40052 + BB aa 32712 32712 37179 37179 BB aa 32712 32712 37179 37179 + BB aa 32712 32712 37179 37179 BB aa 32712 32712 37179 37179 + BB aa 32712 32712 37179 37179 BB aa 32712 32712 37179 37179 + BB bb 32712 32712 32712 32712 BB bb 32712 32712 32712 32712 + BB bb 32712 32712 32712 32712 BB bb 32712 32712 32712 32712 + BB bb 32712 32712 32712 32712 BB bb 32712 32712 32712 32712 + CC aa 29590 29590 30758 30758 CC aa 29590 29590 30758 30758 + CC aa 29590 29590 30758 30758 CC aa 29590 29590 30758 30758 + CC bb 29590 29590 29590 29590 CC bb 29590 29590 29590 29590 + DD aa 25558 25558 28265 28265 DD aa 25558 25558 28265 28265 + DD aa 25558 25558 28265 28265 DD bb 25558 25558 25558 25558 + DD bb 25558 25558 25558 25558 DD bb 25558 25558 25558 25558 + DD bb 25558 25558 25558 25558 EE aa 23019 23019 24668 24668 + EE aa 23019 23019 24668 24668 EE bb 23019 23019 23019 23019 + EE bb 23019 23019 23019 23019 EE bb 23019 23019 23019 23019 + FF aa 17351 17351 20856 20856 FF aa 17351 17351 20856 20856 + FF aa 17351 17351 20856 20856 FF aa 17351 17351 20856 20856 + FF bb 17351 17351 17351 17351 FF bb 17351 17351 17351 17351 + FF bb 17351 17351 17351 17351 FF bb 17351 17351 17351 17351 + FF bb 17351 17351 17351 17351 FF bb 17351 17351 17351 17351 + GG aa 12883 12883 15754 15754 GG aa 12883 12883 15754 15754 + GG aa 12883 12883 15754 15754 GG aa 12883 12883 15754 15754 + GG bb 12883 12883 12883 12883 GG bb 12883 12883 12883 12883 + GG bb 12883 12883 12883 12883 GG bb 12883 12883 12883 12883 + HH aa 7440 7440 10634 10634 HH aa 7440 7440 10634 10634 + HH aa 7440 7440 10634 10634 HH bb 7440 7440 7440 7440 + HH bb 7440 7440 7440 7440 HH bb 7440 7440 7440 7440 + HH bb 7440 7440 7440 7440 HH bb 7440 7440 7440 7440 + HH bb 7440 7440 7440 7440 II aa 3800 3800 6390 6390 + II aa 3800 3800 6390 6390 II bb 3800 3800 3800 3800 + II bb 3800 3800 3800 3800 II bb 3800 3800 3800 3800 + II bb 3800 3800 3800 3800 II bb 3800 3800 3800 3800 + JJ aa {} {} 1786 1786 JJ aa {} {} 1786 1786 JJ aa {} {} 1786 1786 + JJ aa {} {} 1786 1786 JJ bb {} {} {} {} JJ bb {} {} {} {} + JJ bb {} {} {} {} JJ bb {} {} {} {}} + +do_execsql_test 2.1.1 { + SELECT row_number() OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS + ) +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 + 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 + 78 79 80} + +do_execsql_test 2.1.2 { + SELECT nth_value(c, 14) OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS + ) +} {247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247} + +do_execsql_test 2.1.3 { + SELECT min(c) OVER win, max(c) OVER win, sum(c) OVER win FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE NO OTHERS + ) ORDER BY a, b, c; +} {102 223 1358 102 239 2293 102 911 38097 102 934 39960 102 309 4159 + 102 572 10643 102 627 13069 102 870 35417 102 247 2540 102 393 6608 + 102 399 7405 102 412 7817 102 660 16277 102 762 24077 102 633 14331 + 102 705 19673 102 711 20384 102 786 27176 102 792 28758 + 102 840 32858 102 158 767 102 430 8668 102 607 11824 102 759 23315 + 102 346 5506 102 822 31179 102 224 1582 102 256 3298 102 845 34547 + 102 238 2054 102 716 21100 102 794 29552 102 959 42795 102 113 215 + 102 777 26390 102 252 3042 102 629 13698 102 768 25613 102 208 1135 + 102 618 12442 102 667 16944 102 670 17614 102 102 102 102 295 3850 + 102 574 11217 102 726 21826 102 870 36287 102 938 40898 102 148 609 + 102 335 4824 102 480 9591 102 634 14965 102 160 927 102 844 33702 + 102 929 39026 102 938 41836 102 480 10071 102 790 27966 + 102 979 44737 102 133 461 102 330 4489 102 355 6215 102 683 18968 + 102 730 22556 102 963 43758 102 398 7006 102 652 15617 102 250 2790 + 102 421 8238 102 443 9111 102 671 18285 102 805 30357 102 113 328 + 102 234 1816 102 768 24845 102 899 37186 102 257 3555 102 336 5160 + 102 354 5860 102 839 32018} + +do_execsql_test 2.2.1 { + SELECT row_number() OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW + ) +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 + 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 + 78 79 80} + +do_execsql_test 2.2.2 { + SELECT nth_value(c, 14) OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW + ) +} {250 250 250 250 250 250 250 250 250 250 250 250 250 + 250 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247} + +do_execsql_test 2.2.3 { + SELECT min(c) OVER win, max(c) OVER win, sum(c) OVER win FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE CURRENT ROW + ) ORDER BY a, b, c; +} {102 208 1135 102 238 2054 102 899 37186 102 929 39026 102 295 3850 + 102 480 10071 102 618 12442 102 845 34547 102 239 2293 102 355 6215 + 102 398 7006 102 399 7405 102 652 15617 102 759 23315 102 629 13698 + 102 683 18968 102 705 19673 102 777 26390 102 790 27966 + 102 839 32018 102 148 609 102 421 8238 102 574 11217 102 730 22556 + 102 336 5160 102 805 30357 102 223 1358 102 252 3042 102 844 33702 + 102 234 1816 102 711 20384 102 792 28758 102 938 41836 102 102 102 + 102 768 25613 102 250 2790 102 627 13069 102 768 24845 102 160 927 + 102 607 11824 102 660 16277 102 667 16944 {} {} {} 102 257 3555 + 102 572 10643 102 716 21100 102 870 35417 102 934 39960 102 133 461 + 102 330 4489 102 443 9111 102 633 14331 102 158 767 102 840 32858 + 102 911 38097 102 938 40898 102 480 9591 102 786 27176 + 102 963 43758 102 113 328 102 309 4159 102 354 5860 102 671 18285 + 102 726 21826 102 959 42795 102 393 6608 102 634 14965 102 247 2540 + 102 412 7817 102 430 8668 102 670 17614 102 794 29552 102 113 215 + 102 224 1582 102 762 24077 102 870 36287 102 256 3298 102 335 4824 + 102 346 5506 102 822 31179} + +do_execsql_test 2.3.1 { + SELECT row_number() OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP + ) +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 + 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 + 78 79 80} + +do_execsql_test 2.3.2 { + SELECT nth_value(c, 14) OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP + ) +} {250 250 250 250 250 250 250 250 250 250 250 250 250 + 250 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247} + +do_execsql_test 2.3.3 { + SELECT min(c) OVER win, max(c) OVER win, sum(c) OVER win FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE GROUP + ) ORDER BY a, b, c; +} {102 208 1135 102 238 2054 102 899 37186 102 929 39026 102 295 3850 + 102 480 10071 102 618 12442 102 845 34547 102 239 2293 102 355 6215 + 102 398 7006 102 399 7405 102 652 15617 102 759 23315 102 629 13698 + 102 683 18968 102 705 19673 102 777 26390 102 790 27966 + 102 839 32018 102 148 609 102 421 8238 102 574 11217 102 730 22556 + 102 336 5160 102 805 30357 102 223 1358 102 252 3042 102 844 33702 + 102 234 1816 102 711 20384 102 792 28758 102 938 41836 102 102 102 + 102 768 25613 102 250 2790 102 627 13069 102 768 24845 102 160 927 + 102 607 11824 102 660 16277 102 667 16944 {} {} {} 102 257 3555 + 102 572 10643 102 716 21100 102 870 35417 102 934 39960 102 133 461 + 102 330 4489 102 443 9111 102 633 14331 102 158 767 102 840 32858 + 102 911 38097 102 938 40898 102 480 9591 102 786 27176 + 102 963 43758 102 113 328 102 309 4159 102 354 5860 102 671 18285 + 102 726 21826 102 959 42795 102 393 6608 102 634 14965 102 247 2540 + 102 412 7817 102 430 8668 102 670 17614 102 794 29552 102 113 215 + 102 224 1582 102 762 24077 102 870 36287 102 256 3298 102 335 4824 + 102 346 5506 102 822 31179} + +do_execsql_test 2.4.1 { + SELECT row_number() OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES + ) +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 + 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 + 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 + 78 79 80} + +do_execsql_test 2.4.2 { + SELECT nth_value(c, 14) OVER win + FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES + ) +} {247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247 247 247 247 247 247 247 247 247 247 247 247 + 247 247} + +do_execsql_test 2.4.3 { + SELECT min(c) OVER win, max(c) OVER win, sum(c) OVER win FROM t3 + WINDOW win AS ( + ORDER BY c, b, a + ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW EXCLUDE TIES + ) ORDER BY a, b, c; +} {102 223 1358 102 239 2293 102 911 38097 102 934 39960 102 309 4159 + 102 572 10643 102 627 13069 102 870 35417 102 247 2540 102 393 6608 + 102 399 7405 102 412 7817 102 660 16277 102 762 24077 102 633 14331 + 102 705 19673 102 711 20384 102 786 27176 102 792 28758 + 102 840 32858 102 158 767 102 430 8668 102 607 11824 102 759 23315 + 102 346 5506 102 822 31179 102 224 1582 102 256 3298 102 845 34547 + 102 238 2054 102 716 21100 102 794 29552 102 959 42795 102 113 215 + 102 777 26390 102 252 3042 102 629 13698 102 768 25613 102 208 1135 + 102 618 12442 102 667 16944 102 670 17614 102 102 102 102 295 3850 + 102 574 11217 102 726 21826 102 870 36287 102 938 40898 102 148 609 + 102 335 4824 102 480 9591 102 634 14965 102 160 927 102 844 33702 + 102 929 39026 102 938 41836 102 480 10071 102 790 27966 + 102 979 44737 102 133 461 102 330 4489 102 355 6215 102 683 18968 + 102 730 22556 102 963 43758 102 398 7006 102 652 15617 102 250 2790 + 102 421 8238 102 443 9111 102 671 18285 102 805 30357 102 113 328 + 102 234 1816 102 768 24845 102 899 37186 102 257 3555 102 336 5160 + 102 354 5860 102 839 32018} + +#========================================================================== + +do_execsql_test 3.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a REAL, b INTEGER); + INSERT INTO t1 VALUES + (5, 10), (10, 20), (13, 26), (13, 26), + (15, 30), (20, 40), (22,80), (30, 90); +} {} + +do_execsql_test 3.1 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING ) +} {5 30 10 112 13 102 13 102 15 142 20 150 22 120 30 90} + +do_execsql_test 3.2 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 10 PRECEDING AND 5 PRECEDING ) +} {5 {} 10 10 13 10 13 10 15 30 20 102 22 82 30 120} + +do_execsql_test 3.3 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 2 FOLLOWING AND 3 FOLLOWING ) +} {5 {} 10 52 13 30 13 30 15 {} 20 80 22 {} 30 {}} + +do_execsql_test 3.4 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a DESC RANGE BETWEEN 5 PRECEDING AND 5 FOLLOWING ) +} {30 90 22 120 20 150 15 142 13 102 13 102 10 112 5 30} + +do_execsql_test 3.5 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a DESC RANGE BETWEEN 10 PRECEDING AND 5 PRECEDING ) +} {30 {} 22 90 20 90 15 120 13 120 13 120 10 70 5 102} + +do_execsql_test 3.6 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a DESC RANGE BETWEEN 2 FOLLOWING AND 3 FOLLOWING ) +} {30 {} 22 40 20 {} 15 52 13 20 13 20 10 {} 5 {}} + +do_execsql_test 3.7 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 5.1 PRECEDING AND 5.3 FOLLOWING ) +} {5 30 10 112 13 102 13 102 15 142 20 150 22 120 30 90} + +do_execsql_test 3.8 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 10.2 PRECEDING AND 5.4 PRECEDING ) +} {5 {} 10 {} 13 10 13 10 15 10 20 72 22 82 30 120} + +do_execsql_test 3.9 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE BETWEEN 2.6 FOLLOWING AND 3.5 FOLLOWING ) +} {5 {} 10 52 13 {} 13 {} 15 {} 20 {} 22 {} 30 {}} + +do_execsql_test 3.10 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a DESC RANGE BETWEEN 5.7 PRECEDING AND 5.8 FOLLOWING ) +} {30 90 22 120 20 150 15 142 13 102 13 102 10 112 5 30} + +do_execsql_test 3.11 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a DESC RANGE BETWEEN UNBOUNDED PRECEDING AND 5.9 PRECEDING ) +} {30 {} 22 90 20 90 15 170 13 210 13 210 10 210 5 292} + +do_execsql_test 3.12 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a DESC RANGE BETWEEN 2.1 FOLLOWING AND UNBOUNDED FOLLOWING ) +} {30 232 22 112 20 112 15 30 13 30 13 30 10 10 5 {}} + +do_execsql_test 3.13 { + SELECT CAST(a AS INTEGER), sum(b) OVER win FROM t1 WINDOW win AS ( ORDER BY a RANGE 5.1 PRECEDING ) +} {5 10 10 30 13 72 13 72 15 102 20 70 22 120 30 90} + +#========================================================================== + +do_execsql_test 4.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER, b INTEGER); + INSERT INTO t1 VALUES + (NULL, 1), (NULL, 2), (NULL, 3), (10, 4), (10, 5); +} {} + +do_execsql_test 4.1.1 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1; +} {6 6 6 9 9} + +do_execsql_test 4.1.2 { + SELECT sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 5 PRECEDING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1; +} {6 6 6 9 9} + +do_execsql_test 4.2.1 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1 ; +} {{} {} 6 6 6} + +do_execsql_test 4.2.2 { + SELECT sum(b) OVER ( + ORDER BY a DESC RANGE BETWEEN 5 FOLLOWING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1 ; +} {{} {} 6 6 6} + +do_execsql_test 4.3.1 { + SELECT sum(b) OVER ( + ORDER BY a RANGE BETWEEN UNBOUNDED PRECEDING AND 10 FOLLOWING + ) FROM t1 ORDER BY 1 ; +} {6 6 6 15 15} + +do_execsql_test 4.4.1 { + SELECT sum(b) OVER ( + ORDER BY a ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 ; +} {3 6 9 9 12} + +do_execsql_test 4.4.2 { + SELECT sum(b) OVER ( + ORDER BY a DESC ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 ; +} {5 6 8 9 10} + +#========================================================================== + +do_execsql_test 5.0 { + INSERT INTO t3 VALUES + (NULL, 'bb', 355), (NULL, 'cc', 158), (NULL, 'aa', 399), + ('JJ', NULL, 839), ('FF', NULL, 618), ('BB', NULL, 393), + (NULL, 'bb', 629), (NULL, NULL, 667), (NULL, NULL, 870); +} {} + +do_execsql_test 5.1.1.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83} + +do_execsql_test 5.1.1.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1} + +do_execsql_test 5.1.2.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {899 113 9 899 113 9 899 113 9 899 113 9 899 113 9 899 113 9 + 899 113 9 899 113 9 899 113 9 899 113 16 899 113 16 899 113 16 + 899 113 16 899 113 16 899 113 16 899 113 16 979 102 44 979 102 44 + 979 102 44 979 102 44 979 102 44 979 102 44 979 102 44 979 102 44 + 979 102 44 979 102 44 979 102 44 979 102 49 979 102 49 979 102 49 + 979 102 49 979 102 49 979 102 56 979 102 56 979 102 56 979 102 56 + 979 102 56 979 102 56 979 102 56 979 102 62 979 102 62 979 102 62 + 979 102 62 979 102 62 979 102 62 979 102 75 979 102 75 979 102 75 + 979 102 75 979 102 75 979 102 75 979 102 75 979 102 75 979 102 75 + 979 102 75 979 102 75 979 102 75 979 102 75 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 113 25 979 113 25 979 113 25 979 113 25 979 113 25 979 113 25 + 979 113 25 979 113 25 979 113 25 979 113 33 979 113 33 979 113 33 + 979 113 33 979 113 33 979 113 33 979 113 33 979 113 33} + +do_execsql_test 5.1.2.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {2947 81 11 2947 81 11 2947 81 11 2947 81 11 2947 81 11 2947 81 11 + 2947 81 11 2947 81 11 2947 81 11 5287 74 10 5287 74 10 5287 74 10 + 5287 74 10 5287 74 10 5287 74 10 5287 74 10 8400 65 9 8400 65 9 + 8400 65 9 8400 65 9 8400 65 9 8400 65 9 8400 65 9 8400 65 9 + 8400 65 9 9664 57 8 9664 57 8 9664 57 8 9664 57 8 9664 57 8 + 9664 57 8 9664 57 8 9664 57 8 10626 46 7 10626 46 7 10626 46 7 + 10626 46 7 10626 46 7 10626 46 7 10626 46 7 10626 46 7 10626 46 7 + 10626 46 7 10626 46 7 12145 41 6 12145 41 6 12145 41 6 12145 41 6 + 12145 41 6 13949 34 5 13949 34 5 13949 34 5 13949 34 5 13949 34 5 + 13949 34 5 13949 34 5 15315 28 4 15315 28 4 15315 28 4 15315 28 4 + 15315 28 4 15315 28 4 18796 15 3 18796 15 3 18796 15 3 18796 15 3 + 18796 15 3 18796 15 3 18796 15 3 18796 15 3 18796 15 3 18796 15 3 + 18796 15 3 18796 15 3 18796 15 3 21105 7 2 21105 7 2 21105 7 2 + 21105 7 2 21105 7 2 21105 7 2 21105 7 2 21105 7 2 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1} + +do_execsql_test 5.1.3.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {777 113 5 777 113 5 777 113 5 777 113 5 777 113 5 805 250 7 + 805 250 7 805 250 7 805 250 7 805 250 7 805 250 7 805 250 7 + 822 158 6 822 158 6 822 158 6 822 158 6 822 158 6 822 158 6 + 840 247 13 840 247 13 840 247 13 840 247 13 840 247 13 840 247 13 + 840 247 13 840 247 13 840 247 13 840 247 13 840 247 13 840 247 13 + 840 247 13 870 158 0 870 158 0 870 158 0 870 158 0 870 158 0 + 870 158 0 899 113 9 899 113 9 899 113 9 899 113 9 899 113 9 + 899 113 9 899 113 9 899 113 9 899 113 9 934 223 8 934 223 8 + 934 223 8 934 223 8 934 223 8 934 223 8 934 223 8 934 223 8 + 938 102 11 938 102 11 938 102 11 938 102 11 938 102 11 938 102 11 + 938 102 11 938 102 11 938 102 11 938 102 11 938 102 11 938 148 8 + 938 148 8 938 148 8 938 148 8 938 148 8 938 148 8 938 148 8 + 938 148 8 959 224 7 959 224 7 959 224 7 959 224 7 959 224 7 + 959 224 7 959 224 7 979 133 9 979 133 9 979 133 9 979 133 9 + 979 133 9 979 133 9 979 133 9 979 133 9 979 133 9} + +do_execsql_test 5.1.3.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {962 1 1 962 1 1 962 1 1 962 1 1 962 1 1 962 1 1 962 1 1 + 962 1 1 962 1 1 962 1 1 962 1 1 1264 1 1 1264 1 1 1264 1 1 + 1264 1 1 1264 1 1 1264 1 1 1264 1 1 1264 1 1 1366 1 1 1366 1 1 + 1366 1 1 1366 1 1 1366 1 1 1366 1 1 1519 1 1 1519 1 1 1519 1 1 + 1519 1 1 1519 1 1 1804 1 1 1804 1 1 1804 1 1 1804 1 1 1804 1 1 + 1804 1 1 1804 1 1 2050 1 1 2050 1 1 2050 1 1 2050 1 1 2050 1 1 + 2050 1 1 2309 1 1 2309 1 1 2309 1 1 2309 1 1 2309 1 1 2309 1 1 + 2309 1 1 2309 1 1 2340 1 1 2340 1 1 2340 1 1 2340 1 1 2340 1 1 + 2340 1 1 2340 1 1 2947 1 1 2947 1 1 2947 1 1 2947 1 1 2947 1 1 + 2947 1 1 2947 1 1 2947 1 1 2947 1 1 3113 1 1 3113 1 1 3113 1 1 + 3113 1 1 3113 1 1 3113 1 1 3113 1 1 3113 1 1 3113 1 1 3481 1 1 + 3481 1 1 3481 1 1 3481 1 1 3481 1 1 3481 1 1 3481 1 1 3481 1 1 + 3481 1 1 3481 1 1 3481 1 1 3481 1 1 3481 1 1} + +do_execsql_test 5.1.4.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {870 158 0 870 158 0 870 158 0 870 158 0 870 158 0 870 158 0 + 934 158 8 934 158 8 934 158 8 934 158 8 934 158 8 934 158 8 + 934 158 8 934 158 8 934 158 21 934 158 21 934 158 21 934 158 21 + 934 158 21 934 158 21 934 158 21 934 158 21 934 158 21 934 158 21 + 934 158 21 934 158 21 934 158 21 934 158 27 934 158 27 934 158 27 + 934 158 27 934 158 27 934 158 27 959 102 50 959 102 50 959 102 50 + 959 102 50 959 102 50 959 102 50 959 102 50 959 102 50 959 102 50 + 959 102 50 959 102 50 959 102 58 959 102 58 959 102 58 959 102 58 + 959 102 58 959 102 58 959 102 58 959 102 58 959 113 39 959 113 39 + 959 113 39 959 113 39 959 113 39 959 158 34 959 158 34 959 158 34 + 959 158 34 959 158 34 959 158 34 959 158 34 979 102 53 979 102 53 + 979 102 53 979 102 53 979 102 53 979 102 53 979 102 53 979 102 56 + 979 102 56 979 102 56 979 102 56 979 102 56 979 102 56 979 102 56 + 979 102 56 979 102 56 979 102 59 979 102 59 979 102 59 979 102 59 + 979 102 59 979 102 59 979 102 59 979 102 59 979 102 59} + +do_execsql_test 5.1.4.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {2050 1 1 2050 1 1 2050 1 1 2050 1 1 2050 1 1 2050 1 1 4359 7 2 + 4359 7 2 4359 7 2 4359 7 2 4359 7 2 4359 7 2 4359 7 2 4359 7 2 + 7840 15 3 7840 15 3 7840 15 3 7840 15 3 7840 15 3 7840 15 3 + 7840 15 3 7840 15 3 7840 15 3 7840 15 3 7840 15 3 7840 15 3 + 7840 15 3 9206 28 4 9206 28 4 9206 28 4 9206 28 4 9206 28 4 + 9206 28 4 11010 34 5 11010 34 5 11010 34 5 11010 34 5 11010 34 5 + 11010 34 5 11010 34 5 12368 74 10 12368 74 10 12368 74 10 + 12368 74 10 12368 74 10 12368 74 10 12368 74 10 12529 41 6 + 12529 41 6 12529 41 6 12529 41 6 12529 41 6 12705 57 8 12705 57 8 + 12705 57 8 12705 57 8 12705 57 8 12705 57 8 12705 57 8 12705 57 8 + 13491 46 7 13491 46 7 13491 46 7 13491 46 7 13491 46 7 13491 46 7 + 13491 46 7 13491 46 7 13491 46 7 13491 46 7 13491 46 7 13509 65 9 + 13509 65 9 13509 65 9 13509 65 9 13509 65 9 13509 65 9 13509 65 9 + 13509 65 9 13509 65 9 13949 81 11 13949 81 11 13949 81 11 + 13949 81 11 13949 81 11 13949 81 11 13949 81 11 13949 81 11 + 13949 81 11} + +do_execsql_test 5.1.5.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {102 102 1 113 113 2 113 113 2 133 133 1 148 148 1 160 158 2 + 160 158 2 160 158 2 208 208 1 224 223 2 224 223 2 239 234 3 + 239 234 3 239 234 3 252 247 3 257 247 5 257 247 5 257 250 4 + 257 252 3 295 295 1 309 309 1 336 330 3 336 330 3 336 330 3 + 346 346 1 355 354 2 355 354 2 355 354 2 399 393 4 399 393 4 + 399 393 4 399 393 4 399 393 4 412 412 1 421 421 1 430 430 1 + 443 443 1 480 480 2 480 480 2 574 572 2 574 572 2 607 607 1 + 618 618 2 618 618 2 634 627 4 634 627 4 634 627 4 634 627 4 + 634 629 3 652 652 1 667 660 2 671 667 3 671 667 3 671 667 3 + 671 667 3 683 683 1 711 705 2 716 705 3 716 711 2 730 726 2 + 730 726 2 762 759 2 768 759 4 768 762 3 768 762 3 777 777 1 + 792 786 3 794 786 4 794 786 4 794 790 3 805 805 1 822 822 1 + 845 839 5 845 839 5 845 839 5 845 839 5 845 839 5 870 870 2 + 870 870 2 870 870 2 899 899 1 911 911 1 934 929 2 938 929 4 + 938 934 3 938 934 3 963 959 2 963 959 2 979 979 1} + +do_execsql_test 5.1.5.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 5 4 {} 6 5 {} 6 5 {} 8 6 {} 9 7 {} 25 23 {} 34 29 + {} 36 31 {} 38 33 {} 38 33 {} 40 34 {} 41 35 {} 43 37 {} 43 37 + {} 50 42 {} 60 51 {} 61 52 {} 64 55 {} 64 55 {} 67 57 {} 68 58 + {} 69 59 {} 70 60 {} 72 62 {} 78 67 {} 78 67 {} 78 67 {} 85 72 + {} 85 72 133 4 3 223 10 8 223 11 9 226 2 2 226 2 2 239 12 10 + 239 13 11 239 14 12 247 15 13 257 18 16 257 19 17 295 20 18 + 309 21 19 335 22 20 335 23 21 335 24 22 421 35 30 443 37 32 + 504 16 14 504 17 15 607 42 36 683 56 47 710 26 24 710 27 25 + 710 27 25 711 59 50 759 62 53 759 63 54 777 66 56 805 71 61 + 899 81 68 911 82 69 929 83 70 929 84 71 979 89 75 1334 51 43 + 1416 57 48 1416 58 49 1584 29 26 1584 29 26 1584 31 27 1584 32 28 + 1584 32 28 1891 49 41 1922 87 73 1922 88 74 2005 52 44 2005 52 44 + 2005 54 45 2005 55 46 2518 45 38 2518 46 39 2518 46 39 2518 48 40 + 2523 73 63 2523 73 63 2523 75 64 2523 76 65 2523 77 66} + +do_execsql_test 5.1.6.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {102 102 1 113 113 2 113 113 2 133 133 1 148 148 1 158 158 1 + 158 158 1 160 160 1 208 208 1 223 223 1 224 224 1 234 234 1 + 238 238 1 239 239 1 247 247 1 250 250 1 252 252 1 256 256 1 + 257 257 1 295 295 1 309 309 1 330 330 1 335 335 1 336 336 1 + 346 346 1 354 354 1 355 355 1 355 355 1 393 393 2 393 393 2 + 398 398 1 399 399 1 399 399 1 412 412 1 421 421 1 430 430 1 + 443 443 1 480 480 2 480 480 2 572 572 1 574 574 1 607 607 1 + 618 618 2 618 618 2 627 627 1 629 629 1 629 629 1 633 633 1 + 634 634 1 652 652 1 660 660 1 667 667 1 667 667 1 670 670 1 + 671 671 1 683 683 1 705 705 1 711 711 1 716 716 1 726 726 1 + 730 730 1 759 759 1 762 762 1 768 768 2 768 768 2 777 777 1 + 786 786 1 790 790 1 792 792 1 794 794 1 805 805 1 822 822 1 + 839 839 2 839 839 2 840 840 1 844 844 1 845 845 1 870 870 2 + 870 870 2 870 870 2 899 899 1 911 911 1 929 929 1 934 934 1 + 938 938 2 938 938 2 959 959 1 963 963 1 979 979 1} + +do_execsql_test 5.1.6.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 5 4 {} 6 5 {} 6 5 {} 8 6 {} 9 7 {} 11 9 {} 12 10 + {} 13 11 {} 16 14 {} 17 15 {} 18 16 {} 22 20 {} 24 22 {} 25 23 + {} 26 24 {} 31 27 {} 34 29 {} 36 31 {} 38 33 {} 38 33 {} 40 34 + {} 41 35 {} 43 37 {} 43 37 {} 49 41 {} 50 42 {} 51 43 {} 54 45 + {} 59 50 {} 60 51 {} 61 52 {} 63 54 {} 64 55 {} 64 55 {} 67 57 + {} 68 58 {} 69 59 {} 70 60 {} 72 62 {} 75 64 {} 76 65 {} 78 67 + {} 78 67 {} 78 67 {} 84 71 {} 85 72 {} 85 72 133 4 3 223 10 8 + 226 2 2 226 2 2 239 14 12 247 15 13 257 19 17 295 20 18 + 309 21 19 335 23 21 421 35 30 443 37 32 607 42 36 627 45 38 + 633 48 40 671 55 46 683 56 47 705 57 48 710 27 25 710 27 25 + 711 58 49 759 62 53 777 66 56 786 29 26 786 29 26 798 32 28 + 798 32 28 805 71 61 845 77 66 899 81 68 911 82 69 929 83 70 + 959 87 73 963 88 74 979 89 75 1258 46 39 1258 46 39 1334 52 44 + 1334 52 44 1678 73 63 1678 73 63} + +do_execsql_test 5.1.7.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 113 81 979 113 82 979 133 80 979 148 79 979 158 77 + 979 158 78 979 160 77 979 208 76 979 223 75 979 224 74 979 234 73 + 979 238 72 979 239 71 979 247 70 979 250 69 979 252 68 979 256 67 + 979 257 66 979 295 65 979 309 64 979 330 63 979 335 62 979 336 61 + 979 346 60 979 354 59 979 355 58 979 355 58 979 393 56 979 393 57 + 979 398 55 979 399 54 979 399 54 979 412 53 979 421 52 979 430 51 + 979 443 50 979 480 48 979 480 49 979 572 47 979 574 46 979 607 45 + 979 618 43 979 618 44 979 627 42 979 629 41 979 629 41 979 633 40 + 979 634 39 979 652 38 979 660 37 979 667 36 979 667 36 979 670 35 + 979 671 34 979 683 33 979 705 32 979 711 31 979 716 30 979 726 29 + 979 730 28 979 759 27 979 762 26 979 768 24 979 768 25 979 777 23 + 979 786 22 979 790 21 979 792 20 979 794 19 979 805 18 979 822 17 + 979 839 15 979 839 16 979 840 14 979 844 13 979 845 12 979 870 10 + 979 870 11 979 870 11 979 899 9 979 911 8 979 929 7} + +do_execsql_test 5.1.7.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS ) + ORDER BY 1 , 2 , 3 +} {3830 89 89 4741 88 88 5640 84 84 5640 85 85 5640 86 86 5640 87 87 + 6485 81 81 6485 82 82 6485 83 83 7324 80 80 8163 78 78 8163 79 79 + 8968 73 73 8968 74 74 8968 75 75 8968 76 76 8968 77 77 9745 69 69 + 9745 70 70 9745 71 71 9745 72 72 10504 65 65 10504 66 66 + 10504 67 67 10504 68 68 11215 64 64 11920 63 63 12603 62 62 + 13274 60 60 13274 61 61 13941 59 59 14608 55 55 14608 56 56 + 14608 57 57 14608 58 58 15241 54 54 15870 53 53 16499 52 52 + 17126 49 49 17126 50 50 17126 51 51 17733 44 44 17733 45 45 + 17733 46 46 17733 47 47 17733 48 48 18176 42 42 18176 43 43 + 18597 40 40 18597 41 41 18996 39 39 19395 37 37 19395 38 38 + 19788 36 36 20181 35 35 20536 34 34 20891 30 30 20891 31 31 + 20891 32 32 20891 33 33 21226 28 28 21226 29 29 21535 27 27 + 21830 26 26 22087 22 22 22087 23 23 22087 24 24 22087 25 25 + 22334 21 21 22573 17 17 22573 18 18 22573 19 19 22573 20 20 + 22796 11 11 22796 12 12 22796 13 13 22796 14 14 22796 15 15 + 22796 16 16 22929 10 10 23042 9 9 23155 1 1 23155 2 2 23155 3 3 + 23155 4 4 23155 5 5 23155 6 6 23155 7 7 23155 8 8} + +do_execsql_test 5.2.1.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {963 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 113 82} + +do_execsql_test 5.2.1.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {22176 1 1 22192 1 1 22196 1 1 22226 1 1 22244 1 1 22256 1 1 + 22310 1 1 22316 1 1 22316 1 1 22350 1 1 22378 1 1 22396 1 1 + 22444 1 1 22450 1 1 22472 1 1 22484 1 1 22488 1 1 22488 1 1 + 22522 1 1 22526 1 1 22526 1 1 22528 1 1 22548 1 1 22712 1 1 + 22734 1 1 22756 1 1 22756 1 1 22762 1 1 22762 1 1 22800 1 1 + 22800 1 1 22820 1 1 22846 1 1 22860 1 1 22898 1 1 22908 1 1 + 22916 1 1 22932 1 1 23022 1 1 23042 1 1 23042 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1 + 23155 1 1 23155 1 1 23155 1 1 23155 1 1 23155 1 1} + +do_execsql_test 5.2.2.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {839 113 8 899 113 8 899 113 8 899 113 8 899 113 8 899 113 8 + 899 113 8 899 113 8 899 113 15 899 113 15 899 113 15 899 113 15 + 899 113 15 899 113 15 899 113 15 899 234 8 963 113 24 979 102 43 + 979 102 43 979 102 43 979 102 43 979 102 43 979 102 43 979 102 43 + 979 102 43 979 102 43 979 102 43 979 102 48 979 102 48 979 102 48 + 979 102 48 979 102 48 979 102 55 979 102 55 979 102 55 979 102 55 + 979 102 55 979 102 55 979 102 55 979 102 61 979 102 61 979 102 61 + 979 102 61 979 102 61 979 102 61 979 102 74 979 102 74 979 102 74 + 979 102 74 979 102 74 979 102 74 979 102 74 979 102 74 979 102 74 + 979 102 74 979 102 74 979 102 74 979 102 74 979 102 82 979 102 82 + 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 113 24 979 113 24 979 113 24 979 113 24 979 113 24 979 113 24 + 979 113 24 979 113 24 979 113 32 979 113 32 979 113 32 979 113 32 + 979 113 32 979 113 32 979 113 32 979 113 32 979 113 43} + +do_execsql_test 5.2.2.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {2048 81 11 2108 81 11 2108 81 11 2690 81 11 2834 81 11 2947 81 11 + 2947 81 11 2947 81 11 2947 81 11 4482 74 10 4616 74 10 4844 74 10 + 4866 74 10 5287 74 10 5287 74 10 5287 74 10 7421 65 9 7437 65 9 + 7717 65 9 8045 65 9 8267 65 9 8400 65 9 8400 65 9 8400 65 9 + 8400 65 9 8735 57 8 9329 57 8 9664 57 8 9664 57 8 9664 57 8 + 9664 57 8 9664 57 8 9664 57 8 9959 46 7 10331 46 7 10626 46 7 + 10626 46 7 10626 46 7 10626 46 7 10626 46 7 10626 46 7 10626 46 7 + 10626 46 7 10626 46 7 11368 41 6 11516 41 6 12032 41 6 12145 41 6 + 12145 41 6 12990 34 5 13104 34 5 13949 34 5 13949 34 5 13949 34 5 + 13949 34 5 13949 34 5 14556 28 4 14708 28 4 15315 28 4 15315 28 4 + 15315 28 4 15315 28 4 18085 15 3 18091 15 3 18163 15 3 18397 15 3 + 18403 15 3 18403 15 3 18549 15 3 18796 15 3 18796 15 3 18796 15 3 + 18796 15 3 18796 15 3 18796 15 3 20194 7 2 20478 7 2 20796 7 2 + 20866 7 2 20882 7 2 21105 7 2 21105 7 2 21105 7 2 22488 1 1 + 22526 1 1 22756 1 1 22800 1 1 23155 1 1 23155 1 1} + +do_execsql_test 5.2.3.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {667 158 0 671 250 6 759 158 5 768 113 4 777 113 4 777 113 4 + 777 113 4 777 252 4 792 247 12 805 250 6 805 250 6 805 250 6 + 805 250 6 805 250 6 805 398 6 822 158 5 822 158 5 822 158 5 + 822 158 5 822 346 5 839 113 8 840 247 12 840 247 12 840 247 12 + 840 247 12 840 247 12 840 247 12 840 247 12 840 247 12 840 247 12 + 840 247 12 840 247 12 840 393 12 845 224 6 870 102 10 870 158 0 + 870 158 0 870 158 0 870 158 0 870 355 0 899 113 8 899 113 8 + 899 113 8 899 113 8 899 113 8 899 113 8 899 113 8 899 234 8 + 911 223 7 929 148 7 934 223 7 934 223 7 934 223 7 934 223 7 + 934 223 7 934 223 7 934 239 7 938 102 10 938 102 10 938 102 10 + 938 102 10 938 102 10 938 102 10 938 102 10 938 102 10 938 102 10 + 938 148 7 938 148 7 938 148 7 938 148 7 938 148 7 938 148 7 + 938 160 7 938 208 10 959 224 6 959 224 6 959 224 6 959 224 6 + 959 224 6 959 238 6 963 133 8 979 133 8 979 133 8 979 133 8 + 979 133 8 979 133 8 979 133 8 979 133 8 979 330 8} + +do_execsql_test 5.2.3.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {295 1 1 335 1 1 607 1 1 667 1 1 742 1 1 759 1 1 845 1 1 + 890 1 1 929 1 1 959 1 1 962 1 1 962 1 1 962 1 1 962 1 1 + 962 1 1 962 1 1 962 1 1 962 1 1 962 1 1 1264 1 1 1264 1 1 + 1264 1 1 1264 1 1 1264 1 1 1264 1 1 1366 1 1 1366 1 1 1366 1 1 + 1366 1 1 1383 1 1 1398 1 1 1406 1 1 1421 1 1 1519 1 1 1519 1 1 + 1535 1 1 1651 1 1 1669 1 1 1682 1 1 1695 1 1 1804 1 1 1804 1 1 + 1804 1 1 1804 1 1 1804 1 1 1897 1 1 1919 1 1 2000 1 1 2048 1 1 + 2050 1 1 2050 1 1 2070 1 1 2086 1 1 2108 1 1 2108 1 1 2134 1 1 + 2150 1 1 2309 1 1 2309 1 1 2309 1 1 2340 1 1 2340 1 1 2340 1 1 + 2430 1 1 2690 1 1 2758 1 1 2770 1 1 2776 1 1 2834 1 1 2848 1 1 + 2947 1 1 2947 1 1 2947 1 1 2947 1 1 2980 1 1 3082 1 1 3088 1 1 + 3088 1 1 3113 1 1 3113 1 1 3113 1 1 3113 1 1 3234 1 1 3481 1 1 + 3481 1 1 3481 1 1 3481 1 1 3481 1 1 3481 1 1} + +do_execsql_test 5.2.4.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {667 158 0 870 158 0 870 158 0 870 158 0 870 158 0 870 355 0 + 911 158 7 934 158 7 934 158 7 934 158 7 934 158 7 934 158 7 + 934 158 7 934 158 7 934 158 20 934 158 20 934 158 20 934 158 20 + 934 158 20 934 158 20 934 158 20 934 158 20 934 158 20 934 158 20 + 934 158 20 934 158 20 934 158 20 934 158 26 934 158 26 934 158 26 + 934 158 26 934 158 26 934 158 26 934 158 33 959 102 49 959 102 49 + 959 102 49 959 102 49 959 102 49 959 102 49 959 102 49 959 102 49 + 959 102 49 959 102 49 959 102 57 959 102 57 959 102 57 959 102 57 + 959 102 57 959 102 57 959 102 57 959 102 57 959 113 38 959 113 38 + 959 113 38 959 113 38 959 113 49 959 158 33 959 158 33 959 158 33 + 959 158 33 959 158 33 959 158 33 959 158 38 963 102 58 979 102 52 + 979 102 52 979 102 52 979 102 52 979 102 52 979 102 52 979 102 52 + 979 102 55 979 102 55 979 102 55 979 102 55 979 102 55 979 102 55 + 979 102 55 979 102 55 979 102 55 979 102 58 979 102 58 979 102 58 + 979 102 58 979 102 58 979 102 58 979 102 58 979 102 58} + +do_execsql_test 5.2.4.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {1383 1 1 1421 1 1 1651 1 1 1695 1 1 2050 1 1 2050 1 1 3448 7 2 + 3732 7 2 4050 7 2 4120 7 2 4136 7 2 4359 7 2 4359 7 2 4359 7 2 + 7129 15 3 7135 15 3 7207 15 3 7441 15 3 7447 15 3 7447 15 3 + 7593 15 3 7840 15 3 7840 15 3 7840 15 3 7840 15 3 7840 15 3 + 7840 15 3 8447 28 4 8599 28 4 9206 28 4 9206 28 4 9206 28 4 + 9206 28 4 10051 34 5 10165 34 5 11010 34 5 11010 34 5 11010 34 5 + 11010 34 5 11010 34 5 11563 74 10 11697 74 10 11752 41 6 + 11776 57 8 11900 41 6 11925 74 10 11947 74 10 12368 74 10 + 12368 74 10 12368 74 10 12370 57 8 12416 41 6 12529 41 6 + 12529 41 6 12530 65 9 12546 65 9 12705 57 8 12705 57 8 12705 57 8 + 12705 57 8 12705 57 8 12705 57 8 12824 46 7 12826 65 9 + 13050 81 11 13110 81 11 13110 81 11 13154 65 9 13196 46 7 + 13376 65 9 13491 46 7 13491 46 7 13491 46 7 13491 46 7 13491 46 7 + 13491 46 7 13491 46 7 13491 46 7 13491 46 7 13509 65 9 13509 65 9 + 13509 65 9 13509 65 9 13692 81 11 13836 81 11 13949 81 11 + 13949 81 11 13949 81 11 13949 81 11} + +do_execsql_test 5.2.5.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 113 113 1 + 113 113 1 158 158 1 160 158 1 160 158 2 223 223 1 224 224 1 + 238 234 2 239 234 2 239 238 2 252 250 2 256 252 2 257 247 4 + 257 247 4 257 250 3 335 330 2 336 330 2 336 335 2 355 354 1 + 355 354 2 355 355 1 399 393 3 399 393 3 399 393 3 399 393 3 + 399 393 4 480 480 1 480 480 1 572 572 1 574 574 1 618 618 1 + 618 618 1 633 629 2 634 627 3 634 627 3 634 627 4 634 629 3 + 667 667 1 670 667 2 671 667 2 671 667 2 671 667 3 711 711 1 + 711 711 1 716 705 2 726 726 1 730 730 1 762 762 1 768 759 3 + 768 762 2 768 762 2 792 790 2 792 790 2 794 786 3 794 786 3 + 844 839 4 845 839 4 845 839 4 845 839 4 845 839 4 870 870 1 + 870 870 1 870 870 2 934 934 1 938 929 3 938 934 2 938 934 2 + 959 959 1 963 963 1} + +do_execsql_test 5.2.5.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 4 3 {} 5 4 {} 6 5 {} 6 5 {} 8 6 {} 9 7 {} 10 8 + {} 14 12 {} 15 13 {} 19 17 {} 20 18 {} 21 19 {} 23 21 {} 25 23 + {} 34 29 {} 35 30 {} 36 31 {} 37 32 {} 38 33 {} 38 33 {} 40 34 + {} 41 35 {} 42 36 {} 43 37 {} 43 37 {} 50 42 {} 56 47 {} 60 51 + {} 61 52 {} 62 53 {} 64 55 {} 64 55 {} 66 56 {} 67 57 {} 68 58 + {} 69 59 {} 70 60 {} 71 61 {} 72 62 {} 78 67 {} 78 67 {} 78 67 + {} 81 68 {} 82 69 {} 83 70 {} 85 72 {} 85 72 {} 89 75 113 2 2 + 113 2 2 223 11 9 239 12 10 239 13 11 257 18 16 335 22 20 + 335 24 22 355 27 25 355 27 25 504 16 14 504 17 15 705 58 49 + 710 26 24 711 57 48 711 59 50 759 63 54 929 84 71 959 88 74 + 963 87 73 1185 32 28 1185 32 28 1191 29 26 1191 29 26 1334 51 43 + 1334 55 46 1338 52 44 1338 52 44 1584 31 27 1678 77 66 1684 73 63 + 1684 73 63 1885 48 40 1889 46 39 1889 46 39 1891 45 38 1891 49 41 + 2005 54 45 2523 75 64 2523 76 65} + +do_execsql_test 5.2.6.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 113 113 1 + 113 113 1 158 158 0 158 158 1 355 355 0 355 355 1 393 393 1 + 393 393 1 399 399 0 399 399 1 480 480 1 480 480 1 618 618 1 + 618 618 1 629 629 0 629 629 1 667 667 0 667 667 1 768 768 1 + 768 768 1 839 839 1 839 839 1 870 870 1 870 870 1 870 870 2 + 938 938 1 938 938 1} + +do_execsql_test 5.2.6.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 4 3 {} 5 4 {} 6 5 {} 6 5 {} 8 6 {} 9 7 {} 10 8 + {} 11 9 {} 12 10 {} 13 11 {} 14 12 {} 15 13 {} 16 14 {} 17 15 + {} 18 16 {} 19 17 {} 20 18 {} 21 19 {} 22 20 {} 23 21 {} 24 22 + {} 25 23 {} 26 24 {} 31 27 {} 34 29 {} 35 30 {} 36 31 {} 37 32 + {} 38 33 {} 38 33 {} 40 34 {} 41 35 {} 42 36 {} 43 37 {} 43 37 + {} 45 38 {} 48 40 {} 49 41 {} 50 42 {} 51 43 {} 54 45 {} 55 46 + {} 56 47 {} 57 48 {} 58 49 {} 59 50 {} 60 51 {} 61 52 {} 62 53 + {} 63 54 {} 64 55 {} 64 55 {} 66 56 {} 67 57 {} 68 58 {} 69 59 + {} 70 60 {} 71 61 {} 72 62 {} 75 64 {} 76 65 {} 77 66 {} 78 67 + {} 78 67 {} 78 67 {} 81 68 {} 82 69 {} 83 70 {} 84 71 {} 85 72 + {} 85 72 {} 87 73 {} 88 74 {} 89 75 113 2 2 113 2 2 355 27 25 + 355 27 25 393 29 26 393 29 26 399 32 28 399 32 28 629 46 39 + 629 46 39 667 52 44 667 52 44 839 73 63 839 73 63} + +do_execsql_test 5.2.7.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {963 929 6 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 83 979 113 80 979 113 81 979 113 82 979 133 79 979 148 78 + 979 158 76 979 158 77 979 160 76 979 208 75 979 223 74 979 224 73 + 979 234 72 979 238 71 979 239 70 979 247 69 979 250 68 979 252 67 + 979 256 66 979 257 65 979 295 64 979 309 64 979 330 62 979 335 61 + 979 336 60 979 346 59 979 354 59 979 355 57 979 355 57 979 393 55 + 979 393 56 979 398 54 979 399 53 979 399 53 979 412 52 979 421 51 + 979 430 50 979 443 49 979 480 47 979 480 48 979 572 47 979 574 45 + 979 607 44 979 618 42 979 618 43 979 627 41 979 629 40 979 629 41 + 979 633 39 979 634 38 979 652 37 979 660 36 979 667 35 979 667 35 + 979 670 34 979 671 33 979 683 32 979 705 31 979 711 30 979 716 29 + 979 726 28 979 730 27 979 759 26 979 762 25 979 768 23 979 768 24 + 979 777 22 979 786 21 979 790 20 979 792 19 979 794 18 979 805 17 + 979 822 17 979 839 14 979 839 15 979 840 13 979 844 12 979 845 11 + 979 870 9 979 870 10 979 870 10 979 899 8 979 911 7} + +do_execsql_test 5.2.7.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW ) + ORDER BY 1 , 2 , 3 +} {2851 89 89 3778 88 88 4681 87 87 5556 83 83 5574 82 82 5586 81 81 + 5640 84 84 5640 85 85 5640 86 86 7324 80 80 8123 77 77 8129 73 73 + 8129 74 74 8163 78 78 8163 79 79 8940 71 71 8968 75 75 8968 76 76 + 9727 66 66 9745 69 69 9745 70 70 9745 72 72 10504 65 65 + 10504 67 67 10504 68 68 11215 64 64 11844 62 62 11920 63 63 + 13274 60 60 13274 61 61 13897 58 58 13903 57 57 13925 56 56 + 13937 55 55 13941 59 59 15203 53 53 15241 54 54 15832 52 52 + 17100 48 48 17104 46 46 17104 47 47 17106 45 45 17126 49 49 + 17126 50 50 17126 51 51 17569 42 42 17733 44 44 18176 43 43 + 18597 40 40 18597 41 41 18952 37 37 18996 39 39 19395 38 38 + 19760 35 35 19788 36 36 20492 32 32 20492 33 33 20498 30 30 + 20536 34 34 20833 29 29 20871 28 28 20891 31 31 21180 27 27 + 21752 23 23 21830 26 26 22025 21 21 22087 22 22 22087 24 24 + 22087 25 25 22278 20 20 22316 19 19 22549 15 15 22557 14 14 + 22573 17 17 22573 18 18 22706 10 10 22796 11 11 22796 12 12 + 22796 13 13 22796 16 16 23022 4 4 23042 2 2 23042 3 3 23042 9 9 + 23155 1 1 23155 5 5 23155 6 6 23155 7 7 23155 8 8} + +do_execsql_test 5.3.1.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0} + +do_execsql_test 5.3.1.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1} + +do_execsql_test 5.3.2.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 899 113 9 899 113 9 899 113 9 899 113 9 + 899 113 9 899 113 9 899 113 9 899 113 16 899 113 16 899 113 16 + 899 113 16 899 113 16 899 113 16 899 113 16 899 113 16 899 113 16 + 979 102 44 979 102 44 979 102 44 979 102 44 979 102 44 979 102 49 + 979 102 49 979 102 49 979 102 49 979 102 49 979 102 49 979 102 49 + 979 102 56 979 102 56 979 102 56 979 102 56 979 102 56 979 102 56 + 979 102 62 979 102 62 979 102 62 979 102 62 979 102 62 979 102 62 + 979 102 62 979 102 62 979 102 62 979 102 62 979 102 62 979 102 62 + 979 102 62 979 102 75 979 102 75 979 102 75 979 102 75 979 102 75 + 979 102 75 979 102 75 979 102 75 979 102 83 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 113 25 979 113 25 979 113 25 + 979 113 25 979 113 25 979 113 25 979 113 25 979 113 25 979 113 33 + 979 113 33 979 113 33 979 113 33 979 113 33 979 113 33 979 113 33 + 979 113 33 979 113 33 979 113 33 979 113 33} + +do_execsql_test 5.3.2.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} 81 11 {} 81 11 {} 81 11 {} 81 11 {} 81 11 {} 81 11 {} 81 11 + {} 81 11 {} 81 11 2947 74 10 2947 74 10 2947 74 10 2947 74 10 + 2947 74 10 2947 74 10 2947 74 10 5287 65 9 5287 65 9 5287 65 9 + 5287 65 9 5287 65 9 5287 65 9 5287 65 9 5287 65 9 5287 65 9 + 8400 57 8 8400 57 8 8400 57 8 8400 57 8 8400 57 8 8400 57 8 + 8400 57 8 8400 57 8 9664 46 7 9664 46 7 9664 46 7 9664 46 7 + 9664 46 7 9664 46 7 9664 46 7 9664 46 7 9664 46 7 9664 46 7 + 9664 46 7 10626 41 6 10626 41 6 10626 41 6 10626 41 6 10626 41 6 + 12145 34 5 12145 34 5 12145 34 5 12145 34 5 12145 34 5 12145 34 5 + 12145 34 5 13949 28 4 13949 28 4 13949 28 4 13949 28 4 13949 28 4 + 13949 28 4 15315 15 3 15315 15 3 15315 15 3 15315 15 3 15315 15 3 + 15315 15 3 15315 15 3 15315 15 3 15315 15 3 15315 15 3 15315 15 3 + 15315 15 3 15315 15 3 18796 7 2 18796 7 2 18796 7 2 18796 7 2 + 18796 7 2 18796 7 2 18796 7 2 18796 7 2 21105 1 1 21105 1 1 + 21105 1 1 21105 1 1 21105 1 1 21105 1 1} + +do_execsql_test 5.3.3.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0} + +do_execsql_test 5.3.3.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1} + +do_execsql_test 5.3.4.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 870 158 0 + 870 158 0 870 158 0 870 158 0 870 158 0 870 158 0 870 158 0 + 870 158 0 934 158 8 934 158 8 934 158 8 934 158 8 934 158 8 + 934 158 8 934 158 8 934 158 8 934 158 8 934 158 8 934 158 8 + 934 158 8 934 158 8 934 158 21 934 158 21 934 158 21 934 158 21 + 934 158 21 934 158 21 934 158 27 934 158 27 934 158 27 934 158 27 + 934 158 27 934 158 27 934 158 27 959 102 50 959 102 50 959 102 50 + 959 102 50 959 102 50 959 102 50 959 102 50 959 102 50 959 102 50 + 959 102 50 959 102 50 959 102 50 959 102 50 959 102 50 959 102 50 + 959 102 50 959 102 50 959 113 39 959 113 39 959 113 39 959 113 39 + 959 113 39 959 113 39 959 113 39 959 113 39 959 113 39 959 113 39 + 959 113 39 959 158 34 959 158 34 959 158 34 959 158 34 959 158 34 + 979 102 46 979 102 46 979 102 46 979 102 46 979 102 46 979 102 46 + 979 102 46 979 102 47 979 102 47 979 102 47 979 102 47 979 102 47 + 979 102 47 979 102 47 979 102 47 979 102 47} + +do_execsql_test 5.3.4.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 2050 7 2 2050 7 2 + 2050 7 2 2050 7 2 2050 7 2 2050 7 2 2050 7 2 2050 7 2 4359 15 3 + 4359 15 3 4359 15 3 4359 15 3 4359 15 3 4359 15 3 4359 15 3 + 4359 15 3 4359 15 3 4359 15 3 4359 15 3 4359 15 3 4359 15 3 + 7840 28 4 7840 28 4 7840 28 4 7840 28 4 7840 28 4 7840 28 4 + 9206 34 5 9206 34 5 9206 34 5 9206 34 5 9206 34 5 9206 34 5 + 9206 34 5 10028 74 10 10028 74 10 10028 74 10 10028 74 10 + 10028 74 10 10028 74 10 10028 74 10 10396 65 9 10396 65 9 + 10396 65 9 10396 65 9 10396 65 9 10396 65 9 10396 65 9 10396 65 9 + 10396 65 9 11002 81 11 11002 81 11 11002 81 11 11002 81 11 + 11002 81 11 11002 81 11 11002 81 11 11002 81 11 11002 81 11 + 11010 41 6 11010 41 6 11010 41 6 11010 41 6 11010 41 6 11441 57 8 + 11441 57 8 11441 57 8 11441 57 8 11441 57 8 11441 57 8 11441 57 8 + 11441 57 8 12529 46 7 12529 46 7 12529 46 7 12529 46 7 12529 46 7 + 12529 46 7 12529 46 7 12529 46 7 12529 46 7 12529 46 7 12529 46 7} + +do_execsql_test 5.3.5.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 158 158 1 160 160 1 160 160 1 223 223 1 224 224 1 + 238 234 2 239 234 2 239 238 2 252 250 2 256 252 2 257 247 4 + 257 247 4 257 250 3 335 330 2 336 330 2 336 335 2 354 354 1 + 354 354 1 355 355 1 398 393 3 398 393 3 399 393 3 399 398 2 + 399 398 2 572 572 1 574 574 1 633 629 2 634 627 3 634 627 3 + 634 627 3 634 629 3 667 667 1 670 667 2 671 667 2 671 670 2 + 671 670 2 711 711 1 711 711 1 716 705 2 726 726 1 730 730 1 + 762 762 1 762 762 1 762 762 1 768 759 3 792 790 2 792 790 2 + 794 786 3 794 786 3 844 839 4 845 839 4 845 839 4 845 840 3 + 845 840 3 934 934 1 934 934 1 934 934 1 938 929 3 959 959 1 + 963 963 1} + +do_execsql_test 5.3.5.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 2 2 {} 2 2 {} 4 3 {} 5 4 {} 6 5 {} 6 5 {} 8 6 + {} 9 7 {} 10 8 {} 14 12 {} 15 13 {} 19 17 {} 20 18 {} 21 19 + {} 23 21 {} 25 23 {} 27 25 {} 27 25 {} 34 29 {} 35 30 {} 36 31 + {} 37 32 {} 38 33 {} 38 33 {} 40 34 {} 41 35 {} 42 36 {} 43 37 + {} 43 37 {} 50 42 {} 56 47 {} 60 51 {} 61 52 {} 62 53 {} 64 55 + {} 64 55 {} 66 56 {} 67 57 {} 68 58 {} 69 59 {} 70 60 {} 71 61 + {} 72 62 {} 78 67 {} 78 67 {} 78 67 {} 81 68 {} 82 69 {} 83 70 + {} 85 72 {} 85 72 {} 89 75 223 11 9 239 12 10 239 13 11 + 257 18 16 335 22 20 335 24 22 504 16 14 504 17 15 671 52 44 + 671 52 44 705 58 49 710 26 24 711 57 48 711 59 50 759 63 54 + 786 32 28 786 32 28 798 29 26 798 29 26 845 73 63 845 73 63 + 929 84 71 959 88 74 963 87 73 1260 46 39 1260 46 39 1334 51 43 + 1334 55 46 1584 31 27 1678 77 66 1885 48 40 1891 45 38 1891 49 41 + 2005 54 45 2523 75 64 2523 76 65} + +do_execsql_test 5.3.6.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0 + {} {} 0 {} {} 0 {} {} 0 {} {} 0 {} {} 0} + +do_execsql_test 5.3.6.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 2 2 {} 2 2 {} 4 3 {} 5 4 {} 6 5 {} 6 5 {} 8 6 + {} 9 7 {} 10 8 {} 11 9 {} 12 10 {} 13 11 {} 14 12 {} 15 13 + {} 16 14 {} 17 15 {} 18 16 {} 19 17 {} 20 18 {} 21 19 {} 22 20 + {} 23 21 {} 24 22 {} 25 23 {} 26 24 {} 27 25 {} 27 25 {} 29 26 + {} 29 26 {} 31 27 {} 32 28 {} 32 28 {} 34 29 {} 35 30 {} 36 31 + {} 37 32 {} 38 33 {} 38 33 {} 40 34 {} 41 35 {} 42 36 {} 43 37 + {} 43 37 {} 45 38 {} 46 39 {} 46 39 {} 48 40 {} 49 41 {} 50 42 + {} 51 43 {} 52 44 {} 52 44 {} 54 45 {} 55 46 {} 56 47 {} 57 48 + {} 58 49 {} 59 50 {} 60 51 {} 61 52 {} 62 53 {} 63 54 {} 64 55 + {} 64 55 {} 66 56 {} 67 57 {} 68 58 {} 69 59 {} 70 60 {} 71 61 + {} 72 62 {} 73 63 {} 73 63 {} 75 64 {} 76 65 {} 77 66 {} 78 67 + {} 78 67 {} 78 67 {} 81 68 {} 82 69 {} 83 70 {} 84 71 {} 85 72 + {} 85 72 {} 87 73 {} 88 74 {} 89 75} + +do_execsql_test 5.3.7.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {963 929 6 979 102 82 979 102 82 979 102 82 979 102 82 979 102 82 + 979 102 83 979 113 80 979 113 81 979 113 82 979 133 79 979 148 78 + 979 158 76 979 158 77 979 160 76 979 208 75 979 223 74 979 224 73 + 979 234 72 979 238 71 979 239 70 979 247 69 979 250 68 979 252 67 + 979 256 66 979 257 65 979 295 64 979 309 64 979 330 62 979 335 61 + 979 336 60 979 346 59 979 354 59 979 355 57 979 355 57 979 393 55 + 979 393 56 979 398 54 979 399 53 979 399 53 979 412 52 979 421 51 + 979 430 50 979 443 49 979 480 47 979 480 48 979 572 47 979 574 45 + 979 607 44 979 618 42 979 618 43 979 627 41 979 629 40 979 629 41 + 979 633 39 979 634 38 979 652 37 979 660 36 979 667 35 979 667 35 + 979 670 34 979 671 33 979 683 32 979 705 31 979 711 30 979 716 29 + 979 726 28 979 730 27 979 759 26 979 762 25 979 768 23 979 768 24 + 979 777 22 979 786 21 979 790 20 979 792 19 979 794 18 979 805 17 + 979 822 17 979 839 14 979 839 15 979 840 13 979 844 12 979 845 11 + 979 870 9 979 870 10 979 870 10 979 899 8 979 911 7} + +do_execsql_test 5.3.7.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE GROUP ) + ORDER BY 1 , 2 , 3 +} {2851 89 89 3778 88 88 4681 87 87 5556 83 83 5574 82 82 5586 81 81 + 5640 84 84 5640 85 85 5640 86 86 7324 80 80 8123 77 77 8129 73 73 + 8129 74 74 8163 78 78 8163 79 79 8940 71 71 8968 75 75 8968 76 76 + 9727 66 66 9745 69 69 9745 70 70 9745 72 72 10504 65 65 + 10504 67 67 10504 68 68 11215 64 64 11844 62 62 11920 63 63 + 13274 60 60 13274 61 61 13897 58 58 13903 57 57 13925 56 56 + 13937 55 55 13941 59 59 15203 53 53 15241 54 54 15832 52 52 + 17100 48 48 17104 46 46 17104 47 47 17106 45 45 17126 49 49 + 17126 50 50 17126 51 51 17569 42 42 17733 44 44 18176 43 43 + 18597 40 40 18597 41 41 18952 37 37 18996 39 39 19395 38 38 + 19760 35 35 19788 36 36 20492 32 32 20492 33 33 20498 30 30 + 20536 34 34 20833 29 29 20871 28 28 20891 31 31 21180 27 27 + 21752 23 23 21830 26 26 22025 21 21 22087 22 22 22087 24 24 + 22087 25 25 22278 20 20 22316 19 19 22549 15 15 22557 14 14 + 22573 17 17 22573 18 18 22706 10 10 22796 11 11 22796 12 12 + 22796 13 13 22796 16 16 23022 4 4 23042 2 2 23042 3 3 23042 9 9 + 23155 1 1 23155 5 5 23155 6 6 23155 7 7 23155 8 8} + +do_execsql_test 5.4.1.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {102 102 1 113 113 1 113 113 1 133 133 1 148 148 1 158 158 0 + 158 158 1 160 160 1 208 208 1 223 223 1 224 224 1 234 234 1 + 238 238 1 239 239 1 247 247 1 250 250 1 252 252 1 256 256 1 + 257 257 1 295 295 1 309 309 1 330 330 1 335 335 1 336 336 1 + 346 346 1 354 354 1 355 355 0 355 355 1 393 393 1 393 393 1 + 398 398 1 399 399 0 399 399 1 412 412 1 421 421 1 430 430 1 + 443 443 1 480 480 1 480 480 1 572 572 1 574 574 1 607 607 1 + 618 618 1 618 618 1 627 627 1 629 629 0 629 629 1 633 633 1 + 634 634 1 652 652 1 660 660 1 667 667 0 667 667 1 670 670 1 + 671 671 1 683 683 1 705 705 1 711 711 1 716 716 1 726 726 1 + 730 730 1 759 759 1 762 762 1 768 768 1 768 768 1 777 777 1 + 786 786 1 790 790 1 792 792 1 794 794 1 805 805 1 822 822 1 + 839 839 1 839 839 1 840 840 1 844 844 1 845 845 1 870 870 0 + 870 870 1 870 870 1 899 899 1 911 911 1 929 929 1 934 934 1 + 938 938 1 938 938 1 959 959 1 963 963 1 979 979 1} + +do_execsql_test 5.4.1.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + 113 1 1 113 1 1 133 1 1 223 1 1 239 1 1 247 1 1 257 1 1 + 295 1 1 309 1 1 335 1 1 355 1 1 355 1 1 393 1 1 393 1 1 + 399 1 1 399 1 1 421 1 1 443 1 1 607 1 1 627 1 1 629 1 1 + 629 1 1 633 1 1 667 1 1 667 1 1 671 1 1 683 1 1 705 1 1 + 711 1 1 759 1 1 777 1 1 805 1 1 839 1 1 839 1 1 845 1 1 + 899 1 1 911 1 1 929 1 1 959 1 1 963 1 1 979 1 1} + +do_execsql_test 5.4.2.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {113 113 1 234 234 1 257 257 1 336 336 1 354 354 1 768 768 1 + 839 839 1 839 839 1 899 113 10 899 113 10 899 113 10 899 113 10 + 899 113 10 899 113 10 899 113 10 899 113 17 899 113 17 899 113 17 + 899 113 17 899 113 17 899 113 17 899 113 17 899 899 1 963 113 17 + 979 102 34 979 102 45 979 102 45 979 102 45 979 102 45 979 102 45 + 979 102 50 979 102 50 979 102 50 979 102 50 979 102 50 979 102 50 + 979 102 50 979 102 57 979 102 57 979 102 57 979 102 57 979 102 57 + 979 102 57 979 102 63 979 102 63 979 102 63 979 102 63 979 102 63 + 979 102 63 979 102 63 979 102 63 979 102 63 979 102 63 979 102 63 + 979 102 63 979 102 63 979 102 76 979 102 76 979 102 76 979 102 76 + 979 102 76 979 102 76 979 102 76 979 102 76 979 102 83 979 102 83 + 979 102 83 979 102 83 979 102 83 979 102 83 979 113 17 979 113 26 + 979 113 26 979 113 26 979 113 26 979 113 26 979 113 26 979 113 26 + 979 113 26 979 113 34 979 113 34 979 113 34 979 113 34 979 113 34 + 979 113 34 979 113 34 979 113 34 979 113 34 979 113 34} + +do_execsql_test 5.4.2.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {{} 81 11 {} 81 11 {} 81 11 {} 81 11 113 81 11 257 81 11 + 839 81 11 839 81 11 899 81 11 2947 74 10 2947 74 10 2947 74 10 + 3368 74 10 3390 74 10 3618 74 10 3752 74 10 5287 65 9 5287 65 9 + 5287 65 9 5287 65 9 5420 65 9 5642 65 9 5970 65 9 6250 65 9 + 6266 65 9 8400 57 8 8400 57 8 8400 57 8 8400 57 8 8400 57 8 + 8400 57 8 8735 57 8 9329 57 8 9664 46 7 9664 46 7 9664 46 7 + 9664 46 7 9664 46 7 9664 46 7 9664 46 7 9664 46 7 9664 46 7 + 9959 46 7 10331 46 7 10626 41 6 10626 41 6 10739 41 6 11255 41 6 + 11403 41 6 12145 34 5 12145 34 5 12145 34 5 12145 34 5 12145 34 5 + 12990 34 5 13104 34 5 13949 28 4 13949 28 4 13949 28 4 13949 28 4 + 14556 28 4 14708 28 4 15315 15 3 15315 15 3 15315 15 3 15315 15 3 + 15315 15 3 15315 15 3 15562 15 3 15708 15 3 15708 15 3 15714 15 3 + 15948 15 3 16020 15 3 16026 15 3 18796 7 2 18796 7 2 18796 7 2 + 19019 7 2 19035 7 2 19105 7 2 19423 7 2 19707 7 2 21105 1 1 + 21105 1 1 21460 1 1 21504 1 1 21734 1 1 21772 1 1} + +do_execsql_test 5.4.3.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {102 102 1 113 113 1 113 113 1 133 133 1 148 148 1 158 158 0 + 158 158 1 160 160 1 208 208 1 223 223 1 224 224 1 234 234 1 + 238 238 1 239 239 1 247 247 1 250 250 1 252 252 1 256 256 1 + 257 257 1 295 295 1 309 309 1 330 330 1 335 335 1 336 336 1 + 346 346 1 354 354 1 355 355 0 355 355 1 393 393 1 393 393 1 + 398 398 1 399 399 0 399 399 1 412 412 1 421 421 1 430 430 1 + 443 443 1 480 480 1 480 480 1 572 572 1 574 574 1 607 607 1 + 618 618 1 618 618 1 627 627 1 629 629 0 629 629 1 633 633 1 + 634 634 1 652 652 1 660 660 1 667 667 0 667 667 1 670 670 1 + 671 671 1 683 683 1 705 705 1 711 711 1 716 716 1 726 726 1 + 730 730 1 759 759 1 762 762 1 768 768 1 768 768 1 777 777 1 + 786 786 1 790 790 1 792 792 1 794 794 1 805 805 1 822 822 1 + 839 839 1 839 839 1 840 840 1 844 844 1 845 845 1 870 870 0 + 870 870 1 870 870 1 899 899 1 911 911 1 929 929 1 934 934 1 + 938 938 1 938 938 1 959 959 1 963 963 1 979 979 1} + +do_execsql_test 5.4.3.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( PARTITION BY coalesce(a, '') + RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 {} 1 1 + 113 1 1 113 1 1 133 1 1 223 1 1 239 1 1 247 1 1 257 1 1 + 295 1 1 309 1 1 335 1 1 355 1 1 355 1 1 393 1 1 393 1 1 + 399 1 1 399 1 1 421 1 1 443 1 1 607 1 1 627 1 1 629 1 1 + 629 1 1 633 1 1 667 1 1 667 1 1 671 1 1 683 1 1 705 1 1 + 711 1 1 759 1 1 777 1 1 805 1 1 839 1 1 839 1 1 845 1 1 + 899 1 1 911 1 1 929 1 1 959 1 1 963 1 1 979 1 1} + +do_execsql_test 5.4.4.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {158 158 0 355 355 0 399 399 0 629 629 0 667 667 0 870 158 1 + 870 158 1 870 158 1 870 158 1 870 158 1 870 158 1 870 870 0 + 911 158 1 934 158 1 934 158 9 934 158 9 934 158 9 934 158 9 + 934 158 9 934 158 9 934 158 9 934 158 9 934 158 9 934 158 9 + 934 158 9 934 158 9 934 158 9 934 158 22 934 158 22 934 158 22 + 934 158 22 934 158 22 934 158 22 934 158 28 934 158 28 934 158 28 + 934 158 28 934 158 28 934 158 28 959 102 40 959 102 51 959 102 51 + 959 102 51 959 102 51 959 102 51 959 102 51 959 102 51 959 102 51 + 959 102 51 959 102 51 959 102 51 959 102 51 959 102 51 959 102 51 + 959 102 51 959 113 35 959 113 40 959 113 40 959 113 40 959 113 40 + 959 113 40 959 113 40 959 113 40 959 113 40 959 113 40 959 113 40 + 959 158 28 959 158 35 959 158 35 959 158 35 959 158 35 963 102 51 + 979 102 47 979 102 47 979 102 47 979 102 47 979 102 47 979 102 47 + 979 102 47 979 102 48 979 102 48 979 102 48 979 102 48 979 102 48 + 979 102 48 979 102 48 979 102 48 979 102 48 979 102 51} + +do_execsql_test 5.4.4.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY a GROUPS 6 PRECEDING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 1 1 355 1 1 399 1 1 629 1 1 667 1 1 2050 7 2 + 2050 7 2 2050 7 2 2273 7 2 2289 7 2 2359 7 2 2677 7 2 2961 7 2 + 4359 15 3 4359 15 3 4359 15 3 4359 15 3 4359 15 3 4359 15 3 + 4606 15 3 4752 15 3 4752 15 3 4758 15 3 4992 15 3 5064 15 3 + 5070 15 3 7840 28 4 7840 28 4 7840 28 4 7840 28 4 8447 28 4 + 8599 28 4 9206 34 5 9206 34 5 9206 34 5 9206 34 5 9206 34 5 + 10028 74 10 10028 74 10 10028 74 10 10051 34 5 10165 34 5 + 10396 65 9 10396 65 9 10396 65 9 10396 65 9 10449 74 10 + 10471 74 10 10529 65 9 10699 74 10 10751 65 9 10833 74 10 + 11002 81 11 11002 81 11 11002 81 11 11002 81 11 11010 41 6 + 11010 41 6 11079 65 9 11115 81 11 11123 41 6 11259 81 11 + 11359 65 9 11375 65 9 11441 57 8 11441 57 8 11441 57 8 11441 57 8 + 11441 57 8 11441 57 8 11639 41 6 11776 57 8 11787 41 6 + 11841 81 11 11841 81 11 11901 81 11 12370 57 8 12529 46 7 + 12529 46 7 12529 46 7 12529 46 7 12529 46 7 12529 46 7 12529 46 7 + 12529 46 7 12529 46 7 12824 46 7 13196 46 7} + +do_execsql_test 5.4.5.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {102 102 1 113 113 1 113 113 1 133 133 1 148 148 1 160 158 1 + 160 158 2 160 158 2 208 208 1 224 223 2 224 223 2 239 234 3 + 239 234 3 239 234 3 252 247 3 257 247 5 257 247 5 257 250 4 + 257 252 3 295 295 1 309 309 1 336 330 3 336 330 3 336 330 3 + 346 346 1 355 354 1 355 354 2 355 354 2 399 393 3 399 393 3 + 399 393 3 399 393 4 399 393 4 412 412 1 421 421 1 430 430 1 + 443 443 1 480 480 1 480 480 1 574 572 2 574 572 2 607 607 1 + 618 618 1 618 618 1 634 627 3 634 627 4 634 627 4 634 627 4 + 634 629 3 652 652 1 667 660 2 671 667 2 671 667 3 671 667 3 + 671 667 3 683 683 1 711 705 2 716 705 3 716 711 2 730 726 2 + 730 726 2 762 759 2 768 759 4 768 762 2 768 762 2 777 777 1 + 792 786 3 794 786 4 794 786 4 794 790 3 805 805 1 822 822 1 + 845 839 4 845 839 4 845 839 5 845 839 5 845 839 5 870 870 0 + 870 870 1 870 870 1 899 899 1 911 911 1 934 929 2 938 929 4 + 938 934 2 938 934 2 963 959 2 963 959 2 979 979 1} + +do_execsql_test 5.4.5.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 6 PRECEDING AND 7 FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 5 4 {} 6 5 {} 6 5 {} 8 6 {} 9 7 {} 25 23 {} 34 29 + {} 36 31 {} 38 33 {} 38 33 {} 40 34 {} 41 35 {} 43 37 {} 43 37 + {} 50 42 {} 60 51 {} 61 52 {} 64 55 {} 64 55 {} 67 57 {} 68 58 + {} 69 59 {} 70 60 {} 72 62 {} 78 67 {} 78 67 {} 78 67 {} 85 72 + {} 85 72 113 2 2 113 2 2 133 4 3 223 10 8 223 11 9 239 12 10 + 239 13 11 239 14 12 247 15 13 257 18 16 257 19 17 295 20 18 + 309 21 19 335 22 20 335 23 21 335 24 22 355 27 25 355 27 25 + 421 35 30 443 37 32 504 16 14 504 17 15 607 42 36 683 56 47 + 710 26 24 711 59 50 759 62 53 759 63 54 777 66 56 805 71 61 + 899 81 68 911 82 69 929 83 70 929 84 71 979 89 75 1185 32 28 + 1185 32 28 1191 29 26 1191 29 26 1334 51 43 1338 52 44 1338 52 44 + 1416 57 48 1416 58 49 1584 31 27 1684 73 63 1684 73 63 1889 46 39 + 1889 46 39 1891 49 41 1922 87 73 1922 88 74 2005 54 45 2005 55 46 + 2518 45 38 2518 48 40 2523 75 64 2523 76 65 2523 77 66} + +do_execsql_test 5.4.6.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {102 102 1 113 113 1 113 113 1 133 133 1 148 148 1 158 158 0 + 158 158 1 160 160 1 208 208 1 223 223 1 224 224 1 234 234 1 + 238 238 1 239 239 1 247 247 1 250 250 1 252 252 1 256 256 1 + 257 257 1 295 295 1 309 309 1 330 330 1 335 335 1 336 336 1 + 346 346 1 354 354 1 355 355 0 355 355 1 393 393 1 393 393 1 + 398 398 1 399 399 0 399 399 1 412 412 1 421 421 1 430 430 1 + 443 443 1 480 480 1 480 480 1 572 572 1 574 574 1 607 607 1 + 618 618 1 618 618 1 627 627 1 629 629 0 629 629 1 633 633 1 + 634 634 1 652 652 1 660 660 1 667 667 0 667 667 1 670 670 1 + 671 671 1 683 683 1 705 705 1 711 711 1 716 716 1 726 726 1 + 730 730 1 759 759 1 762 762 1 768 768 1 768 768 1 777 777 1 + 786 786 1 790 790 1 792 792 1 794 794 1 805 805 1 822 822 1 + 839 839 1 839 839 1 840 840 1 844 844 1 845 845 1 870 870 0 + 870 870 1 870 870 1 899 899 1 911 911 1 929 929 1 934 934 1 + 938 938 1 938 938 1 959 959 1 963 963 1 979 979 1} + +do_execsql_test 5.4.6.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c RANGE BETWEEN 0 PRECEDING AND 0 FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {{} 1 1 {} 5 4 {} 6 5 {} 6 5 {} 8 6 {} 9 7 {} 11 9 {} 12 10 + {} 13 11 {} 16 14 {} 17 15 {} 18 16 {} 22 20 {} 24 22 {} 25 23 + {} 26 24 {} 31 27 {} 34 29 {} 36 31 {} 38 33 {} 38 33 {} 40 34 + {} 41 35 {} 43 37 {} 43 37 {} 49 41 {} 50 42 {} 51 43 {} 54 45 + {} 59 50 {} 60 51 {} 61 52 {} 63 54 {} 64 55 {} 64 55 {} 67 57 + {} 68 58 {} 69 59 {} 70 60 {} 72 62 {} 75 64 {} 76 65 {} 78 67 + {} 78 67 {} 78 67 {} 84 71 {} 85 72 {} 85 72 113 2 2 113 2 2 + 133 4 3 223 10 8 239 14 12 247 15 13 257 19 17 295 20 18 + 309 21 19 335 23 21 355 27 25 355 27 25 393 29 26 393 29 26 + 399 32 28 399 32 28 421 35 30 443 37 32 607 42 36 627 45 38 + 629 46 39 629 46 39 633 48 40 667 52 44 667 52 44 671 55 46 + 683 56 47 705 57 48 711 58 49 759 62 53 777 66 56 805 71 61 + 839 73 63 839 73 63 845 77 66 899 81 68 911 82 69 929 83 70 + 959 87 73 963 88 74 979 89 75} + +do_execsql_test 5.4.7.1 { + SELECT max(c) OVER win, + min(c) OVER win, + count(a) OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 979 102 83 + 979 102 83 979 113 81 979 113 82 979 133 80 979 148 79 979 158 77 + 979 158 78 979 160 77 979 208 76 979 223 75 979 224 74 979 234 73 + 979 238 72 979 239 71 979 247 70 979 250 69 979 252 68 979 256 67 + 979 257 66 979 295 65 979 309 64 979 330 63 979 335 62 979 336 61 + 979 346 60 979 354 59 979 355 58 979 355 58 979 393 56 979 393 57 + 979 398 55 979 399 54 979 399 54 979 412 53 979 421 52 979 430 51 + 979 443 50 979 480 48 979 480 49 979 572 47 979 574 46 979 607 45 + 979 618 43 979 618 44 979 627 42 979 629 41 979 629 41 979 633 40 + 979 634 39 979 652 38 979 660 37 979 667 36 979 667 36 979 670 35 + 979 671 34 979 683 33 979 705 32 979 711 31 979 716 30 979 726 29 + 979 730 28 979 759 27 979 762 26 979 768 24 979 768 25 979 777 23 + 979 786 22 979 790 21 979 792 20 979 794 19 979 805 18 979 822 17 + 979 839 15 979 839 16 979 840 14 979 844 13 979 845 12 979 870 10 + 979 870 11 979 870 11 979 899 9 979 911 8 979 929 7} + +do_execsql_test 5.4.7.2 { + SELECT sum(c) FILTER (WHERE (c%2)!=0) OVER win, + rank() OVER win, + dense_rank() OVER win + FROM t3 + WINDOW win AS ( ORDER BY c , b , a + ROWS BETWEEN 6 PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE TIES ) + ORDER BY 1 , 2 , 3 +} {3830 89 89 4741 88 88 5640 84 84 5640 85 85 5640 86 86 5640 87 87 + 6485 81 81 6485 82 82 6485 83 83 7324 80 80 8163 78 78 8163 79 79 + 8968 73 73 8968 74 74 8968 75 75 8968 76 76 8968 77 77 9745 69 69 + 9745 70 70 9745 71 71 9745 72 72 10504 65 65 10504 66 66 + 10504 67 67 10504 68 68 11215 64 64 11920 63 63 12603 62 62 + 13274 60 60 13274 61 61 13941 59 59 14608 55 55 14608 56 56 + 14608 57 57 14608 58 58 15241 54 54 15870 53 53 16499 52 52 + 17126 49 49 17126 50 50 17126 51 51 17733 44 44 17733 45 45 + 17733 46 46 17733 47 47 17733 48 48 18176 42 42 18176 43 43 + 18597 40 40 18597 41 41 18996 39 39 19395 37 37 19395 38 38 + 19788 36 36 20181 35 35 20536 34 34 20891 30 30 20891 31 31 + 20891 32 32 20891 33 33 21226 28 28 21226 29 29 21535 27 27 + 21830 26 26 22087 22 22 22087 23 23 22087 24 24 22087 25 25 + 22334 21 21 22573 17 17 22573 18 18 22573 19 19 22573 20 20 + 22796 11 11 22796 12 12 22796 13 13 22796 14 14 22796 15 15 + 22796 16 16 22929 10 10 23042 9 9 23155 1 1 23155 2 2 23155 3 3 + 23155 4 4 23155 5 5 23155 6 6 23155 7 7 23155 8 8} + +#========================================================================== + +do_execsql_test 6.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a TEXT, b INTEGER); + INSERT INTO t2 VALUES('A', NULL); + INSERT INTO t2 VALUES('B', NULL); + INSERT INTO t2 VALUES('C', 1); +} {} + +do_execsql_test 6.1 { + SELECT group_concat(a, '.') OVER ( + ORDER BY b RANGE BETWEEN 7 PRECEDING AND 2 PRECEDING + ) + FROM t2 +} {A.B A.B {}} + +do_execsql_test 6.2 { + SELECT group_concat(a, '.') OVER ( + ORDER BY b DESC RANGE BETWEEN 7 PRECEDING AND 2 PRECEDING + ) + FROM t2 +} {{} A.B A.B} + +finish_test ADDED test/windowerr.tcl Index: test/windowerr.tcl ================================================================== --- /dev/null +++ test/windowerr.tcl @@ -0,0 +1,69 @@ +# 2018 May 19 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# + +source [file join [file dirname $argv0] pg_common.tcl] + +#========================================================================= + +start_test windowerr "2019 March 01" +ifcapable !windowfunc + +execsql_test 1.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER, b INTEGER); + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t1 VALUES(2, 2); + INSERT INTO t1 VALUES(3, 3); + INSERT INTO t1 VALUES(4, 4); + INSERT INTO t1 VALUES(5, 5); +} + +foreach {tn frame} { + 1 "ORDER BY a ROWS BETWEEN -1 PRECEDING AND 1 FOLLOWING" + 2 "ORDER BY a ROWS BETWEEN 1 PRECEDING AND -1 FOLLOWING" + + 3 "ORDER BY a RANGE BETWEEN -1 PRECEDING AND 1 FOLLOWING" + 4 "ORDER BY a RANGE BETWEEN 1 PRECEDING AND -1 FOLLOWING" + + 5 "ORDER BY a GROUPS BETWEEN -1 PRECEDING AND 1 FOLLOWING" + 6 "ORDER BY a GROUPS BETWEEN 1 PRECEDING AND -1 FOLLOWING" + + 7 "ORDER BY a,b RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING" + + 8 "PARTITION BY a RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING" +} { + errorsql_test 1.$tn " + SELECT a, sum(b) OVER ( + $frame + ) FROM t1 ORDER BY 1 + " +} +errorsql_test 2.1 { + SELECT sum( sum(a) OVER () ) FROM t1; +} + +errorsql_test 2.2 { + SELECT sum(a) OVER () AS xyz FROM t1 ORDER BY sum(xyz); +} + +errorsql_test 3.0 { + SELECT sum(a) OVER win FROM t1 + WINDOW win AS (ROWS BETWEEN 'hello' PRECEDING AND 10 FOLLOWING) +} +errorsql_test 3.2 { + SELECT sum(a) OVER win FROM t1 + WINDOW win AS (ROWS BETWEEN 10 PRECEDING AND x'ABCD' FOLLOWING) +} + + +finish_test + ADDED test/windowerr.test Index: test/windowerr.test ================================================================== --- /dev/null +++ test/windowerr.test @@ -0,0 +1,111 @@ +# 2019 March 01 +# +# The author disclaims copyright to this source code. In place of +# a legal notice, here is a blessing: +# +# May you do good and not evil. +# May you find forgiveness for yourself and forgive others. +# May you share freely, never taking more than you give. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. +# + +#################################################### +# DO NOT EDIT! THIS FILE IS AUTOMATICALLY GENERATED! +#################################################### + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +set testprefix windowerr + +ifcapable !windowfunc { finish_test ; return } +do_execsql_test 1.0 { + DROP TABLE IF EXISTS t1; + CREATE TABLE t1(a INTEGER, b INTEGER); + INSERT INTO t1 VALUES(1, 1); + INSERT INTO t1 VALUES(2, 2); + INSERT INTO t1 VALUES(3, 3); + INSERT INTO t1 VALUES(4, 4); + INSERT INTO t1 VALUES(5, 5); +} {} + +# PG says ERROR: frame starting offset must not be negative +do_test 1.1 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a ROWS BETWEEN -1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: frame ending offset must not be negative +do_test 1.2 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a ROWS BETWEEN 1 PRECEDING AND -1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: invalid preceding or following size in window function +do_test 1.3 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN -1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: invalid preceding or following size in window function +do_test 1.4 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a RANGE BETWEEN 1 PRECEDING AND -1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: frame starting offset must not be negative +do_test 1.5 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN -1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: frame ending offset must not be negative +do_test 1.6 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a GROUPS BETWEEN 1 PRECEDING AND -1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: RANGE with offset PRECEDING/FOLLOWING requires exactly one ORDER BY column +do_test 1.7 { catch { execsql { + SELECT a, sum(b) OVER ( + ORDER BY a,b RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: RANGE with offset PRECEDING/FOLLOWING requires exactly one ORDER BY column +do_test 1.8 { catch { execsql { + SELECT a, sum(b) OVER ( + PARTITION BY a RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING + ) FROM t1 ORDER BY 1 +} } } 1 + +# PG says ERROR: aggregate function calls cannot contain window function calls +do_test 2.1 { catch { execsql { + SELECT sum( sum(a) OVER () ) FROM t1; +} } } 1 + +# PG says ERROR: column "xyz" does not exist +do_test 2.2 { catch { execsql { + SELECT sum(a) OVER () AS xyz FROM t1 ORDER BY sum(xyz); +} } } 1 + +# PG says ERROR: invalid input syntax for integer: "hello" +do_test 3.0 { catch { execsql { + SELECT sum(a) OVER win FROM t1 + WINDOW win AS (ROWS BETWEEN 'hello' PRECEDING AND 10 FOLLOWING) +} } } 1 + +# PG says ERROR: argument of ROWS must be type bigint, not type bit +do_test 3.2 { catch { execsql { + SELECT sum(a) OVER win FROM t1 + WINDOW win AS (ROWS BETWEEN 10 PRECEDING AND x'ABCD' FOLLOWING) +} } } 1 + +finish_test Index: test/windowfault.test ================================================================== --- test/windowfault.test +++ test/windowfault.test @@ -26,11 +26,11 @@ INSERT INTO t1 VALUES(5, 6, 7, 8); INSERT INTO t1 VALUES(9, 10, 11, 12); } faultsim_save_and_close -do_faultsim_test 1 -start 1 -faults oom-* -prep { +do_faultsim_test 1 -start 1 -faults oom-t* -prep { faultsim_restore_and_reopen } -body { execsql { SELECT row_number() OVER win, rank() OVER win, @@ -159,7 +159,68 @@ ORDER BY a; } } -test { faultsim_test_result {0 {1 2 5 6 9 10}} } + +#------------------------------------------------------------------------- +# The following test causes a cursor in REQURESEEK state to be passed +# to sqlite3BtreeDelete(). An error is simulated within the seek operation +# to restore the cursors position. +# +reset_db +set big [string repeat x 900] +do_execsql_test 9.0 { + PRAGMA page_size = 512; + PRAGMA cache_size = 2; + CREATE TABLE t(x INTEGER PRIMARY KEY, y TEXT); + WITH s(i) AS ( + VALUES(1) UNION ALL SELECT i+1 FROM s WHERE i<1900 + ) + INSERT INTO t(y) SELECT $big FROM s; +} +db close + +testvfs tvfs -default 1 +tvfs script vfs_callback +tvfs filter xRead + +sqlite3 db test.db +proc vfs_callback {method file args} { + if {$file=="" && [info exists ::tmp_read_fail]} { + incr ::tmp_read_fail -1 + if {$::tmp_read_fail<=0} { + return "SQLITE_IOERR" + } + } + return "SQLITE_OK" +} + +set FAULTSIM(tmpread) [list \ + -injectstart tmpread_injectstart \ + -injectstop tmpread_injectstop \ + -injecterrlist {{1 {disk I/O error}}} \ +] +proc tmpread_injectstart {iFail} { + set ::tmp_read_fail $iFail +} +proc tmpread_injectstop {} { + set ret [expr $::tmp_read_fail<=0] + unset -nocomplain ::tmp_read_fail + return $ret +} + +do_faultsim_test 9 -end 25 -faults tmpread -body { + execsql { + SELECT sum(y) OVER win FROM t + WINDOW win AS ( + ORDER BY x ROWS BETWEEN UNBOUNDED PRECEDING AND 1800 FOLLOWING + ) + } +} -test { + faultsim_test_result {0 {}} +} + +catch {db close} +tvfs delete finish_test Index: test/without_rowid1.test ================================================================== --- test/without_rowid1.test +++ test/without_rowid1.test @@ -352,8 +352,44 @@ } do_execsql_test 10.1 { DELETE FROM t2 WHERE b=1 } + +#------------------------------------------------------------------------- +# UNIQUE constraint violation in an UPDATE with a multi-column PK. +# +reset_db +do_execsql_test 10.0 { + CREATE TABLE t1(a, b, c UNIQUE, PRIMARY KEY(a, b)) WITHOUT ROWID; + INSERT INTO t1 VALUES('a', 'a', 1); + INSERT INTO t1 VALUES('a', 'b', 2); + INSERT INTO t1 VALUES('b', 'a', 3); + INSERT INTO t1 VALUES('b', 'b', 4); +} + +do_catchsql_test 10.1 { + UPDATE t1 SET c=1 WHERE (a, b) = ('a', 'a'); +} {0 {}} +do_catchsql_test 10.2 { + UPDATE t1 SET c=1 WHERE (a, b) = ('a', 'b'); +} {1 {UNIQUE constraint failed: t1.c}} +do_catchsql_test 10.3 { + UPDATE t1 SET c=1 WHERE (a, b) = ('b', 'a'); +} {1 {UNIQUE constraint failed: t1.c}} +do_catchsql_test 10.4 { + UPDATE t1 SET c=1 WHERE (a, b) = ('b', 'b'); +} {1 {UNIQUE constraint failed: t1.c}} +do_catchsql_test 10.5 { + UPDATE t1 SET c=1 WHERE (a, b) = ('c', 'c'); +} {0 {}} + +do_execsql_test 10.6 { + CREATE TRIGGER t1_tr BEFORE UPDATE ON t1 BEGIN + DELETE FROM t1 WHERE a = new.a; + END; + UPDATE t1 SET c = c+1 WHERE a = 'a'; + SELECT * FROM t1; +} {b a 3 b b 4} finish_test DELETED tool/addopcodes.tcl Index: tool/addopcodes.tcl ================================================================== --- tool/addopcodes.tcl +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/tclsh -# -# This script appends additional token codes to the end of the -# parse.h file that lemon generates. These extra token codes are -# not used by the parser. But they are used by the tokenizer and/or -# the code generator. -# -# -set in [open [lindex $argv 0] rb] -set max 0 -while {![eof $in]} { - set line [gets $in] - if {[regexp {^#define TK_} $line]} { - puts $line - set x [lindex $line 2] - if {$x>$max} {set max $x} - } -} -close $in - -# The following are the extra token codes to be added. SPACE and -# ILLEGAL *must* be the last two token codes and they must be in that order. -# -set extras { - TRUEFALSE - ISNOT - FUNCTION - COLUMN - AGG_FUNCTION - AGG_COLUMN - UMINUS - UPLUS - TRUTH - REGISTER - VECTOR - SELECT_COLUMN - IF_NULL_ROW - ASTERISK - SPAN - END_OF_FILE - UNCLOSED_STRING - SPACE - ILLEGAL -} -if {[lrange $extras end-1 end]!="SPACE ILLEGAL"} { - error "SPACE and ILLEGAL must be the last two token codes and they\ - must be in that order" -} -foreach x $extras { - incr max - puts [format "#define TK_%-29s %4d" $x $max] -} - -# Some additional #defines related to token codes. -# -puts "\n/* The token codes above must all fit in 8 bits */" -puts [format "#define %-20s %-6s" TKFLG_MASK 0xff] -puts "\n/* Flags that can be added to a token code when it is not" -puts "** being stored in a u8: */" -foreach {fg val comment} { - TKFLG_DONTFOLD 0x100 {/* Omit constant folding optimizations */} -} { - puts [format "#define %-20s %-6s %s" $fg $val $comment] -} Index: tool/dbtotxt.c ================================================================== --- tool/dbtotxt.c +++ tool/dbtotxt.c @@ -49,11 +49,11 @@ unsigned char aLine[16]; /* A single line of the file */ unsigned char aHdr[100]; /* File header */ unsigned char bShow[256]; /* Characters ok to display */ memset(bShow, '.', sizeof(bShow)); for(i=' '; i<='~'; i++){ - if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = i; + if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i; } for(i=1; i