using System;
using System.Diagnostics;
using System.IO;
using System.Text;

using i16 = System.Int16;
using u8 = System.Byte;
using u16 = System.UInt16;
using u32 = System.UInt32;

using Pgno = System.UInt32;

/*
** The yDbMask datatype for the bitmask of all attached databases.
*/
#if SQLITE_MAX_ATTACHED//>30
//  typedef sqlite3_uint64 yDbMask;
using yDbMask = System.Int64; 
#else
//  typedef unsigned int yDbMask;
using yDbMask = System.Int32; 
#endif

namespace Community.CsharpSqlite
{
  public partial class Sqlite3
  {
    /*
    ** 2001 September 15
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains C code routines that are called by the SQLite parser
    ** when syntax rules are reduced.  The routines in this file handle the
    ** following kinds of SQL syntax:
    **
    **     CREATE TABLE
    **     DROP TABLE
    **     CREATE INDEX
    **     DROP INDEX
    **     creating ID lists
    **     BEGIN TRANSACTION
    **     COMMIT
    **     ROLLBACK
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2011-06-23 19:49:22 4374b7e83ea0a3fbc3691f9c0c936272862f32f2
    **
    *************************************************************************
    */
    //#include "sqliteInt.h"

    /*
    ** This routine is called when a new SQL statement is beginning to
    ** be parsed.  Initialize the pParse structure as needed.
    */
    static void sqlite3BeginParse( Parse pParse, int explainFlag )
    {
      pParse.explain = (byte)explainFlag;
      pParse.nVar = 0;
    }

//#if !SQLITE_OMIT_SHARED_CACHE
#if NO_SQLITE_OMIT_SHARED_CACHE
/*
** The TableLock structure is only used by the sqlite3TableLock() and
** codeTableLocks() functions.
*/
//struct TableLock {
//  int iDb;             /* The database containing the table to be locked */
//  int iTab;            /* The root page of the table to be locked */
//  u8 isWriteLock;      /* True for write lock.  False for a read lock */
//  string zName;   /* Name of the table */
//};

public class TableLock
{
public int iDb;         /* The database containing the table to be locked */
public int iTab;        /* The root page of the table to be locked */
public u8 isWriteLock;  /* True for write lock.  False for a read lock */
public string zName;    /* Name of the table */
}
/*
** Record the fact that we want to lock a table at run-time.
**
** The table to be locked has root page iTab and is found in database iDb.
** A read or a write lock can be taken depending on isWritelock.
**
** This routine just records the fact that the lock is desired.  The
** code to make the lock occur is generated by a later call to
** codeTableLocks() which occurs during sqlite3FinishCoding().
*/
void sqlite3TableLock(
  Parse *pParse,     /* Parsing context */
  int iDb,           /* Index of the database containing the table to lock */
  int iTab,          /* Root page number of the table to be locked */
  u8 isWriteLock,    /* True for a write lock */
  string zName  /* Name of the table to be locked */
){
  Parse *pToplevel = sqlite3ParseToplevel(pParse);
  int i;
  int nBytes;
  TableLock *p;
  Debug.Assert( iDb>=0 );

  for(i=0; i<pToplevel->nTableLock; i++){
    p = pToplevel->aTableLock[i];
    if( p->iDb==iDb && p->iTab==iTab ){
      p->isWriteLock = (p->isWriteLock || isWriteLock);
      return;
    }
  }

  nBytes = sizeof(vtableLock) * (pToplevel->nTableLock+1);
  pToplevel->aTableLock =
      sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes);
  if( pToplevel->aTableLock ){
    p = pToplevel->aTableLock[pToplevel->nTableLock++];
    p->iDb = iDb;
    p->iTab = iTab;
    p->isWriteLock = isWriteLock;
    p->zName = zName;
  }else{
    pToplevel->nTableLock = 0;
    pToplevel->db->mallocFailed = 1;
  }
}

/*
** Code an OP_TableLock instruction for each table locked by the
** statement (configured by calls to sqlite3TableLock()).
*/
static void codeTableLocks( Parse pParse )
{
int i;
Vdbe pVdbe;

pVdbe = sqlite3GetVdbe( pParse );
Debug.Assert( pVdbe != null ); /* sqlite3GetVdbe cannot fail: VDBE already allocated */

for ( i = 0 ; i < pParse.nTableLock ; i++ )
{
TableLock p = pParse.aTableLock[i];
int p1 = p.iDb;
sqlite3VdbeAddOp4( pVdbe, OP_TableLock, p1, p.iTab, p.isWriteLock,
p.zName, P4_STATIC );
}
}
#else
    //  #define codeTableLocks(x)
    static void codeTableLocks( Parse pParse )
    {
    }
#endif

    /*
** This routine is called after a single SQL statement has been
** parsed and a VDBE program to execute that statement has been
** prepared.  This routine puts the finishing touches on the
** VDBE program and resets the pParse structure for the next
** parse.
**
** Note that if an error occurred, it might be the case that
** no VDBE code was generated.
*/
    static void sqlite3FinishCoding( Parse pParse )
    {
      sqlite3 db;
      Vdbe v;

      db = pParse.db;
      //      if ( db.mallocFailed != 0 ) return;
      if ( pParse.nested != 0 )
        return;
      if ( pParse.nErr != 0 )
        return;

      /* Begin by generating some termination code at the end of the
      ** vdbe program
      */
      v = sqlite3GetVdbe( pParse );
//      Debug.Assert( 0 == pParse.isMultiWrite
//#if SQLITE_DEBUG 
//        || sqlite3VdbeAssertMayAbort( v, pParse.mayAbort ) != 0
//#endif
//        );
      if ( v != null )
      {
        sqlite3VdbeAddOp0( v, OP_Halt );

        /* The cookie mask contains one bit for each database file open.
        ** (Bit 0 is for main, bit 1 is for temp, and so forth.)  Bits are
        ** set for each database that is used.  Generate code to start a
        ** transaction on each used database and to verify the schema cookie
        ** on each used database.
        */
        if ( pParse.cookieGoto > 0 )
        {
          u32 mask;
          int iDb;
          sqlite3VdbeJumpHere( v, pParse.cookieGoto - 1 );
          for ( iDb = 0, mask = 1; iDb < db.nDb; mask <<= 1, iDb++ )
          {
            if ( ( mask & pParse.cookieMask ) == 0 )
              continue;
            sqlite3VdbeUsesBtree( v, iDb );
            sqlite3VdbeAddOp2( v, OP_Transaction, iDb, ( mask & pParse.writeMask ) != 0 );
            if ( db.init.busy == 0 )
            {
              Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
              sqlite3VdbeAddOp3( v, OP_VerifyCookie,
                                iDb, pParse.cookieValue[iDb],
                                (int)db.aDb[iDb].pSchema.iGeneration );
            }
          }
#if !SQLITE_OMIT_VIRTUALTABLE
          {
            int i;
            for ( i = 0; i < pParse.nVtabLock; i++ )
            {
              VTable vtab = sqlite3GetVTable( db, pParse.apVtabLock[i] );
              sqlite3VdbeAddOp4( v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB );
            }
            pParse.nVtabLock = 0;
          }
#endif

          /* Once all the cookies have been verified and transactions opened,
** obtain the required table-locks. This is a no-op unless the
** shared-cache feature is enabled.
*/
          codeTableLocks( pParse );

          /* Initialize any AUTOINCREMENT data structures required.
          */
          sqlite3AutoincrementBegin( pParse );

          /* Finally, jump back to the beginning of the executable code. */
          sqlite3VdbeAddOp2( v, OP_Goto, 0, pParse.cookieGoto );
        }
      }


      /* Get the VDBE program ready for execution
      */
      if ( v != null && ALWAYS( pParse.nErr == 0 ) /* && 0 == db.mallocFailed */ )
      {
#if  SQLITE_DEBUG
        TextWriter trace = ( db.flags & SQLITE_VdbeTrace ) != 0 ? Console.Out : null;
        sqlite3VdbeTrace( v, trace );
#endif
        Debug.Assert( pParse.iCacheLevel == 0 );  /* Disables and re-enables match */
        /* A minimum of one cursor is required if autoincrement is used
        *  See ticket [a696379c1f08866] */
        if ( pParse.pAinc != null && pParse.nTab == 0 )
          pParse.nTab = 1;
        sqlite3VdbeMakeReady( v, pParse);
        pParse.rc = SQLITE_DONE;
        pParse.colNamesSet = 0;
      }
      else
      {
        pParse.rc = SQLITE_ERROR;
      }
      pParse.nTab = 0;
      pParse.nMem = 0;
      pParse.nSet = 0;
      pParse.nVar = 0;
      pParse.cookieMask = 0;
      pParse.cookieGoto = 0;
    }

    /*
    ** Run the parser and code generator recursively in order to generate
    ** code for the SQL statement given onto the end of the pParse context
    ** currently under construction.  When the parser is run recursively
    ** this way, the final OP_Halt is not appended and other initialization
    ** and finalization steps are omitted because those are handling by the
    ** outermost parser.
    **
    ** Not everything is nestable.  This facility is designed to permit
    ** INSERT, UPDATE, and DELETE operations against SQLITE_MASTER.  Use
    ** care if you decide to try to use this routine for some other purposes.
    */
    static void sqlite3NestedParse( Parse pParse, string zFormat, params object[] ap )
    {
      string zSql;        //  string zSql;
      string zErrMsg = "";//  char* zErrMsg = 0;
      sqlite3 db = pParse.db;

      //# define SAVE_SZ  (Parse.Length - offsetof(Parse,nVar))
      //  char saveBuf[SAVE_SZ];

      if ( pParse.nErr != 0 )
        return;
      Debug.Assert( pParse.nested < 10 );  /* Nesting should only be of limited depth */
      //  va_list ap;
      lock ( lock_va_list )
      {
        va_start( ap, zFormat );
        zSql = sqlite3VMPrintf( db, zFormat, ap );
        va_end( ref ap );
      }
      //if( zSql=="" ){
      //  return;   /* A malloc must have failed */
      //}
      lock ( nestingLock )
      {
        pParse.nested++;
        pParse.SaveMembers();     //  memcpy(saveBuf, pParse.nVar, SAVE_SZ);
        pParse.ResetMembers();    //  memset(pParse.nVar, 0, SAVE_SZ);
        sqlite3RunParser( pParse, zSql, ref zErrMsg );
        sqlite3DbFree( db, ref zErrMsg );
        sqlite3DbFree( db, ref zSql );
        pParse.RestoreMembers();  //  memcpy(pParse.nVar, saveBuf, SAVE_SZ);
        pParse.nested--;
      }
    }
    static Object nestingLock = new Object();

    /*
    ** Locate the in-memory structure that describes a particular database
    ** table given the name of that table and (optionally) the name of the
    ** database containing the table.  Return NULL if not found.
    **
    ** If zDatabase is 0, all databases are searched for the table and the
    ** first matching table is returned.  (No checking for duplicate table
    ** names is done.)  The search order is TEMP first, then MAIN, then any
    ** auxiliary databases added using the ATTACH command.
    **
    ** See also sqlite3LocateTable().
    */
    static Table sqlite3FindTable( sqlite3 db, string zName, string zDatabase )
    {
      Table p = null;
      int i;
      int nName;
      Debug.Assert( zName != null );
      nName = sqlite3Strlen30( zName );
      /* All mutexes are required for schema access.  Make sure we hold them. */
      Debug.Assert( zDatabase != null || sqlite3BtreeHoldsAllMutexes( db ) );
      for ( i = OMIT_TEMPDB; i < db.nDb; i++ )
      {
        int j = ( i < 2 ) ? i ^ 1 : i;   /* Search TEMP before MAIN */
        if ( zDatabase != null && !zDatabase.Equals( db.aDb[j].zName, StringComparison.InvariantCultureIgnoreCase ) )
          continue;
        Debug.Assert( sqlite3SchemaMutexHeld( db, j, null ) );
        p = sqlite3HashFind( db.aDb[j].pSchema.tblHash, zName, nName, (Table)null );
        if ( p != null )
          break;
      }
      return p;
    }

    /*
    ** Locate the in-memory structure that describes a particular database
    ** table given the name of that table and (optionally) the name of the
    ** database containing the table.  Return NULL if not found.  Also leave an
    ** error message in pParse.zErrMsg.
    **
    ** The difference between this routine and sqlite3FindTable() is that this
    ** routine leaves an error message in pParse.zErrMsg where
    ** sqlite3FindTable() does not.
    */
    static Table sqlite3LocateTable(
    Parse pParse,     /* context in which to report errors */
    int isView,       /* True if looking for a VIEW rather than a TABLE */
    string zName,     /* Name of the table we are looking for */
    string zDbase     /* Name of the database.  Might be NULL */
    )
    {
      Table p;

      /* Read the database schema. If an error occurs, leave an error message
      ** and code in pParse and return NULL. */
      if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
      {
        return null;
      }

      p = sqlite3FindTable( pParse.db, zName, zDbase );
      if ( p == null )
      {
        string zMsg = isView != 0 ? "no such view" : "no such table";
        if ( zDbase != null )
        {
          sqlite3ErrorMsg( pParse, "%s: %s.%s", zMsg, zDbase, zName );
        }
        else
        {
          sqlite3ErrorMsg( pParse, "%s: %s", zMsg, zName );
        }
        pParse.checkSchema = 1;
      }
      return p;
    }

    /*
    ** Locate the in-memory structure that describes
    ** a particular index given the name of that index
    ** and the name of the database that contains the index.
    ** Return NULL if not found.
    **
    ** If zDatabase is 0, all databases are searched for the
    ** table and the first matching index is returned.  (No checking
    ** for duplicate index names is done.)  The search order is
    ** TEMP first, then MAIN, then any auxiliary databases added
    ** using the ATTACH command.
    */
    static Index sqlite3FindIndex( sqlite3 db, string zName, string zDb )
    {
      Index p = null;
      int i;
      int nName = sqlite3Strlen30( zName );
      /* All mutexes are required for schema access.  Make sure we hold them. */
      Debug.Assert( zDb != null || sqlite3BtreeHoldsAllMutexes( db ) );
      for ( i = OMIT_TEMPDB; i < db.nDb; i++ )
      {
        int j = ( i < 2 ) ? i ^ 1 : i;  /* Search TEMP before MAIN */
        Schema pSchema = db.aDb[j].pSchema;
        Debug.Assert( pSchema != null );
        if ( zDb != null && !zDb.Equals( db.aDb[j].zName, StringComparison.InvariantCultureIgnoreCase ) )
          continue;
        Debug.Assert( sqlite3SchemaMutexHeld( db, j, null ) );
        p = sqlite3HashFind( pSchema.idxHash, zName, nName, (Index)null );
        if ( p != null )
          break;
      }
      return p;
    }

    /*
    ** Reclaim the memory used by an index
    */
    static void freeIndex( sqlite3 db, ref Index p )
    {
#if !SQLITE_OMIT_ANALYZE
      sqlite3DeleteIndexSamples( db, p );
#endif
      sqlite3DbFree( db, ref p.zColAff );
      sqlite3DbFree( db, ref p );
    }

    /*
    ** For the index called zIdxName which is found in the database iDb,
    ** unlike that index from its Table then remove the index from
    ** the index hash table and free all memory structures associated
    ** with the index.
    */
    static void sqlite3UnlinkAndDeleteIndex( sqlite3 db, int iDb, string zIdxName )
    {
      Index pIndex;
      int len;
      Hash pHash;

      Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
      pHash = db.aDb[iDb].pSchema.idxHash;

      len = sqlite3Strlen30( zIdxName );
      pIndex = sqlite3HashInsert( ref pHash, zIdxName, len, (Index)null );
      if ( ALWAYS(pIndex) )
      {
        if ( pIndex.pTable.pIndex == pIndex )
        {
          pIndex.pTable.pIndex = pIndex.pNext;
        }
        else
        {
          Index p;
          /* Justification of ALWAYS();  The index must be on the list of
          ** indices. */
          p = pIndex.pTable.pIndex;
          while ( ALWAYS( p != null ) && p.pNext != pIndex )
          {
            p = p.pNext;
          }
          if ( ALWAYS( p != null && p.pNext == pIndex ) )
          {
            p.pNext = pIndex.pNext;
          }
        }
        freeIndex( db, ref pIndex );
      }
      db.flags |= SQLITE_InternChanges;
    }

    /*
    ** Erase all schema information from the in-memory hash tables of
    ** a single database.  This routine is called to reclaim memory
    ** before the database closes.  It is also called during a rollback
    ** if there were schema changes during the transaction or if a
    ** schema-cookie mismatch occurs.
    **
    ** If iDb<0 then reset the internal schema tables for all database
    ** files.  If iDb>=0 then reset the internal schema for only the
    ** single file indicated.
    */
    static void sqlite3ResetInternalSchema( sqlite3 db, int iDb )
    {
      int i, j;
      Debug.Assert( iDb < db.nDb );

      if ( iDb >= 0 )
      {
        /* Case 1:  Reset the single schema identified by iDb */
        Db pDb = db.aDb[iDb];
        Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
        Debug.Assert( pDb.pSchema != null);
        sqlite3SchemaClear( pDb.pSchema );

        /* If any database other than TEMP is reset, then also reset TEMP
        ** since TEMP might be holding triggers that reference tables in the
        ** other database.
        */
        if ( iDb != 1 )
        {
          pDb = db.aDb[1];
          Debug.Assert( pDb.pSchema != null );
          sqlite3SchemaClear( pDb.pSchema );
        }
        return;
      }
      /* Case 2 (from here to the end): Reset all schemas for all attached
      ** databases. */
      Debug.Assert( iDb < 0 );
      sqlite3BtreeEnterAll( db );
      for ( i = 0; i < db.nDb; i++ )
      {
        Db pDb = db.aDb[i];
        if ( pDb.pSchema != null )
        {
          sqlite3SchemaClear( pDb.pSchema );
        }
      }
      db.flags &= ~SQLITE_InternChanges;
      sqlite3VtabUnlockList( db );
      sqlite3BtreeLeaveAll( db );
      /* If one or more of the auxiliary database files has been closed,
      ** then remove them from the auxiliary database list.  We take the
      ** opportunity to do this here since we have just deleted all of the
      ** schema hash tables and therefore do not have to make any changes
      ** to any of those tables.
      */
      for ( i = j = 2; i < db.nDb; i++ )
      {
        Db pDb = db.aDb[i];
        if ( pDb.pBt == null )
        {
          sqlite3DbFree( db, ref pDb.zName );
          continue;
        }
        if ( j < i )
        {
          db.aDb[j] = db.aDb[i];
        }
        j++;
      }
      if ( db.nDb != j )
        db.aDb[j] = new Db();//memset(db.aDb[j], 0, (db.nDb-j)*sizeof(db.aDb[j]));
      db.nDb = j;
      if ( db.nDb <= 2 && db.aDb != db.aDbStatic )
      {
        Array.Copy( db.aDb, db.aDbStatic, 2 );// memcpy(db.aDbStatic, db.aDb, 2*sizeof(db.aDb[0]));
        //sqlite3DbFree( db, ref db.aDb );
        //db.aDb = db.aDbStatic;
      }
    }

    /*
    ** This routine is called when a commit occurs.
    */
    static void sqlite3CommitInternalChanges( sqlite3 db )
    {
      db.flags &= ~SQLITE_InternChanges;
    }

