示例#1
0
        private BoundExpression BindAggregate(ExpressionSyntax aggregate, BoundAggregateExpression boundAggregate)
        {
            var affectedQueryScopes = aggregate.DescendantNodes()
                                      .Select(GetBoundNode <BoundColumnExpression>)
                                      .Where(n => n != null)
                                      .Select(b => b.Symbol)
                                      .OfType <TableColumnInstanceSymbol>()
                                      .Select(c => FindQueryState(c.TableInstance))
                                      .Distinct()
                                      .Take(2)
                                      .ToImmutableArray();

            if (affectedQueryScopes.Length > 1)
            {
                Diagnostics.ReportAggregateContainsColumnsFromDifferentQueries(aggregate.Span);
            }

            var queryState = affectedQueryScopes.DefaultIfEmpty(QueryState)
                             .First();

            if (queryState == null)
            {
                Diagnostics.ReportAggregateInvalidInCurrentContext(aggregate.Span);
            }
            else
            {
                var existingSlot = FindComputedValue(aggregate, queryState.ComputedAggregates);
                if (existingSlot == null)
                {
                    var slot = ValueSlotFactory.CreateTemporary(boundAggregate.Type);
                    queryState.ComputedAggregates.Add(new BoundComputedValueWithSyntax(aggregate, boundAggregate, slot));
                }
            }

            var aggregateBelongsToCurrentQuery = QueryState == queryState;

            if (InOnClause && aggregateBelongsToCurrentQuery)
            {
                Diagnostics.ReportAggregateInOn(aggregate.Span);
            }
            else if (InWhereClause && aggregateBelongsToCurrentQuery)
            {
                Diagnostics.ReportAggregateInWhere(aggregate.Span);
            }
            else if (InGroupByClause && aggregateBelongsToCurrentQuery)
            {
                Diagnostics.ReportAggregateInGroupBy(aggregate.Span);
            }
            else if (InAggregateArgument)
            {
                Diagnostics.ReportAggregateInAggregateArgument(aggregate.Span);
            }

            return(boundAggregate);
        }
示例#2
0
        private BoundExpression BindAllAnySubselect(TextSpan diagnosticSpan, ExpressionSyntax leftNode, bool isAll, QuerySyntax queryNode, BinaryOperatorKind operatorKind)
        {
            // TODO: Ensure query has no ORDER BY unless TOP is also specified

            // First, let's bind the expression and the query

            var left       = BindExpression(leftNode);
            var boundQuery = BindSubquery(queryNode);

            // The right hand side of the binary expression is the first column of the query.

            if (boundQuery.OutputColumns.Length == 0)
            {
                var outputValue = ValueSlotFactory.CreateTemporary(typeof(bool));
                return(new BoundValueSlotExpression(outputValue));
            }

            if (boundQuery.OutputColumns.Length > 1)
            {
                Diagnostics.ReportTooManyExpressionsInSelectListOfSubquery(queryNode.Span);
            }

            var rightColumn = boundQuery.OutputColumns[0];
            var right       = new BoundValueSlotExpression(rightColumn.ValueSlot);

            // Now we need to bind the binary operator.
            //
            // To avoid cascading errors, we'll only validate the operator
            // if we could resolve both sides.

            if (left.Type.IsError() || right.Type.IsError())
            {
                return(new BoundErrorExpression());
            }

            var result = LookupBinaryOperator(operatorKind, left.Type, right.Type);

            if (result.Best == null)
            {
                if (result.Selected == null)
                {
                    Diagnostics.ReportCannotApplyBinaryOperator(diagnosticSpan, operatorKind, left.Type, right.Type);
                }
                else
                {
                    Diagnostics.ReportAmbiguousBinaryOperator(diagnosticSpan, operatorKind, left.Type, right.Type);
                }
            }

            // We may need to convert the arguments to the binary operator, so let's
            // bind them as arguments to the resolved operator.

            var convertedLeft  = BindArgument(left, result, 0);
            var convertedRight = BindArgument(right, result, 1);

            // If we need to convert the right side, then we musy insert a BoundComputeRelation
            // that produces a derived value.

            BoundRelation relation;

            if (convertedRight == right)
            {
                relation = boundQuery.Relation;
            }
            else
            {
                var outputValue     = ValueSlotFactory.CreateTemporary(convertedRight.Type);
                var outputValues    = new[] { outputValue };
                var computedValue   = new BoundComputedValue(convertedRight, outputValue);
                var computedValues  = new[] { computedValue };
                var computeRelation = new BoundComputeRelation(boundQuery.Relation, computedValues);
                relation       = new BoundProjectRelation(computeRelation, outputValues);
                convertedRight = new BoundValueSlotExpression(outputValue);
            }

            // In order to simplify later phases, we'll rewrite the the ALL/ANY subselect into
            // a regular EXISTS subselect. ANY is fairly straight forward:
            //
            //      left op ANY (SELECT right FROM ...)
            //
            //      ===>
            //
            //      EXISTS (SELECT * FROM ... WHERE left op right)
            //
            // ALL requires a bit more trickery as we need to handle NULL values in the negated
            // predicate correctly:
            //
            //      left op ALL (SELECT Column FROM ...)
            //
            //      ===>
            //
            //      NOT EXISTS (SELECT * FROM ... WHERE NOT (left op right) OR (left IS NULL) OR (right IS NULL))

            if (!isAll)
            {
                var condition = new BoundBinaryExpression(convertedLeft, operatorKind, result, convertedRight);
                var filter    = new BoundFilterRelation(relation, condition);
                return(new BoundExistsSubselect(filter));
            }
            else
            {
                var comparison        = new BoundBinaryExpression(convertedLeft, operatorKind, result, right);
                var negatedComparison = BindUnaryExpression(diagnosticSpan, UnaryOperatorKind.LogicalNot, comparison);
                var leftIsNull        = new BoundIsNullExpression(convertedLeft);
                var rightisNull       = new BoundIsNullExpression(right);
                var eitherSideIsNull  = BindBinaryExpression(diagnosticSpan, BinaryOperatorKind.LogicalOr, leftIsNull, rightisNull);
                var condition         = BindBinaryExpression(diagnosticSpan, BinaryOperatorKind.LogicalOr, negatedComparison, eitherSideIsNull);
                var filter            = new BoundFilterRelation(relation, condition);
                var existsSubselect   = new BoundExistsSubselect(filter);
                return(BindUnaryExpression(diagnosticSpan, UnaryOperatorKind.LogicalNot, existsSubselect));
            }
        }