Skip to content

Commit bcd106d

Browse files
Escapes string in AbstractStringType
1 parent bf88006 commit bcd106d

12 files changed

+150
-18
lines changed

doc/reference/modules/configuration.xml

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,20 @@ var session = sessions.OpenSession(conn);
871871
</para>
872872
</entry>
873873
</row>
874+
<row>
875+
<entry>
876+
<literal>escape_backslash_in_strings</literal>
877+
</entry>
878+
<entry>
879+
Indicates if the database needs to have backslash escaped in string literals.
880+
The default value is dialect dependant. That is <literal>false</literal> for
881+
most dialects.
882+
<para>
883+
<emphasis role="strong">eg.</emphasis>
884+
<literal>true</literal> | <literal>false</literal>
885+
</para>
886+
</entry>
887+
</row>
874888
<row>
875889
<entry>
876890
<literal>show_sql</literal>
@@ -1515,12 +1529,6 @@ in the parameter binding.</programlisting>
15151529
<entry><literal>NHibernate.Dialect.PostgreSQLDialect</literal></entry>
15161530
<entry></entry>
15171531
</row>
1518-
<row>
1519-
<entry>PostgreSQL</entry>
1520-
<entry><literal>NHibernate.Dialect.PostgreSQLDialect</literal></entry>
1521-
<entry>
1522-
</entry>
1523-
</row>
15241532
<row>
15251533
<entry>PostgreSQL 8.1</entry>
15261534
<entry><literal>NHibernate.Dialect.PostgreSQL81Dialect</literal></entry>

src/NHibernate/Cfg/Environment.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ public static string Version
119119
/// <summary> Enable formatting of SQL logged to the console</summary>
120120
public const string FormatSql = "format_sql";
121121

122+
/// <summary>
123+
/// Indicates if the database needs to have backslash escaped in string literals.
124+
/// </summary>
125+
/// <remarks>The default value is dialect dependent.</remarks>
126+
public const string EscapeBackslashInStrings = "escape_backslash_in_strings";
127+
122128
// Since v5.0.1
123129
[Obsolete("This setting has no usages and will be removed in a future version")]
124130
public const string UseGetGeneratedKeys = "jdbc.use_get_generated_keys";

src/NHibernate/Dialect/DB2Dialect.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using NHibernate.Dialect.Function;
66
using NHibernate.Dialect.Schema;
77
using NHibernate.SqlCommand;
8+
using NHibernate.SqlTypes;
89

910
namespace NHibernate.Dialect
1011
{
@@ -296,6 +297,30 @@ public override string ForUpdateString
296297

297298
public override long TimestampResolutionInTicks => 10L; // Microseconds.
298299

300+
/// <inheritdoc />
301+
public override string ToStringLiteral(string value, SqlType type)
302+
{
303+
if (value == null)
304+
throw new System.ArgumentNullException(nameof(value));
305+
if (type == null)
306+
throw new System.ArgumentNullException(nameof(value));
307+
308+
// See https://www.ibm.com/docs/en/db2/11.5?topic=elements-constants#r0000731__title__7
309+
var literal = new StringBuilder(value);
310+
var isUnicode = type.DbType == DbType.String || type.DbType == DbType.StringFixedLength;
311+
if (isUnicode)
312+
literal.Replace(@"\", @"\\");
313+
314+
literal
315+
.Replace("'", "''")
316+
.Insert(0, '\'')
317+
.Append('\'');
318+
319+
if (isUnicode)
320+
literal.Insert(0, "U&");
321+
return literal.ToString();
322+
}
323+
299324
#region Overridden informational metadata
300325

301326
public override bool SupportsNullInUnique => false;

src/NHibernate/Dialect/Dialect.cs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ public virtual void Configure(IDictionary<string, string> settings)
208208
DefaultCastLength = PropertiesHelper.GetInt32(Environment.QueryDefaultCastLength, settings, 4000);
209209
DefaultCastPrecision = PropertiesHelper.GetByte(Environment.QueryDefaultCastPrecision, settings, null) ?? 29;
210210
DefaultCastScale = PropertiesHelper.GetByte(Environment.QueryDefaultCastScale, settings, null) ?? 10;
211+
EscapeBackslashInStrings = PropertiesHelper.GetBoolean(Environment.EscapeBackslashInStrings, settings, EscapeBackslashInStrings);
211212
}
212213

213214
#endregion
@@ -1354,14 +1355,6 @@ public virtual CaseFragment CreateCaseFragment()
13541355
return new ANSICaseFragment(this);
13551356
}
13561357

