From c1cf197f5a71bdc5c8feda4f1c5d68a619ad7a52 Mon Sep 17 00:00:00 2001 From: Bruce Dunwiddie Date: Fri, 6 May 2022 10:57:15 -0500 Subject: [PATCH] Fixed argument parsing for CAST function. Fixed some parsing not handling whitespace correctly. #89 --- .../Clauses/Parsers/TSQLOptionClauseParser.cs | 5 + .../Parsers/TSQLSetOperatorClauseParser.cs | 20 +- .../Parsers/TSQLSelectColumnParser.cs | 37 +-- .../Parsers/TSQLArgumentListParser.cs | 38 +-- .../Parsers/TSQLSelectExpressionParser.cs | 233 +---------------- .../TSQLValueAsTypeExpressionParser.cs | 68 +++++ .../Parsers/TSQLValueExpressionParser.cs | 55 +--- .../Expressions/TSQLExpressionType.cs | 7 +- .../Expressions/TSQLValueAsTypeExpression.cs | 25 ++ .../TSQL_Parser/Properties/AssemblyInfo.cs | 4 +- TSQL_Parser/TSQL_Parser/Push.bat | 4 +- .../TSQLLimitedSelectStatementParser.cs | 5 + .../Parsers/TSQLSelectStatementParser.cs | 5 + TSQL_Parser/TSQL_Parser/TSQLIdentifiers.cs | 240 ++++++++++++++++++ TSQL_Parser/TSQL_Parser/TSQLVariables.cs | 11 + TSQL_Parser/TSQL_Parser/TSQL_Parser.csproj | 2 + TSQL_Parser/TSQL_Parser/TSQL_Parser.nuspec | 4 +- .../TSQL_Parser_NetStandard.csproj | 6 +- .../Tokens/Parsers/TSQLTokenParserHelper.cs | 11 +- .../TSQL_Parser/Tokens/TSQLTokenExtensions.cs | 20 ++ .../Tests/Statements/SelectStatementTests.cs | 160 ++++++++++++ .../Tests/Statements/WithStatementTests.cs | 24 ++ 22 files changed, 647 insertions(+), 337 deletions(-) create mode 100644 TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueAsTypeExpressionParser.cs create mode 100644 TSQL_Parser/TSQL_Parser/Expressions/TSQLValueAsTypeExpression.cs diff --git a/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLOptionClauseParser.cs b/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLOptionClauseParser.cs index 4f347af..5645443 100644 --- a/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLOptionClauseParser.cs +++ b/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLOptionClauseParser.cs @@ -4,6 +4,7 @@ using System.Text; using TSQL.Tokens; +using TSQL.Tokens.Parsers; namespace TSQL.Clauses.Parsers { @@ -43,6 +44,10 @@ public TSQLOptionClause Parse(ITSQLTokenizer tokenizer) tokenizer.MoveNext(); } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + option); + return option; } } diff --git a/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLSetOperatorClauseParser.cs b/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLSetOperatorClauseParser.cs index 5f8055e..6b35595 100644 --- a/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLSetOperatorClauseParser.cs +++ b/TSQL_Parser/TSQL_Parser/Clauses/Parsers/TSQLSetOperatorClauseParser.cs @@ -7,6 +7,7 @@ using TSQL.Statements; using TSQL.Statements.Parsers; using TSQL.Tokens; +using TSQL.Tokens.Parsers; namespace TSQL.Clauses.Parsers { @@ -22,8 +23,11 @@ public TSQLSetOperatorClause Parse(ITSQLTokenizer tokenizer) set.Tokens.Add(tokenizer.Current); - if (tokenizer.MoveNext() && - tokenizer.Current.IsKeyword(TSQLKeywords.ALL)) + TSQLTokenParserHelper.ReadThroughAnyCommentsOrWhitespace( + tokenizer, + set.Tokens); + + if (tokenizer.Current.IsKeyword(TSQLKeywords.ALL)) { set.Tokens.Add(tokenizer.Current); @@ -47,6 +51,10 @@ public TSQLSetOperatorClause Parse(ITSQLTokenizer tokenizer) tokenizer.MoveNext(); } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + set); + int level = 0; while (tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses)) @@ -58,6 +66,10 @@ public TSQLSetOperatorClause Parse(ITSQLTokenizer tokenizer) tokenizer.MoveNext(); } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + set); + TSQLSelectStatement select = new TSQLLimitedSelectStatementParser(tokenizer).Parse(); set.Select = select; @@ -73,6 +85,10 @@ public TSQLSetOperatorClause Parse(ITSQLTokenizer tokenizer) tokenizer.MoveNext(); } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + set); + return set; } } diff --git a/TSQL_Parser/TSQL_Parser/Elements/Parsers/TSQLSelectColumnParser.cs b/TSQL_Parser/TSQL_Parser/Elements/Parsers/TSQLSelectColumnParser.cs index 8c293a5..9514bcd 100644 --- a/TSQL_Parser/TSQL_Parser/Elements/Parsers/TSQLSelectColumnParser.cs +++ b/TSQL_Parser/TSQL_Parser/Elements/Parsers/TSQLSelectColumnParser.cs @@ -7,6 +7,7 @@ using TSQL.Expressions; using TSQL.Expressions.Parsers; using TSQL.Tokens; +using TSQL.Tokens.Parsers; namespace TSQL.Elements.Parsers { @@ -22,17 +23,9 @@ public TSQLSelectColumn Parse(ITSQLTokenizer tokenizer) column.Tokens.AddRange(columnExpression.Tokens); - while ( - tokenizer.Current != null && - ( - tokenizer.Current.IsWhitespace() || - tokenizer.Current.IsComment()) - ) - { - column.Tokens.Add(tokenizer.Current); - - tokenizer.MoveNext(); - } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + column); // check for operator =, when expression type is column, and return new column expression with alias // e.g. IsFinishedGoods = p.FinishedGoodsFlag @@ -57,32 +50,28 @@ public TSQLSelectColumn Parse(ITSQLTokenizer tokenizer) } else { - if ( - tokenizer.Current != null && - tokenizer.Current.IsKeyword(TSQLKeywords.AS)) + if (tokenizer.Current.IsKeyword(TSQLKeywords.AS)) { column.Tokens.Add(tokenizer.Current); tokenizer.MoveNext(); - while ( - tokenizer.Current.IsWhitespace() || - tokenizer.Current.IsComment()) - { - column.Tokens.Add(tokenizer.Current); - - tokenizer.MoveNext(); - } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + column); } if (tokenizer.Current != null && - tokenizer.Current.Type.In( + tokenizer.Current.Type.In( TSQLTokenType.Identifier, + TSQLTokenType.SystemIdentifier, TSQLTokenType.IncompleteIdentifier)) { column.Tokens.Add(tokenizer.Current); - if (tokenizer.Current.Type == TSQLTokenType.Identifier) + if (tokenizer.Current.Type.In( + TSQLTokenType.Identifier, + TSQLTokenType.SystemIdentifier)) { column.ColumnAlias = tokenizer.Current.AsIdentifier; } diff --git a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLArgumentListParser.cs b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLArgumentListParser.cs index 15f4612..4234336 100644 --- a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLArgumentListParser.cs +++ b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLArgumentListParser.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using TSQL.Tokens; +using TSQL.Tokens.Parsers; namespace TSQL.Expressions.Parsers { @@ -21,34 +22,18 @@ public TSQLArgumentList Parse(ITSQLTokenizer tokenizer) // need to do this before starting the argument loop // so we can handle an empty argument list of just whitespace // and comments - while ( - tokenizer.Current != null && - ( - tokenizer.Current.IsComment() || - tokenizer.Current.IsWhitespace() - )) - { - tokens.Add(tokenizer.Current); - - tokenizer.MoveNext(); - } + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + tokens); while ( tokenizer.Current != null && !tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)) { - while ( - tokenizer.Current != null && - ( - tokenizer.Current.IsComment() || - tokenizer.Current.IsWhitespace() - )) - { - tokens.Add(tokenizer.Current); + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + tokens); - tokenizer.MoveNext(); - } - TSQLExpression argument = factory.Parse(tokenizer); tokens.AddRange(argument.Tokens); @@ -58,13 +43,12 @@ public TSQLArgumentList Parse(ITSQLTokenizer tokenizer) if (tokenizer.Current.IsCharacter(TSQLCharacters.Comma)) { tokens.Add(tokenizer.Current); - } - if ( - tokenizer.Current != null && - !tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)) - { tokenizer.MoveNext(); + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + tokens); } } diff --git a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLSelectExpressionParser.cs b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLSelectExpressionParser.cs index 91e20e8..8901e88 100644 --- a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLSelectExpressionParser.cs +++ b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLSelectExpressionParser.cs @@ -65,239 +65,8 @@ public TSQLExpression Parse(ITSQLTokenizer tokenizer) private static TSQLExpression ParseNext( ITSQLTokenizer tokenizer) { - if (tokenizer.Current == null) - { - return null; - } - - // look at the current/first token to determine what to do - - if (tokenizer.Current.Text == "*") - { - TSQLMulticolumnExpression simpleMulti = new TSQLMulticolumnExpression(); - - simpleMulti.Tokens.Add(tokenizer.Current); - - TSQLTokenParserHelper.ReadThroughAnyCommentsOrWhitespace( - tokenizer, - simpleMulti.Tokens); - - return simpleMulti; - - // still need to seperately check for p.* below - } - else if (tokenizer.Current.Type.In( - TSQLTokenType.Identifier)) - { - // multi column with alias - - // or column, with or without alias, or with full explicit table name with up to 5 parts - - // or function, up to 4 part naming - - // find last token up to and including possible first paren - // if *, then multi column - // if paren, then function - // else column - - // alias would be any tokens prior to last period, removing whitespace - - List tokens = new List(); - - tokens.Add(tokenizer.Current); - - while (tokenizer.MoveNext()) - { - if (tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses)) - { - #region parse function - - TSQLFunctionExpression function = new TSQLFunctionExpression(); - - function.Tokens.AddRange(tokens); - function.Tokens.Add(tokenizer.Current); - - var identityTokens = tokens - .Where(t => !t.IsComment() && !t.IsWhitespace()) - .ToList(); - - function.Function = - identityTokens[identityTokens.Count - 1] - .AsIdentifier; - - if (identityTokens.Count > 1) - { - function.QualifiedPath = - identityTokens - .GetRange( - 0, - identityTokens.Count - 2); - } - - tokenizer.MoveNext(); - - TSQLArgumentList arguments = new TSQLArgumentListParser().Parse( - tokenizer); - - function.Tokens.AddRange(arguments.Tokens); - - function.Arguments = arguments; - - if (tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)) - { - function.Tokens.Add(tokenizer.Current); - } - - tokenizer.MoveNext(); - - TSQLTokenParserHelper.ReadCommentsAndWhitespace( - tokenizer, - function); - - // look for windowed aggregate - if (tokenizer.Current.IsKeyword(TSQLKeywords.OVER)) - { - function.Tokens.Add(tokenizer.Current); - - tokenizer.MoveNext(); - - TSQLTokenParserHelper.ReadCommentsAndWhitespace( - tokenizer, - function); - - if (tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses)) - { - function.Tokens.Add(tokenizer.Current); - - // recursively look for final close parens - TSQLTokenParserHelper.ReadUntilStop( - tokenizer, - function, - new List { }, - new List { }, - lookForStatementStarts: false); - - if (tokenizer.Current != null && - tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)) - { - function.Tokens.Add(tokenizer.Current); - - tokenizer.MoveNext(); - } - } - } - - return function; - - #endregion - } - else if (tokenizer.Current.Text == "*") - { - #region parse multi column reference - - // e.g. p.* - - TSQLMulticolumnExpression multi = new TSQLMulticolumnExpression(); - - multi.Tokens.AddRange(tokens); - - multi.Tokens.Add(tokenizer.Current); - - List columnReference = tokens - .Where(t => !t.IsComment() && !t.IsWhitespace()) - .ToList(); - - if (columnReference.Count > 0) - { - // p.* will have the single token p in the final list - - // AdventureWorks..ErrorLog.* will have 4 tokens in the final list - // e.g. {AdventureWorks, ., ., ErrorLog} - - multi.TableReference = columnReference - .GetRange(0, columnReference - .FindLastIndex(t => t.IsCharacter(TSQLCharacters.Period))) - .ToList(); - } - - TSQLTokenParserHelper.ReadThroughAnyCommentsOrWhitespace( - tokenizer, - multi.Tokens); - - return multi; - - #endregion - } - else if ( - tokenizer.Current.IsCharacter(TSQLCharacters.Comma) || - tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses) || - tokenizer.Current.Type.In( - TSQLTokenType.Keyword, - TSQLTokenType.Operator) || - - // this will be a nasty check, but I don't want to copy the internal logic elsewhere - - // two identifiers in a row means that the second one is an alias - ( - tokenizer.Current.Type.In( - TSQLTokenType.Identifier, - TSQLTokenType.IncompleteIdentifier) && - tokens - .Where(t => !t.IsComment() && !t.IsWhitespace()) - .LastOrDefault() - ?.Type.In( - TSQLTokenType.Identifier, - TSQLTokenType.BinaryLiteral, - TSQLTokenType.MoneyLiteral, - TSQLTokenType.NumericLiteral, - TSQLTokenType.StringLiteral, - TSQLTokenType.SystemColumnIdentifier, - TSQLTokenType.SystemIdentifier, - TSQLTokenType.SystemVariable, - TSQLTokenType.Variable - ) == true // Operator '&&' cannot be applied to operands of type 'bool' and 'bool?' - )) - { - TSQLColumnExpression column = new TSQLColumnExpression(); - - column.Tokens.AddRange(tokens); - - List columnReference = tokens - .Where(t => !t.IsComment() && !t.IsWhitespace()) - .ToList(); - - if (columnReference.Count > 1) - { - // p.ProductID will have the single token p in the final list - - // AdventureWorks..ErrorLog.ErrorLogID will have 4 tokens in the final list - // e.g. {AdventureWorks, ., ., ErrorLog} - - column.TableReference = columnReference - .GetRange(0, columnReference - .FindLastIndex(t => t.IsCharacter(TSQLCharacters.Period))) - .ToList(); - } - - column.Column = columnReference - .Last() - .AsIdentifier; - - return column; - } - else - { - tokens.Add(tokenizer.Current); - } - } - - return null; - } - else - { - return new TSQLValueExpressionParser().ParseNext( + return new TSQLValueExpressionParser().ParseNext( tokenizer); - } } } } diff --git a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueAsTypeExpressionParser.cs b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueAsTypeExpressionParser.cs new file mode 100644 index 0000000..af8741b --- /dev/null +++ b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueAsTypeExpressionParser.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using TSQL.Tokens; +using TSQL.Tokens.Parsers; + +namespace TSQL.Expressions.Parsers +{ + /// + /// Used to parse the unique argument format of the CAST function. + /// + internal class TSQLValueAsTypeExpressionParser + { + public TSQLArgumentList Parse(ITSQLTokenizer tokenizer) + { + List tokens = new List(); + + // need to do this before starting the argument loop + // so we can handle an empty argument list of just whitespace + // and comments + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + tokens); + + TSQLValueAsTypeExpression argument = new TSQLValueAsTypeExpression(); + + TSQLExpression expression = new TSQLValueExpressionParser().Parse(tokenizer); + + argument.Expression = expression; + + tokens.AddRange(expression.Tokens); + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + tokenizer, + tokens); + + if (!tokenizer.Current.IsKeyword(TSQLKeywords.AS)) + { + throw new InvalidOperationException("AS expected."); + } + + tokens.Add(tokenizer.Current); + + TSQLTokenParserHelper.ReadThroughAnyCommentsOrWhitespace( + tokenizer, + tokens); + + argument.DataType = tokenizer.Current.AsIdentifier; + + tokens.Add(tokenizer.Current); + + // reading until closing paren + TSQLTokenParserHelper.ReadThroughAnyCommentsOrWhitespace( + tokenizer, + tokens); + + TSQLArgumentList argList = new TSQLArgumentList( + new List { argument }); + + argList.Tokens.AddRange(tokens); + + return argList; + } + } +} diff --git a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueExpressionParser.cs b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueExpressionParser.cs index 851a808..390f531 100644 --- a/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueExpressionParser.cs +++ b/TSQL_Parser/TSQL_Parser/Expressions/Parsers/TSQLValueExpressionParser.cs @@ -179,46 +179,8 @@ public TSQLExpression ParseNext( return column; } else if (tokenizer.Current.Type.In( + TSQLTokenType.Identifier, TSQLTokenType.SystemIdentifier)) - { - #region parse system function - - TSQLFunctionExpression function = new TSQLFunctionExpression(); - - function.Function = tokenizer.Current.AsSystemIdentifier; - - function.Tokens.Add(tokenizer.Current); - - if (tokenizer.MoveNext() && - tokenizer.Current.IsCharacter(TSQLCharacters.OpenParentheses)) - { - function.Tokens.Add(tokenizer.Current); - - tokenizer.MoveNext(); - - TSQLArgumentList arguments = new TSQLArgumentListParser().Parse( - tokenizer); - - function.Tokens.AddRange(arguments.Tokens); - - function.Arguments = arguments; - - if (tokenizer.Current.IsCharacter(TSQLCharacters.CloseParentheses)) - { - function.Tokens.Add(tokenizer.Current); - } - - TSQLTokenParserHelper.ReadThroughAnyCommentsOrWhitespace( - tokenizer, - function.Tokens); - } - - return function; - - #endregion - } - else if (tokenizer.Current.Type.In( - TSQLTokenType.Identifier)) { // column, with or without alias, or with full explicit table name with up to 5 parts @@ -265,8 +227,19 @@ public TSQLExpression ParseNext( tokenizer.MoveNext(); - TSQLArgumentList arguments = new TSQLArgumentListParser().Parse( - tokenizer); + TSQLArgumentList arguments = null; + + // CAST function has it's own very unique argument syntax + if (function.Function.IsIdentifier(TSQLIdentifiers.CAST)) + { + arguments = new TSQLValueAsTypeExpressionParser().Parse( + tokenizer); + } + else + { + arguments = new TSQLArgumentListParser().Parse( + tokenizer); + } function.Tokens.AddRange(arguments.Tokens); diff --git a/TSQL_Parser/TSQL_Parser/Expressions/TSQLExpressionType.cs b/TSQL_Parser/TSQL_Parser/Expressions/TSQLExpressionType.cs index 2ff11d7..1a1b2c8 100644 --- a/TSQL_Parser/TSQL_Parser/Expressions/TSQLExpressionType.cs +++ b/TSQL_Parser/TSQL_Parser/Expressions/TSQLExpressionType.cs @@ -49,6 +49,11 @@ public enum TSQLExpressionType /// /// e.g. @ProductID = p.id /// - VariableAssignment + VariableAssignment, + + /// + /// e.g. 123.45 AS INT (only used as an argument to CAST function) + /// + ValueAsType } } diff --git a/TSQL_Parser/TSQL_Parser/Expressions/TSQLValueAsTypeExpression.cs b/TSQL_Parser/TSQL_Parser/Expressions/TSQLValueAsTypeExpression.cs new file mode 100644 index 0000000..851c837 --- /dev/null +++ b/TSQL_Parser/TSQL_Parser/Expressions/TSQLValueAsTypeExpression.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using TSQL.Tokens; + +namespace TSQL.Expressions +{ + public class TSQLValueAsTypeExpression : TSQLExpression + { + public override TSQLExpressionType Type + { + get + { + return TSQLExpressionType.ValueAsType; + } + } + + public TSQLExpression Expression { get; internal set; } + + public TSQLIdentifier DataType { get; internal set; } + } +} diff --git a/TSQL_Parser/TSQL_Parser/Properties/AssemblyInfo.cs b/TSQL_Parser/TSQL_Parser/Properties/AssemblyInfo.cs index 96c3d45..27718c3 100644 --- a/TSQL_Parser/TSQL_Parser/Properties/AssemblyInfo.cs +++ b/TSQL_Parser/TSQL_Parser/Properties/AssemblyInfo.cs @@ -32,7 +32,7 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.1.0")] -[assembly: AssemblyFileVersion("2.0.1.0")] +[assembly: AssemblyVersion("2.1.0.0")] +[assembly: AssemblyFileVersion("2.1.0.0")] [assembly: InternalsVisibleTo("Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100793625650b945744f8a2c57bc75da89cd4d2c551636aa180c3020b7a15b815c10e983e83c312eb02f131c6fcf18aaffd6c8d9af6c4353c91ca0e9206b0fb8fb7805fc07b510a47ff40705ae56977ae8893e2d247d166aa400926582840e8a5602df055762bc3479dd14c9621a77946b6e6b0a00a77204c78fb52c65121bd75ba")] \ No newline at end of file diff --git a/TSQL_Parser/TSQL_Parser/Push.bat b/TSQL_Parser/TSQL_Parser/Push.bat index 199d10c..5185675 100644 --- a/TSQL_Parser/TSQL_Parser/Push.bat +++ b/TSQL_Parser/TSQL_Parser/Push.bat @@ -1,4 +1,4 @@ nuget SetApiKey %NUGET_KEY% -nuget push TSQL.Parser.2.0.1.snupkg -Source https://api.nuget.org/v3/index.json -nuget push TSQL.Parser.2.0.1.nupkg -Source https://api.nuget.org/v3/index.json +nuget push TSQL.Parser.2.1.0.snupkg -Source https://api.nuget.org/v3/index.json +nuget push TSQL.Parser.2.1.0.nupkg -Source https://api.nuget.org/v3/index.json pause \ No newline at end of file diff --git a/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLLimitedSelectStatementParser.cs b/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLLimitedSelectStatementParser.cs index 09b75e4..0523c2f 100644 --- a/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLLimitedSelectStatementParser.cs +++ b/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLLimitedSelectStatementParser.cs @@ -6,6 +6,7 @@ using TSQL.Clauses; using TSQL.Clauses.Parsers; using TSQL.Tokens; +using TSQL.Tokens.Parsers; namespace TSQL.Statements.Parsers { @@ -83,6 +84,10 @@ public TSQLSelectStatement Parse() // OPTION not allowed + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + Tokenizer, + Statement); + return Statement; } } diff --git a/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLSelectStatementParser.cs b/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLSelectStatementParser.cs index 079836c..35db60d 100644 --- a/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLSelectStatementParser.cs +++ b/TSQL_Parser/TSQL_Parser/Statements/Parsers/TSQLSelectStatementParser.cs @@ -6,6 +6,7 @@ using TSQL.Clauses; using TSQL.Clauses.Parsers; using TSQL.Tokens; +using TSQL.Tokens.Parsers; namespace TSQL.Statements.Parsers { @@ -40,6 +41,10 @@ public TSQLSelectStatement Parse() level++; Tokenizer.MoveNext(); + + TSQLTokenParserHelper.ReadCommentsAndWhitespace( + Tokenizer, + Statement); } TSQLSelectClause selectClause = new TSQLSelectClauseParser().Parse(Tokenizer); diff --git a/TSQL_Parser/TSQL_Parser/TSQLIdentifiers.cs b/TSQL_Parser/TSQL_Parser/TSQLIdentifiers.cs index 921a728..353a3bc 100644 --- a/TSQL_Parser/TSQL_Parser/TSQLIdentifiers.cs +++ b/TSQL_Parser/TSQL_Parser/TSQLIdentifiers.cs @@ -36,6 +36,246 @@ public struct TSQLIdentifiers public static readonly TSQLIdentifiers SESSION_USER = new TSQLIdentifiers("SESSION_USER"); public static readonly TSQLIdentifiers SYSTEM_USER = new TSQLIdentifiers("SYSTEM_USER"); public static readonly TSQLIdentifiers TRY_CONVERT = new TSQLIdentifiers("TRY_CONVERT"); + public static readonly TSQLIdentifiers BIT_LENGTH = new TSQLIdentifiers("BIT_LENGTH"); + public static readonly TSQLIdentifiers CONCAT = new TSQLIdentifiers("CONCAT"); + public static readonly TSQLIdentifiers OCTET_LENGTH = new TSQLIdentifiers("OCTET_LENGTH"); + public static readonly TSQLIdentifiers TRUNCATE = new TSQLIdentifiers("TRUNCATE"); + public static readonly TSQLIdentifiers CURDATE = new TSQLIdentifiers("CURDATE"); + public static readonly TSQLIdentifiers CURTIME = new TSQLIdentifiers("CURTIME"); + public static readonly TSQLIdentifiers DAYNAME = new TSQLIdentifiers("DAYNAME"); + public static readonly TSQLIdentifiers DAYOFMONTH = new TSQLIdentifiers("DAYOFMONTH"); + public static readonly TSQLIdentifiers DAYOFWEEK = new TSQLIdentifiers("DAYOFWEEK"); + public static readonly TSQLIdentifiers HOUR = new TSQLIdentifiers("HOUR"); + public static readonly TSQLIdentifiers MINUTE = new TSQLIdentifiers("MINUTE"); + public static readonly TSQLIdentifiers SECOND = new TSQLIdentifiers("SECOND"); + public static readonly TSQLIdentifiers MONTHNAME = new TSQLIdentifiers("MONTHNAME"); + public static readonly TSQLIdentifiers QUARTER = new TSQLIdentifiers("QUARTER"); + public static readonly TSQLIdentifiers WEEK = new TSQLIdentifiers("WEEK"); + public static readonly TSQLIdentifiers APPROX_COUNT_DISTINCT = new TSQLIdentifiers("APPROX_COUNT_DISTINCT"); + public static readonly TSQLIdentifiers AVG = new TSQLIdentifiers("AVG"); + public static readonly TSQLIdentifiers CHECKSUM_AGG = new TSQLIdentifiers("CHECKSUM_AGG"); + public static readonly TSQLIdentifiers COUNT = new TSQLIdentifiers("COUNT"); + public static readonly TSQLIdentifiers COUNT_BIG = new TSQLIdentifiers("COUNT_BIG"); + public static readonly TSQLIdentifiers GROUPING = new TSQLIdentifiers("GROUPING"); + public static readonly TSQLIdentifiers GROUPING_ID = new TSQLIdentifiers("GROUPING_ID"); + public static readonly TSQLIdentifiers MAX = new TSQLIdentifiers("MAX"); + public static readonly TSQLIdentifiers MIN = new TSQLIdentifiers("MIN"); + public static readonly TSQLIdentifiers STDEV = new TSQLIdentifiers("STDEV"); + public static readonly TSQLIdentifiers STDEVP = new TSQLIdentifiers("STDEVP"); + public static readonly TSQLIdentifiers SUM = new TSQLIdentifiers("SUM"); + public static readonly TSQLIdentifiers VAR = new TSQLIdentifiers("VAR"); + public static readonly TSQLIdentifiers VARP = new TSQLIdentifiers("VARP"); + public static readonly TSQLIdentifiers CUME_DIST = new TSQLIdentifiers("CUME_DIST"); + public static readonly TSQLIdentifiers FIRST_VALUE = new TSQLIdentifiers("FIRST_VALUE"); + public static readonly TSQLIdentifiers LAG = new TSQLIdentifiers("LAG"); + public static readonly TSQLIdentifiers LAST_VALUE = new TSQLIdentifiers("LAST_VALUE"); + public static readonly TSQLIdentifiers LEAD = new TSQLIdentifiers("LEAD"); + public static readonly TSQLIdentifiers PERCENT_RANK = new TSQLIdentifiers("PERCENT_RANK"); + public static readonly TSQLIdentifiers PERCENTILE_CONT = new TSQLIdentifiers("PERCENTILE_CONT"); + public static readonly TSQLIdentifiers PERCENTILE_DISC = new TSQLIdentifiers("PERCENTILE_DISC"); + public static readonly TSQLIdentifiers CAST = new TSQLIdentifiers("CAST"); + public static readonly TSQLIdentifiers PARSE = new TSQLIdentifiers("PARSE"); + public static readonly TSQLIdentifiers TRY_CAST = new TSQLIdentifiers("TRY_CAST"); + public static readonly TSQLIdentifiers TRY_PARSE = new TSQLIdentifiers("TRY_PARSE"); + public static readonly TSQLIdentifiers ENCRYPTBYKEY = new TSQLIdentifiers("ENCRYPTBYKEY"); + public static readonly TSQLIdentifiers DECRYPTBYKEY = new TSQLIdentifiers("DECRYPTBYKEY"); + public static readonly TSQLIdentifiers ENCRYPTBYPASSPHRASE = new TSQLIdentifiers("ENCRYPTBYPASSPHRASE"); + public static readonly TSQLIdentifiers DECRYPTBYPASSPHRASE = new TSQLIdentifiers("DECRYPTBYPASSPHRASE"); + public static readonly TSQLIdentifiers KEY_ID = new TSQLIdentifiers("KEY_ID"); + public static readonly TSQLIdentifiers KEY_GUID = new TSQLIdentifiers("KEY_GUID"); + public static readonly TSQLIdentifiers DECRYPTBYKEYAUTOASYMKEY = new TSQLIdentifiers("DECRYPTBYKEYAUTOASYMKEY"); + public static readonly TSQLIdentifiers KEY_NAME = new TSQLIdentifiers("KEY_NAME"); + public static readonly TSQLIdentifiers SYMKEYPROPERTY = new TSQLIdentifiers("SYMKEYPROPERTY"); + public static readonly TSQLIdentifiers ENCRYPTBYASYMKEY = new TSQLIdentifiers("ENCRYPTBYASYMKEY"); + public static readonly TSQLIdentifiers DECRYPTBYASYMKEY = new TSQLIdentifiers("DECRYPTBYASYMKEY"); + public static readonly TSQLIdentifiers ENCRYPTBYCERT = new TSQLIdentifiers("ENCRYPTBYCERT"); + public static readonly TSQLIdentifiers DECRYPTBYCERT = new TSQLIdentifiers("DECRYPTBYCERT"); + public static readonly TSQLIdentifiers ASYMKEYPROPERTY = new TSQLIdentifiers("ASYMKEYPROPERTY"); + public static readonly TSQLIdentifiers ASYMKEY_ID = new TSQLIdentifiers("ASYMKEY_ID"); + public static readonly TSQLIdentifiers SIGNBYASYMKEY = new TSQLIdentifiers("SIGNBYASYMKEY"); + public static readonly TSQLIdentifiers VERIFYSIGNEDBYASMKEY = new TSQLIdentifiers("VERIFYSIGNEDBYASMKEY"); + public static readonly TSQLIdentifiers SIGNBYCERT = new TSQLIdentifiers("SIGNBYCERT"); + public static readonly TSQLIdentifiers VERIGYSIGNEDBYCERT = new TSQLIdentifiers("VERIGYSIGNEDBYCERT"); + public static readonly TSQLIdentifiers IS_OBJECTSIGNED = new TSQLIdentifiers("IS_OBJECTSIGNED"); + public static readonly TSQLIdentifiers DECRYPTBYKEYAUTOCERT = new TSQLIdentifiers("DECRYPTBYKEYAUTOCERT"); + public static readonly TSQLIdentifiers HASHBYTES = new TSQLIdentifiers("HASHBYTES"); + public static readonly TSQLIdentifiers CERTENCODED = new TSQLIdentifiers("CERTENCODED"); + public static readonly TSQLIdentifiers CERTPRIVATEKEY = new TSQLIdentifiers("CERTPRIVATEKEY"); + public static readonly TSQLIdentifiers CURSOR_STATUS = new TSQLIdentifiers("CURSOR_STATUS"); + public static readonly TSQLIdentifiers DATALENGTH = new TSQLIdentifiers("DATALENGTH"); + public static readonly TSQLIdentifiers IDENT_SEED = new TSQLIdentifiers("IDENT_SEED"); + public static readonly TSQLIdentifiers IDENT_CURRENT = new TSQLIdentifiers("IDENT_CURRENT"); + public static readonly TSQLIdentifiers IDENTITY = new TSQLIdentifiers("IDENTITY"); + public static readonly TSQLIdentifiers IDENT_INCR = new TSQLIdentifiers("IDENT_INCR"); + public static readonly TSQLIdentifiers SQL_VARIANT_PROPERTY = new TSQLIdentifiers("SQL_VARIANT_PROPERTY"); + public static readonly TSQLIdentifiers MONTH = new TSQLIdentifiers("MONTH"); + public static readonly TSQLIdentifiers YEAR = new TSQLIdentifiers("YEAR"); + public static readonly TSQLIdentifiers DATEFROMPARTS = new TSQLIdentifiers("DATEFROMPARTS"); + public static readonly TSQLIdentifiers DATETIME2FROMPARTS = new TSQLIdentifiers("DATETIME2FROMPARTS"); + public static readonly TSQLIdentifiers DATETIMEFROMPARTS = new TSQLIdentifiers("DATETIMEFROMPARTS"); + public static readonly TSQLIdentifiers DATETIMEOFFSETFROMPARTS = new TSQLIdentifiers("DATETIMEOFFSETFROMPARTS"); + public static readonly TSQLIdentifiers SMALLDATETIMEFROMPARTS = new TSQLIdentifiers("SMALLDATETIMEFROMPARTS"); + public static readonly TSQLIdentifiers TIMEFROMPARTS = new TSQLIdentifiers("TIMEFROMPARTS"); + public static readonly TSQLIdentifiers DATEDIFF = new TSQLIdentifiers("DATEDIFF"); + public static readonly TSQLIdentifiers DATEDIFF_BIG = new TSQLIdentifiers("DATEDIFF_BIG"); + public static readonly TSQLIdentifiers DATEADD = new TSQLIdentifiers("DATEADD"); + public static readonly TSQLIdentifiers EOMONTH = new TSQLIdentifiers("EOMONTH"); + public static readonly TSQLIdentifiers SWITCHOFFSET = new TSQLIdentifiers("SWITCHOFFSET"); + public static readonly TSQLIdentifiers TODATETIMEOFFSET = new TSQLIdentifiers("TODATETIMEOFFSET"); + public static readonly TSQLIdentifiers ISDATE = new TSQLIdentifiers("ISDATE"); + public static readonly TSQLIdentifiers ISJSON = new TSQLIdentifiers("ISJSON"); + public static readonly TSQLIdentifiers JSON_VALUE = new TSQLIdentifiers("JSON_VALUE"); + public static readonly TSQLIdentifiers JSON_QUERY = new TSQLIdentifiers("JSON_QUERY"); + public static readonly TSQLIdentifiers JSON_MODIFY = new TSQLIdentifiers("JSON_MODIFY"); + public static readonly TSQLIdentifiers ABS = new TSQLIdentifiers("ABS"); + public static readonly TSQLIdentifiers ACOS = new TSQLIdentifiers("ACOS"); + public static readonly TSQLIdentifiers ASIN = new TSQLIdentifiers("ASIN"); + public static readonly TSQLIdentifiers ATAN = new TSQLIdentifiers("ATAN"); + public static readonly TSQLIdentifiers ATN2 = new TSQLIdentifiers("ATN2"); + public static readonly TSQLIdentifiers CEILING = new TSQLIdentifiers("CEILING"); + public static readonly TSQLIdentifiers COS = new TSQLIdentifiers("COS"); + public static readonly TSQLIdentifiers COT = new TSQLIdentifiers("COT"); + public static readonly TSQLIdentifiers DEGREES = new TSQLIdentifiers("DEGREES"); + public static readonly TSQLIdentifiers EXP = new TSQLIdentifiers("EXP"); + public static readonly TSQLIdentifiers FLOOR = new TSQLIdentifiers("FLOOR"); + public static readonly TSQLIdentifiers LOG = new TSQLIdentifiers("LOG"); + public static readonly TSQLIdentifiers LOG10 = new TSQLIdentifiers("LOG10"); + public static readonly TSQLIdentifiers PI = new TSQLIdentifiers("PI"); + public static readonly TSQLIdentifiers POWER = new TSQLIdentifiers("POWER"); + public static readonly TSQLIdentifiers RADIANS = new TSQLIdentifiers("RADIANS"); + public static readonly TSQLIdentifiers RAND = new TSQLIdentifiers("RAND"); + public static readonly TSQLIdentifiers ROUND = new TSQLIdentifiers("ROUND"); + public static readonly TSQLIdentifiers SIGN = new TSQLIdentifiers("SIGN"); + public static readonly TSQLIdentifiers SIN = new TSQLIdentifiers("SIN"); + public static readonly TSQLIdentifiers SQRT = new TSQLIdentifiers("SQRT"); + public static readonly TSQLIdentifiers SQUARE = new TSQLIdentifiers("SQUARE"); + public static readonly TSQLIdentifiers TAN = new TSQLIdentifiers("TAN"); + public static readonly TSQLIdentifiers CHOOSE = new TSQLIdentifiers("CHOOSE"); + public static readonly TSQLIdentifiers IIF = new TSQLIdentifiers("IIF"); + public static readonly TSQLIdentifiers APP_NAME = new TSQLIdentifiers("APP_NAME"); + public static readonly TSQLIdentifiers APPLOCK_MODE = new TSQLIdentifiers("APPLOCK_MODE"); + public static readonly TSQLIdentifiers APPLOCK_TEST = new TSQLIdentifiers("APPLOCK_TEST"); + public static readonly TSQLIdentifiers ASSEMBLYPROPERTY = new TSQLIdentifiers("ASSEMBLYPROPERTY"); + public static readonly TSQLIdentifiers COL_LENGTH = new TSQLIdentifiers("COL_LENGTH"); + public static readonly TSQLIdentifiers COL_NAME = new TSQLIdentifiers("COL_NAME"); + public static readonly TSQLIdentifiers COLUMNPROPERTY = new TSQLIdentifiers("COLUMNPROPERTY"); + public static readonly TSQLIdentifiers DATABASE_PRINCIPAL_ID = new TSQLIdentifiers("DATABASE_PRINCIPAL_ID"); + public static readonly TSQLIdentifiers DATABASEPROPERTYEX = new TSQLIdentifiers("DATABASEPROPERTYEX"); + public static readonly TSQLIdentifiers DB_ID = new TSQLIdentifiers("DB_ID"); + public static readonly TSQLIdentifiers DB_NAME = new TSQLIdentifiers("DB_NAME"); + public static readonly TSQLIdentifiers FILE_ID = new TSQLIdentifiers("FILE_ID"); + public static readonly TSQLIdentifiers FILE_IDEX = new TSQLIdentifiers("FILE_IDEX"); + public static readonly TSQLIdentifiers FILE_NAME = new TSQLIdentifiers("FILE_NAME"); + public static readonly TSQLIdentifiers FILEGROUP_ID = new TSQLIdentifiers("FILEGROUP_ID"); + public static readonly TSQLIdentifiers FILEGROUP_NAME = new TSQLIdentifiers("FILEGROUP_NAME"); + public static readonly TSQLIdentifiers FILEGROUPPROPERTY = new TSQLIdentifiers("FILEGROUPPROPERTY"); + public static readonly TSQLIdentifiers FILEPROPERTY = new TSQLIdentifiers("FILEPROPERTY"); + public static readonly TSQLIdentifiers FULLTEXTCATALOGPROPERTY = new TSQLIdentifiers("FULLTEXTCATALOGPROPERTY"); + public static readonly TSQLIdentifiers FULLTEXTSERVICEPROPERTY = new TSQLIdentifiers("FULLTEXTSERVICEPROPERTY"); + public static readonly TSQLIdentifiers INDEX_COL = new TSQLIdentifiers("INDEX_COL"); + public static readonly TSQLIdentifiers INDEXKEY_PROPERTY = new TSQLIdentifiers("INDEXKEY_PROPERTY"); + public static readonly TSQLIdentifiers INDEXPROPERTY = new TSQLIdentifiers("INDEXPROPERTY"); + public static readonly TSQLIdentifiers OBJECT_DEFINITION = new TSQLIdentifiers("OBJECT_DEFINITION"); + public static readonly TSQLIdentifiers OBJECT_ID = new TSQLIdentifiers("OBJECT_ID"); + public static readonly TSQLIdentifiers OBJECT_NAME = new TSQLIdentifiers("OBJECT_NAME"); + public static readonly TSQLIdentifiers OBJECT_SCHEMA_NAME = new TSQLIdentifiers("OBJECT_SCHEMA_NAME"); + public static readonly TSQLIdentifiers OBJECTPROPERTY = new TSQLIdentifiers("OBJECTPROPERTY"); + public static readonly TSQLIdentifiers OBJECTPROPERTYEX = new TSQLIdentifiers("OBJECTPROPERTYEX"); + public static readonly TSQLIdentifiers ORIGINAL_DB_NAME = new TSQLIdentifiers("ORIGINAL_DB_NAME"); + public static readonly TSQLIdentifiers PARSENAME = new TSQLIdentifiers("PARSENAME"); + public static readonly TSQLIdentifiers SCHEMA_ID = new TSQLIdentifiers("SCHEMA_ID"); + public static readonly TSQLIdentifiers SCHEMA_NAME = new TSQLIdentifiers("SCHEMA_NAME"); + public static readonly TSQLIdentifiers SCOPE_IDENTITY = new TSQLIdentifiers("SCOPE_IDENTITY"); + public static readonly TSQLIdentifiers SERVERPROPERTY = new TSQLIdentifiers("SERVERPROPERTY"); + public static readonly TSQLIdentifiers STATS_DATE = new TSQLIdentifiers("STATS_DATE"); + public static readonly TSQLIdentifiers TYPE_ID = new TSQLIdentifiers("TYPE_ID"); + public static readonly TSQLIdentifiers TYPE_NAME = new TSQLIdentifiers("TYPE_NAME"); + public static readonly TSQLIdentifiers TYPEPROPERTY = new TSQLIdentifiers("TYPEPROPERTY"); + public static readonly TSQLIdentifiers VERSION = new TSQLIdentifiers("VERSION"); + public static readonly TSQLIdentifiers RANK = new TSQLIdentifiers("RANK"); + public static readonly TSQLIdentifiers NTILE = new TSQLIdentifiers("NTILE"); + public static readonly TSQLIdentifiers DENSE_RANK = new TSQLIdentifiers("DENSE_RANK"); + public static readonly TSQLIdentifiers ROW_NUMBER = new TSQLIdentifiers("ROW_NUMBER"); + public static readonly TSQLIdentifiers PUBLISHINGSERVERNAME = new TSQLIdentifiers("PUBLISHINGSERVERNAME"); + public static readonly TSQLIdentifiers PWDCOMPARE = new TSQLIdentifiers("PWDCOMPARE"); + public static readonly TSQLIdentifiers PWDENCRYPT = new TSQLIdentifiers("PWDENCRYPT"); + public static readonly TSQLIdentifiers SUSER_ID = new TSQLIdentifiers("SUSER_ID"); + public static readonly TSQLIdentifiers SUSER_SID = new TSQLIdentifiers("SUSER_SID"); + public static readonly TSQLIdentifiers HAS_PERMS_BY_NAME = new TSQLIdentifiers("HAS_PERMS_BY_NAME"); + public static readonly TSQLIdentifiers SUSER_SNAME = new TSQLIdentifiers("SUSER_SNAME"); + public static readonly TSQLIdentifiers IS_MEMBER = new TSQLIdentifiers("IS_MEMBER"); + public static readonly TSQLIdentifiers IS_ROLEMEMBER = new TSQLIdentifiers("IS_ROLEMEMBER"); + public static readonly TSQLIdentifiers SUSER_NAME = new TSQLIdentifiers("SUSER_NAME"); + public static readonly TSQLIdentifiers IS_SRVROLEMEMBER = new TSQLIdentifiers("IS_SRVROLEMEMBER"); + public static readonly TSQLIdentifiers USER_ID = new TSQLIdentifiers("USER_ID"); + public static readonly TSQLIdentifiers LOGINPROPERTY = new TSQLIdentifiers("LOGINPROPERTY"); + public static readonly TSQLIdentifiers USER_NAME = new TSQLIdentifiers("USER_NAME"); + public static readonly TSQLIdentifiers ORIGINAL_LOGIN = new TSQLIdentifiers("ORIGINAL_LOGIN"); + public static readonly TSQLIdentifiers PERMISSIONS = new TSQLIdentifiers("PERMISSIONS"); + public static readonly TSQLIdentifiers ASCII = new TSQLIdentifiers("ASCII"); + public static readonly TSQLIdentifiers CHAR = new TSQLIdentifiers("CHAR"); + public static readonly TSQLIdentifiers CHARINDEX = new TSQLIdentifiers("CHARINDEX"); + public static readonly TSQLIdentifiers CONCAT_WS = new TSQLIdentifiers("CONCAT_WS"); + public static readonly TSQLIdentifiers DIFFERENCE = new TSQLIdentifiers("DIFFERENCE"); + public static readonly TSQLIdentifiers FORMAT = new TSQLIdentifiers("FORMAT"); + public static readonly TSQLIdentifiers LEFT = new TSQLIdentifiers("LEFT"); + public static readonly TSQLIdentifiers LEN = new TSQLIdentifiers("LEN"); + public static readonly TSQLIdentifiers LOWER = new TSQLIdentifiers("LOWER"); + public static readonly TSQLIdentifiers LTRIM = new TSQLIdentifiers("LTRIM"); + public static readonly TSQLIdentifiers NCHAR = new TSQLIdentifiers("NCHAR"); + public static readonly TSQLIdentifiers PATINDEX = new TSQLIdentifiers("PATINDEX"); + public static readonly TSQLIdentifiers QUOTENAME = new TSQLIdentifiers("QUOTENAME"); + public static readonly TSQLIdentifiers REPLACE = new TSQLIdentifiers("REPLACE"); + public static readonly TSQLIdentifiers REPLICATE = new TSQLIdentifiers("REPLICATE"); + public static readonly TSQLIdentifiers REVERSE = new TSQLIdentifiers("REVERSE"); + public static readonly TSQLIdentifiers RIGHT = new TSQLIdentifiers("RIGHT"); + public static readonly TSQLIdentifiers RTRIM = new TSQLIdentifiers("RTRIM"); + public static readonly TSQLIdentifiers SOUNDEX = new TSQLIdentifiers("SOUNDEX"); + public static readonly TSQLIdentifiers SPACE = new TSQLIdentifiers("SPACE"); + public static readonly TSQLIdentifiers STR = new TSQLIdentifiers("STR"); + public static readonly TSQLIdentifiers STRING_AGG = new TSQLIdentifiers("STRING_AGG"); + public static readonly TSQLIdentifiers STRING_ESCAPE = new TSQLIdentifiers("STRING_ESCAPE"); + public static readonly TSQLIdentifiers STRING_SPLIT = new TSQLIdentifiers("STRING_SPLIT"); + public static readonly TSQLIdentifiers STUFF = new TSQLIdentifiers("STUFF"); + public static readonly TSQLIdentifiers SUBSTRING = new TSQLIdentifiers("SUBSTRING"); + public static readonly TSQLIdentifiers TRANSLATE = new TSQLIdentifiers("TRANSLATE"); + public static readonly TSQLIdentifiers TRIM = new TSQLIdentifiers("TRIM"); + public static readonly TSQLIdentifiers UNICODE = new TSQLIdentifiers("UNICODE"); + public static readonly TSQLIdentifiers UPPER = new TSQLIdentifiers("UPPER"); + public static readonly TSQLIdentifiers ERROR_PROCEDURE = new TSQLIdentifiers("ERROR_PROCEDURE"); + public static readonly TSQLIdentifiers ERROR_SEVERITY = new TSQLIdentifiers("ERROR_SEVERITY"); + public static readonly TSQLIdentifiers ERROR_STATE = new TSQLIdentifiers("ERROR_STATE"); + public static readonly TSQLIdentifiers FORMATMESSAGE = new TSQLIdentifiers("FORMATMESSAGE"); + public static readonly TSQLIdentifiers GET_FILESTREAM_TRANSACTION_CONTEXT = new TSQLIdentifiers("GET_FILESTREAM_TRANSACTION_CONTEXT"); + public static readonly TSQLIdentifiers GETANSINULL = new TSQLIdentifiers("GETANSINULL"); + public static readonly TSQLIdentifiers BINARY_CHECKSUM = new TSQLIdentifiers("BINARY_CHECKSUM"); + public static readonly TSQLIdentifiers HOST_ID = new TSQLIdentifiers("HOST_ID"); + public static readonly TSQLIdentifiers CHECKSUM = new TSQLIdentifiers("CHECKSUM"); + public static readonly TSQLIdentifiers HOST_NAME = new TSQLIdentifiers("HOST_NAME"); + public static readonly TSQLIdentifiers COMPRESS = new TSQLIdentifiers("COMPRESS"); + public static readonly TSQLIdentifiers ISNULL = new TSQLIdentifiers("ISNULL"); + public static readonly TSQLIdentifiers CONNECTIONPROPERTY = new TSQLIdentifiers("CONNECTIONPROPERTY"); + public static readonly TSQLIdentifiers ISNUMERIC = new TSQLIdentifiers("ISNUMERIC"); + public static readonly TSQLIdentifiers CONTEXT_INFO = new TSQLIdentifiers("CONTEXT_INFO"); + public static readonly TSQLIdentifiers MIN_ACTIVE_ROWVERSION = new TSQLIdentifiers("MIN_ACTIVE_ROWVERSION"); + public static readonly TSQLIdentifiers CURRENT_REQUEST_ID = new TSQLIdentifiers("CURRENT_REQUEST_ID"); + public static readonly TSQLIdentifiers NEWID = new TSQLIdentifiers("NEWID"); + public static readonly TSQLIdentifiers CURRENT_TRANSACTION_ID = new TSQLIdentifiers("CURRENT_TRANSACTION_ID"); + public static readonly TSQLIdentifiers NEWSEQUENTIALID = new TSQLIdentifiers("NEWSEQUENTIALID"); + public static readonly TSQLIdentifiers DECOMPRESS = new TSQLIdentifiers("DECOMPRESS"); + public static readonly TSQLIdentifiers ROWCOUNT_BIG = new TSQLIdentifiers("ROWCOUNT_BIG"); + public static readonly TSQLIdentifiers ERROR_LINE = new TSQLIdentifiers("ERROR_LINE"); + public static readonly TSQLIdentifiers SESSION_CONTEXT = new TSQLIdentifiers("SESSION_CONTEXT"); + public static readonly TSQLIdentifiers ERROR_MESSAGE = new TSQLIdentifiers("ERROR_MESSAGE"); + public static readonly TSQLIdentifiers SESSION_ID = new TSQLIdentifiers("SESSION_ID"); + public static readonly TSQLIdentifiers ERROR_NUMBER = new TSQLIdentifiers("ERROR_NUMBER"); + public static readonly TSQLIdentifiers XACT_STATE = new TSQLIdentifiers("XACT_STATE"); + public static readonly TSQLIdentifiers TEXTPTR = new TSQLIdentifiers("TEXTPTR"); + public static readonly TSQLIdentifiers TEXTVALID = new TSQLIdentifiers("TEXTVALID"); + public static readonly TSQLIdentifiers COLUMNS_UPDATED = new TSQLIdentifiers("COLUMNS_UPDATED"); + public static readonly TSQLIdentifiers EVENTDATA = new TSQLIdentifiers("EVENTDATA"); + public static readonly TSQLIdentifiers TRIGGER_NESTLEVEL = new TSQLIdentifiers("TRIGGER_NESTLEVEL"); + public static readonly TSQLIdentifiers UPDATE = new TSQLIdentifiers("UPDATE"); #pragma warning restore 1591 diff --git a/TSQL_Parser/TSQL_Parser/TSQLVariables.cs b/TSQL_Parser/TSQL_Parser/TSQLVariables.cs index 767c827..d4691de 100644 --- a/TSQL_Parser/TSQL_Parser/TSQLVariables.cs +++ b/TSQL_Parser/TSQL_Parser/TSQLVariables.cs @@ -37,6 +37,17 @@ public struct TSQLVariables public static readonly TSQLVariables TOTAL_WRITE = new TSQLVariables("@@TOTAL_WRITE"); public static readonly TSQLVariables TRANCOUNT = new TSQLVariables("@@TRANCOUNT"); public static readonly TSQLVariables VERSION = new TSQLVariables("@@VERSION"); + public static readonly TSQLVariables DATEFIRST = new TSQLVariables("@@DATEFIRST"); + public static readonly TSQLVariables DBTS = new TSQLVariables("@@DBTS"); + public static readonly TSQLVariables LOCK_TIMEOUT = new TSQLVariables("@@LOCK_TIMEOUT"); + public static readonly TSQLVariables MAX_PRECISION = new TSQLVariables("@@MAX_PRECISION"); + public static readonly TSQLVariables NESTLEVEL = new TSQLVariables("@@NESTLEVEL"); + public static readonly TSQLVariables OPTIONS = new TSQLVariables("@@OPTIONS"); + public static readonly TSQLVariables REMSERVER = new TSQLVariables("@@REMSERVER"); + public static readonly TSQLVariables SERVICENAME = new TSQLVariables("@@SERVICENAME"); + public static readonly TSQLVariables CURSOR_ROWS = new TSQLVariables("@@CURSOR_ROWS"); + public static readonly TSQLVariables FETCH_STATUS = new TSQLVariables("@@FETCH_STATUS"); + public static readonly TSQLVariables PROCID = new TSQLVariables("@@PROCID"); #pragma warning restore 1591 diff --git a/TSQL_Parser/TSQL_Parser/TSQL_Parser.csproj b/TSQL_Parser/TSQL_Parser/TSQL_Parser.csproj index ab82ffe..4745879 100644 --- a/TSQL_Parser/TSQL_Parser/TSQL_Parser.csproj +++ b/TSQL_Parser/TSQL_Parser/TSQL_Parser.csproj @@ -113,6 +113,7 @@ + @@ -125,6 +126,7 @@ + diff --git a/TSQL_Parser/TSQL_Parser/TSQL_Parser.nuspec b/TSQL_Parser/TSQL_Parser/TSQL_Parser.nuspec index 5605a6e..b0a57a9 100644 --- a/TSQL_Parser/TSQL_Parser/TSQL_Parser.nuspec +++ b/TSQL_Parser/TSQL_Parser/TSQL_Parser.nuspec @@ -2,7 +2,7 @@ TSQL.Parser - 2.0.1 + 2.1.0 TSQL.Parser Bruce Dunwiddie shriop @@ -10,7 +10,7 @@ https://github.com/bruce-dunwiddie/tsql-parser false Library for Parsing SQL Server T-SQL Scripts - Fixed handling of COUNT(*) in new column parsing logic. + Fixed argument parsing for CAST function. Copyright © 2022 sql parser sql-server tsql diff --git a/TSQL_Parser/TSQL_Parser/TSQL_Parser_NetStandard.csproj b/TSQL_Parser/TSQL_Parser/TSQL_Parser_NetStandard.csproj index 385fb24..6e6be08 100644 --- a/TSQL_Parser/TSQL_Parser/TSQL_Parser_NetStandard.csproj +++ b/TSQL_Parser/TSQL_Parser/TSQL_Parser_NetStandard.csproj @@ -4,9 +4,9 @@ netstandard2.0 TSQL_Parser TSQL_Parser - 2.0.1.0 - 2.0.1.0 - 2.0.1.0 + 2.1.0.0 + 2.1.0.0 + 2.1.0.0 Library for Parsing SQL Server TSQL Scripts Copyright © 2022 diff --git a/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs b/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs index 5a8f2b3..378b2a5 100644 --- a/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs +++ b/TSQL_Parser/TSQL_Parser/Tokens/Parsers/TSQLTokenParserHelper.cs @@ -98,11 +98,20 @@ public static void RecurseParens( public static void ReadCommentsAndWhitespace( ITSQLTokenizer tokenizer, TSQLElement element) + { + ReadCommentsAndWhitespace( + tokenizer, + element.Tokens); + } + + public static void ReadCommentsAndWhitespace( + ITSQLTokenizer tokenizer, + List savedTokens) { while (tokenizer.Current.IsWhitespace() || tokenizer.Current.IsComment()) { - element.Tokens.Add(tokenizer.Current); + savedTokens.Add(tokenizer.Current); tokenizer.MoveNext(); } diff --git a/TSQL_Parser/TSQL_Parser/Tokens/TSQLTokenExtensions.cs b/TSQL_Parser/TSQL_Parser/Tokens/TSQLTokenExtensions.cs index 7e3b480..7571020 100644 --- a/TSQL_Parser/TSQL_Parser/Tokens/TSQLTokenExtensions.cs +++ b/TSQL_Parser/TSQL_Parser/Tokens/TSQLTokenExtensions.cs @@ -49,6 +49,26 @@ public static bool IsCharacter(this TSQLToken token, TSQLCharacters character) return true; } + public static bool IsIdentifier(this TSQLToken token, TSQLIdentifiers identifier) + { + if (token == null) + { + return false; + } + + if (token.Type != TSQLTokenType.SystemIdentifier) + { + return false; + } + + if (token.AsSystemIdentifier.Identifier != identifier) + { + return false; + } + + return true; + } + public static bool IsWhitespace(this TSQLToken token) { if (token == null) diff --git a/TSQL_Parser/Tests/Statements/SelectStatementTests.cs b/TSQL_Parser/Tests/Statements/SelectStatementTests.cs index 116dfaf..1d3fe9b 100644 --- a/TSQL_Parser/Tests/Statements/SelectStatementTests.cs +++ b/TSQL_Parser/Tests/Statements/SelectStatementTests.cs @@ -418,6 +418,7 @@ public void SelectStatement_StartWithParens() Assert.AreEqual(4, select.Tokens.Count); Assert.AreEqual(1, select.Select.Columns.Count); } + [Test] public void SelectStatement_UnionDontOverrun() { @@ -461,6 +462,49 @@ ORDER BY L1.ID Assert.IsNotNull(statements[0].AsSelect.OrderBy); } + [Test] + public void SelectStatement_UnionDontOverrunWithWhitespace() + { + List statements = TSQLStatementReader.ParseStatements( + @" + SELECT TOP 1 L1.ID + FROM + (SELECT 2 AS ID) L1 + WHERE + L1.ID = 2 + GROUP BY + L1.ID + HAVING COUNT(*) > 0 + + UNION ALL + + SELECT TOP 1 L2.ID + FROM + (SELECT 1 AS ID) L2 + WHERE + L2.ID = 1 + GROUP BY + L2.ID + HAVING COUNT(*) > 0 + + -- the only table alias in scope is the first one + -- but all rows from result of UNION are referenced + ORDER BY L1.ID + + OPTION (FAST 2) + + FOR XML PATH;", + includeWhitespace: true); + + Assert.AreEqual(1, statements.Count); + Assert.IsNotNull(statements[0].AsSelect.SetOperators.SingleOrDefault()); + Assert.AreEqual(61, statements[0].AsSelect.SetOperators.Single().Tokens.Count); + Assert.IsNotNull(statements[0].AsSelect.SetOperators.Single().Select); + Assert.AreEqual(57, statements[0].AsSelect.SetOperators.Single().Select.Tokens.Count); + Assert.IsNull(statements[0].AsSelect.SetOperators.Single().Select.OrderBy); + Assert.IsNotNull(statements[0].AsSelect.OrderBy); + } + [Test] public void SelectStatement_UnionParensRegression() { @@ -477,6 +521,22 @@ public void SelectStatement_UnionParensRegression() Assert.IsNotNull(select.SetOperators.SingleOrDefault()); } + [Test] + public void SelectStatement_UnionParensRegressionWithWhitespace() + { + // regression test for https://github.com/bruce-dunwiddie/tsql-parser/issues/69 + + List statements = TSQLStatementReader.ParseStatements( + @"SELECT 1 UNION (SELECT 2)", + includeWhitespace: true); + + TSQLSelectStatement select = statements[0].AsSelect; + + Assert.AreEqual(1, statements.Count); + Assert.AreEqual(11, select.Tokens.Count); + Assert.IsNotNull(select.SetOperators.SingleOrDefault()); + } + [Test] public void SelectStatement_UnionMultiParens() { @@ -493,6 +553,22 @@ public void SelectStatement_UnionMultiParens() Assert.AreEqual(2, select2.Tokens.Count); } + [Test] + public void SelectStatement_UnionMultiParensWithWhitespace() + { + List statements = TSQLStatementReader.ParseStatements( + @"SELECT 1 UNION ((SELECT 2)) SELECT 1", + includeWhitespace: true); + + TSQLSelectStatement select = statements[0].AsSelect; + TSQLSelectStatement select2 = statements[1].AsSelect; + + Assert.AreEqual(2, statements.Count); + Assert.AreEqual(14, select.Tokens.Count); + Assert.IsNotNull(select.SetOperators.SingleOrDefault()); + Assert.AreEqual(3, select2.Tokens.Count); + } + [Test] public void SelectStatement_MultipleSetOperatorsRegression() { @@ -546,6 +622,59 @@ SELECT 2 .Value); } + [Test] + public void SelectStatement_MultipleSetOperatorsRegressionWithWhitespace() + { + // regression test for https://github.com/bruce-dunwiddie/tsql-parser/issues/81 + List statements = TSQLStatementReader.ParseStatements( + @"SELECT 1 + UNION + SELECT 2 + UNION + SELECT 3", + includeWhitespace: true); + + Assert.AreEqual(1, statements.Count); + TSQLSelectStatement select = statements.Single().AsSelect; + Assert.AreEqual(15, select.Tokens.Count); + + Assert.AreEqual(2, select.SetOperators.Count); + + Assert.AreEqual(1, select + .Select + .Columns + .Single() + .Expression + .AsConstant + .Literal + .AsNumericLiteral + .Value); + + Assert.AreEqual(2, select + .SetOperators[0] + .Select + .Select + .Columns + .Single() + .Expression + .AsConstant + .Literal + .AsNumericLiteral + .Value); + + Assert.AreEqual(3, select + .SetOperators[1] + .Select + .Select + .Columns + .Single() + .Expression + .AsConstant + .Literal + .AsNumericLiteral + .Value); + } + [Test] public void SelectStatement_DistinctAndTop() { @@ -584,5 +713,36 @@ public void SelectStatement_CountAlias() Assert.AreEqual("COUNT", count.Expression.AsFunction.Function.Name); Assert.AreEqual("count", count.ColumnAlias.Name); } + + [Test] + public void SelectStatement_CountAliasWithWhitespace() + { + List statements = TSQLStatementReader.ParseStatements( + @"SELECT COUNT ( * ) as count FROM sqlite_master", + includeWhitespace: true); + + TSQLSelectColumn count = statements + .Single() + .AsSelect + .Select + .Columns + .Single(); + + Assert.AreEqual("COUNT", count.Expression.AsFunction.Function.Name); + Assert.AreEqual("count", count.ColumnAlias.Name); + } + + [Test] + public void SelectStatement_CASTMissingASRegression() + { + // regression test for https://github.com/bruce-dunwiddie/tsql-parser/issues/89 + List statements = TSQLStatementReader.ParseStatements( + @"SELECT CAST ( 123.45 AS INT ) ", + includeWhitespace: true); + + Assert.AreEqual(1, statements.Count); + TSQLSelectStatement select = statements.Single().AsSelect; + Assert.AreEqual(14, select.Tokens.Count); + } } } diff --git a/TSQL_Parser/Tests/Statements/WithStatementTests.cs b/TSQL_Parser/Tests/Statements/WithStatementTests.cs index b8d6a39..730a85b 100644 --- a/TSQL_Parser/Tests/Statements/WithStatementTests.cs +++ b/TSQL_Parser/Tests/Statements/WithStatementTests.cs @@ -77,5 +77,29 @@ SELECT 2 AS value Assert.IsInstanceOf(typeof(TSQLSelectStatement), statements[0]); Assert.AreEqual(24, statements[0].AsSelect.Tokens.Count); } + + [Test] + public void WithStatement_SelectInParensWithWhitespace() + { + List statements = TSQLStatementReader.ParseStatements( + @"WITH test AS + ( + SELECT 1 AS value + ), + test2 AS + ( + SELECT 2 AS value + ) + ( + SELECT * + FROM + test + )", + includeWhitespace: true); + + Assert.AreEqual(1, statements.Count); + Assert.IsInstanceOf(typeof(TSQLSelectStatement), statements[0]); + Assert.AreEqual(46, statements[0].AsSelect.Tokens.Count); + } } }