/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
***************************************************************************************
*/
package com.espertech.esper.core.start;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.context.ContextPartitionSelector;
import com.espertech.esper.core.service.EPPreparedQueryResult;
import com.espertech.esper.core.service.EPServicesContext;
import com.espertech.esper.core.service.StatementContext;
import com.espertech.esper.epl.core.StreamTypeServiceImpl;
import com.espertech.esper.epl.expression.core.ExprNodeUtility;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.join.hint.ExcludePlanHint;
import com.espertech.esper.epl.join.plan.QueryGraph;
import com.espertech.esper.epl.spec.*;
import com.espertech.esper.util.AuditPath;
import com.espertech.esper.util.CollectionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
/**
* Starts and provides the stop method for EPL statements.
*/
public abstract class EPPreparedExecuteIUDSingleStream implements EPPreparedExecuteMethod {
private static final Logger QUERY_PLAN_LOG = LoggerFactory.getLogger(AuditPath.QUERYPLAN_LOG);
private static final Logger log = LoggerFactory.getLogger(EPPreparedExecuteIUDSingleStream.class);
protected final StatementSpecCompiled statementSpec;
protected final FireAndForgetProcessor processor;
protected final EPServicesContext services;
protected final EPPreparedExecuteIUDSingleStreamExec executor;
protected final StatementContext statementContext;
protected boolean hasTableAccess;
public abstract EPPreparedExecuteIUDSingleStreamExec getExecutor(QueryGraph queryGraph, String aliasName)
throws ExprValidationException;
/**
* 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
* @throws com.espertech.esper.epl.expression.core.ExprValidationException if the preparation failed
*/
public EPPreparedExecuteIUDSingleStream(StatementSpecCompiled statementSpec,
EPServicesContext services,
StatementContext statementContext)
throws ExprValidationException {
boolean queryPlanLogging = services.getConfigSnapshot().getEngineDefaults().getLogging().isEnableQueryPlan();
if (queryPlanLogging) {
QUERY_PLAN_LOG.info("Query plans for Fire-and-forget query '" + statementContext.getExpression() + "'");
}
this.hasTableAccess = statementSpec.getIntoTableSpec() != null ||
(statementSpec.getTableNodes() != null && statementSpec.getTableNodes().length > 0);
if (statementSpec.getInsertIntoDesc() != null && services.getTableService().getTableMetadata(statementSpec.getInsertIntoDesc().getEventTypeName()) != null) {
hasTableAccess = true;
}
if (statementSpec.getFireAndForgetSpec() instanceof FireAndForgetSpecUpdate ||
statementSpec.getFireAndForgetSpec() instanceof FireAndForgetSpecDelete) {
hasTableAccess |= statementSpec.getStreamSpecs()[0] instanceof TableQueryStreamSpec;
}
this.statementSpec = statementSpec;
this.services = services;
this.statementContext = statementContext;
// validate general FAF criteria
EPPreparedExecuteMethodHelper.validateFAFQuery(statementSpec);
// obtain processor
StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs()[0];
processor = FireAndForgetProcessorFactory.validateResolveProcessor(streamSpec, services);
// obtain name and type
String processorName = processor.getNamedWindowOrTableName();
EventType eventType = processor.getEventTypeResultSetProcessor();
// determine alias
String aliasName = processorName;
if (streamSpec.getOptionalStreamName() != null) {
aliasName = streamSpec.getOptionalStreamName();
}
// compile filter to optimize access to named window
StreamTypeServiceImpl typeService = new StreamTypeServiceImpl(new EventType[]{eventType}, new String[]{aliasName}, new boolean[]{true}, services.getEngineURI(), true);
ExcludePlanHint excludePlanHint = ExcludePlanHint.getHint(typeService.getStreamNames(), statementContext);
QueryGraph queryGraph = new QueryGraph(1, excludePlanHint, false);
if (statementSpec.getFilterRootNode() != null) {
ExprNodeUtility.validateFilterWQueryGraphSafe(queryGraph, statementSpec.getFilterRootNode(), statementContext, typeService);
}
// validate expressions
EPStatementStartMethodHelperValidate.validateNodes(statementSpec, statementContext, typeService, null);
// get executor
executor = getExecutor(queryGraph, aliasName);
}
/**
* Returns the event type of the prepared statement.
*
* @return event type
*/
public EventType getEventType() {
return processor.getEventTypeResultSetProcessor();
}
/**
* Executes the prepared query.
*
* @return query results
*/
public EPPreparedQueryResult execute(ContextPartitionSelector[] contextPartitionSelectors) {
try {
if (contextPartitionSelectors != null && contextPartitionSelectors.length != 1) {
throw new IllegalArgumentException("Number of context partition selectors must be one");
}
ContextPartitionSelector optionalSingleSelector = contextPartitionSelectors != null && contextPartitionSelectors.length > 0 ? contextPartitionSelectors[0] : null;
// validate context
if (processor.getContextName() != null &&
statementSpec.getOptionalContextName() != null &&
!processor.getContextName().equals(statementSpec.getOptionalContextName())) {
throw new EPException("Context for named window is '" + processor.getContextName() + "' and query specifies context '" + statementSpec.getOptionalContextName() + "'");
}
// handle non-specified context
if (statementSpec.getOptionalContextName() == null) {
FireAndForgetInstance processorInstance = processor.getProcessorInstanceNoContext();
if (processorInstance != null) {
EventBean[] rows = executor.execute(processorInstance);
if (rows != null && rows.length > 0) {
dispatch();
}
return new EPPreparedQueryResult(processor.getEventTypePublic(), rows);
}
}
// context partition runtime query
Collection<Integer> agentInstanceIds = EPPreparedExecuteMethodHelper.getAgentInstanceIds(processor, optionalSingleSelector, services.getContextManagementService(), processor.getContextName());
// collect events and agent instances
if (agentInstanceIds.isEmpty()) {
return new EPPreparedQueryResult(processor.getEventTypeResultSetProcessor(), CollectionUtil.EVENTBEANARRAY_EMPTY);
}
if (agentInstanceIds.size() == 1) {
int agentInstanceId = agentInstanceIds.iterator().next();
FireAndForgetInstance processorInstance = processor.getProcessorInstanceContextById(agentInstanceId);
EventBean[] rows = executor.execute(processorInstance);
if (rows.length > 0) {
dispatch();
}
return new EPPreparedQueryResult(processor.getEventTypeResultSetProcessor(), rows);
}
ArrayDeque<EventBean> allRows = new ArrayDeque<EventBean>();
for (int agentInstanceId : agentInstanceIds) {
FireAndForgetInstance processorInstance = processor.getProcessorInstanceContextById(agentInstanceId);
if (processorInstance != null) {
EventBean[] rows = executor.execute(processorInstance);
allRows.addAll(Arrays.asList(rows));
}
}
if (allRows.size() > 0) {
dispatch();
}
return new EPPreparedQueryResult(processor.getEventTypeResultSetProcessor(), allRows.toArray(new EventBean[allRows.size()]));
} finally {
if (hasTableAccess) {
services.getTableService().getTableExprEvaluatorContext().releaseAcquiredLocks();
}
}
}
protected void dispatch() {
services.getInternalEventEngineRouteDest().processThreadWorkQueue();
}
}