1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-29 08:01:23 +03:00

Add tests cases and fix minor issues in the rtreecheck() function.

FossilOrigin-Name: d6130cd226c0ca95e02f0cbabfdc27071acdcf83e0d0cb0eaa47d992479ed9a1
This commit is contained in:
dan
2017-10-25 18:17:24 +00:00
parent 2c0d084400
commit 7e2b38c53b
4 changed files with 119 additions and 39 deletions

View File

@ -3659,7 +3659,11 @@ static sqlite3_stmt *rtreeCheckPrepare(
z = sqlite3_vmprintf(zFmt, ap);
if( pCheck->rc==SQLITE_OK ){
pCheck->rc = sqlite3_prepare_v2(pCheck->db, z, -1, &pRet, 0);
if( z==0 ){
pCheck->rc = SQLITE_NOMEM;
}else{
pCheck->rc = sqlite3_prepare_v2(pCheck->db, z, -1, &pRet, 0);
}
}
sqlite3_free(z);
@ -3781,7 +3785,7 @@ static void rtreeCheckMapping(
);
}else if( rc==SQLITE_ROW ){
i64 ii = sqlite3_column_int64(pStmt, 0);
if( ii!=iVal ){
if( ii!=iVal ){
rtreeCheckAppendMsg(pCheck,
"Found (%lld -> %lld) in %s table, expected (%lld -> %lld)",
iKey, ii, (bLeaf ? "%_rowid" : "%_parent"), iKey, iVal
@ -3791,10 +3795,22 @@ static void rtreeCheckMapping(
rtreeCheckReset(pCheck, pStmt);
}
/*
** Argument pCell points to an array of coordinates stored on an rtree page.
** This function checks that the coordinates are internally consistent (no
** x1>x2 conditions) and adds an error message to the RtreeCheck object
** if they are not.
**
** Additionally, if pParent is not NULL, then it is assumed to point to
** the array of coordinates on the parent page that bound the page
** containing pCell. In this case it is also verified that the two
** sets of coordinates are mutually consistent and an error message added
** to the RtreeCheck object if they are not.
*/
static void rtreeCheckCellCoord(
RtreeCheck *pCheck,
i64 iNode,
int iCell,
i64 iNode, /* Node id to use in error messages */
int iCell, /* Cell number to use in error messages */
u8 *pCell, /* Pointer to cell coordinates */
u8 *pParent /* Pointer to parent coordinates */
){
@ -3829,6 +3845,14 @@ static void rtreeCheckCellCoord(
}
}
/*
** Run rtreecheck() checks on node iNode, which is at depth iDepth within
** the r-tree structure. Argument aParent points to the array of coordinates
** that bound node iNode on the parent node.
**
** If any problems are discovered, an error message is appended to the
** report accumulated in the RtreeCheck object.
*/
static void rtreeCheckNode(
RtreeCheck *pCheck,
int iDepth, /* Depth of iNode (0==leaf) */
@ -3864,19 +3888,20 @@ static void rtreeCheckNode(
"Node %lld is too small for cell count of %d (%d bytes)",
iNode, nCell, nNode
);
}
for(i=0; i<nCell; i++){
u8 *pCell = &aNode[4 + i*(8 + pCheck->nDim*2*4)];
i64 iVal = readInt64(pCell);
rtreeCheckCellCoord(pCheck, iNode, i, &pCell[8], aParent);
}else{
for(i=0; i<nCell; i++){
u8 *pCell = &aNode[4 + i*(8 + pCheck->nDim*2*4)];
i64 iVal = readInt64(pCell);
rtreeCheckCellCoord(pCheck, iNode, i, &pCell[8], aParent);
if( iDepth>0 ){
rtreeCheckMapping(pCheck, 0, iVal, iNode);
rtreeCheckNode(pCheck, iDepth-1, &pCell[8], iVal);
pCheck->nNonLeaf++;
}else{
rtreeCheckMapping(pCheck, 1, iVal, iNode);
pCheck->nLeaf++;
if( iDepth>0 ){
rtreeCheckMapping(pCheck, 0, iVal, iNode);
rtreeCheckNode(pCheck, iDepth-1, &pCell[8], iVal);
pCheck->nNonLeaf++;
}else{
rtreeCheckMapping(pCheck, 1, iVal, iNode);
pCheck->nLeaf++;
}
}
}
}
@ -3884,9 +3909,14 @@ static void rtreeCheckNode(
}
}
static void rtreeCheckCount(
RtreeCheck *pCheck, const char *zTbl, i64 nExpected
){
/*
** The second argument to this function must be either "_rowid" or
** "_parent". This function checks that the number of entries in the
** %_rowid or %_parent table is exactly nExpect. If not, it adds
** an error message to the report in the RtreeCheck object indicated
** by the first argument.
*/
static void rtreeCheckCount(RtreeCheck *pCheck, const char *zTbl, i64 nExpect){
if( pCheck->rc==SQLITE_OK ){
sqlite3_stmt *pCount;
pCount = rtreeCheckPrepare(pCheck, "SELECT count(*) FROM %Q.'%q%s'",
@ -3895,9 +3925,9 @@ static void rtreeCheckCount(
if( pCount ){
if( sqlite3_step(pCount)==SQLITE_ROW ){
i64 nActual = sqlite3_column_int64(pCount, 0);
if( nActual!=nExpected ){
if( nActual!=nExpect ){
rtreeCheckAppendMsg(pCheck, "Wrong number of entries in %%%s table"
" - expected %lld, actual %lld" , zTbl, nExpected, nActual
" - expected %lld, actual %lld" , zTbl, nExpect, nActual
);
}
}
@ -3906,7 +3936,11 @@ static void rtreeCheckCount(
}
}
static int rtreeCheck(
/*
** This function does the bulk of the work for the rtree integrity-check.
** It is called by rtreecheck(), which is the SQL function implementation.
*/
static int rtreeCheckTable(
sqlite3 *db, /* Database handle to access db through */
const char *zDb, /* Name of db ("main", "temp" etc.) */
const char *zTab, /* Name of rtree table to check */
@ -3945,11 +3979,13 @@ static int rtreeCheck(
}
/* Do the actual integrity-check */
if( check.rc==SQLITE_OK ){
rtreeCheckNode(&check, 0, 0, 1);
if( check.nDim>=1 ){
if( check.rc==SQLITE_OK ){
rtreeCheckNode(&check, 0, 0, 1);
}
rtreeCheckCount(&check, "_rowid", check.nLeaf);
rtreeCheckCount(&check, "_parent", check.nNonLeaf);
}
rtreeCheckCount(&check, "_rowid", check.nLeaf);
rtreeCheckCount(&check, "_parent", check.nNonLeaf);
/* Finalize SQL statements used by the integrity-check */
sqlite3_finalize(check.pGetNode);
@ -4018,7 +4054,7 @@ static void rtreecheck(
}else{
zTab = (const char*)sqlite3_value_text(apArg[1]);
}
rc = rtreeCheck(sqlite3_context_db_handle(ctx), zDb, zTab, &zReport);
rc = rtreeCheckTable(sqlite3_context_db_handle(ctx), zDb, zTab, &zReport);
if( rc==SQLITE_OK ){
sqlite3_result_text(ctx, zReport ? zReport : "ok", -1, SQLITE_TRANSIENT);
}else{