Esempio n. 1
0
        public override RC Read(byte[] zBuf, int iAmt, long iOfst)
        {
            // SQLite never tries to read past the end of a rollback journal file
            Debug.Assert(iOfst + iAmt <= endpoint.iOffset);
            FileChunk pChunk;

            if (readpoint.iOffset != iOfst || iOfst == 0)
            {
                var iOff = 0;
                for (pChunk = pFirst; Check.ALWAYS(pChunk != null) && (iOff + JOURNAL_CHUNKSIZE) <= iOfst; pChunk = pChunk.pNext)
                {
                    iOff += JOURNAL_CHUNKSIZE;
                }
            }
            else
            {
                pChunk = readpoint.pChunk;
            }
            var iChunkOffset = (int)(iOfst % JOURNAL_CHUNKSIZE);
            var izOut        = 0;
            var nRead        = iAmt;

            do
            {
                var iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
                var nCopy  = Math.Min(nRead, JOURNAL_CHUNKSIZE - iChunkOffset);
                Buffer.BlockCopy(pChunk.zChunk, iChunkOffset, zBuf, izOut, nCopy);
                izOut       += nCopy;
                nRead       -= iSpace;
                iChunkOffset = 0;
            } while (nRead >= 0 && (pChunk = pChunk.pNext) != null && nRead > 0);
            readpoint.iOffset = (int)(iOfst + iAmt);
            readpoint.pChunk  = pChunk;
            return(RC.OK);
        }
Esempio n. 2
0
        // 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.
        internal static int sqlite3TwoPartName(Parse pParse, Token pName1, Token pName2, ref Token pUnqual)
        {
            sqlite3 db = pParse.db;
            int     iDb;

            if (Check.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);
        }
Esempio n. 3
0
 // was:sqlite3PcacheTruncate
 internal void TruncatePage(Pgno pgno)
 {
     if (pCache != null)
     {
         PgHdr p;
         PgHdr pNext;
         for (p = pDirty; p != null; p = pNext)
         {
             pNext = p.DirtyNext;
             // This routine never gets call with a positive pgno except right after sqlite3PcacheCleanAll().  So if there are dirty pages,
             // it must be that pgno==0.
             Debug.Assert(p.ID > 0);
             if (Check.ALWAYS(p.ID > pgno))
             {
                 Debug.Assert((p.Flags & PgHdr.PGHDR.DIRTY) != 0);
                 MakePageClean(p);
             }
         }
         if (pgno == 0 && pPage1 != null)
         {
             pPage1.Data = MallocEx.sqlite3Malloc(szPage);
             pgno        = 1;
         }
         pCache.xTruncate(pgno + 1);
     }
 }
Esempio n. 4
0
        // 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.
        internal static void sqlite3UnlinkAndDeleteIndex(sqlite3 db, int iDb, string zIdxName)
        {
            Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null));
            var pHash  = db.aDb[iDb].pSchema.idxHash;
            var len    = sqlite3Strlen30(zIdxName);
            var pIndex = sqlite3HashInsert(ref pHash, zIdxName, len, (Index)null);

            if (Check.ALWAYS(pIndex))
            {
                if (pIndex.pTable.pIndex == pIndex)
                {
                    pIndex.pTable.pIndex = pIndex.pNext;
                }
                else
                {
                    // Justification of ALWAYS();  The index must be on the list of indices.
                    var p = pIndex.pTable.pIndex;
                    while (Check.ALWAYS(p != null) && p.pNext != pIndex)
                    {
                        p = p.pNext;
                    }
                    if (Check.ALWAYS(p != null && p.pNext == pIndex))
                    {
                        p.pNext = pIndex.pNext;
                    }
                }
                freeIndex(db, ref pIndex);
            }
            db.flags |= SQLITE_InternChanges;
        }
