Skip to content

Testrun only. Adjustment to PR 484 (NH-3726: Added escape character support in SqlMethods.Like #484) #488

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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
34 changes: 33 additions & 1 deletion src/NHibernate.Test/Linq/FunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,38 @@ where NHibernate.Linq.SqlMethods.Like(e.FirstName, "Ma%et")
Assert.That(query[0].FirstName, Is.EqualTo("Margaret"));
}

[Test]
public void LikeFunctionWithEscapeCharacter()
{
using (var tx = session.BeginTransaction())
{
var employeeName = "Mar%aret";
var escapeChar = '#';
var employeeNameEscaped = employeeName.Replace("%", escapeChar + "%");

//This entity will be flushed to the db, but rolled back when the test completes

session.Save(new Employee { FirstName = employeeName, LastName = "LastName" });
session.Flush();


var query = (from e in db.Employees
where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, escapeChar)
select e).ToList();

Assert.That(query.Count, Is.EqualTo(1));
Assert.That(query[0].FirstName, Is.EqualTo(employeeName));

Assert.Throws<ArgumentException>(() =>
{
(from e in db.Employees
where NHibernate.Linq.SqlMethods.Like(e.FirstName, employeeNameEscaped, e.FirstName.First())
select e).ToList();
});
tx.Rollback();
}
}

private static class SqlMethods
{
public static bool Like(string expression, string pattern)
Expand Down Expand Up @@ -362,4 +394,4 @@ where item.Discount.Equals(-1)
ObjectDumper.Write(query);
}
}
}
}
7 changes: 6 additions & 1 deletion src/NHibernate/Hql/Ast/HqlTreeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ public HqlLike Like(HqlExpression lhs, HqlExpression rhs)
return new HqlLike(_factory, lhs, rhs);
}

public HqlLike Like(HqlExpression lhs, HqlExpression rhs, HqlConstant escapeCharacter)
{
return new HqlLike(_factory, lhs, rhs, escapeCharacter);
}

public HqlConcat Concat(params HqlExpression[] args)
{
return new HqlConcat(_factory, args);
Expand Down Expand Up @@ -466,4 +471,4 @@ public HqlTreeNode Indices(HqlExpression dictionary)
return new HqlIndices(_factory, dictionary);
}
}
}
}
29 changes: 21 additions & 8 deletions src/NHibernate/Hql/Ast/HqlTreeNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected HqlTreeNode(int type, string text, IASTFactory factory, IEnumerable<Hq
AddChildren(children);
}

protected HqlTreeNode(int type, string text, IASTFactory factory, params HqlTreeNode[] children) : this(type, text, factory, (IEnumerable<HqlTreeNode>) children)
protected HqlTreeNode(int type, string text, IASTFactory factory, params HqlTreeNode[] children) : this(type, text, factory, (IEnumerable<HqlTreeNode>)children)
{
}

Expand Down Expand Up @@ -92,7 +92,7 @@ internal IASTNode AstNode

internal void AddChild(HqlTreeNode child)
{
if (child is HqlExpressionSubTreeHolder)
if (child is HqlExpressionSubTreeHolder)
{
AddChildren(child.Children);
}
Expand All @@ -116,7 +116,7 @@ public static HqlBooleanExpression AsBooleanExpression(this HqlTreeNode node)
{
if (node is HqlDot)
{
return new HqlBooleanDot(node.Factory, (HqlDot) node);
return new HqlBooleanDot(node.Factory, (HqlDot)node);
}

// TODO - nice error handling if cast fails
Expand Down Expand Up @@ -220,8 +220,8 @@ internal HqlIdent(IASTFactory factory, System.Type type)
}
if (type == typeof(DateTimeOffset))
{
SetText("datetimeoffset");
break;
SetText("datetimeoffset");
break;
}
throw new NotSupportedException(string.Format("Don't currently support idents of type {0}", type.Name));
}
Expand Down Expand Up @@ -373,7 +373,7 @@ public HqlSkip(IASTFactory factory, HqlExpression parameter)
public class HqlTake : HqlStatement
{
public HqlTake(IASTFactory factory, HqlExpression parameter)
: base(HqlSqlWalker.TAKE, "take", factory, parameter) {}
: base(HqlSqlWalker.TAKE, "take", factory, parameter) { }
}

