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

Add the SQLITE_RECOVER_ROWIDS option. To specify that rowid values that are not also explicit INTEGER PRIMARY KEY values should be preserved.

FossilOrigin-Name: 69cc9aba56a196bbd159bd24868aa5ccc60bed0dc612d09ed8a3ae898f156809
This commit is contained in:
dan
2022-09-05 15:56:09 +00:00
parent 7302079dbe
commit a768b67dcc
5 changed files with 47 additions and 12 deletions

View File

@ -49,6 +49,7 @@ struct RecoverTable {
int nCol; /* Number of columns in table */
RecoverColumn *aCol; /* Array of columns */
int bIntkey; /* True for intkey, false for without rowid */
int iRowidBind; /* If >0, bind rowid to INSERT here */
RecoverTable *pNext;
};
@ -94,6 +95,7 @@ struct sqlite3_recover {
char *zStateDb;
char *zLostAndFound; /* Name of lost-and-found table (or NULL) */
int bFreelistCorrupt;
int bRecoverRowid;
};
/*
@ -476,6 +478,7 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){
if( pStmt ){
int iPk = -1;
int iBind = 1;
RecoverTable *pNew = 0;
int nCol = 0;
int nName = recoverStrlen(zName);
@ -491,7 +494,6 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){
if( pNew ){
int i = 0;
int iField = 0;
int iBind = 1;
char *csr = 0;
pNew->aCol = (RecoverColumn*)&pNew[1];
pNew->zTab = csr = (char*)&pNew->aCol[nCol];
@ -545,7 +547,11 @@ static void recoverAddTable(sqlite3_recover *p, const char *zName, i64 iRoot){
}
recoverFinalize(p, pStmt);
if( iPk>=0 ) pNew->aCol[iPk].bIPK = 1;
if( iPk>=0 ){
pNew->aCol[iPk].bIPK = 1;
}else if( pNew->bIntkey ){
pNew->iRowidBind = iBind++;
}
}
}
@ -646,6 +652,13 @@ static sqlite3_stmt *recoverInsertStmt(
assert( nField<=pTab->nCol );
zSql = recoverMPrintf(p, "INSERT OR IGNORE INTO %Q(", pTab->zTab);
if( pTab->iRowidBind ){
assert( pTab->bIntkey );
zSql = recoverMPrintf(p, "%z_rowid_", zSql);
zBind = recoverMPrintf(p, "%z?%d", zBind, pTab->iRowidBind);
zSep = ", ";
}
for(ii=0; ii<nField; ii++){
int eHidden = pTab->aCol[ii].eHidden;
if( eHidden!=RECOVER_EHIDDEN_VIRTUAL
@ -995,6 +1008,7 @@ static int recoverWriteData(sqlite3_recover *p){
i64 iPrevRoot = -1;
i64 iPrevPage = -1;
int iPrevCell = -1;
int bHaveRowid = 0; /* True if iRowid is valid */
i64 iRowid = 0;
int nVal = -1;
@ -1034,6 +1048,9 @@ static int recoverWriteData(sqlite3_recover *p){
}
}
}
if( p->bRecoverRowid && pTab->iRowidBind>0 && bHaveRowid ){
sqlite3_bind_int64(pInsert, pTab->iRowidBind, iRowid);
}
sqlite3_step(pInsert);
recoverReset(p, pInsert);
@ -1046,6 +1063,7 @@ static int recoverWriteData(sqlite3_recover *p){
apVal[ii] = 0;
}
nVal = -1;
bHaveRowid = 0;
}
if( iRoot==0 ) continue;
@ -1061,6 +1079,7 @@ static int recoverWriteData(sqlite3_recover *p){
iRowid = sqlite3_column_int64(pSel, 4);
assert( nVal==-1 );
nVal = 0;
bHaveRowid = 1;
}else if( iField<nMax ){
assert( apVal[iField]==0 );
apVal[iField] = sqlite3_value_dup( pVal );
@ -1139,6 +1158,10 @@ int sqlite3_recover_config(sqlite3_recover *p, int op, void *pArg){
p->bFreelistCorrupt = (pArg ? 1 : 0);
break;
case SQLITE_RECOVER_ROWIDS:
p->bRecoverRowid = (pArg ? 1 : 0);
break;
default:
rc = SQLITE_NOTFOUND;
break;

View File

@ -58,10 +58,13 @@ int sqlite3_recover_config(sqlite3_recover*, int op, void *pArg);
** appear to be linked into the freelist. Otherwise, pages on the freelist
** are ignored. Setting this option can recover more data from the
** database, but often ends up "recovering" deleted records.
**
** SQLITE_RECOVER_ROWIDS:
*/
#define SQLITE_RECOVER_TESTDB 789
#define SQLITE_RECOVER_LOST_AND_FOUND 790
#define SQLITE_RECOVER_FREELIST_CORRUPT 791
#define SQLITE_RECOVER_ROWIDS 792
/* Step the recovery object. Return SQLITE_DONE if recovery is complete,
** SQLITE_OK if recovery is not complete but no error has occurred, or

View File

@ -82,6 +82,7 @@ static int testRecoverCmd(
"testdb", /* 0 */
"lostandfound", /* 1 */
"freelistcorrupt", /* 2 */
"rowids", /* 3 */
0
};
int iOp = 0;
@ -102,12 +103,20 @@ static int testRecoverCmd(
break;
case 2: {
int iVal = 0;
if( Tcl_GetIntFromObj(interp, objv[3], &iVal) ) return TCL_ERROR;
if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR;
res = sqlite3_recover_config(pTest->p,
SQLITE_RECOVER_FREELIST_CORRUPT, (void*)iVal
);
break;
}
case 3: {
int iVal = 0;
if( Tcl_GetBooleanFromObj(interp, objv[3], &iVal) ) return TCL_ERROR;
res = sqlite3_recover_config(pTest->p,
SQLITE_RECOVER_ROWIDS, (void*)iVal
);
break;
}
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
break;