From cdb95c2499d29ae1c69da25ec9f9a2c7327ab741 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Fri, 1 May 2026 17:02:35 -0400 Subject: [PATCH 1/8] Add Case property to FormatConfig --- SQL.Formatter.Test/FormatConfigTest.cs | 175 ++++++++++++++++++++++++ SQL.Formatter/Core/AbstractFormatter.cs | 4 +- SQL.Formatter/Core/CaseTypes.cs | 13 ++ SQL.Formatter/Core/FormatConfig.cs | 44 +++++- 4 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 SQL.Formatter.Test/FormatConfigTest.cs create mode 100644 SQL.Formatter/Core/CaseTypes.cs diff --git a/SQL.Formatter.Test/FormatConfigTest.cs b/SQL.Formatter.Test/FormatConfigTest.cs new file mode 100644 index 0000000..97ef7d4 --- /dev/null +++ b/SQL.Formatter.Test/FormatConfigTest.cs @@ -0,0 +1,175 @@ +using SQL.Formatter.Core; +using SQL.Formatter.Language; +using Xunit; + +namespace SQL.Formatter.Test +{ + public class FormatConfigTest + { + private static readonly SqlFormatter.Formatter Formatter = SqlFormatter.Of(Dialect.StandardSql); + + [Fact] + public void Upper_FormatsKeywordsInUppercase() + { + var cfg = FormatConfig.Builder().Case(CaseTypes.UPPER).Build(); + var result = Formatter.Format("select id from users where id = 1", cfg); + + Assert.Equal( + "SELECT\n" + + " id\n" + + "FROM\n" + + " users\n" + + "WHERE\n" + + " id = 1", + result); + } + + [Fact] + public void Uppercase_FormatsKeywordsInUppercase() + { +#pragma warning disable CS0618 + var cfg = FormatConfig.Builder().Uppercase(true).Build(); +#pragma warning restore CS0618 + var result = Formatter.Format("select id from users where id = 1", cfg); + + Assert.Equal( + "SELECT\n" + + " id\n" + + "FROM\n" + + " users\n" + + "WHERE\n" + + " id = 1", + result); + } + + [Fact] + public void Uppercase_False_PreservesKeywordCase() + { +#pragma warning disable CS0618 + var cfg = FormatConfig.Builder().Uppercase(false).Build(); +#pragma warning restore CS0618 + var result = Formatter.Format("select id FROM users", cfg); + + Assert.Equal( + "select\n" + + " id\n" + + "FROM\n" + + " users", + result); + } + + [Fact] + public void Lower_FormatsKeywordsInLowercase() + { + var cfg = FormatConfig.Builder().Case(CaseTypes.LOWER).Build(); + var result = Formatter.Format("SELECT id FROM Users WHERE id = 1", cfg); + + Assert.Equal( + "select\n" + + " id\n" + + "from\n" + + " Users\n" + + "where\n" + + " id = 1", + result); + } + + [Fact] + public void Upper_ClearsLower() + { + var cfg = FormatConfig.Builder() + .Case(CaseTypes.LOWER) + .Case(CaseTypes.UPPER) + .Build(); + + Assert.Equal(CaseTypes.UPPER, cfg.Case); + } + + [Fact] + public void Lower_ClearsUpper() + { + var cfg = FormatConfig.Builder() + .Case(CaseTypes.UPPER) + .Case(CaseTypes.LOWER) + .Build(); + + Assert.Equal(CaseTypes.LOWER, cfg.Case); + } + + [Fact] + public void Upper_ClearsLower_FormatsUppercase() + { + var cfg = FormatConfig.Builder() + .Case(CaseTypes.LOWER) + .Case(CaseTypes.UPPER) + .Build(); + + var result = Formatter.Format("select id from users", cfg); + + Assert.Equal( + "SELECT\n" + + " id\n" + + "FROM\n" + + " users", + result); + } + + [Fact] + public void Lower_ClearsUpper_FormatsLowercase() + { + var cfg = FormatConfig.Builder() + .Case(CaseTypes.UPPER) + .Case(CaseTypes.LOWER) + .Build(); + + var result = Formatter.Format("SELECT id FROM users", cfg); + + Assert.Equal( + "select\n" + + " id\n" + + "from\n" + + " users", + result); + } + + [Fact] + public void NeitherUppercaseNorLowercase_PreservesKeywordCase() + { + var cfg = FormatConfig.Builder().Build(); + var result = Formatter.Format("SeLeCt id FrOm users", cfg); + + Assert.Equal( + "SeLeCt\n" + + " id\n" + + "FrOm\n" + + " users", + result); + } + + [Fact] + public void None_PreservesKeywordCase() + { + var cfg = FormatConfig.Builder().Case(CaseTypes.NONE).Build(); + var result = Formatter.Format("SeLeCt id FrOm users", cfg); + + Assert.Equal( + "SeLeCt\n" + + " id\n" + + "FrOm\n" + + " users", + result); + } + + [Theory] + [InlineData(CaseTypes.NONE, false)] + [InlineData(CaseTypes.UPPER, true)] + [InlineData(CaseTypes.LOWER, false)] + public void Case_SetsUppercaseCorrectly(CaseTypes caseType, bool expectedUppercase) + { + var cfg = FormatConfig.Builder().Case(caseType).Build(); +#pragma warning disable CS0618 + Assert.Equal(expectedUppercase, cfg.Uppercase); +#pragma warning restore CS0618 + } + } +} diff --git a/SQL.Formatter/Core/AbstractFormatter.cs b/SQL.Formatter/Core/AbstractFormatter.cs index a5cefa5..e1e63e8 100644 --- a/SQL.Formatter/Core/AbstractFormatter.cs +++ b/SQL.Formatter/Core/AbstractFormatter.cs @@ -282,7 +282,7 @@ protected virtual void FormatQuerySeparator(Token token, StringBuilder query) protected virtual string Show(Token token) { - if (_cfg.Uppercase + if (_cfg.Case > CaseTypes.NONE && (token.Type == TokenTypes.RESERVED || token.Type == TokenTypes.RESERVED_TOP_LEVEL || token.Type == TokenTypes.RESERVED_TOP_LEVEL_NO_INDENT @@ -291,7 +291,7 @@ protected virtual string Show(Token token) || token.Type == TokenTypes.CLOSE_PAREN)) { // Note: If memory is still tight, caching upper-case values at the token generation stage is even better. - return token.Value.ToUpper(); + return _cfg.Case == CaseTypes.UPPER ? token.Value.ToUpper() : token.Value.ToLower(); } return token.Value; diff --git a/SQL.Formatter/Core/CaseTypes.cs b/SQL.Formatter/Core/CaseTypes.cs new file mode 100644 index 0000000..16ddc9b --- /dev/null +++ b/SQL.Formatter/Core/CaseTypes.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SQL.Formatter.Core +{ + public enum CaseTypes + { + NONE, + UPPER, + LOWER, + } +} diff --git a/SQL.Formatter/Core/FormatConfig.cs b/SQL.Formatter/Core/FormatConfig.cs index f95c357..d5c1572 100644 --- a/SQL.Formatter/Core/FormatConfig.cs +++ b/SQL.Formatter/Core/FormatConfig.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; namespace SQL.Formatter.Core { @@ -10,26 +11,51 @@ public class FormatConfig public readonly string Indent; public readonly int MaxColumnLength; public readonly Params Parameters; + [Obsolete("Use Case instead of Uppercase")] public readonly bool Uppercase; + public readonly CaseTypes Case; public readonly int LinesBetweenQueries; public readonly bool SkipWhitespaceNearBlockParentheses; + [Obsolete("Use the constructor with caseType instead of uppercase")] public FormatConfig( string indent, int maxColumnLength, Params parameters, bool uppercase, int linesBetweenQueries, + bool skipWhitespaceNearBlockParentheses) + + : this( + indent, + maxColumnLength, + parameters, + uppercase ? CaseTypes.UPPER : CaseTypes.NONE, + linesBetweenQueries, + skipWhitespaceNearBlockParentheses) + { + } + + public FormatConfig( + string indent, + int maxColumnLength, + Params parameters, + CaseTypes caseType, + int linesBetweenQueries, bool skipWhitespaceNearBlockParentheses) { Indent = indent; MaxColumnLength = maxColumnLength; Parameters = parameters == null ? Params.Empty : parameters; - Uppercase = uppercase; + Case = caseType; +#pragma warning disable CS0618 + Uppercase = caseType == CaseTypes.UPPER; +#pragma warning restore CS0618 LinesBetweenQueries = linesBetweenQueries; SkipWhitespaceNearBlockParentheses = skipWhitespaceNearBlockParentheses; } + public static FormatConfigBuilder Builder() { return new FormatConfigBuilder(); @@ -40,7 +66,7 @@ public class FormatConfigBuilder private string _indent = DefaultIndent; private int _maxColumnLength = DefaultColumnMaxLength; private Params _parameters; - private bool _uppercase; + private CaseTypes _case; private int _linesBetweenQueries; private bool _skipWhitespaceNearBlockParentheses; @@ -76,9 +102,17 @@ public FormatConfigBuilder Params(List parameters) return Params(Core.Params.Of(parameters)); } + [Obsolete("Use Case instead of Uppercase")] public FormatConfigBuilder Uppercase(bool uppercase) { - _uppercase = uppercase; + _case = uppercase ? CaseTypes.UPPER : CaseTypes.NONE; + + return this; + } + + public FormatConfigBuilder Case(CaseTypes caseType) + { + _case = caseType; return this; } @@ -100,7 +134,7 @@ public FormatConfig Build() _indent, _maxColumnLength, _parameters, - _uppercase, + _case, _linesBetweenQueries, _skipWhitespaceNearBlockParentheses); } From 675feae1e7f93b890c929502e5943d3b3440caf8 Mon Sep 17 00:00:00 2001 From: Aaron Sherber Date: Fri, 1 May 2026 23:10:26 -0400 Subject: [PATCH 2/8] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index db0d9f6..7f6e063 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ You can also pass `FormatConfig` object built by builder: SqlFormatter.Format("SELECT * FROM tbl", FormatConfig.Builder() .Indent(" ") // Defaults to two spaces - .Uppercase(true) // Defaults to false (not safe to use when SQL dialect has case-sensitive identifiers) + .Case(CaseTypes.UPPER) // Defaults to NONE (not safe to use when SQL dialect has case-sensitive identifiers) .LinesBetweenQueries(2) // Defaults to 1 .MaxColumnLength(100) // Defaults to 50 .Params(new List{"a", "b", "c"}) // Dictionary or List. See Placeholders replacement. From f2387ab42ed48b18a276f79d2849a030b3c44014 Mon Sep 17 00:00:00 2001 From: Hoki Min Date: Sat, 2 May 2026 21:32:57 +0900 Subject: [PATCH 3/8] Clean up using directives in CaseTypes.cs Removed unnecessary using directives from CaseTypes.cs. --- SQL.Formatter/Core/CaseTypes.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/SQL.Formatter/Core/CaseTypes.cs b/SQL.Formatter/Core/CaseTypes.cs index 16ddc9b..9c283b1 100644 --- a/SQL.Formatter/Core/CaseTypes.cs +++ b/SQL.Formatter/Core/CaseTypes.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Text; - namespace SQL.Formatter.Core { public enum CaseTypes From decaa286530736dc45905dcbee1ca623e92e4813 Mon Sep 17 00:00:00 2001 From: Hoki Min Date: Sat, 2 May 2026 21:43:28 +0900 Subject: [PATCH 4/8] Remove empty line before static Builder method --- SQL.Formatter/Core/FormatConfig.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/SQL.Formatter/Core/FormatConfig.cs b/SQL.Formatter/Core/FormatConfig.cs index d5c1572..8aed17a 100644 --- a/SQL.Formatter/Core/FormatConfig.cs +++ b/SQL.Formatter/Core/FormatConfig.cs @@ -55,7 +55,6 @@ public FormatConfig( SkipWhitespaceNearBlockParentheses = skipWhitespaceNearBlockParentheses; } - public static FormatConfigBuilder Builder() { return new FormatConfigBuilder(); From f8d9041af3bc3b98cdb4e1fd0e236a0eb4e20418 Mon Sep 17 00:00:00 2001 From: Hoki Min Date: Sat, 2 May 2026 21:49:06 +0900 Subject: [PATCH 5/8] Fix whitespace formatting --- SQL.Formatter/Core/FormatConfig.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/SQL.Formatter/Core/FormatConfig.cs b/SQL.Formatter/Core/FormatConfig.cs index 8aed17a..3390792 100644 --- a/SQL.Formatter/Core/FormatConfig.cs +++ b/SQL.Formatter/Core/FormatConfig.cs @@ -24,16 +24,15 @@ public FormatConfig( Params parameters, bool uppercase, int linesBetweenQueries, - bool skipWhitespaceNearBlockParentheses) - + bool skipWhitespaceNearBlockParentheses) : this( - indent, - maxColumnLength, - parameters, - uppercase ? CaseTypes.UPPER : CaseTypes.NONE, - linesBetweenQueries, + indent, + maxColumnLength, + parameters, + uppercase ? CaseTypes.UPPER : CaseTypes.NONE, + linesBetweenQueries, skipWhitespaceNearBlockParentheses) - { + { } public FormatConfig( From 279e24c3ac2ca0388adeb62d341776e9391473d9 Mon Sep 17 00:00:00 2001 From: Hoki Min Date: Sat, 2 May 2026 21:50:06 +0900 Subject: [PATCH 6/8] Fix file encoding From 832135d8b0434ee524dd1be6123c31e7a904907e Mon Sep 17 00:00:00 2001 From: Hoki Min Date: Sat, 2 May 2026 22:01:58 +0900 Subject: [PATCH 7/8] Format UTF-8 with BOM --- SQL.Formatter/Core/CaseTypes.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SQL.Formatter/Core/CaseTypes.cs b/SQL.Formatter/Core/CaseTypes.cs index 9c283b1..10401c5 100644 --- a/SQL.Formatter/Core/CaseTypes.cs +++ b/SQL.Formatter/Core/CaseTypes.cs @@ -1,4 +1,4 @@ -namespace SQL.Formatter.Core +namespace SQL.Formatter.Core { public enum CaseTypes { From 2b972918ffe94572c8f6bcff16868cc5e059f237 Mon Sep 17 00:00:00 2001 From: Hoki Min Date: Sat, 2 May 2026 22:05:42 +0900 Subject: [PATCH 8/8] Rename Formatter to s_formatter --- SQL.Formatter.Test/FormatConfigTest.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/SQL.Formatter.Test/FormatConfigTest.cs b/SQL.Formatter.Test/FormatConfigTest.cs index 97ef7d4..57a146f 100644 --- a/SQL.Formatter.Test/FormatConfigTest.cs +++ b/SQL.Formatter.Test/FormatConfigTest.cs @@ -6,13 +6,13 @@ namespace SQL.Formatter.Test { public class FormatConfigTest { - private static readonly SqlFormatter.Formatter Formatter = SqlFormatter.Of(Dialect.StandardSql); + private static readonly SqlFormatter.Formatter s_formatter = SqlFormatter.Of(Dialect.StandardSql); [Fact] public void Upper_FormatsKeywordsInUppercase() { var cfg = FormatConfig.Builder().Case(CaseTypes.UPPER).Build(); - var result = Formatter.Format("select id from users where id = 1", cfg); + var result = s_formatter.Format("select id from users where id = 1", cfg); Assert.Equal( "SELECT\n" @@ -30,7 +30,7 @@ public void Uppercase_FormatsKeywordsInUppercase() #pragma warning disable CS0618 var cfg = FormatConfig.Builder().Uppercase(true).Build(); #pragma warning restore CS0618 - var result = Formatter.Format("select id from users where id = 1", cfg); + var result = s_formatter.Format("select id from users where id = 1", cfg); Assert.Equal( "SELECT\n" @@ -48,7 +48,7 @@ public void Uppercase_False_PreservesKeywordCase() #pragma warning disable CS0618 var cfg = FormatConfig.Builder().Uppercase(false).Build(); #pragma warning restore CS0618 - var result = Formatter.Format("select id FROM users", cfg); + var result = s_formatter.Format("select id FROM users", cfg); Assert.Equal( "select\n" @@ -62,7 +62,7 @@ public void Uppercase_False_PreservesKeywordCase() public void Lower_FormatsKeywordsInLowercase() { var cfg = FormatConfig.Builder().Case(CaseTypes.LOWER).Build(); - var result = Formatter.Format("SELECT id FROM Users WHERE id = 1", cfg); + var result = s_formatter.Format("SELECT id FROM Users WHERE id = 1", cfg); Assert.Equal( "select\n" @@ -104,7 +104,7 @@ public void Upper_ClearsLower_FormatsUppercase() .Case(CaseTypes.UPPER) .Build(); - var result = Formatter.Format("select id from users", cfg); + var result = s_formatter.Format("select id from users", cfg); Assert.Equal( "SELECT\n" @@ -122,7 +122,7 @@ public void Lower_ClearsUpper_FormatsLowercase() .Case(CaseTypes.LOWER) .Build(); - var result = Formatter.Format("SELECT id FROM users", cfg); + var result = s_formatter.Format("SELECT id FROM users", cfg); Assert.Equal( "select\n" @@ -136,7 +136,7 @@ public void Lower_ClearsUpper_FormatsLowercase() public void NeitherUppercaseNorLowercase_PreservesKeywordCase() { var cfg = FormatConfig.Builder().Build(); - var result = Formatter.Format("SeLeCt id FrOm users", cfg); + var result = s_formatter.Format("SeLeCt id FrOm users", cfg); Assert.Equal( "SeLeCt\n" @@ -150,7 +150,7 @@ public void NeitherUppercaseNorLowercase_PreservesKeywordCase() public void None_PreservesKeywordCase() { var cfg = FormatConfig.Builder().Case(CaseTypes.NONE).Build(); - var result = Formatter.Format("SeLeCt id FrOm users", cfg); + var result = s_formatter.Format("SeLeCt id FrOm users", cfg); Assert.Equal( "SeLeCt\n"