From: dan Date: Tue, 23 Apr 2019 20:48:32 +0000 (+0000) Subject: Have ".recover" handle "\r" and "\n" in the same way as ".dump". X-Git-Tag: version-3.29.0~176^2~11 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=b182588c8e0cdbafc5987df0a919d29021d1ba58;p=thirdparty%2Fsqlite.git Have ".recover" handle "\r" and "\n" in the same way as ".dump". FossilOrigin-Name: f95f0f02ab6c6cf45f25b613c7ab57f68249689d0a9eddf4c9518ddf0edad365 --- diff --git a/manifest b/manifest index 509212bede..e3ab13575f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fixes\sfor\sthe\s".recover"\sshell\scommand. -D 2019-04-23T18:03:02.318 +C Have\s".recover"\shandle\s"\\r"\sand\s"\\n"\sin\sthe\ssame\sway\sas\s".dump". +D 2019-04-23T20:48:32.366 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724 @@ -520,7 +520,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8 F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93 F src/select.c 9263f5c30dd44c7ac2eb29f40a7ec64322a96885b71c00de6bc30b756c2e1c49 -F src/shell.c.in 6c02cbd1de3b878abc04b08f2f74cc4d65d0a8e52dd3e78194db2a79d686b841 +F src/shell.c.in 6e56c60640410885a8c3c264971619407ae5c7f46165e2bfe5fb1ca1a6a58ad0 F src/sqlite.h.in 38390767acc1914d58930e03149595ee4710afa4e3c43ab6c3a8aea3f1a6b8cd F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5 @@ -1821,7 +1821,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P f193ca587f9e4f925f4f2343b0b07053bd6f93dd87fc6f8f41cf4479e90cf562 -R 73bcd5cc8a48367ff5a018a5ccb0a79d +P 8dcc1d89d955bf58c80a8c30a37960f0cf95719953951a92626cc332cc75ec60 +R b9c18dd1095e3f7b028bdaea88f9fcb0 U dan -Z e408dc486ac689ec1d61c890dc79708f +Z edabfc3afbc2656b0ce1a8fb6b1e492b diff --git a/manifest.uuid b/manifest.uuid index 680d92aac8..fdcc68f9a9 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8dcc1d89d955bf58c80a8c30a37960f0cf95719953951a92626cc332cc75ec60 \ No newline at end of file +f95f0f02ab6c6cf45f25b613c7ab57f68249689d0a9eddf4c9518ddf0edad365 \ No newline at end of file diff --git a/src/shell.c.in b/src/shell.c.in index 8ac87fbfa2..7989c9dfde 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -3934,6 +3934,96 @@ readHexDb_error: } #endif /* SQLITE_ENABLE_DESERIALIZE */ +/* +** Scalar function "shell_escape_crnl" used by the .recover command. +** The argument passed to this function is the output of built-in +** function quote(). If the first character of the input is "'", +** indicating that the value passed to quote() was a text value, +** then this function searches the input for "\n" and "\r" characters +** and adds a wrapper similar to the following: +** +** replace(replace(, '\n', char(10), '\r', char(13)); +** +** Or, if the first character of the input is not "'", then a copy +** of the input is returned. +*/ +static void shellEscapeCrnl( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + const char *zText = (const char*)sqlite3_value_text(argv[0]); + if( zText[0]=='\'' ){ + int nText = sqlite3_value_bytes(argv[0]); + int i; + char zBuf1[20]; + char zBuf2[20]; + const char *zNL = 0; + const char *zCR = 0; + int nCR = 0; + int nNL = 0; + + for(i=0; zText[i]; i++){ + if( zNL==0 && zText[i]=='\n' ){ + zNL = unused_string(zText, "\\n", "\\012", zBuf1); + nNL = (int)strlen(zNL); + } + if( zCR==0 && zText[i]=='\r' ){ + zCR = unused_string(zText, "\\r", "\\015", zBuf2); + nCR = (int)strlen(zCR); + } + } + + if( zNL || zCR ){ + int iOut = 0; + i64 nMax = (nNL > nCR) ? nNL : nCR; + i64 nAlloc = nMax * nText + (nMax+12)*2; + char *zOut = (char*)sqlite3_malloc64(nAlloc); + if( zOut==0 ){ + sqlite3_result_error_nomem(context); + return; + } + + if( zNL && zCR ){ + memcpy(&zOut[iOut], "replace(replace(", 16); + iOut += 16; + }else{ + memcpy(&zOut[iOut], "replace(", 8); + iOut += 8; + } + for(i=0; zText[i]; i++){ + if( zText[i]=='\n' ){ + memcpy(&zOut[iOut], zNL, nNL); + iOut += nNL; + }else if( zText[i]=='\r' ){ + memcpy(&zOut[iOut], zCR, nCR); + iOut += nCR; + }else{ + zOut[iOut] = zText[i]; + iOut++; + } + } + + if( zNL ){ + memcpy(&zOut[iOut], ",'", 2); iOut += 2; + memcpy(&zOut[iOut], zNL, nNL); iOut += nNL; + memcpy(&zOut[iOut], "', char(10))", 12); iOut += 12; + } + if( zCR ){ + memcpy(&zOut[iOut], ",'", 2); iOut += 2; + memcpy(&zOut[iOut], zCR, nCR); iOut += nCR; + memcpy(&zOut[iOut], "', char(13))", 12); iOut += 12; + } + + sqlite3_result_text(context, zOut, iOut, SQLITE_TRANSIENT); + sqlite3_free(zOut); + return; + } + } + + sqlite3_result_value(context, argv[0]); +} + /* Flags for open_db(). ** ** The default behavior of open_db() is to exit(1) if the database fails to @@ -4013,6 +4103,8 @@ static void open_db(ShellState *p, int openFlags){ shellModuleSchema, 0, 0); sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, shellPutsFunc, 0, 0); + sqlite3_create_function(p->db, "shell_escape_crnl", 1, SQLITE_UTF8, 0, + shellEscapeCrnl, 0, 0); #ifndef SQLITE_NOHAVE_SYSTEM sqlite3_create_function(p->db, "edit", 1, SQLITE_UTF8, 0, editFunc, 0, 0); @@ -6349,16 +6441,6 @@ static int recoverDatabaseCmd(ShellState *pState){ "GROUP BY pgno, cell;" ); -#if 0 - zSql = "SELECT type ||','|| name ||','|| tbl_name ||','|| rootpage ||','|| sql FROM recovery.schema;"; - shellPrepare(pState->db, &rc, zSql, &pLoop); - while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){ - raw_printf(pState->out, "%s\n", (const char*)sqlite3_column_text(pLoop, 0)); - } - shellFinalize(&rc, pLoop); - return rc; -#endif - /* Open a transaction, then print out all non-virtual, non-"sqlite_%" ** CREATE TABLE statements that extracted from the existing schema. */ if( rc==SQLITE_OK ){ @@ -6398,7 +6480,7 @@ static int recoverDatabaseCmd(ShellState *pState){ raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n"); } shellPreparePrintf(pState->db, &rc, &pData, - "SELECT max(field), group_concat(quote(value), ', ') " + "SELECT max(field), group_concat(shell_escape_crnl(quote(value)),', ')" "FROM sqlite_dbdata WHERE pgno IN (" " SELECT pgno FROM recovery.map WHERE root=%d" ")" @@ -6423,7 +6505,7 @@ static int recoverDatabaseCmd(ShellState *pState){ sqlite3_stmt *pStmt = 0; shellPrepare(pState->db, &rc, "SELECT sql, name FROM recovery.schema " - "WHERE (type='table' AND sql LIKE 'create table%') IS NOT TRUE", &pStmt + "WHERE sql NOT LIKE 'create table%'", &pStmt ); while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){ const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);