1357-
/// <summary> The SQL literal value to which this database maps boolean values. </summary>
1358-
/// <param name="value">The boolean value </param>
1359-
/// <returns> The appropriate SQL literal. </returns>
1360-
public virtual string ToBooleanValueString(bool value)
1361-
{
1362-
return value ? "1" : "0";
1363-
}
1364-
13651358
internal static void ExtractColumnOrAliasNames(SqlString select, out List<SqlString> columnsOrAliases, out Dictionary<SqlString, SqlString> aliasToColumn, out Dictionary<SqlString, SqlString> columnToAlias)
13661359
{
13671360
columnsOrAliases = new List<SqlString>();
@@ -2076,6 +2069,55 @@ public virtual string ConvertQuotesForCatalogName(string catalogName)
20762069

20772070
#endregion
20782071

2072+
#region Literals support
2073+
2074+
/// <summary>The SQL literal value to which this database maps boolean values.</summary>
2075+
/// <param name="value">The boolean value.</param>
2076+
/// <returns>The appropriate SQL literal.</returns>
2077+
public virtual string ToBooleanValueString(bool value)
2078+
=> value ? "1" : "0";
2079+
2080+
/// <summary>
2081+
/// <see langword="true" /> if the database needs to have backslash escaped in string literals.
2082+
/// </summary>
2083+
/// <remarks><see langword="false" /> by default in the base dialect, to conform to SQL standard.</remarks>
2084+
protected virtual bool EscapeBackslashInStrings { get; set; }
2085+
2086+
/// <summary>
2087+
/// <see langword="true" /> if the database needs to have Unicode literals prefixed by <c>N</c>.
2088+
/// </summary>
2089+
/// <remarks><see langword="false" /> by default in the base dialect.</remarks>
2090+
protected virtual bool UseNPrefixForUnicodeStrings => false;
2091+
2092+
/// <summary>The SQL string literal value to which this database maps string values.</summary>
2093+
/// <param name="value">The string value.</param>
2094+
/// <param name="type">The SQL type of the string value.</param>
2095+
/// <returns>The appropriate SQL string literal.</returns>
2096+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="value"/> or
2097+
/// <paramref name="type"/> is <see langword="null" />.</exception>
2098+
public virtual string ToStringLiteral(string value, SqlType type)
2099+
{
2100+
if (value == null)
2101+
throw new ArgumentNullException(nameof(value));
2102+
if (type == null)
2103+
throw new ArgumentNullException(nameof(value));
2104+
2105+
var literal = new StringBuilder(value);
2106+
if (EscapeBackslashInStrings)
2107+
literal.Replace(@"\", @"\\");
2108+
2109+
literal
2110+
.Replace("'", "''")
2111+
.Insert(0, '\'')
2112+
.Append('\'');
2113+
2114+
if (UseNPrefixForUnicodeStrings && (type.DbType == DbType.String || type.DbType == DbType.StringFixedLength))
2115+
literal.Insert(0, 'N');
2116+
return literal.ToString();
2117+
}
2118+
2119+
#endregion
2120+
20792121
#region Union subclass support
20802122

20812123
/// <summary>

src/NHibernate/Dialect/IngresDialect.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ public IngresDialect()
6262
/// <inheritdoc />
6363
public override int MaxAliasLength => 32;
6464

65+
/// <inheritdoc />
66+
/// <remarks><see langword="true" /> by default for Ingres,
67+
/// <see href="https://docs.actian.com/ingres/11.0/index.html#page/SQLRef/String_Literals.htm#ww110572" />.</remarks>
68+
protected override bool UseNPrefixForUnicodeStrings => true;
69+
6570
#region Overridden informational metadata
6671

6772
public override bool SupportsEmptyInList => false;

src/NHibernate/Dialect/MsSql2000Dialect.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,10 @@ public override bool SupportsSqlBatches
755755
/// </summary>
756756
public override int? MaxNumberOfParameters => 2097;
757757

758+
/// <inheritdoc />
759+
/// <remarks><see langword="true" /> by default for SQL Server.</remarks>
760+
protected override bool UseNPrefixForUnicodeStrings => true;
761+
758762
#region Overridden informational metadata
759763

760764
public override bool SupportsEmptyInList => false;

src/NHibernate/Dialect/MySQLDialect.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,16 @@ public override long TimestampResolutionInTicks
530530
/// </remarks>
531531
public override bool SupportsConcurrentWritingConnectionsInSameTransaction => false;
532532

533+
/// <inheritdoc />
534+
/// <remarks><see langword="true" /> by default for MySQL,
535+
/// <see href="https://dev.mysql.com/doc/refman/8.0/en/string-literals.html" />.</remarks>
536+
protected override bool EscapeBackslashInStrings { get; set; } = true;
537+
538+
/// <inheritdoc />
539+
/// <remarks><see langword="true" /> by default for MySQL,
540+
/// <see href="https://dev.mysql.com/doc/refman/8.0/en/string-literals.html" />.</remarks>
541+
protected override bool UseNPrefixForUnicodeStrings => true;
542+
533543
#region Overridden informational metadata
534544

535545
public override bool SupportsEmptyInList => false;

src/NHibernate/Dialect/Oracle8iDialect.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ public override void Configure(IDictionary<string, string> settings)
102102

103103
// If changing the default value, keep it in sync with OracleDataClientDriverBase.Configure.
104104
UseNPrefixedTypesForUnicode = PropertiesHelper.GetBoolean(Environment.OracleUseNPrefixedTypesForUnicode, settings, false);
105+
105106
RegisterCharacterTypeMappings();
106107
RegisterFloatingPointTypeMappings();
107108
}
@@ -561,6 +562,10 @@ public override long TimestampResolutionInTicks
561562
/// <inheritdoc />
562563
public override int MaxAliasLength => 30;
563564

565+
/// <inheritdoc />
566+
/// <remarks>Returns the same value as <see cref="UseNPrefixedTypesForUnicode" />.</remarks>
567+
protected override bool UseNPrefixForUnicodeStrings => UseNPrefixedTypesForUnicode;
568+
564569
#region Overridden informational metadata
565570

566571
public override bool SupportsEmptyInList

src/NHibernate/Dialect/SybaseASA9Dialect.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace NHibernate.Dialect
1010
{
1111
/// <summary>
12-
/// An SQL dialect for Sybase Adaptive Server Anywhere 9.0
12+
/// An SQL dialect for Sybase Adaptive Server Anywhere 9.0. (Renamed SQL Anywhere from its version 10.)
1313
/// </summary>
1414
/// <remarks>
1515
/// <p>
@@ -188,5 +188,15 @@ private static int GetAfterSelectInsertPoint(SqlString sql)
188188
}
189189
return 0;
190190
}
191+
192+
/// <inheritdoc />
193+
/// <remarks><see langword="true" /> by default for SQL Anywhere,
194+
/// <see href="https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/817a3ded6ce21014bd99f3e554573180.html?version=17.0" />.</remarks>
195+
protected override bool EscapeBackslashInStrings { get; set; } = true;
196+
197+
/// <inheritdoc />
198+
/// <remarks><see langword="true" /> by default for SQL Anywhere,
199+
/// <see href="https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/817a2c5f6ce21014aceea962de72126c.html?version=17.0" />.</remarks>
200+
protected override bool UseNPrefixForUnicodeStrings => true;
191201
}
192202
}

