-C Fix\sthe\sreturn\svalue\sof\ssqlite3_vtab_distinct()\sin\scases\swhere\sa\svirtual\stable\squery\shas\ssimilar\sGROUP\sBY\sand\sORDER\sBY\sexpression\slists.
-D 2025-12-17T21:00:06.696
+C Enhance\sthe\svt02.c\stest\svirtual\stable\swith\sthe\sability\sto\sprocess\nORDER\sBY\sDESC.
+D 2025-12-18T12:49:44.221
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F test/view.test 3c23d7a068e9e4a0c4e6907498042772adea725f0630c3d9638ffd4e5a08b92b
F test/view2.test db32c8138b5b556f610b35dfddd38c5a58a292f07fda5281eedb0851b2672679
F test/view3.test ad8a8290ee2b55ff6ce66c9ef1ce3f1e47926273a3814e1c425293e128a95456
-F test/vt02.c 5b44ac67b1a283fedecf2d6e2ceda61e7a157f01d44dcb4490dcb1e87d057060
+F test/vt02.c 49f327ddd077c52eab87d20f8c7267fd9f716aa4bcb8f449a9b62e8d9e8b0c90
F test/vt100-a.sql a3e188a118ca78c08b41681a4db6d0f353e554ceb33f1573b1872d16e2d30596
F test/vtab1.test 09a72330d0f31eda2ffaa828b06a6b917fb86250ee72de0301570af725774c07
F test/vtab2.test 14d4ab26cee13ba6cf5c5601b158e4f57552d3b055cdd9406cf7f711e9c84082
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh d924598cf2f55a4ecbc2aeb055c10bd5f48114793e7ba25f9585435da29e7e98
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 7986a35d27fe0cfd7d9a3bfec0ee0d1c5556b24d571bb68c35dd5eb7251e423c
-R 06ffa773b4d06150e51f5a59f3ea234b
-U dan
-Z 5b36ed65d69bc38cf13ab6a46675d3e9
+P d017ae8640f24543ad5bf37a3e514c1e919730b8c686255c61890448ba22b3e1
+R faaecd2038048c73b75d6f197dafc3f9
+U drh
+Z 1008e4c9d6dee7b8022617033f94919d
# Remove this line to create a well-formed Fossil manifest.
** a=... AND b=... AND c=...
** a=... AND b=... AND c=... AND d=...
**
-** Various ORDER BY constraints are also recognized and consumed. The
-** OFFSET constraint is recognized and consumed.
+** The table will also recognize IN constraints on column D using the
+** sqlite3_vtab_in_first() and sqlite3_vtab_in_next() interfaces:
+**
+** a=... AND d IN (...)
+** a=... AND b=... AND d IN (...)
+** a=... AND b=... AND c=... AND d IN (...)
+**
+** Various ORDER BY constraints are also recognized and consumed.
+**
+** ORDER BY x
+** ORDER BY a
+** ORDER BY a, b
+** ORDER BY a, b, c
+** ORDER BY a, b, c, d
+**
+** The above also work if every term is DESC rather than ASC. However,
+** the orderByConsumed is not set if there are a mixture of ASC and DESC
+** terms in the ORDER BY clause.
+**
+** The sqlite3_vtab_distinct() interface is used to recognize DISTINCT
+** and GROUP BY constraints and consume the corresponding sorting requirements.
+**
+** The OFFSET constraint is recognized and consumed.
**
** ## TABLE-VALUED FUNCTION
**
** ## COMPILING AND RUNNING
**
** This file can also be compiled separately as a loadable extension
-** for SQLite (as long as the -DTH3_VERSION is not defined). To compile as a
-** loadable extension do his:
-**
-** gcc -Wall -g -shared -fPIC -I. -DSQLITE_DEBUG vt02.c -o vt02.so
-**
-** Or on Windows:
+** for SQLite (as long as the -DTH3_VERSION is not defined). To compile as a
+** loadable extension do something like this:
**
-** cl vt02.c -link -dll -out:vt02.dll
+** (linux) gcc -shared -fPIC -I. vt02.c -o vt02.so
+** (mac) clang -dynamiclib -fPIC -I. vt02.c -o vt02.dylib
+** (windows) cl vt02.c -link -dll -out:vt02.dll
**
** Then load into the CLI using:
**
** 2x increment by 100
** 3x increment by 1000
** 1xx Use offset provided by argv[N]
+** 1xxx Reverse output order (to implement ORDER BY ... DESC)
*/
#ifndef TH3_VERSION
/* These bits for separate compilation as a loadable extension, only */
sqlite3_vtab_cursor parent; /* Base class. Must be first */
sqlite3_int64 i; /* Current entry */
sqlite3_int64 iEof; /* Indicate EOF when reaching this value */
+ sqlite3_int64 iMin; /* EOF if dropping below this value */
int iIncr; /* Amount by which to increment */
unsigned int mD; /* Mask of allowed D-column values */
};
*/
static int vt02Eof(sqlite3_vtab_cursor *pCursor){
vt02_cur *pCur = (vt02_cur*)pCursor;
- return pCur->i<0 || pCur->i>=pCur->iEof;
+ return pCur->i<pCur->iMin || pCur->i>=pCur->iEof;
}
/* Advance the cursor to the next row in the table
vt02_cur *pCur = (vt02_cur*)pCursor;
do{
pCur->i += pCur->iIncr;
- if( pCur->i<0 ) pCur->i = pCur->iEof;
- }while( (pCur->mD & (1<<(pCur->i%10)))==0 && pCur->i<pCur->iEof );
+ if( pCur->i<pCur->iMin || pCur->i>=pCur->iEof ) break;
+ }while( (pCur->mD & (1<<(pCur->i%10)))==0 );
return SQLITE_OK;
}
** 2x increment by 100
** 3x increment by 1000
** 1xx Use offset provided by argv[N]
+** 1xxx Output rows in reverse order
*/
static int vt02Filter(
sqlite3_vtab_cursor *pCursor, /* The cursor to rewind */
){
vt02_cur *pCur = (vt02_cur*)pCursor; /* The vt02 cursor */
int bUseOffset = 0; /* True to use OFFSET value */
+ int bReverse = 0; /* Output rows in reverse order */
int iArg = 0; /* argv[] values used so far */
int iOrigIdxNum = idxNum; /* Original value for idxNum */
pCur->iIncr = 1;
+ pCur->iMin = 0;
pCur->mD = 0x3ff;
+ if( idxNum>=1000 ){
+ bReverse = 1;
+ idxNum -= 1000;
+ }
if( idxNum>=100 ){
bUseOffset = 1;
idxNum -= 100;
}else{
goto vt02_bad_idxnum;
}
+ if( bReverse ){
+ sqlite3_int64 x;
+ x = pCur->i + ((pCur->iEof - pCur->i)/pCur->iIncr)*pCur->iIncr;
+ if( x>=pCur->iEof ) x -= pCur->iIncr;
+ pCur->iIncr = -pCur->iIncr;
+ pCur->iMin = pCur->i;
+ pCur->i = x;
+ }
if( bUseOffset ){
int nSkip = sqlite3_value_int(argv[iArg]);
- while( nSkip-- > 0 && pCur->i<pCur->iEof ) vt02Next(pCursor);
+ while( nSkip-- > 0 && !vt02Eof(pCursor) ) vt02Next(pCursor);
}
return SQLITE_OK;
** the same answer.
*/
if( pInfo->nOrderBy>0 && (flags & VT02_NO_SORT_OPT)==0 ){
+ int eDistinct = sqlite3_vtab_distinct(pInfo);
if( pInfo->idxNum==1 ){
/* There will only be one row of output. So it is always sorted. */
pInfo->orderByConsumed = 1;
}else
- if( pInfo->aOrderBy[0].iColumn<=0
- && pInfo->aOrderBy[0].desc==0
- ){
- /* First column of order by is X ascending */
+ if( pInfo->aOrderBy[0].iColumn<=0 ){
+ /* First column of order by is X */
+ if( pInfo->aOrderBy[0].desc ){
+ pInfo->idxNum += 1000; /* Reverse output order */
+ }
pInfo->orderByConsumed = 1;
}else
- if( sqlite3_vtab_distinct(pInfo)>=1 ){
+ if( eDistinct>=1 ){
unsigned int x = 0;
+ int nDesc = 0;
+ int nAsc = 0;
for(i=0; i<pInfo->nOrderBy; i++){
int iCol = pInfo->aOrderBy[i].iColumn;
if( iCol<0 ) iCol = 0;
+ if( pInfo->aOrderBy[i].desc ){
+ nDesc++;
+ }else{
+ nAsc++;
+ }
x |= 1<<iCol;
}
- if( sqlite3_vtab_distinct(pInfo)==2 ){
+ if( nDesc>0 && nAsc>0 ){
+ if( eDistinct!=1 ) eDistinct = -999; /* Never set orderByConsumed */
+ }else if( nAsc==0 ){
+ pInfo->idxNum += 1000; /* Reverse output order */
+ }
+ if( eDistinct==2 ){ /* DISTINCT */
if( x==0x02 ){
/* DISTINCT A */
pInfo->idxNum += 30;
/* DISTINCT A,B,C,D */
pInfo->orderByConsumed = 1;
}
- }else{
+ }else if( eDistinct>=1 ){ /* GROUP BY or (DISTINCT and ORDER BY) */
if( x==0x02 ){
/* GROUP BY A */
pInfo->orderByConsumed = 1;
pInfo->needToFreeIdxStr = 1;
}
if( flags & VT02_BAD_IDXNUM ){
- pInfo->idxNum += 1000;
+ pInfo->idxNum += 10000;
}
if( iOffset>=0 ){