From: dan Date: Sat, 1 Sep 2018 16:05:50 +0000 (+0000) Subject: Have "ALTER TABLE ADD COLUMN" reload the entire db schema, as "RENAME COLUMN" X-Git-Tag: version-3.25.0~39^2~12^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Falter-table-rename-table;p=thirdparty%2Fsqlite.git Have "ALTER TABLE ADD COLUMN" reload the entire db schema, as "RENAME COLUMN" and "RENAME TABLE" do. FossilOrigin-Name: 8d89ddc1a628e983b0fbd929c9c9daac86ee23d18f8dd2709c971012389395c6 --- diff --git a/manifest b/manifest index a887bb1985..24953bad43 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Merge\sfixes\sfrom\sthe\salter-table-rename-column\sbranch\sthat\soccurred\safter\nthis\sbranch\sseparated\sfrom\sthat\sone. -D 2018-09-01T15:55:44.527 +C Have\s"ALTER\sTABLE\sADD\sCOLUMN"\sreload\sthe\sentire\sdb\sschema,\sas\s"RENAME\sCOLUMN"\nand\s"RENAME\sTABLE"\sdo. +D 2018-09-01T16:05:50.207 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 6b650013511fd9d8b094203ac268af9220d292cc7d4e1bc9fbca15aacd8c7995 @@ -434,7 +434,7 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786 F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a -F src/alter.c 245152daa14007b02ecbab086b73fc0a9e372b9aad24b4178b6e5b2f802554c5 +F src/alter.c 86affdc4474e165389f890282caa6a98238a586861945db026d1c14714b6b5d6 F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9 F src/attach.c 4bd5b92633671d3e8ce431153ebb1893b50335818423b5373f3f27969f79769a F src/auth.c 32a5bbe3b755169ab6c66311c5225a3cd4f75a46c041f7fb117e0cbb68055114 @@ -1762,7 +1762,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 589186c083ff3af8d5a6d5ad34e1cefea57806ebf3831ea3bf5a48ef1e173140 62089c6daf9ea51be769c077c23d6fa881ba797255fa22d71baaac191a9c1ba7 -R 1d86d7939ae5916c3a0cbf5cee6b780f -U drh -Z 720f2ed02ab0eac24853dcd860bc927c +P 22e785aa2bbce4ae0852bc3d127d4b12222a192eb6e3ee874bf8e5c8582d05f3 +R 42f4b64c172a70e76760a08e4b49df2d +U dan +Z ba4dbce73bee530c5bae5beba6a96209 diff --git a/manifest.uuid b/manifest.uuid index 1fa397a6f4..f403436ba1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -22e785aa2bbce4ae0852bc3d127d4b12222a192eb6e3ee874bf8e5c8582d05f3 \ No newline at end of file +8d89ddc1a628e983b0fbd929c9c9daac86ee23d18f8dd2709c971012389395c6 \ No newline at end of file diff --git a/src/alter.c b/src/alter.c index 5da5b2add9..4f09179fea 100644 --- a/src/alter.c +++ b/src/alter.c @@ -20,131 +20,6 @@ */ #ifndef SQLITE_OMIT_ALTERTABLE -/* -** This function is used to create the text of expressions of the form: -** -** name= OR name= OR ... -** -** If argument zWhere is NULL, then a pointer string containing the text -** "name=" is returned, where is the quoted version -** of the string passed as argument zConstant. The returned buffer is -** allocated using sqlite3DbMalloc(). It is the responsibility of the -** caller to ensure that it is eventually freed. -** -** If argument zWhere is not NULL, then the string returned is -** " OR name=", where is the contents of zWhere. -** In this case zWhere is passed to sqlite3DbFree() before returning. -** -*/ -static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){ - char *zNew; - if( !zWhere ){ - zNew = sqlite3MPrintf(db, "name=%Q", zConstant); - }else{ - zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant); - sqlite3DbFree(db, zWhere); - } - return zNew; -} - -#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) -/* -** Generate the text of a WHERE expression which can be used to select all -** tables that have foreign key constraints that refer to table pTab (i.e. -** constraints for which pTab is the parent table) from the sqlite_master -** table. -*/ -static char *whereForeignKeys(Parse *pParse, Table *pTab){ - FKey *p; - char *zWhere = 0; - for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ - zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName); - } - return zWhere; -} -#endif - -/* -** Generate the text of a WHERE expression which can be used to select all -** temporary triggers on table pTab from the sqlite_temp_master table. If -** table pTab has no temporary triggers, or is itself stored in the -** temporary database, NULL is returned. -*/ -static char *whereTempTriggers(Parse *pParse, Table *pTab){ - Trigger *pTrig; - char *zWhere = 0; - const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */ - - /* If the table is not located in the temp-db (in which case NULL is - ** returned, loop through the tables list of triggers. For each trigger - ** that is not part of the temp-db schema, add a clause to the WHERE - ** expression being built up in zWhere. - */ - if( pTab->pSchema!=pTempSchema ){ - sqlite3 *db = pParse->db; - for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ - if( pTrig->pSchema==pTempSchema ){ - zWhere = whereOrName(db, zWhere, pTrig->zName); - } - } - } - if( zWhere ){ - char *zNew = sqlite3MPrintf(pParse->db, "type='trigger' AND (%s)", zWhere); - sqlite3DbFree(pParse->db, zWhere); - zWhere = zNew; - } - return zWhere; -} - -/* -** Generate code to drop and reload the internal representation of table -** pTab from the database, including triggers and temporary triggers. -** Argument zName is the name of the table in the database schema at -** the time the generated code is executed. This can be different from -** pTab->zName if this function is being called to code part of an -** "ALTER TABLE RENAME TO" statement. -*/ -static void reloadTableSchema(Parse *pParse, Table *pTab, const char *zName){ - Vdbe *v; - char *zWhere; - int iDb; /* Index of database containing pTab */ -#ifndef SQLITE_OMIT_TRIGGER - Trigger *pTrig; -#endif - - v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); - assert( iDb>=0 ); - -#ifndef SQLITE_OMIT_TRIGGER - /* Drop any table triggers from the internal schema. */ - for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ - int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); - assert( iTrigDb==iDb || iTrigDb==1 ); - sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0); - } -#endif - - /* Drop the table and index from the internal schema. */ - sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); - - /* Reload the table, index and permanent trigger schemas. */ - zWhere = sqlite3MPrintf(pParse->db, "tbl_name=%Q", zName); - if( !zWhere ) return; - sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere); - -#ifndef SQLITE_OMIT_TRIGGER - /* Now, if the table is not stored in the temp database, reload any temp - ** triggers. Don't use IN(...) in case SQLITE_OMIT_SUBQUERY is defined. - */ - if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){ - sqlite3VdbeAddParseSchemaOp(v, 1, zWhere); - } -#endif -} - /* ** Parameter zName is the name of a table that is about to be altered ** (either with ALTER TABLE ... RENAME TO or ALTER TABLE ... ADD COLUMN). @@ -161,6 +36,13 @@ static int isSystemTable(Parse *pParse, const char *zName){ return 0; } +/* +** Generate code to verify that the schemas of database zDb and, if +** bTemp is not true, database "temp", can still be parsed. This is +** called at the end of the generation of an ALTER TABLE ... RENAME ... +** statement to ensure that the operation has not rendered any schema +** objects unusable. +*/ void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ sqlite3NestedParse(pParse, "SELECT 1 " @@ -184,6 +66,19 @@ void renameTestSchema(Parse *pParse, const char *zDb, int bTemp){ } } +/* +** Generate code to reload the schema for database iDb. And, if iDb!=1, for +** the temp database as well. +*/ +void renameReloadSchema(Parse *pParse, int iDb){ + Vdbe *v = pParse->pVdbe; + if( v ){ + sqlite3ChangeCookie(pParse, iDb); + sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); + if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); + } +} + /* ** Generate code to implement the "ALTER TABLE xxx RENAME TO yyy" ** command. @@ -273,8 +168,6 @@ void sqlite3AlterRenameTable( if( v==0 ){ goto exit_rename_table; } - sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb); - sqlite3ChangeCookie(pParse, iDb); /* If this is a virtual table, invoke the xRename() function if ** one is defined. The xRename() callback will modify the names @@ -345,9 +238,7 @@ void sqlite3AlterRenameTable( , zDb, zTabName, zName, zTabName, zTabName, zName); } - sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0); - if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); - + renameReloadSchema(pParse, iDb); renameTestSchema(pParse, zDb, iDb==1); exit_rename_table: @@ -374,12 +265,12 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ Column *pCol; /* The new column */ Expr *pDflt; /* Default value for the new column */ sqlite3 *db; /* The database connection; */ - Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */ + Vdbe *v; /* The prepared statement under construction */ int r1; /* Temporary registers */ + char *zWhere; /* WHERE clause for reloading schema */ db = pParse->db; if( pParse->nErr || db->mallocFailed ) return; - assert( v!=0 ); pNew = pParse->pNewTable; assert( pNew ); @@ -474,17 +365,20 @@ void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){ ** from less than 3 to 4, as that will corrupt any preexisting DESC ** index. */ - r1 = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); - sqlite3VdbeUsesBtree(v, iDb); - sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2); - sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2); - VdbeCoverage(v); - sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); - sqlite3ReleaseTempReg(pParse, r1); - - /* Reload the schema of the modified table. */ - reloadTableSchema(pParse, pTab, pTab->zName); + v = sqlite3GetVdbe(pParse); + if( v ){ + r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT); + sqlite3VdbeUsesBtree(v, iDb); + sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2); + sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2); + VdbeCoverage(v); + sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3); + sqlite3ReleaseTempReg(pParse, r1); + } + + /* Reload the table definition */ + renameReloadSchema(pParse, iDb); } /* @@ -569,12 +463,6 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){ pNew->addColOffset = pTab->addColOffset; pNew->nTabRef = 1; - /* Begin a transaction and increment the schema cookie. */ - sqlite3BeginWriteOperation(pParse, 0, iDb); - v = sqlite3GetVdbe(pParse); - if( !v ) goto exit_begin_add_column; - sqlite3ChangeCookie(pParse, iDb); - exit_begin_add_column: sqlite3SrcListDelete(db, pSrc); return; @@ -692,12 +580,7 @@ void sqlite3AlterRenameColumn( ); /* Drop and reload the database schema. */ - if( pParse->pVdbe ){ - sqlite3ChangeCookie(pParse, iSchema); - sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iSchema, 0); - if( iSchema!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0); - } - + renameReloadSchema(pParse, iSchema); renameTestSchema(pParse, zDb, iSchema==1); exit_rename_column: @@ -745,7 +628,7 @@ struct RenameCtx { void renameTokenClear(Parse *pParse, void *pPtr){ RenameToken *p; - assert( pPtr ); + assert( pPtr || pParse->db->mallocFailed ); for(p=pParse->pRename; p; p=p->pNext){ if( p->p==pPtr ){ p->p = 0; @@ -1538,33 +1421,35 @@ static void renameTableTest( unsigned char const *zDb = sqlite3_value_text(argv[0]); unsigned char const *zInput = sqlite3_value_text(argv[1]); int bTemp = sqlite3_value_int(argv[4]); - int rc; - Parse sParse; #ifndef SQLITE_OMIT_AUTHORIZATION sqlite3_xauth xAuth = db->xAuth; db->xAuth = 0; #endif - rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp); - if( rc==SQLITE_OK ){ - if( sParse.pNewTable && sParse.pNewTable->pSelect ){ - NameContext sNC; - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = &sParse; - sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); - if( sParse.nErr ) rc = sParse.rc; - } + if( zDb && zInput ){ + int rc; + Parse sParse; + rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp); + if( rc==SQLITE_OK ){ + if( sParse.pNewTable && sParse.pNewTable->pSelect ){ + NameContext sNC; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = &sParse; + sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC); + if( sParse.nErr ) rc = sParse.rc; + } - else if( sParse.pNewTrigger ){ - rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); + else if( sParse.pNewTrigger ){ + rc = renameResolveTrigger(&sParse, bTemp ? 0 : zDb); + } } - } - if( rc!=SQLITE_OK ){ - renameColumnParseError(context, 1, argv[2], argv[3], &sParse); + if( rc!=SQLITE_OK ){ + renameColumnParseError(context, 1, argv[2], argv[3], &sParse); + } + renameParseCleanup(&sParse); } - renameParseCleanup(&sParse); #ifndef SQLITE_OMIT_AUTHORIZATION db->xAuth = xAuth;