Experimental implementation of ALTER TABLE ... RENAME COLUMN. Still buggy.

FossilOrigin-Name: fa0fc01eb48a864f0a3d43f9b805d5ed2e530846ee0c34fcbc2eabd9e5696277
diff --git a/src/alter.c b/src/alter.c
index 2d7a5d6..c4dc00b 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -226,22 +226,6 @@
 #endif   /* !SQLITE_OMIT_TRIGGER */
 
 /*
-** Register built-in functions used to help implement ALTER TABLE
-*/
-void sqlite3AlterFunctions(void){
-  static FuncDef aAlterTableFuncs[] = {
-    FUNCTION(sqlite_rename_table,   2, 0, 0, renameTableFunc),
-#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
-  };
-  sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
-}
-
-/*
 ** This function is used to create the text of expressions of the form:
 **
 **   name=<constant1> OR name=<constant2> OR ...
@@ -805,4 +789,306 @@
   sqlite3SrcListDelete(db, pSrc);
   return;
 }
+
+void sqlite3AlterRenameColumn(
+  Parse *pParse, 
+  SrcList *pSrc, 
+  Token *pOld, 
+  Token *pNew
+){
+  sqlite3 *db = pParse->db;
+  Table *pTab;                    /* Table being updated */
+  int iCol;                       /* Index of column being renamed */
+  char *zOld = 0;
+  char *zNew = 0;
+  const char *zDb;
+  int iSchema;
+
+  pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
+  if( !pTab ) goto exit_rename_column;
+  iSchema = sqlite3SchemaToIndex(db, pTab->pSchema);
+  assert( iSchema>=0 );
+  zDb = db->aDb[iSchema].zDbSName;
+
+  zOld = sqlite3NameFromToken(db, pOld);
+  if( !zOld ) goto exit_rename_column;
+  for(iCol=0; iCol<pTab->nCol; iCol++){
+    if( 0==sqlite3StrICmp(pTab->aCol[iCol].zName, zOld) ) break;
+  }
+  if( iCol==pTab->nCol ){
+    sqlite3ErrorMsg(pParse, "no such column: \"%s\"", zOld);
+    goto exit_rename_column;
+  }
+
+  zNew = sqlite3NameFromToken(db, pNew);
+  if( !zNew ) goto exit_rename_column;
+
+  sqlite3NestedParse(pParse, 
+      "UPDATE \"%w\".%s SET "
+      "sql = sqlite_rename_column(sql, %d, %Q) "
+      "WHERE type IN ('table', 'index') AND tbl_name = %Q AND sql!=''",
+      zDb, MASTER_NAME, iCol, zNew, pTab->zName
+  );
+
+  /* Drop and reload the internal table schema. */
+  reloadTableSchema(pParse, pTab, pTab->zName);
+
+ exit_rename_column:
+  sqlite3SrcListDelete(db, pSrc);
+  sqlite3DbFree(db, zOld);
+  sqlite3DbFree(db, zNew);
+  return;
+}
+
+struct RenameToken {
+  void *p;
+  Token t;
+  RenameToken *pNext;
+};
+
+struct RenameCtx {
+  RenameToken *pList;             /* List of tokens to overwrite */
+  int nList;                      /* Number of tokens in pList */
+  int iCol;                       /* Index of column being renamed */
+};
+
+void sqlite3RenameToken(Parse *pParse, void *pPtr, Token *pToken){
+  RenameToken *pNew;
+  pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
+  if( pNew ){
+    pNew->p = pPtr;
+    pNew->t = *pToken;
+    pNew->pNext = pParse->pRename;
+    pParse->pRename = pNew;
+  }
+}
+
+void sqlite3MoveRenameToken(Parse *pParse, void *pTo, void *pFrom){
+  RenameToken *p;
+  for(p=pParse->pRename; p; p=p->pNext){
+    if( p->p==pFrom ){
+      p->p = pTo;
+      break;
+    }
+  }
+  assert( p );
+}
+
+static void renameTokenFree(sqlite3 *db, RenameToken *pToken){
+  RenameToken *pNext;
+  RenameToken *p;
+  for(p=pToken; p; p=pNext){
+    pNext = p->pNext;
+    sqlite3DbFree(db, p);
+  }
+}
+
+static RenameToken *renameTokenFind(Parse *pParse, void *pPtr){
+  RenameToken **pp;
+  for(pp=&pParse->pRename; (*pp); pp=&(*pp)->pNext){
+    if( (*pp)->p==pPtr ){
+      RenameToken *pToken = *pp;
+      *pp = pToken->pNext;
+      pToken->pNext = 0;
+      return pToken;
+    }
+  }
+  return 0;
+}
+
+static int renameColumnExprCb(Walker *pWalker, Expr *pExpr){
+  struct RenameCtx *p = pWalker->u.pRename;
+  if( pExpr->op==TK_COLUMN && pExpr->iColumn==p->iCol ){
+    RenameToken *pTok = renameTokenFind(pWalker->pParse, (void*)pExpr);
+    if( pTok ){
+      pTok->pNext = p->pList;
+      p->pList = pTok;
+      p->nList++;
+    }
+  }
+  return WRC_Continue;
+}
+
+static RenameToken *renameColumnTokenNext(struct RenameCtx *pCtx){
+  RenameToken *pBest = pCtx->pList;
+  RenameToken *pToken;
+  RenameToken **pp;
+
+  for(pToken=pBest->pNext; pToken; pToken=pToken->pNext){
+    if( pToken->t.z>pBest->t.z ) pBest = pToken;
+  }
+  for(pp=&pCtx->pList; *pp!=pBest; pp=&(*pp)->pNext);
+  *pp = pBest->pNext;
+
+  return pBest;
+}
+
+static void renameColumnFunc(
+  sqlite3_context *context,
+  int NotUsed,
+  sqlite3_value **argv
+){
+  sqlite3 *db = sqlite3_context_db_handle(context);
+  struct RenameCtx sCtx;
+  const char *zSql = sqlite3_value_text(argv[0]);
+  int nSql = sqlite3_value_bytes(argv[0]);
+  const char *zNew = sqlite3_value_text(argv[2]);
+  int nNew = sqlite3_value_bytes(argv[2]);
+  int rc;
+  char *zErr = 0;
+  Parse sParse;
+  Walker sWalker;
+  Table *pTab;
+  Index *pIdx;
+  char *zOut = 0;
+
+  char *zQuot = 0;                /* Quoted version of zNew */
+  int nQuot = 0;                  /* Length of zQuot in bytes */
+  int i;
+
+  memset(&sCtx, 0, sizeof(sCtx));
+  sCtx.iCol = sqlite3_value_int(argv[1]);
+
+  memset(&sParse, 0, sizeof(sParse));
+  sParse.eParseMode = PARSE_MODE_RENAME_COLUMN;
+  sParse.db = db;
+  sParse.nQueryLoop = 1;
+  rc = sqlite3RunParser(&sParse, zSql, &zErr);
+  assert( sParse.pNewTable==0 || sParse.pNewIndex==0 );
+  if( rc==SQLITE_OK && sParse.pNewTable==0 && sParse.pNewIndex==0 ){
+    rc = SQLITE_CORRUPT_BKPT;
+  }
+
+  if( rc==SQLITE_OK ){
+    zQuot = sqlite3_mprintf("\"%w\"", zNew);
+    if( zQuot==0 ){
+      rc = SQLITE_NOMEM;
+    }else{
+      nQuot = sqlite3Strlen30(zQuot);
+    }
+  }
+
+  if( rc!=SQLITE_OK ){
+    if( zErr ){
+      sqlite3_result_error(context, zErr, -1);
+    }else{
+      sqlite3_result_error_code(context, rc);
+    }
+    sqlite3DbFree(db, zErr);
+    sqlite3_free(zQuot);
+    return;
+  }
+
+  for(i=0; i<nNew; i++){
+    if( sqlite3IsIdChar(zNew[i])==0 ){
+      zNew = zQuot;
+      nNew = nQuot;
+      break;
+    }
+  }
+
+#ifdef SQLITE_DEBUG
+  assert( sqlite3Strlen30(zSql)==nSql );
+  {
+    RenameToken *pToken;
+    for(pToken=sParse.pRename; pToken; pToken=pToken->pNext){
+      assert( pToken->t.z>=zSql && &pToken->t.z[pToken->t.n]<=&zSql[nSql] );
+    }
+  }
+#endif
+
+  /* Find tokens that need to be replaced. */
+  memset(&sWalker, 0, sizeof(Walker));
+  sWalker.pParse = &sParse;
+  sWalker.xExprCallback = renameColumnExprCb;
+  sWalker.u.pRename = &sCtx;
+
+  if( sParse.pNewTable ){
+    FKey *pFKey;
+    sCtx.pList = renameTokenFind(
+        &sParse, (void*)sParse.pNewTable->aCol[sCtx.iCol].zName
+    );
+    sCtx.nList = 1;
+    sqlite3WalkExprList(&sWalker, sParse.pNewTable->pCheck);
+    for(pIdx=sParse.pNewTable->pIndex; pIdx; pIdx=pIdx->pNext){
+      sqlite3WalkExprList(&sWalker, pIdx->aColExpr);
+    }
+
+    for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+      for(i=0; i<pFKey->nCol; i++){
+        if( pFKey->aCol[i].iFrom==sCtx.iCol ){
+          RenameToken *pTok = renameTokenFind(&sParse, (void*)&pFKey->aCol[i]);
+          if( pTok ){
+            pTok->pNext = sCtx.pList;
+            sCtx.pList = pTok;
+            sCtx.nList++;
+          }
+        }
+      }
+    }
+  }else{
+    sqlite3WalkExprList(&sWalker, sParse.pNewIndex->aColExpr);
+    sqlite3WalkExpr(&sWalker, sParse.pNewIndex->pPartIdxWhere);
+  }
+
+  zOut = sqlite3DbMallocZero(db, nSql + sCtx.nList*nNew + 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);
+
+      int 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);
+  }
+
+  if( sParse.pVdbe ){
+    sqlite3VdbeFinalize(sParse.pVdbe);
+  }
+  sqlite3DeleteTable(db, sParse.pNewTable);
+  if( sParse.pNewIndex ) sqlite3FreeIndex(db, sParse.pNewIndex);
+  renameTokenFree(db, sParse.pRename);
+  sqlite3ParserReset(&sParse);
+  sqlite3_free(zQuot);
+}
+
+/*
+** 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,   3, 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
+  };
+  sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
+}
 #endif  /* SQLITE_ALTER_TABLE */
