public void BuildSetOperationCombinedStatementsPart_UnionAll() { var setOperationCombinedStatement = new SetOperationCombinedStatement(SqlStatementModelObjectMother.CreateSqlStatement(), SetOperation.UnionAll); var sqlStatement = SqlStatementModelObjectMother.CreateMinimalSqlStatement( new SqlStatementBuilder { SetOperationCombinedStatements = { setOperationCombinedStatement } }); _stageMock.Expect(mock => mock.GenerateTextForSqlStatement(_commandBuilder, setOperationCombinedStatement.SqlStatement)) .WhenCalled(mi => ((SqlCommandBuilder)mi.Arguments[0]).Append("statement")); _stageMock.Replay(); _generator.BuildSetOperationCombinedStatementsPart(sqlStatement, _commandBuilder); Assert.That(_commandBuilder.GetCommandText(), Is.EqualTo(" UNION ALL (statement)")); _stageMock.VerifyAllExpectations(); }
public override void HandleResultOperator( TResultOperator resultOperator, SqlStatementBuilder sqlStatementBuilder, UniqueIdentifierGenerator generator, ISqlPreparationStage stage, ISqlPreparationContext context) { ArgumentUtility.CheckNotNull("resultOperator", resultOperator); ArgumentUtility.CheckNotNull("sqlStatementBuilder", sqlStatementBuilder); ArgumentUtility.CheckNotNull("generator", generator); ArgumentUtility.CheckNotNull("stage", stage); ArgumentUtility.CheckNotNull("context", context); UpdateDataInfo(resultOperator, sqlStatementBuilder, sqlStatementBuilder.DataInfo); var source2 = GetSource2(resultOperator); var preparedSubStatement = stage.PrepareResultOperatorItemExpression(source2, context) as SqlSubStatementExpression; if (preparedSubStatement == null) { var message = string.Format( "The '" + _operationName + "' operation is only supported for combining two query results, but a '{0}' was supplied as the " + "second sequence: {1}", source2.GetType().Name, source2); throw new NotSupportedException(message); } var combinedStatement = new SetOperationCombinedStatement(preparedSubStatement.SqlStatement, _setOperation); sqlStatementBuilder.SetOperationCombinedStatements.Add(combinedStatement); // The set operators act as an IQuerySource, i.e., subsequent result operators can refer to its output. // When a result operator references the set operator's output, it should simply refer to the outer statement's select projection instead. AddMappingForItemExpression(context, sqlStatementBuilder.DataInfo, sqlStatementBuilder.SelectProjection); // In SQL, the set operators does not allow the input sequences to contain an "ORDER BY". Therefore, we'll remove them, if any, unless a TOP // expression is specified. if (sqlStatementBuilder.Orderings.Any() && sqlStatementBuilder.TopExpression == null) { sqlStatementBuilder.Orderings.Clear(); } // For the second source, removal of unneeded orderings is already performed by PrepareResultOperatorItemExpression. Assertion.DebugAssert(!combinedStatement.SqlStatement.Orderings.Any() || combinedStatement.SqlStatement.TopExpression != null); // However, if an ORDER BY _is_ included together with a TOP, then the ORDER BY is allowed again as long as the whole set-combined statement is // moved to a substatement. // I.e., this is invalid: // SELECT [t0].[ID] AS [value] FROM [CookTable] AS [t0] WHERE ([t0].[FirstName] = 'Hugo') // UNION (SELECT TOP (2) [t1].[ID] AS [value] FROM [CookTable] AS [t1] WHERE ([t1].[Name] = 'Boss') ORDER BY [t1].[ID] ASC) // but this is valid: // SELECT * FROM // ( // SELECT [t0].[ID] AS [value] FROM [CookTable] AS [t0] WHERE ([t0].[FirstName] = 'Hugo') // UNION (SELECT TOP (2) [t1].[ID] AS [value] FROM [CookTable] AS [t1] WHERE ([t1].[Name] = 'Boss') ORDER BY [t1].[ID] ASC) // ) AS q0 if (sqlStatementBuilder.Orderings.Any() || combinedStatement.SqlStatement.Orderings.Any()) { MoveCurrentStatementToSqlTable( sqlStatementBuilder, context, ti => new SqlTable(ti, JoinSemantics.Inner), stage, OrderingExtractionPolicy.DoNotExtractOrderings); } }