Extend RENAME TABLE to edit triggers and views. Still buggy.

FossilOrigin-Name: 01308bae3acf33f78b5bb90892085eab340df093aafc17e6ccf6a7d6cf324897
diff --git a/src/alter.c b/src/alter.c
index 50690b9..6588075 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -20,211 +20,6 @@
 */
 #ifndef SQLITE_OMIT_ALTERTABLE
 
-
-/*
-** This function is used by SQL generated to implement the 
-** ALTER TABLE command. The first argument is the text of a CREATE TABLE or
-** CREATE INDEX command. The second is a table name. The table name in 
-** the CREATE TABLE or CREATE INDEX statement is replaced with the third
-** argument and the result returned. Examples:
-**
-** sqlite_rename_table('CREATE TABLE abc(a, b, c)', 'def')
-**     -> 'CREATE TABLE def(a, b, c)'
-**
-** sqlite_rename_table('CREATE INDEX i ON abc(a)', 'def')
-**     -> 'CREATE INDEX i ON def(a, b, c)'
-*/
-static void renameTableFunc(
-  sqlite3_context *context,
-  int NotUsed,
-  sqlite3_value **argv
-){
-  unsigned char const *zSql = sqlite3_value_text(argv[0]);
-  unsigned char const *zTableName = sqlite3_value_text(argv[1]);
-
-  int token;
-  Token tname;
-  unsigned char const *zCsr = zSql;
-  int len = 0;
-  char *zRet;
-
-  sqlite3 *db = sqlite3_context_db_handle(context);
-
-  UNUSED_PARAMETER(NotUsed);
-
-  /* The principle used to locate the table name in the CREATE TABLE 
-  ** statement is that the table name is the first non-space token that
-  ** is immediately followed by a TK_LP or TK_USING token.
-  */
-  if( zSql ){
-    do {
-      if( !*zCsr ){
-        /* Ran out of input before finding an opening bracket. Return NULL. */
-        return;
-      }
-
-      /* Store the token that zCsr points to in tname. */
-      tname.z = (char*)zCsr;
-      tname.n = len;
-
-      /* Advance zCsr to the next token. Store that token type in 'token',
-      ** and its length in 'len' (to be used next iteration of this loop).
-      */
-      do {
-        zCsr += len;
-        len = sqlite3GetToken(zCsr, &token);
-      } while( token==TK_SPACE );
-      assert( len>0 || !*zCsr );
-    } while( token!=TK_LP && token!=TK_USING );
-
-    zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
-       zSql, zTableName, tname.z+tname.n);
-    sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
-  }
-}
-
-/*
-** This C function implements an SQL user function that is used by SQL code
-** generated by the ALTER TABLE ... RENAME command to modify the definition
-** of any foreign key constraints that use the table being renamed as the 
-** parent table. It is passed three arguments:
-**
-**   1) The complete text of the CREATE TABLE statement being modified,
-**   2) The old name of the table being renamed, and
-**   3) The new name of the table being renamed.
-**
-** It returns the new CREATE TABLE statement. For example:
-**
-**   sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
-**       -> 'CREATE TABLE t1(a REFERENCES t3)'
-*/
-#ifndef SQLITE_OMIT_FOREIGN_KEY
-static void renameParentFunc(
-  sqlite3_context *context,
-  int NotUsed,
-  sqlite3_value **argv
-){
-  sqlite3 *db = sqlite3_context_db_handle(context);
-  char *zOutput = 0;
-  char *zResult;
-  unsigned char const *zInput = sqlite3_value_text(argv[0]);
-  unsigned char const *zOld = sqlite3_value_text(argv[1]);
-  unsigned char const *zNew = sqlite3_value_text(argv[2]);
-
-  unsigned const char *z;         /* Pointer to token */
-  int n;                          /* Length of token z */
-  int token;                      /* Type of token */
-
-  UNUSED_PARAMETER(NotUsed);
-  if( zInput==0 || zOld==0 ) return;
-  for(z=zInput; *z; z=z+n){
-    n = sqlite3GetToken(z, &token);
-    if( token==TK_REFERENCES ){
-      char *zParent;
-      do {
-        z += n;
-        n = sqlite3GetToken(z, &token);
-      }while( token==TK_SPACE );
-
-      if( token==TK_ILLEGAL ) break;
-      zParent = sqlite3DbStrNDup(db, (const char *)z, n);
-      if( zParent==0 ) break;
-      sqlite3Dequote(zParent);
-      if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
-        char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", 
-            (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
-        );
-        sqlite3DbFree(db, zOutput);
-        zOutput = zOut;
-        zInput = &z[n];
-      }
-      sqlite3DbFree(db, zParent);
-    }
-  }
-
-  zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput);
-  sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC);
-  sqlite3DbFree(db, zOutput);
-}
-#endif
-
-#ifndef SQLITE_OMIT_TRIGGER
-/* This function is used by SQL generated to implement the
-** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER 
-** statement. The second is a table name. The table name in the CREATE 
-** TRIGGER statement is replaced with the third argument and the result 
-** returned. This is analagous to renameTableFunc() above, except for CREATE
-** TRIGGER, not CREATE INDEX and CREATE TABLE.
-*/
-static void renameTriggerFunc(
-  sqlite3_context *context,
-  int NotUsed,
-  sqlite3_value **argv
-){
-  unsigned char const *zSql = sqlite3_value_text(argv[0]);
-  unsigned char const *zTableName = sqlite3_value_text(argv[1]);
-
-  int token;
-  Token tname;
-  int dist = 3;
-  unsigned char const *zCsr = zSql;
-  int len = 0;
-  char *zRet;
-  sqlite3 *db = sqlite3_context_db_handle(context);
-
-  UNUSED_PARAMETER(NotUsed);
-
-  /* The principle used to locate the table name in the CREATE TRIGGER 
-  ** statement is that the table name is the first token that is immediately
-  ** preceded by either TK_ON or TK_DOT and immediately followed by one
-  ** of TK_WHEN, TK_BEGIN or TK_FOR.
-  */
-  if( zSql ){
-    do {
-
-      if( !*zCsr ){
-        /* Ran out of input before finding the table name. Return NULL. */
-        return;
-      }
-
-      /* Store the token that zCsr points to in tname. */
-      tname.z = (char*)zCsr;
-      tname.n = len;
-
-      /* Advance zCsr to the next token. Store that token type in 'token',
-      ** and its length in 'len' (to be used next iteration of this loop).
-      */
-      do {
-        zCsr += len;
-        len = sqlite3GetToken(zCsr, &token);
-      }while( token==TK_SPACE );
-      assert( len>0 || !*zCsr );
-
-      /* Variable 'dist' stores the number of tokens read since the most
-      ** recent TK_DOT or TK_ON. This means that when a WHEN, FOR or BEGIN 
-      ** token is read and 'dist' equals 2, the condition stated above
-      ** to be met.
-      **
-      ** Note that ON cannot be a database, table or column name, so
-      ** there is no need to worry about syntax like 
-      ** "CREATE TRIGGER ... ON ON.ON BEGIN ..." etc.
-      */
-      dist++;
-      if( token==TK_DOT || token==TK_ON ){
-        dist = 0;
-      }
-    } while( dist!=2 || (token!=TK_WHEN && token!=TK_FOR && token!=TK_BEGIN) );
-
-    /* Variable tname now contains the token that is the old table-name
-    ** in the CREATE TRIGGER statement.
-    */
-    zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
-       zSql, zTableName, tname.z+tname.n);
-    sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
-  }
-}
-#endif   /* !SQLITE_OMIT_TRIGGER */
-
 /*
 ** This function is used to create the text of expressions of the form:
 **
@@ -383,9 +178,6 @@
   int nTabName;             /* Number of UTF-8 characters in zTabName */
   const char *zTabName;     /* Original name of the table */
   Vdbe *v;