diff --git a/src/auth.c b/src/auth.c
index a708d0c..918ff46 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -207,7 +207,7 @@
   /* Don't do any authorization checks if the database is initialising
   ** or if the parser is being invoked from within sqlite3_declare_vtab.
   */
-  if( db->init.busy || IN_DECLARE_VTAB ){
+  if( db->init.busy || IN_SPECIAL_PARSE ){
     return SQLITE_OK;
   }
 
diff --git a/src/build.c b/src/build.c
index 62d0ff1..eab9d44 100644
--- a/src/build.c
+++ b/src/build.c
@@ -439,7 +439,7 @@
 /*
 ** Reclaim the memory used by an index
 */
-static void freeIndex(sqlite3 *db, Index *p){
+void sqlite3FreeIndex(sqlite3 *db, Index *p){
 #ifndef SQLITE_OMIT_ANALYZE
   sqlite3DeleteIndexSamples(db, p);
 #endif
@@ -479,7 +479,7 @@
         p->pNext = pIndex->pNext;
       }
     }
-    freeIndex(db, pIndex);
+    sqlite3FreeIndex(db, pIndex);
   }
   db->mDbFlags |= DBFLAG_SchemaChange;
 }
@@ -625,7 +625,7 @@
       assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
       assert( pOld==pIndex || pOld==0 );
     }