    /*
    ** Delete memory allocated for the column names of a table or view (the
    ** Table.aCol[] array).
    */
    static void sqliteDeleteColumnNames( sqlite3 db, Table pTable )
    {
      int i;
      Column pCol;
      Debug.Assert( pTable != null );
      for ( i = 0; i < pTable.nCol; i++ )
      {
        pCol = pTable.aCol[i];
        if ( pCol != null )
        {
          sqlite3DbFree( db, ref pCol.zName );
          sqlite3ExprDelete( db, ref pCol.pDflt );
          sqlite3DbFree( db, ref pCol.zDflt );
          sqlite3DbFree( db, ref pCol.zType );
          sqlite3DbFree( db, ref pCol.zColl );
        }
      }
    }

    /*
    ** Remove the memory data structures associated with the given
    ** Table.  No changes are made to disk by this routine.
    **
    ** This routine just deletes the data structure.  It does not unlink
    ** the table data structure from the hash table.  But it does destroy
    ** memory structures of the indices and foreign keys associated with
    ** the table.
    */
    static void sqlite3DeleteTable( sqlite3 db, ref Table pTable )
    {
      Index pIndex;
      Index pNext;

      Debug.Assert( null == pTable || pTable.nRef > 0 );

      /* Do not delete the table until the reference count reaches zero. */
      if ( null == pTable )
        return;
      if ( (// ( !db || db->pnBytesFreed == 0 ) && 
        ( --pTable.nRef ) > 0 ) )
        return;

      /* Delete all indices associated with this table. */
      for ( pIndex = pTable.pIndex; pIndex != null; pIndex = pNext )
      {
        pNext = pIndex.pNext;
        Debug.Assert( pIndex.pSchema == pTable.pSchema );
        //if( null==db || db.pnBytesFreed==0 ){
        string zName = pIndex.zName;
        //
#if !NDEBUG || SQLITE_COVERAGE_TEST
        //  TESTONLY ( Index pOld = ) sqlite3HashInsert(
        //ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), 0
        //  );
        Index pOld = sqlite3HashInsert(
      ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30( zName ), (Index)null
        );
        Debug.Assert( db == null || sqlite3SchemaMutexHeld( db, 0, pIndex.pSchema ) );
        Debug.Assert( pOld == pIndex || pOld == null );
#else
    //  TESTONLY ( Index pOld = ) sqlite3HashInsert(
    //ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName), 0
    //  );
      sqlite3HashInsert(
    ref pIndex.pSchema.idxHash, zName, sqlite3Strlen30(zName),(Index)null
      );
#endif
        //}
        freeIndex( db, ref pIndex );
      }

      /* Delete any foreign keys attached to this table. */
      sqlite3FkDelete( db, pTable );

      /* Delete the Table structure itself.
*/
      sqliteDeleteColumnNames( db, pTable );
      sqlite3DbFree( db, ref pTable.zName );
      sqlite3DbFree( db, ref pTable.zColAff );
      sqlite3SelectDelete( db, ref pTable.pSelect );
#if !SQLITE_OMIT_CHECK
      sqlite3ExprDelete( db, ref pTable.pCheck );
#endif
#if !SQLITE_OMIT_VIRTUALTABLE
  sqlite3VtabClear(db, pTable);
#endif
      pTable = null;//      sqlite3DbFree( db, ref pTable );
    }

    /*
    ** Unlink the given table from the hash tables and the delete the
    ** table structure with all its indices and foreign keys.
    */
    static void sqlite3UnlinkAndDeleteTable( sqlite3 db, int iDb, string zTabName )
    {
      Table p;
      Db pDb;

      Debug.Assert( db != null );
      Debug.Assert( iDb >= 0 && iDb < db.nDb );
      Debug.Assert( zTabName != null );
      Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
      testcase( zTabName.Length == 0 );  /* Zero-length table names are allowed */
      pDb = db.aDb[iDb];
      p = sqlite3HashInsert( ref pDb.pSchema.tblHash, zTabName,
      sqlite3Strlen30( zTabName ), (Table)null );
      sqlite3DeleteTable( db, ref p );
      db.flags |= SQLITE_InternChanges;
    }

    /*
    ** Given a token, return a string that consists of the text of that
    ** token.  Space to hold the returned string
    ** is obtained from sqliteMalloc() and must be freed by the calling
    ** function.
    **
    ** Any quotation marks (ex:  "name", 'name', [name], or `name`) that
    ** surround the body of the token are removed.
    **
    ** Tokens are often just pointers into the original SQL text and so
    ** are not \000 terminated and are not persistent.  The returned string
    ** is \000 terminated and is persistent.
    */
    static string sqlite3NameFromToken( sqlite3 db, Token pName )
    {
      string zName;
      if ( pName != null && pName.z != null )
      {
        zName = pName.z.Substring( 0, pName.n );//sqlite3DbStrNDup(db, (char)pName.z, pName.n);
        sqlite3Dequote( ref zName );
      }
      else
      {
        return null;
      }
      return zName;
    }

    /*
    ** Open the sqlite_master table stored in database number iDb for
    ** writing. The table is opened using cursor 0.
    */
    static void sqlite3OpenMasterTable( Parse p, int iDb )
    {
      Vdbe v = sqlite3GetVdbe( p );
      sqlite3TableLock( p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE( iDb ) );
      sqlite3VdbeAddOp3( v, OP_OpenWrite, 0, MASTER_ROOT, iDb );
      sqlite3VdbeChangeP4( v, -1, (int)5, P4_INT32 );  /* 5 column table */
      if ( p.nTab == 0 )
      {
        p.nTab = 1;
      }
    }

    /*
    ** Parameter zName points to a nul-terminated buffer containing the name
    ** of a database ("main", "temp" or the name of an attached db). This
    ** function returns the index of the named database in db->aDb[], or
    ** -1 if the named db cannot be found.
    */
    static int sqlite3FindDbName( sqlite3 db, string zName )
    {
      int i = -1;    /* Database number */
      if ( zName != null )
      {
        Db pDb;
        int n = sqlite3Strlen30( zName );
        for ( i = ( db.nDb - 1 ); i >= 0; i-- )
        {
          pDb = db.aDb[i];
          if ( ( OMIT_TEMPDB == 0 || i != 1 ) && n == sqlite3Strlen30( pDb.zName ) &&
          pDb.zName.Equals( zName, StringComparison.InvariantCultureIgnoreCase ) )
          {
            break;
          }
        }
      }
      return i;
    }

    /*
    ** The token *pName contains the name of a database (either "main" or
    ** "temp" or the name of an attached db). This routine returns the
    ** index of the named database in db->aDb[], or -1 if the named db
    ** does not exist.
    */
    static int sqlite3FindDb( sqlite3 db, Token pName )
    {
      int i;                               /* Database number */
      string zName;                         /* Name we are searching for */
      zName = sqlite3NameFromToken( db, pName );
      i = sqlite3FindDbName( db, zName );
      sqlite3DbFree( db, ref zName );
      return i;
    }

    /* The table or view or trigger name is passed to this routine via tokens
    ** pName1 and pName2. If the table name was fully qualified, for example:
    **
    ** CREATE TABLE xxx.yyy (...);
    **
    ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
    ** the table name is not fully qualified, i.e.:
    **
    ** CREATE TABLE yyy(...);
    **
    ** Then pName1 is set to "yyy" and pName2 is "".
    **
    ** This routine sets the ppUnqual pointer to point at the token (pName1 or
    ** pName2) that stores the unqualified table name.  The index of the
    ** database "xxx" is returned.
    */
    static int sqlite3TwoPartName(
    Parse pParse,      /* Parsing and code generating context */
    Token pName1,      /* The "xxx" in the name "xxx.yyy" or "xxx" */
    Token pName2,      /* The "yyy" in the name "xxx.yyy" */
    ref Token pUnqual  /* Write the unqualified object name here */
    )
    {
      int iDb;                    /* Database holding the object */
      sqlite3 db = pParse.db;

      if ( ALWAYS( pName2 != null ) && pName2.n > 0 )
      {
        if ( db.init.busy != 0 )
        {
          sqlite3ErrorMsg( pParse, "corrupt database" );
          pParse.nErr++;
          return -1;
        }
        pUnqual = pName2;
        iDb = sqlite3FindDb( db, pName1 );
        if ( iDb < 0 )
        {
          sqlite3ErrorMsg( pParse, "unknown database %T", pName1 );
          pParse.nErr++;
          return -1;
        }
      }
      else
      {
        Debug.Assert( db.init.iDb == 0 || db.init.busy != 0 );
        iDb = db.init.iDb;
        pUnqual = pName1;
      }
      return iDb;
    }

    /*
    ** This routine is used to check if the UTF-8 string zName is a legal
    ** unqualified name for a new schema object (vtable, index, view or
    ** trigger). All names are legal except those that begin with the string
    ** "sqlite_" (in upper, lower or mixed case). This portion of the namespace
    ** is reserved for internal use.
    */
    static int sqlite3CheckObjectName( Parse pParse, string zName )
    {
      if ( 0 == pParse.db.init.busy && pParse.nested == 0
      && ( pParse.db.flags & SQLITE_WriteSchema ) == 0
      && zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) )
      {
        sqlite3ErrorMsg( pParse, "object name reserved for internal use: %s", zName );
        return SQLITE_ERROR;
      }
      return SQLITE_OK;
    }

    /*
    ** Begin constructing a new table representation in memory.  This is
    ** the first of several action routines that get called in response
    ** to a CREATE TABLE statement.  In particular, this routine is called
    ** after seeing tokens "CREATE" and "TABLE" and the table name. The isTemp
    ** flag is true if the table should be stored in the auxiliary database
    ** file instead of in the main database file.  This is normally the case
    ** when the "TEMP" or "TEMPORARY" keyword occurs in between
    ** CREATE and TABLE.
    **
    ** The new table record is initialized and put in pParse.pNewTable.
    ** As more of the CREATE TABLE statement is parsed, additional action
    ** routines will be called to add more information to this record.
    ** At the end of the CREATE TABLE statement, the sqlite3EndTable() routine
    ** is called to complete the construction of the new table record.
    */
    static void sqlite3StartTable(
    Parse pParse,   /* Parser context */
    Token pName1,   /* First part of the name of the table or view */
    Token pName2,   /* Second part of the name of the table or view */
    int isTemp,      /* True if this is a TEMP table */
    int isView,      /* True if this is a VIEW */
    int isVirtual,   /* True if this is a VIRTUAL table */
    int noErr        /* Do nothing if table already exists */
    )
    {
      Table pTable;
      string zName = null; /* The name of the new table */
      sqlite3 db = pParse.db;
      Vdbe v;
      int iDb;         /* Database number to create the table in */
      Token pName = new Token();    /* Unqualified name of the table to create */

      /* The table or view name to create is passed to this routine via tokens
      ** pName1 and pName2. If the table name was fully qualified, for example:
      **
      ** CREATE TABLE xxx.yyy (...);
      **
      ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
      ** the table name is not fully qualified, i.e.:
      **
      ** CREATE TABLE yyy(...);
      **
      ** Then pName1 is set to "yyy" and pName2 is "".
      **
      ** The call below sets the pName pointer to point at the token (pName1 or
      ** pName2) that stores the unqualified table name. The variable iDb is
      ** set to the index of the database that the table or view is to be
      ** created in.
      */
      iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName );
      if ( iDb < 0 )
        return;
      if ( 0 == OMIT_TEMPDB && isTemp != 0 && pName2.n > 0 && iDb != 1 )
      {
        /* If creating a temp table, the name may not be qualified. Unless 
        ** the database name is "temp" anyway.  */

        sqlite3ErrorMsg( pParse, "temporary table name must be unqualified" );
        return;
      }
      if ( OMIT_TEMPDB == 0 && isTemp != 0 )
        iDb = 1;

      pParse.sNameToken = pName;
      zName = sqlite3NameFromToken( db, pName );
      if ( zName == null )
        return;
      if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) )
      {
        goto begin_table_error;
      }
      if ( db.init.iDb == 1 )
        isTemp = 1;
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
Debug.Assert( (isTemp & 1)==isTemp );
{
int code;
string zDb = db.aDb[iDb].zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
goto begin_table_error;
}
if( isView ){
if( OMIT_TEMPDB ==0&& isTemp ){
code = SQLITE_CREATE_TEMP_VIEW;
}else{
code = SQLITE_CREATE_VIEW;
}
}else{
if( OMIT_TEMPDB ==0&& isTemp ){
code = SQLITE_CREATE_TEMP_TABLE;
}else{
code = SQLITE_CREATE_TABLE;
}
}
if( null==isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
goto begin_table_error;
}
}
#endif

      /* Make sure the new table name does not collide with an existing
** index or table name in the same database.  Issue an error message if
** it does. The exception is if the statement being parsed was passed
** to an sqlite3_declare_vtab() call. In that case only the column names
** and types will be used, so there is no need to test for namespace
** collisions.
*/
      if ( !IN_DECLARE_VTAB( pParse ) )
      {
        String zDb = db.aDb[iDb].zName;
        if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
        {
          goto begin_table_error;
        }
        pTable = sqlite3FindTable( db, zName, zDb );
        if ( pTable != null )
        {
          if ( noErr == 0 )
          {
            sqlite3ErrorMsg( pParse, "table %T already exists", pName );
          }
          else
          {
            Debug.Assert( 0 == db.init.busy );
            sqlite3CodeVerifySchema( pParse, iDb );
          }
          goto begin_table_error;
        }
        if ( sqlite3FindIndex( db, zName, zDb ) != null )
        {
          sqlite3ErrorMsg( pParse, "there is already an index named %s", zName );
          goto begin_table_error;
        }
      }

      pTable = new Table();// sqlite3DbMallocZero(db, Table).Length;
      //if ( pTable == null )
      //{
      //  db.mallocFailed = 1;
      //  pParse.rc = SQLITE_NOMEM;
      //  pParse.nErr++;
      //  goto begin_table_error;
      //}
      pTable.zName = zName;
      pTable.iPKey = -1;
      pTable.pSchema = db.aDb[iDb].pSchema;
      pTable.nRef = 1;
      pTable.nRowEst = 1000000;
      Debug.Assert( pParse.pNewTable == null );
      pParse.pNewTable = pTable;

      /* If this is the magic sqlite_sequence table used by autoincrement,
      ** then record a pointer to this table in the main database structure
      ** so that INSERT can find the table easily.
      */
#if !SQLITE_OMIT_AUTOINCREMENT
      if ( pParse.nested == 0 && zName == "sqlite_sequence" )
      {
        Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
        pTable.pSchema.pSeqTab = pTable;
      }
#endif

      /* Begin generating the code that will insert the table record into
** the SQLITE_MASTER table.  Note in particular that we must go ahead
** and allocate the record number for the table entry now.  Before any
** PRIMARY KEY or UNIQUE keywords are parsed.  Those keywords will cause
** indices to be created and the table record must come before the
** indices.  Hence, the record number for the table must be allocated
** now.
*/
      if ( 0 == db.init.busy && ( v = sqlite3GetVdbe( pParse ) ) != null )
      {
        int j1;
        int fileFormat;
        int reg1, reg2, reg3;
        sqlite3BeginWriteOperation( pParse, 0, iDb );

        if ( isVirtual != 0 )
        {
          sqlite3VdbeAddOp0( v, OP_VBegin );
        }

        /* If the file format and encoding in the database have not been set,
        ** set them now.
        */
        reg1 = pParse.regRowid = ++pParse.nMem;
        reg2 = pParse.regRoot = ++pParse.nMem;
        reg3 = ++pParse.nMem;
        sqlite3VdbeAddOp3( v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT );
        sqlite3VdbeUsesBtree( v, iDb );
        j1 = sqlite3VdbeAddOp1( v, OP_If, reg3 );
        fileFormat = ( db.flags & SQLITE_LegacyFileFmt ) != 0 ?
        1 : SQLITE_MAX_FILE_FORMAT;
        sqlite3VdbeAddOp2( v, OP_Integer, fileFormat, reg3 );
        sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3 );
        sqlite3VdbeAddOp2( v, OP_Integer, ENC( db ), reg3 );
        sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3 );
        sqlite3VdbeJumpHere( v, j1 );

        /* This just creates a place-holder record in the sqlite_master table.
        ** The record created does not contain anything yet.  It will be replaced
        ** by the real entry in code generated at sqlite3EndTable().
        **
        ** The rowid for the new entry is left in register pParse->regRowid.
        ** The root page number of the new table is left in reg pParse->regRoot.
        ** The rowid and root page number values are needed by the code that
        ** sqlite3EndTable will generate.
        */
        if ( isView != 0 || isVirtual != 0 )
        {
          sqlite3VdbeAddOp2( v, OP_Integer, 0, reg2 );
        }
        else
        {
          sqlite3VdbeAddOp2( v, OP_CreateTable, iDb, reg2 );
        }
        sqlite3OpenMasterTable( pParse, iDb );
        sqlite3VdbeAddOp2( v, OP_NewRowid, 0, reg1 );
        sqlite3VdbeAddOp2( v, OP_Null, 0, reg3 );
        sqlite3VdbeAddOp3( v, OP_Insert, 0, reg3, reg1 );
        sqlite3VdbeChangeP5( v, OPFLAG_APPEND );
        sqlite3VdbeAddOp0( v, OP_Close );
      }

      /* Normal (non-error) return. */
      return;

      /* If an error occurs, we jump here */