-#ifndef SQLITE_OMIT_TRIGGER
-  char *zWhere = 0;         /* Where clause to locate temp triggers */
-#endif
   VTable *pVTab = 0;        /* Non-zero if this is a v-tab with an xRename() */
   u32 savedDbFlags;         /* Saved value of db->mDbFlags */
 
@@ -479,31 +271,20 @@
   zTabName = pTab->zName;
   nTabName = sqlite3Utf8CharLen(zTabName, -1);
 
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-  if( db->flags&SQLITE_ForeignKeys ){
-    /* If foreign-key support is enabled, rewrite the CREATE TABLE 
-    ** statements corresponding to all child tables of foreign key constraints
-    ** for which the renamed table is the parent table.  */
-    if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){
-      sqlite3NestedParse(pParse, 
-          "UPDATE \"%w\".%s SET "
-              "sql = sqlite_rename_parent(sql, %Q, %Q) "
-              "WHERE %s;", zDb, MASTER_NAME, zTabName, zName, zWhere);
-      sqlite3DbFree(db, zWhere);
-    }
-  }
-#endif
+  /* Rewrite all CREATE TABLE, INDEX, TRIGGER or VIEW statements in
+  ** the schema to use the new table name.  */
+  sqlite3NestedParse(pParse, 
+      "UPDATE \"%w\".%s SET "
+      "sql = sqlite_rename_table(%Q, sql, %Q, %Q, 0) "
+      "WHERE (type!='index' OR tbl_name=%Q COLLATE nocase)"
+      "AND   name NOT LIKE 'sqlite_%%'"
+      , zDb, MASTER_NAME, zDb, zTabName, zName, zTabName
+  );
 
-  /* Modify the sqlite_master table to use the new table name. */
+  /* Update the tbl_name and name columns of the sqlite_master table
+  ** as required.  */
   sqlite3NestedParse(pParse,
       "UPDATE %Q.%s SET "
-#ifdef SQLITE_OMIT_TRIGGER
-          "sql = sqlite_rename_table(sql, %Q), "
-#else
-          "sql = CASE "
-            "WHEN type = 'trigger' THEN sqlite_rename_trigger(sql, %Q)"
-            "ELSE sqlite_rename_table(sql, %Q) END, "
-#endif
           "tbl_name = %Q, "
           "name = CASE "
             "WHEN type='table' THEN %Q "
@@ -512,11 +293,9 @@
             "ELSE name END "
       "WHERE tbl_name=%Q COLLATE nocase AND "
           "(type='table' OR type='index' OR type='trigger');", 
-      zDb, MASTER_NAME, zName, zName, zName, 
-#ifndef SQLITE_OMIT_TRIGGER
-      zName,
-#endif
-      zName, nTabName, zTabName
+      zDb, MASTER_NAME, 
+      zName, zName, zName, 
+      nTabName, zTabName
   );
 
 #ifndef SQLITE_OMIT_AUTOINCREMENT
@@ -530,35 +309,21 @@
   }
 #endif
 
-#ifndef SQLITE_OMIT_TRIGGER
-  /* If there are TEMP triggers on this table, modify the sqlite_temp_master
-  ** table. Don't do this if the table being ALTERed is itself located in
-  ** the temp database.
-  */
-  if( (zWhere=whereTempTriggers(pParse, pTab))!=0 ){
+  /* If the table being renamed is not itself part of the temp database,
+  ** edit view and trigger definitions within the temp database 
+  ** as required.  */
+  if( iDb!=1 ){
     sqlite3NestedParse(pParse, 
         "UPDATE sqlite_temp_master SET "
-            "sql = sqlite_rename_trigger(sql, %Q), "
-            "tbl_name = %Q "
-            "WHERE %s;", zName, zName, zWhere);
-    sqlite3DbFree(db, zWhere);
+            "sql = sqlite_rename_table(%Q, sql, %Q, %Q, 1), "
+            "tbl_name = "
+              "CASE WHEN tbl_name=%Q COLLATE nocase THEN %Q ELSE tbl_name END "
+            "WHERE type IN ('view', 'trigger')"
+        , zDb, zTabName, zName, zTabName, zTabName, zName);
   }
-#endif
 
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-  if( db->flags&SQLITE_ForeignKeys ){
-    FKey *p;
-    for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
-      Table *pFrom = p->pFrom;
-      if( pFrom!=pTab ){
-        reloadTableSchema(pParse, p->pFrom, pFrom->zName);
-      }
-    }
-  }
-#endif
-
-  /* Drop and reload the internal table schema. */
-  reloadTableSchema(pParse, pTab, zName);
+  sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, iDb, 0);
+  if( iDb!=1 ) sqlite3VdbeAddParseSchemaOp(pParse->pVdbe, 1, 0);
 
 exit_rename_table:
   sqlite3SrcListDelete(db, pSrc);
@@ -946,6 +711,16 @@
   const char *zOld;               /* Old column name */
 };
 
+void renameTokenClear(Parse *pParse, void *pPtr){
+  RenameToken *p;
+  assert( pPtr );
+  for(p=pParse->pRename; p; p=p->pNext){
+    if( p->p==pPtr ){
+      p->p = 0;
+    }
+  }
+}
+
 /*
 ** Add a new RenameToken object mapping parse tree element pPtr into
 ** token *pToken to the Parse object currently under construction.
@@ -954,6 +729,9 @@
 */
 void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
   RenameToken *pNew;
+  assert( pPtr || pParse->db->mallocFailed );
+
+  renameTokenClear(pParse, pPtr);
   pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
   if( pNew ){
     pNew->p = pPtr;
@@ -961,6 +739,7 @@
     pNew->pNext = pParse->pRename;
     pParse->pRename = pNew;
   }
+
   return pPtr;
 }
 