-    freeIndex(db, pIndex);
+    sqlite3FreeIndex(db, pIndex);
   }
 
   /* Delete any foreign keys attached to this table. */
@@ -913,7 +913,7 @@
   ** and types will be used, so there is no need to test for namespace
   ** collisions.
   */
-  if( !IN_DECLARE_VTAB ){
+  if( !IN_SPECIAL_PARSE ){
     char *zDb = db->aDb[iDb].zDbSName;
     if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
       goto begin_table_error;
@@ -1072,6 +1072,7 @@
   }
   z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
   if( z==0 ) return;
+  if( IN_RENAME_COLUMN ) sqlite3RenameToken(pParse, (void*)z, pName);
   memcpy(z, pName->z, pName->n);
   z[pName->n] = 0;
   sqlite3Dequote(z);
@@ -2761,6 +2762,9 @@
           pFromCol->a[i].zName);
         goto fk_end;
       }
+      if( IN_RENAME_COLUMN ){
+        sqlite3MoveRenameToken(pParse, &pFKey->aCol[i], &pFromCol->a[i]);
+      }
     }
   }
   if( pToCol ){
@@ -3099,21 +3103,23 @@
     if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
       goto exit_create_index;
     }
-    if( !db->init.busy ){
-      if( sqlite3FindTable(db, zName, 0)!=0 ){
-        sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
+    if( !IN_RENAME_COLUMN ){
+      if( !db->init.busy ){
+        if( sqlite3FindTable(db, zName, 0)!=0 ){
+          sqlite3ErrorMsg(pParse, "there is already a table named %s", zName);
+          goto exit_create_index;
+        }
+      }
+      if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
+        if( !ifNotExist ){
+          sqlite3ErrorMsg(pParse, "index %s already exists", zName);
+        }else{
+          assert( !db->init.busy );
+          sqlite3CodeVerifySchema(pParse, iDb);
+        }
         goto exit_create_index;
       }
     }
-    if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
-      if( !ifNotExist ){
-        sqlite3ErrorMsg(pParse, "index %s already exists", zName);
-      }else{
-        assert( !db->init.busy );
-        sqlite3CodeVerifySchema(pParse, iDb);
-      }
-      goto exit_create_index;
-    }
   }else{
     int n;
     Index *pLoop;
@@ -3128,13 +3134,13 @@
     ** The following statement converts "sqlite3_autoindex..." into
     ** "sqlite3_butoindex..." in order to make the names distinct.
     ** The "vtab_err.test" test demonstrates the need of this statement. */
-    if( IN_DECLARE_VTAB ) zName[7]++;
+    if( IN_SPECIAL_PARSE ) zName[7]++;
   }
 
   /* Check for authorization to create an index.
   */
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  {
+  if( !IN_RENAME_COLUMN ){
     const char *zDb = pDb->zDbSName;
     if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
       goto exit_create_index;
@@ -3221,7 +3227,12 @@
   ** TODO: Issue a warning if the table primary key is used as part of the
   ** index key.
   */
-  for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
+  pListItem = pList->a;
+  if( IN_RENAME_COLUMN ){
+    pIndex->aColExpr = pList;
+    pList = 0;
+  }
+  for(i=0; i<pIndex->nKeyCol; i++, pListItem++){
     Expr *pCExpr;                  /* The i-th index expression */
     int requestedSortOrder;        /* ASC or DESC on the i-th expression */
     const char *zColl;             /* Collation sequence name */
@@ -3237,12 +3248,8 @@
         goto exit_create_index;
       }
       if( pIndex->aColExpr==0 ){
-        ExprList *pCopy = sqlite3ExprListDup(db, pList, 0);
-        pIndex->aColExpr = pCopy;
-        if( !db->mallocFailed ){
-          assert( pCopy!=0 );
-          pListItem = &pCopy->a[i];
-        }
+        pIndex->aColExpr = pList;
+        pList = 0;
       }
       j = XN_EXPR;
       pIndex->aiColumn[i] = XN_EXPR;
@@ -3381,98 +3388,101 @@
     }
   }
 