begin_table_error:
      sqlite3DbFree( db, ref zName );
      return;
    }

    /*
    ** This macro is used to compare two strings in a case-insensitive manner.
    ** It is slightly faster than calling sqlite3StrICmp() directly, but
    ** produces larger code.
    **
    ** WARNING: This macro is not compatible with the strcmp() family. It
    ** returns true if the two strings are equal, otherwise false.
    */
    //#define STRICMP(x, y) (\
    //sqlite3UpperToLower[*(unsigned char )(x)]==   \
    //sqlite3UpperToLower[*(unsigned char )(y)]     \
    //&& sqlite3StrICmp((x)+1,(y)+1)==0 )

    /*
    ** Add a new column to the table currently being constructed.
    **
    ** The parser calls this routine once for each column declaration
    ** in a CREATE TABLE statement.  sqlite3StartTable() gets called
    ** first to get things going.  Then this routine is called for each
    ** column.
    */
    static void sqlite3AddColumn( Parse pParse, Token pName )
    {
      Table p;
      int i;
      string z;
      Column pCol;
      sqlite3 db = pParse.db;
      if ( ( p = pParse.pNewTable ) == null )
        return;
#if SQLITE_MAX_COLUMN || !SQLITE_MAX_COLUMN
      if ( p.nCol + 1 > db.aLimit[SQLITE_LIMIT_COLUMN] )
      {
        sqlite3ErrorMsg( pParse, "too many columns on %s", p.zName );
        return;
      }
#endif
      z = sqlite3NameFromToken( db, pName );
      if ( z == null )
        return;
      for ( i = 0; i < p.nCol; i++ )
      {
        if ( z.Equals( p.aCol[i].zName, StringComparison.InvariantCultureIgnoreCase ) )
        {//STRICMP(z, p.aCol[i].zName) ){
          sqlite3ErrorMsg( pParse, "duplicate column name: %s", z );
          sqlite3DbFree( db, ref z );
          return;
        }
      }
      if ( ( p.nCol & 0x7 ) == 0 )
      {
        //aNew = sqlite3DbRealloc(db,p.aCol,(p.nCol+8)*sizeof(p.aCol[0]));
        //if( aNew==0 ){
        //  sqlite3DbFree(db,ref z);
        //  return;
        //}
        Array.Resize( ref p.aCol, p.nCol + 8 );
      }
      p.aCol[p.nCol] = new Column();
      pCol = p.aCol[p.nCol];
      //memset(pCol, 0, sizeof(p.aCol[0]));
      pCol.zName = z;

      /* If there is no type specified, columns have the default affinity
      ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will
      ** be called next to set pCol.affinity correctly.
      */
      pCol.affinity = SQLITE_AFF_NONE;
      p.nCol++;
    }

    /*
    ** This routine is called by the parser while in the middle of
    ** parsing a CREATE TABLE statement.  A "NOT NULL" constraint has
    ** been seen on a column.  This routine sets the notNull flag on
    ** the column currently under construction.
    */
    static void sqlite3AddNotNull( Parse pParse, int onError )
    {
      Table p;
      p = pParse.pNewTable;
      if ( p == null || NEVER( p.nCol < 1 ) )
        return;
      p.aCol[p.nCol - 1].notNull = (u8)onError;
    }

    /*
    ** Scan the column type name zType (length nType) and return the
    ** associated affinity type.
    **
    ** This routine does a case-independent search of zType for the
    ** substrings in the following table. If one of the substrings is
    ** found, the corresponding affinity is returned. If zType contains
    ** more than one of the substrings, entries toward the top of
    ** the table take priority. For example, if zType is 'BLOBINT',
    ** SQLITE_AFF_INTEGER is returned.
    **
    ** Substring     | Affinity
    ** --------------------------------
    ** 'INT'         | SQLITE_AFF_INTEGER
    ** 'CHAR'        | SQLITE_AFF_TEXT
    ** 'CLOB'        | SQLITE_AFF_TEXT
    ** 'TEXT'        | SQLITE_AFF_TEXT
    ** 'BLOB'        | SQLITE_AFF_NONE
    ** 'REAL'        | SQLITE_AFF_REAL
    ** 'FLOA'        | SQLITE_AFF_REAL
    ** 'DOUB'        | SQLITE_AFF_REAL
    **
    ** If none of the substrings in the above table are found,
    ** SQLITE_AFF_NUMERIC is returned.
    */
    static char sqlite3AffinityType( string zIn )
    {
      //u32 h = 0;
      //char aff = SQLITE_AFF_NUMERIC;
      zIn = zIn.ToLower();
      if ( zIn.Contains( "char" ) || zIn.Contains( "clob" ) || zIn.Contains( "text" ) )
        return SQLITE_AFF_TEXT;
      if ( zIn.Contains( "blob" ) )
        return SQLITE_AFF_NONE;
      if ( zIn.Contains( "doub" ) || zIn.Contains( "floa" ) || zIn.Contains( "real" ) )
        return SQLITE_AFF_REAL;
      if ( zIn.Contains( "int" ) )
        return SQLITE_AFF_INTEGER;
      return SQLITE_AFF_NUMERIC;
      //      string zEnd = pType.z.Substring(pType.n);

      //      while( zIn!=zEnd ){
      //        h = (h<<8) + sqlite3UpperToLower[*zIn];
      //        zIn++;
      //        if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){             /* CHAR */
      //          aff = SQLITE_AFF_TEXT;
      //        }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){       /* CLOB */
      //          aff = SQLITE_AFF_TEXT;
      //        }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){       /* TEXT */
      //          aff = SQLITE_AFF_TEXT;
      //        }else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b')          /* BLOB */
      //            && (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
      //          aff = SQLITE_AFF_NONE;
      //#if !SQLITE_OMIT_FLOATING_POINT
      //        }else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l')          /* REAL */
      //            && aff==SQLITE_AFF_NUMERIC ){
      //          aff = SQLITE_AFF_REAL;
      //        }else if( h==(('f'<<24)+('l'<<16)+('o'<<8)+'a')          /* FLOA */
      //            && aff==SQLITE_AFF_NUMERIC ){
      //          aff = SQLITE_AFF_REAL;
      //        }else if( h==(('d'<<24)+('o'<<16)+('u'<<8)+'b')          /* DOUB */
      //            && aff==SQLITE_AFF_NUMERIC ){
      //          aff = SQLITE_AFF_REAL;
      //#endif
      //        }else if( (h&0x00FFFFFF)==(('i'<<16)+('n'<<8)+'t') ){    /* INT */
      //          aff = SQLITE_AFF_INTEGER;
      //          break;
      //        }
      //      }

      //      return aff;
    }

    /*
    ** This routine is called by the parser while in the middle of
    ** parsing a CREATE TABLE statement.  The pFirst token is the first
    ** token in the sequence of tokens that describe the type of the
    ** column currently under construction.   pLast is the last token
    ** in the sequence.  Use this information to construct a string
    ** that contains the typename of the column and store that string
    ** in zType.
    */
    static void sqlite3AddColumnType( Parse pParse, Token pType )
    {
      Table p;
      Column pCol;

      p = pParse.pNewTable;
      if ( p == null || NEVER( p.nCol < 1 ) )
        return;
      pCol = p.aCol[p.nCol - 1];
      Debug.Assert( pCol.zType == null );
      pCol.zType = sqlite3NameFromToken( pParse.db, pType );
      pCol.affinity = sqlite3AffinityType( pCol.zType );
    }


    /*
    ** The expression is the default value for the most recently added column
    ** of the table currently under construction.
    **
    ** Default value expressions must be constant.  Raise an exception if this
    ** is not the case.
    **
    ** This routine is called by the parser while in the middle of
    ** parsing a CREATE TABLE statement.
    */
    static void sqlite3AddDefaultValue( Parse pParse, ExprSpan pSpan )
    {
      Table p;
      Column pCol;
      sqlite3 db = pParse.db;
      p = pParse.pNewTable;
      if ( p != null )
      {
        pCol = ( p.aCol[p.nCol - 1] );
        if ( sqlite3ExprIsConstantOrFunction( pSpan.pExpr ) == 0 )
        {
          sqlite3ErrorMsg( pParse, "default value of column [%s] is not constant",
          pCol.zName );
        }
        else
        {
          /* A copy of pExpr is used instead of the original, as pExpr contains
          ** tokens that point to volatile memory. The 'span' of the expression
          ** is required by pragma table_info.
          */
          sqlite3ExprDelete( db, ref pCol.pDflt );
          pCol.pDflt = sqlite3ExprDup( db, pSpan.pExpr, EXPRDUP_REDUCE );
          sqlite3DbFree( db, ref pCol.zDflt );
          pCol.zDflt = pSpan.zStart.Substring( 0, pSpan.zStart.Length - pSpan.zEnd.Length );
          //sqlite3DbStrNDup( db, pSpan.zStart,
          //                               (int)( pSpan.zEnd.Length - pSpan.zStart.Length ) );
        }
      }
      sqlite3ExprDelete( db, ref pSpan.pExpr );
    }

    /*
    ** Designate the PRIMARY KEY for the table.  pList is a list of names
    ** of columns that form the primary key.  If pList is NULL, then the
    ** most recently added column of the table is the primary key.
    **
    ** A table can have at most one primary key.  If the table already has
    ** a primary key (and this is the second primary key) then create an
    ** error.
    **
    ** If the PRIMARY KEY is on a single column whose datatype is INTEGER,
    ** then we will try to use that column as the rowid.  Set the Table.iPKey
    ** field of the table under construction to be the index of the
    ** INTEGER PRIMARY KEY column.  Table.iPKey is set to -1 if there is
    ** no INTEGER PRIMARY KEY.
    **
    ** If the key is not an INTEGER PRIMARY KEY, then create a unique
    ** index for the key.  No index is created for INTEGER PRIMARY KEYs.
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static void sqlite3AddPrimaryKey( Parse pParse, int null_2, int onError, int autoInc, int sortOrder )
    {
      sqlite3AddPrimaryKey( pParse, null, onError, autoInc, sortOrder );
    }
    static void sqlite3AddPrimaryKey(
    Parse pParse,    /* Parsing context */
    ExprList pList,  /* List of field names to be indexed */
    int onError,     /* What to do with a uniqueness conflict */
    int autoInc,    /* True if the AUTOINCREMENT keyword is present */
    int sortOrder   /* SQLITE_SO_ASC or SQLITE_SO_DESC */
    )
    {
      Table pTab = pParse.pNewTable;
      string zType = null;
      int iCol = -1, i;
      if ( pTab == null || IN_DECLARE_VTAB( pParse ) )
        goto primary_key_exit;
      if ( ( pTab.tabFlags & TF_HasPrimaryKey ) != 0 )
      {
        sqlite3ErrorMsg( pParse,
        "table \"%s\" has more than one primary key", pTab.zName );
        goto primary_key_exit;
      }
      pTab.tabFlags |= TF_HasPrimaryKey;
      if ( pList == null )
      {
        iCol = pTab.nCol - 1;
        pTab.aCol[iCol].isPrimKey = 1;
      }
      else
      {
        for ( i = 0; i < pList.nExpr; i++ )
        {
          for ( iCol = 0; iCol < pTab.nCol; iCol++ )
          {
            if ( pList.a[i].zName.Equals( pTab.aCol[iCol].zName, StringComparison.InvariantCultureIgnoreCase ) )
            {
              break;
            }
          }
          if ( iCol < pTab.nCol )
          {
            pTab.aCol[iCol].isPrimKey = 1;
          }
        }
        if ( pList.nExpr > 1 )
          iCol = -1;
      }
      if ( iCol >= 0 && iCol < pTab.nCol )
      {
        zType = pTab.aCol[iCol].zType;
      }
      if ( zType != null && zType.Equals( "INTEGER", StringComparison.InvariantCultureIgnoreCase )
      && sortOrder == SQLITE_SO_ASC )
      {
        pTab.iPKey = iCol;
        pTab.keyConf = (byte)onError;
        Debug.Assert( autoInc == 0 || autoInc == 1 );
        pTab.tabFlags |= (u8)( autoInc * TF_Autoincrement );
      }
      else if ( autoInc != 0 )
      {
#if !SQLITE_OMIT_AUTOINCREMENT
        sqlite3ErrorMsg( pParse, "AUTOINCREMENT is only allowed on an " +
        "INTEGER PRIMARY KEY" );
#endif
      }
      else
      {
        Index p;
        p = sqlite3CreateIndex( pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0 );
        if ( p != null )
        {
          p.autoIndex = 2;
        }
        pList = null;
      }

primary_key_exit:
      sqlite3ExprListDelete( pParse.db, ref pList );
      return;
    }

    /*
    ** Add a new CHECK constraint to the table currently under construction.
    */
    static void sqlite3AddCheckConstraint(
    Parse pParse,    /* Parsing context */
    Expr pCheckExpr  /* The check expression */
    )
    {
      sqlite3 db = pParse.db;
#if !SQLITE_OMIT_CHECK
      Table pTab = pParse.pNewTable;
      if ( pTab != null && !IN_DECLARE_VTAB( pParse ) )
      {
        pTab.pCheck = sqlite3ExprAnd( db, pTab.pCheck, pCheckExpr );
      }
      else
#endif
      {
        sqlite3ExprDelete( db, ref pCheckExpr );
      }
    }
    /*
    ** Set the collation function of the most recently parsed table column
    ** to the CollSeq given.
    */
    static void sqlite3AddCollateType( Parse pParse, Token pToken )
    {
      Table p;
      int i;
      string zColl;              /* Dequoted name of collation sequence */
      sqlite3 db;

      if ( ( p = pParse.pNewTable ) == null )
        return;
      i = p.nCol - 1;
      db = pParse.db;
      zColl = sqlite3NameFromToken( db, pToken );
      if ( zColl == null )
        return;

      if ( sqlite3LocateCollSeq( pParse, zColl ) != null )
      {
        Index pIdx;
        p.aCol[i].zColl = zColl;

        /* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
        ** then an index may have been created on this column before the
        ** collation type was added. Correct this if it is the case.
        */
        for ( pIdx = p.pIndex; pIdx != null; pIdx = pIdx.pNext )
        {
          Debug.Assert( pIdx.nColumn == 1 );
          if ( pIdx.aiColumn[0] == i )
          {
            pIdx.azColl[0] = p.aCol[i].zColl;
          }
        }
      }
      else
      {
        sqlite3DbFree( db, ref zColl );
      }
    }

    /*
    ** This function returns the collation sequence for database native text
    ** encoding identified by the string zName, length nName.
    **
    ** If the requested collation sequence is not available, or not available
    ** in the database native encoding, the collation factory is invoked to
    ** request it. If the collation factory does not supply such a sequence,
    ** and the sequence is available in another text encoding, then that is
    ** returned instead.
    **
    ** If no versions of the requested collations sequence are available, or
    ** another error occurs, NULL is returned and an error message written into
    ** pParse.
    **
    ** This routine is a wrapper around sqlite3FindCollSeq().  This routine
    ** invokes the collation factory if the named collation cannot be found
    ** and generates an error message.
    **
    ** See also: sqlite3FindCollSeq(), sqlite3GetCollSeq()
    */
    static CollSeq sqlite3LocateCollSeq( Parse pParse, string zName )
    {
      sqlite3 db = pParse.db;
      u8 enc = db.aDb[0].pSchema.enc;// ENC(db);
      u8 initbusy = db.init.busy;
      CollSeq pColl;

      pColl = sqlite3FindCollSeq( db, enc, zName, initbusy );
      if ( 0 == initbusy && ( pColl == null || pColl.xCmp == null ) )
      {
        pColl = sqlite3GetCollSeq( db, enc, pColl, zName );
        if ( pColl == null )
        {
          sqlite3ErrorMsg( pParse, "no such collation sequence: %s", zName );
        }
      }

      return pColl;
    }


    /*
    ** Generate code that will increment the schema cookie.
    **
    ** The schema cookie is used to determine when the schema for the
    ** database changes.  After each schema change, the cookie value
    ** changes.  When a process first reads the schema it records the
    ** cookie.  Thereafter, whenever it goes to access the database,
    ** it checks the cookie to make sure the schema has not changed
    ** since it was last read.
    **
    ** This plan is not completely bullet-proof.  It is possible for
    ** the schema to change multiple times and for the cookie to be
    ** set back to prior value.  But schema changes are infrequent
    ** and the probability of hitting the same cookie value is only
    ** 1 chance in 2^32.  So we're safe enough.
    */
    static void sqlite3ChangeCookie( Parse pParse, int iDb )
    {
      int r1 = sqlite3GetTempReg( pParse );
      sqlite3 db = pParse.db;
      Vdbe v = pParse.pVdbe;
      Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
      sqlite3VdbeAddOp2( v, OP_Integer, db.aDb[iDb].pSchema.schema_cookie + 1, r1 );
      sqlite3VdbeAddOp3( v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1 );
      sqlite3ReleaseTempReg( pParse, r1 );
    }

    /*
    ** Measure the number of characters needed to output the given
    ** identifier.  The number returned includes any quotes used
    ** but does not include the null terminator.
    **
    ** The estimate is conservative.  It might be larger that what is
    ** really needed.
    */
    static int identLength( string z )
    {
      int n;
      for ( n = 0; n < z.Length; n++ )
      {
        if ( z[n] == (byte)'"' )
        {
          n++;
        }
      }
      return n + 2;
    }


    /*
    ** The first parameter is a pointer to an output buffer. The second
    ** parameter is a pointer to an integer that contains the offset at
    ** which to write into the output buffer. This function copies the
    ** nul-terminated string pointed to by the third parameter, zSignedIdent,
    ** to the specified offset in the buffer and updates *pIdx to refer
    ** to the first byte after the last byte written before returning.
    **
    ** If the string zSignedIdent consists entirely of alpha-numeric
    ** characters, does not begin with a digit and is not an SQL keyword,
    ** then it is copied to the output buffer exactly as it is. Otherwise,
    ** it is quoted using double-quotes.
    */
    static void identPut( StringBuilder z, ref int pIdx, string zSignedIdent )
    {
      string zIdent = zSignedIdent;
      int i;
      int j;
      bool needQuote;
      i = pIdx;
      for ( j = 0; j < zIdent.Length; j++ )
      {
        if ( !sqlite3Isalnum( zIdent[j] ) && zIdent[j] != '_' )
          break;
      }
      needQuote = sqlite3Isdigit( zIdent[0] ) || sqlite3KeywordCode( zIdent, j ) != TK_ID;
      if ( !needQuote )
      {
        needQuote = ( j < zIdent.Length && zIdent[j] != 0 );
      }
      if ( needQuote )
      {
        if ( i == z.Length )
          z.Append( '\0' );
        z[i++] = '"';
      }
      for ( j = 0; j < zIdent.Length; j++ )
      {
        if ( i == z.Length )
          z.Append( '\0' );
        z[i++] = zIdent[j];
        if ( zIdent[j] == '"' )
        {
          if ( i == z.Length )
            z.Append( '\0' );
          z[i++] = '"';
        }
      }
      if ( needQuote )
      {
        if ( i == z.Length )
          z.Append( '\0' );
        z[i++] = '"';
      }
      //z[i] = 0;
      pIdx = i;
    }

    /*
    ** Generate a CREATE TABLE statement appropriate for the given
    ** table.  Memory to hold the text of the statement is obtained
    ** from sqliteMalloc() and must be freed by the calling function.
    */
    static string createTableStmt( sqlite3 db, Table p )
    {
      int i, k, n;
      StringBuilder zStmt;
      string zSep;
      string zSep2;
      string zEnd;
      Column pCol;
      n = 0;
      for ( i = 0; i < p.nCol; i++ )
      {//, pCol++){
        pCol = p.aCol[i];
        n += identLength( pCol.zName ) + 5;
      }
      n += identLength( p.zName );
      if ( n < 50 )
      {
        zSep = "";
        zSep2 = ",";
        zEnd = ")";
      }
      else
      {
        zSep = "\n  ";
        zSep2 = ",\n  ";
        zEnd = "\n)";
      }
      n += 35 + 6 * p.nCol;
      zStmt = new StringBuilder( n );
      //zStmt = sqlite3DbMallocRaw(0, n);
      //if( zStmt==0 ){
      //  db.mallocFailed = 1;
      //  return 0;
      //}
      //sqlite3_snprintf(n, zStmt,"CREATE TABLE ");
      zStmt.Append( "CREATE TABLE " );
      k = sqlite3Strlen30( zStmt );
      identPut( zStmt, ref k, p.zName );
      zStmt.Append( '(' );//zStmt[k++] = '(';
      for ( i = 0; i < p.nCol; i++ )
      {//, pCol++){
        pCol = p.aCol[i];
        string[] azType = new string[]  {
/* SQLITE_AFF_TEXT    */ " TEXT",
/* SQLITE_AFF_NONE    */ "",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
/* SQLITE_AFF_REAL    */ " REAL"
};
        int len;
        string zType;

        zStmt.Append( zSep );//  sqlite3_snprintf(n-k, zStmt[k], zSep);
        k = sqlite3Strlen30( zStmt );//  k += strlen(zStmt[k]);
        zSep = zSep2;
        identPut( zStmt, ref k, pCol.zName );
        Debug.Assert( pCol.affinity - SQLITE_AFF_TEXT >= 0 );
        Debug.Assert( pCol.affinity - SQLITE_AFF_TEXT < ArraySize( azType ) );
        testcase( pCol.affinity == SQLITE_AFF_TEXT );
        testcase( pCol.affinity == SQLITE_AFF_NONE );
        testcase( pCol.affinity == SQLITE_AFF_NUMERIC );
        testcase( pCol.affinity == SQLITE_AFF_INTEGER );
        testcase( pCol.affinity == SQLITE_AFF_REAL );

        zType = azType[pCol.affinity - SQLITE_AFF_TEXT];
        len = sqlite3Strlen30( zType );
        Debug.Assert( pCol.affinity == SQLITE_AFF_NONE
        || pCol.affinity == sqlite3AffinityType( zType ) );
        zStmt.Append( zType );// memcpy( &zStmt[k], zType, len );
        k += len;
        Debug.Assert( k <= n );
      }
      zStmt.Append( zEnd );//sqlite3_snprintf(n-k, zStmt[k], "%s", zEnd);
      return zStmt.ToString();
    }

    /*
    ** This routine is called to report the final ")" that terminates
    ** a CREATE TABLE statement.
    **
    ** The table structure that other action routines have been building
    ** is added to the internal hash tables, assuming no errors have
    ** occurred.
    **
    ** An entry for the table is made in the master table on disk, unless
    ** this is a temporary table or db.init.busy==1.  When db.init.busy==1
    ** it means we are reading the sqlite_master table because we just
    ** connected to the database or because the sqlite_master table has
    ** recently changed, so the entry for this table already exists in
    ** the sqlite_master table.  We do not want to create it again.
    **
    ** If the pSelect argument is not NULL, it means that this routine
    ** was called to create a table generated from a
    ** "CREATE TABLE ... AS SELECT ..." statement.  The column names of
    ** the new table will match the result set of the SELECT.
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static void sqlite3EndTable( Parse pParse, Token pCons, Token pEnd, int null_4 )
    {
      sqlite3EndTable( pParse, pCons, pEnd, null );
    }
    static void sqlite3EndTable( Parse pParse, int null_2, int null_3, Select pSelect )
    {
      sqlite3EndTable( pParse, null, null, pSelect );
    }

    static void sqlite3EndTable(
    Parse pParse,          /* Parse context */
    Token pCons,           /* The ',' token after the last column defn. */
    Token pEnd,            /* The final ')' token in the CREATE TABLE */
    Select pSelect         /* Select from a "CREATE ... AS SELECT" */
    )
    {
      Table p;
      sqlite3 db = pParse.db;
      int iDb;

      if ( ( pEnd == null && pSelect == null ) /*|| db.mallocFailed != 0 */ )
      {
        return;
      }
      p = pParse.pNewTable;
      if ( p == null )
        return;

      Debug.Assert( 0 == db.init.busy || pSelect == null );

      iDb = sqlite3SchemaToIndex( db, p.pSchema );

#if !SQLITE_OMIT_CHECK
      /* Resolve names in all CHECK constraint expressions.
*/
      if ( p.pCheck != null )
      {
        SrcList sSrc;                   /* Fake SrcList for pParse.pNewTable */
        NameContext sNC;                /* Name context for pParse.pNewTable */

        sNC = new NameContext();// memset(sNC, 0, sizeof(sNC));
        sSrc = new SrcList();// memset(sSrc, 0, sizeof(sSrc));
        sSrc.nSrc = 1;
        sSrc.a = new SrcList_item[1];
        sSrc.a[0] = new SrcList_item();
        sSrc.a[0].zName = p.zName;
        sSrc.a[0].pTab = p;
        sSrc.a[0].iCursor = -1;
        sNC.pParse = pParse;
        sNC.pSrcList = sSrc;
        sNC.isCheck = 1;
        if ( sqlite3ResolveExprNames( sNC, ref p.pCheck ) != 0 )
        {
          return;
        }
      }
#endif // * !SQLITE_OMIT_CHECK) */

      /* If the db.init.busy is 1 it means we are reading the SQL off the
** "sqlite_master" or "sqlite_temp_master" table on the disk.
** So do not write to the disk again.  Extract the root page number
** for the table from the db.init.newTnum field.  (The page number
** should have been put there by the sqliteOpenCb routine.)
*/
      if ( db.init.busy != 0 )
      {
        p.tnum = db.init.newTnum;
      }

      /* If not initializing, then create a record for the new table
      ** in the SQLITE_MASTER table of the database.
      **
      ** If this is a TEMPORARY table, write the entry into the auxiliary
      ** file instead of into the main database file.
      */
      if ( 0 == db.init.busy )
      {
        int n;
        Vdbe v;
        String zType = "";    /* "view" or "table" */
        String zType2 = "";    /* "VIEW" or "TABLE" */
        String zStmt = "";    /* Text of the CREATE TABLE or CREATE VIEW statement */

        v = sqlite3GetVdbe( pParse );
        if ( NEVER( v == null ) )
          return;

        sqlite3VdbeAddOp1( v, OP_Close, 0 );

        /*
        ** Initialize zType for the new view or table.
        */
        if ( p.pSelect == null )
        {
          /* A regular table */
          zType = "table";
          zType2 = "TABLE";
#if !SQLITE_OMIT_VIEW
        }
        else
        {
          /* A view */
          zType = "view";
          zType2 = "VIEW";
#endif
        }

        /* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
        ** statement to populate the new table. The root-page number for the
        ** new table is in register pParse->regRoot.
        **
        ** Once the SELECT has been coded by sqlite3Select(), it is in a
        ** suitable state to query for the column names and types to be used
        ** by the new table.
        **
        ** A shared-cache write-lock is not required to write to the new table,
        ** as a schema-lock must have already been obtained to create it. Since
        ** a schema-lock excludes all other database users, the write-lock would
        ** be redundant.
        */
        if ( pSelect != null )
        {
          SelectDest dest = new SelectDest();
          Table pSelTab;

          Debug.Assert( pParse.nTab == 1 );
          sqlite3VdbeAddOp3( v, OP_OpenWrite, 1, pParse.regRoot, iDb );
          sqlite3VdbeChangeP5( v, 1 );
          pParse.nTab = 2;
          sqlite3SelectDestInit( dest, SRT_Table, 1 );
          sqlite3Select( pParse, pSelect, ref dest );
          sqlite3VdbeAddOp1( v, OP_Close, 1 );
          if ( pParse.nErr == 0 )
          {
            pSelTab = sqlite3ResultSetOfSelect( pParse, pSelect );
            if ( pSelTab == null )
              return;
            Debug.Assert( p.aCol == null );
            p.nCol = pSelTab.nCol;
            p.aCol = pSelTab.aCol;
            pSelTab.nCol = 0;
            pSelTab.aCol = null;
            sqlite3DeleteTable( db, ref pSelTab );
          }
        }

        /* Compute the complete text of the CREATE statement */
        if ( pSelect != null )
        {
          zStmt = createTableStmt( db, p );
        }
        else
        {
          n = (int)( pParse.sNameToken.z.Length - pEnd.z.Length ) + 1;
          zStmt = sqlite3MPrintf( db,
          "CREATE %s %.*s", zType2, n, pParse.sNameToken.z
          );
        }

        /* A slot for the record has already been allocated in the
        ** SQLITE_MASTER table.  We just need to update that slot with all
        ** the information we've collected.
        */
        sqlite3NestedParse( pParse,
        "UPDATE %Q.%s " +
        "SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q " +
        "WHERE rowid=#%d",
        db.aDb[iDb].zName, SCHEMA_TABLE( iDb ),
        zType,
        p.zName,
        p.zName,
        pParse.regRoot,
        zStmt,
        pParse.regRowid
        );
        sqlite3DbFree( db, ref zStmt );
        sqlite3ChangeCookie( pParse, iDb );

#if !SQLITE_OMIT_AUTOINCREMENT
        /* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
        if ( ( p.tabFlags & TF_Autoincrement ) != 0 )
        {
          Db pDb = db.aDb[iDb];
          Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
          if ( pDb.pSchema.pSeqTab == null )
          {
            sqlite3NestedParse( pParse,
            "CREATE TABLE %Q.sqlite_sequence(name,seq)",
            pDb.zName
            );
          }
        }
#endif

        /* Reparse everything to update our internal data structures */
        sqlite3VdbeAddParseSchemaOp( v, iDb,
                   sqlite3MPrintf( db, "tbl_name='%q'", p.zName ) );
      }


      /* Add the table to the in-memory representation of the database.
      */
      if ( db.init.busy != 0 )
      {
        Table pOld;
        Schema pSchema = p.pSchema;
        Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
        pOld = sqlite3HashInsert( ref pSchema.tblHash, p.zName,
        sqlite3Strlen30( p.zName ), p );
        if ( pOld != null )
        {
          Debug.Assert( p == pOld );  /* Malloc must have failed inside HashInsert() */
          //        db.mallocFailed = 1;
          return;
        }
        pParse.pNewTable = null;
        db.nTable++;
        db.flags |= SQLITE_InternChanges;

#if !SQLITE_OMIT_ALTERTABLE
        if ( p.pSelect == null )
        {
          string zName = pParse.sNameToken.z;
          int nName;
          Debug.Assert( pSelect == null && pCons != null && pEnd != null );
          if ( pCons.z == null )
          {
            pCons = pEnd;
          }
          nName = zName.Length - pCons.z.Length;
          p.addColOffset = 13 + nName; // sqlite3Utf8CharLen(zName, nName);
        }
#endif
      }
    }

