1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-08-08 14:02:16 +03:00

First version of sqlite_stat2 (schema forces exactly 10 samples).

FossilOrigin-Name: dd96bda2a85c1d94fb4a0bf5f27e2977f7f7e42e
This commit is contained in:
dan
2009-08-17 17:06:58 +00:00
parent d9c50f7fed
commit 02fa469619
12 changed files with 469 additions and 99 deletions

View File

@@ -17,8 +17,9 @@
#include "sqliteInt.h"
/*
** This routine generates code that opens the sqlite_stat1 table on cursor
** iStatCur.
** This routine generates code that opens the sqlite_stat1 table for
** writing with cursor iStatCur. The sqlite_stat2 table is opened
** for writing using cursor (iStatCur+1).
**
** If the sqlite_stat1 tables does not previously exist, it is created.
** If it does previously exist, all entires associated with table zWhere
@@ -30,53 +31,55 @@ static void openStatTable(
int iStatCur, /* Open the sqlite_stat1 table on this cursor */
const char *zWhere /* Delete entries associated with this table */
){
const char *aName[] = { "sqlite_stat1", "sqlite_stat2" };
const char *aCols[] = { "tbl,idx,stat", "tbl,idx," SQLITE_INDEX_SAMPLE_COLS };
int aRoot[] = {0, 0};
int aCreateTbl[] = {0, 0};
int i;
sqlite3 *db = pParse->db;
Db *pDb;
int iRootPage;
u8 createStat1 = 0;
Table *pStat;
Vdbe *v = sqlite3GetVdbe(pParse);
if( v==0 ) return;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3VdbeDb(v)==db );
pDb = &db->aDb[iDb];
if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){
/* The sqlite_stat1 tables does not exist. Create it.
** Note that a side-effect of the CREATE TABLE statement is to leave
** the rootpage of the new table in register pParse->regRoot. This is
** important because the OpenWrite opcode below will be needing it. */
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)",
pDb->zName
);
iRootPage = pParse->regRoot;
createStat1 = 1; /* Cause rootpage to be taken from top of stack */
}else if( zWhere ){
/* The sqlite_stat1 table exists. Delete all entries associated with
** the table zWhere. */
sqlite3NestedParse(pParse,
"DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q",
pDb->zName, zWhere
);
iRootPage = pStat->tnum;
}else{
/* The sqlite_stat1 table already exists. Delete all rows. */
iRootPage = pStat->tnum;
sqlite3VdbeAddOp2(v, OP_Clear, pStat->tnum, iDb);
for(i=0; i<ArraySize(aName); i++){
Table *pStat;
if( (pStat = sqlite3FindTable(db, aName[i], pDb->zName))==0 ){
/* The sqlite_stat[12] table does not exist. Create it. Note that a
** side-effect of the CREATE TABLE statement is to leave the rootpage
** of the new table in register pParse->regRoot. This is important
** because the OpenWrite opcode below will be needing it. */
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.%s(%s)", pDb->zName, aName[i], aCols[i]
);
aRoot[i] = pParse->regRoot;
aCreateTbl[i] = 1;
}else{
/* The table already exists. If zWhere is not NULL, delete all entries
** associated with the table zWhere. If zWhere is NULL, delete the
** entire contents of the table. */
aRoot[i] = pStat->tnum;
sqlite3TableLock(pParse, iDb, aRoot[i], 1, aName[i]);
if( zWhere ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, aName[i], zWhere
);
}else{
/* The sqlite_stat[12] table already exists. Delete all rows. */
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
}
}
}
/* Open the sqlite_stat1 table for writing. Unless it was created
** by this vdbe program, lock it for writing at the shared-cache level.
** If this vdbe did create the sqlite_stat1 table, then it must have
** already obtained a schema-lock, making the write-lock redundant.
*/
if( !createStat1 ){
sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1");
/* Open the sqlite_stat[12] tables for writing. */
for(i=0; i<ArraySize(aName); i++){
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
}
sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur, iRootPage, iDb);
sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
sqlite3VdbeChangeP5(v, createStat1);
}
/*
@@ -117,6 +120,7 @@ static void analyzeOneTable(
/* Establish a read-lock on the table at the shared-cache level. */
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
iMem += 3;
iIdxCur = pParse->nTab++;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
@@ -126,6 +130,7 @@ static void analyzeOneTable(
int regCol; /* Content of a column from the table being analyzed */
int regRowid; /* Rowid for the inserted record */
int regF2;
int regStat2;
/* Open a cursor to the index to be analyzed
*/
@@ -134,22 +139,35 @@ static void analyzeOneTable(
sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
(char *)pKey, P4_KEYINFO_HANDOFF);
VdbeComment((v, "%s", pIdx->zName));
regFields = iMem+nCol*2;
regStat2 = iMem+nCol*2+1;
regFields = regStat2+2+SQLITE_INDEX_SAMPLES;
regTemp = regRowid = regCol = regFields+3;
regRec = regCol+1;
if( regRec>pParse->nMem ){
pParse->nMem = regRec;
}
/* Memory cells are used as follows:
/* Fill in the register with the total number of rows. */
if( pTab->pIndex==pIdx ){
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, iMem-3);
}
sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem-2);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem-1);
/* Memory cells are used as follows. All memory cell addresses are
** offset by iMem. That is, cell 0 below is actually cell iMem, cell
** 1 is cell 1+iMem, etc.
**
** mem[iMem]: The total number of rows in the table.
** mem[iMem+1]: Number of distinct values in column 1
** ...
** mem[iMem+nCol]: Number of distinct values in column N
** mem[iMem+nCol+1] Last observed value of column 1
** ...
** mem[iMem+nCol+nCol]: Last observed value of column N
** 0: The total number of rows in the table.
**
** 1..nCol: Number of distinct entries in index considering the
** left-most N columns, where N is the same as the
** memory cell number.
**
** nCol+1..2*nCol: Previous value of indexed columns, from left to
** right.
**
** 2*nCol+1..2*nCol+10: 10 evenly spaced samples.
**
** Cells iMem through iMem+nCol are initialized to 0. The others
** are initialized to NULL.
@@ -161,29 +179,35 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1);
}
/* Do the analysis.
*/
/* Start the analysis loop. This loop runs through all the entries inof
** the index b-tree. */
endOfLoop = sqlite3VdbeMakeLabel(v);
sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
topOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
if( i==0 ){
sqlite3VdbeAddOp3(v, OP_Sample, iMem-3, regCol, regStat2+2);
}
sqlite3VdbeAddOp3(v, OP_Ne, regCol, 0, iMem+nCol+i+1);
/**** TODO: add collating sequence *****/
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
for(i=0; i<nCol; i++){
sqlite3VdbeJumpHere(v, topOfLoop + 2*(i + 1));
sqlite3VdbeJumpHere(v, topOfLoop + 1 + 2*(i + 1));
sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
}
/* End of the analysis loop. */
sqlite3VdbeResolveLabel(v, endOfLoop);
sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
/* Store the results.
/* Store the results in sqlite_stat1.
**
** The result is a single row of the sqlite_stat1 table. The first
** two columns are the names of the table and index. The third column
@@ -219,6 +243,16 @@ static void analyzeOneTable(
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
/* Store the results in sqlite_stat2. */
sqlite3VdbeAddOp4(v, OP_String8, 0, regStat2, 0, pTab->zName, 0);
sqlite3VdbeAddOp4(v, OP_String8, 0, regStat2+1, 0, pIdx->zName, 0);
sqlite3VdbeAddOp4(v, OP_MakeRecord, regStat2, SQLITE_INDEX_SAMPLES+2,
regRec, "aabbbbbbbbbb", 0
);
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
sqlite3VdbeJumpHere(v, addr);
}
}
@@ -245,7 +279,8 @@ static void analyzeDatabase(Parse *pParse, int iDb){
int iMem;
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab++;
iStatCur = pParse->nTab;
pParse->nTab += 2;
openStatTable(pParse, iDb, iStatCur, 0);
iMem = pParse->nMem+1;
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
@@ -267,7 +302,8 @@ static void analyzeTable(Parse *pParse, Table *pTab){
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab++;
iStatCur = pParse->nTab;
pParse->nTab += 2;
openStatTable(pParse, iDb, iStatCur, pTab->zName);
analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1);
loadAnalysis(pParse, iDb);
@@ -387,7 +423,8 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
}
/*
** Load the content of the sqlite_stat1 table into the index hash tables.
** Load the content of the sqlite_stat1 and sqlite_stat2 tables into the
** index hash tables.
*/
int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
analysisInfo sInfo;
@@ -412,7 +449,6 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
return SQLITE_ERROR;
}
/* Load new statistics out of the sqlite_stat1 table */
zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1",
sInfo.zDatabase);
@@ -423,8 +459,87 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
(void)sqlite3SafetyOn(db);
sqlite3DbFree(db, zSql);
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
}
/* Load the statistics from the sqlite_stat2 table */
if( rc==SQLITE_OK ){
zSql = sqlite3MPrintf(db,
"SELECT idx," SQLITE_INDEX_SAMPLE_COLS " FROM %Q.sqlite_stat2",
sInfo.zDatabase
);
if( zSql ){
sqlite3_stmt *pStmt = 0;
(void)sqlite3SafetyOff(db);
rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
if( rc==SQLITE_OK ){
while( SQLITE_ROW==sqlite3_step(pStmt) ){
char *zIndex = (char *)sqlite3_column_text(pStmt, 0);
Index *pIdx;
pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase);
if( pIdx ){
char *pSpace;
IndexSample *pSample;
int iCol;
int nAlloc = SQLITE_INDEX_SAMPLES * sizeof(IndexSample);
for(iCol=1; iCol<=SQLITE_INDEX_SAMPLES; iCol++){
int eType = sqlite3_column_type(pStmt, iCol);
if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
nAlloc += sqlite3_column_bytes(pStmt, iCol);
}
}
pSample = sqlite3DbMallocRaw(db, nAlloc);
if( !pSample ){
rc = SQLITE_NOMEM;
break;
}
sqlite3DbFree(db, pIdx->aSample);
pIdx->aSample = pSample;
pSpace = (char *)&pSample[SQLITE_INDEX_SAMPLES];
for(iCol=1; iCol<=SQLITE_INDEX_SAMPLES; iCol++){
int eType = sqlite3_column_type(pStmt, iCol);
pSample[iCol-1].eType = eType;
switch( eType ){
case SQLITE_BLOB:
case SQLITE_TEXT: {
const char *z = (const char *)(
(eType==SQLITE_BLOB) ?
sqlite3_column_blob(pStmt, iCol):
sqlite3_column_text(pStmt, iCol)
);
int n = sqlite3_column_bytes(pStmt, iCol);
if( n>24 ){
n = 24;
}
pSample[iCol-1].nByte = n;
pSample[iCol-1].u.z = pSpace;
memcpy(pSpace, z, n);
pSpace += n;
break;
}
case SQLITE_INTEGER:
case SQLITE_FLOAT:
pSample[iCol-1].u.r = sqlite3_column_double(pStmt, iCol);
break;
case SQLITE_NULL:
break;
}
}
}
}
if( rc==SQLITE_NOMEM ){
sqlite3_finalize(pStmt);
}else{
rc = sqlite3_finalize(pStmt);
}
}
(void)sqlite3SafetyOn(db);
sqlite3DbFree(db, zSql);
}else{
rc = SQLITE_NOMEM;
}
}
if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
return rc;
}