Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

NH-3004: DriverBase.RemoveUnusedCommandParameters removes all parameters when UseNamedPrefixInSql = true and UseNamedPrefixInParameter = false #48

Closed
wants to merge 11 commits into from

2 participants

@oskarb
Owner

Thanks for the patch. However, there is a number of issues.

Biggest problem is that if the command includes an actually unused parameter, the code now fails with an exception.

I hope you can have a look at this - shouldn't be too hard. Before you do, please pull branch NH3004-prepare from my personal repo git://github.com/oskarb/nhibernate-core.git to your branch. This will incorporate cleanup of the below issues, and additonal test case to expose the above issue.

Other issues (fixed in my NH3004-prepare branch):
Formatting: For future work, please use tabs for indenting, as this is NHibernate policy. (If you find an existing file with wrong indenting, please either leave it as is, or make a separate commit with only indenting cleanup, for easer review.)

Also, the test case wasn't part of the project file, and I've removed some boilerplate code that isn't necessary for these tests.

@mickfold

Thanks for your comment and highlighting the problems with my pull request. I'm kicking myself for not trying the function with an unused parameter.

I pulled the branch NH3004-prepare from your personal repo and have made changes to both the unit test, where I added another assert, and RemoveUnusedCommandParameters, so it now handles the unused parameters properly.

I have pushed the new commit to the master branch of my personal repo, git://github.com/mcharalambous/nhibernate-core.git, but am not sure how to add it to the pull request. Any help would be appreciated.

Thanks
Michael

@mickfold

Update on my previous comment.

The commit with the changes I have made have been incorporated in the pull request. But these do not include Oscar's changes. What have I done wrong and how can I update the pull request so they include Oscar's changes?

Thanks

@oskarb
Owner

Thanks Michael for having a renewed look at this. The pull request seems fine to me - the Commits tab lists my commits, and the Diff tab includes the combined effect of your and mine commits. (When I merge to master I will do a squash merge, to keep a cleaner history.)

I have one concern though. What will the computational complexity of the current state be? Looking into this now...

@oskarb
Owner

Problems:
formatter.AssignedParameterNames is now called for every parameter. This method will allocate and fill an array from a list, on-the-fly, so we now do this once for every parameter, which isn't needed.

Also, Contains() is called on that (those) arrays once for each parameter. Contains() should be O(n), meaning the whole expression is now O(n²). The old code should have been linear, since Except() seems to be O(n). This probably doesn't matter all that much for most queries, but occasionally a larger number of parameters is used - especially when using the IN operator.

Just doing formatter.AssignedParameterNames.ToDictionary() before the main expression would probably work.

@mickfold

Hi Oscar. Thanks for looking into this.

Would it be better to make it a HashSet instead of a Dictionary? Then the code would become

var AssignedParameterNames = new HashSet<string>(formatter.AssignedParameterNames);

cmd.Parameters
  .Cast<IDbDataParameter>()
  .Select(p => p.ParameterName)
  .Where(p => !AssignedParameterNames.Contains(UseNamedPrefixInParameter ? p : FormatNameForSql(p)))                
  .ToList()
  .ForEach(unusedParameterName => cmd.Parameters.RemoveAt(unusedParameterName));

Would this be okay?

@oskarb
Owner
@oskarb
Owner

Thanks, applied.

