Index: src/alter.c ================================================================== --- src/alter.c +++ src/alter.c @@ -29,13 +29,12 @@ ** Or, if zName is not a system table, zero is returned. */ 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 + || ( (pTab->tabFlags & TF_Shadow)!=0 + && sqlite3ReadOnlyShadowTables(pParse->db) ) #endif ){ sqlite3ErrorMsg(pParse, "table %s may not be altered", pTab->zName); return 1; Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -854,17 +854,18 @@ sqlite3ErrorMsg(pParse, ""); /* corruptSchema() will supply the error */ return SQLITE_ERROR; } } }else{ - if( pParse->nested==0 - && 0==sqlite3StrNICmp(zName, "sqlite_", 7) + if( (pParse->nested==0 && 0==sqlite3StrNICmp(zName, "sqlite_", 7)) + || (sqlite3ReadOnlyShadowTables(db) && sqlite3ShadowTableName(db, zName)) ){ sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); return SQLITE_ERROR; } + } return SQLITE_OK; } /* @@ -2127,11 +2128,11 @@ ** connection. ** ** zName is temporarily modified while this routine is running, but is ** restored to its original value prior to this routine returning. */ -static int isShadowTableName(sqlite3 *db, char *zName){ +int sqlite3ShadowTableName(sqlite3 *db, const char *zName){ char *zTail; /* Pointer to the last "_" in zName */ Table *pTab; /* Table that zName is a shadow of */ Module *pMod; /* Module for the virtual table */ zTail = strrchr(zName, '_'); @@ -2145,12 +2146,10 @@ if( pMod==0 ) return 0; if( pMod->pModule->iVersion<3 ) return 0; if( pMod->pModule->xShadowName==0 ) return 0; return pMod->pModule->xShadowName(zTail+1); } -#else -# define isShadowTableName(x,y) 0 #endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */ /* ** This routine is called to report the final ")" that terminates ** a CREATE TABLE statement. @@ -2188,11 +2187,11 @@ } assert( !db->mallocFailed ); p = pParse->pNewTable; if( p==0 ) return; - if( pSelect==0 && isShadowTableName(db, p->zName) ){ + if( pSelect==0 && sqlite3ShadowTableName(db, p->zName) ){ p->tabFlags |= TF_Shadow; } /* If the db->init.busy is 1 it means we are reading the SQL off the ** "sqlite_master" or "sqlite_temp_master" table on the disk. @@ -2891,23 +2890,38 @@ } sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); sqlite3ChangeCookie(pParse, iDb); sqliteViewResetAll(db, iDb); } + +/* +** Return TRUE if shadow tables should be read-only in the current +** context. +*/ +int sqlite3ReadOnlyShadowTables(sqlite3 *db){ +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( (db->flags & SQLITE_Defensive)!=0 + && db->pVtabCtx==0 + && db->nVdbeExec==0 + ){ + return 1; + } +#endif + return 0; +} /* ** Return true if it is not allowed to drop the given table */ -static int tableMayNotBeDropped(Parse *pParse, Table *pTab){ +static int tableMayNotBeDropped(sqlite3 *db, 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; + if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){ + return 1; } return 0; } /* @@ -2977,11 +2991,11 @@ if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb) ){ goto exit_drop_table; } } #endif - if( tableMayNotBeDropped(pParse, pTab) ){ + if( tableMayNotBeDropped(db, pTab) ){ sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName); goto exit_drop_table; } #ifndef SQLITE_OMIT_VIEW Index: src/delete.c ================================================================== --- src/delete.c +++ src/delete.c @@ -68,15 +68,11 @@ db = pParse->db; if( (pTab->tabFlags & TF_Readonly)!=0 ){ return sqlite3WritableSchema(db)==0 && pParse->nested==0; } assert( pTab->tabFlags & TF_Shadow ); - return (db->flags & SQLITE_Defensive)!=0 -#ifndef SQLITE_OMIT_VIRTUALTABLE - && db->pVtabCtx==0 -#endif - && db->nVdbeExec==0; + return sqlite3ReadOnlyShadowTables(db); } /* ** Check to make sure the given table is writable. If it is not ** writable, generate an error message and return 1. If it is Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -4544,10 +4544,16 @@ const sqlite3_module*, void*, void(*)(void*) ); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) +#endif +int sqlite3ReadOnlyShadowTables(sqlite3 *db); +#ifndef SQLITE_OMIT_VIRTUALTABLE + int sqlite3ShadowTableName(sqlite3 *db, const char *zName); +#else +# define sqlite3ShadowTableName(A,B) 0 #endif int sqlite3VtabEponymousTableInit(Parse*,Module*); void sqlite3VtabEponymousTableClear(sqlite3*,Module*); void sqlite3VtabMakeWritable(Parse*,Table*); void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int); Index: test/altertab.test ================================================================== --- test/altertab.test +++ test/altertab.test @@ -544,17 +544,33 @@ 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 { + DROP TABLE y1_segments; + } {1 {table y1_segments may not be dropped}} + 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}} + sqlite3_db_config db DEFENSIVE 0 + do_catchsql_test 16.22 { + ALTER TABLE y1_segments RENAME TO abc; + } {0 {}} + sqlite3_db_config db DEFENSIVE 1 + do_catchsql_test 16.23 { + CREATE TABLE y1_segments AS SELECT * FROM abc; + } {1 {object name reserved for internal use: y1_segments}} + do_catchsql_test 16.24 { + CREATE VIEW y1_segments AS SELECT * FROM abc; + } {1 {object name reserved for internal use: y1_segments}} + sqlite3_db_config db DEFENSIVE 0 + do_catchsql_test 16.25 { + ALTER TABLE abc RENAME TO y1_segments; + } {0 {}} + sqlite3_db_config db DEFENSIVE 1 do_execsql_test 16.30 { ALTER TABLE y1 RENAME TO z1; }