** 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
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;
}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.
break;
}
}
- if( idxNum & 8 ){
+ if( idxNum & 0x08 ){
pCur->ss.isReversing = pCur->ss.iStep > 0;
}else{
pCur->ss.isReversing = pCur->ss.iStep < 0;
**
** 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,
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
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 );
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
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;
}
-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
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
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
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.
} {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