@@ -971,13 +750,13 @@
 */
 void sqlite3RenameTokenRemap(Parse *pParse, void *pTo, void *pFrom){
   RenameToken *p;
-  for(p=pParse->pRename; ALWAYS(p); p=p->pNext){
+  if( pTo ) renameTokenClear(pParse, pTo);
+  for(p=pParse->pRename; p; p=p->pNext){
     if( p->p==pFrom ){
       p->p = pTo;
       break;
     }
   }
-  assert( pTo==0 || p );
 }
 
 /*
@@ -1143,6 +922,221 @@
   }
 }
 
+static int renameParseSql(
+  Parse *p, 
+  const char *zDb, 
+  int bTable, 
+  sqlite3 *db, 
+  const char *zSql,
+  int bTemp
+){
+  int rc;
+  char *zErr = 0;
+
+  db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
+
+  /* Parse the SQL statement passed as the first argument. If no error
+  ** occurs and the parse does not result in a new table, index or
+  ** trigger object, the database must be corrupt. */
+  memset(p, 0, sizeof(Parse));
+  p->eParseMode = (bTable ? PARSE_MODE_RENAME_TABLE : PARSE_MODE_RENAME_COLUMN);
+  p->db = db;
+  p->nQueryLoop = 1;
+  rc = sqlite3RunParser(p, zSql, &zErr);
+  assert( p->zErrMsg==0 );
+  assert( rc!=SQLITE_OK || zErr==0 );
+  assert( (0!=p->pNewTable) + (0!=p->pNewIndex) + (0!=p->pNewTrigger)<2 );
+  p->zErrMsg = zErr;
+  if( db->mallocFailed ) rc = SQLITE_NOMEM;
+  if( rc==SQLITE_OK 
+   && p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0 
+  ){
+    rc = SQLITE_CORRUPT_BKPT;
+  }
+
+#ifdef SQLITE_DEBUG
+  /* Ensure that all mappings in the Parse.pRename list really do map to
+  ** a part of the input string.  */
+  if( rc==SQLITE_OK ){
+    int nSql = sqlite3Strlen30(zSql);
+    RenameToken *pToken;
+    for(pToken=p->pRename; pToken; pToken=pToken->pNext){
+      assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
+    }
+  }
+#endif
+
+  db->init.iDb = 0;
+  return rc;
+}
+
+static int renameEditSql(
+  sqlite3_context *pCtx,          /* Return result here */
+  RenameCtx *pRename,             /* Rename context */
+  const char *zSql,               /* SQL statement to edit */
+  const char *zNew,               /* New token text */
+  int bQuote                      /* True to always quote token */
+){
+  int nNew = sqlite3Strlen30(zNew);
+  int nSql = sqlite3Strlen30(zSql);
+  sqlite3 *db = sqlite3_context_db_handle(pCtx);
+  int rc = SQLITE_OK;
+  char *zQuot;
+  char *zOut;
+  int nQuot;
+
+  /* Set zQuot to point to a buffer containing a quoted copy of the 
+  ** identifier zNew. If the corresponding identifier in the original 
+  ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
+  ** point to zQuot so that all substitutions are made using the
+  ** quoted version of the new column name.  */
+  zQuot = sqlite3_mprintf("\"%w\"", zNew);
+  if( zQuot==0 ){
+    return SQLITE_NOMEM;
+  }else{
+    nQuot = sqlite3Strlen30(zQuot);
+  }
+  if( bQuote ){
+    zNew = zQuot;
+    nNew = nQuot;
+  }
+
+  /* At this point pRename->pList contains a list of RenameToken objects
+  ** corresponding to all tokens in the input SQL that must be replaced
+  ** with the new column name. All that remains is to construct and
+  ** return the edited SQL string. */
+  assert( nQuot>=nNew );
+  zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
+  if( zOut ){
+    int nOut = nSql;
+    memcpy(zOut, zSql, nSql);
+    while( pRename->pList ){
+      int iOff;                   /* Offset of token to replace in zOut */
+      RenameToken *pBest = renameColumnTokenNext(pRename);
+
+      u32 nReplace;
+      const char *zReplace;
+      if( sqlite3IsIdChar(*pBest->t.z) ){
+        nReplace = nNew;
+        zReplace = zNew;
+      }else{
+        nReplace = nQuot;
+        zReplace = zQuot;
+      }
+
+      iOff = pBest->t.z - zSql;
+      if( pBest->t.n!=nReplace ){
+        memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], 
+            nOut - (iOff + pBest->t.n)
+        );
+        nOut += nReplace - pBest->t.n;
+        zOut[nOut] = '\0';
+      }
+      memcpy(&zOut[iOff], zReplace, nReplace);
+      sqlite3DbFree(db, pBest);
+    }
+
+    sqlite3_result_text(pCtx, zOut, -1, SQLITE_TRANSIENT);
+    sqlite3DbFree(db, zOut);
+  }else{
+    rc = SQLITE_NOMEM;
+  }
+
+  sqlite3_free(zQuot);
+  return rc;
+}
+
+static int renameResolveTrigger(
+  Parse *pParse,
+  const char *zDb
+){
+  sqlite3 *db = pParse->db;
+  TriggerStep *pStep;
+  NameContext sNC;
+  int rc = SQLITE_OK;
+
+  memset(&sNC, 0, sizeof(sNC));
+  sNC.pParse = pParse;
+  pParse->pTriggerTab = sqlite3FindTable(db, pParse->pNewTrigger->table, zDb);
+  pParse->eTriggerOp = pParse->pNewTrigger->op;
+
+  /* Resolve symbols in WHEN clause */
+  if( pParse->pNewTrigger->pWhen ){
+    rc = sqlite3ResolveExprNames(&sNC, pParse->pNewTrigger->pWhen);
+  }
+
+  for(pStep=pParse->pNewTrigger->step_list; 
+      rc==SQLITE_OK && pStep; 
+      pStep=pStep->pNext
+  ){
+    if( pStep->pSelect ){
+      sqlite3SelectPrep(pParse, pStep->pSelect, &sNC);
+      if( pParse->nErr ) rc = pParse->rc;
+    }
+    if( rc==SQLITE_OK && pStep->zTarget ){ 
+      Table *pTarget = sqlite3LocateTable(pParse, 0, pStep->zTarget, zDb);
+      if( pTarget==0 ){
+        rc = SQLITE_ERROR;
+      }else{
+        SrcList sSrc;
+        memset(&sSrc, 0, sizeof(sSrc));
+        sSrc.nSrc = 1;
+        sSrc.a[0].zName = pStep->zTarget;
+        sSrc.a[0].pTab = pTarget;
+        sNC.pSrcList = &sSrc;
+        if( pStep->pWhere ){
+          rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
+        }
+        if( rc==SQLITE_OK ){
+          rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
+        }
+        assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
+        if( pStep->pUpsert ){
+          Upsert *pUpsert = pStep->pUpsert;
+          assert( rc==SQLITE_OK );
+          pUpsert->pUpsertSrc = &sSrc;
+          sNC.uNC.pUpsert = pUpsert;
+          sNC.ncFlags = NC_UUpsert;
+          rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
+          if( rc==SQLITE_OK ){
+            ExprList *pUpsertSet = pUpsert->pUpsertSet;
+            rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
+          }
+          if( rc==SQLITE_OK ){
+            rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
+          }
+          if( rc==SQLITE_OK ){
+            rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
+          }
+          sNC.ncFlags = 0;
+        }
+      }
+    }
+  }
+  return rc;
+}
+
+static void renameWalkTrigger(Walker *pWalker, Trigger *pTrigger){
+  TriggerStep *pStep;
+
+  /* Find tokens to edit in WHEN clause */
+  sqlite3WalkExpr(pWalker, pTrigger->pWhen);
+
+  /* Find tokens to edit in trigger steps */
+  for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
+    sqlite3WalkSelect(pWalker, pStep->pSelect);
+    sqlite3WalkExpr(pWalker, pStep->pWhere);
+    sqlite3WalkExprList(pWalker, pStep->pExprList);
+    if( pStep->pUpsert ){
+      Upsert *pUpsert = pStep->pUpsert;
+      sqlite3WalkExprList(pWalker, pUpsert->pUpsertTarget);
+      sqlite3WalkExprList(pWalker, pUpsert->pUpsertSet);
+      sqlite3WalkExpr(pWalker, pUpsert->pUpsertWhere);
+      sqlite3WalkExpr(pWalker, pUpsert->pUpsertTargetWhere);
+    }
+  }
+}
+
 /*
 ** SQL function:
 **
@@ -1191,6 +1185,7 @@
   int nNew = sqlite3_value_bytes(argv[6]);
   int bQuote = sqlite3_value_int(argv[7]);
   const char *zOld;
+  int bTemp = 0;
 
   int rc;
   char *zErr = 0;
@@ -1199,8 +1194,6 @@
   Index *pIdx;
   char *zOut = 0;
 
-  char *zQuot = 0;                /* Quoted version of zNew */
-  int nQuot = 0;                  /* Length of zQuot in bytes */
   int i;
   Table *pTab;
 
