]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the vt02.c test virtual table with the ability to process
authordrh <>
Thu, 18 Dec 2025 12:49:44 +0000 (12:49 +0000)
committerdrh <>
Thu, 18 Dec 2025 12:49:44 +0000 (12:49 +0000)
ORDER BY DESC.

FossilOrigin-Name: 35461a8d5a103e373e5b38b3974a3bb1074e914e862825108713b36bef04b6bd

manifest
manifest.uuid
test/vt02.c

index f0e2b70faa6f91461bb1c29c44c71d3e0ac82023..07625c36924b2a296afd724c0e618efdc109b4d2 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-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
@@ -1948,7 +1948,7 @@ F test/veryquick.test 57ab846bacf7b90cf4e9a672721ea5c5b669b661
 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
@@ -2187,8 +2187,8 @@ F tool/version-info.c 33d0390ef484b3b1cb685d59362be891ea162123cea181cb8e6d2cf6dd
 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.
index 8f170058c11a0357566c272ed8a117c45f9773e7..806c5eb9bbe593827f05e5495a65d7e47e712664 100644 (file)
@@ -1 +1 @@
-d017ae8640f24543ad5bf37a3e514c1e919730b8c686255c61890448ba22b3e1
+35461a8d5a103e373e5b38b3974a3bb1074e914e862825108713b36bef04b6bd
index 06fade0969ddf1e8e6cc67c7008bf185592ead8a..e82549d6b0f0877cf4bfc606109f175b12469102 100644 (file)
 **      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 */
@@ -203,6 +223,7 @@ struct vt02_cur {
   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 */
 };
@@ -292,7 +313,7 @@ static int vt02Close(sqlite3_vtab_cursor *pCursor){
 */
 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
@@ -301,8 +322,8 @@ static int vt02Next(sqlite3_vtab_cursor *pCursor){
   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;
 }
 
@@ -324,6 +345,7 @@ static int vt02Next(sqlite3_vtab_cursor *pCursor){
 **    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 */
@@ -334,11 +356,17 @@ static int vt02Filter(
 ){
   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;
@@ -416,9 +444,17 @@ static int vt02Filter(
   }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;
 
@@ -838,24 +874,38 @@ static int vt02BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
   ** 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;
@@ -875,7 +925,7 @@ static int vt02BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
           /* 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;
@@ -901,7 +951,7 @@ static int vt02BestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
     pInfo->needToFreeIdxStr = 1;
   }
   if( flags & VT02_BAD_IDXNUM ){
-    pInfo->idxNum += 1000;
+    pInfo->idxNum += 10000;
   }
 
   if( iOffset>=0 ){