Esempio n. 5
0
        // The parser calls this routine in order to create a new VIEW
        internal static void sqlite3CreateView(Parse pParse, Token pBegin, Token pName1, Token pName2, Select pSelect, int isTemp, int noErr)
        {
            var sFix = new DbFixer();
            var db   = pParse.db;

            if (pParse.nVar > 0)
            {
                sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
                sqlite3SelectDelete(db, ref pSelect);
                return;
            }
            Token pName = null;

            sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
            var p = pParse.pNewTable;

            if (p == null || pParse.nErr != 0)
            {
                sqlite3SelectDelete(db, ref pSelect);
                return;
            }
            sqlite3TwoPartName(pParse, pName1, pName2, ref pName);
            var 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 (0 == db.init.busy)
            {
                sqlite3ViewGetColumnNames(pParse, p);
            }
            // Locate the end of the CREATE VIEW statement.  Make sEnd point to the end.
            var sEnd = pParse.sLastToken;

            if (Check.ALWAYS(sEnd.z[0] != 0) && sEnd.z[0] != ';')
            {
                sEnd.z = sEnd.z.Substring(sEnd.n);
            }
            sEnd.n = 0;
            var n = (int)(pBegin.z.Length - sEnd.z.Length);
            var z = pBegin.z;

            while (Check.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;
        }
Esempio n. 6
0
        // was:sqlite3PagerBegin
        public RC Begin(bool exFlag, bool subjInMemory)
        {
            if (errCode != 0)
            {
                return(errCode);
            }
            Debug.Assert(eState >= PAGER.READER && eState < PAGER.ERROR);
            var rc = RC.OK;

            if (Check.ALWAYS(eState == PAGER.READER))
            {
                Debug.Assert(pInJournal == null);
                if (pagerUseWal())
                {
                    // If the pager is configured to use locking_mode=exclusive, and an exclusive lock on the database is not already held, obtain it now.
                    if (exclusiveMode && pWal.ExclusiveMode(-1))
                    {
                        rc = pagerLockDb(VFSLOCK.EXCLUSIVE);
                        if (rc != RC.OK)
                        {
                            return(rc);
                        }
                        pWal.ExclusiveMode(1);
                    }
                    // Grab the write lock on the log file. If successful, upgrade to PAGER_RESERVED state. Otherwise, return an error code to the caller.
                    // The busy-handler is not invoked if another connection already holds the write-lock. If possible, the upper layer will call it.
                    rc = pWal.BeginWriteTransaction();
                }
                else
                {
                    // Obtain a RESERVED lock on the database file. If the exFlag parameter is true, then immediately upgrade this to an EXCLUSIVE lock. The
                    // busy-handler callback can be used when upgrading to the EXCLUSIVE lock, but not when obtaining the RESERVED lock.
                    rc = pagerLockDb(VFSLOCK.RESERVED);
                    if (rc == RC.OK && exFlag)
                    {
                        rc = pager_wait_on_lock(VFSLOCK.EXCLUSIVE);
                    }
                }
                if (rc == RC.OK)
                {
                    // Change to WRITER_LOCKED state.
                    // WAL mode sets Pager.eState to PAGER_WRITER_LOCKED or CACHEMOD when it has an open transaction, but never to DBMOD or FINISHED.
                    // This is because in those states the code to roll back savepoint transactions may copy data from the sub-journal into the database
                    // file as well as into the page cache. Which would be incorrect in WAL mode.
                    eState     = PAGER.WRITER_LOCKED;
                    dbHintSize = dbSize;
                    dbFileSize = dbSize;
                    dbOrigSize = dbSize;
                    journalOff = 0;
                }
                Debug.Assert(rc == RC.OK || eState == PAGER.READER);
                Debug.Assert(rc != RC.OK || eState == PAGER.WRITER_LOCKED);
                Debug.Assert(assert_pager_state());
            }
            PAGERTRACE("TRANSACTION {0}", PAGERID(this));
            return(rc);
        }
Esempio n. 7
0
 // was:sqlite3BtreeKeyFetch
 public byte[] FetchKey(ref int size, out int offset)
 {
     Debug.Assert(MutexEx.Held(Tree.DB.Mutex));
     Debug.Assert(HoldsMutex());
     if (Check.ALWAYS(State == CursorState.VALID))
     {
         return(FetchPayload(ref size, out offset, false));
     }
     offset = 0;
     return(null);
 }
Esempio n. 8
0
 private static void pcache1FreePage(ref PgHdr1 p)
 {
     if (Check.ALWAYS(p) != null)
     {
         var pCache = p.pCache;
         if (pCache.bPurgeable)
         {
             pCache.pGroup.nCurrentPage--;
         }
         pcache1Free(ref p.pPgHdr);
     }
 }
Esempio n. 9
0
 // Add an INDEXED BY or NOT INDEXED clause to the most recently added element of the source-list passed as the second argument.
 internal static void sqlite3SrcListIndexedBy(Parse pParse, SrcList p, Token pIndexedBy)
 {
     Debug.Assert(pIndexedBy != null);
     if (p != null && Check.ALWAYS(p.nSrc > 0))
     {
         var 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);
         }
     }
 }