-  /* Link the new Index structure to its table and to the other
-  ** in-memory database structures. 
-  */
-  assert( pParse->nErr==0 );
-  if( db->init.busy ){
-    Index *p;
-    assert( !IN_DECLARE_VTAB );
-    assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
-    p = sqlite3HashInsert(&pIndex->pSchema->idxHash, 
-                          pIndex->zName, pIndex);
-    if( p ){
-      assert( p==pIndex );  /* Malloc must have failed */
-      sqlite3OomFault(db);
-      goto exit_create_index;
-    }
-    db->mDbFlags |= DBFLAG_SchemaChange;
-    if( pTblName!=0 ){
-      pIndex->tnum = db->init.newTnum;
-    }
-  }
+  if( !IN_RENAME_COLUMN ){
 
-  /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
-  ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
-  ** emit code to allocate the index rootpage on disk and make an entry for
-  ** the index in the sqlite_master table and populate the index with
-  ** content.  But, do not do this if we are simply reading the sqlite_master
-  ** table to parse the schema, or if this index is the PRIMARY KEY index
-  ** of a WITHOUT ROWID table.
-  **
-  ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
-  ** or UNIQUE index in a CREATE TABLE statement.  Since the table
-  ** has just been created, it contains no data and the index initialization
-  ** step can be skipped.
-  */
-  else if( HasRowid(pTab) || pTblName!=0 ){
-    Vdbe *v;
-    char *zStmt;
-    int iMem = ++pParse->nMem;
-
-    v = sqlite3GetVdbe(pParse);
-    if( v==0 ) goto exit_create_index;
-
-    sqlite3BeginWriteOperation(pParse, 1, iDb);
-
-    /* Create the rootpage for the index using CreateIndex. But before
-    ** doing so, code a Noop instruction and store its address in 
-    ** Index.tnum. This is required in case this index is actually a 
-    ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In 
-    ** that case the convertToWithoutRowidTable() routine will replace
-    ** the Noop with a Goto to jump over the VDBE code generated below. */
-    pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
-    sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY);
-
-    /* Gather the complete text of the CREATE INDEX statement into
-    ** the zStmt variable
+    /* Link the new Index structure to its table and to the other
+    ** in-memory database structures. 
     */
-    if( pStart ){
-      int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
-      if( pName->z[n-1]==';' ) n--;
-      /* A named index with an explicit CREATE INDEX statement */
-      zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
-        onError==OE_None ? "" : " UNIQUE", n, pName->z);
-    }else{
-      /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
-      /* zStmt = sqlite3MPrintf(""); */
-      zStmt = 0;
+    assert( pParse->nErr==0 );
+    if( db->init.busy ){
+      Index *p;
+      assert( !IN_SPECIAL_PARSE );
+      assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
+      p = sqlite3HashInsert(&pIndex->pSchema->idxHash, 
+          pIndex->zName, pIndex);
+      if( p ){
+        assert( p==pIndex );  /* Malloc must have failed */
+        sqlite3OomFault(db);
+        goto exit_create_index;
+      }
+      db->mDbFlags |= DBFLAG_SchemaChange;
+      if( pTblName!=0 ){
+        pIndex->tnum = db->init.newTnum;
+      }
     }
 
-    /* Add an entry in sqlite_master for this index
+    /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
+    ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
+    ** emit code to allocate the index rootpage on disk and make an entry for
+    ** the index in the sqlite_master table and populate the index with
+    ** content.  But, do not do this if we are simply reading the sqlite_master
+    ** table to parse the schema, or if this index is the PRIMARY KEY index
+    ** of a WITHOUT ROWID table.
+    **
+    ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
+    ** or UNIQUE index in a CREATE TABLE statement.  Since the table
+    ** has just been created, it contains no data and the index initialization
+    ** step can be skipped.
     */
-    sqlite3NestedParse(pParse, 
-        "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
-        db->aDb[iDb].zDbSName, MASTER_NAME,
-        pIndex->zName,
-        pTab->zName,
-        iMem,
-        zStmt
-    );
-    sqlite3DbFree(db, zStmt);
+    else if( HasRowid(pTab) || pTblName!=0 ){
+      Vdbe *v;
+      char *zStmt;
+      int iMem = ++pParse->nMem;
 
-    /* Fill the index with data and reparse the schema. Code an OP_Expire
-    ** to invalidate all pre-compiled statements.
-    */
-    if( pTblName ){
-      sqlite3RefillIndex(pParse, pIndex, iMem);
-      sqlite3ChangeCookie(pParse, iDb);
-      sqlite3VdbeAddParseSchemaOp(v, iDb,
-         sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
-      sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
+      v = sqlite3GetVdbe(pParse);
+      if( v==0 ) goto exit_create_index;
+
+      sqlite3BeginWriteOperation(pParse, 1, iDb);
+
+      /* Create the rootpage for the index using CreateIndex. But before
+      ** doing so, code a Noop instruction and store its address in 
+      ** Index.tnum. This is required in case this index is actually a 
+      ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In 
+      ** that case the convertToWithoutRowidTable() routine will replace
+      ** the Noop with a Goto to jump over the VDBE code generated below. */
+      pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
+      sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, iMem, BTREE_BLOBKEY);
+
+      /* Gather the complete text of the CREATE INDEX statement into
+      ** the zStmt variable
+      */
+      if( pStart ){
+        int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
+        if( pName->z[n-1]==';' ) n--;
+        /* A named index with an explicit CREATE INDEX statement */
+        zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
+            onError==OE_None ? "" : " UNIQUE", n, pName->z);
+      }else{
+        /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
+        /* zStmt = sqlite3MPrintf(""); */
+        zStmt = 0;
+      }
+
+      /* Add an entry in sqlite_master for this index
+      */
+      sqlite3NestedParse(pParse, 
+          "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
+          db->aDb[iDb].zDbSName, MASTER_NAME,
+          pIndex->zName,
+          pTab->zName,
+          iMem,
+          zStmt
+          );
+      sqlite3DbFree(db, zStmt);
+
+      /* Fill the index with data and reparse the schema. Code an OP_Expire
+      ** to invalidate all pre-compiled statements.
+      */
+      if( pTblName ){
+        sqlite3RefillIndex(pParse, pIndex, iMem);
+        sqlite3ChangeCookie(pParse, iDb);
+        sqlite3VdbeAddParseSchemaOp(v, iDb,
+            sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
+        sqlite3VdbeAddOp2(v, OP_Expire, 0, 1);
+      }
+
+      sqlite3VdbeJumpHere(v, pIndex->tnum);
     }
-
-    sqlite3VdbeJumpHere(v, pIndex->tnum);
   }
 
   /* When adding an index to the list of indices for a table, make
@@ -3496,10 +3506,15 @@
     }
     pIndex = 0;
   }
+  else if( IN_RENAME_COLUMN ){
+    assert( pParse->pNewIndex==0 );
+    pParse->pNewIndex = pIndex;
+    pIndex = 0;
+  }
 
   /* Clean up before exiting */
 exit_create_index:
-  if( pIndex ) freeIndex(db, pIndex);
+  if( pIndex ) sqlite3FreeIndex(db, pIndex);
   sqlite3ExprDelete(db, pPIWhere);
   sqlite3ExprListDelete(db, pList);
   sqlite3SrcListDelete(db, pTblName);
diff --git a/src/parse.y b/src/parse.y
index ea20529..752f30a 100644
--- a/src/parse.y
+++ b/src/parse.y
@@ -940,6 +940,7 @@
         if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted;
         sqlite3Dequote(p->u.zToken);
       }