#if !SQLITE_OMIT_VIEW
    /*
** The parser calls this routine in order to create a new VIEW
*/
    static void sqlite3CreateView(
    Parse pParse,     /* The parsing context */
    Token pBegin,     /* The CREATE token that begins the statement */
    Token pName1,     /* The token that holds the name of the view */
    Token pName2,     /* The token that holds the name of the view */
    Select pSelect,   /* A SELECT statement that will become the new view */
    int isTemp,      /* TRUE for a TEMPORARY view */
    int noErr         /* Suppress error messages if VIEW already exists */
    )
    {
      Table p;
      int n;
      string z;//string z;
      Token sEnd;
      DbFixer sFix = new DbFixer();
      Token pName = null;
      int iDb;
      sqlite3 db = pParse.db;

      if ( pParse.nVar > 0 )
      {
        sqlite3ErrorMsg( pParse, "parameters are not allowed in views" );
        sqlite3SelectDelete( db, ref pSelect );
        return;
      }
      sqlite3StartTable( pParse, pName1, pName2, isTemp, 1, 0, noErr );
      p = pParse.pNewTable;
      if ( p == null || pParse.nErr != 0 )
      {
        sqlite3SelectDelete( db, ref pSelect );
        return;
      }
      sqlite3TwoPartName( pParse, pName1, pName2, ref pName );
      iDb = sqlite3SchemaToIndex( db, p.pSchema );
      if ( sqlite3FixInit( sFix, pParse, iDb, "view", pName ) != 0
      && sqlite3FixSelect( sFix, pSelect ) != 0
      )
      {
        sqlite3SelectDelete( db, ref pSelect );
        return;
      }

      /* Make a copy of the entire SELECT statement that defines the view.
      ** This will force all the Expr.token.z values to be dynamically
      ** allocated rather than point to the input string - which means that
      ** they will persist after the current sqlite3_exec() call returns.
      */
      p.pSelect = sqlite3SelectDup( db, pSelect, EXPRDUP_REDUCE );
      sqlite3SelectDelete( db, ref pSelect );
      //if ( db.mallocFailed != 0 )
      //{
      //  return;
      //}
      if ( 0 == db.init.busy )
      {
        sqlite3ViewGetColumnNames( pParse, p );
      }

      /* Locate the end of the CREATE VIEW statement.  Make sEnd point to
      ** the end.
      */
      sEnd = pParse.sLastToken;
      if ( ALWAYS( sEnd.z[0] != 0 ) && sEnd.z[0] != ';' )
      {
        sEnd.z = sEnd.z.Substring( sEnd.n );
      }
      sEnd.n = 0;
      n = (int)( pBegin.z.Length - sEnd.z.Length );//sEnd.z - pBegin.z;
      z = pBegin.z;
      while ( ALWAYS( n > 0 ) && sqlite3Isspace( z[n - 1] ) )
      {
        n--;
      }
      sEnd.z = z.Substring( n - 1 );
      sEnd.n = 1;

      /* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
      sqlite3EndTable( pParse, null, sEnd, null );
      return;
    }
#else
    static void sqlite3CreateView(
    Parse pParse,     /* The parsing context */
    Token pBegin,     /* The CREATE token that begins the statement */
    Token pName1,     /* The token that holds the name of the view */
    Token pName2,     /* The token that holds the name of the view */
    Select pSelect,   /* A SELECT statement that will become the new view */
    int isTemp,      /* TRUE for a TEMPORARY view */
    int noErr         /* Suppress error messages if VIEW already exists */
    )
    {
    }
#endif // * SQLITE_OMIT_VIEW */

#if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_VIRTUALTABLE
    /*
** The Table structure pTable is really a VIEW.  Fill in the names of
** the columns of the view in the pTable structure.  Return the number
** of errors.  If an error is seen leave an error message in pParse.zErrMsg.
*/
    static int sqlite3ViewGetColumnNames( Parse pParse, Table pTable )
    {
      Table pSelTab;    /* A fake table from which we get the result set */
      Select pSel;      /* Copy of the SELECT that implements the view */
      int nErr = 0;     /* Number of errors encountered */
      int n;            /* Temporarily holds the number of cursors assigned */
      sqlite3 db = pParse.db;  /* Database connection for malloc errors */
      dxAuth xAuth;     //)(void*,int,const char*,const char*,const char*,const char);

      Debug.Assert( pTable != null );

#if !SQLITE_OMIT_VIRTUALTABLE
if ( sqlite3VtabCallConnect( pParse, pTable )!=0 )
{
return SQLITE_ERROR;
}
#endif
      if ( IsVirtual( pTable ) )
        return 0;

#if !SQLITE_OMIT_VIEW
      /* A positive nCol means the columns names for this view are
** already known.
*/
      if ( pTable.nCol > 0 )
        return 0;

      /* A negative nCol is a special marker meaning that we are currently
      ** trying to compute the column names.  If we enter this routine with
      ** a negative nCol, it means two or more views form a loop, like this:
      **
      **     CREATE VIEW one AS SELECT * FROM two;
      **     CREATE VIEW two AS SELECT * FROM one;
      **
      ** Actually, the error above is now caught prior to reaching this point.
      ** But the following test is still important as it does come up
      ** in the following:
      **
      **     CREATE TABLE main.ex1(a);
      **     CREATE TEMP VIEW ex1 AS SELECT a FROM ex1;
      **     SELECT * FROM temp.ex1;
      */
      if ( pTable.nCol < 0 )
      {
        sqlite3ErrorMsg( pParse, "view %s is circularly defined", pTable.zName );
        return 1;
      }
      Debug.Assert( pTable.nCol >= 0 );

      /* If we get this far, it means we need to compute the table names.
      ** Note that the call to sqlite3ResultSetOfSelect() will expand any
      ** "*" elements in the results set of the view and will assign cursors
      ** to the elements of the FROM clause.  But we do not want these changes
      ** to be permanent.  So the computation is done on a copy of the SELECT
      ** statement that defines the view.
      */
      Debug.Assert( pTable.pSelect != null );
      pSel = sqlite3SelectDup( db, pTable.pSelect, 0 );
      if ( pSel != null )
      {
        u8 enableLookaside = db.lookaside.bEnabled;
        n = pParse.nTab;
        sqlite3SrcListAssignCursors( pParse, pSel.pSrc );
        pTable.nCol = -1;
        db.lookaside.bEnabled = 0;
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
xAuth = db.xAuth;
db.xAuth = 0;
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
db.xAuth = xAuth;
#else
        pSelTab = sqlite3ResultSetOfSelect( pParse, pSel );
#endif
        db.lookaside.bEnabled = enableLookaside;
        pParse.nTab = n;
        if ( pSelTab != null )
        {
          Debug.Assert( pTable.aCol == null );
          pTable.nCol = pSelTab.nCol;
          pTable.aCol = pSelTab.aCol;
          pSelTab.nCol = 0;
          pSelTab.aCol = null;
          sqlite3DeleteTable( db, ref pSelTab );
          Debug.Assert( sqlite3SchemaMutexHeld( db, 0, pTable.pSchema ) );
          pTable.pSchema.flags |= DB_UnresetViews;
        }
        else
        {
          pTable.nCol = 0;
          nErr++;
        }
        sqlite3SelectDelete( db, ref pSel );
      }
      else
      {
        nErr++;
      }
#endif // * SQLITE_OMIT_VIEW */
      return nErr;
    }
#endif // * !SQLITE_OMIT_VIEW) || !SQLITE_OMIT_VIRTUALTABLE) */

#if !SQLITE_OMIT_VIEW
    /*
** Clear the column names from every VIEW in database idx.
*/
    static void sqliteViewResetAll( sqlite3 db, int idx )
    {
      HashElem i;
      Debug.Assert( sqlite3SchemaMutexHeld( db, idx, null ) );
      if ( !DbHasProperty( db, idx, DB_UnresetViews ) )
        return;
      //for(i=sqliteHashFirst(&db.aDb[idx].pSchema.tblHash); i;i=sqliteHashNext(i)){
      for ( i = db.aDb[idx].pSchema.tblHash.first; i != null; i = i.next )
      {
        Table pTab = (Table)i.data;// sqliteHashData( i );
        if ( pTab.pSelect != null )
        {
          sqliteDeleteColumnNames( db, pTab );
          pTab.aCol = null;
          pTab.nCol = 0;

        }
      }
      DbClearProperty( db, idx, DB_UnresetViews );
    }
#else
    //# define sqliteViewResetAll(A,B)
    static void sqliteViewResetAll( sqlite3 A, int B )
    {
    }
#endif // * SQLITE_OMIT_VIEW */

    /*
** This function is called by the VDBE to adjust the internal schema
** used by SQLite when the btree layer moves a table root page. The
** root-page of a table or index in database iDb has changed from iFrom
** to iTo.
**
** Ticket #1728:  The symbol table might still contain information
** on tables and/or indices that are the process of being deleted.
** If you are unlucky, one of those deleted indices or tables might
** have the same rootpage number as the real table or index that is
** being moved.  So we cannot stop searching after the first match
** because the first match might be for one of the deleted indices
** or tables and not the table/index that is actually being moved.
** We must continue looping until all tables and indices with
** rootpage==iFrom have been converted to have a rootpage of iTo
** in order to be certain that we got the right one.
*/
#if !SQLITE_OMIT_AUTOVACUUM
    static void sqlite3RootPageMoved( sqlite3 db, int iDb, int iFrom, int iTo )
    {
      HashElem pElem;
      Hash pHash;
      Db pDb;

      Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
      pDb = db.aDb[iDb];

      pHash = pDb.pSchema.tblHash;
      for ( pElem = pHash.first; pElem != null; pElem = pElem.next )// ( pElem = sqliteHashFirst( pHash ) ; pElem ; pElem = sqliteHashNext( pElem ) )
      {
        Table pTab = (Table)pElem.data;// sqliteHashData( pElem );
        if ( pTab.tnum == iFrom )
        {
          pTab.tnum = iTo;
        }
      }
      pHash = pDb.pSchema.idxHash;
      for ( pElem = pHash.first; pElem != null; pElem = pElem.next )// ( pElem = sqliteHashFirst( pHash ) ; pElem ; pElem = sqliteHashNext( pElem ) )
      {
        Index pIdx = (Index)pElem.data;// sqliteHashData( pElem );
        if ( pIdx.tnum == iFrom )
        {
          pIdx.tnum = iTo;
        }
      }
    }
