コード例 #1
0
        public TSQLUsingClause Parse(ITSQLTokenizer tokenizer)
        {
            TSQLUsingClause usingClause = new TSQLUsingClause();

            if (!tokenizer.Current.IsFutureKeyword(TSQLFutureKeywords.USING))
            {
                throw new InvalidOperationException("USING expected.");
            }

            usingClause.Tokens.Add(tokenizer.Current);

            /* can contain:
             *
             * <table_source> ::=
             * {
             *     table_or_view_name [ [ AS ] table_alias ] [ <tablesample_clause> ]
             *         [ WITH ( table_hint [ [ , ]...n ] ) ]
             *   | rowset_function [ [ AS ] table_alias ]
             *         [ ( bulk_column_alias [ ,...n ] ) ]
             *   | user_defined_function [ [ AS ] table_alias ]
             *   | OPENXML <openxml_clause>
             *   | derived_table [ AS ] table_alias [ ( column_alias [ ,...n ] ) ]
             *   | <joined_table>
             *   | <pivoted_table>
             *   | <unpivoted_table>
             * }
             * https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql?view=sql-server-ver15#syntax
             */

            int nestedLevel = 0;

            while (
                tokenizer.MoveNext() &&
                !tokenizer.Current.IsCharacter(TSQLCharacters.Semicolon) &&
                !(
                    nestedLevel == 0 &&
                    tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)
                    ) &&
                (
                    nestedLevel > 0 ||
                    tokenizer.Current.Type != TSQLTokenType.Keyword ||
                    (
                        tokenizer.Current.Type == TSQLTokenType.Keyword &&
                        !tokenizer.Current.AsKeyword.Keyword.In
                        (
                            // ON is required in MERGE statement after USING
                            TSQLKeywords.ON
                        )
                    )
                ))
            {
                TSQLTokenParserHelper.RecurseParens(
                    tokenizer,
                    usingClause,
                    ref nestedLevel);
            }

            return(usingClause);
        }
コード例 #2
0
        public TSQLSetClause Parse(ITSQLTokenizer tokenizer)
        {
            TSQLSetClause set = new TSQLSetClause();

            if (!tokenizer.Current.IsKeyword(TSQLKeywords.SET))
            {
                throw new InvalidOperationException("SET expected.");
            }

            set.Tokens.Add(tokenizer.Current);

            // TODO: parse this rare but valid horror scenario
            // update output
            // set output.output = 1
            // output deleted.*
            // maybe create assignment expression parser?

            int nestedLevel = 0;

            while (
                tokenizer.MoveNext() &&
                !tokenizer.Current.IsCharacter(TSQLCharacters.Semicolon) &&
                !(
                    nestedLevel == 0 &&
                    tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)
                    ) &&
                (
                    nestedLevel > 0 ||
                    (
                        tokenizer.Current.Type != TSQLTokenType.Keyword &&
                        !tokenizer.Current.IsFutureKeyword(TSQLFutureKeywords.OUTPUT)
                    ) ||
                    (
                        tokenizer.Current.Type == TSQLTokenType.Keyword &&
                        !tokenizer.Current.AsKeyword.Keyword.In
                        (
                            TSQLKeywords.FROM,
                            TSQLKeywords.WHERE,
                            TSQLKeywords.OPTION
                        ) &&
                        !tokenizer.Current.AsKeyword.Keyword.IsStatementStart()
                    )
                ))
            {
                TSQLTokenParserHelper.RecurseParens(
                    tokenizer,
                    set,
                    ref nestedLevel);
            }

            return(set);
        }
コード例 #3
0
        public TSQLIntoClause Parse(ITSQLTokenizer tokenizer)
        {
            TSQLIntoClause into = new TSQLIntoClause();

            if (!tokenizer.Current.IsKeyword(TSQLKeywords.INTO))
            {
                throw new InvalidOperationException("INTO expected.");
            }

            into.Tokens.Add(tokenizer.Current);

            int nestedLevel = 0;

            while (
                tokenizer.MoveNext() &&
                !tokenizer.Current.IsCharacter(TSQLCharacters.Semicolon) &&
                !(
                    nestedLevel == 0 &&
                    tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)
                    ) &&
                (
                    nestedLevel > 0 ||
                    tokenizer.Current.Type == TSQLTokenType.Identifier ||
                    tokenizer.Current.IsCharacter(TSQLCharacters.Period) ||
                    tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses) ||
                    tokenizer.Current.Type == TSQLTokenType.Whitespace ||
                    tokenizer.Current.Type == TSQLTokenType.SingleLineComment ||
                    tokenizer.Current.Type == TSQLTokenType.MultilineComment
                ))
            {
                TSQLTokenParserHelper.RecurseParens(
                    tokenizer,
                    into,
                    ref nestedLevel);
            }

            return(into);
        }
コード例 #4
0
        public TSQLWithClause Parse(ITSQLTokenizer tokenizer)
        {
            TSQLWithClause with = new TSQLWithClause();

            if (!tokenizer.Current.IsKeyword(TSQLKeywords.WITH))
            {
                throw new InvalidOperationException("WITH expected.");
            }

            with.Tokens.Add(tokenizer.Current);

            // subqueries
            int nestedLevel = 0;

            int  parenCount      = 0;
            int  identifierCount = 0;
            bool afterAs         = false;

            while (
                tokenizer.MoveNext() &&
                !tokenizer.Current.IsCharacter(TSQLCharacters.Semicolon) &&
                !(
                    nestedLevel == 0 &&
                    tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)
                    ) &&
                !(
                    // only allow a set of parens at root level
                    // if it's following an AS
                    // or if it's the column list between the CTE name and the AS
                    nestedLevel == 0 &&
                    tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses) &&
                    afterAs &&
                    parenCount >= identifierCount
                    ) &&
                (
                    nestedLevel > 0 ||
                    tokenizer.Current.Type != TSQLTokenType.Keyword ||
                    (
                        tokenizer.Current.Type == TSQLTokenType.Keyword &&
                        !tokenizer.Current.AsKeyword.Keyword.In
                        (
                            TSQLKeywords.SELECT,
                            TSQLKeywords.INSERT,
                            TSQLKeywords.UPDATE,
                            TSQLKeywords.DELETE,
                            TSQLKeywords.MERGE
                        )
                    )
                ))
            {
                if (nestedLevel == 0)
                {
                    if (afterAs && tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses))
                    {
                        parenCount++;
                    }
                    else if (tokenizer.Current.Type == TSQLTokenType.Identifier)
                    {
                        identifierCount++;
                        afterAs = false;
                    }
                    else if (tokenizer.Current.IsKeyword(TSQLKeywords.AS))
                    {
                        afterAs = true;
                    }
                }

                TSQLTokenParserHelper.RecurseParens(
                    tokenizer,
                    with,
                    ref nestedLevel);
            }

            return(with);
        }