@@ -1219,54 +1212,7 @@
   memset(&sCtx, 0, sizeof(sCtx));
   sCtx.iCol = ((iCol==pTab->iPKey) ? -1 : iCol);
 
-  /* Parse the SQL statement passed as the first argument. If no error
-  ** occurs and the parse does not result in a new table, index or
-  ** trigger object, the database must be corrupt. */
-  memset(&sParse, 0, sizeof(sParse));
-  sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
-  sParse.db = db;
-  sParse.nQueryLoop = 1;
-  rc = sqlite3RunParser(&sParse, zSql, &zErr);
-  assert( sParse.zErrMsg==0 );
-  assert( rc!=SQLITE_OK || zErr==0 );
-  assert( (!!sParse.pNewTable)+(!!sParse.pNewIndex)+(!!sParse.pNewTrigger)<2 );
-  sParse.zErrMsg = zErr;
-  if( db->mallocFailed ) rc = SQLITE_NOMEM;
-  if( rc==SQLITE_OK 
-   && sParse.pNewTable==0 && sParse.pNewIndex==0 && sParse.pNewTrigger==0 
-  ){
-    rc = SQLITE_CORRUPT_BKPT;
-  }
-
-#ifdef SQLITE_DEBUG
-  /* Ensure that all mappings in the Parse.pRename list really do map to
-  ** a part of the input string.  */
-  assert( sqlite3Strlen30(zSql)==nSql );
-  if( rc==SQLITE_OK ){
-    RenameToken *pToken;
-    for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
-      assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
-    }
-  }
-#endif
-
-  /* Set zQuot to point to a buffer containing a quoted copy of the 
-  ** identifier zNew. If the corresponding identifier in the original 
-  ** ALTER TABLE statement was quoted (bQuote==1), then set zNew to
-  ** point to zQuot so that all substitutions are made using the
-  ** quoted version of the new column name.  */
-  if( rc==SQLITE_OK ){
-    zQuot = sqlite3_mprintf("\"%w\"", zNew);
-    if( zQuot==0 ){
-      rc = SQLITE_NOMEM;
-    }else{
-      nQuot = sqlite3Strlen30(zQuot);
-    }
-  }
-  if( bQuote ){
-    zNew = zQuot;
-    nNew = nQuot;
-  }
+  rc = renameParseSql(&sParse, zDb, 0, db, zSql, bTemp);
 
   /* Find tokens that need to be replaced. */
   memset(&sWalker, 0, sizeof(Walker));
@@ -1325,140 +1271,35 @@
   }else{
     /* A trigger */
     TriggerStep *pStep;
-    NameContext sNC;
-    memset(&sNC, 0, sizeof(sNC));
-    sNC.pParse = &sParse;
-    sParse.pTriggerTab = sqlite3FindTable(db, sParse.pNewTrigger->table, zDb);
-    sParse.eTriggerOp = sParse.pNewTrigger->op;
+    rc = renameResolveTrigger(&sParse, zDb);
+    if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
 
-    /* Resolve symbols in WHEN clause */
-    if( sParse.pNewTrigger->pWhen ){
-      rc = sqlite3ResolveExprNames(&sNC, sParse.pNewTrigger->pWhen);
-    }
-
-    for(pStep=sParse.pNewTrigger->step_list; 
-        rc==SQLITE_OK && pStep; 
-        pStep=pStep->pNext
-    ){
-      if( pStep->pSelect ){
-        sqlite3SelectPrep(&sParse, pStep->pSelect, &sNC);
-        if( sParse.nErr ) rc = sParse.rc;
-      }
-      if( rc==SQLITE_OK && pStep->zTarget ){ 
+    for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
+      if( pStep->zTarget ){ 
         Table *pTarget = sqlite3LocateTable(&sParse, 0, pStep->zTarget, zDb);
-        if( pTarget==0 ){
-          rc = SQLITE_ERROR;
-        }else{
-          SrcList sSrc;
-          memset(&sSrc, 0, sizeof(sSrc));
-          sSrc.nSrc = 1;
-          sSrc.a[0].zName = pStep->zTarget;
-          sSrc.a[0].pTab = pTarget;
-          sNC.pSrcList = &sSrc;
-          if( pStep->pWhere ){
-            rc = sqlite3ResolveExprNames(&sNC, pStep->pWhere);
-          }
-          if( rc==SQLITE_OK ){
-            rc = sqlite3ResolveExprListNames(&sNC, pStep->pExprList);
-          }
-          assert( !pStep->pUpsert || (!pStep->pWhere && !pStep->pExprList) );
+        if( pTarget==pTab ){
           if( pStep->pUpsert ){
-            Upsert *pUpsert = pStep->pUpsert;
-            assert( rc==SQLITE_OK );
-            pUpsert->pUpsertSrc = &sSrc;
-            sNC.uNC.pUpsert = pUpsert;
-            sNC.ncFlags = NC_UUpsert;
-            rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
-            if( rc==SQLITE_OK ){
-              ExprList *pUpsertSet = pUpsert->pUpsertSet;
-              if( pTarget==pTab ){
-                renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
-              }
-              rc = sqlite3ResolveExprListNames(&sNC, pUpsertSet);
-            }
-            if( rc==SQLITE_OK ){
-              rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertWhere);
-            }
-            if( rc==SQLITE_OK ){
-              rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
-            }
-            sNC.ncFlags = 0;
+            ExprList *pUpsertSet = pStep->pUpsert->pUpsertSet;
+            renameColumnElistNames(&sParse, &sCtx, pUpsertSet, zOld);
           }
-
-          if( rc==SQLITE_OK && pTarget==pTab ){
-            renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
-            renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
-          }
+          renameColumnIdlistNames(&sParse, &sCtx, pStep->pIdList, zOld);
+          renameColumnElistNames(&sParse, &sCtx, pStep->pExprList, zOld);
         }
       }
     }
 
-    if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
 
     /* Find tokens to edit in UPDATE OF clause */
     if( sParse.pTriggerTab==pTab ){
       renameColumnIdlistNames(&sParse, &sCtx,sParse.pNewTrigger->pColumns,zOld);
     }
 
-    /* Find tokens to edit in WHEN clause */
-    sqlite3WalkExpr(&sWalker, sParse.pNewTrigger->pWhen);
-
-    /* Find tokens to edit in trigger steps */
-    for(pStep=sParse.pNewTrigger->step_list; pStep; pStep=pStep->pNext){
-      sqlite3WalkSelect(&sWalker, pStep->pSelect);
-      sqlite3WalkExpr(&sWalker, pStep->pWhere);
-      sqlite3WalkExprList(&sWalker, pStep->pExprList);
-      if( pStep->pUpsert ){
-        Upsert *pUpsert = pStep->pUpsert;
-        sqlite3WalkExprList(&sWalker, pUpsert->pUpsertTarget);
-        sqlite3WalkExprList(&sWalker, pUpsert->pUpsertSet);
-        sqlite3WalkExpr(&sWalker, pUpsert->pUpsertWhere);
-        sqlite3WalkExpr(&sWalker, pUpsert->pUpsertTargetWhere);
-      }
-    }
+    /* Find tokens to edit in various expressions and selects */
+    renameWalkTrigger(&sWalker, sParse.pNewTrigger);
   }
 
-  /* At this point sCtx.pList contains a list of RenameToken objects
-  ** corresponding to all tokens in the input SQL that must be replaced
-  ** with the new column name. All that remains is to construct and
-  ** return the edited SQL string. */
   assert( rc==SQLITE_OK );
-  assert( nQuot>=nNew );
-  zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nQuot + 1);
-  if( zOut ){
-    int nOut = nSql;
-    memcpy(zOut, zSql, nSql);
-    while( sCtx.pList ){
-      int iOff;                   /* Offset of token to replace in zOut */
-      RenameToken *pBest = renameColumnTokenNext(&sCtx);
-
-      u32 nReplace;
-      const char *zReplace;
-      if( sqlite3IsIdChar(*pBest->t.z) ){
-        nReplace = nNew;
-        zReplace = zNew;
-      }else{
-        nReplace = nQuot;
-        zReplace = zQuot;
-      }
-
-      iOff = pBest->t.z - zSql;
-      if( pBest->t.n!=nReplace ){
-        memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], 
-            nOut - (iOff + pBest->t.n)
-        );
-        nOut += nReplace - pBest->t.n;
-        zOut[nOut] = '\0';
-      }
-      memcpy(&zOut[iOff], zReplace, nReplace);
-      sqlite3DbFree(db, pBest);
-    }
-
-    sqlite3_result_text(context, zOut, -1, SQLITE_TRANSIENT);
-    sqlite3DbFree(db, zOut);
-  }else{
-    rc = SQLITE_NOMEM;
-  }
+  rc = renameEditSql(context, &sCtx, zSql, zNew, bQuote);
 
 renameColumnFunc_done:
   if( rc!=SQLITE_OK ){
@@ -1479,23 +1320,180 @@
   renameTokenFree(db, sCtx.pList);
   sqlite3DbFree(db, sParse.zErrMsg);
   sqlite3ParserReset(&sParse);
-  sqlite3_free(zQuot);
   sqlite3BtreeLeaveAll(db);
 }
 
