// Return a dynamicly allocated KeyInfo structure that can be used with OP_OpenRead or OP_OpenWrite to access database index pIdx. // // If successful, a pointer to the new structure is returned. In this case the caller is responsible for calling sqlite3DbFree(db, ) on the returned // pointer. If an error occurs (out of memory or missing collation sequence), NULL is returned and the state of pParse updated to reflect // the error. internal static KeyInfo sqlite3IndexKeyinfo(Parse pParse, Index pIdx) { var nCol = pIdx.nColumn; var db = pParse.db; var pKey = new KeyInfo(); if (pKey != null) { pKey.db = pParse.db; pKey.aSortOrder = new byte[nCol]; pKey.aColl = new CollSeq[nCol]; for (var i = 0; i < nCol; i++) { var zColl = pIdx.azColl[i]; Debug.Assert(zColl != null); pKey.aColl[i] = sqlite3LocateCollSeq(pParse, zColl); pKey.aSortOrder[i] = pIdx.aSortOrder[i]; } pKey.nField = (ushort)nCol; } if (pParse.nErr != 0) { pKey = null; sqlite3DbFree(db, ref pKey); } return pKey; }
internal static void sqlite3HaltConstraint(Parse pParse, int onError, byte[] p4, int p4type) { var v = sqlite3GetVdbe(pParse); if (onError == OE_Abort) sqlite3MayAbort(pParse); sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type); }
// Return the default collation sequence for the expression pExpr. If there is no default collation type, return 0. internal static CollSeq sqlite3ExprCollSeq(Parse pParse, Expr pExpr) { CollSeq pColl = null; var p = pExpr; while (ALWAYS(p)) { pColl = pExpr.pColl; if (pColl != null) break; var op = p.op; if (p.pTab != null && (op == TK.AGG_COLUMN || op == TK.COLUMN || op == TK.REGISTER || op == TK.TRIGGER)) { // op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally a TK_COLUMN but was previously evaluated and cached in a register var j = p.iColumn; if (j >= 0) { var db = pParse.db; var zColl = p.pTab.aCol[j].zColl; pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); pExpr.pColl = pColl; } break; } if (op != TK.CAST && op != TK.UPLUS) break; p = p.pLeft; } if (sqlite3CheckCollSeq(pParse, pColl) != 0) pColl = null; return pColl; }
// Generate VDBE code that will verify the schema cookie and start a read-transaction for all named database files. // // It is important that all schema cookies be verified and all read transactions be started before anything else happens in // the VDBE program. But this routine can be called after much other code has been generated. So here is what we do: // // The first time this routine is called, we code an OP_Goto that will jump to a subroutine at the end of the program. Then we // record every database that needs its schema verified in the pParse.cookieMask field. Later, after all other code has been // generated, the subroutine that does the cookie verifications and starts the transactions will be coded and the OP_Goto P2 value // will be made to point to that subroutine. The generation of the cookie verification subroutine code happens in sqlite3FinishCoding(). // // If iDb<0 then code the OP_Goto only - don't set flag to verify the schema on any databases. This can be used to position the OP_Goto // early in the code, before we know if any database tables will be used. internal static void sqlite3CodeVerifySchema(Parse pParse, int iDb) { var pToplevel = sqlite3ParseToplevel(pParse); if (pToplevel.cookieGoto == 0) { var v = sqlite3GetVdbe(pToplevel); if (v == null) return; // This only happens if there was a prior error pToplevel.cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0) + 1; } if (iDb >= 0) { var db = pToplevel.db; Debug.Assert(iDb < db.nDb); Debug.Assert(db.aDb[iDb].pBt != null || iDb == 1); Debug.Assert(iDb < SQLITE_MAX_ATTACHED + 2); Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null)); yDbMask mask = ((yDbMask)1) << iDb; if ((pToplevel.cookieMask & mask) == 0) { pToplevel.cookieMask |= mask; pToplevel.cookieValue[iDb] = db.aDb[iDb].pSchema.schema_cookie; if (0 == OMIT_TEMPDB && iDb == 1) sqlite3OpenTempDatabase(pToplevel); } } }
// Run the parser and code generator recursively in order to generate code for the SQL statement given onto the end of the pParse context // currently under construction. When the parser is run recursively this way, the final OP_Halt is not appended and other initialization // and finalization steps are omitted because those are handling by the outermost parser. // // Not everything is nestable. This facility is designed to permit INSERT, UPDATE, and DELETE operations against SQLITE_MASTER. Use // care if you decide to try to use this routine for some other purposes. internal static void sqlite3NestedParse(Parse pParse, string zFormat, params object[] ap) { var db = pParse.db; if (pParse.nErr != 0) return; Debug.Assert(pParse.nested < 10); // Nesting should only be of limited depth lock (lock_va_list) { va_start(ap, zFormat); var zSql = sqlite3VMPrintf(db, zFormat, ap); va_end(ref ap); } lock (_nestingLock) { pParse.nested++; pParse.SaveMembers(); pParse.ResetMembers(); string zErrMsg = string.Empty; sqlite3RunParser(pParse, zSql, ref zErrMsg); sqlite3DbFree(db, ref zErrMsg); sqlite3DbFree(db, ref zSql); pParse.RestoreMembers(); pParse.nested--; } }
// This routine is used to check if the UTF-8 string zName is a legal unqualified name for a new schema object (vtable, index, view or // trigger). All names are legal except those that begin with the string "sqlite_" (in upper, lower or mixed case). This portion of the namespace // is reserved for internal use. internal static int sqlite3CheckObjectName(Parse pParse, string zName) { if (0 == pParse.db.init.busy && pParse.nested == 0 && (pParse.db.flags & SQLITE_WriteSchema) == 0 && zName.StartsWith("sqlite_", System.StringComparison.InvariantCultureIgnoreCase)) { sqlite3ErrorMsg(pParse, "object name reserved for internal use: %s", zName); return SQLITE_ERROR; } return SQLITE_OK; }
// Set the collating sequence for expression pExpr to be the collating sequence named by pToken. Return a pointer to the revised expression. // The collating sequence is marked as "explicit" using the EP_ExpCollate flag. An explicit collating sequence will override implicit // collating sequences. internal static Expr sqlite3ExprSetCollByToken(Parse pParse, Expr pExpr, Token pCollName) { var db = pParse.db; var zColl = sqlite3NameFromToken(db, pCollName); // Dequoted name of collation sequence var pColl = sqlite3LocateCollSeq(pParse, zColl); sqlite3ExprSetColl(pExpr, pColl); sqlite3DbFree(db, ref zColl); return pExpr; }
// If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each attached database. Otherwise, invoke it for the database named zDb only. internal static void sqlite3CodeVerifyNamedSchema(Parse pParse, string zDb) { var db = pParse.db; for (var i = 0; i < db.nDb; i++) { var pDb = db.aDb[i]; if (pDb.pBt != null && (null == zDb || 0 == zDb.CompareTo(pDb.zName))) sqlite3CodeVerifySchema(pParse, i); } }
// Recompute all indices of pTab that use the collating sequence pColl. // If pColl == null then recompute all indices of pTab. internal static void reindexTable(Parse pParse, Table pTab, string zColl) { for (var pIndex = pTab.pIndex; pIndex != null; pIndex = pIndex.pNext) if (zColl == null || collationMatch(zColl, pIndex)) { var iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3RefillIndex(pParse, pIndex, -1); } }
// Generate code that will increment the schema cookie. // // The schema cookie is used to determine when the schema for the database changes. After each schema change, the cookie value // changes. When a process first reads the schema it records the cookie. Thereafter, whenever it goes to access the database, // it checks the cookie to make sure the schema has not changed since it was last read. // // This plan is not completely bullet-proof. It is possible for the schema to change multiple times and for the cookie to be // set back to prior value. But schema changes are infrequent and the probability of hitting the same cookie value is only // 1 chance in 2^32. So we're safe enough. internal static void sqlite3ChangeCookie(Parse pParse, int iDb) { var r1 = sqlite3GetTempReg(pParse); var db = pParse.db; var v = pParse.pVdbe; Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null)); sqlite3VdbeAddOp2(v, OP_Integer, db.aDb[iDb].pSchema.schema_cookie + 1, r1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1); sqlite3ReleaseTempReg(pParse, r1); }
// Rollback a transaction internal static void sqlite3RollbackTransaction(Parse pParse) { Debug.Assert(pParse != null); var db = pParse.db; Debug.Assert(db != null); if (sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", null, null) != 0) return; var v = sqlite3GetVdbe(pParse); if (v != null) sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 1); }
// This routine is called when an INITIALLY IMMEDIATE or INITIALLY DEFERRED clause is seen as part of a foreign key definition. The isDeferred // parameter is 1 for INITIALLY DEFERRED and 0 for INITIALLY IMMEDIATE. The behavior of the most recently created foreign key is adjusted // accordingly. internal static void sqlite3DeferForeignKey(Parse pParse, int isDeferred) { #if !SQLITE_OMIT_FOREIGN_KEY Table pTab; FKey pFKey; if ((pTab = pParse.pNewTable) == null || (pFKey = pTab.pFKey) == null) return; Debug.Assert(isDeferred == 0 || isDeferred == 1); pFKey.isDeferred = (byte)isDeferred; #endif }
internal static void sqlite3Reindex(Parse pParse, Token pName1, Token pName2) { var db = pParse.db; var pObjName = new Token(); // Read the database schema. If an error occurs, leave an error message and code in pParse and return NULL. if (SQLITE_OK != sqlite3ReadSchema(pParse)) return; if (pName1 == null) { reindexDatabases(pParse, null); return; } else if (Check.NEVER(pName2 == null) || pName2.z == null || pName2.z.Length == 0) { Debug.Assert(pName1.z != null); var zColl = sqlite3NameFromToken(pParse.db, pName1); if (zColl == null) return; var pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0); if (pColl != null) { reindexDatabases(pParse, zColl); sqlite3DbFree(db, ref zColl); return; } sqlite3DbFree(db, ref zColl); } var iDb = sqlite3TwoPartName(pParse, pName1, pName2, ref pObjName); if (iDb < 0) return; var z = sqlite3NameFromToken(db, pObjName); if (z == null) return; var zDb = db.aDb[iDb].zName; var pTab = sqlite3FindTable(db, z, zDb); if (pTab != null) { reindexTable(pParse, pTab, null); sqlite3DbFree(db, ref z); return; } var pIndex = sqlite3FindIndex(db, z, zDb); sqlite3DbFree(db, ref z); if (pIndex != null) { sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3RefillIndex(pParse, pIndex, -1); return; } sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed"); }
// Generate code that will erase and refill index pIdx. This is used to initialize a newly created index or to recompute the // content of an index in response to a REINDEX command. // // if memRootPage is not negative, it means that the index is newly created. The register specified by memRootPage contains the // root page number of the index. If memRootPage is negative, then the index already exists and must be cleared before being refilled and // the root page number of the index is taken from pIndex.tnum. internal static void sqlite3RefillIndex(Parse pParse, Index pIndex, int memRootPage) { var pTab = pIndex.pTable; var iTab = pParse.nTab++; var iIdx = pParse.nTab++; var db = pParse.db; var iDb = sqlite3SchemaToIndex(db, pIndex.pSchema); if (sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex.zName, 0, db.aDb[iDb].zName)) return; // Require a write-lock on the table to perform this operation sqlite3TableLock(pParse, iDb, pTab.tnum, 1, pTab.zName); var v = sqlite3GetVdbe(pParse); if (v == null) return; int tnum; // Root page of index if (memRootPage >= 0) tnum = memRootPage; else { tnum = pIndex.tnum; sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb); } var pKey = sqlite3IndexKeyinfo(pParse, pIndex); sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb, pKey, P4_KEYINFO_HANDOFF); if (memRootPage >= 0) sqlite3VdbeChangeP5(v, 1); sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead); var addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); var regRecord = sqlite3GetTempReg(pParse); var regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, true); if (pIndex.onError != OE_None) { var regRowid = regIdxKey + pIndex.nColumn; var j2 = sqlite3VdbeCurrentAddr(v) + 2; var pRegKey = regIdxKey; // The registers accessed by the OP_IsUnique opcode were allocated using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey() // call above. Just before that function was freed they were released (made available to the compiler for reuse) using // sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique opcode use the values stored within seems dangerous. However, since // we can be sure that no other temp registers have been allocated since sqlite3ReleaseTempRange() was called, it is safe to do so. sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32); sqlite3HaltConstraint(pParse, OE_Abort, "indexed columns are not unique", P4_STATIC); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); sqlite3ReleaseTempReg(pParse, regRecord); sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1 + 1); sqlite3VdbeJumpHere(v, addr1); sqlite3VdbeAddOp1(v, OP_Close, iTab); sqlite3VdbeAddOp1(v, OP_Close, iIdx); }
// Recompute all indices of all tables in all databases where the indices use the collating sequence pColl. If pColl == null then recompute // all indices everywhere. internal static void reindexDatabases(Parse pParse, string zColl) { var db = pParse.db; Debug.Assert(sqlite3BtreeHoldsAllMutexes(db)); // Needed for schema access for (var iDb = 0; iDb < db.nDb; iDb++) { var pDb = db.aDb[iDb]; Debug.Assert(pDb != null); for (var k = pDb.pSchema.tblHash.first; k != null; k = k.next) { var pTab = (Table)k.data; reindexTable(pParse, pTab, zColl); } } }
internal static void sqlite3Savepoint(Parse pParse, int op, Token pName) { var zName = sqlite3NameFromToken(pParse.db, pName); if (zName != null) { var v = sqlite3GetVdbe(pParse); Debug.Assert(!SAVEPOINT_BEGIN && SAVEPOINT_RELEASE == 1 && SAVEPOINT_ROLLBACK == 2); if (null == v || sqlite3AuthCheck(pParse, SQLITE_SAVEPOINT, az[op], zName, 0)) { sqlite3DbFree(pParse.db, ref zName); return; } sqlite3VdbeAddOp4(v, OP_Savepoint, op, 0, 0, zName, P4_DYNAMIC); } }
// Write code to erase the table with root-page iTable from database iDb. Also write code to modify the sqlite_master table and internal schema // if a root-page of another table is moved by the btree-layer whilst erasing iTable (this can happen with an auto-vacuum database). internal static void destroyRootPage(Parse pParse, int iTable, int iDb) { var v = sqlite3GetVdbe(pParse); var r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); sqlite3MayAbort(pParse); #if !SQLITE_OMIT_AUTOVACUUM // OP_Destroy stores an in integer r1. If this integer is non-zero, then it is the root page number of a table moved to // location iTable. The following code modifies the sqlite_master table to reflect this. // // The "#NNN" in the SQL is a special constant that means whatever value is in register NNN. See grammar rules associated with the TK_REGISTER // token for additional information. sqlite3NestedParse(pParse, "UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d", pParse.db.aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1); #endif sqlite3ReleaseTempReg(pParse, r1); }
// 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; }
// Begin a transaction internal static void sqlite3BeginTransaction(Parse pParse, Parser.TK type) { Debug.Assert(pParse != null); var db = pParse.db; Debug.Assert(db != null); if (sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", null, null) != 0) return; var v = sqlite3GetVdbe(pParse); if (v == null) return; if (type != Parser.TK.DEFERRED) for (var i = 0; i < db.nDb; i++) { sqlite3VdbeAddOp2(v, OP_Transaction, i, type == Parser.TK.EXCLUSIVE ? 2 : 1); sqlite3VdbeUsesBtree(v, i); } sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0); }
// Write VDBE code to erase table pTab and all associated indices on disk. Code to update the sqlite_master tables and internal schema definitions // in case a root-page belonging to another table is moved by the btree layer is also added (this can happen with an auto-vacuum database). internal static void destroyTable(Parse pParse, Table pTab) { #if SQLITE_OMIT_AUTOVACUUM var iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema); destroyRootPage(pParse, pTab.tnum, iDb); for (var pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) destroyRootPage(pParse, pIdx.tnum, iDb); #else // If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM is not defined), then it is important to call OP_Destroy on the // table and index root-pages in order, starting with the numerically largest root-page number. This guarantees that none of the root-pages // to be destroyed is relocated by an earlier OP_Destroy. i.e. if the following were coded: // OP_Destroy 4 0 // ... // OP_Destroy 5 0 // and root page 5 happened to be the largest root-page number in the database, then root page 5 would be moved to page 4 by the // "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit a free-list page. var iTab = pTab.tnum; var iDestroyed = 0; while (true) { var iLargest = 0; if (iDestroyed == 0 || iTab < iDestroyed) iLargest = iTab; for (var pIdx = pTab.pIndex; pIdx != null; pIdx = pIdx.pNext) { var iIdx = pIdx.tnum; Debug.Assert(pIdx.pSchema == pTab.pSchema); if ((iDestroyed == 0 || (iIdx < iDestroyed)) && iIdx > iLargest) iLargest = iIdx; } if (iLargest == 0) return; else { var iDb = sqlite3SchemaToIndex(pParse.db, pTab.pSchema); destroyRootPage(pParse, iLargest, iDb); iDestroyed = iLargest; } } #endif }
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 when a new SQL statement is beginning to be parsed. Initialize the pParse structure as needed. internal static void sqlite3BeginParse(Parse pParse, int explainFlag) { pParse.explain = (byte)explainFlag; pParse.nVar = 0; }
// Make sure the TEMP database is open and available for use. Return the number of errors. Leave any error messages in the pParse structure. internal static int sqlite3OpenTempDatabase(Parse pParse) { var db = pParse.db; if (db.aDb[1].pBt == null && pParse.explain == 0) { Btree pBt = null; var rc = sqlite3BtreeOpen(db.pVfs, null, db, ref pBt, 0, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_TEMP_DB); if (rc != SQLITE_OK) { sqlite3ErrorMsg(pParse, "unable to open a temporary database file for storing temporary tables"); pParse.rc = rc; return 1; } db.aDb[1].pBt = pBt; Debug.Assert(db.aDb[1].pSchema != null); sqlite3BtreeSetPageSize(pBt, db.nextPagesize, -1, 0); } return 0; }
// Generate code for the REINDEX command. // REINDEX -- 1 // REINDEX <collation> -- 2 // REINDEX ?<database>.?<tablename> -- 3 // REINDEX ?<database>.?<indexname> -- 4 // Form 1 causes all indices in all attached databases to be rebuilt. // Form 2 rebuilds all indices in all databases that use the named // collating function. Forms 3 and 4 rebuild the named index or all indices associated with the named table. internal static void sqlite3Reindex(Parse pParse, int null_2, int null_3) { sqlite3Reindex(pParse, null, null); }
/// <summary> /// The following code executes when the parse fails /// </summary> // Here code is inserted which will be executed whenever the parser fails private void ParseFailed() { Parse pParse = this._pParse; if ((this._tracePrompt != null)) { Trace.WriteLine(String.Format("{0}Fail!", this._tracePrompt)); } for ( ; (this._idx >= 0); ) { this.PopParserStack(); } this._pParse = pParse; }
// <summary> // The main parser. // </summary> public virtual void Parse(int major, Token minor, Parse pParse) { if ((this._idx < 0)) { this._idx = 0; this._errors = -1; this._stack[0].stateno = 0; this._stack[0].major = 0; } Minor minorUnion = _zeroMinor; minorUnion.yy0 = minor; bool endOfInput = (major == 0); this._pParse = pParse; if ((this._tracePrompt != null)) { Trace.WriteLine(String.Format("{0}Input {1}", this._tracePrompt, _tokenNames[major])); } for (bool do1 = true; do1; do1 = ((major != NOCODE) && (this._idx >= 0))) { int action = this.FindShiftAction(((byte)(major))); if ((action < STATES)) { Debug.Assert((endOfInput == false)); this.Shift(action, major, minorUnion); this._errors = (this._errors + 1); major = NOCODE; } else { if ((action < 228)) { this.Reduce((action - STATES)); } else { Debug.Assert((action == ERROR_ACTION)); if ((this._tracePrompt != null)) { Trace.WriteLine(String.Format("{0}Syntax Error!", this._tracePrompt)); } if ((this._errors <= 0)) { this.SyntaxError(major, minorUnion); } this._errors = 3; this.Destructor(((byte)(major)), minorUnion); if ((endOfInput == true)) { this.ParseFailed(); } major = NOCODE; } } } }
// This routine is called after a single SQL statement has been parsed and a VDBE program to execute that statement has been // prepared. This routine puts the finishing touches on the VDBE program and resets the pParse structure for the next parse. // // Note that if an error occurred, it might be the case that no VDBE code was generated. static void sqlite3FinishCoding(Parse pParse) { var db = pParse.db; if (pParse.nested != 0) return; if (pParse.nErr != 0) return; // Begin by generating some termination code at the end of the vdbe program var v = sqlite3GetVdbe(pParse); Debug.Assert(0 == pParse.isMultiWrite #if DEBUG || sqlite3VdbeAssertMayAbort(v, pParse.mayAbort) != 0 #endif ); if (v != null) { sqlite3VdbeAddOp0(v, OP_Halt); // The cookie mask contains one bit for each database file open. (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are // set for each database that is used. Generate code to start a transaction on each used database and to verify the schema cookie // on each used database. if (pParse.cookieGoto > 0) { sqlite3VdbeJumpHere(v, pParse.cookieGoto - 1); for (uint iDb = 0, mask = 1; iDb < db.nDb; mask <<= 1, iDb++) { if ((mask & pParse.cookieMask) == 0) continue; sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v, OP_Transaction, iDb, (mask & pParse.writeMask) != 0); if (db.init.busy == 0) { Debug.Assert(sqlite3SchemaMutexHeld(db, iDb, null)); sqlite3VdbeAddOp3(v, OP_VerifyCookie, iDb, pParse.cookieValue[iDb], (int)db.aDb[iDb].pSchema.iGeneration); } } #if !SQLITE_OMIT_VIRTUALTABLE for (var i = 0; i < pParse.nVtabLock; i++) { var vtab = sqlite3GetVTable(db, pParse.apVtabLock[i]); sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); } pParse.nVtabLock = 0; #endif // Once all the cookies have been verified and transactions opened, obtain the required table-locks. This is a no-op unless the // shared-cache feature is enabled. codeTableLocks(pParse); // Initialize any AUTOINCREMENT data structures required. sqlite3AutoincrementBegin(pParse); // Finally, jump back to the beginning of the executable code. sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse.cookieGoto); } } // Get the VDBE program ready for execution if (v != null && Check.ALWAYS(pParse.nErr == 0)) { #if DEBUG var trace = ((db.flags & SQLITE_VdbeTrace) != 0 ? Console.Out : null); sqlite3VdbeTrace(v, trace); #endif Debug.Assert(pParse.iCacheLevel == 0); /* Disables and re-enables match */ // A minimum of one cursor is required if autoincrement is used if (pParse.pAinc != null && pParse.nTab == 0) pParse.nTab = 1; sqlite3VdbeMakeReady(v, pParse); pParse.rc = SQLITE_DONE; pParse.colNamesSet = 0; } else pParse.rc = SQLITE_ERROR; pParse.nTab = 0; pParse.nMem = 0; pParse.nSet = 0; pParse.nVar = 0; pParse.cookieMask = 0; pParse.cookieGoto = 0; }
// 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); }
internal static void sqlite3CreateView(Parse pParse, Token pBegin, Token pName1, Token pName2, Select pSelect, int isTemp, int noErr) { }
// The Table structure pTable is really a VIEW. Fill in the names of the columns of the view in the pTable structure. Return the number // of errors. If an error is seen leave an error message in pParse.zErrMsg. internal static int sqlite3ViewGetColumnNames(Parse pParse, Table pTable) { int nErr = 0; var db = pParse.db; Debug.Assert(pTable != null); #if !SQLITE_OMIT_VIRTUALTABLE if (sqlite3VtabCallConnect(pParse, pTable) != 0) return SQLITE_ERROR; #endif if (IsVirtual(pTable)) return 0; #if !SQLITE_OMIT_VIEW // A positive nCol means the columns names for this view are already known. if (pTable.nCol > 0) return 0; // A negative nCol is a special marker meaning that we are currently trying to compute the column names. If we enter this routine with // a negative nCol, it means two or more views form a loop, like this: // CREATE VIEW one AS SELECT * FROM two; // CREATE VIEW two AS SELECT * FROM one; // Actually, the error above is now caught prior to reaching this point. But the following test is still important as it does come up // in the following: // CREATE TABLE main.ex1(a); // CREATE TEMP VIEW ex1 AS SELECT a FROM ex1; // SELECT * FROM temp.ex1; if (pTable.nCol < 0) { sqlite3ErrorMsg(pParse, "view %s is circularly defined", pTable.zName); return 1; } Debug.Assert(pTable.nCol >= 0); // If we get this far, it means we need to compute the table names. Note that the call to sqlite3ResultSetOfSelect() will expand any // "*" elements in the results set of the view and will assign cursors to the elements of the FROM clause. But we do not want these changes // to be permanent. So the computation is done on a copy of the SELECT statement that defines the view. Debug.Assert(pTable.pSelect != null); var pSel = sqlite3SelectDup(db, pTable.pSelect, 0); if (pSel != null) { var enableLookaside = db.lookaside.bEnabled; var n = pParse.nTab; sqlite3SrcListAssignCursors(pParse, pSel.pSrc); pTable.nCol = -1; db.lookaside.bEnabled = 0; var xAuth = db.xAuth; db.xAuth = 0; var pSelTab = sqlite3ResultSetOfSelect(pParse, pSel); db.xAuth = xAuth; db.lookaside.bEnabled = enableLookaside; pParse.nTab = n; if (pSelTab != null) { Debug.Assert(pTable.aCol == null); pTable.nCol = pSelTab.nCol; pTable.aCol = pSelTab.aCol; pSelTab.nCol = 0; pSelTab.aCol = null; sqlite3DeleteTable(db, ref pSelTab); Debug.Assert(sqlite3SchemaMutexHeld(db, 0, pTable.pSchema)); pTable.pSchema.flags |= DB_UnresetViews; } else { pTable.nCol = 0; nErr++; } sqlite3SelectDelete(db, ref pSel); } else nErr++; #endif return nErr; }