Skip to content

Commit

Permalink
Attempt to use STAT4 information to estimate the selectivity of WHERE…
Browse files Browse the repository at this point in the history
… clause

terms when using the skip-scan optimization.
  • Loading branch information
D. Richard Hipp committed Jun 30, 2014
2 parents d28d14f + 1d91d5e commit b3fe9b0
Show file tree
Hide file tree
Showing 8 changed files with 581 additions and 112 deletions.
2 changes: 2 additions & 0 deletions src/sqliteInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -3431,7 +3431,9 @@ void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
void sqlite3AnalyzeFunctions(void);
int sqlite3Stat4ProbeSetValue(Parse*,Index*,UnpackedRecord**,Expr*,u8,int,int*);
int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
void sqlite3Stat4ProbeFree(UnpackedRecord*);
int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
#endif

/*
Expand Down
1 change: 1 addition & 0 deletions src/vdbe.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);

void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*,int);
Expand Down
1 change: 0 additions & 1 deletion src/vdbeInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,6 @@ void sqlite3VdbeDeleteAuxData(Vdbe*, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*);
int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *);
int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
int sqlite3VdbeExec(Vdbe*);
int sqlite3VdbeList(Vdbe*);
int sqlite3VdbeHalt(Vdbe*);
Expand Down
2 changes: 2 additions & 0 deletions src/vdbeaux.c
Original file line number Diff line number Diff line change
Expand Up @@ -3589,6 +3589,7 @@ int sqlite3VdbeRecordCompare(
** value. */
assert( CORRUPT_DB
|| pPKey2->default_rc==vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)
|| pKeyInfo->db->mallocFailed
);
return pPKey2->default_rc;
}
Expand Down Expand Up @@ -3754,6 +3755,7 @@ static int vdbeRecordCompareString(
|| (res<0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)<0)
|| (res>0 && vdbeRecordCompareDebug(nKey1, pKey1, pPKey2)>0)
|| CORRUPT_DB
|| pPKey2->pKeyInfo->db->mallocFailed
);
return res;
}
Expand Down
166 changes: 133 additions & 33 deletions src/vdbemem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,68 @@ void sqlite3AnalyzeFunctions(void){
}
}

/*
** Attempt to extract a value from pExpr and use it to construct *ppVal.
**
** If pAlloc is not NULL, then an UnpackedRecord object is created for
** pAlloc if one does not exist and the new value is added to the
** UnpackedRecord object.
**
** A value is extracted in the following cases:
**
** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
**
** * The expression is a bound variable, and this is a reprepare, or
**
** * The expression is a literal value.
**
** On success, *ppVal is made to point to the extracted value. The caller
** is responsible for ensuring that the value is eventually freed.
*/
static int stat4ValueFromExpr(
Parse *pParse, /* Parse context */
Expr *pExpr, /* The expression to extract a value from */
u8 affinity, /* Affinity to use */
struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */
sqlite3_value **ppVal /* OUT: New value object (or NULL) */
){
int rc = SQLITE_OK;
sqlite3_value *pVal = 0;
sqlite3 *db = pParse->db;

/* Skip over any TK_COLLATE nodes */
pExpr = sqlite3ExprSkipCollate(pExpr);

if( !pExpr ){
pVal = valueNew(db, pAlloc);
if( pVal ){
sqlite3VdbeMemSetNull((Mem*)pVal);
}
}else if( pExpr->op==TK_VARIABLE
|| NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
){
Vdbe *v;
int iBindVar = pExpr->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
if( (v = pParse->pReprepare)!=0 ){
pVal = valueNew(db, pAlloc);
if( pVal ){
rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
if( rc==SQLITE_OK ){
sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
}
pVal->db = pParse->db;
}
}
}else{
rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc);
}

assert( pVal==0 || pVal->db==db );
*ppVal = pVal;
return rc;
}