src/NHibernate/Dialect/SybaseSQLAnywhere10Dialect.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,5 +969,15 @@ public override IDataBaseSchema GetDataBaseSchema(DbConnection connection)
969969
/// <inheritdoc />
970970
/// <remarks>SQL Anywhere has a micro-second resolution.</remarks>
971971
public override long TimestampResolutionInTicks => 10L;
972+
973+
/// <inheritdoc />
974+
/// <remarks><see langword="true" /> by default for SQL Anywhere,
975+
/// <see href="https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/817a3ded6ce21014bd99f3e554573180.html?version=17.0" />.</remarks>
976+
protected override bool EscapeBackslashInStrings { get; set; } = true;
977+
978+
/// <inheritdoc />
979+
/// <remarks><see langword="true" /> by default for SQL Anywhere,
980+
/// <see href="https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/817a2c5f6ce21014aceea962de72126c.html?version=17.0" />.</remarks>
981+
protected override bool UseNPrefixForUnicodeStrings => true;
972982
}
973983
}

src/NHibernate/Type/AbstractStringType.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,9 @@ public object StringToObject(string xml)
134134

135135
#region ILiteralType Members
136136

137+
/// <inheritdoc />
137138
public string ObjectToSQLString(object value, Dialect.Dialect dialect)
138-
{
139-
return "'" + (string)value + "'";
140-
}
139+
=> dialect.ToStringLiteral((string)value, SqlType);
141140

142141
#endregion
143142

src/NHibernate/nhibernate-configuration.xsd

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@
176176
<xs:enumeration value="default_flush_mode" />
177177
<xs:enumeration value="use_sql_comments" />
178178
<xs:enumeration value="format_sql" />
179+
<xs:enumeration value="escape_backslash_in_strings">
180+
<xs:annotation>
181+
<xs:documentation>
182+
Indicates if the database needs to have backslash escaped in string literals. The default is
183+
dialect dependent.
184+
</xs:documentation>
185+
</xs:annotation>
186+
</xs:enumeration>
179187
<xs:enumeration value="collectiontype.factory_class" />
180188
<xs:enumeration value="order_inserts" />
181189
<xs:enumeration value="order_updates" />

0 commit comments

Comments
 (0)