#endif

    /*
** Write code to erase the table with root-page iTable from database iDb.
** Also write code to modify the sqlite_master table and internal schema
** if a root-page of another table is moved by the btree-layer whilst
** erasing iTable (this can happen with an auto-vacuum database).
*/
    static void destroyRootPage( Parse pParse, int iTable, int iDb )
    {
      Vdbe v = sqlite3GetVdbe( pParse );
      int r1 = sqlite3GetTempReg( pParse );
      sqlite3VdbeAddOp3( v, OP_Destroy, iTable, r1, iDb );
      sqlite3MayAbort( pParse );
#if !SQLITE_OMIT_AUTOVACUUM
      /* OP_Destroy stores an in integer r1. If this integer
** is non-zero, then it is the root page number of a table moved to
** location iTable. The following code modifies the sqlite_master table to
** reflect this.
**
** The "#NNN" in the SQL is a special constant that means whatever value
** is in register NNN.  See grammar rules associated with the TK_REGISTER
** token for additional information.
*/
      sqlite3NestedParse( pParse,
      "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
      pParse.db.aDb[iDb].zName, SCHEMA_TABLE( iDb ), iTable, r1, r1 );
#endif
      sqlite3ReleaseTempReg( pParse, r1 );
    }

    /*
    ** Write VDBE code to erase table pTab and all associated indices on disk.
    ** Code to update the sqlite_master tables and internal schema definitions
    ** in case a root-page belonging to another table is moved by the btree layer
    ** is also added (this can happen with an auto-vacuum database).
    */
    static void destroyTable( Parse pParse, Table pTab )
    {
#if  SQLITE_OMIT_AUTOVACUUM
Index pIdx;
int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
destroyRootPage( pParse, pTab.tnum, iDb );
for ( pIdx = pTab.pIndex ; pIdx != null ; pIdx = pIdx.pNext )
{
destroyRootPage( pParse, pIdx.tnum, iDb );
}
#else
      /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM
** is not defined), then it is important to call OP_Destroy on the
** table and index root-pages in order, starting with the numerically
** largest root-page number. This guarantees that none of the root-pages
** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the
** following were coded:
**
** OP_Destroy 4 0
** ...
** OP_Destroy 5 0
**
** and root page 5 happened to be the largest root-page number in the
** database, then root page 5 would be moved to page 4 by the
** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit
** a free-list page.
*/
      int iTab = pTab.tnum;
      int iDestroyed = 0;

      while ( true )
      {
        Index pIdx;
        int iLargest = 0;

        if ( iDestroyed == 0 || iTab < iDestroyed )
        {
          iLargest = iTab;
        }
        for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
        {
          int iIdx = pIdx.tnum;
          Debug.Assert( pIdx.pSchema == pTab.pSchema );
          if ( ( iDestroyed == 0 || ( iIdx < iDestroyed ) ) && iIdx > iLargest )
          {
            iLargest = iIdx;
          }
        }
        if ( iLargest == 0 )
        {
          return;
        }
        else
        {
          int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
          destroyRootPage( pParse, iLargest, iDb );
          iDestroyed = iLargest;
        }
      }
#endif
    }

    /*
    ** This routine is called to do the work of a DROP TABLE statement.
    ** pName is the name of the table to be dropped.
    */
    static void sqlite3DropTable( Parse pParse, SrcList pName, int isView, int noErr )
    {
      Table pTab;
      Vdbe v;
      sqlite3 db = pParse.db;
      int iDb;

      //if ( db.mallocFailed != 0 )
      //{
      //  goto exit_drop_table;
      //}
      Debug.Assert( pParse.nErr == 0 );
      Debug.Assert( pName.nSrc == 1 );
      if ( noErr != 0 )
        db.suppressErr++;
      pTab = sqlite3LocateTable( pParse, isView,
      pName.a[0].zName, pName.a[0].zDatabase );
      if ( noErr != 0 )
        db.suppressErr--;

      if ( pTab == null )
      {
        if ( noErr != 0)
          sqlite3CodeVerifyNamedSchema( pParse, pName.a[0].zDatabase );
        goto exit_drop_table;
      }
      iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
      Debug.Assert( iDb >= 0 && iDb < db.nDb );

      /* If pTab is a virtual table, call ViewGetColumnNames() to ensure
      ** it is initialized.
      */
      if ( IsVirtual( pTab ) && sqlite3ViewGetColumnNames( pParse, pTab ) != 0 )
      {
        goto exit_drop_table;
      }
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
{
int code;
string zTab = SCHEMA_TABLE(iDb);
string zDb = db.aDb[iDb].zName;
string zArg2 = 0;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
}
if( isView ){
if( OMIT_TEMPDB ==0&& iDb==1 ){
code = SQLITE_DROP_TEMP_VIEW;
}else{
code = SQLITE_DROP_VIEW;
}
}else if( IsVirtual(pTab) ){
code = SQLITE_DROP_VTABLE;
zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName;
}else{
if( OMIT_TEMPDB ==0&& iDb==1 ){
code = SQLITE_DROP_TEMP_TABLE;
}else{
code = SQLITE_DROP_TABLE;
}
}
if( sqlite3AuthCheck(pParse, code, pTab.zName, zArg2, zDb) ){
goto exit_drop_table;
}
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab.zName, 0, zDb) ){
goto exit_drop_table;
}
}
#endif
      if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) )
      {
        sqlite3ErrorMsg( pParse, "table %s may not be dropped", pTab.zName );
        goto exit_drop_table;
      }

#if !SQLITE_OMIT_VIEW
      /* Ensure DROP TABLE is not used on a view, and DROP VIEW is not used
** on a table.
*/
      if ( isView != 0 && pTab.pSelect == null )
      {
        sqlite3ErrorMsg( pParse, "use DROP TABLE to delete table %s", pTab.zName );
        goto exit_drop_table;
      }
      if ( 0 == isView && pTab.pSelect != null )
      {
        sqlite3ErrorMsg( pParse, "use DROP VIEW to delete view %s", pTab.zName );
        goto exit_drop_table;
      }
#endif

      /* Generate code to remove the table from the master table
** on disk.
*/
      v = sqlite3GetVdbe( pParse );
      if ( v != null )
      {
        Trigger pTrigger;
        Db pDb = db.aDb[iDb];
        sqlite3BeginWriteOperation( pParse, 1, iDb );

#if !SQLITE_OMIT_VIRTUALTABLE
        if ( IsVirtual( pTab ) )
        {
          sqlite3VdbeAddOp0( v, OP_VBegin );
        }
#endif
        sqlite3FkDropTable( pParse, pName, pTab );

        /* Drop all triggers associated with the table being dropped. Code
        ** is generated to remove entries from sqlite_master and/or
        ** sqlite_temp_master if required.
        */
        pTrigger = sqlite3TriggerList( pParse, pTab );
        while ( pTrigger != null )
        {
          Debug.Assert( pTrigger.pSchema == pTab.pSchema ||
          pTrigger.pSchema == db.aDb[1].pSchema );
          sqlite3DropTriggerPtr( pParse, pTrigger );
          pTrigger = pTrigger.pNext;
        }

#if !SQLITE_OMIT_AUTOINCREMENT
        /* Remove any entries of the sqlite_sequence table associated with
** the table being dropped. This is done before the table is dropped
** at the btree level, in case the sqlite_sequence table needs to
** move as a result of the drop (can happen in auto-vacuum mode).
*/
        if ( ( pTab.tabFlags & TF_Autoincrement ) != 0 )
        {
          sqlite3NestedParse( pParse,
          "DELETE FROM %s.sqlite_sequence WHERE name=%Q",
          pDb.zName, pTab.zName
          );
        }
#endif

        /* Drop all SQLITE_MASTER table and index entries that refer to the
** table. The program name loops through the master table and deletes
** every row that refers to a table of the same name as the one being
** dropped. Triggers are handled seperately because a trigger can be
** created in the temp database that refers to a table in another
** database.
*/
        sqlite3NestedParse( pParse,
        "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
        pDb.zName, SCHEMA_TABLE( iDb ), pTab.zName );

        /* Drop any statistics from the sqlite_stat1 table, if it exists */
        if ( sqlite3FindTable( db, "sqlite_stat1", db.aDb[iDb].zName ) != null )
        {
          sqlite3NestedParse( pParse,
          "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", pDb.zName, pTab.zName
          );
        }

        if ( 0 == isView && !IsVirtual( pTab ) )
        {
          destroyTable( pParse, pTab );
        }

        /* Remove the table entry from SQLite's internal schema and modify
        ** the schema cookie.
        */
        if ( IsVirtual( pTab ) )
        {
          sqlite3VdbeAddOp4( v, OP_VDestroy, iDb, 0, 0, pTab.zName, 0 );
        }
        sqlite3VdbeAddOp4( v, OP_DropTable, iDb, 0, 0, pTab.zName, 0 );
        sqlite3ChangeCookie( pParse, iDb );
      }
      sqliteViewResetAll( db, iDb );

exit_drop_table:
      sqlite3SrcListDelete( db, ref pName );
    }

    /*
    ** This routine is called to create a new foreign key on the table
    ** currently under construction.  pFromCol determines which columns
    ** in the current table point to the foreign key.  If pFromCol==0 then
    ** connect the key to the last column inserted.  pTo is the name of
    ** the table referred to.  pToCol is a list of tables in the other
    ** pTo table that the foreign key points to.  flags contains all
    ** information about the conflict resolution algorithms specified
    ** in the ON DELETE, ON UPDATE and ON INSERT clauses.
    **
    ** An FKey structure is created and added to the table currently
    ** under construction in the pParse.pNewTable field.
    **
    ** The foreign key is set for IMMEDIATE processing.  A subsequent call
    ** to sqlite3DeferForeignKey() might change this to DEFERRED.
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static void sqlite3CreateForeignKey( Parse pParse, int null_2, Token pTo, ExprList pToCol, int flags )
    {
      sqlite3CreateForeignKey( pParse, null, pTo, pToCol, flags );
    }
    static void sqlite3CreateForeignKey(
    Parse pParse,       /* Parsing context */
    ExprList pFromCol,  /* Columns in this table that point to other table */
    Token pTo,          /* Name of the other table */
    ExprList pToCol,    /* Columns in the other table */
    int flags           /* Conflict resolution algorithms. */
    )
    {
      sqlite3 db = pParse.db;
#if !SQLITE_OMIT_FOREIGN_KEY
      FKey pFKey = null;
      FKey pNextTo;
      Table p = pParse.pNewTable;
      int nByte;
      int i;
      int nCol;
      //string z;

      Debug.Assert( pTo != null );
      if ( p == null || IN_DECLARE_VTAB( pParse ) )
        goto fk_end;
      if ( pFromCol == null )
      {
        int iCol = p.nCol - 1;
        if ( NEVER( iCol < 0 ) )
          goto fk_end;
        if ( pToCol != null && pToCol.nExpr != 1 )
        {
          sqlite3ErrorMsg( pParse, "foreign key on %s" +
          " should reference only one column of table %T",
          p.aCol[iCol].zName, pTo );
          goto fk_end;
        }
        nCol = 1;
      }
      else if ( pToCol != null && pToCol.nExpr != pFromCol.nExpr )
      {
        sqlite3ErrorMsg( pParse,
        "number of columns in foreign key does not match the number of " +
        "columns in the referenced table" );
        goto fk_end;
      }
      else
      {
        nCol = pFromCol.nExpr;
      }
      //nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey.aCol[0]) + pTo.n + 1;
      //if( pToCol ){
      //  for(i=0; i<pToCol.nExpr; i++){
      //    nByte += sqlite3Strlen30(pToCol->a[i].zName) + 1;
      //  }
      //}
      pFKey = new FKey();//sqlite3DbMallocZero(db, nByte );
      if ( pFKey == null )
      {
        goto fk_end;
      }
      pFKey.pFrom = p;
      pFKey.pNextFrom = p.pFKey;
      //z = pFKey.aCol[nCol].zCol;
      pFKey.aCol = new FKey.sColMap[nCol];// z;
      pFKey.aCol[0] = new FKey.sColMap();
      pFKey.zTo = pTo.z.Substring( 0, pTo.n );      //memcpy( z, pTo.z, pTo.n );
      //z[pTo.n] = 0;
      sqlite3Dequote( ref pFKey.zTo );
      //z += pTo.n + 1;
      pFKey.nCol = nCol;
      if ( pFromCol == null )
      {
        pFKey.aCol[0].iFrom = p.nCol - 1;
      }
      else
      {
        for ( i = 0; i < nCol; i++ )
        {
          if ( pFKey.aCol[i] == null )
            pFKey.aCol[i] = new FKey.sColMap();
          int j;
          for ( j = 0; j < p.nCol; j++ )
          {
            if ( p.aCol[j].zName.Equals( pFromCol.a[i].zName, StringComparison.InvariantCultureIgnoreCase ) )
            {
              pFKey.aCol[i].iFrom = j;
              break;
            }
          }
          if ( j >= p.nCol )
          {
            sqlite3ErrorMsg( pParse,
            "unknown column \"%s\" in foreign key definition",
            pFromCol.a[i].zName );
            goto fk_end;
          }
        }
      }
      if ( pToCol != null )
      {
        for ( i = 0; i < nCol; i++ )
        {
          int n = sqlite3Strlen30( pToCol.a[i].zName );
          if ( pFKey.aCol[i] == null )
            pFKey.aCol[i] = new FKey.sColMap();
          pFKey.aCol[i].zCol = pToCol.a[i].zName;
          //memcpy( z, pToCol.a[i].zName, n );
          //z[n] = 0;
          //z += n + 1;
        }
      }
      pFKey.isDeferred = 0;
      pFKey.aAction[0] = (u8)( flags & 0xff );            /* ON DELETE action */
      pFKey.aAction[1] = (u8)( ( flags >> 8 ) & 0xff );    /* ON UPDATE action */

      Debug.Assert( sqlite3SchemaMutexHeld( db, 0, p.pSchema ) );
      pNextTo = sqlite3HashInsert( ref p.pSchema.fkeyHash,
          pFKey.zTo, sqlite3Strlen30( pFKey.zTo ), pFKey
      );
      //if( pNextTo==pFKey ){
      //  db.mallocFailed = 1;
      //  goto fk_end;
      //}
      if ( pNextTo != null )
      {
        Debug.Assert( pNextTo.pPrevTo == null );
        pFKey.pNextTo = pNextTo;
        pNextTo.pPrevTo = pFKey;
      }
      /* Link the foreign key to the table as the last step.
      */
      p.pFKey = pFKey;
      pFKey = null;

fk_end:
      sqlite3DbFree( db, ref pFKey );
#endif // * !SQLITE_OMIT_FOREIGN_KEY) */
      sqlite3ExprListDelete( db, ref pFromCol );
      sqlite3ExprListDelete( db, ref pToCol );
    }

    /*
    ** This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED
    ** clause is seen as part of a foreign key definition.  The isDeferred
    ** parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE.
    ** The behavior of the most recently created foreign key is adjusted
    ** accordingly.
    */
    static void sqlite3DeferForeignKey( Parse pParse, int isDeferred )
    {
#if !SQLITE_OMIT_FOREIGN_KEY
      Table pTab;
      FKey pFKey;
      if ( ( pTab = pParse.pNewTable ) == null || ( pFKey = pTab.pFKey ) == null )
        return;
      Debug.Assert( isDeferred == 0 || isDeferred == 1 ); /* EV: R-30323-21917 */
      pFKey.isDeferred = (u8)isDeferred;
#endif
    }

    /*
    ** Generate code that will erase and refill index pIdx.  This is
    ** used to initialize a newly created index or to recompute the
    ** content of an index in response to a REINDEX command.
    **
    ** if memRootPage is not negative, it means that the index is newly
    ** created.  The register specified by memRootPage contains the
    ** root page number of the index.  If memRootPage is negative, then
    ** the index already exists and must be cleared before being refilled and
    ** the root page number of the index is taken from pIndex.tnum.
    */
    static void sqlite3RefillIndex( Parse pParse, Index pIndex, int memRootPage )
    {
      Table pTab = pIndex.pTable;   /* The table that is indexed */
      int iTab = pParse.nTab++;     /* Btree cursor used for pTab */
      int iIdx = pParse.nTab++;     /* Btree cursor used for pIndex */
      int addr1;                    /* Address of top of loop */
      int tnum;                     /* Root page of index */
      Vdbe v;                       /* Generate code into this virtual machine */
      KeyInfo pKey;                 /* KeyInfo for index */
      int regIdxKey;                /* Registers containing the index key */
      int regRecord;                /* Register holding assemblied index record */
      sqlite3 db = pParse.db;       /* The database connection */
      int iDb = sqlite3SchemaToIndex( db, pIndex.pSchema );

#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex.zName, 0,
db.aDb[iDb].zName ) ){
return;
}
#endif

      /* Require a write-lock on the table to perform this operation */
      sqlite3TableLock( pParse, iDb, pTab.tnum, 1, pTab.zName );
      v = sqlite3GetVdbe( pParse );
      if ( v == null )
        return;
      if ( memRootPage >= 0 )
      {
        tnum = memRootPage;
      }
      else
      {
        tnum = pIndex.tnum;
        sqlite3VdbeAddOp2( v, OP_Clear, tnum, iDb );
      }
      pKey = sqlite3IndexKeyinfo( pParse, pIndex );
      sqlite3VdbeAddOp4( v, OP_OpenWrite, iIdx, tnum, iDb,
      pKey, P4_KEYINFO_HANDOFF );
      if ( memRootPage >= 0 )
      {
        sqlite3VdbeChangeP5( v, 1 );
      }
      sqlite3OpenTable( pParse, iTab, iDb, pTab, OP_OpenRead );
      addr1 = sqlite3VdbeAddOp2( v, OP_Rewind, iTab, 0 );
      regRecord = sqlite3GetTempReg( pParse );
      regIdxKey = sqlite3GenerateIndexKey( pParse, pIndex, iTab, regRecord, true );
      if ( pIndex.onError != OE_None )
      {
        int regRowid = regIdxKey + pIndex.nColumn;
        int j2 = sqlite3VdbeCurrentAddr( v ) + 2;
        int pRegKey = regIdxKey;// SQLITE_INT_TO_PTR( regIdxKey );

        /* The registers accessed by the OP_IsUnique opcode were allocated
        ** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey()
        ** call above. Just before that function was freed they were released
        ** (made available to the compiler for reuse) using
        ** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique
        ** opcode use the values stored within seems dangerous. However, since
        ** we can be sure that no other temp registers have been allocated
        ** since sqlite3ReleaseTempRange() was called, it is safe to do so.
        */
        sqlite3VdbeAddOp4( v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32 );
        sqlite3HaltConstraint(
            pParse, OE_Abort, "indexed columns are not unique", P4_STATIC );
      }
      sqlite3VdbeAddOp2( v, OP_IdxInsert, iIdx, regRecord );
      sqlite3VdbeChangeP5( v, OPFLAG_USESEEKRESULT );
      sqlite3ReleaseTempReg( pParse, regRecord );
      sqlite3VdbeAddOp2( v, OP_Next, iTab, addr1 + 1 );
      sqlite3VdbeJumpHere( v, addr1 );
      sqlite3VdbeAddOp1( v, OP_Close, iTab );
      sqlite3VdbeAddOp1( v, OP_Close, iIdx );
    }

    /*
    ** Create a new index for an SQL table.  pName1.pName2 is the name of the index
    ** and pTblList is the name of the table that is to be indexed.  Both will
    ** be NULL for a primary key or an index that is created to satisfy a
    ** UNIQUE constraint.  If pTable and pIndex are NULL, use pParse.pNewTable
    ** as the table to be indexed.  pParse.pNewTable is a table that is
    ** currently being constructed by a CREATE TABLE statement.
    **
    ** pList is a list of columns to be indexed.  pList will be NULL if this
    ** is a primary key or unique-constraint on the most recent column added
    ** to the table currently under construction.
    **
    ** If the index is created successfully, return a pointer to the new Index
    ** structure. This is used by sqlite3AddPrimaryKey() to mark the index
    ** as the tables primary key (Index.autoIndex==2).
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static Index sqlite3CreateIndex( Parse pParse, int null_2, int null_3, int null_4, int null_5, int onError, int null_7, int null_8, int sortOrder, int ifNotExist )
    {
      return sqlite3CreateIndex( pParse, null, null, null, null, onError, null, null, sortOrder, ifNotExist );
    }
    static Index sqlite3CreateIndex( Parse pParse, int null_2, int null_3, int null_4, ExprList pList, int onError, int null_7, int null_8, int sortOrder, int ifNotExist )
    {
      return sqlite3CreateIndex( pParse, null, null, null, pList, onError, null, null, sortOrder, ifNotExist );
    }
    static Index sqlite3CreateIndex(
    Parse pParse,     /* All information about this Parse */
    Token pName1,     /* First part of index name. May be NULL */
    Token pName2,     /* Second part of index name. May be NULL */
    SrcList pTblName, /* Table to index. Use pParse.pNewTable if 0 */
    ExprList pList,   /* A list of columns to be indexed */
    int onError,      /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
    Token pStart,     /* The CREATE token that begins this statement */
    Token pEnd,       /* The ")" that closes the CREATE INDEX statement */
    int sortOrder,    /* Sort order of primary key when pList==NULL */
    int ifNotExist    /* Omit error if index already exists */
    )
    {
      Index pRet = null;            /* Pointer to return */
      Table pTab = null;            /* Table to be indexed */
      Index pIndex = null;          /* The index to be created */
      string zName = null;          /* Name of the index */
      int nName;                    /* Number of characters in zName */
      int i, j;
      Token nullId = new Token();   /* Fake token for an empty ID list */
      DbFixer sFix = new DbFixer(); /* For assigning database names to pTable */
      int sortOrderMask;            /* 1 to honor DESC in index.  0 to ignore. */
      sqlite3 db = pParse.db;
      Db pDb;                       /* The specific table containing the indexed database */
      int iDb;                      /* Index of the database that is being written */
      Token pName = null;           /* Unqualified name of the index to create */
      ExprList_item pListItem;      /* For looping over pList */
      int nCol;
      int nExtra = 0;
      StringBuilder zExtra = new StringBuilder();

      Debug.Assert( pStart == null || pEnd != null ); /* pEnd must be non-NULL if pStart is */
      Debug.Assert( pParse.nErr == 0 );      /* Never called with prior errors */
      if ( /* db.mallocFailed != 0  || */
       IN_DECLARE_VTAB( pParse ) )
      {
        goto exit_create_index;
      }
      if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
      {
        goto exit_create_index;
      }

      /*
      ** Find the table that is to be indexed.  Return early if not found.
      */
      if ( pTblName != null )
      {

        /* Use the two-part index name to determine the database
        ** to search for the table. 'Fix' the table name to this db
        ** before looking up the table.
        */
        Debug.Assert( pName1 != null && pName2 != null );
        iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName );
        if ( iDb < 0 )
          goto exit_create_index;

#if !SQLITE_OMIT_TEMPDB
        /* If the index name was unqualified, check if the the table
** is a temp table. If so, set the database to 1. Do not do this
** if initialising a database schema.
*/
        if ( 0 == db.init.busy )
        {
          pTab = sqlite3SrcListLookup( pParse, pTblName );
          if ( pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema )
          {
            iDb = 1;
          }
        }
#endif

        if ( sqlite3FixInit( sFix, pParse, iDb, "index", pName ) != 0 &&
        sqlite3FixSrcList( sFix, pTblName ) != 0
        )
        {
          /* Because the parser constructs pTblName from a single identifier,
          ** sqlite3FixSrcList can never fail. */
          Debugger.Break();
        }
        pTab = sqlite3LocateTable( pParse, 0, pTblName.a[0].zName,
        pTblName.a[0].zDatabase );
        if ( pTab == null /*|| db.mallocFailed != 0 */ )
          goto exit_create_index;
        Debug.Assert( db.aDb[iDb].pSchema == pTab.pSchema );
      }
      else
      {
        Debug.Assert( pName == null );
        pTab = pParse.pNewTable;
        if ( pTab == null )
          goto exit_create_index;
        iDb = sqlite3SchemaToIndex( db, pTab.pSchema );
      }
      pDb = db.aDb[iDb];

      Debug.Assert( pTab != null );
      Debug.Assert( pParse.nErr == 0 );

      if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase )
        && !pTab.zName.StartsWith( "sqlite_altertab_", System.StringComparison.InvariantCultureIgnoreCase ) )
      {
        sqlite3ErrorMsg( pParse, "table %s may not be indexed", pTab.zName );
        goto exit_create_index;
      }