public class HqlConstant : HqlExpression
Expand Down Expand Up @@ -690,7 +690,7 @@ public HqlMax(IASTFactory factory, HqlExpression expression)
: base(HqlSqlWalker.AGGREGATE, "max", factory, expression)
{
}
}
}

public class HqlMin : HqlExpression
{
Expand Down Expand Up @@ -818,6 +818,19 @@ public HqlLike(IASTFactory factory, HqlExpression lhs, HqlExpression rhs)
: base(HqlSqlWalker.LIKE, "like", factory, lhs, rhs)
{
}

public HqlLike(IASTFactory factory, HqlExpression lhs, HqlExpression rhs, HqlConstant escapeCharacter)
: base(HqlSqlWalker.LIKE, "like", factory, lhs, rhs, new HqlEscape(factory, escapeCharacter))
{
}
}

public class HqlEscape : HqlStatement
{
public HqlEscape(IASTFactory factory, HqlConstant escapeCharacter)
: base(HqlSqlWalker.ESCAPE, "escape", factory, escapeCharacter)
{
}
}

public class HqlConcat : HqlExpression
Expand Down Expand Up @@ -899,4 +912,4 @@ public HqlInList(IASTFactory factory, HqlTreeNode source)
{
}
}
}
}
23 changes: 18 additions & 5 deletions src/NHibernate/Linq/Functions/StringGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,22 @@ public IEnumerable<MethodInfo> SupportedMethods
public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnlyCollection<Expression> arguments,
HqlTreeBuilder treeBuilder, IHqlExpressionVisitor visitor)
{
return treeBuilder.Like(
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression());
if (arguments.Count == 2)
{
return treeBuilder.Like(
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression());
}
if (arguments[2].NodeType == ExpressionType.Constant)
{
var escapeCharExpression = (ConstantExpression)arguments[2];
return treeBuilder.Like(
visitor.Visit(arguments[0]).AsExpression(),
visitor.Visit(arguments[1]).AsExpression(),
treeBuilder.Constant(escapeCharExpression.Value));
}
throw new ArgumentException("The escape character must be specified as literal value or a string variable");

}

public bool SupportsMethod(MethodInfo method)
Expand All @@ -34,7 +47,7 @@ public bool SupportsMethod(MethodInfo method)
// to avoid referencing Linq2Sql or Linq2NHibernate, if they so wish.

return method != null && method.Name == "Like" &&
method.GetParameters().Length == 2 &&
(method.GetParameters().Length == 2 || method.GetParameters().Length == 3) &&
method.DeclaringType != null &&
method.DeclaringType.FullName.EndsWith("SqlMethods");
}
Expand Down Expand Up @@ -284,4 +297,4 @@ public HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, ReadOnly
return treeBuilder.MethodCall("str", visitor.Visit(targetObject).AsExpression());
}
}
}
}
17 changes: 15 additions & 2 deletions src/NHibernate/Linq/SqlMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,29 @@ namespace NHibernate.Linq
public static class SqlMethods
{
/// <summary>
/// Use the SqlMethods.Like() method in a Linq2NHibernate expression to generate
/// Use this method in a Linq2NHibernate expression to generate
/// an SQL LIKE expression. (If you want to avoid depending on the NHibernate.Linq namespace,
/// you can define your own replica of this method. Any 2-argument method named Like in a class named SqlMethods
/// will be translated.) This method can only be used in Linq2NHibernate expression, and will throw
/// will be translated.) This method can only be used in Linq2NHibernate expressions, and will throw
/// if called directly.
/// </summary>
public static bool Like(this string matchExpression, string sqlLikePattern)
{
throw new NotSupportedException(
"The NHibernate.Linq.SqlMethods.Like(string, string) method can only be used in Linq2NHibernate expressions.");
}

/// <summary>
/// Use this method in a Linq2NHibernate expression to generate
/// an SQL LIKE expression with an escape character defined. (If you want to avoid depending on the NHibernate.Linq namespace,
/// you can define your own replica of this method. Any 3-argument method named Like in a class named SqlMethods
/// will be translated.) This method can only be used in Linq2NHibernate expressions, and will throw
/// if called directly.
/// </summary>
public static bool Like(this string matchExpression, string sqlLikePattern, char escapeCharacter)
{
throw new NotSupportedException(
"The NHibernate.Linq.SqlMethods.Like(string, string, char) method can only be used in Linq2NHibernate expressions.");
}
}
}