Refactor the Table object to reduce its memory footprint.

FossilOrigin-Name: bbb6759bcf6e01d36dfc787a82a610d359f50aaeac8104b73883a84906d54e1f
diff --git a/src/alter.c b/src/alter.c
index 1d1e2d8..75220a1 100644
--- a/src/alter.c
+++ b/src/alter.c
@@ -175,7 +175,7 @@
   }
 
 #ifndef SQLITE_OMIT_VIEW
-  if( pTab->pSelect ){
+  if( IsView(pTab) ){
     sqlite3ErrorMsg(pParse, "view %s may not be altered", pTab->zName);
     goto exit_rename_table;
   }
@@ -371,7 +371,7 @@
     if( pDflt && pDflt->pLeft->op==TK_NULL ){
       pDflt = 0;
     }
-    if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
+    if( (db->flags&SQLITE_ForeignKeys) && pNew->u.tab.pFKey && pDflt ){
       sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
           "Cannot add a REFERENCES column with non-NULL default value");
     }
@@ -415,12 +415,13 @@
     db->mDbFlags |= DBFLAG_PreferBuiltin;
     /* substr() operations on characters, but addColOffset is in bytes. So we
     ** have to use printf() to translate between these units: */
+    assert( !IsVirtual(pTab) );
     sqlite3NestedParse(pParse, 
         "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
           "sql = printf('%%.%ds, ',sql) || %Q"
           " || substr(sql,1+length(printf('%%.%ds',sql))) "
         "WHERE type = 'table' AND name = %Q", 
-      zDb, pNew->addColOffset, zCol, pNew->addColOffset,
+      zDb, pNew->u.tab.addColOffset, zCol, pNew->u.tab.addColOffset,
       zTab
     );
     sqlite3DbFree(db, zCol);
@@ -500,7 +501,7 @@
 #endif
 
   /* Make sure this is not an attempt to ALTER a view. */
-  if( pTab->pSelect ){
+  if( IsView(pTab) ){
     sqlite3ErrorMsg(pParse, "Cannot add a column to a view");
     goto exit_begin_add_column;
   }
@@ -509,7 +510,7 @@
   }
 
   sqlite3MayAbort(pParse);
-  assert( pTab->addColOffset>0 );
+  assert( pTab->u.tab.addColOffset>0 );
   iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
 
   /* Put a copy of the Table struct in Parse.pNewTable for the
@@ -540,9 +541,10 @@
     pCol->hName = sqlite3StrIHash(pCol->zName);
     pCol->zColl = 0;
   }
-  pNew->pDfltList = sqlite3ExprListDup(db, pTab->pDfltList, 0);
+  assert( !IsVirtual(pNew) );
+  pNew->u.tab.pDfltList = sqlite3ExprListDup(db, pTab->u.tab.pDfltList, 0);
   pNew->pSchema = db->aDb[iDb].pSchema;
-  pNew->addColOffset = pTab->addColOffset;
+  pNew->u.tab.addColOffset = pTab->u.tab.addColOffset;
   pNew->nTabRef = 1;
 
 exit_begin_add_column:
@@ -562,7 +564,7 @@
 static int isRealTable(Parse *pParse, Table *pTab, int bDrop){
   const char *zType = 0;
 #ifndef SQLITE_OMIT_VIEW
-  if( pTab->pSelect ){
+  if( IsView(pTab) ){
     zType = "view";
   }
 #endif
@@ -1494,8 +1496,8 @@
   sCtx.pTab = pTab;
   if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
   if( sParse.pNewTable ){
-    Select *pSelect = sParse.pNewTable->pSelect;
-    if( pSelect ){
+    if( IsView(sParse.pNewTable) ){
+      Select *pSelect = sParse.pNewTable->u.view.pSelect;
       pSelect->selFlags &= ~SF_View;
       sParse.rc = SQLITE_OK;
       sqlite3SelectPrep(&sParse, pSelect, 0);
@@ -1504,11 +1506,10 @@
         sqlite3WalkSelect(&sWalker, pSelect);
       }
       if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
-    }else{
+    }else if( IsOrdinaryTable(sParse.pNewTable) ){
       /* A regular table */
       int bFKOnly = sqlite3_stricmp(zTable, sParse.pNewTable->zName);
       FKey *pFKey;