#if !SQLITE_OMIT_VIEW
      if ( pTab.pSelect != null )
      {
        sqlite3ErrorMsg( pParse, "views may not be indexed" );
        goto exit_create_index;
      }
#endif
      if ( IsVirtual( pTab ) )
      {
        sqlite3ErrorMsg( pParse, "virtual tables may not be indexed" );
        goto exit_create_index;
      }

      /*
      ** Find the name of the index.  Make sure there is not already another
      ** index or table with the same name.
      **
      ** Exception:  If we are reading the names of permanent indices from the
      ** sqlite_master table (because some other process changed the schema) and
      ** one of the index names collides with the name of a temporary table or
      ** index, then we will continue to process this index.
      **
      ** If pName==0 it means that we are
      ** dealing with a primary key or UNIQUE constraint.  We have to invent our
      ** own name.
      */
      if ( pName != null )
      {
        zName = sqlite3NameFromToken( db, pName );
        if ( zName == null )
          goto exit_create_index;
        if ( SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) )
        {
          goto exit_create_index;
        }
        if ( 0 == db.init.busy )
        {
          if ( sqlite3FindTable( db, zName, null ) != null )
          {
            sqlite3ErrorMsg( pParse, "there is already a table named %s", zName );
            goto exit_create_index;
          }
        }
        if ( sqlite3FindIndex( db, zName, pDb.zName ) != null )
        {
          if ( ifNotExist == 0 )
          {
            sqlite3ErrorMsg( pParse, "index %s already exists", zName );
          }
          else
          {
            Debug.Assert( 0 == db.init.busy );
            sqlite3CodeVerifySchema( pParse, iDb );
          }
          goto exit_create_index;
        }
      }
      else
      {
        int n = 0;
        Index pLoop;
        for ( pLoop = pTab.pIndex, n = 1; pLoop != null; pLoop = pLoop.pNext, n++ )
        {
        }
        zName = sqlite3MPrintf( db, "sqlite_autoindex_%s_%d", pTab.zName, n );
        if ( zName == null )
        {
          goto exit_create_index;
        }
      }

      /* Check for authorization to create an index.
      */
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
{
string zDb = pDb.zName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
goto exit_create_index;
}
i = SQLITE_CREATE_INDEX;
if( OMIT_TEMPDB ==0&& iDb==1 ) i = SQLITE_CREATE_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, i, zName, pTab.zName, zDb) ){
goto exit_create_index;
}
}
#endif

      /* If pList==0, it means this routine was called to make a primary
** key out of the last column added to the table under construction.
** So create a fake list to simulate this.
*/
      if ( pList == null )
      {
        nullId.z = pTab.aCol[pTab.nCol - 1].zName;
        nullId.n = sqlite3Strlen30( nullId.z );
        pList = sqlite3ExprListAppend( pParse, null, null );
        if ( pList == null )
          goto exit_create_index;
        sqlite3ExprListSetName( pParse, pList, nullId, 0 );
        pList.a[0].sortOrder = (u8)sortOrder;
      }

      /* Figure out how many bytes of space are required to store explicitly
      ** specified collation sequence names.
      */
      for ( i = 0; i < pList.nExpr; i++ )
      {
        Expr pExpr = pList.a[i].pExpr;
        if ( pExpr != null )
        {
          CollSeq pColl = pExpr.pColl;
          /* Either pColl!=0 or there was an OOM failure.  But if an OOM
          ** failure we have quit before reaching this point. */
          if ( ALWAYS( pColl != null ) )
          {
            nExtra += ( 1 + sqlite3Strlen30( pColl.zName ) );
          }
        }
      }

      /*
      ** Allocate the index structure.
      */
      nName = sqlite3Strlen30( zName );
      nCol = pList.nExpr;
      pIndex = new Index();
      // sqlite3DbMallocZero( db,
      //    Index.Length +              /* Index structure  */
      //    sizeof( int ) * nCol +           /* Index.aiColumn   */
      //    sizeof( int ) * ( nCol + 1 ) +       /* Index.aiRowEst   */
      //    sizeof( char* ) * nCol +        /* Index.azColl     */
      //    u8.Length * nCol +            /* Index.aSortOrder */
      //    nName + 1 +                  /* Index.zName      */
      //    nExtra                       /* Collation sequence names */
      //);
      //if ( db.mallocFailed != 0 )
      //{
      //  goto exit_create_index;
      //}
      pIndex.azColl = new string[nCol + 1];//(char*)(pIndex[1]);
      pIndex.aiColumn = new int[nCol + 1];//(int )(pIndex->azColl[nCol]);
      pIndex.aiRowEst = new int[nCol + 1];//(unsigned )(pIndex->aiColumn[nCol]);
      pIndex.aSortOrder = new byte[nCol + 1];//(u8 )(pIndex->aiRowEst[nCol+1]);
      //pIndex.zName = null;// (char)( &pIndex->aSortOrder[nCol] );
      zExtra = new StringBuilder( nName + 1 );// (char)( &pIndex.zName[nName + 1] );
      if ( zName.Length == nName )
        pIndex.zName = zName;
      else
      {
        pIndex.zName = zName.Substring( 0, nName );
      }// memcpy( pIndex.zName, zName, nName + 1 );
      pIndex.pTable = pTab;
      pIndex.nColumn = pList.nExpr;
      pIndex.onError = (u8)onError;
      pIndex.autoIndex = (u8)( pName == null ? 1 : 0 );
      pIndex.pSchema = db.aDb[iDb].pSchema;
      Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
      
      /* Check to see if we should honor DESC requests on index columns
      */
      if ( pDb.pSchema.file_format >= 4 )
      {
        sortOrderMask = 1;   /* Honor DESC */
      }
      else
      {
        sortOrderMask = 0;    /* Ignore DESC */
      }

      /* Scan the names of the columns of the table to be indexed and
      ** load the column indices into the Index structure.  Report an error
      ** if any column is not found.
      **
      ** TODO:  Add a test to make sure that the same column is not named
      ** more than once within the same index.  Only the first instance of
      ** the column will ever be used by the optimizer.  Note that using the
      ** same column more than once cannot be an error because that would
      ** break backwards compatibility - it needs to be a warning.
      */
      for ( i = 0; i < pList.nExpr; i++ )
      {//, pListItem++){
        pListItem = pList.a[i];
        string zColName = pListItem.zName;
        Column pTabCol;
        byte requestedSortOrder;
        string zColl;                   /* Collation sequence name */

        for ( j = 0; j < pTab.nCol; j++ )
        {//, pTabCol++){
          pTabCol = pTab.aCol[j];
          if ( zColName.Equals( pTabCol.zName, StringComparison.InvariantCultureIgnoreCase ) )
            break;
        }
        if ( j >= pTab.nCol )
        {
          sqlite3ErrorMsg( pParse, "table %s has no column named %s",
          pTab.zName, zColName );
          pParse.checkSchema = 1;
          goto exit_create_index;
        }
        pIndex.aiColumn[i] = j;
        /* Justification of the ALWAYS(pListItem->pExpr->pColl):  Because of
        ** the way the "idxlist" non-terminal is constructed by the parser,
        ** if pListItem->pExpr is not null then either pListItem->pExpr->pColl
        ** must exist or else there must have been an OOM error.  But if there
        ** was an OOM error, we would never reach this point. */
        if ( pListItem.pExpr != null && ALWAYS( pListItem.pExpr.pColl ) )
        {
          int nColl;
          zColl = pListItem.pExpr.pColl.zName;
          nColl = sqlite3Strlen30( zColl );
          Debug.Assert( nExtra >= nColl );
          zExtra = new StringBuilder( zColl.Substring( 0, nColl ) );// memcpy( zExtra, zColl, nColl );
          zColl = zExtra.ToString();
          //zExtra += nColl;
          nExtra -= nColl;
        }
        else
        {
          zColl = pTab.aCol[j].zColl;
          if ( zColl == null )
          {
            zColl = db.pDfltColl.zName;
          }
        }
        if ( 0 == db.init.busy && sqlite3LocateCollSeq( pParse, zColl ) == null )
        {
          goto exit_create_index;
        }
        pIndex.azColl[i] = zColl;
        requestedSortOrder = (u8)( ( pListItem.sortOrder & sortOrderMask ) != 0 ? 1 : 0 );
        pIndex.aSortOrder[i] = (u8)requestedSortOrder;
      }
      sqlite3DefaultRowEst( pIndex );

      if ( pTab == pParse.pNewTable )
      {
        /* This routine has been called to create an automatic index as a
        ** result of a PRIMARY KEY or UNIQUE clause on a column definition, or
        ** a PRIMARY KEY or UNIQUE clause following the column definitions.
        ** i.e. one of:
        **
        ** CREATE TABLE t(x PRIMARY KEY, y);
        ** CREATE TABLE t(x, y, UNIQUE(x, y));
        **
        ** Either way, check to see if the table already has such an index. If
        ** so, don't bother creating this one. This only applies to
        ** automatically created indices. Users can do as they wish with
        ** explicit indices.
        **
        ** Two UNIQUE or PRIMARY KEY constraints are considered equivalent
        ** (and thus suppressing the second one) even if they have different
        ** sort orders.
        **
        ** If there are different collating sequences or if the columns of
        ** the constraint occur in different orders, then the constraints are
        ** considered distinct and both result in separate indices.
        */
        Index pIdx;
        for ( pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext )
        {
          int k;
          Debug.Assert( pIdx.onError != OE_None );
          Debug.Assert( pIdx.autoIndex != 0 );
          Debug.Assert( pIndex.onError != OE_None );

          if ( pIdx.nColumn != pIndex.nColumn )
            continue;
          for ( k = 0; k < pIdx.nColumn; k++ )
          {
            string z1;
            string z2;
            if ( pIdx.aiColumn[k] != pIndex.aiColumn[k] )
              break;
            z1 = pIdx.azColl[k];
            z2 = pIndex.azColl[k];
            if ( z1 != z2 && !z1.Equals( z2, StringComparison.InvariantCultureIgnoreCase ) )
              break;
          }
          if ( k == pIdx.nColumn )
          {
            if ( pIdx.onError != pIndex.onError )
            {
              /* This constraint creates the same index as a previous
              ** constraint specified somewhere in the CREATE TABLE statement.
              ** However the ON CONFLICT clauses are different. If both this
              ** constraint and the previous equivalent constraint have explicit
              ** ON CONFLICT clauses this is an error. Otherwise, use the
              ** explicitly specified behavior for the index.
              */
              if ( !( pIdx.onError == OE_Default || pIndex.onError == OE_Default ) )
              {
                sqlite3ErrorMsg( pParse,
                "conflicting ON CONFLICT clauses specified", 0 );
              }
              if ( pIdx.onError == OE_Default )
              {
                pIdx.onError = pIndex.onError;
              }
            }
            goto exit_create_index;
          }
        }
      }

      /* Link the new Index structure to its table and to the other
      ** in-memory database structures.
      */
      if ( db.init.busy != 0 )
      {
        Index p;
        Debug.Assert( sqlite3SchemaMutexHeld( db, 0, pIndex.pSchema ) );
        p = sqlite3HashInsert( ref pIndex.pSchema.idxHash,
        pIndex.zName, sqlite3Strlen30( pIndex.zName ),
        pIndex );
        if ( p != null )
        {
          Debug.Assert( p == pIndex );  /* Malloc must have failed */
          //        db.mallocFailed = 1;
          goto exit_create_index;
        }
        db.flags |= SQLITE_InternChanges;
        if ( pTblName != null )
        {
          pIndex.tnum = db.init.newTnum;
        }
      }

          /* If the db.init.busy is 0 then create the index on disk.  This
          ** involves writing the index into the master table and filling in the
          ** index with the current table contents.
          **
          ** The db.init.busy is 0 when the user first enters a CREATE INDEX
          ** command.  db.init.busy is 1 when a database is opened and
          ** CREATE INDEX statements are read out of the master table.  In
          ** the latter case the index already exists on disk, which is why
          ** we don't want to recreate it.
          **
          ** If pTblName==0 it means this index is generated as a primary key
          ** or UNIQUE constraint of 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 ( 0 == db.init.busy )
      {
        Vdbe v;
        string zStmt;
        int iMem = ++pParse.nMem;

        v = sqlite3GetVdbe( pParse );
        if ( v == null )
          goto exit_create_index;


        /* Create the rootpage for the index
        */
        sqlite3BeginWriteOperation( pParse, 1, iDb );
        sqlite3VdbeAddOp2( v, OP_CreateIndex, iDb, iMem );

        /* Gather the complete text of the CREATE INDEX statement into
        ** the zStmt variable
        */
        if ( pStart != null )
        {
          Debug.Assert( pEnd != null );
          /* A named index with an explicit CREATE INDEX statement */
          zStmt = sqlite3MPrintf( db, "CREATE%s INDEX %.*s",
          onError == OE_None ? "" : " UNIQUE",
           (int)(pName.z.Length - pEnd.z.Length) + 1,
          pName.z );
        }
        else
        {
          /* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
          /* zStmt = sqlite3MPrintf(""); */
          zStmt = null;
        }

        /* Add an entry in sqlite_master for this index
        */
        sqlite3NestedParse( pParse,
        "INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
        db.aDb[iDb].zName, SCHEMA_TABLE( iDb ),
        pIndex.zName,
        pTab.zName,
        iMem,
        zStmt
        );
        sqlite3DbFree( db, ref zStmt );

        /* Fill the index with data and reparse the schema. Code an OP_Expire
        ** to invalidate all pre-compiled statements.
        */
        if ( pTblName != null )
        {
          sqlite3RefillIndex( pParse, pIndex, iMem );
          sqlite3ChangeCookie( pParse, iDb );
          sqlite3VdbeAddParseSchemaOp( v, iDb,
            sqlite3MPrintf( db, "name='%q' AND type='index'", pIndex.zName ) );
          sqlite3VdbeAddOp1( v, OP_Expire, 0 );
        }
      }

      /* When adding an index to the list of indices for a table, make
      ** sure all indices labeled OE_Replace come after all those labeled
      ** OE_Ignore.  This is necessary for the correct constraint check
      ** processing (in sqlite3GenerateConstraintChecks()) as part of
      ** UPDATE and INSERT statements.
      */
      if ( db.init.busy != 0 || pTblName == null )
      {
        if ( onError != OE_Replace || pTab.pIndex == null
        || pTab.pIndex.onError == OE_Replace )
        {
          pIndex.pNext = pTab.pIndex;
          pTab.pIndex = pIndex;
        }
        else
        {
          Index pOther = pTab.pIndex;
          while ( pOther.pNext != null && pOther.pNext.onError != OE_Replace )
          {
            pOther = pOther.pNext;
          }
          pIndex.pNext = pOther.pNext;
          pOther.pNext = pIndex;
        }
        pRet = pIndex;
        pIndex = null;
      }

        /* Clean up before exiting */
