Skip to content

Add session filter support for DML statement #1931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 14, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/NHibernate.Test/Async/NHSpecificTest/GH1921/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by AsyncGenerator.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------


using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH1921
{
using System.Threading.Tasks;
[TestFixture]
public class FixtureAsync : BugTestCase
{
protected override bool AppliesTo(Dialect.Dialect dialect)
{
return TestDialect.NativeGeneratorSupportsBulkInsertion;
}

protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
var e1 = new Entity {Name = "Bob"};
session.Save(e1);

var e2 = new Entity {Name = "Sally"};
session.Save(e2);

transaction.Commit();
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}
}

[Theory]
public async Task DmlInsertAsync(bool filtered)
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
if (filtered)
session.EnableFilter("NameFilter").SetParameter("name", "Bob");
var rowCount = await (session.CreateQuery("insert into Entity (Name) select e.Name from Entity e").ExecuteUpdateAsync());
await (transaction.CommitAsync());

Assert.That(rowCount, Is.EqualTo(filtered ? 1 : 2));
}
}

[Theory]
public async Task DmlUpdateAsync(bool filtered)
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
if (filtered)
session.EnableFilter("NameFilter").SetParameter("name", "Bob");
var rowCount = await (session.CreateQuery("update Entity e set Name = 'newName'").ExecuteUpdateAsync());
await (transaction.CommitAsync());

Assert.That(rowCount, Is.EqualTo(filtered ? 1 : 2));
}
}

[Theory]
public async Task DmlDeleteAsync(bool filtered)
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
if (filtered)
session.EnableFilter("NameFilter").SetParameter("name", "Bob");
var rowCount = await (session.CreateQuery("delete Entity").ExecuteUpdateAsync());
await (transaction.CommitAsync());

Assert.That(rowCount, Is.EqualTo(filtered ? 1 : 2));
}
}
}
}
10 changes: 10 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH1921/Entity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace NHibernate.Test.NHSpecificTest.GH1921
{
class Entity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}
}
84 changes: 84 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH1921/Fixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.GH1921
{
[TestFixture]
public class Fixture : BugTestCase
{
protected override bool AppliesTo(Dialect.Dialect dialect)
{
return TestDialect.NativeGeneratorSupportsBulkInsertion;
}

protected override void OnSetUp()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
var e1 = new Entity {Name = "Bob"};
session.Save(e1);

var e2 = new Entity {Name = "Sally"};
session.Save(e2);

transaction.Commit();
}
}

protected override void OnTearDown()
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
session.CreateQuery("delete from System.Object").ExecuteUpdate();

transaction.Commit();
}
}

[Theory]
public void DmlInsert(bool filtered)
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
if (filtered)
session.EnableFilter("NameFilter").SetParameter("name", "Bob");
var rowCount = session.CreateQuery("insert into Entity (Name) select e.Name from Entity e").ExecuteUpdate();
transaction.Commit();

Assert.That(rowCount, Is.EqualTo(filtered ? 1 : 2));
}
}

[Theory]
public void DmlUpdate(bool filtered)
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
if (filtered)
session.EnableFilter("NameFilter").SetParameter("name", "Bob");
var rowCount = session.CreateQuery("update Entity e set Name = 'newName'").ExecuteUpdate();
transaction.Commit();

Assert.That(rowCount, Is.EqualTo(filtered ? 1 : 2));
}
}

[Theory]
public void DmlDelete(bool filtered)
{
using (var session = OpenSession())
using (var transaction = session.BeginTransaction())
{
if (filtered)
session.EnableFilter("NameFilter").SetParameter("name", "Bob");
var rowCount = session.CreateQuery("delete Entity").ExecuteUpdate();
transaction.Commit();

Assert.That(rowCount, Is.EqualTo(filtered ? 1 : 2));
}
}
}
}
15 changes: 15 additions & 0 deletions src/NHibernate.Test/NHSpecificTest/GH1921/Mappings.hbm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
namespace="NHibernate.Test.NHSpecificTest.GH1921">

<class name="Entity">
<id name="Id" generator="native"/>
<property name="Name"/>

<filter name="NameFilter"/>
</class>

<filter-def name="NameFilter" condition="Name = :name">
<filter-param name="name" type="System.String"/>
</filter-def>
</hibernate-mapping>
9 changes: 9 additions & 0 deletions src/NHibernate.Test/TestDialect.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using NHibernate.Hql.Ast.ANTLR;
using NHibernate.Id;
using NHibernate.SqlTypes;

Expand Down Expand Up @@ -33,6 +34,14 @@ public TestDialect(Dialect.Dialect dialect)
public bool HasIdentityNativeGenerator
=> _dialect.NativeIdentifierGeneratorClass == typeof(IdentityGenerator);