+      if( IN_RENAME_COLUMN ) sqlite3RenameToken(pParse, (void*)p, &t);
 #if SQLITE_MAX_EXPR_DEPTH>0
       p->nHeight = 1;
 #endif  
@@ -955,6 +956,7 @@
 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 ) sqlite3RenameToken(pParse, (void*)temp2, &Y);
   A = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
 }
 expr(A) ::= nm(X) DOT nm(Y) DOT nm(Z). {
@@ -962,6 +964,7 @@
   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 ) sqlite3RenameToken(pParse, (void*)temp3, &Z);
   A = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
 }
 term(A) ::= NULL|FLOAT|BLOB(X). {A=tokenExpr(pParse,@X,X); /*A-overwrites-X*/}
@@ -1308,6 +1311,9 @@
                          pIdToken->n, pIdToken->z);
     }
     sqlite3ExprListSetName(pParse, p, pIdToken, 1);
+    if( IN_RENAME_COLUMN ){
+      sqlite3RenameToken(pParse, (void*)&(p->a[p->nExpr-1]), pIdToken);
+    }
     return p;
   }
 } // end %include
@@ -1532,8 +1538,13 @@
   disableLookaside(pParse);
   sqlite3AlterBeginAddColumn(pParse, X);
 }
+cmd ::= ALTER TABLE fullname(X) RENAME kwcolumn_opt nm(Y) TO nm(Z). {
+  sqlite3AlterRenameColumn(pParse, X, &Y, &Z);
+}
+
 kwcolumn_opt ::= .
 kwcolumn_opt ::= COLUMNKW.