Esempio n. 10
0
        internal bool removeFromSharingList()
        {
#if !SQLITE_OMIT_SHARED_CACHE
            MutexEx  pMaster;
            BtShared list;
            bool     removed = false;
            Debug.Assert(MutexEx.NotHeld(Mutex));
            pMaster = MutexEx.Alloc(MUTEX.STATIC_MASTER);
            MutexEx.Enter(pMaster);
            nRef--;
            if (nRef <= 0)
            {
                if (SysEx.getGLOBAL <BtShared>(Btree.s_sqlite3SharedCacheList) == this)
                {
                    SysEx.setGLOBAL <BtShared>(Btree.s_sqlite3SharedCacheList, Next);
                }
                else
                {
                    list = SysEx.getGLOBAL <BtShared>(Btree.s_sqlite3SharedCacheList);
                    while (Check.ALWAYS(list) != null && list.Next != this)
                    {
                        list = list.Next;
                    }
                    if (Check.ALWAYS(list) != null)
                    {
                        list.Next = Next;
                    }
                }
                if (MutexEx.SQLITE_THREADSAFE)
                {
                    MutexEx.Free(Mutex);
                }
                removed = true;
            }
            MutexEx.Leave(pMaster);
            return(removed);
#else
            return(true);
#endif
        }
Esempio n. 11
0
        private static PgHdr pcacheSortDirtyList(PgHdr pIn)
        {
            var   a = new PgHdr[N_SORT_BUCKET];
            PgHdr p;

            while (pIn != null)
            {
                p        = pIn;
                pIn      = p.Dirtys;
                p.Dirtys = null;
                int i;
                for (i = 0; Check.ALWAYS(i < N_SORT_BUCKET - 1); i++)
                {
                    if (a[i] == null)
                    {
                        a[i] = p;
                        break;
                    }
                    else
                    {
                        p    = pcacheMergeDirtyList(a[i], p);
                        a[i] = null;
                    }
                }
                if (Check.NEVER(i == N_SORT_BUCKET - 1))
                {
                    // To get here, there need to be 2^(N_SORT_BUCKET) elements in the input list.  But that is impossible.
                    a[i] = pcacheMergeDirtyList(a[i], p);
                }
            }
            p = a[0];
            for (var i = 1; i < N_SORT_BUCKET; i++)
            {
                p = pcacheMergeDirtyList(p, a[i]);
            }
            return(p);
        }
Esempio n. 12
0
        private RC pager_incr_changecounter(bool isDirectMode)
        {
            var rc = RC.OK;

            Debug.Assert(this.eState == PAGER.WRITER_CACHEMOD || this.eState == PAGER.WRITER_DBMOD);
            Debug.Assert(assert_pager_state());
            // Declare and initialize constant integer 'isDirect'. If the atomic-write optimization is enabled in this build, then isDirect
            // is initialized to the value passed as the isDirectMode parameter to this function. Otherwise, it is always set to zero.
            // The idea is that if the atomic-write optimization is not enabled at compile time, the compiler can omit the tests of
            // 'isDirect' below, as well as the block enclosed in the "if( isDirect )" condition.
#if !SQLITE_ENABLE_ATOMIC_WRITE
            var DIRECT_MODE = false;
            Debug.Assert(!isDirectMode);
            SysEx.UNUSED_PARAMETER(isDirectMode);
#else
            var DIRECT_MODE = isDirectMode;
#endif
            if (!this.changeCountDone && this.dbSize > 0)
            {
                PgHdr pPgHdr = null; // Reference to page 1
                Debug.Assert(!this.tempFile && this.fd.IsOpen);
                // Open page 1 of the file for writing.
                rc = Get(1, ref pPgHdr);
                Debug.Assert(pPgHdr == null || rc == RC.OK);
                // If page one was fetched successfully, and this function is not operating in direct-mode, make page 1 writable.  When not in
                // direct mode, page 1 is always held in cache and hence the PagerGet() above is always successful - hence the ALWAYS on rc==SQLITE.OK.
                if (!DIRECT_MODE && Check.ALWAYS(rc == RC.OK))
                {
                    rc = Write(pPgHdr);
                }
                if (rc == RC.OK)
                {
                    // Actually do the update of the change counter
                    pager_write_changecounter(pPgHdr);
                    // If running in direct mode, write the contents of page 1 to the file.
                    if (DIRECT_MODE)
                    {
                        byte[] zBuf = null;
                        Debug.Assert(this.dbFileSize > 0);
                        if (CODEC2(this, pPgHdr.Data, 1, codec_ctx.ENCRYPT_WRITE_CTX, ref zBuf))
                        {
                            return(rc = RC.NOMEM);
                        }
                        if (rc == RC.OK)
                        {
                            rc = this.fd.Write(zBuf, this.pageSize, 0);
                        }
                        if (rc == RC.OK)
                        {
                            this.changeCountDone = true;
                        }
                    }
                    else
                    {
                        this.changeCountDone = true;
                    }
                }
                // Release the page reference.
                Unref(pPgHdr);
            }
            return(rc);
        }
