]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the generated_series() table-valued-function to respond to
authordrh <>
Fri, 26 Apr 2024 13:30:48 +0000 (13:30 +0000)
committerdrh <>
Fri, 26 Apr 2024 13:30:48 +0000 (13:30 +0000)
LIMIT and OFFSET.  Use this to add new test cases for LIMIT and OFFSET
on virtual tables in a compound SELECT.

FossilOrigin-Name: 408d47ecaa3b906d0886f76a22b76339ec5878270ffe8d1838c74de09c29a33e

ext/misc/series.c
manifest
manifest.uuid
test/tabfunc01.test

index 3e859a8f23be8127346ed9235f92749387e75f91..22968854ca3387d36aa8403f3de51f18b01359fb 100644 (file)
@@ -376,13 +376,13 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
 ** parameter.  (idxStr is not used in this implementation.)  idxNum
 ** is a bitmask showing which constraints are available:
 **
-**    1:    start=VALUE
-**    2:    stop=VALUE
-**    4:    step=VALUE
-**
-** Also, if bit 8 is set, that means that the series should be output
-** in descending order rather than in ascending order.  If bit 16 is
-** set, then output must appear in ascending order.
+**   0x01:    start=VALUE
+**   0x02:    stop=VALUE
+**   0x04:    step=VALUE
+**   0x08:    descending order
+**   0x10:    ascending order
+**   0x20:    LIMIT  VALUE
+**   0x40:    OFFSET  VALUE
 **
 ** This routine should initialize the cursor and position it so that it
 ** is pointing at the first row, or pointing off the end of the table
