Skip to content

Commit 4847062

Browse files
Merge pull request #1678 from fredericDelaporte/BackPortBitwiseFix4.1
Back port bitwise thread safety fix in 4.1
2 parents b64b6b7 + 4d77d03 commit 4847062

File tree

6 files changed

+278
-113
lines changed

6 files changed

+278
-113
lines changed

build-common/common.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
effectively SP0).
5353
-->
5454

55-
<property name="project.version" value="4.1.1.GA" overwrite="false" />
55+
<property name="project.version" value="4.1.2.GA" overwrite="false" />
5656

5757
<!-- This version number should be changed if, but only if, there are incompatible
5858
changes compared to the previous version. -->

releasenotes.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
Build 4.1.2.GA
2+
=============================
3+
4+
Release notes - NHibernate - Version 4.1.2.GA
5+
6+
** Bug
7+
* #1355 NH-3928 - Random invalid SQL generated when using bitwise operators
8+
19
Build 4.1.1.GA
210
=============================
311

src/NHibernate.Test/Hql/HQLFunctions.cs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
using System;
22
using System.Collections;
3+
using System.Collections.Concurrent;
4+
using System.Collections.Generic;
5+
using System.Threading;
6+
using System.Threading.Tasks;
37
using NHibernate.Dialect;
48
using NUnit.Framework;
59

