// 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; }
/* ** The following set of routines walk through the parse tree and assign ** a specific database to all table references where the database name ** was left unspecified in the original SQL statement. The pFix structure ** must have been initialized by a prior call to sqlite3FixInit(). ** ** These routines are used to make sure that an index, trigger, or ** view in one database does not refer to objects in a different database. ** (Exception: indices, triggers, and views in the TEMP database are ** allowed to refer to anything.) If a reference is explicitly made ** to an object in a different database, an error message is added to ** pParse.zErrMsg and these routines return non-zero. If everything ** checks out, these routines return 0. */ static int sqlite3FixSrcList( DbFixer pFix, /* Context of the fixation */ SrcList pList /* The Source list to check and modify */ ) { int i; string zDb; SrcList_item pItem; if (NEVER(pList == null)) { return(0); } zDb = pFix.zDb; for (i = 0; i < pList.nSrc; i++) {//, pItem++){ pItem = pList.a[i]; if (pItem.zDatabase == null) { pItem.zDatabase = zDb;// sqlite3DbStrDup( pFix.pParse.db, zDb ); } else if (!pItem.zDatabase.Equals(zDb, StringComparison.OrdinalIgnoreCase)) { sqlite3ErrorMsg(pFix.pParse, "%s %T cannot reference objects in database %s", pFix.zType, pFix.pName, pItem.zDatabase); return(1); } #if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER if (sqlite3FixSelect(pFix, pItem.pSelect) != 0) { return(1); } if (sqlite3FixExpr(pFix, pItem.pOn) != 0) { return(1); } #endif } return(0); }
static int sqlite3FixExprList( DbFixer pFix, /* Context of the fixation */ ExprList pList /* The expression to be fixed to one database */ ) { int i; ExprList_item pItem; if (pList == null) { return(0); } for (i = 0; i < pList.nExpr; i++)//, pItem++ ) { pItem = pList.a[i]; if (sqlite3FixExpr(pFix, pItem.pExpr) != 0) { return(1); } } return(0); }
/* ** Initialize a DbFixer structure. This routine must be called prior ** to passing the structure to one of the sqliteFixAAAA() routines below. ** ** The return value indicates whether or not fixation is required. TRUE ** means we do need to fix the database references, FALSE means we do not. */ static int sqlite3FixInit( DbFixer pFix, /* The fixer to be initialized */ Parse pParse, /* Error messages will be written here */ int iDb, /* This is the database that must be used */ string zType, /* "view", "trigger", or "index" */ Token pName /* Name of the view, trigger, or index */ ) { sqlite3 db; if (NEVER(iDb < 0) || iDb == 1) { return(0); } db = pParse.db; Debug.Assert(db.nDb > iDb); pFix.pParse = pParse; pFix.zDb = db.aDb[iDb].zName; pFix.zType = zType; pFix.pName = pName; return(1); }
static int sqlite3FixTriggerStep( DbFixer pFix, /* Context of the fixation */ TriggerStep pStep /* The trigger step be fixed to one database */ ) { while (pStep != null) { if (sqlite3FixSelect(pFix, pStep.pSelect) != 0) { return(1); } if (sqlite3FixExpr(pFix, pStep.pWhere) != 0) { return(1); } if (sqlite3FixExprList(pFix, pStep.pExprList) != 0) { return(1); } pStep = pStep.pNext; } return(0); }
static int sqlite3FixTriggerStep( DbFixer pFix, /* Context of the fixation */ TriggerStep pStep /* The trigger step be fixed to one database */ ) { while ( pStep != null ) { if ( sqlite3FixSelect( pFix, pStep.pSelect ) != 0 ) { return 1; } if ( sqlite3FixExpr( pFix, pStep.pWhere ) != 0 ) { return 1; } if ( sqlite3FixExprList( pFix, pStep.pExprList ) != 0 ) { return 1; } pStep = pStep.pNext; } return 0; }
static int sqlite3FixExprList( DbFixer pFix, /* Context of the fixation */ ExprList pList /* The expression to be fixed to one database */ ) { int i; ExprList_item pItem; if ( pList == null ) return 0; for ( i = 0; i < pList.nExpr; i++ )//, pItem++ ) { pItem = pList.a[i]; if ( sqlite3FixExpr( pFix, pItem.pExpr ) != 0 ) { return 1; } } return 0; }
static int sqlite3FixExpr( DbFixer pFix, /* Context of the fixation */ Expr pExpr /* The expression to be fixed to one database */ ) { while ( pExpr != null ) { if ( ExprHasAnyProperty( pExpr, EP_TokenOnly ) ) break; if ( ExprHasProperty( pExpr, EP_xIsSelect ) ) { if ( sqlite3FixSelect( pFix, pExpr.x.pSelect ) != 0 ) return 1; } else { if ( sqlite3FixExprList( pFix, pExpr.x.pList ) != 0 ) return 1; } if ( sqlite3FixExpr( pFix, pExpr.pRight ) != 0 ) { return 1; } pExpr = pExpr.pLeft; } return 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); }
/* ** The following set of routines walk through the parse tree and assign ** a specific database to all table references where the database name ** was left unspecified in the original SQL statement. The pFix structure ** must have been initialized by a prior call to sqlite3FixInit(). ** ** These routines are used to make sure that an index, trigger, or ** view in one database does not refer to objects in a different database. ** (Exception: indices, triggers, and views in the TEMP database are ** allowed to refer to anything.) If a reference is explicitly made ** to an object in a different database, an error message is added to ** pParse.zErrMsg and these routines return non-zero. If everything ** checks out, these routines return 0. */ static int sqlite3FixSrcList( DbFixer pFix, /* Context of the fixation */ SrcList pList /* The Source list to check and modify */ ) { int i; string zDb; SrcList_item pItem; if ( NEVER( pList == null ) ) return 0; zDb = pFix.zDb; for ( i = 0; i < pList.nSrc; i++ ) {//, pItem++){ pItem = pList.a[i]; if ( pItem.zDatabase == null ) { pItem.zDatabase = zDb;// sqlite3DbStrDup( pFix.pParse.db, zDb ); } else if ( !pItem.zDatabase.Equals( zDb ,StringComparison.InvariantCultureIgnoreCase ) ) { sqlite3ErrorMsg( pFix.pParse, "%s %T cannot reference objects in database %s", pFix.zType, pFix.pName, pItem.zDatabase ); return 1; } #if !SQLITE_OMIT_VIEW || !SQLITE_OMIT_TRIGGER if ( sqlite3FixSelect( pFix, pItem.pSelect ) != 0 ) return 1; if ( sqlite3FixExpr( pFix, pItem.pOn ) != 0 ) return 1; #endif } return 0; }
/* ** Initialize a DbFixer structure. This routine must be called prior ** to passing the structure to one of the sqliteFixAAAA() routines below. ** ** The return value indicates whether or not fixation is required. TRUE ** means we do need to fix the database references, FALSE means we do not. */ static int sqlite3FixInit( DbFixer pFix, /* The fixer to be initialized */ Parse pParse, /* Error messages will be written here */ int iDb, /* This is the database that must be used */ string zType, /* "view", "trigger", or "index" */ Token pName /* Name of the view, trigger, or index */ ) { sqlite3 db; if ( NEVER( iDb < 0 ) || iDb == 1 ) return 0; db = pParse.db; Debug.Assert( db.nDb > iDb ); pFix.pParse = pParse; pFix.zDb = db.aDb[iDb].zName; pFix.zType = zType; pFix.pName = pName; return 1; }
/* ** This is called by the parser when it sees a CREATE TRIGGER statement ** up to the point of the BEGIN before the trigger actions. A Trigger ** structure is generated based on the information available and stored ** in pParse.pNewTrigger. After the trigger actions have been parsed, the ** sqlite3FinishTrigger() function is called to complete the trigger ** construction process. */ static void sqlite3BeginTrigger( Parse pParse, /* The parse context of the CREATE TRIGGER statement */ Token pName1, /* The name of the trigger */ Token pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList pColumns, /* column list if this is an UPDATE OF trigger */ SrcList pTableName, /* The name of the table/view the trigger applies to */ Expr pWhen, /* WHEN clause */ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ) { Trigger pTrigger = null; /* The new trigger */ Table pTab; /* Table that the trigger fires off of */ string zName = null; /* Name of the trigger */ sqlite3 db = pParse.db; /* The database connection */ int iDb; /* The database to store the trigger in */ Token pName = null; /* The unqualified db name */ DbFixer sFix = new DbFixer(); /* State vector for the DB fixer */ int iTabDb; /* Index of the database holding pTab */ Debug.Assert(pName1 != null); /* pName1.z might be NULL, but not pName1 itself */ Debug.Assert(pName2 != null); Debug.Assert(op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE); Debug.Assert(op > 0 && op < 0xff); if (isTemp != 0) { /* If TEMP was specified, then the trigger name may not be qualified. */ if (pName2.n > 0) { sqlite3ErrorMsg(pParse, "temporary trigger may not have qualified name"); goto trigger_cleanup; } iDb = 1; pName = pName1; } else { /* Figure out the db that the the trigger will be created in */ iDb = sqlite3TwoPartName(pParse, pName1, pName2, ref pName); if (iDb < 0) { goto trigger_cleanup; } } /* If the trigger name was unqualified, and the table is a temp table, ** then set iDb to 1 to create the trigger in the temporary database. ** If sqlite3SrcListLookup() returns 0, indicating the table does not ** exist, the error is caught by the block below. */ if (pTableName == null /*|| db.mallocFailed != 0 */) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup(pParse, pTableName); if (pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema) { iDb = 1; } /* Ensure the table name matches database name and that the table exists */ // if ( db.mallocFailed != 0 ) goto trigger_cleanup; Debug.Assert(pTableName.nSrc == 1); if (sqlite3FixInit(sFix, pParse, iDb, "trigger", pName) != 0 && sqlite3FixSrcList(sFix, pTableName) != 0) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup(pParse, pTableName); if (pTab == null) { /* The table does not exist. */ if (db.init.iDb == 1) { /* Ticket #3810. ** Normally, whenever a table is dropped, all associated triggers are ** dropped too. But if a TEMP trigger is created on a non-TEMP table ** and the table is dropped by a different database connection, the ** trigger is not visible to the database connection that does the ** drop so the trigger cannot be dropped. This results in an ** "orphaned trigger" - a trigger whose associated table is missing. */ db.init.orphanTrigger = 1; } goto trigger_cleanup; } if (IsVirtual(pTab)) { sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables"); goto trigger_cleanup; } /* Check that the trigger name is not reserved and that no trigger of the ** specified name exists */ zName = sqlite3NameFromToken(db, pName); if (zName == null || SQLITE_OK != sqlite3CheckObjectName(pParse, zName)) { goto trigger_cleanup; } if (sqlite3HashFind((db.aDb[iDb].pSchema.trigHash), zName, sqlite3Strlen30(zName)) != null) { if (noErr == 0) { sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); } goto trigger_cleanup; } /* Do not create a trigger on a system table */ if (sqlite3StrNICmp(pTab.zName, "sqlite_", 7) == 0) { sqlite3ErrorMsg(pParse, "cannot create trigger on system table"); pParse.nErr++; goto trigger_cleanup; } /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. */ if (pTab.pSelect != null && tr_tm != TK_INSTEAD) { sqlite3ErrorMsg(pParse, "cannot create %s trigger on view: %S", (tr_tm == TK_BEFORE) ? "BEFORE" : "AFTER", pTableName, 0); goto trigger_cleanup; } if (pTab.pSelect == null && tr_tm == TK_INSTEAD) { sqlite3ErrorMsg(pParse, "cannot create INSTEAD OF" + " trigger on table: %S", pTableName, 0); goto trigger_cleanup; } iTabDb = sqlite3SchemaToIndex(db, pTab.pSchema); #if !SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_CREATE_TRIGGER; string zDb = db.aDb[iTabDb].zName; string zDbTrig = isTemp ? db.aDb[1].zName : zDb; if (iTabDb == 1 || isTemp) { code = SQLITE_CREATE_TEMP_TRIGGER; } if (sqlite3AuthCheck(pParse, code, zName, pTab.zName, zDbTrig)) { goto trigger_cleanup; } if (sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb), 0, zDb)) { goto trigger_cleanup; } } #endif /* INSTEAD OF triggers can only appear on views and BEFORE triggers ** cannot appear on views. So we might as well translate every ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code ** elsewhere. */ if (tr_tm == TK_INSTEAD) { tr_tm = TK_BEFORE; } /* Build the Trigger object */ pTrigger = new Trigger();// (Trigger*)sqlite3DbMallocZero( db, sizeof(Trigger )) if (pTrigger == null) { goto trigger_cleanup; } pTrigger.name = zName; pTrigger.table = pTableName.a[0].zName;// sqlite3DbStrDup( db, pTableName.a[0].zName ); pTrigger.pSchema = db.aDb[iDb].pSchema; pTrigger.pTabSchema = pTab.pSchema; pTrigger.op = (u8)op; pTrigger.tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger.pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); pTrigger.pColumns = sqlite3IdListDup(db, pColumns); Debug.Assert(pParse.pNewTrigger == null); pParse.pNewTrigger = pTrigger; trigger_cleanup: //sqlite3DbFree( db, ref zName ); sqlite3SrcListDelete(db, ref pTableName); sqlite3IdListDelete(db, ref pColumns); sqlite3ExprDelete(db, ref pWhen); if (pParse.pNewTrigger == null) { sqlite3DeleteTrigger(db, ref pTrigger); } else { Debug.Assert(pParse.pNewTrigger == pTrigger); } }
/* ** This routine is called after all of the trigger actions have been parsed ** in order to complete the process of building the trigger. */ static void sqlite3FinishTrigger( Parse pParse, /* Parser context */ TriggerStep pStepList, /* The triggered program */ Token pAll /* Token that describes the complete CREATE TRIGGER */ ) { Trigger pTrig = pParse.pNewTrigger; /* Trigger being finished */ string zName; /* Name of trigger */ sqlite3 db = pParse.db; /* The database */ DbFixer sFix = new DbFixer(); int iDb; /* Database containing the trigger */ Token nameToken = new Token(); /* Trigger name for error reporting */ pTrig = pParse.pNewTrigger; pParse.pNewTrigger = null; if (NEVER(pParse.nErr != 0) || pTrig == null) { goto triggerfinish_cleanup; } zName = pTrig.name; iDb = sqlite3SchemaToIndex(pParse.db, pTrig.pSchema); pTrig.step_list = pStepList; while (pStepList != null) { pStepList.pTrig = pTrig; pStepList = pStepList.pNext; } nameToken.z = pTrig.name; nameToken.n = sqlite3Strlen30(nameToken.z); if (sqlite3FixInit(sFix, pParse, iDb, "trigger", nameToken) != 0 && sqlite3FixTriggerStep(sFix, pTrig.step_list) != 0) { goto triggerfinish_cleanup; } /* if we are not initializing, and this trigger is not on a TEMP table, ** build the sqlite_master entry */ if (0 == db.init.busy) { Vdbe v; string z; /* Make an entry in the sqlite_master table */ v = sqlite3GetVdbe(pParse); if (v == null) { goto triggerfinish_cleanup; } sqlite3BeginWriteOperation(pParse, 0, iDb); z = pAll.z.Substring(0, pAll.n);//sqlite3DbStrNDup( db, (char*)pAll.z, pAll.n ); sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", db.aDb[iDb].zName, SCHEMA_TABLE(iDb), zName, pTrig.table, z); //sqlite3DbFree( db, ref z ); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp4(v, OP_ParseSchema, iDb, 0, 0, sqlite3MPrintf( db, "type='trigger' AND name='%q'", zName), P4_DYNAMIC ); } if (db.init.busy != 0) { Trigger pLink = pTrig; Hash pHash = db.aDb[iDb].pSchema.trigHash; pTrig = (Trigger)sqlite3HashInsert(ref pHash, zName, sqlite3Strlen30(zName), pTrig); if (pTrig != null) { //db.mallocFailed = 1; } else if (pLink.pSchema == pLink.pTabSchema) { Table pTab; int n = sqlite3Strlen30(pLink.table); pTab = (Table)sqlite3HashFind(pLink.pTabSchema.tblHash, pLink.table, n); Debug.Assert(pTab != null); pLink.pNext = pTab.pTrigger; pTab.pTrigger = pLink; } } triggerfinish_cleanup: sqlite3DeleteTrigger(db, ref pTrig); Debug.Assert(pParse.pNewTrigger == null); sqlite3DeleteTriggerStep(db, ref pStepList); }
/* ** This is called by the parser when it sees a CREATE TRIGGER statement ** up to the point of the BEGIN before the trigger actions. A Trigger ** structure is generated based on the information available and stored ** in pParse.pNewTrigger. After the trigger actions have been parsed, the ** sqlite3FinishTrigger() function is called to complete the trigger ** construction process. */ static void sqlite3BeginTrigger( Parse pParse, /* The parse context of the CREATE TRIGGER statement */ Token pName1, /* The name of the trigger */ Token pName2, /* The name of the trigger */ int tr_tm, /* One of TK_BEFORE, TK_AFTER, TK_INSTEAD */ int op, /* One of TK_INSERT, TK_UPDATE, TK_DELETE */ IdList pColumns, /* column list if this is an UPDATE OF trigger */ SrcList pTableName,/* The name of the table/view the trigger applies to */ Expr pWhen, /* WHEN clause */ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ) { Trigger pTrigger = null; /* The new trigger */ Table pTab; /* Table that the trigger fires off of */ string zName = null; /* Name of the trigger */ sqlite3 db = pParse.db; /* The database connection */ int iDb; /* The database to store the trigger in */ Token pName = null; /* The unqualified db name */ DbFixer sFix = new DbFixer(); /* State vector for the DB fixer */ int iTabDb; /* Index of the database holding pTab */ Debug.Assert( pName1 != null ); /* pName1.z might be NULL, but not pName1 itself */ Debug.Assert( pName2 != null ); Debug.Assert( op == TK_INSERT || op == TK_UPDATE || op == TK_DELETE ); Debug.Assert( op > 0 && op < 0xff ); if ( isTemp != 0 ) { /* If TEMP was specified, then the trigger name may not be qualified. */ if ( pName2.n > 0 ) { sqlite3ErrorMsg( pParse, "temporary trigger may not have qualified name" ); goto trigger_cleanup; } iDb = 1; pName = pName1; } else { /* Figure out the db that the the trigger will be created in */ iDb = sqlite3TwoPartName( pParse, pName1, pName2, ref pName ); if ( iDb < 0 ) { goto trigger_cleanup; } } if ( null == pTableName ) //|| db.mallocFailed { goto trigger_cleanup; } /* A long-standing parser bug is that this syntax was allowed: ** ** CREATE TRIGGER attached.demo AFTER INSERT ON attached.tab .... ** ^^^^^^^^ ** ** To maintain backwards compatibility, ignore the database ** name on pTableName if we are reparsing our of SQLITE_MASTER. */ if ( db.init.busy != 0 && iDb != 1 ) { //sqlite3DbFree( db, pTableName.a[0].zDatabase ); pTableName.a[0].zDatabase = null; } /* If the trigger name was unqualified, and the table is a temp table, ** then set iDb to 1 to create the trigger in the temporary database. ** If sqlite3SrcListLookup() returns 0, indicating the table does not ** exist, the error is caught by the block below. */ if ( pTableName == null /*|| db.mallocFailed != 0 */ ) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup( pParse, pTableName ); if ( db.init.busy == 0 && pName2.n == 0 && pTab != null && pTab.pSchema == db.aDb[1].pSchema ) { iDb = 1; } /* Ensure the table name matches database name and that the table exists */ // if ( db.mallocFailed != 0 ) goto trigger_cleanup; Debug.Assert( pTableName.nSrc == 1 ); if ( sqlite3FixInit( sFix, pParse, iDb, "trigger", pName ) != 0 && sqlite3FixSrcList( sFix, pTableName ) != 0 ) { goto trigger_cleanup; } pTab = sqlite3SrcListLookup( pParse, pTableName ); if ( pTab == null ) { /* The table does not exist. */ if ( db.init.iDb == 1 ) { /* Ticket #3810. ** Normally, whenever a table is dropped, all associated triggers are ** dropped too. But if a TEMP trigger is created on a non-TEMP table ** and the table is dropped by a different database connection, the ** trigger is not visible to the database connection that does the ** drop so the trigger cannot be dropped. This results in an ** "orphaned trigger" - a trigger whose associated table is missing. */ db.init.orphanTrigger = 1; } goto trigger_cleanup; } if ( IsVirtual( pTab ) ) { sqlite3ErrorMsg( pParse, "cannot create triggers on virtual tables" ); goto trigger_cleanup; } /* Check that the trigger name is not reserved and that no trigger of the ** specified name exists */ zName = sqlite3NameFromToken( db, pName ); if ( zName == null || SQLITE_OK != sqlite3CheckObjectName( pParse, zName ) ) { goto trigger_cleanup; } Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); if ( sqlite3HashFind( ( db.aDb[iDb].pSchema.trigHash ), zName, sqlite3Strlen30( zName ), (Trigger)null ) != null ) { if ( noErr == 0 ) { sqlite3ErrorMsg( pParse, "trigger %T already exists", pName ); } else { Debug.Assert( 0==db.init.busy ); sqlite3CodeVerifySchema( pParse, iDb ); } goto trigger_cleanup; } /* Do not create a trigger on a system table */ if ( pTab.zName.StartsWith( "sqlite_", System.StringComparison.InvariantCultureIgnoreCase ) ) { sqlite3ErrorMsg( pParse, "cannot create trigger on system table" ); pParse.nErr++; goto trigger_cleanup; } /* INSTEAD of triggers are only for views and views only support INSTEAD ** of triggers. */ if ( pTab.pSelect != null && tr_tm != TK_INSTEAD ) { sqlite3ErrorMsg( pParse, "cannot create %s trigger on view: %S", ( tr_tm == TK_BEFORE ) ? "BEFORE" : "AFTER", pTableName, 0 ); goto trigger_cleanup; } if ( pTab.pSelect == null && tr_tm == TK_INSTEAD ) { sqlite3ErrorMsg( pParse, "cannot create INSTEAD OF" + " trigger on table: %S", pTableName, 0 ); goto trigger_cleanup; } iTabDb = sqlite3SchemaToIndex( db, pTab.pSchema ); #if !SQLITE_OMIT_AUTHORIZATION { int code = SQLITE_CREATE_TRIGGER; string zDb = db.aDb[iTabDb].zName; string zDbTrig = isTemp ? db.aDb[1].zName : zDb; if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER; if( sqlite3AuthCheck(pParse, code, zName, pTab.zName, zDbTrig) ){ goto trigger_cleanup; } if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iTabDb),0,zDb)){ goto trigger_cleanup; } } #endif /* INSTEAD OF triggers can only appear on views and BEFORE triggers ** cannot appear on views. So we might as well translate every ** INSTEAD OF trigger into a BEFORE trigger. It simplifies code ** elsewhere. */ if ( tr_tm == TK_INSTEAD ) { tr_tm = TK_BEFORE; } /* Build the Trigger object */ pTrigger = new Trigger();// (Trigger*)sqlite3DbMallocZero( db, sizeof(Trigger )) if ( pTrigger == null ) goto trigger_cleanup; pTrigger.zName = zName; pTrigger.table = pTableName.a[0].zName;// sqlite3DbStrDup( db, pTableName.a[0].zName ); pTrigger.pSchema = db.aDb[iDb].pSchema; pTrigger.pTabSchema = pTab.pSchema; pTrigger.op = (u8)op; pTrigger.tr_tm = tr_tm == TK_BEFORE ? TRIGGER_BEFORE : TRIGGER_AFTER; pTrigger.pWhen = sqlite3ExprDup( db, pWhen, EXPRDUP_REDUCE ); pTrigger.pColumns = sqlite3IdListDup( db, pColumns ); Debug.Assert( pParse.pNewTrigger == null ); pParse.pNewTrigger = pTrigger; trigger_cleanup: sqlite3DbFree( db, ref zName ); sqlite3SrcListDelete( db, ref pTableName ); sqlite3IdListDelete( db, ref pColumns ); sqlite3ExprDelete( db, ref pWhen ); if ( pParse.pNewTrigger == null ) { sqlite3DeleteTrigger( db, ref pTrigger ); } else { Debug.Assert( pParse.pNewTrigger == pTrigger ); } }
static int sqlite3FixSelect( DbFixer pFix, /* Context of the fixation */ Select pSelect /* The SELECT statement to be fixed to one database */ ) { while ( pSelect != null ) { if ( sqlite3FixExprList( pFix, pSelect.pEList ) != 0 ) { return 1; } if ( sqlite3FixSrcList( pFix, pSelect.pSrc ) != 0 ) { return 1; } if ( sqlite3FixExpr( pFix, pSelect.pWhere ) != 0 ) { return 1; } if ( sqlite3FixExpr( pFix, pSelect.pHaving ) != 0 ) { return 1; } pSelect = pSelect.pPrior; } return 0; }
/* ** This routine is called after all of the trigger actions have been parsed ** in order to complete the process of building the trigger. */ static void sqlite3FinishTrigger( Parse pParse, /* Parser context */ TriggerStep pStepList, /* The triggered program */ Token pAll /* Token that describes the complete CREATE TRIGGER */ ) { Trigger pTrig = pParse.pNewTrigger; /* Trigger being finished */ string zName; /* Name of trigger */ sqlite3 db = pParse.db; /* The database */ DbFixer sFix = new DbFixer(); /* Fixer object */ int iDb; /* Database containing the trigger */ Token nameToken = new Token(); /* Trigger name for error reporting */ pParse.pNewTrigger = null; if ( NEVER( pParse.nErr != 0 ) || pTrig == null ) goto triggerfinish_cleanup; zName = pTrig.zName; iDb = sqlite3SchemaToIndex( pParse.db, pTrig.pSchema ); pTrig.step_list = pStepList; while ( pStepList != null ) { pStepList.pTrig = pTrig; pStepList = pStepList.pNext; } nameToken.z = pTrig.zName; nameToken.n = sqlite3Strlen30( nameToken.z ); if ( sqlite3FixInit( sFix, pParse, iDb, "trigger", nameToken ) != 0 && sqlite3FixTriggerStep( sFix, pTrig.step_list ) != 0 ) { goto triggerfinish_cleanup; } /* if we are not initializing, ** build the sqlite_master entry */ if ( 0 == db.init.busy ) { Vdbe v; string z; /* Make an entry in the sqlite_master table */ v = sqlite3GetVdbe( pParse ); if ( v == null ) goto triggerfinish_cleanup; sqlite3BeginWriteOperation( pParse, 0, iDb ); z = pAll.z.Substring( 0, pAll.n );//sqlite3DbStrNDup( db, (char*)pAll.z, pAll.n ); sqlite3NestedParse( pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", db.aDb[iDb].zName, SCHEMA_TABLE( iDb ), zName, pTrig.table, z ); sqlite3DbFree( db, ref z ); sqlite3ChangeCookie( pParse, iDb ); sqlite3VdbeAddParseSchemaOp( v, iDb, sqlite3MPrintf( db, "type='trigger' AND name='%q'", zName ) ); } if ( db.init.busy != 0 ) { Trigger pLink = pTrig; Hash pHash = db.aDb[iDb].pSchema.trigHash; Debug.Assert( sqlite3SchemaMutexHeld( db, iDb, null ) ); pTrig = sqlite3HashInsert( ref pHash, zName, sqlite3Strlen30( zName ), pTrig ); if ( pTrig != null ) { //db.mallocFailed = 1; } else if ( pLink.pSchema == pLink.pTabSchema ) { Table pTab; int n = sqlite3Strlen30( pLink.table ); pTab = sqlite3HashFind( pLink.pTabSchema.tblHash, pLink.table, n, (Table)null ); Debug.Assert( pTab != null ); pLink.pNext = pTab.pTrigger; pTab.pTrigger = pLink; } } triggerfinish_cleanup: sqlite3DeleteTrigger( db, ref pTrig ); Debug.Assert( pParse.pNewTrigger == null ); sqlite3DeleteTriggerStep( db, ref pStepList ); }
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; }