aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2011-10-11 12:58:38 +0000
committerdrh <drh@noemail.net>2011-10-11 12:58:38 +0000
commit29c21c77af012bed32e3c68c71b813f885c4641d (patch)
treed3d62d6714ba3e71397149a98943fab48ecff2b1 /src
parent946e610a8497e3868e47ad2c976913ec37fb0df6 (diff)
parent67855877ffa8a1d70ab17a65ead5dd3872e4623a (diff)
downloadsqlite-29c21c77af012bed32e3c68c71b813f885c4641d.tar.gz
sqlite-29c21c77af012bed32e3c68c71b813f885c4641d.zip
Merge all the latest trunk changes into the sessions branch - especially
the SQLITE_ENABLE_STAT3 enhancements. FossilOrigin-Name: 403431cac6b039b0693915c5422f08dc60dae230
Diffstat (limited to 'src')
-rw-r--r--src/analyze.c755
-rw-r--r--src/backup.c9
-rw-r--r--src/btree.c58
-rw-r--r--src/build.c152
-rw-r--r--src/ctime.c6
-rw-r--r--src/insert.c3
-rw-r--r--src/os_unix.c12
-rw-r--r--src/os_win.c2
-rw-r--r--src/pager.c33
-rw-r--r--src/pager.h1
-rw-r--r--src/pcache1.c250
-rw-r--r--src/pragma.c6
-rw-r--r--src/shell.c11
-rw-r--r--src/sqlite.h.in32
-rw-r--r--src/sqliteInt.h43
-rw-r--r--src/status.c22
-rw-r--r--src/tclsqlite.c67
-rw-r--r--src/test_config.c18
-rw-r--r--src/test_malloc.c6
-rw-r--r--src/test_stat.c63
-rw-r--r--src/utf.c2
-rw-r--r--src/vacuum.c14
-rw-r--r--src/vdbeaux.c8
-rw-r--r--src/vdbemem.c6
-rw-r--r--src/where.c530
25 files changed, 1262 insertions, 847 deletions
diff --git a/src/analyze.c b/src/analyze.c
index 17c1de83a..99929e93c 100644
--- a/src/analyze.c
+++ b/src/analyze.c
@@ -10,6 +10,108 @@
**
*************************************************************************
** This file contains code associated with the ANALYZE command.
+**
+** The ANALYZE command gather statistics about the content of tables
+** and indices. These statistics are made available to the query planner
+** to help it make better decisions about how to perform queries.
+**
+** The following system tables are or have been supported:
+**
+** CREATE TABLE sqlite_stat1(tbl, idx, stat);
+** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample);
+** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample);
+**
+** Additional tables might be added in future releases of SQLite.
+** The sqlite_stat2 table is not created or used unless the SQLite version
+** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
+** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
+** The sqlite_stat2 table is superceded by sqlite_stat3, which is only
+** created and used by SQLite versions 3.7.9 and later and with
+** SQLITE_ENABLE_STAT3 defined. The fucntionality of sqlite_stat3
+** is a superset of sqlite_stat2.
+**
+** Format of sqlite_stat1:
+**
+** There is normally one row per index, with the index identified by the
+** name in the idx column. The tbl column is the name of the table to
+** which the index belongs. In each such row, the stat column will be
+** a string consisting of a list of integers. The first integer in this
+** list is the number of rows in the index and in the table. The second
+** integer is the average number of rows in the index that have the same
+** value in the first column of the index. The third integer is the average
+** number of rows in the index that have the same value for the first two
+** columns. The N-th integer (for N>1) is the average number of rows in
+** the index which have the same value for the first N-1 columns. For
+** a K-column index, there will be K+1 integers in the stat column. If
+** the index is unique, then the last integer will be 1.
+**
+** The list of integers in the stat column can optionally be followed
+** by the keyword "unordered". The "unordered" keyword, if it is present,
+** must be separated from the last integer by a single space. If the
+** "unordered" keyword is present, then the query planner assumes that
+** the index is unordered and will not use the index for a range query.
+**
+** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat
+** column contains a single integer which is the (estimated) number of
+** rows in the table identified by sqlite_stat1.tbl.
+**
+** Format of sqlite_stat2:
+**
+** The sqlite_stat2 is only created and is only used if SQLite is compiled
+** with SQLITE_ENABLE_STAT2 and if the SQLite version number is between
+** 3.6.18 and 3.7.8. The "stat2" table contains additional information
+** about the distribution of keys within an index. The index is identified by
+** the "idx" column and the "tbl" column is the name of the table to which
+** the index belongs. There are usually 10 rows in the sqlite_stat2
+** table for each index.
+**
+** The sqlite_stat2 entries for an index that have sampleno between 0 and 9
+** inclusive are samples of the left-most key value in the index taken at
+** evenly spaced points along the index. Let the number of samples be S
+** (10 in the standard build) and let C be the number of rows in the index.
+** Then the sampled rows are given by:
+**
+** rownumber = (i*C*2 + C)/(S*2)
+**
+** For i between 0 and S-1. Conceptually, the index space is divided into
+** S uniform buckets and the samples are the middle row from each bucket.
+**
+** The format for sqlite_stat2 is recorded here for legacy reference. This
+** version of SQLite does not support sqlite_stat2. It neither reads nor
+** writes the sqlite_stat2 table. This version of SQLite only supports
+** sqlite_stat3.
+**
+** Format for sqlite_stat3:
+**
+** The sqlite_stat3 is an enhancement to sqlite_stat2. A new name is
+** used to avoid compatibility problems.
+**
+** The format of the sqlite_stat3 table is similar to the format of
+** the sqlite_stat2 table. There are multiple entries for each index.
+** The idx column names the index and the tbl column is the table of the
+** index. If the idx and tbl columns are the same, then the sample is
+** of the INTEGER PRIMARY KEY. The sample column is a value taken from
+** the left-most column of the index. The nEq column is the approximate
+** number of entires in the index whose left-most column exactly matches
+** the sample. nLt is the approximate number of entires whose left-most
+** column is less than the sample. The nDLt column is the approximate
+** number of distinct left-most entries in the index that are less than
+** the sample.
+**
+** Future versions of SQLite might change to store a string containing
+** multiple integers values in the nDLt column of sqlite_stat3. The first
+** integer will be the number of prior index entires that are distinct in
+** the left-most column. The second integer will be the number of prior index
+** entries that are distinct in the first two columns. The third integer
+** will be the number of prior index entries that are distinct in the first
+** three columns. And so forth. With that extension, the nDLt field is
+** similar in function to the sqlite_stat1.stat field.
+**
+** There can be an arbitrary number of sqlite_stat3 entries per index.
+** The ANALYZE command will typically generate sqlite_stat3 tables
+** that contain between 10 and 40 samples which are distributed across
+** the key space, though not uniformly, and which include samples with
+** largest possible nEq values.
*/
#ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h"
@@ -42,8 +144,8 @@ static void openStatTable(
const char *zCols;
} aTable[] = {
{ "sqlite_stat1", "tbl,idx,stat" },
-#ifdef SQLITE_ENABLE_STAT2
- { "sqlite_stat2", "tbl,idx,sampleno,sample" },
+#ifdef SQLITE_ENABLE_STAT3
+ { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
#endif
};
@@ -59,6 +161,9 @@ static void openStatTable(
assert( sqlite3VdbeDb(v)==db );
pDb = &db->aDb[iDb];
+ /* Create new statistic tables if they do not exist, or clear them
+ ** if they do already exist.
+ */
for(i=0; i<ArraySize(aTable); i++){
const char *zTab = aTable[i].zName;
Table *pStat;
@@ -89,7 +194,7 @@ static void openStatTable(
}
}
- /* Open the sqlite_stat[12] tables for writing. */
+ /* Open the sqlite_stat[13] tables for writing. */
for(i=0; i<ArraySize(aTable); i++){
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
@@ -98,6 +203,226 @@ static void openStatTable(
}
/*
+** Recommended number of samples for sqlite_stat3
+*/
+#ifndef SQLITE_STAT3_SAMPLES
+# define SQLITE_STAT3_SAMPLES 24
+#endif
+
+/*
+** Three SQL functions - stat3_init(), stat3_push(), and stat3_pop() -
+** share an instance of the following structure to hold their state
+** information.
+*/
+typedef struct Stat3Accum Stat3Accum;
+struct Stat3Accum {
+ tRowcnt nRow; /* Number of rows in the entire table */
+ tRowcnt nPSample; /* How often to do a periodic sample */
+ int iMin; /* Index of entry with minimum nEq and hash */
+ int mxSample; /* Maximum number of samples to accumulate */
+ int nSample; /* Current number of samples */
+ u32 iPrn; /* Pseudo-random number used for sampling */
+ struct Stat3Sample {
+ i64 iRowid; /* Rowid in main table of the key */
+ tRowcnt nEq; /* sqlite_stat3.nEq */
+ tRowcnt nLt; /* sqlite_stat3.nLt */
+ tRowcnt nDLt; /* sqlite_stat3.nDLt */
+ u8 isPSample; /* True if a periodic sample */
+ u32 iHash; /* Tiebreaker hash */
+ } *a; /* An array of samples */
+};
+
+#ifdef SQLITE_ENABLE_STAT3
+/*
+** Implementation of the stat3_init(C,S) SQL function. The two parameters
+** are the number of rows in the table or index (C) and the number of samples
+** to accumulate (S).
+**
+** This routine allocates the Stat3Accum object.
+**
+** The return value is the Stat3Accum object (P).
+*/
+static void stat3Init(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Stat3Accum *p;
+ tRowcnt nRow;
+ int mxSample;
+ int n;
+
+ UNUSED_PARAMETER(argc);
+ nRow = (tRowcnt)sqlite3_value_int64(argv[0]);
+ mxSample = sqlite3_value_int(argv[1]);
+ n = sizeof(*p) + sizeof(p->a[0])*mxSample;
+ p = sqlite3_malloc( n );
+ if( p==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ memset(p, 0, n);
+ p->a = (struct Stat3Sample*)&p[1];
+ p->nRow = nRow;
+ p->mxSample = mxSample;
+ p->nPSample = p->nRow/(mxSample/3+1) + 1;
+ sqlite3_randomness(sizeof(p->iPrn), &p->iPrn);
+ sqlite3_result_blob(context, p, sizeof(p), sqlite3_free);
+}
+static const FuncDef stat3InitFuncdef = {
+ 2, /* nArg */
+ SQLITE_UTF8, /* iPrefEnc */
+ 0, /* flags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ stat3Init, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat3_init", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
+};
+
+
+/*
+** Implementation of the stat3_push(nEq,nLt,nDLt,rowid,P) SQL function. The
+** arguments describe a single key instance. This routine makes the
+** decision about whether or not to retain this key for the sqlite_stat3
+** table.
+**
+** The return value is NULL.
+*/
+static void stat3Push(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[4]);
+ tRowcnt nEq = sqlite3_value_int64(argv[0]);
+ tRowcnt nLt = sqlite3_value_int64(argv[1]);
+ tRowcnt nDLt = sqlite3_value_int64(argv[2]);
+ i64 rowid = sqlite3_value_int64(argv[3]);
+ u8 isPSample = 0;
+ u8 doInsert = 0;
+ int iMin = p->iMin;
+ struct Stat3Sample *pSample;
+ int i;
+ u32 h;
+
+ UNUSED_PARAMETER(context);
+ UNUSED_PARAMETER(argc);
+ if( nEq==0 ) return;
+ h = p->iPrn = p->iPrn*1103515245 + 12345;
+ if( (nLt/p->nPSample)!=((nEq+nLt)/p->nPSample) ){
+ doInsert = isPSample = 1;
+ }else if( p->nSample<p->mxSample ){
+ doInsert = 1;
+ }else{
+ if( nEq>p->a[iMin].nEq || (nEq==p->a[iMin].nEq && h>p->a[iMin].iHash) ){
+ doInsert = 1;
+ }
+ }
+ if( !doInsert ) return;
+ if( p->nSample==p->mxSample ){
+ assert( p->nSample - iMin - 1 >= 0 );
+ memmove(&p->a[iMin], &p->a[iMin+1], sizeof(p->a[0])*(p->nSample-iMin-1));
+ pSample = &p->a[p->nSample-1];
+ }else{
+ pSample = &p->a[p->nSample++];
+ }
+ pSample->iRowid = rowid;
+ pSample->nEq = nEq;
+ pSample->nLt = nLt;
+ pSample->nDLt = nDLt;
+ pSample->iHash = h;
+ pSample->isPSample = isPSample;
+
+ /* Find the new minimum */
+ if( p->nSample==p->mxSample ){
+ pSample = p->a;
+ i = 0;
+ while( pSample->isPSample ){
+ i++;
+ pSample++;
+ assert( i<p->nSample );
+ }
+ nEq = pSample->nEq;
+ h = pSample->iHash;
+ iMin = i;
+ for(i++, pSample++; i<p->nSample; i++, pSample++){
+ if( pSample->isPSample ) continue;
+ if( pSample->nEq<nEq
+ || (pSample->nEq==nEq && pSample->iHash<h)
+ ){
+ iMin = i;
+ nEq = pSample->nEq;
+ h = pSample->iHash;
+ }
+ }
+ p->iMin = iMin;
+ }
+}
+static const FuncDef stat3PushFuncdef = {
+ 5, /* nArg */
+ SQLITE_UTF8, /* iPrefEnc */
+ 0, /* flags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ stat3Push, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat3_push", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
+};
+
+/*
+** Implementation of the stat3_get(P,N,...) SQL function. This routine is
+** used to query the results. Content is returned for the Nth sqlite_stat3
+** row where N is between 0 and S-1 and S is the number of samples. The
+** value returned depends on the number of arguments.
+**
+** argc==2 result: rowid
+** argc==3 result: nEq
+** argc==4 result: nLt
+** argc==5 result: nDLt
+*/
+static void stat3Get(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int n = sqlite3_value_int(argv[1]);
+ Stat3Accum *p = (Stat3Accum*)sqlite3_value_blob(argv[0]);
+
+ assert( p!=0 );
+ if( p->nSample<=n ) return;
+ switch( argc ){
+ case 2: sqlite3_result_int64(context, p->a[n].iRowid); break;
+ case 3: sqlite3_result_int64(context, p->a[n].nEq); break;
+ case 4: sqlite3_result_int64(context, p->a[n].nLt); break;
+ default: sqlite3_result_int64(context, p->a[n].nDLt); break;
+ }
+}
+static const FuncDef stat3GetFuncdef = {
+ -1, /* nArg */
+ SQLITE_UTF8, /* iPrefEnc */
+ 0, /* flags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ stat3Get, /* xFunc */
+ 0, /* xStep */
+ 0, /* xFinalize */
+ "stat3_get", /* zName */
+ 0, /* pHash */
+ 0 /* pDestructor */
+};
+#endif /* SQLITE_ENABLE_STAT3 */
+
+
+
+
+/*
** Generate code to do an analysis of all indices associated with
** a single table.
*/
@@ -119,20 +444,27 @@ static void analyzeOneTable(
int iDb; /* Index of database containing pTab */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
- int regSampleno = iMem++; /* Register containing next sample number */
- int regCol = iMem++; /* Content of a column analyzed table */
+ int regStat1 = iMem++; /* The stat column of sqlite_stat1 */
+#ifdef SQLITE_ENABLE_STAT3
+ int regNumEq = regStat1; /* Number of instances. Same as regStat1 */
+ int regNumLt = iMem++; /* Number of keys less than regSample */
+ int regNumDLt = iMem++; /* Number of distinct keys less than regSample */
+ int regSample = iMem++; /* The next sample value */
+ int regRowid = regSample; /* Rowid of a sample */
+ int regAccum = iMem++; /* Register to hold Stat3Accum object */
+ int regLoop = iMem++; /* Loop counter */
+ int regCount = iMem++; /* Number of rows in the table or index */
+ int regTemp1 = iMem++; /* Intermediate register */
+ int regTemp2 = iMem++; /* Intermediate register */
+ int once = 1; /* One-time initialization */
+ int shortJump = 0; /* Instruction address */
+ int iTabCur = pParse->nTab++; /* Table cursor */
+#endif
+ int regCol = iMem++; /* Content of a column in analyzed table */
int regRec = iMem++; /* Register holding completed record */
int regTemp = iMem++; /* Temporary use register */
- int regRowid = iMem++; /* Rowid for the inserted record */
-
-#ifdef SQLITE_ENABLE_STAT2
- int addr = 0; /* Instruction address */
- int regTemp2 = iMem++; /* Temporary use register */
- int regSamplerecno = iMem++; /* Index of next sample to record */
- int regRecno = iMem++; /* Current sample index */
- int regLast = iMem++; /* Index of last sample to record */
- int regFirst = iMem++; /* Index of first sample to record */
-#endif
+ int regNewRowid = iMem++; /* Rowid for the inserted record */
+
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
@@ -165,9 +497,14 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol;
KeyInfo *pKey;
+ int addrIfNot = 0; /* address of OP_IfNot */
+ int *aChngAddr; /* Array of jump instruction addresses */
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
+ VdbeNoopComment((v, "Begin analysis of %s", pIdx->zName));
nCol = pIdx->nColumn;
+ aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*nCol);
+ if( aChngAddr==0 ) continue;
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
if( iMem+1+(nCol*2)>pParse->nMem ){
pParse->nMem = iMem+1+(nCol*2);
@@ -182,31 +519,20 @@ static void analyzeOneTable(
/* Populate the register containing the index name. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
-#ifdef SQLITE_ENABLE_STAT2
-
- /* If this iteration of the loop is generating code to analyze the
- ** first index in the pTab->pIndex list, then register regLast has
- ** not been populated. In this case populate it now. */
- if( pTab->pIndex==pIdx ){
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2);
-
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast);
- sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst);
- addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast);
- sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst);
- sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast);
- sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2);
- sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast);
- sqlite3VdbeJumpHere(v, addr);
+#ifdef SQLITE_ENABLE_STAT3
+ if( once ){
+ once = 0;
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
}
-
- /* Zero the regSampleno and regRecno registers. */
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno);
- sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno);
-#endif
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
+ sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_STAT3_SAMPLES, regTemp1);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumEq);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regNumLt);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regNumDLt);
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regCount, regAccum,
+ (char*)&stat3InitFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2);
+#endif /* SQLITE_ENABLE_STAT3 */
/* The block of memory cells initialized here is used as follows.
**
@@ -236,75 +562,83 @@ static void analyzeOneTable(
endOfLoop = sqlite3VdbeMakeLabel(v);
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
topOfLoop = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
+ sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); /* Increment row counter */
for(i=0; i<nCol; i++){
CollSeq *pColl;
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
if( i==0 ){
-#ifdef SQLITE_ENABLE_STAT2
- /* Check if the record that cursor iIdxCur points to contains a
- ** value that should be stored in the sqlite_stat2 table. If so,
- ** store it. */
- int ne = sqlite3VdbeAddOp3(v, OP_Ne, regRecno, 0, regSamplerecno);
- assert( regTabname+1==regIdxname
- && regTabname+2==regSampleno
- && regTabname+3==regCol
- );
- sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
-
- /* Calculate new values for regSamplerecno and regSampleno.
- **
- ** sampleno = sampleno + 1
- ** samplerecno = samplerecno+(remaining records)/(remaining samples)
- */
- sqlite3VdbeAddOp2(v, OP_AddImm, regSampleno, 1);
- sqlite3VdbeAddOp3(v, OP_Subtract, regRecno, regLast, regTemp);
- sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2);
- sqlite3VdbeAddOp3(v, OP_Subtract, regSampleno, regTemp2, regTemp2);
- sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regTemp, regTemp);
- sqlite3VdbeAddOp3(v, OP_Add, regSamplerecno, regTemp, regSamplerecno);
-
- sqlite3VdbeJumpHere(v, ne);
- sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
-#endif
-
/* Always record the very first row */
- sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
+ addrIfNot = sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
}
assert( pIdx->azColl!=0 );
assert( pIdx->azColl[i]!=0 );
pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
- sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
- (char*)pColl, P4_COLLSEQ);
+ aChngAddr[i] = sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
+ (char*)pColl, P4_COLLSEQ);
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- }
- if( db->mallocFailed ){
- /* If a malloc failure has occurred, then the result of the expression
- ** passed as the second argument to the call to sqlite3VdbeJumpHere()
- ** below may be negative. Which causes an assert() to fail (or an
- ** out-of-bounds write if SQLITE_DEBUG is not defined). */
- return;
+ VdbeComment((v, "jump if column %d changed", i));
+#ifdef SQLITE_ENABLE_STAT3
+ if( i==0 ){
+ sqlite3VdbeAddOp2(v, OP_AddImm, regNumEq, 1);
+ VdbeComment((v, "incr repeat count"));
+ }
+#endif
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
for(i=0; i<nCol; i++){
- int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
+ sqlite3VdbeJumpHere(v, aChngAddr[i]); /* Set jump dest for the OP_Ne */
if( i==0 ){
- sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
+ sqlite3VdbeJumpHere(v, addrIfNot); /* Jump dest for OP_IfNot */
+#ifdef SQLITE_ENABLE_STAT3
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
+ (char*)&stat3PushFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 5);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, pIdx->nColumn, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Add, regNumEq, regNumLt, regNumLt);
+ sqlite3VdbeAddOp2(v, OP_AddImm, regNumDLt, 1);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, regNumEq);
+#endif
}
- sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
}
+ sqlite3DbFree(db, aChngAddr);
- /* End of the analysis loop. */
+ /* Always jump here after updating the iMem+1...iMem+1+nCol counters */
sqlite3VdbeResolveLabel(v, endOfLoop);
+
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
+#ifdef SQLITE_ENABLE_STAT3
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regNumEq, regTemp2,
+ (char*)&stat3PushFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 5);
+ sqlite3VdbeAddOp2(v, OP_Integer, -1, regLoop);
+ shortJump =
+ sqlite3VdbeAddOp2(v, OP_AddImm, regLoop, 1);
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regTemp1,
+ (char*)&stat3GetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2);
+ sqlite3VdbeAddOp1(v, OP_IsNull, regTemp1);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iTabCur, shortJump, regTemp1);
+ sqlite3VdbeAddOp3(v, OP_Column, iTabCur, pIdx->aiColumn[0], regSample);
+ sqlite3ColumnDefault(v, pTab, pIdx->aiColumn[0], regSample);
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumEq,
+ (char*)&stat3GetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 3);
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumLt,
+ (char*)&stat3GetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 4);
+ sqlite3VdbeAddOp4(v, OP_Function, 1, regAccum, regNumDLt,
+ (char*)&stat3GetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 5);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 6, regRec, "bbbbbb", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regNewRowid);
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, shortJump);
+ sqlite3VdbeJumpHere(v, shortJump+2);
+#endif
/* Store the results in sqlite_stat1.
**
@@ -324,22 +658,22 @@ static void analyzeOneTable(
** If K>0 then it is always the case the D>0 so division by zero
** is never possible.
*/
- sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
+ sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regStat1);
if( jZeroRows<0 ){
jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
}
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
+ sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
+ sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regStat1, regStat1);
}
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
}
@@ -349,22 +683,23 @@ static void analyzeOneTable(
if( pTab->pIndex==0 ){
sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
VdbeComment((v, "%s", pTab->zName));
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat1);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
- jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno);
+ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
}else{
sqlite3VdbeJumpHere(v, jZeroRows);
jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
}
sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regNewRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
if( pParse->nMem<regRec ) pParse->nMem = regRec;
sqlite3VdbeJumpHere(v, jZeroRows);
}
+
/*
** Generate code that will cause the most recent index analysis to
** be loaded into internal hash tables where is can be used.
@@ -388,7 +723,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
- pParse->nTab += 2;
+ pParse->nTab += 3;
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
@@ -413,7 +748,7 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
- pParse->nTab += 2;
+ pParse->nTab += 3;
if( pOnlyIdx ){
openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx");
}else{
@@ -518,7 +853,7 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
Index *pIndex;
Table *pTable;
int i, c, n;
- unsigned int v;
+ tRowcnt v;
const char *z;
assert( argc==3 );
@@ -561,10 +896,10 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
if( pIdx->aSample ){
int j;
- for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
+ for(j=0; j<pIdx->nSample; j++){
IndexSample *p = &pIdx->aSample[j];
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
sqlite3DbFree(db, p->u.z);
@@ -572,25 +907,157 @@ void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
}
sqlite3DbFree(db, pIdx->aSample);
}
+ if( db && db->pnBytesFreed==0 ){
+ pIdx->nSample = 0;
+ pIdx->aSample = 0;
+ }
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
#endif
}
+#ifdef SQLITE_ENABLE_STAT3
/*
-** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
+** Load content from the sqlite_stat3 table into the Index.aSample[]
+** arrays of all indices.
+*/
+static int loadStat3(sqlite3 *db, const char *zDb){
+ int rc; /* Result codes from subroutines */
+ sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
+ char *zSql; /* Text of the SQL statement */
+ Index *pPrevIdx = 0; /* Previous index in the loop */
+ int idx = 0; /* slot in pIdx->aSample[] for next sample */
+ int eType; /* Datatype of a sample */
+ IndexSample *pSample; /* A slot in pIdx->aSample[] */
+
+ if( !sqlite3FindTable(db, "sqlite_stat3", zDb) ){
+ return SQLITE_OK;
+ }
+
+ zSql = sqlite3MPrintf(db,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat3"
+ " GROUP BY idx", zDb);
+ if( !zSql ){
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ sqlite3DbFree(db, zSql);
+ if( rc ) return rc;
+
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int nSample; /* Number of samples */
+
+ zIndex = (char *)sqlite3_column_text(pStmt, 0);
+ if( zIndex==0 ) continue;
+ nSample = sqlite3_column_int(pStmt, 1);
+ pIdx = sqlite3FindIndex(db, zIndex, zDb);
+ if( pIdx==0 ) continue;
+ assert( pIdx->nSample==0 );
+ pIdx->nSample = nSample;
+ pIdx->aSample = sqlite3MallocZero( nSample*sizeof(IndexSample) );
+ pIdx->avgEq = pIdx->aiRowEst[1];
+ if( pIdx->aSample==0 ){
+ db->mallocFailed = 1;
+ sqlite3_finalize(pStmt);
+ return SQLITE_NOMEM;
+ }
+ }
+ rc = sqlite3_finalize(pStmt);
+ if( rc ) return rc;
+
+ zSql = sqlite3MPrintf(db,
+ "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat3", zDb);
+ if( !zSql ){
+ return SQLITE_NOMEM;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ sqlite3DbFree(db, zSql);
+ if( rc ) return rc;
+
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int i; /* Loop counter */
+ tRowcnt sumEq; /* Sum of the nEq values */
+
+ zIndex = (char *)sqlite3_column_text(pStmt, 0);
+ if( zIndex==0 ) continue;
+ pIdx = sqlite3FindIndex(db, zIndex, zDb);
+ if( pIdx==0 ) continue;
+ if( pIdx==pPrevIdx ){
+ idx++;
+ }else{
+ pPrevIdx = pIdx;
+ idx = 0;
+ }
+ assert( idx<pIdx->nSample );
+ pSample = &pIdx->aSample[idx];
+ pSample->nEq = (tRowcnt)sqlite3_column_int64(pStmt, 1);
+ pSample->nLt = (tRowcnt)sqlite3_column_int64(pStmt, 2);
+ pSample->nDLt = (tRowcnt)sqlite3_column_int64(pStmt, 3);
+ if( idx==pIdx->nSample-1 ){
+ if( pSample->nDLt>0 ){
+ for(i=0, sumEq=0; i<=idx-1; i++) sumEq += pIdx->aSample[i].nEq;
+ pIdx->avgEq = (pSample->nLt - sumEq)/pSample->nDLt;
+ }
+ if( pIdx->avgEq<=0 ) pIdx->avgEq = 1;
+ }
+ eType = sqlite3_column_type(pStmt, 4);
+ pSample->eType = (u8)eType;
+ switch( eType ){
+ case SQLITE_INTEGER: {
+ pSample->u.i = sqlite3_column_int64(pStmt, 4);
+ break;
+ }
+ case SQLITE_FLOAT: {
+ pSample->u.r = sqlite3_column_double(pStmt, 4);
+ break;
+ }
+ case SQLITE_NULL: {
+ break;
+ }
+ default: assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); {
+ const char *z = (const char *)(
+ (eType==SQLITE_BLOB) ?
+ sqlite3_column_blob(pStmt, 4):
+ sqlite3_column_text(pStmt, 4)
+ );
+ int n = z ? sqlite3_column_bytes(pStmt, 4) : 0;
+ pSample->nByte = n;
+ if( n < 1){
+ pSample->u.z = 0;
+ }else{
+ pSample->u.z = sqlite3Malloc(n);
+ if( pSample->u.z==0 ){
+ db->mallocFailed = 1;
+ sqlite3_finalize(pStmt);
+ return SQLITE_NOMEM;
+ }
+ memcpy(pSample->u.z, z, n);
+ }
+ }
+ }
+ }
+ return sqlite3_finalize(pStmt);
+}
+#endif /* SQLITE_ENABLE_STAT3 */
+
+/*
+** Load the content of the sqlite_stat1 and sqlite_stat3 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat2 are used to populate the
+** arrays. The contents of sqlite_stat3 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined
-** during compilation and the sqlite_stat2 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT3 was defined
+** during compilation and the sqlite_stat3 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT2 was defined during compilation and the
-** sqlite_stat2 table is not present in the database, SQLITE_ERROR is
+** If SQLITE_ENABLE_STAT3 was defined during compilation and the
+** sqlite_stat3 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
**
@@ -612,8 +1079,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
+#ifdef SQLITE_ENABLE_STAT3
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
+#endif
}
/* Check to make sure the sqlite_stat1 table exists */
@@ -625,7 +1094,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
/* Load new statistics out of the sqlite_stat1 table */
zSql = sqlite3MPrintf(db,
- "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
+ "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
if( zSql==0 ){
rc = SQLITE_NOMEM;
}else{
@@ -634,78 +1103,10 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
}
- /* Load the statistics from the sqlite_stat2 table. */
-#ifdef SQLITE_ENABLE_STAT2
- if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){
- rc = SQLITE_ERROR;
- }
+ /* Load the statistics from the sqlite_stat3 table. */
+#ifdef SQLITE_ENABLE_STAT3
if( rc==SQLITE_OK ){
- sqlite3_stmt *pStmt = 0;
-
- zSql = sqlite3MPrintf(db,
- "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase);
- if( !zSql ){
- rc = SQLITE_NOMEM;
- }else{
- rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
- sqlite3DbFree(db, zSql);
- }
-
- if( rc==SQLITE_OK ){
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
- char *zIndex; /* Index name */
- Index *pIdx; /* Pointer to the index object */
-
- zIndex = (char *)sqlite3_column_text(pStmt, 0);
- pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0;
- if( pIdx ){
- int iSample = sqlite3_column_int(pStmt, 1);
- if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
- int eType = sqlite3_column_type(pStmt, 2);
-
- if( pIdx->aSample==0 ){
- static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
- pIdx->aSample = (IndexSample *)sqlite3DbMallocRaw(0, sz);
- if( pIdx->aSample==0 ){
- db->mallocFailed = 1;
- break;
- }
- memset(pIdx->aSample, 0, sz);
- }
-
- assert( pIdx->aSample );
- {
- IndexSample *pSample = &pIdx->aSample[iSample];
- pSample->eType = (u8)eType;
- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
- pSample->u.r = sqlite3_column_double(pStmt, 2);
- }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
- const char *z = (const char *)(
- (eType==SQLITE_BLOB) ?
- sqlite3_column_blob(pStmt, 2):
- sqlite3_column_text(pStmt, 2)
- );
- int n = sqlite3_column_bytes(pStmt, 2);
- if( n>24 ){
- n = 24;
- }
- pSample->nByte = (u8)n;
- if( n < 1){
- pSample->u.z = 0;
- }else{
- pSample->u.z = sqlite3DbStrNDup(0, z, n);
- if( pSample->u.z==0 ){
- db->mallocFailed = 1;
- break;
- }
- }
- }
- }
- }
- }
- }
- rc = sqlite3_finalize(pStmt);
- }
+ rc = loadStat3(db, sInfo.zDatabase);
}
#endif
diff --git a/src/backup.c b/src/backup.c
index 70a782665..411a9b8d6 100644
--- a/src/backup.c
+++ b/src/backup.c
@@ -669,10 +669,18 @@ void sqlite3BackupRestart(sqlite3_backup *pBackup){
*/
int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
int rc;
+ sqlite3_file *pFd; /* File descriptor for database pTo */
sqlite3_backup b;
sqlite3BtreeEnter(pTo);
sqlite3BtreeEnter(pFrom);
+ assert( sqlite3BtreeIsInTrans(pTo) );
+ pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
+ if( pFd->pMethods ){
+ i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
+ sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
+ }
+
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
** to 0. This is used by the implementations of sqlite3_backup_step()
** and sqlite3_backup_finish() to detect that they are being called
@@ -698,6 +706,7 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
pTo->pBt->pageSizeFixed = 0;
}
+ assert( sqlite3BtreeIsInTrans(pTo)==0 );
sqlite3BtreeLeave(pFrom);
sqlite3BtreeLeave(pTo);
return rc;
diff --git a/src/btree.c b/src/btree.c
index 7e6e02f14..72d61cc26 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -3938,21 +3938,55 @@ static int accessPayload(
/* Need to read this page properly. It contains some of the
** range of data that is being read (eOp==0) or written (eOp!=0).
*/
- DbPage *pDbPage;
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ sqlite3_file *fd;
+#endif
int a = amt;
- rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
- if( rc==SQLITE_OK ){
- aPayload = sqlite3PagerGetData(pDbPage);
- nextPage = get4byte(aPayload);
- if( a + offset > ovflSize ){
- a = ovflSize - offset;
+ if( a + offset > ovflSize ){
+ a = ovflSize - offset;
+ }
+
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ /* If all the following are true:
+ **
+ ** 1) this is a read operation, and
+ ** 2) data is required from the start of this overflow page, and
+ ** 3) the database is file-backed, and
+ ** 4) there is no open write-transaction, and
+ ** 5) the database is not a WAL database,
+ **
+ ** then data can be read directly from the database file into the
+ ** output buffer, bypassing the page-cache altogether. This speeds
+ ** up loading large records that span many overflow pages.
+ */
+ if( eOp==0 /* (1) */
+ && offset==0 /* (2) */
+ && pBt->inTransaction==TRANS_READ /* (4) */
+ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
+ && pBt->pPage1->aData[19]==0x01 /* (5) */
+ ){
+ u8 aSave[4];
+ u8 *aWrite = &pBuf[-4];
+ memcpy(aSave, aWrite, 4);
+ rc = sqlite3OsRead(fd, aWrite, a+4, pBt->pageSize * (nextPage-1));
+ nextPage = get4byte(aWrite);
+ memcpy(aWrite, aSave, 4);
+ }else
+#endif
+
+ {
+ DbPage *pDbPage;
+ rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
+ if( rc==SQLITE_OK ){
+ aPayload = sqlite3PagerGetData(pDbPage);
+ nextPage = get4byte(aPayload);
+ rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
+ sqlite3PagerUnref(pDbPage);
+ offset = 0;
}
- rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
- sqlite3PagerUnref(pDbPage);
- offset = 0;
- amt -= a;
- pBuf += a;
}
+ amt -= a;
+ pBuf += a;
}
}
}
diff --git a/src/build.c b/src/build.c
index 27130ef28..d7f08e496 100644
--- a/src/build.c
+++ b/src/build.c
@@ -1990,7 +1990,11 @@ static void sqlite3ClearStatTables(
const char *zType, /* "idx" or "tbl" */
const char *zName /* Name of index or table */
){
- static const char *azStatTab[] = { "sqlite_stat1", "sqlite_stat2" };
+ static const char *azStatTab[] = {
+ "sqlite_stat1",
+ "sqlite_stat2",
+ "sqlite_stat3",
+ };
int i;
const char *zDbName = pParse->db->aDb[iDb].zName;
for(i=0; i<ArraySize(azStatTab); i++){
@@ -2004,6 +2008,76 @@ static void sqlite3ClearStatTables(
}
/*
+** Generate code to drop a table.
+*/
+void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
+ Vdbe *v;
+ sqlite3 *db = pParse->db;
+ Trigger *pTrigger;
+ Db *pDb = &db->aDb[iDb];
+
+ v = sqlite3GetVdbe(pParse);
+ assert( v!=0 );
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( IsVirtual(pTab) ){
+ sqlite3VdbeAddOp0(v, OP_VBegin);
+ }
+#endif
+
+ /* Drop all triggers associated with the table being dropped. Code
+ ** is generated to remove entries from sqlite_master and/or
+ ** sqlite_temp_master if required.
+ */
+ pTrigger = sqlite3TriggerList(pParse, pTab);
+ while( pTrigger ){
+ assert( pTrigger->pSchema==pTab->pSchema ||
+ pTrigger->pSchema==db->aDb[1].pSchema );
+ sqlite3DropTriggerPtr(pParse, pTrigger);
+ pTrigger = pTrigger->pNext;
+ }
+
+#ifndef SQLITE_OMIT_AUTOINCREMENT
+ /* Remove any entries of the sqlite_sequence table associated with
+ ** the table being dropped. This is done before the table is dropped
+ ** at the btree level, in case the sqlite_sequence table needs to
+ ** move as a result of the drop (can happen in auto-vacuum mode).
+ */
+ if( pTab->tabFlags & TF_Autoincrement ){
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
+ pDb->zName, pTab->zName
+ );
+ }
+#endif
+
+ /* Drop all SQLITE_MASTER table and index entries that refer to the
+ ** table. The program name loops through the master table and deletes
+ ** every row that refers to a table of the same name as the one being
+ ** dropped. Triggers are handled seperately because a trigger can be
+ ** created in the temp database that refers to a table in another
+ ** database.
+ */
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
+ pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
+ if( !isView && !IsVirtual(pTab) ){
+ destroyTable(pParse, pTab);
+ }
+
+ /* Remove the table entry from SQLite's internal schema and modify
+ ** the schema cookie.
+ */
+ if( IsVirtual(pTab) ){
+ sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
+ }
+ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
+ sqlite3ChangeCookie(pParse, iDb);
+ sqliteViewResetAll(db, iDb);
+}
+
+/*
** This routine is called to do the work of a DROP TABLE statement.
** pName is the name of the table to be dropped.
*/
@@ -2071,7 +2145,8 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
}
}
#endif
- if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
+ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
+ && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -2095,68 +2170,11 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){
*/
v = sqlite3GetVdbe(pParse);
if( v ){
- Trigger *pTrigger;
- Db *pDb = &db->aDb[iDb];
sqlite3BeginWriteOperation(pParse, 1, iDb);
-
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(pTab) ){
- sqlite3VdbeAddOp0(v, OP_VBegin);
- }
-#endif
- sqlite3FkDropTable(pParse, pName, pTab);
-
- /* Drop all triggers associated with the table being dropped. Code
- ** is generated to remove entries from sqlite_master and/or
- ** sqlite_temp_master if required.
- */
- pTrigger = sqlite3TriggerList(pParse, pTab);
- while( pTrigger ){
- assert( pTrigger->pSchema==pTab->pSchema ||
- pTrigger->pSchema==db->aDb[1].pSchema );
- sqlite3DropTriggerPtr(pParse, pTrigger);
- pTrigger = pTrigger->pNext;
- }
-
-#ifndef SQLITE_OMIT_AUTOINCREMENT
- /* Remove any entries of the sqlite_sequence table associated with
- ** the table being dropped. This is done before the table is dropped
- ** at the btree level, in case the sqlite_sequence table needs to
- ** move as a result of the drop (can happen in auto-vacuum mode).
- */
- if( pTab->tabFlags & TF_Autoincrement ){
- sqlite3NestedParse(pParse,
- "DELETE FROM %s.sqlite_sequence WHERE name=%Q",
- pDb->zName, pTab->zName
- );
- }
-#endif
-
- /* Drop all SQLITE_MASTER table and index entries that refer to the
- ** table. The program name loops through the master table and deletes
- ** every row that refers to a table of the same name as the one being
- ** dropped. Triggers are handled seperately because a trigger can be
- ** created in the temp database that refers to a table in another
- ** database.
- */
- sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
- pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
- if( !isView && !IsVirtual(pTab) ){
- destroyTable(pParse, pTab);
- }
-
- /* Remove the table entry from SQLite's internal schema and modify
- ** the schema cookie.
- */
- if( IsVirtual(pTab) ){
- sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
- }
- sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
- sqlite3ChangeCookie(pParse, iDb);
+ sqlite3FkDropTable(pParse, pName, pTab);
+ sqlite3CodeDropTable(pParse, pTab, iDb, isView);
}
- sqliteViewResetAll(db, iDb);
exit_drop_table:
sqlite3SrcListDelete(db, pName);
@@ -2639,8 +2657,8 @@ Index *sqlite3CreateIndex(
nCol = pList->nExpr;
pIndex = sqlite3DbMallocZero(db,
sizeof(Index) + /* Index structure */
+ sizeof(tRowcnt)*(nCol+1) + /* Index.aiRowEst */
sizeof(int)*nCol + /* Index.aiColumn */
- sizeof(int)*(nCol+1) + /* Index.aiRowEst */
sizeof(char *)*nCol + /* Index.azColl */
sizeof(u8)*nCol + /* Index.aSortOrder */
nName + 1 + /* Index.zName */
@@ -2649,10 +2667,10 @@ Index *sqlite3CreateIndex(
if( db->mallocFailed ){
goto exit_create_index;
}
- pIndex->azColl = (char**)(&pIndex[1]);
+ pIndex->aiRowEst = (tRowcnt*)(&pIndex[1]);
+ pIndex->azColl = (char**)(&pIndex->aiRowEst[nCol+1]);
pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
- pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]);
- pIndex->aSortOrder = (u8 *)(&pIndex->aiRowEst[nCol+1]);
+ pIndex->aSortOrder = (u8 *)(&pIndex->aiColumn[nCol]);
pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
zExtra = (char *)(&pIndex->zName[nName+1]);
memcpy(pIndex->zName, zName, nName+1);
@@ -2929,9 +2947,9 @@ exit_create_index:
** are based on typical values found in actual indices.
*/
void sqlite3DefaultRowEst(Index *pIdx){
- unsigned *a = pIdx->aiRowEst;
+ tRowcnt *a = pIdx->aiRowEst;
int i;
- unsigned n;
+ tRowcnt n;
assert( a!=0 );
a[0] = pIdx->pTable->nRowEst;
if( a[0]<10 ) a[0] = 10;
diff --git a/src/ctime.c b/src/ctime.c
index cbf8ed55f..bea7faaf4 100644
--- a/src/ctime.c
+++ b/src/ctime.c
@@ -117,6 +117,9 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_ENABLE_STAT2
"ENABLE_STAT2",
#endif
+#ifdef SQLITE_ENABLE_STAT3
+ "ENABLE_STAT3",
+#endif
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
"ENABLE_UNLOCK_NOTIFY",
#endif
@@ -329,9 +332,6 @@ static const char * const azCompileOpt[] = {
#ifdef SQLITE_OMIT_XFER_OPT
"OMIT_XFER_OPT",
#endif
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- "PAGECACHE_BLOCKALLOC",
-#endif
#ifdef SQLITE_PERFORMANCE_TRACE
"PERFORMANCE_TRACE",
#endif
diff --git a/src/insert.c b/src/insert.c
index ca4f19904..bd0e4a11e 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -1757,6 +1757,9 @@ static int xferOptimization(
return 0;
}
#endif
+ if( (pParse->db->flags & SQLITE_CountRows)!=0 ){
+ return 0;
+ }
/* If we get this far, it means either:
**
diff --git a/src/os_unix.c b/src/os_unix.c
index d85a7949b..a23a76234 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4888,8 +4888,16 @@ static int findCreateFileMode(
** used by the test_multiplex.c module.
*/
nDb = sqlite3Strlen30(zPath) - 1;
- while( nDb>0 && zPath[nDb]!='-' ) nDb--;
- if( nDb==0 ) return SQLITE_OK;
+#ifdef SQLITE_ENABLE_8_3_NAMES
+ while( nDb>0 && zPath[nDb]!='-' && zPath[nDb]!='/' ) nDb--;
+ if( nDb==0 || zPath[nDb]=='/' ) return SQLITE_OK;
+#else
+ while( zPath[nDb]!='-' ){
+ assert( nDb>0 );
+ assert( zPath[nDb]!='\n' );
+ nDb--;
+ }
+#endif
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
diff --git a/src/os_win.c b/src/os_win.c
index 33ca96c92..b68b03670 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -2615,7 +2615,7 @@ static int winOpen(
pFile->lastErrno = GetLastError();
winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
free(zConverted);
- if( isReadWrite ){
+ if( isReadWrite && !isExclusive ){
return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
}else{
diff --git a/src/pager.c b/src/pager.c
index f8d3ba998..99a3ebd4c 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -670,8 +670,8 @@ struct Pager {
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
+ int nHit, nMiss; /* Total cache hits and misses */
#ifdef SQLITE_TEST
- int nHit, nMiss; /* Cache hits and missing */
int nRead, nWrite; /* Database pages read/written */
#endif
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
@@ -4169,7 +4169,7 @@ static int pagerStress(void *p, PgHdr *pPg){
**
** Spilling is also prohibited when in an error state since that could
** lead to database corruption. In the current implementaton it
- ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
+ ** is impossible for sqlite3PcacheFetch() to be called with createFlag==1
** while in the error state, hence it is impossible for this routine to
** be called in the error state. Nevertheless, we include a NEVER()
** test for the error state as a safeguard against future changes.
@@ -5005,14 +5005,13 @@ int sqlite3PagerAcquire(
/* In this case the pcache already contains an initialized copy of
** the page. Return without further ado. */
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
- PAGER_INCR(pPager->nHit);
+ pPager->nHit++;
return SQLITE_OK;
}else{
/* The pager cache has created a new page. Its content needs to
** be initialized. */
- PAGER_INCR(pPager->nMiss);
pPg = *ppPage;
pPg->pPager = pPager;
@@ -5048,6 +5047,7 @@ int sqlite3PagerAcquire(
IOTRACE(("ZERO %p %d\n", pPager, pgno));
}else{
assert( pPg->pPager==pPager );
+ pPager->nMiss++;
rc = readDbPage(pPg);
if( rc!=SQLITE_OK ){
goto pager_acquire_err;
@@ -6083,6 +6083,31 @@ int *sqlite3PagerStats(Pager *pPager){
#endif
/*
+** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
+** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
+** current cache hit or miss count, according to the value of eStat. If the
+** reset parameter is non-zero, the cache hit or miss count is zeroed before
+** returning.
+*/
+void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+ int *piStat;
+
+ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
+ || eStat==SQLITE_DBSTATUS_CACHE_MISS
+ );
+ if( eStat==SQLITE_DBSTATUS_CACHE_HIT ){
+ piStat = &pPager->nHit;
+ }else{
+ piStat = &pPager->nMiss;
+ }
+
+ *pnVal += *piStat;
+ if( reset ){
+ *piStat = 0;
+ }
+}
+
+/*
** Return true if this is an in-memory pager.
*/
int sqlite3PagerIsMemdb(Pager *pPager){
diff --git a/src/pager.h b/src/pager.h
index eab7ddaf8..540557248 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -155,6 +155,7 @@ const char *sqlite3PagerJournalname(Pager*);
int sqlite3PagerNosync(Pager*);
void *sqlite3PagerTempSpace(Pager*);
int sqlite3PagerIsMemdb(Pager*);
+void sqlite3PagerCacheStat(Pager *, int, int, int *);
/* Functions used to truncate the database file. */
void sqlite3PagerTruncateImage(Pager*,Pgno);
diff --git a/src/pcache1.c b/src/pcache1.c
index de96e5242..077a7b216 100644
--- a/src/pcache1.c
+++ b/src/pcache1.c
@@ -24,8 +24,6 @@ typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
-typedef struct PGroupBlock PGroupBlock;
-typedef struct PGroupBlockList PGroupBlockList;
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
** of one or more PCaches that are able to recycle each others unpinned
@@ -56,66 +54,8 @@ struct PGroup {
int mxPinned; /* nMaxpage + 10 - nMinPage */
int nCurrentPage; /* Number of purgeable pages allocated */
PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- int isBusy; /* Do not run ReleaseMemory() if true */
- PGroupBlockList *pBlockList; /* List of block-lists for this group */
-#endif
-};
-
-/*
-** If SQLITE_PAGECACHE_BLOCKALLOC is defined when the library is built,
-** each PGroup structure has a linked list of the the following starting
-** at PGroup.pBlockList. There is one entry for each distinct page-size
-** currently used by members of the PGroup (i.e. 1024 bytes, 4096 bytes
-** etc.). Variable PGroupBlockList.nByte is set to the actual allocation
-** size requested by each pcache, which is the database page-size plus
-** the various header structures used by the pcache, pager and btree layers.
-** Usually around (pgsz+200) bytes.
-**
-** This size (pgsz+200) bytes is not allocated efficiently by some
-** implementations of malloc. In particular, some implementations are only
-** able to allocate blocks of memory chunks of 2^N bytes, where N is some
-** integer value. Since the page-size is a power of 2, this means we
-** end up wasting (pgsz-200) bytes in each allocation.
-**
-** If SQLITE_PAGECACHE_BLOCKALLOC is defined, the (pgsz+200) byte blocks
-** are not allocated directly. Instead, blocks of roughly M*(pgsz+200) bytes
-** are requested from malloc allocator. After a block is returned,
-** sqlite3MallocSize() is used to determine how many (pgsz+200) byte
-** allocations can fit in the space returned by malloc(). This value may
-** be more than M.
-**
-** The blocks are stored in a doubly-linked list. Variable PGroupBlock.nEntry
-** contains the number of allocations that will fit in the aData[] space.
-** nEntry is limited to the number of bits in bitmask mUsed. If a slot
-** within aData is in use, the corresponding bit in mUsed is set. Thus
-** when (mUsed+1==(1 << nEntry)) the block is completely full.
-**
-** Each time a slot within a block is freed, the block is moved to the start
-** of the linked-list. And if a block becomes completely full, then it is
-** moved to the end of the list. As a result, when searching for a free
-** slot, only the first block in the list need be examined. If it is full,
-** then it is guaranteed that all blocks are full.
-*/
-struct PGroupBlockList {
- int nByte; /* Size of each allocation in bytes */
- PGroupBlock *pFirst; /* First PGroupBlock in list */
- PGroupBlock *pLast; /* Last PGroupBlock in list */
- PGroupBlockList *pNext; /* Next block-list attached to group */
};
-struct PGroupBlock {
- Bitmask mUsed; /* Mask of used slots */
- int nEntry; /* Maximum number of allocations in aData[] */
- u8 *aData; /* Pointer to data block */
- PGroupBlock *pNext; /* Next PGroupBlock in list */
- PGroupBlock *pPrev; /* Previous PGroupBlock in list */
- PGroupBlockList *pList; /* Owner list */
-};
-
-/* Minimum value for PGroupBlock.nEntry */
-#define PAGECACHE_BLOCKALLOC_MINENTRY 15
-
/* Each page cache is an instance of the following object. Every
** open database file (including each in-memory database and each
** temporary or transient database) has a single page cache which
@@ -220,17 +160,6 @@ static SQLITE_WSD struct PCacheGlobal {
#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
/*
-** Blocks used by the SQLITE_PAGECACHE_BLOCKALLOC blocks to store/retrieve
-** a PGroupBlock pointer based on a pointer to a page buffer.
-*/
-#define PAGE_SET_BLOCKPTR(pCache, pPg, pBlock) \
- ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) = pBlock )
-
-#define PAGE_GET_BLOCKPTR(pCache, pPg) \
- ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) )
-
-
-/*
** Macros to enter and leave the PCache LRU mutex.
*/
#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
@@ -355,139 +284,14 @@ static int pcache1MemSize(void *p){
}
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
-/*
-** The block pBlock belongs to list pList but is not currently linked in.
-** Insert it into the start of the list.
-*/
-static void addBlockToList(PGroupBlockList *pList, PGroupBlock *pBlock){
- pBlock->pPrev = 0;
- pBlock->pNext = pList->pFirst;
- pList->pFirst = pBlock;
- if( pBlock->pNext ){
- pBlock->pNext->pPrev = pBlock;
- }else{
- assert( pList->pLast==0 );
- pList->pLast = pBlock;
- }
-}
-
-/*
-** If there are no blocks in the list headed by pList, remove pList
-** from the pGroup->pBlockList list and free it with sqlite3_free().
-*/
-static void freeListIfEmpty(PGroup *pGroup, PGroupBlockList *pList){
- assert( sqlite3_mutex_held(pGroup->mutex) );
- if( pList->pFirst==0 ){
- PGroupBlockList **pp;
- for(pp=&pGroup->pBlockList; *pp!=pList; pp=&(*pp)->pNext);
- *pp = (*pp)->pNext;
- sqlite3_free(pList);
- }
-}
-#endif /* SQLITE_PAGECACHE_BLOCKALLOC */
-
/*
** Allocate a new page object initially associated with cache pCache.
*/
static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
int nByte = sizeof(PgHdr1) + pCache->szPage;
- void *pPg = 0;
- PgHdr1 *p;
-
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- PGroup *pGroup = pCache->pGroup;
- PGroupBlockList *pList;
- PGroupBlock *pBlock;
- int i;
-
- nByte += sizeof(PGroupBlockList *);
- nByte = ROUND8(nByte);
-
- for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
- if( pList->nByte==nByte ) break;
- }
- if( pList==0 ){
- PGroupBlockList *pNew;
- assert( pGroup->isBusy==0 );
- assert( sqlite3_mutex_held(pGroup->mutex) );
- pGroup->isBusy = 1; /* Disable sqlite3PcacheReleaseMemory() */
- pNew = (PGroupBlockList *)sqlite3MallocZero(sizeof(PGroupBlockList));
- pGroup->isBusy = 0; /* Reenable sqlite3PcacheReleaseMemory() */
- if( pNew==0 ){
- /* malloc() failure. Return early. */
- return 0;
- }
-#ifdef SQLITE_DEBUG
- for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
- assert( pList->nByte!=nByte );
- }
-#endif
- pNew->nByte = nByte;
- pNew->pNext = pGroup->pBlockList;
- pGroup->pBlockList = pNew;
- pList = pNew;
- }
+ PgHdr1 *p = 0;
+ void *pPg;
- pBlock = pList->pFirst;
- if( pBlock==0 || pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) ){
- int sz;
-
- /* Allocate a new block. Try to allocate enough space for the PGroupBlock
- ** structure and MINENTRY allocations of nByte bytes each. If the
- ** allocator returns more memory than requested, then more than MINENTRY
- ** allocations may fit in it. */
- assert( sqlite3_mutex_held(pGroup->mutex) );
- pcache1LeaveMutex(pCache->pGroup);
- sz = sizeof(PGroupBlock) + PAGECACHE_BLOCKALLOC_MINENTRY * nByte;
- pBlock = (PGroupBlock *)sqlite3Malloc(sz);
- pcache1EnterMutex(pCache->pGroup);
-
- if( !pBlock ){
- freeListIfEmpty(pGroup, pList);
- return 0;
- }
- pBlock->nEntry = (sqlite3MallocSize(pBlock) - sizeof(PGroupBlock)) / nByte;
- if( pBlock->nEntry>=BMS ){
- pBlock->nEntry = BMS-1;
- }
- pBlock->pList = pList;
- pBlock->mUsed = 0;
- pBlock->aData = (u8 *)&pBlock[1];
- addBlockToList(pList, pBlock);
-
- sz = sqlite3MallocSize(pBlock);
- sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
- sqlite3_mutex_leave(pcache1.mutex);
- }
-
- for(i=0; pPg==0 && ALWAYS(i<pBlock->nEntry); i++){
- if( 0==(pBlock->mUsed & ((Bitmask)1<<i)) ){
- pBlock->mUsed |= ((Bitmask)1<<i);
- pPg = (void *)&pBlock->aData[pList->nByte * i];
- }
- }
- assert( pPg );
- PAGE_SET_BLOCKPTR(pCache, pPg, pBlock);
-
- /* If the block is now full, shift it to the end of the list */
- if( pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) && pList->pLast!=pBlock ){
- assert( pList->pFirst==pBlock );
- assert( pBlock->pPrev==0 );
- assert( pList->pLast->pNext==0 );
- pList->pFirst = pBlock->pNext;
- pList->pFirst->pPrev = 0;
- pBlock->pPrev = pList->pLast;
- pBlock->pNext = 0;
- pList->pLast->pNext = pBlock;
- pList->pLast = pBlock;
- }
- p = PAGE_TO_PGHDR1(pCache, pPg);
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage++;
- }
-#else
/* The group mutex must be released before pcache1Alloc() is called. This
** is because it may call sqlite3_release_memory(), which assumes that
** this mutex is not held. */
@@ -495,15 +299,13 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
pcache1LeaveMutex(pCache->pGroup);
pPg = pcache1Alloc(nByte);
pcache1EnterMutex(pCache->pGroup);
+
if( pPg ){
p = PAGE_TO_PGHDR1(pCache, pPg);
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage++;
}
- }else{
- p = 0;
}
-#endif
return p;
}
@@ -517,49 +319,8 @@ static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
static void pcache1FreePage(PgHdr1 *p){
if( ALWAYS(p) ){
PCache1 *pCache = p->pCache;
- void *pPg = PGHDR1_TO_PAGE(p);
-
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- PGroupBlock *pBlock = PAGE_GET_BLOCKPTR(pCache, pPg);
- PGroupBlockList *pList = pBlock->pList;
- int i = ((u8 *)pPg - pBlock->aData) / pList->nByte;
-
- assert( pPg==(void *)&pBlock->aData[i*pList->nByte] );
- assert( pBlock->mUsed & ((Bitmask)1<<i) );
- pBlock->mUsed &= ~((Bitmask)1<<i);
-
- /* Remove the block from the list. If it is completely empty, free it.
- ** Or if it is not completely empty, re-insert it at the start of the
- ** list. */
- if( pList->pFirst==pBlock ){
- pList->pFirst = pBlock->pNext;
- if( pList->pFirst ) pList->pFirst->pPrev = 0;
- }else{
- pBlock->pPrev->pNext = pBlock->pNext;
- }
- if( pList->pLast==pBlock ){
- pList->pLast = pBlock->pPrev;
- if( pList->pLast ) pList->pLast->pNext = 0;
- }else{
- pBlock->pNext->pPrev = pBlock->pPrev;
- }
-
- if( pBlock->mUsed==0 ){
- PGroup *pGroup = p->pCache->pGroup;
-
- int sz = sqlite3MallocSize(pBlock);
- sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -sz);
- sqlite3_mutex_leave(pcache1.mutex);
- freeListIfEmpty(pGroup, pList);
- sqlite3_free(pBlock);
- }else{
- addBlockToList(pList, pBlock);
- }
-#else
assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
- pcache1Free(pPg);
-#endif
+ pcache1Free(PGHDR1_TO_PAGE(p));
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage--;
}
@@ -1170,9 +931,6 @@ void sqlite3PCacheSetDefault(void){
*/
int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- if( pcache1.grp.isBusy ) return 0;
-#endif
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
if( pcache1.pStart==0 ){
diff --git a/src/pragma.c b/src/pragma.c
index cd2aab223..11345078a 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -533,8 +533,10 @@ void sqlite3Pragma(
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
int ii; /* Loop counter */
- /* Force the schema to be loaded on all databases. This cases all
- ** database files to be opened and the journal_modes set. */
+ /* Force the schema to be loaded on all databases. This causes all
+ ** database files to be opened and the journal_modes set. This is
+ ** necessary because subsequent processing must know if the databases
+ ** are in WAL mode. */
if( sqlite3ReadSchema(pParse) ){
goto pragma_out;
}
diff --git a/src/shell.c b/src/shell.c
index 9759e93b0..48933bc79 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -1029,7 +1029,12 @@ static int display_stats(
fprintf(pArg->out, "Lookaside failures due to OOM: %d\n", iHiwtr);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_USED, &iCur, &iHiwtr, bReset);
- fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur);
+ fprintf(pArg->out, "Pager Heap Usage: %d bytes\n", iCur); iHiwtr = iCur = -1;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
+ fprintf(pArg->out, "Page cache hits: %d\n", iCur);
+ iHiwtr = iCur = -1;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
+ fprintf(pArg->out, "Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
fprintf(pArg->out, "Schema Heap Usage: %d bytes\n", iCur);
@@ -1673,7 +1678,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
fprintf(stderr, "Error: non-null separator required for import\n");
return 1;
}
- zSql = sqlite3_mprintf("SELECT * FROM '%q'", zTable);
+ zSql = sqlite3_mprintf("SELECT * FROM %s", zTable);
if( zSql==0 ){
fprintf(stderr, "Error: out of memory\n");
return 1;
@@ -1695,7 +1700,7 @@ static int do_meta_command(char *zLine, struct callback_data *p){
fprintf(stderr, "Error: out of memory\n");
return 1;
}
- sqlite3_snprintf(nByte+20, zSql, "INSERT INTO '%q' VALUES(?", zTable);
+ sqlite3_snprintf(nByte+20, zSql, "INSERT INTO %s VALUES(?", zTable);
j = strlen30(zSql);
for(i=1; i<nCol; i++){
zSql[j++] = ',';
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 4ac73770a..6dcdd6fc2 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -766,7 +766,11 @@ struct sqlite3_io_methods {
** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
** WAL mode. If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
-**
+**
+** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
+** a write transaction to indicate that, unless it is rolled back for some
+** reason, the entire database file will be overwritten by the current
+** transaction. This is used by VACUUM operations.
*/
#define SQLITE_FCNTL_LOCKSTATE 1
#define SQLITE_GET_LOCKPROXYFILE 2
@@ -778,6 +782,7 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_SYNC_OMITTED 8
#define SQLITE_FCNTL_WIN32_AV_RETRY 9
#define SQLITE_FCNTL_PERSIST_WAL 10
+#define SQLITE_FCNTL_OVERWRITE 11
/*
** CAPI3REF: Mutex Handle
@@ -2845,7 +2850,7 @@ int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled.
+** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
** the
** </li>
** </ol>
@@ -3348,6 +3353,12 @@ int sqlite3_step(sqlite3_stmt*);
** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
** interfaces) then sqlite3_data_count(P) returns 0.
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
+** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
+** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P)
+** will return non-zero if previous call to [sqlite3_step](P) returned
+** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]
+** where it always returns zero since each step of that multi-step
+** pragma returns 0 columns of data.
**
** See also: [sqlite3_column_count()]
*/
@@ -5810,6 +5821,18 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
** the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.
** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
+** <dd>This parameter returns the number of pager cache hits that have
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
+** is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
+** <dd>This parameter returns the number of pager cache misses that have
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
+** is always 0.
+** </dd>
** </dl>
*/
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@@ -5819,7 +5842,9 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
-#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */
+#define SQLITE_DBSTATUS_CACHE_HIT 7
+#define SQLITE_DBSTATUS_CACHE_MISS 8
+#define SQLITE_DBSTATUS_MAX 8 /* Largest defined DBSTATUS */
/*
@@ -5873,7 +5898,6 @@ int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** A non-zero value in this counter may indicate an opportunity to
** improvement performance by adding permanent indices that do not
** need to be reinitialized each time the statement is run.</dd>
-**
** </dl>
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index b1cc52437..7fca71332 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -156,7 +156,7 @@
** assertion will be triggered.
**
** (Historical note: There used to be several other options, but we've
-** pared it down to just these two.)
+** pared it down to just these three.)
**
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
** the default.
@@ -452,6 +452,18 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
#define SQLITE_MAX_U32 ((((u64)1)<<32)-1)
/*
+** The datatype used to store estimates of the number of rows in a
+** table or index. This is an unsigned integer type. For 99.9% of
+** the world, a 32-bit integer is sufficient. But a 64-bit integer
+** can be used at compile-time if desired.
+*/
+#ifdef SQLITE_64BIT_STATS
+ typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */
+#else
+ typedef u32 tRowcnt; /* 32-bit is the default */
+#endif
+
+/*
** Macros to determine whether the machine is big or little endian,
** evaluated at runtime.
*/
@@ -1292,7 +1304,7 @@ struct Table {
Column *aCol; /* Information about each column */
Index *pIndex; /* List of SQL indexes on this table. */
int tnum; /* Root BTree node for this table (see note above) */
- unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
+ tRowcnt nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
u16 nRef; /* Number of pointers to this Table */
u8 tabFlags; /* Mask of TF_* values */
@@ -1491,7 +1503,7 @@ struct Index {
char *zName; /* Name of this index */
int nColumn; /* Number of columns in the table used by this index */
int *aiColumn; /* Which columns are used by this index. 1st is 0 */
- unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
+ tRowcnt *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
Table *pTable; /* The SQL table being indexed */
int tnum; /* Page containing root of this index in database file */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
@@ -1502,7 +1514,11 @@ struct Index {
Schema *pSchema; /* Schema containing this index */
u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
char **azColl; /* Array of collation sequence names for index */
- IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */
+#ifdef SQLITE_ENABLE_STAT3
+ int nSample; /* Number of elements in aSample[] */
+ tRowcnt avgEq; /* Average nEq value for key values not in aSample */
+ IndexSample *aSample; /* Samples of the left-most key */
+#endif
};
/*
@@ -1512,10 +1528,14 @@ struct Index {
struct IndexSample {
union {
char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
- double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */
+ double r; /* Value if eType is SQLITE_FLOAT */
+ i64 i; /* Value if eType is SQLITE_INTEGER */
} u;
u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
- u8 nByte; /* Size in byte of text or blob. */
+ int nByte; /* Size in byte of text or blob. */
+ tRowcnt nEq; /* Est. number of rows where the key equals this sample */
+ tRowcnt nLt; /* Est. number of rows where key is less than this sample */
+ tRowcnt nDLt; /* Est. number of distinct keys less than this sample */
};
/*
@@ -1967,10 +1987,10 @@ struct WhereLevel {
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
-#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */
-#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
-#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
-#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */
+#define WHERE_OMIT_OPEN_CLOSE 0x0010 /* Table cursors are already open */
+#define WHERE_FORCE_TABLE 0x0020 /* Do not use an index-only search */
+#define WHERE_ONETABLE_ONLY 0x0040 /* Only code the 1st table in pTabList */
+#define WHERE_AND_ONLY 0x0080 /* Don't use indices for OR terms */
/*
** The WHERE clause processing routine has two halves. The
@@ -2725,6 +2745,7 @@ void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
#endif
void sqlite3DropTable(Parse*, SrcList*, int, int);
+void sqlite3CodeDropTable(Parse*, Table*, int, int);
void sqlite3DeleteTable(sqlite3*, Table*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
void sqlite3AutoincrementBegin(Parse *pParse);
@@ -2981,7 +3002,7 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
void sqlite3ValueFree(sqlite3_value*);
sqlite3_value *sqlite3ValueNew(sqlite3 *);
char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
#endif
int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
diff --git a/src/status.c b/src/status.c
index b8c1d58df..b23238bb1 100644
--- a/src/status.c
+++ b/src/status.c
@@ -218,6 +218,28 @@ int sqlite3_db_status(
break;
}
+ /*
+ ** Set *pCurrent to the total cache hits or misses encountered by all
+ ** pagers the database handle is connected to. *pHighwater is always set
+ ** to zero.
+ */
+ case SQLITE_DBSTATUS_CACHE_HIT:
+ case SQLITE_DBSTATUS_CACHE_MISS: {
+ int i;
+ int nRet = 0;
+ assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
+
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt ){
+ Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
+ sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
+ }
+ }
+ *pHighwater = 0;
+ *pCurrent = nRet;
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
}
diff --git a/src/tclsqlite.c b/src/tclsqlite.c
index 66cacc130..ddd960853 100644
--- a/src/tclsqlite.c
+++ b/src/tclsqlite.c
@@ -3685,33 +3685,34 @@ int Md5_Register(sqlite3 *db){
** the TCL interpreter reads and evaluates that file.
*/
#if TCLSH==1
-static char zMainloop[] =
- "set line {}\n"
- "while {![eof stdin]} {\n"
- "if {$line!=\"\"} {\n"
- "puts -nonewline \"> \"\n"
- "} else {\n"
- "puts -nonewline \"% \"\n"
- "}\n"
- "flush stdout\n"
- "append line [gets stdin]\n"
- "if {[info complete $line]} {\n"
- "if {[catch {uplevel #0 $line} result]} {\n"
- "puts stderr \"Error: $result\"\n"
- "} elseif {$result!=\"\"} {\n"
- "puts $result\n"
+static const char *tclsh_main_loop(void){
+ static const char zMainloop[] =
+ "set line {}\n"
+ "while {![eof stdin]} {\n"
+ "if {$line!=\"\"} {\n"
+ "puts -nonewline \"> \"\n"
+ "} else {\n"
+ "puts -nonewline \"% \"\n"
+ "}\n"
+ "flush stdout\n"
+ "append line [gets stdin]\n"
+ "if {[info complete $line]} {\n"
+ "if {[catch {uplevel #0 $line} result]} {\n"
+ "puts stderr \"Error: $result\"\n"
+ "} elseif {$result!=\"\"} {\n"
+ "puts $result\n"
+ "}\n"
+ "set line {}\n"
+ "} else {\n"
+ "append line \\n\n"
"}\n"
- "set line {}\n"
- "} else {\n"
- "append line \\n\n"
"}\n"
- "}\n"
-;
+ ;
+ return zMainloop;
+}
#endif
#if TCLSH==2
-static char zMainloop[] =
-#include "spaceanal_tcl.h"
-;
+static const char *tclsh_main_loop(void);
#endif
#ifdef SQLITE_TEST
@@ -3795,6 +3796,17 @@ static void init_all(Tcl_Interp *interp){
Md5_Init(interp);
#endif
+ /* Install the [register_dbstat_vtab] command to access the implementation
+ ** of virtual table dbstat (source file test_stat.c). This command is
+ ** required for testfixture and sqlite3_analyzer, but not by the production
+ ** Tcl extension. */
+#if defined(SQLITE_TEST) || TCLSH==2
+ {
+ extern int SqlitetestStat_Init(Tcl_Interp*);
+ SqlitetestStat_Init(interp);
+ }
+#endif
+
#ifdef SQLITE_TEST
{
extern int Sqliteconfig_Init(Tcl_Interp*);
@@ -3824,7 +3836,6 @@ static void init_all(Tcl_Interp *interp){
extern int Sqlitetestbackup_Init(Tcl_Interp*);
extern int Sqlitetestintarray_Init(Tcl_Interp*);
extern int Sqlitetestvfs_Init(Tcl_Interp *);
- extern int SqlitetestStat_Init(Tcl_Interp*);
extern int Sqlitetestrtree_Init(Tcl_Interp*);
extern int Sqlitequota_Init(Tcl_Interp*);
extern int Sqlitemultiplex_Init(Tcl_Interp*);
@@ -3870,7 +3881,6 @@ static void init_all(Tcl_Interp *interp){
Sqlitetestbackup_Init(interp);
Sqlitetestintarray_Init(interp);
Sqlitetestvfs_Init(interp);
- SqlitetestStat_Init(interp);
Sqlitetestrtree_Init(interp);
Sqlitequota_Init(interp);
Sqlitemultiplex_Init(interp);
@@ -3908,12 +3918,13 @@ int TCLSH_MAIN(int argc, char **argv){
** sqlite3_initialize() is. */
sqlite3_shutdown();
+ Tcl_FindExecutable(argv[0]);
+ interp = Tcl_CreateInterp();
+
#if TCLSH==2
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
#endif
- Tcl_FindExecutable(argv[0]);
- interp = Tcl_CreateInterp();
init_all(interp);
if( argc>=2 ){
int i;
@@ -3934,7 +3945,7 @@ int TCLSH_MAIN(int argc, char **argv){
}
}
if( TCLSH==2 || argc<=1 ){
- Tcl_GlobalEval(interp, zMainloop);
+ Tcl_GlobalEval(interp, tclsh_main_loop());
}
return 0;
}
diff --git a/src/test_config.c b/src/test_config.c
index da13083d8..bebf62f4e 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -55,6 +55,12 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "debug", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ Tcl_SetVar2(interp, "sqlite_options", "direct_read", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "direct_read", "0", TCL_GLOBAL_ONLY);
+#endif
+
#ifdef SQLITE_DISABLE_DIRSYNC
Tcl_SetVar2(interp, "sqlite_options", "dirsync", "0", TCL_GLOBAL_ONLY);
#else
@@ -436,6 +442,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "stat2", "0", TCL_GLOBAL_ONLY);
#endif
+#ifdef SQLITE_ENABLE_STAT3
+ Tcl_SetVar2(interp, "sqlite_options", "stat3", "1", TCL_GLOBAL_ONLY);
+#else
+ Tcl_SetVar2(interp, "sqlite_options", "stat3", "0", TCL_GLOBAL_ONLY);
+#endif
+
#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
# if defined(__APPLE__)
# define SQLITE_ENABLE_LOCKING_STYLE 1
@@ -567,12 +579,6 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "yytrackmaxstackdepth", "0", TCL_GLOBAL_ONLY);
#endif
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- Tcl_SetVar2(interp, "sqlite_options", "blockalloc", "1", TCL_GLOBAL_ONLY);
-#else
- Tcl_SetVar2(interp, "sqlite_options", "blockalloc", "0", TCL_GLOBAL_ONLY);
-#endif
-
#define LINKVAR(x) { \
static const int cv_ ## x = SQLITE_ ## x; \
Tcl_LinkVar(interp, "SQLITE_" #x, (char *)&(cv_ ## x), \
diff --git a/src/test_malloc.c b/src/test_malloc.c
index 46ec94d32..e955d5790 100644
--- a/src/test_malloc.c
+++ b/src/test_malloc.c
@@ -1325,11 +1325,13 @@ static int test_db_status(
{ "STMT_USED", SQLITE_DBSTATUS_STMT_USED },
{ "LOOKASIDE_HIT", SQLITE_DBSTATUS_LOOKASIDE_HIT },
{ "LOOKASIDE_MISS_SIZE", SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE },
- { "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL }
+ { "LOOKASIDE_MISS_FULL", SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL },
+ { "CACHE_HIT", SQLITE_DBSTATUS_CACHE_HIT },
+ { "CACHE_MISS", SQLITE_DBSTATUS_CACHE_MISS }
};
Tcl_Obj *pResult;
if( objc!=4 ){
- Tcl_WrongNumArgs(interp, 1, objv, "PARAMETER RESETFLAG");
+ Tcl_WrongNumArgs(interp, 1, objv, "DB PARAMETER RESETFLAG");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
diff --git a/src/test_stat.c b/src/test_stat.c
index c85463e52..ef8176904 100644
--- a/src/test_stat.c
+++ b/src/test_stat.c
@@ -18,7 +18,9 @@
** for an example implementation.
*/
-#include "sqliteInt.h"
+#ifndef SQLITE_AMALGAMATION
+# include "sqliteInt.h"
+#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -62,20 +64,11 @@
" ncell INTEGER, /* Cells on page (0 for overflow) */" \
" payload INTEGER, /* Bytes of payload on this page */" \
" unused INTEGER, /* Bytes of unused space on this page */" \
- " mx_payload INTEGER /* Largest payload size of all cells */" \
+ " mx_payload INTEGER, /* Largest payload size of all cells */" \
+ " pgoffset INTEGER, /* Offset of page in file */" \
+ " pgsize INTEGER /* Size of the page */" \
");"
-#if 0
-#define VTAB_SCHEMA2 \
- "CREATE TABLE yy( " \
- " pageno INTEGER, /* B-tree page number */" \
- " cellno INTEGER, /* Cell number within page */" \
- " local INTEGER, /* Bytes of content stored locally */" \
- " payload INTEGER, /* Total cell payload size */" \
- " novfl INTEGER /* Number of overflow pages */" \
- ");"
-#endif
-
typedef struct StatTable StatTable;
typedef struct StatCursor StatCursor;
@@ -124,6 +117,8 @@ struct StatCursor {
int nPayload; /* Value of 'payload' column */
int nUnused; /* Value of 'unused' column */
int nMxPayload; /* Value of 'mx_payload' column */
+ i64 iOffset; /* Value of 'pgOffset' column */
+ int szPage; /* Value of 'pgSize' column */
};
struct StatTable {
@@ -281,6 +276,7 @@ static int statDecodePage(Btree *pBt, StatPage *p){
int iOff;
int nHdr;
int isLeaf;
+ int szPage;
u8 *aData = sqlite3PagerGetData(p->pPg);
u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
@@ -301,10 +297,11 @@ static int statDecodePage(Btree *pBt, StatPage *p){
}
p->nUnused = nUnused;
p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
+ szPage = sqlite3BtreeGetPageSize(pBt);
if( p->nCell ){
int i; /* Used to iterate through cells */
- int nUsable = sqlite3BtreeGetPageSize(pBt) - sqlite3BtreeGetReserve(pBt);
+ int nUsable = szPage - sqlite3BtreeGetReserve(pBt);
p->aCell = sqlite3_malloc((p->nCell+1) * sizeof(StatCell));
memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
@@ -360,6 +357,32 @@ static int statDecodePage(Btree *pBt, StatPage *p){
}
/*
+** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on
+** the current value of pCsr->iPageno.
+*/
+static void statSizeAndOffset(StatCursor *pCsr){
+ StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab;
+ Btree *pBt = pTab->db->aDb[0].pBt;
+ Pager *pPager = sqlite3BtreePager(pBt);
+ sqlite3_file *fd;
+ sqlite3_int64 x[2];
+
+ /* The default page size and offset */
+ pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
+ pCsr->iOffset = pCsr->szPage * (pCsr->iPageno - 1);
+
+ /* If connected to a ZIPVFS backend, override the page size and
+ ** offset with actual values obtained from ZIPVFS.
+ */
+ fd = sqlite3PagerFile(pPager);
+ x[0] = pCsr->iPageno;
+ if( sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
+ pCsr->iOffset = x[0];
+ pCsr->szPage = x[1];
+ }
+}
+
+/*
** Move a statvfs cursor to the next entry in the file.
*/
static int statNext(sqlite3_vtab_cursor *pCursor){
@@ -417,6 +440,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
}
pCell->iOvfl++;
+ statSizeAndOffset(pCsr);
return SQLITE_OK;
}
if( p->iRightChildPg ) break;
@@ -454,6 +478,7 @@ static int statNext(sqlite3_vtab_cursor *pCursor){
pCsr->iPageno = p->iPgno;
statDecodePage(pBt, p);
+ statSizeAndOffset(pCsr);
switch( p->flags ){
case 0x05: /* table internal */
@@ -529,6 +554,12 @@ static int statColumn(
case 7: /* mx_payload */
sqlite3_result_int(ctx, pCsr->nMxPayload);
break;
+ case 8: /* pgoffset */
+ sqlite3_result_int64(ctx, pCsr->iOffset);
+ break;
+ case 9: /* pgsize */
+ sqlite3_result_int(ctx, pCsr->szPage);
+ break;
}
return SQLITE_OK;
}
@@ -568,7 +599,7 @@ int sqlite3_dbstat_register(sqlite3 *db){
#endif
-#ifdef SQLITE_TEST
+#if defined(SQLITE_TEST) || TCLSH==2
#include <tcl.h>
static int test_dbstat(
@@ -604,4 +635,4 @@ int SqlitetestStat_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp, "register_dbstat_vtab", test_dbstat, 0, 0);
return TCL_OK;
}
-#endif
+#endif /* if defined(SQLITE_TEST) || TCLSH==2 */
diff --git a/src/utf.c b/src/utf.c
index 17f3a09a4..e94815b5a 100644
--- a/src/utf.c
+++ b/src/utf.c
@@ -464,7 +464,7 @@ char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){
** If a malloc failure occurs, NULL is returned and the db.mallocFailed
** flag set.
*/
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
Mem m;
memset(&m, 0, sizeof(m));
diff --git a/src/vacuum.c b/src/vacuum.c
index 5a4ed3205..58a3c9d68 100644
--- a/src/vacuum.c
+++ b/src/vacuum.c
@@ -45,7 +45,7 @@ static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
return sqlite3_errcode(db);
}
VVA_ONLY( rc = ) sqlite3_step(pStmt);
- assert( rc!=SQLITE_ROW );
+ assert( rc!=SQLITE_ROW || (db->flags&SQLITE_CountRows) );
return vacuumFinalize(db, pStmt, pzErrMsg);
}
@@ -263,13 +263,11 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
);
if( rc ) goto end_of_vacuum;
- /* At this point, unless the main db was completely empty, there is now a
- ** transaction open on the vacuum database, but not on the main database.
- ** Open a btree level transaction on the main database. This allows a
- ** call to sqlite3BtreeCopyFile(). The main database btree level
- ** transaction is then committed, so the SQL level never knows it was
- ** opened for writing. This way, the SQL transaction used to create the
- ** temporary database never needs to be committed.
+ /* At this point, there is a write transaction open on both the
+ ** vacuum database and the main database. Assuming no error occurs,
+ ** both transactions are closed by this block - the main database
+ ** transaction by sqlite3BtreeCopyFile() and the other by an explicit
+ ** call to sqlite3BtreeCommit().
*/
{
u32 meta;
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 709272440..927bb6e48 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -576,8 +576,8 @@ void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- assert( addr>=0 );
- sqlite3VdbeChangeP2(p, addr, p->nOp);
+ assert( addr>=0 || p->db->mallocFailed );
+ if( addr>=0 ) sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -1143,7 +1143,7 @@ int sqlite3VdbeList(
sqlite3 *db = p->db; /* The database connection */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
- Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */
+ Mem *pMem = &p->aMem[1]; /* First Mem of result set */
assert( p->explain );
assert( p->magic==VDBE_MAGIC_RUN );
@@ -1154,6 +1154,7 @@ int sqlite3VdbeList(
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
releaseMemArray(pMem, 8);
+ p->pResultSet = 0;
if( p->rc==SQLITE_NOMEM ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
@@ -1308,6 +1309,7 @@ int sqlite3VdbeList(
}
p->nResColumn = 8 - 4*(p->explain-1);
+ p->pResultSet = &p->aMem[1];
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
}
diff --git a/src/vdbemem.c b/src/vdbemem.c
index d51257282..e6e915627 100644
--- a/src/vdbemem.c
+++ b/src/vdbemem.c
@@ -1026,11 +1026,11 @@ int sqlite3ValueFromExpr(
}
op = pExpr->op;
- /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
+ /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3.
** The ifdef here is to enable us to achieve 100% branch test coverage even
- ** when SQLITE_ENABLE_STAT2 is omitted.
+ ** when SQLITE_ENABLE_STAT3 is omitted.
*/
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
if( op==TK_REGISTER ) op = pExpr->op2;
#else
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
diff --git a/src/where.c b/src/where.c
index fd7da6274..3c0244e68 100644
--- a/src/where.c
+++ b/src/where.c
@@ -118,21 +118,31 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
-# define TERM_VNULL 0x00 /* Disabled if not using stat2 */
+# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
#endif
/*
** An instance of the following structure holds all information about a
** WHERE clause. Mostly this is a container for one or more WhereTerms.
+**
+** Explanation of pOuter: For a WHERE clause of the form
+**
+** a AND ((b AND c) OR (d AND e)) AND f
+**
+** There are separate WhereClause objects for the whole clause and for
+** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the
+** subclauses points to the WhereClause object for the whole clause.
*/
struct WhereClause {
Parse *pParse; /* The parser context */
WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */
Bitmask vmask; /* Bitmask identifying virtual table cursors */
+ WhereClause *pOuter; /* Outer conjunction */
u8 op; /* Split operator. TK_AND or TK_OR */
+ u16 wctrlFlags; /* Might include WHERE_AND_ONLY */
int nTerm; /* Number of terms */
int nSlot; /* Number of entries in a[] */
WhereTerm *a; /* Each a[] describes a term of the WHERE cluase */
@@ -261,14 +271,17 @@ struct WhereCost {
static void whereClauseInit(
WhereClause *pWC, /* The WhereClause to be initialized */
Parse *pParse, /* The parsing context */
- WhereMaskSet *pMaskSet /* Mapping from table cursor numbers to bitmasks */
+ WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmasks */
+ u16 wctrlFlags /* Might include WHERE_AND_ONLY */
){
pWC->pParse = pParse;
pWC->pMaskSet = pMaskSet;
+ pWC->pOuter = 0;
pWC->nTerm = 0;
pWC->nSlot = ArraySize(pWC->aStatic);
pWC->a = pWC->aStatic;
pWC->vmask = 0;
+ pWC->wctrlFlags = wctrlFlags;
}
/* Forward reference */
@@ -584,36 +597,38 @@ static WhereTerm *findTerm(
int k;
assert( iCur>=0 );
op &= WO_ALL;
- for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
- if( pTerm->leftCursor==iCur
- && (pTerm->prereqRight & notReady)==0
- && pTerm->u.leftColumn==iColumn
- && (pTerm->eOperator & op)!=0
- ){
- if( pIdx && pTerm->eOperator!=WO_ISNULL ){
- Expr *pX = pTerm->pExpr;
- CollSeq *pColl;
- char idxaff;
- int j;
- Parse *pParse = pWC->pParse;
-
- idxaff = pIdx->pTable->aCol[iColumn].affinity;
- if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
-
- /* Figure out the collation sequence required from an index for
- ** it to be useful for optimising expression pX. Store this
- ** value in variable pColl.
- */
- assert(pX->pLeft);
- pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- assert(pColl || pParse->nErr);
-
- for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
- if( NEVER(j>=pIdx->nColumn) ) return 0;
+ for(; pWC; pWC=pWC->pOuter){
+ for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
+ if( pTerm->leftCursor==iCur
+ && (pTerm->prereqRight & notReady)==0
+ && pTerm->u.leftColumn==iColumn
+ && (pTerm->eOperator & op)!=0
+ ){
+ if( pIdx && pTerm->eOperator!=WO_ISNULL ){
+ Expr *pX = pTerm->pExpr;
+ CollSeq *pColl;
+ char idxaff;
+ int j;
+ Parse *pParse = pWC->pParse;
+
+ idxaff = pIdx->pTable->aCol[iColumn].affinity;
+ if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
+
+ /* Figure out the collation sequence required from an index for
+ ** it to be useful for optimising expression pX. Store this
+ ** value in variable pColl.
+ */
+ assert(pX->pLeft);
+ pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
+ assert(pColl || pParse->nErr);
+
+ for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
+ if( NEVER(j>=pIdx->nColumn) ) return 0;
+ }
+ if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
}
- if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
+ return pTerm;
}
- return pTerm;
}
}
return 0;
@@ -690,7 +705,7 @@ static int isLikeOrGlob(
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
}
- sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-31526-56213 */
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
z = pRight->u.zToken;
@@ -708,7 +723,7 @@ static int isLikeOrGlob(
*ppPrefix = pPrefix;
if( op==TK_VARIABLE ){
Vdbe *v = pParse->pVdbe;
- sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */
+ sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-31526-56213 */
if( *pisComplete && pRight->u.zToken[1] ){
/* If the rhs of the LIKE expression is a variable, and the current
** value of the variable means there is no need to invoke the LIKE
@@ -877,7 +892,7 @@ static void exprAnalyzeOrTerm(
if( pOrInfo==0 ) return;
pTerm->wtFlags |= TERM_ORINFO;
pOrWc = &pOrInfo->wc;
- whereClauseInit(pOrWc, pWC->pParse, pMaskSet);
+ whereClauseInit(pOrWc, pWC->pParse, pMaskSet, pWC->wctrlFlags);
whereSplit(pOrWc, pExpr, TK_OR);
exprAnalyzeAll(pSrc, pOrWc);
if( db->mallocFailed ) return;
@@ -904,9 +919,10 @@ static void exprAnalyzeOrTerm(
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
pAndWC = &pAndInfo->wc;
- whereClauseInit(pAndWC, pWC->pParse, pMaskSet);
+ whereClauseInit(pAndWC, pWC->pParse, pMaskSet, pWC->wctrlFlags);
whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
exprAnalyzeAll(pSrc, pAndWC);
+ pAndWC->pOuter = pWC;
testcase( db->mallocFailed );
if( !db->mallocFailed ){
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
@@ -1340,8 +1356,8 @@ static void exprAnalyze(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-#ifdef SQLITE_ENABLE_STAT2
- /* When sqlite_stat2 histogram data is available an operator of the
+#ifdef SQLITE_ENABLE_STAT3
+ /* When sqlite_stat3 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
** virtual term of that form.
@@ -1379,7 +1395,7 @@ static void exprAnalyze(
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
-#endif /* SQLITE_ENABLE_STAT2 */
+#endif /* SQLITE_ENABLE_STAT */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -1801,11 +1817,14 @@ static void bestOrClauseIndex(
WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
WhereTerm *pTerm; /* A single term of the WHERE clause */
- /* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses
- ** are used */
+ /* The OR-clause optimization is disallowed if the INDEXED BY or
+ ** NOT INDEXED clauses are used or if the WHERE_AND_ONLY bit is set. */
if( pSrc->notIndexed || pSrc->pIndex!=0 ){
return;
}
+ if( pWC->wctrlFlags & WHERE_AND_ONLY ){
+ return;
+ }
/* Search the WHERE clause terms for a usable WO_OR term. */
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
@@ -1833,6 +1852,7 @@ static void bestOrClauseIndex(
WhereClause tempWC;
tempWC.pParse = pWC->pParse;
tempWC.pMaskSet = pWC->pMaskSet;
+ tempWC.pOuter = pWC;
tempWC.op = TK_AND;
tempWC.a = pOrTerm;
tempWC.nTerm = 1;
@@ -2427,67 +2447,86 @@ static void bestVirtualIndex(
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
+#ifdef SQLITE_ENABLE_STAT3
/*
-** Argument pIdx is a pointer to an index structure that has an array of
-** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column
-** stored in Index.aSample. These samples divide the domain of values stored
-** the index into (SQLITE_INDEX_SAMPLES+1) regions.
-** Region 0 contains all values less than the first sample value. Region
-** 1 contains values between the first and second samples. Region 2 contains
-** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES
-** contains values larger than the last sample.
-**
-** If the index contains many duplicates of a single value, then it is
-** possible that two or more adjacent samples can hold the same value.
-** When that is the case, the smallest possible region code is returned
-** when roundUp is false and the largest possible region code is returned
-** when roundUp is true.
-**
-** If successful, this function determines which of the regions value
-** pVal lies in, sets *piRegion to the region index (a value between 0
-** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK.
-** Or, if an OOM occurs while converting text values between encodings,
-** SQLITE_NOMEM is returned and *piRegion is undefined.
+** Estimate the location of a particular key among all keys in an
+** index. Store the results in aStat as follows:
+**
+** aStat[0] Est. number of rows less than pVal
+** aStat[1] Est. number of rows equal to pVal
+**
+** Return SQLITE_OK on success.
*/
-#ifdef SQLITE_ENABLE_STAT2
-static int whereRangeRegion(
+static int whereKeyStats(
Parse *pParse, /* Database connection */
Index *pIdx, /* Index to consider domain of */
sqlite3_value *pVal, /* Value to consider */
- int roundUp, /* Return largest valid region if true */
- int *piRegion /* OUT: Region of domain in which value lies */
+ int roundUp, /* Round up if true. Round down if false */
+ tRowcnt *aStat /* OUT: stats written here */
){
+ tRowcnt n;
+ IndexSample *aSample;
+ int i, eType;
+ int isEq = 0;
+ i64 v;
+ double r, rS;
+
assert( roundUp==0 || roundUp==1 );
- if( ALWAYS(pVal) ){
- IndexSample *aSample = pIdx->aSample;
- int i = 0;
- int eType = sqlite3_value_type(pVal);
-
- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
- double r = sqlite3_value_double(pVal);
- for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
- if( aSample[i].eType==SQLITE_NULL ) continue;
- if( aSample[i].eType>=SQLITE_TEXT ) break;
- if( roundUp ){
- if( aSample[i].u.r>r ) break;
- }else{
- if( aSample[i].u.r>=r ) break;
+ assert( pIdx->nSample>0 );
+ if( pVal==0 ) return SQLITE_ERROR;
+ n = pIdx->aiRowEst[0];
+ aSample = pIdx->aSample;
+ i = 0;
+ eType = sqlite3_value_type(pVal);
+
+ if( eType==SQLITE_INTEGER ){
+ v = sqlite3_value_int64(pVal);
+ r = (i64)v;
+ for(i=0; i<pIdx->nSample; i++){
+ if( aSample[i].eType==SQLITE_NULL ) continue;
+ if( aSample[i].eType>=SQLITE_TEXT ) break;
+ if( aSample[i].eType==SQLITE_INTEGER ){
+ if( aSample[i].u.i>=v ){
+ isEq = aSample[i].u.i==v;
+ break;
+ }
+ }else{
+ assert( aSample[i].eType==SQLITE_FLOAT );
+ if( aSample[i].u.r>=r ){
+ isEq = aSample[i].u.r==r;
+ break;
}
}
- }else if( eType==SQLITE_NULL ){
- i = 0;
- if( roundUp ){
- while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++;
+ }
+ }else if( eType==SQLITE_FLOAT ){
+ r = sqlite3_value_double(pVal);
+ for(i=0; i<pIdx->nSample; i++){
+ if( aSample[i].eType==SQLITE_NULL ) continue;
+ if( aSample[i].eType>=SQLITE_TEXT ) break;
+ if( aSample[i].eType==SQLITE_FLOAT ){
+ rS = aSample[i].u.r;
+ }else{
+ rS = aSample[i].u.i;
}
- }else{
+ if( rS>=r ){
+ isEq = rS==r;
+ break;
+ }
+ }
+ }else if( eType==SQLITE_NULL ){
+ i = 0;
+ if( aSample[0].eType==SQLITE_NULL ) isEq = 1;
+ }else{
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ for(i=0; i<pIdx->nSample; i++){
+ if( aSample[i].eType==SQLITE_TEXT || aSample[i].eType==SQLITE_BLOB ){
+ break;
+ }
+ }
+ if( i<pIdx->nSample ){
sqlite3 *db = pParse->db;
CollSeq *pColl;
const u8 *z;
- int n;
-
- /* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */
- assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
-
if( eType==SQLITE_BLOB ){
z = (const u8 *)sqlite3_value_blob(pVal);
pColl = db->pDfltColl;
@@ -2506,12 +2545,12 @@ static int whereRangeRegion(
assert( z && pColl && pColl->xCmp );
}
n = sqlite3ValueBytes(pVal, pColl->enc);
-
- for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
+
+ for(; i<pIdx->nSample; i++){
int c;
int eSampletype = aSample[i].eType;
- if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
- if( (eSampletype!=eType) ) break;
+ if( eSampletype<eType ) continue;
+ if( eSampletype!=eType ) break;
#ifndef SQLITE_OMIT_UTF16
if( pColl->enc!=SQLITE_UTF8 ){
int nSample;
@@ -2529,16 +2568,47 @@ static int whereRangeRegion(
{
c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
}
- if( c-roundUp>=0 ) break;
+ if( c>=0 ){
+ if( c==0 ) isEq = 1;
+ break;
+ }
}
}
+ }
- assert( i>=0 && i<=SQLITE_INDEX_SAMPLES );
- *piRegion = i;
+ /* At this point, aSample[i] is the first sample that is greater than
+ ** or equal to pVal. Or if i==pIdx->nSample, then all samples are less
+ ** than pVal. If aSample[i]==pVal, then isEq==1.
+ */
+ if( isEq ){
+ assert( i<pIdx->nSample );
+ aStat[0] = aSample[i].nLt;
+ aStat[1] = aSample[i].nEq;
+ }else{
+ tRowcnt iLower, iUpper, iGap;
+ if( i==0 ){
+ iLower = 0;
+ iUpper = aSample[0].nLt;
+ }else{
+ iUpper = i>=pIdx->nSample ? n : aSample[i].nLt;
+ iLower = aSample[i-1].nEq + aSample[i-1].nLt;
+ }
+ aStat[1] = pIdx->avgEq;
+ if( iLower>=iUpper ){
+ iGap = 0;
+ }else{
+ iGap = iUpper - iLower;
+ }
+ if( roundUp ){
+ iGap = (iGap*2)/3;
+ }else{
+ iGap = iGap/3;
+ }
+ aStat[0] = iLower + iGap;
}
return SQLITE_OK;
}
-#endif /* #ifdef SQLITE_ENABLE_STAT2 */
+#endif /* SQLITE_ENABLE_STAT3 */
/*
** If expression pExpr represents a literal value, set *pp to point to
@@ -2556,7 +2626,7 @@ static int whereRangeRegion(
**
** If an error occurs, return an error code. Otherwise, SQLITE_OK.
*/
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
static int valueFromExpr(
Parse *pParse,
Expr *pExpr,
@@ -2567,7 +2637,7 @@ static int valueFromExpr(
|| (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
){
int iVar = pExpr->iColumn;
- sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-31526-56213 */
*pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
return SQLITE_OK;
}
@@ -2604,17 +2674,15 @@ static int valueFromExpr(
**
** then nEq should be passed 0.
**
-** The returned value is an integer between 1 and 100, inclusive. A return
-** value of 1 indicates that the proposed range scan is expected to visit
-** approximately 1/100th (1%) of the rows selected by the nEq equality
-** constraints (if any). A return value of 100 indicates that it is expected
-** that the range scan will visit every row (100%) selected by the equality
-** constraints.
-**
-** In the absence of sqlite_stat2 ANALYZE data, each range inequality
-** reduces the search space by 3/4ths. Hence a single constraint (x>?)
-** results in a return of 25 and a range constraint (x>? AND x<?) results
-** in a return of 6.
+** The returned value is an integer divisor to reduce the estimated
+** search space. A return value of 1 means that range constraints are
+** no help at all. A return value of 2 means range constraints are
+** expected to reduce the search space by half. And so forth...
+**
+** In the absence of sqlite_stat3 ANALYZE data, each range inequality
+** reduces the search space by a factor of 4. Hence a single constraint (x>?)
+** results in a return of 4 and a range constraint (x>? AND x<?) results
+** in a return of 16.
*/
static int whereRangeScanEst(
Parse *pParse, /* Parsing & code generating context */
@@ -2622,84 +2690,72 @@ static int whereRangeScanEst(
int nEq, /* index into p->aCol[] of the range-compared column */
WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
- int *piEst /* OUT: Return value */
+ double *pRangeDiv /* OUT: Reduce search space by this divisor */
){
int rc = SQLITE_OK;
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
- if( nEq==0 && p->aSample ){
- sqlite3_value *pLowerVal = 0;
- sqlite3_value *pUpperVal = 0;
- int iEst;
- int iLower = 0;
- int iUpper = SQLITE_INDEX_SAMPLES;
- int roundUpUpper = 0;
- int roundUpLower = 0;
+ if( nEq==0 && p->nSample ){
+ sqlite3_value *pRangeVal;
+ tRowcnt iLower = 0;
+ tRowcnt iUpper = p->aiRowEst[0];
+ tRowcnt a[2];
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pLower ){
Expr *pExpr = pLower->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal);
+ rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
- roundUpLower = (pLower->eOperator==WO_GT) ?1:0;
+ if( rc==SQLITE_OK
+ && whereKeyStats(pParse, p, pRangeVal, 0, a)==SQLITE_OK
+ ){
+ iLower = a[0];
+ if( pLower->eOperator==WO_GT ) iLower += a[1];
+ }
+ sqlite3ValueFree(pRangeVal);
}
if( rc==SQLITE_OK && pUpper ){
Expr *pExpr = pUpper->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal);
+ rc = valueFromExpr(pParse, pExpr, aff, &pRangeVal);
assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
- roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0;
- }
-
- if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){
- sqlite3ValueFree(pLowerVal);
- sqlite3ValueFree(pUpperVal);
- goto range_est_fallback;
- }else if( pLowerVal==0 ){
- rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
- if( pLower ) iLower = iUpper/2;
- }else if( pUpperVal==0 ){
- rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
- if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2;
- }else{
- rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
- if( rc==SQLITE_OK ){
- rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
+ if( rc==SQLITE_OK
+ && whereKeyStats(pParse, p, pRangeVal, 1, a)==SQLITE_OK
+ ){
+ iUpper = a[0];
+ if( pUpper->eOperator==WO_LE ) iUpper += a[1];
}
+ sqlite3ValueFree(pRangeVal);
}
- WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper));
-
- iEst = iUpper - iLower;
- testcase( iEst==SQLITE_INDEX_SAMPLES );
- assert( iEst<=SQLITE_INDEX_SAMPLES );
- if( iEst<1 ){
- *piEst = 50/SQLITE_INDEX_SAMPLES;
- }else{
- *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES;
+ if( rc==SQLITE_OK ){
+ if( iUpper<=iLower ){
+ *pRangeDiv = (double)p->aiRowEst[0];
+ }else{
+ *pRangeDiv = (double)p->aiRowEst[0]/(double)(iUpper - iLower);
+ }
+ WHERETRACE(("range scan regions: %u..%u div=%g\n",
+ (u32)iLower, (u32)iUpper, *pRangeDiv));
+ return SQLITE_OK;
}
- sqlite3ValueFree(pLowerVal);
- sqlite3ValueFree(pUpperVal);
- return rc;
}
-range_est_fallback:
#else
UNUSED_PARAMETER(pParse);
UNUSED_PARAMETER(p);
UNUSED_PARAMETER(nEq);
#endif
assert( pLower || pUpper );
- *piEst = 100;
- if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4;
- if( pUpper ) *piEst /= 4;
+ *pRangeDiv = (double)1;
+ if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *pRangeDiv *= (double)4;
+ if( pUpper ) *pRangeDiv *= (double)4;
return rc;
}
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
-** column of an index and sqlite_stat2 histogram data is available
+** column of an index and sqlite_stat3 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
@@ -2719,12 +2775,12 @@ static int whereEqualScanEst(
double *pnRow /* Write the revised row estimate here */
){
sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
- int iLower, iUpper; /* Range of histogram regions containing pRhs */
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
- double nRowEst; /* New estimate of the number of rows */
+ tRowcnt a[2]; /* Statistics */
assert( p->aSample!=0 );
+ assert( p->nSample>0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pExpr ){
rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
@@ -2733,26 +2789,18 @@ static int whereEqualScanEst(
pRhs = sqlite3ValueNew(pParse->db);
}
if( pRhs==0 ) return SQLITE_NOTFOUND;
- rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower);
- if( rc ) goto whereEqualScanEst_cancel;
- rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper);
- if( rc ) goto whereEqualScanEst_cancel;
- WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper));
- if( iLower>=iUpper ){
- nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2);
- if( nRowEst<*pnRow ) *pnRow = nRowEst;
- }else{
- nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES;
- *pnRow = nRowEst;
+ rc = whereKeyStats(pParse, p, pRhs, 0, a);
+ if( rc==SQLITE_OK ){
+ WHERETRACE(("equality scan regions: %d\n", (int)a[1]));
+ *pnRow = a[1];
}
-
whereEqualScanEst_cancel:
sqlite3ValueFree(pRhs);
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT2) */
+#endif /* defined(SQLITE_ENABLE_STAT3) */
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -2775,60 +2823,25 @@ static int whereInScanEst(
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
double *pnRow /* Write the revised row estimate here */
){
- sqlite3_value *pVal = 0; /* One value from list */
- int iLower, iUpper; /* Range of histogram regions containing pRhs */
- u8 aff; /* Column affinity */
- int rc = SQLITE_OK; /* Subfunction return code */
- double nRowEst; /* New estimate of the number of rows */
- int nSpan = 0; /* Number of histogram regions spanned */
- int nSingle = 0; /* Histogram regions hit by a single value */
- int nNotFound = 0; /* Count of values that are not constants */
- int i; /* Loop counter */
- u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */
- u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */
+ int rc = SQLITE_OK; /* Subfunction return code */
+ double nEst; /* Number of rows for a single term */
+ double nRowEst = (double)0; /* New estimate of the number of rows */
+ int i; /* Loop counter */
assert( p->aSample!=0 );
- aff = p->pTable->aCol[p->aiColumn[0]].affinity;
- memset(aSpan, 0, sizeof(aSpan));
- memset(aSingle, 0, sizeof(aSingle));
- for(i=0; i<pList->nExpr; i++){
- sqlite3ValueFree(pVal);
- rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal);
- if( rc ) break;
- if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
- nNotFound++;
- continue;
- }
- rc = whereRangeRegion(pParse, p, pVal, 0, &iLower);
- if( rc ) break;
- rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper);
- if( rc ) break;
- if( iLower>=iUpper ){
- aSingle[iLower] = 1;
- }else{
- assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES );
- while( iLower<iUpper ) aSpan[iLower++] = 1;
- }
+ for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
+ nEst = p->aiRowEst[0];
+ rc = whereEqualScanEst(pParse, p, pList->a[i].pExpr, &nEst);
+ nRowEst += nEst;
}
if( rc==SQLITE_OK ){
- for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){
- if( aSpan[i] ){
- nSpan++;
- }else if( aSingle[i] ){
- nSingle++;
- }
- }
- nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES)
- + nNotFound*p->aiRowEst[1];
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
*pnRow = nRowEst;
- WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n",
- nSpan, nSingle, nNotFound, nRowEst));
+ WHERETRACE(("IN row estimate: est=%g\n", nRowEst));
}
- sqlite3ValueFree(pVal);
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT2) */
+#endif /* defined(SQLITE_ENABLE_STAT3) */
/*
@@ -2875,7 +2888,7 @@ static void bestBtreeIndex(
int eqTermMask; /* Current mask of valid equality operators */
int idxEqTermMask; /* Index mask of valid equality operators */
Index sPk; /* A fake index object for the primary key */
- unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
+ tRowcnt aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */
@@ -2930,10 +2943,10 @@ static void bestBtreeIndex(
/* Loop over all indices looking for the best one to use
*/
for(; pProbe; pIdx=pProbe=pProbe->pNext){
- const unsigned int * const aiRowEst = pProbe->aiRowEst;
+ const tRowcnt * const aiRowEst = pProbe->aiRowEst;
double cost; /* Cost of using pProbe */
double nRow; /* Estimated number of rows in result set */
- double log10N; /* base-10 logarithm of nRow (inexact) */
+ double log10N = (double)1; /* base-10 logarithm of nRow (inexact) */
int rev; /* True to scan in reverse order */
int wsFlags = 0;
Bitmask used = 0;
@@ -2973,14 +2986,12 @@ static void bestBtreeIndex(
** IN operator must be a SELECT, not a value list, for this variable
** to be true.
**
- ** estBound:
- ** An estimate on the amount of the table that must be searched. A
- ** value of 100 means the entire table is searched. Range constraints
- ** might reduce this to a value less than 100 to indicate that only
- ** a fraction of the table needs searching. In the absence of
- ** sqlite_stat2 ANALYZE data, a single inequality reduces the search
- ** space to 1/4rd its original size. So an x>? constraint reduces
- ** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6.
+ ** rangeDiv:
+ ** An estimate of a divisor by which to reduce the search space due
+ ** to inequality constraints. In the absence of sqlite_stat3 ANALYZE
+ ** data, a single inequality reduces the search space to 1/4rd its
+ ** original size (rangeDiv==4). Two inequalities reduce the search
+ ** space to 1/16th of its original size (rangeDiv==16).
**
** bSort:
** Boolean. True if there is an ORDER BY clause that will require an
@@ -3005,13 +3016,13 @@ static void bestBtreeIndex(
int nEq; /* Number of == or IN terms matching index */
int bInEst = 0; /* True if "x IN (SELECT...)" seen */
int nInMul = 1; /* Number of distinct equalities to lookup */
- int estBound = 100; /* Estimated reduction in search space */
+ double rangeDiv = (double)1; /* Estimated reduction in search space */
int nBound = 0; /* Number of range constraints seen */
int bSort = !!pOrderBy; /* True if external sort required */
int bDist = !!pDistinct; /* True if index cannot help with DISTINCT */
int bLookup = 0; /* True if not a covering index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
WhereTerm *pFirstTerm = 0; /* First term matching the index */
#endif
@@ -3021,6 +3032,7 @@ static void bestBtreeIndex(
pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx);
if( pTerm==0 ) break;
wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
+ testcase( pTerm->pWC!=pWC );
if( pTerm->eOperator & WO_IN ){
Expr *pExpr = pTerm->pExpr;
wsFlags |= WHERE_COLUMN_IN;
@@ -3035,28 +3047,30 @@ static void bestBtreeIndex(
}else if( pTerm->eOperator & WO_ISNULL ){
wsFlags |= WHERE_COLUMN_NULL;
}
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
#endif
used |= pTerm->prereqRight;
}
- /* Determine the value of estBound. */
+ /* Determine the value of rangeDiv */
if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){
int j = pProbe->aiColumn[nEq];
if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx);
- whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound);
+ whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &rangeDiv);
if( pTop ){
nBound = 1;
wsFlags |= WHERE_TOP_LIMIT;
used |= pTop->prereqRight;
+ testcase( pTop->pWC!=pWC );
}
if( pBtm ){
nBound++;
wsFlags |= WHERE_BTM_LIMIT;
used |= pBtm->prereqRight;
+ testcase( pBtm->pWC!=pWC );
}
wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
}
@@ -3119,7 +3133,7 @@ static void bestBtreeIndex(
nInMul = (int)(nRow / aiRowEst[nEq]);
}
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3
/* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
** and we do not think that values of x are unique and if histogram
** data is available for column x, then it might be possible
@@ -3127,20 +3141,22 @@ static void bestBtreeIndex(
** VALUE and how common that value is according to the histogram.
*/
if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
+ assert( (pFirstTerm->eOperator & (WO_EQ|WO_ISNULL|WO_IN))!=0 );
if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
testcase( pFirstTerm->eOperator==WO_EQ );
testcase( pFirstTerm->eOperator==WO_ISNULL );
whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
- }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){
+ }else if( bInEst==0 ){
+ assert( pFirstTerm->eOperator==WO_IN );
whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
}
}
-#endif /* SQLITE_ENABLE_STAT2 */
+#endif /* SQLITE_ENABLE_STAT3 */
/* Adjust the number of output rows and downward to reflect rows
** that are excluded by range constraints.
*/
- nRow = (nRow * (double)estBound) / (double)100;
+ nRow = nRow/rangeDiv;
if( nRow<1 ) nRow = 1;
/* Experiments run on real SQLite databases show that the time needed
@@ -3269,10 +3285,10 @@ static void bestBtreeIndex(
WHERETRACE((
- "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
+ "%s(%s): nEq=%d nInMul=%d rangeDiv=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
" notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n",
pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
- nEq, nInMul, estBound, bSort, bLookup, wsFlags,
+ nEq, nInMul, (int)rangeDiv, bSort, bLookup, wsFlags,
notReady, log10N, nRow, cost, used
));
@@ -3776,7 +3792,8 @@ static Bitmask codeOneLoopStart(
WhereInfo *pWInfo, /* Complete information about the WHERE clause */
int iLevel, /* Which level of pWInfo->a[] should be coded */
u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
- Bitmask notReady /* Which tables are currently available */
+ Bitmask notReady, /* Which tables are currently available */
+ Expr *pWhere /* Complete WHERE clause */
){
int j, k; /* Loop counters */
int iCur; /* The VDBE cursor for the table */
@@ -4258,7 +4275,8 @@ static Bitmask codeOneLoopStart(
int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
int iRetInit; /* Address of regReturn init */
int untestedTerms = 0; /* Some terms not completely tested */
- int ii;
+ int ii; /* Loop counter */
+ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
pTerm = pLevel->plan.u.pTerm;
assert( pTerm!=0 );
@@ -4308,13 +4326,28 @@ static Bitmask codeOneLoopStart(
}
iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
+ /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
+ ** Then for every term xN, evaluate as the subexpression: xN AND z
+ ** That way, terms in y that are factored into the disjunction will
+ ** be picked up by the recursive calls to sqlite3WhereBegin() below.
+ */
+ if( pWC->nTerm>1 ){
+ pAndExpr = sqlite3ExprAlloc(pParse->db, TK_AND, 0, 0);
+ pAndExpr->pRight = pWhere;
+ }
+
for(ii=0; ii<pOrWc->nTerm; ii++){
WhereTerm *pOrTerm = &pOrWc->a[ii];
if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
WhereInfo *pSubWInfo; /* Info for single OR-term scan */
+ Expr *pOrExpr = pOrTerm->pExpr;
+ if( pAndExpr ){
+ pAndExpr->pLeft = pOrExpr;
+ pOrExpr = pAndExpr;
+ }
/* Loop through table entries that match term pOrTerm. */
- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
- WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
+ WHERE_OMIT_OPEN_CLOSE | WHERE_AND_ONLY |
WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
if( pSubWInfo ){
explainOneScan(
@@ -4342,6 +4375,7 @@ static Bitmask codeOneLoopStart(
}
}
}
+ sqlite3DbFree(pParse->db, pAndExpr);
sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
sqlite3VdbeResolveLabel(v, iLoopBody);
@@ -4623,7 +4657,7 @@ WhereInfo *sqlite3WhereBegin(
** subexpression is separated by an AND operator.
*/
initMaskSet(pMaskSet);
- whereClauseInit(pWC, pParse, pMaskSet);
+ whereClauseInit(pWC, pParse, pMaskSet, wctrlFlags);
sqlite3ExprCodeConstants(pParse, pWhere);
whereSplit(pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
@@ -4951,7 +4985,7 @@ WhereInfo *sqlite3WhereBegin(
}else
#endif
if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- && (wctrlFlags & WHERE_OMIT_OPEN)==0 ){
+ && (wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0 ){
int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
testcase( pTab->nCol==BMS-1 );
@@ -4996,7 +5030,7 @@ WhereInfo *sqlite3WhereBegin(
for(i=0; i<nTabList; i++){
pLevel = &pWInfo->a[i];
explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags);
- notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
+ notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady, pWhere);
pWInfo->iContinue = pLevel->addrCont;
}
@@ -5131,7 +5165,7 @@ void sqlite3WhereEnd(WhereInfo *pWInfo){
assert( pTab!=0 );
if( (pTab->tabFlags & TF_Ephemeral)==0
&& pTab->pSelect==0
- && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0
+ && (pWInfo->wctrlFlags & WHERE_OMIT_OPEN_CLOSE)==0
){
int ws = pLevel->plan.wsFlags;
if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){