/// <summary>
/// Has a native generator strategy supporting id generation on DML insert.
/// </summary>
public bool NativeGeneratorSupportsBulkInsertion
=> HqlSqlWalker.SupportsIdGenWithBulkInsertion(
(IIdentifierGenerator) Cfg.Environment.ObjectsFactory.CreateInstance(
_dialect.NativeIdentifierGeneratorClass));

public virtual bool SupportsOperatorAll => true;
public virtual bool SupportsOperatorSome => true;
public virtual bool SupportsLocate => true;
Expand Down
2 changes: 1 addition & 1 deletion src/NHibernate/Async/Engine/Query/NativeSQLQueryPlan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public async Task<int> PerformExecuteUpdateAsync(QueryParameters queryParameters
try
{
var parametersSpecifications = customQuery.CollectedParametersSpecifications.ToList();
SqlString sql = ExpandDynamicFilterParameters(customQuery.SQL, parametersSpecifications, session);
SqlString sql = FilterHelper.ExpandDynamicFilterParameters(customQuery.SQL, parametersSpecifications, session);
// After the last modification to the SqlString we can collect all parameters types.
parametersSpecifications.ResetEffectiveExpectedType(queryParameters);

Expand Down
11 changes: 8 additions & 3 deletions src/NHibernate/Async/Hql/Ast/ANTLR/Exec/BasicExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
using NHibernate.Engine;
using NHibernate.Exceptions;
using NHibernate.Hql.Ast.ANTLR.Tree;
using NHibernate.Impl;
using NHibernate.Param;
using NHibernate.SqlCommand;
using NHibernate.SqlTypes;
using NHibernate.Type;
using NHibernate.Util;
using IQueryable = NHibernate.Persister.Entity.IQueryable;

namespace NHibernate.Hql.Ast.ANTLR.Exec
Expand All @@ -45,10 +48,12 @@ public override async Task<int> ExecuteAsync(QueryParameters parameters, ISessio
{
CheckParametersExpectedType(parameters); // NH Different behavior (NH-1898)

var sqlQueryParametersList = sql.GetParameters().ToList();
var sqlString = sql.Copy();
sqlString = FilterHelper.ExpandDynamicFilterParameters(sqlString, Parameters, session);
var sqlQueryParametersList = sqlString.GetParameters().ToList();
SqlType[] parameterTypes = Parameters.GetQueryParameterTypes(sqlQueryParametersList, session.Factory);

st = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sql, parameterTypes, cancellationToken)).ConfigureAwait(false);
st = await (session.Batcher.PrepareCommandAsync(CommandType.Text, sqlString, parameterTypes, cancellationToken)).ConfigureAwait(false);
foreach (var parameterSpecification in Parameters)
{
await (parameterSpecification.BindAsync(st, sqlQueryParametersList, parameters, session, cancellationToken)).ConfigureAwait(false);
Expand Down Expand Up @@ -78,4 +83,4 @@ public override async Task<int> ExecuteAsync(QueryParameters parameters, ISessio
}
}
}
}
}
18 changes: 15 additions & 3 deletions src/NHibernate/Engine/JoinSequence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,30 @@ public JoinFragment ToJoinFragment(
bool includeExtraJoins,
SqlString withClauseFragment,
string withClauseJoinAlias)
{
return ToJoinFragment(enabledFilters, includeExtraJoins, withClauseFragment, withClauseJoinAlias, rootAlias);
}

internal JoinFragment ToJoinFragment(
IDictionary<string, IFilter> enabledFilters,
bool includeExtraJoins,
SqlString withClauseFragment,
string withClauseJoinAlias,
string withRootAlias)
{
QueryJoinFragment joinFragment = new QueryJoinFragment(factory.Dialect, useThetaStyle);
if (rootJoinable != null)
{
joinFragment.AddCrossJoin(rootJoinable.TableName, rootAlias);
string filterCondition = rootJoinable.FilterFragment(rootAlias, enabledFilters);
joinFragment.AddCrossJoin(rootJoinable.TableName, withRootAlias);
string filterCondition = rootJoinable.FilterFragment(withRootAlias, enabledFilters);
// JoinProcessor needs to know if the where clause fragment came from a dynamic filter or not so it
// can put the where clause fragment in the right place in the SQL AST. 'hasFilterCondition' keeps track
// of that fact.
joinFragment.HasFilterCondition = joinFragment.AddCondition(filterCondition);
if (includeExtraJoins)
{
//TODO: not quite sure about the full implications of this!
AddExtraJoins(joinFragment, rootAlias, rootJoinable, true);
AddExtraJoins(joinFragment, withRootAlias, rootJoinable, true);
}
}

Expand Down Expand Up @@ -313,5 +323,7 @@ public interface ISelector
{
bool IncludeSubclasses(string alias);
}

internal string RootAlias => rootAlias;
}
}
Loading