bool CanBeCompiled(Expression expr)
		{
			return null == expr.Find(ex =>
			{
				if (IsServerSideOnly(ex))
					return true;

				switch (ex.NodeType)
				{
					case ExpressionType.Parameter :
						return ex != ParametersParam;

					case ExpressionType.MemberAccess :
						{
							var attr = GetFunctionAttribute(((MemberExpression)ex).Member);
							return attr != null && attr.ServerSideOnly;
						}

					case ExpressionType.Call :
						{
							var attr = GetFunctionAttribute(((MethodCallExpression)ex).Method);
							return attr != null && attr.ServerSideOnly;
						}
				}

				return false;
			});
		}
		bool CanBeConstant(Expression expr)
		{
			return null == expr.Find(ex =>
			{
				if (ex is BinaryExpression || ex is UnaryExpression || ex.NodeType == ExpressionType.Convert)
					return false;

				switch (ex.NodeType)
				{
					case ExpressionType.Constant:
						{
							var c = (ConstantExpression)ex;

							if (c.Value == null || ExpressionHelper.IsConstant(ex.Type))
								return false;

							break;
						}

					case ExpressionType.MemberAccess:
						{
							var ma = (MemberExpression)ex;

							if (ExpressionHelper.IsConstant(ma.Member.DeclaringType) || TypeHelper.IsNullableValueMember(ma.Member))
								return false;

							break;
						}

					case ExpressionType.Call:
						{
							var mc = (MethodCallExpression)ex;

							if (ExpressionHelper.IsConstant(mc.Method.DeclaringType) || mc.Method.DeclaringType == typeof(object))
								return false;

							var attr = GetFunctionAttribute(mc.Method);

							if (attr != null && !attr.ServerSideOnly)
								return false;

							break;
						}
				}

				return true;
			});
		}
		bool CanBeTranslatedToSql(IParseContext context, Expression expr, bool canBeCompiled)
		{
			return null == expr.Find(pi =>
			{
				switch (pi.NodeType)
				{
					case ExpressionType.MemberAccess:
						{
							var ma = (MemberExpression)pi;
							var l  = ConvertMember(ma.Member);

							if (l != null)
								return !CanBeTranslatedToSql(context, l.Body.Unwrap(), canBeCompiled);

							var attr = GetFunctionAttribute(ma.Member);

							if (attr == null && !TypeHelper.IsNullableValueMember(ma.Member))
								goto case ExpressionType.Parameter;

							break;
						}

					case ExpressionType.Parameter:
						{
							if (canBeCompiled && GetContext(context, pi) == null)
								return !CanBeCompiled(pi);
							break;
						}

					case ExpressionType.Call:
						{
							var e = pi as MethodCallExpression;

							if (e.Method.DeclaringType != typeof(Enumerable))
							{
								var cm = ConvertMethod(e);

								if (cm != null)
									return !CanBeTranslatedToSql(context, cm, canBeCompiled);

								var attr = GetFunctionAttribute(e.Method);

								if (attr == null && canBeCompiled)
									return !CanBeCompiled(pi);
							}

							break;
						}

					case ExpressionType.New: return true;
				}

				return false;
			});
		}
		bool CheckSubQueryForWhere(IParseContext context, Expression expression, out bool makeHaving)
		{
			//var checkParameter = true; //context.IsExpression(expression, 0, RequestFor.ScalarExpression);
			var makeSubQuery   = false;
			var isHaving       = false;
			var isWhere        = false;

			expression.Find(expr =>
			{
				if (IsSubQuery(context, expr))
					return isWhere = true;

				var stopWalking = false;

				switch (expr.NodeType)
				{
					case ExpressionType.MemberAccess:
						{
							var ma = (MemberExpression)expr;

							if (TypeHelper.IsNullableValueMember(ma.Member))
								break;

							if (ConvertMember(ma.Member) == null)
							{
								var ctx = GetContext(context, expr);

								if (ctx != null)
								{
									if (ctx.IsExpression(expr, 0, RequestFor.Expression))
										makeSubQuery = true;
									stopWalking = true;
								}
							}

							isWhere = true;

							break;
						}

					case ExpressionType.Call:
						{
							var e = (MethodCallExpression)expr;

							if (e.Method.DeclaringType == typeof(Enumerable) && e.Method.Name != "Contains")
								return isHaving = true;

							isWhere = true;

							break;
						}

					case ExpressionType.Parameter:
						{
							var ctx = GetContext(context, expr);

							if (ctx != null)
							{
								if (ctx.IsExpression(expr, 0, RequestFor.Expression))
									makeSubQuery = true;
								stopWalking = true;
							}

							isWhere = true;

							break;
						}
				}

				return stopWalking;
			});

			makeHaving = isHaving && !isWhere;
			return makeSubQuery || isHaving && isWhere;
		}
		static Expression FindExpression(Expression expr)
		{
			var ret = expr.Find(pi =>
			{
				switch (pi.NodeType)
				{
					case ExpressionType.Convert      :
						{
							var e = (UnaryExpression)expr;

							return
								e.Operand.NodeType == ExpressionType.ArrayIndex &&
								((BinaryExpression)e.Operand).Left == ParametersParam;
						}

					case ExpressionType.MemberAccess :
					case ExpressionType.New          :
						return true;
				}

				return false;
			});

			if (ret == null)
				throw new NotImplementedException();

			return ret;
		}