internal static SrcList sqlite3SrcListAppend(sqlite3 db, SrcList pList, Token pTable, Token pDatabase) { Debug.Assert(pDatabase == null || pTable != null); // Cannot have C without B if (pList == null) { pList = new SrcList(); pList.nAlloc = 1; pList.a = new SrcList_item[1]; } pList = sqlite3SrcListEnlarge(db, pList, 1, pList.nSrc); var pItem = pList.a[pList.nSrc - 1]; if (pDatabase != null && String.IsNullOrEmpty(pDatabase.z)) pDatabase = null; if (pDatabase != null) { var pTemp = pDatabase; pDatabase = pTable; pTable = pTemp; } pItem.zName = sqlite3NameFromToken(db, pTable); pItem.zDatabase = sqlite3NameFromToken(db, pDatabase); return pList; }
internal static SrcList sqlite3SrcListAppendFromTerm(Parse pParse, SrcList p, Token pTable, Token pDatabase, Token pAlias, Select pSubquery, Expr pOn, IdList pUsing) { var 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); var 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; }
// This routine is called to do the work of a DROP TABLE statement. pName is the name of the table to be dropped. internal static void sqlite3DropTable(Parse pParse, SrcList pName, int isView, int noErr) { var db = pParse.db; Debug.Assert(pParse.nErr == 0); Debug.Assert(pName.nSrc == 1); if (noErr != 0) db.suppressErr++; var 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; } var 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; var zTab = SCHEMA_TABLE(iDb); var zDb = db.aDb[iDb].zName; string zArg2 = null; if (sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)) goto exit_drop_table; int code; if (isView) code = (OMIT_TEMPDB == 0 && iDb == 1 ? SQLITE_DROP_TEMP_VIEW : SQLITE_DROP_VIEW); else if (IsVirtual(pTab)) { code = SQLITE_DROP_VTABLE; zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName; } else code = (OMIT_TEMPDB == 0 && iDb == 1 ? SQLITE_DROP_TEMP_TABLE : 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; 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. var v = sqlite3GetVdbe(pParse); if (v != null) { var 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. var 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 will drop an existing named index. This routine implements the DROP INDEX statement. internal static void sqlite3DropIndex(Parse pParse, SrcList pName, int ifExists) { Debug.Assert(pParse.nErr == 0); // Never called with prior errors Debug.Assert(pName.nSrc == 1); if (SQLITE_OK != sqlite3ReadSchema(pParse)) goto exit_drop_index; var db = pParse.db; var 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; } var iDb = sqlite3SchemaToIndex(db, pIndex.pSchema); var pTab = pIndex.pTable; var zDb = db.aDb[iDb].zName; var zTab = SCHEMA_TABLE(iDb); if (sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)) goto exit_drop_index; var code (OMIT_TEMPDB == 0 && iDb ? SQLITE_DROP_TEMP_INDEX : SQLITE_DROP_INDEX); if (sqlite3AuthCheck(pParse, code, pIndex.zName, pTab.zName, zDb)) goto exit_drop_index; // Generate code to remove the index and from the master table var 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); }
/* ** If cursors, triggers, views and subqueries are all omitted from ** the build, then none of the following routines, except for ** sqlite3SelectDup(), can be called. sqlite3SelectDup() is sometimes ** called with a NULL argument. */ #if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER || !SQLITE_OMIT_SUBQUERY static SrcList sqlite3SrcListDup(sqlite3 db, SrcList p, int flags) { SrcList pNew; int i; int nByte; if (p == null) return null; //nByte = sizeof(*p) + (p.nSrc>0 ? sizeof(p.a[0]) * (p.nSrc-1) : 0); pNew = new SrcList();//sqlite3DbMallocRaw(db, nByte ); if (p.nSrc > 0) pNew.a = new SrcList_item[p.nSrc]; if (pNew == null) return null; pNew.nSrc = pNew.nAlloc = p.nSrc; for (i = 0; i < p.nSrc; i++) { pNew.a[i] = new SrcList_item(); SrcList_item pNewItem = pNew.a[i]; SrcList_item pOldItem = p.a[i]; Table pTab; pNewItem.zDatabase = pOldItem.zDatabase;// sqlite3DbStrDup(db, pOldItem.zDatabase); pNewItem.zName = pOldItem.zName;// sqlite3DbStrDup(db, pOldItem.zName); pNewItem.zAlias = pOldItem.zAlias;// sqlite3DbStrDup(db, pOldItem.zAlias); pNewItem.jointype = pOldItem.jointype; pNewItem.iCursor = pOldItem.iCursor; pNewItem.isPopulated = pOldItem.isPopulated; pNewItem.zIndex = pOldItem.zIndex;// sqlite3DbStrDup( db, pOldItem.zIndex ); pNewItem.notIndexed = pOldItem.notIndexed; pNewItem.pIndex = pOldItem.pIndex; pTab = pNewItem.pTab = pOldItem.pTab; if (pTab != null) { pTab.nRef++; } pNewItem.pSelect = sqlite3SelectDup(db, pOldItem.pSelect, flags); pNewItem.pOn = sqlite3ExprDup(db, pOldItem.pOn, flags); pNewItem.pUsing = sqlite3IdListDup(db, pOldItem.pUsing); pNewItem.colUsed = pOldItem.colUsed; } return pNew; }
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); }
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; }
internal 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); }
// 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. internal 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); }
// 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. internal static void sqlite3SrcListShiftJoinType(SrcList p) { if (p != null && p.a != null) { for (var i = p.nSrc - 1; i > 0; i--) p.a[i].jointype = p.a[i - 1].jointype; p.a[0].jointype = 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); } }
// 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. internal static SrcList sqlite3SrcListEnlarge(sqlite3 db, SrcList pSrc, int nExtra, int iStart) { 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) { var nAlloc = pSrc.nSrc + nExtra; pSrc.nAlloc = (short)nAlloc; Array.Resize(ref pSrc.a, nAlloc); } // Move existing slots that come after the newly inserted slots out of the way for (var i = pSrc.nSrc - 1; i >= iStart; i--) pSrc.a[i + nExtra] = pSrc.a[i]; pSrc.nSrc += (short)nExtra; // Zero the newly allocated slots for (var i = iStart; i < iStart + nExtra; i++) { pSrc.a[i] = new SrcList_item(); pSrc.a[i].iCursor = -1; } return pSrc; }
// Delete an entire SrcList including all its substructure. internal static void sqlite3SrcListDelete(sqlite3 db, ref SrcList pList) { if (pList == null) return; for (var i = 0; i < pList.nSrc; i++) { var 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); }
// Assign VdbeCursor index numbers to all tables in a SrcList internal static void sqlite3SrcListAssignCursors(Parse pParse, SrcList pList) { Debug.Assert(pList != null); if (pList != null) for (var i = 0; i < pList.nSrc; i++) { var pItem = pList.a[i]; if (pItem.iCursor >= 0) break; pItem.iCursor = pParse.nTab++; if (pItem.pSelect != null) sqlite3SrcListAssignCursors(pParse, pItem.pSelect.pSrc); } }