public override void Attach(
            EventType parentEventType,
            int streamNumber,
            ViewForgeEnv viewForgeEnv)
        {
            var validated = ViewForgeSupport.Validate(
                ViewName,
                parentEventType,
                viewParameters,
                true,
                viewForgeEnv,
                streamNumber);
            if (viewParameters.Count != 2) {
                throw new ViewParameterException(ViewParamMessage);
            }

            if (!validated[0].Forge.EvaluationType.IsNumeric()) {
                throw new ViewParameterException(ViewParamMessage);
            }

            timestampExpression = validated[0];
            ViewForgeSupport.AssertReturnsNonConstant(ViewName, validated[0], 0);

            timePeriodComputeForge = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory(
                ViewName,
                viewParameters[1],
                ViewParamMessage,
                1,
                viewForgeEnv,
                streamNumber);
            eventType = parentEventType;
        }
예제 #2
0
 public RowRecogDescForge(
     EventType parentEventType,
     EventType rowEventType,
     EventType compositeEventType,
     EventType multimatchEventType,
     int[] multimatchStreamNumToVariable,
     int[] multimatchVariableToStreamNum,
     ExprNode[] partitionBy,
     MultiKeyClassRef partitionByMultiKey,
     LocalMap variableStreams,
     bool hasInterval,
     bool iterateOnly,
     bool unbound,
     bool orTerminated,
     bool collectMultimatches,
     bool defineAsksMultimatches,
     int numEventsEventsPerStreamDefine,
     string[] multimatchVariablesArray,
     RowRecogNFAStateForge[] startStates,
     RowRecogNFAStateForge[] allStates,
     bool allMatches,
     MatchRecognizeSkipEnum skip,
     ExprNode[] columnEvaluators,
     string[] columnNames,
     TimePeriodComputeForge intervalCompute,
     int[] previousRandomAccessIndexes,
     AggregationServiceForgeDesc[] aggregationServices)
 {
     this._parentEventType = parentEventType;
     RowEventType = rowEventType;
     this._compositeEventType = compositeEventType;
     this._multimatchEventType = multimatchEventType;
     this._multimatchStreamNumToVariable = multimatchStreamNumToVariable;
     this._multimatchVariableToStreamNum = multimatchVariableToStreamNum;
     this._partitionBy = partitionBy;
     this._partitionByMultiKey = partitionByMultiKey;
     this._variableStreams = variableStreams;
     this._hasInterval = hasInterval;
     this._iterateOnly = iterateOnly;
     this._unbound = unbound;
     this._orTerminated = orTerminated;
     this._collectMultimatches = collectMultimatches;
     this._defineAsksMultimatches = defineAsksMultimatches;
     this._numEventsEventsPerStreamDefine = numEventsEventsPerStreamDefine;
     this._multimatchVariablesArray = multimatchVariablesArray;
     this._startStates = startStates;
     this._allStates = allStates;
     this._allMatches = allMatches;
     this._skip = skip;
     this._columnEvaluators = columnEvaluators;
     this._columnNames = columnNames;
     this._intervalCompute = intervalCompute;
     this._previousRandomAccessIndexes = previousRandomAccessIndexes;
     this._aggregationServices = aggregationServices;
 }
예제 #3
0
        public override void Attach(
            EventType parentEventType,
            int streamNumber,
            ViewForgeEnv viewForgeEnv)
        {
            var windowName = ViewName;
            var validated = ViewForgeSupport.Validate(
                windowName,
                parentEventType,
                viewParameters,
                true,
                viewForgeEnv,
                streamNumber);
            if (viewParameters.Count < 2 || viewParameters.Count > 3) {
                throw new ViewParameterException(ViewParamMessage);
            }

            // validate first parameter: timestamp expression
            if (!validated[0].Forge.EvaluationType.IsNumeric()) {
                throw new ViewParameterException(ViewParamMessage);
            }

            timestampExpression = validated[0];
            ViewForgeSupport.AssertReturnsNonConstant(windowName, validated[0], 0);

            timePeriodComputeForge = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory(
                ViewName,
                viewParameters[1],
                ViewParamMessage,
                1,
                viewForgeEnv,
                streamNumber);

            // validate optional parameters
            if (validated.Length == 3) {
                var constant = ViewForgeSupport.ValidateAndEvaluate(
                    windowName,
                    validated[2],
                    viewForgeEnv,
                    streamNumber);
                if (!constant.IsNumber() || constant.IsFloatingPointNumber()) {
                    throw new ViewParameterException(
                        "Externally-timed batch view requires a Long-typed reference point in msec as a third parameter");
                }

                optionalReferencePoint = constant.AsInt64();
            }

            eventType = parentEventType;
        }
예제 #4
0
        public override void SetViewParameters(
            IList<ExprNode> parameters,
            ViewForgeEnv viewForgeEnv,
            int streamNumber)
        {
            if (parameters.Count != 1) {
                throw new ViewParameterException(ViewParamMessage);
            }

            timePeriodCompute = ViewFactoryTimePeriodHelper.ValidateAndEvaluateTimeDeltaFactory(
                ViewName,
                parameters[0],
                ViewParamMessage,
                0,
                viewForgeEnv,
                streamNumber);
        }
