/// <summary>
        /// Attempts to merge group columns with select/order columns, returns group columns which were not used in select.
        /// </summary>
        private IEnumerable <QueryGroupColumnData> MergeAndGetOrphanGroupColumns()
        {
            if (_queryParts.Groups.Count == 1)
            {
                foreach (var selectColumn in _queryParts.Select)
                {
                    var shouldReplaceColumnPath =
                        (selectColumn.AggregationType.HasValue && string.IsNullOrEmpty(selectColumn.ColumnPath)) ||
                        (!selectColumn.AggregationType.HasValue && selectColumn.ColumnPath == "Key");

                    if (shouldReplaceColumnPath)
                    {
                        selectColumn.ColumnPath = _queryParts.Groups.First().ColumnPath;
                    }
                }
            }

            foreach (var groupColumn in _queryParts.Groups)
            {
                // in some cases ColumnPath contains wrong ESQ column path
                // because of re-linq tree visit rules. All cases are described:
                Func <QuerySelectColumnData, bool> selector = (column) =>
                                                              // case:
                                                              // .GroupBy(item => new object[] { item.Column<int>("Column1"), item.Column<string>("Column2") })
                                                              // .Select(group => new { Column1 = group.Key[0], Column2 = group.Key[1] })
                                                              column.ColumnPath == QueryUtils.GetIndexMemberName(groupColumn.Position)
                                                              // case:
                                                              // .GroupBy(item => new { Column1 = item.Column<int>("Column1"), Column2 = item.Column<string>("Column2") })
                                                              // .Select(group => new { Alias1 = group.Key.Column1, Alias2 = group.Key.Column2 })
                                                              || column.ColumnPath == QueryUtils.GetAliasMemberName(groupColumn.Alias)
                                                              // case:
                                                              // .GroupBy(item => item.Column<int>("Column1"))
                                                              // .Select(group => new { Alias1 = group.Key })
                                                              || !column.AggregationType.HasValue && column.ColumnPath == QueryUtils.SingleGroupColumnAlias
                                                              // case (aggregate columns have no column path):
                                                              // .GroupBy(item => item.Column<int>("Column1"))
                                                              // .Select(group => new { Alias1 = group.Key, Count = group.Count() })
                                                              || column.AggregationType.HasValue && string.IsNullOrEmpty(column.ColumnPath);

                // get select columns with incorrect column path
                var selectKeyColumns = _queryParts.Select
                                       .Where(selector)
                                       .ToArray();

                // same for order columns
                var orderKeyColumns = _queryParts.Orders
                                      .Where(selector)
                                      .ToArray();

                // 1. if column is contained in both Select/OrderBy AND GroupBy clause, then it will be added to ESQ
                //    in Select/OrderBy block and should not be added again in GroupBy block.
                // 2. if column is only contained in GroupBy clause, it will not be added in Select/OrderBy block
                //    and query will be broken - so we need to add it in GroupBy block, hence yield return.
                if (selectKeyColumns.Any() || orderKeyColumns.Any())
                {
                    selectKeyColumns.ForEach(column => column.ColumnPath = groupColumn.ColumnPath);
                    orderKeyColumns.ForEach(column => column.ColumnPath  = groupColumn.ColumnPath);
                }
                else
                {
                    yield return(groupColumn);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Processes binary expression (e.g. comparison operator)
        /// </summary>
        private void ProcessBinaryExpression(BinaryExpression expression)
        {
            LogWriter.WriteLine($"ConvertBinary: [{expression.Left}], operation {expression.NodeType}, [{expression.Right}]");

            if (ComparisonMappings.TryGetValue(expression.NodeType, out var comparisonType))
            {
                var leftOperand  = Evaluate(expression.Left);
                var rightOperand = Evaluate(expression.Right);
                var column       = _state.LastColumn;

                // Where(item => item == 123) should work same way as Where(item => 123 == item)
                var value = column == leftOperand?.ToString()
                                        ? rightOperand
                                        : leftOperand;

                if (null == value)
                {
                    FilterComparisonType nullComparisonType;
                    switch (comparisonType)
                    {
                    case FilterComparisonType.Equal:
                        nullComparisonType = FilterComparisonType.IsNull;
                        break;

                    case FilterComparisonType.NotEqual:
                        nullComparisonType = FilterComparisonType.IsNotNull;
                        break;

                    default:
                        throw new InvalidOperationException($"Cannot apply {comparisonType} operator to NULL argument.");
                    }

                    comparisonType = nullComparisonType;
                }

                _state.SetComparison(comparisonType, value);

                LogWriter.WriteLine($"Assuming comparison: {leftOperand} {comparisonType} {rightOperand}");
                return;
            }

            if (LogicalOperations.TryGetValue(expression.NodeType, out var logicalOperation))
            {
                using (_state.PushFilter(logicalOperation))
                {
                    VisitLogicalOperandExpression(expression.Left);
                    VisitLogicalOperandExpression(expression.Right);
                }

                LogWriter.WriteLine($"Assuming logical operation: {logicalOperation}");
                return;
            }

            if (expression.NodeType == ExpressionType.ArrayIndex)
            {
                var index = (int)Evaluate(expression.Right);
                _state.SetColumn(QueryUtils.GetIndexMemberName(index));

                return;
            }

            throw new InvalidOperationException($"Operation {expression} is not supported by this LINQ provider.");
        }