1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-27 20:41:58 +03:00

Fix recovery of utf-16 databases.

FossilOrigin-Name: 5b05be0861f35804270fbd184ad4b89c23e98cc2fbd56b9e4fe6197daef5fe49
This commit is contained in:
dan
2022-09-14 16:37:59 +00:00
parent 322967df59
commit b8eaf9a10d
7 changed files with 138 additions and 23 deletions

View File

@ -75,6 +75,7 @@
#include "sqlite3ext.h"
typedef unsigned char u8;
typedef unsigned int u32;
#endif
SQLITE_EXTENSION_INIT1
@ -107,6 +108,7 @@ struct DbdataCursor {
int iField; /* Current field number */
u8 *pHdrPtr;
u8 *pPtr;
u32 enc; /* Text encoding */
sqlite3_int64 iIntkey; /* Integer key value */
};
@ -299,14 +301,14 @@ static int dbdataClose(sqlite3_vtab_cursor *pCursor){
/*
** Utility methods to decode 16 and 32-bit big-endian unsigned integers.
*/
static unsigned int get_uint16(unsigned char *a){
static u32 get_uint16(unsigned char *a){
return (a[0]<<8)|a[1];
}
static unsigned int get_uint32(unsigned char *a){
return ((unsigned int)a[0]<<24)
| ((unsigned int)a[1]<<16)
| ((unsigned int)a[2]<<8)
| ((unsigned int)a[3]);
static u32 get_uint32(unsigned char *a){
return ((u32)a[0]<<24)
| ((u32)a[1]<<16)
| ((u32)a[2]<<8)
| ((u32)a[3]);
}
/*
@ -321,7 +323,7 @@ static unsigned int get_uint32(unsigned char *a){
*/
static int dbdataLoadPage(
DbdataCursor *pCsr, /* Cursor object */
unsigned int pgno, /* Page number of page to load */
u32 pgno, /* Page number of page to load */
u8 **ppPage, /* OUT: pointer to page buffer */
int *pnPage /* OUT: Size of (*ppPage) in bytes */
){
@ -405,6 +407,7 @@ static int dbdataValueBytes(int eType){
*/
static void dbdataValue(
sqlite3_context *pCtx,
u32 enc,
int eType,
u8 *pData,
int nData
@ -449,7 +452,17 @@ static void dbdataValue(
default: {
int n = ((eType-12) / 2);
if( eType % 2 ){
sqlite3_result_text(pCtx, (const char*)pData, n, SQLITE_TRANSIENT);
switch( enc ){
case SQLITE_UTF16BE:
sqlite3_result_text16be(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
break;
case SQLITE_UTF16LE:
sqlite3_result_text16le(pCtx, (void*)pData, n, SQLITE_TRANSIENT);
break;
default:
sqlite3_result_text(pCtx, (char*)pData, n, SQLITE_TRANSIENT);
break;
}
}else{
sqlite3_result_blob(pCtx, pData, n, SQLITE_TRANSIENT);
}
@ -588,7 +601,7 @@ static int dbdataNext(sqlite3_vtab_cursor *pCursor){
/* Load content from overflow pages */
if( nPayload>nLocal ){
sqlite3_int64 nRem = nPayload - nLocal;
unsigned int pgnoOvfl = get_uint32(&pCsr->aPage[iOff]);
u32 pgnoOvfl = get_uint32(&pCsr->aPage[iOff]);
while( nRem>0 ){
u8 *aOvfl = 0;
int nOvfl = 0;
@ -703,6 +716,25 @@ static int dbdataDbsize(DbdataCursor *pCsr, const char *zSchema){
return rc;
}
/*
** Attempt to figure out the encoding of the database by retrieving page 1
** and inspecting the header field. If successful, set the pCsr->enc variable
** and return SQLITE_OK. Otherwise, return an SQLite error code.
*/
static int dbdataGetEncoding(DbdataCursor *pCsr){
int rc = SQLITE_OK;
int nPg1 = 0;
u8 *aPg1 = 0;
rc = dbdataLoadPage(pCsr, 1, &aPg1, &nPg1);
assert( rc!=SQLITE_OK || nPg1==0 || nPg1>=512 );
if( rc==SQLITE_OK && nPg1>0 ){
pCsr->enc = get_uint32(&aPg1[56]);
}
sqlite3_free(aPg1);
return rc;
}
/*
** xFilter method for sqlite_dbdata and sqlite_dbptr.
*/
@ -725,7 +757,6 @@ static int dbdataFilter(
pCsr->iPgno = sqlite3_value_int(argv[(idxNum & 0x01)]);
pCsr->bOnePage = 1;
}else{
pCsr->nPage = dbdataDbsize(pCsr, zSchema);
rc = dbdataDbsize(pCsr, zSchema);
}
@ -754,6 +785,13 @@ static int dbdataFilter(
}else{
pTab->base.zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pTab->db));
}
/* Try to determine the encoding of the db by inspecting the header
** field on page 1. */
if( rc==SQLITE_OK ){
rc = dbdataGetEncoding(pCsr);
}
if( rc==SQLITE_OK ){
rc = dbdataNext(pCursor);
}
@ -808,7 +846,8 @@ static int dbdataColumn(
sqlite3_int64 iType;
dbdataGetVarint(pCsr->pHdrPtr, &iType);
dbdataValue(
ctx, iType, pCsr->pPtr, &pCsr->pRec[pCsr->nRec] - pCsr->pPtr
ctx, pCsr->enc, iType, pCsr->pPtr,
&pCsr->pRec[pCsr->nRec] - pCsr->pPtr
);
}
break;

View File

@ -192,6 +192,7 @@ do_execsql_test 11.1 {
INSERT INTO u1 VALUES('edvin marton', 'bond');
INSERT INTO u1 VALUES(1, 4.0);
}
do_recover_test 11

View File

@ -78,7 +78,6 @@ ifcapable utf16 {
faultsim_save_and_close
proc my_sql_hook {sql} {
puts "HOOK $sql"
lappend ::lSql $sql
return 0
}

View File

@ -229,6 +229,40 @@ static int test_sqlite3_recover_init(
return TCL_OK;
}
/*
** Declaration for public API function in file dbdata.c. This may be called
** with NULL as the final two arguments to register the sqlite_dbptr and
** sqlite_dbdata virtual tables with a database handle.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
int sqlite3_dbdata_init(sqlite3*, char**, const sqlite3_api_routines*);
/*
** sqlite3_recover_init DB DBNAME URI
*/
static int test_sqlite3_dbdata_init(
void *clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *CONST objv[]
){
sqlite3 *db = 0;
if( objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB");
return TCL_ERROR;
}
if( getDbPointer(interp, objv[1], &db) ) return TCL_ERROR;
sqlite3_dbdata_init(db, 0, 0);
Tcl_ResetResult(interp);
return TCL_OK;
}
int TestRecover_Init(Tcl_Interp *interp){
struct Cmd {
const char *zCmd;
@ -237,6 +271,7 @@ int TestRecover_Init(Tcl_Interp *interp){
} aCmd[] = {
{ "sqlite3_recover_init", test_sqlite3_recover_init, 0 },
{ "sqlite3_recover_init_sql", test_sqlite3_recover_init, (void*)1 },
{ "sqlite3_dbdata_init", test_sqlite3_dbdata_init, (void*)1 },
};
int i;