/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* 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;
import com.espertech.esper.client.*;
import com.espertech.esper.client.annotation.HintEnum;
import com.espertech.esper.client.annotation.HookType;
import com.espertech.esper.client.hook.SQLColumnTypeConversion;
import com.espertech.esper.client.hook.SQLOutputRowConversion;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.collection.UniformPair;
import com.espertech.esper.epl.agg.AggregationService;
import com.espertech.esper.epl.agg.AggregationServiceFactory;
import com.espertech.esper.epl.core.*;
import com.espertech.esper.epl.db.DatabasePollingViewableFactory;
import com.espertech.esper.epl.declexpr.ExprDeclaredNode;
import com.espertech.esper.epl.expression.*;
import com.espertech.esper.epl.join.base.*;
import com.espertech.esper.epl.join.plan.CoercionDesc;
import com.espertech.esper.epl.join.plan.CoercionUtil;
import com.espertech.esper.epl.join.plan.QueryPlanIndexBuilder;
import com.espertech.esper.epl.join.table.*;
import com.espertech.esper.epl.lookup.*;
import com.espertech.esper.epl.named.*;
import com.espertech.esper.epl.spec.*;
import com.espertech.esper.epl.subquery.SubqueryStopCallback;
import com.espertech.esper.epl.subquery.SubselectAggregatorView;
import com.espertech.esper.epl.subquery.SubselectBufferObserver;
import com.espertech.esper.epl.variable.CreateVariableView;
import com.espertech.esper.epl.variable.OnSetVariableView;
import com.espertech.esper.epl.variable.VariableDeclarationException;
import com.espertech.esper.epl.variable.VariableExistsException;
import com.espertech.esper.epl.view.FilterExprView;
import com.espertech.esper.epl.view.OutputConditionExpression;
import com.espertech.esper.epl.view.OutputProcessView;
import com.espertech.esper.epl.view.OutputProcessViewFactory;
import com.espertech.esper.epl.virtualdw.VirtualDWView;
import com.espertech.esper.epl.virtualdw.VirtualDWViewFactory;
import com.espertech.esper.event.EventAdapterException;
import com.espertech.esper.event.EventTypeMetadata;
import com.espertech.esper.event.EventTypeUtility;
import com.espertech.esper.event.map.MapEventType;
import com.espertech.esper.event.vaevent.ValueAddEventProcessor;
import com.espertech.esper.filter.FilterSpecCompiled;
import com.espertech.esper.pattern.EvalRootNode;
import com.espertech.esper.pattern.PatternContext;
import com.espertech.esper.pattern.PatternMatchCallback;
import com.espertech.esper.pattern.PatternStopCallback;
import com.espertech.esper.rowregex.EventRowRegexNFAViewFactory;
import com.espertech.esper.util.AuditPath;
import com.espertech.esper.util.JavaClassHelper;
import com.espertech.esper.util.StopCallback;
import com.espertech.esper.util.UuidGenerator;
import com.espertech.esper.view.*;
import com.espertech.esper.view.internal.BufferView;
import com.espertech.esper.view.internal.RouteResultView;
import com.espertech.esper.view.internal.SingleStreamDispatchView;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.naming.NamingException;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* Starts and provides the stop method for EPL statements.
*/
public class EPStatementStartMethod
{
private static final Log log = LogFactory.getLog(EPStatementStartMethod.class);
private static final Log queryPlanLog = LogFactory.getLog(AuditPath.QUERYPLAN_LOG);
private final StatementSpecCompiled statementSpec;
private final EPServicesContext services;
private final StatementContext statementContext;
private final boolean queryPlanLogging;
/**
* Ctor.
* @param statementSpec is a container for the definition of all statement constructs that
* may have been used in the statement, i.e. if defines the select clauses, insert into, outer joins etc.
* @param services is the service instances for dependency injection
* @param statementContext is statement-level information and statement services
*/
public EPStatementStartMethod(StatementSpecCompiled statementSpec,
EPServicesContext services,
StatementContext statementContext)
{
this.statementSpec = statementSpec;
this.services = services;
this.statementContext = statementContext;
this.queryPlanLogging = services.getConfigSnapshot().getEngineDefaults().getLogging().isEnableQueryPlan();
if (queryPlanLogging && queryPlanLog.isInfoEnabled()) {
queryPlanLog.info("Query plans for statement '" + statementContext.getStatementName() + "' expression '" + statementContext.getExpression() + "'");
}
}
/**
* Starts the EPL statement.
* @return a viewable to attach to for listening to events, and a stop method to invoke to clean up
* @param isNewStatement indicator whether the statement is new or a stop-restart statement
* @param isRecoveringStatement true to indicate the statement is in the process of being recovered
* @param isRecoveringResilient true to indicate the statement is in the process of being recovered and that statement is resilient
* @throws ExprValidationException when the expression validation fails
* @throws ViewProcessingException when views cannot be started
*/
public EPStatementStartResult start(boolean isNewStatement, boolean isRecoveringStatement, boolean isRecoveringResilient)
throws ExprValidationException, ViewProcessingException
{
statementContext.getVariableService().setLocalVersion(); // get current version of variables
if (statementSpec.getUpdateSpec() != null)
{
return startUpdate();
}
if (statementSpec.getOnTriggerDesc() != null)
{
return startOnTrigger();
}
else if (statementSpec.getCreateWindowDesc() != null)
{
return startCreateWindow(isNewStatement, isRecoveringStatement);
}
else if (statementSpec.getCreateIndexDesc() != null)
{
return startCreateIndex();
}
else if (statementSpec.getCreateSchemaDesc() != null)
{
return startCreateSchema();
}
else if (statementSpec.getCreateVariableDesc() != null)
{
return startCreateVariable(isNewStatement);
}
else
{
return startSelect(isRecoveringResilient);
}
}
private EPStatementStartResult startCreateIndex()
throws ExprValidationException, ViewProcessingException
{
final CreateIndexDesc spec = statementSpec.getCreateIndexDesc();
final NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(spec.getWindowName());
processor.getRootView().addExplicitIndex(spec.getWindowName(), spec.getIndexName(), spec.getColumns());
EPStatementStopMethod stopMethod = new EPStatementStopMethod() {
public void stop()
{
processor.getRootView().removeExplicitIndex(spec.getIndexName());
}
};
Viewable viewable = new ViewableDefaultImpl(processor.getNamedWindowType());
return new EPStatementStartResult(viewable, stopMethod, null);
}
private EPStatementStartResult startCreateSchema() throws ExprValidationException
{
final CreateSchemaDesc spec = statementSpec.getCreateSchemaDesc();
EventType eventType = null;
try {
if (!spec.isVariant()) {
if (spec.getTypes().isEmpty()) {
Map<String, Object> typing = TypeBuilderUtil.buildType(spec.getColumns());
eventType = services.getEventAdapterService().addNestableMapType(spec.getSchemaName(), typing, spec.getInherits(), false, false, true, false, false);
}
else {
if (spec.getTypes().size() == 1) {
String typeName = spec.getTypes().iterator().next();
try {
eventType = services.getEventAdapterService().addBeanType(spec.getSchemaName(), spec.getTypes().iterator().next(), false, false, false, true);
}
catch (EventAdapterException ex) {
Class clazz;
try {
clazz = services.getEngineImportService().resolveClass(typeName);
eventType = services.getEventAdapterService().addBeanType(spec.getSchemaName(), clazz, false, false, true);
}
catch (EngineImportException e) {
log.debug("Engine import failed to resolve event type '" + typeName + "'");
throw ex;
}
}
}
}
}
else {
boolean isAny = false;
ConfigurationVariantStream config = new ConfigurationVariantStream();
for (String typeName : spec.getTypes()) {
if (typeName.trim().equals("*")) {
isAny = true;
break;
}
config.addEventTypeName(typeName);
}
if (!isAny) {
config.setTypeVariance(ConfigurationVariantStream.TypeVariance.PREDEFINED);
}
else {
config.setTypeVariance(ConfigurationVariantStream.TypeVariance.ANY);
}
services.getValueAddEventService().addVariantStream(spec.getSchemaName(), config, services.getEventAdapterService());
eventType = services.getValueAddEventService().getValueAddProcessor(spec.getSchemaName()).getValueAddEventType();
}
}
catch (RuntimeException ex) {
throw new ExprValidationException(ex.getMessage(), ex);
}
// enter a reference
services.getStatementEventTypeRefService().addReferences(statementContext.getStatementName(), Collections.singleton(spec.getSchemaName()));
final EventType allocatedEventType = eventType;
EPStatementStopMethod stopMethod = new EPStatementStopMethod() {
public void stop()
{
services.getStatementEventTypeRefService().removeReferencesStatement(statementContext.getStatementName());
if (services.getStatementEventTypeRefService().getStatementNamesForType(spec.getSchemaName()).isEmpty()) {
services.getEventAdapterService().removeType(allocatedEventType.getName());
}
}
};
Viewable viewable = new ViewableDefaultImpl(eventType);
EPStatementStartResult result = new EPStatementStartResult(viewable, stopMethod, null);
return result;
}
private EPStatementStartResult startOnTrigger()
throws ExprValidationException, ViewProcessingException
{
final List<StopCallback> stopCallbacks = new LinkedList<StopCallback>();
SubSelectStreamCollection subSelectStreamDesc = createSubSelectStreams(true, statementSpec.getAnnotations());
// Create streams
Viewable eventStreamParentViewable;
final StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(0);
String triggereventTypeName = null;
if (streamSpec instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) streamSpec;
triggereventTypeName = filterStreamSpec.getFilterSpec().getFilterForEventTypeName();
// Since only for non-joins we get the existing stream's lock and try to reuse it's views
boolean filterSubselectSameStream = determineSubquerySameStream(filterStreamSpec);
Pair<EventStream, StatementLock> streamLockPair = services.getStreamService().createStream(statementContext.getStatementId(), filterStreamSpec.getFilterSpec(),
statementContext.getFilterService(), statementContext.getEpStatementHandle(), false, false, statementContext, true, filterSubselectSameStream, statementContext.getAnnotations());
eventStreamParentViewable = streamLockPair.getFirst();
// Use the re-used stream's lock for all this statement's locking needs
if (streamLockPair.getSecond() != null)
{
statementContext.getEpStatementHandle().setStatementLock(streamLockPair.getSecond());
}
}
else if (streamSpec instanceof PatternStreamSpecCompiled)
{
PatternStreamSpecCompiled patternStreamSpec = (PatternStreamSpecCompiled) streamSpec;
boolean usedByChildViews = !streamSpec.getViewSpecs().isEmpty() || (statementSpec.getInsertIntoDesc() != null);
final EventType eventType = services.getEventAdapterService().createSemiAnonymousMapType(patternStreamSpec.getTaggedEventTypes(), patternStreamSpec.getArrayEventTypes(), usedByChildViews);
final EventStream sourceEventStream = new ZeroDepthStream(eventType);
eventStreamParentViewable = sourceEventStream;
EvalRootNode rootNode = services.getPatternNodeFactory().makeRootNode();
rootNode.addChildNode(patternStreamSpec.getEvalNode());
PatternMatchCallback callback = new PatternMatchCallback() {
public void matchFound(Map<String, Object> matchEvent)
{
EventBean compositeEvent = statementContext.getEventAdapterService().adaptorForTypedMap(matchEvent, eventType);
sourceEventStream.insert(compositeEvent);
}
};
PatternContext patternContext = statementContext.getPatternContextFactory().createContext(statementContext, 0, rootNode, !patternStreamSpec.getArrayEventTypes().isEmpty());
PatternStopCallback patternStopCallback = rootNode.start(callback, patternContext);
stopCallbacks.add(patternStopCallback);
}
else if (streamSpec instanceof NamedWindowConsumerStreamSpec)
{
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
eventStreamParentViewable = processor.addConsumer(namedSpec.getFilterExpressions(), namedSpec.getOptPropertyEvaluator(), statementContext.getEpStatementHandle(), statementContext.getStatementStopService());
triggereventTypeName = namedSpec.getWindowName();
}
else
{
throw new ExprValidationException("Unknown stream specification type: " + streamSpec);
}
// create stop method using statement stream specs
EPStatementStopMethod stopMethod = new EPStatementStopMethod()
{
public void stop()
{
statementContext.getStatementStopService().fireStatementStopped();
if (streamSpec instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) streamSpec;
boolean filterSubselectSameStream = determineSubquerySameStream(filterStreamSpec);
services.getStreamService().dropStream(filterStreamSpec.getFilterSpec(), statementContext.getFilterService(), false, false, true, filterSubselectSameStream);
}
for (StopCallback stopCallback : stopCallbacks)
{
stopCallback.stop();
}
}
};
View onExprView;
try {
final EventType streamEventType = eventStreamParentViewable.getEventType();
ResultSetProcessor resultSetProcessor;
// For on-delete and on-select and on-update triggers
if (statementSpec.getOnTriggerDesc() instanceof OnTriggerWindowDesc)
{
// Determine event types
OnTriggerWindowDesc onTriggerDesc = (OnTriggerWindowDesc) statementSpec.getOnTriggerDesc();
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(onTriggerDesc.getWindowName());
EventType namedWindowType = processor.getNamedWindowType();
statementContext.getDynamicReferenceEventTypes().add(onTriggerDesc.getWindowName());
String namedWindowName = onTriggerDesc.getOptionalAsName();
if (namedWindowName == null)
{
namedWindowName = "stream_0";
}
String streamName = streamSpec.getOptionalStreamName();
if (streamName == null)
{
streamName = "stream_1";
}
String namedWindowTypeName = onTriggerDesc.getWindowName();
// Materialize sub-select views
// 0 - named window stream
// 1 - arriving stream
startSubSelect(subSelectStreamDesc, new String[]{namedWindowName, streamSpec.getOptionalStreamName()}, new EventType[] {processor.getNamedWindowType(), streamEventType}, new String[]{namedWindowTypeName, triggereventTypeName}, stopCallbacks, statementSpec.getAnnotations(), statementSpec.getDeclaredExpressions());
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] {namedWindowType, streamEventType}, new String[] {namedWindowName, streamName}, new boolean[] {false, true}, services.getEngineURI(), false);
if (onTriggerDesc instanceof OnTriggerWindowUpdateDesc) {
OnTriggerWindowUpdateDesc updateDesc = (OnTriggerWindowUpdateDesc) onTriggerDesc;
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
for (OnTriggerSetAssignment assignment : updateDesc.getAssignments())
{
ExprNode validated = ExprNodeUtil.getValidatedSubtree(assignment.getExpression(), validationContext);
assignment.setExpression(validated);
validateNoAggregations(validated, "Aggregation functions may not be used within an on-update-clause");
}
}
if (onTriggerDesc instanceof OnTriggerMergeDesc) {
OnTriggerMergeDesc mergeDesc = (OnTriggerMergeDesc) onTriggerDesc;
validateMergeDesc(mergeDesc, statementContext, processor.getNamedWindowType(), namedWindowName, streamEventType, streamName);
}
// validate join expression
ExprNode validatedJoin = validateJoinNamedWindow(statementSpec.getFilterRootNode(),
namedWindowType, namedWindowName, namedWindowTypeName,
streamEventType, streamName, triggereventTypeName);
// validate filter, output rate limiting
validateNodes(statementSpec, statementContext, typeService, null);
// Construct a processor for results; for use in on-select to process selection results
// Use a wildcard select if the select-clause is empty, such as for on-delete.
// For on-select the select clause is not empty.
if (statementSpec.getSelectClauseSpec().getSelectExprList().size() == 0)
{
statementSpec.getSelectClauseSpec().add(new SelectClauseElementWildcard());
}
resultSetProcessor = ResultSetProcessorFactory.getProcessor(
statementSpec, statementContext, typeService, null, new boolean[0], true);
InternalEventRouter routerService = null;
boolean addToFront = false;
if (statementSpec.getInsertIntoDesc() != null || onTriggerDesc instanceof OnTriggerMergeDesc) {
routerService = services.getInternalEventRouter();
}
if (statementSpec.getInsertIntoDesc() != null) {
addToFront = statementContext.getNamedWindowService().isNamedWindow(statementSpec.getInsertIntoDesc().getEventTypeName());
}
onExprView = processor.addOnExpr(onTriggerDesc, validatedJoin, streamEventType, streamSpec.getOptionalStreamName(), statementContext.getStatementStopService(), routerService, addToFront, resultSetProcessor, statementContext.getEpStatementHandle(), statementContext.getStatementResultService(), statementContext, statementSpec.getSelectClauseSpec().isDistinct());
eventStreamParentViewable.addView(onExprView);
}
// variable assignments
else if (statementSpec.getOnTriggerDesc() instanceof OnTriggerSetDesc)
{
OnTriggerSetDesc desc = (OnTriggerSetDesc) statementSpec.getOnTriggerDesc();
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] {streamEventType}, new String[] {streamSpec.getOptionalStreamName()}, new boolean[] {true}, services.getEngineURI(), false);
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
// Materialize sub-select views
startSubSelect(subSelectStreamDesc, new String[]{streamSpec.getOptionalStreamName()}, new EventType[] {streamEventType}, new String[]{triggereventTypeName}, stopCallbacks, statementSpec.getAnnotations(), statementSpec.getDeclaredExpressions());
for (OnTriggerSetAssignment assignment : desc.getAssignments())
{
ExprNode validated = ExprNodeUtil.getValidatedSubtree(assignment.getExpression(), validationContext);
assignment.setExpression(validated);
}
try {
onExprView = new OnSetVariableView(desc, statementContext.getEventAdapterService(), statementContext.getVariableService(), statementContext.getStatementResultService(), statementContext);
}
catch (VariableValueException ex) {
throw new ExprValidationException("Error in variable assignment: " + ex.getMessage(), ex);
}
eventStreamParentViewable.addView(onExprView);
}
// split-stream use case
else
{
OnTriggerSplitStreamDesc desc = (OnTriggerSplitStreamDesc) statementSpec.getOnTriggerDesc();
String streamName = streamSpec.getOptionalStreamName();
if (streamName == null)
{
streamName = "stream_0";
}
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] {streamEventType}, new String[] {streamName}, new boolean[] {true}, services.getEngineURI(), false);
if (statementSpec.getInsertIntoDesc() == null)
{
throw new ExprValidationException("Required insert-into clause is not provided, the clause is required for split-stream syntax");
}
if ((!statementSpec.getGroupByExpressions().isEmpty()) || (statementSpec.getHavingExprRootNode() != null) || (!statementSpec.getOrderByList().isEmpty()))
{
throw new ExprValidationException("A group-by clause, having-clause or order-by clause is not allowed for the split stream syntax");
}
// Materialize sub-select views
startSubSelect(subSelectStreamDesc, new String[]{streamSpec.getOptionalStreamName()}, new EventType[] {streamEventType}, new String[]{triggereventTypeName}, stopCallbacks, statementSpec.getAnnotations(), statementSpec.getDeclaredExpressions());
validateNodes(statementSpec, statementContext, typeService, null);
ResultSetProcessor[] processors = new ResultSetProcessor[desc.getSplitStreams().size() + 1];
ExprNode[] whereClauses = new ExprNode[desc.getSplitStreams().size() + 1];
processors[0] = ResultSetProcessorFactory.getProcessor(
statementSpec, statementContext, typeService, null, new boolean[0], false);
whereClauses[0] = statementSpec.getFilterRootNode();
boolean[] isNamedWindowInsert = new boolean[desc.getSplitStreams().size() + 1];
isNamedWindowInsert[0] = false;
int index = 1;
for (OnTriggerSplitStream splits : desc.getSplitStreams())
{
StatementSpecCompiled splitSpec = new StatementSpecCompiled();
splitSpec.setInsertIntoDesc(splits.getInsertInto());
splitSpec.setSelectClauseSpec(StatementLifecycleSvcImpl.compileSelectAllowSubselect(splits.getSelectClause()));
splitSpec.setFilterExprRootNode(splits.getWhereClause());
validateNodes(splitSpec, statementContext, typeService, null);
processors[index] = ResultSetProcessorFactory.getProcessor(
splitSpec, statementContext, typeService, null, new boolean[0], false);
whereClauses[index] = splitSpec.getFilterRootNode();
isNamedWindowInsert[index] = statementContext.getNamedWindowService().isNamedWindow(splits.getInsertInto().getEventTypeName());
index++;
}
onExprView = new RouteResultView(desc.isFirst(), streamEventType, statementContext.getEpStatementHandle(), services.getInternalEventRouter(), isNamedWindowInsert, processors, whereClauses, statementContext);
eventStreamParentViewable.addView(onExprView);
}
// For on-delete, create an output processor that passes on as a wildcard the underlying event
if ((statementSpec.getOnTriggerDesc().getOnTriggerType() == OnTriggerType.ON_DELETE) ||
(statementSpec.getOnTriggerDesc().getOnTriggerType() == OnTriggerType.ON_SET) ||
(statementSpec.getOnTriggerDesc().getOnTriggerType() == OnTriggerType.ON_UPDATE) ||
(statementSpec.getOnTriggerDesc().getOnTriggerType() == OnTriggerType.ON_MERGE))
{
StatementSpecCompiled defaultSelectAllSpec = new StatementSpecCompiled();
defaultSelectAllSpec.getSelectClauseSpec().add(new SelectClauseElementWildcard());
StreamTypeService streamTypeService = new StreamTypeServiceImpl(new EventType[] {onExprView.getEventType()}, new String[] {"trigger_stream"}, new boolean[] {true}, services.getEngineURI(), false);
ResultSetProcessor outputResultSetProcessor = ResultSetProcessorFactory.getProcessor(
defaultSelectAllSpec, statementContext, streamTypeService, null, new boolean[0], true);
// Attach output view
OutputProcessView outputView = OutputProcessViewFactory.makeView(outputResultSetProcessor, defaultSelectAllSpec, statementContext, services.getInternalEventRouter());
onExprView.addView(outputView);
onExprView = outputView;
}
}
catch (ExprValidationException ex) {
handleException(stopMethod);
throw ex;
}
catch (RuntimeException ex) {
handleException(stopMethod);
throw ex;
}
log.debug(".start Statement start completed");
return new EPStatementStartResult(onExprView, stopMethod);
}
private ExprNode validateExprNoAgg(ExprNode exprNode, StreamTypeService streamTypeService, StatementContext statementContext, String errorMsg) throws ExprValidationException {
ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
ExprNode validated = ExprNodeUtil.getValidatedSubtree(exprNode, validationContext);
validateNoAggregations(validated, errorMsg);
return validated;
}
private void validateMergeDesc(OnTriggerMergeDesc mergeDesc, StatementContext statementContext, EventType namedWindowType, String namedWindowName, EventType triggerStreamType, String triggerStreamName)
throws ExprValidationException
{
String exprNodeErrorMessage = "Aggregation functions may not be used within an merge-clause";
for (OnTriggerMergeMatched matchedItem : mergeDesc.getItems()) {
EventType dummyTypeNoProperties = new MapEventType(EventTypeMetadata.createAnonymous("merge_named_window_insert"), "merge_named_window_insert", null, Collections.<String, Object>emptyMap(), null, null);
StreamTypeService twoStreamTypeSvc = new StreamTypeServiceImpl(new EventType[] {namedWindowType, triggerStreamType},
new String[] {namedWindowName, triggerStreamName}, new boolean[] {true, true}, statementContext.getEngineURI(), false);
StreamTypeService insertOnlyTypeSvc = new StreamTypeServiceImpl(new EventType[] {dummyTypeNoProperties, triggerStreamType},
new String[] {UuidGenerator.generate(), triggerStreamName}, new boolean[] {true, true}, statementContext.getEngineURI(), false);
if (matchedItem.getOptionalMatchCond() != null) {
StreamTypeService matchValidStreams = matchedItem.isMatchedUnmatched() ? twoStreamTypeSvc : insertOnlyTypeSvc;
matchedItem.setOptionalMatchCond(validateExprNoAgg(matchedItem.getOptionalMatchCond(), matchValidStreams, statementContext, exprNodeErrorMessage));
}
for (OnTriggerMergeAction item : matchedItem.getActions()) {
if (item instanceof OnTriggerMergeActionDelete) {
OnTriggerMergeActionDelete delete = (OnTriggerMergeActionDelete) item;
if (delete.getOptionalWhereClause() != null) {
delete.setOptionalWhereClause(validateExprNoAgg(delete.getOptionalWhereClause(), twoStreamTypeSvc, statementContext, exprNodeErrorMessage));
}
}
else if (item instanceof OnTriggerMergeActionUpdate) {
OnTriggerMergeActionUpdate update = (OnTriggerMergeActionUpdate) item;
if (update.getOptionalWhereClause() != null) {
update.setOptionalWhereClause(validateExprNoAgg(update.getOptionalWhereClause(), twoStreamTypeSvc, statementContext, exprNodeErrorMessage));
}
for (OnTriggerSetAssignment assignment : update.getAssignments())
{
assignment.setExpression(validateExprNoAgg(assignment.getExpression(), twoStreamTypeSvc, statementContext, exprNodeErrorMessage));
}
}
else if (item instanceof OnTriggerMergeActionInsert) {
OnTriggerMergeActionInsert insert = (OnTriggerMergeActionInsert) item;
List<SelectClauseElementCompiled> compiledSelect = new ArrayList<SelectClauseElementCompiled>();
if (insert.getOptionalWhereClause() != null) {
insert.setOptionalWhereClause(validateExprNoAgg(insert.getOptionalWhereClause(), insertOnlyTypeSvc, statementContext, exprNodeErrorMessage));
}
int colIndex = 0;
int contentStreamNumber = 1;
for (SelectClauseElementRaw raw : insert.getSelectClause())
{
if (raw instanceof SelectClauseStreamRawSpec)
{
SelectClauseStreamRawSpec rawStreamSpec = (SelectClauseStreamRawSpec) raw;
if (!rawStreamSpec.getStreamName().equals(insertOnlyTypeSvc.getStreamNames()[contentStreamNumber]))
{
throw new ExprValidationException("Stream by name '" + rawStreamSpec.getStreamName() + "' was not found");
}
SelectClauseStreamCompiledSpec streamSelectSpec = new SelectClauseStreamCompiledSpec(rawStreamSpec.getStreamName(), rawStreamSpec.getOptionalAsName());
streamSelectSpec.setStreamNumber(contentStreamNumber);
compiledSelect.add(streamSelectSpec);
}
else if (raw instanceof SelectClauseExprRawSpec)
{
SelectClauseExprRawSpec exprSpec = (SelectClauseExprRawSpec) raw;
ExprValidationContext validationContext = new ExprValidationContext(insertOnlyTypeSvc, statementContext.getMethodResolutionService(), null, statementContext.getTimeProvider(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
ExprNode exprCompiled = ExprNodeUtil.getValidatedSubtree(exprSpec.getSelectExpression(), validationContext);
String resultName = exprSpec.getOptionalAsName();
if (resultName == null)
{
if (insert.getColumns().size() > colIndex) {
resultName = insert.getColumns().get(colIndex);
}
else {
resultName = exprCompiled.toExpressionString();
}
}
compiledSelect.add(new SelectClauseExprCompiledSpec(exprCompiled, resultName));
validateNoAggregations(exprCompiled, "Expression in a merge-selection may not utilize aggregation functions");
}
else if (raw instanceof SelectClauseElementWildcard)
{
compiledSelect.add(new SelectClauseElementWildcard());
}
else
{
throw new IllegalStateException("Unknown select clause item:" + raw);
}
colIndex++;
}
insert.setSelectClauseCompiled(compiledSelect);
}
else {
throw new IllegalArgumentException("Unrecognized merge item '" + item.getClass().getName() + "'");
}
}
}
}
private EPStatementStartResult startUpdate()
throws ExprValidationException, ViewProcessingException
{
final List<StopCallback> stopCallbacks = new LinkedList<StopCallback>();
// First we create streams for subselects, if there are any
SubSelectStreamCollection subSelectStreamDesc = createSubSelectStreams(false, statementSpec.getAnnotations());
final StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(0);
final UpdateDesc desc = statementSpec.getUpdateSpec();
String triggereventTypeName;
if (streamSpec instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) streamSpec;
triggereventTypeName = filterStreamSpec.getFilterSpec().getFilterForEventTypeName();
}
else if (streamSpec instanceof NamedWindowConsumerStreamSpec)
{
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;
triggereventTypeName = namedSpec.getWindowName();
}
else
{
throw new ExprValidationException("Unknown stream specification streamEventType: " + streamSpec);
}
// determine a stream name
String streamName = triggereventTypeName;
if (desc.getOptionalStreamName() != null)
{
streamName = desc.getOptionalStreamName();
}
final EventType streamEventType = services.getEventAdapterService().getExistsTypeByName(triggereventTypeName);
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] {streamEventType}, new String[] {streamName}, new boolean[] {true}, services.getEngineURI(), false);
// determine subscriber result types
statementContext.getStatementResultService().setSelectClause(new Class[] {streamEventType.getUnderlyingType()}, new String[] {"*"}, false, null, statementContext);
// Materialize sub-select views
startSubSelect(subSelectStreamDesc, new String[]{streamName}, new EventType[] {streamEventType}, new String[]{triggereventTypeName}, stopCallbacks, statementSpec.getAnnotations(), statementSpec.getDeclaredExpressions());
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
for (OnTriggerSetAssignment assignment : desc.getAssignments())
{
ExprNode validated = ExprNodeUtil.getValidatedSubtree(assignment.getExpression(), validationContext);
assignment.setExpression(validated);
validateNoAggregations(validated, "Aggregation functions may not be used within an update-clause");
}
if (desc.getOptionalWhereClause() != null)
{
ExprNode validated = ExprNodeUtil.getValidatedSubtree(desc.getOptionalWhereClause(), validationContext);
desc.setOptionalWhereClause(validated);
validateNoAggregations(validated, "Aggregation functions may not be used within an update-clause");
}
InternalRoutePreprocessView onExprView = new InternalRoutePreprocessView(streamEventType, statementContext.getStatementResultService());
services.getInternalEventRouter().addPreprocessing(streamEventType, desc, statementSpec.getAnnotations(), onExprView);
stopCallbacks.add(new StopCallback()
{
public void stop()
{
services.getInternalEventRouter().removePreprocessing(streamEventType, desc);
}
});
EPStatementStopMethod stopMethod = new EPStatementStopMethod()
{
public void stop()
{
statementContext.getStatementStopService().fireStatementStopped();
for (StopCallback stopCallback : stopCallbacks)
{
stopCallback.stop();
}
}
};
return new EPStatementStartResult(onExprView, stopMethod);
}
private EPStatementStartResult startCreateWindow(boolean isNewStatement, boolean isRecoveringStatement)
throws ExprValidationException, ViewProcessingException
{
final FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) statementSpec.getStreamSpecs().get(0);
String windowName = statementSpec.getCreateWindowDesc().getWindowName();
EventType windowType = filterStreamSpec.getFilterSpec().getFilterForEventType();
// Create streams and views
Viewable eventStreamParentViewable;
ViewFactoryChain unmaterializedViewChain;
// Create view factories and parent view based on a filter specification
// Since only for non-joins we get the existing stream's lock and try to reuse it's views
boolean filterSubselectSameStream = determineSubquerySameStream(filterStreamSpec);
Pair<EventStream, StatementLock> streamLockPair = services.getStreamService().createStream(statementContext.getStatementId(), filterStreamSpec.getFilterSpec(),
statementContext.getFilterService(), statementContext.getEpStatementHandle(), false, false, statementContext, true, filterSubselectSameStream, statementContext.getAnnotations());
eventStreamParentViewable = streamLockPair.getFirst();
// Use the re-used stream's lock for all this statement's locking needs
if (streamLockPair.getSecond() != null)
{
statementContext.getEpStatementHandle().setStatementLock(streamLockPair.getSecond());
}
// Create data window view factories
unmaterializedViewChain = services.getViewService().createFactories(0, eventStreamParentViewable.getEventType(), filterStreamSpec.getViewSpecs(), filterStreamSpec.getOptions(), statementContext);
ValueAddEventProcessor optionalRevisionProcessor = statementContext.getValueAddEventService().getValueAddProcessor(windowName);
boolean isPrioritized = services.getEngineSettingsService().getEngineSettings().getExecution().isPrioritized();
boolean isEnableSubqueryIndexShare = HintEnum.ENABLE_WINDOW_SUBQUERY_INDEXSHARE.getHint(statementSpec.getAnnotations()) != null;
if (!isEnableSubqueryIndexShare && unmaterializedViewChain.getViewFactoryChain().get(0) instanceof VirtualDWViewFactory) {
isEnableSubqueryIndexShare = true; // index share is always enabled for virtual data window (otherwise it wouldn't make sense)
}
services.getNamedWindowService().addProcessor(windowName, windowType, statementContext.getEpStatementHandle(), statementContext.getStatementResultService(), optionalRevisionProcessor, statementContext.getExpression(), statementContext.getStatementName(), isPrioritized, statementContext, isEnableSubqueryIndexShare);
// The root view of the named window
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(statementSpec.getCreateWindowDesc().getWindowName());
View rootView = processor.getRootView();
eventStreamParentViewable.addView(rootView);
// request remove stream capability from views
ViewResourceDelegate viewResourceDelegate = new ViewResourceDelegateImpl(new ViewFactoryChain[] {unmaterializedViewChain}, statementContext);
if (!viewResourceDelegate.requestCapability(0, new RemoveStreamViewCapability(false), null))
{
throw new ExprValidationException(NamedWindowService.ERROR_MSG_DATAWINDOWS);
}
// Materialize views
Viewable finalView = services.getViewService().createViews(rootView, unmaterializedViewChain.getViewFactoryChain(), statementContext);
// If this is a virtual data window implementation, bind it to the context for easy lookup
StopCallback envStopCallback = null;
if (finalView instanceof VirtualDWView) {
final String objectName = "/virtualdw/" + windowName;
final VirtualDWView virtualDWView = (VirtualDWView) finalView;
try {
services.getEngineEnvContext().bind(objectName, virtualDWView.getVirtualDataWindow());
}
catch (NamingException e) {
throw new ViewProcessingException("Invalid name for adding to context:" + e.getMessage(), e);
}
envStopCallback = new StopCallback() {
public void stop() {
try {
virtualDWView.destroy();
services.getEngineEnvContext().unbind(objectName);
} catch (NamingException e) {}
}
};
}
final StopCallback environmentStopCallback = envStopCallback;
// create stop method using statement stream specs
EPStatementStopMethod stopMethod = new EPStatementStopMethod()
{
public void stop()
{
statementContext.getStatementStopService().fireStatementStopped();
boolean filterSubselectSameStream = determineSubquerySameStream(filterStreamSpec);
services.getStreamService().dropStream(filterStreamSpec.getFilterSpec(), statementContext.getFilterService(), false,false, true, filterSubselectSameStream);
String windowName = statementSpec.getCreateWindowDesc().getWindowName();
services.getNamedWindowService().removeProcessor(windowName);
if (environmentStopCallback != null) {
environmentStopCallback.stop();
}
}
};
// Attach tail view
boolean isBatchView = finalView instanceof BatchingDataWindowView;
NamedWindowTailView tailView = processor.getTailView();
tailView.setBatchView(isBatchView);
processor.getRootView().setBatchView(isBatchView);
finalView.addView(tailView);
finalView = tailView;
// Add a wildcard to the select clause as subscribers received the window contents
statementSpec.getSelectClauseSpec().getSelectExprList().clear();
statementSpec.getSelectClauseSpec().add(new SelectClauseElementWildcard());
statementSpec.setSelectStreamDirEnum(SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH);
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] {windowType}, new String[] {windowName}, new boolean[] {true}, services.getEngineURI(), false);
ResultSetProcessor resultSetProcessor = ResultSetProcessorFactory.getProcessor(
statementSpec, statementContext, typeService, null, new boolean[0], true);
// Attach output view
OutputProcessView outputView = OutputProcessViewFactory.makeView(resultSetProcessor, statementSpec, statementContext, services.getInternalEventRouter());
finalView.addView(outputView);
finalView = outputView;
// Handle insert case
if (statementSpec.getCreateWindowDesc().isInsert() && !isRecoveringStatement)
{
String insertFromWindow = statementSpec.getCreateWindowDesc().getInsertFromWindow();
NamedWindowProcessor sourceWindow = services.getNamedWindowService().getProcessor(insertFromWindow);
List<EventBean> events = new ArrayList<EventBean>();
if (statementSpec.getCreateWindowDesc().getInsertFilter() != null)
{
EventBean[] eventsPerStream = new EventBean[1];
ExprEvaluator filter = statementSpec.getCreateWindowDesc().getInsertFilter().getExprEvaluator();
for (Iterator<EventBean> it = sourceWindow.getTailView().iterator(); it.hasNext();)
{
EventBean candidate = it.next();
eventsPerStream[0] = candidate;
Boolean result = (Boolean) filter.evaluate(eventsPerStream, true, statementContext);
if ((result == null) || (!result))
{
continue;
}
events.add(candidate);
}
}
else
{
for (Iterator<EventBean> it = sourceWindow.getTailView().iterator(); it.hasNext();)
{
events.add(it.next());
}
}
if (events.size() > 0)
{
EventType rootViewType = rootView.getEventType();
EventBean[] convertedEvents = services.getEventAdapterService().typeCast(events, rootViewType);
rootView.update(convertedEvents, null);
}
}
log.debug(".start Statement start completed");
return new EPStatementStartResult(finalView, stopMethod);
}
private EPStatementStartResult startCreateVariable(boolean isNewStatement)
throws ExprValidationException, ViewProcessingException
{
final CreateVariableDesc createDesc = statementSpec.getCreateVariableDesc();
// Get assignment value
Object value = null;
if (createDesc.getAssignment() != null)
{
// Evaluate assignment expression
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[0], new String[0], new boolean[0], services.getEngineURI(), false);
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
ExprNode validated = ExprNodeUtil.getValidatedSubtree(createDesc.getAssignment(), validationContext);
value = validated.getExprEvaluator().evaluate(null, true, statementContext);
}
// Create variable
try
{
services.getVariableService().createNewVariable(createDesc.getVariableName(), createDesc.getVariableType(), value, statementContext.getExtensionServicesContext());
}
catch (VariableExistsException ex)
{
// for new statement we don't allow creating the same variable
if (isNewStatement)
{
throw new ExprValidationException("Cannot create variable: " + ex.getMessage());
}
}
catch (VariableDeclarationException ex)
{
throw new ExprValidationException("Cannot create variable: " + ex.getMessage());
}
final CreateVariableView createView = new CreateVariableView(services.getEventAdapterService(), services.getVariableService(), createDesc.getVariableName(), statementContext.getStatementResultService());
final int variableNum = services.getVariableService().getReader(createDesc.getVariableName()).getVariableNumber();
services.getVariableService().registerCallback(variableNum, createView);
statementContext.getStatementStopService().addSubscriber(new StatementStopCallback() {
public void statementStopped()
{
services.getVariableService().unregisterCallback(variableNum, createView);
}
});
// Create result set processor, use wildcard selection
statementSpec.getSelectClauseSpec().getSelectExprList().clear();
statementSpec.getSelectClauseSpec().add(new SelectClauseElementWildcard());
statementSpec.setSelectStreamDirEnum(SelectClauseStreamSelectorEnum.RSTREAM_ISTREAM_BOTH);
StreamTypeService typeService = new StreamTypeServiceImpl(new EventType[] {createView.getEventType()}, new String[] {"create_variable"}, new boolean[] {true}, services.getEngineURI(), false);
ResultSetProcessor resultSetProcessor = ResultSetProcessorFactory.getProcessor(
statementSpec, statementContext, typeService, null, new boolean[0], true);
// Attach output view
OutputProcessView outputView = OutputProcessViewFactory.makeView(resultSetProcessor, statementSpec, statementContext, services.getInternalEventRouter());
createView.addView(outputView);
services.getStatementVariableRefService().addReferences(statementContext.getStatementName(), Collections.singleton(createDesc.getVariableName()));
EPStatementDestroyMethod destroyMethod = new EPStatementDestroyMethod() {
public void destroy() {
try {
services.getStatementVariableRefService().removeReferencesStatement(statementContext.getStatementName());
}
catch (RuntimeException ex) {
log.error("Error removing variable '" + createDesc.getVariableName() + "': " + ex.getMessage());
}
}
};
EPStatementStopMethod stopMethod = new EPStatementStopMethod(){
public void stop()
{
}
};
return new EPStatementStartResult(outputView, stopMethod, destroyMethod);
}
private EPStatementStartResult startSelect(boolean isRecoveringResilient)
throws ExprValidationException, ViewProcessingException
{
// Determine stream names for each stream - some streams may not have a name given
String[] streamNames = determineStreamNames(statementSpec.getStreamSpecs());
final boolean isJoin = statementSpec.getStreamSpecs().size() > 1;
// First we create streams for subselects, if there are any
SubSelectStreamCollection subSelectStreamDesc = createSubSelectStreams(isJoin, statementSpec.getAnnotations());
int numStreams = streamNames.length;
final List<StopCallback> stopCallbacks = new LinkedList<StopCallback>();
// Create streams and views
Viewable[] eventStreamParentViewable = new Viewable[numStreams];
ViewFactoryChain[] unmaterializedViewChain = new ViewFactoryChain[numStreams];
String[] eventTypeNamees = new String[numStreams];
boolean[] isNamedWindow = new boolean[numStreams];
// verify for joins that required views are present
StreamJoinAnalysisResult joinAnalysisResult = verifyJoinViews(statementSpec.getStreamSpecs(), statementContext.getNamedWindowService());
for (int i = 0; i < statementSpec.getStreamSpecs().size(); i++)
{
StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(i);
// Create view factories and parent view based on a filter specification
if (streamSpec instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) streamSpec;
eventTypeNamees[i] = filterStreamSpec.getFilterSpec().getFilterForEventTypeName();
// Since only for non-joins we get the existing stream's lock and try to reuse it's views
boolean filterSubselectSameStream = determineSubquerySameStream(filterStreamSpec);
Pair<EventStream, StatementLock> streamLockPair = services.getStreamService().createStream(statementContext.getStatementId(), filterStreamSpec.getFilterSpec(),
statementContext.getFilterService(), statementContext.getEpStatementHandle(), isJoin, false, statementContext, false | !statementSpec.getOrderByList().isEmpty(), filterSubselectSameStream, statementContext.getAnnotations());
eventStreamParentViewable[i] = streamLockPair.getFirst();
// Use the re-used stream's lock for all this statement's locking needs
if (streamLockPair.getSecond() != null)
{
statementContext.getEpStatementHandle().setStatementLock(streamLockPair.getSecond());
}
unmaterializedViewChain[i] = services.getViewService().createFactories(i, eventStreamParentViewable[i].getEventType(), streamSpec.getViewSpecs(), streamSpec.getOptions(), statementContext);
}
// Create view factories and parent view based on a pattern expression
else if (streamSpec instanceof PatternStreamSpecCompiled)
{
PatternStreamSpecCompiled patternStreamSpec = (PatternStreamSpecCompiled) streamSpec;
boolean usedByChildViews = !streamSpec.getViewSpecs().isEmpty() || (statementSpec.getInsertIntoDesc() != null);
final EventType eventType = services.getEventAdapterService().createSemiAnonymousMapType(patternStreamSpec.getTaggedEventTypes(), patternStreamSpec.getArrayEventTypes(), usedByChildViews);
final EventStream sourceEventStream = new ZeroDepthStream(eventType);
eventStreamParentViewable[i] = sourceEventStream;
unmaterializedViewChain[i] = services.getViewService().createFactories(i, sourceEventStream.getEventType(), streamSpec.getViewSpecs(), streamSpec.getOptions(), statementContext);
EvalRootNode rootNode = services.getPatternNodeFactory().makeRootNode();
rootNode.addChildNode(patternStreamSpec.getEvalNode());
PatternMatchCallback callback = new PatternMatchCallback() {
public void matchFound(Map<String, Object> matchEvent)
{
EventBean compositeEvent = statementContext.getEventAdapterService().adaptorForTypedMap(matchEvent, eventType);
sourceEventStream.insert(compositeEvent);
}
};
PatternContext patternContext = statementContext.getPatternContextFactory().createContext(statementContext,
i, rootNode, !patternStreamSpec.getArrayEventTypes().isEmpty());
PatternStopCallback patternStopCallback = rootNode.start(callback, patternContext);
stopCallbacks.add(patternStopCallback);
}
// Create view factories and parent view based on a database SQL statement
else if (streamSpec instanceof DBStatementStreamSpec)
{
if (!streamSpec.getViewSpecs().isEmpty())
{
throw new ExprValidationException("Historical data joins do not allow views onto the data, view '"
+ streamSpec.getViewSpecs().get(0).getObjectNamespace() + ':' + streamSpec.getViewSpecs().get(0).getObjectName() + "' is not valid in this context");
}
DBStatementStreamSpec sqlStreamSpec = (DBStatementStreamSpec) streamSpec;
SQLColumnTypeConversion typeConversionHook = (SQLColumnTypeConversion) JavaClassHelper.getAnnotationHook(statementSpec.getAnnotations(), HookType.SQLCOL, SQLColumnTypeConversion.class, statementContext.getMethodResolutionService());
SQLOutputRowConversion outputRowConversionHook = (SQLOutputRowConversion) JavaClassHelper.getAnnotationHook(statementSpec.getAnnotations(), HookType.SQLROW, SQLOutputRowConversion.class, statementContext.getMethodResolutionService());
HistoricalEventViewable historicalEventViewable = DatabasePollingViewableFactory.createDBStatementView(i, sqlStreamSpec, services.getDatabaseRefService(), services.getEventAdapterService(), statementContext.getEpStatementHandle(), typeConversionHook, outputRowConversionHook,
statementContext.getConfigSnapshot().getEngineDefaults().getLogging().isEnableJDBC());
unmaterializedViewChain[i] = new ViewFactoryChain(historicalEventViewable.getEventType(), new LinkedList<ViewFactory>());
eventStreamParentViewable[i] = historicalEventViewable;
stopCallbacks.add(historicalEventViewable);
}
else if (streamSpec instanceof MethodStreamSpec)
{
if (!streamSpec.getViewSpecs().isEmpty())
{
throw new ExprValidationException("Method data joins do not allow views onto the data, view '"
+ streamSpec.getViewSpecs().get(0).getObjectNamespace() + ':' + streamSpec.getViewSpecs().get(0).getObjectName() + "' is not valid in this context");
}
MethodStreamSpec methodStreamSpec = (MethodStreamSpec) streamSpec;
HistoricalEventViewable historicalEventViewable = MethodPollingViewableFactory.createPollMethodView(i, methodStreamSpec, services.getEventAdapterService(), statementContext.getEpStatementHandle(), statementContext.getMethodResolutionService(), services.getEngineImportService(), statementContext.getSchedulingService(), statementContext.getScheduleBucket(), statementContext);
unmaterializedViewChain[i] = new ViewFactoryChain(historicalEventViewable.getEventType(), new LinkedList<ViewFactory>());
eventStreamParentViewable[i] = historicalEventViewable;
stopCallbacks.add(historicalEventViewable);
}
else if (streamSpec instanceof NamedWindowConsumerStreamSpec)
{
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
NamedWindowConsumerView consumerView = processor.addConsumer(namedSpec.getFilterExpressions(), namedSpec.getOptPropertyEvaluator(), statementContext.getEpStatementHandle(), statementContext.getStatementStopService());
eventStreamParentViewable[i] = consumerView;
unmaterializedViewChain[i] = services.getViewService().createFactories(i, consumerView.getEventType(), namedSpec.getViewSpecs(), namedSpec.getOptions(), statementContext);
joinAnalysisResult.setNamedWindow(i);
eventTypeNamees[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
ViewResourceDelegate viewResourceDelegate = new ViewResourceDelegateImpl(unmaterializedViewChain, statementContext);
viewResourceDelegate.requestCapability(i, new NotADataWindowViewCapability(), null);
}
else
{
throw new ExprValidationException("Unknown stream specification type: " + streamSpec);
}
}
if (statementSpec.getMatchRecognizeSpec() != null)
{
if (isJoin)
{
throw new ExprValidationException("Joins are not allowed when using match recognize");
}
boolean isUnbound = (unmaterializedViewChain[0].getViewFactoryChain().isEmpty()) && (!(statementSpec.getStreamSpecs().get(0) instanceof NamedWindowConsumerStreamSpec));
EventRowRegexNFAViewFactory factory = new EventRowRegexNFAViewFactory(unmaterializedViewChain[0], statementSpec.getMatchRecognizeSpec(), statementContext, isUnbound, statementSpec.getAnnotations());
unmaterializedViewChain[0].getViewFactoryChain().add(factory);
}
// Obtain event types from ViewFactoryChains
EventType[] streamEventTypes = new EventType[statementSpec.getStreamSpecs().size()];
for (int i = 0; i < unmaterializedViewChain.length; i++)
{
streamEventTypes[i] = unmaterializedViewChain[i].getEventType();
}
// Materialize sub-select views
startSubSelect(subSelectStreamDesc, streamNames, streamEventTypes, eventTypeNamees, stopCallbacks, statementSpec.getAnnotations(), statementSpec.getDeclaredExpressions());
// List of statement streams
final List<StreamSpecCompiled> statementStreamSpecs = new ArrayList<StreamSpecCompiled>();
statementStreamSpecs.addAll(statementSpec.getStreamSpecs());
// Construct type information per stream
StreamTypeService typeService = new StreamTypeServiceImpl(streamEventTypes, streamNames, getHasIStreamOnly(isNamedWindow, unmaterializedViewChain), services.getEngineURI(), false);
ViewResourceDelegate viewResourceDelegate = new ViewResourceDelegateImpl(unmaterializedViewChain, statementContext);
// boolean multiple expiry policy
for (int i = 0; i < unmaterializedViewChain.length; i++)
{
if (unmaterializedViewChain[i].getDataWindowViewFactoryCount() > 1)
{
if (!viewResourceDelegate.requestCapability(i, new RemoveStreamViewCapability(true), null))
{
log.warn("Combination of multiple data window expiry policies with views that do not support remove streams is not allowed");
}
}
}
// create stop method using statement stream specs
EPStatementStopMethod stopMethod = new EPStatementStopMethod()
{
public void stop()
{
statementContext.getStatementStopService().fireStatementStopped();
for (StreamSpecCompiled streamSpec : statementStreamSpecs)
{
if (streamSpec instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) streamSpec;
boolean filterSubselectSameStream = determineSubquerySameStream(filterStreamSpec);
services.getStreamService().dropStream(filterStreamSpec.getFilterSpec(), statementContext.getFilterService(), isJoin, false, false | !statementSpec.getOrderByList().isEmpty(), filterSubselectSameStream);
}
}
for (StopCallback stopCallback : stopCallbacks)
{
stopCallback.stop();
}
for (ExprSubselectNode subselect : statementSpec.getSubSelectExpressions())
{
StreamSpecCompiled subqueryStreamSpec = subselect.getStatementSpecCompiled().getStreamSpecs().get(0);
if (subqueryStreamSpec instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) subselect.getStatementSpecCompiled().getStreamSpecs().get(0);
services.getStreamService().dropStream(filterStreamSpec.getFilterSpec(), statementContext.getFilterService(), isJoin, true, false, false);
}
}
}
};
Viewable finalView;
try {
// Validate views that require validation, specifically streams that don't have
// sub-views such as DB SQL joins
for (int stream = 0; stream < eventStreamParentViewable.length; stream++)
{
Viewable viewable = eventStreamParentViewable[stream];
if (viewable instanceof ValidatedView)
{
ValidatedView validatedView = (ValidatedView) viewable;
validatedView.validate(services.getEngineImportService(),
typeService,
statementContext.getMethodResolutionService(),
statementContext.getTimeProvider(),
statementContext.getVariableService(), statementContext,
services.getConfigSnapshot(), services.getSchedulingService(), services.getEngineURI(),
statementSpec.getSqlParameters(),
statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
}
if (viewable instanceof HistoricalEventViewable)
{
HistoricalEventViewable historicalView = (HistoricalEventViewable) viewable;
if (historicalView.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");
}
}
}
// 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.
ResultSetProcessor resultSetProcessor = ResultSetProcessorFactory.getProcessor(
statementSpec, statementContext, typeService, viewResourceDelegate, joinAnalysisResult.getUnidirectionalInd(), true);
// Validate where-clause filter tree, outer join clause and output limit expression
validateNodes(statementSpec, statementContext, typeService, viewResourceDelegate);
// Materialize views
Viewable[] streamViews = new Viewable[streamEventTypes.length];
for (int i = 0; i < streamViews.length; i++)
{
streamViews[i] = services.getViewService().createViews(eventStreamParentViewable[i], unmaterializedViewChain[i].getViewFactoryChain(), statementContext);
}
// For just 1 event stream without joins, handle the one-table process separatly.
JoinPreloadMethod joinPreloadMethod = null;
if (streamNames.length == 1)
{
finalView = handleSimpleSelect(streamViews[0], resultSetProcessor, statementContext);
}
else
{
Pair<Viewable, JoinPreloadMethod> pair = handleJoin(streamNames, streamEventTypes, streamViews, resultSetProcessor, statementSpec.getSelectStreamSelectorEnum(), statementContext, stopCallbacks, joinAnalysisResult);
finalView = pair.getFirst();
joinPreloadMethod = pair.getSecond();
}
// Replay any named window data, for later consumers of named data windows
boolean hasNamedWindow = false;
for (int i = 0; i < statementSpec.getStreamSpecs().size(); i++)
{
StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(i);
if (streamSpec instanceof NamedWindowConsumerStreamSpec)
{
hasNamedWindow = true;
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
NamedWindowTailView consumerView = processor.getTailView();
NamedWindowConsumerView view = (NamedWindowConsumerView) eventStreamParentViewable[i];
// preload view for stream unless the expiry policy is batch window
ArrayList<EventBean> eventsInWindow = new ArrayList<EventBean>();
if (!consumerView.isParentBatchWindow())
{
for (EventBean aConsumerView : consumerView)
{
eventsInWindow.add(aConsumerView);
}
}
if (!eventsInWindow.isEmpty() && !isRecoveringResilient)
{
EventBean[] newEvents = eventsInWindow.toArray(new EventBean[eventsInWindow.size()]);
view.update(newEvents, null);
if (joinPreloadMethod != null && !joinPreloadMethod.isPreloading() && statementContext.getEpStatementHandle().getOptionalDispatchable() != null) {
statementContext.getEpStatementHandle().getOptionalDispatchable().execute(statementContext);
}
}
// in a join, preload indexes, if any
if (joinPreloadMethod != null)
{
joinPreloadMethod.preloadFromBuffer(i);
}
else
{
if (statementContext.getEpStatementHandle().getOptionalDispatchable() != null) {
statementContext.getEpStatementHandle().getOptionalDispatchable().execute(statementContext);
}
}
}
}
// last, for aggregation we need to send the current join results to the result set processor
if ((hasNamedWindow) && (joinPreloadMethod != null) && (!isRecoveringResilient) && resultSetProcessor.hasAggregation())
{
joinPreloadMethod.preloadAggregation(resultSetProcessor);
}
}
catch (ExprValidationException ex) {
handleException(stopMethod);
throw ex;
}
catch (RuntimeException ex) {
handleException(stopMethod);
throw ex;
}
log.debug(".start Statement start completed");
return new EPStatementStartResult(finalView, stopMethod);
}
private void handleException(EPStatementStopMethod stopMethod) {
try {
stopMethod.stop();
}
catch (RuntimeException e) {
log.debug("Failed to perform statement stop for statement '" + this.statementContext.getStatementName() +
"' expression '" + statementContext.getExpression() + "' : " + e.getMessage());
}
}
private boolean determineSubquerySameStream(FilterStreamSpecCompiled filterStreamSpec) {
for (ExprSubselectNode subselect : statementSpec.getSubSelectExpressions()) {
StreamSpecCompiled streamSpec = subselect.getStatementSpecCompiled().getStreamSpecs().get(0);
if (!(streamSpec instanceof FilterStreamSpecCompiled)) {
continue;
}
FilterStreamSpecCompiled filterStream = (FilterStreamSpecCompiled) streamSpec;
EventType typeSubselect = filterStream.getFilterSpec().getFilterForEventType();
EventType typeFiltered = filterStreamSpec.getFilterSpec().getFilterForEventType();
if (EventTypeUtility.isTypeOrSubTypeOf(typeSubselect, typeFiltered) || EventTypeUtility.isTypeOrSubTypeOf(typeFiltered, typeSubselect)) {
return true;
}
}
return false;
}
private boolean[] getHasIStreamOnly(boolean[] isNamedWindow, ViewFactoryChain[] unmaterializedViewChain)
{
boolean[] result = new boolean[unmaterializedViewChain.length];
for (int i = 0; i < unmaterializedViewChain.length; i++) {
if (isNamedWindow[i]) {
continue;
}
result[i] = unmaterializedViewChain[i].getDataWindowViewFactoryCount() == 0;
}
return result;
}
/**
* Joins require a remove stream: therefore a view is required for each stream, since all views post a remove stream.
* <p>
* If a view is polling or unidirectional, it does not require a view.
* @param streamSpecs streams
* @return analysis result
* @throws ExprValidationException if constraints violated
*/
private StreamJoinAnalysisResult verifyJoinViews(List<StreamSpecCompiled> streamSpecs, NamedWindowService namedWindowService)
throws ExprValidationException
{
StreamJoinAnalysisResult analysisResult = new StreamJoinAnalysisResult(streamSpecs.size());
if (streamSpecs.size() < 2)
{
return analysisResult;
}
// Determine if any stream has a unidirectional keyword
// inspect unidirection indicator and named window flags
int unidirectionalStreamNumber = -1;
for (int i = 0; i < statementSpec.getStreamSpecs().size(); i++)
{
StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(i);
if (streamSpec.getOptions().isUnidirectional())
{
analysisResult.setUnidirectionalInd(i);
if (unidirectionalStreamNumber != -1)
{
throw new ExprValidationException("The unidirectional keyword can only apply to one stream in a join");
}
unidirectionalStreamNumber = i;
}
if (!streamSpec.getViewSpecs().isEmpty())
{
analysisResult.setHasChildViews(i);
}
if (streamSpec instanceof NamedWindowConsumerStreamSpec)
{
NamedWindowConsumerStreamSpec nwSpec = (NamedWindowConsumerStreamSpec) streamSpec;
analysisResult.setNamedWindow(i);
NamedWindowProcessor processor = namedWindowService.getProcessor(nwSpec.getWindowName());
if (processor.getRootView().getViews().get(0) instanceof VirtualDWView) {
analysisResult.getViewExternal()[i] = (VirtualDWView) processor.getRootView().getViews().get(0);
}
}
}
if ((unidirectionalStreamNumber != -1) && (analysisResult.getHasChildViews()[unidirectionalStreamNumber]))
{
throw new ExprValidationException("The unidirectional keyword requires that no views are declared onto the stream");
}
analysisResult.setUnidirectionalStreamNumber(unidirectionalStreamNumber);
// count streams that provide data, excluding streams that poll data (DB and method)
int countProviderNonpolling = 0;
for (int i = 0; i < statementSpec.getStreamSpecs().size(); i++)
{
StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(i);
if ((streamSpec instanceof MethodStreamSpec) ||
(streamSpec instanceof DBStatementStreamSpec))
{
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().isEmpty())
{
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().size(); i++)
{
StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs().get(i);
if (!streamSpec.getViewSpecs().isEmpty())
{
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 Pair<Viewable, JoinPreloadMethod> handleJoin(String[] streamNames,
EventType[] streamTypes,
Viewable[] streamViews,
ResultSetProcessor resultSetProcessor,
SelectClauseStreamSelectorEnum selectStreamSelectorEnum,
StatementContext statementContext,
List<StopCallback> stopCallbacks,
StreamJoinAnalysisResult joinAnalysisResult)
throws ExprValidationException
{
// Handle joins
final JoinSetComposer composer = statementContext.getJoinSetComposerFactory().makeComposer(statementSpec.getOuterJoinDescList(), statementSpec.getFilterRootNode(), streamTypes, streamNames, streamViews, selectStreamSelectorEnum, joinAnalysisResult, statementContext, queryPlanLogging, statementContext.getAnnotations());
stopCallbacks.add(new StopCallback(){
public void stop()
{
composer.destroy();
}
});
ExprEvaluator filterEval = statementSpec.getFilterRootNode() == null ? null : statementSpec.getFilterRootNode().getExprEvaluator();
JoinSetFilter filter = new JoinSetFilter(filterEval);
OutputProcessView indicatorView = OutputProcessViewFactory.makeView(resultSetProcessor, statementSpec,
statementContext, services.getInternalEventRouter());
// Create strategy for join execution
JoinExecutionStrategy execution = new JoinExecutionStrategyImpl(composer, filter, indicatorView, statementContext);
// The view needs a reference to the join execution to pull iterator values
indicatorView.setJoinExecutionStrategy(execution);
// Hook up dispatchable with buffer and execution strategy
JoinExecStrategyDispatchable joinStatementDispatch = new JoinExecStrategyDispatchable(execution, statementSpec.getStreamSpecs().size());
statementContext.getEpStatementHandle().setOptionalDispatchable(joinStatementDispatch);
JoinPreloadMethod preloadMethod;
if (joinAnalysisResult.getUnidirectionalStreamNumber() >= 0)
{
preloadMethod = new JoinPreloadMethodNull();
}
else
{
preloadMethod = new JoinPreloadMethodImpl(streamNames.length, composer);
}
// Create buffer for each view. Point buffer to dispatchable for join.
for (int i = 0; i < statementSpec.getStreamSpecs().size(); i++)
{
BufferView buffer = new BufferView(i);
streamViews[i].addView(buffer);
buffer.setObserver(joinStatementDispatch);
preloadMethod.setBuffer(buffer, i);
}
return new Pair<Viewable, JoinPreloadMethod>(indicatorView, preloadMethod);
}
/**
* Returns a stream name assigned for each stream, generated if none was supplied.
* @param streams - stream specifications
* @return array of stream names
*/
@SuppressWarnings({"StringContatenationInLoop"})
protected static String[] determineStreamNames(List<StreamSpecCompiled> streams)
{
String[] streamNames = new String[streams.size()];
for (int i = 0; i < streams.size(); i++)
{
// Assign a stream name for joins, if not supplied
streamNames[i] = streams.get(i).getOptionalStreamName();
if (streamNames[i] == null)
{
streamNames[i] = "stream_" + i;
}
}
return streamNames;
}
/**
* Validate filter and join expression nodes.
* @param statementSpec the compiled statement
* @param statementContext the statement services
* @param typeService the event types for streams
* @param viewResourceDelegate the delegate to verify expressions that use view resources
*/
protected static void validateNodes(StatementSpecCompiled statementSpec,
StatementContext statementContext,
StreamTypeService typeService,
ViewResourceDelegate viewResourceDelegate)
{
MethodResolutionService methodResolutionService = statementContext.getMethodResolutionService();
if (statementSpec.getFilterRootNode() != null)
{
ExprNode optionalFilterNode = statementSpec.getFilterRootNode();
// Validate where clause, initializing nodes to the stream ids used
try
{
ExprValidationContext validationContext = new ExprValidationContext(typeService, methodResolutionService, viewResourceDelegate, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
optionalFilterNode = ExprNodeUtil.getValidatedSubtree(optionalFilterNode, validationContext);
if (optionalFilterNode.getExprEvaluator().getType() != boolean.class && optionalFilterNode.getExprEvaluator().getType() != Boolean.class) {
throw new ExprValidationException("The where-clause filter expression must return a boolean value");
}
statementSpec.setFilterExprRootNode(optionalFilterNode);
// Make sure there is no aggregation in the where clause
List<ExprAggregateNode> aggregateNodes = new LinkedList<ExprAggregateNode>();
ExprAggregateNodeUtil.getAggregatesBottomUp(optionalFilterNode, aggregateNodes);
if (!aggregateNodes.isEmpty())
{
throw new ExprValidationException("An aggregate function may not appear in a WHERE clause (use the HAVING clause)");
}
}
catch (ExprValidationException ex)
{
log.debug(".validateNodes Validation exception for filter=" + optionalFilterNode.toExpressionString(), ex);
throw new EPStatementException("Error validating expression: " + ex.getMessage(), statementContext.getExpression());
}
}
if ((statementSpec.getOutputLimitSpec() != null) && (statementSpec.getOutputLimitSpec().getWhenExpressionNode() != null))
{
ExprNode outputLimitWhenNode = statementSpec.getOutputLimitSpec().getWhenExpressionNode();
// Validate where clause, initializing nodes to the stream ids used
try
{
EventType outputLimitType = OutputConditionExpression.getBuiltInEventType(statementContext.getEventAdapterService());
StreamTypeService typeServiceOutputWhen = new StreamTypeServiceImpl(new EventType[] {outputLimitType}, new String[]{null}, new boolean[] {true}, statementContext.getEngineURI(), false);
ExprValidationContext validationContext = new ExprValidationContext(typeServiceOutputWhen, methodResolutionService, null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
outputLimitWhenNode = ExprNodeUtil.getValidatedSubtree(outputLimitWhenNode, validationContext);
statementSpec.getOutputLimitSpec().setWhenExpressionNode(outputLimitWhenNode);
if (JavaClassHelper.getBoxedType(outputLimitWhenNode.getExprEvaluator().getType()) != Boolean.class)
{
throw new ExprValidationException("The when-trigger expression in the OUTPUT WHEN clause must return a boolean-type value");
}
validateNoAggregations(outputLimitWhenNode, "An aggregate function may not appear in a OUTPUT LIMIT clause");
if (statementSpec.getOutputLimitSpec().getThenExpressions() != null)
{
for (OnTriggerSetAssignment assign : statementSpec.getOutputLimitSpec().getThenExpressions())
{
ExprNode node = ExprNodeUtil.getValidatedSubtree(assign.getExpression(), validationContext);
assign.setExpression(node);
validateNoAggregations(node, "An aggregate function may not appear in a OUTPUT LIMIT clause");
}
}
}
catch (ExprValidationException ex)
{
throw new EPStatementException("Error validating expression: " + ex.getMessage(), statementContext.getExpression());
}
}
for (int outerJoinCount = 0; outerJoinCount < statementSpec.getOuterJoinDescList().size(); outerJoinCount++)
{
OuterJoinDesc outerJoinDesc = statementSpec.getOuterJoinDescList().get(outerJoinCount);
UniformPair<Integer> streamIdPair = validateOuterJoinPropertyPair(statementContext, outerJoinDesc.getLeftNode(), outerJoinDesc.getRightNode(), outerJoinCount,
typeService, viewResourceDelegate);
if (outerJoinDesc.getAdditionalLeftNodes() != null)
{
Set<Integer> streamSet = new HashSet<Integer>();
streamSet.add(streamIdPair.getFirst());
streamSet.add(streamIdPair.getSecond());
for (int i = 0; i < outerJoinDesc.getAdditionalLeftNodes().length; i++)
{
UniformPair<Integer> streamIdPairAdd = validateOuterJoinPropertyPair(statementContext, outerJoinDesc.getAdditionalLeftNodes()[i], outerJoinDesc.getAdditionalRightNodes()[i], outerJoinCount,
typeService, viewResourceDelegate);
// make sure all additional properties point to the same two streams
if ((!streamSet.contains(streamIdPairAdd.getFirst()) || (!streamSet.contains(streamIdPairAdd.getSecond()))))
{
String message = "Outer join ON-clause columns must refer to properties of the same joined streams" +
" when using multiple columns in the on-clause";
throw new EPStatementException("Error validating expression: " + message, statementContext.getExpression());
}
}
}
}
}
private static void validateNoAggregations(ExprNode exprNode, String errorMsg)
throws ExprValidationException
{
// Make sure there is no aggregation in the where clause
List<ExprAggregateNode> aggregateNodes = new LinkedList<ExprAggregateNode>();
ExprAggregateNodeUtil.getAggregatesBottomUp(exprNode, aggregateNodes);
if (!aggregateNodes.isEmpty())
{
throw new ExprValidationException(errorMsg);
}
}
private static UniformPair<Integer> validateOuterJoinPropertyPair(
StatementContext statementContext,
ExprIdentNode leftNode,
ExprIdentNode rightNode,
int outerJoinCount,
StreamTypeService typeService,
ViewResourceDelegate viewResourceDelegate)
{
// Validate the outer join clause using an artificial equals-node on top.
// Thus types are checked via equals.
// Sets stream ids used for validated nodes.
ExprNode equalsNode = new ExprEqualsNodeImpl(false);
equalsNode.addChildNode(leftNode);
equalsNode.addChildNode(rightNode);
try
{
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getMethodResolutionService(), viewResourceDelegate, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
ExprNodeUtil.getValidatedSubtree(equalsNode, validationContext);
}
catch (ExprValidationException ex)
{
log.debug("Validation exception for outer join node=" + equalsNode.toExpressionString(), ex);
throw new EPStatementException("Error validating expression: " + ex.getMessage(), statementContext.getExpression());
}
// Make sure we have left-hand-side and right-hand-side refering to different streams
int streamIdLeft = leftNode.getStreamId();
int streamIdRight = rightNode.getStreamId();
if (streamIdLeft == streamIdRight)
{
String message = "Outer join ON-clause cannot refer to properties of the same stream";
throw new EPStatementException("Error validating expression: " + message, statementContext.getExpression());
}
// Make sure one of the properties refers to the acutual stream currently being joined
int expectedStreamJoined = outerJoinCount + 1;
if ((streamIdLeft != expectedStreamJoined) && (streamIdRight != expectedStreamJoined))
{
String message = "Outer join ON-clause must refer to at least one property of the joined stream" +
" for stream " + expectedStreamJoined;
throw new EPStatementException("Error validating expression: " + message, statementContext.getExpression());
}
// Make sure neither of the streams refer to a 'future' stream
String badPropertyName = null;
if (streamIdLeft > outerJoinCount + 1)
{
badPropertyName = leftNode.getResolvedPropertyName();
}
if (streamIdRight > outerJoinCount + 1)
{
badPropertyName = rightNode.getResolvedPropertyName();
}
if (badPropertyName != null)
{
String message = "Outer join ON-clause invalid scope for property" +
" '" + badPropertyName + "', expecting the current or a prior stream scope";
throw new EPStatementException("Error validating expression: " + message, statementContext.getExpression());
}
return new UniformPair<Integer>(streamIdLeft, streamIdRight);
}
private Viewable handleSimpleSelect(Viewable view,
ResultSetProcessor resultSetProcessor,
StatementContext statementContext)
throws ExprValidationException
{
Viewable finalView = view;
// Add filter view that evaluates the filter expression
if (statementSpec.getFilterRootNode() != null)
{
FilterExprView filterView = new FilterExprView(statementSpec.getFilterRootNode().getExprEvaluator(), statementContext);
finalView.addView(filterView);
finalView = filterView;
}
// for ordered deliver without output limit/buffer
if (!statementSpec.getOrderByList().isEmpty() && (statementSpec.getOutputLimitSpec() == null)) {
SingleStreamDispatchView bf = new SingleStreamDispatchView();
statementContext.getEpStatementHandle().setOptionalDispatchable(bf);
finalView.addView(bf);
finalView = bf;
}
OutputProcessView selectView = OutputProcessViewFactory.makeView(resultSetProcessor, statementSpec,
statementContext, services.getInternalEventRouter());
finalView.addView(selectView);
finalView = selectView;
return finalView;
}
private SubSelectStreamCollection createSubSelectStreams(boolean isJoin, Annotation[] annotations)
throws ExprValidationException, ViewProcessingException
{
SubSelectStreamCollection subSelectStreamDesc = new SubSelectStreamCollection();
int subselectStreamNumber = 1024;
// Process all subselect expression nodes
for (ExprSubselectNode subselect : statementSpec.getSubSelectExpressions())
{
StatementSpecCompiled statementSpec = subselect.getStatementSpecCompiled();
if (statementSpec.getStreamSpecs().get(0) instanceof FilterStreamSpecCompiled)
{
FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) statementSpec.getStreamSpecs().get(0);
// A child view is required to limit the stream
if (filterStreamSpec.getViewSpecs().size() == 0)
{
throw new ExprValidationException("Subqueries require one or more views to limit the stream, consider declaring a length or time window");
}
subselectStreamNumber++;
// Register filter, create view factories
Pair<EventStream, StatementLock> streamLockPair = services.getStreamService().createStream(statementContext.getStatementId(), filterStreamSpec.getFilterSpec(),
statementContext.getFilterService(), statementContext.getEpStatementHandle(), isJoin, true, statementContext, false, false, statementContext.getAnnotations());
Viewable viewable = streamLockPair.getFirst();
ViewFactoryChain viewFactoryChain = services.getViewService().createFactories(subselectStreamNumber, viewable.getEventType(), filterStreamSpec.getViewSpecs(), filterStreamSpec.getOptions(), statementContext);
subselect.setRawEventType(viewFactoryChain.getEventType());
// Add lookup to list, for later starts
subSelectStreamDesc.add(subselect, subselectStreamNumber, viewable, viewFactoryChain);
}
else
{
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) statementSpec.getStreamSpecs().get(0);
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
// if named-window index sharing is disabled (the default) or filter expressions are provided then consume the insert-remove stream
boolean disableIndexShare = HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE.getHint(annotations) != null;
if (!namedSpec.getFilterExpressions().isEmpty() || !processor.isEnableSubqueryIndexShare() || disableIndexShare) {
NamedWindowConsumerView consumerView = processor.addConsumer(namedSpec.getFilterExpressions(), namedSpec.getOptPropertyEvaluator(), statementContext.getEpStatementHandle(), statementContext.getStatementStopService());
ViewFactoryChain viewFactoryChain = services.getViewService().createFactories(0, consumerView.getEventType(), namedSpec.getViewSpecs(), namedSpec.getOptions(), statementContext);
subselect.setRawEventType(viewFactoryChain.getEventType());
subSelectStreamDesc.add(subselect, subselectStreamNumber, consumerView, viewFactoryChain);
}
// else if there are no named window stream filter expressions and index sharing is enabled
else {
ViewFactoryChain viewFactoryChain = services.getViewService().createFactories(0, processor.getNamedWindowType(), namedSpec.getViewSpecs(), namedSpec.getOptions(), statementContext);
subselect.setRawEventType(processor.getNamedWindowType());
subSelectStreamDesc.add(subselect, subselectStreamNumber, null, viewFactoryChain);
}
}
}
return subSelectStreamDesc;
}
private void startSubSelect(SubSelectStreamCollection subSelectStreamDesc, String[] outerStreamNames, EventType[] outerEventTypesSelect, String[] outerEventTypeNamees, List<StopCallback> stopCallbacks, Annotation[] annotations, List<ExprDeclaredNode> declaredExpressions)
throws ExprValidationException
{
boolean fullTableScan = HintEnum.SET_NOINDEX.getHint(annotations) != null;
int subqueryNum = 0;
for (ExprSubselectNode subselect : statementSpec.getSubSelectExpressions())
{
if (queryPlanLogging && queryPlanLog.isInfoEnabled()) {
queryPlanLog.info("For statement '" + statementContext.getStatementName() + "' subquery " + subqueryNum);
}
StatementSpecCompiled statementSpec = subselect.getStatementSpecCompiled();
StreamSpecCompiled filterStreamSpec = statementSpec.getStreamSpecs().get(0);
String subselecteventTypeName = null;
if (filterStreamSpec instanceof FilterStreamSpecCompiled)
{
subselecteventTypeName = ((FilterStreamSpecCompiled) filterStreamSpec).getFilterSpec().getFilterForEventTypeName();
}
else if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec)
{
subselecteventTypeName = ((NamedWindowConsumerStreamSpec) filterStreamSpec).getWindowName();
}
ViewFactoryChain viewFactoryChain = subSelectStreamDesc.getViewFactoryChain(subselect);
EventType eventType = viewFactoryChain.getEventType();
// determine a stream name unless one was supplied
String subexpressionStreamName = filterStreamSpec.getOptionalStreamName();
int subselectStreamNumber = subSelectStreamDesc.getStreamNumber(subselect);
if (subexpressionStreamName == null)
{
subexpressionStreamName = "$subselect_" + subselectStreamNumber;
}
// Named windows don't allow data views
if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec)
{
ViewResourceDelegate viewResourceDelegate = new ViewResourceDelegateImpl(new ViewFactoryChain[] {viewFactoryChain}, statementContext);
viewResourceDelegate.requestCapability(0, new NotADataWindowViewCapability(), null);
}
// Expression declarations are copies of a predefined expression body with their own stream context.
// Should only be invoked if the subselect belongs to that instance.
if (!declaredExpressions.isEmpty()) {
// Find that subselect within that declaration
ExprNodeSubselectVisitor visitor = new ExprNodeSubselectVisitor();
for (ExprDeclaredNode declaration : declaredExpressions) {
visitor.reset();
declaration.accept(visitor);
if (visitor.getSubselects().contains(subselect)) {
declaration.setSubselectOuterStreamNames(outerStreamNames, outerEventTypesSelect, outerEventTypeNamees, services.getEngineURI(), subselect, subexpressionStreamName, eventType, subselecteventTypeName);
}
}
}
EventType[] outerEventTypes;
StreamTypeService subselectTypeService;
// Use the override provided by the subselect if applicable
if (subselect.getFilterSubqueryStreamTypes() != null) {
subselectTypeService = subselect.getFilterSubqueryStreamTypes();
outerEventTypes = new EventType[subselectTypeService.getEventTypes().length - 1];
System.arraycopy(subselectTypeService.getEventTypes(), 1, outerEventTypes, 0, subselectTypeService.getEventTypes().length - 1);
}
else {
// Streams event types are the original stream types with the stream zero the subselect stream
LinkedHashMap<String, Pair<EventType, String>> namesAndTypes = new LinkedHashMap<String, Pair<EventType, String>>();
namesAndTypes.put(subexpressionStreamName, new Pair<EventType, String>(eventType, subselecteventTypeName));
for (int i = 0; i < outerEventTypesSelect.length; i++)
{
Pair<EventType, String> pair = new Pair<EventType, String>(outerEventTypesSelect[i], outerEventTypeNamees[i]);
namesAndTypes.put(outerStreamNames[i], pair);
}
subselectTypeService = new StreamTypeServiceImpl(namesAndTypes, services.getEngineURI(), true, true);
outerEventTypes = outerEventTypesSelect;
}
ViewResourceDelegate viewResourceDelegateSubselect = new ViewResourceDelegateImpl(new ViewFactoryChain[] {viewFactoryChain}, statementContext);
// Validate select expression
SelectClauseSpecCompiled selectClauseSpec = subselect.getStatementSpecCompiled().getSelectClauseSpec();
AggregationService aggregationService = null;
List<ExprNode> selectExpressions = new ArrayList<ExprNode>();
List<String> assignedNames = new ArrayList<String>();
boolean isWildcard = false;
boolean isStreamWildcard = false;
if (selectClauseSpec.getSelectExprList().size() > 0)
{
List<ExprAggregateNode> aggExprNodes = new LinkedList<ExprAggregateNode>();
ExprValidationContext validationContext = new ExprValidationContext(subselectTypeService, statementContext.getMethodResolutionService(), viewResourceDelegateSubselect, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
for (int i = 0; i < selectClauseSpec.getSelectExprList().size(); i++) {
SelectClauseElementCompiled element = selectClauseSpec.getSelectExprList().get(i);
if (element instanceof SelectClauseExprCompiledSpec)
{
// validate
SelectClauseExprCompiledSpec compiled = (SelectClauseExprCompiledSpec) element;
ExprNode selectExpression = compiled.getSelectExpression();
selectExpression = ExprNodeUtil.getValidatedSubtree(selectExpression, validationContext);
selectExpressions.add(selectExpression);
assignedNames.add(compiled.getAssignedName());
// handle aggregation
ExprAggregateNodeUtil.getAggregatesBottomUp(selectExpression, aggExprNodes);
if (aggExprNodes.size() > 0)
{
// This stream (stream 0) properties must either all be under aggregation, or all not be.
List<Pair<Integer, String>> propertiesNotAggregated = getExpressionProperties(selectExpression, false);
for (Pair<Integer, String> pair : propertiesNotAggregated)
{
if (pair.getFirst() == 0)
{
throw new ExprValidationException("Subselect properties must all be within aggregation functions");
}
}
}
}
else if (element instanceof SelectClauseElementWildcard) {
isWildcard = true;
}
else if (element instanceof SelectClauseStreamCompiledSpec) {
isStreamWildcard = true;
}
} // end of for loop
if (!selectExpressions.isEmpty()) {
subselect.setSelectClause(selectExpressions.toArray(new ExprNode[selectExpressions.size()]));
subselect.setSelectAsNames(assignedNames.toArray(new String[assignedNames.size()]));
if (isWildcard || isStreamWildcard) {
throw new ExprValidationException("Subquery multi-column select does not allow wildcard or stream wildcard when selecting multiple columns.");
}
if (selectExpressions.size() > 1 && !subselect.isAllowMultiColumnSelect()) {
throw new ExprValidationException("Subquery multi-column select is not allowed in this context.");
}
if ((selectExpressions.size() > 1 && aggExprNodes.size() > 0)) {
// all properties must be aggregated
if (!ExprNodeUtility.getNonAggregatedProps(selectExpressions).isEmpty()) {
throw new ExprValidationException("Subquery with multi-column select requires that either all or none of the selected columns are under aggregation.");
}
}
}
if (aggExprNodes.size() > 0)
{
List<ExprAggregateNode> havingAgg = Collections.emptyList();
List<ExprAggregateNode> orderByAgg = Collections.emptyList();
aggregationService = AggregationServiceFactory.getService(aggExprNodes, havingAgg, orderByAgg, false, statementContext.getMethodResolutionService(), statementContext, annotations, statementContext.getVariableService(), statementContext.getStatementStopService(), false, statementSpec.getFilterRootNode(), statementSpec.getHavingExprRootNode());
// Other stream properties, if there is aggregation, cannot be under aggregation.
for (ExprAggregateNode aggNode : aggExprNodes)
{
List<Pair<Integer, String>> propertiesNodesAggregated = getExpressionProperties(aggNode, true);
for (Pair<Integer, String> pair : propertiesNodesAggregated)
{
if (pair.getFirst() != 0)
{
throw new ExprValidationException("Subselect aggregation functions cannot aggregate across correlated properties");
}
}
}
}
}
// no aggregation functions allowed in filter
if (statementSpec.getFilterRootNode() != null)
{
List<ExprAggregateNode> aggExprNodesFilter = new LinkedList<ExprAggregateNode>();
ExprAggregateNodeUtil.getAggregatesBottomUp(statementSpec.getFilterRootNode(), aggExprNodesFilter);
if (aggExprNodesFilter.size() > 0)
{
throw new ExprValidationException("Aggregation functions are not supported within subquery filters, consider using insert-into instead");
}
}
// Validate filter expression, if there is one
ExprNode filterExpr = statementSpec.getFilterRootNode();
boolean correlatedSubquery = false;
if (filterExpr != null)
{
ExprValidationContext validationContext = new ExprValidationContext(subselectTypeService, statementContext.getMethodResolutionService(), viewResourceDelegateSubselect, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
filterExpr = ExprNodeUtil.getValidatedSubtree(filterExpr, validationContext);
if (JavaClassHelper.getBoxedType(filterExpr.getExprEvaluator().getType()) != Boolean.class)
{
throw new ExprValidationException("Subselect filter expression must return a boolean value");
}
// check the presence of a correlated filter, not allowed with aggregation
ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(true);
filterExpr.accept(visitor);
List<Pair<Integer, String>> propertiesNodes = visitor.getExprProperties();
for (Pair<Integer, String> pair : propertiesNodes)
{
if (pair.getFirst() != 0)
{
correlatedSubquery = true;
break;
}
}
}
// Finally create views
Viewable viewableRoot = subSelectStreamDesc.getRootViewable(subselect);
Viewable subselectView = services.getViewService().createViews(viewableRoot, viewFactoryChain.getViewFactoryChain(), statementContext);
// If we do aggregation, then the view results must be added and removed from aggregation
final EventTable eventIndex;
// Under aggregation conditions, there is no lookup/corelated subquery strategy, and
// the view-supplied events are simply aggregated, a null-event supplied to the stream for the select-clause, and not kept in index.
// Note that "var1 + max(var2)" is not allowed as some properties are not under aggregation (which event to use?).
if (aggregationService != null)
{
subselect.setStrategy(new SubordTableLookupStrategyNullRow());
subselect.setFilterExpr(null); // filter not evaluated by subselect expression as not correlated
ExprEvaluator filterExprEval = (filterExpr == null) ? null : filterExpr.getExprEvaluator();
// If we have window index sharing, the subselectView will be null
if (!correlatedSubquery && subselectView != null) {
SubselectAggregatorView aggregatorView = new SubselectAggregatorView(aggregationService, filterExprEval, statementContext);
subselectView.addView(aggregatorView);
subselectView = aggregatorView;
eventIndex = null;
}
else {
Pair<EventTable, SubordTableLookupStrategy> indexPair = determineSubqueryIndex(filterExpr, eventType,
outerEventTypes, subselectTypeService, fullTableScan);
subselect.setStrategy(indexPair.getSecond());
subselect.setFilterExpr(null); // this will be evaluated in the preprocessor
eventIndex = indexPair.getFirst();
SubselectAggregationPreprocessor preprocessor = new SubselectAggregationPreprocessor(aggregationService, filterExpr.getExprEvaluator());
subselect.setSubselectAggregationPreprocessor(preprocessor);
}
}
else
{
// Determine indexing of the filter expression
Pair<EventTable, SubordTableLookupStrategy> indexPair = determineSubqueryIndex(filterExpr, eventType,
outerEventTypes, subselectTypeService, fullTableScan);
ExprEvaluator filterExprEval = (filterExpr == null) ? null : filterExpr.getExprEvaluator();
subselect.setStrategy(indexPair.getSecond());
subselect.setFilterExpr(filterExprEval);
eventIndex = indexPair.getFirst();
}
boolean disableIndexShare = HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE.getHint(annotations) != null;
SubordTableLookupStrategy namedWindowSubqueryLookup = null;
if ((filterStreamSpec instanceof NamedWindowConsumerStreamSpec) && (!disableIndexShare)) {
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec;
if (namedSpec.getFilterExpressions().isEmpty()) {
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
if (processor.isEnableSubqueryIndexShare()) {
if (queryPlanLogging && queryPlanLog.isInfoEnabled()) {
queryPlanLog.info("prefering shared index");
}
SubordPropPlan joinedPropPlan = QueryPlanIndexBuilder.getJoinProps(filterExpr, outerEventTypes.length, subselectTypeService.getEventTypes());
namedWindowSubqueryLookup = processor.getRootView().getAddSubqueryLookupStrategy(outerEventTypesSelect, joinedPropPlan, fullTableScan);
subselect.setStrategy(namedWindowSubqueryLookup);
stopCallbacks.add(new NamedWindowSubqueryStopCallback(processor, namedWindowSubqueryLookup));
}
}
}
// Clear out index on statement stop
if (namedWindowSubqueryLookup == null) {
stopCallbacks.add(new SubqueryStopCallback(eventIndex));
}
// Preload
if (namedWindowSubqueryLookup == null) {
if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec)
{
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec ;
NamedWindowProcessor processor = services.getNamedWindowService().getProcessor(namedSpec.getWindowName());
NamedWindowTailView consumerView = processor.getTailView();
// preload view for stream
ArrayList<EventBean> eventsInWindow = new ArrayList<EventBean>();
if (namedSpec.getFilterExpressions() != null) {
EventBean[] events = new EventBean[1];
for (EventBean event : consumerView) {
events[0] = event;
boolean add = true;
for (ExprNode filter : namedSpec.getFilterExpressions()) {
Object result = filter.getExprEvaluator().evaluate(events, true, statementContext);
if ((result == null) || (!((Boolean) result))) {
add = false;
break;
}
}
if (add) {
eventsInWindow.add(events[0]);
}
}
}
else {
for(Iterator<EventBean> it = consumerView.iterator(); it.hasNext();)
{
eventsInWindow.add(it.next());
}
}
EventBean[] newEvents = eventsInWindow.toArray(new EventBean[eventsInWindow.size()]);
((View)viewableRoot).update(newEvents, null); // fill view
if (eventIndex != null)
{
eventIndex.add(newEvents); // fill index
}
}
else // preload from the data window that sit on top
{
// Start up event table from the iterator
Iterator<EventBean> it = subselectView.iterator();
if ((it != null) && (it.hasNext()))
{
ArrayList<EventBean> preloadEvents = new ArrayList<EventBean>();
for (;it.hasNext();)
{
preloadEvents.add(it.next());
}
if (eventIndex != null)
{
eventIndex.add(preloadEvents.toArray(new EventBean[preloadEvents.size()]));
}
}
}
subqueryNum++;
}
// hook up subselect viewable and event table
if (subselectView != null) {
BufferView bufferView = new BufferView(subselectStreamNumber);
bufferView.setObserver(new SubselectBufferObserver(eventIndex));
subselectView.addView(bufferView);
}
}
}
private Pair<EventTable, SubordTableLookupStrategy> determineSubqueryIndex(ExprNode filterExpr,
EventType viewableEventType,
EventType[] outerEventTypes,
StreamTypeService subselectTypeService,
boolean fullTableScan)
throws ExprValidationException
{
Pair<EventTable, SubordTableLookupStrategy> result = determineSubqueryIndexInternal(filterExpr, viewableEventType, outerEventTypes, subselectTypeService, fullTableScan);
if (queryPlanLogging && queryPlanLog.isInfoEnabled()) {
queryPlanLog.info("local index");
queryPlanLog.info("strategy " + result.getSecond().toQueryPlan());
queryPlanLog.info("table " + result.getFirst().toQueryPlan());
}
return result;
}
private Pair<EventTable, SubordTableLookupStrategy> determineSubqueryIndexInternal(ExprNode filterExpr,
EventType viewableEventType,
EventType[] outerEventTypes,
StreamTypeService subselectTypeService,
boolean fullTableScan)
throws ExprValidationException
{
// No filter expression means full table scan
if ((filterExpr == null) || fullTableScan)
{
UnindexedEventTable table = new UnindexedEventTable(0);
SubordFullTableScanLookupStrategy strategy = new SubordFullTableScanLookupStrategy(table);
return new Pair<EventTable, SubordTableLookupStrategy>(table, strategy);
}
// Build a list of streams and indexes
SubordPropPlan joinPropDesc = QueryPlanIndexBuilder.getJoinProps(filterExpr, outerEventTypes.length, subselectTypeService.getEventTypes());
Map<String, SubordPropHashKey> hashKeys = joinPropDesc.getHashProps();
Map<String, SubordPropRangeKey> rangeKeys = joinPropDesc.getRangeProps();
List<SubordPropHashKey> hashKeyList = new ArrayList<SubordPropHashKey>(hashKeys.values());
List<SubordPropRangeKey> rangeKeyList = new ArrayList<SubordPropRangeKey>(rangeKeys.values());
// build table (local table)
EventTable eventTable;
CoercionDesc hashCoercionDesc;
CoercionDesc rangeCoercionDesc;
if (hashKeys.size() != 0 && rangeKeys.isEmpty())
{
String indexedProps[] = hashKeys.keySet().toArray(new String[hashKeys.keySet().size()]);
hashCoercionDesc = CoercionUtil.getCoercionTypesHash(viewableEventType, indexedProps, hashKeyList);
rangeCoercionDesc = new CoercionDesc(false, null);
if (hashKeys.size() == 1) {
if (!hashCoercionDesc.isCoerce()) {
eventTable = new PropertyIndexedEventTableSingle(0, viewableEventType, indexedProps[0]);
}
else {
eventTable = new PropertyIndexedEventTableSingleCoerceAdd(0, viewableEventType, indexedProps[0], hashCoercionDesc.getCoercionTypes()[0]);
}
}
else {
if (!hashCoercionDesc.isCoerce())
{
eventTable = new PropertyIndexedEventTable(0, viewableEventType, indexedProps);
}
else
{
eventTable = new PropertyIndexedEventTableCoerceAdd(0, viewableEventType, indexedProps, hashCoercionDesc.getCoercionTypes());
}
}
}
else if (hashKeys.isEmpty() && rangeKeys.isEmpty())
{
eventTable = new UnindexedEventTable(0);
hashCoercionDesc = new CoercionDesc(false, null);
rangeCoercionDesc = new CoercionDesc(false, null);
}
else if (hashKeys.isEmpty() && rangeKeys.size() == 1)
{
String indexedProp = rangeKeys.keySet().iterator().next();
CoercionDesc coercionRangeTypes = CoercionUtil.getCoercionTypesRange(viewableEventType, rangeKeys, outerEventTypes);
if (!coercionRangeTypes.isCoerce()) {
eventTable = new PropertySortedEventTable(0, viewableEventType, indexedProp);
}
else {
eventTable = new PropertySortedEventTableCoerced(0, viewableEventType, indexedProp, coercionRangeTypes.getCoercionTypes()[0]);
}
hashCoercionDesc = new CoercionDesc(false, null);
rangeCoercionDesc = coercionRangeTypes;
}
else {
String[] indexedKeyProps = hashKeys.keySet().toArray(new String[hashKeys.keySet().size()]);
Class[] coercionKeyTypes = SubordPropUtil.getCoercionTypes(hashKeys.values());
String[] indexedRangeProps = rangeKeys.keySet().toArray(new String[rangeKeys.keySet().size()]);
CoercionDesc coercionRangeTypes = CoercionUtil.getCoercionTypesRange(viewableEventType, rangeKeys, outerEventTypes);
eventTable = new PropertyCompositeEventTable(0, viewableEventType, indexedKeyProps, coercionKeyTypes, indexedRangeProps, coercionRangeTypes.getCoercionTypes());
hashCoercionDesc = CoercionUtil.getCoercionTypesHash(viewableEventType, indexedKeyProps, hashKeyList);
rangeCoercionDesc = coercionRangeTypes;
}
SubordTableLookupStrategy subqTableLookupStrategy = SubordinateTableLookupStrategyFactory.getLookupStrategy(outerEventTypes,
hashKeyList, hashCoercionDesc, rangeKeyList, rangeCoercionDesc, false, eventTable);
return new Pair<EventTable, SubordTableLookupStrategy>(eventTable, subqTableLookupStrategy);
}
// For delete actions from named windows
private ExprNode validateJoinNamedWindow(ExprNode deleteJoinExpr,
EventType namedWindowType,
String namedWindowStreamName,
String namedWindowName,
EventType filteredType,
String filterStreamName,
String filteredTypeName) throws ExprValidationException
{
if (deleteJoinExpr == null)
{
return null;
}
LinkedHashMap<String, Pair<EventType, String>> namesAndTypes = new LinkedHashMap<String, Pair<EventType, String>>();
namesAndTypes.put(namedWindowStreamName, new Pair<EventType, String>(namedWindowType, namedWindowName));
namesAndTypes.put(filterStreamName, new Pair<EventType, String>(filteredType, filteredTypeName));
StreamTypeService typeService = new StreamTypeServiceImpl(namesAndTypes, services.getEngineURI(), false, false);
ExprValidationContext validationContext = new ExprValidationContext(typeService, statementContext.getMethodResolutionService(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getAnnotations());
return ExprNodeUtil.getValidatedSubtree(deleteJoinExpr, validationContext);
}
/**
* Walk expression returning properties used.
* @param exprNode to walk
* @param visitAggregateNodes true to visit aggregation nodes
* @return list of props
*/
public static List<Pair<Integer, String>> getExpressionProperties(ExprNode exprNode, boolean visitAggregateNodes)
{
ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(visitAggregateNodes);
exprNode.accept(visitor);
return visitor.getExprProperties();
}
}