public override ICollection<CodegenExpression> AdditionalParams(
            CodegenMethod method,
            SAIFFInitializeSymbol symbols,
            CodegenClassScope classScope)
        {
            var forges = QueryGraphValueEntryHashKeyedForge.GetForges(HashKeys);
            var types = ExprNodeUtilityQuery.GetExprResultTypes(forges);

            // we take coercion types from the index plan as the index plan is always accurate but not always available (for tables it is not)
            Type[] coercionTypes;
            var indexForge = _indexSpecs.Items.Get(IndexNum[0]);
            if (indexForge != null) {
                coercionTypes = indexForge.HashTypes;
            }
            else {
                coercionTypes = _optionalCoercionTypes;
            }

            CodegenExpression getter;
            EventType eventType = typesPerStream[LookupStream];
            EventPropertyGetterSPI[] getterSPIS = QueryGraphValueEntryHashKeyedForge.GetGettersIfPropsOnly(_hashKeys);
            if (indexForge != null) {
                if (getterSPIS != null) {
                    getter = MultiKeyCodegen.CodegenGetterMayMultiKey(
                        eventType,
                        getterSPIS,
                        types,
                        coercionTypes,
                        indexForge.HashMultiKeyClasses,
                        method,
                        classScope);
                }
                else {
                    getter = MultiKeyCodegen.CodegenExprEvaluatorMayMultikey(forges, coercionTypes, indexForge.HashMultiKeyClasses, method, classScope);
                }
            }
            else {
                if (getterSPIS != null) {
                    getter = MultiKeyCodegen.CodegenGetterMayMultiKey(
                        eventType,
                        getterSPIS,
                        types,
                        coercionTypes,
                        _optionalEPLTableLookupMultiKey,
                        method,
                        classScope);
                }
                else {
                    getter = MultiKeyCodegen.CodegenExprEvaluatorMayMultikey(forges, coercionTypes, _optionalEPLTableLookupMultiKey, method, classScope);
                }
            }

            return Collections.SingletonList(getter);
        }
        public override ICollection<CodegenExpression> AdditionalParams(
            CodegenMethod method,
            SAIFFInitializeSymbol symbols,
            CodegenClassScope classScope)
        {
            var hashGetter = ConstantNull();
            if (!_hashKeys.IsEmpty()) {
                var indexForge = _indexSpecs.Items.Get(IndexNum[0]);
                var forges = QueryGraphValueEntryHashKeyedForge.GetForges(_hashKeys.ToArray());
                if (indexForge != null) {
                    hashGetter = MultiKeyCodegen.CodegenExprEvaluatorMayMultikey(
                        forges,
                        _hashCoercionTypes,
                        indexForge.HashMultiKeyClasses,
                        method,
                        classScope);
                } else {
                    hashGetter = MultiKeyCodegen.CodegenExprEvaluatorMayMultikey(
                        forges,
                        _hashCoercionTypes,
                        _optionalEPLTableLookupMultiKey,
                        method,
                        classScope);
                }
            }

            var rangeGetters = method.MakeChild(typeof(QueryGraphValueEntryRange[]), GetType(), classScope);
            rangeGetters.Block.DeclareVar<QueryGraphValueEntryRange[]>(
                "rangeGetters",
                NewArrayByLength(typeof(QueryGraphValueEntryRange), Constant(_rangeKeyPairs.Count)));
            for (var i = 0; i < _rangeKeyPairs.Count; i++) {
                var optCoercionType = _optRangeCoercionTypes == null ? null : _optRangeCoercionTypes[i];
                rangeGetters.Block.AssignArrayElement(
                    Ref("rangeGetters"),
                    Constant(i),
                    _rangeKeyPairs[i].Make(optCoercionType, rangeGetters, symbols, classScope));
            }

            rangeGetters.Block.MethodReturn(Ref("rangeGetters"));

            return Arrays.AsList(hashGetter, LocalMethod(rangeGetters));
        }
        /// <summary>
        ///     Constructs indexing and lookup strategy for a given relationship that a historical stream may have with another
        ///     stream (historical or not) that looks up into results of a poll of a historical stream.
        ///     <para />
        ///     The term "polled" refers to the assumed-historical stream.
        /// </summary>
        /// <param name="queryGraph">relationship representation of where-clause filter and outer join on-expressions</param>
        /// <param name="polledViewType">the event type of the historical that is indexed</param>
        /// <param name="streamViewType">the event type of the stream looking up in indexes</param>
        /// <param name="polledViewStreamNum">the stream number of the historical that is indexed</param>
        /// <param name="streamViewStreamNum">the stream number of the historical that is looking up</param>
        /// <param name="raw"></param>
        /// <param name="serdeResolver"></param>
        /// <returns>indexing and lookup strategy pair</returns>
        public static JoinSetComposerPrototypeHistoricalDesc DetermineIndexing(
            QueryGraphForge queryGraph,
            EventType polledViewType,
            EventType streamViewType,
            int polledViewStreamNum,
            int streamViewStreamNum,
            StatementRawInfo raw,
            SerdeCompileTimeResolver serdeResolver)
        {
            var queryGraphValue = queryGraph.GetGraphValue(streamViewStreamNum, polledViewStreamNum);
            var hashKeysAndIndes = queryGraphValue.HashKeyProps;
            var rangeKeysAndIndex = queryGraphValue.RangeProps;

            // index and key property names
            var hashKeys = hashKeysAndIndes.Keys;
            var hashIndexes = hashKeysAndIndes.Indexed;
            var rangeKeys = rangeKeysAndIndex.Keys;
            var rangeIndexes = rangeKeysAndIndex.Indexed;

            // If the analysis revealed no join columns, must use the brute-force full table scan
            if (hashKeys.IsEmpty() && rangeKeys.IsEmpty()) {
                var inKeywordSingles = queryGraphValue.InKeywordSingles;
                if (inKeywordSingles != null && inKeywordSingles.Indexed.Length != 0) {
                    var indexed = inKeywordSingles.Indexed[0];
                    var lookup = inKeywordSingles.Key[0];
                    var strategy =
                        new HistoricalIndexLookupStrategyInKeywordSingleForge(streamViewStreamNum, lookup.KeyExprs);
                    var indexing = new PollResultIndexingStrategyHashForge(
                        polledViewStreamNum,
                        polledViewType,
                        new string[]{ indexed },
                        null,
                        null);
                    return new JoinSetComposerPrototypeHistoricalDesc(strategy, indexing, EmptyList<StmtClassForgeableFactory>.Instance);
                }

                var multis = queryGraphValue.InKeywordMulti;
                if (!multis.IsEmpty()) {
                    var multi = multis[0];
                    var strategy =
                        new HistoricalIndexLookupStrategyInKeywordMultiForge(streamViewStreamNum, multi.Key.KeyExpr);
                    var indexing =
                        new PollResultIndexingStrategyInKeywordMultiForge(
                            polledViewStreamNum,
                            polledViewType,
                            ExprNodeUtilityQuery.GetIdentResolvedPropertyNames(multi.Indexed));
                    return new JoinSetComposerPrototypeHistoricalDesc(strategy, indexing, EmptyList<StmtClassForgeableFactory>.Instance);
                }

                return new JoinSetComposerPrototypeHistoricalDesc(
                    HistoricalIndexLookupStrategyNoIndexForge.INSTANCE,
                    PollResultIndexingStrategyNoIndexForge.INSTANCE,
                    EmptyList<StmtClassForgeableFactory>.Instance);
            }

            CoercionDesc keyCoercionTypes = CoercionUtil.GetCoercionTypesHash(
                new[] {streamViewType, polledViewType},
                0,
                1,
                hashKeys,
                hashIndexes);

            if (rangeKeys.IsEmpty()) {
                var hashEvals = QueryGraphValueEntryHashKeyedForge.GetForges(hashKeys.ToArray());
                var multiKeyPlan = MultiKeyPlanner.PlanMultiKey(hashEvals, false, raw, serdeResolver);
                var lookup = new HistoricalIndexLookupStrategyHashForge(
                    streamViewStreamNum, hashEvals, keyCoercionTypes.CoercionTypes, multiKeyPlan.ClassRef);
                var indexing = new PollResultIndexingStrategyHashForge(
                    polledViewStreamNum,
                    polledViewType,
                    hashIndexes,
                    keyCoercionTypes.CoercionTypes,
                    multiKeyPlan.ClassRef);
                return new JoinSetComposerPrototypeHistoricalDesc(lookup, indexing, multiKeyPlan.MultiKeyForgeables);
            }

            CoercionDesc rangeCoercionTypes = CoercionUtil.GetCoercionTypesRange(
                new[] {streamViewType, polledViewType},
                1,
                rangeIndexes,
                rangeKeys);

            if (rangeKeys.Count == 1 && hashKeys.Count == 0) {
                var rangeCoercionType = rangeCoercionTypes.CoercionTypes[0];
                var indexing = new PollResultIndexingStrategySortedForge(
                    polledViewStreamNum,
                    polledViewType,
                    rangeIndexes[0],
                    rangeCoercionType);
                var lookup = new HistoricalIndexLookupStrategySortedForge(
                    streamViewStreamNum,
                    rangeKeys[0],
                    rangeCoercionType);
                return new JoinSetComposerPrototypeHistoricalDesc(lookup, indexing, EmptyList<StmtClassForgeableFactory>.Instance);
            }
            else {
                var hashEvals = QueryGraphValueEntryHashKeyedForge.GetForges(hashKeys.ToArray());
                var multiKeyPlan = MultiKeyPlanner.PlanMultiKey(hashEvals, false, raw, serdeResolver);
                var strategy = new HistoricalIndexLookupStrategyCompositeForge(
                    streamViewStreamNum,
                    hashEvals,
                    multiKeyPlan.ClassRef,
                    rangeKeys.ToArray());
                var indexing = new PollResultIndexingStrategyCompositeForge(
                    polledViewStreamNum,
                    polledViewType,
                    hashIndexes,
                    keyCoercionTypes.CoercionTypes,
                    multiKeyPlan.ClassRef,
                    rangeIndexes,
                    rangeCoercionTypes.CoercionTypes);
                return new JoinSetComposerPrototypeHistoricalDesc(strategy, indexing, multiKeyPlan.MultiKeyForgeables);
            }
        }