]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Have "ALTER TABLE ADD COLUMN" reload the entire db schema, as "RENAME COLUMN" alter-table-rename-table
authordan <dan@noemail.net>
Sat, 1 Sep 2018 16:05:50 +0000 (16:05 +0000)
committerdan <dan@noemail.net>
Sat, 1 Sep 2018 16:05:50 +0000 (16:05 +0000)
and "RENAME TABLE" do.

FossilOrigin-Name: 8d89ddc1a628e983b0fbd929c9c9daac86ee23d18f8dd2709c971012389395c6

manifest
manifest.uuid
src/alter.c

index a887bb1985368e96eecf0e8c51885c3936d5e40b..24953bad437f4ea6cdbf066001d680d2dc05187c 100644 (file)
--- 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
index 1fa397a6f4bc451ebd2aa5be215502c6547eb736..f403436ba12d8ca7b61d5d135dab20130af80848 100644 (file)
@@ -1 +1 @@
-22e785aa2bbce4ae0852bc3d127d4b12222a192eb6e3ee874bf8e5c8582d05f3
\ No newline at end of file
+8d89ddc1a628e983b0fbd929c9c9daac86ee23d18f8dd2709c971012389395c6
\ No newline at end of file
index 5da5b2add9fc447f33fb86b33fa1a9f5891c48fa..4f09179feaf3070d2d631fc318821f502d82f7eb 100644 (file)
 */
 #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);
-
-  /* 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;