public Backup Next; // Next backup associated with source pager static Btree FindBtree(Context errorCtx, Context ctx, string dbName) { int i = Parse.FindDbName(ctx, dbName); if (i == 1) { RC rc = 0; Parse parse = new Parse(); if (parse == null) { Main.Error(errorCtx, RC.NOMEM, "out of memory"); rc = RC.NOMEM; } else { parse.Ctx = ctx; if (parse.OpenTempDatabase() != 0) { Main.Error(errorCtx, parse.RC, "%s", parse.ErrMsg); rc = RC.ERROR; } C._tagfree(errorCtx, ref parse.ErrMsg); } if (rc != 0) return null; } if (i < 0) { Main.Error(errorCtx, RC.ERROR, "unknown database %s", dbName); return null; } return ctx.DBs[i].Bt; }
public static void Delete(Context ctx, ref Select p) { if (p != null) { ClearSelect(ctx, p); C._tagfree(ctx, ref p); } }
public static RC LoadExtension_(Context ctx, string fileName, string procName, ref string errMsgOut) { if (errMsgOut != null) errMsgOut = null; // Ticket #1863. To avoid a creating security problems for older applications that relink against newer versions of SQLite, the // ability to run load_extension is turned off by default. One must call core_enable_load_extension() to turn on extension // loading. Otherwise you get the following error. if ((ctx.Flags & Context.FLAG.LoadExtension) == 0) { errMsgOut = C._mprintf("not authorized"); return RC.ERROR; } if (procName == null) procName = "sqlite3_extension_init"; VSystem vfs = ctx.Vfs; HANDLE handle = (HANDLE)vfs.DlOpen(fileName); StringBuilder errmsg = new StringBuilder(100); int msgLength = 300; if (handle == IntPtr.Zero) { errMsgOut = string.Empty; C.__snprintf(errmsg, msgLength, "unable to open shared library [%s]", fileName); vfs.DlError(msgLength - 1, errmsg.ToString()); return RC.ERROR; } Func<Context, StringBuilder, core_api_routines, RC> init = (Func<Context, StringBuilder, core_api_routines, RC>)vfs.DlSym(handle, procName); Debugger.Break(); if (init == null) { msgLength += procName.Length; C.__snprintf(errmsg, msgLength, "no entry point [%s] in shared library [%s]", procName, fileName); vfs.DlError(msgLength - 1, errMsgOut = errmsg.ToString()); vfs.DlClose(handle); return RC.ERROR; } else if (init(ctx, errmsg, g_apis) != 0) { errMsgOut = C._mprintf("error during initialization: %s", errmsg.ToString()); C._tagfree(ctx, ref errmsg); vfs.DlClose(handle); return RC.ERROR; } // Append the new shared library handle to the db.aExtension array. object[] handles = new object[ctx.Extensions.length + 1]; if (handles == null) return RC.NOMEM; if (ctx.Extensions.length > 0) Array.Copy(ctx.Extensions.data, handles, ctx.Extensions.length); C._tagfree(ctx, ref ctx.Extensions.data); ctx.Extensions.data = handles; ctx.Extensions[ctx.Extensions.length++] = handle; return RC.OK; }
public static bool SafetyCheckSickOrOk(Context ctx) { MAGIC magic = ctx.Magic; if (magic != MAGIC.SICK && magic != MAGIC.OPEN && magic != MAGIC.BUSY) { C.ASSERTCOVERAGE(SysEx._GlobalStatics.Log != null); LogBadConnection("invalid"); return false; } return true; }
public static RC ApiExit(Context ctx, RC rc) { // If the ctx handle is not NULL, then we must hold the connection handle mutex here. Otherwise the read (and possible write) of db->mallocFailed is unsafe, as is the call to sqlite3Error(). Debug.Assert(ctx == null || MutexEx.Held(ctx.Mutex)); if (ctx != null && (ctx.MallocFailed || rc == RC.IOERR_NOMEM)) { Error(ctx, RC.NOMEM, null); ctx.MallocFailed = false; rc = RC.NOMEM; } return (RC)((int)rc & (ctx != null ? ctx.ErrMask : 0xff)); }
static void ClearSelect(Context ctx, Select p) { Expr.ListDelete(ctx, ref p.EList); Parse.SrcListDelete(ctx, ref p.Src); Expr.Delete(ctx, ref p.Where); Expr.ListDelete(ctx, ref p.GroupBy); Expr.Delete(ctx, ref p.Having); Expr.ListDelete(ctx, ref p.OrderBy); Select.Delete(ctx, ref p.Prior); Expr.Delete(ctx, ref p.Limit); Expr.Delete(ctx, ref p.Offset); }
public static Vdbe Create(Context ctx) { Vdbe p = new Vdbe(); if (p == null) return null; p.Ctx = ctx; if (ctx.Vdbes != null) ctx.Vdbes.Prev = p; p.Next = ctx.Vdbes; p.Prev = null; ctx.Vdbes = p; p.Magic = VDBE_MAGIC_INIT; return p; }
//public static void Error(Context ctx, RC rc, int noString) { Error(ctx, rc, (rc == 0 ? null : string.Empty)); } public static void Error(Context ctx, RC rc, string fmt, params object[] args) { if (ctx != null && (ctx.Err != null || (ctx.Err = Vdbe.ValueNew(ctx)) != null)) { ctx.ErrCode = rc; if (fmt != null) { string z = C._vmtagprintf(ctx, fmt, args); Vdbe.ValueSetStr(ctx.Err, -1, z, TEXTENCODE.UTF8, C.DESTRUCTOR_DYNAMIC); } else Vdbe.ValueSetStr(ctx.Err, 0, null, TEXTENCODE.UTF8, C.DESTRUCTOR_STATIC); } }
public static Backup Init(Context destCtx, string destDbName, Context srcCtx, string srcDbName) { // Lock the source database handle. The destination database handle is not locked in this routine, but it is locked in // sqlite3_backup_step(). The user is required to ensure that no other thread accesses the destination handle for the duration // of the backup operation. Any attempt to use the destination database connection while a backup is in progress may cause // a malfunction or a deadlock. MutexEx.Enter(srcCtx.Mutex); MutexEx.Enter(destCtx.Mutex); Backup p; if (srcCtx == destCtx) { Main.Error(destCtx, RC.ERROR, "source and destination must be distinct"); p = null; } else { // Allocate space for a new sqlite3_backup object... EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a // call to sqlite3_backup_init() and is destroyed by a call to sqlite3_backup_finish(). p = new Backup(); if (p == null) Main.Error(destCtx, RC.NOMEM, null); } // If the allocation succeeded, populate the new object. if (p != null) { p.Src = FindBtree(destCtx, srcCtx, srcDbName); p.Dest = FindBtree(destCtx, destCtx, destDbName); p.DestCtx = destCtx; p.SrcCtx = srcCtx; p.NextId = 1; p.IsAttached = false; if (p.Src == null || p.Dest == null || SetDestPgsz(p) == RC.NOMEM) { // One (or both) of the named databases did not exist or an OOM error was hit. The error has already been written into the // pDestDb handle. All that is left to do here is free the sqlite3_backup structure. //C._free(ref p); p = null; } } if (p != null) p.Src.Backups++; MutexEx.Leave(destCtx.Mutex); MutexEx.Leave(srcCtx.Mutex); return p; }
public static void DeleteTriggerStep(Context ctx, ref TriggerStep triggerStep) { while (triggerStep != null) { TriggerStep tmp = triggerStep; triggerStep = triggerStep.Next; Expr.Delete(ctx, ref tmp.Where); Expr.ListDelete(ctx, ref tmp.ExprList); Select.Delete(ctx, ref tmp.Select); Parse.IdListDelete(ctx, ref tmp.IdList); C._tagfree(ctx, ref tmp); triggerStep = null; //: C# } }
public static bool SafetyCheckOk(Context ctx) { if (ctx == null) { LogBadConnection("NULL"); return false; } MAGIC magic = ctx.Magic; if (magic != MAGIC.OPEN) { if (SafetyCheckSickOrOk(ctx)) { C.ASSERTCOVERAGE(SysEx._GlobalStatics.Log != null); LogBadConnection("unopened"); } return false; } return true; }
public static TriggerStep TriggerDeleteStep(Context ctx, Token tableName, Expr where_) { TriggerStep triggerStep = TriggerStepAllocate(ctx, TK.DELETE, tableName); if (triggerStep != null) { triggerStep.Where = Expr.Dup(ctx, where_, E.EXPRDUP_REDUCE); triggerStep.Orconf = OE.Default; } Expr.Delete(ctx, ref where_); return triggerStep; }
public static TriggerStep TriggerUpdateStep(Context ctx, Token tableName, ExprList list, Expr where, OE orconf) { TriggerStep triggerStep = TriggerStepAllocate(ctx, TK.UPDATE, tableName); if (triggerStep != null) { triggerStep.ExprList = Expr.ListDup(ctx, list, E.EXPRDUP_REDUCE); triggerStep.Where = Expr.Dup(ctx, where, E.EXPRDUP_REDUCE); triggerStep.Orconf = orconf; } Expr.ListDelete(ctx, ref list); Expr.Delete(ctx, ref where); return triggerStep; }
static TriggerPrg CodeRowTrigger(Parse parse, Trigger trigger, Table table, OE orconf) { Parse top = E.Parse_Toplevel(parse); Context ctx = parse.Ctx; // Database handle Debug.Assert(trigger.Name == null || table == TableOfTrigger(trigger)); Debug.Assert(top.V != null); // Allocate the TriggerPrg and SubProgram objects. To ensure that they are freed if an error occurs, link them into the Parse.pTriggerPrg // list of the top-level Parse object sooner rather than later. TriggerPrg prg = new TriggerPrg(); // Value to return //: _tagalloc(ctx, sizeof(TriggerPrg), true); if (prg == null) { return(null); } prg.Next = top.TriggerPrg; top.TriggerPrg = prg; Vdbe.SubProgram program; // Sub-vdbe for trigger program prg.Program = program = new Vdbe.SubProgram(); // sqlite3DbMallocZero( db, sizeof( SubProgram ) ); if (program == null) { return(null); } top.V.LinkSubProgram(program); prg.Trigger = trigger; prg.Orconf = orconf; prg.Colmasks[0] = 0xffffffff; prg.Colmasks[1] = 0xffffffff; // Allocate and populate a new Parse context to use for coding the trigger sub-program. Parse subParse = new Parse(); // Parse context for sub-vdbe //: _scratchalloc(ctx, sizeof(Parse), true); if (subParse == null) { return(null); } NameContext sNC = new NameContext(); // Name context for sub-vdbe sNC.Parse = subParse; subParse.Ctx = ctx; subParse.TriggerTab = table; subParse.Toplevel = top; subParse.AuthContext = trigger.Name; subParse.TriggerOp = trigger.OP; subParse.QueryLoops = parse.QueryLoops; int endTrigger = 0; // Label to jump to if WHEN is false Vdbe v = subParse.GetVdbe(); // Temporary VM if (v != null) { #if DEBUG v.Comment("Start: %s.%s (%s %s%s%s ON %s)", trigger.Name, OnErrorText(orconf), (trigger.TRtm == TRIGGER.BEFORE ? "BEFORE" : "AFTER"), (trigger.OP == TK.UPDATE ? "UPDATE" : string.Empty), (trigger.OP == TK.INSERT ? "INSERT" : string.Empty), (trigger.OP == TK.DELETE ? "DELETE" : string.Empty), table.Name); #endif #if !OMIT_TRACE v.ChangeP4(-1, C._mtagprintf(ctx, "-- TRIGGER %s", trigger.Name), Vdbe.P4T.DYNAMIC); #endif // If one was specified, code the WHEN clause. If it evaluates to false (or NULL) the sub-vdbe is immediately halted by jumping to the // OP_Halt inserted at the end of the program. if (trigger.When != null) { Expr when = Expr.Dup(ctx, trigger.When, 0); // Duplicate of trigger WHEN expression if (ResolveExprNames(sNC, ref when) == RC.OK && !ctx.MallocFailed) { endTrigger = v.MakeLabel(); subParse.IfFalse(when, endTrigger, RC_JUMPIFNULL); } Expr.Delete(ctx, ref when); } // Code the trigger program into the sub-vdbe. CodeTriggerProgram(subParse, trigger.StepList, orconf); // Insert an OP_Halt at the end of the sub-program. if (endTrigger != 0) { v.ResolveLabel(endTrigger); } v.AddOp0(Core.OP.Halt); #if DEBUG v.Comment("End: %s.%s", trigger.Name, OnErrorText(orconf)); #endif TransferParseError(parse, subParse); if (!ctx.MallocFailed) { program.Ops.data = v.TakeOpArray(ref program.Ops.length, ref top.MaxArgs); } program.Mems = subParse.Mems; program.Csrs = subParse.Tabs; program.Token = trigger.GetHashCode(); prg.Colmasks[0] = subParse.Oldmask; prg.Colmasks[1] = subParse.Newmask; Vdbe.Delete(v); } Debug.Assert(subParse.Ainc == null && subParse.ZombieTab == null); Debug.Assert(subParse.TriggerPrg == null && subParse.MaxArgs == 0); C._scratchfree(ctx, ref subParse); return(prg); }
public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, int null_4, Select select, OE orconf) { return(TriggerInsertStep(ctx, tableName, column, null, select, orconf)); }
public static Mem ValueNew(Context ctx) { Mem p = null; p = C._tagalloc(ctx, p); if (p != null) { p.Flags = MEM.Null; p.Type = TYPE.NULL; p.Ctx = ctx; } return p; }
public static void ConnectionBlocked(Context a, Context b) { }
static int CheckSavepointCount(Context db) { int n = 0; for (Savepoint p = db.Savepoints; p != null; p = p.Next) n++; Debug.Assert((db.SavepointsLength + db.IsTransactionSavepoint) == n); return 1; }
public static void FKDelete(Context ctx, Table table) { Debug.Assert(ctx == null || Btree.SchemaMutexHeld(ctx, 0, table.Schema)); FKey next; // Copy of pFKey.pNextFrom for (FKey fkey = table.FKeys; fkey != null; fkey = next) { // Remove the FK from the fkeyHash hash table. //: if (!ctx || ctx->BytesFreed == 0) { if (fkey.PrevTo != null) fkey.PrevTo.NextTo = fkey.NextTo; else { FKey p = fkey.NextTo; string z = (p != null ? fkey.NextTo.To : fkey.To); table.Schema.FKeyHash.Insert(z, z.Length, p); } if (fkey.NextTo != null) fkey.NextTo.PrevTo = fkey.PrevTo; } // EV: R-30323-21917 Each foreign key constraint in SQLite is classified as either immediate or deferred. Debug.Assert(fkey.IsDeferred == false || fkey.IsDeferred == true); // Delete any triggers created to implement actions for this FK. #if !OMIT_TRIGGER FKTriggerDelete(ctx, fkey.Triggers[0]); FKTriggerDelete(ctx, fkey.Triggers[1]); #endif next = fkey.NextFrom; C._tagfree(ctx, ref fkey); } }
static void FKTriggerDelete(Context ctx, Trigger p) { if (p != null) { TriggerStep step = p.StepList; Expr.Delete(ctx, ref step.Where); Expr.ListDelete(ctx, ref step.ExprList); Select.Delete(ctx, ref step.Select); Expr.Delete(ctx, ref p.When); C._tagfree(ctx, ref p); } }
static int CodeTriggerProgram(Parse parse, TriggerStep stepList, OE orconf) { Vdbe v = parse.V; Context ctx = parse.Ctx; Debug.Assert(parse.TriggerTab != null && parse.Toplevel != null); Debug.Assert(stepList != null); Debug.Assert(v != null); for (TriggerStep step = stepList; step != null; step = step.Next) { // Figure out the ON CONFLICT policy that will be used for this step of the trigger program. If the statement that caused this trigger // to fire had an explicit ON CONFLICT, then use it. Otherwise, use the ON CONFLICT policy that was specified as part of the trigger // step statement. Example: // // CREATE TRIGGER AFTER INSERT ON t1 BEGIN; // INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); // END; // // INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy // INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy parse.Orconf = (orconf == OE.Default ? step.Orconf : orconf); switch (step.OP) { case TK.UPDATE: Update(parse, TargetSrcList(parse, step), Expr.ListDup(ctx, step.ExprList, 0), Expr.Dup(ctx, step.Where, 0), parse.Orconf); break; case TK.INSERT: Insert(parse, TargetSrcList(parse, step), Expr.ListDup(ctx, step.ExprList, 0), Select.Dup(ctx, step.Select, 0), Expr.IdListDup(ctx, step.IdList), parse.Orconf); break; case TK.DELETE: DeleteFrom(parse, TargetSrcList(parse, step), Expr.Dup(ctx, step.Where, 0)); break; default: Debug.Assert(step.OP == TK.SELECT); SelectDest sDest = new SelectDest(); Select select = Expr.SelectDup(ctx, step.Select, 0); Select.DestInit(sDest, SRT.Discard, 0); Select.Select_(parse, select, ref sDest); Select.Delete(ctx, ref select); break; } if (step.OP != TK.SELECT) { v.AddOp0(OP.ResetCount); } } return(0); }
public static void AutoLoadExtensions(Context ctx) { if (g_autoext.ExtsLength == 0) return; // Common case: early out without every having to acquire a mutex bool go = true; for (int i = 0; go; i++) { string errmsg = null; #if THREADSAFE MutexEx mutex = MutexEx.Alloc(MutexEx.MUTEX.STATIC_MASTER); #endif MutexEx.Enter(mutex); Func<Context, string, core_api_routines, RC> init; if (i >= g_autoext.ExtsLength) { init = null; go = false; } else init = g_autoext.Exts[i]; MutexEx.Leave(mutex); errmsg = null; RC rc; if (init != null && (rc = init(ctx, errmsg, g_apis)) != 0) { Error(ctx, rc, "automatic extension loading failed: %s", errmsg); go = false; } C._tagfree(ctx, ref errmsg); } }
public static RC EnableLoadExtension(Context ctx, bool onoff) { MutexEx.Enter(ctx.Mutex); if (onoff) ctx.Flags |= Context.FLAG.LoadExtension; else ctx.Flags &= ~Context.FLAG.LoadExtension; MutexEx.Leave(ctx.Mutex); return RC.OK; }
public static void DeleteTrigger(Context ctx, ref Trigger trigger) { if (trigger == null) return; DeleteTriggerStep(ctx, ref trigger.StepList); C._tagfree(ctx, ref trigger.Name); C._tagfree(ctx, ref trigger.Table); Expr.Delete(ctx, ref trigger.When); Expr.IdListDelete(ctx, ref trigger.Columns); C._tagfree(ctx, ref trigger); trigger = null; }
public static void UnlinkAndDeleteTrigger(Context ctx, int db, string name) { Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); Trigger trigger = ctx.DBs[db].Schema.TriggerHash.Insert(name, name.Length, (Trigger)null); if (C._ALWAYS(trigger != null)) { if (trigger.Schema == trigger.TabSchema) { Table table = TableOfTrigger(trigger); //: Trigger** pp; //: for (pp = &table->Triggers; *pp != trigger; pp = &((*pp)->Next)) ; //: *pp = (*pp)->Next; if (table.Triggers == trigger) table.Triggers = trigger.Next; else { Trigger cc = table.Triggers; while (cc != null) { if (cc.Next == trigger) { cc.Next = cc.Next.Next; break; } cc = cc.Next; } Debug.Assert(cc != null); } } DeleteTrigger(ctx, ref trigger); ctx.Flags |= Context.FLAG.InternChanges; } }
public static void BeginTrigger(Parse parse, Token name1, Token name2, TK trTm, TK op, IdList columns, SrcList tableName, Expr when, bool isTemp, int noErr) { Context ctx = parse.Ctx; // The database connection Debug.Assert(name1 != null); // pName1.z might be NULL, but not pName1 itself Debug.Assert(name2 != null); Debug.Assert(op == TK.INSERT || op == TK.UPDATE || op == TK.DELETE); Debug.Assert(op > 0 && op < (TK)0xff); Trigger trigger = null; // The new trigger int db; // The database to store the trigger in Token name = null; // The unqualified db name if (isTemp) { // If TEMP was specified, then the trigger name may not be qualified. if (name2.length > 0) { parse.ErrorMsg("temporary trigger may not have qualified name"); goto trigger_cleanup; } db = 1; name = name1; } else { // Figure out the db that the the trigger will be created in db = parse.TwoPartName(name1, name2, ref name); if (db < 0) { goto trigger_cleanup; } } if (tableName == null || ctx.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 (ctx.Init.Busy && db != 1) { C._tagfree(ctx, ref tableName.Ids[0].Database); tableName.Ids[0].Database = 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 (tableName == null) goto trigger_cleanup; Table table = Delete.SrcListLookup(parse, tableName); // Table that the trigger fires off of if (ctx.Init.Busy == null && name2.length == 0 && table != null && table.Schema == ctx.DBs[1].Schema) { db = 1; } // Ensure the table name matches database name and that the table exists if (ctx.MallocFailed) { goto trigger_cleanup; } Debug.Assert(tableName.Srcs == 1); DbFixer sFix = new DbFixer(); // State vector for the DB fixer if (sFix.FixInit(parse, db, "trigger", name) && sFix.FixSrcList(tableName)) { goto trigger_cleanup; } table = Delete.SrcListLookup(parse, tableName); if (table == null) { // The table does not exist. if (ctx.Init.DB == 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. ctx.Init.OrphanTrigger = true; } goto trigger_cleanup; } if (E.IsVirtual(table)) { parse.ErrorMsg("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 string nameAsString = Parse.NameFromToken(ctx, name); if (nameAsString == null || parse.CheckObjectName(nameAsString) != RC.OK) { goto trigger_cleanup; } Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); if (ctx.DBs[db].Schema.TriggerHash.Find(nameAsString, nameAsString.Length, (Trigger)null) != null) { if (noErr == 0) { parse.ErrorMsg("trigger %T already exists", name); } else { Debug.Assert(!ctx.Init.Busy); parse.CodeVerifySchema(db); } goto trigger_cleanup; } // Do not create a trigger on a system table if (table.Name.StartsWith("sqlite_", StringComparison.InvariantCultureIgnoreCase)) { parse.ErrorMsg("cannot create trigger on system table"); parse.Errs++; goto trigger_cleanup; } // INSTEAD of triggers are only for views and views only support INSTEAD of triggers. if (table.Select != null && trTm != TK.INSTEAD) { parse.ErrorMsg("cannot create %s trigger on view: %S", (trTm == TK.BEFORE ? "BEFORE" : "AFTER"), tableName, 0); goto trigger_cleanup; } if (table.Select == null && trTm == TK.INSTEAD) { parse.ErrorMsg("cannot create INSTEAD OF trigger on table: %S", tableName, 0); goto trigger_cleanup; } #if !OMIT_AUTHORIZATION { int tabDb = Prepare.SchemaToIndex(ctx, table.Schema); // Index of the database holding pTab AUTH code = AUTH.CREATE_TRIGGER; string dbName = ctx.DBs[tabDb].Name; string dbTrigName = (isTemp ? ctx.DBs[1].Name : dbName); if (tabDb == 1 || isTemp) { code = AUTH.CREATE_TEMP_TRIGGER; } if (Auth.Check(parse, code, nameAsString, table.Name, dbTrigName) != 0 || Auth.Check(parse, AUTH.INSERT, E.SCHEMA_TABLE(tabDb), 0, dbName)) { 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 (trTm == TK.INSTEAD) { trTm = TK.BEFORE; } // Build the Trigger object trigger = new Trigger(); //: (Trigger *)_tagalloc(db, sizeof(Trigger), true); if (trigger == null) { goto trigger_cleanup; } trigger.Name = name; trigger.Table = tableName.Ids[0].Name; //: _tagstrdup(ctx, tableName->Ids[0].Name); trigger.Schema = ctx.DBs[db].Schema; trigger.TabSchema = table.Schema; trigger.OP = op; trigger.TRtm = (trTm == TK.BEFORE ? TRIGGER.BEFORE : TRIGGER.AFTER); trigger.When = Expr.Dup(db, when, E.EXPRDUP_REDUCE); trigger.Columns = Expr.IdListDup(ctx, columns); Debug.Assert(parse.NewTrigger == null); parse.NewTrigger = trigger; trigger_cleanup: C._tagfree(ctx, ref name); Expr.SrcListDelete(ctx, ref tableName); Expr.IdListDelete(ctx, ref columns); Expr.Delete(ctx, ref when); if (parse.NewTrigger == null) { DeleteTrigger(ctx, ref trigger); } else { Debug.Assert(parse.NewTrigger == trigger); } }
static int CheckSavepointCount(Context db) { return 1; }
public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, int null_5, OE orconf) { return(TriggerInsertStep(ctx, tableName, column, list, null, orconf)); }
static void EnterAll(Context ctx) { }
public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, int null_5, OE orconf) { return TriggerInsertStep(ctx, tableName, column, list, null, orconf); }
public static RC ValueFromExpr(Context ctx, Expr expr, TEXTENCODE encode, AFF affinity, ref Mem value) { if (expr == null) { value = null; return RC.OK; } TK op = expr.OP; // op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT3. The ifdef here is to enable us to achieve 100% branch test coverage even when SQLITE_ENABLE_STAT3 is omitted. #if ENABLE_STAT3 if (op == TK.REGISTER) op = expr.OP2; #else if (C._NEVER(op == TK.REGISTER)) op = expr.OP2; #endif // Handle negative integers in a single step. This is needed in the case when the value is -9223372036854775808. int negInt = 1; string neg = string.Empty; if (op == TK.UMINUS && (expr.Left.OP == TK.INTEGER || expr.Left.OP == TK.FLOAT)) { expr = expr.Left; op = expr.OP; negInt = -1; neg = "-"; } Mem mem = null; string memAsString = null; if (op == TK.STRING || op == TK.FLOAT || op == TK.INTEGER) { mem = ValueNew(ctx); if (mem == null) goto no_mem; if (E.ExprHasProperty(expr, EP.IntValue)) MemSetInt64(mem, (long)expr.u.I * negInt); else { memAsString = C._mtagprintf(ctx, "%s%s", neg, expr.u.Token); if (memAsString == null) goto no_mem; ValueSetStr(mem, -1, memAsString, TEXTENCODE.UTF8, C.DESTRUCTOR_DYNAMIC); if (op == TK.FLOAT) mem.Type = TYPE.FLOAT; } if ((op == TK.INTEGER || op == TK.FLOAT) && affinity == AFF.NONE) ValueApplyAffinity(mem, AFF.NUMERIC, TEXTENCODE.UTF8); else ValueApplyAffinity(mem, affinity, TEXTENCODE.UTF8); if ((mem.Flags & (MEM.Int | MEM.Real)) != 0) mem.Flags &= ~MEM.Str; if (encode != TEXTENCODE.UTF8) ChangeEncoding(mem, encode); } else if (op == TK.UMINUS) { // This branch happens for multiple negative signs. Ex: -(-5) if (ValueFromExpr(ctx, expr.Left, encode, affinity, ref mem) == RC.OK) { MemNumerify(mem); if (mem.u.I == long.MinValue) { mem.Flags &= MEM.Int; mem.Flags |= MEM.Real; mem.R = (double)int.MaxValue; } else mem.u.I = -mem.u.I; mem.R = -mem.R; ValueApplyAffinity(mem, affinity, encode); } } else if (op == TK.NULL) { mem = ValueNew(ctx); if (mem == null) goto no_mem; } #if !OMIT_BLOB_LITERAL else if (op == TK.BLOB) { Debug.Assert(expr.u.Token[0] == 'x' || expr.u.Token[0] == 'X'); Debug.Assert(expr.u.Token[1] == '\''); mem = ValueNew(ctx); if (mem == null) goto no_mem; memAsString = expr.u.Token.Substring(2); int memAsStringLength = memAsString.Length - 1; Debug.Assert(memAsString[memAsStringLength] == '\''); byte[] blob = C._taghextoblob(ctx, memAsString, memAsStringLength); MemSetStr(mem, Encoding.UTF8.GetString(blob, 0, blob.Length), memAsStringLength / 2, 0, C.DESTRUCTOR_DYNAMIC); } #endif if (mem != null) MemStoreType(mem); value = mem; return RC.OK; no_mem: ctx.MallocFailed = true; C._tagfree(ctx, ref memAsString); ValueFree(ref mem); value = null; return RC.NOMEM; }
public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, int null_4, Select select, OE orconf) { return TriggerInsertStep(ctx, tableName, column, null, select, orconf); }
public static TriggerStep SelectStep(Context ctx, Select select) { TriggerStep triggerStep = new TriggerStep(); //: _tagalloc(ctx, sizeof(TriggerStep), true); if (triggerStep == null) { Select.Delete(ctx, ref select); return null; } triggerStep.OP = TK.SELECT; triggerStep.Select = select; triggerStep.Orconf = OE.Default; return triggerStep; }
public static TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, Select select, OE orconf) { Debug.Assert(list == null || select == null); Debug.Assert(list != null || select != null || ctx.MallocFailed); TriggerStep triggerStep = TriggerStepAllocate(ctx, TK.INSERT, tableName); if (triggerStep != null) { triggerStep.Select = Select.Dup(ctx, select, E.EXPRDUP_REDUCE); triggerStep.IdList = column; triggerStep.ExprList = Expr.ListDup(ctx, list, E.EXPRDUP_REDUCE); triggerStep.Orconf = orconf; } else Expr.IdListDelete(ctx, ref column); Expr.ListDelete(ctx, ref list); Select.Delete(ctx, ref select); return triggerStep; }
static TriggerStep TriggerStepAllocate(Context ctx, TK op, Token name) { TriggerStep triggerStep = new TriggerStep(); //: _tagalloc(ctx, sizeof(TriggerStep) + name->length, true); if (triggerStep != null) { string z = name.data; triggerStep.Target.data = z; triggerStep.Target.length = name.length; triggerStep.OP = op; } return triggerStep; }
public static void FinishTrigger(Parse parse, TriggerStep stepList, Token all) { Trigger trig = parse.NewTrigger; // Trigger being finished Context ctx = parse.Ctx; // The database Token nameToken = new Token(); // Trigger name for error reporting parse.NewTrigger = null; if (C._NEVER(parse.Errs != 0) || trig == null) { goto triggerfinish_cleanup; } string name = trig.Name; // Name of trigger int db = Prepare.SchemaToIndex(parse.Ctx, trig.Schema); // Database containing the trigger trig.StepList = stepList; while (stepList != null) { stepList.Trig = trig; stepList = stepList.Next; } nameToken.data = trig.Name; nameToken.length = (uint)nameToken.data.Length; DbFixer sFix = new DbFixer(); // Fixer object if (sFix.FixInit(parse, db, "trigger", nameToken) && sFix.FixTriggerStep(trig.StepList)) { goto triggerfinish_cleanup; } // if we are not initializing, build the sqlite_master entry if (ctx.Init.Busy) { // Make an entry in the sqlite_master table Vdbe v = parse.GetVdbe(); if (v == null) { goto triggerfinish_cleanup; } parse.BeginWriteOperation(0, db); string z = all.data.Substring(0, (int)all.length); //: _tagstrndup(ctx, (char *)all->data, all->length); parse.NestedParse("INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", ctx.DBs[db].Name, E.SCHEMA_TABLE(db), name, trig.Table, z); C._tagfree(ctx, ref z); parse.ChangeCookie(db); v.AddParseSchemaOp(db, C._mtagprintf(ctx, "type='trigger' AND name='%q'", name)); } if (!ctx.Init.Busy) { Trigger link = trig; Debug.Assert(Btree.SchemaMutexHeld(ctx, db, null)); trig = ctx.DBs[db].Schema.TriggerHash.Insert(name, name.Length, trig); if (trig != null) { ctx.MallocFailed = true; } else if (link.Schema == link.TabSchema) { int tableLength = link.Table.Length; Table table = (Table)link.TabSchema.TableHash.Find(link.Table, tableLength, (Table)null); Debug.Assert(table != null); link.Next = table.Triggers; table.Triggers = link; } } triggerfinish_cleanup: DeleteTrigger(ctx, ref trig); Debug.Assert(parse.NewTrigger == null); DeleteTriggerStep(ctx, ref stepList); }