+static int renameTableExprCb(Walker *pWalker, Expr *pExpr){
+  RenameCtx *p = pWalker->u.pRename;
+  if( pExpr->op==TK_COLUMN && p->pTab==pExpr->pTab ){
+    renameTokenFind(pWalker->pParse, p, (void*)&pExpr->pTab);
+  }
+  return WRC_Continue;
+}
+
+/*
+** This is a Walker select callback. 
+*/
+static int renameTableSelectCb(Walker *pWalker, Select *pSelect){
+  int i;
+  RenameCtx *p = pWalker->u.pRename;
+  SrcList *pSrc = pSelect->pSrc;
+  for(i=0; i<pSrc->nSrc; i++){
+    struct SrcList_item *pItem = &pSrc->a[i];
+    if( pItem->pTab==p->pTab ){
+      renameTokenFind(pWalker->pParse, p, pItem->zName);
+    }
+  }
+
+  return WRC_Continue;
+}
+
+
+/*
+** This C function implements an SQL user function that is used by SQL code
+** generated by the ALTER TABLE ... RENAME command to modify the definition
+** of any foreign key constraints that use the table being renamed as the 
+** parent table. It is passed three arguments:
+**
+**   1) The complete text of the CREATE TABLE statement being modified,
+**   2) The old name of the table being renamed, and
+**   3) The new name of the table being renamed.
+**
+** It returns the new CREATE TABLE statement. For example:
+**
+**   sqlite_rename_table('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3')
+**       -> 'CREATE TABLE t1(a REFERENCES t3)'
+*/
+static void renameTableFunc(
+  sqlite3_context *context,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  sqlite3 *db = sqlite3_context_db_handle(context);
+  char *zOutput = 0;
+  char *zResult;
+  unsigned char const *zDb = sqlite3_value_text(argv[0]);
+  unsigned char const *zInput = sqlite3_value_text(argv[1]);
+  unsigned char const *zOld = sqlite3_value_text(argv[2]);
+  unsigned char const *zNew = sqlite3_value_text(argv[3]);
+  int bTemp = sqlite3_value_int(argv[4]);
+
+  unsigned const char *z;         /* Pointer to token */
+  int n;                          /* Length of token z */
+  int token;                      /* Type of token */
+
+  Parse sParse;
+  int rc;
+  int bQuote = 1;
+  RenameCtx sCtx;
+  Walker sWalker;
+
+  if( zInput==0 || zOld==0 || zNew==0 ) return;
+
+  memset(&sCtx, 0, sizeof(RenameCtx));
+  sCtx.pTab = sqlite3FindTable(db, zOld, zDb);
+  memset(&sWalker, 0, sizeof(Walker));
+  sWalker.pParse = &sParse;
+  sWalker.xExprCallback = renameTableExprCb;
+  sWalker.xSelectCallback = renameTableSelectCb;
+  sWalker.u.pRename = &sCtx;
+
+  sqlite3BtreeEnterAll(db);
+
+  rc = renameParseSql(&sParse, zDb, 1, db, zInput, bTemp);
+
+  if( rc==SQLITE_OK ){
+    if( sParse.pNewTable ){
+      Table *pTab = sParse.pNewTable;
+
+      if( pTab->pSelect ){
+        NameContext sNC;
+        memset(&sNC, 0, sizeof(sNC));
+        sNC.pParse = &sParse;
+
+        sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
+        if( sParse.nErr ) rc = sParse.rc;
+        sqlite3WalkSelect(&sWalker, pTab->pSelect);
+      }else{
+        /* Modify any FK definitions to point to the new table. */
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+        FKey *pFKey;
+        for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+          if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
+            renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
+          }
+        }
+#endif
+
+        /* If this is the table being altered, fix any table refs in CHECK
+        ** expressions. Also update the name that appears right after the
+        ** "CREATE [VIRTUAL] TABLE" bit. */
+        if( sqlite3_stricmp(zOld, pTab->zName)==0 ){
+          sCtx.pTab = pTab;
+          sqlite3WalkExprList(&sWalker, pTab->pCheck);
+          renameTokenFind(&sParse, &sCtx, pTab->zName);
+        }
+      }
+    }
+
+    else if( sParse.pNewIndex ){
+      renameTokenFind(&sParse, &sCtx, sParse.pNewIndex->zName);
+      sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
+    }
+
+#ifndef SQLITE_OMIT_TRIGGER
+    else if( sParse.pNewTrigger ){
+      Trigger *pTrigger = sParse.pNewTrigger;
+      TriggerStep *pStep;
+      if( 0==sqlite3_stricmp(sParse.pNewTrigger->table, zOld) 
+       && sCtx.pTab->pSchema==pTrigger->pTabSchema
+      ){
+        renameTokenFind(&sParse, &sCtx, sParse.pNewTrigger->table);
+      }
+
+      rc = renameResolveTrigger(&sParse, zDb);
+      if( rc==SQLITE_OK ){
+        renameWalkTrigger(&sWalker, pTrigger);
+      }
+
+      for(pStep=pTrigger->step_list; pStep; pStep=pStep->pNext){
+        if( pStep->zTarget && 0==sqlite3_stricmp(pStep->zTarget, zOld) ){
+          renameTokenFind(&sParse, &sCtx, pStep->zTarget);
+        }
+      }
+    }
+#endif
+  }
+
+  if( rc==SQLITE_OK ){
+    rc = renameEditSql(context, &sCtx, zInput, zNew, bQuote);
+  }
+
+  if( rc!=SQLITE_OK ){
+    sqlite3_result_error_code(context, rc);
+  }
+  if( sParse.pVdbe ){
+    sqlite3VdbeFinalize(sParse.pVdbe);
+  }
+  sqlite3DeleteTable(db, sParse.pNewTable);
+  if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex);
+  sqlite3DeleteTrigger(db, sParse.pNewTrigger);
+  renameTokenFree(db, sParse.pRename);
+  renameTokenFree(db, sCtx.pList);
+  sqlite3DbFree(db, sParse.zErrMsg);
+  sqlite3ParserReset(&sParse);
+  sqlite3BtreeLeaveAll(db);
+
+  return;
+}
+
 /*
 ** Register built-in functions used to help implement ALTER TABLE
 */
 void sqlite3AlterFunctions(void){
   static FuncDef aAlterTableFuncs[] = {
-    FUNCTION(sqlite_rename_table,   2, 0, 0, renameTableFunc),
     FUNCTION(sqlite_rename_column,  8, 0, 0, renameColumnFunc),
-#ifndef SQLITE_OMIT_TRIGGER
-    FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
-#endif
-#ifndef SQLITE_OMIT_FOREIGN_KEY
-    FUNCTION(sqlite_rename_parent,  3, 0, 0, renameParentFunc),
-#endif
+    FUNCTION(sqlite_rename_table,  5, 0, 0, renameTableFunc),
   };
   sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
 }