@oskarb oskarb closed this
@hazzik hazzik referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
58 src/NHibernate.Test/NHSpecificTest/NH3004/Fixture.cs
@@ -0,0 +1,58 @@
+using System.Collections;
+using NHibernate.SqlCommand;
+using NUnit.Framework;
+using System.Data;
+
+namespace NHibernate.Test.NHSpecificTest.NH3004
+{
+ [TestFixture]
+ public class Fixture
+ {
+ [Test]
+ public void RemoveUnusedCommandParametersBug_1()
+ {
+ /* UseNamedPrefixInSql is true
+ * UseNamedPrefixInParameter is false
+ * */
+ var driver = new TestSqlClientDriver(true, false);
+
+ RunTest(driver);
+ }
+
+ [Test]
+ public void RemoveUnusedCommandParametersBug_2()
+ {
+ /* UseNamedPrefixInSql is true
+ * UseNamedPrefixInParameter is true
+ * */
+ var driver = new TestSqlClientDriver(true, true);
+
+ RunTest(driver);
+ }
+
+ private static void RunTest(TestSqlClientDriver driver)
+ {
+ var command = driver.CreateCommand();
+
+ var usedParam = command.CreateParameter();
+ usedParam.ParameterName = driver.FormatNameForParameter("p0");
+ command.Parameters.Add(usedParam);
+
+ var unusedParam = command.CreateParameter();
+ unusedParam.ParameterName = driver.FormatNameForParameter("unused");
+ command.Parameters.Add(unusedParam);
+
+ Assert.AreEqual(command.Parameters.Count, 2);
+
+ SqlString sqlString = new SqlStringBuilder()
+ .AddParameter()
+ .ToSqlString();
+
+ driver.RemoveUnusedCommandParameters(command, sqlString);
+
+ Assert.AreEqual(command.Parameters.Count, 1);
+
+ Assert.AreEqual(command.Parameters[0], usedParam);
+ }
+ }
+}
View
42 src/NHibernate.Test/NHSpecificTest/NH3004/TestSqlClientDriver.cs
@@ -0,0 +1,42 @@
+using System.Data;
+using System.Data.SqlClient;
+using NHibernate.AdoNet;
+using NHibernate.Dialect;
+using NHibernate.Engine;
+using NHibernate.SqlCommand;
+using NHibernate.SqlTypes;
+using NHibernate.Driver;
+
+namespace NHibernate.Test.NHSpecificTest.NH3004
+{
+ /// <summary>
+ /// A NHibernate Driver for using the SqlClient DataProvider
+ /// </summary>
+ public class TestSqlClientDriver : SqlClientDriver
+ {
+ bool _UseNamedPrefixInSql = true;
+ bool _UseNamedPrefixInParameter = false;
+
+ public TestSqlClientDriver()
+ {
+
+ }
+
+ public TestSqlClientDriver(bool UseNamedPrefixInSql, bool UseNamedPrefixInParameter)
+ {
+ _UseNamedPrefixInSql = UseNamedPrefixInSql;
+ _UseNamedPrefixInParameter = UseNamedPrefixInParameter;
+ }
+
+ public override bool UseNamedPrefixInSql
+ {
+ get { return _UseNamedPrefixInSql; }
+
+ }
+
+ public override bool UseNamedPrefixInParameter
+ {
+ get { return _UseNamedPrefixInParameter; }
+ }
+ }
+}
View
2  src/NHibernate.Test/NHibernate.Test.csproj
@@ -926,6 +926,8 @@
<Compile Include="NHSpecificTest\NH2913\Fixture.cs" />
<Compile Include="NHSpecificTest\NH2959\Entity.cs" />
<Compile Include="NHSpecificTest\NH2959\Fixture.cs" />
+ <Compile Include="NHSpecificTest\NH3004\Fixture.cs" />
+ <Compile Include="NHSpecificTest\NH3004\TestSqlClientDriver.cs" />
<Compile Include="NHSpecificTest\NH941\Domain.cs" />
<Compile Include="NHSpecificTest\NH941\Fixture.cs" />
<Compile Include="NHSpecificTest\NH941\FixtureUsingList.cs" />
View
5 src/NHibernate/Driver/DriverBase.cs
@@ -221,13 +221,14 @@ public void RemoveUnusedCommandParameters(IDbCommand cmd, SqlString sqlString)
var formatter = GetSqlStringFormatter();
formatter.Format(sqlString);
+ var AssignedParameterNames = new HashSet<string>(formatter.AssignedParameterNames);
cmd.Parameters
.Cast<IDbDataParameter>()
.Select(p => p.ParameterName)
- .Except(formatter.AssignedParameterNames)
+ .Where(p => !AssignedParameterNames.Contains(UseNamedPrefixInParameter ? p : FormatNameForSql(p)))
.ToList()
- .ForEach(ununsedParameterName => cmd.Parameters.RemoveAt(ununsedParameterName));
+ .ForEach(unusedParameterName => cmd.Parameters.RemoveAt(unusedParameterName));
}
public virtual void ExpandQueryParameters(IDbCommand cmd, SqlString sqlString)
Something went wrong with that request. Please try again.