+
 %endif  SQLITE_OMIT_ALTERTABLE
 
 //////////////////////// CREATE VIRTUAL TABLE ... /////////////////////////////
diff --git a/src/resolve.c b/src/resolve.c
index 5c1dd09..fc815ee 100644
--- a/src/resolve.c
+++ b/src/resolve.c
@@ -668,12 +668,15 @@
         if( pRight->op==TK_ID ){
           zDb = 0;
           zTable = pExpr->pLeft->u.zToken;
-          zColumn = pRight->u.zToken;
         }else{
           assert( pRight->op==TK_DOT );
           zDb = pExpr->pLeft->u.zToken;
           zTable = pRight->pLeft->u.zToken;
-          zColumn = pRight->pRight->u.zToken;
+          pRight = pRight->pRight;
+        }
+        zColumn = pRight->u.zToken;
+        if( IN_RENAME_COLUMN ){
+          sqlite3MoveRenameToken(pParse, (void*)pExpr, (void*)pRight);
         }
       }
       return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 6304680..12812f0 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -1087,6 +1087,7 @@
 typedef struct Parse Parse;
 typedef struct PreUpdate PreUpdate;
 typedef struct PrintfArguments PrintfArguments;
+typedef struct RenameToken RenameToken;
 typedef struct RowSet RowSet;
 typedef struct Savepoint Savepoint;
 typedef struct Select Select;
