Subject: [PATCH] In defensive mode, do not allow shadow tables to be renamed using ALTER TABLE and do not allow shadow tables to be dropped. diff --git a/src/alter.c b/src/alter.c index 0fa24c0..707472a 100644 --- a/src/alter.c +++ b/src/alter.c @@ -28,9 +28,16 @@ ** ** Or, if zName is not a system table, zero is returned. */ -static int isSystemTable(Parse *pParse, const char *zName){ - if( 0==sqlite3StrNICmp(zName, "sqlite_", 7) ){ - sqlite3ErrorMsg(pParse, "table %s may not be altered", zName); +static int isAlterableTable(Parse *pParse, Table *pTab){ + if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) +#ifndef SQLITE_OMIT_VIRTUALTABLE + || ( (pTab->tabFlags & TF_Shadow) + && (pParse->db->flags & SQLITE_Defensive) + && pParse->db->nVdbeExec==0 + ) +#endif + ){ + sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); return 1; } return 0; @@ -129,7 +136,7 @@ void sqlite3AlterRenameTable( /* Make sure it is not a system table being altered, or a reserved name ** that the table is being renamed to. */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ goto exit_rename_table; } if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto @@ -427,7 +434,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ sqlite3ErrorMsg(pParse, "Cannot add a column to a view"); goto exit_begin_add_column; } - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ){ + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ){ goto exit_begin_add_column; } @@ -529,7 +536,7 @@ void sqlite3AlterRenameColumn( if( !pTab ) goto exit_rename_column; /* Cannot alter a system table */ - if( SQLITE_OK!=isSystemTable(pParse, pTab->zName) ) goto exit_rename_column; + if( SQLITE_OK!=isAlterableTable(pParse, pTab) ) goto exit_rename_column; if( SQLITE_OK!=isRealTable(pParse, pTab) ) goto exit_rename_column; /* Which schema holds the table to be altered */ diff --git a/src/build.c b/src/build.c index 1dc2614..3412670 100644 --- a/src/build.c +++ b/src/build.c @@ -2661,6 +2661,22 @@ void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){ sqliteViewResetAll(db, iDb); } +/* +** Return true if it is not allowed to drop the given table +*/ +static int tableMayNotBeDropped(Parse *pParse, Table *pTab){ + if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){ + if( sqlite3StrNICmp(pTab->zName+7, "stat", 4)==0 ) return 0; + if( sqlite3StrNICmp(pTab->zName+7, "parameters", 10)==0 ) return 0; + return 1; + } + if( pTab->tabFlags & TF_Shadow ){ + sqlite3 *db = pParse->db; + if( (db->flags & SQLITE_Defensive)!=0 && db->nVdbeExec==0 ) return 1; + } + return 0; +} + /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. @@ -2730,8 +2746,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView, int noErr){ } } #endif - if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 - && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){ + if( tableMayNotBeDropped(pParse, pTab) ){ sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); goto exit_drop_table; } diff --git a/test/altertab.test b/test/altertab.test index a364207..891b081 100644 --- a/test/altertab.test +++ b/test/altertab.test @@ -505,5 +505,62 @@ do_execsql_test 15.5 { SELECT sql FROM sqlite_master WHERE name = 'y'; } {{CREATE VIEW y AS SELECT f2 AS f1 FROM x}} +#------------------------------------------------------------------------- +# Test that it is not possible to rename a shadow table in DEFENSIVE mode. +# +ifcapable fts3 { + proc vtab_command {method args} { + switch -- $method { + xConnect { + if {[info exists ::vtab_connect_sql]} { + execsql $::vtab_connect_sql + } + return "CREATE TABLE t1(a, b, c)" + } + + xBestIndex { + set clist [lindex $args 0] + if {[llength $clist]!=1} { error "unexpected constraint list" } + catch { array unset C } + array set C [lindex $clist 0] + if {$C(usable)} { + return "omit 0 cost 0 rows 1 idxnum 555 idxstr eq!" + } else { + return "cost 1000000 rows 0 idxnum 0 idxstr scan..." + } + } + } + + return {} + } + + register_tcl_module db + + sqlite3_db_config db DEFENSIVE 1 + + do_execsql_test 16.0 { + CREATE VIRTUAL TABLE y1 USING fts3; + } + + do_catchsql_test 16.10 { + INSERT INTO y1_segments VALUES(1, X'1234567890'); + } {1 {table y1_segments may not be modified}} + + do_catchsql_test 16.20 { + ALTER TABLE y1_segments RENAME TO abc; + } {1 {table y1_segments may not be altered}} + + do_catchsql_test 16.21 { + DROP TABLE y1_segments; + } {1 {table y1_segments may not be dropped}} + + do_execsql_test 16.30 { + ALTER TABLE y1 RENAME TO z1; + } + + do_execsql_test 16.40 { + SELECT * FROM z1_segments; + } +} finish_test