exit_create_index:
      if ( pIndex != null )
      {
        //sqlite3DbFree(db, ref pIndex.zColAff );
        sqlite3DbFree( db, ref pIndex );
      }
      sqlite3ExprListDelete( db, ref pList );
      sqlite3SrcListDelete( db, ref pTblName );
      sqlite3DbFree( db, ref zName );
      return pRet;
    }

    /*
    ** Fill the Index.aiRowEst[] array with default information - information
    ** to be used when we have not run the ANALYZE command.
    **
    ** aiRowEst[0] is suppose to contain the number of elements in the index.
    ** Since we do not know, guess 1 million.  aiRowEst[1] is an estimate of the
    ** number of rows in the table that match any particular value of the
    ** first column of the index.  aiRowEst[2] is an estimate of the number
    ** of rows that match any particular combiniation of the first 2 columns
    ** of the index.  And so forth.  It must always be the case that
    *
    **           aiRowEst[N]<=aiRowEst[N-1]
    **           aiRowEst[N]>=1
    **
    ** Apart from that, we have little to go on besides intuition as to
    ** how aiRowEst[] should be initialized.  The numbers generated here
    ** are based on typical values found in actual indices.
    */
    static void sqlite3DefaultRowEst( Index pIdx )
    {
      int[] a = pIdx.aiRowEst;
      int i;
      int n;
      Debug.Assert( a != null );
      a[0] = (int)pIdx.pTable.nRowEst;
      if ( a[0] < 10 )
        a[0] = 10;
      n = 10;
      for ( i = 1; i <= pIdx.nColumn; i++ )
      {
        a[i] = n;
        if ( n > 5 )
          n--;
      }
      if ( pIdx.onError != OE_None )
      {
        a[pIdx.nColumn] = 1;
      }
    }

    /*
    ** This routine will drop an existing named index.  This routine
    ** implements the DROP INDEX statement.
    */
    static void sqlite3DropIndex( Parse pParse, SrcList pName, int ifExists )
    {
      Index pIndex;
      Vdbe v;
      sqlite3 db = pParse.db;
      int iDb;

      Debug.Assert( pParse.nErr == 0 );   /* Never called with prior errors */
      //if ( db.mallocFailed != 0 )
      //{
      //  goto exit_drop_index;
      //}
      Debug.Assert( pName.nSrc == 1 );
      if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
      {
        goto exit_drop_index;
      }
      pIndex = sqlite3FindIndex( db, pName.a[0].zName, pName.a[0].zDatabase );
      if ( pIndex == null )
      {
        if ( ifExists == 0 )
        {
          sqlite3ErrorMsg( pParse, "no such index: %S", pName, 0 );
        }
        else
        {
          sqlite3CodeVerifyNamedSchema( pParse, pName.a[0].zDatabase );
        }
        pParse.checkSchema = 1;
        goto exit_drop_index;
      }
      if ( pIndex.autoIndex != 0 )
      {
        sqlite3ErrorMsg( pParse, "index associated with UNIQUE " +
        "or PRIMARY KEY constraint cannot be dropped", 0 );
        goto exit_drop_index;
      }
      iDb = sqlite3SchemaToIndex( db, pIndex.pSchema );
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_INDEX;
Table pTab = pIndex.pTable;
string zDb = db.aDb[iDb].zName;
string zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
}
if( OMIT_TEMPDB ==0&& iDb ) code = SQLITE_DROP_TEMP_INDEX;
if( sqlite3AuthCheck(pParse, code, pIndex.zName, pTab.zName, zDb) ){
goto exit_drop_index;
}
}
#endif

      /* Generate code to remove the index and from the master table */
      v = sqlite3GetVdbe( pParse );
      if ( v != null )
      {
        sqlite3BeginWriteOperation( pParse, 1, iDb );
        sqlite3NestedParse( pParse,
        "DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
        db.aDb[iDb].zName, SCHEMA_TABLE( iDb ),
        pIndex.zName
        );
        if ( sqlite3FindTable( db, "sqlite_stat1", db.aDb[iDb].zName ) != null )
        {
          sqlite3NestedParse( pParse,
          "DELETE FROM %Q.sqlite_stat1 WHERE idx=%Q",
          db.aDb[iDb].zName, pIndex.zName
          );
        }
        sqlite3ChangeCookie( pParse, iDb );
        destroyRootPage( pParse, pIndex.tnum, iDb );
        sqlite3VdbeAddOp4( v, OP_DropIndex, iDb, 0, 0, pIndex.zName, 0 );
      }

exit_drop_index:
      sqlite3SrcListDelete( db, ref pName );
    }

    /*
    ** pArray is a pointer to an array of objects.  Each object in the
    ** array is szEntry bytes in size.  This routine allocates a new
    ** object on the end of the array.
    **
    ** pnEntry is the number of entries already in use.  pnAlloc is
    ** the previously allocated size of the array.  initSize is the
    ** suggested initial array size allocation.
    **
    ** The index of the new entry is returned in pIdx.
    **
    ** This routine returns a pointer to the array of objects.  This
    ** might be the same as the pArray parameter or it might be a different
    ** pointer if the array was resized.
    */
    static T[] sqlite3ArrayAllocate<T>(
    sqlite3 db,       /* Connection to notify of malloc failures */
    T[] pArray,    /* Array of objects.  Might be reallocated */
    int szEntry,      /* Size of each object in the array */
    int initSize,     /* Suggested initial allocation, in elements */
    ref int pnEntry,      /* Number of objects currently in use */
    ref int pnAlloc,      /* Current size of the allocation, in elements */
    ref int pIdx      /* Write the index of a new slot here */
    ) where T : new()
    {
      //char* z;
      if ( pnEntry >= pnAlloc )
      {
        //void* pNew;
        int newSize;
        newSize = ( pnAlloc ) * 2 + initSize;
        //pNew = sqlite3DbRealloc(db, pArray, newSize * szEntry);
        //if (pNew == 0)
        //{
        //  pIdx = -1;
        //  return pArray;
        //}
        pnAlloc = newSize; //sqlite3DbMallocSize(db, pNew)/szEntry;
        //pArray = pNew;
        Array.Resize( ref pArray, newSize );
      }
      pArray[pnEntry] = new T();
      //z = (char)pArray;
      //memset(z[*pnEntry * szEntry], 0, szEntry);
      pIdx = pnEntry;
      ++pnEntry;
      return pArray;
    }

    /*
    ** Append a new element to the given IdList.  Create a new IdList if
    ** need be.
    **
    ** A new IdList is returned, or NULL if malloc() fails.
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static IdList sqlite3IdListAppend( sqlite3 db, int null_2, Token pToken )
    {
      return sqlite3IdListAppend( db, null, pToken );
    }
    static IdList sqlite3IdListAppend( sqlite3 db, IdList pList, Token pToken )
    {
      int i = 0;
      if ( pList == null )
      {
        pList = new IdList();//sqlite3DbMallocZero(db, sizeof(IdList));
        if ( pList == null )
          return null;
        pList.nAlloc = 0;
      }
      pList.a = (IdList_item[])sqlite3ArrayAllocate(
      db,
      pList.a,
      -1,//sizeof(pList.a[0]),
      5,
      ref pList.nId,
      ref pList.nAlloc,
      ref i
      );
      if ( i < 0 )
      {
        sqlite3IdListDelete( db, ref pList );
        return null;
      }
      pList.a[i].zName = sqlite3NameFromToken( db, pToken );
      return pList;
    }

    /*
    ** Delete an IdList.
    */
    static void sqlite3IdListDelete( sqlite3 db, ref IdList pList )
    {
      int i;
      if ( pList == null )
        return;
      for ( i = 0; i < pList.nId; i++ )
      {
        sqlite3DbFree( db, ref pList.a[i].zName );
      }
      sqlite3DbFree( db, ref pList.a );
      sqlite3DbFree( db, ref pList );
    }

    /*
    ** Return the index in pList of the identifier named zId.  Return -1
    ** if not found.
    */
    static int sqlite3IdListIndex( IdList pList, string zName )
    {
      int i;
      if ( pList == null )
        return -1;
      for ( i = 0; i < pList.nId; i++ )
      {
        if ( pList.a[i].zName.Equals( zName, StringComparison.InvariantCultureIgnoreCase ) )
          return i;
      }
      return -1;
    }

    /*
    ** Expand the space allocated for the given SrcList object by
    ** creating nExtra new slots beginning at iStart.  iStart is zero based.
    ** New slots are zeroed.
    **
    ** For example, suppose a SrcList initially contains two entries: A,B.
    ** To append 3 new entries onto the end, do this:
    **
    **    sqlite3SrcListEnlarge(db, pSrclist, 3, 2);
    **
    ** After the call above it would contain:  A, B, nil, nil, nil.
    ** If the iStart argument had been 1 instead of 2, then the result
    ** would have been:  A, nil, nil, nil, B.  To prepend the new slots,
    ** the iStart value would be 0.  The result then would
    ** be: nil, nil, nil, A, B.
    **
    ** If a memory allocation fails the SrcList is unchanged.  The
    ** db.mallocFailed flag will be set to true.
    */
    static SrcList sqlite3SrcListEnlarge(
    sqlite3 db,       /* Database connection to notify of OOM errors */
    SrcList pSrc,     /* The SrcList to be enlarged */
    int nExtra,        /* Number of new slots to add to pSrc.a[] */
    int iStart         /* Index in pSrc.a[] of first new slot */
    )
    {
      int i;

      /* Sanity checking on calling parameters */
      Debug.Assert( iStart >= 0 );
      Debug.Assert( nExtra >= 1 );
      Debug.Assert( pSrc != null );
      Debug.Assert( iStart <= pSrc.nSrc );

      /* Allocate additional space if needed */
      if ( pSrc.nSrc + nExtra > pSrc.nAlloc )
      {
        int nAlloc = pSrc.nSrc + nExtra;
        int nGot;
        // sqlite3DbRealloc(db, pSrc,
        //     sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc.a[0]) );
        pSrc.nAlloc = (i16)nAlloc;
        Array.Resize( ref pSrc.a, nAlloc );
        //    nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
        //pSrc->nAlloc = (u16)nGot;
      }

      /* Move existing slots that come after the newly inserted slots
      ** out of the way */
      for ( i = pSrc.nSrc - 1; i >= iStart; i-- )
      {
        pSrc.a[i + nExtra] = pSrc.a[i];
      }
      pSrc.nSrc += (i16)nExtra;

      /* Zero the newly allocated slots */
      //memset(&pSrc.a[iStart], 0, sizeof(pSrc.a[0])*nExtra);
      for ( i = iStart; i < iStart + nExtra; i++ )
      {
        pSrc.a[i] = new SrcList_item();
        pSrc.a[i].iCursor = -1;
      }

      /* Return a pointer to the enlarged SrcList */
      return pSrc;
    }


    /*
    ** Append a new table name to the given SrcList.  Create a new SrcList if
    ** need be.  A new entry is created in the SrcList even if pTable is NULL.
    **
    ** A SrcList is returned, or NULL if there is an OOM error.  The returned
    ** SrcList might be the same as the SrcList that was input or it might be
    ** a new one.  If an OOM error does occurs, then the prior value of pList
    ** that is input to this routine is automatically freed.
    **
    ** If pDatabase is not null, it means that the table has an optional
    ** database name prefix.  Like this:  "database.table".  The pDatabase
    ** points to the table name and the pTable points to the database name.
    ** The SrcList.a[].zName field is filled with the table name which might
    ** come from pTable (if pDatabase is NULL) or from pDatabase.
    ** SrcList.a[].zDatabase is filled with the database name from pTable,
    ** or with NULL if no database is specified.
    **
    ** In other words, if call like this:
    **
    **         sqlite3SrcListAppend(D,A,B,0);
    **
    ** Then B is a table name and the database name is unspecified.  If called
    ** like this:
    **
    **         sqlite3SrcListAppend(D,A,B,C);
    **
    ** Then C is the table name and B is the database name.  If C is defined
    ** then so is B.  In other words, we never have a case where:
    **
    **         sqlite3SrcListAppend(D,A,0,C);
    **
    ** Both pTable and pDatabase are assumed to be quoted.  They are dequoted
    ** before being added to the SrcList.
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static SrcList sqlite3SrcListAppend( sqlite3 db, int null_2, Token pTable, int null_4 )
    {
      return sqlite3SrcListAppend( db, null, pTable, null );
    }
    static SrcList sqlite3SrcListAppend( sqlite3 db, int null_2, Token pTable, Token pDatabase )
    {
      return sqlite3SrcListAppend( db, null, pTable, pDatabase );
    }
    static SrcList sqlite3SrcListAppend(
    sqlite3 db,        /* Connection to notify of malloc failures */
    SrcList pList,     /* Append to this SrcList. NULL creates a new SrcList */
    Token pTable,      /* Table to append */
    Token pDatabase    /* Database of the table */
    )
    {
      SrcList_item pItem;
      Debug.Assert( pDatabase == null || pTable != null );  /* Cannot have C without B */
      if ( pList == null )
      {
        pList = new SrcList();//sqlite3DbMallocZero(db, SrcList.Length );
        //if ( pList == null ) return null;
        pList.nAlloc = 1;
        pList.a = new SrcList_item[1];
      }
      pList = sqlite3SrcListEnlarge( db, pList, 1, pList.nSrc );
      //if ( db.mallocFailed != 0 )
      //{
      //  sqlite3SrcListDelete( db, ref pList );
      //  return null;
      //}
      pItem = pList.a[pList.nSrc - 1];
      if ( pDatabase != null && String.IsNullOrEmpty( pDatabase.z ) )
      {
        pDatabase = null;
      }
      if ( pDatabase != null )
      {
        Token pTemp = pDatabase;
        pDatabase = pTable;
        pTable = pTemp;
      }
      pItem.zName = sqlite3NameFromToken( db, pTable );
      pItem.zDatabase = sqlite3NameFromToken( db, pDatabase );
      return pList;
    }

    /*
    ** Assign VdbeCursor index numbers to all tables in a SrcList
    */
    static void sqlite3SrcListAssignCursors( Parse pParse, SrcList pList )
    {
      int i;
      SrcList_item pItem;
      Debug.Assert( pList != null /* || pParse.db.mallocFailed != 0 */ );
      if ( pList != null )
      {
        for ( i = 0; i < pList.nSrc; i++ )
        {
          pItem = pList.a[i];
          if ( pItem.iCursor >= 0 )
            break;
          pItem.iCursor = pParse.nTab++;
          if ( pItem.pSelect != null )
          {
            sqlite3SrcListAssignCursors( pParse, pItem.pSelect.pSrc );
          }
        }
      }
    }

    /*
    ** Delete an entire SrcList including all its substructure.
    */
    static void sqlite3SrcListDelete( sqlite3 db, ref SrcList pList )
    {
      int i;
      SrcList_item pItem;
      if ( pList == null )
        return;
      for ( i = 0; i < pList.nSrc; i++ )
      {//, pItem++){
        pItem = pList.a[i];
        sqlite3DbFree( db, ref pItem.zDatabase );
        sqlite3DbFree( db, ref pItem.zName );
        sqlite3DbFree( db, ref pItem.zAlias );
        sqlite3DbFree( db, ref pItem.zIndex );
        sqlite3DeleteTable( db, ref pItem.pTab );
        sqlite3SelectDelete( db, ref pItem.pSelect );
        sqlite3ExprDelete( db, ref pItem.pOn );
        sqlite3IdListDelete( db, ref pItem.pUsing );
      }
      sqlite3DbFree( db, ref pList );
    }

    /*
    ** This routine is called by the parser to add a new term to the
    ** end of a growing FROM clause.  The "p" parameter is the part of
    ** the FROM clause that has already been constructed.  "p" is NULL
    ** if this is the first term of the FROM clause.  pTable and pDatabase
    ** are the name of the table and database named in the FROM clause term.
    ** pDatabase is NULL if the database name qualifier is missing - the
    ** usual case.  If the term has a alias, then pAlias points to the
    ** alias token.  If the term is a subquery, then pSubquery is the
    ** SELECT statement that the subquery encodes.  The pTable and
    ** pDatabase parameters are NULL for subqueries.  The pOn and pUsing
    ** parameters are the content of the ON and USING clauses.
    **
    ** Return a new SrcList which encodes is the FROM with the new
    ** term added.
    */
    // OVERLOADS, so I don't need to rewrite parse.c
    static SrcList sqlite3SrcListAppendFromTerm( Parse pParse, SrcList p, int null_3, int null_4, Token pAlias, Select pSubquery, Expr pOn, IdList pUsing )
    {
      return sqlite3SrcListAppendFromTerm( pParse, p, null, null, pAlias, pSubquery, pOn, pUsing );
    }
    static SrcList sqlite3SrcListAppendFromTerm( Parse pParse, SrcList p, Token pTable, Token pDatabase, Token pAlias, int null_6, Expr pOn, IdList pUsing )
    {
      return sqlite3SrcListAppendFromTerm( pParse, p, pTable, pDatabase, pAlias, null, pOn, pUsing );
    }
    static SrcList sqlite3SrcListAppendFromTerm(
    Parse pParse,          /* Parsing context */
    SrcList p,             /* The left part of the FROM clause already seen */
    Token pTable,          /* Name of the table to add to the FROM clause */
    Token pDatabase,       /* Name of the database containing pTable */
    Token pAlias,          /* The right-hand side of the AS subexpression */
    Select pSubquery,      /* A subquery used in place of a table name */
    Expr pOn,              /* The ON clause of a join */
    IdList pUsing          /* The USING clause of a join */
    )
    {
      SrcList_item pItem;
      sqlite3 db = pParse.db;
      if ( null == p && ( pOn != null || pUsing != null ) )
      {
        sqlite3ErrorMsg( pParse, "a JOIN clause is required before %s",
          ( pOn != null ? "ON" : "USING" )
        );
        goto append_from_error;
      }
      p = sqlite3SrcListAppend( db, p, pTable, pDatabase );
      //if ( p == null || NEVER( p.nSrc == 0 ) )
      //{
      //  goto append_from_error;
      //}
      pItem = p.a[p.nSrc - 1];
      Debug.Assert( pAlias != null );
      if ( pAlias.n != 0 )
      {
        pItem.zAlias = sqlite3NameFromToken( db, pAlias );
      }
      pItem.pSelect = pSubquery;
      pItem.pOn = pOn;
      pItem.pUsing = pUsing;
      return p;
append_from_error:
      Debug.Assert( p == null );
      sqlite3ExprDelete( db, ref pOn );
      sqlite3IdListDelete( db, ref pUsing );
      sqlite3SelectDelete( db, ref pSubquery );
      return null;
    }

    /*
    ** Add an INDEXED BY or NOT INDEXED clause to the most recently added
    ** element of the source-list passed as the second argument.
    */
    static void sqlite3SrcListIndexedBy( Parse pParse, SrcList p, Token pIndexedBy )
    {
      Debug.Assert( pIndexedBy != null );
      if ( p != null && ALWAYS( p.nSrc > 0 ) )
      {
        SrcList_item pItem = p.a[p.nSrc - 1];
        Debug.Assert( 0 == pItem.notIndexed && pItem.zIndex == null );
        if ( pIndexedBy.n == 1 && null == pIndexedBy.z )
        {
          /* A "NOT INDEXED" clause was supplied. See parse.y
          ** construct "indexed_opt" for details. */
          pItem.notIndexed = 1;
        }
        else
        {
          pItem.zIndex = sqlite3NameFromToken( pParse.db, pIndexedBy );
        }
      }
    }

    /*
    ** When building up a FROM clause in the parser, the join operator
    ** is initially attached to the left operand.  But the code generator
    ** expects the join operator to be on the right operand.  This routine
    ** Shifts all join operators from left to right for an entire FROM
    ** clause.
    **
    ** Example: Suppose the join is like this:
    **
    **           A natural cross join B
    **
    ** The operator is "natural cross join".  The A and B operands are stored
    ** in p.a[0] and p.a[1], respectively.  The parser initially stores the
    ** operator with A.  This routine shifts that operator over to B.
    */
    static void sqlite3SrcListShiftJoinType( SrcList p )
    {
      if ( p != null && p.a != null )
      {
        int i;
        for ( i = p.nSrc - 1; i > 0; i-- )
        {
          p.a[i].jointype = p.a[i - 1].jointype;
        }
        p.a[0].jointype = 0;
      }
    }

    /*
    ** Begin a transaction
    */
    static void sqlite3BeginTransaction( Parse pParse, int type )
    {
      sqlite3 db;
      Vdbe v;
      int i;

      Debug.Assert( pParse != null );
      db = pParse.db;
      Debug.Assert( db != null );
      /*  if( db.aDb[0].pBt==0 ) return; */
      if ( sqlite3AuthCheck( pParse, SQLITE_TRANSACTION, "BEGIN", null, null ) != 0 )
      {
        return;
      }
      v = sqlite3GetVdbe( pParse );
      if ( v == null )
        return;
      if ( type != TK_DEFERRED )
      {
        for ( i = 0; i < db.nDb; i++ )
        {
          sqlite3VdbeAddOp2( v, OP_Transaction, i, ( type == TK_EXCLUSIVE ) ? 2 : 1 );
          sqlite3VdbeUsesBtree( v, i );
        }
      }
      sqlite3VdbeAddOp2( v, OP_AutoCommit, 0, 0 );
    }

    /*
    ** Commit a transaction
    */
    static void sqlite3CommitTransaction( Parse pParse )
    {
      sqlite3 db;
      Vdbe v;

      Debug.Assert( pParse != null );
      db = pParse.db;
      Debug.Assert( db != null );
      /*  if( db.aDb[0].pBt==0 ) return; */
      if ( sqlite3AuthCheck( pParse, SQLITE_TRANSACTION, "COMMIT", null, null ) != 0 )
      {
        return;
      }
      v = sqlite3GetVdbe( pParse );
      if ( v != null )
      {
        sqlite3VdbeAddOp2( v, OP_AutoCommit, 1, 0 );
      }
    }

    /*
    ** Rollback a transaction
    */
    static void sqlite3RollbackTransaction( Parse pParse )
    {
      sqlite3 db;
      Vdbe v;

      Debug.Assert( pParse != null );
      db = pParse.db;
      Debug.Assert( db != null );
      /*  if( db.aDb[0].pBt==0 ) return; */
      if ( sqlite3AuthCheck( pParse, SQLITE_TRANSACTION, "ROLLBACK", null, null ) != 0 )
      {
        return;
      }
      v = sqlite3GetVdbe( pParse );
      if ( v != null )
      {
        sqlite3VdbeAddOp2( v, OP_AutoCommit, 1, 1 );
      }
    }

    /*
    ** This function is called by the parser when it parses a command to create,
    ** release or rollback an SQL savepoint.
    */
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
const string[] az = { "BEGIN", "RELEASE", "ROLLBACK" };
#endif
    static void sqlite3Savepoint( Parse pParse, int op, Token pName )
    {
      string zName = sqlite3NameFromToken( pParse.db, pName );
      if ( zName != null )
      {
        Vdbe v = sqlite3GetVdbe( pParse );
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
Debug.Assert( !SAVEPOINT_BEGIN && SAVEPOINT_RELEASE==1 && SAVEPOINT_ROLLBACK==2 );
#endif
        if ( null == v
#if NO_SQLITE_OMIT_AUTHORIZATION //#if !SQLITE_OMIT_AUTHORIZATION
|| sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0)
#endif
 )
        {
          sqlite3DbFree( pParse.db, ref zName );
          return;
        }
        sqlite3VdbeAddOp4( v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC );
      }
    }

    /*
    ** Make sure the TEMP database is open and available for use.  Return
    ** the number of errors.  Leave any error messages in the pParse structure.
    */
    static int sqlite3OpenTempDatabase( Parse pParse )
    {
      sqlite3 db = pParse.db;
      if ( db.aDb[1].pBt == null && pParse.explain == 0 )
      {
        int rc;
        Btree pBt = null;
        const int flags =
        SQLITE_OPEN_READWRITE |
        SQLITE_OPEN_CREATE |
        SQLITE_OPEN_EXCLUSIVE |
        SQLITE_OPEN_DELETEONCLOSE |
        SQLITE_OPEN_TEMP_DB;

        rc = sqlite3BtreeOpen(db.pVfs, null, db, ref pBt, 0, flags );
        if ( rc != SQLITE_OK )
        {
          sqlite3ErrorMsg( pParse, "unable to open a temporary database " +
          "file for storing temporary tables" );
          pParse.rc = rc;
          return 1;
        }
        db.aDb[1].pBt = pBt;
        Debug.Assert( db.aDb[1].pSchema != null );
        if ( SQLITE_NOMEM == sqlite3BtreeSetPageSize( pBt, db.nextPagesize, -1, 0 ) )
        {
          //  db.mallocFailed = 1;
        }
      }
      return 0;
    }

    /*
    ** Generate VDBE code that will verify the schema cookie and start
    ** a read-transaction for all named database files.
    **
    ** It is important that all schema cookies be verified and all
    ** read transactions be started before anything else happens in
    ** the VDBE program.  But this routine can be called after much other
    ** code has been generated.  So here is what we do:
    **
    ** The first time this routine is called, we code an OP_Goto that
    ** will jump to a subroutine at the end of the program.  Then we
    ** record every database that needs its schema verified in the
    ** pParse.cookieMask field.  Later, after all other code has been
    ** generated, the subroutine that does the cookie verifications and
    ** starts the transactions will be coded and the OP_Goto P2 value
    ** will be made to point to that subroutine.  The generation of the
    ** cookie verification subroutine code happens in sqlite3FinishCoding().
    **
    ** If iDb<0 then code the OP_Goto only - don't set flag to verify the
    ** schema on any databases.  This can be used to position the OP_Goto
    ** early in the code, before we know if any database tables will be used.
    */
    static void sqlite3CodeVerifySchema( Parse pParse, int iDb )
    {
      Parse pToplevel = sqlite3ParseToplevel( pParse );

      if ( pToplevel.cookieGoto == 0 )
      {
        Vdbe v = sqlite3GetVdbe( pToplevel );
        if ( v == null )
          return;  /* This only happens if there was a prior error */
        pToplevel.cookieGoto = sqlite3VdbeAddOp2( v, OP_Goto, 0, 0 ) + 1;
      }
      if ( iDb >= 0 )
      {
        sqlite3 db = pToplevel.db;
        yDbMask mask;
        Debug.Assert( iDb < db.nDb );
        Debug.Assert( db.aDb[iDb].pBt != null || iDb == 1 );
        Debug.Assert( iDb < SQLITE_MAX_ATTACHED + 2 );
        Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) );
        mask = ( (yDbMask)1 ) << iDb;
        if ( ( pToplevel.cookieMask & mask ) == 0 )
        {
          pToplevel.cookieMask |= mask;
          pToplevel.cookieValue[iDb] = db.aDb[iDb].pSchema.schema_cookie;
          if ( 0 == OMIT_TEMPDB && iDb == 1 )
          {
            sqlite3OpenTempDatabase( pToplevel );
          }
        }
      }
    }

    /*
    ** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each 
    ** attached database. Otherwise, invoke it for the database named zDb only.
    */
    static void sqlite3CodeVerifyNamedSchema( Parse pParse, string zDb )
    {
      sqlite3 db = pParse.db;
      int i;
      for ( i = 0; i < db.nDb; i++ )
      {
        Db pDb = db.aDb[i];
        if ( pDb.pBt != null && ( null == zDb || 0 == zDb.CompareTo(pDb.zName ) ) )
        {
          sqlite3CodeVerifySchema( pParse, i );
        }
      }
    }
    /*
    ** Generate VDBE code that prepares for doing an operation that
    ** might change the database.
    **
    ** This routine starts a new transaction if we are not already within
    ** a transaction.  If we are already within a transaction, then a checkpoint
    ** is set if the setStatement parameter is true.  A checkpoint should
    ** be set for operations that might fail (due to a constraint) part of
    ** the way through and which will need to undo some writes without having to
    ** rollback the whole transaction.  For operations where all constraints
    ** can be checked before any changes are made to the database, it is never
    ** necessary to undo a write and the checkpoint should not be set.
    */
    static void sqlite3BeginWriteOperation( Parse pParse, int setStatement, int iDb )
    {
      Parse pToplevel = sqlite3ParseToplevel( pParse );
      sqlite3CodeVerifySchema( pParse, iDb );
      pToplevel.writeMask |= ( (yDbMask)1 ) << iDb;
      pToplevel.isMultiWrite |= (u8)setStatement;
    }

    /*
    ** Indicate that the statement currently under construction might write
    ** more than one entry (example: deleting one row then inserting another,
    ** inserting multiple rows in a table, or inserting a row and index entries.)
    ** If an abort occurs after some of these writes have completed, then it will
    ** be necessary to undo the completed writes.
    */
    static void sqlite3MultiWrite( Parse pParse )
    {
      Parse pToplevel = sqlite3ParseToplevel( pParse );
      pToplevel.isMultiWrite = 1;
    }

    /* 
    ** The code generator calls this routine if is discovers that it is
    ** possible to abort a statement prior to completion.  In order to 
    ** perform this abort without corrupting the database, we need to make
    ** sure that the statement is protected by a statement transaction.
    **
    ** Technically, we only need to set the mayAbort flag if the
    ** isMultiWrite flag was previously set.  There is a time dependency
    ** such that the abort must occur after the multiwrite.  This makes
    ** some statements involving the REPLACE conflict resolution algorithm
    ** go a little faster.  But taking advantage of this time dependency
    ** makes it more difficult to prove that the code is correct (in 
    ** particular, it prevents us from writing an effective
    ** implementation of sqlite3AssertMayAbort()) and so we have chosen
    ** to take the safe route and skip the optimization.
    */
    static void sqlite3MayAbort( Parse pParse )
    {
      Parse pToplevel = sqlite3ParseToplevel( pParse );
      pToplevel.mayAbort = 1;
    }

    /*
    ** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT
    ** error. The onError parameter determines which (if any) of the statement
    ** and/or current transaction is rolled back.
    */
    static void sqlite3HaltConstraint( Parse pParse, int onError, string p4, int p4type )
    {
      Vdbe v = sqlite3GetVdbe( pParse );
      if ( onError == OE_Abort )
      {
        sqlite3MayAbort( pParse );
      }
      sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type );
    }

    static void sqlite3HaltConstraint( Parse pParse, int onError, byte[] p4, int p4type )
    {
      Vdbe v = sqlite3GetVdbe( pParse );
      if ( onError == OE_Abort )
      {
        sqlite3MayAbort( pParse );
      }
      sqlite3VdbeAddOp4( v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type );
    }

    /*
    ** Check to see if pIndex uses the collating sequence pColl.  Return
    ** true if it does and false if it does not.
    */
