353 lines
14 KiB
Diff
353 lines
14 KiB
Diff
From afa1ecac9b6fba76861dca85b211ca1022f9e378 Mon Sep 17 00:00:00 2001
|
|
From: drh <drh@noemail.net>
|
|
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; i<p->nCol; 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->nSample<p->mxSample || 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; i<p->nCol; 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; i<iChng; i++){
|
|
@@ -775,15 +774,15 @@ static const FuncDef statPushFuncdef = {
|
|
/*
|
|
** Implementation of the stat_get(P,J) SQL function. This routine is
|
|
** used to query statistical information that has been gathered into
|
|
-** the Stat4Accum object by prior calls to stat_push(). The P parameter
|
|
-** has type BLOB but it is really just a pointer to the Stat4Accum object.
|
|
+** the StatAccum object by prior calls to stat_push(). The P parameter
|
|
+** has type BLOB but it is really just a pointer to the StatAccum object.
|
|
** The content to returned is determined by the parameter J
|
|
** which is one of the STAT_GET_xxxx values defined above.
|
|
**
|
|
** The stat_get(P,J) function is not available to generic SQL. It is
|
|
** inserted as part of a manually constructed bytecode program. (See
|
|
** the callStatGet() routine below.) It is guaranteed that the P
|
|
-** parameter will always be a poiner to a Stat4Accum object, never a
|
|
+** parameter will always be a poiner to a StatAccum object, never a
|
|
** NULL.
|
|
**
|
|
** If STAT4 is not enabled, then J is always
|
|
@@ -796,7 +795,7 @@ static void statGet(
|
|
int argc,
|
|
sqlite3_value **argv
|
|
){
|
|
- Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
|
|
+ StatAccum *p = (StatAccum*)sqlite3_value_blob(argv[0]);
|
|
#ifdef SQLITE_ENABLE_STAT4
|
|
/* STAT4 has a parameter on this routine. */
|
|
int eCall = sqlite3_value_int(argv[1]);
|
|
@@ -860,7 +859,7 @@ static void statGet(
|
|
p->iGet = 0;
|
|
}
|
|
if( p->iGet<p->nSample ){
|
|
- 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
|
|
|