-C Minor\schange\sto\sos_unix.c\sto\sfacilitate\s100%\sMC/DC\stesting.
-D 2024-01-08T15:23:45.210
+C Automatically\sturn\soff\sDEFENSIVE\smode\sin\sthe\sshell\stool\swhen\sexecuting\sscripts\sgenerated\sby\sthe\s".dump"\scommand\sagainst\san\sempty\sdatabase.\sAdd\sa\swarning\sto\sthe\stop\sof\sgenerated\s".dump"\sscripts\sthat\spopulate\svirtual\stables.
+D 2024-01-08T18:46:34.303
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F src/resolve.c e25f51a473a5f30a0d978e4df2aaa98aeec84eac29ecae1ad4708a6c3e669345
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c f1a81ff4f8e9e76c224e2ab3a4baa799add0db22158c7fcede65d8cc4a6fa2da
-F src/shell.c.in 85f8d52fa4f7773823736dd39d0a268fd739207fcae95883c9ec8ce4af59f7df
+F src/shell.c.in 3d19abd924ed1cec9c9908d5a10cb1580b8ca30df24c26bfe80efa0c00f664d8
F src/sqlite.h.in 61a60b4ea04db8ead15e1579b20b64cb56e9f55d52c5f9f9694de630110593a3
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
F test/shell6.test 1ceb51b2678c472ba6cf1e5da96679ce8347889fe2c3bf93a0e0fa73f00b00d3
F test/shell7.test 115132f66d0463417f408562cc2cf534f6bbc6d83a6d50f0072a9eb171bae97f
F test/shell8.test 3fd093d481aaa94dc77fb73f1044c1f19c7efe3477a395cc4f7450133bc54915
+F test/shell9.test e540b457297efcd737a84505c23367ed6eb5d2f239a5e1901d89518a9f794c67
F test/shmlock.test 3dbf017d34ab0c60abe6a44e447d3552154bd0c87b41eaf5ceacd408dd13fda5
F test/shortread1.test bb591ef20f0fd9ed26d0d12e80eee6d7ac8897a3
F test/show_speedtest1_rtree.tcl 32e6c5f073d7426148a6936a0408f4b5b169aba5
F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d1631311a16
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
-F test/tester.tcl 68454ef88508c196d19e8694daa27bff7107a91857799eaa12f417188ae53ede
+F test/tester.tcl 6f6e53981b4bdd42ef088f52e23236bc1ba0ca41ed395cbd7f33cbcff7d74d3c
F test/testrunner.tcl 8e2a5c7550b78d3283eee6103104ae2bcf56aa1df892dbd1608f27b93ebf4de8
F test/testrunner_data.tcl 7ffd951527bbc614e723fd8d123b6834321878530696adecfdf6035100bac64e
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a8e9af1356f5fb2ec460f932dfbe89283bb4e3cf9fa677d1acdbe77ffa11dd04
-R 37733634dd468dc6ea6d1e6aa3b50fba
-U drh
-Z 78db706fe3fe0ce3a2f3eeab5f6bda34
+P 0dfa7b4da134db281c3c4eddb4569c53a450f955f0af2f410e13db801aff4ea2
+R 42cc9dd174017b0dfd22a357c6d362d7
+T *branch * shell-dump-fix
+T *sym-shell-dump-fix *
+T -sym-trunk *
+U dan
+Z e611ff595823ddf49b515f1d4f66f784
# Remove this line to create a well-formed Fossil manifest.
-0dfa7b4da134db281c3c4eddb4569c53a450f955f0af2f410e13db801aff4ea2
\ No newline at end of file
+6e9e96b7e7afb9420110f4b93d10b945c9eadfde5e9c81e59ae9ee8167e75707
\ No newline at end of file
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
u8 bSafeMode; /* True to prohibit unsafe operations */
u8 bSafeModePersist; /* The long-term value of bSafeMode */
+ u8 eRestoreState; /* See comments above doAutoDetectRestore() */
ColModeOpts cmOpts; /* Option values affecting columnar mode output */
unsigned statsOn; /* True to display memory stats before each finalize */
unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */
return SQLITE_ERROR;
}
-#if !defined SQLITE_OMIT_VIRTUALTABLE
static void shellPrepare(
sqlite3 *db,
int *pRc,
/*
** Create a prepared statement using printf-style arguments for the SQL.
-**
-** This routine is could be marked "static". But it is not always used,
-** depending on compile-time options. By omitting the "static", we avoid
-** nuisance compiler warnings about "defined but not used".
*/
-void shellPreparePrintf(
+static void shellPreparePrintf(
sqlite3 *db,
int *pRc,
sqlite3_stmt **ppStmt,
}
}
-/* Finalize the prepared statement created using shellPreparePrintf().
-**
-** This routine is could be marked "static". But it is not always used,
-** depending on compile-time options. By omitting the "static", we avoid
-** nuisance compiler warnings about "defined but not used".
+/*
+** Finalize the prepared statement created using shellPreparePrintf().
*/
-void shellFinalize(
+static void shellFinalize(
int *pRc,
sqlite3_stmt *pStmt
){
}
}
+#if !defined SQLITE_OMIT_VIRTUALTABLE
/* Reset the prepared statement created using shellPreparePrintf().
**
** This routine is could be marked "static". But it is not always used,
}
}
+/*
+** Check if the sqlite_schema table contains one or more virtual tables. If
+** parameter zLike is not NULL, then it is an SQL expression that the
+** sqlite_schema row must also match. If one or more such rows are found,
+** print the following warning to the output:
+**
+** WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled
+*/
+static int outputDumpWarning(ShellState *p, const char *zLike){
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pStmt = 0;
+ shellPreparePrintf(p->db, &rc, &pStmt,
+ "SELECT 1 FROM sqlite_schema o WHERE "
+ "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
+ );
+ if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
+ oputz("/* WARNING: "
+ "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n"
+ );
+ }
+ shellFinalize(&rc, pStmt);
+ return rc;
+}
+
/*
** If an input line begins with "." then invoke this routine to
** process that line.
open_db(p, 0);
+ outputDumpWarning(p, zLike);
if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
/* When playing back a "dump", the content might appear in an order
** which causes immediate foreign key constraints to be violated.
return rc;
}
+/*
+** This function is called after processing each line of SQL in the
+** runOneSqlLine() function. Its purpose is to detect scenarios where
+** defensive mode should be automatically turned off. Specifically, when
+**
+** 1. The first line of input is "PRAGMA foreign_keys=OFF;",
+** 2. The second line of input is "BEGIN TRANSACTION;",
+** 3. The database is empty, and
+** 4. The shell is not running in --safe mode.
+**
+** The implementation uses the ShellState.eRestoreState to maintain state:
+**
+** 0: Have not seen any SQL.
+** 1: Have seen "PRAGMA foreign_keys=OFF;".
+** 2: Currently assuming we are parsing ".dump" restore, defensive mode
+** should be disabled following the current transaction.
+** 3: Nothing left to do.
+*/
+static int doAutoDetectRestore(ShellState *p, const char *zSql){
+ int rc = SQLITE_OK;
+
+ switch( p->eRestoreState ){
+ case 0: {
+ int bDefense = 0; /* True if in defensive mode */
+ const char *zExpect = "PRAGMA foreign_keys=OFF;";
+ assert( strlen(zExpect)==24 );
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense);
+ if( p->bSafeMode==0 && bDefense && memcmp(zSql, zExpect, 25)==0 ){
+ p->eRestoreState = 1;
+ }else{
+ p->eRestoreState = 3;
+ }
+ break;
+ };
+
+ case 1: {
+ const char *zExpect = "BEGIN TRANSACTION;";
+ assert( strlen(zExpect)==18 );
+ if( memcmp(zSql, zExpect, 19)==0 ){
+ /* Now check if the database is empty. */
+ const char *zQuery = "SELECT 1 FROM sqlite_schema LIMIT 1";
+ sqlite3_stmt *pStmt = 0;
+ int bEmpty = 1;
+
+ shellPrepare(p->db, &rc, zQuery, &pStmt);
+ if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
+ bEmpty = 0;
+ }
+ shellFinalize(&rc, pStmt);
+ if( bEmpty && rc==SQLITE_OK ){
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
+ }else{
+ p->eRestoreState = 3;
+ }
+ }
+ break;
+ }
+
+ case 2: {
+ if( sqlite3_get_autocommit(p->db) ){
+ sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
+ p->eRestoreState = 3;
+ }
+ break;
+ }
+
+ default: /* Nothing to do */
+ assert( p->eRestoreState==3 );
+ break;
+ }
+
+ return rc;
+}
+
/*
** Run a single line of SQL. Return the number of errors.
*/
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
oputf("%s\n", zLineBuf);
}
+
+ if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}
--- /dev/null
+# 2009 Nov 11
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# The focus of this file is testing the CLI shell tool. Specifically,
+# testing that it is possible to run a ".dump" script that creates
+# virtual tables without explicitly disabling defensive mode.
+#
+
+# Test plan:
+#
+# shell1-1.*: Basic command line option handling.
+# shell1-2.*: Basic "dot" command token parsing.
+# shell1-3.*: Basic test that "dot" command can be called.
+# shell1-{4-8}.*: Test various "dot" commands's functionality.
+# shell1-9.*: Basic test that "dot" commands and SQL intermix ok.
+#
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+set CLI [test_cli_invocation]
+
+set ::testprefix shell9
+
+ifcapable !fts5 {
+ finish_test
+ return
+}
+
+#----------------------------------------------------------------------------
+# Test cases shell9-1.* verify that scripts output by .dump may be parsed
+# by the shell tool without explicitly disabling DEFENSIVE mode, unless
+# the shell is in safe mode.
+#
+do_execsql_test 1.0 {
+ CREATE VIRTUAL TABLE t1 USING fts5(a, b, c);
+ INSERT INTO t1 VALUES('one', 'two', 'three');
+}
+db close
+
+# Create .dump file in "testdump.txt".
+#
+set out [open testdump.txt w]
+puts $out [lindex [catchcmd test.db .dump] 1]
+close $out
+
+# Check testdump.txt can be processed if the initial db is empty.
+#
+do_test 1.1.1 {
+ forcedelete test.db
+ catchcmd test.db ".read testdump.txt"
+} {0 {}}
+sqlite3 db test.db
+do_execsql_test 1.1.2 {
+ SELECT * FROM t1;
+} {one two three}
+
+# Check testdump.txt cannot be processed if the initial db is not empty.
+#
+reset_db
+do_execsql_test 1.2.1 {
+ CREATE TABLE t4(hello);
+}
+db close
+do_test 1.2.2 {
+ catchcmd test.db ".read testdump.txt"
+} {1 {Parse error near line 5: table sqlite_master may not be modified}}
+
+# Check testdump.txt cannot be processed if the db is in safe mode
+#
+do_test 1.3.1 {
+ forcedelete test.db
+ catchsafecmd test.db ".read testdump.txt"
+} {1 {line 1: cannot run .read in safe mode}}
+do_test 1.3.2 {
+ set fd [open testdump.txt]
+ set script [read $fd]
+ close $fd
+ forcedelete test.db
+ catchsafecmd test.db $script
+} {1 {Parse error near line 5: table sqlite_master may not be modified}}
+do_test 1.3.3 {
+ # Quick check that the above would have worked but for safe mode.
+ forcedelete test.db
+ catchcmd test.db $script
+} {0 {}}
+
+#----------------------------------------------------------------------------
+# Test cases shell9-2.* verify that a warning is printed at the top of
+# .dump scripts that contain virtual tables.
+#
+proc contains_warning {text} {
+ return [string match "*WARNING: Script requires that*" $text]
+}
+
+reset_db
+do_execsql_test 2.0.1 {
+ CREATE TABLE t1(x);
+ CREATE TABLE t2(y);
+ INSERT INTO t1 VALUES('one');
+ INSERT INTO t2 VALUES('two');
+}
+do_test 2.0.2 {
+ contains_warning [catchcmd test.db .dump]
+} 0
+
+do_execsql_test 2.1.1 {
+ CREATE virtual TABLE r1 USING fts5(x);
+}
+do_test 2.1.2 {
+ contains_warning [catchcmd test.db .dump]
+} 1
+
+do_test 2.2.1 {
+ contains_warning [catchcmd test.db ".dump t1"]
+} 0
+do_test 2.2.2 {
+ contains_warning [catchcmd test.db ".dump r1"]
+} 1
+
+finish_test
set rc [catch { eval $line } msg]
list $rc $msg
}
+proc catchsafecmd {db {cmd ""}} {
+ global CLI
+ set out [open cmds.txt w]
+ puts $out $cmd
+ close $out
+ set line "exec $CLI -safe $db < cmds.txt"
+ set rc [catch { eval $line } msg]
+ list $rc $msg
+}
proc catchcmdex {db {cmd ""}} {
global CLI