1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-07-30 19:03:16 +03:00

Add the --preserve-rowids option to the ".dump" command in the CLI.

FossilOrigin-Name: c60aee24714a47ce12ee2a4dcefb9f55211d3761
This commit is contained in:
drh
2017-03-08 11:44:00 +00:00
parent 5cc95ebf37
commit e611f14471
4 changed files with 295 additions and 123 deletions

View File

@ -1,5 +1,5 @@
C Add\stest\sscript\sext/rbu/rbu_round_trip.tcl.\sUses\s"dbselftest"\sto\stest\sthat\n"rbu"\sand\s"sqldiff"\swork\stogether. C Add\sthe\s--preserve-rowids\soption\sto\sthe\s".dump"\scommand\sin\sthe\sCLI.
D 2017-03-07T20:03:25.030 D 2017-03-08T11:44:00.069
F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918 F Makefile.in edb6bcdd37748d2b1c3422ff727c748df7ffe918
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc a89ea37ab5928026001569f056973b9059492fe2 F Makefile.msc a89ea37ab5928026001569f056973b9059492fe2
@ -399,7 +399,7 @@ F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
F src/resolve.c 3e518b962d932a997fae373366880fc028c75706 F src/resolve.c 3e518b962d932a997fae373366880fc028c75706
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
F src/select.c d12f3539f80db38b09015561b569e0eb1c4b6c5f F src/select.c d12f3539f80db38b09015561b569e0eb1c4b6c5f
F src/shell.c 27d2b31099fd2cd749e44d025ef9b54ca26692cb F src/shell.c c42c3031f715712a0cd47d8f08bd2d1dfec8baa0
F src/sqlite.h.in 4d0c08f8640c586564a7032b259c5f69bf397850 F src/sqlite.h.in 4d0c08f8640c586564a7032b259c5f69bf397850
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
@ -1110,7 +1110,7 @@ F test/sharedA.test 0cdf1a76dfa00e6beee66af5b534b1e8df2720f5
F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e F test/sharedB.test 16cc7178e20965d75278f410943109b77b2e645e
F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506 F test/shared_err.test 2f2aee20db294b9924e81f6ccbe60f19e21e8506
F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304 F test/sharedlock.test 5ede3c37439067c43b0198f580fd374ebf15d304
F test/shell1.test 52ac23a345772ab0d6d3241a21a633fdaa3ed581 F test/shell1.test 5df739ee2fe5c9dbbb2f9a0158b9723bda0910b8
F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b F test/shell2.test e242a9912f44f4c23c3d1d802a83e934e84c853b
F test/shell3.test 9b95ba643eaa228376f06a898fb410ee9b6e57c1 F test/shell3.test 9b95ba643eaa228376f06a898fb410ee9b6e57c1
F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d F test/shell4.test 89ad573879a745974ff2df20ff97c5d6ffffbd5d
@ -1563,7 +1563,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 2cb71583d631cd417acbeebbb4ee950573a9deef P 961e79da73b4550b3e5b0f9a617133a76485db67
R ee3baff0fd57ede16b35b4777ef32cf9 R c754df2de2ef21ae6643ebeaf45e8d82
U dan U drh
Z 9c10ed94bada1b1224e7c4aff135f1d6 Z bce538a1e35be638681ea14d388ad037

View File

@ -1 +1 @@
961e79da73b4550b3e5b0f9a617133a76485db67 c60aee24714a47ce12ee2a4dcefb9f55211d3761

View File

