public Token GetToken(TokenType type) { Token t = new Token(this.StartPos, this.EndPos); t.Type = type; return t; }
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); }
public Scanner() { Regex regex; Patterns = new Dictionary<TokenType, Regex>(); Tokens = new List<TokenType>(); LookAheadToken = null; Skipped = new List<Token>(); SkipList = new List<TokenType>(); SkipList.Add(TokenType.WHITESPACE); SkipList.Add(TokenType.HASCOMMENT); SkipList.Add(TokenType.PYTHONCOMMENT); regex = new Regex(@"[A-Za-z]", RegexOptions.Compiled); Patterns.Add(TokenType.CHAR, regex); Tokens.Add(TokenType.CHAR); regex = new Regex(@"\(", RegexOptions.Compiled); Patterns.Add(TokenType.BROPEN, regex); Tokens.Add(TokenType.BROPEN); regex = new Regex(@"\)", RegexOptions.Compiled); Patterns.Add(TokenType.BRCLOSE, regex); Tokens.Add(TokenType.BRCLOSE); regex = new Regex(@"\{", RegexOptions.Compiled); Patterns.Add(TokenType.COOPEN, regex); Tokens.Add(TokenType.COOPEN); regex = new Regex(@"\}", RegexOptions.Compiled); Patterns.Add(TokenType.COCLOSE, regex); Tokens.Add(TokenType.COCLOSE); regex = new Regex(@"^$", RegexOptions.Compiled); Patterns.Add(TokenType.EOF, regex); Tokens.Add(TokenType.EOF); regex = new Regex(@";", RegexOptions.Compiled); Patterns.Add(TokenType.SEMICOLON, regex); Tokens.Add(TokenType.SEMICOLON); regex = new Regex(@",", RegexOptions.Compiled); Patterns.Add(TokenType.COMMA, regex); Tokens.Add(TokenType.COMMA); regex = new Regex(@"\\", RegexOptions.Compiled); Patterns.Add(TokenType.LAMBDAPREFIX, regex); Tokens.Add(TokenType.LAMBDAPREFIX); regex = new Regex(@"\.", RegexOptions.Compiled); Patterns.Add(TokenType.DOT, regex); Tokens.Add(TokenType.DOT); regex = new Regex(@"if", RegexOptions.Compiled); Patterns.Add(TokenType.IF, regex); Tokens.Add(TokenType.IF); regex = new Regex(@"then", RegexOptions.Compiled); Patterns.Add(TokenType.THEN, regex); Tokens.Add(TokenType.THEN); regex = new Regex(@"else", RegexOptions.Compiled); Patterns.Add(TokenType.ELSE, regex); Tokens.Add(TokenType.ELSE); regex = new Regex(@"[A-Za-z]+", RegexOptions.Compiled); Patterns.Add(TokenType.STRING, regex); Tokens.Add(TokenType.STRING); regex = new Regex(@"[0-9]+", RegexOptions.Compiled); Patterns.Add(TokenType.DIGIT, regex); Tokens.Add(TokenType.DIGIT); regex = new Regex(@"[0-9]\.([0-9])+", RegexOptions.Compiled); Patterns.Add(TokenType.DOUBLE, regex); Tokens.Add(TokenType.DOUBLE); regex = new Regex(@"var", RegexOptions.Compiled); Patterns.Add(TokenType.LETPREFIX, regex); Tokens.Add(TokenType.LETPREFIX); regex = new Regex(@"set", RegexOptions.Compiled); Patterns.Add(TokenType.SETPREFIX, regex); Tokens.Add(TokenType.SETPREFIX); regex = new Regex(@"=", RegexOptions.Compiled); Patterns.Add(TokenType.EQUAL, regex); Tokens.Add(TokenType.EQUAL); regex = new Regex(@"[A-Za-z=]([A-Za-z0-9=_\$])*", RegexOptions.Compiled); Patterns.Add(TokenType.DEFINITION, regex); Tokens.Add(TokenType.DEFINITION); regex = new Regex(@"\^", RegexOptions.Compiled); Patterns.Add(TokenType.TIMES, regex); Tokens.Add(TokenType.TIMES); regex = new Regex(@"(\+|-)", RegexOptions.Compiled); Patterns.Add(TokenType.PLUSMINUS, regex); Tokens.Add(TokenType.PLUSMINUS); regex = new Regex(@"\*|/|%", RegexOptions.Compiled); Patterns.Add(TokenType.MULTDIV, regex); Tokens.Add(TokenType.MULTDIV); regex = new Regex(@"\`", RegexOptions.Compiled); Patterns.Add(TokenType.QUTA, regex); Tokens.Add(TokenType.QUTA); regex = new Regex(@"defun", RegexOptions.Compiled); Patterns.Add(TokenType.DEFUN, regex); Tokens.Add(TokenType.DEFUN); regex = new Regex("\".*\"", RegexOptions.Compiled); Patterns.Add(TokenType.STRINGVAL, regex); Tokens.Add(TokenType.STRINGVAL); regex = new Regex(@"[\s\r\n\t]+", RegexOptions.Compiled); Patterns.Add(TokenType.WHITESPACE, regex); Tokens.Add(TokenType.WHITESPACE); regex = new Regex(@"--.*", RegexOptions.Compiled); Patterns.Add(TokenType.HASCOMMENT, regex); Tokens.Add(TokenType.HASCOMMENT); regex = new Regex(@"#.*", RegexOptions.Compiled); Patterns.Add(TokenType.PYTHONCOMMENT, regex); Tokens.Add(TokenType.PYTHONCOMMENT); }
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 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; }
/// <summary> /// executes a lookahead of the next token /// and will advance the scan on the input string /// </summary> /// <returns></returns> public Token Scan(params TokenType[] expectedtokens) { Token tok = LookAhead(expectedtokens); // temporarely retrieve the lookahead LookAheadToken = null; // reset lookahead token, so scanning will continue StartPos = tok.EndPos; EndPos = tok.EndPos; // set the tokenizer to the new scan position return tok; }
public static JT JoinType(Parse parse, Token a, Token b, int null_4) { return JoinType(parse, a, b, null); }
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 TriggerStep TriggerInsertStep(Context ctx, Token tableName, IdList column, ExprList list, int null_5, OE orconf) { return TriggerInsertStep(ctx, tableName, column, list, null, orconf); }
static Trigger FKActionTrigger(Parse parse, Table table, FKey fkey, ExprList changes) { Context ctx = parse.Ctx; // Database handle int actionId = (changes != null ? 1 : 0); // 1 for UPDATE, 0 for DELETE OE action = fkey.Actions[actionId]; // One of OE_None, OE_Cascade etc. Trigger trigger = fkey.Triggers[actionId]; // Trigger definition to return if (action != OE.None && trigger == null) { Index index = null; // Parent key index for this FK int[] cols = null; // child table cols . parent key cols if (LocateFkeyIndex(parse, table, fkey, out index, out cols) != 0) return null; Debug.Assert(cols != null || fkey.Cols.length == 1); Expr where_ = null; // WHERE clause of trigger step Expr when = null; // WHEN clause for the trigger ExprList list = null; // Changes list if ON UPDATE CASCADE for (int i = 0; i < fkey.Cols.length; i++) { Token oldToken = new Token("old", 3); // Literal "old" token Token newToken = new Token("new", 3); // Literal "new" token int fromColId = (cols != null ? cols[i] : fkey.Cols[0].From); // Idx of column in child table Debug.Assert(fromColId >= 0); Token fromCol = new Token(); // Name of column in child table Token toCol = new Token(); // Name of column in parent table toCol.data = (index != null ? table.Cols[index.Columns[i]].Name : "oid"); fromCol.data = fkey.From.Cols[fromColId].Name; toCol.length = (uint)toCol.data.Length; fromCol.length = (uint)fromCol.data.Length; // Create the expression "OLD.zToCol = zFromCol". It is important that the "OLD.zToCol" term is on the LHS of the = operator, so // that the affinity and collation sequence associated with the parent table are used for the comparison. Expr eq = Expr.PExpr(parse, TK.EQ, Expr.PExpr(parse, TK.DOT, Expr.PExpr(parse, TK.ID, null, null, oldToken), Expr.PExpr(parse, TK.ID, null, null, toCol) , 0), Expr.PExpr(parse, TK.ID, null, null, fromCol) , 0); // tFromCol = OLD.tToCol where_ = Expr.And(ctx, where_, eq); // For ON UPDATE, construct the next term of the WHEN clause. The final WHEN clause will be like this: // // WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) if (changes != null) { eq = Expr.PExpr(parse, TK.IS, Expr.PExpr(parse, TK.DOT, Expr.PExpr(parse, TK.ID, null, null, oldToken), Expr.PExpr(parse, TK.ID, null, null, toCol), 0), Expr.PExpr(parse, TK.DOT, Expr.PExpr(parse, TK.ID, null, null, newToken), Expr.PExpr(parse, TK.ID, null, null, toCol), 0), 0); when = Expr.And(ctx, when, eq); } if (action != OE.Restrict && (action != OE.Cascade || changes != null)) { Expr newExpr; if (action == OE.Cascade) newExpr = Expr.PExpr(parse, TK.DOT, Expr.PExpr(parse, TK.ID, null, null, newToken), Expr.PExpr(parse, TK.ID, null, null, toCol) , 0); else if (action == OE.SetDflt) { Expr dfltExpr = fkey.From.Cols[fromColId].Dflt; if (dfltExpr != null) newExpr = Expr.Dup(ctx, dfltExpr, 0); else newExpr = Expr.PExpr(parse, TK.NULL, 0, 0, 0); } else newExpr = Expr.PExpr(parse, TK.NULL, 0, 0, 0); list = Expr.ListAppend(parse, list, newExpr); Expr.ListSetName(parse, list, fromCol, 0); } } C._tagfree(ctx, ref cols); string fromName = fkey.From.Name; // Name of child table int fromNameLength = fromName.Length; // Length in bytes of zFrom Select select = null; // If RESTRICT, "SELECT RAISE(...)" if (action == OE.Restrict) { Token from = new Token(); from.data = fromName; from.length = fromNameLength; Expr raise = Expr.Expr(ctx, TK.RAISE, "foreign key constraint failed"); if (raise != null) raise.Affinity = OE.Abort; select = Select.New(parse, Expr.ListAppend(parse, 0, raise), SrcListAppend(ctx, 0, from, null), where_, null, null, null, 0, null, null); where_ = null; } // Disable lookaside memory allocation bool enableLookaside = ctx.Lookaside.Enabled; // Copy of ctx->lookaside.bEnabled ctx.Lookaside.Enabled = false; trigger = new Trigger(); //: trigger = (Trigger *)_tagalloc(ctx, //: sizeof(Trigger) + // Trigger //: sizeof(TriggerStep) + // Single step in trigger program //: fromNameLength + 1 // Space for pStep->target.z //: , true); TriggerStep step = null; // First (only) step of trigger program if (trigger != null) { step = trigger.StepList = new TriggerStep(); //: (TriggerStep)trigger[1]; step.Target.data = fromName; //: (char *)&step[1]; step.Target.length = fromNameLength; //: _memcpy((const char *)step->Target.data, fromName, fromNameLength); step.Where = Expr.Dup(ctx, where_, EXPRDUP_REDUCE); step.ExprList = Expr.ListDup(ctx, list, EXPRDUP_REDUCE); step.Select = Select.Dup(ctx, select, EXPRDUP_REDUCE); if (when != null) { when = Expr.PExpr(parse, TK.NOT, when, 0, 0); trigger.When = Expr.Dup(ctx, when, EXPRDUP_REDUCE); } } // Re-enable the lookaside buffer, if it was disabled earlier. ctx.Lookaside.Enabled = enableLookaside; Expr.Delete(ctx, ref where_); Expr.Delete(ctx, ref when); Expr.ListDelete(ctx, ref list); Select.Delete(ctx, ref select); if (ctx.MallocFailed) { FKTriggerDelete(ctx, trigger); return null; } switch (action) { case OE.Restrict: step.OP = TK.SELECT; break; case OE.Cascade: if (changes == null) { step.OP = TK.DELETE; break; } goto default; default: step.OP = TK.UPDATE; break; } step.Trigger = trigger; trigger.Schema = table.Schema; trigger.TabSchema = table.Schema; fkey.Triggers[actionId] = trigger; trigger.OP = (TK)(changes != null ? TK.UPDATE : TK.DELETE); } return trigger; }
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); }
public bool FixInit(Parse parse, int db, string type, Token name) { if (C._NEVER(db < 0) || db == 1) return false; Context ctx = parse.Ctx; Debug.Assert(ctx.DBs.length > db); Parse = parse; DB = ctx.DBs[db].Name; Schema = ctx.DBs[db].Schema; Type = type; Name = name; return true; }
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); } }
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 void Init(string input) { this.Input = input; StartPos = 0; EndPos = 0; CurrentLine = 0; CurrentColumn = 0; CurrentPosition = 0; LookAheadToken = null; }
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); }
/// <summary> /// returns token with longest best match /// </summary> /// <returns></returns> public Token LookAhead(params TokenType[] expectedtokens) { int i; int startpos = StartPos; Token tok = null; List<TokenType> scantokens; // this prevents double scanning and matching // increased performance if (LookAheadToken != null && LookAheadToken.Type != TokenType._UNDETERMINED_ && LookAheadToken.Type != TokenType._NONE_) return LookAheadToken; // if no scantokens specified, then scan for all of them (= backward compatible) if (expectedtokens.Length == 0) scantokens = Tokens; else { scantokens = new List<TokenType>(expectedtokens); scantokens.AddRange(SkipList); } do { int len = -1; TokenType index = (TokenType)int.MaxValue; string input = Input.Substring(startpos); tok = new Token(startpos, EndPos); for (i = 0; i < scantokens.Count; i++) { Regex r = Patterns[scantokens[i]]; Match m = r.Match(input); if (m.Success && m.Index == 0 && ((m.Length > len) || (scantokens[i] < index && m.Length == len ))) { len = m.Length; index = scantokens[i]; } } if (index >= 0 && len >= 0) { tok.EndPos = startpos + len; tok.Text = Input.Substring(tok.StartPos, len); tok.Type = index; } else if (tok.StartPos < tok.EndPos - 1) { tok.Text = Input.Substring(tok.StartPos, 1); } if (SkipList.Contains(tok.Type)) { startpos = tok.EndPos; Skipped.Add(tok); } else { // only assign to non-skipped tokens tok.Skipped = Skipped; // assign prior skips to this token Skipped = new List<Token>(); //reset skips } } while (SkipList.Contains(tok.Type)); LookAheadToken = tok; return tok; }
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; }
public void UpdateRange(Token token) { if (token.StartPos < startpos) startpos = token.StartPos; if (token.EndPos > endpos) endpos = token.EndPos; }
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; }
public static JT JoinType(Parse parse, Token a, Token b, Token c) { Token[] alls = new Token[3]; alls[0] = a; alls[1] = b; alls[2] = c; JT jointype = 0; for (int i = 0; i < 3 && alls[i] != null; i++) { Token p = alls[i]; int j; for (j = 0; j < _keywords.Length; j++) { if (p.length == _keywords[j].Chars && p.data.StartsWith(_keyTexts.Substring(_keywords[j].I, _keywords[j].Chars), StringComparison.OrdinalIgnoreCase)) { jointype |= _keywords[j].Code; break; } } C.ASSERTCOVERAGE(j == 0 || j == 1 || j == 2 || j == 3 || j == 4 || j == 5 || j == 6); if (j >= _keywords.Length) { jointype |= JT.ERROR; break; } } if ((jointype & (JT.INNER | JT.OUTER)) == (JT.INNER | JT.OUTER) || (jointype & JT.ERROR) != 0) { string sp = " "; Debug.Assert(b != null); if (c == null) sp = ""; parse.ErrorMsg("unknown or unsupported join type: %T %T%s%T", a, b, sp, c); jointype = JT.INNER; } else if ((jointype & JT.OUTER) != 0 && (jointype & (JT.LEFT | JT.RIGHT)) != JT.LEFT) { parse.ErrorMsg("RIGHT and FULL OUTER JOINs are not currently supported"); jointype = JT.INNER; } return jointype; }
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); }