Browse Source

LINQ-To-Profiler

- implemented correct handling of string constants
- GLOB and LIKE operators

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@5023 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 16 years ago
parent
commit
a6b74847d6
  1. 32
      src/AddIns/Misc/Profiler/Controller/Data/Linq/ExpressionSqlWriter.cs
  2. 26
      src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs
  3. 84
      src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs

32
src/AddIns/Misc/Profiler/Controller/Data/Linq/ExpressionSqlWriter.cs

@ -34,6 +34,11 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -34,6 +34,11 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
this.callTreeNodeParameter = callTreeNodeParameter;
}
string EscapeString(string str)
{
return "'" + str.Replace("'", "''") + "'";
}
public void Write(Expression expression)
{
switch (expression.NodeType) {
@ -57,7 +62,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -57,7 +62,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
break;
}
case ExpressionType.Constant:
w.Write((int)((ConstantExpression)expression).Value);
var ce = (ConstantExpression)expression;
if (ce.Type == typeof(int))
w.Write((int)ce.Value);
else if (ce.Type == typeof(string))
w.Write(EscapeString((string)ce.Value));
else
throw new NotSupportedException("constant of type not supported: " + ce.Type.FullName);
break;
case ExpressionType.MemberAccess:
{
@ -81,6 +92,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -81,6 +92,9 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
if (me.Member == KnownMembers.NameMapping_ID) {
w.Write(nameSet.NameID);
break;
} else if (me.Member == KnownMembers.NameMapping_Name) {
w.Write("(SELECT name FROM namemapping WHERE namemapping.id = " + nameSet.NameID + ")");
break;
} else {
throw new NotSupportedException(me.Member.ToString());
}
@ -102,6 +116,20 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -102,6 +116,20 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
}
w.Write(')');
break;
} else if (mc.Method == KnownMembers.Like) {
w.Write("( ");
Write(mc.Arguments[0]);
w.Write(" LIKE ");
Write(mc.Arguments[1]);
w.Write(" ESCAPE '\' )");
break;
} else if (mc.Method == KnownMembers.Glob) {
w.Write("( ");
Write(mc.Arguments[0]);
w.Write(" GLOB ");
Write(mc.Arguments[1]);
w.Write(" )");
break;
} else {
throw new NotSupportedException(mc.Method.ToString());
}
@ -109,7 +137,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -109,7 +137,7 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
case ExpressionType.Not:
w.Write("(NOT ");
UnaryExpression unary = (UnaryExpression)expression;
w.Write(unary.Operand);
Write(unary.Operand);
w.Write(")");
break;
default:

26
src/AddIns/Misc/Profiler/Controller/Data/Linq/KnownMembers.cs

@ -26,9 +26,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -26,9 +26,13 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
public static readonly PropertyInfo CallTreeNode_NameMapping = PropertyOf((CallTreeNode c) => c.NameMapping);
public static readonly PropertyInfo CallTreeNode_Children = PropertyOf((CallTreeNode c) => c.Children);
public static readonly PropertyInfo NameMapping_ID = PropertyOf((NameMapping n) => n.Id);
public static readonly PropertyInfo NameMapping_Name = PropertyOf((NameMapping n) => n.Name);
public static readonly MethodInfo QuerableOfCallTreeNode_Select = MethodOf((IQueryable<CallTreeNode> q) => q.Select(x => x));
public static readonly MethodInfo QuerableOfCallTreeNode_Where = MethodOf((IQueryable<CallTreeNode> q) => q.Where(x => true));
public static readonly MethodInfo Merge = MethodOf((IQueryable<CallTreeNode> q) => q.Merge());
public static readonly MethodInfo String_StartsWith = MethodOf((string s) => s.StartsWith(s, default(StringComparison)));
public static readonly MethodInfo Like = MethodOf(() => LikeImpl("", ""));
public static readonly MethodInfo Glob = MethodOf(() => GlobImpl("", ""));
#region InfoOf Helper Methods
static MethodInfo MethodOf<T, R>(Expression<Func<T, R>> ex)
@ -36,12 +40,22 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -36,12 +40,22 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
return (MethodInfo)InfoOf(ex);
}
static MethodInfo MethodOf<T1, T2, R>(Expression<Func<T1, T2, R>> ex)
{
return (MethodInfo)InfoOf(ex);
}
static MethodInfo MethodOf<R>(Expression<Func<R>> ex)
{
return (MethodInfo)InfoOf(ex);
}
static PropertyInfo PropertyOf<T, R>(Expression<Func<T, R>> ex)
{
return (PropertyInfo)InfoOf(ex);
}
static MemberInfo InfoOf<T, R>(Expression<Func<T, R>> ex)
static MemberInfo InfoOf(LambdaExpression ex)
{
var me = ex.Body as MemberExpression;
if (me != null)
@ -52,5 +66,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -52,5 +66,15 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
throw new ArgumentException("InfoOf must be called with a member access or method call.");
}
#endregion
static bool LikeImpl(string input, string pattern)
{
throw new NotImplementedException();
}
static bool GlobImpl(string input, string pattern)
{
throw new NotImplementedException();
}
}
}

