diff --git a/0008-Cleaner_separation_of_the_STAT4-specific_logic.patch b/0008-Cleaner_separation_of_the_STAT4-specific_logic.patch new file mode 100644 index 0000000000000000000000000000000000000000..85d434beade5dde8da5eb8702612f5879813b933 --- /dev/null +++ b/0008-Cleaner_separation_of_the_STAT4-specific_logic.patch @@ -0,0 +1,352 @@ +From afa1ecac9b6fba76861dca85b211ca1022f9e378 Mon Sep 17 00:00:00 2001 +From: drh +Date: Mon, 9 Mar 2020 18:26:11 +0000 +Subject: [PATCH] Cleaner separation of the STAT4-specific logic in the + implementation of ANALYZE. +patch reference: +https://www.sqlite.org/src/info/3df07e5a9a3781a4 + +FossilOrigin-Name: 3df07e5a9a3781a4cf866fc6ee0e5c6f9cd7ca35ce0a6eb3aa7f5f3502e0ffae +--- + src/analyze.c | 113 +++++++++++++++++++++++++------------------------- + 1 file changed, 56 insertions(+), 57 deletions(-) + +diff --git a/src/analyze.c b/src/analyze.c +index 2a071ef..daf3af7 100644 +--- a/src/analyze.c ++++ b/src/analyze.c +@@ -256,9 +256,9 @@ static void openStatTable( + ** share an instance of the following structure to hold their state + ** information. + */ +-typedef struct Stat4Accum Stat4Accum; +-typedef struct Stat4Sample Stat4Sample; +-struct Stat4Sample { ++typedef struct StatAccum StatAccum; ++typedef struct StatSample StatSample; ++struct StatSample { + tRowcnt *anEq; /* sqlite_stat4.nEq */ + tRowcnt *anDLt; /* sqlite_stat4.nDLt */ + #ifdef SQLITE_ENABLE_STAT4 +@@ -273,27 +273,30 @@ struct Stat4Sample { + u32 iHash; /* Tiebreaker hash */ + #endif + }; +-struct Stat4Accum { ++struct StatAccum { ++ sqlite3 *db; /* Database connection, for malloc() */ + tRowcnt nRow; /* Number of rows in the entire table */ +- tRowcnt nPSample; /* How often to do a periodic sample */ + int nCol; /* Number of columns in index + pk/rowid */ + int nKeyCol; /* Number of index columns w/o the pk/rowid */ ++ StatSample current; /* Current row as a StatSample */ ++#ifdef SQLITE_ENABLE_STAT4 ++ tRowcnt nPSample; /* How often to do a periodic sample */ + int mxSample; /* Maximum number of samples to accumulate */ + Stat4Sample current; /* Current row as a Stat4Sample */ + u32 iPrn; /* Pseudo-random number used for sampling */ +- Stat4Sample *aBest; /* Array of nCol best samples */ ++ StatSample *aBest; /* Array of nCol best samples */ + int iMin; /* Index in a[] of entry with minimum score */ + int nSample; /* Current number of samples */ + int nMaxEqZero; /* Max leading 0 in anEq[] for any a[] entry */ + int iGet; /* Index of current sample accessed by stat_get() */ +- Stat4Sample *a; /* Array of mxSample Stat4Sample objects */ +- sqlite3 *db; /* Database connection, for malloc() */ ++ StatSample *a; /* Array of mxSample StatSample objects */ ++#endif + }; + +-/* Reclaim memory used by a Stat4Sample ++/* Reclaim memory used by a StatSample + */ + #ifdef SQLITE_ENABLE_STAT4 +-static void sampleClear(sqlite3 *db, Stat4Sample *p){ ++static void sampleClear(sqlite3 *db, StatSample *p){ + assert( db!=0 ); + if( p->nRowid ){ + sqlite3DbFree(db, p->u.aRowid); +@@ -305,7 +308,7 @@ static void sampleClear(sqlite3 *db, Stat4Sample *p){ + /* Initialize the BLOB value of a ROWID + */ + #ifdef SQLITE_ENABLE_STAT4 +-static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){ ++static void sampleSetRowid(sqlite3 *db, StatSample *p, int n, const u8 *pData){ + assert( db!=0 ); + if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); + p->u.aRowid = sqlite3DbMallocRawNN(db, n); +@@ -321,7 +324,7 @@ static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){ + /* Initialize the INTEGER value of a ROWID. + */ + #ifdef SQLITE_ENABLE_STAT4 +-static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){ ++static void sampleSetRowidInt64(sqlite3 *db, StatSample *p, i64 iRowid){ + assert( db!=0 ); + if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid); + p->nRowid = 0; +@@ -334,7 +337,7 @@ static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){ + ** Copy the contents of object (*pFrom) into (*pTo). + */ + #ifdef SQLITE_ENABLE_STAT4 +-static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ ++static void sampleCopy(StatAccum *p, StatSample *pTo, StatSample *pFrom){ + pTo->isPSample = pFrom->isPSample; + pTo->iCol = pFrom->iCol; + pTo->iHash = pFrom->iHash; +@@ -352,8 +355,8 @@ static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){ + /* + ** Reclaim all memory of a Stat4Accum structure. + */ +-static void stat4Destructor(void *pOld){ +- Stat4Accum *p = (Stat4Accum*)pOld; ++ static void statAccumDestructor(void *pOld){ ++ StatAccum *p = (StatAccum*)pOld; + #ifdef SQLITE_ENABLE_STAT4 + int i; + for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i); +@@ -381,8 +384,8 @@ static void stat4Destructor(void *pOld){ + ** PRIMARY KEY of the table. The covering index that implements the + ** original WITHOUT ROWID table as N==K as a special case. + ** +-** This routine allocates the Stat4Accum object in heap memory. The return +-** value is a pointer to the Stat4Accum object. The datatype of the ++** This routine allocates the StatAccum object in heap memory. The return ++** value is a pointer to the StatAccum object. The datatype of the + ** return value is BLOB, but it is really just a pointer to the Stat4Accum + ** object. + */ +@@ -391,7 +394,7 @@ static void statInit( + int argc, + sqlite3_value **argv + ){ +- Stat4Accum *p; ++ StatAccum *p; + int nCol; /* Number of columns in index being sampled */ + int nKeyCol; /* Number of key columns */ + int nColUp; /* nCol rounded up for alignment */ +@@ -410,13 +413,13 @@ static void statInit( + assert( nKeyCol<=nCol ); + assert( nKeyCol>0 ); + +- /* Allocate the space required for the Stat4Accum object */ ++ /* Allocate the space required for the StatAccum object */ + n = sizeof(*p) +- + sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */ +- + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */ ++ + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ ++ + sizeof(tRowcnt)*nColUp /* StatAccum.anDLt */ + #ifdef SQLITE_ENABLE_STAT4 +- + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */ +- + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */ ++ + sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ ++ + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ + + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample) + #endif + ; +@@ -445,8 +448,8 @@ static void statInit( + p->current.anLt = &p->current.anEq[nColUp]; + p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]); + +- /* Set up the Stat4Accum.a[] and aBest[] arrays */ +- p->a = (struct Stat4Sample*)&p->current.anLt[nColUp]; ++ /* Set up the StatAccum.a[] and aBest[] arrays */ ++ p->a = (struct StatSample*)&p->current.anLt[nColUp]; + p->aBest = &p->a[mxSample]; + pSpace = (u8*)(&p->a[mxSample+nCol]); + for(i=0; i<(mxSample+nCol); i++){ +@@ -466,7 +469,7 @@ static void statInit( + ** only the pointer (the 2nd parameter) matters. The size of the object + ** (given by the 3rd parameter) is never used and can be any positive + ** value. */ +- sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor); ++ sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor); + } + static const FuncDef statInitFuncdef = { + 2+IsStat4, /* nArg */ +@@ -493,9 +496,9 @@ static const FuncDef statInitFuncdef = { + ** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid. + */ + static int sampleIsBetterPost( +- Stat4Accum *pAccum, +- Stat4Sample *pNew, +- Stat4Sample *pOld ++ StatAccum *pAccum, ++ StatSample *pNew, ++ StatSample *pOld + ){ + int nCol = pAccum->nCol; + int i; +@@ -517,9 +520,9 @@ static int sampleIsBetterPost( + ** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid. + */ + static int sampleIsBetter( +- Stat4Accum *pAccum, +- Stat4Sample *pNew, +- Stat4Sample *pOld ++ StatAccum *pAccum, ++ StatSample *pNew, ++ StatSample *pOld + ){ + tRowcnt nEqNew = pNew->anEq[pNew->iCol]; + tRowcnt nEqOld = pOld->anEq[pOld->iCol]; +@@ -539,21 +542,21 @@ static int sampleIsBetter( + ** Copy the contents of sample *pNew into the p->a[] array. If necessary, + ** remove the least desirable sample from p->a[] to make room. + */ +-static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ +- Stat4Sample *pSample = 0; ++static void sampleInsert(StatAccum *p, StatSample *pNew, int nEqZero){ ++ StatSample *pSample = 0; + int i; + + assert( IsStat4 || nEqZero==0 ); + +- /* Stat4Accum.nMaxEqZero is set to the maximum number of leading 0 +- ** values in the anEq[] array of any sample in Stat4Accum.a[]. In ++ /* StatAccum.nMaxEqZero is set to the maximum number of leading 0 ++ ** values in the anEq[] array of any sample in StatAccum.a[]. In + ** other words, if nMaxEqZero is n, then it is guaranteed that there + ** are no samples with Stat4Sample.anEq[m]==0 for (m>=n). */ + if( nEqZero>p->nMaxEqZero ){ + p->nMaxEqZero = nEqZero; + } + if( pNew->isPSample==0 ){ +- Stat4Sample *pUpgrade = 0; ++ StatSample *pUpgrade = 0; + assert( pNew->anEq[pNew->iCol]>0 ); + + /* This sample is being added because the prefix that ends in column +@@ -562,7 +565,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ + ** this one. Instead, upgrade the priority of the highest priority + ** existing sample that shares this prefix. */ + for(i=p->nSample-1; i>=0; i--){ +- Stat4Sample *pOld = &p->a[i]; ++ StatSample *pOld = &p->a[i]; + if( pOld->anEq[pNew->iCol]==0 ){ + if( pOld->isPSample ) return; + assert( pOld->iCol>pNew->iCol ); +@@ -581,7 +584,7 @@ static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){ + + /* If necessary, remove sample iMin to make room for the new sample. */ + if( p->nSample>=p->mxSample ){ +- Stat4Sample *pMin = &p->a[p->iMin]; ++ StatSample *pMin = &p->a[p->iMin]; + tRowcnt *anEq = pMin->anEq; + tRowcnt *anLt = pMin->anLt; + tRowcnt *anDLt = pMin->anDLt; +@@ -623,6 +626,7 @@ find_new_min: + } + } + #endif /* SQLITE_ENABLE_STAT4 */ ++#ifdef SQLITE_ENABLE_STAT4 + + /* + ** Field iChng of the index being scanned has changed. So at this point +@@ -630,14 +634,13 @@ find_new_min: + ** index. The value of anEq[iChng] and subsequent anEq[] elements are + ** correct at this point. + */ +-static void samplePushPrevious(Stat4Accum *p, int iChng){ +-#ifdef SQLITE_ENABLE_STAT4 ++static void samplePushPrevious(StatAccum *p, int iChng){ + int i; + + /* Check if any samples from the aBest[] array should be pushed + ** into IndexSample.a[] at this point. */ + for(i=(p->nCol-2); i>=iChng; i--){ +- Stat4Sample *pBest = &p->aBest[i]; ++ StatSample *pBest = &p->aBest[i]; + pBest->anEq[i] = p->current.anEq[i]; + if( p->nSamplemxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){ + sampleInsert(p, pBest, i); +@@ -661,19 +664,14 @@ static void samplePushPrevious(Stat4Accum *p, int iChng){ + } + p->nMaxEqZero = iChng; + } +-#endif +- +-#ifndef SQLITE_ENABLE_STAT4 +- UNUSED_PARAMETER( p ); +- UNUSED_PARAMETER( iChng ); +-#endif + } ++#endif /* SQLITE_ENABLE_STAT4 */ + + /* + ** Implementation of the stat_push SQL function: stat_push(P,C,R) + ** Arguments: + ** +-** P Pointer to the Stat4Accum object created by stat_init() ++** P Pointer to the StatAccum object created by stat_init() + ** C Index of left-most column to differ from previous row + ** R Rowid for the current row. Might be a key record for + ** WITHOUT ROWID tables. +@@ -693,7 +691,7 @@ static void statPush( + int i; + + /* The three function arguments */ +- Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]); ++ StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]); + int iChng = sqlite3_value_int(argv[1]); + + UNUSED_PARAMETER( argc ); +@@ -706,8 +704,9 @@ static void statPush( + for(i=0; inCol; i++) p->current.anEq[i] = 1; + }else{ + /* Second and subsequent calls get processed here */ ++#ifdef SQLITE_ENABLE_STAT4 + samplePushPrevious(p, iChng); +- ++#endif + /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply + ** to the current row of the index. */ + for(i=0; iiGet = 0; + } + if( p->iGetnSample ){ +- Stat4Sample *pS = p->a + p->iGet; ++ StatSample *pS = p->a + p->iGet; + if( pS->nRowid==0 ){ + sqlite3_result_int64(context, pS->u.iRowid); + }else{ +@@ -951,7 +950,7 @@ static void analyzeOneTable( + int iDb; /* Index of database containing pTab */ + u8 needTableCnt = 1; /* True to count the table */ + int regNewRowid = iMem++; /* Rowid for the inserted record */ +- int regStat4 = iMem++; /* Register to hold Stat4Accum object */ ++ int regStat4 = iMem++; /* Register to hold StatAccum object */ + int regChng = iMem++; /* Index of changed index field */ + #ifdef SQLITE_ENABLE_STAT4 + int regRowid = iMem++; /* Rowid argument passed to stat_push() */ +-- +2.23.0 + diff --git a/0009-Background_work_for_experiments.patch b/0009-Background_work_for_experiments.patch new file mode 100644 index 0000000000000000000000000000000000000000..0ba191dac60af8fc4d112800ca6288911a72dc59 --- /dev/null +++ b/0009-Background_work_for_experiments.patch @@ -0,0 +1,211 @@ +From 9d33d9e7ac064aed9de16d25544c7f45dcb76465 Mon Sep 17 00:00:00 2001 +From: drh +Date: Tue, 10 Mar 2020 01:24:13 +0000 +Subject: [PATCH] Background work for experiments trying to enhance ANALYZE so + that it runs off of samples of the entire index and does not need to read the + entire index. + +patch reference: +https://www.sqlite.org/src/info/29d1cc5c3619a882 + +FossilOrigin-Name: 29d1cc5c3619a88229f18c3c8131228f8a2d151ac3d9203f0c7fc538a996ecec +--- + src/analyze.c | 90 ++++++++++++++++++++++++++++----------------------- + 1 file changed, 50 insertions(+), 40 deletions(-) + +diff --git a/src/analyze.c b/src/analyze.c +index daf3af7..11ac430 100644 +--- a/src/analyze.c ++++ b/src/analyze.c +@@ -188,6 +188,11 @@ static void openStatTable( + Vdbe *v = sqlite3GetVdbe(pParse); + int aRoot[ArraySize(aTable)]; + u8 aCreateTbl[ArraySize(aTable)]; ++#ifdef SQLITE_ENABLE_STAT4 ++ const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1; ++#else ++ const int nToOpen = 1; ++#endif + + if( v==0 ) return; + assert( sqlite3BtreeHoldsAllMutexes(db) ); +@@ -200,8 +205,9 @@ static void openStatTable( + for(i=0; izDbSName))==0 ){ +- if( aTable[i].zCols ){ ++ if( iregRoot. This is important +@@ -217,7 +223,6 @@ static void openStatTable( + ** associated with the table zWhere. If zWhere is NULL, delete the + ** entire contents of the table. */ + aRoot[i] = pStat->tnum; +- aCreateTbl[i] = 0; + sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); + if( zWhere ){ + sqlite3NestedParse(pParse, +@@ -236,7 +241,7 @@ static void openStatTable( + } + + /* Open the sqlite_stat[134] tables for writing. */ +- for(i=0; aTable[i].zCols; i++){ ++ for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i); +- for(i=0; imxSample; i++) sampleClear(p->db, p->a+i); +- sampleClear(p->db, &p->current); ++ if( p->mxSample ){ ++ int i; ++ for(i=0; inCol; i++) sampleClear(p->db, p->aBest+i); ++ for(i=0; imxSample; i++) sampleClear(p->db, p->a+i); ++ sampleClear(p->db, &p->current); ++ } + #endif + sqlite3DbFree(p->db, p); + } +@@ -401,7 +408,7 @@ static void statInit( + int n; /* Bytes of space to allocate */ + sqlite3 *db; /* Database connection */ + #ifdef SQLITE_ENABLE_STAT4 +- int mxSample = SQLITE_STAT4_SAMPLES; ++ int mxSample = sqlite3_value_int(argv[2]) ? SQLITE_STAT4_SAMPLES : 0; + #endif + + /* Decode the three function arguments */ +@@ -438,7 +445,7 @@ static void statInit( + p->current.anEq = &p->current.anDLt[nColUp]; + + #ifdef SQLITE_ENABLE_STAT4 +- { ++ if( mxSample ){ + u8 *pSpace; /* Allocated space not yet assigned */ + int i; /* Used to iterate through p->aSample[] */ + +@@ -705,7 +712,7 @@ static void statPush( + }else{ + /* Second and subsequent calls get processed here */ + #ifdef SQLITE_ENABLE_STAT4 +- samplePushPrevious(p, iChng); ++ if( p->mxSample ) samplePushPrevious(p, iChng); + #endif + /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply + ** to the current row of the index. */ +@@ -715,26 +722,22 @@ static void statPush( + for(i=iChng; inCol; i++){ + p->current.anDLt[i]++; + #ifdef SQLITE_ENABLE_STAT4 +- p->current.anLt[i] += p->current.anEq[i]; ++ if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i]; + #endif + p->current.anEq[i] = 1; + } + } + p->nRow++; + #ifdef SQLITE_ENABLE_STAT4 +- if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ +- sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); +- }else{ +- sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]), +- sqlite3_value_blob(argv[2])); +- } +- p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; +-#endif +- +-#ifdef SQLITE_ENABLE_STAT4 +- { +- tRowcnt nLt = p->current.anLt[p->nCol-1]; +- ++ if( p->mxSample ){ ++ tRowcnt nLt; ++ if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){ ++ sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2])); ++ }else{ ++ sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),sqlite3_value_blob(argv[2])); ++ } ++ p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345; ++ nLt = p->current.anLt[p->nCol-1]; + /* Check if this is to be a periodic sample. If so, add it. */ + if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){ + p->current.isPSample = 1; +@@ -804,6 +807,7 @@ static void statGet( + || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT + || eCall==STAT_GET_NDLT + ); ++ assert( eCall==STAT_GET_STAT1 || p->mxSample ); + if( eCall==STAT_GET_STAT1 ) + #else + assert( argc==1 ); +@@ -1089,7 +1093,11 @@ static void analyzeOneTable( + ** The third argument is only used for STAT4 + */ + #ifdef SQLITE_ENABLE_STAT4 +- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3); ++ if( OptimizationEnabled(db, SQLITE_Stat4) ){ ++ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3); ++ }else{ ++ sqlite3VdbeAddOp2(v, OP_Integer, 0, regStat4+3); ++ } + #endif + sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1); + sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2); +@@ -1171,21 +1179,23 @@ static void analyzeOneTable( + ** if !eof(csr) goto next_row; + */ + #ifdef SQLITE_ENABLE_STAT4 +- assert( regRowid==(regStat4+2) ); +- if( HasRowid(pTab) ){ +- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); +- }else{ +- Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); +- int j, k, regKey; +- regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); +- for(j=0; jnKeyCol; j++){ +- k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); +- assert( k>=0 && knColumn ); +- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); +- VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName)); ++ if( OptimizationEnabled(db, SQLITE_Stat4) ){ ++ assert( regRowid==(regStat4+2) ); ++ if( HasRowid(pTab) ){ ++ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid); ++ }else{ ++ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable); ++ int j, k, regKey; ++ regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol); ++ for(j=0; jnKeyCol; j++){ ++ k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]); ++ assert( k>=0 && knColumn ); ++ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j); ++ VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName)); ++ } ++ sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); ++ sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); + } +- sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid); +- sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol); + } + #endif + assert( regChng==(regStat4+1) ); +@@ -1206,7 +1216,7 @@ static void analyzeOneTable( + + /* Add the entries to the stat4 table. */ + #ifdef SQLITE_ENABLE_STAT4 +- { ++ if( OptimizationEnabled(db, SQLITE_Stat4) ){ + int regEq = regStat1; + int regLt = regStat1+1; + int regDLt = regStat1+2; +-- +2.23.0 + diff --git a/0010-Fix_handling_of_window_functions.patch b/0010-Fix_handling_of_window_functions.patch new file mode 100644 index 0000000000000000000000000000000000000000..47650c9c0c9bfd941dc4937ffea787bca6b124de --- /dev/null +++ b/0010-Fix_handling_of_window_functions.patch @@ -0,0 +1,145 @@ +From 553948e51433715f32d57e6977db6e0992b7f8cd Mon Sep 17 00:00:00 2001 +From: dan +Date: Mon, 16 Mar 2020 18:52:53 +0000 +Subject: [PATCH] Fix handling of window functions in aggregate queries that + have no GROUP BY clause. Also remove a faulty assert causing the error + reported in [618156e3]. + +patch reference: +https://www.sqlite.org/src/info/38e3dd389d142e52 + +--- + src/select.c | 1 - + src/window.c | 2 ++ + test/window4.tcl | 14 ++++++++++++++ + test/window4.test | 14 ++++++++++++++ + test/window9.test | 33 +++++++++++++++++++++++++++++++++ + 5 files changed, 63 insertions(+), 1 deletion(-) + +diff --git a/src/select.c b/src/select.c +index 7f88e35..f61fbce 100644 +--- a/src/select.c ++++ b/src/select.c +@@ -103,7 +103,6 @@ static void clearSelect(sqlite3 *db, Select *p, int bFree){ + if( OK_IF_ALWAYS_TRUE(p->pWinDefn) ){ + sqlite3WindowListDelete(db, p->pWinDefn); + } +- assert( p->pWin==0 ); + #endif + if( OK_IF_ALWAYS_TRUE(p->pWith) ) sqlite3WithDelete(db, p->pWith); + if( bFree ) sqlite3DbFreeNN(db, p); +diff --git a/src/window.c b/src/window.c +index a72ec0d..9bb3217 100644 +--- a/src/window.c ++++ b/src/window.c +@@ -933,6 +933,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ + Window *pMWin = p->pWin; /* Master window object */ + Window *pWin; /* Window object iterator */ + Table *pTab; ++ u32 selFlags = p->selFlags; + + pTab = sqlite3DbMallocZero(db, sizeof(Table)); + if( pTab==0 ){ +@@ -1022,6 +1023,7 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ + sqlite3SrcListAssignCursors(pParse, p->pSrc); + pSub->selFlags |= SF_Expanded; + pTab2 = sqlite3ResultSetOfSelect(pParse, pSub, SQLITE_AFF_NONE); ++ pSub->selFlags |= (selFlags & SF_Aggregate); + if( pTab2==0 ){ + /* Might actually be some other kind of error, but in that case + ** pParse->nErr will be set, so if SQLITE_NOMEM is set, we will get +diff --git a/test/window4.tcl b/test/window4.tcl +index 1b2b2ef..0b91d76 100644 +--- a/test/window4.tcl ++++ b/test/window4.tcl +@@ -385,6 +385,20 @@ execsql_test 11.4 { + ) sub; + } + ++execsql_test 11.5 { ++ SELECT sum( min(t) ) OVER () FROM t8 GROUP BY total; ++} ++execsql_test 11.5 { ++ SELECT sum( max(t) ) OVER () FROM t8 GROUP BY total; ++} ++ ++execsql_test 11.7 { ++ SELECT sum( min(t) ) OVER () FROM t8; ++} ++execsql_test 11.8 { ++ SELECT sum( max(t) ) OVER () FROM t8; ++} ++ + execsql_test 12.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER); +diff --git a/test/window4.test b/test/window4.test +index 6951a23..a0344e0 100644 +--- a/test/window4.test ++++ b/test/window4.test +@@ -1324,6 +1324,20 @@ do_execsql_test 11.4 { + ) sub; + } {0 1 2} + ++do_execsql_test 11.5 { ++ SELECT sum( min(t) ) OVER () FROM t8 GROUP BY total; ++} {5 5} ++ ++do_execsql_test 11.5 { ++ SELECT sum( max(t) ) OVER () FROM t8 GROUP BY total; ++} {10 10} ++do_execsql_test 11.7 { ++ SELECT sum( min(t) ) OVER () FROM t8; ++} {0} ++do_execsql_test 11.8 { ++ SELECT sum( max(t) ) OVER () FROM t8; ++} {10} ++ + do_execsql_test 12.0 { + DROP TABLE IF EXISTS t2; + CREATE TABLE t2(a INTEGER); +diff --git a/test/window9.test b/test/window9.test +index adfeaba..686afc9 100644 +--- a/test/window9.test ++++ b/test/window9.test +@@ -232,4 +232,37 @@ do_execsql_test 7.4 { + 7.2 8.75 10.0 11.0 15.0 + } + ++#------------------------------------------------------------------------- ++reset_db ++do_execsql_test 8.1.1 { ++ CREATE TABLE t1(a, b); ++ INSERT INTO t1 VALUES(1, 2), (3, 4); ++ SELECT min( sum(a) ) OVER () FROM t1; ++} {4} ++ ++do_execsql_test 8.1.2 { ++ SELECT min( sum(a) ) OVER () FROM t1 GROUP BY a; ++} {1 1} ++ ++do_execsql_test 8.2 { ++ CREATE VIEW v1 AS ++ SELECT 0 AS x ++ UNION ++ SELECT count() OVER() FROM (SELECT 0) ++ ORDER BY 1 ++ ; ++} ++ ++do_catchsql_test 8.3 { ++ SELECT min( max((SELECT x FROM v1)) ) OVER() ++} {1 {misuse of aggregate: max()}} ++ ++do_execsql_test 8.4 { ++ SELECT( ++ SELECT x UNION ++ SELECT sum( avg((SELECT x FROM v1)) ) OVER() ++ ) ++ FROM v1; ++} {0.0} ++ + finish_test +-- +2.23.0 + diff --git a/0011-Remove_the_SQLITE_OMIT_BTREECOUNT_option.patch b/0011-Remove_the_SQLITE_OMIT_BTREECOUNT_option.patch new file mode 100644 index 0000000000000000000000000000000000000000..5eb6cc01dc61451f7f0933f3b2490e5062094d2b --- /dev/null +++ b/0011-Remove_the_SQLITE_OMIT_BTREECOUNT_option.patch @@ -0,0 +1,168 @@ +From e50478d7279b72a9df4836729c9974abfbce08ff Mon Sep 17 00:00:00 2001 +From: drh +Date: Tue, 17 Mar 2020 13:41:51 +0000 +Subject: [PATCH] Remove the SQLITE_OMIT_BTREECOUNT option. Btree count is + required. + +patch reference: +https://www.sqlite.org/src/info/a9bfa47aeea27e91 +--- + src/analyze.c | 14 ++++++++------ + src/btree.c | 2 -- + src/btree.h | 2 -- + src/ctime.c | 3 --- + src/pragma.c | 2 -- + src/select.c | 7 ++----- + src/vdbe.c | 2 -- + 7 files changed, 10 insertions(+), 22 deletions(-) + +diff --git a/src/analyze.c b/src/analyze.c +index 11ac430..fa01925 100644 +--- a/src/analyze.c ++++ b/src/analyze.c +@@ -408,7 +408,8 @@ static void statInit( + int n; /* Bytes of space to allocate */ + sqlite3 *db; /* Database connection */ + #ifdef SQLITE_ENABLE_STAT4 +- int mxSample = sqlite3_value_int(argv[2]) ? SQLITE_STAT4_SAMPLES : 0; ++ /* Maximum number of samples. 0 if STAT4 data is not collected */ ++ int mxSample = sqlite3_value_int64(argv[2]) ? SQLITE_STAT4_SAMPLES : 0; + #endif + + /* Decode the three function arguments */ +@@ -423,13 +424,14 @@ static void statInit( + /* Allocate the space required for the StatAccum object */ + n = sizeof(*p) + + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */ +- + sizeof(tRowcnt)*nColUp /* StatAccum.anDLt */ ++ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */ + #ifdef SQLITE_ENABLE_STAT4 +- + sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ +- + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ +- + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample) ++ if( mxSample ){ ++ n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */ ++ + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */ ++ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample); ++ } + #endif +- ; + db = sqlite3_context_db_handle(context); + p = sqlite3DbMallocZero(db, n); + if( p==0 ){ +diff --git a/src/btree.c b/src/btree.c +index be5d639..78a99d2 100644 +--- a/src/btree.c ++++ b/src/btree.c +@@ -9514,7 +9514,6 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){ + return rc; + } + +-#ifndef SQLITE_OMIT_BTREECOUNT + /* + ** The first argument, pCur, is a cursor opened on some b-tree. Count the + ** number of entries in the b-tree and write the result to *pnEntry. +@@ -9587,7 +9586,6 @@ int sqlite3BtreeCount(sqlite3 *db, BtCursor *pCur, i64 *pnEntry){ + /* An error has occurred. Return an error code. */ + return rc; + } +-#endif + + /* + ** Return the pager associated with a BTree. This routine is used for +diff --git a/src/btree.h b/src/btree.h +index 4bd41f7..2085c07 100644 +--- a/src/btree.h ++++ b/src/btree.h +@@ -336,9 +336,7 @@ int sqlite3BtreeCursorIsValid(BtCursor*); + #endif + int sqlite3BtreeCursorIsValidNN(BtCursor*); + +-#ifndef SQLITE_OMIT_BTREECOUNT + int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*); +-#endif + + #ifdef SQLITE_TEST + int sqlite3BtreeCursorInfo(BtCursor*, int*, int); +diff --git a/src/ctime.c b/src/ctime.c +index 013a0c2..aeaaa4f 100644 +--- a/src/ctime.c ++++ b/src/ctime.c +@@ -514,9 +514,6 @@ static const char * const sqlite3azCompileOpt[] = { + #if SQLITE_OMIT_BLOB_LITERAL + "OMIT_BLOB_LITERAL", + #endif +-#if SQLITE_OMIT_BTREECOUNT +- "OMIT_BTREECOUNT", +-#endif + #if SQLITE_OMIT_CAST + "OMIT_CAST", + #endif +diff --git a/src/pragma.c b/src/pragma.c +index 4d33e8c..0de9dc3 100644 +--- a/src/pragma.c ++++ b/src/pragma.c +@@ -1729,7 +1729,6 @@ void sqlite3Pragma( + } + sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v); + sqlite3VdbeJumpHere(v, loopTop-1); +-#ifndef SQLITE_OMIT_BTREECOUNT + if( !isQuick ){ + sqlite3VdbeLoadString(v, 2, "wrong # of entries in index "); + for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ +@@ -1743,7 +1742,6 @@ void sqlite3Pragma( + sqlite3VdbeJumpHere(v, addr); + } + } +-#endif /* SQLITE_OMIT_BTREECOUNT */ + } + } + { +diff --git a/src/select.c b/src/select.c +index f61fbce..0cc6b87 100644 +--- a/src/select.c ++++ b/src/select.c +@@ -6620,7 +6620,6 @@ int sqlite3Select( + + } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ + else { +-#ifndef SQLITE_OMIT_BTREECOUNT + Table *pTab; + if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ + /* If isSimpleCount() returns a pointer to a Table structure, then +@@ -6678,10 +6677,8 @@ int sqlite3Select( + sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); + explainSimpleCount(pParse, pTab, pBest); +- }else +-#endif /* SQLITE_OMIT_BTREECOUNT */ +- { +- int regAcc = 0; /* "populate accumulators" flag */ ++ }else{ ++ int regAcc = 0; /* "populate accumulators" flag */ + + /* If there are accumulator registers but no min() or max() functions + ** without FILTER clauses, allocate register regAcc. Register regAcc +diff --git a/src/vdbe.c b/src/vdbe.c +index b9d2d12..acb4a14 100644 +--- a/src/vdbe.c ++++ b/src/vdbe.c +@@ -3194,7 +3194,6 @@ case OP_MakeRecord: { + ** Store the number of entries (an integer value) in the table or index + ** opened by cursor P1 in register P2 + */ +-#ifndef SQLITE_OMIT_BTREECOUNT + case OP_Count: { /* out2 */ + i64 nEntry; + BtCursor *pCrsr; +@@ -3209,7 +3208,6 @@ case OP_Count: { /* out2 */ + pOut->u.i = nEntry; + goto check_for_interrupt; + } +-#endif + + /* Opcode: Savepoint P1 * * P4 * + ** +-- +2.23.0 + diff --git a/0012-Improvements_to_parse-tree_tracing_logic.patch b/0012-Improvements_to_parse-tree_tracing_logic.patch new file mode 100644 index 0000000000000000000000000000000000000000..7725b1a61839bc20915bcdd619b6ed8d686d8c79 --- /dev/null +++ b/0012-Improvements_to_parse-tree_tracing_logic.patch @@ -0,0 +1,47 @@ +From ca74fbf6f164ad6fa9c6bd79050dc57cdcd69388 Mon Sep 17 00:00:00 2001 +From: drh +Date: Sun, 24 May 2020 02:05:04 +0000 +Subject: [PATCH] Improvements to parse-tree tracing logic. No changes in + deliverable code. + +patch reference: +https://www.sqlite.org/src/info/f7e5a68a7ebbb97a + +FossilOrigin-Name: f7e5a68a7ebbb97a5beb050a75b3b4cf2fd6adc54653da993a8950fb3a5799f7 +--- + src/select.c | 2 +- + src/treeview.c | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/select.c b/src/select.c +index 0cc6b87..9a16e8e 100644 +--- a/src/select.c ++++ b/src/select.c +@@ -6379,7 +6379,7 @@ int sqlite3Select( + #if SELECTTRACE_ENABLED + if( sqlite3SelectTrace & 0x400 ){ + int ii; +- SELECTTRACE(0x400,pParse,p,("After aggregate analysis:\n")); ++ SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", &sAggInfo)); + sqlite3TreeViewSelect(0, p, 0); + for(ii=0; iiop==TK_AGG_FUNCTION ){ +- sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s", +- pExpr->op2, pExpr->u.zToken, zFlgs); ++ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q%s iAgg=%d agg=%p", ++ pExpr->op2, pExpr->u.zToken, zFlgs, ++ pExpr->iAgg, pExpr->pAggInfo); + }else if( pExpr->op2!=0 ){ + const char *zOp2; + char zBuf[8]; +-- +2.23.0 + diff --git a/0013-Fix-CVE-2020-13871.patch b/0013-Fix-CVE-2020-13871.patch new file mode 100644 index 0000000000000000000000000000000000000000..53c3fa7c173ee755c5a5fb8765bf37bf36555383 --- /dev/null +++ b/0013-Fix-CVE-2020-13871.patch @@ -0,0 +1,706 @@ +From 896366282dae3789fb277c2dad8660784a0895a3 Mon Sep 17 00:00:00 2001 +From: drh +Date: Sun, 7 Jun 2020 17:33:18 +0000 +Subject: [PATCH 1/3] Alternative fix to ticket [c8d3b9f0a750a529]: Prior to + deleting or modifying an Expr not that is referenced by an AggInfo, modify + the AggInfo to get its own copy of the original Expr. + +patch reference: +https://www.sqlite.org/src/info/6e6b3729e0549de0 + +FossilOrigin-Name: 7682d8a768fbccfe0cc956e9f6481637146e1ab9763b248ff11052761ce32e32 +--- + src/build.c | 12 +++- + src/expr.c | 61 ++++++++++++++++++- + src/prepare.c | 15 +++++ + src/select.c | 128 ++++++++++++++++++++++++---------------- + src/sqliteInt.h | 12 +++- + src/window.c | 4 ++ + test/window1.test | 146 ++++++++++++++++++++++++++++++++++++++++++++++ + 7 files changed, 324 insertions(+), 54 deletions(-) + +diff --git a/src/build.c b/src/build.c +index f1d078f..5846215 100644 +--- a/src/build.c ++++ b/src/build.c +@@ -208,11 +208,21 @@ void sqlite3FinishCoding(Parse *pParse){ + sqlite3AutoincrementBegin(pParse); + + /* Code constant expressions that where factored out of inner loops */ ++ /* Code constant expressions that where factored out of inner loops. ++ ** ++ ** The pConstExpr list might also contain expressions that we simply ++ ** want to keep around until the Parse object is deleted. Such ++ ** expressions have iConstExprReg==0. Do not generate code for ++ ** those expressions, of course. ++ */ + if( pParse->pConstExpr ){ + ExprList *pEL = pParse->pConstExpr; + pParse->okConstFactor = 0; + for(i=0; inExpr; i++){ +- sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg); ++ int iReg = pEL->a[i].u.iConstExprReg; ++ if( iReg>0 ){ ++ sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg); ++ } + } + } + +diff --git a/src/expr.c b/src/expr.c +index af779d3..07e1612 100644 +--- a/src/expr.c ++++ b/src/expr.c +@@ -52,7 +52,12 @@ char sqlite3ExprAffinity(Expr *pExpr){ + op = pExpr->op; + if( op==TK_SELECT ){ + assert( pExpr->flags&EP_xIsSelect ); +- return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); ++ if( ALWAYS(pExpr->x.pSelect) ++ && pExpr->x.pSelect->pEList ++ && ALWAYS(pExpr->x.pSelect->pEList->a[0].pExpr) ++ ){ ++ return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr); ++ } + } + if( op==TK_REGISTER ) op = pExpr->op2; + #ifndef SQLITE_OMIT_CAST +@@ -5658,6 +5663,60 @@ int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){ + #endif + return cnt.nThis>0 || cnt.nOther==0; + } ++/* ++** This is a Walker expression node callback. ++** For Expr nodes that contain pAggInfo pointers, make sure the AggInfo ++** object that is referenced does not refer directly to the Expr. If ++** it does, make a copy. This is done because the pExpr argument is ++** subject to change. ++** ++** The copy is stored on pParse->pConstExpr with a register number of 0. ++** This will cause the expression to be deleted automatically when the ++** Parse object is destroyed, but the zero register number means that it ++** will not generate any code in the preamble. ++*/ ++static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){ ++ if( ALWAYS(!ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced)) ++ && pExpr->pAggInfo!=0 ++ ){ ++ AggInfo *pAggInfo = pExpr->pAggInfo; ++ int iAgg = pExpr->iAgg; ++ Parse *pParse = pWalker->pParse; ++ sqlite3 *db = pParse->db; ++ assert( pExpr->op==TK_AGG_COLUMN || pExpr->op==TK_AGG_FUNCTION ); ++ if( pExpr->op==TK_AGG_COLUMN ){ ++ assert( iAgg>=0 && iAggnColumn ); ++ if( pAggInfo->aCol[iAgg].pExpr==pExpr ){ ++ pExpr = sqlite3ExprDup(db, pExpr, 0); ++ if( pExpr ){ ++ pAggInfo->aCol[iAgg].pExpr = pExpr; ++ pParse->pConstExpr = sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); ++ } ++ } ++ }else{ ++ assert( iAgg>=0 && iAggnFunc ); ++ if( pAggInfo->aFunc[iAgg].pExpr==pExpr ){ ++ pExpr = sqlite3ExprDup(db, pExpr, 0); ++ if( pExpr ){ ++ pAggInfo->aFunc[iAgg].pExpr = pExpr; ++ pParse->pConstExpr = sqlite3ExprListAppend(pParse, pParse->pConstExpr, pExpr); ++ } ++ } ++ } ++ } ++ return WRC_Continue; ++} ++ ++/* ++** Initialize a Walker object so that will persist AggInfo entries referenced ++** by the tree that is walked. ++*/ ++void sqlite3AggInfoPersistWalkerInit(Walker *pWalker, Parse *pParse){ ++ memset(pWalker, 0, sizeof(*pWalker)); ++ pWalker->pParse = pParse; ++ pWalker->xExprCallback = agginfoPersistExprCb; ++ pWalker->xSelectCallback = sqlite3SelectWalkNoop; ++} + + /* + ** Add a new element to the pAggInfo->aCol[] array. Return the index of +diff --git a/src/prepare.c b/src/prepare.c +index 2d928f1..e012ad4 100644 +--- a/src/prepare.c ++++ b/src/prepare.c +@@ -530,11 +530,26 @@ int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){ + return i; + } + ++/* ++** Deallocate a single AggInfo object ++*/ ++static void agginfoFree(sqlite3 *db, AggInfo *p){ ++ sqlite3DbFree(db, p->aCol); ++ sqlite3DbFree(db, p->aFunc); ++ sqlite3DbFree(db, p); ++} ++ + /* + ** Free all memory allocations in the pParse object + */ + void sqlite3ParserReset(Parse *pParse){ + sqlite3 *db = pParse->db; ++ AggInfo *pThis = pParse->pAggList; ++ while( pThis ){ ++ AggInfo *pNext = pThis->pNext; ++ agginfoFree(db, pThis); ++ pThis = pNext; ++ } + sqlite3DbFree(db, pParse->aLabel); + sqlite3ExprListDelete(db, pParse->pConstExpr); + if( db ){ +diff --git a/src/select.c b/src/select.c +index 9a16e8e..f117cd1 100644 +--- a/src/select.c ++++ b/src/select.c +@@ -3752,6 +3752,7 @@ static int flattenSubquery( + Expr *pWhere; /* The WHERE clause */ + struct SrcList_item *pSubitem; /* The subquery */ + sqlite3 *db = pParse->db; ++ Walker w; /* Walker to persist agginfo data */ + + /* Check to see if flattening is permitted. Return 0 if not. + */ +@@ -4120,6 +4121,8 @@ static int flattenSubquery( + /* Finially, delete what is left of the subquery and return + ** success. + */ ++ sqlite3AggInfoPersistWalkerInit(&w, pParse); ++ sqlite3WalkSelect(&w,pSub1); + sqlite3SelectDelete(db, pSub1); + + #if SELECTTRACE_ENABLED +@@ -5729,10 +5732,10 @@ int sqlite3Select( + Expr *pWhere; /* The WHERE clause. May be NULL */ + ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */ + Expr *pHaving; /* The HAVING clause. May be NULL */ ++ AggInfo *pAggInfo = 0; /* Aggregate information */ + int rc = 1; /* Value to return from this function */ + DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */ + SortCtx sSort; /* Info on how to code the ORDER BY clause */ +- AggInfo sAggInfo; /* Information used by aggregate queries */ + int iEnd; /* Address of the end of the query */ + sqlite3 *db; /* The database connection */ + ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */ +@@ -5744,7 +5747,6 @@ int sqlite3Select( + return 1; + } + if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1; +- memset(&sAggInfo, 0, sizeof(sAggInfo)); + #if SELECTTRACE_ENABLED + SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain)); + if( sqlite3SelectTrace & 0x100 ){ +@@ -6335,14 +6337,20 @@ int sqlite3Select( + ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the + ** SELECT statement. + */ ++ pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) ); ++ if( pAggInfo==0 ){ ++ goto select_end; ++ } ++ pAggInfo->pNext = pParse->pAggList; ++ pParse->pAggList = pAggInfo; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pParse; + sNC.pSrcList = pTabList; +- sNC.uNC.pAggInfo = &sAggInfo; ++ sNC.uNC.pAggInfo = pAggInfo; + VVA_ONLY( sNC.ncFlags = NC_UAggInfo; ) +- sAggInfo.mnReg = pParse->nMem+1; +- sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; +- sAggInfo.pGroupBy = pGroupBy; ++ pAggInfo->mnReg = pParse->nMem+1; ++ pAggInfo->nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0; ++ pAggInfo->pGroupBy = pGroupBy; + sqlite3ExprAnalyzeAggList(&sNC, pEList); + sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy); + if( pHaving ){ +@@ -6355,14 +6363,14 @@ int sqlite3Select( + } + sqlite3ExprAnalyzeAggregates(&sNC, pHaving); + } +- sAggInfo.nAccumulator = sAggInfo.nColumn; +- if( p->pGroupBy==0 && p->pHaving==0 && sAggInfo.nFunc==1 ){ +- minMaxFlag = minMaxQuery(db, sAggInfo.aFunc[0].pExpr, &pMinMaxOrderBy); ++ pAggInfo->nAccumulator = pAggInfo->nColumn; ++ if( p->pGroupBy==0 && p->pHaving==0 && pAggInfo->nFunc==1 ){ ++ minMaxFlag = minMaxQuery(db, pAggInfo->aFunc[0].pExpr, &pMinMaxOrderBy); + }else{ + minMaxFlag = WHERE_ORDERBY_NORMAL; + } +- for(i=0; inFunc; i++){ ++ Expr *pExpr = pAggInfo->aFunc[i].pExpr; + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + sNC.ncFlags |= NC_InAggFunc; + sqlite3ExprAnalyzeAggList(&sNC, pExpr->x.pList); +@@ -6374,22 +6382,22 @@ int sqlite3Select( + #endif + sNC.ncFlags &= ~NC_InAggFunc; + } +- sAggInfo.mxReg = pParse->nMem; ++ pAggInfo->mxReg = pParse->nMem; + if( db->mallocFailed ) goto select_end; + #if SELECTTRACE_ENABLED + if( sqlite3SelectTrace & 0x400 ){ + int ii; +- SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", &sAggInfo)); ++ SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo)); + sqlite3TreeViewSelect(0, p, 0); +- for(ii=0; iinColumn; ii++){ + sqlite3DebugPrintf("agg-column[%d] iMem=%d\n", +- ii, sAggInfo.aCol[ii].iMem); +- sqlite3TreeViewExpr(0, sAggInfo.aCol[ii].pExpr, 0); ++ ii,pAggInfo->aCol[ii].iMem); ++ sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pExpr, 0); + } +- for(ii=0; iinFunc; ii++){ + sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n", +- ii, sAggInfo.aFunc[ii].iMem); +- sqlite3TreeViewExpr(0, sAggInfo.aFunc[ii].pExpr, 0); ++ ii, pAggInfo->aFunc[ii].iMem); ++ sqlite3TreeViewExpr(0, pAggInfo->aFunc[ii].pExpr, 0); + } + } + #endif +@@ -6414,10 +6422,11 @@ int sqlite3Select( + ** that we do not need it after all, the OP_SorterOpen instruction + ** will be converted into a Noop. + */ +- sAggInfo.sortingIdx = pParse->nTab++; +- pKeyInfo = sqlite3KeyInfoFromExprList(pParse,pGroupBy,0,sAggInfo.nColumn); +- addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, +- sAggInfo.sortingIdx, sAggInfo.nSortingColumn, ++ pAggInfo->sortingIdx = pParse->nTab++; ++ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pGroupBy, ++ 0, pAggInfo->nColumn); ++ addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen, ++ pAggInfo->sortingIdx, pAggInfo->nSortingColumn, + 0, (char*)pKeyInfo, P4_KEYINFO); + + /* Initialize memory locations used by GROUP BY aggregate processing +@@ -6472,8 +6481,8 @@ int sqlite3Select( + nGroupBy = pGroupBy->nExpr; + nCol = nGroupBy; + j = nGroupBy; +- for(i=0; i=j ){ ++ for(i=0; inColumn; i++){ ++ if( pAggInfo->aCol[i].iSorterColumn>=j ){ + nCol++; + j++; + } +@@ -6481,8 +6490,8 @@ int sqlite3Select( + regBase = sqlite3GetTempRange(pParse, nCol); + sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0); + j = nGroupBy; +- for(i=0; inColumn; i++){ ++ struct AggInfo_col *pCol = &pAggInfo->aCol[i]; + if( pCol->iSorterColumn>=j ){ + int r1 = j + regBase; + sqlite3ExprCodeGetColumnOfTable(v, +@@ -6492,16 +6501,16 @@ int sqlite3Select( + } + regRecord = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regRecord); +- sqlite3VdbeAddOp2(v, OP_SorterInsert, sAggInfo.sortingIdx, regRecord); ++ sqlite3VdbeAddOp2(v, OP_SorterInsert, pAggInfo->sortingIdx, regRecord); + sqlite3ReleaseTempReg(pParse, regRecord); + sqlite3ReleaseTempRange(pParse, regBase, nCol); + sqlite3WhereEnd(pWInfo); +- sAggInfo.sortingIdxPTab = sortPTab = pParse->nTab++; ++ pAggInfo->sortingIdxPTab = sortPTab = pParse->nTab++; + sortOut = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol); +- sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd); ++ sqlite3VdbeAddOp2(v, OP_SorterSort, pAggInfo->sortingIdx, addrEnd); + VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v); +- sAggInfo.useSortingIdx = 1; ++ pAggInfo->useSortingIdx = 1; + } + + /* If the index or temporary table used by the GROUP BY sort +@@ -6525,14 +6534,14 @@ int sqlite3Select( + */ + addrTopOfLoop = sqlite3VdbeCurrentAddr(v); + if( groupBySort ){ +- sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx, ++ sqlite3VdbeAddOp3(v, OP_SorterData, pAggInfo->sortingIdx, + sortOut, sortPTab); + } + for(j=0; jnExpr; j++){ + if( groupBySort ){ + sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j); + }else{ +- sAggInfo.directMode = 1; ++ pAggInfo->directMode = 1; + sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j); + } + } +@@ -6562,14 +6571,14 @@ int sqlite3Select( + ** the current row + */ + sqlite3VdbeJumpHere(v, addr1); +- updateAccumulator(pParse, iUseFlag, &sAggInfo); ++ updateAccumulator(pParse, iUseFlag, pAggInfo); + sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); + VdbeComment((v, "indicate data in accumulator")); + + /* End of the loop + */ + if( groupBySort ){ +- sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop); ++ sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop); + VdbeCoverage(v); + }else{ + sqlite3WhereEnd(pWInfo); +@@ -6602,7 +6611,7 @@ int sqlite3Select( + VdbeCoverage(v); + VdbeComment((v, "Groupby result generator entry point")); + sqlite3VdbeAddOp1(v, OP_Return, regOutputRow); +- finalizeAggFunctions(pParse, &sAggInfo); ++ finalizeAggFunctions(pParse, pAggInfo); + sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL); + selectInnerLoop(pParse, p, -1, &sSort, + &sDistinct, pDest, +@@ -6613,7 +6622,7 @@ int sqlite3Select( + /* Generate a subroutine that will reset the group-by accumulator + */ + sqlite3VdbeResolveLabel(v, addrReset); +- resetAccumulator(pParse, &sAggInfo); ++ resetAccumulator(pParse, pAggInfo); + sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag); + VdbeComment((v, "indicate accumulator empty")); + sqlite3VdbeAddOp1(v, OP_Return, regReset); +@@ -6621,7 +6630,7 @@ int sqlite3Select( + } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */ + else { + Table *pTab; +- if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){ ++ if( (pTab = isSimpleCount(p, pAggInfo))!=0 ){ + /* If isSimpleCount() returns a pointer to a Table structure, then + ** the SQL statement is of the form: + ** +@@ -6674,7 +6683,7 @@ int sqlite3Select( + if( pKeyInfo ){ + sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO); + } +- sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem); ++ sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem); + sqlite3VdbeAddOp1(v, OP_Close, iCsr); + explainSimpleCount(pParse, pTab, pBest); + }else{ +@@ -6689,12 +6698,16 @@ int sqlite3Select( + ** first row visited by the aggregate, so that they are updated at + ** least once even if the FILTER clause means the min() or max() + ** function visits zero rows. */ +- if( sAggInfo.nAccumulator ){ +- for(i=0; ifuncFlags&SQLITE_FUNC_NEEDCOLL ) break; ++ if( pAggInfo->nAccumulator ){ ++ for(i=0; inFunc; i++){ ++ if( ExprHasProperty(pAggInfo->aFunc[i].pExpr, EP_WinFunc) ){ ++ continue; ++ } ++ if( pAggInfo->aFunc[i].pFunc->funcFlags&SQLITE_FUNC_NEEDCOLL ){ ++ break; ++ } + } +- if( i==sAggInfo.nFunc ){ ++ if( i==pAggInfo->nFunc ){ + regAcc = ++pParse->nMem; + sqlite3VdbeAddOp2(v, OP_Integer, 0, regAcc); + } +@@ -6705,7 +6718,7 @@ int sqlite3Select( + ** of output. + */ + assert( p->pGroupBy==0 ); +- resetAccumulator(pParse, &sAggInfo); ++ resetAccumulator(pParse, pAggInfo); + + /* If this query is a candidate for the min/max optimization, then + ** minMaxFlag will have been previously set to either +@@ -6721,7 +6734,7 @@ int sqlite3Select( + if( pWInfo==0 ){ + goto select_end; + } +- updateAccumulator(pParse, regAcc, &sAggInfo); ++ updateAccumulator(pParse, regAcc, pAggInfo); + if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc); + if( sqlite3WhereIsOrdered(pWInfo)>0 ){ + sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo)); +@@ -6729,7 +6742,7 @@ int sqlite3Select( + (minMaxFlag==WHERE_ORDERBY_MIN?"min":"max"))); + } + sqlite3WhereEnd(pWInfo); +- finalizeAggFunctions(pParse, &sAggInfo); ++ finalizeAggFunctions(pParse, pAggInfo); + } + + sSort.pOrderBy = 0; +@@ -6768,8 +6781,25 @@ int sqlite3Select( + */ + select_end: + sqlite3ExprListDelete(db, pMinMaxOrderBy); +- sqlite3DbFree(db, sAggInfo.aCol); +- sqlite3DbFree(db, sAggInfo.aFunc); ++#ifdef SQLITE_DEBUG ++ if( pAggInfo ){ ++ for(i=0; inColumn; i++){ ++ Expr *pExpr = pAggInfo->aCol[i].pExpr; ++ assert( pExpr!=0 || db->mallocFailed ); ++ if( pExpr==0 ) continue; ++ assert( pExpr->pAggInfo==pAggInfo ); ++ assert( pExpr->iAgg==i ); ++ } ++ for(i=0; inFunc; i++){ ++ Expr *pExpr = pAggInfo->aFunc[i].pExpr; ++ assert( pExpr!=0 || db->mallocFailed ); ++ if( pExpr==0 ) continue; ++ assert( pExpr->pAggInfo==pAggInfo ); ++ assert( pExpr->iAgg==i ); ++ } ++ } ++#endif ++ + #if SELECTTRACE_ENABLED + SELECTTRACE(0x1,pParse,p,("end processing\n")); + if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){ +diff --git a/src/sqliteInt.h b/src/sqliteInt.h +index 514df18..fe24c3a 100644 +--- a/src/sqliteInt.h ++++ b/src/sqliteInt.h +@@ -2489,11 +2489,11 @@ struct AggInfo { + ExprList *pGroupBy; /* The group by clause */ + struct AggInfo_col { /* For each column used in source tables */ + Table *pTab; /* Source table */ ++ Expr *pExpr; /* The original expression */ + int iTable; /* Cursor number of the source table */ +- int iColumn; /* Column number within the source table */ +- int iSorterColumn; /* Column number in the sorting index */ + int iMem; /* Memory location that acts as accumulator */ +- Expr *pExpr; /* The original expression */ ++ i16 iColumn; /* Column number within the source table */ ++ i16 iSorterColumn; /* Column number in the sorting index */ + } *aCol; + int nColumn; /* Number of used entries in aCol[] */ + int nAccumulator; /* Number of columns that show through to the output. +@@ -2506,6 +2506,10 @@ struct AggInfo { + int iDistinct; /* Ephemeral table used to enforce DISTINCT */ + } *aFunc; + int nFunc; /* Number of entries in aFunc[] */ ++#ifdef SQLITE_DEBUG ++ int iAggMagic; /* Magic number when valid */ ++#endif ++ AggInfo *pNext; /* Next in list of them all */ + }; + + /* +@@ -3291,6 +3295,7 @@ struct Parse { + Parse *pToplevel; /* Parse structure for main program (or NULL) */ + Table *pTriggerTab; /* Table triggers are being coded for */ + Parse *pParentParse; /* Parent parser if this parser is nested */ ++ AggInfo *pAggList; /* List of all AggInfo objects */ + int addrCrTab; /* Address of OP_CreateBtree opcode on CREATE TABLE */ + u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */ + u32 oldmask; /* Mask of old.* columns referenced */ +@@ -4254,6 +4259,7 @@ int sqlite3ExprCompareSkip(Expr*, Expr*, int); + int sqlite3ExprListCompare(ExprList*, ExprList*, int); + int sqlite3ExprImpliesExpr(Parse*,Expr*, Expr*, int); + int sqlite3ExprImpliesNonNullRow(Expr*,int); ++void sqlite3AggInfoPersistWalkerInit(Walker*,Parse*); + void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); + void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); + int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx); +diff --git a/src/window.c b/src/window.c +index 9bb3217..f4919d6 100644 +--- a/src/window.c ++++ b/src/window.c +@@ -933,12 +933,16 @@ int sqlite3WindowRewrite(Parse *pParse, Select *p){ + Window *pMWin = p->pWin; /* Master window object */ + Window *pWin; /* Window object iterator */ + Table *pTab; ++ Walker w; ++ + u32 selFlags = p->selFlags; + + pTab = sqlite3DbMallocZero(db, sizeof(Table)); + if( pTab==0 ){ + return sqlite3ErrorToParser(db, SQLITE_NOMEM); + } ++ sqlite3AggInfoPersistWalkerInit(&w, pParse); ++ sqlite3WalkSelect(&w, p); + + p->pSrc = 0; + p->pWhere = 0; +diff --git a/test/window1.test b/test/window1.test +index 18b9bdc..1d554f9 100644 +--- a/test/window1.test ++++ b/test/window1.test +@@ -1603,4 +1603,150 @@ do_catchsql_test 51.1 { + sum(b) OVER(PARTITION BY min(DISTINCT c), c ORDER BY b))); + } {1 {row value misused}} + ++#------------------------------------------------------------------------- ++reset_db ++do_execsql_test 54.1 { ++ CREATE TABLE t1(a VARCHAR(20), b FLOAT); ++ INSERT INTO t1 VALUES('1',10.0); ++} ++ ++do_catchsql_test 54.2 { ++ SELECT * FROM ( ++ SELECT sum(b) OVER() AS c FROM t1 ++ UNION ++ SELECT b AS c FROM t1 ++ ) WHERE c>10; ++} {1 {misuse of window function sum()}} ++ ++do_execsql_test 54.3 { ++ INSERT INTO t1 VALUES('2',5.0); ++ INSERT INTO t1 VALUES('3',15.0); ++} ++ ++do_catchsql_test 54.4 { ++ SELECT * FROM ( ++ SELECT sum(b) OVER() AS c FROM t1 ++ UNION ++ SELECT b AS c FROM t1 ++ ) WHERE c>10; ++} {1 {misuse of window function sum()}} ++ ++# 2020-06-05 ticket c8d3b9f0a750a529 ++reset_db ++do_execsql_test 55.1 { ++ CREATE TABLE a(b); ++ SELECT ++ (SELECT b FROM a ++ GROUP BY b ++ HAVING (SELECT COUNT()OVER() + lead(b)OVER(ORDER BY SUM(DISTINCT b) + b)) ++ ) ++ FROM a ++ UNION ++ SELECT 99 ++ ORDER BY 1; ++} {99} ++ ++#------------------------------------------------------------------------ ++reset_db ++do_execsql_test 56.1 { ++ CREATE TABLE t1(a, b INTEGER); ++ CREATE TABLE t2(c, d); ++} ++do_catchsql_test 56.2 { ++ SELECT avg(b) FROM t1 ++ UNION ALL ++ SELECT min(c) OVER () FROM t2 ++ ORDER BY nosuchcolumn; ++} {1 {1st ORDER BY term does not match any column in the result set}} ++ ++reset_db ++do_execsql_test 57.1 { ++ CREATE TABLE t4(a, b, c, d, e); ++} ++ ++do_catchsql_test 57.2 { ++ SELECT b FROM t4 ++ UNION ++ SELECT a FROM t4 ++ ORDER BY ( ++ SELECT sum(x) OVER() FROM ( ++ SELECT c AS x FROM t4 ++ UNION ++ SELECT d FROM t4 ++ ORDER BY (SELECT e FROM t4) ++ ) ++ ); ++} {1 {1st ORDER BY term does not match any column in the result set}} ++ ++# 2020-06-06 various dbsqlfuzz finds and ++# ticket 0899cf62f597d7e7 ++# ++reset_db ++do_execsql_test 57.1 { ++ CREATE TABLE t1(a, b, c); ++ INSERT INTO t1 VALUES(NULL,NULL,NULL); ++ SELECT ++ sum(a), ++ min(b) OVER (), ++ count(c) OVER (ORDER BY b) ++ FROM t1; ++} {{} {} 0} ++do_execsql_test 57.2 { ++ CREATE TABLE v0 ( v1 INTEGER PRIMARY KEY ) ; ++ INSERT INTO v0 VALUES ( 10 ) ; ++ SELECT DISTINCT v1, lead(v1) OVER() FROM v0 GROUP BY v1 ORDER BY 2; ++} {10 {}} ++do_catchsql_test 57.3 { ++ DROP TABLE t1; ++ CREATE TABLE t1(a); ++ INSERT INTO t1(a) VALUES(22); ++ CREATE TABLE t3(y); ++ INSERT INTO t3(y) VALUES(5),(11),(-9); ++ SELECT ( ++ SELECT max(y) OVER( ORDER BY (SELECT x FROM (SELECT sum(y) AS x FROM t1))) ++ ) ++ FROM t3; ++} {1 {misuse of aggregate: sum()}} ++ ++# 2020-06-06 ticket 1f6f353b684fc708 ++reset_db ++do_execsql_test 58.1 { ++ CREATE TABLE a(a, b, c); ++ INSERT INTO a VALUES(1, 2, 3); ++ INSERT INTO a VALUES(4, 5, 6); ++ SELECT sum(345+b) OVER (ORDER BY b), ++ sum(avg(678)) OVER (ORDER BY c) FROM a; ++} {347 678.0} ++ ++# 2020-06-06 ticket e5504e987e419fb0 ++do_catchsql_test 59.1 { ++ DROP TABLE IF EXISTS t1; ++ CREATE TABLE t1(x INTEGER PRIMARY KEY); ++ INSERT INTO t1 VALUES (123); ++ SELECT ++ ntile( (SELECT sum(x)) ) OVER(ORDER BY x), ++ min(x) OVER(ORDER BY x) ++ FROM t1; ++} {1 {misuse of aggregate: sum()}} ++ ++# 2020-06-07 ticket f7d890858f361402 ++do_execsql_test 60.1 { ++ DROP TABLE IF EXISTS t1; ++ CREATE TABLE t1 (x INTEGER PRIMARY KEY); ++ INSERT INTO t1 VALUES (99); ++ SELECT EXISTS(SELECT count(*) OVER() FROM t1 ORDER BY sum(x) OVER()); ++} {1} ++ ++# 2020-06-07 test case generated by dbsqlfuzz showing how an AggInfo ++# object might be referenced after the sqlite3Select() call that created ++# it returns. This proves the need to persist all AggInfo objects until ++# the Parse object is destroyed. ++# ++reset_db ++do_execsql_test 61.1 { ++CREATE TABLE t1(a); ++INSERT INTO t1 VALUES(5),(NULL),('seventeen'); ++SELECT (SELECT max(x)OVER(ORDER BY x) % min(x)OVER(ORDER BY CASE x WHEN 889 THEN x WHEN x THEN x END)) FROM (SELECT (SELECT sum(CAST(a IN(SELECT (SELECT max(x)OVER(ORDER BY CASE x WHEN 889 THEN 299 WHEN 863 THEN 863 END)) FROM (SELECT (SELECT sum(CAST((SELECT (SELECT max(x)OVER(ORDER BY x) / min(x)OVER(ORDER BY CASE x WHEN 889 THEN 299 WHEN -true THEN 863 END)) FROM (SELECT (SELECT sum(CAST(a IN(SELECT (SELECT max(x) & sum ( a )OVER(ORDER BY CASE x WHEN -8 THEN 299 WHEN 863 THEN 863 END)) FROM (SELECT (SELECT sum(CAST(a AS )) FROM t1) AS x FROM t1)) AS t1 )) FROM t1) AS x FROM t1)) AS x )) FROM t1) AS x FROM t1)) AS real)) FROM t1) AS x FROM t1); ++} {{} {} {}} ++ + finish_test +-- +2.23.0 + diff --git a/sqlite.spec b/sqlite.spec index d0188373273baf8a7c69e59b60f8aedbe6f39c52..d27438b76b44a9f069938b955c5502113d0dd50c 100644 --- a/sqlite.spec +++ b/sqlite.spec @@ -7,7 +7,7 @@ Name: sqlite Version: 3.31.1 -Release: 1 +Release: 2 Summary: Embeded SQL database License: Public Domain URL: http://www.sqlite.org/ @@ -23,6 +23,12 @@ Patch4: 0004-Fix-CVE-2020-11656.patch Patch5: 0005-Fix-CVE-2020-15358.patch Patch6: 0006-Fix-CVE-2020-13631.patch Patch7: 0007-sqlite-no-malloc-usable-size.patch +Patch8: 0008-Cleaner_separation_of_the_STAT4-specific_logic.patch +Patch9: 0009-Background_work_for_experiments.patch +Patch10: 0010-Fix_handling_of_window_functions.patch +Patch11: 0011-Remove_the_SQLITE_OMIT_BTREECOUNT_option.patch +Patch12: 0012-Improvements_to_parse-tree_tracing_logic.patch +Patch13: 0013-Fix-CVE-2020-13871.patch BuildRequires: gcc autoconf tcl tcl-devel BuildRequires: ncurses-devel readline-devel glibc-devel @@ -72,6 +78,12 @@ This contains man files and HTML files for the using of sqlite. %patch5 -p1 %patch6 -p1 %patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 rm -f %{name}-doc-%{extver}/sqlite.css~ || : @@ -145,9 +157,15 @@ make test %{_mandir}/man*/* %changelog +* Tue Aug 4 2020 yanglongkang - 3.31.1-2 +- Type:cves +- ID:CVE-2020-13871 +- SUG: NA +- DESC: fix cve + * Mon Aug 3 2020 yanglongkang - 3.31.1-1 - Type:cves -- ID:CVE-2020-13871 CVE-2020-13631 +- ID:CVE-2020-15358 CVE-2020-13631 - SUG: NA - DESC: fix cve