@@ -103,16 +103,20 @@ SQLITE_EXTENSION_INIT1
103103** index is ix. The 0th member is given by smBase. The sequence members
104104** progress per ix increment by smStep.
105105*/
106- static sqlite3_int64 genSeqMember (sqlite3_int64 smBase ,
107- sqlite3_int64 smStep ,
108- sqlite3_uint64 ix ){
109- if ( ix >=(sqlite3_uint64 )LLONG_MAX ){
106+ static sqlite3_int64 genSeqMember (
107+ sqlite3_int64 smBase ,
108+ sqlite3_int64 smStep ,
109+ sqlite3_uint64 ix
110+ ){
111+ static const sqlite3_uint64 mxI64 =
112+ ((sqlite3_uint64 )0x7fffffff )<<32 | 0xffffffff ;
113+ if ( ix >=mxI64 ){
110114 /* Get ix into signed i64 range. */
111- ix -= ( sqlite3_uint64 ) LLONG_MAX ;
115+ ix -= mxI64 ;
112116 /* With 2's complement ALU, this next can be 1 step, but is split into
113117 * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
114- smBase += (LLONG_MAX /2 ) * smStep ;
115- smBase += (LLONG_MAX - LLONG_MAX /2 ) * smStep ;
118+ smBase += (mxI64 /2 ) * smStep ;
119+ smBase += (mxI64 - mxI64 /2 ) * smStep ;
116120 }
117121 /* Under UBSAN (or on 1's complement machines), must do this last term
118122 * in steps to avoid the dreaded (and harmless) signed multiply overlow. */
@@ -372,13 +376,13 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
372376** parameter. (idxStr is not used in this implementation.) idxNum
373377** is a bitmask showing which constraints are available:
374378**
375- ** 1 : start=VALUE
376- ** 2 : stop=VALUE
377- ** 4 : step=VALUE
378- **
379- ** Also, if bit 8 is set, that means that the series should be output
380- ** in descending order rather than in ascending order. If bit 16 is
381- ** set, then output must appear in ascending order.
379+ ** 0x01 : start=VALUE
380+ ** 0x02 : stop=VALUE
381+ ** 0x04 : step=VALUE
382+ ** 0x08: descending order
383+ ** 0x10: ascending order
384+ ** 0x20: LIMIT VALUE
385+ ** 0x40: OFFSET VALUE
382386**
383387** This routine should initialize the cursor and position it so that it
384388** is pointing at the first row, or pointing off the end of the table
@@ -392,26 +396,44 @@ static int seriesFilter(
392396 series_cursor * pCur = (series_cursor * )pVtabCursor ;
393397 int i = 0 ;
394398 (void )idxStrUnused ;
395- if ( idxNum & 1 ){
399+ if ( idxNum & 0x01 ){
396400 pCur -> ss .iBase = sqlite3_value_int64 (argv [i ++ ]);
397401 }else {
398402 pCur -> ss .iBase = 0 ;
399403 }
400- if ( idxNum & 2 ){
404+ if ( idxNum & 0x02 ){
401405 pCur -> ss .iTerm = sqlite3_value_int64 (argv [i ++ ]);
402406 }else {
403407 pCur -> ss .iTerm = 0xffffffff ;
404408 }
405- if ( idxNum & 4 ){
409+ if ( idxNum & 0x04 ){
406410 pCur -> ss .iStep = sqlite3_value_int64 (argv [i ++ ]);
407411 if ( pCur -> ss .iStep == 0 ){
408412 pCur -> ss .iStep = 1 ;
409413 }else if ( pCur -> ss .iStep < 0 ){
410- if ( (idxNum & 16 )== 0 ) idxNum |= 8 ;
414+ if ( (idxNum & 0x10 )== 0 ) idxNum |= 0x08 ;
411415 }
412416 }else {
413417 pCur -> ss .iStep = 1 ;
414418 }
419+ if ( idxNum & 0x20 ){
420+ sqlite3_int64 iLimit = sqlite3_value_int64 (argv [i ++ ]);
421+ sqlite3_int64 iTerm ;
422+ if ( idxNum & 0x40 ){
423+ sqlite3_int64 iOffset = sqlite3_value_int64 (argv [i ++ ]);
424+ if ( iOffset > 0 ){
425+ pCur -> ss .iBase += pCur -> ss .iStep * iOffset ;
426+ }
427+ }
428+ if ( iLimit >=0 ){
429+ iTerm = pCur -> ss .iBase + (iLimit - 1 )* pCur -> ss .iStep ;
430+ if ( pCur -> ss .iStep < 0 ){
431+ if ( iTerm > pCur -> ss .iTerm ) pCur -> ss .iTerm = iTerm ;
432+ }else {
433+ if ( iTerm < pCur -> ss .iTerm ) pCur -> ss .iTerm = iTerm ;
434+ }
435+ }
436+ }
415437 for (i = 0 ; i < argc ; i ++ ){
416438 if ( sqlite3_value_type (argv [i ])== SQLITE_NULL ){
417439 /* If any of the constraints have a NULL value, then return no rows.
@@ -422,7 +444,7 @@ static int seriesFilter(
422444 break ;
423445 }
424446 }
425- if ( idxNum & 8 ){
447+ if ( idxNum & 0x08 ){
426448 pCur -> ss .isReversing = pCur -> ss .iStep > 0 ;
427449 }else {
428450 pCur -> ss .isReversing = pCur -> ss .iStep < 0 ;
@@ -442,50 +464,81 @@ static int seriesFilter(
442464**
443465** The query plan is represented by bits in idxNum:
444466**
445- ** (1) start = $value -- constraint exists
446- ** (2) stop = $value -- constraint exists
447- ** (4) step = $value -- constraint exists
448- ** (8) output in descending order
467+ ** 0x01 start = $value -- constraint exists
468+ ** 0x02 stop = $value -- constraint exists
469+ ** 0x04 step = $value -- constraint exists
470+ ** 0x08 output is in descending order
471+ ** 0x10 output is in ascending order
472+ ** 0x20 LIMIT $value -- constraint exists
473+ ** 0x40 OFFSET $value -- constraint exists
449474*/
450475static int seriesBestIndex (
451476 sqlite3_vtab * pVTab ,
452477 sqlite3_index_info * pIdxInfo
453478){
454479 int i , j ; /* Loop over constraints */
455480 int idxNum = 0 ; /* The query plan bitmask */
481+ #ifndef ZERO_ARGUMENT_GENERATE_SERIES
456482 int bStartSeen = 0 ; /* EQ constraint seen on the START column */
483+ #endif
457484 int unusableMask = 0 ; /* Mask of unusable constraints */
458485 int nArg = 0 ; /* Number of arguments that seriesFilter() expects */
459- int aIdx [3 ]; /* Constraints on start, stop, and step */
486+ int aIdx [5 ]; /* Constraints on start, stop, step, LIMIT, OFFSET */
460487 const struct sqlite3_index_constraint * pConstraint ;
461488
462489 /* This implementation assumes that the start, stop, and step columns
463490 ** are the last three columns in the virtual table. */
464491 assert ( SERIES_COLUMN_STOP == SERIES_COLUMN_START + 1 );
465492 assert ( SERIES_COLUMN_STEP == SERIES_COLUMN_START + 2 );
466493
467- aIdx [0 ] = aIdx [1 ] = aIdx [2 ] = -1 ;
494+ aIdx [0 ] = aIdx [1 ] = aIdx [2 ] = aIdx [ 3 ] = aIdx [ 4 ] = -1 ;
468495 pConstraint = pIdxInfo -> aConstraint ;
469496 for (i = 0 ; i < pIdxInfo -> nConstraint ; i ++ , pConstraint ++ ){
470497 int iCol ; /* 0 for start, 1 for stop, 2 for step */
471498 int iMask ; /* bitmask for those column */
499+ int op = pConstraint -> op ;
500+ if ( op >=SQLITE_INDEX_CONSTRAINT_LIMIT
501+ && op <=SQLITE_INDEX_CONSTRAINT_OFFSET
502+ ){
503+ if ( pConstraint -> usable == 0 ){
504+ /* do nothing */
505+ }else if ( op == SQLITE_INDEX_CONSTRAINT_LIMIT ){
506+ aIdx [3 ] = i ;
507+ idxNum |= 0x20 ;
508+ }else {
509+ assert ( op == SQLITE_INDEX_CONSTRAINT_OFFSET );
510+ aIdx [4 ] = i ;
511+ idxNum |= 0x40 ;
512+ }
513+ continue ;
514+ }
472515 if ( pConstraint -> iColumn < SERIES_COLUMN_START ) continue ;
473516 iCol = pConstraint -> iColumn - SERIES_COLUMN_START ;
474517 assert ( iCol >=0 && iCol <=2 );
475518 iMask = 1 << iCol ;
476- if ( iCol == 0 ) bStartSeen = 1 ;
519+ #ifndef ZERO_ARGUMENT_GENERATE_SERIES
520+ if ( iCol == 0 && op == SQLITE_INDEX_CONSTRAINT_EQ ){
521+ bStartSeen = 1 ;
522+ }
523+ #endif
477524 if ( pConstraint -> usable == 0 ){
478525 unusableMask |= iMask ;
479526 continue ;
480- }else if ( pConstraint -> op == SQLITE_INDEX_CONSTRAINT_EQ ){
527+ }else if ( op == SQLITE_INDEX_CONSTRAINT_EQ ){
481528 idxNum |= iMask ;
482529 aIdx [iCol ] = i ;
483530 }
484531 }
485- for (i = 0 ; i < 3 ; i ++ ){
532+ if ( aIdx [3 ]== 0 ){
533+ /* Ignore OFFSET if LIMIT is omitted */
534+ idxNum &= ~0x60 ;
535+ aIdx [4 ] = 0 ;
536+ }
537+ for (i = 0 ; i < 5 ; i ++ ){
486538 if ( (j = aIdx [i ])>=0 ){
487539 pIdxInfo -> aConstraintUsage [j ].argvIndex = ++ nArg ;
488- pIdxInfo -> aConstraintUsage [j ].omit = !SQLITE_SERIES_CONSTRAINT_VERIFY ;
540+ pIdxInfo -> aConstraintUsage [j ].omit =
541+ !SQLITE_SERIES_CONSTRAINT_VERIFY || i >=3 ;
489542 }
490543 }
491544 /* The current generate_column() implementation requires at least one
@@ -506,19 +559,22 @@ static int seriesBestIndex(
506559 ** this plan is unusable */
507560 return SQLITE_CONSTRAINT ;
508561 }
509- if ( (idxNum & 3 )== 3 ){
562+ if ( (idxNum & 0x03 )== 0x03 ){
510563 /* Both start= and stop= boundaries are available. This is the
511564 ** the preferred case */
512565 pIdxInfo -> estimatedCost = (double )(2 - ((idxNum & 4 )!= 0 ));
513566 pIdxInfo -> estimatedRows = 1000 ;
514567 if ( pIdxInfo -> nOrderBy >=1 && pIdxInfo -> aOrderBy [0 ].iColumn == 0 ){
515568 if ( pIdxInfo -> aOrderBy [0 ].desc ){
516- idxNum |= 8 ;
569+ idxNum |= 0x08 ;
517570 }else {
518- idxNum |= 16 ;
571+ idxNum |= 0x10 ;
519572 }
520573 pIdxInfo -> orderByConsumed = 1 ;
521574 }
575+ }else if ( (idxNum & 0x21 )== 0x21 ){
576+ /* We have start= and LIMIT */
577+ pIdxInfo -> estimatedRows = 2500 ;
522578 }else {
523579 /* If either boundary is missing, we have to generate a huge span
524580 ** of numbers. Make this case very expensive so that the query
0 commit comments