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); }
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)); } }