@ -455,28 +455,6 @@ static int isNumber(const char *z, int *realnum){
return *z==0; return *z==0;
} }
/*
** A global char* and an SQL function to access its current value
** from within an SQL statement. This program used to use the
** sqlite_exec_printf() API to substitue a string into an SQL statement.
** The correct way to do this with sqlite3 is to use the bind API, but
** since the shell is built around the callback paradigm it would be a lot
** of work. Instead just use this hack, which is quite harmless.
*/
static const char *zShellStatic = 0;
static void shellstaticFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
assert( 0==argc );
assert( zShellStatic );
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
sqlite3_result_text(context, zShellStatic, -1, SQLITE_STATIC);
}
/* /*
** Compute a string length that is limited to what can be stored in ** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer. ** lower 30 bits of a 32-bit signed integer.
@ -618,6 +596,7 @@ struct ShellState {
int countChanges; /* True to display change counts */ int countChanges; /* True to display change counts */
int backslashOn; /* Resolve C-style \x escapes in SQL input text */ int backslashOn; /* Resolve C-style \x escapes in SQL input text */
int outCount; /* Revert to stdout when reaching zero */ int outCount; /* Revert to stdout when reaching zero */
int preserveRowid; /* Preserver ROWID values on a ".dump" command */
int cnt; /* Number of records displayed so far */ int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */ FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */ FILE *traceOut; /* Output for sqlite3_trace() */
@ -1333,6 +1312,16 @@ static void set_table_name(ShellState *p, const char *zName){
z[n] = 0; z[n] = 0;
} }
/*
** A variable length string to which one can append text.
*/
typedef struct ShellString ShellString;
struct ShellString {
char *z;
int n;
int nAlloc;
};
/* zIn is either a pointer to a NULL-terminated string in memory obtained /* zIn is either a pointer to a NULL-terminated string in memory obtained
** from malloc(), or a NULL pointer. The string pointed to by zAppend is ** from malloc(), or a NULL pointer. The string pointed to by zAppend is
** added to zIn, and the result returned in memory obtained from malloc(). ** added to zIn, and the result returned in memory obtained from malloc().
@ -1341,13 +1330,12 @@ static void set_table_name(ShellState *p, const char *zName){
** If the third argument, quote, is not '\0', then it is used as a ** If the third argument, quote, is not '\0', then it is used as a
** quote character for zAppend. ** quote character for zAppend.
*/ */
static char *appendText(char *zIn, char const *zAppend, char quote){ static void appendText(ShellString *p, char const *zAppend, char quote){
int len; int len;
int i; int i;
int nAppend = strlen30(zAppend); int nAppend = strlen30(zAppend);
int nIn = (zIn?strlen30(zIn):0);
len = nAppend+nIn+1; len = nAppend+p->n+1;
if( quote ){ if( quote ){
len += 2; len += 2;
for(i=0; i<nAppend; i++){ for(i=0; i<nAppend; i++){
@ -1355,27 +1343,30 @@ static char *appendText(char *zIn, char const *zAppend, char quote){
} }
} }
zIn = (char *)realloc(zIn, len); if( p->n+len>=p->nAlloc ){
if( !zIn ){ p->nAlloc = p->nAlloc*2 + len + 20;
return 0; p->z = realloc(p->z, p->nAlloc);
if( p->z==0 ){
memset(p, 0, sizeof(*p));
return;
}
} }
if( quote ){ if( quote ){
char *zCsr = &zIn[nIn]; char *zCsr = p->z+p->n;
*zCsr++ = quote; *zCsr++ = quote;
for(i=0; i<nAppend; i++){ for(i=0; i<nAppend; i++){
*zCsr++ = zAppend[i]; *zCsr++ = zAppend[i];
if( zAppend[i]==quote ) *zCsr++ = quote; if( zAppend[i]==quote ) *zCsr++ = quote;
} }
*zCsr++ = quote; *zCsr++ = quote;
*zCsr++ = '\0'; p->n = (int)(zCsr - p->z);
assert( (zCsr-zIn)==len ); *zCsr = '\0';
}else{ }else{
memcpy(&zIn[nIn], zAppend, nAppend); memcpy(p->z+p->n, zAppend, nAppend);
zIn[len-1] = '\0'; p->n += nAppend;
p->z[p->n] = '\0';
} }
return zIn;
} }
@ -2024,6 +2015,124 @@ static int shell_exec(
return rc; return rc;
} }
/*
** Release memory previously allocated by tableColumnList().
*/
static void freeColumnList(char **azCol){
int i;
for(i=1; azCol[i]; i++){
sqlite3_free(azCol[i]);
}
/* azCol[0] is a static string */
sqlite3_free(azCol);
}
/*
** Return a list of pointers to strings which are the names of all
** columns in table zTab. The memory to hold the names is dynamically
** allocated and must be released by the caller using a subsequent call
** to freeColumnList().
**
** The azCol[0] entry is usually NULL. However, if zTab contains a rowid
** value that needs to be preserved, then azCol[0] is filled in with the
** name of the rowid column.
**
** The first regular column in the table is azCol[1]. The list is terminated
** by an entry with azCol[i]==0.
*/
static char **tableColumnList(ShellState *p, const char *zTab){
char **azCol = 0;
sqlite3_stmt *pStmt;
char *zSql;
int nCol = 0;
int nAlloc = 0;
int nPK = 0; /* Number of PRIMARY KEY columns seen */
int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
int preserveRowid = p->preserveRowid;
int rc;
zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ) return 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
azCol = sqlite3_realloc(azCol, nAlloc*sizeof(azCol[0]));
if( azCol==0 ){
raw_printf(stderr, "Error: out of memory\n");
exit(1);
}
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
if( sqlite3_column_int(pStmt, 5) ){
nPK++;
if( nPK==1
&& sqlite3_stricmp((const char*)sqlite3_column_text(pStmt,2),
"INTEGER")==0
){
isIPK = 1;
}else{
isIPK = 0;
}
}
}
sqlite3_finalize(pStmt);
azCol[0] = 0;
azCol[nCol+1] = 0;
/* The decision of whether or not a rowid really needs to be preserved
** is tricky. We never need to preserve a rowid for a WITHOUT ROWID table
** or a table with an INTEGER PRIMARY KEY. We are unable to preserve
** rowids on tables where the rowid is inaccessible because there are other
** columns in the table named "rowid", "_rowid_", and "oid".
*/
if( preserveRowid && isIPK ){
/* If a single PRIMARY KEY column with type INTEGER was seen, then it
** might be an alise for the ROWID. But it might also be a WITHOUT ROWID
** table or a INTEGER PRIMARY KEY DESC column, neither of which are
** ROWID aliases. To distinguish these cases, check to see if
** there is a "pk" entry in "PRAGMA index_list". There will be
** no "pk" index if the PRIMARY KEY really is an alias for the ROWID.
*/
zSql = sqlite3_mprintf("SELECT 1 FROM pragma_index_list(%Q)"
" WHERE origin='pk'", zTab);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ){
freeColumnList(azCol);
return 0;
}
rc = sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
preserveRowid = rc==SQLITE_ROW;
}
if( preserveRowid ){
/* Only preserve the rowid if we can find a name to use for the
** rowid */
static char *azRowid[] = { "rowid", "_rowid_", "oid" };
int i, j;
for(j=0; j<3; j++){
for(i=1; i<=nCol; i++){
if( sqlite3_stricmp(azRowid[j],azCol[i])==0 ) break;
}
if( i>nCol ){
/* At this point, we know that azRowid[j] is not the name of any
** ordinary column in the table. Verify that azRowid[j] is a valid
** name for the rowid before adding it to azCol[0]. WITHOUT ROWID
** tables will fail this last check */
int rc;
rc = sqlite3_table_column_metadata(p->db,0,zTab,azRowid[j],0,0,0,0,0);
if( rc==SQLITE_OK ) azCol[0] = azRowid[j];
break;
}
}
}
return azCol;
}
/* /*
** This is a different callback routine used for dumping the database. ** This is a different callback routine used for dumping the database.
@ -2036,7 +2145,6 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
const char *zTable; const char *zTable;
const char *zType; const char *zType;
const char *zSql; const char *zSql;
const char *zPrepStmt = 0;
ShellState *p = (ShellState *)pArg; ShellState *p = (ShellState *)pArg;
UNUSED_PARAMETER(azCol); UNUSED_PARAMETER(azCol);
@ -2046,7 +2154,7 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
zSql = azArg[2]; zSql = azArg[2];
if( strcmp(zTable, "sqlite_sequence")==0 ){ if( strcmp(zTable, "sqlite_sequence")==0 ){
zPrepStmt = "DELETE FROM sqlite_sequence;\n"; raw_printf(p->out, "DELETE FROM sqlite_sequence;\n");
}else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){ }else if( sqlite3_strglob("sqlite_stat?", zTable)==0 ){
raw_printf(p->out, "ANALYZE sqlite_master;\n"); raw_printf(p->out, "ANALYZE sqlite_master;\n");
}else if( strncmp(zTable, "sqlite_", 7)==0 ){ }else if( strncmp(zTable, "sqlite_", 7)==0 ){
@ -2069,58 +2177,64 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
} }
if( strcmp(zType, "table")==0 ){ if( strcmp(zType, "table")==0 ){
sqlite3_stmt *pTableInfo = 0; ShellString sSelect;
char *zSelect = 0; ShellString sTable;
char *zTableInfo = 0; char **azCol;
char *zTmp = 0; int i;
int nRow = 0; char *savedDestTable;
int savedMode;
zTableInfo = appendText(zTableInfo, "PRAGMA table_info(", 0); azCol = tableColumnList(p, zTable);
zTableInfo = appendText(zTableInfo, zTable, '"'); if( azCol==0 ){
zTableInfo = appendText(zTableInfo, ");", 0); p->nErr++;
return 0;
rc = sqlite3_prepare_v2(p->db, zTableInfo, -1, &pTableInfo, 0);
free(zTableInfo);
if( rc!=SQLITE_OK || !pTableInfo ){
return 1;
} }
zSelect = appendText(zSelect, "SELECT 'INSERT INTO ' || ", 0);
/* Always quote the table name, even if it appears to be pure ascii, /* Always quote the table name, even if it appears to be pure ascii,
** in case it is a keyword. Ex: INSERT INTO "table" ... */ ** in case it is a keyword. Ex: INSERT INTO "table" ... */
zTmp = appendText(zTmp, zTable, '"'); memset(&sTable, 0, sizeof(sTable));
if( zTmp ){ appendText(&sTable, zTable, '"');
zSelect = appendText(zSelect, zTmp, '\''); /* If preserving the rowid, add a column list after the table name.
free(zTmp); ** In other words: "INSERT INTO tab(rowid,a,b,c,...) VALUES(...)"
} ** instead of the usual "INSERT INTO tab VALUES(...)".
zSelect = appendText(zSelect, " || ' VALUES(' || ", 0); */
rc = sqlite3_step(pTableInfo); if( azCol[0] ){
while( rc==SQLITE_ROW ){ appendText(&sTable, "(", 0);
const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1); appendText(&sTable, azCol[0], 0);
zSelect = appendText(zSelect, "quote(", 0); for(i=1; azCol[i]; i++){
zSelect = appendText(zSelect, zText, '"'); appendText(&sTable, ",", 0);
rc = sqlite3_step(pTableInfo); appendText(&sTable, azCol[i], '"');
if( rc==SQLITE_ROW ){
zSelect = appendText(zSelect, "), ", 0);
}else{
zSelect = appendText(zSelect, ") ", 0);
} }
nRow++; appendText(&sTable, ")", 0);
} }
rc = sqlite3_finalize(pTableInfo);
if( rc!=SQLITE_OK || nRow==0 ){
free(zSelect);
return 1;
}
zSelect = appendText(zSelect, "|| ')' FROM ", 0);
zSelect = appendText(zSelect, zTable, '"');
rc = run_table_dump_query(p, zSelect, zPrepStmt); /* Build an appropriate SELECT statement */
if( rc==SQLITE_CORRUPT ){ memset(&sSelect, 0, sizeof(sSelect));
zSelect = appendText(zSelect, " ORDER BY rowid DESC", 0); appendText(&sSelect, "SELECT ", 0);
run_table_dump_query(p, zSelect, 0); if( azCol[0] ){
appendText(&sSelect, azCol[0], 0);
appendText(&sSelect, ",", 0);
} }
free(zSelect); for(i=1; azCol[i]; i++){
appendText(&sSelect, azCol[i], 0);
if( azCol[i+1] ){
appendText(&sSelect, ",", 0);
}
}
freeColumnList(azCol);
appendText(&sSelect, " FROM ", 0);
appendText(&sSelect, zTable, '"');
savedDestTable = p->zDestTable;
savedMode = p->mode;
p->zDestTable = sTable.z;
p->mode = p->cMode = MODE_Insert;
rc = shell_exec(p->db, sSelect.z, shell_callback, p, 0);
p->zDestTable = savedDestTable;
p->mode = savedMode;
free(sTable.z);
free(sSelect.z);
if( rc ) p->nErr++;
} }
return 0; return 0;
} }
@ -2425,10 +2539,6 @@ static void open_db(ShellState *p, int keepAlive){
sqlite3_initialize(); sqlite3_initialize();
sqlite3_open(p->zDbFilename, &p->db); sqlite3_open(p->zDbFilename, &p->db);
globalDb = p->db; globalDb = p->db;
if( p->db && sqlite3_errcode(p->db)==SQLITE_OK ){
sqlite3_create_function(p->db, "shellstatic", 0, SQLITE_UTF8, 0,
shellstaticFunc, 0, 0);
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){ if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n", utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(p->db)); p->zDbFilename, sqlite3_errmsg(p->db));
@ -3722,21 +3832,42 @@ static int do_meta_command(char *zLine, ShellState *p){
}else }else
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){ if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
const char *zLike = 0;
int i;
p->preserveRowid = 0;
for(i=1; i<nArg; i++){
if( azArg[i][0]=='-' ){
const char *z = azArg[i]+1;
if( z[0]=='-' ) z++;
if( strcmp(z,"preserve-rowids")==0 ){
p->preserveRowid = 1;
}else
{
raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
rc = 1;
goto meta_command_exit;
}
}else if( zLike ){
raw_printf(stderr, "Usage: .dump ?--preserve-rowids? ?LIKE-PATTERN?\n");
rc = 1;
goto meta_command_exit;
}else{
zLike = azArg[i];
}
}
open_db(p, 0); open_db(p, 0);
/* When playing back a "dump", the content might appear in an order /* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated. ** which causes immediate foreign key constraints to be violated.
** So disable foreign-key constraint enforcement to prevent problems. */ ** So disable foreign-key constraint enforcement to prevent problems. */
if( nArg!=1 && nArg!=2 ){
raw_printf(stderr, "Usage: .dump ?LIKE-PATTERN?\n");
rc = 1;
goto meta_command_exit;
}
raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n"); raw_printf(p->out, "PRAGMA foreign_keys=OFF;\n");
raw_printf(p->out, "BEGIN TRANSACTION;\n"); raw_printf(p->out, "BEGIN TRANSACTION;\n");
p->writableSchema = 0; p->writableSchema = 0;
/* Set writable_schema=ON since doing so forces SQLite to initialize
** as much of the schema as it can even if the sqlite_master table is
** corrupt. */
sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0); sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
p->nErr = 0; p->nErr = 0;
if( nArg==1 ){ if( zLike==0 ){
run_schema_dump_query(p, run_schema_dump_query(p,
"SELECT name, type, sql FROM sqlite_master " "SELECT name, type, sql FROM sqlite_master "
"WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'" "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
@ -3750,21 +3881,20 @@ static int do_meta_command(char *zLine, ShellState *p){
"WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0 "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
); );
}else{ }else{
int i; char *zSql;
for(i=1; i<nArg; i++){ zSql = sqlite3_mprintf(
zShellStatic = azArg[i]; "SELECT name, type, sql FROM sqlite_master "
run_schema_dump_query(p, "WHERE tbl_name LIKE %Q AND type=='table'"
"SELECT name, type, sql FROM sqlite_master " " AND sql NOT NULL", zLike);
"WHERE tbl_name LIKE shellstatic() AND type=='table'" run_schema_dump_query(p,zSql);
" AND sql NOT NULL"); sqlite3_free(zSql);
run_table_dump_query(p, zSql = sqlite3_mprintf(
"SELECT sql FROM sqlite_master " "SELECT sql FROM sqlite_master "
"WHERE sql NOT NULL" "WHERE sql NOT NULL"
" AND type IN ('index','trigger','view')" " AND type IN ('index','trigger','view')"
" AND tbl_name LIKE shellstatic()", 0 " AND tbl_name LIKE %Q", zLike);
); run_table_dump_query(p, zSql, 0);
zShellStatic = 0; sqlite3_free(zSql);
}
} }
if( p->writableSchema ){ if( p->writableSchema ){
raw_printf(p->out, "PRAGMA writable_schema=OFF;\n"); raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
@ -4574,17 +4704,17 @@ static int do_meta_command(char *zLine, ShellState *p){
callback(&data, 1, new_argv, new_colv); callback(&data, 1, new_argv, new_colv);
rc = SQLITE_OK; rc = SQLITE_OK;
}else{ }else{
zShellStatic = azArg[1]; char *zSql;
rc = sqlite3_exec(p->db, zSql = sqlite3_mprintf(
"SELECT sql FROM " "SELECT sql FROM "
" (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x"
" FROM sqlite_master UNION ALL" " FROM sqlite_master UNION ALL"
" SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) "
"WHERE lower(tbl_name) LIKE shellstatic()" "WHERE lower(tbl_name) LIKE %Q"
" AND type!='meta' AND sql NOTNULL " " AND type!='meta' AND sql NOTNULL "
"ORDER BY rowid", "ORDER BY rowid", azArg[1]);
callback, &data, &zErrMsg); rc = sqlite3_exec(p->db, zSql, callback, &data, &zErrMsg);
zShellStatic = 0; sqlite3_free(zSql);
} }
}else if( nArg==1 ){ }else if( nArg==1 ){
rc = sqlite3_exec(p->db, rc = sqlite3_exec(p->db,

View File

@ -300,7 +300,7 @@ do_test shell1-3.4.2 {
do_test shell1-3.4.3 { do_test shell1-3.4.3 {
# too many arguments # too many arguments
catchcmd "test.db" ".dump FOO BAD" catchcmd "test.db" ".dump FOO BAD"
} {1 {Usage: .dump ?LIKE-PATTERN?}} } {1 {Usage: .dump ?--preserve-rowids? ?LIKE-PATTERN?}}
# .echo ON|OFF Turn command echo on or off # .echo ON|OFF Turn command echo on or off
do_test shell1-3.5.1 { do_test shell1-3.5.1 {
@ -745,16 +745,58 @@ INSERT INTO "t1" VALUES('');
INSERT INTO "t1" VALUES(1); INSERT INTO "t1" VALUES(1);
INSERT INTO "t1" VALUES(2.25); INSERT INTO "t1" VALUES(2.25);
INSERT INTO "t1" VALUES('hello'); INSERT INTO "t1" VALUES('hello');
INSERT INTO "t1" VALUES(X'807F'); INSERT INTO "t1" VALUES(X'807f');
CREATE TABLE t3(x,y); CREATE TABLE t3(x,y);
INSERT INTO "t3" VALUES(1,NULL); INSERT INTO "t3" VALUES(1,NULL);
INSERT INTO "t3" VALUES(2,''); INSERT INTO "t3" VALUES(2,'');
INSERT INTO "t3" VALUES(3,1); INSERT INTO "t3" VALUES(3,1);
INSERT INTO "t3" VALUES(4,2.25); INSERT INTO "t3" VALUES(4,2.25);
INSERT INTO "t3" VALUES(5,'hello'); INSERT INTO "t3" VALUES(5,'hello');
INSERT INTO "t3" VALUES(6,X'807F'); INSERT INTO "t3" VALUES(6,X'807f');
COMMIT;}} COMMIT;}}
do_test shell1-4.1.1 {
catchcmd test.db {.dump --preserve-rowids}
} {0 {PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE t1(x);
INSERT INTO "t1"(rowid,"x") VALUES(1,NULL);
INSERT INTO "t1"(rowid,"x") VALUES(2,'');
INSERT INTO "t1"(rowid,"x") VALUES(3,1);
INSERT INTO "t1"(rowid,"x") VALUES(4,2.25);
INSERT INTO "t1"(rowid,"x") VALUES(5,'hello');
INSERT INTO "t1"(rowid,"x") VALUES(6,X'807f');
CREATE TABLE t3(x,y);
INSERT INTO "t3"(rowid,"x","y") VALUES(1,1,NULL);
INSERT INTO "t3"(rowid,"x","y") VALUES(2,2,'');
INSERT INTO "t3"(rowid,"x","y") VALUES(3,3,1);
INSERT INTO "t3"(rowid,"x","y") VALUES(4,4,2.25);
INSERT INTO "t3"(rowid,"x","y") VALUES(5,5,'hello');
INSERT INTO "t3"(rowid,"x","y") VALUES(6,6,X'807f');
COMMIT;}}
do_test shell1-4.1.2 {
db close
forcedelete test2.db
sqlite3 db test2.db
db eval {
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
INSERT INTO t1 VALUES(1,null), (2,''), (3,1),
(4,2.25), (5,'hello'), (6,x'807f');
}
catchcmd test2.db {.dump --preserve-rowids}
} {0 {PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE t1(x INTEGER PRIMARY KEY, y);
INSERT INTO "t1" VALUES(1,NULL);
INSERT INTO "t1" VALUES(2,'');
INSERT INTO "t1" VALUES(3,1);
INSERT INTO "t1" VALUES(4,2.25);
INSERT INTO "t1" VALUES(5,'hello');
INSERT INTO "t1" VALUES(6,X'807f');
COMMIT;}}
# Test the output of ".mode insert" # Test the output of ".mode insert"
# #
do_test shell1-4.2.1 { do_test shell1-4.2.1 {