@@ -21,6 +25,18 @@ static HQLFunctions()
2125
{"locate", new[] {typeof (SQLiteDialect)}},
2226
{"bit_length", new[] {typeof (SQLiteDialect)}},
2327
{"extract", new[] {typeof (SQLiteDialect)}},
28+
{
29+
"bxor",
30+
new[]
31+
{
32+
// Could be supported like Oracle, with a template
33+
typeof (SQLiteDialect),
34+
// Could be supported by overriding registration with # instead of ^
35+
typeof (PostgreSQLDialect),
36+
typeof (PostgreSQL81Dialect),
37+
typeof (PostgreSQL82Dialect)
38+
}
39+
},
2440
{"nullif", new[] {typeof (Oracle8iDialect)}}
2541
};
2642
}
@@ -1014,7 +1030,202 @@ public void ParameterLikeArgument()
10141030
Assert.AreEqual(1, l.Count);
10151031
}
10161032
}
1033+
1034+
[Test]
1035+
public void BitwiseAnd()
1036+
{
1037+
IgnoreIfNotSupported("band");
1038+
CreateMaterialResources();
1039+
1040+
using (var s = OpenSession())
1041+
using (var tx = s.BeginTransaction())
1042+
{
1043+
var query = s.CreateQuery("from MaterialResource m where (m.State & 1) > 0");
1044+
var result = query.List();
1045+
Assert.That(result, Has.Count.EqualTo(1), "& 1");
1046+
1047+
query = s.CreateQuery("from MaterialResource m where (m.State & 2) > 0");
1048+
result = query.List();
1049+
Assert.That(result, Has.Count.EqualTo(1), "& 2");
1050+
1051+
query = s.CreateQuery("from MaterialResource m where (m.State & 3) > 0");
1052+
result = query.List();
1053+
Assert.That(result, Has.Count.EqualTo(2), "& 3");
1054+
1055+
tx.Commit();
1056+
}
1057+
DeleteMaterialResources();
1058+
}
1059+
1060+
[Test]
1061+
public void BitwiseOr()
1062+
{
1063+
IgnoreIfNotSupported("bor");
1064+
CreateMaterialResources();
1065+
1066+
using (var s = OpenSession())
1067+
using (var tx = s.BeginTransaction())
1068+
{
1069+
var query = s.CreateQuery("from MaterialResource m where (m.State | 1) > 0");
1070+
var result = query.List();
1071+
Assert.That(result, Has.Count.EqualTo(3), "| 1) > 0");
1072+
1073+
query = s.CreateQuery("from MaterialResource m where (m.State | 1) > 1");
1074+
result = query.List();
1075+
Assert.That(result, Has.Count.EqualTo(1), "| 1) > 1");
1076+
1077+
query = s.CreateQuery("from MaterialResource m where (m.State | 0) > 0");
1078+
result = query.List();
1079+
Assert.That(result, Has.Count.EqualTo(2), "| 0) > 0");
1080+
1081+
tx.Commit();
1082+
}
1083+
DeleteMaterialResources();
1084+
}
1085+
1086+
[Test]
1087+
public void BitwiseXor()
1088+
{
1089+
IgnoreIfNotSupported("bxor");
1090+
CreateMaterialResources();
1091+
1092+
using (var s = OpenSession())
1093+
using (var tx = s.BeginTransaction())
1094+
{
1095+
var query = s.CreateQuery("from MaterialResource m where (m.State ^ 1) > 0");
1096+
var result = query.List();
1097+
Assert.That(result, Has.Count.EqualTo(2), "^ 1");
1098+
1099+
query = s.CreateQuery("from MaterialResource m where (m.State ^ 2) > 0");
1100+
result = query.List();
1101+
Assert.That(result, Has.Count.EqualTo(2), "^ 2");
1102+
1103+
query = s.CreateQuery("from MaterialResource m where (m.State ^ 3) > 0");
1104+
result = query.List();
1105+
Assert.That(result, Has.Count.EqualTo(3), "^ 3");
1106+
1107+
tx.Commit();
1108+
}
1109+
DeleteMaterialResources();
1110+
}
1111+
1112+
[Test]
1113+
public void BitwiseNot()
1114+
{
1115+
IgnoreIfNotSupported("bnot");
1116+
IgnoreIfNotSupported("band");
1117+
CreateMaterialResources();
1118+
1119+
using (var s = OpenSession())
1120+
using (var tx = s.BeginTransaction())
1121+
{
1122+
// ! takes not precedence over & at least with some dialects (maybe all).
1123+
var query = s.CreateQuery("from MaterialResource m where ((!m.State) & 3) = 3");
1124+
var result = query.List();
1125+
Assert.That(result, Has.Count.EqualTo(1), "((!m.State) & 3) = 3");
1126+
1127+
query = s.CreateQuery("from MaterialResource m where ((!m.State) & 3) = 2");
1128+
result = query.List();
1129+
Assert.That(result, Has.Count.EqualTo(1), "((!m.State) & 3) = 2");
1130+
1131+
query = s.CreateQuery("from MaterialResource m where ((!m.State) & 3) = 1");
1132+
result = query.List();
1133+
Assert.That(result, Has.Count.EqualTo(1), "((!m.State) & 3) = 1");
1134+
1135+
tx.Commit();
1136+
}
1137+
DeleteMaterialResources();
1138+
}
1139+
1140+
// #1670
1141+
[Test]
1142+
public void BitwiseIsThreadsafe()
1143+
{
1144+
IgnoreIfNotSupported("band");
1145+
IgnoreIfNotSupported("bor");
1146+
IgnoreIfNotSupported("bxor");
1147+
IgnoreIfNotSupported("bnot");
1148+
var queries = new List<Tuple<string, int>>
1149+
{
1150+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State & 1) > 0", 1),
1151+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State & 2) > 0", 1),
1152+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State & 3) > 0", 2),
1153+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State | 1) > 0", 3),
1154+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State | 1) > 1", 1),
1155+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State | 0) > 0", 2),
1156+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State ^ 1) > 0", 2),
1157+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State ^ 2) > 0", 2),
1158+
new Tuple<string, int> ("select count(*) from MaterialResource m where (m.State ^ 3) > 0", 3),
1159+
new Tuple<string, int> ("select count(*) from MaterialResource m where ((!m.State) & 3) = 3", 1),
1160+
new Tuple<string, int> ("select count(*) from MaterialResource m where ((!m.State) & 3) = 2", 1),
1161+
new Tuple<string, int> ("select count(*) from MaterialResource m where ((!m.State) & 3) = 1", 1)
1162+
};
1163+
// Do not use a ManualResetEventSlim, it does not support async and exhausts the task thread pool in the
1164+
// async counterparts of this test. SemaphoreSlim has the async support and release the thread when waiting.
1165+
var semaphore = new SemaphoreSlim(0);
1166+
var failures = new ConcurrentBag<Exception>();
1167+
1168+
CreateMaterialResources();
1169+
1170+
Parallel.For(
1171+
0, queries.Count + 1,
1172+
i =>
1173+
{
1174+
if (i >= queries.Count)
1175+
{
1176+
// Give some time to threads for reaching the wait, having all of them ready to do the
1177+
// critical part of their job concurrently.
1178+
Thread.Sleep(100);
1179+
semaphore.Release(queries.Count);
1180+
return;
1181+
}
1182+
1183+
try
1184+
{
1185+
var query = queries[i];
1186+
using (var s = OpenSession())
1187+
using (var tx = s.BeginTransaction())
1188+
{
1189+
semaphore.Wait();
1190+
var q = s.CreateQuery(query.Item1);
1191+
var result = q.UniqueResult<long>();
1192+
Assert.That(result, Is.EqualTo(query.Item2), query.Item1);
1193+
tx.Commit();
1194+
}
1195+
}
1196+
catch (Exception e)
1197+
{
1198+
failures.Add(e);
1199+
}
1200+
});
1201+
1202+
Assert.That(failures, Is.Empty, $"{failures.Count} task(s) failed.");
1203+
DeleteMaterialResources();
1204+
}
1205+
1206+
private void CreateMaterialResources()
1207+
{
1208+
using (var s = OpenSession())
1209+
using (var tx = s.BeginTransaction())
1210+
{
1211+
s.Save(new MaterialResource("m1", "18", MaterialResource.MaterialState.Available));
1212+
s.Save(new MaterialResource("m2", "19", MaterialResource.MaterialState.Reserved));
1213+
s.Save(new MaterialResource("m3", "20", MaterialResource.MaterialState.Discarded));
1214+
tx.Commit();
1215+
}
1216+
}
1217+
1218+
private void DeleteMaterialResources()
1219+
{
1220+
using (var s = OpenSession())
1221+
using (var tx = s.BeginTransaction())
1222+
{
1223+
s.CreateQuery("delete from MaterialResource").ExecuteUpdate();
1224+
tx.Commit();
1225+
}
1226+
}
10171227
}
1228+
10181229
public class ForNh1725
10191230
{
10201231
public string Description { get; set; }

src/NHibernate.sln.DotSettings

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
2020
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
2121
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
22+
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
2223
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
2324
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
2425
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
26+
<s:Boolean x:Key="/Default/Environment/UnitTesting/SeparateAppDomainPerAssembly/@EntryValue">True</s:Boolean>
2527
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=0BE95D01E0F2244E97F5FEFAD1EB1A63/@KeyIndexDefined">True</s:Boolean>
2628
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=0BE95D01E0F2244E97F5FEFAD1EB1A63/Comment/@EntryValue">NUnit Assert.NotNull</s:String>
2729
<s:Boolean x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=0BE95D01E0F2244E97F5FEFAD1EB1A63/CustomPatternPlaceholder/=actual/@KeyIndexDefined">True</s:Boolean>

src/NHibernate/Dialect/BitwiseFunctionOperation.cs

Lines changed: 27 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,99 +14,63 @@ namespace NHibernate.Dialect
1414
public class BitwiseFunctionOperation : ISQLFunction
1515
{
1616
private readonly string _functionName;
17-
private SqlStringBuilder _sqlBuffer;
18-
private Queue _args;
1917

2018
/// <summary>
21-
/// Creates an instance of this class using the provided function name
19+
/// Creates an instance of this class using the provided function name.
2220
/// </summary>
2321
/// <param name="functionName">
24-
/// The bitwise function name as defined by the SQL-Dialect
22+
/// The bitwise function name as defined by the SQL-Dialect.
2523
/// </param>
2624
public BitwiseFunctionOperation(string functionName)
2725
{
28-
_functionName = functionName;
26+
_functionName = functionName;
2927
}
3028

3129
#region ISQLFunction Members
3230

31+
/// <inheritdoc />
3332
public IType ReturnType(IType columnType, IMapping mapping)
3433
{
3534
return NHibernateUtil.Int64;
3635
}
3736

38-
public bool HasArguments
39-
{
40-
get { return true; }
41-
}
37+
/// <inheritdoc />
38+
public bool HasArguments => true;
4239

43-
public bool HasParenthesesIfNoArguments
44-
{
45-
get { return true; }
46-
}
40+
/// <inheritdoc />
41+
public bool HasParenthesesIfNoArguments => true;
4742

43+
/// <inheritdoc />
4844
public SqlString Render(IList args, ISessionFactoryImplementor factory)
4945
{
50-
Prepare(args);
51-
52-
AddFunctionName();
53-
OpenParens();
54-
AddArguments();
55-
CloseParens();
56-
57-
return SqlResult();
58-
}
59-
60-
#endregion
46+
var sqlBuffer = new SqlStringBuilder();
6147

62-
private void Prepare(IList args)
63-
{
64-
_sqlBuffer = new SqlStringBuilder();
65-
_args = new Queue();
48+
sqlBuffer.Add(_functionName);
49+
sqlBuffer.Add("(");
6650
foreach (var arg in args)
6751
{
68-
if (!IsParens(arg.ToString()))
69-
_args.Enqueue(arg);
70-
}
71-
}
72-
73-
private static bool IsParens(string candidate)
74-
{
75-
return candidate == "(" || candidate == ")";
76-
}
77-
78-
private void AddFunctionName()
79-
{
80-
_sqlBuffer.Add(_functionName);
81-
}
82-
83-
private void OpenParens()
84-
{
85-
_sqlBuffer.Add("(");
86-
}
87-
88-
private void AddArguments()
89-
{
90-
while (_args.Count > 0)
91-
{
92-
var arg = _args.Dequeue();
52+
// The actual second argument may be surrounded by parentesis as additional arguments.
53+
// They have to be ignored, otherwise it would emit "functionName(firstArg, (, secondArg, ))"
54+
if (IsParens(arg.ToString()))
55+
continue;
9356
if (arg is Parameter || arg is SqlString)
94-
_sqlBuffer.AddObject(arg);
57+
sqlBuffer.AddObject(arg);
9558
else
96-
_sqlBuffer.Add(arg.ToString());
97-
if (_args.Count > 0)
98-
_sqlBuffer.Add(", ");
59+
sqlBuffer.Add(arg.ToString());
60+
sqlBuffer.Add(", ");
9961
}
100-
}
10162

102-
private void CloseParens()
103-
{
104-
_sqlBuffer.Add(")");
63+
sqlBuffer.RemoveAt(sqlBuffer.Count - 1);
64+
sqlBuffer.Add(")");
65+
66+
return sqlBuffer.ToSqlString();
10567
}
10668

107-
private SqlString SqlResult()
69+
#endregion
70+
71+
private static bool IsParens(string candidate)
10872
{
109-
return _sqlBuffer.ToSqlString();
73+
return candidate == "(" || candidate == ")";
11074
}
11175
}
11276
}

0 commit comments

Comments
 (0)