@@ -3087,8 +3088,10 @@
   ynVar nVar;               /* Number of '?' variables seen in the SQL so far */
   u8 iPkSortOrder;          /* ASC or DESC for INTEGER PRIMARY KEY */
   u8 explain;               /* True if the EXPLAIN flag is found on the query */
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_ALTERTABLE)
+  u8 eParseMode;            /* PARSE_MODE_XXX constant */
+#endif
 #ifndef SQLITE_OMIT_VIRTUALTABLE
-  u8 declareVtab;           /* True if inside sqlite3_declare_vtab() */
   int nVtabLock;            /* Number of virtual tables to lock */
 #endif
   int nHeight;              /* Expression tree height of current sub-select */
@@ -3099,6 +3102,7 @@
   Vdbe *pReprepare;         /* VM being reprepared (sqlite3Reprepare()) */
   const char *zTail;        /* All SQL text past the last semicolon parsed */
   Table *pNewTable;         /* A table being constructed by CREATE TABLE */
+  Index *pNewIndex;         /* An index being constructed by CREATE INDEX */
   Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
   const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -3109,8 +3113,15 @@
   TriggerPrg *pTriggerPrg;  /* Linked list of coded triggers */
   With *pWith;              /* Current WITH clause, or NULL */
   With *pWithToFree;        /* Free this WITH object at the end of the parse */
+#ifndef SQLITE_OMIT_ALTERTABLE
+  RenameToken *pRename;
+#endif
 };
 
