mirror of
https://github.com/sqlite/sqlite.git
synced 2026-10-30 01:43:10 +03:00
Simplify the ".clone" command so that it just makes a copy of the main
database. Move the implementation into a separate dotCmdClone() function and provide better help text explaining its history. FossilOrigin-Name: 8b5c87a5f4dc6dccf2130d41df958ea59f0892836b574388539ee7d905cd014d
This commit is contained in:
14
manifest
14
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.
|
||||
|
||||
@@ -1 +1 @@
|
||||
3f8b4c7760148eabbf721ff9b8321793972d372fa3156dc8c60ed680f5467d0c
|
||||
8b5c87a5f4dc6dccf2130d41df958ea59f0892836b574388539ee7d905cd014d
|
||||
|
||||
249
src/shell.c.in
249
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; j<n; j++){
|
||||
memcpy(zInsert+i, ",?", 2);
|
||||
i += 2;
|
||||
zSql = sqlite3_mprintf("VACUUM INTO %Q", azArg[1]);
|
||||
rc = sqlite3_prepare_v2(p->db, 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; i<n; i++){
|
||||
switch( sqlite3_column_type(pQuery, i) ){
|
||||
case SQLITE_NULL: {
|
||||
sqlite3_bind_null(pInsert, i+1);
|
||||
break;
|
||||
}
|
||||
case SQLITE_INTEGER: {
|
||||
sqlite3_bind_int64(pInsert, i+1, sqlite3_column_int64(pQuery,i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_FLOAT: {
|
||||
sqlite3_bind_double(pInsert, i+1, sqlite3_column_double(pQuery,i));
|
||||
break;
|
||||
}
|
||||
case SQLITE_TEXT: {
|
||||
sqlite3_bind_text(pInsert, i+1,
|
||||
(const char*)sqlite3_column_text(pQuery,i),
|
||||
-1, SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
case SQLITE_BLOB: {
|
||||
sqlite3_bind_blob(pInsert, i+1, sqlite3_column_blob(pQuery,i),
|
||||
sqlite3_column_bytes(pQuery,i),
|
||||
SQLITE_STATIC);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} /* End for */
|
||||
rc = sqlite3_step(pInsert);
|
||||
if( rc!=SQLITE_OK && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
|
||||
cli_printf(stderr,"Error %d: %s\n",
|
||||
sqlite3_extended_errcode(newDb), sqlite3_errmsg(newDb));
|
||||
}
|
||||
sqlite3_reset(pInsert);
|
||||
cnt++;
|
||||
if( (cnt%spinRate)==0 ){
|
||||
printf("%c\b", "|/-\\"[(cnt/spinRate)%4]);
|
||||
fflush(stdout);
|
||||
}
|
||||
} /* End while */
|
||||
if( rc==SQLITE_DONE ) break;
|
||||
sqlite3_finalize(pQuery);
|
||||
sqlite3_free(zQuery);
|
||||
zQuery = sqlite3_mprintf("SELECT * FROM \"%w\" ORDER BY rowid DESC;",
|
||||
zTable);
|
||||
shell_check_oom(zQuery);
|
||||
rc = sqlite3_prepare_v2(p->db, 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) */
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user