/*
***************************************************************************************
* 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.epl.table.mgmt;
import com.espertech.esper.client.ConfigurationPlugInAggregationMultiFunction;
import com.espertech.esper.client.EventType;
import com.espertech.esper.collection.Pair;
import com.espertech.esper.core.context.util.AgentInstanceContext;
import com.espertech.esper.core.service.StatementContext;
import com.espertech.esper.epl.core.EngineImportService;
import com.espertech.esper.epl.core.StreamTypeService;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeBase;
import com.espertech.esper.epl.expression.core.ExprChainedSpec;
import com.espertech.esper.epl.expression.core.ExprEvaluator;
import com.espertech.esper.epl.expression.core.ExprNode;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.expression.table.ExprTableIdentNode;
import com.espertech.esper.epl.expression.table.ExprTableIdentNodeSubpropAccessor;
import com.espertech.esper.epl.join.plan.QueryPlanIndexItem;
import com.espertech.esper.epl.lookup.IndexMultiKey;
import com.espertech.esper.epl.parse.ASTAggregationHelper;
import com.espertech.esper.epl.table.strategy.*;
import com.espertech.esper.epl.table.upd.TableUpdateStrategy;
import com.espertech.esper.epl.table.upd.TableUpdateStrategyFactory;
import com.espertech.esper.epl.table.upd.TableUpdateStrategyReceiver;
import com.espertech.esper.epl.updatehelper.EventBeanUpdateHelper;
import com.espertech.esper.event.arr.ObjectArrayEventType;
import com.espertech.esper.plugin.PlugInAggregationMultiFunctionFactory;
import com.espertech.esper.util.AuditPath;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.util.LazyAllocatedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.locks.Lock;
public class TableServiceImpl implements TableService {
private static final Logger QUERY_PLAN_LOG = LoggerFactory.getLogger(AuditPath.QUERYPLAN_LOG);
private final Map<String, TableMetadata> tables = new HashMap<String, TableMetadata>();
private final TableExprEvaluatorContext tableExprEvaluatorContext = new TableExprEvaluatorContext();
public TableServiceImpl() {
}
public void validateAddIndex(String createIndexStatementName, TableMetadata tableMetadata, String explicitIndexName, QueryPlanIndexItem explicitIndexDesc, IndexMultiKey imk) throws ExprValidationException {
tableMetadata.validateAddIndexAssignUpdateStrategies(createIndexStatementName, imk, explicitIndexName, explicitIndexDesc);
}
public TableUpdateStrategy getTableUpdateStrategy(TableMetadata tableMetadata, EventBeanUpdateHelper updateHelper, boolean isOnMerge)
throws ExprValidationException {
return TableUpdateStrategyFactory.validateGetTableUpdateStrategy(tableMetadata, updateHelper, isOnMerge);
}
public Collection<Integer> getAgentInstanceIds(String name) {
TableMetadata metadata = tables.get(name);
if (metadata == null) {
throw new IllegalArgumentException("Failed to find table for name '" + name + "'");
}
return metadata.getAgentInstanceIds();
}
public TableExprEvaluatorContext getTableExprEvaluatorContext() {
return tableExprEvaluatorContext;
}
public TableMetadata getTableMetadata(String tableName) {
return tables.get(tableName);
}
public TableMetadata addTable(String tableName, String eplExpression, String statementName, Class[] keyTypes, Map<String, TableMetadataColumn> tableColumns, TableStateRowFactory tableStateRowFactory, int numberMethodAggregations, StatementContext statementContext, ObjectArrayEventType internalEventType, ObjectArrayEventType publicEventType, TableMetadataInternalEventToPublic eventToPublic, boolean queryPlanLogging) throws ExprValidationException {
final TableMetadata metadata = new TableMetadata(tableName, eplExpression, statementName, keyTypes, tableColumns, tableStateRowFactory, numberMethodAggregations, statementContext, internalEventType, publicEventType, eventToPublic, queryPlanLogging);
// determine table state factory
TableStateFactory tableStateFactory;
if (keyTypes.length == 0) { // ungrouped
tableStateFactory = new TableStateFactory() {
public TableStateInstance makeTableState(AgentInstanceContext agentInstanceContext) {
return new TableStateInstanceUngroupedImpl(metadata, agentInstanceContext);
}
};
} else {
tableStateFactory = new TableStateFactory() {
public TableStateInstance makeTableState(AgentInstanceContext agentInstanceContext) {
return new TableStateInstanceGroupedImpl(metadata, agentInstanceContext);
}
};
}
metadata.setTableStateFactory(tableStateFactory);
tables.put(tableName, metadata);
return metadata;
}
public void removeTableIfFound(String tableName) {
TableMetadata metadata = tables.remove(tableName);
if (metadata != null) {
metadata.clearTableInstances();
}
}
public TableStateInstance getState(String name, int agentInstanceId) {
return assertGetState(name, agentInstanceId);
}
private TableStateInstance assertGetState(String name, int agentInstanceId) {
TableMetadata metadata = tables.get(name);
if (metadata == null) {
throw new IllegalArgumentException("Failed to find table for name '" + name + "'");
}
return metadata.getState(agentInstanceId);
}
public static Logger getQueryPlanLog() {
return QUERY_PLAN_LOG;
}
public TableMetadata getTableMetadataFromEventType(EventType type) {
String tableName = TableServiceUtil.getTableNameFromEventType(type);
if (tableName == null) {
return null;
}
return tables.get(tableName);
}
public Pair<ExprNode, List<ExprChainedSpec>> getTableNodeChainable(StreamTypeService streamTypeService,
List<ExprChainedSpec> chainSpec,
EngineImportService engineImportService)
throws ExprValidationException {
chainSpec = new ArrayList<ExprChainedSpec>(chainSpec);
String unresolvedPropertyName = chainSpec.get(0).getName();
StreamTableColWStreamName col = findTableColumnMayByPrefixed(streamTypeService, unresolvedPropertyName);
if (col == null) {
return null;
}
StreamTableColPair pair = col.getPair();
if (pair.getColumn() instanceof TableMetadataColumnAggregation) {
TableMetadataColumnAggregation agg = (TableMetadataColumnAggregation) pair.getColumn();
if (chainSpec.size() > 1) {
String candidateAccessor = chainSpec.get(1).getName();
ExprAggregateNodeBase exprNode = (ExprAggregateNodeBase) ASTAggregationHelper.tryResolveAsAggregation(engineImportService, false, candidateAccessor, new LazyAllocatedMap<ConfigurationPlugInAggregationMultiFunction, PlugInAggregationMultiFunctionFactory>(), streamTypeService.getEngineURIQualifier());
if (exprNode != null) {
ExprNode node = new ExprTableIdentNodeSubpropAccessor(pair.getStreamNum(), col.getOptionalStreamName(), agg, exprNode);
exprNode.addChildNodes(chainSpec.get(1).getParameters());
chainSpec.remove(0);
chainSpec.remove(0);
return new Pair<ExprNode, List<ExprChainedSpec>>(node, chainSpec);
}
}
ExprTableIdentNode node = new ExprTableIdentNode(null, unresolvedPropertyName);
ExprEvaluator eval = ExprTableEvalStrategyFactory.getTableAccessEvalStrategy(node, pair.getTableMetadata().getTableName(), pair.getStreamNum(), agg);
node.setEval(eval);
chainSpec.remove(0);
return new Pair<ExprNode, List<ExprChainedSpec>>(node, chainSpec);
}
return null;
}
public ExprTableIdentNode getTableIdentNode(StreamTypeService streamTypeService, String unresolvedPropertyName, String streamOrPropertyName)
throws ExprValidationException {
String propertyPrefixed = unresolvedPropertyName;
if (streamOrPropertyName != null) {
propertyPrefixed = streamOrPropertyName + "." + unresolvedPropertyName;
}
StreamTableColWStreamName col = findTableColumnMayByPrefixed(streamTypeService, propertyPrefixed);
if (col == null) {
return null;
}
StreamTableColPair pair = col.getPair();
if (pair.getColumn() instanceof TableMetadataColumnAggregation) {
TableMetadataColumnAggregation agg = (TableMetadataColumnAggregation) pair.getColumn();
ExprTableIdentNode node = new ExprTableIdentNode(streamOrPropertyName, unresolvedPropertyName);
ExprEvaluator eval = ExprTableEvalStrategyFactory.getTableAccessEvalStrategy(node, pair.getTableMetadata().getTableName(), pair.getStreamNum(), agg);
node.setEval(eval);
return node;
}
return null;
}
public void addTableUpdateStrategyReceiver(TableMetadata tableMetadata, String statementName, TableUpdateStrategyReceiver receiver, EventBeanUpdateHelper updateHelper, boolean isOnMerge) {
tableMetadata.addTableUpdateStrategyReceiver(statementName, receiver, updateHelper, isOnMerge);
}
public void removeTableUpdateStrategyReceivers(TableMetadata tableMetadata, String statementName) {
tableMetadata.removeTableUpdateStrategyReceivers(statementName);
}
public String[] getTables() {
return CollectionUtil.toArray(tables.keySet());
}
public TableAndLockProvider getStateProvider(String tableName, int agentInstanceId, boolean writesToTables) {
TableStateInstance instance = assertGetState(tableName, agentInstanceId);
Lock lock = writesToTables ? instance.getTableLevelRWLock().writeLock() : instance.getTableLevelRWLock().readLock();
if (instance instanceof TableStateInstanceGrouped) {
return new TableAndLockProviderGroupedImpl(new TableAndLockGrouped(lock, (TableStateInstanceGrouped) instance));
} else {
return new TableAndLockProviderUngroupedImpl(new TableAndLockUngrouped(lock, (TableStateInstanceUngrouped) instance));
}
}
private StreamTableColWStreamName findTableColumnMayByPrefixed(StreamTypeService streamTypeService, String streamAndPropName)
throws ExprValidationException {
int indexDot = streamAndPropName.indexOf(".");
if (indexDot == -1) {
StreamTableColPair pair = findTableColumnAcrossStreams(streamTypeService, streamAndPropName);
if (pair != null) {
return new StreamTableColWStreamName(pair, null);
}
} else {
String streamName = streamAndPropName.substring(0, indexDot);
String colName = streamAndPropName.substring(indexDot + 1);
int streamNum = streamTypeService.getStreamNumForStreamName(streamName);
if (streamNum == -1) {
return null;
}
StreamTableColPair pair = findTableColumnForType(streamNum, streamTypeService.getEventTypes()[streamNum], colName);
if (pair != null) {
return new StreamTableColWStreamName(pair, streamName);
}
}
return null;
}
public void removeIndexReferencesStmtMayRemoveIndex(String statementName, TableMetadata tableMetadata) {
tableMetadata.removeIndexReferencesStatement(statementName);
}
private StreamTableColPair findTableColumnAcrossStreams(StreamTypeService streamTypeService, String columnName)
throws ExprValidationException {
StreamTableColPair found = null;
for (int i = 0; i < streamTypeService.getEventTypes().length; i++) {
EventType type = streamTypeService.getEventTypes()[i];
StreamTableColPair pair = findTableColumnForType(i, type, columnName);
if (pair == null) {
continue;
}
if (found != null) {
if (streamTypeService.isStreamZeroUnambigous() && found.getStreamNum() == 0) {
continue;
}
throw new ExprValidationException("Ambiguous table column '" + columnName + "' should be prefixed by a stream name");
}
found = pair;
}
return found;
}
private StreamTableColPair findTableColumnForType(int streamNum, EventType type, String columnName) {
TableMetadata tableMetadata = getTableMetadataFromEventType(type);
if (tableMetadata != null) {
TableMetadataColumn column = tableMetadata.getTableColumns().get(columnName);
if (column != null) {
return new StreamTableColPair(streamNum, column, tableMetadata);
}
}
return null;
}
private static class StreamTableColPair {
private final int streamNum;
private final TableMetadataColumn column;
private final TableMetadata tableMetadata;
private StreamTableColPair(int streamNum, TableMetadataColumn column, TableMetadata tableMetadata) {
this.streamNum = streamNum;
this.column = column;
this.tableMetadata = tableMetadata;
}
public int getStreamNum() {
return streamNum;
}
public TableMetadataColumn getColumn() {
return column;
}
public TableMetadata getTableMetadata() {
return tableMetadata;
}
}
private static class StreamTableColWStreamName {
private final StreamTableColPair pair;
private final String optionalStreamName;
private StreamTableColWStreamName(StreamTableColPair pair, String optionalStreamName) {
this.pair = pair;
this.optionalStreamName = optionalStreamName;
}
public StreamTableColPair getPair() {
return pair;
}
public String getOptionalStreamName() {
return optionalStreamName;
}
}
}