Skip to content

Commit 37ca8d5

Browse files
Merge pull request #216 from joshcobalt/sqlkata#215
Adds ability to escape identifiers w/ tests
2 parents db2012f + 4c595d8 commit 37ca8d5

5 files changed

Lines changed: 45 additions & 5 deletions

File tree

QueryBuilder.Tests/GeneralTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,16 @@ public void Raw_WrapIdentifiers()
123123
Assert.Equal("SELECT \"Id\", \"Name\", \"Age\" FROM \"Users\"", c[EngineCodes.PostgreSql]);
124124
Assert.Equal("SELECT \"Id\", \"Name\", \"Age\" FROM \"USERS\"", c[EngineCodes.Firebird]);
125125
}
126+
127+
[Fact]
128+
public void Raw_WrapIdentifiers_Escaped()
129+
{
130+
var query = new Query("Users").SelectRaw("'\\{1,2,3\\}'::int\\[\\]");
131+
132+
var c = Compile(query);
133+
134+
Assert.Equal("SELECT '{1,2,3}'::int[] FROM \"Users\"", c[EngineCodes.PostgreSql]);
135+
}
126136

127137
[Fact]
128138
public void WrapWithSpace()

QueryBuilder.Tests/HelperTests.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,5 +224,14 @@ public void ExpandParameters()
224224

225225
Assert.Equal("where id = ? or id in (?,?) or id in ()", expanded);
226226
}
227+
228+
[Theory]
229+
[InlineData(@"\{ text {", @"\", "{", "[", "{ text [")]
230+
[InlineData(@"{ text {", @"\", "{", "[", "[ text [")]
231+
public void WrapIdentifiers(string input, string escapeCharacter, string identifier, string newIdentifier, string expected)
232+
{
233+
var result = input.ReplaceIdentifierUnlessEscaped(escapeCharacter, identifier, newIdentifier);
234+
Assert.Equal(expected, result);
235+
}
227236
}
228237
}

QueryBuilder.Tests/SelectTests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,5 +619,15 @@ public void JoinTypes(string given, string output)
619619
$"SELECT * FROM \"USERS\" \n{output} \"COUNTRIES\" ON \"COUNTRIES\".\"ID\" = \"USERS\".\"COUNTRY_ID\"",
620620
c[EngineCodes.Firebird]);
621621
}
622+
623+
[Fact]
624+
public void OrWhereRawEscaped()
625+
{
626+
var query = new Query("Table").WhereRaw("[MyCol] = ANY(?::int\\[\\])", "{1,2,3}");
627+
628+
var c = Compile(query);
629+
630+
Assert.Equal("SELECT * FROM \"Table\" WHERE \"MyCol\" = ANY('{1,2,3}'::int[])", c[EngineCodes.PostgreSql]);
631+
}
622632
}
623633
}

QueryBuilder/Compilers/Compiler.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public partial class Compiler
1515
protected virtual string ColumnAsKeyword { get; set; } = "AS ";
1616
protected virtual string TableAsKeyword { get; set; } = "AS ";
1717
protected virtual string LastId { get; set; } = "";
18+
protected virtual string EscapeCharacter { get; set; } = "\\";
1819

1920
protected Compiler()
2021
{
@@ -809,12 +810,11 @@ public virtual string WrapIdentifiers(string input)
809810
return input
810811

811812
// deprecated
812-
.Replace("{", this.OpeningIdentifier)
813-
.Replace("}", this.ClosingIdentifier)
813+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"{", this.OpeningIdentifier)
814+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"}", this.ClosingIdentifier)
814815

815-
.Replace("[", this.OpeningIdentifier)
816-
.Replace("]", this.ClosingIdentifier);
816+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"[", this.OpeningIdentifier)
817+
.ReplaceIdentifierUnlessEscaped(this.EscapeCharacter,"]", this.ClosingIdentifier);
817818
}
818-
819819
}
820820
}

QueryBuilder/Helper.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,5 +158,16 @@ public static IEnumerable<string> Repeat(this string str, int count)
158158
{
159159
return Enumerable.Repeat(str, count);
160160
}
161+
162+
public static string ReplaceIdentifierUnlessEscaped(this string input, string escapeCharacter, string identifier, string newIdentifier)
163+
{
164+
//Replace standard, non-escaped identifiers first
165+
var nonEscapedRegex = new Regex($@"(?<!{Regex.Escape(escapeCharacter)}){Regex.Escape(identifier)}");
166+
var nonEscapedReplace = nonEscapedRegex.Replace(input, newIdentifier);
167+
168+
//Then replace escaped identifiers, by just removing the escape character
169+
var escapedRegex = new Regex($@"{Regex.Escape(escapeCharacter)}{Regex.Escape(identifier)}");
170+
return escapedRegex.Replace(nonEscapedReplace, identifier);
171+
}
161172
}
162173
}

0 commit comments

Comments
 (0)