Beispiel #1
0
 public Token GetToken(TokenType type)
 {
     Token t = new Token(this.StartPos, this.EndPos);
     t.Type = type;
     return t;
 }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
 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));
 }
Beispiel #5
0
 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;
 }
Beispiel #6
0
 /// <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;
 }
Beispiel #7
0
 public static JT JoinType(Parse parse, Token a, Token b, int null_4) { return JoinType(parse, a, b, null); }
Beispiel #8
0
 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;
 }
Beispiel #9
0
 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); }
Beispiel #10
0
        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;
        }
Beispiel #11
0
        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);
        }
Beispiel #12
0
 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;
 }
Beispiel #13
0
        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);
            }
        }
Beispiel #14
0
 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));
 }
Beispiel #15
0
 public void Init(string input)
 {
     this.Input = input;
     StartPos = 0;
     EndPos = 0;
     CurrentLine = 0;
     CurrentColumn = 0;
     CurrentPosition = 0;
     LookAheadToken = null;
 }
Beispiel #16
0
 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); }
Beispiel #17
0
        /// <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;
        }
Beispiel #18
0
 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;
 }
Beispiel #19
0
 public void UpdateRange(Token token)
 {
     if (token.StartPos < startpos) startpos = token.StartPos;
     if (token.EndPos > endpos) endpos = token.EndPos;
 }
Beispiel #20
0
 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;
 }
Beispiel #21
0
 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;
 }
Beispiel #22
0
        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);
        }