Have "ALTER TABLE ADD COLUMN" reload the entire db schema, as "RENAME COLUMN"

and "RENAME TABLE" do.

FossilOrigin-Name: 8d89ddc1a628e983b0fbd929c9c9daac86ee23d18f8dd2709c971012389395c6
This commit is contained in:
dan 2018-09-01 16:05:50 +00:00
parent 010f8ee2fb
commit 0923650365
3 changed files with 65 additions and 180 deletions

View File

@ -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

View File

@ -1 +1 @@
22e785aa2bbce4ae0852bc3d127d4b12222a192eb6e3ee874bf8e5c8582d05f3
8d89ddc1a628e983b0fbd929c9c9daac86ee23d18f8dd2709c971012389395c6

View File

@ -20,131 +20,6 @@
*/
#ifndef SQLITE_OMIT_ALTERTABLE
/*
** This function is used to create the text of expressions of the form:
**
** name=<constant1> OR name=<constant2> OR ...
**
** If argument zWhere is NULL, then a pointer string containing the text
** "name=<constant>" is returned, where <constant> 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
** "<where> OR name=<constant>", where <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);
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 schema of the modified table. */
reloadTableSchema(pParse, pTab, pTab->zName);
/* 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,34 +1421,36 @@ 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);
}
renameParseCleanup(&sParse);
}
if( rc!=SQLITE_OK ){
renameColumnParseError(context, 1, argv[2], argv[3], &sParse);
}
renameParseCleanup(&sParse);
#ifndef SQLITE_OMIT_AUTHORIZATION
db->xAuth = xAuth;
#endif