+#define PARSE_MODE_NORMAL        0
+#define PARSE_MODE_DECLARE_VTAB  1
+#define PARSE_MODE_RENAME_COLUMN 2
+
 /*
 ** Sizes and pointers of various parts of the Parse object.
 */
@@ -3125,7 +3136,19 @@
 #ifdef SQLITE_OMIT_VIRTUALTABLE
   #define IN_DECLARE_VTAB 0
 #else
-  #define IN_DECLARE_VTAB (pParse->declareVtab)
+  #define IN_DECLARE_VTAB (pParse->eParseMode==PARSE_MODE_DECLARE_VTAB)
+#endif
+
+#if defined(SQLITE_OMIT_ALTERTABLE)
+  #define IN_RENAME_COLUMN 0
+#else
+  #define IN_RENAME_COLUMN (pParse->eParseMode==PARSE_MODE_RENAME_COLUMN)
+#endif
+
+#if defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_OMIT_ALTERTABLE)
+  #define IN_SPECIAL_PARSE 0
+#else
+  #define IN_SPECIAL_PARSE (pParse->eParseMode!=PARSE_MODE_NORMAL)
 #endif
 
 /*
@@ -3409,6 +3432,7 @@
     Select *pSelect;                          /* HAVING to WHERE clause ctx */
     struct WindowRewrite *pRewrite;           /* Window rewrite context */
     struct WhereConst *pConst;                /* WHERE clause constants */
+    struct RenameCtx *pRename;                /* RENAME COLUMN context */
   } u;
 };
 
@@ -3844,6 +3868,7 @@
 void sqlite3DropTable(Parse*, SrcList*, int, int);
 void sqlite3CodeDropTable(Parse*, Table*, int, int);
 void sqlite3DeleteTable(sqlite3*, Table*);
+void sqlite3FreeIndex(sqlite3*, Index*);
 #ifndef SQLITE_OMIT_AUTOINCREMENT
   void sqlite3AutoincrementBegin(Parse *pParse);
   void sqlite3AutoincrementEnd(Parse *pParse);
@@ -4188,6 +4213,7 @@
 void sqlite3Reindex(Parse*, Token*, Token*);
 void sqlite3AlterFunctions(void);
 void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
+void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
 int sqlite3GetToken(const unsigned char *, int *);
 void sqlite3NestedParse(Parse*, const char*, ...);
 void sqlite3ExpirePreparedStatements(sqlite3*, int);
@@ -4203,6 +4229,8 @@
 void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
 void sqlite3AlterFinishAddColumn(Parse *, Token *);
 void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
+void sqlite3RenameToken(Parse*, void*, Token*);
+void sqlite3MoveRenameToken(Parse*, void *pTo, void *pFrom);
 CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
 char sqlite3AffinityType(const char*, Column*);
 void sqlite3Analyze(Parse*, Token*, Token*);
diff --git a/src/tokenize.c b/src/tokenize.c
index 6cdff74..685d10b 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -690,7 +690,7 @@
   sqlite3_free(pParse->apVtabLock);
 #endif
 
-  if( !IN_DECLARE_VTAB ){
+  if( !IN_SPECIAL_PARSE ){
     /* If the pParse->declareVtab flag is set, do not delete any table 
     ** structure built up in pParse->pNewTable. The calling code (see vtab.c)
     ** will take responsibility for freeing the Table structure.
diff --git a/src/vtab.c b/src/vtab.c
index c9ce838..7b459be 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -758,7 +758,7 @@
   assert( IsVirtual(pTab) );
 
   memset(&sParse, 0, sizeof(sParse));
-  sParse.declareVtab = 1;
+  sParse.eParseMode = PARSE_MODE_DECLARE_VTAB;
   sParse.db = db;
   sParse.nQueryLoop = 1;
   if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) 
@@ -799,7 +799,7 @@
     sqlite3DbFree(db, zErr);
     rc = SQLITE_ERROR;
   }
-  sParse.declareVtab = 0;
+  sParse.eParseMode = PARSE_MODE_NORMAL;
 
   if( sParse.pVdbe ){
     sqlite3VdbeFinalize(sParse.pVdbe);