84
src/AddIns/Misc/Profiler/Controller/Data/Linq/SQLiteQueryProvider.cs

@ -46,12 +46,14 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -46,12 +46,14 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Only a limited set of expressions are valid in conditions and sort descriptors.
These are checked by the SafeExpressionImporter.
The set of valid expressions is:
- Integer constants
- Binary operators: < <= > >= == != && ||
- Unary operator: !
- value(List<int>).Contains(validExpr)
- if c is the lambda parameter, then these expressions are valid:
c.NameMapping.ID
- Integer constants
- String constants
- Binary operators: < <= > >= == != && || LIKE GLOB
- Unary operator: !
- value(List<int>).Contains(validExpr)
- if c is the lambda parameter, then these expressions are valid:
c.NameMapping.ID
c.NameMapping.Name
Additionally, field references on a lambda parameter of type SingleCall are valid inside
filters that operate directly on "AllCalls" (e.g. AllCalls.Filter()).
@ -86,6 +88,8 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -86,6 +88,8 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
Any valid expressions (as defined in 'valid expressions in QueryAst nodes') are copied over directly.
Moreover, these expressions are be converted into valid expressions:
c.IsUserCode -> c.NameMapping.ID > 0
c.NameMapping.Name.StartsWith(constantString, StringComparison.Ordinal) -> Glob(c.NameMapping.Name, constantString);
c.NameMapping.Name.StartsWith(constantString, StringComparison.OrdinalIgnoreCase) -> Like(c.NameMapping.Name, constantString);
Optimization of QueryAst:
The OptimizeQueryExpressionVisitor is performing these optimizations:
@ -265,15 +269,27 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -265,15 +269,27 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
}
}
object GetConstantValue(Expression expr)
{
return ((ConstantExpression)expr).Value;
}
/// <summary>
/// The list of characters forbidden in GLOB arguments.
/// </summary>
static readonly char[] forbiddenGlobChars = new[] { '*', '?', '[', ']' };
public Expression Import(Expression expr)
{
switch (expr.NodeType) {
case ExpressionType.Constant:
// Only integer constants are supported
// Only integer and string constants are supported
if (expr.Type == typeof(int))
return expr;
else
return null;
else if (expr.Type == typeof(string))
return expr;
return null;
case ExpressionType.MemberAccess:
{
MemberExpression me = (MemberExpression)expr;
@ -289,6 +305,28 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -289,6 +305,28 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
if (me.Member == KnownMembers.NameMapping_ID)
return me;
}
}
return null;
case ExpressionType.Call:
{
MethodCallExpression mc = (MethodCallExpression)expr;
if (IsMemberOnNameMappingOnParameter(mc.Object, KnownMembers.NameMapping_Name)) {
if (mc.Arguments[0].NodeType == ExpressionType.Constant && mc.Arguments[1].NodeType == ExpressionType.Constant) {
StringComparison cmp = (StringComparison)GetConstantValue(mc.Arguments[1]);
string pattern = (string)GetConstantValue(mc.Arguments[0]);
if (mc.Method == KnownMembers.String_StartsWith) {
if (cmp == StringComparison.Ordinal && pattern.IndexOfAny(forbiddenGlobChars) == -1)
return Expression.Call(KnownMembers.Glob, mc.Object, Expression.Constant(pattern + "*"));
else if (cmp == StringComparison.OrdinalIgnoreCase)
return Expression.Call(KnownMembers.Like, mc.Object, Expression.Constant(EscapeLikeExpr(pattern, "\\") + "%"));
else
return null;
}
}
}
return null;
}
case ExpressionType.AndAlso:
@ -320,14 +358,38 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq @@ -320,14 +358,38 @@ namespace ICSharpCode.Profiler.Controller.Data.Linq
}
}
string EscapeLikeExpr(string expression, string escape)
{
return expression.Replace(escape, escape + escape)
.Replace("%", escape + "%")
.Replace("_", escape + "_");
}
string EscapeGlobExpr(string expression, string escape)
{
return expression.Replace(escape, escape + escape)
.Replace("*", escape + "*")
.Replace("?", escape + "?");
}
bool IsNameMappingOnParameter(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess) {
var me = (MemberExpression)expr;
return me.Expression == callTreeNodeParameter && me.Member == KnownMembers.CallTreeNode_NameMapping;
} else {
return false;
}
return false;
}
bool IsMemberOnNameMappingOnParameter(Expression expr, MemberInfo member)
{
if (expr.NodeType == ExpressionType.MemberAccess) {
var me = (MemberExpression)expr;
return IsNameMappingOnParameter(me.Expression) && me.Member == member;
}
return false;
}
}
#endregion

Loading…
Cancel
Save