public static int GenerateIndexKey(Parse parse, Index index, int curId, int regOut, bool doMakeRec) { Vdbe v = parse.V; Table table = index.Table; int cols = index.Columns.length; int regBase = Expr.GetTempRange(parse, cols + 1); v.AddOp2(OP.Rowid, curId, regBase + cols); for (int j = 0; j < cols; j++) { int idx = index.Columns[j]; if (idx == table.PKey) v.AddOp2(OP.SCopy, regBase + cols, regBase + j); else { v.AddOp3(OP.Column, curId, idx, regBase + j); v.ColumnDefault(table, idx, -1); } } if (doMakeRec) { string affName = (table.Select != null || E.CtxOptimizationDisabled(parse.Ctx, SQLITE.IdxRealAsInt) ? null : sqlite3IndexAffinityStr(v, index)); v.AddOp3(OP.MakeRecord, regBase, cols + 1, regOut); v.ChangeP4(-1, affName, Vdbe.P4T.TRANSIENT); } Expr.ReleaseTempRange(parse, regBase, cols + 1); return regBase; }
public static void GenerateRowDelete(Parse parse, Table table, int curId, int rowid, int count, Trigger trigger, OE onconf) { // Vdbe is guaranteed to have been allocated by this stage. Vdbe v = parse.V; Debug.Assert(v != null); // Seek cursor iCur to the row to delete. If this row no longer exists (this can happen if a trigger program has already deleted it), do // not attempt to delete it or fire any DELETE triggers. int label = v.MakeLabel(); // Label resolved to end of generated code v.AddOp3(OP.NotExists, curId, label, rowid); // If there are any triggers to fire, allocate a range of registers to use for the old.* references in the triggers. if (FKey.FkRequired(parse, table, null, 0) != 0 || trigger != null) { // TODO: Could use temporary registers here. Also could attempt to avoid copying the contents of the rowid register. uint mask = sqlite3TriggerColmask(parse, trigger, null, 0, TRIGGER.BEFORE | TRIGGER.AFTER, table, onconf); // Mask of OLD.* columns in use mask |= sqlite3FkOldmask(parse, table); int oldId = parse.Mems + 1; // First register in OLD.* array parse.Mems += (1 + table.Cols.length); // Populate the OLD.* pseudo-table register array. These values will be used by any BEFORE and AFTER triggers that exist. v.AddOp2(OP.Copy, rowid, oldId); for (int col = 0; col < table.Cols.length; col++) // Iterator used while populating OLD.* if (mask == 0xffffffff || (mask & (1 << col)) != 0) Expr.CodeGetColumnOfTable(v, table, curId, col, oldId + col + 1); // Invoke BEFORE DELETE trigger programs. sqlite3CodeRowTrigger(parse, trigger, TK.DELETE, null, TRIGGER.BEFORE, table, oldId, onconf, label); // Seek the cursor to the row to be deleted again. It may be that the BEFORE triggers coded above have already removed the row // being deleted. Do not attempt to delete the row a second time, and do not fire AFTER triggers. v.AddOp3(OP.NotExists, curId, label, rowid); // Do FK processing. This call checks that any FK constraints that refer to this table (i.e. constraints attached to other tables) are not violated by deleting this row. FKey.FkCheck(parse, table, oldId, 0); } // Delete the index and table entries. Skip this step if table is really a view (in which case the only effect of the DELETE statement is to fire the INSTEAD OF triggers). if (table.Select == null) { GenerateRowIndexDelete(parse, table, curId, null); v.AddOp2(OP.Delete, curId, (count != 0 ? (int)OPFLAG.NCHANGE : 0)); if (count != 0) v.ChangeP4(-1, table.Name, Vdbe.P4T.TRANSIENT); } // Do any ON CASCADE, SET NULL or SET DEFAULT operations required to handle rows (possibly in other tables) that refer via a foreign key to the row just deleted. FKey.FkActions(parse, table, null, oldId); // Invoke AFTER DELETE trigger programs. sqlite3CodeRowTrigger(parse, trigger, TK.DELETE, null, TRIGGER.AFTER, table, oldId, onconf, label); // Jump here if the row had already been deleted before any BEFORE trigger programs were invoked. Or if a trigger program throws a RAISE(IGNORE) exception. v.ResolveLabel(label); }
static void OpenStatTable(Parse parse, int db, int statCur, string where_, string whereType) { int[] roots = new int[] { 0, 0 }; byte[] createTbls = new byte[] { 0, 0 }; Context ctx = parse.Ctx; Vdbe v = parse.GetVdbe(); if (v == null) { return; } Debug.Assert(Btree.HoldsAllMutexes(ctx)); Debug.Assert(v.Ctx == ctx); Context.DB dbObj = ctx.DBs[db]; for (int i = 0; i < _tables.Length; i++) { string tableName = _tables[i].Name; Table stat; if ((stat = Parse.FindTable(ctx, tableName, dbObj.Name)) == null) { // The sqlite_stat[12] table does not exist. Create it. Note that a side-effect of the CREATE TABLE statement is to leave the rootpage // of the new table in register pParse.regRoot. This is important because the OpenWrite opcode below will be needing it. parse.NestedParse("CREATE TABLE %Q.%s(%s)", dbObj.Name, tableName, _tables[i].Cols); roots[i] = parse.RegRoot; createTbls[i] = Vdbe::OPFLAG_P2ISREG; } else { // The table already exists. If zWhere is not NULL, delete all entries associated with the table zWhere. If zWhere is NULL, delete the // entire contents of the table. roots[i] = stat.Id; sqlite3TableLock(parse, db, roots[i], 1, tableName); if (where_ == null) { parse.NestedParse("DELETE FROM %Q.%s WHERE %s=%Q", dbObj.Name, tableName, whereType, where_); } else { v.AddOp2(OP.Clear, roots[i], db); // The sqlite_stat[12] table already exists. Delete all rows. } } } // Open the sqlite_stat[12] tables for writing. for (int i = 0; i < _tables.Length; i++) { v.AddOp3(OP.OpenWrite, statCur + i, roots[i], db); v.ChangeP4(-1, 3, Vdbe.P4T.INT32); v.ChangeP5(createTbls[i]); } }
static void CodeAttach(Parse parse, AUTH type, FuncDef func, Expr authArg, Expr filename, Expr dbName, Expr key) { Context ctx = parse.Ctx; NameContext sName; sName = new NameContext(); sName.Parse = parse; if (ResolveAttachExpr(sName, filename) != RC.OK || ResolveAttachExpr(sName, dbName) != RC.OK || ResolveAttachExpr(sName, key) != RC.OK) { parse.Errs++; goto attach_end; } #if !OMIT_AUTHORIZATION if (authArg != null) { string authArgToken = (authArg.OP == TK.STRING ? authArg.u.Token : null); ARC arc = Auth.Check(parse, type, authArgToken, null, null); if (arc != ARC.OK) { goto attach_end; } } #endif Vdbe v = parse.GetVdbe(); int regArgs = Expr.GetTempRange(parse, 4); Expr.Code(parse, filename, regArgs); Expr.Code(parse, dbName, regArgs + 1); Expr.Code(parse, key, regArgs + 2); Debug.Assert(v != null || ctx.MallocFailed); if (v != null) { v.AddOp3(OP.Function, 0, regArgs + 3 - func.Args, regArgs + 3); Debug.Assert(func.Args == -1 || (func.Args & 0xff) == func.Args); v.ChangeP5((byte)(func.Args)); v.ChangeP4(-1, func, Vdbe.P4T.FUNCDEF); // Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this statement only). For DETACH, set it to false (expire all existing statements). v.AddOp1(OP.Expire, (type == AUTH.ATTACH ? 1 : 0)); } attach_end: Expr.Delete(ctx, ref filename); Expr.Delete(ctx, ref dbName); Expr.Delete(ctx, ref key); }
public static void sqlite3ColumnDefault(Vdbe v, Table table, int i, int regId) { Debug.Assert(table != null); if (table.Select == null) { TEXTENCODE encode = Context.CTXENCODE(v.Ctx); Column col = table.Cols[i]; v.VdbeComment("%s.%s", table.Name, col.Name); Debug.Assert(i < table.Cols.length); Mem value = new Mem(); sqlite3ValueFromExpr(v.Ctx, col.Dflt, encode, col.Affinity, ref value); if (value != null) { v.ChangeP4(-1, value, Vdbe.P4T.MEM); } #if !OMIT_FLOATING_POINT if (regId >= 0 && table.Cols[i].Affinity == AFF.REAL) { v.AddOp1(OP.RealAffinity, regId); } #endif } }