1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-10-21 11:13:54 +03:00

Revamp the implementation of generate_series to simplify and improve

long-term maintainability.

FossilOrigin-Name: 9041ca8543ff3d2a617ce4df696aa53269a7803716caaf55a8e61e59ee667040
This commit is contained in:
drh
2025-09-28 18:25:59 +00:00
4 changed files with 414 additions and 258 deletions

View File

@@ -30,19 +30,20 @@
** SELECT * FROM generate_series(0,100,5);
**
** The query above returns integers from 0 through 100 counting by steps
** of 5.
** of 5. In other words, 0, 5, 10, 15, ..., 90, 95, 100. There are a total
** of 21 rows.
**
** SELECT * FROM generate_series(0,100);
**
** Integers from 0 through 100 with a step size of 1.
** Integers from 0 through 100 with a step size of 1. 101 rows.
**
** SELECT * FROM generate_series(20) LIMIT 10;
**
** Integers 20 through 29.
** Integers 20 through 29. 10 rows.
**
** SELECT * FROM generate_series(0,-100,-5);
**
** Integers 0 -5 -10 ... -100.
** Integers 0 -5 -10 ... -100. 21 rows.
**
** SELECT * FROM generate_series(0,-1);
**
@@ -118,162 +119,89 @@ SQLITE_EXTENSION_INIT1
#include <math.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/*
** Return that member of a generate_series(...) sequence whose 0-based
** index is ix. The 0th member is given by smBase. The sequence members
** progress per ix increment by smStep.
*/
static sqlite3_int64 genSeqMember(
sqlite3_int64 smBase,
sqlite3_int64 smStep,
sqlite3_uint64 ix
){
static const sqlite3_uint64 mxI64 =
((sqlite3_uint64)0x7fffffff)<<32 | 0xffffffff;
if( ix>=mxI64 ){
/* Get ix into signed i64 range. */
ix -= mxI64;
/* With 2's complement ALU, this next can be 1 step, but is split into
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
smBase += (mxI64/2) * smStep;
smBase += (mxI64 - mxI64/2) * smStep;
}
/* Under UBSAN (or on 1's complement machines), must do this last term
* in steps to avoid the dreaded (and harmless) signed multiply overflow. */
if( ix>=2 ){
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
smBase += ix2*smStep;
ix -= ix2;
}
return smBase + ((sqlite3_int64)ix)*smStep;
}
typedef unsigned char u8;
typedef struct SequenceSpec {
sqlite3_int64 iOBase; /* Original starting value ("start") */
sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
sqlite3_int64 iBase; /* Starting value to actually use */
sqlite3_int64 iTerm; /* Terminal value to actually use */
sqlite3_int64 iStep; /* Increment ("step") */
sqlite3_uint64 uSeqIndexMax; /* maximum sequence index (aka "n") */
sqlite3_uint64 uSeqIndexNow; /* Current index during generation */
sqlite3_int64 iValueNow; /* Current value during generation */
u8 isNotEOF; /* Sequence generation not exhausted */
u8 isReversing; /* Sequence is being reverse generated */
} SequenceSpec;
/*
** Return the number of steps between pSS->iBase and pSS->iTerm if
** the step width is pSS->iStep.
*/
static sqlite3_uint64 seriesSteps(SequenceSpec *pSS){
sqlite3_uint64 uBase, uTerm, uStep, uSpan;
assert( pSS->iStep!=0 );
assert( sizeof(uBase)==sizeof(&pSS->iBase) );
assert( sizeof(uTerm)==sizeof(&pSS->iTerm) );
assert( sizeof(uStep)==sizeof(&pSS->iStep) );
memcpy(&uBase, &pSS->iBase, sizeof(uBase));
memcpy(&uTerm, &pSS->iTerm, sizeof(uTerm));
memcpy(&uStep, &pSS->iStep, sizeof(uStep));
if( pSS->iStep>0 ){
uSpan = uTerm - uBase;
}else{
uSpan = uBase - uTerm;
uStep = 1 + ~uStep;
}
return uSpan/uStep;
}
/*
** Prepare a SequenceSpec for use in generating an integer series
** given initialized iBase, iTerm and iStep values. Sequence is
** initialized per given isReversing. Other members are computed.
*/
static void setupSequence( SequenceSpec *pss ){
int bSameSigns;
pss->uSeqIndexMax = 0;
pss->isNotEOF = 0;
bSameSigns = (pss->iBase < 0)==(pss->iTerm < 0);
if( pss->iTerm < pss->iBase ){
sqlite3_uint64 nuspan = 0;
if( bSameSigns ){
nuspan = (sqlite3_uint64)(pss->iBase - pss->iTerm);
}else{
/* Under UBSAN (or on 1's complement machines), must do this in steps.
* In this clause, iBase>=0 and iTerm<0 . */
nuspan = 1;
nuspan += pss->iBase;
nuspan += -(pss->iTerm+1);
}
if( pss->iStep<0 ){
pss->isNotEOF = 1;
if( nuspan==ULONG_MAX ){
pss->uSeqIndexMax = ( pss->iStep>LLONG_MIN )? nuspan/-pss->iStep : 1;
}else if( pss->iStep>LLONG_MIN ){
pss->uSeqIndexMax = nuspan/-pss->iStep;
}
}
}else if( pss->iTerm > pss->iBase ){
sqlite3_uint64 puspan = 0;
if( bSameSigns ){
puspan = (sqlite3_uint64)(pss->iTerm - pss->iBase);
}else{
/* Under UBSAN (or on 1's complement machines), must do this in steps.
* In this clause, iTerm>=0 and iBase<0 . */
puspan = 1;
puspan += pss->iTerm;
puspan += -(pss->iBase+1);
}
if( pss->iStep>0 ){
pss->isNotEOF = 1;
pss->uSeqIndexMax = puspan/pss->iStep;
}
}else if( pss->iTerm == pss->iBase ){
pss->isNotEOF = 1;
pss->uSeqIndexMax = 0;
}
pss->uSeqIndexNow = (pss->isReversing)? pss->uSeqIndexMax : 0;
pss->iValueNow = (pss->isReversing)
? genSeqMember(pss->iBase, pss->iStep, pss->uSeqIndexMax)
: pss->iBase;
}
/*
** Progress sequence generator to yield next value, if any.
** Leave its state to either yield next value or be at EOF.
** Return whether there is a next value, or 0 at EOF.
*/
static int progressSequence( SequenceSpec *pss ){
if( !pss->isNotEOF ) return 0;
if( pss->isReversing ){
if( pss->uSeqIndexNow > 0 ){
pss->uSeqIndexNow--;
pss->iValueNow -= pss->iStep;
}else{
pss->isNotEOF = 0;
}
}else{
if( pss->uSeqIndexNow < pss->uSeqIndexMax ){
pss->uSeqIndexNow++;
pss->iValueNow += pss->iStep;
}else{
pss->isNotEOF = 0;
}
}
return pss->isNotEOF;
}
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
** over rows of the result.
**
** iOBase, iOTerm, and iOStep are the original values of the
** start=, stop=, and step= constraints on the query. These are
** the values reported by the start, stop, and step columns of the
** virtual table.
**
** iBase, iTerm, iStep, and bDescp are the actual values used to generate
** the sequence. These might be different from the iOxxxx values.
** For example in
**
** SELECT value FROM generate_series(1,11,2)
** WHERE value BETWEEN 4 AND 8;
**
** The iOBase is 1, but the iBase is 5. iOTerm is 11 but iTerm is 7.
** Another example:
**
** SELECT value FROM generate_series(1,15,3) ORDER BY value DESC;
**
** The cursor initialization for the above query is:
**
** iOBase = 1 iBase = 13
** iOTerm = 15 iTerm = 1
** iOStep = 3 iStep = 3 bDesc = 1
**
** The actual step size is unsigned so that can have a value of
** +9223372036854775808 which is needed for querys like this:
**
** SELECT value
** FROM generate_series(9223372036854775807,
** -9223372036854775808,
** -9223372036854775808)
** ORDER BY value ASC;
**
** The setup for the previous query will be:
**
** iOBase = 9223372036854775807 iBase = -1
** iOTerm = -9223372036854775808 iTerm = 9223372036854775807
** iOStep = -9223372036854775808 iStep = 9223372036854775808 bDesc = 0
*/
typedef unsigned char u8;
typedef struct series_cursor series_cursor;
struct series_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
SequenceSpec ss; /* (this) Derived class data */
sqlite3_int64 iOBase; /* Original starting value ("start") */
sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
sqlite3_int64 iOStep; /* Original step value */
sqlite3_int64 iBase; /* Starting value to actually use */
sqlite3_int64 iTerm; /* Terminal value to actually use */
sqlite3_uint64 iStep; /* The step size */
sqlite3_int64 iValue; /* Current value */
u8 bDesc; /* iStep is really negative */
u8 bDone; /* True if stepped past last element */
};
/*
** Computed the difference between two 64-bit signed integers using a
** convoluted computation designed to work around the silly restriction
** against signed integer overflow in C.
*/
static sqlite3_uint64 span64(sqlite3_int64 a, sqlite3_int64 b){
assert( a>=b );
return (*(sqlite3_uint64*)&a) - (*(sqlite3_uint64*)&b);
}
/*
** Add or substract an unsigned 64-bit integer from a signed 64-bit integer
** and return the new signed 64-bit integer.
*/
static sqlite3_int64 add64(sqlite3_int64 a, sqlite3_uint64 b){
sqlite3_uint64 x = *(sqlite3_uint64*)&a;
x += b;
return *(sqlite3_int64*)&x;
}
static sqlite3_int64 sub64(sqlite3_int64 a, sqlite3_uint64 b){
sqlite3_uint64 x = *(sqlite3_uint64*)&a;
x -= b;
return *(sqlite3_int64*)&x;
}
/*
** The seriesConnect() method is invoked to create a new
** series_vtab that describes the generate_series virtual table.
@@ -354,7 +282,15 @@ static int seriesClose(sqlite3_vtab_cursor *cur){
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
progressSequence( & pCur->ss );
if( pCur->iValue==pCur->iTerm ){
pCur->bDone = 1;
}else if( pCur->bDesc ){
pCur->iValue = sub64(pCur->iValue, pCur->iStep);
assert( pCur->iValue>=pCur->iTerm );
}else{
pCur->iValue = add64(pCur->iValue, pCur->iStep);
assert( pCur->iValue<=pCur->iTerm );
}
return SQLITE_OK;
}
@@ -370,10 +306,10 @@ static int seriesColumn(
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case SERIES_COLUMN_START: x = pCur->ss.iOBase; break;
case SERIES_COLUMN_STOP: x = pCur->ss.iOTerm; break;
case SERIES_COLUMN_STEP: x = pCur->ss.iStep; break;
default: x = pCur->ss.iValueNow; break;
case SERIES_COLUMN_START: x = pCur->iOBase; break;
case SERIES_COLUMN_STOP: x = pCur->iOTerm; break;
case SERIES_COLUMN_STEP: x = pCur->iOStep; break;
default: x = pCur->iValue; break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
@@ -390,7 +326,7 @@ static int seriesColumn(
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
*pRowid = pCur->ss.iValueNow;
*pRowid = pCur->iValue;
return SQLITE_OK;
}
@@ -400,7 +336,7 @@ static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
*/
static int seriesEof(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
return !pCur->ss.isNotEOF;
return pCur->bDone;
}
/* True to cause run-time checking of the start=, stop=, and/or step=
@@ -411,6 +347,20 @@ static int seriesEof(sqlite3_vtab_cursor *cur){
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
#endif
/*
** Return the number of steps between pCur->iBase and pCur->iTerm if
** the step width is pCur->iStep.
*/
static sqlite3_uint64 seriesSteps(series_cursor *pCur){
if( pCur->bDesc ){
assert( pCur->iBase >= pCur->iTerm );
return span64(pCur->iBase, pCur->iTerm)/pCur->iStep;
}else{
assert( pCur->iBase <= pCur->iTerm );
return span64(pCur->iTerm, pCur->iBase)/pCur->iStep;
}
}
/*
** This method is called to "rewind" the series_cursor object back
** to the first row of output. This method is always called at least
@@ -444,33 +394,41 @@ static int seriesFilter(
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
int i = 0;
int returnNoRows = 0;
sqlite3_int64 iMin = SMALLEST_INT64;
sqlite3_int64 iMax = LARGEST_INT64;
sqlite3_int64 iLimit = 0;
sqlite3_int64 iOffset = 0;
int iArg = 0; /* Arguments used so far */
int i; /* Loop counter */
sqlite3_int64 iMin = SMALLEST_INT64; /* Smallest allowed output value */
sqlite3_int64 iMax = LARGEST_INT64; /* Largest allowed output value */
sqlite3_int64 iLimit = 0; /* if >0, the value of the LIMIT */
sqlite3_int64 iOffset = 0; /* if >0, the value of the OFFSET */
(void)idxStrUnused;
/* If any constraints have a NULL value, then return no rows.
** See ticket https://sqlite.org/src/info/fac496b61722daf2
*/
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
goto series_no_rows;
}
}
/* Capture the three HIDDEN parameters to the virtual table and insert
** default values for any parameters that are omitted.
*/
if( idxNum & 0x01 ){
pCur->ss.iBase = sqlite3_value_int64(argv[i++]);
pCur->iOBase = sqlite3_value_int64(argv[iArg++]);
}else{
pCur->ss.iBase = 0;
pCur->iOBase = 0;
}
if( idxNum & 0x02 ){
pCur->ss.iTerm = sqlite3_value_int64(argv[i++]);
pCur->iOTerm = sqlite3_value_int64(argv[iArg++]);
}else{
pCur->ss.iTerm = 0xffffffff;
pCur->iOTerm = 0xffffffff;
}
if( idxNum & 0x04 ){
pCur->ss.iStep = sqlite3_value_int64(argv[i++]);
if( pCur->ss.iStep==0 ){
pCur->ss.iStep = 1;
}else if( pCur->ss.iStep<0 ){
if( (idxNum & 0x10)==0 ) idxNum |= 0x08;
}
pCur->iOStep = sqlite3_value_int64(argv[iArg++]);
}else{
pCur->ss.iStep = 1;
pCur->iOStep = 1;
}
/* If there are constraints on the value column but there are
@@ -480,72 +438,91 @@ static int seriesFilter(
** further below.
*/
if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
pCur->ss.iBase = SMALLEST_INT64;
pCur->iOBase = SMALLEST_INT64;
}
if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
pCur->ss.iTerm = LARGEST_INT64;
pCur->iOTerm = LARGEST_INT64;
}
pCur->iBase = pCur->iOBase;
pCur->iTerm = pCur->iOTerm;
if( pCur->iOStep>0 ){
pCur->iStep = pCur->iOStep;
}else if( pCur->iOStep>SMALLEST_INT64 ){
pCur->iStep = -pCur->iOStep;
}else{
pCur->iStep = LARGEST_INT64;
pCur->iStep++;
}
pCur->bDesc = pCur->iOStep<0;
if( pCur->bDesc==0 && pCur->iBase>pCur->iTerm ){
goto series_no_rows;
}
if( pCur->bDesc!=0 && pCur->iBase<pCur->iTerm ){
goto series_no_rows;
}
pCur->ss.iOBase = pCur->ss.iBase;
pCur->ss.iOTerm = pCur->ss.iTerm;
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
** The range must first be constrained by the limits on value.
*/
if( idxNum & 0x20 ){
iLimit = sqlite3_value_int64(argv[i++]);
iLimit = sqlite3_value_int64(argv[iArg++]);
if( idxNum & 0x40 ){
iOffset = sqlite3_value_int64(argv[i++]);
iOffset = sqlite3_value_int64(argv[iArg++]);
}
}
/* Narrow the range of iMin and iMax (the minimum and maximum outputs)
** based on equality and inequality constraints on the "value" column.
*/
if( idxNum & 0x3380 ){
/* Extract the maximum range of output values determined by
** constraints on the "value" column.
*/
if( idxNum & 0x0080 ){
if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[i++]);
if( r==ceil(r) ){
if( idxNum & 0x0080 ){ /* value=X */
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if( r==ceil(r) && r>=SMALLEST_INT64 && r<=LARGEST_INT64 ){
iMin = iMax = (sqlite3_int64)r;
}else{
returnNoRows = 1;
goto series_no_rows;
}
}else{
iMin = iMax = sqlite3_value_int64(argv[i++]);
iMin = iMax = sqlite3_value_int64(argv[iArg++]);
}
}else{
if( idxNum & 0x0300 ){
if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[i++]);
if( idxNum & 0x0200 && r==ceil(r) ){
if( idxNum & 0x0300 ){ /* value>X or value>=X */
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if( r<SMALLEST_INT64 ){
iMin = SMALLEST_INT64;
}else if( (idxNum & 0x0200)!=0 && r==ceil(r) ){
iMin = (sqlite3_int64)ceil(r+1.0);
}else{
iMin = (sqlite3_int64)ceil(r);
}
}else{
iMin = sqlite3_value_int64(argv[i++]);
if( idxNum & 0x0200 ){
iMin = sqlite3_value_int64(argv[iArg++]);
if( (idxNum & 0x0200)!=0 ){
if( iMin==LARGEST_INT64 ){
returnNoRows = 1;
goto series_no_rows;
}else{
iMin++;
}
}
}
}
if( idxNum & 0x3000 ){
if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[i++]);
if( (idxNum & 0x2000)!=0 && r==floor(r) ){
if( idxNum & 0x3000 ){ /* value<X or value<=X */
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if( r>LARGEST_INT64 ){
iMax = LARGEST_INT64;
}else if( (idxNum & 0x2000)!=0 && r==floor(r) ){
iMax = (sqlite3_int64)(r-1.0);
}else{
iMax = (sqlite3_int64)floor(r);
}
}else{
iMax = sqlite3_value_int64(argv[i++]);
iMax = sqlite3_value_int64(argv[iArg++]);
if( idxNum & 0x2000 ){
if( iMax==SMALLEST_INT64 ){
returnNoRows = 1;
goto series_no_rows;
}else{
iMax--;
}
@@ -553,75 +530,100 @@ static int seriesFilter(
}
}
if( iMin>iMax ){
returnNoRows = 1;
goto series_no_rows;
}
}
/* Try to reduce the range of values to be generated based on
** constraints on the "value" column.
*/
if( pCur->ss.iStep>0 ){
sqlite3_int64 szStep = pCur->ss.iStep;
if( pCur->ss.iBase<iMin ){
sqlite3_uint64 d = iMin - pCur->ss.iBase;
pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
if( pCur->bDesc==0 ){
if( pCur->iBase<iMin ){
sqlite3_uint64 span = span64(iMin,pCur->iBase);
pCur->iBase = add64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
if( pCur->iBase<iMin ){
if( pCur->iBase > sub64(LARGEST_INT64, pCur->iStep) ){
goto series_no_rows;
}
pCur->iBase = add64(pCur->iBase, pCur->iStep);
}
}
if( pCur->ss.iTerm>iMax ){
pCur->ss.iTerm = iMax;
if( pCur->iTerm>iMax ){
pCur->iTerm = iMax;
}
}else if( pCur->ss.iStep>SMALLEST_INT64 ){
sqlite3_int64 szStep = -pCur->ss.iStep;
assert( szStep>0 );
if( pCur->ss.iBase>iMax ){
sqlite3_uint64 d = pCur->ss.iBase - iMax;
pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
}else{
if( pCur->iBase>iMax ){
sqlite3_uint64 span = span64(pCur->iBase,iMax);
pCur->iBase = sub64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
if( pCur->iBase>iMax ){
if( pCur->iBase < add64(SMALLEST_INT64, pCur->iStep) ){
goto series_no_rows;
}
pCur->iBase = sub64(pCur->iBase, pCur->iStep);
}
}
if( pCur->ss.iTerm<iMin ){
pCur->ss.iTerm = iMin;
if( pCur->iTerm<iMin ){
pCur->iTerm = iMin;
}
}
}
/* Adjust iTerm so that it is exactly the last value of the series.
*/
if( pCur->bDesc==0 ){
if( pCur->iBase>pCur->iTerm ){
goto series_no_rows;
}
pCur->iTerm = sub64(pCur->iTerm,
span64(pCur->iTerm,pCur->iBase) % pCur->iStep);
}else{
if( pCur->iBase<pCur->iTerm ){
goto series_no_rows;
}
pCur->iTerm = add64(pCur->iTerm,
span64(pCur->iBase,pCur->iTerm) % pCur->iStep);
}
/* Transform the series generator to output values in the requested
** order.
*/
if( ((idxNum & 0x0008)!=0 && pCur->bDesc==0)
|| ((idxNum & 0x0010)!=0 && pCur->bDesc!=0)
){
sqlite3_int64 tmp = pCur->iBase;
pCur->iBase = pCur->iTerm;
pCur->iTerm = tmp;
pCur->bDesc = !pCur->bDesc;
}
/* Apply LIMIT and OFFSET constraints, if any */
assert( pCur->ss.iStep!=0 );
assert( pCur->iStep!=0 );
if( idxNum & 0x20 ){
sqlite3_uint64 nStep;
if( iOffset>0 ){
if( seriesSteps(&pCur->ss) < (sqlite3_uint64)iOffset ){
returnNoRows = 1;
iLimit = -1;
if( seriesSteps(pCur) < (sqlite3_uint64)iOffset ){
goto series_no_rows;
}else if( pCur->bDesc ){
pCur->iBase = sub64(pCur->iBase, pCur->iStep*iOffset);
}else{
pCur->ss.iBase += pCur->ss.iStep*iOffset;
pCur->iBase = add64(pCur->iBase, pCur->iStep*iOffset);
}
}
if( iLimit>=0
&& (nStep = seriesSteps(&pCur->ss)) > (sqlite3_uint64)iLimit
){
pCur->ss.iTerm = pCur->ss.iBase + (iLimit - 1)*pCur->ss.iStep;
if( iLimit>=0 && (nStep = seriesSteps(pCur)) > (sqlite3_uint64)iLimit ){
pCur->iTerm = add64(pCur->iBase, (iLimit - 1)*pCur->iStep);
}
}
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.
** See ticket https://sqlite.org/src/info/fac496b61722daf2 */
returnNoRows = 1;
break;
}
}
if( returnNoRows ){
pCur->ss.iBase = 1;
pCur->ss.iTerm = 0;
pCur->ss.iStep = 1;
}
if( idxNum & 0x08 ){
pCur->ss.isReversing = pCur->ss.iStep > 0;
}else{
pCur->ss.isReversing = pCur->ss.iStep < 0;
}
setupSequence( &pCur->ss );
pCur->iValue = pCur->iBase;
pCur->bDone = 0;
return SQLITE_OK;
series_no_rows:
pCur->iBase = 0;
pCur->iTerm = 0;
pCur->iStep = 1;
pCur->bDesc = 0;
pCur->bDone = 1;
return SQLITE_OK;
}
/*

View File

@@ -1,5 +1,5 @@
C Remove\sthe\slong-defunct\swasmfs\sbuild\sof\sspeedtest1\sfrom\sindex.html\sand\sre-enable\sthe\swasmfs\sscratchpad\sapp\s(which\sstill\sworks).\sFix\sthe\sCFLAGS\sfor\sthe\swasmfs\sbuild\sand\sthe\spath\sto\sthe\swasm\sfile\sfor\sone\sof\sthe\sJS.
D 2025-09-28T18:03:29.667
C Revamp\sthe\simplementation\sof\sgenerate_series\sto\ssimplify\sand\simprove\nlong-term\smaintainability.
D 2025-09-28T18:25:59.467
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
@@ -393,7 +393,7 @@ F ext/misc/regexp.c 548151f3e57506fda678e6a65e85a763f4eece653287e1ad44e167f9485e
F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
F ext/misc/rot13.c 51ac5f51e9d5fd811db58a9c23c628ad5f333c173f1fc53c8491a3603d38556c
F ext/misc/scrub.c 2a44b0d44c69584c0580ad2553f6290a307a49df4668941d2812135bfb96a946
F ext/misc/series.c 31437e7253fbcc4cc62e9d419b6528e1a83802e6abfd5b12c90d90e044d5dda3
F ext/misc/series.c 42f4d9acd8c497a147363dcd69208d09ba9173f448811ccfb8d26765394d3d25
F ext/misc/sha1.c cb5002148c2661b5946f34561701e9105e9d339b713ec8ac057fd888b196dcb9
F ext/misc/shathree.c fd22d70620f86a0467acfdd3acd8435d5cb54eb1e2d9ff36ae44e389826993df
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
@@ -1678,7 +1678,7 @@ F test/sync.test a619e407ede58a7b6e3e44375328628559fc9695a9c24c47cb5690a866b0031
F test/sync2.test 06152269ed73128782c450c355988fe8dd794d305833af75e1a5e79edd4dae47
F test/syscall.test a067468b43b8cb2305e9f9fe414e5f40c875bb5d2cba5f00b8154396e95fcf37
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
F test/tabfunc01.test f9a612a6a021e9f0d8df7d182762fea55dff671c29a5a15100cad4388e08151c
F test/tabfunc01.test be36fc7f6d221482216f7f1032d6a4595ac542cf427132f4728a65697830ab9c
F test/table.test e87294bf1c80bfd7792142b84ab32ea5beb4f3f71e535d7fb263a6b2068377bf
F test/tableapi.test e37c33e6be2276e3a96bb54b00eea7f321277115d10e5b30fdb52a112b432750
F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
@@ -2169,8 +2169,9 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
P f7cab028c97b2c9ac9b561a4c33475aadd3f71cbd48ec4dd6e09c1bb19ec00cb
R 111bf66cecc5d4d8dbf7a19268f337ee
U stephan
Z 6cfd7d970babb3978d704fe1297979dc
P acaaf8cce9e780bf937436b4c987ed284aa0bba6f5ca9508d9dbad598af2991d 86dcc68d1816d970d8d5fb158696c3fe6c5ddcac2c255823110d656372503885
R 762cba5cbbf74e8c3ba8ca4699030460
T +closed 86dcc68d1816d970d8d5fb158696c3fe6c5ddcac2c255823110d656372503885
U drh
Z e1db3f0aba6773a2fe5bbd6bf6414138
# Remove this line to create a well-formed Fossil manifest.

View File

@@ -1 +1 @@
acaaf8cce9e780bf937436b4c987ed284aa0bba6f5ca9508d9dbad598af2991d
9041ca8543ff3d2a617ce4df696aa53269a7803716caaf55a8e61e59ee667040

View File

@@ -513,6 +513,159 @@ do_execsql_test 1200 {
FROM t1 RIGHT JOIN generate_series(1,3,1) AS t2 USING(value);
} {1 1 2 2 3 3}
#--------------------------------------------------------------------------
# New test cases for generate_series associated with the refactor on branch
# serices-refactor circa 2025-09-27.
#
do_execsql_test 1300 {
SELECT value
FROM generate_series(-9223372036854775808, 9223372036854775807, 2)
WHERE value BETWEEN 1 AND 5;
} {2 4}
do_execsql_test 1301 {
SELECT value
FROM generate_series(-9223372036854775808, 9223372036854775807, 2)
WHERE value BETWEEN 0 AND 6;
} {0 2 4 6}
do_execsql_test 1302 {
SELECT value
FROM generate_series(-9223372036854775808, 9223372036854775807, 2)
WHERE value BETWEEN 0.5 AND 6.25;
} {2 4 6}
do_execsql_test 1303 {
SELECT value
FROM generate_series(-9223372036854775808, 9223372036854775807, 2)
WHERE value BETWEEN 9223372036854775803 AND 9223372036854775807
} {9223372036854775804 9223372036854775806}
do_execsql_test 1304 {
SELECT value
FROM generate_series(-9223372036854775808, 9223372036854775807, 2)
WHERE value BETWEEN 9223372036854775803 AND 9223372036854775807
ORDER BY value DESC;
} {9223372036854775806 9223372036854775804}
do_execsql_test 1310 {
SELECT value FROM generate_series(0) LIMIT 5;
} {0 1 2 3 4}
do_execsql_test 1311 {
SELECT value FROM generate_series(5) LIMIT 5;
} {5 6 7 8 9}
do_execsql_test 1312 {
SELECT value FROM generate_series(0) WHERE stop=6;
} {0 1 2 3 4 5 6}
do_execsql_test 1313 {
SELECT value FROM generate_series(1,10) WHERE step IS NULL;
} {}
do_execsql_test 1314 {
SELECT value FROM generate_series(0xfffffffd);
} {4294967293 4294967294 4294967295}
do_execsql_test 1315 {
SELECT value FROM generate_series(4,3,1);
} {}
do_execsql_test 1316 {
SELECT value FROM generate_series(3,4,-1);
} {}
do_execsql_test 1320 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value=9.2234e18;
} {}
do_execsql_test 1321 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value=-9.2234e18;
} {}
do_execsql_test 1322 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value>9223372036854775807;
} {}
do_execsql_test 1323 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value>9223372036854775806;
} {9223372036854775807}
do_execsql_test 1324 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value>=9223372036854775807;
} {9223372036854775807}
do_execsql_test 1330 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value<-9223372036854775808;
} {}
do_execsql_test 1331 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value<=-9223372036854775808;
} {-9223372036854775808}
do_execsql_test 1332 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value<-9223372036854775807;
} {-9223372036854775808}
do_execsql_test 1333 {
SELECT value FROM generate_series(-9223372036854775808,+9223372036854775807)
WHERE value BETWEEN 4 AND 1;
} {}
do_execsql_test 1340 {
SELECT value FROM generate_series(100,0,-10)
WHERE value BETWEEN 33 AND 66;
} {60 50 40}
do_execsql_test 1341 {
SELECT value FROM generate_series(100,0,-10)
WHERE value BETWEEN 33 AND 60;
} {60 50 40}
do_execsql_test 1342 {
SELECT value FROM generate_series(100,0,-10)
WHERE value BETWEEN 33 AND 59;
} {50 40}
do_execsql_test 1343 {
SELECT value
FROM generate_series(-9223372036854760000,-9223372036854775808,-10000);
} {-9223372036854760000 -9223372036854770000}
do_execsql_test 1344 {
SELECT value
FROM generate_series(-9223372036854760000,-9223372036854775808,-10000)
WHERE value < -9223372036854770001;
} {}
do_execsql_test 1345 {
SELECT value
FROM generate_series(9223372036854760000,9223372036854775807,10000);
} {9223372036854760000 9223372036854770000}
do_execsql_test 1346 {
SELECT value
FROM generate_series(9223372036854760000,9223372036854775807,10000)
WHERE value > 9223372036854770001;
} {}
do_execsql_test 1350 {
SELECT value FROM generate_series(100,0,-10)
WHERE value BETWEEN 33 AND 38;
} {}
do_execsql_test 1351 {
SELECT value FROM generate_series(0,100,+10)
WHERE value BETWEEN 33 AND 38;
} {}
do_execsql_test 1360 {
SELECT * FROM generate_series(0,-9223372036854775808,-9223372036854775808);
} {0 -9223372036854775808}
do_execsql_test 1361 {
SELECT * FROM generate_series(0,-9223372036854775808,-9223372036854775808)
LIMIT 1 OFFSET 0;
} {0}
do_execsql_test 1362 {
SELECT * FROM generate_series(0,-9223372036854775808,-9223372036854775808)
ORDER BY value ASC;
} {-9223372036854775808 0}
do_execsql_test 1363 {
SELECT * FROM generate_series(0,-9223372036854775808,-9223372036854775808)
ORDER BY value ASC LIMIT 10 OFFSET 1;
} {0}
do_execsql_test 1364 {
SELECT * FROM generate_series(0,-9223372036854775808,-9223372036854775808)
ORDER BY value ASC LIMIT 10 OFFSET 40000000;
} {}
# Free up memory allocations
intarray_addr
int64array_addr