protected override Expression VisitExtension(Expression node)
        {
            Filter      filter      = node as Filter;
            MergeByName mergeByName = node as MergeByName;

            if (filter != null)
            {
                return(VisitFilter(filter));
            }
            else if (mergeByName != null)
            {
                return(VisitMergeByName(mergeByName));
            }
            else
            {
                return(base.VisitExtension(node));
            }
        }
		/// <summary>
		/// Tries to combine nested filters;
		/// move 'MergeByName' nodes out of filter, if possible
		/// </summary>
		QueryNode ReorderFilter(Filter filter)
		{
			if (filter.Target is Filter) {
				// x.Filter(y).Filter(z) -> x.Filter(y && z)
				Filter innerFilter = (Filter)filter.Target;
				return ReorderFilter(new Filter(innerFilter.Target, innerFilter.Conditions.Concat(filter.Conditions).ToArray()));
			} else if (filter.Target is MergeByName) {
				// x.MergeByName().Filter(<criteria>) -> x.Filter(x, <criteria>).MergeByName() for some safe criterias
				QueryNode innerTarget = filter.Target.Target;
				var conditionsToMoveIntoFilter = filter.Conditions.Where(c => IsConditionSafeVisitor.Test(c, SafeMembersForMoveIntoMergeByName)).ToArray();
				if (conditionsToMoveIntoFilter.Length != 0) {
					MergeByName newTarget = new MergeByName(ReorderFilter(new Filter(innerTarget, conditionsToMoveIntoFilter)));
					var conditionsKeptOutsideFilter = filter.Conditions.Except(conditionsToMoveIntoFilter).ToArray();
					if (conditionsKeptOutsideFilter.Length == 0)
						return newTarget;
					else
						return new Filter(newTarget, conditionsKeptOutsideFilter);
				} else {
					return filter;
				}
			} else {
				return filter;
			}
		}
 /// <summary>
 /// Tries to combine nested filters;
 /// move 'MergeByName' nodes out of filter, if possible
 /// </summary>
 QueryNode ReorderFilter(Filter filter)
 {
     if (filter.Target is Filter)
     {
         // x.Filter(y).Filter(z) -> x.Filter(y && z)
         Filter innerFilter = (Filter)filter.Target;
         return(ReorderFilter(new Filter(innerFilter.Target, innerFilter.Conditions.Concat(filter.Conditions).ToArray())));
     }
     else if (filter.Target is MergeByName)
     {
         // x.MergeByName().Filter(<criteria>) -> x.Filter(x, <criteria>).MergeByName() for some safe criterias
         QueryNode innerTarget = filter.Target.Target;
         var       conditionsToMoveIntoFilter = filter.Conditions.Where(c => IsConditionSafeVisitor.Test(c, SafeMembersForMoveIntoMergeByName)).ToArray();
         if (conditionsToMoveIntoFilter.Length != 0)
         {
             MergeByName newTarget = new MergeByName(ReorderFilter(new Filter(innerTarget, conditionsToMoveIntoFilter)));
             var         conditionsKeptOutsideFilter = filter.Conditions.Except(conditionsToMoveIntoFilter).ToArray();
             if (conditionsKeptOutsideFilter.Length == 0)
             {
                 return(newTarget);
             }
             else
             {
                 return(new Filter(newTarget, conditionsKeptOutsideFilter));
             }
         }
         else
         {
             return(filter);
         }
     }
     else
     {
         return(filter);
     }
 }
		QueryNode VisitMergeByName(MergeByName merge)
		{
			// First optimize the Target expression
			QueryNode target = Visit(merge.Target);
			if (target is MergeByName) {
				// x.MergeByName().MergeByName() -> x.MergeByName()
				return target;
			}
			if (target == AllCalls.Instance) {
				// AllCalls.MergeByName() -> AllFunctions
				return new AllFunctions();
			}
			if (target is Filter && target.Target == AllCalls.Instance) {
				// AllCalls.Filter(criteria).MergeByName() -> AllFunctions.Filter(criteria)
				// If criteria accesses no CallTreeNode properties except for NameMapping.
				// Criteria of the form 'start <= c.DataSetID && c.DataSetID <= end' will be converted into AllFunctions(start,end)
				List<LambdaExpression> newConditions = new List<LambdaExpression>();
				bool allIsSafe = true;
				int startDataSetID = -1;
				int endDataSetID = -1;
				foreach (LambdaExpression condition in ((Filter)target).Conditions) {
					if (IsConditionSafeVisitor.Test(condition, SafeMembersForMoveIntoMergeByName)) {
						newConditions.Add(condition);
					} else if (condition.Body.NodeType == ExpressionType.AndAlso && startDataSetID < 0) {
						// try match 'constant <= c.DataSetID && c.DataSetID <= constant', but only if we
						// haven't found it already (startDataSetID is still -1)
						BinaryExpression bin = (BinaryExpression)condition.Body;
						if (bin.Left.NodeType == ExpressionType.LessThanOrEqual && bin.Right.NodeType == ExpressionType.LessThanOrEqual) {
							BinaryExpression left = (BinaryExpression)bin.Left;
							BinaryExpression right = (BinaryExpression)bin.Right;
							if (left.Left.NodeType == ExpressionType.Constant && left.Right.NodeType == ExpressionType.MemberAccess
							    && right.Left.NodeType == ExpressionType.MemberAccess && right.Right.NodeType == ExpressionType.Constant
							    && ((MemberExpression)left.Right).Member == SingleCall.DataSetIdField
							    && ((MemberExpression)right.Left).Member == SingleCall.DataSetIdField)
							{
								startDataSetID = (int)GetConstantValue(left.Left);
								endDataSetID = (int)GetConstantValue(right.Right);
							} else {
								allIsSafe = false;
							}
						} else {
							allIsSafe = false;
						}
					} else {
						allIsSafe = false;
					}
				}
				if (allIsSafe) {
					if (newConditions.Count > 0)
						return new Filter(new AllFunctions(startDataSetID, endDataSetID), newConditions.ToArray());
					else
						return new AllFunctions(startDataSetID, endDataSetID);
				}
			}
			return new MergeByName(target);
		}
        QueryNode VisitMergeByName(MergeByName merge)
        {
            // First optimize the Target expression
            QueryNode target = Visit(merge.Target);

            if (target is MergeByName)
            {
                // x.MergeByName().MergeByName() -> x.MergeByName()
                return(target);
            }
            if (target == AllCalls.Instance)
            {
                // AllCalls.MergeByName() -> AllFunctions
                return(new AllFunctions());
            }
            if (target is Filter && target.Target == AllCalls.Instance)
            {
                // AllCalls.Filter(criteria).MergeByName() -> AllFunctions.Filter(criteria)
                // If criteria accesses no CallTreeNode properties except for NameMapping.
                // Criteria of the form 'start <= c.DataSetID && c.DataSetID <= end' will be converted into AllFunctions(start,end)
                List <LambdaExpression> newConditions = new List <LambdaExpression>();
                bool allIsSafe      = true;
                int  startDataSetID = -1;
                int  endDataSetID   = -1;
                foreach (LambdaExpression condition in ((Filter)target).Conditions)
                {
                    if (IsConditionSafeVisitor.Test(condition, SafeMembersForMoveIntoMergeByName))
                    {
                        newConditions.Add(condition);
                    }
                    else if (condition.Body.NodeType == ExpressionType.AndAlso && startDataSetID < 0)
                    {
                        // try match 'constant <= c.DataSetID && c.DataSetID <= constant', but only if we
                        // haven't found it already (startDataSetID is still -1)
                        BinaryExpression bin = (BinaryExpression)condition.Body;
                        if (bin.Left.NodeType == ExpressionType.LessThanOrEqual && bin.Right.NodeType == ExpressionType.LessThanOrEqual)
                        {
                            BinaryExpression left  = (BinaryExpression)bin.Left;
                            BinaryExpression right = (BinaryExpression)bin.Right;
                            if (left.Left.NodeType == ExpressionType.Constant && left.Right.NodeType == ExpressionType.MemberAccess &&
                                right.Left.NodeType == ExpressionType.MemberAccess && right.Right.NodeType == ExpressionType.Constant &&
                                ((MemberExpression)left.Right).Member == SingleCall.DataSetIdField &&
                                ((MemberExpression)right.Left).Member == SingleCall.DataSetIdField)
                            {
                                startDataSetID = (int)GetConstantValue(left.Left);
                                endDataSetID   = (int)GetConstantValue(right.Right);
                            }
                            else
                            {
                                allIsSafe = false;
                            }
                        }
                        else
                        {
                            allIsSafe = false;
                        }
                    }
                    else
                    {
                        allIsSafe = false;
                    }
                }
                if (allIsSafe)
                {
                    if (newConditions.Count > 0)
                    {
                        return(new Filter(new AllFunctions(startDataSetID, endDataSetID), newConditions.ToArray()));
                    }
                    else
                    {
                        return(new AllFunctions(startDataSetID, endDataSetID));
                    }
                }
            }
            return(new MergeByName(target));
        }