mirror of
https://github.com/sqlite/sqlite.git
synced 2025-07-30 19:03:16 +03:00
Add a way for virtual tables to return the expected number of rows for a scan (not just the overall cost) to SQLite. Have the rtree module make use of this.
FossilOrigin-Name: 5a3cfd747a85480d215784817c3821d87ecfa2f7
This commit is contained in:
@ -137,6 +137,16 @@ typedef union RtreeCoord RtreeCoord;
|
||||
*/
|
||||
#define HASHSIZE 128
|
||||
|
||||
/* The xBestIndex method of this virtual table requires an estimate of
|
||||
** the number of rows in the virtual table to calculate the costs of
|
||||
** various strategies. If possible, this estimate is loaded from the
|
||||
** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum).
|
||||
** Otherwise, if no sqlite_stat1 entry is available, use
|
||||
** RTREE_DEFAULT_ROWEST.
|
||||
*/
|
||||
#define RTREE_DEFAULT_ROWEST 1048576
|
||||
#define RTREE_MIN_ROWEST 100
|
||||
|
||||
/*
|
||||
** An rtree virtual-table object.
|
||||
*/
|
||||
@ -151,6 +161,7 @@ struct Rtree {
|
||||
char *zName; /* Name of r-tree table */
|
||||
RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
|
||||
int nBusy; /* Current number of users of this structure */
|
||||
i64 nRowEst; /* Estimated number of rows in this table */
|
||||
|
||||
/* List of nodes removed during a CondenseTree operation. List is
|
||||
** linked together via the pointer normally used for hash chains -
|
||||
@ -1343,6 +1354,19 @@ static int rtreeFilter(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
|
||||
** extension is currently being used by a version of SQLite too old to
|
||||
** support estimatedRows. In that case this function is a no-op.
|
||||
*/
|
||||
static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
|
||||
#if SQLITE_VERSION_NUMBER>=308002
|
||||
if( sqlite3_libversion_number()>=300802 ){
|
||||
pIdxInfo->estimatedRows = nRow;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Rtree virtual table module xBestIndex method. There are three
|
||||
** table scan strategies to choose from (in order from most to
|
||||
@ -1378,13 +1402,14 @@ static int rtreeFilter(
|
||||
** is 'a', the second from the left 'b' etc.
|
||||
*/
|
||||
static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
Rtree *pRtree = (Rtree*)tab;
|
||||
int rc = SQLITE_OK;
|
||||
int ii;
|
||||
i64 nRow; /* Estimated rows returned by this scan */
|
||||
|
||||
int iIdx = 0;
|
||||
char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
|
||||
memset(zIdxStr, 0, sizeof(zIdxStr));
|
||||
UNUSED_PARAMETER(tab);
|
||||
|
||||
assert( pIdxInfo->idxStr==0 );
|
||||
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
|
||||
@ -1404,9 +1429,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
/* This strategy involves a two rowid lookups on an B-Tree structures
|
||||
** and then a linear search of an R-Tree node. This should be
|
||||
** considered almost as quick as a direct rowid lookup (for which
|
||||
** sqlite uses an internal cost of 0.0).
|
||||
** sqlite uses an internal cost of 0.0). It is expected to return
|
||||
** a single row.
|
||||
*/
|
||||
pIdxInfo->estimatedCost = 10.0;
|
||||
pIdxInfo->estimatedCost = 30.0;
|
||||
setEstimatedRows(pIdxInfo, 1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@ -1435,8 +1462,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
|
||||
if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
assert( iIdx>=0 );
|
||||
pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1));
|
||||
|
||||
nRow = pRtree->nRowEst / (iIdx + 1);
|
||||
pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
|
||||
setEstimatedRows(pIdxInfo, nRow);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -2911,6 +2941,37 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** This function populates the pRtree->nRowEst variable with an estimate
|
||||
** of the number of rows in the virtual table. If possible, this is based
|
||||
** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST.
|
||||
*/
|
||||
static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
|
||||
const char *zSql = "SELECT stat FROM sqlite_stat1 WHERE tbl= ? || '_rowid'";
|
||||
sqlite3_stmt *p;
|
||||
int rc;
|
||||
i64 nRow = 0;
|
||||
|
||||
rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
sqlite3_bind_text(p, 1, pRtree->zName, -1, SQLITE_STATIC);
|
||||
if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0);
|
||||
rc = sqlite3_finalize(p);
|
||||
}else if( rc!=SQLITE_NOMEM ){
|
||||
rc = SQLITE_OK;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
if( nRow==0 ){
|
||||
pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
|
||||
}else{
|
||||
pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static sqlite3_module rtreeModule = {
|
||||
0, /* iVersion */
|
||||
rtreeCreate, /* xCreate - create a table */
|
||||
@ -2996,6 +3057,7 @@ static int rtreeSqlInit(
|
||||
appStmt[7] = &pRtree->pWriteParent;
|
||||
appStmt[8] = &pRtree->pDeleteParent;
|
||||
|
||||
rc = rtreeQueryStat1(db, pRtree);
|
||||
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
|
||||
char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
|
||||
if( zSql ){
|
||||
|
Reference in New Issue
Block a user