Compare commits

...

9 Commits

Author SHA1 Message Date
drh bd360185f7 Add code to actually use the sqlite_stat2.cnt field in the query planner.
This changes some plans resulting in a few failures in analyze5.test.

FossilOrigin-Name: d1248165e3e02aaf8a2a7872793918b4a9f102a8
2011-08-08 17:18:40 +00:00
drh e8882b2f33 The ANALYZE command adds the sqlite_stat2.cnt column if it does not already
exist.

FossilOrigin-Name: 794fde6f918b405ebe47068dea76a2d3f7d97733
2011-08-07 00:21:17 +00:00
drh ad6dc0ccd7 The sqlite_stat2.cnt field is parsed if it is present. But it is not yet
used.  A large comment added to analyze.c to explain the format of the
ANALYZE system tables.

FossilOrigin-Name: 6d1e2372fe73f4c04561108aac6bc8c95f6e7a1a
2011-08-06 19:48:53 +00:00
drh ed6185ccde Merge together the fork in the query-planner-tweaks branch.
FossilOrigin-Name: 2daab6bd42b34b32de46db513437cc4d6ca17975
2011-08-06 02:03:10 +00:00
drh 17eef8750e Fix a compiler warning when STAT2 is off. More test cases. Fix legacy tests
to deal with the new STAT2 logic.

FossilOrigin-Name: 7e914aa999d9f3f7be58a4494d33a7876af56603
2011-08-06 01:22:50 +00:00
drh 78183ce13f Bug fixes to the sample-count logic for STAT2. A few test cases added.
FossilOrigin-Name: e93c248c8470622944cf7f9e5af4f10c4e4809e3
2011-08-05 22:31:19 +00:00
drh 37c5858fba Add a column to the sqlite_stat2 table that contains the number of entries
with exactly the same key as the sample.  We do not yet do anything with this
extra value.  Some tests in analyze2.test are failing.

FossilOrigin-Name: eb434228277c4bbbb1ad153ed3e6e3eeffe799a4
2011-08-05 21:13:42 +00:00
drh 892abac74a Allow the sqlite_stat2 table to contain a number of samples other than
the default of 10.

