protected virtual void VisitComputeRelation(BoundComputeRelation node)
        {
            VisitRelation(node.Input);

            foreach (var computedValue in node.DefinedValues)
            {
                VisitExpression(computedValue.Expression);
            }
        }
Example #2
0
 protected virtual BoundRelation RewriteComputeRelation(BoundComputeRelation node)
 {
     return(node.Update(RewriteRelation(node.Input),
                        RewriteComputedValues(node.DefinedValues)));
 }
Example #3
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));
            }
        }