/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.core.start; import com.espertech.esper.client.EventType; import com.espertech.esper.client.annotation.HookType; import com.espertech.esper.client.annotation.IterableUnbound; import com.espertech.esper.client.hook.SQLColumnTypeConversion; import com.espertech.esper.client.hook.SQLOutputRowConversion; import com.espertech.esper.core.context.activator.ViewableActivator; import com.espertech.esper.core.context.activator.ViewableActivatorFactory; import com.espertech.esper.core.context.factory.StatementAgentInstanceFactorySelect; import com.espertech.esper.core.context.subselect.SubSelectActivationCollection; import com.espertech.esper.core.context.subselect.SubSelectStrategyCollection; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.core.context.util.ContextPropertyRegistry; import com.espertech.esper.core.context.util.EPStatementAgentInstanceHandle; import com.espertech.esper.core.service.EPServicesContext; import com.espertech.esper.core.service.ExprEvaluatorContextStatement; import com.espertech.esper.core.service.StatementContext; import com.espertech.esper.core.service.StreamJoinAnalysisResult; import com.espertech.esper.epl.annotation.AnnotationUtil; import com.espertech.esper.epl.core.*; import com.espertech.esper.epl.db.DatabasePollingViewableFactory; import com.espertech.esper.epl.expression.core.ExprEvaluator; import com.espertech.esper.epl.expression.core.ExprNodeUtility; import com.espertech.esper.epl.expression.core.ExprValidationException; import com.espertech.esper.epl.join.base.HistoricalViewableDesc; import com.espertech.esper.epl.join.base.JoinSetComposerPrototype; import com.espertech.esper.epl.join.base.JoinSetComposerPrototypeFactory; import com.espertech.esper.epl.named.NamedWindowMgmtService; import com.espertech.esper.epl.named.NamedWindowProcessor; import com.espertech.esper.epl.spec.*; import com.espertech.esper.epl.table.mgmt.TableMetadata; import com.espertech.esper.epl.util.EPLValidationUtil; import com.espertech.esper.epl.view.OutputProcessViewCallback; import com.espertech.esper.epl.view.OutputProcessViewFactory; import com.espertech.esper.epl.view.OutputProcessViewFactoryFactory; import com.espertech.esper.epl.virtualdw.VirtualDWView; import com.espertech.esper.epl.virtualdw.VirtualDWViewProviderForAgentInstance; import com.espertech.esper.filter.FilterSpecCompiled; import com.espertech.esper.metrics.instrumentation.InstrumentationAgent; import com.espertech.esper.metrics.instrumentation.InstrumentationHelper; import com.espertech.esper.pattern.EvalRootFactoryNode; import com.espertech.esper.pattern.PatternContext; import com.espertech.esper.rowregex.EventRowRegexNFAViewFactory; import com.espertech.esper.type.OuterJoinType; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.util.StopCallback; import com.espertech.esper.view.HistoricalEventViewable; import com.espertech.esper.view.ViewFactoryChain; import java.util.LinkedList; import java.util.List; /** * Starts and provides the stop method for EPL statements. */ public class EPStatementStartMethodSelectUtil { public static EPStatementStartMethodSelectDesc prepare(StatementSpecCompiled statementSpec, EPServicesContext services, StatementContext statementContext, boolean recoveringResilient, AgentInstanceContext defaultAgentInstanceContext, boolean queryPlanLogging, ViewableActivatorFactory optionalViewableActivatorFactory, OutputProcessViewCallback optionalOutputProcessViewCallback, SelectExprProcessorDeliveryCallback selectExprProcessorDeliveryCallback) throws ExprValidationException { // define stop and destroy final List<StopCallback> stopCallbacks = new LinkedList<StopCallback>(); EPStatementDestroyCallbackList destroyCallbacks = new EPStatementDestroyCallbackList(); // determine context final String contextName = statementSpec.getOptionalContextName(); final ContextPropertyRegistry contextPropertyRegistry = (contextName != null) ? services.getContextManagementService().getContextDescriptor(contextName).getContextPropertyRegistry() : null; // Determine stream names for each stream - some streams may not have a name given String[] streamNames = EPStatementStartMethodHelperUtil.determineStreamNames(statementSpec.getStreamSpecs()); int numStreams = streamNames.length; if (numStreams == 0) { throw new ExprValidationException("The from-clause is required but has not been specified"); } final boolean isJoin = statementSpec.getStreamSpecs().length > 1; final boolean hasContext = statementSpec.getOptionalContextName() != null; // First we create streams for subselects, if there are any SubSelectActivationCollection subSelectStreamDesc = EPStatementStartMethodHelperSubselect.createSubSelectActivation(services, statementSpec, statementContext, destroyCallbacks); // Create streams and views ViewableActivator[] eventStreamParentViewableActivators = new ViewableActivator[numStreams]; ViewFactoryChain[] unmaterializedViewChain = new ViewFactoryChain[numStreams]; String[] eventTypeNames = new String[numStreams]; boolean[] isNamedWindow = new boolean[numStreams]; HistoricalEventViewable[] historicalEventViewables = new HistoricalEventViewable[numStreams]; // verify for joins that required views are present StreamJoinAnalysisResult joinAnalysisResult = verifyJoinViews(statementSpec, statementContext.getNamedWindowMgmtService()); final ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); for (int i = 0; i < statementSpec.getStreamSpecs().length; i++) { StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs()[i]; boolean isCanIterateUnbound = streamSpec.getViewSpecs().length == 0 && (services.getConfigSnapshot().getEngineDefaults().getViewResources().isIterableUnbound() || AnnotationUtil.findAnnotation(statementSpec.getAnnotations(), IterableUnbound.class) != null); // Create view factories and parent view based on a filter specification if (streamSpec instanceof FilterStreamSpecCompiled) { final FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) streamSpec; eventTypeNames[i] = filterStreamSpec.getFilterSpec().getFilterForEventTypeName(); // Since only for non-joins we get the existing stream's lock and try to reuse it's views final boolean filterSubselectSameStream = EPStatementStartMethodHelperUtil.determineSubquerySameStream(statementSpec, filterStreamSpec); // create activator ViewableActivator activatorDeactivator; if (optionalViewableActivatorFactory != null) { activatorDeactivator = optionalViewableActivatorFactory.createActivatorSimple(filterStreamSpec); if (activatorDeactivator == null) { throw new IllegalStateException("Viewable activate is null for " + filterStreamSpec.getFilterSpec().getFilterForEventType().getName()); } } else { if (!hasContext) { activatorDeactivator = services.getViewableActivatorFactory().createStreamReuseView(services, statementContext, statementSpec, filterStreamSpec, isJoin, evaluatorContextStmt, filterSubselectSameStream, i, isCanIterateUnbound); } else { InstrumentationAgent instrumentationAgentFilter = null; if (InstrumentationHelper.ENABLED) { final String eventTypeName = filterStreamSpec.getFilterSpec().getFilterForEventType().getName(); final int streamNumber = i; instrumentationAgentFilter = new InstrumentationAgent() { public void indicateQ() { InstrumentationHelper.get().qFilterActivationStream(eventTypeName, streamNumber); } public void indicateA() { InstrumentationHelper.get().aFilterActivationStream(); } }; } activatorDeactivator = services.getViewableActivatorFactory().createFilterProxy(services, filterStreamSpec.getFilterSpec(), statementSpec.getAnnotations(), false, instrumentationAgentFilter, isCanIterateUnbound, i); } } eventStreamParentViewableActivators[i] = activatorDeactivator; EventType resultEventType = filterStreamSpec.getFilterSpec().getResultEventType(); unmaterializedViewChain[i] = services.getViewService().createFactories(i, resultEventType, streamSpec.getViewSpecs(), streamSpec.getOptions(), statementContext, false, -1); } else if (streamSpec instanceof PatternStreamSpecCompiled) { // Create view factories and parent view based on a pattern expression PatternStreamSpecCompiled patternStreamSpec = (PatternStreamSpecCompiled) streamSpec; boolean usedByChildViews = streamSpec.getViewSpecs().length > 0 || (statementSpec.getInsertIntoDesc() != null); String patternTypeName = statementContext.getStatementId() + "_pattern_" + i; final EventType eventType = services.getEventAdapterService().createSemiAnonymousMapType(patternTypeName, patternStreamSpec.getTaggedEventTypes(), patternStreamSpec.getArrayEventTypes(), usedByChildViews); unmaterializedViewChain[i] = services.getViewService().createFactories(i, eventType, streamSpec.getViewSpecs(), streamSpec.getOptions(), statementContext, false, -1); final EvalRootFactoryNode rootFactoryNode = services.getPatternNodeFactory().makeRootNode(patternStreamSpec.getEvalFactoryNode()); final PatternContext patternContext = statementContext.getPatternContextFactory().createContext(statementContext, i, rootFactoryNode, patternStreamSpec.getMatchedEventMapMeta(), true); // create activator ViewableActivator patternActivator = services.getViewableActivatorFactory().createPattern(patternContext, rootFactoryNode, eventType, EPStatementStartMethodHelperUtil.isConsumingFilters(patternStreamSpec.getEvalFactoryNode()), patternStreamSpec.isSuppressSameEventMatches(), patternStreamSpec.isDiscardPartialsOnMatch(), isCanIterateUnbound); eventStreamParentViewableActivators[i] = patternActivator; } else if (streamSpec instanceof DBStatementStreamSpec) { // Create view factories and parent view based on a database SQL statement validateNoViews(streamSpec, "Historical data"); DBStatementStreamSpec sqlStreamSpec = (DBStatementStreamSpec) streamSpec; SQLColumnTypeConversion typeConversionHook = (SQLColumnTypeConversion) JavaClassHelper.getAnnotationHook(statementSpec.getAnnotations(), HookType.SQLCOL, SQLColumnTypeConversion.class, statementContext.getEngineImportService()); SQLOutputRowConversion outputRowConversionHook = (SQLOutputRowConversion) JavaClassHelper.getAnnotationHook(statementSpec.getAnnotations(), HookType.SQLROW, SQLOutputRowConversion.class, statementContext.getEngineImportService()); EPStatementAgentInstanceHandle epStatementAgentInstanceHandle = defaultAgentInstanceContext.getEpStatementAgentInstanceHandle(); HistoricalEventViewable historicalEventViewable = DatabasePollingViewableFactory.createDBStatementView(statementContext.getStatementId(), i, sqlStreamSpec, services.getDatabaseRefService(), services.getEventAdapterService(), epStatementAgentInstanceHandle, typeConversionHook, outputRowConversionHook, statementContext.getConfigSnapshot().getEngineDefaults().getLogging().isEnableJDBC(), services.getDataCacheFactory(), statementContext); historicalEventViewables[i] = historicalEventViewable; unmaterializedViewChain[i] = ViewFactoryChain.fromTypeNoViews(historicalEventViewable.getEventType()); eventStreamParentViewableActivators[i] = services.getViewableActivatorFactory().makeHistorical(historicalEventViewable); stopCallbacks.add(historicalEventViewable); } else if (streamSpec instanceof MethodStreamSpec) { validateNoViews(streamSpec, "Method data"); MethodStreamSpec methodStreamSpec = (MethodStreamSpec) streamSpec; EPStatementAgentInstanceHandle epStatementAgentInstanceHandle = defaultAgentInstanceContext.getEpStatementAgentInstanceHandle(); HistoricalEventViewable historicalEventViewable = MethodPollingViewableFactory.createPollMethodView(i, methodStreamSpec, services.getEventAdapterService(), epStatementAgentInstanceHandle, statementContext.getEngineImportService(), statementContext.getSchedulingService(), statementContext.getScheduleBucket(), evaluatorContextStmt, statementContext.getVariableService(), statementContext.getContextName(), services.getDataCacheFactory(), statementContext); historicalEventViewables[i] = historicalEventViewable; unmaterializedViewChain[i] = ViewFactoryChain.fromTypeNoViews(historicalEventViewable.getEventType()); eventStreamParentViewableActivators[i] = services.getViewableActivatorFactory().makeHistorical(historicalEventViewable); stopCallbacks.add(historicalEventViewable); } else if (streamSpec instanceof TableQueryStreamSpec) { validateNoViews(streamSpec, "Table data"); TableQueryStreamSpec tableStreamSpec = (TableQueryStreamSpec) streamSpec; if (isJoin && tableStreamSpec.getFilterExpressions().size() > 0) { throw new ExprValidationException("Joins with tables do not allow table filter expressions, please add table filters to the where-clause instead"); } TableMetadata metadata = services.getTableService().getTableMetadata(tableStreamSpec.getTableName()); ExprEvaluator[] tableFilterEvals = null; if (tableStreamSpec.getFilterExpressions().size() > 0) { tableFilterEvals = ExprNodeUtility.getEvaluators(tableStreamSpec.getFilterExpressions()); } EPLValidationUtil.validateContextName(true, metadata.getTableName(), metadata.getContextName(), statementSpec.getOptionalContextName(), false); eventStreamParentViewableActivators[i] = services.getViewableActivatorFactory().createTable(metadata, tableFilterEvals); unmaterializedViewChain[i] = ViewFactoryChain.fromTypeNoViews(metadata.getInternalEventType()); eventTypeNames[i] = tableStreamSpec.getTableName(); joinAnalysisResult.setTablesForStream(i, metadata); if (tableStreamSpec.getOptions().isUnidirectional()) { throw new ExprValidationException("Tables cannot be marked as unidirectional"); } if (tableStreamSpec.getOptions().isRetainIntersection() || tableStreamSpec.getOptions().isRetainUnion()) { throw new ExprValidationException("Tables cannot be marked with retain"); } if (isJoin) { destroyCallbacks.addCallback(new EPStatementDestroyCallbackTableIdxRef(services.getTableService(), metadata, statementContext.getStatementName())); } services.getStatementVariableRefService().addReferences(statementContext.getStatementName(), metadata.getTableName()); } else if (streamSpec instanceof NamedWindowConsumerStreamSpec) { final NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec; final NamedWindowProcessor processor = services.getNamedWindowMgmtService().getProcessor(namedSpec.getWindowName()); EventType namedWindowType = processor.getTailView().getEventType(); if (namedSpec.getOptPropertyEvaluator() != null) { namedWindowType = namedSpec.getOptPropertyEvaluator().getFragmentEventType(); } eventStreamParentViewableActivators[i] = services.getViewableActivatorFactory().createNamedWindow(processor, namedSpec, statementContext); services.getNamedWindowConsumerMgmtService().addConsumer(statementContext, namedSpec); unmaterializedViewChain[i] = services.getViewService().createFactories(i, namedWindowType, namedSpec.getViewSpecs(), namedSpec.getOptions(), statementContext, false, -1); joinAnalysisResult.setNamedWindow(i); eventTypeNames[i] = namedSpec.getWindowName(); isNamedWindow[i] = true; // Consumers to named windows cannot declare a data window view onto the named window to avoid duplicate remove streams EPStatementStartMethodHelperValidate.validateNoDataWindowOnNamedWindow(unmaterializedViewChain[i].getViewFactoryChain()); } else { throw new ExprValidationException("Unknown stream specification type: " + streamSpec); } } // handle match-recognize pattern if (statementSpec.getMatchRecognizeSpec() != null) { if (isJoin) { throw new ExprValidationException("Joins are not allowed when using match-recognize"); } if (joinAnalysisResult.getTablesPerStream()[0] != null) { throw new ExprValidationException("Tables cannot be used with match-recognize"); } boolean isUnbound = (unmaterializedViewChain[0].getViewFactoryChain().isEmpty()) && (!(statementSpec.getStreamSpecs()[0] instanceof NamedWindowConsumerStreamSpec)); EventRowRegexNFAViewFactory factory = services.getRegexHandlerFactory().makeViewFactory(unmaterializedViewChain[0], statementSpec.getMatchRecognizeSpec(), defaultAgentInstanceContext, isUnbound, statementSpec.getAnnotations(), services.getConfigSnapshot().getEngineDefaults().getMatchRecognize()); unmaterializedViewChain[0].getViewFactoryChain().add(factory); EPStatementStartMethodHelperAssignExpr.assignAggregations(factory.getAggregationService(), factory.getAggregationExpressions()); } // Obtain event types from view factory chains EventType[] streamEventTypes = new EventType[statementSpec.getStreamSpecs().length]; for (int i = 0; i < unmaterializedViewChain.length; i++) { streamEventTypes[i] = unmaterializedViewChain[i].getEventType(); } // Add uniqueness information useful for joins joinAnalysisResult.addUniquenessInfo(unmaterializedViewChain, statementSpec.getAnnotations()); // Validate sub-select views SubSelectStrategyCollection subSelectStrategyCollection = EPStatementStartMethodHelperSubselect.planSubSelect(services, statementContext, queryPlanLogging, subSelectStreamDesc, streamNames, streamEventTypes, eventTypeNames, statementSpec.getDeclaredExpressions(), contextPropertyRegistry); // Construct type information per stream StreamTypeService typeService = new StreamTypeServiceImpl(streamEventTypes, streamNames, EPStatementStartMethodHelperUtil.getHasIStreamOnly(isNamedWindow, unmaterializedViewChain), services.getEngineURI(), false); ViewResourceDelegateUnverified viewResourceDelegateUnverified = new ViewResourceDelegateUnverified(); // Validate views that require validation, specifically streams that don't have // sub-views such as DB SQL joins HistoricalViewableDesc historicalViewableDesc = new HistoricalViewableDesc(numStreams); for (int stream = 0; stream < historicalEventViewables.length; stream++) { HistoricalEventViewable historicalEventViewable = historicalEventViewables[stream]; if (historicalEventViewable == null) { continue; } historicalEventViewable.validate(services.getEngineImportService(), typeService, statementContext.getTimeProvider(), statementContext.getVariableService(), statementContext.getTableService(), evaluatorContextStmt, services.getConfigSnapshot(), services.getSchedulingService(), services.getEngineURI(), statementSpec.getSqlParameters(), statementContext.getEventAdapterService(), statementContext); historicalViewableDesc.setHistorical(stream, historicalEventViewable.getRequiredStreams()); if (historicalEventViewable.getRequiredStreams().contains(stream)) { throw new ExprValidationException("Parameters for historical stream " + stream + " indicate that the stream is subordinate to itself as stream parameters originate in the same stream"); } } // unidirectional is not supported with into-table if (joinAnalysisResult.isUnidirectional() && statementSpec.getIntoTableSpec() != null) { throw new ExprValidationException("Into-table does not allow unidirectional joins"); } // Construct a processor for results posted by views and joins, which takes care of aggregation if required. // May return null if we don't need to post-process results posted by views or joins. ResultSetProcessorFactoryDesc resultSetProcessorPrototypeDesc = ResultSetProcessorFactoryFactory.getProcessorPrototype( statementSpec, statementContext, typeService, viewResourceDelegateUnverified, joinAnalysisResult.getUnidirectionalInd(), true, contextPropertyRegistry, selectExprProcessorDeliveryCallback, services.getConfigSnapshot(), services.getResultSetProcessorHelperFactory(), false, false); // Validate where-clause filter tree, outer join clause and output limit expression EPStatementStartMethodHelperValidate.validateNodes(statementSpec, statementContext, typeService, viewResourceDelegateUnverified); // Handle 'prior' function nodes in terms of view requirements ViewResourceDelegateVerified viewResourceDelegateVerified = EPStatementStartMethodHelperViewResources.verifyPreviousAndPriorRequirements(unmaterializedViewChain, viewResourceDelegateUnverified); // handle join JoinSetComposerPrototype joinSetComposerPrototype = null; if (numStreams > 1) { boolean selectsRemoveStream = statementSpec.getSelectStreamSelectorEnum().isSelectsRStream() || statementSpec.getOutputLimitSpec() != null; boolean hasAggregations = !resultSetProcessorPrototypeDesc.getAggregationServiceFactoryDesc().getExpressions().isEmpty(); joinSetComposerPrototype = JoinSetComposerPrototypeFactory.makeComposerPrototype( statementContext.getStatementName(), statementContext.getStatementId(), statementSpec.getOuterJoinDescList(), statementSpec.getFilterRootNode(), typeService.getEventTypes(), streamNames, joinAnalysisResult, queryPlanLogging, statementContext, historicalViewableDesc, defaultAgentInstanceContext, selectsRemoveStream, hasAggregations, services.getTableService(), false, services.getEventTableIndexService().allowInitIndex(recoveringResilient)); } // obtain factory for output limiting OutputProcessViewFactory outputViewFactory = OutputProcessViewFactoryFactory.make(statementSpec, services.getInternalEventRouter(), statementContext, resultSetProcessorPrototypeDesc.getResultSetProcessorFactory().getResultEventType(), optionalOutputProcessViewCallback, services.getTableService(), resultSetProcessorPrototypeDesc.getResultSetProcessorFactory().getResultSetProcessorType(), services.getResultSetProcessorHelperFactory(), services.getStatementVariableRefService()); // Factory for statement-context instances StatementAgentInstanceFactorySelect factory = new StatementAgentInstanceFactorySelect( numStreams, eventStreamParentViewableActivators, statementContext, statementSpec, services, typeService, unmaterializedViewChain, resultSetProcessorPrototypeDesc, joinAnalysisResult, recoveringResilient, joinSetComposerPrototype, subSelectStrategyCollection, viewResourceDelegateVerified, outputViewFactory); final EPStatementStopMethod stopMethod = new EPStatementStopMethodImpl(statementContext, stopCallbacks); return new EPStatementStartMethodSelectDesc(factory, subSelectStrategyCollection, viewResourceDelegateUnverified, resultSetProcessorPrototypeDesc, stopMethod, destroyCallbacks); } private static void validateNoViews(StreamSpecCompiled streamSpec, String conceptName) throws ExprValidationException { if (streamSpec.getViewSpecs().length > 0) { throw new ExprValidationException(conceptName + " joins do not allow views onto the data, view '" + streamSpec.getViewSpecs()[0].getObjectName() + "' is not valid in this context"); } } private static StreamJoinAnalysisResult verifyJoinViews(StatementSpecCompiled statementSpec, NamedWindowMgmtService namedWindowMgmtService) throws ExprValidationException { StreamSpecCompiled[] streamSpecs = statementSpec.getStreamSpecs(); StreamJoinAnalysisResult analysisResult = new StreamJoinAnalysisResult(streamSpecs.length); if (streamSpecs.length < 2) { return analysisResult; } // Determine if any stream has a unidirectional keyword // inspect unidirectional indicator and named window flags for (int i = 0; i < statementSpec.getStreamSpecs().length; i++) { StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs()[i]; if (streamSpec.getOptions().isUnidirectional()) { analysisResult.setUnidirectionalInd(i); } if (streamSpec.getViewSpecs().length > 0) { analysisResult.setHasChildViews(i); } if (streamSpec instanceof NamedWindowConsumerStreamSpec) { NamedWindowConsumerStreamSpec nwSpec = (NamedWindowConsumerStreamSpec) streamSpec; if (nwSpec.getOptPropertyEvaluator() != null && !streamSpec.getOptions().isUnidirectional()) { throw new ExprValidationException("Failed to validate named window use in join, contained-event is only allowed for named windows when marked as unidirectional"); } analysisResult.setNamedWindow(i); final NamedWindowProcessor processor = namedWindowMgmtService.getProcessor(nwSpec.getWindowName()); String[][] uniqueIndexes = processor.getUniqueIndexes(); analysisResult.getUniqueKeys()[i] = uniqueIndexes; if (processor.isVirtualDataWindow()) { analysisResult.getViewExternal()[i] = new VirtualDWViewProviderForAgentInstance() { public VirtualDWView getView(AgentInstanceContext agentInstanceContext) { return processor.getProcessorInstance(agentInstanceContext).getRootViewInstance().getVirtualDataWindow(); } }; } } } // non-outer-join: verify unidirectional can be on a single stream only if (statementSpec.getStreamSpecs().length > 1 && analysisResult.isUnidirectional()) { verifyJoinUnidirectional(analysisResult, statementSpec); } // count streams that provide data, excluding streams that poll data (DB and method) int countProviderNonpolling = 0; for (int i = 0; i < statementSpec.getStreamSpecs().length; i++) { StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs()[i]; if ((streamSpec instanceof MethodStreamSpec) || (streamSpec instanceof DBStatementStreamSpec) || (streamSpec instanceof TableQueryStreamSpec)) { continue; } countProviderNonpolling++; } // if there is only one stream providing data, the analysis is done if (countProviderNonpolling == 1) { return analysisResult; } // there are multiple driving streams, verify the presence of a view for insert/remove stream // validation of join views works differently for unidirectional as there can be self-joins that don't require a view // see if this is a self-join in which all streams are filters and filter specification is the same. FilterSpecCompiled unidirectionalFilterSpec = null; FilterSpecCompiled lastFilterSpec = null; boolean pureSelfJoin = true; for (StreamSpecCompiled streamSpec : statementSpec.getStreamSpecs()) { if (!(streamSpec instanceof FilterStreamSpecCompiled)) { pureSelfJoin = false; continue; } FilterSpecCompiled filterSpec = ((FilterStreamSpecCompiled) streamSpec).getFilterSpec(); if ((lastFilterSpec != null) && (!lastFilterSpec.equalsTypeAndFilter(filterSpec))) { pureSelfJoin = false; } if (streamSpec.getViewSpecs().length > 0) { pureSelfJoin = false; } lastFilterSpec = filterSpec; if (streamSpec.getOptions().isUnidirectional()) { unidirectionalFilterSpec = filterSpec; } } // self-join without views and not unidirectional if (pureSelfJoin && (unidirectionalFilterSpec == null)) { analysisResult.setPureSelfJoin(true); return analysisResult; } // weed out filter and pattern streams that don't have a view in a join for (int i = 0; i < statementSpec.getStreamSpecs().length; i++) { StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs()[i]; if (streamSpec.getViewSpecs().length > 0) { continue; } String name = streamSpec.getOptionalStreamName(); if ((name == null) && (streamSpec instanceof FilterStreamSpecCompiled)) { name = ((FilterStreamSpecCompiled) streamSpec).getFilterSpec().getFilterForEventTypeName(); } if ((name == null) && (streamSpec instanceof PatternStreamSpecCompiled)) { name = "pattern event stream"; } if (streamSpec.getOptions().isUnidirectional()) { continue; } // allow a self-join without a child view, in that the filter spec is the same as the unidirection's stream filter if ((unidirectionalFilterSpec != null) && (streamSpec instanceof FilterStreamSpecCompiled) && (((FilterStreamSpecCompiled) streamSpec).getFilterSpec().equalsTypeAndFilter(unidirectionalFilterSpec))) { analysisResult.setUnidirectionalNonDriving(i); continue; } if ((streamSpec instanceof FilterStreamSpecCompiled) || (streamSpec instanceof PatternStreamSpecCompiled)) { throw new ExprValidationException("Joins require that at least one view is specified for each stream, no view was specified for " + name); } } return analysisResult; } private static void verifyJoinUnidirectional(StreamJoinAnalysisResult analysisResult, StatementSpecCompiled statementSpec) throws ExprValidationException { int numUnidirectionalStreams = analysisResult.getUnidirectionalCount(); int numStreams = statementSpec.getStreamSpecs().length; // only a single stream is unidirectional (applies to all but all-full-outer-join) if (!isFullOuterJoinAllStreams(statementSpec)) { if (numUnidirectionalStreams > 1) { throw new ExprValidationException("The unidirectional keyword can only apply to one stream in a join"); } } else { // verify full-outer-join: requires unidirectional for all streams if (numUnidirectionalStreams > 1 && numUnidirectionalStreams < numStreams) { throw new ExprValidationException("The unidirectional keyword must either apply to a single stream or all streams in a full outer join"); } } // verify no-child-view for unidirectional for (int i = 0; i < statementSpec.getStreamSpecs().length; i++) { if (analysisResult.getUnidirectionalInd()[i]) { if (analysisResult.getHasChildViews()[i]) { throw new ExprValidationException("The unidirectional keyword requires that no views are declared onto the stream (applies to stream " + i + ")"); } } } } private static boolean isFullOuterJoinAllStreams(StatementSpecCompiled statementSpec) { if (statementSpec.getOuterJoinDescList() == null || statementSpec.getOuterJoinDescList().length == 0) { return false; } for (int stream = 0; stream < statementSpec.getStreamSpecs().length - 1; stream++) { if (statementSpec.getOuterJoinDescList()[stream].getOuterJoinType() != OuterJoinType.FULL) { return false; } } return true; } }