Esempio n. 13
0
        // 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)
        {
            var db = pParse.db;

            if (pParse.nested != 0)
            {
                return;
            }
            if (pParse.nErr != 0)
            {
                return;
            }
            // Begin by generating some termination code at the end of the vdbe program
            var v = sqlite3GetVdbe(pParse);

            Debug.Assert(0 == pParse.isMultiWrite
#if 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)
                {
                    sqlite3VdbeJumpHere(v, pParse.cookieGoto - 1);
                    for (uint 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
                    for (var i = 0; i < pParse.nVtabLock; i++)
                    {
                        var 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 && Check.ALWAYS(pParse.nErr == 0))
            {
#if DEBUG
                var 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
                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;
        }
Esempio n. 14
0
        static Index sqlite3CreateIndex(Parse pParse, Token pName1, Token pName2, SrcList pTblName, ExprList pList, int onError, Token pStart, Token pEnd, int sortOrder, int ifNotExist)
        {
            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 */
            var           nullId = new Token();
            var           sFix   = new DbFixer();
            int           sortOrderMask;  /* 1 to honor DESC in index.  0 to ignore. */
            var           db = pParse.db;
            Db            pDb;            /* The specific table containing the indexed database */
            Token         pName = null;   /* Unqualified name of the index to create */
            ExprList_item pListItem;      /* For looping over pList */
            int           nCol;
            int           nExtra = 0;
            var           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 (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);
                var 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)
                {
                    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;
                for (var 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.
            var zDb = pDb.zName;
            if (sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb))
            {
                goto exit_create_index;
            }
            var code = (OMIT_TEMPDB == 0 && iDb == 1 ? SQLITE_CREATE_TEMP_INDEX : SQLITE_CREATE_INDEX);
            if (sqlite3AuthCheck(pParse, i, zName, pTab.zName, zDb))
            {
                goto exit_create_index;
            }
            // 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 (var i = 0; i < pList.nExpr; i++)
            {
                var pExpr = pList.a[i].pExpr;
                if (pExpr != null)
                {
                    var 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 (Check.ALWAYS(pColl != null))
                    {
                        nExtra += (1 + sqlite3Strlen30(pColl.zName));
                    }
                }
            }
            // Allocate the index structure.
            nName             = sqlite3Strlen30(zName);
            nCol              = pList.nExpr;
            pIndex            = new Index();
            pIndex.azColl     = new string[nCol + 1];
            pIndex.aiColumn   = new int[nCol + 1];
            pIndex.aiRowEst   = new int[nCol + 1];
            pIndex.aSortOrder = new byte[nCol + 1];
            zExtra            = new StringBuilder(nName + 1);
            if (zName.Length == nName)
            {
                pIndex.zName = zName;
            }
            else
            {
                pIndex.zName = zName.Substring(0, nName);
            }
            pIndex.pTable    = pTab;
            pIndex.nColumn   = pList.nExpr;
            pIndex.onError   = (byte)onError;
            pIndex.autoIndex = (byte)(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
            sortOrderMask = (pDb.pSchema.file_format >= 4 ? 1 : 0);
            // 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.
            for (var i = 0; i < pList.nExpr; i++)
            {
                int j;
                var zColName = pListItem.zName;
                for (j = 0; j < pTab.nCol; j++)
                {
                    var 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.
                string zColl;
                pListItem = pList.a[i];
                if (pListItem.pExpr != null && Check.ALWAYS(pListItem.pExpr.pColl))
                {
                    zColl = pListItem.pExpr.pColl.zName;
                    var nColl = sqlite3Strlen30(zColl);
                    Debug.Assert(nExtra >= nColl);
                    zExtra  = new StringBuilder(zColl.Substring(0, nColl));// memcpy( zExtra, zColl, nColl );
                    zColl   = zExtra.ToString();
                    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;
                var requestedSortOrder = (byte)((pListItem.sortOrder & sortOrderMask) != 0 ? 1 : 0);
                pIndex.aSortOrder[i] = (byte)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);
        }