FossilOrigin-Name: b9d41c3490bf325915227a32b612e6772fb62fa9
2011-08-05 01:09:49 +00:00
drh 4cd32c4e39 Adjust the output row estimating logic when using STAT2.
FossilOrigin-Name: c8ba8855d8e93cd1e7dbbc9398a5f850fed5a382
2011-08-03 01:07:25 +00:00
11 changed files with 695 additions and 258 deletions
+14 -13
View File
@@ -1,5 +1,5 @@
C Merge\sthe\swinopen-retry-logic\sbranch\sinto\strunk.\s\sThe\sbiggest\schange\shere\nis\sto\stest\sscripts,\swhich\sshould\snow\suse\ssuch\sas\scopy_file\sand\ndelete_file\sfrom\stester.tcl\srather\sthan\sthe\sraw\sfile\scommands\sof\sTCL.
D 2011-08-03T22:06:39.312
C Add\scode\sto\sactually\suse\sthe\ssqlite_stat2.cnt\sfield\sin\sthe\squery\splanner.\nThis\schanges\ssome\splans\sresulting\sin\sa\sfew\sfailures\sin\sanalyze5.test.
D 2011-08-08T17:18:40.515
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in 1e6988b3c11dee9bd5edc0c804bd4468d74a9cdc
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -118,7 +118,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
F src/alter.c ac80a0f31189f8b4a524ebf661e47e84536ee7f5
F src/analyze.c a425d62e8fa9ebcb4359ab84ff0c62c6563d2e2a
F src/analyze.c dcdbff4a23afef0983c4a6fb049cceca905e38a5
F src/attach.c 12c6957996908edc31c96d7c68d4942c2474405f
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 986c15232757f2873dff35ee3b35cbf935fc573c
@@ -183,7 +183,7 @@ F src/select.c d219c4b68d603cc734b6f9b1e2780fee12a1fa0d
F src/shell.c bbe7818ff5bc8614105ceb81ad67b8bdc0b671dd
F src/sqlite.h.in 0b3cab7b2ea51f58396e8871fa5f349cfece5330
F src/sqlite3ext.h 1a1a4f784aa9c3b00edd287940197de52487cd93
F src/sqliteInt.h ba4a6d6288efb25b84bc0d7d0aaf80f9b42523ba
F src/sqliteInt.h fefed2b4717d42c6637285ff4f339c5c648ed5c4
F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
F src/status.c 7ac64842c86cec2fc1a1d0e5c16d3beb8ad332bf
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -238,7 +238,7 @@ F src/update.c 74a6cfb34e9732c1e2a86278b229913b4b51eeec
F src/utf.c c53eb7404b3eb5c1cbb5655c6a7a0e0ce6bd50f0
F src/util.c 06302ffd2b80408d4f6c7af71f7090e0cf8d8ff7
F src/vacuum.c 05513dca036a1e7848fe18d5ed1265ac0b32365e
F src/vdbe.c 49d834f0fe49d305e07f9c212e94007fda2028e9
F src/vdbe.c b46e0dde62b62ada9988ae4478fd06eb393be067
F src/vdbe.h 5cf09e7ee8a3f7d93bc51f196a96550786afe7a1
F src/vdbeInt.h ad84226cc0adcb1185c22b70696b235a1678bb45
F src/vdbeapi.c 11dc47987abacb76ad016dcf5abc0dc422482a98
@@ -250,7 +250,7 @@ F src/vtab.c 901791a47318c0562cd0c676a2c6ff1bc530e582
F src/wal.c 3154756177d6219e233d84291d5b05f4e06ff5e9
F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c 7d09f4c1512affb60cc1190a4b33d121d4ce039a
F src/where.c 67c87af7047a913e88fc755aa4de5412af82a3e8
F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
@@ -261,12 +261,13 @@ F test/alter3.test 49c9d9fba2b8fcdce2dedeca97bbf1f369cc548d
F test/alter4.test b2debc14d8cbe4c1d12ccd6a41eef88a8c1f15d5
F test/altermalloc.test e81ac9657ed25c6c5bb09bebfa5a047cd8e4acfc
F test/analyze.test c8cb89e8736336f1f0646c8123e6028a14c7b55e
F test/analyze2.test 8f2b1534d43f5547ce9a6b736c021d4192c75be3
F test/analyze2.test b2c53cf72aa65383da991d97b9d3ec7b45f72c03
F test/analyze3.test d61f55d8b472fc6e713160b1e577f7a68e63f38b
F test/analyze4.test 757b37875cf9bb528d46f74497bc789c88365045
F test/analyze5.test 1de8d66b11aae5a1453aa042d62e834a476bac9c
F test/analyze5.test f6c250012b173f9b10ae5d577e9592767b376b1c
F test/analyze6.test c125622a813325bba1b4999040ddc213773c2290
F test/analyze7.test 5508e7828164ea0b518ed219bed7320a481863d4
F test/analyze7.test ed672a2c09c4b5a5b528ca38951318af463bb17e
F test/analyze8.test fbb378563ec2f68f8dff4b6f70b7d35088acfd7b
F test/async.test 1d0e056ba1bb9729283a0f22718d3a25e82c277b
F test/async2.test c0a9bd20816d7d6a2ceca7b8c03d3d69c28ffb8b
F test/async3.test d73a062002376d7edc1fe3edff493edbec1fc2f7
@@ -733,7 +734,7 @@ F test/tkt-9d68c883.test 458f7d82a523d7644b54b497c986378a7d8c8b67
F test/tkt-b351d95f9.test d14a503c414c5c58fdde3e80f9a3cfef986498c0
F test/tkt-b72787b1.test e6b62b2b2785c04d0d698d6a603507e384165049
F test/tkt-bd484a090c.test 60460bf946f79a79712b71f202eda501ca99b898
F test/tkt-cbd054fa6b.test f14f97ea43662e6f70c9e63287081e8be5d9d589
F test/tkt-cbd054fa6b.test 2263389efa52207e8527dac7314f839e5edeccda
F test/tkt-d11f09d36e.test fb44f7961aa6d4b632fb7b9768239832210b5fc7
F test/tkt-d635236375.test 9d37e988b47d87505bc9445be0ca447002df5d09
F test/tkt-d82e3f3721.test 731359dfdcdb36fea0559cd33fec39dd0ceae8e6
@@ -955,7 +956,7 @@ F tool/symbols.sh caaf6ccc7300fd43353318b44524853e222557d5
F tool/tostr.awk 11760e1b94a5d3dcd42378f3cc18544c06cfa576
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
F tool/warnings.sh 2ebae31e1eb352696f3c2f7706a34c084b28c262
P a2135ad13049c170b33315a949b1544e6a136183 4cb17881d9676fa3359394391b9ba53f08e5809a
R 69e73a0d833d24111d142317de3be42c
P 794fde6f918b405ebe47068dea76a2d3f7d97733
R 5323b0a6bd81040b68da13fcd743d5fd
U drh
Z e1999267404d37fe86d90d3e80b00531
Z 22bc19a9fa3fe23c4b6a155fd0f36f68
+1 -1
View File
@@ -1 +1 @@
b90c28be3840169651022ef36cd7cf416bc22305
d1248165e3e02aaf8a2a7872793918b4a9f102a8
+295 -135
View File
@@ -10,6 +10,99 @@
**
*************************************************************************
** 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 the best way to implement a
** query.
**
** Two system tables are created as follows:
**
** CREATE TABLE sqlite_stat1(tbl, idx, stat);
** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample, cnt);
**
** Additional tables might be added in future releases of SQLite.
** The sqlite_stat2 table is only created and used if SQLite is
** compiled with SQLITE_ENABLE_STAT2. Older versions of SQLite
** omit the sqlite_stat2.cnt column. Newer versions of SQLite are
** able to use older versions of the stat2 table that lack the cnt
** column.
**
** 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. The "stat2" table contains additional information
** about the key distribution 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 multiple rows in the sqlite_stat2
** table for each index.
**
** The sqlite_stat2 entires for an index that have sampleno>=0 are
** sampled key values for the first column of the index taken at
** intervals along the index. The sqlite_stat2.sample column holds
** the value of the key in the left-most column of the index.
**
** The samples are numbered from 0 to S-1
** where S is 10 by default. The number of samples created by the
** ANALYZE command can be adjusted at compile-time using the
** SQLITE_INDEX_SAMPLES macro. The maximum number of samples is
** SQLITE_MAX_SAMPLES, currently set to 100. There are places in the
** code that use an unsigned character to count samples, so an upper
** bound on SQLITE_MAX_SAMPLES is 255.
**
** Suppose the index contains C rows. And let the number
** of samples be S. SQLite assumes that the samples are taken from the
** following rows for i between 0 and S-1:
**
** rownumber = (i*C*2 + C)/(S*2)
**
** Conceptually, the index is divided into S bins and the sample is
** taken from the middle of each bin. The ANALYZE will not attempt
** to populate sqlite_stat2 for an index that holds fewer than S*2
** entries.
**
** If the key value for a sample (the sqlite_stat2.sample column) is a
** large string or blob, SQLite will only use the first 255 bytes of
** that string or blob.
**
** The sqlite_stat2.cnt column contains the number of entries in the
** index for which sqlite_stat2.sample matches the left-most column
** of the index. In other words, sqlite_stat2.cnt holds the number of
** times the sqlite_stat2.sample value appears in the index.. Many
** older versions of SQLite omit the sqlite_stat2.cnt column.
**
** If the sqlite_stat2.sampleno value is -1, then that row holds a first-
** column key that is a frequently used key in the index. The
** sqlite_stat2.cnt column will hold the number of occurrances of that key.
** This information is useful to the query planner in cases where a
** large percentage of the rows in indexed field have one of a small
** handful of value but the balance of the rows in the index have
** distinct or nearly distinct keys.
*/
#ifndef SQLITE_OMIT_ANALYZE
#include "sqliteInt.h"
@@ -43,7 +136,7 @@ static void openStatTable(
} aTable[] = {
{ "sqlite_stat1", "tbl,idx,stat" },
#ifdef SQLITE_ENABLE_STAT2
{ "sqlite_stat2", "tbl,idx,sampleno,sample" },
{ "sqlite_stat2", "tbl,idx,sampleno,sample,cnt" },
#endif
};
@@ -86,6 +179,15 @@ static void openStatTable(
/* The sqlite_stat[12] table already exists. Delete all rows. */
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
}
#ifdef SQLITE_ENABLE_STAT2
if( i==1 && iDb!=1 && pStat->nCol==4 ){
sqlite3NestedParse(pParse,
"UPDATE %Q.sqlite_master SET sql='CREATE TABLE sqlite_stat2(%s)'"
" WHERE name='sqlite_stat2'", pDb->zName, aTable[i].zCols
);
sqlite3ChangeCookie(pParse, iDb);
}
#endif
}
}
@@ -94,6 +196,7 @@ static void openStatTable(
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
VdbeComment((v, "%s", aTable[i].zName));
}
}
@@ -119,20 +222,26 @@ 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 regSampleno = iMem++; /* Sampleno (stat2) or stat (stat1) */
#ifdef SQLITE_ENABLE_STAT2
int regSample = iMem++; /* The next sample value */
int regSampleCnt = iMem++; /* Number of occurrances of regSample value */
int shortJump = 0; /* Instruction address */
int addrStoreStat2 = 0; /* Address of subroutine to wrote to stat2 */
int regNext = iMem++; /* Index of next sample to record */
int regSampleIdx = iMem++; /* Index of next sample */
int regReady = iMem++; /* True if ready to store a stat2 entry */
int regGosub = iMem++; /* Register holding subroutine return addr */
int regSample2 = iMem++; /* Number of samples to acquire times 2 */
int regCount = iMem++; /* Number of rows in the table */
int regCount2 = iMem++; /* regCount*2 */
int once = 1; /* One-time initialization */
#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
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
@@ -165,13 +274,18 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol;
KeyInfo *pKey;
int addrIfNot; /* 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;
pKey = sqlite3IndexKeyinfo(pParse, pIdx);
if( iMem+1+(nCol*2)>pParse->nMem ){
pParse->nMem = iMem+1+(nCol*2);
}
aChngAddr = sqlite3DbMallocRaw(db, sizeof(int)*pIdx->nColumn);
if( aChngAddr==0 ) continue;
/* Open a cursor to the index to be analyzed. */
assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
@@ -187,26 +301,39 @@ static void analyzeOneTable(
/* 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);
if( once ){
once = 0;
sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regSample2);
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);
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regCount);
sqlite3VdbeAddOp3(v, OP_Add, regCount, regCount, regCount2);
/* Generate code for a subroutine that store the most recent sample
** in the sqlite_stat2 table
*/
shortJump = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 5, regRec, "aaaba", 0);
VdbeComment((v, "begin stat2 write subroutine"));
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
sqlite3VdbeAddOp2(v, OP_AddImm, regSampleno, 1);
sqlite3VdbeAddOp2(v, OP_AddImm, regReady, -1);
addrStoreStat2 = sqlite3VdbeAddOp2(v, OP_IfPos, regReady, shortJump+1);
sqlite3VdbeAddOp1(v, OP_Return, regGosub);
VdbeComment((v, "end stat2 write subroutine"));
sqlite3VdbeJumpHere(v, shortJump);
}
/* Zero the regSampleno and regRecno registers. */
/* Reset state registers */
sqlite3VdbeAddOp2(v, OP_Copy, regCount2, regNext);
shortJump = sqlite3VdbeAddOp3(v, OP_Lt, regSample2, 0, regCount);
sqlite3VdbeAddOp3(v, OP_Divide, regSample2, regCount, regNext);
sqlite3VdbeJumpHere(v, shortJump);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno);
sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno);
#endif
sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleIdx);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regReady);
#endif /* SQLITE_ENABLE_STAT2 */
/* The block of memory cells initialized here is used as follows.
**
@@ -236,75 +363,76 @@ 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_STAT2
if( i==0 && addrStoreStat2 ){
sqlite3VdbeAddOp2(v, OP_AddImm, regSampleCnt, 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_STAT2
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrStoreStat2);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regSampleCnt);
#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);
#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_Le, regNext, 0, iMem);
VdbeComment((v, "jump if not a sample"));
shortJump = sqlite3VdbeAddOp1(v, OP_If, regReady);
sqlite3VdbeAddOp2(v, OP_Copy, iMem+nCol+1, regSample);
sqlite3VdbeJumpHere(v, shortJump);
sqlite3VdbeAddOp2(v, OP_AddImm, regReady, 1);
/* Calculate new values for regNextSample. Where N is the number
** of rows in the table and S is the number of samples to take:
**
** nextSample = (sampleNumber*N*2 + N)/(2*S)
*/
sqlite3VdbeAddOp2(v, OP_AddImm, regSampleIdx, 1);
sqlite3VdbeAddOp3(v, OP_Multiply, regSampleIdx, regCount2, regNext);
sqlite3VdbeAddOp3(v, OP_Add, regNext, regCount, regNext);
sqlite3VdbeAddOp3(v, OP_Divide, regSample2, regNext, regNext);
sqlite3VdbeJumpHere(v, ne);
#endif
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
#ifdef SQLITE_ENABLE_STAT2
sqlite3VdbeAddOp2(v, OP_Gosub, regGosub, addrStoreStat2);
#endif
/* Store the results in sqlite_stat1.
**
@@ -488,7 +616,7 @@ void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
}
sqlite3DbFree(db, z);
}
}
}
}
}
@@ -556,33 +684,68 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
return 0;
}
#if SQLITE_ENABLE_STAT2
/*
** If the Index.aSample variable is not NULL, delete the aSample[] array
** and its contents.
** Delete an array of IndexSample objects
*/
static void deleteIndexSampleArray(
sqlite3 *db, /* The database connection */
IndexSampleArray *pArray /* Array of IndexSample objects */
){
int j;
if( pArray->a==0 ) return;
for(j=0; j<pArray->n; j++){
IndexSample *p = &pArray->a[j];
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
sqlite3_free(p->u.z);
}
}
sqlite3_free(pArray->a);
memset(pArray, 0, sizeof(*pArray));
}
#endif
/*
** Delete the sample and common-key arrays from the index.
*/
void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
#ifdef SQLITE_ENABLE_STAT2
if( pIdx->aSample ){
int j;
for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
IndexSample *p = &pIdx->aSample[j];
if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
sqlite3DbFree(db, p->u.z);
}
}
sqlite3DbFree(db, pIdx->aSample);
}
deleteIndexSampleArray(db, &pIdx->sample);
deleteIndexSampleArray(db, &pIdx->comkey);
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
#endif
}
#ifdef SQLITE_ENABLE_STAT2
/*
** Enlarge an array of IndexSample objects.
*/
static IndexSample *allocIndexSample(
sqlite3 *db, /* Database connection to malloc against */
IndexSampleArray *pArray, /* The array to enlarge */
int i /* Return this element */
){
IndexSample *p;
if( i>=pArray->nAlloc ){
int szNew = i+1;
p = (IndexSample*)sqlite3_realloc(pArray->a, szNew*sizeof(IndexSample));
if( p==0 ) return 0;
pArray->a = p;
memset(&pArray->a[pArray->n], 0, (szNew-(pArray->n))*sizeof(IndexSample));
pArray->nAlloc = szNew;
}
if( i>=pArray->n ) pArray->n = i+1;
return &pArray->a[i];
}
#endif
/*
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
** arrays. The contents of sqlite_stat2 are used to populate the
** Index.aSample[] arrays.
** Index.sample and Index.comkey 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
@@ -603,6 +766,7 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
HashElem *i;
char *zSql;
int rc;
Table *pTab; /* Stat1 or Stat2 table */
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
@@ -613,13 +777,12 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
Index *pIdx = sqliteHashData(i);
sqlite3DefaultRowEst(pIdx);
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
}
/* Check to make sure the sqlite_stat1 table exists */
sInfo.db = db;
sInfo.zDatabase = db->aDb[iDb].zName;
if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){
if( (pTab=sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase))==0 ){
return SQLITE_ERROR;
}
@@ -636,14 +799,17 @@ 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) ){
if( rc==SQLITE_OK
&& (pTab=sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase))==0 ){
rc = SQLITE_ERROR;
}
if( rc==SQLITE_OK ){
sqlite3_stmt *pStmt = 0;
zSql = sqlite3MPrintf(db,
"SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase);
"SELECT idx, sampleno, sample, %s FROM %Q.sqlite_stat2"
" ORDER BY rowid DESC",
pTab->nCol>=5 ? "cnt" : "0", sInfo.zDatabase);
if( !zSql ){
rc = SQLITE_NOMEM;
}else{
@@ -655,51 +821,45 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
while( sqlite3_step(pStmt)==SQLITE_ROW ){
char *zIndex; /* Index name */
Index *pIdx; /* Pointer to the index object */
int iSample;
int eType;
IndexSample *pSample;
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;
}
}
}
if( zIndex==0 ) continue;
pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase);
if( pIdx==0 ) continue;
iSample = sqlite3_column_int(pStmt, 1);
if( iSample>=SQLITE_MAX_SAMPLES ) continue;
if( iSample<0 ){
pSample = allocIndexSample(db, &pIdx->comkey, pIdx->comkey.n);
}else{
pSample = allocIndexSample(db, &pIdx->sample, iSample);
}
if( pSample==0 ) break;
eType = sqlite3_column_type(pStmt, 2);
pSample->eType = (u8)eType;
pSample->nCopy = sqlite3_column_int(pStmt, 3);
if( eType==SQLITE_INTEGER ){
pSample->u.i = sqlite3_column_int64(pStmt, 2);
}else if( 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>255 ) n = 255;
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;
}
}
}
+33 -15
View File
@@ -81,7 +81,10 @@
** construct a histogram of the table content when running ANALYZE
** and with SQLITE_ENABLE_STAT2
*/
#define SQLITE_INDEX_SAMPLES 10
#ifndef SQLITE_INDEX_SAMPLES
# define SQLITE_INDEX_SAMPLES 10
#endif
#define SQLITE_MAX_SAMPLES 100
/*
** The following macros are used to cast pointers to integers and
@@ -612,6 +615,7 @@ typedef struct FuncDefHash FuncDefHash;
typedef struct IdList IdList;
typedef struct Index Index;
typedef struct IndexSample IndexSample;
typedef struct IndexSampleArray IndexSampleArray;
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
@@ -1447,6 +1451,30 @@ struct UnpackedRecord {
#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
/*
** Each sample stored in the sqlite_stat2 table is represented in memory
** using a structure of this type.
*/
struct IndexSample {
union {
char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
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. */
u32 nCopy; /* How many copies of this sample are in the database */
};
/*
** An array of IndexSample elements is as follows:
*/
struct IndexSampleArray {
u16 n; /* Number of elements in the array */
u16 nAlloc; /* Space allocated to a[] */
IndexSample *a; /* The samples */
};
/*
** Each SQL index is represented in memory by an
** instance of the following structure.
@@ -1488,20 +1516,10 @@ 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 */
};
/*
** Each sample stored in the sqlite_stat2 table is represented in memory
** using a structure of this type.
*/
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 */
} u;
u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
u8 nByte; /* Size in byte of text or blob. */
#ifdef SQLITE_ENABLE_STAT2
IndexSampleArray sample; /* Sampled histogram for the first column */
IndexSampleArray comkey; /* The most common keys */
#endif
};
/*
+2 -1
View File
@@ -1053,7 +1053,6 @@ case OP_Copy: { /* in1, out2 */
assert( pOut!=pIn1 );
sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
Deephemeralize(pOut);
REGISTER_TRACE(pOp->p2, pOut);
break;
}
@@ -1540,6 +1539,7 @@ case OP_AddImm: { /* in1 */
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
pIn1->u.i += pOp->p2;
REGISTER_TRACE(pOp->p1, pIn1);
break;
}
@@ -2468,6 +2468,7 @@ case OP_MakeRecord: {
*/
for(pRec=pData0; pRec<=pLast; pRec++){
assert( memIsValid(pRec) );
REGISTER_TRACE((pRec-pData0)+pOp->p1, pRec);
if( zAffinity ){
applyAffinity(pRec, zAffinity[pRec-pData0], encoding);
}
+86 -52
View File
@@ -2422,13 +2422,17 @@ static void bestVirtualIndex(
/*
** 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.
** pIdx->sample.n (hereafter "S") evenly spaced samples of the first indexed
** column stored in Index.sample. These samples divide the domain of values
** stored the index into S+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 S contains values larger than the last sample.
**
** Note that samples are computed as being centered on S buckets where each
** bucket contains the nearly same number of rows. This routine takes samples
** to be dividers between regions, though. Hence, region 0 and region S
** contain half as many rows as the interior regions.
**
** 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.
@@ -2438,7 +2442,7 @@ static void bestVirtualIndex(
**
** 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.
** and S, inclusive) and returns SQLITE_OK.
** Or, if an OOM occurs while converting text values between encodings,
** SQLITE_NOMEM is returned and *piRegion is undefined.
*/
@@ -2448,19 +2452,35 @@ static int whereRangeRegion(
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 *piRegion, /* OUT: Region of domain in which value lies */
u32 *pnCopy /* OUT: Number of rows with pVal, or -1 if unk */
){
assert( roundUp==0 || roundUp==1 );
if( ALWAYS(pVal) ){
IndexSample *aSample = pIdx->aSample;
IndexSample *aSample = pIdx->sample.a;
int nSample = pIdx->sample.n;
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++){
assert( nSample>0 );
if( eType==SQLITE_INTEGER ){
i64 x = sqlite3_value_int64(pVal);
for(i=0; i<nSample; i++){
if( aSample[i].eType==SQLITE_NULL ) continue;
if( aSample[i].eType>=SQLITE_TEXT ) break;
if( aSample[i].u.i==x ) *pnCopy = aSample[i].nCopy;
if( roundUp ){
if( aSample[i].u.i>x ) break;
}else{
if( aSample[i].u.i>=x ) break;
}
}
}else if( eType==SQLITE_FLOAT ){
double r = sqlite3_value_double(pVal);
for(i=0; i<nSample; i++){
if( aSample[i].eType==SQLITE_NULL ) continue;
if( aSample[i].eType>=SQLITE_TEXT ) break;
if( aSample[i].u.r==r ) *pnCopy = aSample[i].nCopy;
if( roundUp ){
if( aSample[i].u.r>r ) break;
}else{
@@ -2469,8 +2489,9 @@ static int whereRangeRegion(
}
}else if( eType==SQLITE_NULL ){
i = 0;
if( aSample[0].eType==SQLITE_NULL ) *pnCopy = aSample[0].nCopy;
if( roundUp ){
while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++;
while( i<nSample && aSample[i].eType==SQLITE_NULL ) i++;
}
}else{
sqlite3 *db = pParse->db;
@@ -2478,9 +2499,7 @@ static int whereRangeRegion(
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;
@@ -2500,7 +2519,7 @@ static int whereRangeRegion(
}
n = sqlite3ValueBytes(pVal, pColl->enc);
for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
for(i=0; i<nSample; i++){
int c;
int eSampletype = aSample[i].eType;
if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
@@ -2522,11 +2541,12 @@ static int whereRangeRegion(
{
c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
}
if( c==0 ) *pnCopy = aSample[i].nCopy;
if( c-roundUp>=0 ) break;
}
}
assert( i>=0 && i<=SQLITE_INDEX_SAMPLES );
assert( i>=0 && i<=pIdx->sample.n );
*piRegion = i;
}
return SQLITE_OK;
@@ -2621,14 +2641,16 @@ static int whereRangeScanEst(
#ifdef SQLITE_ENABLE_STAT2
if( nEq==0 && p->aSample ){
if( nEq==0 && p->sample.a ){
sqlite3_value *pLowerVal = 0;
sqlite3_value *pUpperVal = 0;
int iEst;
int iLower = 0;
int iUpper = SQLITE_INDEX_SAMPLES;
int nSample = p->sample.n;
int iUpper = p->sample.n;
int roundUpUpper = 0;
int roundUpLower = 0;
u32 nC = 0;
u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pLower ){
@@ -2649,26 +2671,26 @@ static int whereRangeScanEst(
sqlite3ValueFree(pUpperVal);
goto range_est_fallback;
}else if( pLowerVal==0 ){
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper, &nC);
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;
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower, &nC);
if( pUpper ) iUpper = (iLower + p->sample.n + 1)/2;
}else{
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper, &nC);
if( rc==SQLITE_OK ){
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower, &nC);
}
}
WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper));
iEst = iUpper - iLower;
testcase( iEst==SQLITE_INDEX_SAMPLES );
assert( iEst<=SQLITE_INDEX_SAMPLES );
testcase( iEst==nSample );
assert( iEst<=nSample );
assert( nSample>0 );
if( iEst<1 ){
*piEst = 50/SQLITE_INDEX_SAMPLES;
*piEst = 50/nSample;
}else{
*piEst = (iEst*100)/SQLITE_INDEX_SAMPLES;
*piEst = (iEst*100)/nSample;
}
sqlite3ValueFree(pLowerVal);
sqlite3ValueFree(pUpperVal);
@@ -2716,8 +2738,10 @@ static int whereEqualScanEst(
u8 aff; /* Column affinity */
int rc; /* Subfunction return code */
double nRowEst; /* New estimate of the number of rows */
u32 nC = 0; /* Key copy count */
assert( p->aSample!=0 );
assert( p->sample.a!=0 );
assert( p->sample.n>0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
if( pExpr ){
rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
@@ -2726,17 +2750,24 @@ static int whereEqualScanEst(
pRhs = sqlite3ValueNew(pParse->db);
}
if( pRhs==0 ) return SQLITE_NOTFOUND;
rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower);
rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower, &nC);
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;
if( nC==0 ){
rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper, &nC);
if( rc ) goto whereEqualScanEst_cancel;
}
if( nC ){
WHERETRACE(("equality scan count: %u\n", nC));
*pnRow = nC;
}else{
nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES;
*pnRow = nRowEst;
WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper));
if( iLower>=iUpper ){
nRowEst = p->aiRowEst[0]/(p->sample.n*3);
if( nRowEst<*pnRow ) *pnRow = nRowEst;
}else{
nRowEst = (iUpper-iLower)*p->aiRowEst[0]/p->sample.n;
*pnRow = nRowEst;
}
}
whereEqualScanEst_cancel:
@@ -2776,14 +2807,17 @@ static int whereInScanEst(
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 i; /* Loop counter */
u32 nC; /* Exact count of rows for a key */
int nSample = p->sample.n; /* Number of samples */
u8 aSpan[SQLITE_MAX_SAMPLES+1]; /* Histogram regions that are spanned */
u8 aSingle[SQLITE_MAX_SAMPLES+1]; /* Histogram regions hit once */
assert( p->aSample!=0 );
assert( p->sample.a!=0 );
assert( nSample>0 );
aff = p->pTable->aCol[p->aiColumn[0]].affinity;
memset(aSpan, 0, sizeof(aSpan));
memset(aSingle, 0, sizeof(aSingle));
memset(aSpan, 0, nSample+1);
memset(aSingle, 0, nSample+1);
for(i=0; i<pList->nExpr; i++){
sqlite3ValueFree(pVal);
rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal);
@@ -2792,26 +2826,26 @@ static int whereInScanEst(
nNotFound++;
continue;
}
rc = whereRangeRegion(pParse, p, pVal, 0, &iLower);
rc = whereRangeRegion(pParse, p, pVal, 0, &iLower, &nC);
if( rc ) break;
rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper);
rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper, &nC);
if( rc ) break;
if( iLower>=iUpper ){
aSingle[iLower] = 1;
}else{
assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES );
assert( iLower>=0 && iUpper<=nSample );
while( iLower<iUpper ) aSpan[iLower++] = 1;
}
}
if( rc==SQLITE_OK ){
for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){
for(i=nSpan=0; i<=nSample; i++){
if( aSpan[i] ){
nSpan++;
}else if( aSingle[i] ){
nSingle++;
}
}
nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES)
nRowEst = (nSpan*3+nSingle)*p->aiRowEst[0]/(3*nSample)
+ nNotFound*p->aiRowEst[1];
if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
*pnRow = nRowEst;
@@ -3029,7 +3063,7 @@ static void bestBtreeIndex(
wsFlags |= WHERE_COLUMN_NULL;
}
#ifdef SQLITE_ENABLE_STAT2
if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
if( nEq==0 && pProbe->sample.a ) pFirstTerm = pTerm;
#endif
used |= pTerm->prereqRight;
}
+20 -20
View File
@@ -71,27 +71,27 @@ do_test analyze2-1.1 {
}
execsql {
ANALYZE;
SELECT * FROM sqlite_stat2;
SELECT tbl, idx, sampleno, sample FROM sqlite_stat2;
}
} [list t1 sqlite_autoindex_t1_1 0 50 \
t1 sqlite_autoindex_t1_1 1 149 \
t1 sqlite_autoindex_t1_1 2 249 \
t1 sqlite_autoindex_t1_1 3 349 \
t1 sqlite_autoindex_t1_1 4 449 \
t1 sqlite_autoindex_t1_1 5 549 \
t1 sqlite_autoindex_t1_1 6 649 \
t1 sqlite_autoindex_t1_1 7 749 \
t1 sqlite_autoindex_t1_1 8 849 \
t1 sqlite_autoindex_t1_1 9 949 \
t1 sqlite_autoindex_t1_1 1 150 \
t1 sqlite_autoindex_t1_1 2 250 \
t1 sqlite_autoindex_t1_1 3 350 \
t1 sqlite_autoindex_t1_1 4 450 \
t1 sqlite_autoindex_t1_1 5 550 \
t1 sqlite_autoindex_t1_1 6 650 \
t1 sqlite_autoindex_t1_1 7 750 \
t1 sqlite_autoindex_t1_1 8 850 \
t1 sqlite_autoindex_t1_1 9 950 \
]
do_test analyze2-1.2 {
execsql {
DELETE FROM t1 WHERe x>9;
DELETE FROM t1 WHERE x>20;
ANALYZE;
SELECT tbl, idx, group_concat(sample, ' ') FROM sqlite_stat2;
}
} {t1 sqlite_autoindex_t1_1 {0 1 2 3 4 5 6 7 8 9}}
} {t1 sqlite_autoindex_t1_1 {1 3 5 7 9 11 13 15 17 19}}
do_test analyze2-1.3 {
execsql {
DELETE FROM t1 WHERE x>8;
@@ -189,7 +189,7 @@ do_test analyze2-3.1 {
WHERE idx = 't1_x'
GROUP BY tbl,idx
}
} {t1 t1_x {100 299 499 699 899 ajj cjj ejj gjj ijj}}
} {t1 t1_x {100 300 500 700 900 baa daa faa haa jaa}}
do_test analyze2-3.2 {
execsql {
SELECT tbl,idx,group_concat(sample,' ')
@@ -197,12 +197,12 @@ do_test analyze2-3.2 {
WHERE idx = 't1_y'
GROUP BY tbl,idx
}
} {t1 t1_y {100 299 499 699 899 ajj cjj ejj gjj ijj}}
} {t1 t1_y {100 300 500 700 900 baa daa faa haa jaa}}
do_eqp_test 3.3 {
SELECT * FROM t1 WHERE x BETWEEN 100 AND 500 AND y BETWEEN 'a' AND 'b'
} {
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~50 rows)}
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>? AND y<?) (~25 rows)}
}
do_eqp_test 3.4 {
SELECT * FROM t1 WHERE x BETWEEN 100 AND 400 AND y BETWEEN 'a' AND 'h'
@@ -212,12 +212,12 @@ do_eqp_test 3.4 {
do_eqp_test 3.5 {
SELECT * FROM t1 WHERE x<'a' AND y>'h'
} {
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)}
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~133 rows)}
}
do_eqp_test 3.6 {
SELECT * FROM t1 WHERE x<444 AND y>'h'
} {
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~66 rows)}
0 0 0 {SEARCH TABLE t1 USING INDEX t1_y (y>?) (~133 rows)}
}
do_eqp_test 3.7 {
SELECT * FROM t1 WHERE x<221 AND y>'g'
@@ -249,7 +249,7 @@ do_test analyze2-4.2 {
GROUP BY tbl,idx;
PRAGMA automatic_index=ON;
}
} {t3 t3a {AfA bEj CEj dEj EEj fEj GEj hEj IEj jEj}}
} {t3 t3a {AfA bfA CfA dfA EfA ffA GfA hfA IfA jfA}}
do_test analyze2-4.3 {
execsql {
SELECT tbl,idx,group_concat(sample,' ')
@@ -257,7 +257,7 @@ do_test analyze2-4.3 {
WHERE idx = 't3b'
GROUP BY tbl,idx
}
} {t3 t3b {AbA CIj EIj GIj IIj bIj dIj fIj hIj jIj}}
} {t3 t3b {AbA CbA EbA GbA IbA bbA dbA fbA hbA jbA}}
do_eqp_test 4.4 {
SELECT * FROM t3 WHERE a > 'A' AND a < 'C' AND b > 'A' AND b < 'C'
@@ -297,7 +297,7 @@ ifcapable utf16 {
WHERE tbl = 't4'
GROUP BY tbl,idx
}
} {t4 t4x {afa bej cej dej eej fej gej hej iej jej}}
} {t4 t4x {afa bfa cfa dfa efa ffa gfa hfa ifa jfa}}
do_eqp_test 5.3 {
SELECT * FROM t4 WHERE x>'ccc'
} {0 0 0 {SEARCH TABLE t4 USING COVERING INDEX t4x (x>?) (~800 rows)}}
+15 -15
View File
@@ -128,28 +128,28 @@ foreach {testid where index rows} {
51 {z>=-100 AND z<=2.0} t1z 900
52 {z>=-100 AND z<3.0} t1z 900
101 {z=-1} t1z 50
101 {z=-1} t1z 33
102 {z=0} t1z 400
103 {z=1} t1z 300
104 {z=2} t1z 200
105 {z=3} t1z 100
106 {z=4} t1z 50
107 {z=-10.0} t1z 50
106 {z=4} t1z 33
107 {z=-10.0} t1z 33
108 {z=0.0} t1z 400
109 {z=1.0} t1z 300
110 {z=2.0} t1z 200
111 {z=3.0} t1z 100
112 {z=4.0} t1z 50
113 {z=1.5} t1z 50
114 {z=2.5} t1z 50
112 {z=4.0} t1z 33
113 {z=1.5} t1z 33
114 {z=2.5} t1z 33
201 {z IN (-1)} t1z 50
201 {z IN (-1)} t1z 33
202 {z IN (0)} t1z 400
203 {z IN (1)} t1z 300
204 {z IN (2)} t1z 200
205 {z IN (3)} t1z 100
206 {z IN (4)} t1z 50
207 {z IN (0.5)} t1z 50
206 {z IN (4)} t1z 33
207 {z IN (0.5)} t1z 33
208 {z IN (0,1)} t1z 700
209 {z IN (0,1,2)} t1z 900
210 {z IN (0,1,2,3)} {} 100
@@ -157,12 +157,12 @@ foreach {testid where index rows} {
212 {z IN (1,2)} t1z 500
213 {z IN (2,3)} t1z 300
214 {z=3 OR z=2} t1z 300
215 {z IN (-1,3)} t1z 150
216 {z=-1 OR z=3} t1z 150
215 {z IN (-1,3)} t1z 133
216 {z=-1 OR z=3} t1z 133
300 {y=0} {} 100
301 {y=1} t1y 50
302 {y=0.1} t1y 50
301 {y=1} t1y 33
302 {y=0.1} t1y 33
400 {x IS NULL} t1x 400
@@ -205,9 +205,9 @@ db eval {
#
foreach {testid where index rows} {
500 {x IS NULL AND u='charlie'} t1u 20
501 {x=1 AND u='charlie'} t1x 5
501 {x=1 AND u='charlie'} t1x 3
502 {x IS NULL} {} 100
503 {x=1} t1x 50
503 {x=1} t1x 33
504 {x IS NOT NULL} t1x 25
505 {+x IS NOT NULL} {} 500
506 {upper(x) IS NOT NULL} {} 500
+4 -2
View File
@@ -99,8 +99,10 @@ do_test analyze7-3.3 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND b=123}
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
do_test analyze7-3.4 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1b (b=?) (~2 rows)}}
set x [execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=123 AND b=123}]
regsub -all {[bcd]+} $x {x} x
set x
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1x (x=?) (~2 rows)}}
do_test analyze7-3.5 {
execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE a=123 AND c=123}
} {0 0 0 {SEARCH TABLE t1 USING INDEX t1a (a=?) (~1 rows)}}
+201
View File
@@ -0,0 +1,201 @@
# 2011 August 5
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# May you do good and not evil.
# May you find forgiveness for yourself and forgive others.
# May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.
# This file implements tests for the ANALYZE command under STAT2.
# Testing the logic that computes the number of copies of each sample.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# There is nothing to test if ANALYZE is disable for this build.
#
ifcapable {!analyze||!vtab||!stat2} {
finish_test
return
}
# Generate some test data
#
do_test analyze8-1.0 {
set x 100
set y 1
set ycnt 0
set yinc 10
execsql {
CREATE TABLE t1(x,y);
ANALYZE;
BEGIN;
CREATE INDEX t1x ON t1(x);
CREATE INDEX t1y ON t1(y);
}
for {set i 0} {$i<20} {incr i} {
for {set j 0} {$j<300} {incr j} {
execsql {INSERT INTO t1 VALUES($x,$y)}
incr ycnt
if {$ycnt>=$yinc} {set ycnt 0; incr y}
}
for {set j 0} {$j<100} {incr j} {
incr x
execsql {INSERT INTO t1 VALUES($x,$y)}
incr ycnt
if {$ycnt>=$yinc} {set ycnt 0; incr y}
}
}
execsql {
COMMIT;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno;
}
} {200 301 400 301 600 301 800 301 1000 301 1200 301 1400 301 1600 301 1800 301 2000 301}
do_test analyze8-1.1 {
execsql {
SELECT count(*) FROM t1 WHERE x=200;
}
} {301}
do_test analyze8-2.0 {
execsql {
BEGIN;
DELETE FROM t1;
}
for {set x 1} {$x<200} {incr x} {
execsql {INSERT INTO t1 VALUES($x,$x)}
}
for {set i 0} {$i<200} {incr i} {
execsql {INSERT INTO t1 VALUES(999,999)}
}
execsql {
COMMIT;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno;
}
} {20 1 60 1 100 1 140 1 180 1 999 200 999 200 999 200 999 200 999 200}
do_test analyze8-2.1 {
for {set i 0} {$i<200} {incr i} {
execsql {INSERT INTO t1 VALUES(0,999)}
}
execsql {
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1x' ORDER BY sampleno;
}
} {0 200 0 200 0 200 10 1 70 1 130 1 190 1 999 200 999 200 999 200}
do_test analyze8-3.0 {
execsql {
BEGIN;
DROP TABLE t1;
CREATE TABLE t1(a,b);
CREATE INDEX t1all ON t1(a,b);
INSERT INTO t1 VALUES(0,1);
INSERT INTO t1 VALUES(0,2);
INSERT INTO t1 VALUES(0,3);
INSERT INTO t1 VALUES(1,4);
INSERT INTO t1 SELECT a+2, b+4 FROM t1;
INSERT INTO t1 SELECT a+4, b+8 FROM t1;
INSERT INTO t1 SELECT a+8, b+16 FROM t1;
COMMIT;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {0 3 2 3 4 3 5 1 6 3 8 3 10 3 12 3 13 1 14 3}
do_test analyze8-3.1 {
execsql {
DELETE FROM t1;
INSERT INTO t1 VALUES(1,1);
INSERT INTO t1 VALUES(2,2);
INSERT INTO t1 SELECT a+2, b+2 FROM t1;
INSERT INTO t1 SELECT a+4, b+4 FROM t1;
INSERT INTO t1 SELECT a+8, b+8 FROM t1;
INSERT INTO t1 SELECT a+16, b+16 FROM t1;
DELETE FROM t1 WHERE a>21;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {2 1 4 1 6 1 8 1 10 1 12 1 14 1 16 1 18 1 20 1}
do_test analyze8-3.2 {
execsql {
UPDATE t1 SET a=123;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {123 21 123 21 123 21 123 21 123 21 123 21 123 21 123 21 123 21 123 21}
do_test analyze8-3.3 {
execsql {
DELETE FROM t1 WHERE b=1 OR b=2;
ANALYZE;
SELECT count(*) FROM t1;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {19}
do_test analyze8-3.4 {
execsql {
UPDATE t1 SET a=b;
INSERT INTO t1 VALUES(1,1);
INSERT INTO t1 VALUES(2,2);
INSERT INTO t1 SELECT a, b FROM t1;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {2 2 4 2 6 2 8 2 10 2 12 2 14 2 16 2 18 2 20 2}
do_test analyze8-3.5 {
execsql {
UPDATE t1 SET a=1 WHERE b<20;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {1 38 1 38 1 38 1 38 1 38 1 38 1 38 1 38 1 38 20 2}
do_test analyze8-3.6 {
execsql {
UPDATE t1 SET a=b;
UPDATE t1 SET a=20 WHERE b>2;
ANALYZE;
SELECT sample, cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {2 2 20 38 20 38 20 38 20 38 20 38 20 38 20 38 20 38 20 38}
# Verify that the 5th "cnt" column is added to the sqlite_stat2 table
# on a full ANALYZE if the column is not already present.
#
do_test analyze8-4.0 {
execsql {
UPDATE t1 SET a=b;
ANALYZE;
PRAGMA writable_schema=ON;
UPDATE sqlite_master
SET sql='CREATE TABLE sqlite_stat2(tbl,idx,sampleno,sample)'
WHERE name='sqlite_stat2';
}
db close
sqlite3 db test.db
execsql {
SELECT sample FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno
}
} {2 4 6 8 10 12 14 16 18 20}
do_test analyze8-4.1 {
catchsql {SELECT sample, cnt FROM sqlite_stat2}
} {1 {no such column: cnt}}
do_test analyze8-4.2 {
execsql {
ANALYZE;
}
db close;
sqlite3 db test.db
execsql {
SELECT sample, +cnt FROM sqlite_stat2 WHERE idx='t1all' ORDER BY sampleno;
}
} {2 2 4 2 6 2 8 2 10 2 12 2 14 2 16 2 18 2 20 2}
finish_test
+24 -4
View File
@@ -35,9 +35,19 @@ do_test tkt-cbd05-1.1 {
INSERT INTO t1 VALUES (NULL, 'G');
INSERT INTO t1 VALUES (NULL, 'H');
INSERT INTO t1 VALUES (NULL, 'I');
INSERT INTO t1 VALUES (NULL, 'J');
INSERT INTO t1 VALUES (NULL, 'K');
INSERT INTO t1 VALUES (NULL, 'L');
INSERT INTO t1 VALUES (NULL, 'M');
INSERT INTO t1 VALUES (NULL, 'N');
INSERT INTO t1 VALUES (NULL, 'O');
INSERT INTO t1 VALUES (NULL, 'P');
INSERT INTO t1 VALUES (NULL, 'Q');
INSERT INTO t1 VALUES (NULL, 'R');
INSERT INTO t1 VALUES (NULL, 'S');
SELECT count(*) FROM t1;
}
} {10}
} {20}
do_test tkt-cbd05-1.2 {
db eval {
ANALYZE;
@@ -50,7 +60,7 @@ do_test tkt-cbd05-1.3 {
WHERE idx = 't1_x'
GROUP BY tbl,idx
}
} {t1 t1_x { A B C D E F G H I}}
} {t1 t1_x {A C E G I K M O Q S}}
do_test tkt-cbd05-2.1 {
db eval {
@@ -67,9 +77,19 @@ do_test tkt-cbd05-2.1 {
INSERT INTO t1 VALUES(NULL, X'47');
INSERT INTO t1 VALUES(NULL, X'48');
INSERT INTO t1 VALUES(NULL, X'49');
INSERT INTO t1 VALUES(NULL, X'4A');
INSERT INTO t1 VALUES(NULL, X'4B');
INSERT INTO t1 VALUES(NULL, X'4C');
INSERT INTO t1 VALUES(NULL, X'4D');
INSERT INTO t1 VALUES(NULL, X'4E');
INSERT INTO t1 VALUES(NULL, X'4F');
INSERT INTO t1 VALUES(NULL, X'50');
INSERT INTO t1 VALUES(NULL, X'51');
INSERT INTO t1 VALUES(NULL, X'52');
INSERT INTO t1 VALUES(NULL, X'53');
SELECT count(*) FROM t1;
}
} {10}
} {20}
do_test tkt-cbd05-2.2 {
db eval {
ANALYZE;
@@ -82,6 +102,6 @@ do_test tkt-cbd05-2.3 {
WHERE idx = 't1_x'
GROUP BY tbl,idx
}
} {t1 t1_x { A B C D E F G H I}}
} {t1 t1_x {A C E G I K M O Q S}}
finish_test