@@ -396,17 +396,17 @@ static int seriesFilter(
   series_cursor *pCur = (series_cursor *)pVtabCursor;
   int i = 0;
   (void)idxStrUnused;
-  if( idxNum & 1 ){
+  if( idxNum & 0x01 ){
     pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
   }else{
     pCur->ss.iBase = 0;
   }
-  if( idxNum & 2 ){
+  if( idxNum & 0x02 ){
     pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
   }else{
     pCur->ss.iTerm = 0xffffffff;
   }
-  if( idxNum & 4 ){
+  if( idxNum & 0x04 ){
     pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
     if( pCur->ss.iStep==0 ){
       pCur->ss.iStep = 1;
@@ -416,6 +416,24 @@ static int seriesFilter(
   }else{
     pCur->ss.iStep = 1;
   }
+  if( idxNum & 0x20 ){
+    sqlite3_int64 iLimit = sqlite3_value_int64(argv[i++]);
+    sqlite3_int64 iTerm;
+    if( idxNum & 0x40 ){
+      sqlite3_int64 iOffset = sqlite3_value_int64(argv[i++]);
+      if( iOffset>0 ){
+        pCur->ss.iBase += pCur->ss.iStep*iOffset;
+      }
+    }
+    if( iLimit>=0 ){
+      iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
+      if( pCur->ss.iStep<0 ){
+        if( iTerm>pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
+      }else{
+        if( iTerm<pCur->ss.iTerm ) pCur->ss.iTerm = iTerm;
+      }
+    }
+  }
   for(i=0; i<argc; i++){
     if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
       /* If any of the constraints have a NULL value, then return no rows.
@@ -426,7 +444,7 @@ static int seriesFilter(
       break;
     }
   }
-  if( idxNum & 8 ){
+  if( idxNum & 0x08 ){
     pCur->ss.isReversing = pCur->ss.iStep > 0;
   }else{
     pCur->ss.isReversing = pCur->ss.iStep < 0;
@@ -446,10 +464,13 @@ static int seriesFilter(
 **
 ** The query plan is represented by bits in idxNum:
 **
-**  (1)  start = $value  -- constraint exists
-**  (2)  stop = $value   -- constraint exists
-**  (4)  step = $value   -- constraint exists
-**  (8)  output in descending order
+**   0x01  start = $value  -- constraint exists
+**   0x02  stop = $value   -- constraint exists
+**   0x04  step = $value   -- constraint exists
+**   0x08  output is in descending order
+**   0x10  output is in ascending order
+**   0x20  LIMIT $value    -- constraint exists
+**   0x40  OFFSET $value   -- constraint exists
 */
 static int seriesBestIndex(
   sqlite3_vtab *pVTab,
@@ -460,7 +481,7 @@ static int seriesBestIndex(
   int bStartSeen = 0;    /* EQ constraint seen on the START column */
   int unusableMask = 0;  /* Mask of unusable constraints */
   int nArg = 0;          /* Number of arguments that seriesFilter() expects */
-  int aIdx[3];           /* Constraints on start, stop, and step */
+  int aIdx[5];           /* Constraints on start, stop, step, LIMIT, OFFSET */
   const struct sqlite3_index_constraint *pConstraint;
 
   /* This implementation assumes that the start, stop, and step columns
@@ -468,11 +489,22 @@ static int seriesBestIndex(
   assert( SERIES_COLUMN_STOP == SERIES_COLUMN_START+1 );
   assert( SERIES_COLUMN_STEP == SERIES_COLUMN_START+2 );
 
-  aIdx[0] = aIdx[1] = aIdx[2] = -1;
+  aIdx[0] = aIdx[1] = aIdx[2] = aIdx[3] = aIdx[4] = -1;
   pConstraint = pIdxInfo->aConstraint;
   for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
     int iCol;    /* 0 for start, 1 for stop, 2 for step */
     int iMask;   /* bitmask for those column */
+    int op = pConstraint->op;
+    if( op!=SQLITE_INDEX_CONSTRAINT_EQ ){
+      if( op==SQLITE_INDEX_CONSTRAINT_LIMIT ){
+        aIdx[3] = i;
+        idxNum |= 0x20;
+      }else if( op==SQLITE_INDEX_CONSTRAINT_OFFSET ){
+        aIdx[4] = i;
+        idxNum |= 0x40;
+      }
+      continue;
+    }
     if( pConstraint->iColumn<SERIES_COLUMN_START ) continue;
     iCol = pConstraint->iColumn - SERIES_COLUMN_START;
     assert( iCol>=0 && iCol<=2 );
@@ -486,10 +518,16 @@ static int seriesBestIndex(
       aIdx[iCol] = i;
     }
   }
-  for(i=0; i<3; i++){
+  if( aIdx[3]==0 ){
+    /* Ignore OFFSET if LIMIT is omitted */
+    idxNum &= ~0x60;
+    aIdx[4] = 0;
+  }
+  for(i=0; i<5; i++){
     if( (j = aIdx[i])>=0 ){
       pIdxInfo->aConstraintUsage[j].argvIndex = ++nArg;
-      pIdxInfo->aConstraintUsage[j].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY;
+      pIdxInfo->aConstraintUsage[j].omit =
+         !SQLITE_SERIES_CONSTRAINT_VERIFY || i>=3;
     }
   }
   /* The current generate_column() implementation requires at least one
@@ -517,9 +555,9 @@ static int seriesBestIndex(
     pIdxInfo->estimatedRows = 1000;
     if( pIdxInfo->nOrderBy>=1 && pIdxInfo->aOrderBy[0].iColumn==0 ){
       if( pIdxInfo->aOrderBy[0].desc ){
-        idxNum |= 8;
+        idxNum |= 0x08;
       }else{
-        idxNum |= 16;
+        idxNum |= 0x10;
       }
       pIdxInfo->orderByConsumed = 1;
     }
index 5aaf6f32cc02e03413d57eabb6594ddf5638bade..df3495002e2298b1de697965c732103ceb213423 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\stest\sdemonstrating\sthe\sproblem\sat\s[forum:/forumpost/c243b8f856|forum\spost\sc243b8f856].\sNo\sfix\syet.
-D 2024-04-26T12:01:17.532
+C Enhance\sthe\sgenerated_series()\stable-valued-function\sto\srespond\sto\nLIMIT\sand\sOFFSET.\s\sUse\sthis\sto\sadd\snew\stest\scases\sfor\sLIMIT\sand\sOFFSET\non\svirtual\stables\sin\sa\scompound\sSELECT.
+D 2024-04-26T13:30:48.724
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -411,7 +411,7 @@ F ext/misc/regexp.c 4bdd0045912f81c84908bd535ec5ad3b1c8540b4287c70ab840709636240
 F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
 F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
 F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
-F ext/misc/series.c 384f93a8a09cf45e1aa6575660cb580ed61d372c590aad05cdcd4a84fbd8f6ab
+F ext/misc/series.c 69df6c989a37c58d831dcd4c6742ea7c98bc1517d45905290445f3bb277d2cbf
 F ext/misc/sha1.c 4011aef176616872b2a0d5bccf0ecfb1f7ce3fe5c3d107f3a8e949d8e1e3f08d
 F ext/misc/shathree.c 543af7ce71d391cd3a9ab6924a6a1124efc63211fd0f2e240dc4b56077ba88ac
 F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
@@ -1679,7 +1679,7 @@ F test/sync.test 89539f4973c010eda5638407e71ca7fddbcd8e0594f4c9980229f804d433309
 F test/sync2.test 8f9f7d4f6d5be8ca8941a8dadcc4299e558cb6a1ff653a9469146c7a76ef2039
 F test/syscall.test a39d9a36f852ae6e4800f861bc2f2e83f68bbc2112d9399931ecfadeabd2d69d
 F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
-F test/tabfunc01.test 54f27eacd054aa528a8b6e3331192c484104f30aaee351ad035f2b39a00f87c4
+F test/tabfunc01.test f150d206294471d20f50029e6b46b76b87a7a010b16dc57eb44245c76dd02802
 F test/table.test 7862a00b58b5541511a26757ea9c5c7c3f8298766e98aa099deec703d9c0a8e0
 F test/tableapi.test ecbcc29c4ab62c1912c3717c48ea5c5e59f7d64e4a91034e6148bd2b82f177f4
 F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
@@ -2186,11 +2186,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P e6df846f36209bac3e420dd80ce2bbbd87ab7a20b8063fce05f78a3c7ab6027e
-R 86e9e2a691d55b415e3c7d753f120811
-T *branch * vtab-limit-fix
-T *sym-vtab-limit-fix *
-T -sym-trunk *
-U dan
-Z 98d2dacd3c5e1da28914a036142cb128
+P 1685495c0a00238c9c92cce01af8108204a2fad22433ed3e7bba3c9da9ee0766
+R 5bce82b446834e1e285fe6cb5599985f
+U drh
+Z 002d1fa139abb35e0569e061564589d9
 # Remove this line to create a well-formed Fossil manifest.
index 80649ce5f970daeec82913e9237ec2427293470b..9a585c8218a333eb09123c8fcda1ad4d2a43913f 100644 (file)
@@ -1 +1 @@
-1685495c0a00238c9c92cce01af8108204a2fad22433ed3e7bba3c9da9ee0766
\ No newline at end of file
+408d47ecaa3b906d0886f76a22b76339ec5878270ffe8d1838c74de09c29a33e
\ No newline at end of file
index 8e90c549fa4d4d316a6978cd16e57854bc182569..3a62b81f9990632d39739730c0345657d6dfabf0 100644 (file)
@@ -322,6 +322,32 @@ ifcapable altertable {
   } {1 {table pragma_compile_options may not be altered}}
 }
 
+#-----------------------------------------------------------------------------
+# 2024-04-26  LIMIT and OFFSET passed into virtual tables
+# https://sqlite.org/forum/forumpost/c243b8f856
+#
+do_execsql_test tabfunc01-900 {
+  SELECT * FROM (
+    SELECT * FROM generate_series(1,10)
+    UNION ALL
+    SELECT * FROM generate_series(101,104)
+  ) LIMIT 10 OFFSET 5;
+} {6 7 8 9 10 101 102 103 104}
+do_execsql_test tabfunc01-910 {
+  SELECT * FROM (
+    SELECT * FROM generate_series(1,10)
+    UNION ALL
+    SELECT * FROM generate_series(101,104)
+  ) LIMIT -1 OFFSET 5;
+} {6 7 8 9 10 101 102 103 104}
+do_execsql_test tabfunc01-920 {
+  SELECT * FROM (
+    SELECT * FROM generate_series(1,10)
+    UNION ALL
+    SELECT * FROM generate_series(101,104)
+  ) LIMIT -1 OFFSET 0;
+} {1 2 3 4 5 6 7 8 9 10 101 102 103 104}
+
 
 # Free up memory allocations
 intarray_addr