/*
** This function is used to allocate and populate UnpackedRecord
** structures intended to be compared against sample index keys stored
Expand Down Expand Up @@ -1191,50 +1253,88 @@ int sqlite3Stat4ProbeSetValue(
int iVal, /* Array element to populate */
int *pbOk /* OUT: True if value was extracted */
){
int rc = SQLITE_OK;
int rc;
sqlite3_value *pVal = 0;
sqlite3 *db = pParse->db;


struct ValueNewStat4Ctx alloc;

alloc.pParse = pParse;
alloc.pIdx = pIdx;
alloc.ppRec = ppRec;
alloc.iVal = iVal;

/* Skip over any TK_COLLATE nodes */
pExpr = sqlite3ExprSkipCollate(pExpr);

if( !pExpr ){
pVal = valueNew(db, &alloc);
if( pVal ){
sqlite3VdbeMemSetNull((Mem*)pVal);
}
}else if( pExpr->op==TK_VARIABLE
|| NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
){
Vdbe *v;
int iBindVar = pExpr->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
if( (v = pParse->pReprepare)!=0 ){
pVal = valueNew(db, &alloc);
if( pVal ){
rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
if( rc==SQLITE_OK ){
sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
}
pVal->db = pParse->db;
}
}
}else{
rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, &alloc);
}
rc = stat4ValueFromExpr(pParse, pExpr, affinity, &alloc, &pVal);
assert( pVal==0 || pVal->db==pParse->db );
*pbOk = (pVal!=0);

assert( pVal==0 || pVal->db==db );
return rc;
}

/*
** Attempt to extract a value from expression pExpr using the methods
** as described for sqlite3Stat4ProbeSetValue() above.
**
** If successful, set *ppVal to point to a new value object and return
** SQLITE_OK. If no value can be extracted, but no other error occurs
** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error
** does occur, return an SQLite error code. The final value of *ppVal
** is undefined in this case.
*/
int sqlite3Stat4ValueFromExpr(
Parse *pParse, /* Parse context */
Expr *pExpr, /* The expression to extract a value from */
u8 affinity, /* Affinity to use */
sqlite3_value **ppVal /* OUT: New value object (or NULL) */
){
return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal);
}

/*
** Extract the iCol-th column from the nRec-byte record in pRec. Write
** the column value into *ppVal. If *ppVal is initially NULL then a new
** sqlite3_value object is allocated.
**
** If *ppVal is initially NULL then the caller is responsible for
** ensuring that the value written into *ppVal is eventually freed.
*/
int sqlite3Stat4Column(
sqlite3 *db, /* Database handle */
const void *pRec, /* Pointer to buffer containing record */
int nRec, /* Size of buffer pRec in bytes */
int iCol, /* Column to extract */
sqlite3_value **ppVal /* OUT: Extracted value */
){
u32 t; /* a column type code */
int nHdr; /* Size of the header in the record */
int iHdr; /* Next unread header byte */
int iField; /* Next unread data byte */
int szField; /* Size of the current data field */
int i; /* Column index */
u8 *a = (u8*)pRec; /* Typecast byte array */
Mem *pMem = *ppVal; /* Write result into this Mem object */

assert( iCol>0 );
iHdr = getVarint32(a, nHdr);
if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
iField = nHdr;
for(i=0; i<=iCol; i++){
iHdr += getVarint32(&a[iHdr], t);
testcase( iHdr==nHdr );
testcase( iHdr==nHdr+1 );
if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT;
szField = sqlite3VdbeSerialTypeLen(t);
iField += szField;
}
testcase( iField==nRec );
testcase( iField==nRec+1 );
if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
if( pMem==0 ){
pMem = *ppVal = sqlite3ValueNew(db);
if( pMem==0 ) return SQLITE_NOMEM;
}
sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
pMem->enc = ENC(db);
return SQLITE_OK;
}

/*
** Unless it is NULL, the argument must be an UnpackedRecord object returned
** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes
Expand Down
Loading

0 comments on commit b3fe9b0

Please sign in to comment.