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); }
// 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); }
// 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); } }
// 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; }
// 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; }
// 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); }
// 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); }
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); } }
// 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); } } }
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 }
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); }
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); }
// 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; }
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); }