예제 #5
0
        private static void RecursiveCompile(
            EvalForgeNode evalNode,
            ISet<string> eventTypeReferences,
            bool isInsertInto,
            MatchEventSpec tags,
            Stack<EvalForgeNode> parentNodeStack,
            ISet<string> allTagNamesOrdered,
            int streamNum,
            StatementRawInfo statementRawInfo,
            StatementCompileTimeServices services)
        {
            parentNodeStack.Push(evalNode);
            foreach (var child in evalNode.ChildNodes) {
                RecursiveCompile(
                    child,
                    eventTypeReferences,
                    isInsertInto,
                    tags,
                    parentNodeStack,
                    allTagNamesOrdered,
                    streamNum,
                    statementRawInfo,
                    services);
            }

            parentNodeStack.Pop();

            IDictionary<string, Pair<EventType, string>> newTaggedEventTypes = null;
            IDictionary<string, Pair<EventType, string>> newArrayEventTypes = null;

            if (evalNode is EvalFilterForgeNode) {
                var filterNode = (EvalFilterForgeNode) evalNode;
                var eventName = filterNode.RawFilterSpec.EventTypeName;
                if (services.TableCompileTimeResolver.Resolve(eventName) != null) {
                    throw new ExprValidationException("Tables cannot be used in pattern filter atoms");
                }

                var resolvedEventType = ResolveTypeName(eventName, services.EventTypeCompileTimeResolver);
                var finalEventType = resolvedEventType;
                var optionalTag = filterNode.EventAsName;
                var isPropertyEvaluation = false;
                var isParentMatchUntil = IsParentMatchUntil(evalNode, parentNodeStack);

                // obtain property event type, if final event type is properties
                if (filterNode.RawFilterSpec.OptionalPropertyEvalSpec != null) {
                    var optionalPropertyEvaluator = PropertyEvaluatorForgeFactory.MakeEvaluator(
                        filterNode.RawFilterSpec.OptionalPropertyEvalSpec,
                        resolvedEventType,
                        filterNode.EventAsName,
                        statementRawInfo,
                        services);
                    finalEventType = optionalPropertyEvaluator.FragmentEventType;
                    isPropertyEvaluation = true;
                }

                // If a tag was supplied for the type, the tags must stay with this type, i.e. a=BeanA -> b=BeanA -> a=BeanB is a no
                if (optionalTag != null) {
                    var pair = tags.TaggedEventTypes.Get(optionalTag);
                    EventType existingType = null;
                    if (pair != null) {
                        existingType = pair.First;
                    }

                    if (existingType == null) {
                        pair = tags.ArrayEventTypes.Get(optionalTag);
                        if (pair != null) {
                            throw new ExprValidationException(
                                "Tag '" +
                                optionalTag +
                                "' for event '" +
                                eventName +
                                "' used in the repeat-until operator cannot also appear in other filter expressions");
                        }
                    }

                    if (existingType != null && existingType != finalEventType) {
                        throw new ExprValidationException(
                            "Tag '" +
                            optionalTag +
                            "' for event '" +
                            eventName +
                            "' has already been declared for events of type " +
                            existingType.UnderlyingType.Name);
                    }

                    pair = new Pair<EventType, string>(finalEventType, eventName);

                    // add tagged type
                    if (isPropertyEvaluation || isParentMatchUntil) {
                        newArrayEventTypes = new LinkedHashMap<string, Pair<EventType, string>>();
                        newArrayEventTypes.Put(optionalTag, pair);
                    }
                    else {
                        newTaggedEventTypes = new LinkedHashMap<string, Pair<EventType, string>>();
                        newTaggedEventTypes.Put(optionalTag, pair);
                    }
                }

                // For this filter, filter types are all known tags at this time,
                // and additionally stream 0 (self) is our event type.
                // Stream type service allows resolution by property name event if that name appears in other tags.
                // by defaulting to stream zero.
                // Stream zero is always the current event type, all others follow the order of the map (stream 1 to N).
                var selfStreamName = optionalTag;
                if (selfStreamName == null) {
                    selfStreamName = "s_" + UuidGenerator.Generate();
                }

                var filterTypes = new LinkedHashMap<string, Pair<EventType, string>>();
                var typePair = new Pair<EventType, string>(finalEventType, eventName);
                filterTypes.Put(selfStreamName, typePair);
                filterTypes.PutAll(tags.TaggedEventTypes);

                // for the filter, specify all tags used
                var filterTaggedEventTypes = new LinkedHashMap<string, Pair<EventType, string>>(tags.TaggedEventTypes);
                filterTaggedEventTypes.Remove(optionalTag);

                // handle array tags (match-until clause)
                IDictionary<string, Pair<EventType, string>> arrayCompositeEventTypes = null;
                if (tags.ArrayEventTypes != null && !tags.ArrayEventTypes.IsEmpty()) {
                    arrayCompositeEventTypes = new LinkedHashMap<string, Pair<EventType, string>>();

                    foreach (var entry in tags.ArrayEventTypes) {
                        var specificArrayType = new LinkedHashMap<string, Pair<EventType, string>>();
                        specificArrayType.Put(entry.Key, entry.Value);

                        var eventTypeName = services.EventTypeNameGeneratorStatement.GetAnonymousPatternNameWTag(
                            streamNum,
                            evalNode.FactoryNodeId,
                            entry.Key);
                        var mapProps = GetMapProperties(
                            Collections.GetEmptyMap<string, Pair<EventType, string>>(),
                            specificArrayType);
                        var metadata = new EventTypeMetadata(
                            eventTypeName,
                            statementRawInfo.ModuleName,
                            EventTypeTypeClass.PATTERNDERIVED,
                            EventTypeApplicationType.MAP,
                            NameAccessModifier.TRANSIENT,
                            EventTypeBusModifier.NONBUS,
                            false,
                            EventTypeIdPair.Unassigned());
                        var mapEventType = BaseNestableEventUtil.MakeMapTypeCompileTime(
                            metadata,
                            mapProps,
                            null,
                            null,
                            null,
                            null,
                            services.BeanEventTypeFactoryPrivate,
                            services.EventTypeCompileTimeResolver);
                        services.EventTypeCompileTimeRegistry.NewType(mapEventType);

                        var tag = entry.Key;
                        if (!filterTypes.ContainsKey(tag)) {
                            var pair = new Pair<EventType, string>(mapEventType, tag);
                            filterTypes.Put(tag, pair);
                            arrayCompositeEventTypes.Put(tag, pair);
                        }
                    }
                }

                StreamTypeService streamTypeService = new StreamTypeServiceImpl(filterTypes, true, false);
                var exprNodes = filterNode.RawFilterSpec.FilterExpressions;

                var spec = FilterSpecCompiler.MakeFilterSpec(
                    resolvedEventType,
                    eventName,
                    exprNodes,
                    filterNode.RawFilterSpec.OptionalPropertyEvalSpec,
                    filterTaggedEventTypes,
                    arrayCompositeEventTypes,
                    streamTypeService,
                    null,
                    statementRawInfo,
                    services);
                filterNode.FilterSpec = spec;
            }
            else if (evalNode is EvalObserverForgeNode) {
                var observerNode = (EvalObserverForgeNode) evalNode;
                try {
                    var observerForge =
                        services.PatternResolutionService.Create(observerNode.PatternObserverSpec);

                    var streamTypeService = GetStreamTypeService(
                        tags.TaggedEventTypes,
                        tags.ArrayEventTypes,
                        observerNode,
                        streamNum,
                        statementRawInfo,
                        services);
                    var validationContext = new ExprValidationContextBuilder(
                        streamTypeService,
                        statementRawInfo,
                        services).Build();
                    var validated = ValidateExpressions(
                        ExprNodeOrigin.PATTERNOBSERVER,
                        observerNode.PatternObserverSpec.ObjectParameters,
                        validationContext);

                    var convertor = new MatchedEventConvertorForge(
                        tags.TaggedEventTypes,
                        tags.ArrayEventTypes,
                        allTagNamesOrdered);

                    observerNode.ObserverFactory = observerForge;
                    observerForge.SetObserverParameters(validated, convertor, validationContext);
                }
                catch (ObserverParameterException e) {
                    throw new ExprValidationException(
                        "Invalid parameter for pattern observer '" +
                        observerNode.ToPrecedenceFreeEPL() +
                        "': " +
                        e.Message,
                        e);
                }
                catch (PatternObjectException e) {
                    throw new ExprValidationException(
                        "Failed to resolve pattern observer '" + observerNode.ToPrecedenceFreeEPL() + "': " + e.Message,
                        e);
                }
            }
            else if (evalNode is EvalGuardForgeNode) {
                var guardNode = (EvalGuardForgeNode) evalNode;
                try {
                    var guardForge = services.PatternResolutionService.Create(guardNode.PatternGuardSpec);

                    var streamTypeService = GetStreamTypeService(
                        tags.TaggedEventTypes,
                        tags.ArrayEventTypes,
                        guardNode,
                        streamNum,
                        statementRawInfo,
                        services);
                    var validationContext = new ExprValidationContextBuilder(
                        streamTypeService,
                        statementRawInfo,
                        services).Build();
                    var validated = ValidateExpressions(
                        ExprNodeOrigin.PATTERNGUARD,
                        guardNode.PatternGuardSpec.ObjectParameters,
                        validationContext);

                    var convertor = new MatchedEventConvertorForge(
                        tags.TaggedEventTypes,
                        tags.ArrayEventTypes,
                        allTagNamesOrdered);

                    guardNode.GuardForge = guardForge;
                    guardForge.SetGuardParameters(validated, convertor, services);
                }
                catch (GuardParameterException e) {
                    throw new ExprValidationException(
                        "Invalid parameter for pattern guard '" + guardNode.ToPrecedenceFreeEPL() + "': " + e.Message,
                        e);
                }
                catch (PatternObjectException e) {
                    throw new ExprValidationException(
                        "Failed to resolve pattern guard '" + guardNode.ToPrecedenceFreeEPL() + "': " + e.Message,
                        e);
                }
            }
            else if (evalNode is EvalEveryDistinctForgeNode) {
                var distinctNode = (EvalEveryDistinctForgeNode) evalNode;
                var matchEventFromChildNodes = AnalyzeMatchEvent(distinctNode);
                var streamTypeService = GetStreamTypeService(
                    matchEventFromChildNodes.TaggedEventTypes,
                    matchEventFromChildNodes.ArrayEventTypes,
                    distinctNode,
                    streamNum,
                    statementRawInfo,
                    services);
                var validationContext =
                    new ExprValidationContextBuilder(streamTypeService, statementRawInfo, services).Build();
                IList<ExprNode> validated;
                try {
                    validated = ValidateExpressions(
                        ExprNodeOrigin.PATTERNEVERYDISTINCT,
                        distinctNode.Expressions,
                        validationContext);
                }
                catch (ExprValidationPropertyException ex) {
                    throw new ExprValidationPropertyException(
                        ex.Message +
                        ", every-distinct requires that all properties resolve from sub-expressions to the every-distinct",
                        ex.InnerException);
                }

                var convertor = new MatchedEventConvertorForge(
                    matchEventFromChildNodes.TaggedEventTypes,
                    matchEventFromChildNodes.ArrayEventTypes,
                    allTagNamesOrdered);

                distinctNode.Convertor = convertor;

                // Determine whether some expressions are constants or time period
                IList<ExprNode> distinctExpressions = new List<ExprNode>();
                TimePeriodComputeForge timePeriodComputeForge = null;
                ExprNode expiryTimeExp = null;
                var count = -1;
                var last = validated.Count - 1;
                foreach (var expr in validated) {
                    count++;
                    if (count == last && expr is ExprTimePeriod) {
                        expiryTimeExp = expr;
                        var timePeriodExpr = (ExprTimePeriod) expiryTimeExp;
                        timePeriodComputeForge = timePeriodExpr.TimePeriodComputeForge;
                    }
                    else if (expr.Forge.ForgeConstantType.IsCompileTimeConstant) {
                        if (count == last) {
                            var value = expr.Forge.ExprEvaluator.Evaluate(null, true, null);
                            if (!value.IsNumber()) {
                                throw new ExprValidationException(
                                    "Invalid parameter for every-distinct, expected number of seconds constant (constant not considered for distinct)");
                            }

                            var secondsExpire = expr.Forge.ExprEvaluator.Evaluate(null, true, null);
                            var timeExpire = secondsExpire == null
                                ? (long?) null
                                : (long?) services.ImportServiceCompileTime.TimeAbacus.DeltaForSecondsNumber(
                                    secondsExpire);
                            if (timeExpire != null && timeExpire > 0) {
                                timePeriodComputeForge = new TimePeriodComputeConstGivenDeltaForge(timeExpire.Value);
                                expiryTimeExp = expr;
                            }
                            else {
                                Log.Warn(
                                    "Invalid seconds-expire " +
                                    timeExpire +
                                    " for " +
                                    ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(expr));
                            }
                        }
                        else {
                            Log.Warn(
                                "Every-distinct node utilizes an expression returning a constant value, please check expression '" +
                                ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(expr) +
                                "', not adding expression to distinct-value expression list");
                        }
                    }
                    else {
                        distinctExpressions.Add(expr);
                    }
                }

                if (distinctExpressions.IsEmpty()) {
                    throw new ExprValidationException(
                        "Every-distinct node requires one or more distinct-value expressions that each return non-constant result values");
                }

                distinctNode.SetDistinctExpressions(distinctExpressions, timePeriodComputeForge, expiryTimeExp);
            }
            else if (evalNode is EvalMatchUntilForgeNode) {
                var matchUntilNode = (EvalMatchUntilForgeNode) evalNode;

                // compile bounds expressions, if any
                var untilMatchEventSpec = new MatchEventSpec(tags.TaggedEventTypes, tags.ArrayEventTypes);
                var streamTypeService = GetStreamTypeService(
                    untilMatchEventSpec.TaggedEventTypes,
                    untilMatchEventSpec.ArrayEventTypes,
                    matchUntilNode,
                    streamNum,
                    statementRawInfo,
                    services);
                var validationContext =
                    new ExprValidationContextBuilder(streamTypeService, statementRawInfo, services).Build();

                var lower = ValidateBounds(matchUntilNode.LowerBounds, validationContext);
                matchUntilNode.LowerBounds = lower;

                var upper = ValidateBounds(matchUntilNode.UpperBounds, validationContext);
                matchUntilNode.UpperBounds = upper;

                var single = ValidateBounds(matchUntilNode.SingleBound, validationContext);
                matchUntilNode.SingleBound = single;

                bool tightlyBound;
                if (matchUntilNode.SingleBound != null) {
                    ValidateMatchUntil(matchUntilNode.SingleBound, matchUntilNode.SingleBound, false);
                    tightlyBound = true;
                }
                else {
                    var allowZeroLowerBounds = matchUntilNode.LowerBounds != null && matchUntilNode.UpperBounds != null;
                    tightlyBound = ValidateMatchUntil(
                        matchUntilNode.LowerBounds,
                        matchUntilNode.UpperBounds,
                        allowZeroLowerBounds);
                }

                if (matchUntilNode.SingleBound == null && !tightlyBound && matchUntilNode.ChildNodes.Count < 2) {
                    throw new ExprValidationException("Variable bounds repeat operator requires an until-expression");
                }

                var convertor = new MatchedEventConvertorForge(
                    untilMatchEventSpec.TaggedEventTypes,
                    untilMatchEventSpec.ArrayEventTypes,
                    allTagNamesOrdered);

                matchUntilNode.Convertor = convertor;

                // compile new tag lists
                ISet<string> arrayTags = null;
                var matchUntilAnalysisResult = EvalNodeUtil.RecursiveAnalyzeChildNodes(matchUntilNode.ChildNodes[0]);
                foreach (var filterNode in matchUntilAnalysisResult.FilterNodes) {
                    var optionalTag = filterNode.EventAsName;
                    if (optionalTag != null) {
                        if (arrayTags == null) {
                            arrayTags = new HashSet<string>();
                        }

                        arrayTags.Add(optionalTag);
                    }
                }

                if (arrayTags != null) {
                    foreach (var arrayTag in arrayTags) {
                        if (!tags.ArrayEventTypes.ContainsKey(arrayTag)) {
                            tags.ArrayEventTypes.Put(arrayTag, tags.TaggedEventTypes.Get(arrayTag));
                            tags.TaggedEventTypes.Remove(arrayTag);
                        }
                    }
                }

                matchUntilNode.TagsArrayedSet = GetIndexesForTags(allTagNamesOrdered, arrayTags);
            }
            else if (evalNode is EvalFollowedByForgeNode) {
                var followedByNode = (EvalFollowedByForgeNode) evalNode;
                StreamTypeService streamTypeService = new StreamTypeServiceImpl(false);
                var validationContext =
                    new ExprValidationContextBuilder(streamTypeService, statementRawInfo, services).Build();

                if (followedByNode.OptionalMaxExpressions != null) {
                    IList<ExprNode> validated = new List<ExprNode>();
                    foreach (var maxExpr in followedByNode.OptionalMaxExpressions) {
                        if (maxExpr == null) {
                            validated.Add(null);
                        }
                        else {
                            var visitor = new ExprNodeSummaryVisitor();
                            maxExpr.Accept(visitor);
                            if (!visitor.IsPlain) {
                                var errorMessage = "Invalid maximum expression in followed-by, " +
                                                   visitor.Message +
                                                   " are not allowed within the expression";
                                Log.Error(errorMessage);
                                throw new ExprValidationException(errorMessage);
                            }

                            var validatedExpr = ExprNodeUtilityValidate.GetValidatedSubtree(
                                ExprNodeOrigin.FOLLOWEDBYMAX,
                                maxExpr,
                                validationContext);
                            validated.Add(validatedExpr);
                            var returnType = validatedExpr.Forge.EvaluationType;
                            if (returnType == null || !returnType.IsNumeric()) {
                                var message =
                                    "Invalid maximum expression in followed-by, the expression must return an integer value";
                                throw new ExprValidationException(message);
                            }
                        }
                    }

                    followedByNode.OptionalMaxExpressions = validated;
                }
            }

            if (newTaggedEventTypes != null) {
                tags.TaggedEventTypes.PutAll(newTaggedEventTypes);
            }

            if (newArrayEventTypes != null) {
                tags.ArrayEventTypes.PutAll(newArrayEventTypes);
            }
        }
