From 2c8b51a027a11c1c4db8490e4e42b29b069d50e5 Mon Sep 17 00:00:00 2001 From: Dan Kennedy Date: Fri, 12 Jan 2024 11:44:49 +0000 Subject: [PATCH] Have the shell tool automatically enable SQLITE_CONFIG_DQS_DDL when executing a ".dump" script against an empty db. --- src/shell.c.in | 96 ++++++++++++++++++++++++++++++-------------------------- test/shell4.test | 2 +- test/shell9.test | 21 +++++++++++++ test/tester.tcl | 2 +- 4 files changed, 75 insertions(+), 46 deletions(-) diff --git a/src/shell.c.in b/src/shell.c.in index 19574dc79c..da3b9f8706 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -11424,61 +11424,69 @@ static int line_is_complete(char *zSql, int nSql){ ** ** 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. +** 2-6: Currently running .dump transaction. If the "2" bit is set, +** disable DEFENSIVE when done. If "4" is set, disable DQS_DDL. +** 7: Nothing left to do. This function becomes a no-op. */ 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; + if( p->eRestoreState<7 ){ + switch( p->eRestoreState ){ + case 0: { + const char *zExpect = "PRAGMA foreign_keys=OFF;"; + assert( strlen(zExpect)==24 ); + if( p->bSafeMode==0 && memcmp(zSql, zExpect, 25)==0 ){ + p->eRestoreState = 1; + }else{ + p->eRestoreState = 7; } - shellFinalize(&rc, pStmt); - if( bEmpty && rc==SQLITE_OK ){ + break; + }; + + case 1: { + int bIsDump = 0; + 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; + + bIsDump = 1; + shellPrepare(p->db, &rc, zQuery, &pStmt); + if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){ + bIsDump = 0; + } + shellFinalize(&rc, pStmt); + } + if( bIsDump && rc==SQLITE_OK ){ + int bDefense = 0; + int bDqsDdl = 0; + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense); + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, -1, &bDqsDdl); sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0); + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 1, 0); + p->eRestoreState = (bDefense ? 2 : 0) + (bDqsDdl ? 4 : 0); }else{ - p->eRestoreState = 3; + p->eRestoreState = 7; } + break; } - break; - } - - case 2: { - if( sqlite3_get_autocommit(p->db) ){ - sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0); - p->eRestoreState = 3; + + default: { + if( sqlite3_get_autocommit(p->db) ){ + if( (p->eRestoreState & 2) ){ + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0); + } + if( (p->eRestoreState & 4) ){ + sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 0, 0); + } + p->eRestoreState = 7; + } + break; } - break; } - - default: /* Nothing to do */ - assert( p->eRestoreState==3 ); - break; } return rc; diff --git a/test/shell4.test b/test/shell4.test index 193dc8b69b..eee59b02b8 100644 --- a/test/shell4.test +++ b/test/shell4.test @@ -117,7 +117,7 @@ do_test shell4-2.2 { } {0 {}} do_test shell4-2.3 { catchcmd ":memory:" ".trace stdout\n.dump\n.trace off\n" -} {/^0 {PRAGMA.*}$/} +} {/^0 {SELECT.*}$/} do_test shell4-2.4 { catchcmd ":memory:" ".trace stdout\nCREATE TABLE t1(x);SELECT * FROM t1;" } {0 {CREATE TABLE t1(x); diff --git a/test/shell9.test b/test/shell9.test index d7759551f4..34c9d8c5d6 100644 --- a/test/shell9.test +++ b/test/shell9.test @@ -13,6 +13,9 @@ # testing that it is possible to run a ".dump" script that creates # virtual tables without explicitly disabling defensive mode. # +# And, that it can process a ".dump" script that contains strings +# delimited using double-quotes in the schema (DQS_DDL setting). +# # Test plan: # @@ -124,4 +127,22 @@ do_test 2.2.2 { contains_warning [catchcmd test.db ".dump r1"] } 1 +#------------------------------------------------------------------------- +reset_db +sqlite3_db_config db DQS_DDL 1 +do_execsql_test 3.1.0 { + CREATE TABLE t4(hello, check( hello IS NOT "xyz") ); +} +db close + +# Create .dump file in "testdump.txt". +# +set out [open testdump.txt w] +puts $out [lindex [catchcmd test.db .dump] 1] +close $out +do_test 3.1.1 { + forcedelete test.db + catchcmd test.db ".read testdump.txt" +} {0 {}} + finish_test diff --git a/test/tester.tcl b/test/tester.tcl index 5754d7037e..b96bc505d8 100644 --- a/test/tester.tcl +++ b/test/tester.tcl @@ -554,7 +554,7 @@ if {[info exists cmdlinearg]==0} { } unset -nocomplain a set testdir [file normalize $testdir] - set cmdlinearg(TESTFIXTURE_HOME) [pwd] + set cmdlinearg(TESTFIXTURE_HOME) [file dirname [info nameofexec]] set cmdlinearg(INFO_SCRIPT) [file normalize [info script]] set argv0 [file normalize $argv0] if {$cmdlinearg(testdir)!=""} { -- 2.11.4.GIT