#if !SQLITE_OMIT_REINDEX
    static bool collationMatch( string zColl, Index pIndex )
    {
      int i;
      Debug.Assert( zColl != null );
      for ( i = 0; i < pIndex.nColumn; i++ )
      {
        string z = pIndex.azColl[i];
        Debug.Assert( z != null );
        if ( z.Equals( zColl, StringComparison.InvariantCultureIgnoreCase ) )
        {
          return true;
        }
      }
      return false;
    }
#endif

    /*
** Recompute all indices of pTab that use the collating sequence pColl.
** If pColl == null then recompute all indices of pTab.
*/
#if !SQLITE_OMIT_REINDEX
    static void reindexTable( Parse pParse, Table pTab, string zColl )
    {
      Index pIndex;              /* An index associated with pTab */

      for ( pIndex = pTab.pIndex; pIndex != null; pIndex = pIndex.pNext )
      {
        if ( zColl == null || collationMatch( zColl, pIndex ) )
        {
          int iDb = sqlite3SchemaToIndex( pParse.db, pTab.pSchema );
          sqlite3BeginWriteOperation( pParse, 0, iDb );
          sqlite3RefillIndex( pParse, pIndex, -1 );
        }
      }
    }
#endif

    /*
** Recompute all indices of all tables in all databases where the
** indices use the collating sequence pColl.  If pColl == null then recompute
** all indices everywhere.
*/
#if !SQLITE_OMIT_REINDEX
    static void reindexDatabases( Parse pParse, string zColl )
    {
      Db pDb;                    /* A single database */
      int iDb;                   /* The database index number */
      sqlite3 db = pParse.db;    /* The database connection */
      HashElem k;                /* For looping over tables in pDb */
      Table pTab;                /* A table in the database */

      Debug.Assert( sqlite3BtreeHoldsAllMutexes( db ) );  /* Needed for schema access */
      for ( iDb = 0; iDb < db.nDb; iDb++ )//, pDb++ )
      {
        pDb = db.aDb[iDb];
        Debug.Assert( pDb != null );
        for ( k = pDb.pSchema.tblHash.first; k != null; k = k.next ) //for ( k = sqliteHashFirst( pDb.pSchema.tblHash ) ; k != null ; k = sqliteHashNext( k ) )
        {
          pTab = (Table)k.data;// sqliteHashData( k );
          reindexTable( pParse, pTab, zColl );
        }
      }
    }
#endif

    /*
** Generate code for the REINDEX command.
**
**        REINDEX                            -- 1
**        REINDEX  <collation>               -- 2
**        REINDEX  ?<database>.?<tablename>  -- 3
**        REINDEX  ?<database>.?<indexname>  -- 4
**
** Form 1 causes all indices in all attached databases to be rebuilt.
** Form 2 rebuilds all indices in all databases that use the named
** collating function.  Forms 3 and 4 rebuild the named index or all
** indices associated with the named table.
*/
#if !SQLITE_OMIT_REINDEX
    // OVERLOADS, so I don't need to rewrite parse.c
    static void sqlite3Reindex( Parse pParse, int null_2, int null_3 )
    {
      sqlite3Reindex( pParse, null, null );
    }
    static void sqlite3Reindex( Parse pParse, Token pName1, Token pName2 )
    {
      CollSeq pColl;                /* Collating sequence to be reindexed, or NULL */
      string z;                     /* Name of a table or index */
      string zDb;                   /* Name of the database */
      Table pTab;                   /* A table in the database */
      Index pIndex;                 /* An index associated with pTab */
      int iDb;                      /* The database index number */
      sqlite3 db = pParse.db;       /* The database connection */
      Token pObjName = new Token();  /* Name of the table or index to be reindexed */

      /* Read the database schema. If an error occurs, leave an error message
      ** and code in pParse and return NULL. */
      if ( SQLITE_OK != sqlite3ReadSchema( pParse ) )
      {
        return;
      }

      if ( pName1 == null )
      {
        reindexDatabases( pParse, null );
        return;
      }
      else if ( NEVER( pName2 == null ) || pName2.z == null || pName2.z.Length == 0 )
      {
        string zColl;
        Debug.Assert( pName1.z != null );
        zColl = sqlite3NameFromToken( pParse.db, pName1 );
        if ( zColl == null )
          return;
        pColl = sqlite3FindCollSeq( db, ENC( db ), zColl, 0 );
        if ( pColl != null )
        {
          reindexDatabases( pParse, zColl );
          sqlite3DbFree( db, ref zColl );
          return;
        }
        sqlite3DbFree( db, ref zColl );
      }
      iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pObjName );
      if ( iDb < 0 )
        return;
      z = sqlite3NameFromToken( db, pObjName );
      if ( z == null )
        return;
      zDb = db.aDb[iDb].zName;
      pTab = sqlite3FindTable( db, z, zDb );
      if ( pTab != null )
      {
        reindexTable( pParse, pTab, null );
        sqlite3DbFree( db, ref z );
        return;
      }
      pIndex = sqlite3FindIndex( db, z, zDb );
      sqlite3DbFree( db, ref z );
      if ( pIndex != null )
      {
        sqlite3BeginWriteOperation( pParse, 0, iDb );
        sqlite3RefillIndex( pParse, pIndex, -1 );
        return;
      }
      sqlite3ErrorMsg( pParse, "unable to identify the object to be reindexed" );
    }
#endif

    /*
** Return a dynamicly allocated KeyInfo structure that can be used
** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
**
** If successful, a pointer to the new structure is returned. In this case
** the caller is responsible for calling sqlite3DbFree(db, ) on the returned
** pointer. If an error occurs (out of memory or missing collation
** sequence), NULL is returned and the state of pParse updated to reflect
** the error.
*/
    static KeyInfo sqlite3IndexKeyinfo( Parse pParse, Index pIdx )
    {
      int i;
      int nCol = pIdx.nColumn;
      //int nBytes = KeyInfo.Length + (nCol - 1) * CollSeq*.Length + nCol;
      sqlite3 db = pParse.db;
      KeyInfo pKey = new KeyInfo();// (KeyInfo)sqlite3DbMallocZero(db, nBytes);

      if ( pKey != null )
      {
        pKey.db = pParse.db;
        pKey.aSortOrder = new byte[nCol];
        pKey.aColl = new CollSeq[nCol];// (u8)&(pKey.aColl[nCol]);
        //        Debug.Assert(pKey.aSortOrder[nCol] == (((u8)pKey)[nBytes]));
        for ( i = 0; i < nCol; i++ )
        {
          string zColl = pIdx.azColl[i];
          Debug.Assert( zColl != null );
          pKey.aColl[i] = sqlite3LocateCollSeq( pParse, zColl );
          pKey.aSortOrder[i] = pIdx.aSortOrder[i];
        }
        pKey.nField = (u16)nCol;
      }

      if ( pParse.nErr != 0 )
      {
        pKey = null;
        sqlite3DbFree( db, ref pKey );
      }
      return pKey;
    }
  }
}