예제 #6
0
        public static RowRecogPlan ValidateAndPlan(
            IContainer container,
            EventType parentEventType,
            bool unbound,
            StatementBaseInfo @base,
            StatementCompileTimeServices services)
        {
            var statementRawInfo = @base.StatementRawInfo;
            var matchRecognizeSpec = @base.StatementSpec.Raw.MatchRecognizeSpec;
            var annotations = statementRawInfo.Annotations;
            var iterateOnly = HintEnum.ITERATE_ONLY.GetHint(annotations) != null;
            var additionalForgeables = new List<StmtClassForgeableFactory>();

            // Expanded pattern already there
            RowRecogExprNode expandedPatternNode = matchRecognizeSpec.Pattern;

            // Determine single-row and multiple-row variables
            var variablesSingle = new LinkedHashSet<string>();
            var variablesMultiple = new LinkedHashSet<string>();
            RowRecogHelper.RecursiveInspectVariables(expandedPatternNode, false, variablesSingle, variablesMultiple);

            // each variable gets associated with a stream number (multiple-row variables as well to hold the current event for the expression).
            var streamNum = 0;
            var variableStreams = new LinkedHashMap<string, Pair<int, bool>>();
            foreach (var variableSingle in variablesSingle) {
                variableStreams.Put(variableSingle, new Pair<int, bool>(streamNum, false));
                streamNum++;
            }

            foreach (var variableMultiple in variablesMultiple) {
                variableStreams.Put(variableMultiple, new Pair<int, bool>(streamNum, true));
                streamNum++;
            }

            // mapping of stream to variable
            var streamVariables = new OrderedListDictionary<int, string>();
            foreach (var entry in variableStreams) {
                streamVariables.Put(entry.Value.First, entry.Key);
            }

            // determine visibility rules
            var visibility = RowRecogHelper.DetermineVisibility(expandedPatternNode);

            // assemble all single-row variables for expression validation
            var allStreamNames = new string[variableStreams.Count];
            var allTypes = new EventType[variableStreams.Count];

            streamNum = 0;
            foreach (var variableSingle in variablesSingle) {
                allStreamNames[streamNum] = variableSingle;
                allTypes[streamNum] = parentEventType;
                streamNum++;
            }

            foreach (var variableMultiple in variablesMultiple) {
                allStreamNames[streamNum] = variableMultiple;
                allTypes[streamNum] = parentEventType;
                streamNum++;
            }

            // determine type service for use with DEFINE
            // validate each DEFINE clause expression
            ISet<string> definedVariables = new HashSet<string>();
            IList<ExprAggregateNode> aggregateNodes = new List<ExprAggregateNode>();
            var isExprRequiresMultimatchState = new bool[variableStreams.Count];
            var previousNodes = new OrderedListDictionary<int, IList<ExprPreviousMatchRecognizeNode>>();

            for (var defineIndex = 0; defineIndex < matchRecognizeSpec.Defines.Count; defineIndex++) {
                MatchRecognizeDefineItem defineItem = matchRecognizeSpec.Defines[defineIndex];
                if (definedVariables.Contains(defineItem.Identifier)) {
                    throw new ExprValidationException(
                        "Variable '" + defineItem.Identifier + "' has already been defined");
                }

                definedVariables.Add(defineItem.Identifier);

                // stream-type visibilities handled here
                var typeServiceDefines = BuildDefineStreamTypeServiceDefine(
                    defineIndex,
                    variableStreams,
                    defineItem,
                    visibility,
                    parentEventType,
                    statementRawInfo,
                    services);

                var exprNodeResult = HandlePreviousFunctions(defineItem.Expression, previousNodes);
                var validationContext = new ExprValidationContextBuilder(typeServiceDefines, statementRawInfo, services)
                    .WithAllowBindingConsumption(true)
                    .WithDisablePropertyExpressionEventCollCache(true)
                    .Build();

                ExprNode validated;
                try {
                    // validate
                    validated = ExprNodeUtilityValidate.GetValidatedSubtree(
                        ExprNodeOrigin.MATCHRECOGDEFINE,
                        exprNodeResult,
                        validationContext);

                    // check aggregates
                    defineItem.Expression = validated;
                    ExprAggregateNodeUtil.GetAggregatesBottomUp(validated, aggregateNodes);
                    if (!aggregateNodes.IsEmpty()) {
                        throw new ExprValidationException("An aggregate function may not appear in a DEFINE clause");
                    }
                }
                catch (ExprValidationException ex) {
                    throw new ExprValidationException(
                        "Failed to validate condition expression for variable '" +
                        defineItem.Identifier +
                        "': " +
                        ex.Message,
                        ex);
                }

                // determine access to event properties from multi-matches
                var visitor = new ExprNodeStreamRequiredVisitor();
                validated.Accept(visitor);
                var streamsRequired = visitor.StreamsRequired;
                foreach (var streamRequired in streamsRequired) {
                    if (streamRequired >= variableStreams.Count) {
                        var streamNumIdent = variableStreams.Get(defineItem.Identifier).First;
                        isExprRequiresMultimatchState[streamNumIdent] = true;
                        break;
                    }
                }
            }

            var defineAsksMultimatches = CollectionUtil.IsAnySet(isExprRequiresMultimatchState);

            // determine type service for use with MEASURE
            IDictionary<string, object> measureTypeDef = new LinkedHashMap<string, object>();
            foreach (var variableSingle in variablesSingle) {
                measureTypeDef.Put(variableSingle, parentEventType);
            }

            foreach (var variableMultiple in variablesMultiple) {
                measureTypeDef.Put(variableMultiple, new[] {parentEventType});
            }

            var compositeTypeName = services.EventTypeNameGeneratorStatement.AnonymousRowrecogCompositeName;
            var compositeTypeMetadata = new EventTypeMetadata(
                compositeTypeName,
                @base.ModuleName,
                EventTypeTypeClass.MATCHRECOGDERIVED,
                EventTypeApplicationType.OBJECTARR,
                NameAccessModifier.TRANSIENT,
                EventTypeBusModifier.NONBUS,
                false,
                EventTypeIdPair.Unassigned());
            var compositeEventType = BaseNestableEventUtil.MakeOATypeCompileTime(
                compositeTypeMetadata,
                measureTypeDef,
                null,
                null,
                null,
                null,
                services.BeanEventTypeFactoryPrivate,
                services.EventTypeCompileTimeResolver);
            services.EventTypeCompileTimeRegistry.NewType(compositeEventType);
            StreamTypeService compositeTypeServiceMeasure =
                new StreamTypeServiceImpl(compositeEventType, "MATCH_RECOGNIZE", true);

            // find MEASURE clause aggregations
            var measureReferencesMultivar = false;
            IList<ExprAggregateNode> measureAggregateExprNodes = new List<ExprAggregateNode>();
            foreach (var measureItem in matchRecognizeSpec.Measures) {
                ExprAggregateNodeUtil.GetAggregatesBottomUp(measureItem.Expr, measureAggregateExprNodes);
            }

            AggregationServiceForgeDesc[] aggregationServices = null;
            if (!measureAggregateExprNodes.IsEmpty()) {
                aggregationServices = PlanAggregations(
                    measureAggregateExprNodes,
                    compositeTypeServiceMeasure,
                    allStreamNames,
                    allTypes,
                    streamVariables,
                    variablesMultiple,
                    @base,
                    services);
                foreach (AggregationServiceForgeDesc svc in aggregationServices) {
                    if (svc != null) {
                        additionalForgeables.AddAll(svc.AdditionalForgeables);
                    }
                }
            }

            // validate each MEASURE clause expression
            IDictionary<string, object> rowTypeDef = new LinkedHashMap<string, object>();
            var streamRefVisitor = new ExprNodeStreamUseCollectVisitor();
            foreach (var measureItem in matchRecognizeSpec.Measures) {
                if (measureItem.Name == null) {
                    throw new ExprValidationException(
                        "The measures clause requires that each expression utilizes the AS keyword to assign a column name");
                }

                var validated = ValidateMeasureClause(
                    measureItem.Expr,
                    compositeTypeServiceMeasure,
                    variablesMultiple,
                    variablesSingle,
                    statementRawInfo,
                    services);
                measureItem.Expr = validated;
                rowTypeDef.Put(measureItem.Name, validated.Forge.EvaluationType);
                validated.Accept(streamRefVisitor);
            }

            // Determine if any of the multi-var streams are referenced in the measures (non-aggregated only)
            foreach (var @ref in streamRefVisitor.Referenced) {
                var rootPropName = @ref.RootPropertyNameIfAny;
                if (rootPropName != null) {
                    if (variablesMultiple.Contains(rootPropName)) {
                        measureReferencesMultivar = true;
                        break;
                    }
                }

                var streamRequired = @ref.StreamReferencedIfAny;
                if (streamRequired != null) {
                    var streamVariable = streamVariables.Get(streamRequired.Value);
                    if (streamVariable != null) {
                        var def = variableStreams.Get(streamVariable);
                        if (def != null && def.Second) {
                            measureReferencesMultivar = true;
                            break;
                        }
                    }
                }
            }

            var collectMultimatches = measureReferencesMultivar || defineAsksMultimatches;

            // create rowevent type
            var rowTypeName = services.EventTypeNameGeneratorStatement.AnonymousRowrecogRowName;
            var rowTypeMetadata = new EventTypeMetadata(
                rowTypeName,
                @base.ModuleName,
                EventTypeTypeClass.MATCHRECOGDERIVED,
                EventTypeApplicationType.MAP,
                NameAccessModifier.TRANSIENT,
                EventTypeBusModifier.NONBUS,
                false,
                EventTypeIdPair.Unassigned());
            var rowEventType = BaseNestableEventUtil.MakeMapTypeCompileTime(
                rowTypeMetadata,
                rowTypeDef,
                null,
                null,
                null,
                null,
                services.BeanEventTypeFactoryPrivate,
                services.EventTypeCompileTimeResolver);
            services.EventTypeCompileTimeRegistry.NewType(rowEventType);

            // validate partition-by expressions, if any
            ExprNode[] partitionBy;
            MultiKeyClassRef partitionMultiKey;
            if (!matchRecognizeSpec.PartitionByExpressions.IsEmpty()) {
                StreamTypeService typeServicePartition = new StreamTypeServiceImpl(
                    parentEventType,
                    "MATCH_RECOGNIZE_PARTITION",
                    true);
                IList<ExprNode> validated = new List<ExprNode>();
                var validationContext =
                    new ExprValidationContextBuilder(typeServicePartition, statementRawInfo, services)
                        .WithAllowBindingConsumption(true)
                        .Build();
                foreach (var partitionExpr in matchRecognizeSpec.PartitionByExpressions) {
                    validated.Add(
                        ExprNodeUtilityValidate.GetValidatedSubtree(
                            ExprNodeOrigin.MATCHRECOGPARTITION,
                            partitionExpr,
                            validationContext));
                }

                matchRecognizeSpec.PartitionByExpressions = validated;
                partitionBy = ExprNodeUtilityQuery.ToArray(validated);
                MultiKeyPlan multiKeyPlan = MultiKeyPlanner.PlanMultiKey(partitionBy, false, @base.StatementRawInfo, services.SerdeResolver);
                partitionMultiKey = multiKeyPlan.ClassRef;
                additionalForgeables.AddAll(multiKeyPlan.MultiKeyForgeables);
            }
            else {
                partitionBy = null;
                partitionMultiKey = null;
            }

            // validate interval if present
            if (matchRecognizeSpec.Interval != null) {
                var validationContext =
                    new ExprValidationContextBuilder(new StreamTypeServiceImpl(false), statementRawInfo, services)
                        .WithAllowBindingConsumption(true)
                        .Build();
                var validated = (ExprTimePeriod) ExprNodeUtilityValidate.GetValidatedSubtree(
                    ExprNodeOrigin.MATCHRECOGINTERVAL,
                    matchRecognizeSpec.Interval.TimePeriodExpr,
                    validationContext);
                matchRecognizeSpec.Interval.TimePeriodExpr = validated;
            }

            // compile variable definition expressions
            IDictionary<string, ExprNode> variableDefinitions = new Dictionary<string, ExprNode>();
            foreach (var defineItem in matchRecognizeSpec.Defines) {
                variableDefinitions.Put(defineItem.Identifier, defineItem.Expression);
            }

            // create evaluators
            var columnNames = new string[matchRecognizeSpec.Measures.Count];
            var columnForges = new ExprNode[matchRecognizeSpec.Measures.Count];
            var count = 0;
            foreach (var measureItem in matchRecognizeSpec.Measures) {
                columnNames[count] = measureItem.Name;
                columnForges[count] = measureItem.Expr;
                count++;
            }

            // build states
            var strand = RowRecogHelper.BuildStartStates(
                expandedPatternNode,
                variableDefinitions,
                variableStreams,
                isExprRequiresMultimatchState);
            var startStates = strand.StartStates.ToArray();
            RowRecogNFAStateForge[] allStates = strand.AllStates.ToArray();

            if (Log.IsInfoEnabled) {
                Log.Info("NFA tree:\n" + RowRecogNFAViewUtil.Print(startStates));
            }

            // determine names of multimatching variables
            string[] multimatchVariablesArray;
            int[] multimatchStreamNumToVariable;
            int[] multimatchVariableToStreamNum;
            if (variablesSingle.Count == variableStreams.Count) {
                multimatchVariablesArray = new string[0];
                multimatchStreamNumToVariable = new int[0];
                multimatchVariableToStreamNum = new int[0];
            }
            else {
                multimatchVariablesArray = new string[variableStreams.Count - variablesSingle.Count];
                multimatchVariableToStreamNum = new int[multimatchVariablesArray.Length];
                multimatchStreamNumToVariable = new int[variableStreams.Count];
                CompatExtensions.Fill(multimatchStreamNumToVariable, -1);
                count = 0;
                foreach (var entry in variableStreams) {
                    if (entry.Value.Second) {
                        var index = count;
                        multimatchVariablesArray[index] = entry.Key;
                        multimatchVariableToStreamNum[index] = entry.Value.First;
                        multimatchStreamNumToVariable[entry.Value.First] = index;
                        count++;
                    }
                }
            }

            var numEventsEventsPerStreamDefine =
                defineAsksMultimatches ? variableStreams.Count + 1 : variableStreams.Count;

            // determine interval-or-terminated
            var orTerminated = matchRecognizeSpec.Interval != null && matchRecognizeSpec.Interval.IsOrTerminated;
            TimePeriodComputeForge intervalCompute = null;
            if (matchRecognizeSpec.Interval != null) {
                intervalCompute = matchRecognizeSpec.Interval.TimePeriodExpr.TimePeriodComputeForge;
            }

            EventType multimatchEventType = null;
            if (defineAsksMultimatches) {
                multimatchEventType = GetDefineMultimatchEventType(variableStreams, parentEventType, @base, services);
            }

            // determine previous-access indexes and assign "prev" node indexes
            // Since an expression such as "prior(2, price), prior(8, price)" translates into {2, 8} the relative index is {0, 1}.
            // Map the expression-supplied index to a relative index
            int[] previousRandomAccessIndexes = null;
            if (!previousNodes.IsEmpty()) {
                previousRandomAccessIndexes = new int[previousNodes.Count];
                var countPrev = 0;
                foreach (var entry in previousNodes) {
                    previousRandomAccessIndexes[countPrev] = entry.Key;
                    foreach (var callback in entry.Value) {
                        callback.AssignedIndex = countPrev;
                    }

                    countPrev++;
                }
            }

            RowRecogDescForge forge = new RowRecogDescForge(
                parentEventType,
                rowEventType,
                compositeEventType,
                multimatchEventType,
                multimatchStreamNumToVariable,
                multimatchVariableToStreamNum,
                partitionBy,
                partitionMultiKey,
                variableStreams,
                matchRecognizeSpec.Interval != null,
                iterateOnly,
                unbound,
                orTerminated,
                collectMultimatches,
                defineAsksMultimatches,
                numEventsEventsPerStreamDefine,
                multimatchVariablesArray,
                startStates,
                allStates,
                matchRecognizeSpec.IsAllMatches,
                matchRecognizeSpec.Skip.Skip,
                columnForges,
                columnNames,
                intervalCompute,
                previousRandomAccessIndexes,
                aggregationServices);
            
            return new RowRecogPlan(forge, additionalForgeables);
        }