protected virtual Expression AnalyzeProjectionQuery(SqlFunctionType specialExpressionType, IList<Expression> parameters,
            TranslationContext context, bool canHaveFilter = true)
        {
            if (context.IsExternalInExpressionChain)
            {
                var operand0 = Analyze(parameters[0], context);
                Expression functionOperand = null;
                Expression projectionOperand;

                if (    context.CurrentSelect.NextSelectExpression != null
                    ||  context.CurrentSelect.Operands.Count() > 0
                    ||  context.CurrentSelect.Group.Count > 0
                   )
                {
                    // No TableInfo in projection
                    operand0 = new SubSelectExpression(context.CurrentSelect, operand0.Type, "source", null);
                    context.NewParentSelect();

                    // In the new scope we should not have MaximumDatabaseLoad
                    //context.QueryContext.MaximumDatabaseLoad = false;

                    context.CurrentSelect.Tables.Add(operand0 as TableExpression);
                }

                // basically, we have three options for projection methods:
                // - projection on grouped table (1 operand, a GroupExpression)
                // - projection on grouped column (2 operands, GroupExpression and ColumnExpression)
                // - projection on table/column, with optional restriction
                var groupOperand0 = operand0 as GroupExpression;
                if (groupOperand0 != null)
                {
                    if (parameters.Count > 1)
                        projectionOperand = Analyze(parameters[1], groupOperand0.GroupedExpression, context);
                    else
                        projectionOperand = Analyze(groupOperand0.GroupedExpression, context);
                }
                else
                {
                    projectionOperand = operand0;
                    if (parameters.Count > 1)
                      functionOperand = Analyze(parameters[1], operand0, context);
                    int filterIndex = canHaveFilter ? 1 : -1; //special case for Average - its second parameter is NOT filter
                    CheckWhere(projectionOperand, parameters, filterIndex, context);
                }

                if (projectionOperand is TableExpression)
                    projectionOperand = RegisterTable((TableExpression)projectionOperand, context);

                if (groupOperand0 != null) {
                  var childColumns = GetChildColumns(projectionOperand, context);
                  projectionOperand = new GroupExpression(projectionOperand, groupOperand0.KeyExpression, childColumns);
                }

                var opList = new List<Expression>();
                opList.Add(projectionOperand);
                if (functionOperand != null)
                  opList.Add(functionOperand);
                return CreateSqlFunction(specialExpressionType, opList.ToArray());
            }
            else
            {
                var subQueryContext = context.NewSelect();

                var tableExpression = Analyze(parameters[0], subQueryContext);

                //RI: new stuff - handling grouping with aggregates
                //if (IsAggregate(specialExpressionType)) {
                  var grpExpr = tableExpression as GroupExpression;
                  var srcTable = grpExpr == null ? tableExpression : grpExpr.GroupedExpression ;
                  SqlFunctionExpression specialExpr;
                  if (parameters.Count > 1) {
                    var predicate = Analyze(parameters[1], srcTable, subQueryContext);
                    specialExpr = CreateSqlFunction(specialExpressionType, tableExpression, predicate);
                  } else {
                    specialExpr = CreateSqlFunction(specialExpressionType, tableExpression);
                  }
                  // If subQuery context has no tables added, it is not a subquery, it's just an aggregate function over 'main' table
                  var currSelect = subQueryContext.CurrentSelect;
                  if (currSelect.Tables.Count == 0)
                    return specialExpr;
                  //this is a real subquery, so mutate and return the current select from this context
                  currSelect = currSelect.ChangeOperands(new Expression[] { specialExpr }, currSelect.Operands);
                  return currSelect;
                //}
                //RI: end my special code
            }
        }
        /// <summary>
        /// "Distinct" means select X group by X
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual Expression AnalyzeDistinct(IList<Expression> parameters, TranslationContext context)
        {
            var expression = Analyze(parameters[0], context);
            // we select and group by the same criterion
            // RI: adding explicit list of columns, to catch situation of duplicate column names in joins
            var childColumns = GetChildColumns(expression, context);
            // some providers (ex SQL CE) do not allow Text columns in distinct output
            CheckDistinctClauseColumns(childColumns, context);

            var group = new GroupExpression(expression, expression, childColumns);
            if (context.CurrentSelect.NextSelectExpression != null)
            {
                var tableInfo = context.DbModel.GetTable(expression.Type);
                expression = new SubSelectExpression(context.CurrentSelect, expression.Type, "source", tableInfo);
                context.NewParentSelect();

                // In the new scope we should not have MaximumDatabaseLoad
                //context.QueryContext.MaximumDatabaseLoad = false;

                context.CurrentSelect.Tables.Add(expression as TableExpression);
            }
            context.CurrentSelect.Group.Add(group);

            //RI: added this special check
            // var table
            // var cols = RegisterAllColumns()

            // "Distinct" method is equivalent to a GroupBy
            // but for some obscure reasons, Linq expects a IQueryable instead of an IGrouping
            // so we return the column, not the group
            return expression;
        }
 protected virtual Expression AnalyzeGroupBy(MethodInfo method, IList<Expression> parameters, TranslationContext context)
 {
     // there are 8 overloads of Queryable.GroupBy method. 4 of them have last parameter IEqualityComparer - reject these overloads
       // The remaining 4 have Expression<> as last parameter - we use it to detect these.
       var methodParams = method.GetParameters();
       var lastParamType = methodParams[methodParams.Length - 1].ParameterType;
       if (methodParams.Length > 2 && !lastParamType.IsSubclassOf(typeof(LambdaExpression)))
       throw Util.Throw("The overload of GroupBy method with IEqualityComparer parameter is not supported.");
       /* The remaining overloads are the following:
        (#1)   public static IQueryable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector);
                   -- 2 args, 2 type args
        (#2)   public static IQueryable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,
                                                                                            Expression<Func<TSource, TElement>> elementSelector);
                   -- 3 args, 3 type args
        (#3)   public static IQueryable<TResult> GroupBy<TSource, TKey, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,
                                                                         Expression<Func<TKey, IEnumerable<TSource>, TResult>> resultSelector);
                   -- 3 args, 3 type args
        (#4)   public static IQueryable<TResult> GroupBy<TSource, TKey, TElement, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector,
                                         Expression<Func<TSource, TElement>> elementSelector, Expression<Func<TKey, IEnumerable<TElement>, TResult>> resultSelector);
                   -- 4 args, 4 type args
     We can distinguish them by number of args; for #2 and #3 - both have 3 args and 3 type args. We distinguish them by analyzing type argument
     */
       // Detect which overload we have
       int ovldNum = 0;
       switch (methodParams.Length) {
     case 2: ovldNum = 1; break;
     case 4: ovldNum = 4; break;
     case 3:
       //analyze type argument of the last parameter (generic expression)
       var funcType = lastParamType.GenericTypeArguments[0];
       //this is a Func<..>, check it count of arguments
       var funcGenArgCount = funcType.GenericTypeArguments.Length;
       switch(funcGenArgCount) {
         case 2: ovldNum = 2; break;
         case 3: ovldNum = 3; break;
       }
       break;
       }
       if (ovldNum == 0)
     throw Util.Throw("Unknown/unsupported overload of GroupBy method - failed to detect overload type.");
       //analyze parameters
       var table = Analyze(parameters[0], context);
       var keySelector = Analyze(parameters[1], table, context);
       Expression result = null;
       bool clrGrouping = false;
       switch(ovldNum) {
     case 1: //2 params
       result = table; // we return the whole table
       clrGrouping = true;
       break;
     case 2: //3 params
       result = Analyze(parameters[2], new Expression[] {table }, context);
       clrGrouping = true;
       break;
     case 3: // 3 params
       result = Analyze(parameters[2], new Expression[] { keySelector, table }, context);
       break;
     case 4: // 4 params
       Util.Throw("This overload of GroupBy method is not supported. Rewrite the LINQ query.");
       break;
       }
       var childColumns = GetChildColumns(keySelector, context);
       var group = new GroupExpression(result, keySelector, childColumns, clrGrouping);
       context.CurrentSelect.Group.Add(group);
       return group;
 }
        /// <summary>
        /// Any() returns true if the given condition satisfies at least one of provided elements
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        protected virtual Expression AnalyzeAny(IList<Expression> parameters, TranslationContext context)
        {
            if (context.IsExternalInExpressionChain)
            {
                var tableExpression = Analyze(parameters[0], context);
                Expression projectionOperand;

                if (context.CurrentSelect.NextSelectExpression != null)
                {
                    TableExpression currentTableExpression = tableExpression as TableExpression;
                    tableExpression = new SubSelectExpression(context.CurrentSelect, currentTableExpression.Type, "source", currentTableExpression.TableInfo);
                    context.NewParentSelect();

                    // In the new scope we should not have MaximumDatabaseLoad
                    //context.QueryContext.MaximumDatabaseLoad = false;

                    context.CurrentSelect.Tables.Add(tableExpression as TableExpression);
                }

                // basically, we have three options for projection methods:
                // - projection on grouped table (1 operand, a GroupExpression)
                // - projection on grouped column (2 operands, GroupExpression and ColumnExpression)
                // - projection on table/column, with optional restriction
                var groupOperand0 = tableExpression as GroupExpression;
                if (groupOperand0 != null)
                {
                    if (parameters.Count > 1)
                    {
                        projectionOperand = Analyze(parameters[1], groupOperand0.GroupedExpression,
                                                    context);
                    }
                    else
                        projectionOperand = Analyze(groupOperand0.GroupedExpression, context);
                }
                else
                {
                    projectionOperand = tableExpression;
                    CheckWhere(projectionOperand, parameters, 1, context);
                }

                if (projectionOperand is TableExpression)
                    projectionOperand = RegisterTable((TableExpression)projectionOperand, context);

                if (groupOperand0 != null) {
                  var childColumns = GetChildColumns(groupOperand0.KeyExpression, context);
                  projectionOperand = new GroupExpression(projectionOperand, groupOperand0.KeyExpression, childColumns);
                }

                return ExpressionUtil.MakeGreaterThanZero(CreateSqlFunction(SqlFunctionType.Count, projectionOperand));
            }
            else
            {
                var anyBuilderContext = context.NewSelect();
                var tableExpression = Analyze(parameters[0], anyBuilderContext);

                if (!(tableExpression is TableExpression))
                    tableExpression = Analyze(tableExpression, anyBuilderContext);

                // from here we build a custom clause:
                // <anyClause> ==> "(select count(*) from <table> where <anyClause>)>0"
                // TODO (later...): see if some vendors support native Any operator and avoid this substitution
                if (parameters.Count > 1)
                {
                    var anyClause = Analyze(parameters[1], tableExpression, anyBuilderContext);
                    RegisterWhere(anyClause, anyBuilderContext);
                }
                var countExpr = CreateSqlFunction(SqlFunctionType.Count, tableExpression);
                var currSelect = anyBuilderContext.CurrentSelect;
                anyBuilderContext.CurrentSelect = currSelect.ChangeOperands(new Expression[] {countExpr}, currSelect.Operands);
                // TODO: see if we need to register the tablePiece here (we probably don't)

                // we now switch back to current context, and compare the result with 0
                // note - we might have count returning int or long, so we must adjust 0 constant (int or long)
                var anyExpression = ExpressionUtil.MakeGreaterThanZero(anyBuilderContext.CurrentSelect);
                return anyExpression;
            }
        }