├── Laan.Sql.Formatter ├── readme.md ├── Interfaces │ ├── IStatementFormatter.cs │ ├── IExpressionFormatter.cs │ ├── IFormattingEngine.cs │ ├── IIndentable.cs │ └── Indentation.cs ├── Laan.Sql.Formatter.csproj ├── ExpressionFormatters │ ├── DefaultExpressionFormatter.cs │ ├── NegationExpressionFormatter.cs │ ├── SelectExpressionFormatter.cs │ ├── ExpressionListFormatter.cs │ ├── NestedExpressionFormatter.cs │ ├── BetweenExpressionFormatter.cs │ ├── CaseWhenExpressionFormatter.cs │ ├── CustomExpressionFormatter.cs │ └── FunctionExpressionFormatter.cs ├── StatementFormatters │ ├── DeclareStatementFormatter.cs │ ├── BlockStatementFormatter.cs │ ├── IfStatementFormatter.cs │ ├── GoTerminatorFormatter.cs │ ├── DeleteStatementFormatter.cs │ ├── RollbackTransactionStatementFormatter.cs │ ├── CommitTransactionStatementFormatter.cs │ ├── TransactionStatementFormatter.cs │ ├── BeginTransactionStatementFormatter.cs │ ├── CreateViewStatementFormatter.cs │ ├── CreateProcedureStatementFormatter.cs │ ├── VariableDefinitionFormatter.cs │ ├── ExecStatementFormatter.cs │ ├── CommonTableExpressionStatementFormatter.cs │ ├── UpdateStatementFormatter.cs │ └── ExecuteSqlStatementFormatter.cs ├── IndentScope.cs ├── Extensions.cs ├── FormattingEngine.cs └── Factories │ └── ExpressionFormatterFactory.cs ├── Laan.Sql.Parser ├── readme.md ├── Properties │ └── launchSettings.json ├── Entities │ ├── TableHint.cs │ ├── SetOperation.cs │ ├── DeleteStatement.cs │ ├── SetDeadlockPriorityStatement.cs │ ├── SetStatement.cs │ ├── Extensions.cs │ ├── SetOptionStatement.cs │ ├── SetVariableStatement.cs │ ├── Statement.cs │ ├── ITableHints.cs │ ├── GoTerminator.cs │ ├── IfStatement.cs │ ├── DeclareStatement.cs │ ├── GrantStatement.cs │ ├── InsertDeleteStatementBase.cs │ ├── SetDateFirstStatement.cs │ ├── Field.cs │ ├── ProjectionStatement.cs │ ├── BlockStatement.cs │ ├── CustomStatement.cs │ ├── IndexedColumn.cs │ ├── UpdateStatement.cs │ ├── Top.cs │ ├── CreateViewStatement.cs │ ├── InsertStatement.cs │ ├── VariableDefinition.cs │ ├── Identity.cs │ ├── CommonTableExpressionStatement.cs │ ├── ExecuteSqlStatement.cs │ ├── AlterTableStatement.cs │ ├── TransactionStatement.cs │ ├── CreateTableStatement.cs │ ├── Table.cs │ ├── SortedField.cs │ ├── CreateProcedureStatement.cs │ ├── SqlType.cs │ ├── SelectStatement.cs │ ├── AliasedEntities.cs │ └── CreateIndexStatement.cs ├── packages.config ├── Interfaces │ ├── IInlineFormattable.cs │ ├── IStatement.cs │ ├── IProjection.cs │ └── ITokenizer.cs ├── Parsers │ ├── TableStatementParser.cs │ ├── GoTerminatorParser.cs │ ├── BeginStatementParser.cs │ ├── DeleteStatementParser.cs │ ├── UpdateStatementParser.cs │ ├── DeclareStatementParser.cs │ ├── CreateViewStatementParser.cs │ ├── AlterStatementParser.cs │ ├── GrantStatementParser.cs │ ├── CreateStatementParser.cs │ ├── CreateProcedureStatementParser.cs │ ├── IfStatementParser.cs │ ├── VariableDefinitionParser.cs │ └── CommonTableExpressionStatementParser.cs ├── Utilities │ ├── BracketStructure.cs │ ├── EnumerableExtensions.cs │ └── SqlTypeParser.cs ├── Laan.Sql.Parser.csproj ├── Expressions │ ├── NegationExpression.cs │ ├── StringExpression.cs │ ├── SelectExpression.cs │ ├── IdentifierExpression.cs │ ├── Expression.cs │ ├── NestedExpression.cs │ ├── CountExpression.cs │ ├── ExpressionList.cs │ ├── CastExpression.cs │ ├── BetweenExpression.cs │ ├── CriteriaExpression.cs │ ├── FunctionExpression.cs │ ├── OperatorExpression.cs │ └── RankingFunctionExpression.cs ├── Tokenizer │ ├── CandidateDefinition.cs │ ├── Position.cs │ ├── TokenDefinition.cs │ ├── SqlTokenizer.cs │ └── CustomTokenizer.cs └── Exceptions.cs ├── Laan.Sql.Tools.sln.vsext.disable ├── Laan.NHibernate.Appender ├── readme.md ├── Laan.NHibernate.Appender.csproj └── ParamBuilderFormatter.cs ├── Scripts ├── build-app.cmd ├── test.ps1 └── build.ps1 ├── .nuget ├── NuGet.exe └── nuget.config ├── Laan.Sql.Formatter.Web.Blazor ├── Shared │ ├── MainLayout.razor │ └── NavBar.razor ├── wwwroot │ ├── manifest.json │ ├── service-worker.js │ ├── lib │ │ └── prettify │ │ │ ├── prettify.min.css │ │ │ └── lang-sql.min.js │ ├── js │ │ └── interop.js │ ├── css │ │ └── app.css │ ├── index.html │ └── service-worker.published.js ├── _Imports.razor ├── libman.json ├── App.razor ├── Laan.Sql.Formatter.Web.Blazor.csproj ├── Program.cs └── Properties │ └── launchSettings.json ├── Laan.AddIns.Ssms.VsExtension ├── key.snk ├── Resources │ └── SqlFormatCommand.png ├── Utils │ ├── Cursor.cs │ └── ScopedUndoContext.cs ├── Commands │ ├── SelectCursorLeftWordAction.cs │ ├── SelectCursorRightWordAction.cs │ ├── MoveCursorLeftWordAction.cs │ ├── MoveCursorRightWordAction.cs │ ├── WrapBracketAction.cs │ ├── WrapSquareBraceAction.cs │ ├── SqlTemplateOptionAction.cs │ ├── BaseCursorAction.cs │ ├── DuplicateLineAction.cs │ ├── SqlFormatterAction.cs │ ├── BaseLeftCusorAction.cs │ └── BaseRightCusorAction.cs ├── SqlTemplateOption │ ├── SqlTemplateOptionView.xaml.cs │ ├── DelegateCommand.cs │ ├── DialogHost.cs │ └── DialogHost.Designer.cs ├── source.extension.vsixmanifest ├── Properties │ └── AssemblyInfo.cs └── Models │ └── Template.cs ├── Laan.Sql.Formatter.Console ├── test.sql ├── Properties │ └── launchSettings.json ├── Laan.Sql.Formatter.Console.csproj ├── Argument.cs └── Program.cs ├── Laan.Sql.Formatter.Test ├── BaseFormattingTest.cs ├── App.config ├── Laan.Sql.Formatter.Test.csproj ├── TestCreateViewStatementFormatting.cs ├── TestTransactionsStatementFormatting.cs └── TestGoTerminatorStatementFormatting.cs ├── Options ├── App.config ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ └── AssemblyInfo.cs ├── App.xaml ├── App.xaml.cs ├── MainWindow.xaml └── MainWindow.xaml.cs ├── .gitignore ├── Build └── Nuget.targets ├── Laan.Sql.Parser.Test ├── Entities │ ├── TestStatement.cs │ └── TestAlias.cs ├── Laan.Sql.Parser.Test.csproj ├── TestBatchStatements.cs ├── Statements │ ├── TestBeginBlockStatementParser.cs │ ├── TestGrantStatementParser.cs │ ├── TestBatchTerminators.cs │ ├── TestStatementParsingWithComments.cs │ ├── TestTransactionParsers.cs │ └── TestIfStatements.cs ├── TestNHibernateStatements.cs └── TestTokens.cs ├── Laan.NHibernate.Appender.Test ├── Laan.NHibernate.Appender.Test.csproj └── ParamBuilderFormatterTest.cs └── licence.txt /Laan.Sql.Formatter/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Laan.Sql.Tools.sln.vsext.disable: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Laan.NHibernate.Appender/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Scripts/build-app.cmd: -------------------------------------------------------------------------------- 1 | pushd .\app 2 | au build --env %1 3 | popd -------------------------------------------------------------------------------- /.nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlaan/sqlformat/HEAD/.nuget/NuGet.exe -------------------------------------------------------------------------------- /Scripts/test.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlaan/sqlformat/HEAD/Scripts/test.ps1 -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/Shared/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | @Body 3 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlaan/sqlformat/HEAD/Laan.AddIns.Ssms.VsExtension/key.snk -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Console/test.sql: -------------------------------------------------------------------------------- 1 | SELECT A, B, C FROM [Table] T JOIN 2 | Other O ON O.Id = T.OtherId where A 3 | = 1 OR B 4 | =2 -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Test/BaseFormattingTest.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlaan/sqlformat/HEAD/Laan.Sql.Formatter.Test/BaseFormattingTest.cs -------------------------------------------------------------------------------- /Laan.Sql.Parser/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Laan.Sql.Parser": { 4 | "commandName": "Project" 5 | } 6 | } 7 | } -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Resources/SqlFormatCommand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benlaan/sqlformat/HEAD/Laan.AddIns.Ssms.VsExtension/Resources/SqlFormatCommand.png -------------------------------------------------------------------------------- /Scripts/build.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [string]$config = "debug", 3 | [string]$build = "" 4 | ) 5 | 6 | dotnet build .\Laan.Sql.Tools.sln -c $config -v minimal --interactive 7 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/TableHint.cs: -------------------------------------------------------------------------------- 1 | namespace Laan.Sql.Parser.Entities 2 | { 3 | public class TableHint 4 | { 5 | public string Hint { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Options/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | *.suo 4 | *.user 5 | *.PublishSettings 6 | PublishProfiles 7 | packages 8 | node_modules 9 | */publish/ 10 | .cr 11 | /.vs* 12 | /Build/VersionAssemblyInfo.cs 13 | /dist 14 | /.test-results 15 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Interfaces/IInlineFormattable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Expressions 4 | { 5 | public interface IInlineFormattable 6 | { 7 | bool CanInline { get; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SetOperation.cs: -------------------------------------------------------------------------------- 1 | namespace Laan.Sql.Parser.Entities 2 | { 3 | public class SetOperation 4 | { 5 | public SelectStatement Statement { get; set; } 6 | public SetType Type { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Interfaces/IStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser 4 | { 5 | public interface IStatement 6 | { 7 | bool Terminated { get; set; } 8 | string Identifier { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Options/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Console/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Laan.Sql.Formatter.Console": { 4 | "commandName": "Project", 5 | "commandLineArgs": " -Diagnostics -Output formatted.sql -Sql \"SELECT 1\"" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Interfaces/IStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Formatter 4 | { 5 | public interface IStatementFormatter 6 | { 7 | void Execute(); 8 | bool CanInline { get; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Interfaces/IExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Formatter 4 | { 5 | public interface IExpressionFormatter 6 | { 7 | int Offset { get; set; } 8 | string Execute(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Laan.Sql.Formatter.Web.Blazor", 3 | "short_name": "Laan.Sql.Formatter.Web.Blazor", 4 | "start_url": "./", 5 | "display": "standalone", 6 | "background_color": "#ffffff", 7 | "theme_color": "#03173d" 8 | } 9 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/DeleteStatement.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Laan.Sql.Parser.Entities 3 | { 4 | public class DeleteStatement : InsertDeleteStatementBase 5 | { 6 | public Top Top { get; set; } 7 | public string TableName { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Interfaces/IProjection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | 6 | namespace Laan.Sql.Parser 7 | { 8 | public interface IProjection 9 | { 10 | List Fields { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SetDeadlockPriorityStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public class SetDeadlockPriorityStatement : SetStatement 6 | { 7 | public SetDeadlockPriorityStatement() 8 | { 9 | 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/service-worker.js: -------------------------------------------------------------------------------- 1 | // In development, always fetch from the network and do not enable offline support. 2 | // This is because caching would make development more difficult (changes would not 3 | // be reflected on the first load after each change). 4 | self.addEventListener('fetch', () => { }); 5 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/TableStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Parsers 4 | { 5 | public abstract class TableStatementParser : StatementParser where T : class, IStatement 6 | { 7 | internal TableStatementParser( ITokenizer tokenizer ) : base( tokenizer ) { } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Interfaces/IFormattingEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser; 4 | 5 | namespace Laan.Sql.Formatter 6 | { 7 | public interface IFormattingEngine 8 | { 9 | string Execute( string sql ); 10 | int TabSize { get; set; } 11 | bool UseTabChar { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Options/App.xaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SetStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Laan.Sql.Parser.Expressions; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public abstract class SetStatement : Statement 7 | { 8 | public SetStatement() 9 | { 10 | 11 | } 12 | 13 | public Expression Assignment { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | internal static class Extensions 6 | { 7 | public static string WithBrackets( this string field ) 8 | { 9 | return String.Format( "[{0}]", field.Trim( new char[] { '[', ']' } ) ); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SetOptionStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Laan.Sql.Parser.Expressions; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class SetOptionStatement : SetStatement 7 | { 8 | public SetOptionStatement() 9 | { 10 | 11 | } 12 | 13 | public string Option { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SetVariableStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Laan.Sql.Parser.Expressions; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class SetVariableStatement : SetStatement 7 | { 8 | public SetVariableStatement() 9 | { 10 | 11 | } 12 | 13 | public string Variable { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Build/Nuget.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | $(Major).$(Minor).$(Build) 7 | Ben Laan 8 | benlaan.com 9 | 10 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/Statement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class Statement : IStatement 7 | { 8 | public bool Terminated { get; set; } 9 | 10 | public virtual string Identifier 11 | { 12 | get { return String.Empty; } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Utils/Cursor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.AddIns.Ssms.VsExtension.Utils 4 | { 5 | public class Cursor 6 | { 7 | public Cursor( int col, int row ) 8 | { 9 | Column = col; 10 | Row = row; 11 | } 12 | 13 | public int Column { get; set; } 14 | public int Row { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/ITableHints.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Laan.Sql.Parser.Entities; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | /// 7 | /// Entity that can have table hints 8 | /// 9 | public interface ITableHints 10 | { 11 | List TableHints { get; set; } 12 | bool ExplicitWith { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/GoTerminator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public class GoTerminator : IStatement 6 | { 7 | 8 | #region IStatement Members 9 | 10 | public bool Terminated { get; set; } 11 | public string Identifier 12 | { 13 | get { return "GO"; } 14 | } 15 | 16 | #endregion 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/IfStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | public class IfStatement : CustomStatement 9 | { 10 | public IStatement If { get; set; } 11 | public Expression Condition { get; set; } 12 | public IStatement Else { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Options/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace Options 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/DeclareStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class DeclareStatement : Statement 7 | { 8 | public DeclareStatement() 9 | { 10 | Definitions = new List(); 11 | } 12 | 13 | public List Definitions { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 7 | @using Microsoft.JSInterop 8 | @using Laan.Sql.Formatter.Web.Blazor 9 | @using Laan.Sql.Formatter.Web.Blazor.Shared 10 | @using Blazorise -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Interfaces/IIndentable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | 6 | using Laan.Sql.Parser; 7 | using Laan.Sql.Parser.Expressions; 8 | 9 | namespace Laan.Sql.Formatter 10 | { 11 | public interface IIndentable 12 | { 13 | void IncreaseIndent(); 14 | void DecreaseIndent(); 15 | string Indent { get; set; } 16 | int IndentLevel { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/GrantStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Laan.Sql.Parser.Expressions; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | public class GrantStatement : Statement 8 | { 9 | public GrantStatement() 10 | { 11 | 12 | } 13 | 14 | public string Operation { get; set; } 15 | public string TableName { get; set; } 16 | public string Grantee { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/InsertDeleteStatementBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public class InsertDeleteStatementBase : CustomStatement, ITableHints 6 | { 7 | internal InsertDeleteStatementBase() 8 | { 9 | TableHints = new List(); 10 | } 11 | 12 | public List TableHints { get; set; } 13 | public bool ExplicitWith { get; set; } 14 | } 15 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SetDateFirstStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Laan.Sql.Parser.Expressions; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class SetDateFirstStatement : SetStatement 7 | { 8 | public SetDateFirstStatement() 9 | { 10 | 11 | } 12 | 13 | } 14 | 15 | public class SetDateFormatStatement : SetStatement 16 | { 17 | public SetDateFormatStatement() 18 | { 19 | 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/GoTerminatorParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Entities; 4 | 5 | namespace Laan.Sql.Parser.Parsers 6 | { 7 | public class GoTerminatorParser : StatementParser 8 | { 9 | public GoTerminatorParser( ITokenizer tokenizer ) : base( tokenizer ) 10 | { 11 | } 12 | 13 | public override GoTerminator Execute() 14 | { 15 | return new GoTerminator(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Entities/TestStatement.cs: -------------------------------------------------------------------------------- 1 | using Laan.Sql.Parser.Entities; 2 | 3 | using NUnit.Framework; 4 | 5 | namespace Laan.Sql.Parser.Test.Entities 6 | { 7 | [TestFixture] 8 | public class TestStatement 9 | { 10 | [Test] 11 | public void Get_Identifier() 12 | { 13 | var sut = new Statement(); 14 | 15 | var result = sut.Identifier; 16 | 17 | Assert.AreEqual( string.Empty, result ); 18 | } 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Console/Laan.Sql.Formatter.Console.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | Program 5 | sqlformat 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Laan.Sql.Formatter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | readme.md 5 | SQL Formatter component 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/Field.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | using Laan.Sql.Parser.Expressions; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | [DebuggerDisplay( "{Expression.Value} ({Alias})" )] 8 | public class Field : AliasedEntity 9 | { 10 | /// 11 | /// Initializes a new instance of the Field class. 12 | /// 13 | public Field() 14 | { 15 | } 16 | 17 | public Expression Expression { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /Options/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/SelectCursorLeftWordAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class SelectCursorLeftWordAction : BaseLeftCusorAction 9 | { 10 | public override int CommandId => 4008; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | CursorLeft(true); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/SelectCursorRightWordAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class SelectCursorRightWordAction : BaseRightCusorAction 9 | { 10 | public override int CommandId => 4009; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | CursorRight(true); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/DefaultExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Expressions; 4 | 5 | namespace Laan.Sql.Formatter 6 | { 7 | public class DefaultExpressionFormatter : CustomExpressionFormatter 8 | { 9 | public DefaultExpressionFormatter( Expression expression ) 10 | : base( expression ) 11 | { 12 | 13 | } 14 | 15 | public override string Execute() 16 | { 17 | return _expression.Value; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Utilities/BracketStructure.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser 4 | { 5 | public class BracketStructure : IDisposable 6 | { 7 | private ITokenizer _tokenizer; 8 | 9 | public BracketStructure(ITokenizer tokenizer) 10 | { 11 | _tokenizer = tokenizer; 12 | _tokenizer.ExpectToken(Constants.OpenBracket); 13 | } 14 | 15 | public void Dispose() 16 | { 17 | _tokenizer.ExpectToken(Constants.CloseBracket); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [ 5 | { 6 | "library": "twitter-bootstrap@4.5.2", 7 | "destination": "wwwroot/lib/bootstrap", 8 | "files": [ 9 | "css/bootstrap.min.css" 10 | ] 11 | }, 12 | { 13 | "library": "prettify@r298", 14 | "destination": "wwwroot/lib/prettify", 15 | "files": [ 16 | "prettify.min.js", 17 | "lang-sql.min.js", 18 | "prettify.min.css" 19 | ] 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /.nuget/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/ProjectionStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Laan.Sql.Parser.Expressions; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | public class ProjectionStatement : CustomStatement 8 | { 9 | /// 10 | /// Initializes a new instance of the BaseStatement class. 11 | /// 12 | public ProjectionStatement() 13 | { 14 | Fields = new List(); 15 | } 16 | 17 | public List Fields { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/BlockStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | public class BlockStatement : CustomStatement 9 | { 10 | /// 11 | /// Initializes a new instance of the BlockStatement class. 12 | /// 13 | public BlockStatement() 14 | { 15 | Statements = new List(); 16 | } 17 | 18 | public List Statements { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/CustomStatement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using Laan.Sql.Parser.Expressions; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | public class CustomStatement : Statement 8 | { 9 | /// 10 | /// Initializes a new instance of the BaseStatement class. 11 | /// 12 | public CustomStatement() 13 | { 14 | From = new List(); 15 | } 16 | 17 | public List
From { get; set; } 18 | public Expression Where { get; set; } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/IndexedColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public enum Order 6 | { 7 | Ascending, Descending 8 | } 9 | 10 | public class IndexedColumn 11 | { 12 | /// 13 | /// Initializes a new instance of the IndexedColumn class. 14 | /// 15 | public IndexedColumn() 16 | { 17 | Order = Order.Ascending; 18 | } 19 | 20 | public string Name { get; set; } 21 | public Order Order { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/UpdateStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Laan.Sql.Parser.Entities; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | public class UpdateStatement : ProjectionStatement, ITableHints 8 | { 9 | public Top Top { get; set; } 10 | public string TableName { get; set; } 11 | public List TableHints { get; set; } 12 | public bool ExplicitWith { get; set; } 13 | 14 | public UpdateStatement() 15 | { 16 | TableHints = new List(); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Utilities/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Laan.Sql.Parser 6 | { 7 | public static class EnumerableExtensions 8 | { 9 | public static string Join(this IEnumerable enumerable, string separator) 10 | { 11 | return String.Join(separator, enumerable.Select(e => e.ToString()).ToArray()); 12 | } 13 | 14 | public static string ToCsv(this IEnumerable enumerable) 15 | { 16 | return enumerable.Join(", "); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/Top.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | public class Top 9 | { 10 | public Top(Expression value) 11 | { 12 | Expression = value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return String.Format(" TOP {0}{1}", Expression.Value, Percent ? " PERCENT " : ""); 18 | } 19 | 20 | public Expression Expression { get; set; } 21 | public bool Percent { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Laan.Sql.Parser.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Interfaces/Indentation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser; 4 | 5 | namespace Laan.Sql.Formatter 6 | { 7 | public class Indentation : IIndentable 8 | { 9 | public Indentation() 10 | { 11 | IndentLevel = 0; 12 | Indent = new string(' ', 4); 13 | } 14 | 15 | public void IncreaseIndent() 16 | { 17 | IndentLevel++; 18 | } 19 | public void DecreaseIndent() 20 | { 21 | IndentLevel--; 22 | } 23 | 24 | public string Indent { get; set; } 25 | public int IndentLevel { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/NegationExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Expressions; 4 | using System.Text; 5 | 6 | namespace Laan.Sql.Formatter 7 | { 8 | public class NegationExpressionFormatter : CustomExpressionFormatter 9 | { 10 | public NegationExpressionFormatter( NegationExpression expression ) : base( expression ) 11 | { 12 | } 13 | 14 | #region IExpressionFormatter Members 15 | 16 | public override string Execute() 17 | { 18 | return "NOT " + _expression.Expression.FormattedValue( Offset, this ); 19 | } 20 | 21 | #endregion 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Utils/ScopedUndoContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.Shell; 3 | 4 | namespace Laan.AddIns.Ssms.VsExtension.Utils 5 | { 6 | internal class ScopedUndoContext : IDisposable 7 | { 8 | private BaseAction _action; 9 | 10 | public ScopedUndoContext(BaseAction addIn, string name) 11 | { 12 | ThreadHelper.ThrowIfNotOnUIThread(); 13 | 14 | _action = addIn; 15 | _action.OpenUndoContext(name, true); 16 | } 17 | 18 | public void Dispose() 19 | { 20 | ThreadHelper.ThrowIfNotOnUIThread(); 21 | 22 | _action.CloseUndoContext(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/CreateViewStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public class CreateViewStatement : Statement 6 | { 7 | public IStatement Definition { get; set; } 8 | public bool IsAlter { get; set; } 9 | public string Name { get; set; } 10 | 11 | public override string Identifier 12 | { 13 | get 14 | { 15 | return String.Format( 16 | "{0} VIEW {1} AS {2}", 17 | IsAlter ? "ALTER" : "CREATE", 18 | Name, 19 | Definition.Identifier 20 | ); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/lib/prettify/prettify.min.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:700}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:700}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:700}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/Shared/NavBar.razor: -------------------------------------------------------------------------------- 1 | 6 | Laan Sql Format 7 | 8 | 9 | 10 | 11 | Read the code 12 | 13 | 14 | Found a bug? 15 | 16 | 17 | 18 | 19 | by Ben Laan 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/SelectExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Formatter 7 | { 8 | public class SelectExpressionFormatter : CustomExpressionFormatter 9 | { 10 | public SelectExpressionFormatter(SelectExpression expression) : base(expression) 11 | { 12 | } 13 | 14 | public override string Execute() 15 | { 16 | var sql = new StringBuilder(); 17 | var formatter = new SelectStatementFormatter(this, sql, _expression.Statement); 18 | formatter.Execute(); 19 | return sql.ToString(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/InsertStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | public class InsertStatement : InsertDeleteStatementBase 9 | { 10 | public string TableName { get; set; } 11 | public bool HasInto { get; set; } 12 | public List Columns { get; set; } 13 | public List> Values { get; set; } 14 | public SelectStatement SourceStatement { get; set; } 15 | 16 | internal InsertStatement() 17 | { 18 | Columns = new List(); 19 | Values = new List>(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/VariableDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | [DebuggerDisplay("{Name}: {Type} = {DefaultValue}")] 9 | public class VariableDefinition 10 | { 11 | /// 12 | /// Initializes a new instance of the VariableDefinition class. 13 | /// 14 | public VariableDefinition(string name, string type) 15 | { 16 | Name = name; 17 | Type = type; 18 | } 19 | 20 | public string Name { get; set; } 21 | public string Type { get; set; } 22 | public Expression DefaultValue { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/MoveCursorLeftWordAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class MoveCursorLeftWordAction : BaseLeftCusorAction 9 | { 10 | public override int CommandId => 4006; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | CursorLeft(false); 17 | } 18 | 19 | protected override bool CanExecute() 20 | { 21 | ThreadHelper.ThrowIfNotOnUIThread(); 22 | 23 | return base.CanExecute() 24 | && CurrentSelection.Length == 0; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Laan.Sql.Parser.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | readme.md 5 | SQL Parser component 6 | 7 | 8 | 9 | 10 | all 11 | runtime; build; native; contentfiles; analyzers; buildtransitive 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/MoveCursorRightWordAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class MoveCursorRightWordAction : BaseRightCusorAction 9 | { 10 | public override int CommandId => 4007; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | CursorRight(false); 17 | } 18 | 19 | protected override bool CanExecute() 20 | { 21 | ThreadHelper.ThrowIfNotOnUIThread(); 22 | 23 | return base.CanExecute() 24 | && CurrentSelection.Length == 0; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Laan.NHibernate.Appender.Test/Laan.NHibernate.Appender.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/TestBatchStatements.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using NUnit.Framework; 6 | 7 | using Laan.Sql.Parser.Entities; 8 | 9 | namespace Laan.Sql.Parser.Test 10 | { 11 | [TestFixture] 12 | public class TestBatchStatements 13 | { 14 | [Test] 15 | public void Implicit_Termination_Of_Statement() 16 | { 17 | // Setup 18 | var sql = "SELECT * FROM Table SELECT * FROM OtherTable"; 19 | 20 | // Exercise 21 | List statements = ParserFactory.Execute( sql ); 22 | 23 | // Verify outcome 24 | Assert.AreEqual( 2, statements.Count ); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Laan.NHibernate.Appender/Laan.NHibernate.Appender.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | readme.md 5 | SQL Formatter for NHibernate via the Log4net Appender infrastructure 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/ExpressionListFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Formatter 7 | { 8 | public class ExpressionListFormatter : CustomExpressionFormatter 9 | { 10 | public ExpressionListFormatter( ExpressionList expression ) : base( expression ) 11 | { 12 | } 13 | 14 | #region IExpressionFormatter Members 15 | 16 | public override string Execute() 17 | { 18 | return GetIndent( false ) + 19 | String.Join( ", ", _expression.Identifiers.Select( id => id.FormattedValue( Offset, this ) ).ToArray() 20 | ); 21 | } 22 | 23 | #endregion 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/js/interop.js: -------------------------------------------------------------------------------- 1 | window.copyFormatted = function () { 2 | 3 | var formattedCode = document.getElementById("formattedCode"); 4 | 5 | var range = document.createRange(); 6 | range.selectNode(formattedCode); 7 | window.getSelection().addRange(range); 8 | var success = document.execCommand("copy"); 9 | window.getSelection().empty(); 10 | 11 | return success; 12 | 13 | } 14 | 15 | window.prettify = function () { 16 | 17 | var formattedCode = document.getElementById("formattedCode"); 18 | var value = formattedCode.textContent; 19 | if (value) { 20 | 21 | pretty = prettyPrintOne(value) 22 | this.formattedCode.innerHTML = pretty; 23 | return true; 24 | } 25 | 26 | return false; 27 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/Identity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public class Identity 6 | { 7 | public int Start { get; set; } 8 | public int Increment { get; set; } 9 | 10 | public override int GetHashCode() 11 | { 12 | return Start.GetHashCode() + Increment.GetHashCode(); 13 | } 14 | 15 | public override bool Equals( object obj ) 16 | { 17 | Identity other = (Identity) obj; 18 | return Start == other.Start && Increment == other.Increment; 19 | } 20 | 21 | public override string ToString() 22 | { 23 | return String.Format( "IDENTITY({0}{1}{2})", Start, ",", Increment ); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/NegationExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Expressions 4 | { 5 | public class NegationExpression : Expression 6 | { 7 | /// 8 | /// Initializes a new instance of the NegationExpression class. 9 | /// 10 | public NegationExpression( Expression parent ) : base ( parent ) 11 | { 12 | } 13 | 14 | public Expression Expression { get; set; } 15 | 16 | public override string Value 17 | { 18 | get { return String.Format( "{0} {1}", Constants.Not, Expression.Value ); } 19 | } 20 | 21 | public override bool CanInline 22 | { 23 | get { return Expression.CanInline; } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/StringExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace Laan.Sql.Parser.Expressions 3 | { 4 | public class StringExpression : Expression 5 | { 6 | public StringExpression( string value, Expression parent ) : base ( parent ) 7 | { 8 | Value = value; 9 | } 10 | 11 | public override bool CanInline 12 | { 13 | get { return true; } 14 | } 15 | 16 | public string Content 17 | { 18 | get 19 | { 20 | string content = Value.Replace("''", "'"); 21 | if (content.StartsWith("N'")) 22 | content = content.Substring(1); 23 | return content.Trim(new[] { '\'' }); 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/WrapBracketAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class WrapBracketAction : BaseAction 9 | { 10 | public override int CommandId => 4004; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | TextDocument.Selection.Text = String.Format("({0})", TextDocument.Selection.Text); 17 | } 18 | 19 | protected override bool CanExecute() 20 | { 21 | ThreadHelper.ThrowIfNotOnUIThread(); 22 | 23 | return IsCurrentDocumentExtension("sql") 24 | && CurrentSelection.Length > 0; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/SelectExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | using Laan.Sql.Parser.Entities; 8 | 9 | namespace Laan.Sql.Parser.Expressions 10 | { 11 | public class SelectExpression : Expression 12 | { 13 | 14 | public SelectExpression() : base( null ) 15 | { 16 | Statement = new SelectStatement(); 17 | } 18 | 19 | public SelectStatement Statement { get; set; } 20 | 21 | public override string Value 22 | { 23 | get { return Statement.ToString(); } 24 | } 25 | 26 | public override bool CanInline 27 | { 28 | get { return Statement.CanInLine(); } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Test/Laan.Sql.Formatter.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/WrapSquareBraceAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class WrapSquareBraceAction : BaseAction 9 | { 10 | public override int CommandId => 4003; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | TextDocument.Selection.Text = String.Format("[{0}]", TextDocument.Selection.Text); 17 | } 18 | 19 | protected override bool CanExecute() 20 | { 21 | ThreadHelper.ThrowIfNotOnUIThread(); 22 | 23 | return IsCurrentDocumentExtension("sql") 24 | && CurrentSelection.Length > 0; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/DeclareStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class DeclareStatementFormatter : StatementFormatter, IStatementFormatter 11 | { 12 | public DeclareStatementFormatter(IIndentable indentable, StringBuilder sql, DeclareStatement statement) 13 | : base(indentable, sql, statement) 14 | { 15 | } 16 | 17 | public void Execute() 18 | { 19 | IndentAppend("DECLARE"); 20 | 21 | var formatter = new VariableDefinitionFormatter(_statement.Definitions, this, _sql); 22 | formatter.Execute(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Statements/TestBeginBlockStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | 6 | using NUnit.Framework; 7 | 8 | namespace Laan.Sql.Parser.Test 9 | { 10 | [TestFixture] 11 | public class TestBeginBlockStatementParser 12 | { 13 | [Test] 14 | [TestCase("begin select id from t end", 1)] 15 | [TestCase("begin select id from t select id from x end", 2)] 16 | public void Begin_End_Block(string sql, int statementCount) 17 | { 18 | // Exercise 19 | var statement = ParserFactory.Execute(sql).First(); 20 | 21 | // Verify outcome 22 | Assert.IsNotNull(statement); 23 | Assert.AreEqual(statementCount, statement.Statements.Count); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/BeginStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Entities; 4 | 5 | namespace Laan.Sql.Parser.Parsers 6 | { 7 | public class BeginStatementParser : CustomParser, IParser 8 | { 9 | public BeginStatementParser(ITokenizer tokenizer) : base(tokenizer) 10 | { 11 | } 12 | 13 | public IStatement Execute() 14 | { 15 | if (Tokenizer.IsNextToken(Constants.Tran, Constants.Transaction, Constants.Distributed)) 16 | return new BeginTransactionStatementParser(Tokenizer).Execute(); 17 | 18 | var statement = new BlockStatement(); 19 | statement.Statements.AddRange(ParserFactory.Execute(Tokenizer, false)); 20 | Tokenizer.ExpectToken(Constants.End); 21 | return statement; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/SqlTemplateOption/SqlTemplateOptionView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows; 6 | using System.Windows.Controls; 7 | using System.Windows.Data; 8 | using System.Windows.Documents; 9 | using System.Windows.Input; 10 | using System.Windows.Media; 11 | using System.Windows.Media.Imaging; 12 | using System.Windows.Navigation; 13 | using System.Windows.Shapes; 14 | 15 | namespace Laan.AddIns.Ssms.VsExtension.SqlTemplateOption 16 | { 17 | /// 18 | /// Interaction logic for SqlTemplateOptionView.xaml 19 | /// 20 | public partial class SqlTemplateOptionView : UserControl 21 | { 22 | public SqlTemplateOptionView() 23 | { 24 | InitializeComponent(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/App.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Sorry, there's nothing at this address.

9 |
10 |
11 |
12 |
13 | 14 | @code { 15 | 16 | private Theme theme = new Theme 17 | { 18 | IsRounded = false, 19 | ColorOptions = new ThemeColorOptions 20 | { 21 | Primary = "#337ab7", 22 | }, 23 | BackgroundOptions = new ThemeBackgroundOptions 24 | { 25 | Primary = "#337ab7" 26 | } 27 | }; 28 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/IdentifierExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | public class IdentifierExpression : Expression 10 | { 11 | public IdentifierExpression( string value, Expression parent ) : base( parent ) 12 | { 13 | Parts = value.Split( new string[] { Constants.Dot }, StringSplitOptions.RemoveEmptyEntries ).ToList(); 14 | } 15 | 16 | public List Parts { get; set; } 17 | 18 | public override string Value 19 | { 20 | get { return String.Join( Constants.Dot, Parts.ToArray() ); } 21 | } 22 | 23 | public override bool CanInline 24 | { 25 | get { return true; } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/IndentScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser; 4 | 5 | namespace Laan.Sql.Formatter 6 | { 7 | internal class IndentScope : IDisposable 8 | { 9 | private IIndentable _formatter; 10 | private bool _indent; 11 | 12 | public IndentScope( IIndentable formatter ) : this( formatter, true ) 13 | { 14 | } 15 | 16 | public IndentScope( IIndentable formatter, bool indent ) 17 | { 18 | _indent = indent; 19 | _formatter = formatter; 20 | 21 | if ( _indent ) 22 | _formatter.IncreaseIndent(); 23 | } 24 | 25 | #region IDisposable Members 26 | 27 | public void Dispose() 28 | { 29 | if ( _indent ) 30 | _formatter.DecreaseIndent(); 31 | } 32 | 33 | #endregion 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/DeleteStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Entities; 4 | 5 | namespace Laan.Sql.Parser.Parsers 6 | { 7 | public class DeleteStatementParser : CriteriaStatementParser 8 | { 9 | public DeleteStatementParser( ITokenizer tokenizer ) : base( tokenizer ) { } 10 | 11 | public override DeleteStatement Execute() 12 | { 13 | _statement = new DeleteStatement(); 14 | 15 | if ( Tokenizer.IsNextToken( Constants.Top ) ) 16 | _statement.Top = GetTop(); 17 | 18 | if ( !Tokenizer.IsNextToken( Constants.From ) ) 19 | _statement.TableName = GetTableName(); 20 | 21 | ProcessFrom(); 22 | ProcessWhere(); 23 | ProcessTerminator(); 24 | 25 | return _statement; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/Expression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | [DebuggerDisplay( "{Value}" )] 10 | public class Expression : IInlineFormattable 11 | { 12 | /// 13 | /// Initializes a new instance of the Expression class. 14 | /// 15 | public Expression( Expression parent ) 16 | { 17 | Parent = parent; 18 | } 19 | 20 | public virtual string Value { get; protected set; } 21 | public Expression Parent { get; set; } 22 | 23 | #region IInlineFormattable Members 24 | 25 | public virtual bool CanInline 26 | { 27 | get { return false; } 28 | } 29 | 30 | #endregion 31 | } 32 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/CommonTableExpressionStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | public class CommonTableExpression : SelectStatement 8 | { 9 | public CommonTableExpression() 10 | { 11 | ColumnNames = new List(); 12 | } 13 | 14 | public string Name { get; set; } 15 | public List ColumnNames { get; set; } 16 | } 17 | 18 | public class CommonTableExpressionStatement : CustomStatement 19 | { 20 | public CommonTableExpressionStatement() 21 | { 22 | CommonTableExpressions = new List(); 23 | } 24 | 25 | public List CommonTableExpressions { get; set; } 26 | public SelectStatement Statement { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/NestedExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | public class NestedExpression : Expression 10 | { 11 | /// 12 | /// Initializes a new instance of the NestedExpression class. 13 | /// 14 | public NestedExpression( Expression parent ) : base ( parent ) 15 | { 16 | } 17 | 18 | public Expression Expression { get; set; } 19 | 20 | public override string Value 21 | { 22 | get { return Constants.OpenBracket + Expression.Value + Constants.CloseBracket; } 23 | } 24 | 25 | public override bool CanInline 26 | { 27 | get { return Expression.CanInline; } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/CountExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Laan.Sql.Parser.Expressions 5 | { 6 | public class CountExpression : FunctionExpression 7 | { 8 | public CountExpression(Expression parent) : base(parent) 9 | { 10 | Name = Constants.Count; 11 | } 12 | 13 | public CountExpression(Expression parent, bool distinct) : this(parent) 14 | { 15 | Distinct = distinct; 16 | } 17 | 18 | public override bool CanInline 19 | { 20 | get { return true; } 21 | } 22 | 23 | public bool Distinct { get; set; } 24 | 25 | public override string Value 26 | { 27 | get 28 | { 29 | return String.Format("COUNT({0}{1})", Distinct ? "DISTINCT " : "", Arguments.First().Value); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/SqlTemplateOptionAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | using Laan.AddIns.Forms; 7 | using Laan.AddIns.Ssms.VsExtension.Models; 8 | using Laan.AddIns.Ssms.VsExtension.SqlTemplateOption; 9 | 10 | namespace Laan.AddIns.Ssms.VsExtension.Commands 11 | { 12 | public class SqlTemplateOptionAction : BaseAction 13 | { 14 | public override int CommandId => 4002; 15 | 16 | protected override void Execute() 17 | { 18 | var dialogHost = new DialogHost(); 19 | 20 | var templates = TemplateDocument.Load(); 21 | var viewModel = new SqlTemplateOptionViewModel(templates); 22 | 23 | if (dialogHost.Show(viewModel) == DialogResult.OK) 24 | TemplateDocument.Save(viewModel.Templates.ToList()); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/ExecuteSqlStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class Argument 7 | { 8 | public string Name { get; set; } 9 | public string Type { get; set; } 10 | public object Value { get; set; } 11 | } 12 | 13 | public class ExecStatement : Statement 14 | { 15 | public ExecStatement() 16 | { 17 | Arguments = new List(); 18 | } 19 | 20 | public string FunctionName { get; set; } 21 | public List Arguments { get; private set; } 22 | } 23 | 24 | public class ExecuteSqlStatement : ExecStatement 25 | { 26 | public ExecuteSqlStatement() 27 | { 28 | InnerStatements = new List(); 29 | } 30 | 31 | public IList InnerStatements { get; set; } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/ExpressionList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | public class ExpressionList : Expression, IInlineFormattable 10 | { 11 | public ExpressionList() : base( null ) 12 | { 13 | Identifiers = new List(); 14 | } 15 | 16 | public List Identifiers { get; set; } 17 | 18 | public override string Value 19 | { 20 | get { return String.Join( ", ", Identifiers.Select( id => id.Value ).ToArray() ); } 21 | } 22 | 23 | #region IInlineFormattable Members 24 | 25 | public override bool CanInline 26 | { 27 | get { return Value.Length < 80 && Identifiers.All( id => id.CanInline ); } 28 | } 29 | 30 | #endregion 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Tokenizer/CandidateDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace Laan.Sql.Parser 7 | { 8 | [DebuggerDisplay("\\{ Position = {Position}, Match = {Match}, Definition = {Definition} \\}")] 9 | public class CandidateDefinition 10 | { 11 | public CandidateDefinition(int position, Match match, TokenDefinition definition) 12 | { 13 | Position = position; 14 | Match = match; 15 | Definition = definition; 16 | } 17 | 18 | public override string ToString() 19 | { 20 | return $"{{ Position = {Position}, Match = {Match}, Definition = {Definition} }}"; 21 | } 22 | 23 | public int Position { get; private set; } 24 | public Match Match { get; private set; } 25 | public TokenDefinition Definition { get; private set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/TestNHibernateStatements.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Collections.Generic; 3 | using System; 4 | 5 | using Laan.Sql.Parser.Entities; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Laan.Sql.Parser.Test 10 | { 11 | [TestFixture] 12 | public class TestNHibernateStatements 13 | { 14 | private const string sample = 15 | @"Batch commands: 16 | command 0:INSERT INTO dbo.[SimpleEntity] (Version, Name, UserName, Created, Modified, IsDeleted, Id) VALUES (@p0, @p1, @p2, @p3, @p4, @p5, @p6)"; 17 | 18 | [Test] 19 | public void Test_ParserFactory_Correctly_Strips_Batch_Commands_Preamble() 20 | { 21 | // Arrange 22 | 23 | // Act 24 | List results = ParserFactory.Execute(sample); 25 | 26 | 27 | // Assert 28 | Assert.IsNotEmpty(results); 29 | Assert.IsInstanceOf(results.First()); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/CastExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | 6 | namespace Laan.Sql.Parser.Expressions 7 | { 8 | public class CastExpression : FunctionExpression 9 | { 10 | public CastExpression( Expression parent ) : base( parent ) 11 | { 12 | Name = Constants.Cast; 13 | } 14 | 15 | public CastExpression( Expression parent, SqlType outputType) : this( parent ) 16 | { 17 | OutputType = outputType; 18 | } 19 | 20 | public override bool CanInline 21 | { 22 | get { return true; } 23 | } 24 | 25 | public SqlType OutputType { get; set; } 26 | 27 | public override string Value 28 | { 29 | get 30 | { 31 | return String.Format( "CAST({0} AS {1})", Arguments.First().Value, OutputType ); 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Tokenizer/Position.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser 4 | { 5 | public class Position 6 | { 7 | private CustomTokenizer _tokenizer; 8 | 9 | public Position( CustomTokenizer tokenizer ) 10 | { 11 | _tokenizer = tokenizer; 12 | Row = 0; 13 | Column = 1; 14 | } 15 | 16 | public override string ToString() 17 | { 18 | int currentTokenLength = 0; 19 | if ( _tokenizer != null && _tokenizer.Current != (Token) null ) 20 | currentTokenLength = _tokenizer.Current.Value.Length; 21 | 22 | return String.Format( "Row: {0}, Col: {1}", Row, Column - currentTokenLength ); 23 | } 24 | 25 | internal void NewRow() 26 | { 27 | Row++; 28 | Column = 1; 29 | } 30 | 31 | public int Column { get; set; } 32 | public int Row { get; set; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/UpdateStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Entities; 4 | 5 | namespace Laan.Sql.Parser.Parsers 6 | { 7 | public class UpdateStatementParser : CriteriaStatementParser 8 | { 9 | public UpdateStatementParser( ITokenizer tokenizer ) : base( tokenizer ) { } 10 | 11 | public override UpdateStatement Execute() 12 | { 13 | _statement = new UpdateStatement(); 14 | 15 | if ( Tokenizer.IsNextToken( Constants.Top ) ) 16 | _statement.Top = GetTop(); 17 | 18 | _statement.TableName = GetTableName(); 19 | 20 | ProcessTableHints( _statement ); 21 | Tokenizer.ExpectToken( Constants.Set ); 22 | 23 | ProcessFields( FieldType.Update, _statement.Fields ); 24 | ProcessFrom(); 25 | ProcessWhere(); 26 | ProcessTerminator(); 27 | 28 | return _statement; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/BaseCursorAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Microsoft.VisualStudio.Shell; 4 | 5 | namespace Laan.AddIns.Ssms.VsExtension 6 | { 7 | public abstract class BaseCursorAction : BaseAction 8 | { 9 | protected static bool IsCapital(string line, int position) 10 | { 11 | if (position < 0) 12 | return true; 13 | 14 | return line[position] >= 'A' && line[position] <= 'Z'; 15 | } 16 | 17 | protected static bool IsSpace(string rightOfCursor, int position) 18 | { 19 | if (position > rightOfCursor.Length) 20 | return false; 21 | 22 | return rightOfCursor[position] == ' '; 23 | } 24 | 25 | protected override bool CanExecute() 26 | { 27 | ThreadHelper.ThrowIfNotOnUIThread(); 28 | 29 | return IsCurrentDocumentExtension("sql") 30 | && AllText.Length > 0; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/css/app.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | height: 100vh; 6 | } 7 | 8 | .container-main { 9 | height: 100vh; 10 | } 11 | 12 | .flex-parent { 13 | display: flex; 14 | flex-direction: column; 15 | transition: all 0.5s; 16 | } 17 | 18 | .flex-fill { 19 | flex: 1 !important; 20 | } 21 | 22 | textarea { 23 | box-sizing: border-box; 24 | resize: none; 25 | color: black; 26 | border-width: 0 !important; 27 | width: 100%; 28 | height: 100% !important; 29 | font-family: monospace 30 | } 31 | 32 | .expand { 33 | flex-grow: 3 !important; 34 | } 35 | 36 | .shrink { 37 | max-height: 100px; 38 | min-height: 100px; 39 | } 40 | 41 | .panel-body { 42 | padding: 0; 43 | margin: 15px; 44 | } 45 | 46 | .card { 47 | flex: 1 !important; 48 | width: 100% !important; 49 | } 50 | 51 | .row { 52 | padding: 15px; 53 | } 54 | 55 | pre { 56 | 57 | overflow: auto !important; 58 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/DeclareStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | using Laan.Sql.Parser.Exceptions; 6 | 7 | namespace Laan.Sql.Parser.Parsers 8 | { 9 | public class DeclareStatementParser : StatementParser 10 | { 11 | public DeclareStatementParser(ITokenizer tokenizer) : base(tokenizer) 12 | { 13 | _statement = new DeclareStatement(); 14 | } 15 | 16 | public override DeclareStatement Execute() 17 | { 18 | if (Tokenizer.HasMoreTokens) 19 | { 20 | var parser = new VariableDefinitionParser(Tokenizer); 21 | _statement.Definitions = parser.Execute(); 22 | } 23 | 24 | if (_statement.Definitions.Count == 0) 25 | throw new SyntaxException("DECLARE requires at least one variable declaration"); 26 | 27 | return _statement; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/AlterTableStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class AlterTableStatement : IStatement 7 | { 8 | public bool Terminated { get; set; } 9 | internal AlterTableStatement() 10 | { 11 | } 12 | 13 | public List PrimaryKeys { get; set; } 14 | public string ConstraintName { get; set; } 15 | public string TableName { get; set; } 16 | 17 | #region IStatement Members 18 | 19 | public string Identifier 20 | { 21 | get 22 | { 23 | return String.Format( 24 | "ALTER TABLE {0} ADD CONSTRAINT {1} PRIMARY KEY CLUSTERED ({2})", 25 | TableName, 26 | ConstraintName, 27 | String.Join( ", ", PrimaryKeys.ToArray() ) 28 | ); 29 | } 30 | } 31 | 32 | #endregion 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/TransactionStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Laan.Sql.Parser.Expressions; 4 | 5 | namespace Laan.Sql.Parser.Entities 6 | { 7 | public enum TransactionDescriptor 8 | { 9 | None, 10 | Tran, 11 | Transaction, 12 | Work 13 | } 14 | 15 | public abstract class TransactionStatement : CustomStatement 16 | { 17 | public TransactionStatement() 18 | { 19 | Descriptor = TransactionDescriptor.None; 20 | } 21 | 22 | public TransactionDescriptor Descriptor { get; set; } 23 | public string Name { get; set; } 24 | } 25 | 26 | public class BeginTransactionStatement : TransactionStatement 27 | { 28 | public bool Distributed { get; set; } 29 | } 30 | 31 | public class CommitTransactionStatement : TransactionStatement 32 | { 33 | } 34 | 35 | public class RollbackTransactionStatement : TransactionStatement 36 | { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/BlockStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Laan.Sql.Parser; 5 | using Laan.Sql.Parser.Entities; 6 | 7 | namespace Laan.Sql.Formatter 8 | { 9 | public class BlockStatementFormatter : CustomStatementFormatter, IStatementFormatter 10 | { 11 | 12 | public BlockStatementFormatter(IIndentable indentable, StringBuilder sql, BlockStatement statement) 13 | : base(indentable, sql, statement) 14 | { 15 | 16 | } 17 | 18 | public void Execute() 19 | { 20 | IndentAppendLine("BEGIN"); 21 | IndentAppendLine(""); 22 | using (new IndentScope(this)) 23 | { 24 | foreach (IStatement statement in _statement.Statements) 25 | { 26 | FormatStatement(statement); 27 | NewLine(2); 28 | } 29 | } 30 | IndentAppend("END"); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Formatter 7 | { 8 | public static class Extensions 9 | { 10 | public static string FormattedValue(this Expression expr, int offset, IIndentable indent) 11 | { 12 | var formatter = ExpressionFormatterFactory.GetFormatter(indent, expr); 13 | formatter.Offset = offset; 14 | (formatter as IIndentable).Indent = indent.Indent; 15 | (formatter as IIndentable).IndentLevel = indent.IndentLevel; 16 | return formatter.Execute(); 17 | } 18 | 19 | public static bool HasAncestorOfType(this Expression expr, Type type) 20 | { 21 | while (expr.Parent != null) 22 | { 23 | expr = expr.Parent; 24 | if (type.IsAssignableFrom(expr.GetType())) 25 | return true; 26 | } 27 | return false; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Test/TestCreateViewStatementFormatting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using NUnit.Framework; 5 | 6 | namespace Laan.Sql.Formatter.Test 7 | { 8 | [TestFixture] 9 | public class TestCreateViewStatementFormatting : BaseFormattingTest 10 | { 11 | [Test] 12 | [TestCase("create")] 13 | [TestCase("alter")] 14 | public void Can_Format_Simple_Create_View_Statement(string modificationType) 15 | { 16 | // Setup 17 | var sut = new FormattingEngine(); 18 | 19 | // Exercise 20 | var actual = sut.Execute(string.Format(@"{0} VIEW ben.MyView AS SELECT * FROM [Table]", modificationType)); 21 | 22 | // Verify outcome 23 | var expected = new[] 24 | { 25 | modificationType.ToUpper() + " VIEW ben.MyView", 26 | "AS", 27 | "SELECT *", 28 | "FROM [Table]" 29 | }; 30 | 31 | Compare(actual, expected); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/CreateTableStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Diagnostics; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | [ DebuggerDisplay("{Value}") ] 9 | public class CreateTableStatement : IStatement 10 | { 11 | public bool Terminated { get; set; } 12 | public CreateTableStatement() 13 | { 14 | Fields = new FieldDefinitions(); 15 | } 16 | 17 | public string TableName { get; set; } 18 | public FieldDefinitions Fields { get; set; } 19 | 20 | #region IStatement Members 21 | 22 | public string Identifier 23 | { 24 | get 25 | { 26 | return String.Format( 27 | "CREATE TABLE {0}\n(\n{1}\n)", 28 | TableName, 29 | String.Join( ",\n", Fields.Select( fld => "\t" + fld.ToString() ).ToArray() ) 30 | ); 31 | } 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/BetweenExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Expressions 4 | { 5 | public class BetweenExpression : Expression 6 | { 7 | public BetweenExpression( Expression parent ) : base( parent ) 8 | { 9 | 10 | } 11 | 12 | public Expression Expression { get; set; } 13 | public Expression From { get; set; } 14 | public Expression To { get; set; } 15 | public bool Negated { get; set; } 16 | 17 | public override string Value 18 | { 19 | get { 20 | return String.Format( 21 | "{0} {1}BETWEEN {2} AND {3}", 22 | Expression.Value, 23 | Negated ? "NOT " : "", 24 | From.Value, 25 | To.Value 26 | ); 27 | } 28 | } 29 | 30 | public override bool CanInline 31 | { 32 | get { return Expression.CanInline && From.CanInline && To.CanInline && Value.Length < 80; } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/CriteriaExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Laan.Sql.Parser.Expressions 5 | { 6 | public class CriteriaExpression : Expression 7 | { 8 | private string _operator; 9 | 10 | public CriteriaExpression(Expression parent) : base(parent) 11 | { 12 | Left = new Expression(this); 13 | Right = new Expression(this); 14 | } 15 | 16 | public Expression Left { get; set; } 17 | public Expression Right { get; set; } 18 | 19 | public string Operator 20 | { 21 | get { return _operator; } 22 | set { _operator = value.ToUpper(); } 23 | } 24 | 25 | public override string Value 26 | { 27 | get { return String.Format("{0} {1} {2}", Left.Value, Operator, Right.Value); } 28 | } 29 | 30 | public override bool CanInline 31 | { 32 | get { return !String.Equals(Operator, Constants.Or, StringComparison.CurrentCultureIgnoreCase); } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/Table.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class Table : AliasedEntity, ITableHints 7 | { 8 | public string Name { get; set; } 9 | public List Joins { get; set; } 10 | 11 | /// 12 | /// Initializes a new instance of the Table class. 13 | /// 14 | public Table() 15 | { 16 | Joins = new List(); 17 | TableHints = new List(); 18 | } 19 | 20 | public override string Value 21 | { 22 | get { return Alias != null ? Name : String.Format( "{0} ({1})", Name, Alias.Name ); } 23 | } 24 | public List TableHints { get; set; } 25 | public bool ExplicitWith { get; set; } 26 | } 27 | 28 | public class DerivedTable : Table 29 | { 30 | public SelectStatement SelectStatement { get; set; } 31 | 32 | public override string Value => String.Format("({0})", SelectStatement); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SortedField.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public enum SortOrder 7 | { 8 | Implicit, Ascending, Descending 9 | } 10 | 11 | public class SortedField : Field 12 | { 13 | Dictionary _map; 14 | 15 | /// 16 | /// Initializes a new instance of the SortedField class. 17 | /// 18 | /// 19 | public SortedField() 20 | { 21 | _map = new Dictionary 22 | { 23 | { SortOrder.Implicit, "" }, 24 | { SortOrder.Ascending, " ASC" }, 25 | { SortOrder.Descending, " DESC" } 26 | }; 27 | } 28 | 29 | public SortOrder SortOrder { get; set; } 30 | 31 | public override string Value 32 | { 33 | get { return base.Value + _map[ SortOrder ]; } 34 | protected set { base.Value = value; } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/Laan.Sql.Formatter.Web.Blazor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | True 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/FunctionExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | public class FunctionExpression : Expression 10 | { 11 | public FunctionExpression( Expression parent ) : base ( parent ) 12 | { 13 | Arguments = new List(); 14 | } 15 | 16 | public string Name { get; set; } 17 | public List Arguments { get; set; } 18 | 19 | public override string Value 20 | { 21 | get 22 | { 23 | string[] args = Arguments.Select( arg => arg.Value ).ToArray(); 24 | return String.Format( "{0}({1})", Name, String.Join( Constants.Comma, args ) ); 25 | } 26 | } 27 | 28 | public override bool CanInline 29 | { 30 | get 31 | { 32 | return Arguments.All( arg => arg.CanInline ) && Value.Length < 40; 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/OperatorExpression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | public class OperatorExpression : Expression, IInlineFormattable 10 | { 11 | /// 12 | /// Initializes a new instance of the OperatorExpression class. 13 | /// 14 | public OperatorExpression( Expression parent ) : base ( parent ) 15 | { 16 | } 17 | 18 | public Expression Left { get; set; } 19 | public Expression Right { get; set; } 20 | public string Operator { get; set; } 21 | 22 | public override string Value 23 | { 24 | get { return String.Format( "{0} {1} {2}", Left.Value, Operator, Right.Value ); } 25 | } 26 | 27 | #region IInlineFormattable Members 28 | 29 | public override bool CanInline 30 | { 31 | get { return Left.CanInline && Right.CanInline; } 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/IfStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class IfStatementFormatter : CustomStatementFormatter, IStatementFormatter 11 | { 12 | public IfStatementFormatter( IIndentable indentable, StringBuilder sql, IfStatement statement ) 13 | : base( indentable, sql, statement ) 14 | { 15 | 16 | } 17 | 18 | #region IStatementFormatter Members 19 | 20 | public void Execute() 21 | { 22 | IndentAppendLineFormat( "{0} {1}", Constants.If, _statement.Condition.FormattedValue( 0, this ) ); 23 | FormatBlock( _statement.If ); 24 | if ( _statement.Else != null ) 25 | { 26 | NewLine(); 27 | IndentAppendLine( Constants.Else ); 28 | FormatBlock( _statement.Else ); 29 | } 30 | } 31 | 32 | #endregion 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/GoTerminatorFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class GoTerminatorFormatter : IStatementFormatter 11 | { 12 | protected int IndentStep; 13 | protected StringBuilder _sql; 14 | protected GoTerminator _statement; 15 | protected string Indent; 16 | 17 | public GoTerminatorFormatter( IIndentable indentable, StringBuilder sql, GoTerminator statement ) 18 | { 19 | Indent = indentable.Indent; 20 | IndentStep = indentable.IndentLevel; 21 | _sql = sql; 22 | _statement = statement; 23 | } 24 | 25 | #region IStatementFormatter Members 26 | 27 | public void Execute() 28 | { 29 | _sql.Append( Constants.Go ); 30 | } 31 | 32 | public bool CanInline 33 | { 34 | get { return false; } 35 | } 36 | 37 | #endregion 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/CreateProcedureStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Laan.Sql.Parser.Entities 5 | { 6 | public class CreateProcedureStatement : Statement 7 | { 8 | public CreateProcedureStatement() 9 | { 10 | Arguments = new List(); 11 | } 12 | 13 | public string Name { get; set; } 14 | public bool IsAlter { get; set; } 15 | public bool IsShortForm { get; set; } 16 | public bool HasBracketedArguments { get; set; } 17 | 18 | public IStatement Definition { get; set; } 19 | public List Arguments { get; set; } 20 | 21 | public override string Identifier 22 | { 23 | get 24 | { 25 | return String.Format( 26 | "{0} {1} {2} AS {3}", 27 | IsAlter ? "ALTER" : "CREATE", 28 | IsShortForm ? "PROC" : "PROCEDURE", 29 | Name, 30 | Definition.Identifier 31 | ); 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/DeleteStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class DeleteStatementFormatter : CustomStatementFormatter, IStatementFormatter 11 | { 12 | public DeleteStatementFormatter( IIndentable indentable, StringBuilder sql, DeleteStatement statement ) 13 | : base( indentable, sql, statement ) 14 | { 15 | } 16 | 17 | #region IStatementFormatter Members 18 | 19 | public void Execute() 20 | { 21 | FormatDelete(); 22 | FormatFrom(); 23 | FormatWhere(); 24 | FormatTerminator(); 25 | } 26 | 27 | private void FormatDelete() 28 | { 29 | IndentAppend( Constants.Delete ); 30 | FormatTop( _statement.Top ); 31 | Append( _statement.TableName != null ? " " + _statement.TableName : "" ); 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Statements/TestGrantStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using NUnit.Framework; 7 | 8 | using Laan.Sql.Parser; 9 | using Laan.Sql.Parser.Entities; 10 | 11 | namespace Laan.Sql.Parser.Test 12 | { 13 | [TestFixture] 14 | public class TestGrantStatementParser 15 | { 16 | [Test] 17 | [TestCase( "SELECT" ), TestCase( "INSERT" ), TestCase( "UPDATE" ), TestCase( "DELETE" ), TestCase( "ALL" )] 18 | public void TestGrantOperation( string operation ) 19 | { 20 | // Exercise 21 | var statement = ParserFactory.Execute( 22 | String.Format( "GRANT {0} ON [dbo].[SomeTable] TO [SomeUser]", operation ) 23 | ).First(); 24 | 25 | // Verify outcome 26 | Assert.IsNotNull( statement ); 27 | Assert.AreEqual( "[dbo].[SomeTable]", statement.TableName ); 28 | Assert.AreEqual( operation, statement.Operation ); 29 | Assert.AreEqual( "[SomeUser]", statement.Grantee ); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/RollbackTransactionStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Laan.Sql.Parser; 5 | using Laan.Sql.Parser.Entities; 6 | 7 | namespace Laan.Sql.Formatter 8 | { 9 | public class RollbackTransactionStatementFormatter : TransactionStatementFormatter, IStatementFormatter 10 | { 11 | public RollbackTransactionStatementFormatter( IIndentable indentable, StringBuilder sql, RollbackTransactionStatement statement ) 12 | : base( indentable, sql, statement ) 13 | { 14 | 15 | } 16 | 17 | #region IStatementFormatter Members 18 | 19 | public void Execute() 20 | { 21 | DecreaseIndent(); 22 | IndentAppendFormat( 23 | "ROLLBACK{0}{1}", 24 | GetDescription(), 25 | ( !String.IsNullOrEmpty( _statement.Name ) ? " " + _statement.Name : "" ) 26 | ); 27 | } 28 | 29 | public override bool CanInline 30 | { 31 | get { return false; } 32 | } 33 | 34 | #endregion 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/DuplicateLineAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Microsoft.VisualStudio.Shell; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public class DuplicateLineAction : BaseAction 9 | { 10 | public override int CommandId => 4005; 11 | 12 | protected override void Execute() 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | var cursor = TextDocument.Selection.ActivePoint; 17 | var startPoint = cursor.CreateEditPoint(); 18 | 19 | var line = CurrentLine; 20 | TextDocument.Selection.StartOfLine(); 21 | InsertText(line + Environment.NewLine); 22 | TextDocument.Selection.Cancel(); 23 | 24 | TextDocument.Selection.MoveToPoint(startPoint); 25 | } 26 | 27 | protected override bool CanExecute() 28 | { 29 | ThreadHelper.ThrowIfNotOnUIThread(); 30 | 31 | return IsCurrentDocumentExtension("sql") 32 | && AllText.Length > 0 33 | && CurrentSelection.Length == 0; 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Test/TestTransactionsStatementFormatting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using NUnit.Framework; 4 | 5 | namespace Laan.Sql.Formatter.Test 6 | { 7 | [TestFixture] 8 | public class TestTransactionsStatementFormatting : BaseFormattingTest 9 | { 10 | [Test] 11 | public void Can_Format_Begin_Transaction_With_Rollback() 12 | { 13 | // Setup 14 | var sut = new FormattingEngine(); 15 | 16 | // Exercise 17 | var actual = sut.Execute( "BEGIN TRAN SELECT TOP 20 Field1, Field2 FROM dbo.Table T ROLLBACK" ); 18 | 19 | // Verify outcome 20 | var expected = new[] 21 | { 22 | @"BEGIN TRAN", 23 | "", 24 | " SELECT TOP 20", 25 | " Field1,", 26 | " Field2", 27 | "", 28 | " FROM dbo.Table T", 29 | "", 30 | "ROLLBACK" 31 | }; 32 | 33 | Compare( actual, expected ); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/SqlFormatterAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.AddIns.Ssms.VsExtension.Utils; 4 | using Laan.Sql.Formatter; 5 | 6 | using Microsoft.VisualStudio.Shell; 7 | 8 | namespace Laan.AddIns.Ssms.VsExtension.Commands 9 | { 10 | public class SqlFormatterAction : BaseAction 11 | { 12 | public override int CommandId => 4001; 13 | 14 | protected override void Execute() 15 | { 16 | ThreadHelper.ThrowIfNotOnUIThread(); 17 | 18 | using (var undo = new ScopedUndoContext(this, "SqlFormatter")) 19 | { 20 | var textDocument = TextDocument; 21 | 22 | if (textDocument.Selection.IsEmpty) 23 | textDocument.Selection.SelectAll(); 24 | 25 | try 26 | { 27 | var engine = new FormattingEngine(); 28 | InsertText(engine.Execute(textDocument.Selection.Text) + Environment.NewLine); 29 | } 30 | finally 31 | { 32 | textDocument.Selection.Cancel(); 33 | } 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/CommitTransactionStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Linq; 5 | 6 | using Laan.Sql.Parser; 7 | using Laan.Sql.Parser.Entities; 8 | 9 | namespace Laan.Sql.Formatter 10 | { 11 | public class CommitTransactionStatementFormatter : TransactionStatementFormatter, IStatementFormatter 12 | { 13 | public CommitTransactionStatementFormatter( IIndentable indentable, StringBuilder sql, CommitTransactionStatement statement ) 14 | : base( indentable, sql, statement ) 15 | { 16 | 17 | } 18 | 19 | #region IStatementFormatter Members 20 | 21 | public void Execute() 22 | { 23 | DecreaseIndent(); 24 | IndentAppendFormat( 25 | "COMMIT {0}{1}", 26 | GetDescription(), 27 | ( !String.IsNullOrEmpty( _statement.Name ) ? " " + _statement.Name : "" ) 28 | ); 29 | } 30 | 31 | public override bool CanInline 32 | { 33 | get { return false; } 34 | } 35 | 36 | #endregion 37 | } 38 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/TransactionStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public abstract class TransactionStatementFormatter : CustomStatementFormatter where T : TransactionStatement 11 | { 12 | public TransactionStatementFormatter( IIndentable indentable, StringBuilder sql, T statement ) 13 | : base( indentable, sql, statement ) 14 | { 15 | 16 | } 17 | 18 | protected string GetDescription() 19 | { 20 | var description = new Dictionary 21 | { 22 | { TransactionDescriptor.Work, "WORK" }, 23 | { TransactionDescriptor.Tran, "TRAN" }, 24 | { TransactionDescriptor.Transaction, "TRANSACTION" } 25 | }; 26 | string value = ""; 27 | if ( description.TryGetValue( _statement.Descriptor, out value ) ) 28 | return " " + value; 29 | else 30 | return ""; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/SqlTemplateOption/DelegateCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Input; 4 | 5 | namespace Laan.AddIns.Ssms.VsExtension.SqlTemplateOption 6 | { 7 | public class DelegateCommand : ICommand 8 | { 9 | private readonly Func _canExecute; 10 | private readonly Action _execute; 11 | 12 | public event EventHandler CanExecuteChanged; 13 | 14 | public DelegateCommand(Action execute) 15 | : this(execute, null) 16 | { 17 | } 18 | 19 | public DelegateCommand(Action execute, Func canExecute) 20 | { 21 | _execute = execute; 22 | _canExecute = canExecute; 23 | } 24 | 25 | public bool CanExecute(object parameter) 26 | { 27 | return _canExecute == null || _canExecute(); 28 | } 29 | 30 | public void Execute(object parameter) 31 | { 32 | _execute(); 33 | } 34 | 35 | public void RaiseCanExecuteChanged() 36 | { 37 | if (CanExecuteChanged != null) 38 | CanExecuteChanged(this, EventArgs.Empty); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Options/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Options.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.6.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SqlType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Entities 4 | { 5 | public class SqlType 6 | { 7 | public SqlType(string name) 8 | { 9 | Name = name; 10 | Collation = null; 11 | } 12 | 13 | public string Name { get; set; } 14 | public string Collation { get; set; } 15 | public int? Length { get; set; } 16 | public bool Max { get; set; } 17 | public int? Scale { get; set; } 18 | 19 | public override string ToString() 20 | { 21 | string lengthDisplay = null; 22 | if (Length.HasValue) 23 | lengthDisplay = String.Format( 24 | "({0})" + (Collation != null ? " " + Collation : String.Empty), 25 | Length 26 | ); 27 | else if (Max) 28 | lengthDisplay = "(MAX)"; 29 | 30 | string precisionDisplay = null; 31 | if (Scale.HasValue) 32 | precisionDisplay = String.Format("({0}, {1})", Length, Scale); 33 | 34 | return String.Format("{0}{1}", Name, precisionDisplay ?? lengthDisplay ?? ""); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/FormattingEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using Laan.Sql.Parser; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class FormattingEngine : IFormattingEngine 11 | { 12 | public FormattingEngine() 13 | { 14 | UseTabChar = false; 15 | } 16 | 17 | public string Execute(string sql) 18 | { 19 | var outSql = new StringBuilder(sql.Length * 2); 20 | var statements = ParserFactory.Execute(sql); 21 | 22 | var indentation = new Indentation(); 23 | 24 | foreach (var statement in statements) 25 | { 26 | var formatter = StatementFormatterFactory.GetFormatter(indentation, outSql, statement); 27 | formatter.Execute(); 28 | 29 | if (statement != statements.Last()) 30 | outSql.AppendLine(Environment.NewLine); 31 | } 32 | 33 | return outSql.ToString(); 34 | } 35 | 36 | public int IndentStep { get; set; } 37 | public int TabSize { get; set; } 38 | public bool UseTabChar { get; set; } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/BeginTransactionStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class BeginTransactionStatementFormatter : TransactionStatementFormatter, IStatementFormatter 11 | { 12 | public BeginTransactionStatementFormatter( IIndentable indentable, StringBuilder sql, BeginTransactionStatement statement ) 13 | : base( indentable, sql, statement ) 14 | { 15 | 16 | } 17 | 18 | #region IStatementFormatter Members 19 | 20 | public void Execute() 21 | { 22 | IndentAppendFormat( 23 | "BEGIN{0}{1}", 24 | ( _statement.Distributed ? " DISTRIBUTED" : "" ), 25 | GetDescription(), 26 | ( !String.IsNullOrEmpty( _statement.Name ) ? " " + _statement.Name : "" ) 27 | ); 28 | IncreaseIndent(); 29 | } 30 | 31 | public override bool CanInline 32 | { 33 | get { return false; } 34 | } 35 | 36 | #endregion 37 | } 38 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/Program.cs: -------------------------------------------------------------------------------- 1 | using Blazorise; 2 | using Blazorise.Bootstrap; 3 | using Blazorise.Icons.FontAwesome; 4 | 5 | using Microsoft.AspNetCore.Components.Web; 6 | using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 7 | 8 | namespace Laan.Sql.Formatter.Web.Blazor 9 | { 10 | public static class Program 11 | { 12 | public static async Task Main(string[] args) 13 | { 14 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 15 | 16 | builder.Services 17 | .AddBlazorise(options => 18 | { 19 | options.ChangeTextOnKeyPress = true; 20 | }) 21 | .AddBootstrapProviders() 22 | .AddFontAwesomeIcons(); 23 | 24 | builder.Services.AddSingleton(new HttpClient 25 | { 26 | BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) 27 | }); 28 | 29 | builder.RootComponents.Add("#app"); 30 | builder.RootComponents.Add("head::after"); 31 | 32 | builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); 33 | 34 | await builder.Build().RunAsync(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/CreateViewStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Entities; 4 | 5 | namespace Laan.Sql.Parser.Parsers 6 | { 7 | public class CreateViewStatementParser : StatementParser 8 | { 9 | internal CreateViewStatementParser(ITokenizer tokenizer) : base(tokenizer) 10 | { 11 | } 12 | 13 | public override CreateViewStatement Execute() 14 | { 15 | _statement = new CreateViewStatement() { Name = GetDotNotationIdentifier(), IsAlter = IsAlter }; 16 | 17 | ExpectToken(Constants.As); 18 | 19 | if (Tokenizer.IsNextToken(Constants.Select)) 20 | { 21 | ReadNextToken(); 22 | 23 | var parser = new SelectStatementParser(Tokenizer); 24 | _statement.Definition = parser.Execute(); 25 | } 26 | else if (Tokenizer.IsNextToken(Constants.With)) 27 | { 28 | ReadNextToken(); 29 | 30 | var parser = new CommonTableExpressionStatementParser(Tokenizer); 31 | _statement.Definition = parser.Execute(); 32 | } 33 | 34 | return _statement; 35 | } 36 | 37 | public bool IsAlter { get; set; } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Test/TestGoTerminatorStatementFormatting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using NUnit.Framework; 7 | 8 | namespace Laan.Sql.Formatter.Test 9 | { 10 | [TestFixture] 11 | public class TestGoTerminatorStatementFormatting : BaseFormattingTest 12 | { 13 | [Test] 14 | public void Can_Create_Formatting_Engine() 15 | { 16 | // Setup 17 | var sut = new FormattingEngine(); 18 | 19 | // Exercise 20 | 21 | // Verify outcome 22 | Assert.IsNotNull( sut ); 23 | } 24 | 25 | [Test] 26 | public void Can_Format_Simple_Select_Statement() 27 | { 28 | // Setup 29 | var sut = new FormattingEngine(); 30 | 31 | // Exercise 32 | var actual = sut.Execute( "SELECT TOP 20 Field1, Field2 FROM dbo.Table T" ); 33 | 34 | // Verify outcome 35 | var expected = new[] 36 | { 37 | @"SELECT TOP 20", 38 | " Field1,", 39 | " Field2", 40 | "", 41 | "FROM dbo.Table T", 42 | }; 43 | 44 | Compare( actual, expected ); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/AlterStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Parsers 4 | { 5 | public class AlterStatementParser : IParser 6 | { 7 | private ITokenizer _tokenizer; 8 | 9 | /// 10 | /// Initializes a new instance of the CreateStatementParser class. 11 | /// 12 | public AlterStatementParser(ITokenizer tokenizer) 13 | { 14 | _tokenizer = tokenizer; 15 | } 16 | 17 | public IStatement Execute() 18 | { 19 | IParser parser = null; 20 | 21 | if (_tokenizer.TokenEquals(Constants.Table)) 22 | parser = new AlterTableStatementParser(_tokenizer); 23 | 24 | if (_tokenizer.TokenEquals(Constants.View)) 25 | parser = new CreateViewStatementParser(_tokenizer) { IsAlter = true }; 26 | 27 | if (_tokenizer.TokenEquals(Constants.Procedure) || _tokenizer.TokenEquals(Constants.Proc)) 28 | parser = new CreateProcedureStatementParser(_tokenizer) { IsAlter = true }; 29 | 30 | //if ( _tokenizer.TokenEquals( Constants.Trigger ) ) 31 | // parser = new AlterTriggerStatementParser( _tokenizer ); 32 | 33 | return parser != null ? parser.Execute() : null; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/CreateViewStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using Laan.Sql.Parser.Entities; 6 | 7 | namespace Laan.Sql.Formatter 8 | { 9 | public class CreateViewStatementFormatter : StatementFormatter, IStatementFormatter 10 | { 11 | public CreateViewStatementFormatter(IIndentable indentable, StringBuilder sql, CreateViewStatement statement) 12 | : base(indentable, sql, statement) 13 | { 14 | } 15 | 16 | public void Execute() 17 | { 18 | _sql.AppendFormat("{0} VIEW {1}\n", _statement.IsAlter ? "ALTER" : "CREATE", _statement.Name); 19 | _sql.AppendLine("AS"); 20 | 21 | if (_statement.Definition is SelectStatement) 22 | { 23 | var formatter = new SelectStatementFormatter(this, _sql, (SelectStatement)_statement.Definition); 24 | formatter.Execute(); 25 | } 26 | else if (_statement.Definition is CommonTableExpressionStatement) 27 | { 28 | var formatter = new CommonTableExpressionStatementFormatter(this, _sql, (CommonTableExpressionStatement)_statement.Definition); 29 | formatter.Execute(); 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/NestedExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Formatter 7 | { 8 | public class NestedExpressionFormatter : CustomExpressionFormatter 9 | { 10 | public NestedExpressionFormatter(NestedExpression expression) : base(expression) 11 | { 12 | } 13 | 14 | public override string Execute() 15 | { 16 | string formattedValue = _expression.Expression.FormattedValue(0, this).Replace(Environment.NewLine, " "); 17 | if (CanInlineExpression(_expression.Expression, Offset + formattedValue.Length)) 18 | { 19 | return BaseFormatter.FormatBrackets(formattedValue); 20 | } 21 | else 22 | { 23 | var sql = new StringBuilder(); 24 | sql.AppendLine("("); 25 | sql.AppendLine(); 26 | using (new IndentScope(this)) 27 | { 28 | sql.Append(_expression.Expression.FormattedValue(Offset, this)); 29 | } 30 | sql.AppendLine(); 31 | sql.AppendLine(); 32 | sql.Append(GetIndent(false) + ")"); 33 | 34 | return sql.ToString(); 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/GrantStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Entities; 4 | using Laan.Sql.Parser.Exceptions; 5 | 6 | namespace Laan.Sql.Parser.Parsers 7 | { 8 | public class GrantStatementParser : StatementParser 9 | { 10 | public GrantStatementParser( ITokenizer tokenizer ) : base( tokenizer ) 11 | { 12 | } 13 | 14 | public override GrantStatement Execute() 15 | { 16 | GrantStatement statement = new GrantStatement(); 17 | 18 | if ( Tokenizer.IsNextToken( Constants.Select, Constants.Insert, Constants.Update, Constants.Delete, Constants.All ) ) 19 | { 20 | string token = Tokenizer.Current.Value; 21 | ReadNextToken(); 22 | statement.Operation = token; 23 | } 24 | else 25 | throw new ExpectedTokenNotFoundException( 26 | "'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'ALL'", 27 | CurrentToken, 28 | Tokenizer.Position 29 | ); 30 | 31 | Tokenizer.ExpectToken( Constants.On ); 32 | statement.TableName = GetTableName(); 33 | Tokenizer.ExpectToken( Constants.To ); 34 | statement.Grantee = GetDotNotationIdentifier(); 35 | 36 | return statement; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Laan Sql Tools Extension 6 | Laan Sql Tools - providing formatting and sql snippets / templates 7 | 8 | 9 | 10 | amd64 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Laan.NHibernate.Appender/ParamBuilderFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Laan.Sql.Formatter; 6 | using Laan.Sql.Parser; 7 | 8 | namespace Laan.NHibernate.Appender 9 | { 10 | public class ParamBuilderFormatter 11 | { 12 | private readonly IFormattingEngine _engine; 13 | 14 | /// 15 | /// Initializes a new instance of the ParamBuilderFormatter class. 16 | /// 17 | /// 18 | public ParamBuilderFormatter(IFormattingEngine engine) 19 | { 20 | _engine = engine; 21 | } 22 | 23 | public string Execute(string sql) 24 | { 25 | var originalSql = sql; 26 | // designed to convert the following format 27 | // "SELECT * FROM Table WHERE ID=@P1 AND Name=@P2;@P1=20,@P2='Users'" 28 | try 29 | { 30 | 31 | sql = ParserFactory.TrimBatchMetadata(sql); 32 | 33 | var parameterSubstituter = new ParameterSubstituter(); 34 | return _engine.Execute(parameterSubstituter.UpdateParamsWithValues(sql)); 35 | } 36 | catch (Exception ex) 37 | { 38 | return String.Format("-- Error: {0}\n{1}", ex.Message, originalSql); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Laan.AddIns.Ssms.VsExtension")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Laan.AddIns.Ssms.VsExtension")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Statements/TestBatchTerminators.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using NUnit.Framework; 5 | 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Parser.Test 9 | { 10 | [TestFixture] 11 | public class TestBatchTerminators 12 | { 13 | [Test] 14 | public void Statements_With_Semicolon_Terminators() 15 | { 16 | // Exercise 17 | var statements = ParserFactory.Execute(@" 18 | 19 | SELECT 1; 20 | UPDATE dbo.Table SET Fields = 1; 21 | DELETE FROM dbo.Table WHERE ID < 10; 22 | SELECT 2; 23 | " 24 | ); 25 | 26 | // Verify outcome 27 | Assert.IsNotNull(statements); 28 | Assert.AreEqual(4, statements.Count); 29 | } 30 | 31 | [Test] 32 | public void Statements_With_Go_Terminators() 33 | { 34 | // Exercise 35 | var statements = ParserFactory.Execute(@" 36 | 37 | SELECT 1 38 | GO 39 | SELECT 2 40 | GO 41 | " 42 | ); 43 | 44 | // Verify outcome 45 | Assert.IsNotNull(statements); 46 | Assert.AreEqual(4, statements.Count); 47 | Assert.AreEqual(2, statements.Count(st => typeof(GoTerminator).IsAssignableFrom(st.GetType()))); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:55035", 7 | "sslPort": 44361 8 | } 9 | }, 10 | "profiles": { 11 | "http": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 16 | "applicationUrl": "http://localhost:5147", 17 | "environmentVariables": { 18 | "ASPNETCORE_ENVIRONMENT": "Development" 19 | } 20 | }, 21 | "https": { 22 | "commandName": "Project", 23 | "dotnetRunMessages": true, 24 | "launchBrowser": true, 25 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 26 | "applicationUrl": "https://localhost:7013;http://localhost:5147", 27 | "environmentVariables": { 28 | "ASPNETCORE_ENVIRONMENT": "Development" 29 | } 30 | }, 31 | "IIS Express": { 32 | "commandName": "IISExpress", 33 | "launchBrowser": true, 34 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 35 | "environmentVariables": { 36 | "ASPNETCORE_ENVIRONMENT": "Development" 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/CreateProcedureStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using Laan.Sql.Parser.Entities; 6 | using Laan.Sql.Parser; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class CreateProcedureStatementFormatter : StatementFormatter, IStatementFormatter 11 | { 12 | public CreateProcedureStatementFormatter(IIndentable indentable, StringBuilder sql, CreateProcedureStatement statement) 13 | : base(indentable, sql, statement) 14 | { 15 | } 16 | 17 | public void Execute() 18 | { 19 | _sql.AppendFormat( 20 | "{0} {1} {2}{3}", 21 | _statement.IsAlter ? "ALTER" : Constants.Create, 22 | _statement.IsShortForm ? "PROC" : "PROCEDURE", 23 | _statement.Name, 24 | _statement.HasBracketedArguments ? " (" : String.Empty 25 | ); 26 | 27 | if (_statement.Arguments.Any()) 28 | { 29 | var formatter = new VariableDefinitionFormatter(_statement.Arguments, this, _sql); 30 | formatter.Execute(); 31 | } 32 | 33 | _sql.AppendLine(); 34 | 35 | if (_statement.HasBracketedArguments) 36 | _sql.AppendLine(")"); 37 | 38 | _sql.AppendLine("AS"); 39 | 40 | FormatStatement(_statement.Definition); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/SelectStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Laan.Sql.Parser.Expressions; 6 | 7 | namespace Laan.Sql.Parser.Entities 8 | { 9 | public enum SetType 10 | { 11 | None, 12 | Union, 13 | UnionAll, 14 | Intersect, 15 | Except 16 | } 17 | 18 | public class SelectStatement : ProjectionStatement 19 | { 20 | private const int MaxInlineColumns = 1; 21 | 22 | public SelectStatement() : base() 23 | { 24 | Distinct = false; 25 | Top = null; 26 | OrderBy = new List(); 27 | GroupBy = new List(); 28 | } 29 | 30 | public bool Distinct { get; set; } 31 | public Top Top { get; set; } 32 | public string Into { get; set; } 33 | public List OrderBy { get; set; } 34 | public List GroupBy { get; set; } 35 | public Expression Having { get; set; } 36 | public SetOperation SetOperation { get; set; } 37 | 38 | public bool CanInLine() 39 | { 40 | return Fields.Count <= MaxInlineColumns 41 | && 42 | From.Count == 1 && 43 | !From.First().Joins.Any() 44 | && ( Where == null || Where.CanInline ) 45 | && ( GroupBy == null || !GroupBy.Any() ) 46 | && ( OrderBy == null || !OrderBy.Any() ); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009, Laan Software Foundry (Ben Laan) 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | * Neither the name of the Laan Software Foundry nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Entities/TestAlias.cs: -------------------------------------------------------------------------------- 1 | using Laan.Sql.Parser.Entities; 2 | 3 | using NUnit.Framework; 4 | 5 | namespace Laan.Sql.Parser.Test.Entities 6 | { 7 | [TestFixture] 8 | public class TestAlias 9 | { 10 | [Test] 11 | [TestCase("test name")] 12 | public void Implicit_Value(string name) 13 | { 14 | // Arrange 15 | var sut = new Alias(null); 16 | sut.Name = name; 17 | sut.Type = AliasType.Implicit; 18 | 19 | // Act 20 | var result = sut.Value; 21 | 22 | // Assert 23 | Assert.AreEqual(" " + name, result); 24 | } 25 | 26 | [Test] 27 | [TestCase("test name")] 28 | public void Equals_Value(string name) 29 | { 30 | // Arrange 31 | var sut = new Alias(null); 32 | sut.Name = name; 33 | sut.Type = AliasType.Equals; 34 | 35 | // Act 36 | var result = sut.Value; 37 | 38 | // Assert 39 | Assert.AreEqual(name + " = ", result); 40 | } 41 | 42 | [Test] 43 | [TestCase("test name")] 44 | public void As_Value(string name) 45 | { 46 | // Arrange 47 | var sut = new Alias(null); 48 | sut.Name = name; 49 | sut.Type = AliasType.As; 50 | 51 | // Act 52 | var result = sut.Value; 53 | 54 | // Assert 55 | Assert.AreEqual(" AS " + name, result); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Options/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using Laan.AddIns.Ssms.Actions; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | using System.Windows.Controls; 9 | using System.Windows.Data; 10 | using System.Windows.Documents; 11 | using System.Windows.Input; 12 | using System.Windows.Media; 13 | using System.Windows.Media.Imaging; 14 | using System.Windows.Navigation; 15 | using System.Windows.Shapes; 16 | 17 | namespace Options 18 | { 19 | /// 20 | /// Interaction logic for MainWindow.xaml 21 | /// 22 | public partial class MainWindow : System.Windows.Window 23 | { 24 | private SqlTemplateOptionViewModel _viewModel; 25 | 26 | public MainWindow() 27 | { 28 | InitializeComponent(); 29 | var templates = TemplateDocument.Load(); 30 | 31 | _viewModel = new SqlTemplateOptionViewModel(templates); 32 | _viewModel.OnSave += viewModel_OnSave; 33 | _viewModel.OnCancel += _viewModel_OnCancel; 34 | 35 | DataContext = _viewModel; 36 | } 37 | 38 | private void _viewModel_OnCancel(object sender, EventArgs e) 39 | { 40 | Close(); 41 | } 42 | 43 | private void viewModel_OnSave(object sender, EventArgs e) 44 | { 45 | TemplateDocument.Save(_viewModel.Templates.ToList()); 46 | Close(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Tokenizer/TokenDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace Laan.Sql.Parser 8 | { 9 | [DebuggerDisplay("{Type} : {Regex}")] 10 | public class TokenDefinition 11 | { 12 | public TokenDefinition(TokenType type, bool skip, string regex) 13 | : this(type, skip, regex, null) 14 | { 15 | } 16 | 17 | public TokenDefinition(TokenType type, bool skip, string regex, string multiLineTerminator) 18 | { 19 | if (!String.IsNullOrEmpty(multiLineTerminator)) 20 | { 21 | MultiLineTerminator = new Regex(multiLineTerminator, RegexOptions.Compiled | RegexOptions.ExplicitCapture); 22 | MultiLineContinuation = new Regex(String.Format("[^{0}]*", multiLineTerminator), RegexOptions.Compiled | RegexOptions.ExplicitCapture); 23 | } 24 | 25 | Skip = skip; 26 | Regex = new Regex(regex, RegexOptions.Compiled | RegexOptions.ExplicitCapture); 27 | Type = type; 28 | } 29 | 30 | public Regex Regex { get; private set; } 31 | public bool Skip { get; set; } 32 | public TokenType Type { get; private set; } 33 | public bool WithinQuotesOnly { get; private set; } 34 | public bool IsMultiLine { get { return MultiLineTerminator != null; } } 35 | public Regex MultiLineTerminator { get; private set; } 36 | public Regex MultiLineContinuation { get; private set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/BetweenExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Laan.Sql.Parser; 5 | using Laan.Sql.Parser.Expressions; 6 | 7 | namespace Laan.Sql.Formatter 8 | { 9 | public class BetweenExpressionFormatter : CustomExpressionFormatter 10 | { 11 | public BetweenExpressionFormatter( BetweenExpression expression ) : base( expression ) 12 | { 13 | } 14 | 15 | public override string Execute() 16 | { 17 | if ( _expression.CanInline ) 18 | return String.Format( 19 | "{0} {1}BETWEEN {2} AND {3}", 20 | _expression.Expression.FormattedValue( Offset, this ), 21 | _expression.Negated ? "NOT " : "", 22 | _expression.From.FormattedValue( Offset, this ), 23 | _expression.To.FormattedValue( Offset, this ) 24 | ); 25 | 26 | var _sql = new StringBuilder(); 27 | _sql.AppendFormat( "{0} {1} ", _expression.Expression.FormattedValue( Offset, this ), Constants.Between ); 28 | 29 | int offset = Offset + GetCurrentColumn( _sql ) - Constants.And.Length; 30 | _sql.Append( _expression.From.FormattedValue( Offset, this ) ); 31 | _sql.AppendFormat( 32 | "\n{0}{1} {2}", 33 | GetSpaces( offset ), 34 | Constants.And, 35 | _expression.To.FormattedValue( offset, this ) 36 | ); 37 | 38 | return _sql.ToString(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/AliasedEntities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | public class AliasedEntity : Expression 9 | { 10 | public AliasedEntity() : base ( null ) 11 | { 12 | Alias = new Alias( this ); 13 | } 14 | 15 | public Alias Alias { get; set; } 16 | } 17 | 18 | public enum AliasType { None, Implicit, As, Equals } 19 | 20 | public class Alias : Expression 21 | { 22 | public Alias( Expression parent ) : base ( parent ) 23 | { 24 | Type = AliasType.Implicit; 25 | } 26 | 27 | public string Name { get; set; } 28 | public AliasType Type { get; set; } 29 | 30 | public override string Value 31 | { 32 | get 33 | { 34 | string format = ""; 35 | switch ( Type ) 36 | { 37 | case AliasType.Implicit: 38 | format = !String.IsNullOrEmpty( Name ) ? String.Format( " {0}", Name ) : ""; 39 | break; 40 | 41 | case AliasType.Equals: 42 | format = !String.IsNullOrEmpty( Name ) ? String.Format( "{0} = ", Name ) : ""; 43 | break; 44 | 45 | case AliasType.As: 46 | format = String.Format( " AS {0}", Name ); 47 | break; 48 | } 49 | return format; 50 | } 51 | protected set { base.Value = value; } 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/VariableDefinitionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class VariableDefinitionFormatter : BaseFormatter 11 | { 12 | private IList _definitions; 13 | 14 | public VariableDefinitionFormatter(IList definitions, IIndentable indentable, StringBuilder sql) : base(indentable, sql) 15 | { 16 | _definitions = definitions; 17 | } 18 | 19 | public void Execute() 20 | { 21 | var maxNameLength = _definitions.Max(def => def.Name.Length) * -1; 22 | var maxTypeLength = _definitions.Max(def => def.Type.Length) * -1; 23 | string format = String.Format("{{0,{0}}} {{1,{1}}}{{2}}", maxNameLength, maxTypeLength); 24 | 25 | int count = _definitions.Count; 26 | 27 | using (new IndentScope(this)) 28 | { 29 | foreach (var definition in _definitions) 30 | { 31 | NewLine(); 32 | 33 | var variableDecaration = String.Format( 34 | format, 35 | definition.Name, 36 | definition.Type, 37 | (definition.DefaultValue != null ? " = " + definition.DefaultValue.FormattedValue(0, this) : "") 38 | ).TrimEnd() + (--count > 0 ? "," : ""); 39 | 40 | IndentAppend(variableDecaration); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/CaseWhenExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Formatter 7 | { 8 | public class CaseWhenExpressionFormatter : CaseExpressionFormatter 9 | { 10 | public CaseWhenExpressionFormatter( CaseWhenExpression expression ) : base( expression ) 11 | { 12 | } 13 | 14 | #region IExpressionFormatter Members 15 | 16 | public override string Execute() 17 | { 18 | if ( CanInlineExpression( _expression, Offset ) ) 19 | return _expression.Value; 20 | 21 | var sql = new StringBuilder(); 22 | 23 | sql.AppendFormat( "{0}CASE", _expression.Parent is CaseExpression ? GetIndent( true ) : "" ); 24 | foreach ( var caseItem in _expression.Cases ) 25 | { 26 | using ( new IndentScope( this ) ) 27 | { 28 | sql.AppendFormat( "{0}WHEN ", GetIndent( true ) ); 29 | sql.AppendFormat( "{0} THEN ", caseItem.When.FormattedValue( Offset, this ) ); 30 | 31 | int off = GetCurrentColumn( sql ); 32 | using ( new IndentScope( this ) ) 33 | sql.Append( caseItem.Then.FormattedValue( Offset + off, this ) ); 34 | } 35 | } 36 | 37 | if ( _expression.Else != null ) 38 | sql.Append( FormatCaseElseExpression( Offset, _expression ) ); 39 | 40 | sql.Append( GetIndent( true ) + "END" ); 41 | 42 | return sql.ToString(); 43 | } 44 | 45 | #endregion 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Statements/TestStatementParsingWithComments.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using NUnit.Framework; 7 | 8 | using Laan.Sql.Parser.Expressions; 9 | using Laan.Sql.Parser.Entities; 10 | using Laan.Sql.Parser.Exceptions; 11 | 12 | namespace Laan.Sql.Parser.Test 13 | { 14 | [TestFixture] 15 | public class TestStatementParsingWithComments 16 | { 17 | [Test] 18 | public void Select_Statement_With_Inline_Comment() 19 | { 20 | // Exercise 21 | var statement = ParserFactory.Execute( 22 | "select * -- all fields\n\r from dbo.table" 23 | ).First(); 24 | 25 | // Verify outcome 26 | Assert.IsNotNull(statement); 27 | Assert.AreEqual(1, statement.Fields.Count); 28 | Assert.AreEqual("*", statement.Fields[0].Expression.Value); 29 | Assert.IsNull(statement.Top); 30 | Assert.AreEqual("dbo.table", statement.From[0].Name); 31 | } 32 | 33 | [Test] 34 | public void Select_Statement_With_Block_Comment() 35 | { 36 | var statement = ParserFactory.Execute( 37 | @"select * from /* dbo.table t */ dbo.otherTable t" 38 | ).First(); 39 | 40 | // Verify outcome 41 | Assert.IsNotNull(statement); 42 | Assert.AreEqual(1, statement.Fields.Count); 43 | Assert.AreEqual("*", statement.Fields[0].Expression.Value); 44 | Assert.IsNull(statement.Top); 45 | Assert.AreEqual("dbo.otherTable", statement.From[0].Name); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/CreateStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Parsers 4 | { 5 | public class CreateStatementParser : IParser 6 | { 7 | private ITokenizer _tokenizer; 8 | 9 | /// 10 | /// Initializes a new instance of the CreateStatementParser class. 11 | /// 12 | public CreateStatementParser(ITokenizer tokenizer) 13 | { 14 | _tokenizer = tokenizer; 15 | } 16 | 17 | #region IParser 18 | 19 | public IStatement Execute() 20 | { 21 | IParser parser = null; 22 | 23 | if (_tokenizer.TokenEquals(Constants.Table)) 24 | parser = new CreateTableStatementParser(_tokenizer); 25 | 26 | if (_tokenizer.TokenEquals(Constants.View)) 27 | parser = new CreateViewStatementParser(_tokenizer); 28 | 29 | if (_tokenizer.TokenEquals(Constants.Procedure) || _tokenizer.TokenEquals(Constants.Proc)) 30 | parser = new CreateProcedureStatementParser(_tokenizer) { IsShortForm = _tokenizer.Current == Constants.Proc }; 31 | 32 | //if ( _tokenizer.TokenEquals( Constants.Trigger ) ) 33 | // parser = new CreateTriggerStatementParser( _tokenizer ); 34 | 35 | if (_tokenizer.IsNextToken( 36 | Constants.Unique, 37 | Constants.Clustered, 38 | Constants.NonClustered, 39 | Constants.Index 40 | ) 41 | ) 42 | parser = new CreateIndexParser(_tokenizer); 43 | 44 | return parser != null ? parser.Execute() : null; 45 | } 46 | #endregion 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/BaseLeftCusorAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.AddIns.Ssms.VsExtension.Utils; 5 | 6 | using Microsoft.VisualStudio.Shell; 7 | 8 | namespace Laan.AddIns.Ssms.VsExtension.Commands 9 | { 10 | public abstract class BaseLeftCusorAction : BaseCursorAction 11 | { 12 | protected void CursorLeft(bool applySelection) 13 | { 14 | ThreadHelper.ThrowIfNotOnUIThread(); 15 | 16 | var textDocument = TextDocument; 17 | var cursor = new Cursor(textDocument.Selection.CurrentColumn, textDocument.Selection.TopPoint.Line); 18 | 19 | if (cursor.Column == 1) 20 | return; 21 | 22 | var line = CurrentLine; 23 | if (String.IsNullOrEmpty(line)) 24 | return; 25 | 26 | var leftOfCursor = line.Substring(0, cursor.Column - 1); 27 | 28 | var position = leftOfCursor.Length - 1; 29 | while (position > 0) 30 | { 31 | if (IsSpace(leftOfCursor, position) && position != leftOfCursor.Length - 1) 32 | { 33 | textDocument.Selection.CharLeft(applySelection, leftOfCursor.Length - position); 34 | return; 35 | } 36 | 37 | if (IsCapital(leftOfCursor, position) && !IsCapital(leftOfCursor, position - 1)) 38 | { 39 | textDocument.Selection.CharLeft(applySelection, leftOfCursor.Length - position); 40 | 41 | return; 42 | } 43 | 44 | position--; 45 | } 46 | 47 | textDocument.Selection.CharLeft(applySelection, leftOfCursor.Length); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Utilities/SqlTypeParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Laan.Sql.Parser.Parsers; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Parser 9 | { 10 | public class SqlTypeParser : CustomParser 11 | { 12 | public SqlTypeParser(ITokenizer tokenizer) : base(tokenizer) 13 | { 14 | } 15 | 16 | public SqlType Execute() 17 | { 18 | string identifier = GetIdentifier(); 19 | if (String.Equals(identifier, Constants.As, StringComparison.CurrentCultureIgnoreCase)) 20 | return null; 21 | 22 | var result = new SqlType(identifier); 23 | 24 | if (Tokenizer.IsNextToken(Constants.Precision)) 25 | { 26 | result.Name = String.Format("{0} {1}", result.Name, Constants.Precision); 27 | ReadNextToken(); 28 | } 29 | 30 | if (!Tokenizer.IsNextToken(Constants.OpenBracket)) 31 | return result; 32 | 33 | using (Tokenizer.ExpectBrackets()) 34 | { 35 | string token = CurrentToken; 36 | ReadNextToken(); 37 | result.Max = (String.Compare(token, Constants.Max, true) == 0); 38 | 39 | if (!result.Max) 40 | { 41 | result.Length = Int32.Parse(token); 42 | 43 | if (Tokenizer.TokenEquals(Constants.Comma)) 44 | { 45 | result.Scale = Int32.Parse(CurrentToken); 46 | ReadNextToken(); 47 | } 48 | } 49 | } 50 | 51 | return result; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/CreateProcedureStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | using Laan.Sql.Parser.Exceptions; 6 | 7 | namespace Laan.Sql.Parser.Parsers 8 | { 9 | public class CreateProcedureStatementParser : StatementParser 10 | { 11 | internal CreateProcedureStatementParser(ITokenizer tokenizer) : base(tokenizer) 12 | { 13 | } 14 | 15 | public override CreateProcedureStatement Execute() 16 | { 17 | _statement = new CreateProcedureStatement() { Name = GetDotNotationIdentifier(), IsAlter = IsAlter, IsShortForm = IsShortForm }; 18 | 19 | if (!Tokenizer.IsNextToken(Constants.As)) 20 | { 21 | var parser = new VariableDefinitionParser(Tokenizer); 22 | 23 | if (Tokenizer.IsNextToken(Constants.OpenBracket)) 24 | { 25 | using (Tokenizer.ExpectBrackets()) 26 | { 27 | _statement.Arguments = parser.Execute(); 28 | 29 | if (_statement.Arguments.Count == 0) 30 | throw new SyntaxException("expected 1 or more arguments"); 31 | } 32 | _statement.HasBracketedArguments = true; 33 | } 34 | else 35 | _statement.Arguments = parser.Execute(); 36 | } 37 | 38 | ExpectToken(Constants.As); 39 | 40 | _statement.Definition = ParserFactory.Execute(Tokenizer, true).Single(); 41 | 42 | return _statement; 43 | } 44 | 45 | public bool IsAlter { get; set; } 46 | public bool IsShortForm { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Interfaces/ITokenizer.cs: -------------------------------------------------------------------------------- 1 | using Laan.Sql.Parser.Exceptions; 2 | 3 | namespace Laan.Sql.Parser 4 | { 5 | public interface ITokenizer 6 | { 7 | bool SkipWhiteSpace { set; } 8 | bool HasMoreTokens { get; } 9 | 10 | /// 11 | /// Returns true if is contained in 12 | /// 13 | /// 14 | /// 15 | bool IsNextToken( params string[] tokenSet ); 16 | 17 | /// 18 | /// Check that the current token equals one of the supplied values. 19 | /// If so the current token is advanced 20 | /// 21 | /// Tokens to compare 22 | /// 23 | bool TokenEquals( string value ); 24 | 25 | void ReadNextToken(); 26 | 27 | Token Current { get; } 28 | 29 | /// 30 | /// Verify current token matches expected token. Read next token if successful. 31 | /// 32 | /// Expected token 33 | /// current token did not match 34 | void ExpectToken( string token ); 35 | 36 | /// 37 | /// Verify current tokens matche expected tokens. Read next token if successful. 38 | /// 39 | /// Expected tokens 40 | /// current token did not match 41 | void ExpectTokens( string[] tokens ); 42 | 43 | Position Position { get; } 44 | 45 | BracketStructure ExpectBrackets(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Console/Argument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | public class Argument 7 | { 8 | /// 9 | /// Parses command line argument in the form -File (file) -Sql (sql) [-Output (output)] [-Diagnostic] 10 | /// 11 | /// 12 | public Argument(string[] args) 13 | { 14 | PropertyInfo property = null; 15 | var typeConverter = new TypeConverter(); 16 | 17 | void SetValue(object value) 18 | { 19 | property.SetValue(this, value); 20 | property = null; 21 | } 22 | 23 | if (args.Length == 0) 24 | { 25 | Console.WriteLine("usage: sqlformat.exe -File (file) -Sql (sql) [-Output (output)] [-Diagnostics]"); 26 | Environment.Exit(1); 27 | return; 28 | } 29 | 30 | foreach (string arg in args) 31 | { 32 | if (property == null) 33 | { 34 | if (arg.StartsWith("-")) 35 | { 36 | property = GetType().GetProperties().SingleOrDefault(p => String.Compare(p.Name, arg.Trim('-'), StringComparison.OrdinalIgnoreCase) == 0); 37 | 38 | if (property?.PropertyType == typeof(bool)) 39 | SetValue(true); 40 | } 41 | } 42 | else 43 | { 44 | SetValue(property.PropertyType != typeof(string) ? typeConverter.ConvertFromString(arg) : arg); 45 | } 46 | } 47 | } 48 | 49 | public bool Diagnostics { get; set; } 50 | public string File { get; set; } 51 | public string Output { get; set; } 52 | public string Sql { get; set; } 53 | } 54 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/lib/prettify/lang-sql.min.js: -------------------------------------------------------------------------------- 1 | PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xa0]+/,null," \n\r  "],["str",/^(?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\n\r]*|\/\*[\S\s]*?(?:\*\/|$))/],["kwd",/^(?:add|all|alter|and|any|apply|as|asc|authorization|backup|begin|between|break|browse|bulk|by|cascade|case|check|checkpoint|close|clustered|coalesce|collate|column|commit|compute|connect|constraint|contains|containstable|continue|convert|create|cross|current|current_date|current_time|current_timestamp|current_user|cursor|database|dbcc|deallocate|declare|default|delete|deny|desc|disk|distinct|distributed|double|drop|dummy|dump|else|end|errlvl|escape|except|exec|execute|exists|exit|fetch|file|fillfactor|following|for|foreign|freetext|freetexttable|from|full|function|goto|grant|group|having|holdlock|identity|identitycol|identity_insert|if|in|index|inner|insert|intersect|into|is|join|key|kill|left|like|lineno|load|match|matched|merge|natural|national|nocheck|nonclustered|nocycle|not|null|nullif|of|off|offsets|on|open|opendatasource|openquery|openrowset|openxml|option|or|order|outer|over|partition|percent|pivot|plan|preceding|precision|primary|print|proc|procedure|public|raiserror|read|readtext|reconfigure|references|replication|restore|restrict|return|revoke|right|rollback|rowcount|rowguidcol|rows?|rule|save|schema|select|session_user|set|setuser|shutdown|some|start|statistics|system_user|table|textsize|then|to|top|tran|transaction|trigger|truncate|tsequal|unbounded|union|unique|unpivot|update|updatetext|use|user|using|values|varying|view|waitfor|when|where|while|with|within|writetext|xml)(?=[^\w-]|$)/i,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:\.\d+|\d+(?:\.\d*)?)(?:e[+-]?\d+)?)/i],["pln",/^[_a-z][\w-]*/i],["pun",/^[^\w\t\n\r "'\xa0][^\w\t\n\r "'+\xa0-]*/]]),["sql"]); -------------------------------------------------------------------------------- /Laan.Sql.Parser/Expressions/RankingFunctionExpression.cs: -------------------------------------------------------------------------------- 1 | using Laan.Sql.Parser.Entities; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Laan.Sql.Parser.Expressions 8 | { 9 | public class RankingFunctionExpression : Expression, IInlineFormattable 10 | { 11 | /// 12 | /// Initializes a new instance of the RankingFunctionExpression class. 13 | /// 14 | public RankingFunctionExpression(Expression parent) : base(parent) 15 | { 16 | OrderBy = new List(); 17 | PartitionBy = new List(); 18 | } 19 | 20 | public List OrderBy { get; set; } 21 | public List PartitionBy { get; set; } 22 | 23 | public string Name { get; set; } 24 | 25 | public override string Value 26 | { 27 | get 28 | { 29 | List parts = new List(); 30 | 31 | if (PartitionBy.Any()) 32 | { 33 | parts.Add(String.Format( 34 | "{0} {1} {2}", 35 | Constants.Partition, 36 | Constants.By, 37 | PartitionBy.Select(arg => arg.Expression.Value + arg.Value).ToCsv() 38 | )); 39 | } 40 | 41 | parts.Add(String.Format( 42 | "{0} {1} {2}", 43 | Constants.Order, 44 | Constants.By, 45 | OrderBy.Select(arg => arg.Expression.Value + arg.Value).ToCsv() 46 | )); 47 | 48 | return String.Format("{0} OVER ({1})", Name, parts.Join(" ")); 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/ExecStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using Laan.Sql.Parser.Entities; 6 | using Laan.Sql.Parser; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class ExecStatementFormatter : StatementFormatter, IStatementFormatter 11 | { 12 | public ExecStatementFormatter(IIndentable indentable, StringBuilder sql, ExecStatement statement) : base(indentable, sql, statement) 13 | { 14 | } 15 | 16 | private static string GetArgumentFormatted(Argument argument) 17 | { 18 | return !String.IsNullOrEmpty(argument.Name) ? String.Format("{0} = {1}", argument.Name, argument.Value) : argument.Value.ToString(); 19 | } 20 | 21 | public void Execute() 22 | { 23 | IndentAppendFormat("{0} {1}", Constants.Exec, _statement.FunctionName); 24 | 25 | if (!_statement.Arguments.Any()) 26 | return; 27 | 28 | var inline = String.Join(", ", _statement.Arguments.Select(a => GetArgumentFormatted(a)).ToArray()); 29 | 30 | if (FitsOnRow(inline)) 31 | IndentAppend(" " + inline); 32 | else 33 | { 34 | IndentAppendLine(String.Empty); 35 | using (new IndentScope(this)) 36 | { 37 | var lastArgument = _statement.Arguments.Last(); 38 | foreach (var argument in _statement.Arguments) 39 | { 40 | var lineEnding = (argument != lastArgument ? ",\n\r" : ""); 41 | IndentAppend(GetArgumentFormatted(argument) + lineEnding); 42 | } 43 | } 44 | } 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Commands/BaseRightCusorAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.AddIns.Ssms.VsExtension.Utils; 5 | 6 | namespace Laan.AddIns.Ssms.VsExtension.Commands 7 | { 8 | public abstract class BaseRightCusorAction : BaseCursorAction 9 | { 10 | protected void CursorRight(bool applySelection) 11 | { 12 | Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); 13 | 14 | var textDocument = TextDocument; 15 | var cursor = new Cursor(textDocument.Selection.CurrentColumn, textDocument.Selection.TopPoint.Line); 16 | 17 | if (cursor.Column == CurrentLine.Length) 18 | return; 19 | 20 | var line = CurrentLine; 21 | if (String.IsNullOrEmpty(line)) 22 | return; 23 | 24 | var rightOfCursor = line.Substring(cursor.Column - 1, line.Length - cursor.Column + 1); 25 | 26 | var position = 1; 27 | while (position < rightOfCursor.Length - 1) 28 | { 29 | if (IsSpace(rightOfCursor, position)) 30 | { 31 | textDocument.Selection.CharRight(applySelection, position + 1); 32 | return; 33 | } 34 | 35 | if (IsCapital(rightOfCursor, position) 36 | && !IsCapital(rightOfCursor, position + 1) 37 | && !IsSpace(rightOfCursor, position + 1) 38 | ) 39 | { 40 | textDocument.Selection.CharRight(applySelection, position); 41 | return; 42 | } 43 | 44 | position++; 45 | } 46 | 47 | textDocument.Selection.CharRight(applySelection, rightOfCursor.Length); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Laan.NHibernate.Appender.Test/ParamBuilderFormatterTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using Laan.Sql.Formatter; 6 | 7 | using NUnit.Framework; 8 | 9 | namespace Laan.NHibernate.Appender.Test 10 | { 11 | [TestFixture] 12 | public class ParamBuilderFormatterTest 13 | { 14 | private const string sample = 15 | @"Batch commands: 16 | command 0:INSERT INTO dbo.[Table] (Version, TypeID, Name, ShortName, Data, UserName, Created, Modified, IsDeleted, Id) VALUES " + 17 | "(@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9);@p0 = 1 [Type: Int32 (0)], @p1 = '0' [Type: String (255)], @p2 = " + 18 | "'Reference' [Type: String (4000)], @p3 = NULL [Type: String (4000)], @p4 = 0 [Type: Int32 (0)], @p5 = 'auser' " + 19 | "[Type: String (4000)], @p6 = 9/03/2012 11:39:50 AM [Type: DateTime (0)], @p7 = 9/03/2012 11:39:50 AM [Type: DateTime (0)], " + 20 | "@p8 = False [Type: Boolean (0)], @p9 = d4a462ab-da14-46d2-9118-a00f00c037c6 [Type: Guid (0)]"; 21 | 22 | private const string expected = 23 | @"INSERT INTO dbo.[Table] ( 24 | Version, TypeID, Name, ShortName, Data, UserName, Created, Modified, IsDeleted, 25 | Id 26 | ) 27 | VALUES (1, '0', 'Reference', NULL, 0, 'auser', '9/03/2012 11:39:50 AM', '9/03/2012 11:39:50 AM', False, 'D4A462AB-DA14-46D2-9118-A00F00C037C6')"; 28 | [Test] 29 | public void TestMethod() 30 | { 31 | // Arrange 32 | var engine = new FormattingEngine(); 33 | var sut = new ParamBuilderFormatter(engine); 34 | 35 | // Act 36 | string result = sut.Execute(sample); 37 | 38 | 39 | // Assert 40 | Assert.AreEqual(expected, result); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/CommonTableExpressionStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class CommonTableExpressionStatementFormatter : StatementFormatter, IStatementFormatter 11 | { 12 | public CommonTableExpressionStatementFormatter(IIndentable indentable, StringBuilder sql, CommonTableExpressionStatement statement) 13 | : base(indentable, sql, statement) 14 | { 15 | } 16 | 17 | public void Execute() 18 | { 19 | _sql.Append("WITH "); 20 | 21 | foreach (var commonTableExpression in _statement.CommonTableExpressions) 22 | { 23 | if (commonTableExpression.ColumnNames.Any()) 24 | { 25 | var fields = commonTableExpression.ColumnNames.ToCsv(); 26 | _sql.AppendFormat("{0} ({1}) AS ({2}{2}", commonTableExpression.Name, fields, Environment.NewLine); 27 | } 28 | else 29 | _sql.AppendFormat("{0} AS ({1}{1}", commonTableExpression.Name, Environment.NewLine); 30 | 31 | using (new IndentScope(this)) 32 | { 33 | var cteFormatter = new SelectStatementFormatter(this, _sql, commonTableExpression); 34 | cteFormatter.Execute(); 35 | 36 | _sql.AppendFormat( 37 | "{1}){0}{1}", 38 | (commonTableExpression != _statement.CommonTableExpressions.Last() ? "," : String.Empty), 39 | Environment.NewLine 40 | ); 41 | } 42 | } 43 | 44 | var formatter = new SelectStatementFormatter(this, _sql, _statement.Statement); 45 | formatter.Execute(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Console/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | using Laan.Sql.Formatter; 6 | 7 | internal static class Program 8 | { 9 | // TODO: Implement a proper argument class for passing SQL, or a file, as well as output 10 | private static void Main(string[] args) 11 | { 12 | var argument = new Argument(args); 13 | var engine = new FormattingEngine(); 14 | 15 | var timer = new System.Diagnostics.Stopwatch(); 16 | timer.Start(); 17 | try 18 | { 19 | string sql = null; 20 | 21 | if (argument.Sql != null) 22 | sql = argument.Sql; 23 | else 24 | if (argument.File != null && File.Exists(argument.File)) 25 | sql = File.ReadAllText(argument.File); 26 | 27 | if (sql == null) 28 | { 29 | Console.WriteLine("No SQL found - either supply -Sql or -File arguments"); 30 | Environment.Exit(2); 31 | return; 32 | } 33 | 34 | var output = engine.Execute(sql); 35 | 36 | if (argument.Output != null) 37 | { 38 | File.WriteAllText(argument.Output, output, Encoding.UTF8); 39 | } 40 | else 41 | { 42 | var formattedSql = output.TrimEnd().Split(new[] { "\r\n" }, StringSplitOptions.None); 43 | foreach (var line in formattedSql) 44 | Console.WriteLine(line); 45 | } 46 | 47 | if (argument.Diagnostics) 48 | Console.WriteLine("\nElapsed Time: " + TimeSpan.FromMilliseconds(timer.ElapsedMilliseconds)); 49 | 50 | Environment.Exit(0); 51 | } 52 | catch (Exception ex) 53 | { 54 | Console.WriteLine(ex); 55 | Environment.Exit(3); 56 | } 57 | finally 58 | { 59 | timer.Stop(); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/IfStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Laan.Sql.Parser.Expressions; 4 | using Laan.Sql.Parser.Entities; 5 | using Laan.Sql.Parser.Exceptions; 6 | 7 | namespace Laan.Sql.Parser.Parsers 8 | { 9 | public class IfStatementParser : StatementParser 10 | { 11 | public IfStatementParser( ITokenizer tokenizer ) : base( tokenizer ) 12 | { 13 | } 14 | 15 | private void ProcessIfCondition() 16 | { 17 | var condition = new ExpressionParser( Tokenizer ).Execute(); 18 | if ( !( condition is CriteriaExpression || condition is FunctionExpression || condition is NegationExpression || condition is NestedExpression ) ) 19 | throw new SyntaxException( "IF expects a boolean result expression" ); 20 | 21 | _statement.Condition = condition; 22 | } 23 | 24 | private void ProcessTrueBlock() 25 | { 26 | if ( !Tokenizer.HasMoreTokens ) 27 | throw new SyntaxException( "missing success block for IF" ); 28 | 29 | _statement.If = ParserFactory 30 | .GetParser( Tokenizer ) 31 | .Execute(); 32 | } 33 | 34 | private void ProcessFalseBlock() 35 | { 36 | if ( Tokenizer.TokenEquals( Constants.Else ) ) 37 | { 38 | if ( !Tokenizer.HasMoreTokens ) 39 | throw new SyntaxException( "missing else block for IF" ); 40 | 41 | _statement.Else = ParserFactory 42 | .GetParser( Tokenizer ) 43 | .Execute(); 44 | } 45 | } 46 | 47 | public override IfStatement Execute() 48 | { 49 | if ( !Tokenizer.HasMoreTokens ) 50 | throw new SyntaxException( "missing condition for IF" ); 51 | 52 | _statement = new IfStatement(); 53 | 54 | ProcessIfCondition(); 55 | ProcessTrueBlock(); 56 | ProcessFalseBlock(); 57 | 58 | return _statement; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/Models/Template.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using System.Xml; 7 | using System.Xml.Serialization; 8 | 9 | namespace Laan.AddIns.Ssms.VsExtension.Models 10 | { 11 | public class Template : INotifyPropertyChanged 12 | { 13 | private string _body; 14 | private string _code; 15 | private string _name; 16 | private string _description; 17 | 18 | [XmlElement("name")] 19 | public string Name 20 | { 21 | get => _name; 22 | set { Notify(ref _name, value); } 23 | } 24 | 25 | [XmlElement("code")] 26 | public string Code 27 | { 28 | get => _code; 29 | set { Notify(ref _code, value); } 30 | } 31 | 32 | [XmlElement("description")] 33 | public string Description 34 | { 35 | get => _description; 36 | set { Notify(ref _description, value); } 37 | } 38 | 39 | [XmlElement("body")] 40 | public XmlCDataSection CDataBody 41 | { 42 | get => new XmlDocument().CreateCDataSection(Body); 43 | set => Body = value.Value.TrimStart(); 44 | } 45 | 46 | [XmlIgnore] 47 | public string Body 48 | { 49 | get => _body; 50 | set => Notify(ref _body, value); 51 | } 52 | 53 | public event PropertyChangedEventHandler PropertyChanged; 54 | 55 | private void Notify(ref T field, T value, [CallerMemberName]string propertyName = "") 56 | { 57 | if (field != null && field.Equals(value)) 58 | return; 59 | 60 | field = value; 61 | 62 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 63 | } 64 | 65 | public Template Clone() 66 | { 67 | var clone = (Template)MemberwiseClone(); 68 | clone.Name += " (Copy)"; 69 | return clone; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/Factories/ExpressionFormatterFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser; 5 | using Laan.Sql.Parser.Expressions; 6 | 7 | namespace Laan.Sql.Formatter 8 | { 9 | public class ExpressionFormatterFactory 10 | { 11 | private static Dictionary _formatters; 12 | 13 | static ExpressionFormatterFactory() 14 | { 15 | _formatters = new Dictionary 16 | { 17 | { typeof( CriteriaExpression ), typeof( CriteriaExpressionFormatter ) }, 18 | { typeof( CaseSwitchExpression ), typeof( CaseSwitchExpressionFormatter ) }, 19 | { typeof( CaseWhenExpression ), typeof( CaseWhenExpressionFormatter ) }, 20 | { typeof( FunctionExpression ), typeof( FunctionExpressionFormatter ) }, 21 | { typeof( NestedExpression ), typeof( NestedExpressionFormatter ) }, 22 | { typeof( SelectExpression ), typeof( SelectExpressionFormatter ) }, 23 | { typeof( ExpressionList ), typeof( ExpressionListFormatter ) }, 24 | { typeof( BetweenExpression ), typeof( BetweenExpressionFormatter ) }, 25 | { typeof( NegationExpression ), typeof( NegationExpressionFormatter ) }, 26 | }; 27 | } 28 | 29 | public static IExpressionFormatter GetFormatter(IIndentable indentable, Expression expression) 30 | { 31 | Type formatterType; 32 | if (!_formatters.TryGetValue(expression.GetType(), out formatterType)) 33 | return new DefaultExpressionFormatter(expression); 34 | 35 | var formatter = Activator.CreateInstance(formatterType, expression) as IExpressionFormatter; 36 | (formatter as IIndentable).IndentLevel = indentable.IndentLevel; 37 | (formatter as IIndentable).Indent = indentable.Indent; 38 | 39 | if (formatter == null) 40 | throw new ArgumentNullException("Formatter not instantiated: " + formatterType.Name); 41 | 42 | return formatter; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/UpdateStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Laan.Sql.Parser; 6 | using Laan.Sql.Parser.Entities; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class UpdateStatementFormatter : CustomStatementFormatter, IStatementFormatter 11 | { 12 | public UpdateStatementFormatter( IIndentable indentable, StringBuilder sql, UpdateStatement statement ) 13 | : base( indentable, sql, statement ) 14 | { 15 | } 16 | 17 | #region IStatementFormatter Members 18 | 19 | public void Execute() 20 | { 21 | FormatUpdate(); 22 | FormatFieldAssignment(); 23 | FormatFrom(); 24 | FormatWhere(); 25 | FormatTerminator(); 26 | } 27 | 28 | #endregion 29 | 30 | private void FormatFieldAssignment() 31 | { 32 | string format = String.Format( 33 | " {{0}} {{1,{0}}} = {{2}}{{3}}", 34 | -1 * _statement.Fields.Max( f => f.Alias.Name.Length ) 35 | ); 36 | 37 | foreach ( Field field in _statement.Fields ) 38 | { 39 | string separator = field != _statement.Fields.Last() ? Constants.Comma + Environment.NewLine : ""; 40 | string set = field == _statement.Fields.First() ? "SET" : " "; 41 | 42 | IndentAppendFormat( 43 | format, 44 | set, 45 | field.Alias.Name, 46 | field.Expression.FormattedValue( 0, this ), 47 | separator 48 | ); 49 | } 50 | } 51 | 52 | private void FormatUpdate() 53 | { 54 | IndentAppend( Constants.Update ); 55 | FormatTop( _statement.Top ); 56 | _sql.AppendFormat( " {0}\n", _statement.TableName ); 57 | } 58 | 59 | protected override bool CanCompactFormat() 60 | { 61 | return false; 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Entities/CreateIndexStatement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Laan.Sql.Parser.Expressions; 5 | 6 | namespace Laan.Sql.Parser.Entities 7 | { 8 | public enum IndexWithOption 9 | { 10 | PadIndex, 11 | SortInTempDb, 12 | IgnoreDupKey, 13 | StatisticsNorecompute, 14 | DropExisting, 15 | Online, 16 | AllowRowLocks, 17 | AllowPageLocks 18 | } 19 | 20 | public class RelationalIndexOption 21 | { 22 | 23 | /// 24 | /// Initializes a new instance of the SortedField class. 25 | /// 26 | /// 27 | public RelationalIndexOption() 28 | { 29 | } 30 | 31 | public IndexWithOption Option { get; set; } 32 | 33 | public Expression Assignment { get; set; } 34 | 35 | } 36 | 37 | public class CreateIndexStatement : CustomStatement 38 | { 39 | public CreateIndexStatement() 40 | { 41 | Unique = false; 42 | Clustered = false; 43 | Columns = new List(); 44 | RelationalIndexOptions = new List(); 45 | } 46 | 47 | public bool Clustered { get; set; } 48 | public bool Unique { get; set; } 49 | public string TableName { get; set; } 50 | public string IndexName { get; set; } 51 | public List Columns { get; set; } 52 | public List RelationalIndexOptions { get; set; } 53 | public string FileGroupName { get; set; } 54 | 55 | #region IStatement Members 56 | 57 | public override string Identifier 58 | { 59 | get 60 | { 61 | return String.Format( 62 | "CREATE INDEX {0}ON {1}({2})", 63 | IndexName, 64 | TableName, 65 | String.Join( ", ", Columns.Select( c => c.Name ).ToArray() ) 66 | ); 67 | } 68 | } 69 | 70 | #endregion 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/CustomExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | using Laan.Sql.Formatter; 6 | using Laan.Sql.Parser.Expressions; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class CustomExpressionFormatter : IIndentable, IExpressionFormatter where T : Expression 11 | { 12 | private const int MaxColumnWidth = 80; 13 | private const int TabSize = 4; 14 | 15 | protected T _expression; 16 | 17 | public CustomExpressionFormatter(T expression) 18 | { 19 | Indent = GetSpaces(TabSize); 20 | _expression = expression; 21 | } 22 | 23 | protected bool CanInlineExpression(Expression expr, int offset) 24 | { 25 | return expr is IInlineFormattable 26 | && ((IInlineFormattable)expr).CanInline 27 | && expr.Value.Length < MaxColumnWidth - offset; 28 | } 29 | 30 | protected int GetCurrentColumn(StringBuilder sql) 31 | { 32 | return sql.ToString().Split('\n').Last().Length; 33 | } 34 | 35 | protected string GetIndent(bool includeNewLine) 36 | { 37 | var result = new StringBuilder(includeNewLine ? Environment.NewLine : String.Empty); 38 | 39 | for (int index = 0; index < IndentLevel; index++) 40 | result.Append(Indent); 41 | 42 | return result.ToString(); 43 | } 44 | 45 | protected static string GetSpaces(int offset) 46 | { 47 | return new string(' ', offset); 48 | } 49 | 50 | protected T _statement; 51 | 52 | 53 | public virtual string Execute() 54 | { 55 | return _expression.Value; 56 | } 57 | 58 | public int Offset { get; set; } 59 | 60 | public string Indent { get; set; } 61 | public int IndentLevel { get; set; } 62 | 63 | public void IncreaseIndent() 64 | { 65 | IndentLevel++; 66 | } 67 | 68 | public void DecreaseIndent() 69 | { 70 | IndentLevel--; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Laan.Sql.Formatter.Web.Blazor 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/StatementFormatters/ExecuteSqlStatementFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using Laan.Sql.Parser; 7 | using Laan.Sql.Parser.Entities; 8 | using Laan.Sql.Parser.Expressions; 9 | 10 | namespace Laan.Sql.Formatter 11 | { 12 | public class ExecuteSqlStatementFormatter : StatementFormatter, IStatementFormatter 13 | { 14 | public ExecuteSqlStatementFormatter(IIndentable indentable, StringBuilder sql, ExecuteSqlStatement statement) 15 | : base(indentable, sql, statement) 16 | { 17 | } 18 | 19 | public void Execute() 20 | { 21 | var statements = new List(); 22 | 23 | if (_statement.Arguments.Any()) 24 | { 25 | DeclareStatement declare = new DeclareStatement(); 26 | declare.Definitions.AddRange( 27 | _statement.Arguments.Select(a => new VariableDefinition(a.Name, a.Type)) 28 | ); 29 | statements.Add(declare); 30 | 31 | SelectStatement initialise = new SelectStatement(); 32 | initialise.Fields.AddRange( 33 | _statement.Arguments.Select(a => 34 | new Field 35 | { 36 | Expression = new CriteriaExpression(null) 37 | { 38 | Left = new StringExpression(a.Name, null), 39 | Operator = Constants.Assignment, 40 | Right = new StringExpression(a.Value.ToString(), null) 41 | } 42 | } 43 | ) 44 | ); 45 | 46 | statements.Add(initialise); 47 | } 48 | 49 | statements.AddRange(_statement.InnerStatements); 50 | 51 | var last = statements.Last(); 52 | foreach (IStatement statement in statements) 53 | { 54 | FormatStatement(statement); 55 | if (statement != last) 56 | NewLine(2); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter/ExpressionFormatters/FunctionExpressionFormatter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Expressions; 5 | using Laan.Sql.Parser; 6 | using System.Text; 7 | 8 | namespace Laan.Sql.Formatter 9 | { 10 | public class FunctionExpressionFormatter : CustomExpressionFormatter 11 | { 12 | public FunctionExpressionFormatter( FunctionExpression expression ) : base( expression ) 13 | { 14 | } 15 | 16 | #region IExpressionFormatter Members 17 | 18 | public override string Execute() 19 | { 20 | if ( String.Compare( _expression.Name, Constants.Exists, true ) == 0 ) 21 | { 22 | StringBuilder sql = new StringBuilder(); 23 | sql.AppendFormat( "{0}(\r\n", _expression.Name ); 24 | sql.AppendLine(); 25 | using ( new IndentScope( this ) ) 26 | { 27 | sql.Append( _expression.Arguments.First().FormattedValue( Offset, this ) ); 28 | } 29 | sql.AppendLine(); 30 | sql.Append( GetIndent( true ) + ")" ); 31 | return sql.ToString(); 32 | } 33 | else 34 | { 35 | string[] args = _expression.Arguments 36 | .Select( arg => arg.FormattedValue( Offset, this ) ) 37 | .ToArray(); 38 | 39 | bool canInline = _expression.Value.Length <= 40; 40 | string comma = Constants.Comma + ( canInline ? " " : "" ); 41 | 42 | StringBuilder sql = new StringBuilder(); 43 | sql.AppendFormat( "{0}(", _expression.Name ); 44 | using ( new IndentScope( this ) ) 45 | { 46 | if ( !canInline ) 47 | sql.Append( GetIndent( true ) ); 48 | string separator = !canInline ? comma + GetIndent( true ) : comma; 49 | sql.Append( String.Join( separator, args ) ); 50 | } 51 | sql.Append( ( canInline ? "" : GetIndent( true ) ) + ")" ); 52 | return sql.ToString(); 53 | } 54 | 55 | } 56 | 57 | #endregion 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/SqlTemplateOption/DialogHost.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Forms; 6 | 7 | namespace Laan.AddIns.Forms 8 | { 9 | public interface IDialog 10 | { 11 | event EventHandler OnSave; 12 | event EventHandler OnCancel; 13 | bool CanCancel { get; } 14 | } 15 | 16 | public partial class DialogHost : Form 17 | { 18 | private IDialog _dialog; 19 | 20 | public DialogHost() 21 | { 22 | InitializeComponent(); 23 | } 24 | 25 | public DialogResult Show(object model) where T : FrameworkElement, new() 26 | { 27 | var element = new T() { DataContext = model }; 28 | 29 | Height = (int)element.Height; 30 | Width = (int)element.Width; 31 | 32 | wpfElementHost.AutoSize = true; 33 | wpfElementHost.Child = element; 34 | 35 | KeyPreview = true; 36 | 37 | _dialog = (IDialog)model; 38 | if (_dialog != null) 39 | { 40 | _dialog.OnSave += dialog_Save; 41 | _dialog.OnCancel += dialog_Cancel; 42 | } 43 | 44 | KeyDown += (sender, e) => 45 | { 46 | if (CanCancel()) 47 | CancelDialog(); 48 | }; 49 | this.FormClosing += DialogHost_FormClosing; 50 | 51 | return ShowDialog(); 52 | } 53 | 54 | private void DialogHost_FormClosing(object sender, FormClosingEventArgs e) 55 | { 56 | e.Cancel = !CanCancel(); 57 | } 58 | 59 | private void dialog_Cancel(object sender, EventArgs e) 60 | { 61 | CancelDialog(); 62 | } 63 | 64 | private void dialog_Save(object sender, EventArgs e) 65 | { 66 | DialogResult = DialogResult.OK; 67 | Close(); 68 | } 69 | 70 | private bool CanCancel() 71 | { 72 | return _dialog == null || _dialog.CanCancel; 73 | } 74 | 75 | private void CancelDialog() 76 | { 77 | DialogResult = DialogResult.Cancel; 78 | Close(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/TestTokens.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using NUnit.Framework; 4 | 5 | namespace Laan.Sql.Parser.Test 6 | { 7 | [TestFixture] 8 | public class TestTokens 9 | { 10 | [Test] 11 | public void Empty_Tokens_Are_The_Same() 12 | { 13 | // Setup 14 | Token empty1 = new Token(); 15 | Token empty2 = new Token(); 16 | 17 | // Verify outcome 18 | Assert.IsTrue( empty1 == empty2 ); 19 | } 20 | 21 | [Test] 22 | public void One_Empty_And_One_Non_Empty_Tokens_Are_Different() 23 | { 24 | // Setup 25 | Token nonEmpty = new Token( "SELECT", TokenType.AlphaNumeric ); 26 | Token empty = new Token(); 27 | 28 | // Verify outcome 29 | Assert.IsTrue( nonEmpty != empty ); 30 | } 31 | 32 | [Test] 33 | public void Two_Non_Empty_Tokens_Are_Same() 34 | { 35 | // Setup 36 | Token nonEmpty1 = new Token( "SELECT", TokenType.AlphaNumeric ); 37 | Token nonEmpty2 = new Token( "SELECT", TokenType.AlphaNumeric ); 38 | 39 | // Verify outcome 40 | Assert.IsTrue( nonEmpty1 == nonEmpty2 ); 41 | } 42 | 43 | [Test] 44 | public void Two_Non_Empty_Tokens_Are_Different() 45 | { 46 | // Setup 47 | Token nonEmpty1 = new Token( "SELECT", TokenType.AlphaNumeric ); 48 | Token nonEmpty2 = new Token( "UPDATE", TokenType.AlphaNumeric ); 49 | 50 | // Verify outcome 51 | Assert.IsTrue( nonEmpty1 != nonEmpty2 ); 52 | } 53 | 54 | [Test] 55 | public void One_Non_Empty_Tokens_And_One_String_Are_Same() 56 | { 57 | // Setup 58 | Token nonEmpty = new Token( "SELECT", TokenType.AlphaNumeric ); 59 | string value = "SELECT"; 60 | 61 | // Verify outcome 62 | Assert.IsTrue( nonEmpty == value ); 63 | } 64 | 65 | [Test] 66 | public void One_Non_Empty_Tokens_And_One_String_Are_Different() 67 | { 68 | // Setup 69 | Token nonEmpty = new Token( "SELECT", TokenType.AlphaNumeric ); 70 | string value = "UPDATE"; 71 | 72 | // Verify outcome 73 | Assert.IsTrue( nonEmpty != value ); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Laan.Sql.Formatter.Web.Blazor/wwwroot/service-worker.published.js: -------------------------------------------------------------------------------- 1 | // Caution! Be sure you understand the caveats before publishing an application with 2 | // offline support. See https://aka.ms/blazor-offline-considerations 3 | 4 | self.importScripts('./service-worker-assets.js'); 5 | self.addEventListener('install', event => event.waitUntil(onInstall(event))); 6 | self.addEventListener('activate', event => event.waitUntil(onActivate(event))); 7 | self.addEventListener('fetch', event => event.respondWith(onFetch(event))); 8 | 9 | const cacheNamePrefix = 'offline-cache-'; 10 | const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`; 11 | const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/ ]; 12 | const offlineAssetsExclude = [ /^service-worker\.js$/ ]; 13 | 14 | async function onInstall(event) { 15 | console.info('Service worker: Install'); 16 | 17 | // Fetch and cache all matching items from the assets manifest 18 | const assetsRequests = self.assetsManifest.assets 19 | .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url))) 20 | .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url))) 21 | .map(asset => new Request(asset.url, { integrity: asset.hash })); 22 | await caches.open(cacheName).then(cache => cache.addAll(assetsRequests)); 23 | } 24 | 25 | async function onActivate(event) { 26 | console.info('Service worker: Activate'); 27 | 28 | // Delete unused caches 29 | const cacheKeys = await caches.keys(); 30 | await Promise.all(cacheKeys 31 | .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName) 32 | .map(key => caches.delete(key))); 33 | } 34 | 35 | async function onFetch(event) { 36 | let cachedResponse = null; 37 | if (event.request.method === 'GET') { 38 | // For all navigation requests, try to serve index.html from cache 39 | // If you need some URLs to be server-rendered, edit the following check to exclude those URLs 40 | const shouldServeIndexHtml = event.request.mode === 'navigate'; 41 | 42 | const request = shouldServeIndexHtml ? 'index.html' : event.request; 43 | const cache = await caches.open(cacheName); 44 | cachedResponse = await cache.match(request); 45 | } 46 | 47 | return cachedResponse || fetch(event.request); 48 | } 49 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Tokenizer/SqlTokenizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | namespace Laan.Sql.Parser 6 | { 7 | public class SqlTokenizer : RegexTokenizer, ITokenizer 8 | { 9 | public SqlTokenizer(string input) : base(input) 10 | { 11 | TokenDefinitions.AddRange(new[] 12 | { 13 | new TokenDefinition( TokenType.InLineComment, true, @"\-\-[^\n^\r]*" ), 14 | new TokenDefinition( TokenType.MultiLineComment, true, @"(\/\*)", @"(\*\/)"), 15 | new TokenDefinition( TokenType.String, false, @"N?'", @"'"), 16 | new TokenDefinition( TokenType.Variable, false, @"(:|@{1,2}|#{1,2})[A-Za-z_]+[[A-Za-z_0-9]*" ), 17 | new TokenDefinition( TokenType.AlphaNumeric, false, @"[A-Za-z_]+\w*" ), 18 | new TokenDefinition( TokenType.Numeric, false, @"[-]?\d*[0-9](|.\d*[0-9]|,\d*[0-9])?" ), 19 | new TokenDefinition( TokenType.OpenBracket, false, @"\(" ), 20 | new TokenDefinition( TokenType.CloseBracket, false, @"\)" ), 21 | new TokenDefinition( TokenType.DoubleQuote, false, @""".*"""), 22 | new TokenDefinition( TokenType.BlockedText, false, @"\[.*?\]" ), 23 | new TokenDefinition( TokenType.Symbol, false, @"[,.;:]" ), 24 | new TokenDefinition( TokenType.Operator, false, @"(\<\>)|[\*\\\/+\-!\<\>]?=|[\<\>]|[\+\-\*/\\/\%\^]" ), 25 | new TokenDefinition( TokenType.WhiteSpace, true, @"\s+" ) 26 | }); 27 | } 28 | 29 | public bool SkipComments 30 | { 31 | set 32 | { 33 | TokenDefinitions 34 | .Where(tdef => tdef.Type == TokenType.InLineComment || tdef.Type == TokenType.MultiLineComment) 35 | .ToList() 36 | .ForEach(tdef => tdef.Skip = value); 37 | } 38 | } 39 | 40 | public bool SkipWhiteSpace 41 | { 42 | set 43 | { 44 | var definitions = TokenDefinitions 45 | .Where(tdef => tdef.Type == TokenType.WhiteSpace) 46 | .ToList(); 47 | 48 | foreach (var definition in definitions) 49 | definition.Skip = value; 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Exceptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Laan.Sql.Parser.Exceptions 4 | { 5 | public abstract class ParserException : Exception 6 | { 7 | public ParserException() : base() 8 | { 9 | } 10 | 11 | public ParserException(string message) : base(message) 12 | { 13 | } 14 | 15 | public ParserException(string message, Exception inner) : base(message, inner) 16 | { 17 | } 18 | } 19 | 20 | public class ExpectedTokenNotFoundException : ParserException 21 | { 22 | internal ExpectedTokenNotFoundException(string token, string foundToken, Position position) 23 | : base("Expected: '" + token + "' but found: '" + foundToken + "' at " + position.ToString()) { } 24 | 25 | public ExpectedTokenNotFoundException() : base() { } 26 | 27 | public ExpectedTokenNotFoundException(string message) : base(message) { } 28 | 29 | public ExpectedTokenNotFoundException(string message, Exception innerException) : base(message, innerException) { } 30 | } 31 | 32 | public class SyntaxException : ParserException 33 | { 34 | public SyntaxException() : base() { } 35 | public SyntaxException(string message) : base(message) { } 36 | public SyntaxException(string message, Exception innerException) : base(message, innerException) { } 37 | 38 | } 39 | 40 | public class UnknownTokenException : ParserException 41 | { 42 | public UnknownTokenException() : base() { } 43 | public UnknownTokenException(string message) : base("'" + message + "'") { } 44 | public UnknownTokenException(string message, Exception innerException) : base(message, innerException) { } 45 | } 46 | 47 | public class ParserNotImplementedException : ParserException 48 | { 49 | public ParserNotImplementedException() : base() { } 50 | public ParserNotImplementedException(string message) : base(message) { } 51 | public ParserNotImplementedException(string message, Exception innerException) : base(message, innerException) { } 52 | } 53 | 54 | public class FormatterNotImplementedException : ParserException 55 | { 56 | public FormatterNotImplementedException() : base() { } 57 | public FormatterNotImplementedException(string message) : base(message) { } 58 | public FormatterNotImplementedException(string message, Exception innerException) : base(message, innerException) { } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/VariableDefinitionParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | using Laan.Sql.Parser.Exceptions; 6 | 7 | namespace Laan.Sql.Parser.Parsers 8 | { 9 | public class VariableDefinitionParser : CustomParser 10 | { 11 | private List _statement; 12 | 13 | public VariableDefinitionParser(ITokenizer tokenizer) : base(tokenizer) 14 | { 15 | _statement = new List(); 16 | } 17 | 18 | private string GetSqlType() 19 | { 20 | string typeName = Tokenizer.Current.Value; 21 | Tokenizer.ReadNextToken(); 22 | 23 | if (Tokenizer.IsNextToken(Constants.OpenBracket)) 24 | { 25 | using (new BracketStructure(Tokenizer)) 26 | { 27 | string number = Tokenizer.Current.Value; 28 | Tokenizer.ReadNextToken(); 29 | 30 | if (Tokenizer.TokenEquals(Constants.Comma)) 31 | { 32 | string precision = Tokenizer.Current.Value; 33 | Tokenizer.ReadNextToken(); 34 | return String.Format("{0}({1}, {2})", typeName, number, precision); 35 | } 36 | else 37 | return String.Format("{0}({1})", typeName, number); 38 | } 39 | } 40 | 41 | return typeName; 42 | } 43 | 44 | public List Execute() 45 | { 46 | do 47 | { 48 | string name = Tokenizer.Current.Value; 49 | Tokenizer.ReadNextToken(); 50 | 51 | if (!Tokenizer.HasMoreTokens) 52 | throw new SyntaxException(String.Format("type missing for declaration of variable '{0}'", name)); 53 | 54 | string type = GetSqlType(); 55 | var definition = new VariableDefinition(name, type); 56 | 57 | if (Tokenizer.TokenEquals(Constants.Assignment)) 58 | { 59 | var parser = new ExpressionParser(Tokenizer); 60 | definition.DefaultValue = parser.Execute(); 61 | } 62 | 63 | _statement.Add(definition); 64 | } 65 | while (Tokenizer.TokenEquals(Constants.Comma)); 66 | 67 | return _statement; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /Options/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("Options")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("Options")] 15 | [assembly: AssemblyCopyright("Copyright © Laan Software 2017")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | [assembly: ComVisible(false)] 23 | 24 | //In order to begin building localizable applications, set 25 | //CultureYouAreCodingWith in your .csproj file 26 | //inside a . For example, if you are using US english 27 | //in your source files, set the to en-US. Then uncomment 28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 29 | //the line below to match the UICulture setting in the project file. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 36 | //(used if a resource is not found in the page, 37 | // or application resource dictionaries) 38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 39 | //(used if a resource is not found in the page, 40 | // app, or any theme specific resource dictionaries) 41 | )] 42 | 43 | 44 | // Version information for an assembly consists of the following four values: 45 | // 46 | // Major Version 47 | // Minor Version 48 | // Build Number 49 | // Revision 50 | // 51 | // You can specify all the values or you can default the Build and Revision Numbers 52 | // by using the '*' as shown below: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.0.0.0")] 55 | [assembly: AssemblyFileVersion("1.0.0.0")] 56 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Statements/TestTransactionParsers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using NUnit.Framework; 6 | using Laan.Sql.Parser.Expressions; 7 | using Laan.Sql.Parser.Entities; 8 | 9 | namespace Laan.Sql.Parser.Test 10 | { 11 | [TestFixture] 12 | public class TestTransactionParsers 13 | { 14 | [Test] 15 | [TestCase( "begin tran select id from t commit" )] 16 | [TestCase( "begin tran select id from t commit tran" )] 17 | [TestCase( "begin tran select id from t commit transaction" )] 18 | [TestCase( "begin transaction select id from t commit" )] 19 | public void Basic_Begin_Tran_With_Commit( string sql ) 20 | { 21 | // Exercise 22 | List statements = ParserFactory.Execute( sql ); 23 | 24 | // Verify outcome 25 | Assert.IsNotNull( statements ); 26 | Assert.AreEqual( 3, statements.Count ); 27 | } 28 | 29 | [Test] 30 | [TestCase( "begin tran select id from t rollback" )] 31 | [TestCase( "begin tran a select id from t rollback tran a" )] 32 | [TestCase( "begin tran a select id from t rollback transaction a" )] 33 | public void Basic_Begin_Tran_With_RollBack( string sql ) 34 | { 35 | // Exercise 36 | List statements = ParserFactory.Execute( sql ); 37 | 38 | // Verify outcome 39 | Assert.IsNotNull( statements ); 40 | Assert.AreEqual( 3, statements.Count ); 41 | } 42 | 43 | [Test] 44 | [TestCase( "begin tran 't1' ", "'t1'", TransactionDescriptor.Tran, false )] 45 | [TestCase( "begin transaction 't1' ", "'t1'", TransactionDescriptor.Transaction, false )] 46 | [TestCase( "begin distributed transaction 't1' ", "'t1'", TransactionDescriptor.Transaction, true )] 47 | public void Begin_Tran_With_Name_And_Distribution( string sql, string name, TransactionDescriptor descriptor, bool distributed ) 48 | { 49 | // Exercise 50 | BeginTransactionStatement statement = ParserFactory.Execute( sql ).First(); 51 | 52 | // Verify outcome 53 | Assert.IsNotNull( statement ); 54 | Assert.AreEqual( name, statement.Name ); 55 | Assert.AreEqual(descriptor, statement.Descriptor ); 56 | Assert.AreEqual( distributed, statement.Distributed ); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Laan.AddIns.Ssms.VsExtension/SqlTemplateOption/DialogHost.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace Laan.AddIns.Forms 2 | { 3 | partial class DialogHost 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.wpfElementHost = new System.Windows.Forms.Integration.ElementHost(); 32 | this.SuspendLayout(); 33 | // 34 | // wpfElementHost 35 | // 36 | this.wpfElementHost.Dock = System.Windows.Forms.DockStyle.Fill; 37 | this.wpfElementHost.Location = new System.Drawing.Point(0, 0); 38 | this.wpfElementHost.Name = "wpfElementHost"; 39 | this.wpfElementHost.Size = new System.Drawing.Size(554, 550); 40 | this.wpfElementHost.TabIndex = 0; 41 | this.wpfElementHost.Text = "elementHost1"; 42 | this.wpfElementHost.Child = null; 43 | // 44 | // DialogHost 45 | // 46 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 47 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 48 | this.ClientSize = new System.Drawing.Size(554, 550); 49 | this.Controls.Add(this.wpfElementHost); 50 | this.MaximizeBox = false; 51 | this.MinimizeBox = false; 52 | this.Name = "DialogHost"; 53 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 54 | this.Text = "Laan Sql AddIn - Templates"; 55 | this.ResumeLayout(false); 56 | 57 | } 58 | 59 | #endregion 60 | 61 | private System.Windows.Forms.Integration.ElementHost wpfElementHost; 62 | } 63 | } -------------------------------------------------------------------------------- /Laan.Sql.Parser/Parsers/CommonTableExpressionStatementParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Entities; 5 | 6 | namespace Laan.Sql.Parser.Parsers 7 | { 8 | public class CommonTableExpressionStatementParser : CriteriaStatementParser 9 | { 10 | public CommonTableExpressionStatementParser(ITokenizer tokenizer) : base(tokenizer) 11 | { 12 | 13 | } 14 | 15 | public override CommonTableExpressionStatement Execute() 16 | { 17 | _statement = new CommonTableExpressionStatement(); 18 | do 19 | { 20 | var commonTableExpression = new CommonTableExpression(); 21 | 22 | commonTableExpression.Name = GetDotNotationIdentifier(); 23 | 24 | if (Tokenizer.IsNextToken(Constants.OpenBracket)) 25 | { 26 | using (Tokenizer.ExpectBrackets()) 27 | { 28 | commonTableExpression.ColumnNames.AddRange(GetIdentifierList()); 29 | } 30 | } 31 | 32 | ExpectToken(Constants.As); 33 | 34 | using (Tokenizer.ExpectBrackets()) 35 | { 36 | ExpectToken(Constants.Select); 37 | 38 | var parser = new SelectStatementParser(Tokenizer); 39 | var statement = parser.Execute(); 40 | 41 | commonTableExpression.Fields = statement.Fields; 42 | commonTableExpression.From = statement.From; 43 | commonTableExpression.Top = statement.Top; 44 | commonTableExpression.Distinct = statement.Distinct; 45 | commonTableExpression.GroupBy = statement.GroupBy; 46 | commonTableExpression.OrderBy = statement.OrderBy; 47 | commonTableExpression.Having = statement.Having; 48 | commonTableExpression.Where = statement.Where; 49 | commonTableExpression.SetOperation = statement.SetOperation; 50 | } 51 | 52 | _statement.CommonTableExpressions.Add(commonTableExpression); 53 | } 54 | while (Tokenizer.HasMoreTokens && Tokenizer.TokenEquals(Constants.Comma)); 55 | 56 | ExpectToken(Constants.Select); 57 | 58 | var statementParser = new SelectStatementParser(Tokenizer); 59 | _statement.Statement = statementParser.Execute(); 60 | 61 | return _statement; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Laan.Sql.Parser/Tokenizer/CustomTokenizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using Laan.Sql.Parser.Exceptions; 5 | 6 | namespace Laan.Sql.Parser 7 | { 8 | public abstract class CustomTokenizer 9 | { 10 | //private abstract void InternalSetSkipWhiteSpace(); 11 | 12 | //public bool SkipWhiteSpace 13 | //{ 14 | // set { InternalSetSkipWhiteSpace(); } 15 | //} 16 | 17 | public virtual bool IsNextToken(params string[] tokenSet) 18 | { 19 | foreach (var token in tokenSet) 20 | if (Current == token) 21 | return true; 22 | 23 | return false; 24 | } 25 | 26 | public bool TokenEquals(string value) 27 | { 28 | bool areEqual = Current == value; 29 | 30 | if (areEqual) 31 | ReadNextToken(); 32 | 33 | return areEqual; 34 | } 35 | 36 | public virtual void ReadNextToken() 37 | { 38 | // do nothing 39 | } 40 | 41 | public virtual Token Current 42 | { 43 | get { return new Token(); } 44 | } 45 | 46 | /// 47 | /// Verify current token matches expected token. Read next token if successful. 48 | /// 49 | /// Expected token 50 | /// current token did not match 51 | public void ExpectToken(string token) 52 | { 53 | if (Current == Token.Null || Current != token) 54 | throw new ExpectedTokenNotFoundException(token, Current != Token.Null ? Current.Value : "", Position); 55 | 56 | ReadNextToken(); 57 | } 58 | 59 | /// 60 | /// Verify current tokens match expected tokens. Read next token if successful. 61 | /// 62 | /// Expected tokens 63 | /// current token did not match 64 | public void ExpectTokens(string[] tokens) 65 | { 66 | foreach (string token in tokens) 67 | ExpectToken(token); 68 | } 69 | 70 | public Position Position { get; protected set; } 71 | 72 | public BracketStructure ExpectBrackets() 73 | { 74 | return new BracketStructure(this as ITokenizer); 75 | } 76 | 77 | public virtual bool HasMoreTokens 78 | { 79 | get { return false; } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Laan.Sql.Parser.Test/Statements/TestIfStatements.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | using NUnit.Framework; 5 | 6 | using Laan.Sql.Parser.Expressions; 7 | using Laan.Sql.Parser.Entities; 8 | using Laan.Sql.Parser.Exceptions; 9 | 10 | namespace Laan.Sql.Parser.Test 11 | { 12 | 13 | [TestFixture] 14 | public class TestIfStatements 15 | { 16 | [Test] 17 | public void Declare_Statement_With_No_Variables_Should_Fail() 18 | { 19 | // Setup 20 | var sql = "IF"; 21 | 22 | // Exercise 23 | Assert.Catch( 24 | () => ParserFactory.Execute(sql), 25 | "missing condition for IF" 26 | ); 27 | } 28 | 29 | [Test] 30 | public void If_Statement_Without_Statement_Should_Fail() 31 | { 32 | // Setup 33 | var sql = "IF @A = 1"; 34 | 35 | // Exercise 36 | Assert.Catch( 37 | () => ParserFactory.Execute(sql), 38 | "missing success block for IF" 39 | ); 40 | } 41 | 42 | [Test] 43 | public void If_Statement_With_Simple_Statement() 44 | { 45 | // Setup 46 | var sql = "IF @A = 1 SELECT 1"; 47 | 48 | // Exercise 49 | var statement = ParserFactory.Execute(sql).First(); 50 | 51 | // Verify outcome 52 | Assert.IsNotNull(statement); 53 | Assert.AreEqual(typeof(CriteriaExpression), statement.Condition.GetType()); 54 | Assert.AreEqual(typeof(SelectStatement), statement.If.GetType()); 55 | } 56 | 57 | [Test] 58 | public void If_Statement_With_Else_Statement() 59 | { 60 | // Setup 61 | var sql = @" 62 | IF @A = 1 63 | SELECT Field 64 | FROM dbo.ATable 65 | ELSE 66 | UPDATE dbo.ATable 67 | SET Field = 1 68 | 69 | WHERE ID = 10 70 | "; 71 | 72 | // Exercise 73 | var statement = ParserFactory.Execute(sql).First(); 74 | 75 | // Verify outcome 76 | Assert.IsNotNull(statement); 77 | Assert.AreEqual(typeof(CriteriaExpression), statement.Condition.GetType()); 78 | Assert.AreEqual(typeof(SelectStatement), statement.If.GetType()); 79 | Assert.AreEqual(typeof(UpdateStatement), statement.Else.GetType()); 80 | } 81 | } 82 | } 83 | --------------------------------------------------------------------------------