public JoinSetComposerDesc Create(
            Viewable[] streamViews,
            bool isFireAndForget,
            AgentInstanceContext agentInstanceContext)
        {
            var queryStrategies = new QueryStrategy[_streamTypes.Length];

            var viewable = (HistoricalEventViewable)streamViews[_polledViewNum];
            var outerJoinEqualsNodeEval = _outerJoinEqualsNode == null
                ? null
                : _outerJoinEqualsNode.ExprEvaluator;

            queryStrategies[_streamViewNum] = new HistoricalDataQueryStrategy(
                _streamViewNum, _polledViewNum, viewable, _isOuterJoin, outerJoinEqualsNodeEval,
                _indexStrategies.First, _indexStrategies.Second);

            // for strictly historical joins, create a query strategy for the non-subordinate historical view
            if (_isAllHistoricalNoSubordinate)
            {
                var isOuterJoin = false;
                if (_outerJoinDescList.Length > 0)
                {
                    var outerJoinDesc = _outerJoinDescList[0];
                    if (outerJoinDesc.OuterJoinType.Equals(OuterJoinType.FULL))
                    {
                        isOuterJoin = true;
                    }
                    else if ((outerJoinDesc.OuterJoinType.Equals(OuterJoinType.LEFT)) &&
                             (_polledViewNum == 0))
                    {
                        isOuterJoin = true;
                    }
                    else if ((outerJoinDesc.OuterJoinType.Equals(OuterJoinType.RIGHT)) &&
                             (_polledViewNum == 1))
                    {
                        isOuterJoin = true;
                    }
                }
                viewable = (HistoricalEventViewable)streamViews[_streamViewNum];
                queryStrategies[_polledViewNum] = new HistoricalDataQueryStrategy(
                    _polledViewNum, _streamViewNum, viewable, isOuterJoin, outerJoinEqualsNodeEval,
                    new HistoricalIndexLookupStrategyNoIndex(), new PollResultIndexingStrategyNoIndex());
            }

            JoinSetComposer composer = new JoinSetComposerHistoricalImpl(
                null, queryStrategies, streamViews, _exprEvaluatorContext);
            var postJoinEval = _optionalFilterNode == null ? null : _optionalFilterNode.ExprEvaluator;

            return(new JoinSetComposerDesc(composer, postJoinEval));
        }
        public JoinSetComposerDesc Create(Viewable[] streamViews, bool isFireAndForget, AgentInstanceContext agentInstanceContext)
        {
            // Build indexes
            var indexesPerStream         = new IDictionary <TableLookupIndexReqKey, EventTable> [_indexSpecs.Length];
            var tableSecondaryIndexLocks = new ILockable[_indexSpecs.Length];
            var hasTable = false;

            for (var streamNo = 0; streamNo < _indexSpecs.Length; streamNo++)
            {
                if (_indexSpecs[streamNo] == null)
                {
                    continue;
                }

                var items = _indexSpecs[streamNo].Items;
                indexesPerStream[streamNo] = new LinkedHashMap <TableLookupIndexReqKey, EventTable>();

                if (_streamJoinAnalysisResult.TablesPerStream[streamNo] != null)
                {
                    // build for tables
                    var metadata = _streamJoinAnalysisResult.TablesPerStream[streamNo];
                    var state    = _tableService.GetState(metadata.TableName, agentInstanceContext.AgentInstanceId);
                    foreach (var indexName in state.SecondaryIndexes)       // add secondary indexes
                    {
                        var eventTable = state.GetIndex(indexName);
                        indexesPerStream[streamNo].Put(new TableLookupIndexReqKey(indexName, metadata.TableName), eventTable);
                    }
                    var index = state.GetIndex(metadata.TableName);     // add primary index
                    indexesPerStream[streamNo].Put(new TableLookupIndexReqKey(metadata.TableName, metadata.TableName), index);
                    hasTable = true;
                    tableSecondaryIndexLocks[streamNo] = agentInstanceContext.StatementContext.IsWritesToTables
                        ? state.TableLevelRWLock.WriteLock
                        : state.TableLevelRWLock.ReadLock;
                }
                else
                {
                    // build tables for implicit indexes
                    foreach (var entry in items)
                    {
                        EventTable index;
                        if (_streamJoinAnalysisResult.ViewExternal[streamNo] != null)
                        {
                            var view = _streamJoinAnalysisResult.ViewExternal[streamNo].Invoke(agentInstanceContext);
                            index = view.GetJoinIndexTable(items.Get(entry.Key));
                        }
                        else
                        {
                            index = EventTableUtil.BuildIndex(streamNo, items.Get(entry.Key), _streamTypes[streamNo], false, entry.Value.IsUnique, null);
                        }
                        indexesPerStream[streamNo].Put(entry.Key, index);
                    }
                }
            }

            // obtain any external views
            var externalViewProviders = _streamJoinAnalysisResult.ViewExternal;
            var externalViews         = new VirtualDWView[externalViewProviders.Length];

            for (var i = 0; i < externalViews.Length; i++)
            {
                if (externalViewProviders[i] != null)
                {
                    externalViews[i] = _streamJoinAnalysisResult.ViewExternal[i].Invoke(agentInstanceContext);
                }
            }

            // Build strategies
            var queryExecSpecs  = _queryPlan.ExecNodeSpecs;
            var queryStrategies = new QueryStrategy[queryExecSpecs.Length];

            for (var i = 0; i < queryExecSpecs.Length; i++)
            {
                var planNode = queryExecSpecs[i];
                if (planNode == null)
                {
                    Log.Debug(".makeComposer No execution node for stream " + i + " '" + _streamNames[i] + "'");
                    continue;
                }

                var executionNode = planNode.MakeExec(_statementName, _statementId, _annotations, indexesPerStream, _streamTypes, streamViews, _historicalStreamIndexLists, externalViews, tableSecondaryIndexLocks);

                if (Log.IsDebugEnabled)
                {
                    Log.Debug(".makeComposer Execution nodes for stream " + i + " '" + _streamNames[i] +
                              "' : \n" + ExecNode.Print(executionNode));
                }

                queryStrategies[i] = new ExecNodeQueryStrategy(i, _streamTypes.Length, executionNode);
            }

            // Remove indexes that are from tables as these are only available to query strategies
            if (hasTable)
            {
                indexesPerStream = RemoveTableIndexes(indexesPerStream, _streamJoinAnalysisResult.TablesPerStream);
            }

            // If this is not unidirectional and not a self-join (excluding self-outer-join)
            JoinSetComposerDesc joinSetComposerDesc;

            if ((!_streamJoinAnalysisResult.IsUnidirectional) &&
                (!_streamJoinAnalysisResult.IsPureSelfJoin || _outerJoinDescList.Length > 0))
            {
                JoinSetComposer composer;
                if (_historicalViewableDesc.HasHistorical)
                {
                    composer = new JoinSetComposerHistoricalImpl(indexesPerStream, queryStrategies, streamViews, _exprEvaluatorContext);
                }
                else
                {
                    if (isFireAndForget)
                    {
                        composer = new JoinSetComposerFAFImpl(indexesPerStream, queryStrategies, _streamJoinAnalysisResult.IsPureSelfJoin, _exprEvaluatorContext, _joinRemoveStream, _isOuterJoins);
                    }
                    else
                    {
                        composer = new JoinSetComposerImpl(indexesPerStream, queryStrategies, _streamJoinAnalysisResult.IsPureSelfJoin, _exprEvaluatorContext, _joinRemoveStream);
                    }
                }

                // rewrite the filter expression for all-inner joins in case "on"-clause outer join syntax was used to include those expressions
                var filterExpression = GetFilterExpressionInclOnClause(_optionalFilterNode, _outerJoinDescList);

                var postJoinEval = filterExpression == null ? null : filterExpression.ExprEvaluator;
                joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval);
            }
            else
            {
                QueryStrategy driver;
                int           unidirectionalStream;
                if (_streamJoinAnalysisResult.UnidirectionalStreamNumber != -1)
                {
                    unidirectionalStream = _streamJoinAnalysisResult.UnidirectionalStreamNumber;
                    driver = queryStrategies[unidirectionalStream];
                }
                else
                {
                    unidirectionalStream = 0;
                    driver = queryStrategies[0];
                }

                JoinSetComposer composer = new JoinSetComposerStreamToWinImpl(indexesPerStream, _streamJoinAnalysisResult.IsPureSelfJoin,
                                                                              unidirectionalStream, driver, _streamJoinAnalysisResult.UnidirectionalNonDriving);
                var postJoinEval = _optionalFilterNode == null ? null : _optionalFilterNode.ExprEvaluator;
                joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval);
            }

            // compile prior events per stream to preload any indexes
            var eventsPerStream = new EventBean[_streamNames.Length][];
            var events          = new List <EventBean>();

            for (var i = 0; i < eventsPerStream.Length; i++)
            {
                // For named windows and tables, we don't need to preload indexes from the iterators as this is always done already
                if (_streamJoinAnalysisResult.NamedWindow[i] || _streamJoinAnalysisResult.TablesPerStream[i] != null)
                {
                    continue;
                }

                IEnumerator <EventBean> en = null;
                if (!(streamViews[i] is HistoricalEventViewable) && !(streamViews[i] is DerivedValueView))
                {
                    try
                    {
                        en = streamViews[i].GetEnumerator();
                    }
                    catch (UnsupportedOperationException)
                    {
                        // Joins do not support the iterator
                    }
                }

                if (en != null)
                {
                    while (en.MoveNext())
                    {
                        events.Add(en.Current);
                    }
                    eventsPerStream[i] = events.ToArray();
                    events.Clear();
                }
                else
                {
                    eventsPerStream[i] = new EventBean[0];
                }
            }

            // init
            joinSetComposerDesc.JoinSetComposer.Init(eventsPerStream);

            return(joinSetComposerDesc);
        }