diff --git a/src/build.c b/src/build.c
index 7f8013a..4ab467b 100644
--- a/src/build.c
+++ b/src/build.c
@@ -783,7 +783,7 @@
       return -1;
     }
   }else{
-    assert( db->init.iDb==0 || db->init.busy
+    assert( db->init.iDb==0 || db->init.busy || IN_RENAME_OBJECT
              || (db->mDbFlags & DBFLAG_Vacuum)!=0);
     iDb = db->init.iDb;
     *pUnqual = pName1;
@@ -878,6 +878,9 @@
     }
     if( !OMIT_TEMPDB && isTemp ) iDb = 1;
     zName = sqlite3NameFromToken(db, pName);
+    if( IN_RENAME_OBJECT ){
+      sqlite3RenameTokenMap(pParse, (void*)zName, pName);
+    }
   }
   pParse->sNameToken = *pName;
   if( zName==0 ) return;
@@ -1072,7 +1075,7 @@
   }
   z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
   if( z==0 ) return;
-  if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
+  if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
   memcpy(z, pName->z, pName->n);
   z[pName->n] = 0;
   sqlite3Dequote(z);
@@ -1370,7 +1373,7 @@
    && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
    && sortOrder!=SQLITE_SO_DESC
   ){
-    if( IN_RENAME_COLUMN && pList ){
+    if( IN_RENAME_OBJECT && pList ){
       sqlite3RenameTokenRemap(pParse, &pTab->iPKey, pList->a[0].pExpr);
     }
     pTab->iPKey = iCol;
@@ -2175,7 +2178,7 @@
   ** allocated rather than point to the input string - which means that
   ** they will persist after the current sqlite3_exec() call returns.
   */
-  if( IN_RENAME_COLUMN ){
+  if( IN_RENAME_OBJECT ){
     p->pSelect = pSelect;
     pSelect = 0;
   }else{
@@ -2748,6 +2751,9 @@
   pFKey->pNextFrom = p->pFKey;
   z = (char*)&pFKey->aCol[nCol];
   pFKey->zTo = z;
+  if( IN_RENAME_OBJECT ){
+    sqlite3RenameTokenMap(pParse, (void*)z, pTo);
+  }
   memcpy(z, pTo->z, pTo->n);
   z[pTo->n] = 0;
   sqlite3Dequote(z);
@@ -2770,7 +2776,7 @@
           pFromCol->a[i].zName);
         goto fk_end;
       }
-      if( IN_RENAME_COLUMN ){
+      if( IN_RENAME_OBJECT ){
         sqlite3RenameTokenRemap(pParse, &pFKey->aCol[i], pFromCol->a[i].zName);
       }
     }
@@ -2779,7 +2785,7 @@
     for(i=0; i<nCol; i++){
       int n = sqlite3Strlen30(pToCol->a[i].zName);
       pFKey->aCol[i].zCol = z;
-      if( IN_RENAME_COLUMN ){
+      if( IN_RENAME_OBJECT ){
         sqlite3RenameTokenRemap(pParse, z, pToCol->a[i].zName);
       }
       memcpy(z, pToCol->a[i].zName, n);
@@ -3114,7 +3120,7 @@
     if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
       goto exit_create_index;
     }
-    if( !IN_RENAME_COLUMN ){
+    if( !IN_RENAME_OBJECT ){
       if( !db->init.busy ){
         if( sqlite3FindTable(db, zName, 0)!=0 ){
           sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
@@ -3151,7 +3157,7 @@
   /* Check for authorization to create an index.
   */
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  if( !IN_RENAME_COLUMN ){
+  if( !IN_RENAME_OBJECT ){
     const char *zDb = pDb->zDbSName;
     if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
       goto exit_create_index;
@@ -3239,7 +3245,7 @@
   ** index key.
   */
   pListItem = pList->a;
-  if( IN_RENAME_COLUMN ){
+  if( IN_RENAME_OBJECT ){
     pIndex->aColExpr = pList;
     pList = 0;
   }
@@ -3399,7 +3405,7 @@
     }
   }
 
-  if( !IN_RENAME_COLUMN ){
+  if( !IN_RENAME_OBJECT ){
 
     /* Link the new Index structure to its table and to the other
     ** in-memory database structures. 
@@ -3517,7 +3523,7 @@
     }
     pIndex = 0;
   }
-  else if( IN_RENAME_COLUMN ){
+  else if( IN_RENAME_OBJECT ){
     assert( pParse->pNewIndex==0 );
     pParse->pNewIndex = pIndex;
     pIndex = 0;
@@ -3713,7 +3719,7 @@
     return 0;
   }
   pList->a[i].zName = sqlite3NameFromToken(db, pToken);
-  if( IN_RENAME_COLUMN && pList->a[i].zName ){
+  if( IN_RENAME_OBJECT && pList->a[i].zName ){
     sqlite3RenameTokenMap(pParse, (void*)pList->a[i].zName, pToken);
   }
   return pList;
@@ -3962,6 +3968,10 @@
   }
   assert( p->nSrc>0 );
   pItem = &p->a[p->nSrc-1];
+  if( IN_RENAME_OBJECT && pItem->zName ){
+    Token *pToken = (pDatabase && pDatabase->z) ? pDatabase : pTable;
+    sqlite3RenameTokenMap(pParse, pItem->zName, pToken);
+  }
   assert( pAlias!=0 );
   if( pAlias->n ){
     pItem->zAlias = sqlite3NameFromToken(db, pAlias);
diff --git a/src/expr.c b/src/expr.c
index 6f9c733..701f108 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -1666,7 +1666,7 @@
     assert( pItem->zName==0 );
     pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
     if( dequote ) sqlite3Dequote(pItem->zName);
-    if( IN_RENAME_COLUMN ){
+    if( IN_RENAME_OBJECT ){
       sqlite3RenameTokenMap(pParse, (void*)pItem->zName, pName);
     }
   }
diff --git a/src/parse.y b/src/parse.y
index 44e300e..8bf5d83 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -325,7 +325,7 @@
     sqlite3ExprIdToTrueFalse(p);
     testcase( p->op==TK_TRUEFALSE && sqlite3ExprTruthValue(p) );
   }
-  sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n);
+    sqlite3AddDefaultValue(pParse,p,X.z,X.z+X.n);
 }
 
 // In addition to the type name, we also care about the primary key and
@@ -683,10 +683,14 @@
 
 %type fullname {SrcList*}
 %destructor fullname {sqlite3SrcListDelete(pParse->db, $$);}
-fullname(A) ::= nm(X).  
-   {A = sqlite3SrcListAppend(pParse->db,0,&X,0); /*A-overwrites-X*/}
-fullname(A) ::= nm(X) DOT nm(Y).  
-   {A = sqlite3SrcListAppend(pParse->db,0,&X,&Y); /*A-overwrites-X*/}
+fullname(A) ::= nm(X).  {
+  A = sqlite3SrcListAppend(pParse->db,0,&X,0);
+  if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &X);
+}
+fullname(A) ::= nm(X) DOT nm(Y). {
+  A = sqlite3SrcListAppend(pParse->db,0,&X,&Y);
+  if( IN_RENAME_OBJECT && A ) sqlite3RenameTokenMap(pParse, A->a[0].zName, &Y);
+}
 
 %type xfullname {SrcList*}
 %destructor xfullname {sqlite3SrcListDelete(pParse->db, $$);}
@@ -954,7 +958,7 @@
 #if SQLITE_MAX_EXPR_DEPTH>0
       p->nHeight = 1;
 #endif  
-      if( IN_RENAME_COLUMN ){
+      if( IN_RENAME_OBJECT ){
         return (Expr*)sqlite3RenameTokenMap(pParse, (void*)p, &t);
       }
     }
@@ -970,7 +974,10 @@
 expr(A) ::= nm(X) DOT nm(Y). {
   Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &X, 1);
   Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
-  if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
+  if( IN_RENAME_OBJECT ){
+    sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
+    sqlite3RenameTokenMap(pParse, (void*)temp1, &X);
+  }
   A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
 }
 expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
@@ -978,7 +985,10 @@
   Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &Y, 1);
   Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &Z, 1);
   Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
-  if( IN_RENAME_COLUMN ) sqlite3RenameTokenMap(pParse, (void*)temp3, &Z);
+  if( IN_RENAME_OBJECT ){
+    sqlite3RenameTokenMap(pParse, (void*)temp3, &Z);
+    sqlite3RenameTokenMap(pParse, (void*)temp2, &Y);
+  }
   A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
 }
 term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
@@ -1277,6 +1287,9 @@
   sqlite3CreateIndex(pParse, &X, &D, 
                      sqlite3SrcListAppend(pParse->db,0,&Y,0), Z, U,
                       &S, W, SQLITE_SO_ASC, NE, SQLITE_IDXTYPE_APPDEF);
+  if( IN_RENAME_OBJECT && pParse->pNewIndex ){
+    sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &Y);
+  }
 }
 
 %type uniqueflag {int}
@@ -1325,9 +1338,6 @@
                          pIdToken->n, pIdToken->z);
     }
     sqlite3ExprListSetName(pParse, p, pIdToken, 1);
