diff --git a/manifest b/manifest index 189cb0be46..4eeabb2d87 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sshellB.test\smodule\sso\sthat\sit\sdoes\snot\stry\sto\srun\sdump.sql\nwithout\svirtual\stables\savailable. -D 2026-10-20T19:21:43.489 +C Simplify\sthe\s".clone"\scommand\sso\sthat\sit\sjust\smakes\sa\scopy\sof\sthe\smain\ndatabase.\s\sMove\sthe\simplementation\sinto\sa\sseparate\sdotCmdClone()\sfunction\nand\sprovide\sbetter\shelp\stext\sexplaining\sits\shistory. +D 2026-10-20T19:30:24.831 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -808,7 +808,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c F src/resolve.c 658d5d8f67b7565cbed56394d0fdbb124561a92c7455253186189db5e090e661 F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97 F src/select.c 7b82615d355666993b777da78a6892e3e48220efe9fd24eb0bb10fc3953c7bc0 -F src/shell.c.in d64c2a5d75158d1c2d6bfc7648c00481a4c6c26d077d83328831aaf40ddd5352 +F src/shell.c.in 808c0bd4a593a3552777c059e90c9c47e459f507fa25d2afd298edf09d3140f5 F src/sqlite.h.in d73bee6ea0612260b5edf72e3e58de7bd2a50cff000e0f4b3c3549d3e4c35171 F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479 F src/sqlite3ext.h 5e113c4c0442f2f1b3345d44cbe4b82fad45f2fc0c35f0ed4eef27fb8f1cb394 @@ -1714,7 +1714,7 @@ F test/shared_err.test 32634e404a3317eeb94abc7a099c556a346fdb8fb3858dbe222a4cbb8 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/shell-prompt.sql 8a320a78812d75349bd5e1a95178f7ff1f2194bb4cceee572865d127c51684b3 F test/shell1.test 677b23fc543ec5eec5bec10b5c29e74c684c2ac286390b7179e4459ea0d54136 -F test/shell2.test eee473558d67c4a7cd9a0c974b4001ef1bf7977874111a6b5b1ef54564984173 +F test/shell2.test 598f17feda8de7c6c7cec522a6eafbbfafee6766f85229a93f71f97c78b883b6 F test/shell3.test 91efdd545097a61a1f72cf79c9ad5b49da080f3f10282eaf4c3c272cd1012db2 F test/shell4.test 931c7555d5a0dcac255561a936fcf1f54c12dd66f668e27e8b0d9ed0d4b9fe22 F test/shell5.test a0c43d82a811a463a5d07d6418c5a045ab01a072544db8aa31ae394e93845d8d @@ -2295,8 +2295,8 @@ F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee F tool/warnings.sh a554d13f6e5cf3760f041b87939e3d616ec6961859c3245e8ef701d1eafc2ca2 F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f F tool/winmain.c 00c8fb88e365c9017db14c73d3c78af62194d9644feaf60e220ab0f411f3604c -P 7ee2a48bb3765bfe8cef4ae400eacf77c660f9d5dbaa289ab5f17474fca98cae -R 9fd1483f6628090c18eb1ed219fdf80a +P 3f8b4c7760148eabbf721ff9b8321793972d372fa3156dc8c60ed680f5467d0c +R 30f8f60c48ac60a693d47e31704ae3c6 U drh -Z a38aed9d67bb138830822f46db8da54c +Z 993d09f4999c4f0c0b7b58894f899ac6 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 11705e496c..b705fe11e3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3f8b4c7760148eabbf721ff9b8321793972d372fa3156dc8c60ed680f5467d0c +8b5c87a5f4dc6dccf2130d41df958ea59f0892836b574388539ee7d905cd014d diff --git a/src/shell.c.in b/src/shell.c.in index 26e690ca79..c06649b5f2 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -5872,223 +5872,44 @@ static char *SQLITE_CDECL ascii_read_one_field(ImportCtx *p){ return p->z; } +#ifndef SQLITE_SHELL_FIDDLE /* -** Try to transfer data for table zTable. If an error is seen while -** moving forward, try to go backwards. The backwards movement won't -** work for WITHOUT ROWID tables. +** DOT-COMMAND: .clone +** +** USAGE: .clone FILE +** +** The original purpose of the ".clone" command (when it was first introduced +** in 2014) was to try to recover content for a corrupt database file and +** write the recovered content into a new database named FILE. However, +** the command has always been experimental and undocumented. Today, it +** does nothing more than make a copy of the main database into a new +** database named by its only argument, FILE. No repair attempts are +** made. The command is retained for legacy only. */ -static void tryToCloneData( - ShellState *p, - sqlite3 *newDb, - const char *zTable -){ - sqlite3_stmt *pQuery = 0; - sqlite3_stmt *pInsert = 0; - char *zQuery = 0; - char *zInsert = 0; +static int dotCmdClone(ShellState *p){ + int nArg = p->dot.nArg; /* Number of arguments */ + char **azArg = p->dot.azArg;/* Argument list */ + char *zSql; + sqlite3_stmt *pStmt = 0; int rc; - int i, j, n; - int nTable = strlen30(zTable); - int k = 0; - int cnt = 0; - const int spinRate = 10000; - zQuery = sqlite3_mprintf("SELECT * FROM \"%w\"", zTable); - shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); - if( rc ){ - cli_printf(stderr,"Error %d: %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); - goto end_data_xfer; + failIfSafeMode(p, "cannot run .clone in safe mode"); + if( nArg!=2 ){ + dotCmdError(p, nArg, 0, "missing FILE argument"); + return 1; } - n = sqlite3_column_count(pQuery); - zInsert = sqlite3_malloc64(200 + nTable + n*3); - shell_check_oom(zInsert); - sqlite3_snprintf(200+nTable,zInsert, - "INSERT OR IGNORE INTO \"%s\" VALUES(?", zTable); - i = strlen30(zInsert); - for(j=1; jdb, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc!=SQLITE_OK || SQLITE_DONE!=sqlite3_step(pStmt) ){ + cli_printf(stderr,"Error: cannot clone into \"%s\"\n", azArg[1]); + sqlite3_finalize(pStmt); + return 1; } - memcpy(zInsert+i, ");", 3); - rc = sqlite3_prepare_v2(newDb, zInsert, -1, &pInsert, 0); - if( rc ){ - cli_printf(stderr,"Error %d: %s on [%s]\n", - sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb), zInsert); - goto end_data_xfer; - } - for(k=0; k<2; k++){ - while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ - for(i=0; idb, zQuery, -1, &pQuery, 0); - if( rc ){ - cli_printf(stderr,"Warning: cannot step \"%s\" backwards", zTable); - break; - } - } /* End for(k=0...) */ - -end_data_xfer: - sqlite3_finalize(pQuery); - sqlite3_finalize(pInsert); - sqlite3_free(zQuery); - sqlite3_free(zInsert); -} - - -/* -** Try to transfer all rows of the schema that match zWhere. For -** each row, invoke xForEach() on the object defined by that row. -** If an error is encountered while moving forward through the -** sqlite_schema table, try again moving backwards. -*/ -static void tryToCloneSchema( - ShellState *p, - sqlite3 *newDb, - const char *zWhere, - void (*xForEach)(ShellState*,sqlite3*,const char*) -){ - sqlite3_stmt *pQuery = 0; - char *zQuery = 0; - int rc; - const unsigned char *zName; - const unsigned char *zSql; - char *zErrMsg = 0; - - zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" - " WHERE %s ORDER BY rowid ASC", zWhere); - shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); - if( rc ){ - cli_printf(stderr, - "Error: (%d) %s on [%s]\n", sqlite3_extended_errcode(p->db), - sqlite3_errmsg(p->db), zQuery); - goto end_schema_xfer; - } - while( (rc = sqlite3_step(pQuery))==SQLITE_ROW ){ - zName = sqlite3_column_text(pQuery, 0); - zSql = sqlite3_column_text(pQuery, 1); - if( zName==0 || zSql==0 ) continue; - if( sqlite3_stricmp((char*)zName, "sqlite_sequence")!=0 ){ - cli_printf(stdout, "%s... ", zName); fflush(stdout); - sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); - if( zErrMsg ){ - cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); - sqlite3_free(zErrMsg); - zErrMsg = 0; - } - } - if( xForEach ){ - xForEach(p, newDb, (const char*)zName); - } - sputz(stdout, "done\n"); - } - if( rc!=SQLITE_DONE ){ - sqlite3_finalize(pQuery); - sqlite3_free(zQuery); - zQuery = sqlite3_mprintf("SELECT name, sql FROM sqlite_schema" - " WHERE %s ORDER BY rowid DESC", zWhere); - shell_check_oom(zQuery); - rc = sqlite3_prepare_v2(p->db, zQuery, -1, &pQuery, 0); - if( rc ){ - cli_printf(stderr,"Error: (%d) %s on [%s]\n", - sqlite3_extended_errcode(p->db), sqlite3_errmsg(p->db), zQuery); - goto end_schema_xfer; - } - while( sqlite3_step(pQuery)==SQLITE_ROW ){ - zName = sqlite3_column_text(pQuery, 0); - zSql = sqlite3_column_text(pQuery, 1); - if( zName==0 || zSql==0 ) continue; - if( sqlite3_stricmp((char*)zName, "sqlite_sequence")==0 ) continue; - cli_printf(stdout, "%s... ", zName); fflush(stdout); - sqlite3_exec(newDb, (const char*)zSql, 0, 0, &zErrMsg); - if( zErrMsg ){ - cli_printf(stderr,"Error: %s\nSQL: [%s]\n", zErrMsg, zSql); - sqlite3_free(zErrMsg); - zErrMsg = 0; - } - if( xForEach ){ - xForEach(p, newDb, (const char*)zName); - } - sputz(stdout, "done\n"); - } - } -end_schema_xfer: - sqlite3_finalize(pQuery); - sqlite3_free(zQuery); -} - -/* -** Open a new database file named "zNewDb". Try to recover as much information -** as possible out of the main database (which might be corrupt) and write it -** into zNewDb. -*/ -static void tryToClone(ShellState *p, const char *zNewDb){ - int rc; - sqlite3 *newDb = 0; - if( access(zNewDb,0)==0 ){ - cli_printf(stderr,"File \"%s\" already exists.\n", zNewDb); - return; - } - rc = sqlite3_open(zNewDb, &newDb); - if( rc ){ - cli_printf(stderr, - "Cannot create output database: %s\n", sqlite3_errmsg(newDb)); - }else{ - sqlite3_exec(p->db, "PRAGMA writable_schema=ON;", 0, 0, 0); - sqlite3_exec(newDb, "BEGIN EXCLUSIVE;", 0, 0, 0); - tryToCloneSchema(p, newDb, "type='table'", tryToCloneData); - tryToCloneSchema(p, newDb, "type!='table'", 0); - sqlite3_exec(newDb, "COMMIT;", 0, 0, 0); - sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0); - } - close_db(newDb); + sqlite3_finalize(pStmt); + return 0; } +#endif /* !defined(SQLITE_SHELL_FIDDLE) */ #ifndef SQLITE_SHELL_FIDDLE /* @@ -10308,13 +10129,7 @@ static int do_meta_command(const char *zLine, ShellState *p){ #ifndef SQLITE_SHELL_FIDDLE if( c=='c' && cli_strncmp(azArg[0], "clone", n)==0 ){ - failIfSafeMode(p, "cannot run .clone in safe mode"); - if( nArg==2 ){ - tryToClone(p, azArg[1]); - }else{ - eputz("Usage: .clone FILENAME\n"); - rc = 1; - } + rc = dotCmdClone(p); }else #endif /* !defined(SQLITE_SHELL_FIDDLE) */ diff --git a/test/shell2.test b/test/shell2.test index 6a1534de68..a205534541 100644 --- a/test/shell2.test +++ b/test/shell2.test @@ -225,9 +225,7 @@ do_test shell2-1.4.9 { .clone clone.db .open clone.db SELECT max(seq) FROM sqlite_sequence;}]] -} {0 {t... done -done -2}} +} {0 2} ifcapable vtab { # Verify that generate_series stays sane near 64-bit range boundaries.