aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <Dan Kennedy>2024-01-08 18:46:34 +0000
committerdan <Dan Kennedy>2024-01-08 18:46:34 +0000
commit7cda91c33eaca40e98b3c8eee4cc8ff8618fd8b3 (patch)
tree2f0347d6814a134e3ce7467eca3690b1a4c51ec9 /src
parent3e2ffbd476f0366d40df2510b246a88a7c9a1d04 (diff)
downloadsqlite-7cda91c33eaca40e98b3c8eee4cc8ff8618fd8b3.tar.gz
sqlite-7cda91c33eaca40e98b3c8eee4cc8ff8618fd8b3.zip
Automatically turn off DEFENSIVE mode in the shell tool when executing scripts generated by the ".dump" command against an empty database. Add a warning to the top of generated ".dump" scripts that populate virtual tables.
FossilOrigin-Name: 6e9e96b7e7afb9420110f4b93d10b945c9eadfde5e9c81e59ae9ee8167e75707
Diffstat (limited to 'src')
-rw-r--r--src/shell.c.in119
1 files changed, 107 insertions, 12 deletions
diff --git a/src/shell.c.in b/src/shell.c.in
index 8f8389052..19574dc79 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -1292,6 +1292,7 @@ struct ShellState {
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 */
@@ -6731,7 +6732,6 @@ static int lintDotCommand(
return SQLITE_ERROR;
}
-#if !defined SQLITE_OMIT_VIRTUALTABLE
static void shellPrepare(
sqlite3 *db,
int *pRc,
@@ -6750,12 +6750,8 @@ static void shellPrepare(
/*
** 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,
@@ -6778,13 +6774,10 @@ void shellPreparePrintf(
}
}
-/* 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
){
@@ -6800,6 +6793,7 @@ void shellFinalize(
}
}
+#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,
@@ -7867,6 +7861,30 @@ FROM (\
}
/*
+** 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.
**
@@ -8328,6 +8346,7 @@ static int do_meta_command(char *zLine, ShellState *p){
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.
@@ -11392,6 +11411,80 @@ static int line_is_complete(char *zSql, int nSql){
}
/*
+** 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.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
@@ -11438,6 +11531,8 @@ static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
oputf("%s\n", zLineBuf);
}
+
+ if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}