-    if( IN_RENAME_COLUMN && p ){
-      sqlite3RenameTokenMap(pParse, (void*)(p->a[p->nExpr-1].zName), pIdToken);
-    }
     return p;
   }
 } // end %include
diff --git a/src/prepare.c b/src/prepare.c
index 4d4b98d..602e4dc 100644
--- a/src/prepare.c
+++ b/src/prepare.c
@@ -93,7 +93,7 @@
     rc = db->errCode;
     assert( (rc&0xFF)==(rcp&0xFF) );
     db->init.iDb = saved_iDb;
-    assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 );
+    /* assert( saved_iDb==0 || (db->mDbFlags & DBFLAG_Vacuum)!=0 ); */
     if( SQLITE_OK!=rc ){
       if( db->init.orphanTrigger ){
         assert( iDb==1 );
diff --git a/src/resolve.c b/src/resolve.c
index fb38332..9fb4d4c 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -264,6 +264,9 @@
           if( sqlite3StrICmp(zTabName, zTab)!=0 ){
             continue;
           }
+          if( IN_RENAME_OBJECT && pItem->zAlias ){
+            sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->pTab);
+          }
         }
         if( 0==(cntTab++) ){
           pMatch = pItem;
@@ -349,7 +352,7 @@
 #ifndef SQLITE_OMIT_UPSERT
           if( pExpr->iTable==2 ){
             testcase( iCol==(-1) );
-            if( IN_RENAME_COLUMN ){
+            if( IN_RENAME_OBJECT ){
               pExpr->iColumn = iCol;
               pExpr->pTab = pTab;
               eNewExprOp = TK_COLUMN;
@@ -442,7 +445,7 @@
           cnt = 1;
           pMatch = 0;
           assert( zTab==0 && zDb==0 );
-          if( IN_RENAME_COLUMN ){
+          if( IN_RENAME_OBJECT ){
             sqlite3RenameTokenRemap(pParse, 0, (void*)pExpr);
           }
           goto lookupname_end;
@@ -672,21 +675,25 @@
         zTable = 0;
         zColumn = pExpr->u.zToken;
       }else{
+        Expr *pLeft = pExpr->pLeft;
         notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
         pRight = pExpr->pRight;
         if( pRight->op==TK_ID ){
           zDb = 0;
-          zTable = pExpr->pLeft->u.zToken;
         }else{
           assert( pRight->op==TK_DOT );
-          zDb = pExpr->pLeft->u.zToken;
-          zTable = pRight->pLeft->u.zToken;
+          zDb = pLeft->u.zToken;
+          pLeft = pRight->pLeft;
           pRight = pRight->pRight;
         }
+        zTable = pLeft->u.zToken;
         zColumn = pRight->u.zToken;
-        if( IN_RENAME_COLUMN ){
+        if( IN_RENAME_OBJECT ){
           sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
         }
+        if( IN_RENAME_OBJECT ){
+          sqlite3RenameTokenRemap(pParse, (void*)&pExpr->pTab, (void*)pLeft);
+        }
       }
       return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
     }
@@ -769,7 +776,7 @@
         }
       }
 
-      if( 0==IN_RENAME_COLUMN ){
+      if( 0==IN_RENAME_OBJECT ){
 #ifndef SQLITE_OMIT_WINDOWFUNC
         assert( is_agg==0 || (pDef->funcFlags & SQLITE_FUNC_MINMAX)
           || (pDef->xValue==0 && pDef->xInverse==0)
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 25f101d..d8e3a79 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -3123,6 +3123,7 @@
 #define PARSE_MODE_NORMAL        0
 #define PARSE_MODE_DECLARE_VTAB  1
 #define PARSE_MODE_RENAME_COLUMN 2
+#define PARSE_MODE_RENAME_TABLE  3
 
 /*
 ** Sizes and pointers of various parts of the Parse object.
@@ -3142,9 +3143,9 @@
 #endif
 
 #if defined(SQLITE_OMIT_ALTERTABLE)
-  #define IN_RENAME_COLUMN 0
+  #define IN_RENAME_OBJECT 0
 #else
-  #define IN_RENAME_COLUMN (pParse->eParseMode==PARSE_MODE_RENAME_COLUMN)
+  #define IN_RENAME_OBJECT (pParse->eParseMode>=PARSE_MODE_RENAME_COLUMN)
 #endif
 
 #if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)
diff --git a/src/tokenize.c b/src/tokenize.c
index 94a5b85..262144f 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -695,7 +695,7 @@
     */
     sqlite3DeleteTable(db, pParse->pNewTable);
   }
-  if( !IN_RENAME_COLUMN ){
+  if( !IN_RENAME_OBJECT ){
     sqlite3DeleteTrigger(db, pParse->pNewTrigger);
   }
 
diff --git a/src/trigger.c b/src/trigger.c
index 330e14e..dd6224d 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -181,7 +181,7 @@
     goto trigger_cleanup;
   }
   assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
-  if( !IN_RENAME_COLUMN ){
+  if( !IN_RENAME_OBJECT ){
     if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
       if( !noErr ){
         sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
@@ -214,7 +214,7 @@
   }
 
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  if( !IN_RENAME_COLUMN ){
+  if( !IN_RENAME_OBJECT ){
     int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
     int code = SQLITE_CREATE_TRIGGER;
     const char *zDb = db->aDb[iTabDb].zDbSName;
@@ -248,7 +248,8 @@
   pTrigger->pTabSchema = pTab->pSchema;
   pTrigger->op = (u8)op;
   pTrigger->tr_tm = tr_tm==TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER;
-  if( IN_RENAME_COLUMN ){
+  if( IN_RENAME_OBJECT ){
+    sqlite3RenameTokenRemap(pParse, pTrigger->table, pTableName->a[0].zName);
     pTrigger->pWhen = pWhen;
     pWhen = 0;
   }else{
@@ -305,7 +306,7 @@
   }
 
 #ifndef SQLITE_OMIT_ALTERTABLE
-  if( IN_RENAME_COLUMN ){
+  if( IN_RENAME_OBJECT ){
     assert( !db->init.busy );
     pParse->pNewTrigger = pTrig;
     pTrig = 0;
@@ -353,7 +354,7 @@
 
 triggerfinish_cleanup:
   sqlite3DeleteTrigger(db, pTrig);
-  assert( IN_RENAME_COLUMN || !pParse->pNewTrigger );
+  assert( IN_RENAME_OBJECT || !pParse->pNewTrigger );
   sqlite3DeleteTriggerStep(db, pStepList);
 }
 
@@ -400,12 +401,13 @@
 ** If an OOM error occurs, NULL is returned and db->mallocFailed is set.
 */
 static TriggerStep *triggerStepAllocate(
-  sqlite3 *db,                /* Database connection */
+  Parse *pParse,              /* Parser context */
   u8 op,                      /* Trigger opcode */
   Token *pName,               /* The target name */
   const char *zStart,         /* Start of SQL text */
   const char *zEnd            /* End of SQL text */
 ){
+  sqlite3 *db = pParse->db;
   TriggerStep *pTriggerStep;
 
   pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
@@ -416,6 +418,9 @@
     pTriggerStep->zTarget = z;
     pTriggerStep->op = op;
     pTriggerStep->zSpan = triggerSpanDup(db, zStart, zEnd);
+    if( IN_RENAME_OBJECT ){
+      sqlite3RenameTokenMap(pParse, pTriggerStep->zTarget, pName);
+    }
   }
   return pTriggerStep;
 }
@@ -442,9 +447,9 @@
 
   assert(pSelect != 0 || db->mallocFailed);
 
-  pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName, zStart, zEnd);
+  pTriggerStep = triggerStepAllocate(pParse, TK_INSERT, pTableName,zStart,zEnd);
   if( pTriggerStep ){
-    if( IN_RENAME_COLUMN ){
+    if( IN_RENAME_OBJECT ){
       pTriggerStep->pSelect = pSelect;
       pSelect = 0;
     }else{
@@ -481,9 +486,9 @@
   sqlite3 *db = pParse->db;
   TriggerStep *pTriggerStep;
 
-  pTriggerStep = triggerStepAllocate(db, TK_UPDATE, pTableName, zStart, zEnd);
+  pTriggerStep = triggerStepAllocate(pParse, TK_UPDATE, pTableName,zStart,zEnd);
   if( pTriggerStep ){
-    if( IN_RENAME_COLUMN ){
+    if( IN_RENAME_OBJECT ){
       pTriggerStep->pExprList = pEList;
       pTriggerStep->pWhere = pWhere;
       pEList = 0;
@@ -514,9 +519,9 @@
   sqlite3 *db = pParse->db;
   TriggerStep *pTriggerStep;
 
-  pTriggerStep = triggerStepAllocate(db, TK_DELETE, pTableName, zStart, zEnd);
+  pTriggerStep = triggerStepAllocate(pParse, TK_DELETE, pTableName,zStart,zEnd);
   if( pTriggerStep ){
-    if( IN_RENAME_COLUMN ){
+    if( IN_RENAME_OBJECT ){
       pTriggerStep->pWhere = pWhere;
       pWhere = 0;
     }else{