{
EventType parentViewType = viewChain.getEventType();
this.matchRecognizeSpec = matchRecognizeSpec;
this.isUnbound = isUnbound;
this.isIterateOnly = HintEnum.ITERATE_ONLY.getHint(annotations) != null;
StatementContext statementContext = agentInstanceContext.getStatementContext();
// Determine single-row and multiple-row variables
variablesSingle = new LinkedHashSet<String>();
Set<String> variablesMultiple = new LinkedHashSet<String>();
EventRowRegexHelper.recursiveInspectVariables(matchRecognizeSpec.getPattern(), false, variablesSingle, variablesMultiple);
// each variable gets associated with a stream number (multiple-row variables as well to hold the current event for the expression).
int streamNum = 0;
variableStreams = new LinkedHashMap<String, Pair<Integer, Boolean>>();
for (String variableSingle : variablesSingle)
{
variableStreams.put(variableSingle, new Pair<Integer, Boolean>(streamNum, false));
streamNum++;
}
for (String variableMultiple : variablesMultiple)
{
variableStreams.put(variableMultiple, new Pair<Integer, Boolean>(streamNum, true));
streamNum++;
}
// mapping of stream to variable
streamVariables = new TreeMap<Integer, String>();
for (Map.Entry<String, Pair<Integer, Boolean>> entry : variableStreams.entrySet())
{
streamVariables.put(entry.getValue().getFirst(), entry.getKey());
}
// determine visibility rules
Map<String, Set<String>> visibility = EventRowRegexHelper.determineVisibility(matchRecognizeSpec.getPattern());
// assemble all single-row variables for expression validation
String[] allStreamNames = new String[variableStreams.size()];
EventType[] allTypes = new EventType[variableStreams.size()];
streamNum = 0;
for (String variableSingle : variablesSingle)
{
allStreamNames[streamNum] = variableSingle;
allTypes[streamNum] = parentViewType;
streamNum++;
}
for (String variableMultiple : variablesMultiple)
{
allStreamNames[streamNum] = variableMultiple;
allTypes[streamNum] = parentViewType;
streamNum++;
}
// determine type service for use with DEFINE
// validate each DEFINE clause expression
Set<String> definedVariables = new HashSet<String>();
List<ExprAggregateNode> aggregateNodes = new ArrayList<ExprAggregateNode>();
ExprEvaluatorContextStatement exprEvaluatorContext = new ExprEvaluatorContextStatement(statementContext);
this.isExprRequiresMultimatchState = new boolean[variableStreams.size()];
for (int defineIndex = 0; defineIndex < matchRecognizeSpec.getDefines().size(); defineIndex++)
{
MatchRecognizeDefineItem defineItem = matchRecognizeSpec.getDefines().get(defineIndex);
if (definedVariables.contains(defineItem.getIdentifier()))
{
throw new ExprValidationException("Variable '" + defineItem.getIdentifier() + "' has already been defined");
}
definedVariables.add(defineItem.getIdentifier());
// stream-type visibilities handled here
StreamTypeService typeServiceDefines = EventRowRegexNFAViewFactoryHelper.buildDefineStreamTypeServiceDefine(statementContext, variableStreams, defineItem, visibility, parentViewType);
ExprNode exprNodeResult = handlePreviousFunctions(defineItem.getExpression());
ExprValidationContext validationContext = new ExprValidationContext(typeServiceDefines, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), exprEvaluatorContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), true, false);
ExprNode validated;
try {
// validate
validated = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.MATCHRECOGDEFINE, exprNodeResult, validationContext);
// check aggregates
defineItem.setExpression(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.getIdentifier() + "': " + ex.getMessage(), ex);
}
// determine access to event properties from multi-matches
ExprNodeIdentifierCollectVisitor visitor = new ExprNodeIdentifierCollectVisitor();
validated.accept(visitor);
Set<Integer> streamsRequired = visitor.getStreamsRequired();
for (int streamRequired : streamsRequired) {
if (streamRequired >= variableStreams.size()) {
int streamNumIdent = variableStreams.get(defineItem.getIdentifier()).getFirst();
isExprRequiresMultimatchState[streamNumIdent] = true;
break;
}
}
}
isDefineAsksMultimatches = CollectionUtil.isAnySet(isExprRequiresMultimatchState);
defineMultimatchEventBean = isDefineAsksMultimatches ? EventRowRegexNFAViewFactoryHelper.getDefineMultimatchBean(statementContext, variableStreams, parentViewType) : null;
// 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 countPrev = 0;
for (Map.Entry<Integer, List<ExprPreviousMatchRecognizeNode>> entry : callbacksPerIndex.entrySet()) {
for (ExprPreviousMatchRecognizeNode callback : entry.getValue()) {
callback.setAssignedIndex(countPrev);
}
countPrev++;
}
// determine type service for use with MEASURE
Map<String, Object> measureTypeDef = new LinkedHashMap<String, Object>();
for (String variableSingle : variablesSingle)
{
measureTypeDef.put(variableSingle, parentViewType);
}
for (String variableMultiple : variablesMultiple)
{
measureTypeDef.put(variableMultiple, new EventType[] {parentViewType});
}
String outputEventTypeName = statementContext.getStatementId() + "_rowrecog";
compositeEventType = (ObjectArrayEventType) statementContext.getEventAdapterService().createAnonymousObjectArrayType(outputEventTypeName, measureTypeDef);
StreamTypeService typeServiceMeasure = new StreamTypeServiceImpl(compositeEventType, "MATCH_RECOGNIZE", true, statementContext.getEngineURI());
// find MEASURE clause aggregations
boolean measureReferencesMultivar = false;
List<ExprAggregateNode> measureAggregateExprNodes = new ArrayList<ExprAggregateNode>();
for (MatchRecognizeMeasureItem measureItem : matchRecognizeSpec.getMeasures())
{
ExprAggregateNodeUtil.getAggregatesBottomUp(measureItem.getExpr(), measureAggregateExprNodes);
}
if (!measureAggregateExprNodes.isEmpty())
{
boolean[] isIStreamOnly = new boolean[allStreamNames.length];
Arrays.fill(isIStreamOnly, true);
StreamTypeServiceImpl typeServiceAggregateMeasure = new StreamTypeServiceImpl(allTypes, allStreamNames, isIStreamOnly, statementContext.getEngineURI(), false);
Map<Integer, List<ExprAggregateNode>> measureExprAggNodesPerStream = new HashMap<Integer, List<ExprAggregateNode>>();
for (ExprAggregateNode aggregateNode : measureAggregateExprNodes)
{
int count = 0;
ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(true);
ExprValidationContext validationContext = new ExprValidationContext(typeServiceAggregateMeasure, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), exprEvaluatorContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false);
for (ExprNode child : aggregateNode.getChildNodes())
{
ExprNode validated = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.MATCHRECOGMEASURE, child, validationContext);
validated.accept(visitor);
aggregateNode.setChildNode(count++, new ExprNodeValidated(validated));
}
validationContext = new ExprValidationContext(typeServiceMeasure, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), exprEvaluatorContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false);
aggregateNode.validate(validationContext);
// verify properties used within the aggregation
Set<Integer> aggregatedStreams = new HashSet<Integer>();
for (Pair<Integer, String> pair : visitor.getExprProperties())
{
aggregatedStreams.add(pair.getFirst());
}
Integer multipleVarStream = null;
for (int streamNumAggregated : aggregatedStreams)
{
String variable = streamVariables.get(streamNumAggregated);
if (variablesMultiple.contains(variable))
{
measureReferencesMultivar = true;
if (multipleVarStream == null)
{
multipleVarStream = streamNumAggregated;
continue;
}
throw new ExprValidationException("Aggregation functions in the measure-clause must only refer to properties of exactly one group variable returning multiple events");
}
}
if (multipleVarStream == null)
{
throw new ExprValidationException("Aggregation functions in the measure-clause must refer to one or more properties of exactly one group variable returning multiple events");
}
List<ExprAggregateNode> aggNodesForStream = measureExprAggNodesPerStream.get(multipleVarStream);
if (aggNodesForStream == null)
{
aggNodesForStream = new ArrayList<ExprAggregateNode>();
measureExprAggNodesPerStream.put(multipleVarStream, aggNodesForStream);
}
aggNodesForStream.add(aggregateNode);
}
AggregationServiceMatchRecognizeFactoryDesc factoryDesc = AggregationServiceFactoryFactory.getServiceMatchRecognize(streamVariables.size(), measureExprAggNodesPerStream, exprEvaluatorContext);
aggregationService = factoryDesc.getAggregationServiceFactory().makeService(agentInstanceContext);
aggregationExpressions = factoryDesc.getExpressions();
}
else
{
aggregationService = null;
aggregationExpressions = Collections.emptyList();
}
// validate each MEASURE clause expression
Map<String, Object> rowTypeDef = new LinkedHashMap<String, Object>();
ExprNodeStreamUseCollectVisitor streamRefVisitor = new ExprNodeStreamUseCollectVisitor();
for (MatchRecognizeMeasureItem measureItem : matchRecognizeSpec.getMeasures())
{
if (measureItem.getName() == null)
{
throw new ExprValidationException("The measures clause requires that each expression utilizes the AS keyword to assign a column name");
}
ExprNode validated = validateMeasureClause(measureItem.getExpr(), typeServiceMeasure, variablesMultiple, variablesSingle, statementContext);
measureItem.setExpr(validated);
rowTypeDef.put(measureItem.getName(), validated.getExprEvaluator().getType());
validated.accept(streamRefVisitor);
}
// Determine if any of the multi-var streams are referenced in the measures (non-aggregated only)
for (ExprStreamRefNode ref : streamRefVisitor.getReferenced()) {
String rootPropName = ref.getRootPropertyNameIfAny();
if (rootPropName != null) {
if (variablesMultiple.contains(rootPropName)) {
measureReferencesMultivar = true;
break;
}
}
Integer streamRequired = ref.getStreamReferencedIfAny();
if (streamRequired != null) {
String streamVariable = streamVariables.get(streamRequired);
if (streamVariable != null) {
Pair<Integer, Boolean> def = variableStreams.get(streamVariable);
if (def != null && def.getSecond()) {
measureReferencesMultivar = true;
break;
}
}
}
}
isCollectMultimatches = measureReferencesMultivar || isDefineAsksMultimatches;
// create rowevent type
String rowEventTypeName = statementContext.getStatementId() + "_rowrecogrow";
rowEventType = statementContext.getEventAdapterService().createAnonymousMapType(rowEventTypeName, rowTypeDef);
// validate partition-by expressions, if any
if (!matchRecognizeSpec.getPartitionByExpressions().isEmpty())
{
StreamTypeService typeServicePartition = new StreamTypeServiceImpl(parentViewType, "MATCH_RECOGNIZE_PARTITION", true, statementContext.getEngineURI());
List<ExprNode> validated = new ArrayList<ExprNode>();
ExprValidationContext validationContext = new ExprValidationContext(typeServicePartition, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), exprEvaluatorContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false);
for (ExprNode partitionExpr : matchRecognizeSpec.getPartitionByExpressions())
{
validated.add(ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.MATCHRECOGPARTITION, partitionExpr, validationContext));
}
matchRecognizeSpec.setPartitionByExpressions(validated);
}
// validate interval if present
if (matchRecognizeSpec.getInterval() != null) {
ExprValidationContext validationContext = new ExprValidationContext(new StreamTypeServiceImpl(statementContext.getEngineURI(), false), statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), exprEvaluatorContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false);
matchRecognizeSpec.getInterval().validate(validationContext);
}
}