-      assert( sParse.pNewTable->pSelect==0 );
       sCtx.pTab = sParse.pNewTable;
       if( bFKOnly==0 ){
         if( iCol<sParse.pNewTable->nCol ){
@@ -1535,7 +1536,8 @@
 #endif
       }
 
-      for(pFKey=sParse.pNewTable->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+      assert( !IsVirtual(sParse.pNewTable) );
+      for(pFKey=sParse.pNewTable->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
         for(i=0; i<pFKey->nCol; i++){
           if( bFKOnly==0 && pFKey->aCol[i].iFrom==iCol ){
             renameTokenFind(&sParse, &sCtx, (void*)&pFKey->aCol[i]);
@@ -1701,28 +1703,31 @@
       if( sParse.pNewTable ){
         Table *pTab = sParse.pNewTable;
 
-        if( pTab->pSelect ){
+        if( IsView(pTab) ){
           if( isLegacy==0 ){
-            Select *pSelect = pTab->pSelect;
+            Select *pSelect = pTab->u.view.pSelect;
             NameContext sNC;
             memset(&sNC, 0, sizeof(sNC));
             sNC.pParse = &sParse;
 
             assert( pSelect->selFlags & SF_View );
             pSelect->selFlags &= ~SF_View;
-            sqlite3SelectPrep(&sParse, pTab->pSelect, &sNC);
+            sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
             if( sParse.nErr ){
               rc = sParse.rc;
             }else{
-              sqlite3WalkSelect(&sWalker, pTab->pSelect);
+              sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect);
             }
           }
         }else{
           /* Modify any FK definitions to point to the new table. */
 #ifndef SQLITE_OMIT_FOREIGN_KEY
-          if( isLegacy==0 || (db->flags & SQLITE_ForeignKeys) ){
+          if( (isLegacy==0 || (db->flags & SQLITE_ForeignKeys))
+           && !IsVirtual(pTab)
+          ){
             FKey *pFKey;
-            for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+            assert( !IsVirtual(pTab) );
+            for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
               if( sqlite3_stricmp(pFKey->zTo, zOld)==0 ){
                 renameTokenFind(&sParse, &sCtx, (void*)pFKey->zTo);
               }
@@ -1862,8 +1867,8 @@
       sWalker.u.pRename = &sCtx;
 
       if( sParse.pNewTable ){
-        Select *pSelect = sParse.pNewTable->pSelect;
-        if( pSelect ){
+        if( IsView(sParse.pNewTable) ){
+          Select *pSelect = sParse.pNewTable->u.view.pSelect;
           pSelect->selFlags &= ~SF_View;
           sParse.rc = SQLITE_OK;
           sqlite3SelectPrep(&sParse, pSelect, 0);
@@ -1961,11 +1966,11 @@
     rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
     db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
     if( rc==SQLITE_OK ){
-      if( isLegacy==0 && sParse.pNewTable && sParse.pNewTable->pSelect ){
+      if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
         NameContext sNC;
         memset(&sNC, 0, sizeof(sNC));
         sNC.pParse = &sParse;
-        sqlite3SelectPrep(&sParse, sParse.pNewTable->pSelect, &sNC);
+        sqlite3SelectPrep(&sParse, sParse.pNewTable->u.view.pSelect, &sNC);
         if( sParse.nErr ) rc = sParse.rc;
       }
 
@@ -2042,7 +2047,8 @@
     pEnd = renameTokenFind(&sParse, 0, (void*)pTab->aCol[iCol+1].zName);
     zEnd = (const char*)pEnd->t.z;
   }else{
-    zEnd = (const char*)&zSql[pTab->addColOffset];
+    assert( !IsVirtual(pTab) );
+    zEnd = (const char*)&zSql[pTab->u.tab.addColOffset];
     while( ALWAYS(pCol->t.z[0]!=0) && pCol->t.z[0]!=',' ) pCol->t.z--;
   }
 
diff --git a/src/build.c b/src/build.c
index 33f0015..09222ac 100644
--- a/src/build.c
+++ b/src/build.c
@@ -678,13 +678,15 @@
   Column *pCol,     /* The column to receive the new DEFAULT expression */
   Expr *pExpr       /* The new default expression */
 ){
-  ExprList *pList = pTab->pDfltList;
+  ExprList *pList;
+  assert( !IsVirtual(pTab) );
+  pList = pTab->u.tab.pDfltList;
   if( pCol->iDflt==0
    || pList==0
    || pList->nExpr<pCol->iDflt
   ){
     pCol->iDflt = pList==0 ? 1 : pList->nExpr+1;
-    pTab->pDfltList = sqlite3ExprListAppend(pParse, pList, pExpr);
+    pTab->u.tab.pDfltList = sqlite3ExprListAppend(pParse, pList, pExpr);
   }else{
     sqlite3ExprDelete(pParse->db, pList->a[pCol->iDflt-1].pExpr);
     pList->a[pCol->iDflt-1].pExpr = pExpr;
@@ -698,9 +700,10 @@
 */
 Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
   if( pCol->iDflt==0 ) return 0;
-  if( pTab->pDfltList==0 ) return 0;
-  if( pTab->pDfltList->nExpr<pCol->iDflt ) return 0;
-  return pTab->pDfltList->a[pCol->iDflt-1].pExpr;
+  if( IsVirtual(pTab) ) return 0;
+  if( pTab->u.tab.pDfltList==0 ) return 0;
+  if( pTab->u.tab.pDfltList->nExpr<pCol->iDflt ) return 0;
+  return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
 }
 
 /*
@@ -718,11 +721,15 @@
       sqlite3DbFree(db, pCol->zColl);
     }
     sqlite3DbFree(db, pTable->aCol);
-    sqlite3ExprListDelete(db, pTable->pDfltList);
+    if( !IsVirtual(pTable) ){
+      sqlite3ExprListDelete(db, pTable->u.tab.pDfltList);
+    }
     if( db==0 || db->pnBytesFreed==0 ){
       pTable->aCol = 0;
       pTable->nCol = 0;
-      pTable->pDfltList = 0;
+      if( !IsVirtual(pTable) ){
+        pTable->u.tab.pDfltList = 0;
+      }
     }
   }
 }
@@ -775,19 +782,25 @@
     sqlite3FreeIndex(db, pIndex);
   }
 
-  /* Delete any foreign keys attached to this table. */
-  sqlite3FkDelete(db, pTable);
+  if( IsOrdinaryTable(pTable) ){
+    sqlite3FkDelete(db, pTable);
+  }
+#ifndef SQLITE_OMIT_VIRTUAL_TABLE
+  else if( IsVirtual(pTable) ){
+    sqlite3VtabClear(db, pTable);
+  }
+#endif
+  else{
+    assert( IsView(pTable) );
+    sqlite3SelectDelete(db, pTable->u.view.pSelect);
+  }
 
   /* Delete the Table structure itself.
   */
   sqlite3DeleteColumnNames(db, pTable);
   sqlite3DbFree(db, pTable->zName);
   sqlite3DbFree(db, pTable->zColAff);
-  sqlite3SelectDelete(db, pTable->pSelect);
   sqlite3ExprListDelete(db, pTable->pCheck);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
-  sqlite3VtabClear(db, pTable);
-#endif
   sqlite3DbFree(db, pTable);
 
   /* Verify that no lookaside memory was used by schema tables */
@@ -2420,7 +2433,7 @@
   nName = sqlite3Strlen30(pTab->zName);
   if( sqlite3_strnicmp(zName, pTab->zName, nName)!=0 ) return 0;
   if( zName[nName]!='_' ) return 0;
-  pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->azModuleArg[0]);
+  pMod = (Module*)sqlite3HashFind(&db->aModule, pTab->u.vtab.azArg[0]);
   if( pMod==0 ) return 0;
   if( pMod->pModule->iVersion<3 ) return 0;
   if( pMod->pModule->xShadowName==0 ) return 0;
@@ -2632,7 +2645,7 @@
     /* 
     ** Initialize zType for the new view or table.
     */
-    if( p->pSelect==0 ){
+    if( IsOrdinaryTable(p) ){
       /* A regular table */
       zType = "table";
       zType2 = "TABLE";
@@ -2782,12 +2795,12 @@
   }
 
 #ifndef SQLITE_OMIT_ALTERTABLE
-  if( !pSelect && !p->pSelect ){
+  if( !pSelect && IsOrdinaryTable(p) ){
     assert( pCons && pEnd );
     if( pCons->z==0 ){
       pCons = pEnd;
     }
-    p->addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z);
+    p->u.tab.addColOffset = 13 + (int)(pCons->z - pParse->sNameToken.z);
   }
 #endif
 }
@@ -2844,12 +2857,13 @@
   */
   pSelect->selFlags |= SF_View;
   if( IN_RENAME_OBJECT ){
-    p->pSelect = pSelect;
+    p->u.view.pSelect = pSelect;
     pSelect = 0;
   }else{
-    p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
+    p->u.view.pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
   }
   p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
+  p->eTabType = TABTYP_VIEW;
   if( db->mallocFailed ) goto create_view_fail;
 
   /* Locate the end of the CREATE VIEW statement.  Make sEnd point to
@@ -2946,8 +2960,8 @@
   ** to be permanent.  So the computation is done on a copy of the SELECT
   ** statement that defines the view.
   */
-  assert( pTable->pSelect );
-  pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
+  assert( IsView(pTable) );
+  pSel = sqlite3SelectDup(db, pTable->u.view.pSelect, 0);
   if( pSel ){
     u8 eParseMode = pParse->eParseMode;
     pParse->eParseMode = PARSE_MODE_NORMAL;
@@ -3022,7 +3036,7 @@
   if( !DbHasProperty(db, idx, DB_UnresetViews) ) return;
   for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
     Table *pTab = sqliteHashData(i);
-    if( pTab->pSelect ){
+    if( IsView(pTab) ){
       sqlite3DeleteColumnNames(db, pTab);
     }
   }
@@ -3364,11 +3378,11 @@
   /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
   ** on a table.
   */
-  if( isView && pTab->pSelect==0 ){
+  if( isView && !IsView(pTab) ){
     sqlite3ErrorMsg(pParse, "use DROP TABLE to delete table %s", pTab->zName);
     goto exit_drop_table;
   }
-  if( !isView && pTab->pSelect ){
+  if( !isView && IsView(pTab) ){
     sqlite3ErrorMsg(pParse, "use DROP VIEW to delete view %s", pTab->zName);
     goto exit_drop_table;
   }
@@ -3455,7 +3469,7 @@
     goto fk_end;
   }
   pFKey->pFrom = p;
-  pFKey->pNextFrom = p->pFKey;
+  pFKey->pNextFrom = p->u.tab.pFKey;
   z = (char*)&pFKey->aCol[nCol];
   pFKey->zTo = z;
   if( IN_RENAME_OBJECT ){
@@ -3520,7 +3534,8 @@
 
   /* Link the foreign key to the table as the last step.
   */
-  p->pFKey = pFKey;
+  assert( !IsVirtual(p) );
+  p->u.tab.pFKey = pFKey;
   pFKey = 0;
 
 fk_end:
@@ -3541,7 +3556,9 @@
 #ifndef SQLITE_OMIT_FOREIGN_KEY
   Table *pTab;
   FKey *pFKey;
-  if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return;
+  if( (pTab = pParse->pNewTable)==0 ) return;
+  if( IsVirtual(pTab) ) return;
+  if( (pFKey = pTab->u.tab.pFKey)==0 ) return;
   assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */
   pFKey->isDeferred = (u8)isDeferred;
 #endif
@@ -3833,7 +3850,7 @@
     goto exit_create_index;
   }
 #ifndef SQLITE_OMIT_VIEW
-  if( pTab->pSelect ){
+  if( IsView(pTab) ){
     sqlite3ErrorMsg(pParse, "views may not be indexed");
     goto exit_create_index;
   }
diff --git a/src/delete.c b/src/delete.c
index dd074bb..459e932 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -84,7 +84,7 @@
     return 1;
   }
 #ifndef SQLITE_OMIT_VIEW
-  if( !viewOk && pTab->pSelect ){
+  if( !viewOk && IsView(pTab) ){
     sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName);
     return 1;
   }
@@ -301,7 +301,7 @@
   */
 #ifndef SQLITE_OMIT_TRIGGER
   pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
-  isView = pTab->pSelect!=0;
+  isView = IsView(pTab);
 #else
 # define pTrigger 0
 # define isView 0
@@ -551,7 +551,7 @@
     if( eOnePass!=ONEPASS_OFF ){
       assert( nKey==nPk );  /* OP_Found will use an unpacked key */
       if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){
-        assert( pPk!=0 || pTab->pSelect!=0 );
+        assert( pPk!=0 || IsView(pTab) );
         sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
         VdbeCoverage(v);
       }
@@ -785,7 +785,7 @@
   ** the update-hook is not invoked for rows removed by REPLACE, but the 
   ** pre-update-hook is.
   */ 
-  if( pTab->pSelect==0 ){
+  if( !IsView(pTab) ){
     u8 p5 = 0;
     sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
     sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
diff --git a/src/expr.c b/src/expr.c
index 04881be..ca0c00a 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2513,7 +2513,7 @@
   if( pSrc->a[0].pSelect ) return 0;     /* FROM is not a subquery or view */
   pTab = pSrc->a[0].pTab;
   assert( pTab!=0 );
-  assert( pTab->pSelect==0 );            /* FROM clause is not a view */
+  assert( !IsView(pTab)  );              /* FROM clause is not a view */
   if( IsVirtual(pTab) ) return 0;        /* FROM clause not a virtual table */
   pEList = p->pEList;
   assert( pEList!=0 );
diff --git a/src/fkey.c b/src/fkey.c
index 2e743c7..ffb3a73 100644
--- a/src/fkey.c
+++ b/src/fkey.c
@@ -723,7 +723,8 @@
     Vdbe *v = sqlite3GetVdbe(pParse);
 
     assert( v );                  /* VDBE has already been allocated */
-    assert( pTab->pSelect==0 );   /* Not a view */
+    assert( !IsView(pTab) );      /* Not a view */
+    assert( !IsVirtual(pTab) );
     if( sqlite3FkReferences(pTab)==0 ){
       /* Search for a deferred foreign key constraint for which this table
       ** is the child table. If one cannot be found, return without 
@@ -731,7 +732,7 @@
       ** the entire DELETE if there are no outstanding deferred constraints
       ** when this statement is run.  */
       FKey *p;
-      for(p=pTab->pFKey; p; p=p->pNextFrom){
+      for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){
         if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break;
       }
       if( !p ) return;
@@ -893,7 +894,8 @@
 
   /* Loop through all the foreign key constraints for which pTab is the
   ** child table (the table that the foreign key definition is part of).  */
-  for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+  assert( !IsVirtual(pTab) );
+  for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
     Table *pTo;                   /* Parent table of foreign key pFKey */
     Index *pIdx = 0;              /* Index on key columns in pTo */
     int *aiFree = 0;
@@ -1078,7 +1080,8 @@
   if( pParse->db->flags&SQLITE_ForeignKeys ){
     FKey *p;
     int i;
-    for(p=pTab->pFKey; p; p=p->pNextFrom){
+    assert( !IsVirtual(pTab) );
+    for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){
       for(i=0; i<p->nCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom);
     }
     for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
@@ -1128,19 +1131,19 @@
 ){
   int eRet = 1;                   /* Value to return if bHaveFK is true */
   int bHaveFK = 0;                /* If FK processing is required */
-  if( pParse->db->flags&SQLITE_ForeignKeys ){
+  if( pParse->db->flags&SQLITE_ForeignKeys && !IsVirtual(pTab) ){
     if( !aChange ){
       /* A DELETE operation. Foreign key processing is required if the 
       ** table in question is either the child or parent table for any 
       ** foreign key constraint.  */
-      bHaveFK = (sqlite3FkReferences(pTab) || pTab->pFKey);
+      bHaveFK = (sqlite3FkReferences(pTab) || pTab->u.tab.pFKey);
     }else{
       /* This is an UPDATE. Foreign key processing is only required if the
       ** operation modifies one or more child or parent key columns. */
       FKey *p;
 
       /* Check if any child key columns are being modified. */
-      for(p=pTab->pFKey; p; p=p->pNextFrom){
+      for(p=pTab->u.tab.pFKey; p; p=p->pNextFrom){
         if( fkChildIsModified(pTab, p, aChange, chngRowid) ){
           if( 0==sqlite3_stricmp(pTab->zName, p->zTo) ) eRet = 2;
           bHaveFK = 1;
@@ -1416,9 +1419,9 @@
   FKey *pFKey;                    /* Iterator variable */
   FKey *pNext;                    /* Copy of pFKey->pNextFrom */
 
-  assert( db==0 || IsVirtual(pTab)
-         || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
-  for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
+  assert( !IsVirtual(pTab) );
+  assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
+  for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pNext){
 
     /* Remove the FK from the fkeyHash hash table. */
     if( !db || db->pnBytesFreed==0 ){
diff --git a/src/insert.c b/src/insert.c
index 867ab76..bff75a0 100644
--- a/src/insert.c
+++ b/src/insert.c
@@ -703,7 +703,7 @@
   */
 #ifndef SQLITE_OMIT_TRIGGER
   pTrigger = sqlite3TriggersExist(pParse, pTab, TK_INSERT, 0, &tmask);
-  isView = pTab->pSelect!=0;
+  isView = IsView(pTab);
 #else
 # define pTrigger 0
 # define tmask 0
@@ -989,7 +989,7 @@
               pTab->zName);
       goto insert_cleanup;
     }
-    if( pTab->pSelect ){
+    if( IsView(pTab) ){
       sqlite3ErrorMsg(pParse, "cannot UPSERT a view");
       goto insert_cleanup;
     }
@@ -1624,7 +1624,7 @@
   db = pParse->db;
   v = pParse->pVdbe;
   assert( v!=0 );
-  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
+  assert( !IsView(pTab) );  /* This table is not a VIEW */
   nCol = pTab->nCol;
   
   /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
@@ -2185,7 +2185,7 @@
      && ( 0==(db->flags&SQLITE_RecTriggers) ||      /* Condition 4 */
           0==sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0))
      && ( 0==(db->flags&SQLITE_ForeignKeys) ||      /* Condition 5 */
-         (0==pTab->pFKey && 0==sqlite3FkReferences(pTab)))
+         (0==pTab->u.tab.pFKey && 0==sqlite3FkReferences(pTab)))
     ){
       sqlite3VdbeResolveLabel(v, addrUniqueOk);
       continue;
@@ -2484,7 +2484,7 @@
 
   v = pParse->pVdbe;
   assert( v!=0 );
-  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
+  assert( !IsView(pTab) );  /* This table is not a VIEW */
   for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
     /* All REPLACE indexes are at the end of the list */
     assert( pIdx->onError!=OE_Replace
@@ -2786,13 +2786,8 @@
   if( HasRowid(pDest)!=HasRowid(pSrc) ){
     return 0;   /* source and destination must both be WITHOUT ROWID or not */
   }
-#ifndef SQLITE_OMIT_VIRTUALTABLE
-  if( IsVirtual(pSrc) ){
-    return 0;   /* tab2 must not be a virtual table */
-  }
-#endif
-  if( pSrc->pSelect ){
-    return 0;   /* tab2 may not be a view */
+  if( !IsOrdinaryTable(pSrc) ){
+    return 0;   /* tab2 may not be a view or virtual table */
   }
   if( pDest->nCol!=pSrc->nCol ){
     return 0;   /* Number of columns must be the same in tab1 and tab2 */
@@ -2899,7 +2894,7 @@
   ** the extra complication to make this rule less restrictive is probably
   ** not worth the effort.  Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
   */
-  if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
+  if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->u.tab.pFKey!=0 ){
     return 0;
   }
 #endif
diff --git a/src/main.c b/src/main.c
index fc0fa6a..2dc2959 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3731,7 +3731,7 @@
 
   /* Locate the table in question */
   pTab = sqlite3FindTable(db, zTableName, zDbName);
-  if( !pTab || pTab->pSelect ){
+  if( !pTab || IsView(pTab) ){
     pTab = 0;
     goto error_out;
   }
diff --git a/src/pragma.c b/src/pragma.c
index 0427bb9..2606e85 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -1360,8 +1360,8 @@
     FKey *pFK;
     Table *pTab;
     pTab = sqlite3FindTable(db, zRight, zDb);
-    if( pTab ){
-      pFK = pTab->pFKey;
+    if( pTab && !IsVirtual(pTab) ){
+      pFK = pTab->u.tab.pFKey;
       if( pFK ){
         int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
         int i = 0; 
@@ -1420,7 +1420,7 @@
         pTab = (Table*)sqliteHashData(k);
         k = sqliteHashNext(k);
       }
-      if( pTab==0 || pTab->pFKey==0 ) continue;
+      if( pTab==0 || IsVirtual(pTab) || pTab->u.tab.pFKey==0 ) continue;
       iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
       zDb = db->aDb[iDb].zDbSName;
       sqlite3CodeVerifySchema(pParse, iDb);
@@ -1428,7 +1428,8 @@
       if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
       sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
       sqlite3VdbeLoadString(v, regResult, pTab->zName);
-      for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
+      assert( !IsVirtual(pTab) );
+      for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){
         pParent = sqlite3FindTable(db, pFK->zTo, zDb);
         if( pParent==0 ) continue;
         pIdx = 0;
@@ -1450,7 +1451,8 @@
       if( pFK ) break;
       if( pParse->nTab<i ) pParse->nTab = i;
       addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v);
-      for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
+      assert( !IsVirtual(pTab) );
+      for(i=1, pFK=pTab->u.tab.pFKey; pFK; i++, pFK=pFK->pNextFrom){
         pParent = sqlite3FindTable(db, pFK->zTo, zDb);
         pIdx = 0;
         aiCols = 0;
diff --git a/src/select.c b/src/select.c
index 66e1434..d6708c2 100644
--- a/src/select.c
+++ b/src/select.c
@@ -4933,7 +4933,7 @@
   }
   pTab = p->pSrc->a[0].pTab;
   pExpr = p->pEList->a[0].pExpr;
-  assert( pTab && !pTab->pSelect && pExpr );
+  assert( pTab && !IsView(pTab) && pExpr );
 
   if( IsVirtual(pTab) ) return 0;
   if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
@@ -5478,30 +5478,31 @@
         return WRC_Abort;
       }
 #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
-      if( IsVirtual(pTab) || pTab->pSelect ){
+      if( !IsOrdinaryTable(pTab) ){
         i16 nCol;
         u8 eCodeOrig = pWalker->eCode;
         if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
         assert( pFrom->pSelect==0 );
-        if( pTab->pSelect
-         && (db->flags & SQLITE_EnableView)==0
-         && pTab->pSchema!=db->aDb[1].pSchema
-        ){
-          sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
-            pTab->zName);
-        }
+        if( IsView(pTab) ){
+          if( (db->flags & SQLITE_EnableView)==0
+           && pTab->pSchema!=db->aDb[1].pSchema
+          ){
+            sqlite3ErrorMsg(pParse, "access to view \"%s\" prohibited",
+              pTab->zName);
+          }
+          pFrom->pSelect = sqlite3SelectDup(db, pTab->u.view.pSelect, 0);
+        }else
 #ifndef SQLITE_OMIT_VIRTUALTABLE
-        assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 );
-        if( IsVirtual(pTab)
+        if( ALWAYS(IsVirtual(pTab))
          && pFrom->fg.fromDDL
-         && ALWAYS(pTab->pVTable!=0)
-         && pTab->pVTable->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0)
+         && ALWAYS(pTab->u.vtab.p!=0)
+         && pTab->u.vtab.p->eVtabRisk > ((db->flags & SQLITE_TrustedSchema)!=0)
         ){
           sqlite3ErrorMsg(pParse, "unsafe use of virtual table \"%s\"",
                                   pTab->zName);
         }
+        assert( SQLITE_VTABRISK_Normal==1 && SQLITE_VTABRISK_High==2 );
 #endif
-        pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
         nCol = pTab->nCol;
         pTab->nCol = -1;
         pWalker->eCode = 1;  /* Turn on Select.selId renumbering */
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index 3fbb148..9a2c49e 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -2201,19 +2201,16 @@
 #define SQLITE_VTABRISK_High         2
 
 /*
-** The schema for each SQL table and view is represented in memory
-** by an instance of the following structure.
+** The schema for each SQL table, virtual table, and view is represented
+** in memory by an instance of the following structure.
 */
 struct Table {
   char *zName;         /* Name of the table or view */
   Column *aCol;        /* Information about each column */
   Index *pIndex;       /* List of SQL indexes on this table. */
-  Select *pSelect;     /* NULL for tables.  Points to definition if a view. */
-  FKey *pFKey;         /* Linked list of all foreign keys in this table */
   char *zColAff;       /* String defining the affinity of each column */
   ExprList *pCheck;    /* All CHECK constraints */
                        /*   ... also used as column name list in a VIEW */
-  ExprList *pDfltList; /* DEFAULT clauses on various columns */
   Pgno tnum;           /* Root BTree page for this table */
   u32 nTabRef;         /* Number of pointers to this Table */
   u32 tabFlags;        /* Mask of TF_* values */
@@ -2226,15 +2223,24 @@
   LogEst costMult;     /* Cost multiplier for using this table */
 #endif
   u8 keyConf;          /* What to do in case of uniqueness conflict on iPKey */
-#ifndef SQLITE_OMIT_ALTERTABLE
-  int addColOffset;    /* Offset in CREATE TABLE stmt to add a new column */
-#endif
-#ifndef SQLITE_OMIT_VIRTUALTABLE
-  int nModuleArg;      /* Number of arguments to the module */
-  char **azModuleArg;  /* 0: module 1: schema 2: vtab name 3...: args */
-  VTable *pVTable;     /* List of VTable objects. */
-#endif
-  Trigger *pTrigger;   /* List of triggers stored in pSchema */
+  u8 eTabType;         /* 0: normal, 1: virtual, 2: view */
+  union {
+    struct {             /* Used by ordinary tables: */
+      int addColOffset;    /* Offset in CREATE TABLE stmt to add a new column */
+      FKey *pFKey;         /* Linked list of all foreign keys in this table */
+      ExprList *pDfltList; /* DEFAULT clauses on various columns.
+                           ** Or the AS clause for generated columns. */
+    } tab;
+    struct {             /* Used by views: */
+      Select *pSelect;     /* View definition */
+    } view;
+    struct {             /* Used by virtual tables only: */
+      int nArg;            /* Number of arguments to the module */
+      char **azArg;        /* 0: module 1: schema 2: vtab name 3...: args */
+      VTable *p;           /* List of VTable objects. */
+    } vtab;
+  } u;
+  Trigger *pTrigger;   /* List of triggers on this object */
   Schema *pSchema;     /* Schema that contains this table */
 };
 
@@ -2253,24 +2259,34 @@
 **         TF_HasStored  == COLFLAG_STORED
 **         TF_HasHidden  == COLFLAG_HIDDEN
 */
-#define TF_Readonly        0x0001    /* Read-only system table */
-#define TF_HasHidden       0x0002    /* Has one or more hidden columns */
-#define TF_HasPrimaryKey   0x0004    /* Table has a primary key */
-#define TF_Autoincrement   0x0008    /* Integer primary key is autoincrement */
-#define TF_HasStat1        0x0010    /* nRowLogEst set from sqlite_stat1 */
-#define TF_HasVirtual      0x0020    /* Has one or more VIRTUAL columns */
-#define TF_HasStored       0x0040    /* Has one or more STORED columns */
-#define TF_HasGenerated    0x0060    /* Combo: HasVirtual + HasStored */
-#define TF_WithoutRowid    0x0080    /* No rowid.  PRIMARY KEY is the key */
-#define TF_StatsUsed       0x0100    /* Query planner decisions affected by
+#define TF_Readonly       0x00000001 /* Read-only system table */
+#define TF_HasHidden      0x00000002 /* Has one or more hidden columns */
+#define TF_HasPrimaryKey  0x00000004 /* Table has a primary key */
+#define TF_Autoincrement  0x00000008 /* Integer primary key is autoincrement */
+#define TF_HasStat1       0x00000010 /* nRowLogEst set from sqlite_stat1 */
+#define TF_HasVirtual     0x00000020 /* Has one or more VIRTUAL columns */
+#define TF_HasStored      0x00000040 /* Has one or more STORED columns */
+#define TF_HasGenerated   0x00000060 /* Combo: HasVirtual + HasStored */
+#define TF_WithoutRowid   0x00000080 /* No rowid.  PRIMARY KEY is the key */
+#define TF_StatsUsed      0x00000100 /* Query planner decisions affected by
                                      ** Index.aiRowLogEst[] values */
-#define TF_NoVisibleRowid  0x0200    /* No user-visible "rowid" column */
-#define TF_OOOHidden       0x0400    /* Out-of-Order hidden columns */
-#define TF_HasNotNull      0x0800    /* Contains NOT NULL constraints */
-#define TF_Shadow          0x1000    /* True for a shadow table */
-#define TF_HasStat4        0x2000    /* STAT4 info available for this table */
-#define TF_Ephemeral       0x4000    /* An ephemeral table */
-#define TF_Eponymous       0x8000    /* An eponymous virtual table */
+#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */
+#define TF_OOOHidden      0x00000400 /* Out-of-Order hidden columns */
+#define TF_HasNotNull     0x00000800 /* Contains NOT NULL constraints */
+#define TF_Shadow         0x00001000 /* True for a shadow table */
+#define TF_HasStat4       0x00002000 /* STAT4 info available for this table */
+#define TF_Ephemeral      0x00004000 /* An ephemeral table */
+#define TF_Eponymous      0x00008000 /* An eponymous virtual table */
+
+/*
+** Allowed values for Table.eTabType
+*/
+#define TABTYP_NORM      0     /* Ordinary table */
+#define TABTYP_VTAB      1     /* Virtual table */
+#define TABTYP_VIEW      2     /* A view */
+
+#define IsView(X)           ((X)->eTabType==TABTYP_VIEW)
+#define IsOrdinaryTable(X)  ((X)->eTabType==TABTYP_NORM)
 
 /*
 ** Test to see whether or not a table is a virtual table.  This is
@@ -2278,9 +2294,9 @@
 ** table support is omitted from the build.
 */
 #ifndef SQLITE_OMIT_VIRTUALTABLE
-#  define IsVirtual(X)      ((X)->nModuleArg)
+#  define IsVirtual(X)      ((X)->eTabType==TABTYP_VTAB)
 #  define ExprIsVtab(X)  \
-              ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->nModuleArg)
+    ((X)->op==TK_COLUMN && (X)->y.pTab!=0 && (X)->y.pTab->eTabType==TABTYP_VTAB)
 #else
 #  define IsVirtual(X)      0
 #  define ExprIsVtab(X)     0
diff --git a/src/trigger.c b/src/trigger.c
index c5beeb9..4db697c 100644
--- a/src/trigger.c
+++ b/src/trigger.c
@@ -219,12 +219,12 @@
   /* INSTEAD of triggers are only for views and views only support INSTEAD
   ** of triggers.
   */
-  if( pTab->pSelect && tr_tm!=TK_INSTEAD ){
+  if( IsView(pTab) && tr_tm!=TK_INSTEAD ){
     sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", 
         (tr_tm == TK_BEFORE)?"BEFORE":"AFTER", pTableName->a);
     goto trigger_orphan_error;
   }
-  if( !pTab->pSelect && tr_tm==TK_INSTEAD ){
+  if( !IsView(pTab) && tr_tm==TK_INSTEAD ){
     sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF"
         " trigger on table: %S", pTableName->a);
     goto trigger_orphan_error;
diff --git a/src/update.c b/src/update.c
index f93d85b..6f503c3 100644
--- a/src/update.c
+++ b/src/update.c
@@ -60,7 +60,7 @@
 */
 void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
   assert( pTab!=0 );
-  if( !pTab->pSelect ){
+  if( !IsView(pTab) ){
     sqlite3_value *pValue = 0;
     u8 enc = ENC(sqlite3VdbeDb(v));
     Column *pCol = &pTab->aCol[i];
@@ -237,7 +237,7 @@
       pList = sqlite3ExprListAppend(pParse, pList, pNew);
     }
     eDest = IsVirtual(pTab) ? SRT_Table : SRT_Upfrom;
-  }else if( pTab->pSelect ){
+  }else if( IsView(pTab) ){
     for(i=0; i<pTab->nCol; i++){
       pList = sqlite3ExprListAppend(pParse, pList, exprRowColumn(pParse, i));
     }
@@ -362,7 +362,7 @@
   */
 #ifndef SQLITE_OMIT_TRIGGER
   pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask);
-  isView = pTab->pSelect!=0;
+  isView = IsView(pTab);
   assert( pTrigger || tmask==0 );
 #else
 # define pTrigger 0
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index a4e79bf..34cf880 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -167,7 +167,7 @@
       sqlite3ErrorMsg(&sParse, "cannot open table without rowid: %s", zTable);
     }
 #ifndef SQLITE_OMIT_VIEW
-    if( pTab && pTab->pSelect ){
+    if( pTab && IsView(pTab) ){
       pTab = 0;
       sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
     }
@@ -212,7 +212,8 @@
         ** key columns must be indexed. The check below will pick up this 
         ** case.  */
         FKey *pFKey;
-        for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){
+        assert( !IsVirtual(pTab) );
+        for(pFKey=pTab->u.tab.pFKey; pFKey; pFKey=pFKey->pNextFrom){
           int j;
           for(j=0; j<pFKey->nCol; j++){
             if( pFKey->aCol[j].iFrom==iCol ){
diff --git a/src/vtab.c b/src/vtab.c
index 25672bc..c66a154 100644
--- a/src/vtab.c
+++ b/src/vtab.c
@@ -192,7 +192,7 @@
 VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){
   VTable *pVtab;
   assert( IsVirtual(pTab) );
-  for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
+  for(pVtab=pTab->u.vtab.p; pVtab && pVtab->db!=db; pVtab=pVtab->pNext);
   return pVtab;
 }
 
@@ -220,21 +220,21 @@
 
 /*
 ** Table p is a virtual table. This function moves all elements in the
-** p->pVTable list to the sqlite3.pDisconnect lists of their associated
+** p->u.vtab.p list to the sqlite3.pDisconnect lists of their associated
 ** database connections to be disconnected at the next opportunity. 
 ** Except, if argument db is not NULL, then the entry associated with
-** connection db is left in the p->pVTable list.
+** connection db is left in the p->u.vtab.p list.
 */
 static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
   VTable *pRet = 0;
-  VTable *pVTable = p->pVTable;
-  p->pVTable = 0;
+  VTable *pVTable = p->u.vtab.p;
+  p->u.vtab.p = 0;
 
   /* Assert that the mutex (if any) associated with the BtShared database 
   ** that contains table p is held by the caller. See header comments 
   ** above function sqlite3VtabUnlockList() for an explanation of why
   ** this makes it safe to access the sqlite3.pDisconnect list of any
-  ** database connection that may have an entry in the p->pVTable list.
+  ** database connection that may have an entry in the p->u.vtab.p list.
   */
   assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
 
@@ -244,7 +244,7 @@
     assert( db2 );
     if( db2==db ){
       pRet = pVTable;
-      p->pVTable = pRet;
+      p->u.vtab.p = pRet;
       pRet->pNext = 0;
     }else{
       pVTable->pNext = db2->pDisconnect;
@@ -272,7 +272,7 @@
   assert( sqlite3BtreeHoldsAllMutexes(db) );
   assert( sqlite3_mutex_held(db->mutex) );
 
-  for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){
+  for(ppVTab=&p->u.vtab.p; *ppVTab; ppVTab=&(*ppVTab)->pNext){
     if( (*ppVTab)->db==db  ){
       VTable *pVTab = *ppVTab;
       *ppVTab = pVTab->pNext;
@@ -336,36 +336,36 @@
 */
 void sqlite3VtabClear(sqlite3 *db, Table *p){
   if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p);
-  if( p->azModuleArg ){
+  if( p->u.vtab.azArg ){
     int i;
-    for(i=0; i<p->nModuleArg; i++){
-      if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]);
+    for(i=0; i<p->u.vtab.nArg; i++){
+      if( i!=1 ) sqlite3DbFree(db, p->u.vtab.azArg[i]);
     }
-    sqlite3DbFree(db, p->azModuleArg);
+    sqlite3DbFree(db, p->u.vtab.azArg);
   }
 }
 
 /*
-** Add a new module argument to pTable->azModuleArg[].
+** Add a new module argument to pTable->u.vtab.azArg[].
 ** The string is not copied - the pointer is stored.  The
 ** string will be freed automatically when the table is
 ** deleted.
 */
 static void addModuleArgument(Parse *pParse, Table *pTable, char *zArg){
-  sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->nModuleArg);
+  sqlite3_int64 nBytes = sizeof(char *)*(2+pTable->u.vtab.nArg);
   char **azModuleArg;
   sqlite3 *db = pParse->db;
-  if( pTable->nModuleArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){
+  if( pTable->u.vtab.nArg+3>=db->aLimit[SQLITE_LIMIT_COLUMN] ){
     sqlite3ErrorMsg(pParse, "too many columns on %s", pTable->zName);
   }
-  azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
+  azModuleArg = sqlite3DbRealloc(db, pTable->u.vtab.azArg, nBytes);
   if( azModuleArg==0 ){
     sqlite3DbFree(db, zArg);
   }else{
-    int i = pTable->nModuleArg++;
+    int i = pTable->u.vtab.nArg++;
     azModuleArg[i] = zArg;
     azModuleArg[i+1] = 0;
-    pTable->azModuleArg = azModuleArg;
+    pTable->u.vtab.azArg = azModuleArg;
   }
 }
 
@@ -388,10 +388,11 @@
   pTable = pParse->pNewTable;
   if( pTable==0 ) return;
   assert( 0==pTable->pIndex );
+  pTable->eTabType = TABTYP_VTAB;
 
   db = pParse->db;
 
-  assert( pTable->nModuleArg==0 );
+  assert( pTable->u.vtab.nArg==0 );
   addModuleArgument(pParse, pTable, sqlite3NameFromToken(db, pModuleName));
   addModuleArgument(pParse, pTable, 0);
   addModuleArgument(pParse, pTable, sqlite3DbStrDup(db, pTable->zName));
@@ -408,11 +409,11 @@
   ** sqlite_schema table, has already been made by sqlite3StartTable().
   ** The second call, to obtain permission to create the table, is made now.
   */
-  if( pTable->azModuleArg ){
+  if( pTable->u.vtab.azArg ){
     int iDb = sqlite3SchemaToIndex(db, pTable->pSchema);
     assert( iDb>=0 ); /* The database the table is being created in */
     sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName, 
-            pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName);
+            pTable->u.vtab.azArg[0], pParse->db->aDb[iDb].zDbSName);
   }
 #endif
 }
@@ -442,7 +443,7 @@
   if( pTab==0 ) return;
   addArgumentToVtab(pParse);
   pParse->sArg.z = 0;
-  if( pTab->nModuleArg<1 ) return;
+  if( pTab->u.vtab.nArg<1 ) return;
   
   /* If the CREATE VIRTUAL TABLE statement is being entered for the
   ** first time (in other words if the virtual table is actually being
@@ -557,8 +558,8 @@
   VtabCtx sCtx;
   VTable *pVTable;
   int rc;
-  const char *const*azArg = (const char *const*)pTab->azModuleArg;
-  int nArg = pTab->nModuleArg;
+  const char *const*azArg = (const char *const*)pTab->u.vtab.azArg;
+  int nArg = pTab->u.vtab.nArg;
   char *zErr = 0;
   char *zModuleName;
   int iDb;
@@ -590,7 +591,7 @@
   pVTable->eVtabRisk = SQLITE_VTABRISK_Normal;
 
   iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
-  pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;
+  pTab->u.vtab.azArg[1] = db->aDb[iDb].zDbSName;
 
   /* Invoke the virtual table constructor */
   assert( &db->pVtabCtx );
@@ -629,12 +630,12 @@
       int iCol;
       u16 oooHidden = 0;
       /* If everything went according to plan, link the new VTable structure
-      ** into the linked list headed by pTab->pVTable. Then loop through the 
+      ** into the linked list headed by pTab->u.vtab.p. Then loop through the 
       ** columns of the table to see if any of them contain the token "hidden".
       ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from
       ** the type string.  */
-      pVTable->pNext = pTab->pVTable;
-      pTab->pVTable = pVTable;
+      pVTable->pNext = pTab->u.vtab.p;
+      pTab->u.vtab.p = pVTable;
 
       for(iCol=0; iCol<pTab->nCol; iCol++){
         char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
@@ -692,11 +693,11 @@
   }
 
   /* Locate the required virtual table module */
-  zMod = pTab->azModuleArg[0];
+  zMod = pTab->u.vtab.azArg[0];
   pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
 
   if( !pMod ){
-    const char *zModule = pTab->azModuleArg[0];
+    const char *zModule = pTab->u.vtab.azArg[0];
     sqlite3ErrorMsg(pParse, "no such module: %s", zModule);
     rc = SQLITE_ERROR;
   }else{
@@ -759,10 +760,10 @@
   const char *zMod;
 
   pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
-  assert( pTab && IsVirtual(pTab) && !pTab->pVTable );
+  assert( pTab && IsVirtual(pTab) && !pTab->u.vtab.p );
 
   /* Locate the required virtual table module */
-  zMod = pTab->azModuleArg[0];
+  zMod = pTab->u.vtab.azArg[0];
   pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
 
   /* If the module has been registered and includes a Create method, 
@@ -822,19 +823,17 @@
   if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable, &zErr) 
    && sParse.pNewTable
    && !db->mallocFailed
-   && !sParse.pNewTable->pSelect
-   && !IsVirtual(sParse.pNewTable)
+   && IsOrdinaryTable(sParse.pNewTable)
   ){
     if( !pTab->aCol ){
       Table *pNew = sParse.pNewTable;
       Index *pIdx;
       pTab->aCol = pNew->aCol;
-      pTab->pDfltList = pNew->pDfltList;
+      sqlite3ExprListDelete(db, pNew->u.tab.pDfltList);
       pTab->nNVCol = pTab->nCol = pNew->nCol;
       pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
       pNew->nCol = 0;
       pNew->aCol = 0;
-      pNew->pDfltList = 0;
       assert( pTab->pIndex==0 );
       assert( HasRowid(pNew) || sqlite3PrimaryKeyIndex(pNew)!=0 );
       if( !HasRowid(pNew)
@@ -885,10 +884,10 @@
   Table *pTab;
 
   pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
-  if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){
+  if( pTab!=0 && ALWAYS(pTab->u.vtab.p!=0) ){
     VTable *p;
     int (*xDestroy)(sqlite3_vtab *);
-    for(p=pTab->pVTable; p; p=p->pNext){
+    for(p=pTab->u.vtab.p; p; p=p->pNext){
       assert( p->pVtab );
       if( p->pVtab->nRef>0 ){
         return SQLITE_LOCKED;
@@ -902,9 +901,9 @@
     rc = xDestroy(p->pVtab);
     /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */
     if( rc==SQLITE_OK ){
-      assert( pTab->pVTable==p && p->pNext==0 );
+      assert( pTab->u.vtab.p==p && p->pNext==0 );
       p->pVtab = 0;
-      pTab->pVTable = 0;
+      pTab->u.vtab.p = 0;
       sqlite3VtabUnlock(p);
     }
     sqlite3DeleteTable(db, pTab);
@@ -1221,8 +1220,9 @@
   }
   pMod->pEpoTab = pTab;
   pTab->nTabRef = 1;
+  pTab->eTabType = TABTYP_VTAB;
   pTab->pSchema = db->aDb[0].pSchema;
-  assert( pTab->nModuleArg==0 );
+  assert( pTab->u.vtab.nArg==0 );
   pTab->iPKey = -1;
   pTab->tabFlags |= TF_Eponymous;
   addModuleArgument(pParse, pTab, sqlite3DbStrDup(db, pTab->zName));
diff --git a/src/where.c b/src/where.c
index 4ea04e5..d7ef319 100644
--- a/src/where.c
+++ b/src/where.c
@@ -3060,7 +3060,7 @@
         ** those objects, since there is no opportunity to add schema
         ** indexes on subqueries and views. */
         pNew->rSetup = rLogSize + rSize;
-        if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
+        if( !IsView(pTab) && (pTab->tabFlags & TF_Ephemeral)==0 ){
           pNew->rSetup += 28;
         }else{
           pNew->rSetup -= 10;
@@ -5211,7 +5211,7 @@
     pTab = pTabItem->pTab;
     iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
     pLoop = pLevel->pWLoop;
-    if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
+    if( (pTab->tabFlags & TF_Ephemeral)!=0 || IsView(pTab) ){
       /* Do nothing */
     }else
 #ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -5580,7 +5580,7 @@
     ** created for the ONEPASS optimization.
     */
     if( (pTab->tabFlags & TF_Ephemeral)==0
-     && pTab->pSelect==0
+     && !IsView(pTab)
      && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
     ){
       int ws = pLoop->wsFlags;