/*
* *************************************************************************************
* 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.epl.join.base;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.core.service.StreamJoinAnalysisResult;
import com.espertech.esper.epl.expression.*;
import com.espertech.esper.epl.join.exec.base.ExecNode;
import com.espertech.esper.epl.join.plan.QueryPlan;
import com.espertech.esper.epl.join.plan.QueryPlanIndex;
import com.espertech.esper.epl.join.plan.QueryPlanIndexItem;
import com.espertech.esper.epl.join.plan.QueryPlanNode;
import com.espertech.esper.epl.join.table.EventTable;
import com.espertech.esper.epl.join.table.EventTableUtil;
import com.espertech.esper.epl.join.table.HistoricalStreamIndexList;
import com.espertech.esper.epl.spec.OuterJoinDesc;
import com.espertech.esper.view.HistoricalEventViewable;
import com.espertech.esper.view.Viewable;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.lang.annotation.Annotation;
import java.util.*;
public class JoinSetComposerPrototypeImpl implements JoinSetComposerPrototype {
private static final Log log = LogFactory.getLog(JoinSetComposerPrototypeFactory.class);
private final String statementName;
private final String statementId;
private final List<OuterJoinDesc> outerJoinDescList;
private final ExprNode optionalFilterNode;
private final EventType[] streamTypes;
private final String[] streamNames;
private final StreamJoinAnalysisResult streamJoinAnalysisResult;
private final Annotation[] annotations;
private final HistoricalViewableDesc historicalViewableDesc;
private final ExprEvaluatorContext exprEvaluatorContext;
private final QueryPlanIndex[] indexSpecs;
private final QueryPlan queryPlan;
private final HistoricalStreamIndexList[] historicalStreamIndexLists;
private final boolean joinRemoveStream;
private final boolean isOuterJoins;
public JoinSetComposerPrototypeImpl(String statementName,
String statementId,
List<OuterJoinDesc> outerJoinDescList,
ExprNode optionalFilterNode,
EventType[] streamTypes,
String[] streamNames,
StreamJoinAnalysisResult streamJoinAnalysisResult,
Annotation[] annotations,
HistoricalViewableDesc historicalViewableDesc,
ExprEvaluatorContext exprEvaluatorContext,
QueryPlanIndex[] indexSpecs,
QueryPlan queryPlan,
HistoricalStreamIndexList[] historicalStreamIndexLists,
boolean joinRemoveStream,
boolean isOuterJoins) {
this.statementName = statementName;
this.statementId = statementId;
this.outerJoinDescList = outerJoinDescList;
this.optionalFilterNode = optionalFilterNode;
this.streamTypes = streamTypes;
this.streamNames = streamNames;
this.streamJoinAnalysisResult = streamJoinAnalysisResult;
this.annotations = annotations;
this.historicalViewableDesc = historicalViewableDesc;
this.exprEvaluatorContext = exprEvaluatorContext;
this.indexSpecs = indexSpecs;
this.queryPlan = queryPlan;
this.historicalStreamIndexLists = historicalStreamIndexLists;
this.joinRemoveStream = joinRemoveStream;
this.isOuterJoins = isOuterJoins;
}
public JoinSetComposerDesc create(Viewable[] streamViews, boolean isFireAndForget) {
// Build indexes
Map<String, EventTable>[] indexesPerStream = new HashMap[indexSpecs.length];
for (int streamNo = 0; streamNo < indexSpecs.length; streamNo++)
{
if (indexSpecs[streamNo] == null)
{
continue;
}
Map<String, QueryPlanIndexItem> items = indexSpecs[streamNo].getItems();
indexesPerStream[streamNo] = new LinkedHashMap<String, EventTable>();
for (Map.Entry<String, QueryPlanIndexItem> entry : items.entrySet()) {
EventTable index;
if (streamJoinAnalysisResult.getViewExternal()[streamNo] != null) {
index = streamJoinAnalysisResult.getViewExternal()[streamNo].getJoinIndexTable(items.get(entry.getKey()));
}
else {
index = EventTableUtil.buildIndex(streamNo, items.get(entry.getKey()), streamTypes[streamNo], false, entry.getValue().isUnique(), null);
}
indexesPerStream[streamNo].put(entry.getKey(), index);
}
}
// Build strategies
QueryPlanNode[] queryExecSpecs = queryPlan.getExecNodeSpecs();
QueryStrategy[] queryStrategies = new QueryStrategy[queryExecSpecs.length];
for (int i = 0; i < queryExecSpecs.length; i++)
{
QueryPlanNode planNode = queryExecSpecs[i];
if (planNode == null)
{
log.debug(".makeComposer No execution node for stream " + i + " '" + streamNames[i] + "'");
continue;
}
ExecNode executionNode = planNode.makeExec(statementName, statementId, annotations, indexesPerStream, streamTypes, streamViews, historicalStreamIndexLists, streamJoinAnalysisResult.getViewExternal());
if (log.isDebugEnabled())
{
log.debug(".makeComposer Execution nodes for stream " + i + " '" + streamNames[i] +
"' : \n" + ExecNode.print(executionNode));
}
queryStrategies[i] = new ExecNodeQueryStrategy(i, streamTypes.length, executionNode);
}
// If this is not unidirectional and not a self-join (excluding self-outer-join)
JoinSetComposerDesc joinSetComposerDesc;
if ((!streamJoinAnalysisResult.isUnidirectional()) &&
(!streamJoinAnalysisResult.isPureSelfJoin() || !outerJoinDescList.isEmpty()))
{
JoinSetComposer composer;
if (historicalViewableDesc.isHasHistorical())
{
composer = new JoinSetComposerHistoricalImpl(indexesPerStream, queryStrategies, streamViews, exprEvaluatorContext);
}
else
{
if (isFireAndForget) {
composer = new JoinSetComposerFAFImpl(indexesPerStream, queryStrategies, streamJoinAnalysisResult.isPureSelfJoin(), exprEvaluatorContext, joinRemoveStream, isOuterJoins);
}
else {
composer = new JoinSetComposerImpl(indexesPerStream, queryStrategies, streamJoinAnalysisResult.isPureSelfJoin(), exprEvaluatorContext, joinRemoveStream);
}
}
// rewrite the filter expression for all-inner joins in case "on"-clause outer join syntax was used to include those expressions
ExprNode filterExpression = getFilterExpressionInclOnClause(optionalFilterNode, outerJoinDescList);
ExprEvaluator postJoinEval = filterExpression == null ? null : filterExpression.getExprEvaluator();
joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval);
}
else
{
QueryStrategy driver;
int unidirectionalStream;
if (streamJoinAnalysisResult.getUnidirectionalStreamNumber() != -1)
{
unidirectionalStream = streamJoinAnalysisResult.getUnidirectionalStreamNumber();
driver = queryStrategies[unidirectionalStream];
}
else
{
unidirectionalStream = 0;
driver = queryStrategies[0];
}
JoinSetComposer composer = new JoinSetComposerStreamToWinImpl(indexesPerStream, streamJoinAnalysisResult.isPureSelfJoin(),
unidirectionalStream, driver, streamJoinAnalysisResult.getUnidirectionalNonDriving());
ExprEvaluator postJoinEval = optionalFilterNode == null ? null : optionalFilterNode.getExprEvaluator();
joinSetComposerDesc = new JoinSetComposerDesc(composer, postJoinEval);
}
// compile prior events per stream to preload any indexes
EventBean[][] eventsPerStream = new EventBean[streamNames.length][];
ArrayList<EventBean> events = new ArrayList<EventBean>();
for (int i = 0; i < eventsPerStream.length; i++)
{
// For named windows, we don't need to preload indexes from the iterators as this is always done already
if (streamJoinAnalysisResult.getNamedWindow()[i])
{
continue;
}
Iterator<EventBean> it = null;
if (!(streamViews[i] instanceof HistoricalEventViewable))
{
try
{
it = streamViews[i].iterator();
}
catch (UnsupportedOperationException ex)
{
// Joins do not support the iterator
}
}
if (it != null)
{
for (;it.hasNext();)
{
events.add(it.next());
}
eventsPerStream[i] = events.toArray(new EventBean[events.size()]);
events.clear();
}
else
{
eventsPerStream[i] = new EventBean[0];
}
}
// init
joinSetComposerDesc.getJoinSetComposer().init(eventsPerStream);
return joinSetComposerDesc;
}
private ExprNode getFilterExpressionInclOnClause(ExprNode optionalFilterNode, List<OuterJoinDesc> outerJoinDescList)
{
if (optionalFilterNode == null) { // no need to add as query planning is fully based on on-clause
return null;
}
if (outerJoinDescList.isEmpty()) { // not an outer-join syntax
return optionalFilterNode;
}
if (!OuterJoinDesc.consistsOfAllInnerJoins(outerJoinDescList)) { // all-inner joins
return optionalFilterNode;
}
ExprAndNode andNode = new ExprAndNodeImpl();
andNode.addChildNode(optionalFilterNode);
for (OuterJoinDesc outerJoinDesc : outerJoinDescList) {
andNode.addChildNode(outerJoinDesc.makeExprNode(null));
}
try {
andNode.validate(null);
}
catch (ExprValidationException ex) {
throw new RuntimeException("Unexpected exception validating expression: " + ex.getMessage(), ex);
}
return andNode;
}
}