From: dan Date: Thu, 6 Oct 2022 21:00:23 +0000 (+0000) Subject: Ensure the page-size, auto-vacuum and other settings are copied into the recovered... X-Git-Tag: version-3.40.0~91^2~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=88a0637f5781420a2811ba09dc20e3af2f4e7df0;p=thirdparty%2Fsqlite.git Ensure the page-size, auto-vacuum and other settings are copied into the recovered database. FossilOrigin-Name: 078520f2825bcb82d01b30209409e9bf8b6c99084c192345df41c9f531d08f51 --- diff --git a/ext/recover/recover1.test b/ext/recover/recover1.test index aadc5b556e..99ddc1f4b3 100644 --- a/ext/recover/recover1.test +++ b/ext/recover/recover1.test @@ -36,6 +36,12 @@ proc compare_dbs {db1 db2} { foreach tbl [$db1 eval {SELECT name FROM sqlite_master WHERE type='table'}] { compare_result $db1 $db2 "SELECT * FROM $tbl" } + + compare_result $db1 $db2 "PRAGMA page_size" + compare_result $db1 $db2 "PRAGMA auto_vacuum" + compare_result $db1 $db2 "PRAGMA encoding" + compare_result $db1 $db2 "PRAGMA user_version" + compare_result $db1 $db2 "PRAGMA application_id" } proc do_recover_test {tn} { @@ -186,11 +192,19 @@ do_recover_test 10 #------------------------------------------------------------------------- reset_db do_execsql_test 11.1 { + PRAGMA page_size = 4096; PRAGMA encoding='utf16'; + PRAGMA auto_vacuum = 2; + PRAGMA user_version = 45; + PRAGMA application_id = 22; + CREATE TABLE u1(u, v); INSERT INTO u1 VALUES('edvin marton', 'bond'); INSERT INTO u1 VALUES(1, 4.0); } +do_execsql_test 11.1a { + PRAGMA auto_vacuum; +} {2} do_recover_test 11 diff --git a/ext/recover/recoverslowidx.test b/ext/recover/recoverslowidx.test index 838b771eaf..4dfb7e5f53 100644 --- a/ext/recover/recoverslowidx.test +++ b/ext/recover/recoverslowidx.test @@ -17,7 +17,7 @@ if {![info exists testdir]} { } source [file join [file dirname [info script]] recover_common.tcl] source $testdir/tester.tcl -set testprefix recoverrowid +set testprefix recoverslowidx ifcapable !vtab { finish_test; return @@ -46,6 +46,11 @@ do_test 1.2 { } [list {*}{ {BEGIN} {PRAGMA writable_schema = on} + {PRAGMA encoding = 'UTF-8'} + {PRAGMA page_size = '1024'} + {PRAGMA auto_vacuum = '0'} + {PRAGMA user_version = '0'} + {PRAGMA application_id = '0'} {CREATE TABLE t1(a, b)} {INSERT OR IGNORE INTO 't1'(_rowid_, 'a', 'b') VALUES (1, 1, 1)} {INSERT OR IGNORE INTO 't1'(_rowid_, 'a', 'b') VALUES (2, 2, 2)} @@ -69,6 +74,11 @@ do_test 1.4 { } [list {*}{ {BEGIN} {PRAGMA writable_schema = on} + {PRAGMA encoding = 'UTF-8'} + {PRAGMA page_size = '1024'} + {PRAGMA auto_vacuum = '0'} + {PRAGMA user_version = '0'} + {PRAGMA application_id = '0'} {CREATE TABLE t1(a, b)} {CREATE INDEX i1 ON t1(a)} {INSERT OR IGNORE INTO 't1'(_rowid_, 'a', 'b') VALUES (1, 1, 1)} diff --git a/ext/recover/sqlite3recover.c b/ext/recover/sqlite3recover.c index 3c70542989..4b61d6b3ec 100644 --- a/ext/recover/sqlite3recover.c +++ b/ext/recover/sqlite3recover.c @@ -801,6 +801,120 @@ static void recoverEscapeCrnl( sqlite3_result_value(context, argv[0]); } +/* +** This function is a no-op if recover handle p already contains an error +** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in +** this case. +** +** Otherwise, attempt to populate temporary table "recovery.schema" with the +** parts of the database schema that can be extracted from the input database. +** +** If no error occurs, SQLITE_OK is returned. Otherwise, an error code +** and error message are left in the recover handle and a copy of the +** error code returned. It is not considered an error if part of all of +** the database schema cannot be recovered due to corruption. +*/ +static int recoverCacheSchema(sqlite3_recover *p){ + return recoverExec(p, p->dbOut, + "WITH RECURSIVE pages(p) AS (" + " SELECT 1" + " UNION" + " SELECT child FROM sqlite_dbptr('getpage()'), pages WHERE pgno=p" + ")" + "INSERT INTO recovery.schema SELECT" + " max(CASE WHEN field=0 THEN value ELSE NULL END)," + " max(CASE WHEN field=1 THEN value ELSE NULL END)," + " max(CASE WHEN field=2 THEN value ELSE NULL END)," + " max(CASE WHEN field=3 THEN value ELSE NULL END)," + " max(CASE WHEN field=4 THEN value ELSE NULL END)" + "FROM sqlite_dbdata('getpage()') WHERE pgno IN (" + " SELECT p FROM pages" + ") GROUP BY pgno, cell" + ); +} + +/* +** If this recover handle is not in SQL callback mode (i.e. was not created +** using sqlite3_recover_init_sql()) of if an error has already occurred, +** this function is a no-op. Otherwise, issue a callback with SQL statement +** zSql as the parameter. +** +** If the callback returns non-zero, set the recover handle error code to +** the value returned (so that the caller will abandon processing). +*/ +static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){ + if( p->errCode==SQLITE_OK && p->xSql ){ + int res = p->xSql(p->pSqlCtx, zSql); + if( res ){ + recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res); + } + } +} + +/* +** Transfer the following settings from the input database to the output +** database: +** +** + page-size, +** + auto-vacuum settings, +** + database encoding, +** + user-version (PRAGMA user_version), and +** + application-id (PRAGMA application_id), and +*/ +static void recoverTransferSettings(sqlite3_recover *p){ + const char *aPragma[] = { + "encoding", + "page_size", + "auto_vacuum", + "user_version", + "application_id" + }; + int ii; + + /* Truncate the output database to 0 pages in size. This is done by + ** opening a new, empty, temp db, then using the backup API to clobber + ** any existing output db with a copy of it. */ + if( p->errCode==SQLITE_OK ){ + sqlite3 *db2 = 0; + int rc = sqlite3_open("", &db2); + if( rc!=SQLITE_OK ){ + recoverDbError(p, db2); + return; + } + + for(ii=0; iidbIn, "PRAGMA %Q.%s", p->zDb, zPrag); + if( p->errCode==SQLITE_OK && sqlite3_step(p1)==SQLITE_ROW ){ + const char *zArg = (const char*)sqlite3_column_text(p1, 0); + char *z2 = recoverMPrintf(p, "PRAGMA %s = %Q", zPrag, zArg); + recoverSqlCallback(p, z2); + recoverExec(p, db2, z2); + sqlite3_free(z2); + if( zArg==0 ){ + recoverError(p, SQLITE_NOMEM, 0); + } + } + recoverFinalize(p, p1); + } + recoverExec(p, db2, "CREATE TABLE t1(a); DROP TABLE t1;"); + + if( p->errCode==SQLITE_OK ){ + sqlite3 *db = p->dbOut; + sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main"); + if( pBackup ){ + sqlite3_backup_step(pBackup, -1); + p->errCode = sqlite3_backup_finish(pBackup); + }else{ + recoverDbError(p, db); + } + } + + sqlite3_close(db2); + } +} + /* ** This function is a no-op if recover handle p already contains an error ** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in @@ -833,19 +947,8 @@ static int recoverOpenOutput(sqlite3_recover *p){ assert( p->dbOut==0 ); - if( p->errCode==SQLITE_OK ){ - if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){ - recoverDbError(p, db); - }else{ - char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb); - recoverExec(p, db, zSql); - recoverExec(p, db, - "PRAGMA writable_schema = 1;" - "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);" - "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" - ); - sqlite3_free(zSql); - } + if( sqlite3_open_v2(p->zUri, &db, flags, 0) ){ + recoverDbError(p, db); } /* Register the sqlite_dbdata and sqlite_dbptr virtual table modules. @@ -863,62 +966,22 @@ static int recoverOpenOutput(sqlite3_recover *p){ ); } - /* Truncate the output database to 0 pages in size. This is done by - ** opening a new, empty, temp db, then using the backup API to clobber - ** any existing output db with a copy of it. */ - if( p->errCode==SQLITE_OK ){ - sqlite3 *db2 = 0; - int rc = sqlite3_open("", &db2); - if( rc==SQLITE_OK ){ - sqlite3_backup *pBackup = sqlite3_backup_init(db, "main", db2, "main"); - if( pBackup ){ - sqlite3_backup_step(pBackup, -1); - p->errCode = sqlite3_backup_finish(pBackup); - }else{ - recoverDbError(p, db); - } - }else{ - recoverDbError(p, db2); - } - sqlite3_close(db2); - } - p->dbOut = db; return p->errCode; } -/* -** This function is a no-op if recover handle p already contains an error -** (if p->errCode!=SQLITE_OK). A copy of the error code is returned in -** this case. -** -** Otherwise, attempt to populate temporary table "recovery.schema" with the -** parts of the database schema that can be extracted from the input database. -** -** If no error occurs, SQLITE_OK is returned. Otherwise, an error code -** and error message are left in the recover handle and a copy of the -** error code returned. It is not considered an error if part of all of -** the database schema cannot be recovered due to corruption. -*/ -static int recoverCacheSchema(sqlite3_recover *p){ - return recoverExec(p, p->dbOut, - "WITH RECURSIVE pages(p) AS (" - " SELECT 1" - " UNION" - " SELECT child FROM sqlite_dbptr('getpage()'), pages WHERE pgno=p" - ")" - "INSERT INTO recovery.schema SELECT" - " max(CASE WHEN field=0 THEN value ELSE NULL END)," - " max(CASE WHEN field=1 THEN value ELSE NULL END)," - " max(CASE WHEN field=2 THEN value ELSE NULL END)," - " max(CASE WHEN field=3 THEN value ELSE NULL END)," - " max(CASE WHEN field=4 THEN value ELSE NULL END)" - "FROM sqlite_dbdata('getpage()') WHERE pgno IN (" - " SELECT p FROM pages" - ") GROUP BY pgno, cell" +static void recoverOpenRecovery(sqlite3_recover *p){ + char *zSql = recoverMPrintf(p, "ATTACH %Q AS recovery;", p->zStateDb); + recoverExec(p, p->dbOut, zSql); + recoverExec(p, p->dbOut, + "PRAGMA writable_schema = 1;" + "CREATE TABLE recovery.map(pgno INTEGER PRIMARY KEY, parent INT);" + "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);" ); + sqlite3_free(zSql); } + /* ** This function is a no-op if recover handle p already contains an error ** (if p->errCode!=SQLITE_OK). @@ -1022,24 +1085,6 @@ static void recoverAddTable( } } -/* -** If this recover handle is not in SQL callback mode (i.e. was not created -** using sqlite3_recover_init_sql()) of if an error has already occurred, -** this function is a no-op. Otherwise, issue a callback with SQL statement -** zSql as the parameter. -** -** If the callback returns non-zero, set the recover handle error code to -** the value returned (so that the caller will abandon processing). -*/ -static void recoverSqlCallback(sqlite3_recover *p, const char *zSql){ - if( p->errCode==SQLITE_OK && p->xSql ){ - int res = p->xSql(p->pSqlCtx, zSql); - if( res ){ - recoverError(p, SQLITE_ERROR, "callback returned an error - %d", res); - } - } -} - /* ** This function is called after recoverCacheSchema() has cached those parts ** of the input database schema that could be recovered in temporary table @@ -2057,12 +2102,16 @@ static int recoverVfsDetectPagesize(sqlite3_file *pFd, i64 nSz, u32 *pPgsz){ int iBlk = 0; u8 *aPg = 0; u8 *aTmp = 0; + int nBlk = 0; aPg = (u8*)sqlite3_malloc(2*nMax); if( aPg==0 ) return SQLITE_NOMEM; aTmp = &aPg[nMax]; - for(iBlk=0; rc==SQLITE_OK && iBlk<((nSz+nMax-1)/nMax); iBlk++){ + nBlk = (nSz+nMax-1)/nMax; + if( nBlk>nMaxBlk ) nBlk = nMaxBlk; + + for(iBlk=0; rc==SQLITE_OK && iBlk=((iBlk+1)*nMax)) ? nMax : (nSz % nMax); memset(aPg, 0, nMax); rc = pFd->pMethods->xRead(pFd, aPg, nByte, iBlk*nMax); @@ -2313,24 +2362,27 @@ static void recoverStep(sqlite3_recover *p){ recoverSqlCallback(p, "BEGIN"); recoverSqlCallback(p, "PRAGMA writable_schema = on"); + sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP2) ); + recoverInstallWrapper(p); + /* Open the output database. And register required virtual tables and ** user functions with the new handle. */ recoverOpenOutput(p); - sqlite3_mutex_enter( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP2) ); - recoverInstallWrapper(p); - /* Open transactions on both the input and output databases. */ recoverExec(p, p->dbIn, "PRAGMA writable_schema = on"); recoverExec(p, p->dbIn, "BEGIN"); if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; - recoverExec(p, p->dbOut, "BEGIN"); - + recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); + recoverTransferSettings(p); + recoverOpenRecovery(p); recoverCacheSchema(p); recoverUninstallWrapper(p); sqlite3_mutex_leave( sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_APP2) ); + recoverExec(p, p->dbOut, "BEGIN"); + recoverWriteSchema1(p); p->eState = RECOVER_STATE_WRITING; break; diff --git a/manifest b/manifest index 4d01f35c31..bb0cc53623 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\scode\sto\sdetermine\sthe\sdatabase\spage-size\sby\ssearching\sfor\swell-formed\spages. -D 2022-10-06T17:20:12.045 +C Ensure\sthe\spage-size,\sauto-vacuum\sand\sother\ssettings\sare\scopied\sinto\sthe\srecovered\sdatabase. +D 2022-10-06T21:00:23.727 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -386,7 +386,7 @@ F ext/rbu/rbuvacuum4.test a78898e438a44803eb2bc897ba3323373c9f277418e2d6d76e90f2 F ext/rbu/sqlite3rbu.c 8737cabdfbee84bb25a7851ecef8b1312be332761238da9be6ddb10c62ad4291 F ext/rbu/sqlite3rbu.h 1dc88ab7bd32d0f15890ea08d23476c4198d3da3056985403991f8c9cd389812 F ext/rbu/test_rbu.c 03f6f177096a5f822d68d8e4069ad8907fe572c62ff2d19b141f59742821828a -F ext/recover/recover1.test d9844f37b2e6da021816d699424d4aaf85712f50a9a207bf5f85f8ea7b48eb9b +F ext/recover/recover1.test 6b59ef31e3c0beef8b66e112d50af160e6f7ad8df95bee10192a9e44169d03ec F ext/recover/recover_common.tcl 6679af7dffc858e345053a91c9b0a897595b4a13007aceffafca75304ccb137c F ext/recover/recoverclobber.test 294dcc894124ab4ca3a7b35766630742a3d25810fceac22220beb64f70a33a60 F ext/recover/recovercorrupt.test 69af3d68aedc2cf1c3c41dbd6afb45b3cebadb57796d513550b5fd1e2a8b3fba @@ -395,9 +395,9 @@ F ext/recover/recoverfault.test 3a0a32b9fc216592b97775d69220695b0926980c0f7424b7 F ext/recover/recoverfault2.test 321036336af23e778a87f148c4cc4407f88fbdab1fd72ddb661669be9020d36b F ext/recover/recoverold.test 46e9d99b595fac583d4c67f74d7d89c20a435c752ef6eeb3e918b599940c88e0 F ext/recover/recoverrowid.test 1694a1a5526d825f71279f3d02ab02a1ee4c5265de18858bf54cb8ec54487ac8 -F ext/recover/recoverslowidx.test 7e3889e50758c614b9b3e75187eeeea425c0ca8a2387441fc19ae749a96a7949 +F ext/recover/recoverslowidx.test f356bb9fba7ffd6fc50e045e419464f0129ac6e24decf6e919584f79c3493727 F ext/recover/recoversql.test f9872ff2114e13ffd8ee31e1de06919f62b9b48bc080191b5bd076d10becb60f -F ext/recover/sqlite3recover.c 1b68d2593b83a130cea794c8921ad7af564cfafd848ccc80e4b9a0e893942c51 +F ext/recover/sqlite3recover.c dcd00bbb95150b3d2a1ae75abd194fa863e4ed86971a3589da226c716fb5a6c0 F ext/recover/sqlite3recover.h f698ccc94bd4da38761035415ad08c4549a408491ff9fd5f52d34d2214f64e36 F ext/recover/test_recover.c 61ec931e47abca6b2210f46239cafd9f3060741605e3d3c45a7c7a53f63dd957 F ext/repair/README.md 92f5e8aae749a4dae14f02eea8e1bb42d4db2b6ce5e83dbcdd6b1446997e0c15 @@ -2014,8 +2014,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 2be0dba12fb9b98cd9632f1ff3f30aeb29661a2e121b68c2f3335617b027797b -R 428f4add1572ea8a473b960c2888f37e +P 0dbd0ccef532e076fa82c9fad4dc9fe632c0ef43e3483cc288cbb7eca336a9b9 +R c47f69833d79cdde413f6662a615cd06 U dan -Z 0bbefdeff86a57418ac328248f517762 +Z b397112aade8ffe2182cfeaa2f331f80 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 3fa25f41b4..5806dd4688 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0dbd0ccef532e076fa82c9fad4dc9fe632c0ef43e3483cc288cbb7eca336a9b9 \ No newline at end of file +078520f2825bcb82d01b30209409e9bf8b6c99084c192345df41c9f531d08f51 \ No newline at end of file