com.espertech.esper.epl.agg.service.AggregationServiceFactoryFactory Maven / Gradle / Ivy
Show all versions of esper Show documentation
/*
***************************************************************************************
* 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.agg.service;
import com.espertech.esper.client.EPException;
import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.EventType;
import com.espertech.esper.client.annotation.Hint;
import com.espertech.esper.client.annotation.HintEnum;
import com.espertech.esper.client.annotation.HookType;
import com.espertech.esper.epl.agg.access.AggregationAccessor;
import com.espertech.esper.epl.agg.access.AggregationAccessorSlotPair;
import com.espertech.esper.epl.agg.access.AggregationAgent;
import com.espertech.esper.epl.agg.util.*;
import com.espertech.esper.epl.core.EngineImportService;
import com.espertech.esper.epl.declexpr.ExprDeclaredNode;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateLocalGroupByDesc;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateNode;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeBase;
import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeGroupKey;
import com.espertech.esper.epl.expression.core.*;
import com.espertech.esper.epl.expression.methodagg.ExprMethodAggUtil;
import com.espertech.esper.epl.expression.table.ExprTableNodeUtil;
import com.espertech.esper.epl.expression.visitor.ExprNodePreviousVisitorWParent;
import com.espertech.esper.epl.spec.IntoTableSpec;
import com.espertech.esper.epl.table.mgmt.TableColumnMethodPair;
import com.espertech.esper.epl.table.mgmt.TableMetadata;
import com.espertech.esper.epl.table.mgmt.TableMetadataColumnAggregation;
import com.espertech.esper.epl.table.mgmt.TableService;
import com.espertech.esper.epl.util.EPLValidationUtil;
import com.espertech.esper.epl.variable.VariableService;
import com.espertech.esper.util.JavaClassHelper;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* Factory for aggregation service instances.
*
* Consolidates aggregation nodes such that result futures point to a single instance and
* no re-evaluation of the same result occurs.
*/
public class AggregationServiceFactoryFactory {
/**
* Produces an aggregation service for use with match-recognice.
*
* @param numStreams number of streams
* @param measureExprNodesPerStream measure nodes
* @param typesPerStream type information
* @return service
* @throws ExprValidationException for validation errors
*/
public static AggregationServiceMatchRecognizeFactoryDesc getServiceMatchRecognize(int numStreams,
Map> measureExprNodesPerStream,
EventType[] typesPerStream)
throws ExprValidationException {
Map> equivalencyListPerStream = new TreeMap>();
for (Map.Entry> entry : measureExprNodesPerStream.entrySet()) {
List equivalencyList = new ArrayList();
equivalencyListPerStream.put(entry.getKey(), equivalencyList);
for (ExprAggregateNode selectAggNode : entry.getValue()) {
addEquivalent(selectAggNode, equivalencyList);
}
}
LinkedHashMap aggregatorsPerStream = new LinkedHashMap();
Map evaluatorsPerStream = new HashMap();
for (Map.Entry> equivalencyPerStream : equivalencyListPerStream.entrySet()) {
int index = 0;
int stream = equivalencyPerStream.getKey();
AggregationMethodFactory[] aggregators = new AggregationMethodFactory[equivalencyPerStream.getValue().size()];
aggregatorsPerStream.put(stream, aggregators);
ExprEvaluator[] evaluators = new ExprEvaluator[equivalencyPerStream.getValue().size()];
evaluatorsPerStream.put(stream, evaluators);
for (AggregationServiceAggExpressionDesc aggregation : equivalencyPerStream.getValue()) {
ExprAggregateNode aggregateNode = aggregation.getAggregationNode();
if (aggregateNode.getChildNodes().length > 1) {
evaluators[index] = ExprMethodAggUtil.getMultiNodeEvaluator(aggregateNode.getChildNodes(), typesPerStream.length > 1, typesPerStream);
} else if (aggregateNode.getChildNodes().length > 0) {
// Use the evaluation node under the aggregation node to obtain the aggregation value
evaluators[index] = aggregateNode.getChildNodes()[0].getExprEvaluator();
} else {
// For aggregation that doesn't evaluate any particular sub-expression, return null on evaluation
evaluators[index] = new ExprEvaluator() {
public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext exprEvaluatorContext) {
return null;
}
public Class getType() {
return null;
}
};
}
aggregators[index] = aggregateNode.getFactory();
index++;
}
}
// Assign a column number to each aggregation node. The regular aggregation goes first followed by access-aggregation.
int columnNumber = 0;
List allExpressions = new ArrayList();
for (Map.Entry> equivalencyPerStream : equivalencyListPerStream.entrySet()) {
for (AggregationServiceAggExpressionDesc entry : equivalencyPerStream.getValue()) {
entry.setColumnNum(columnNumber++);
}
allExpressions.addAll(equivalencyPerStream.getValue());
}
AggregationServiceMatchRecognizeFactory factory = new AggregationServiceMatchRecognizeFactoryImpl(numStreams, aggregatorsPerStream, evaluatorsPerStream);
return new AggregationServiceMatchRecognizeFactoryDesc(factory, allExpressions);
}
public static AggregationServiceFactoryDesc getService(List selectAggregateExprNodes,
Map selectClauseNamedNodes,
List declaredExpressions,
ExprNode[] groupByNodes,
List havingAggregateExprNodes,
List orderByAggregateExprNodes,
List groupKeyExpressions,
boolean hasGroupByClause,
Annotation[] annotations,
VariableService variableService,
boolean isJoin,
boolean isDisallowNoReclaim,
ExprNode whereClause,
ExprNode havingClause,
AggregationServiceFactoryService factoryService,
EventType[] typesPerStream,
AggregationGroupByRollupDesc groupByRollupDesc,
String optionalContextName,
IntoTableSpec intoTableSpec,
TableService tableService,
boolean isUnidirectional,
boolean isFireAndForget,
boolean isOnSelect,
EngineImportService engineImportService)
throws ExprValidationException {
// No aggregates used, we do not need this service
if ((selectAggregateExprNodes.isEmpty()) && (havingAggregateExprNodes.isEmpty())) {
if (intoTableSpec != null) {
throw new ExprValidationException("Into-table requires at least one aggregation function");
}
return new AggregationServiceFactoryDesc(factoryService.getNullAggregationService(), Collections.emptyList(), Collections.emptyList());
}
// Validate the absence of "prev" function in where-clause:
// Since the "previous" function does not post remove stream results, disallow when used with aggregations.
if ((whereClause != null) || (havingClause != null)) {
ExprNodePreviousVisitorWParent visitor = new ExprNodePreviousVisitorWParent();
if (whereClause != null) {
whereClause.accept(visitor);
}
if (havingClause != null) {
havingClause.accept(visitor);
}
if ((visitor.getPrevious() != null) && (!visitor.getPrevious().isEmpty())) {
String funcname = visitor.getPrevious().get(0).getSecond().getPreviousType().toString().toLowerCase(Locale.ENGLISH);
throw new ExprValidationException("The '" + funcname + "' function may not occur in the where-clause or having-clause of a statement with aggregations as 'previous' does not provide remove stream data; Use the 'first','last','window' or 'count' aggregation functions instead");
}
}
// Compile a map of aggregation nodes and equivalent-to aggregation nodes.
// Equivalent-to functions are for example "select sum(a*b), 5*sum(a*b)".
// Reducing the total number of aggregation functions.
List aggregations = new ArrayList();
for (ExprAggregateNode selectAggNode : selectAggregateExprNodes) {
addEquivalent(selectAggNode, aggregations);
}
for (ExprAggregateNode havingAggNode : havingAggregateExprNodes) {
addEquivalent(havingAggNode, aggregations);
}
for (ExprAggregateNode orderByAggNode : orderByAggregateExprNodes) {
addEquivalent(orderByAggNode, aggregations);
}
// Construct a list of evaluation node for the aggregation functions (regular agg).
// For example "sum(2 * 3)" would make the sum an evaluation node.
List methodAggEvaluatorsList = new ArrayList();
for (AggregationServiceAggExpressionDesc aggregation : aggregations) {
ExprAggregateNode aggregateNode = aggregation.getAggregationNode();
if (!aggregateNode.getFactory().isAccessAggregation()) {
ExprEvaluator evaluator = aggregateNode.getFactory().getMethodAggregationEvaluator(typesPerStream.length > 1, typesPerStream);
methodAggEvaluatorsList.add(evaluator);
}
}
// determine local group-by, report when hook provided
AggregationGroupByLocalGroupDesc localGroupDesc = analyzeLocalGroupBy(aggregations, groupByNodes, groupByRollupDesc, intoTableSpec);
// determine binding
if (intoTableSpec != null) {
// obtain metadata
TableMetadata metadata = tableService.getTableMetadata(intoTableSpec.getName());
if (metadata == null) {
throw new ExprValidationException("Invalid into-table clause: Failed to find table by name '" + intoTableSpec.getName() + "'");
}
EPLValidationUtil.validateContextName(true, intoTableSpec.getName(), metadata.getContextName(), optionalContextName, false);
// validate group keys
Class[] groupByTypes = ExprNodeUtility.getExprResultTypes(groupByNodes);
ExprTableNodeUtil.validateExpressions(intoTableSpec.getName(), groupByTypes, "group-by", groupByNodes,
metadata.getKeyTypes(), "group-by");
// determine how this binds to existing aggregations, assign column numbers
BindingMatchResult bindingMatchResult = matchBindingsAssignColumnNumbers(intoTableSpec, metadata, aggregations, selectClauseNamedNodes, methodAggEvaluatorsList, declaredExpressions);
// return factory
AggregationServiceFactory serviceFactory;
if (!hasGroupByClause) {
serviceFactory = factoryService.getNoGroupWBinding(bindingMatchResult.getAccessors(), isJoin, bindingMatchResult.getMethodPairs(), intoTableSpec.getName(), bindingMatchResult.getTargetStates(), bindingMatchResult.getAccessStateExpr(), bindingMatchResult.getAgents());
} else {
serviceFactory = factoryService.getGroupWBinding(metadata, bindingMatchResult.getMethodPairs(), bindingMatchResult.getAccessors(), isJoin, intoTableSpec, bindingMatchResult.getTargetStates(), bindingMatchResult.getAccessStateExpr(), bindingMatchResult.getAgents(), groupByRollupDesc);
}
return new AggregationServiceFactoryDesc(serviceFactory, aggregations, groupKeyExpressions);
}
// Assign a column number to each aggregation node. The regular aggregation goes first followed by access-aggregation.
int columnNumber = 0;
for (AggregationServiceAggExpressionDesc entry : aggregations) {
if (!entry.getFactory().isAccessAggregation()) {
entry.setColumnNum(columnNumber++);
}
}
for (AggregationServiceAggExpressionDesc entry : aggregations) {
if (entry.getFactory().isAccessAggregation()) {
entry.setColumnNum(columnNumber++);
}
}
// determine method aggregation factories and evaluators(non-access)
ExprEvaluator[] methodAggEvaluators = methodAggEvaluatorsList.toArray(new ExprEvaluator[methodAggEvaluatorsList.size()]);
AggregationMethodFactory[] methodAggFactories = new AggregationMethodFactory[methodAggEvaluators.length];
int count = 0;
for (AggregationServiceAggExpressionDesc aggregation : aggregations) {
ExprAggregateNode aggregateNode = aggregation.getAggregationNode();
if (!aggregateNode.getFactory().isAccessAggregation()) {
methodAggFactories[count] = aggregateNode.getFactory();
count++;
}
}
// handle access aggregations
AggregationMultiFunctionAnalysisResult multiFunctionAggPlan = AggregationMultiFunctionAnalysisHelper.analyzeAccessAggregations(aggregations);
AggregationAccessorSlotPair[] accessorPairs = multiFunctionAggPlan.getAccessorPairs();
AggregationStateFactory[] accessAggregations = multiFunctionAggPlan.getStateFactories();
AggregationServiceFactory serviceFactory;
// analyze local group by
AggregationLocalGroupByPlan localGroupByPlan = null;
if (localGroupDesc != null) {
localGroupByPlan = AggregationGroupByLocalGroupByAnalyzer.analyze(methodAggEvaluators, methodAggFactories, accessAggregations, localGroupDesc, groupByNodes, accessorPairs);
try {
AggregationLocalLevelHook hook = (AggregationLocalLevelHook) JavaClassHelper.getAnnotationHook(annotations, HookType.INTERNAL_AGGLOCALLEVEL, AggregationLocalLevelHook.class, engineImportService);
if (hook != null) {
hook.planned(localGroupDesc, localGroupByPlan);
}
} catch (ExprValidationException e) {
throw new EPException("Failed to obtain hook for " + HookType.INTERNAL_AGGLOCALLEVEL);
}
}
// Handle without a group-by clause: we group all into the same pot
if (!hasGroupByClause) {
if (localGroupByPlan != null) {
serviceFactory = factoryService.getNoGroupLocalGroupBy(isJoin, localGroupByPlan, isUnidirectional, isFireAndForget, isOnSelect);
} else if ((methodAggEvaluators.length > 0) && (accessorPairs.length == 0)) {
serviceFactory = factoryService.getNoGroupNoAccess(methodAggEvaluators, methodAggFactories, isUnidirectional, isFireAndForget, isOnSelect);
} else if ((methodAggEvaluators.length == 0) && (accessorPairs.length > 0)) {
serviceFactory = factoryService.getNoGroupAccessOnly(accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect);
} else {
serviceFactory = factoryService.getNoGroupAccessMixed(methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect);
}
} else {
boolean hasNoReclaim = HintEnum.DISABLE_RECLAIM_GROUP.getHint(annotations) != null;
Hint reclaimGroupAged = HintEnum.RECLAIM_GROUP_AGED.getHint(annotations);
Hint reclaimGroupFrequency = HintEnum.RECLAIM_GROUP_AGED.getHint(annotations);
if (localGroupByPlan != null) {
serviceFactory = factoryService.getGroupLocalGroupBy(isJoin, localGroupByPlan, isUnidirectional, isFireAndForget, isOnSelect);
} else {
if (!isDisallowNoReclaim && hasNoReclaim) {
if (groupByRollupDesc != null) {
throw getRollupReclaimEx();
}
if ((methodAggEvaluators.length > 0) && (accessorPairs.length == 0)) {
serviceFactory = factoryService.getGroupedNoReclaimNoAccess(groupByNodes, methodAggEvaluators, methodAggFactories, isUnidirectional, isFireAndForget, isOnSelect);
} else if ((methodAggEvaluators.length == 0) && (accessorPairs.length > 0)) {
serviceFactory = factoryService.getGroupNoReclaimAccessOnly(groupByNodes, accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect);
} else {
serviceFactory = factoryService.getGroupNoReclaimMixed(groupByNodes, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect);
}
} else if (!isDisallowNoReclaim && reclaimGroupAged != null) {
if (groupByRollupDesc != null) {
throw getRollupReclaimEx();
}
serviceFactory = factoryService.getGroupReclaimAged(groupByNodes, methodAggEvaluators, methodAggFactories, reclaimGroupAged, reclaimGroupFrequency, variableService, accessorPairs, accessAggregations, isJoin, optionalContextName, isUnidirectional, isFireAndForget, isOnSelect);
} else if (groupByRollupDesc != null) {
serviceFactory = factoryService.getGroupReclaimMixableRollup(groupByNodes, groupByRollupDesc, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, groupByRollupDesc, isUnidirectional, isFireAndForget, isOnSelect);
} else {
if ((methodAggEvaluators.length > 0) && (accessorPairs.length == 0)) {
serviceFactory = factoryService.getGroupReclaimNoAccess(groupByNodes, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect);
} else {
serviceFactory = factoryService.getGroupReclaimMixable(groupByNodes, methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, isUnidirectional, isFireAndForget, isOnSelect);
}
}
}
}
return new AggregationServiceFactoryDesc(serviceFactory, aggregations, groupKeyExpressions);
}
private static AggregationGroupByLocalGroupDesc analyzeLocalGroupBy(List aggregations, ExprNode[] groupByNodes, AggregationGroupByRollupDesc groupByRollupDesc, IntoTableSpec intoTableSpec) throws ExprValidationException {
boolean hasOver = false;
for (AggregationServiceAggExpressionDesc desc : aggregations) {
if (desc.getAggregationNode().getOptionalLocalGroupBy() != null) {
hasOver = true;
break;
}
}
if (!hasOver) {
return null;
}
if (groupByRollupDesc != null) {
throw new ExprValidationException("Roll-up and group-by parameters cannot be combined");
}
if (intoTableSpec != null) {
throw new ExprValidationException("Into-table and group-by parameters cannot be combined");
}
List partitions = new ArrayList();
for (AggregationServiceAggExpressionDesc desc : aggregations) {
ExprAggregateLocalGroupByDesc localGroupBy = desc.getAggregationNode().getOptionalLocalGroupBy();
ExprNode[] partitionExpressions = localGroupBy == null ? groupByNodes : localGroupBy.getPartitionExpressions();
List found = findPartition(partitions, partitionExpressions);
if (found == null) {
found = new ArrayList();
AggregationGroupByLocalGroupLevel level = new AggregationGroupByLocalGroupLevel(partitionExpressions, found);
partitions.add(level);
}
found.add(desc);
}
// check single group-by partition and it matches the group-by clause
if (partitions.size() == 1 && ExprNodeUtility.deepEqualsIgnoreDupAndOrder(partitions.get(0).getPartitionExpr(), groupByNodes)) {
return null;
}
return new AggregationGroupByLocalGroupDesc(aggregations.size(), partitions.toArray(new AggregationGroupByLocalGroupLevel[partitions.size()]));
}
private static List findPartition(List partitions, ExprNode[] partitionExpressions) {
for (AggregationGroupByLocalGroupLevel level : partitions) {
if (ExprNodeUtility.deepEqualsIgnoreDupAndOrder(level.getPartitionExpr(), partitionExpressions)) {
return level.getExpressions();
}
}
return null;
}
private static BindingMatchResult matchBindingsAssignColumnNumbers(IntoTableSpec bindings,
TableMetadata metadata,
List aggregations,
Map selectClauseNamedNodes,
List methodAggEvaluatorsList, List declaredExpressions)
throws ExprValidationException {
Map methodAggs = new LinkedHashMap();
Map accessAggs = new LinkedHashMap();
for (AggregationServiceAggExpressionDesc aggDesc : aggregations) {
// determine assigned name
String columnName = findColumnNameForAggregation(selectClauseNamedNodes, declaredExpressions, aggDesc.getAggregationNode());
if (columnName == null) {
throw new ExprValidationException("Failed to find an expression among the select-clause expressions for expression '" + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(aggDesc.getAggregationNode()) + "'");
}
// determine binding metadata
TableMetadataColumnAggregation columnMetadata = (TableMetadataColumnAggregation) metadata.getTableColumns().get(columnName);
if (columnMetadata == null) {
throw new ExprValidationException("Failed to find name '" + columnName + "' among the columns for table '" + bindings.getName() + "'");
}
// validate compatible
validateIntoTableCompatible(bindings.getName(), columnName, columnMetadata, aggDesc);
if (!columnMetadata.getFactory().isAccessAggregation()) {
methodAggs.put(aggDesc, columnMetadata);
} else {
accessAggs.put(aggDesc, columnMetadata);
}
}
// handle method-aggs
TableColumnMethodPair[] methodPairs = new TableColumnMethodPair[methodAggEvaluatorsList.size()];
int methodIndex = -1;
for (Map.Entry methodEntry : methodAggs.entrySet()) {
methodIndex++;
int targetIndex = methodEntry.getValue().getMethodOffset();
methodPairs[methodIndex] = new TableColumnMethodPair(methodAggEvaluatorsList.get(methodIndex), targetIndex, methodEntry.getKey().getAggregationNode());
methodEntry.getKey().setColumnNum(targetIndex);
}
// handle access-aggs
Map accessSlots = new LinkedHashMap();
List accessReadPairs = new ArrayList();
int accessIndex = -1;
List agents = new ArrayList();
for (Map.Entry accessEntry : accessAggs.entrySet()) {
accessIndex++;
int slot = accessEntry.getValue().getAccessAccessorSlotPair().getSlot();
AggregationMethodFactory aggregationMethodFactory = accessEntry.getKey().getFactory();
AggregationAccessor accessor = aggregationMethodFactory.getAccessor();
accessSlots.put(slot, accessEntry.getKey().getAggregationNode());
accessReadPairs.add(new AggregationAccessorSlotPair(slot, accessor));
accessEntry.getKey().setColumnNum(metadata.getNumberMethodAggregations() + accessIndex);
agents.add(aggregationMethodFactory.getAggregationStateAgent());
}
AggregationAgent[] agentArr = agents.toArray(new AggregationAgent[agents.size()]);
AggregationAccessorSlotPair[] accessReads = accessReadPairs.toArray(new AggregationAccessorSlotPair[accessReadPairs.size()]);
int[] targetStates = new int[accessSlots.size()];
ExprNode[] accessStateExpr = new ExprNode[accessSlots.size()];
int count = 0;
for (Map.Entry entry : accessSlots.entrySet()) {
targetStates[count] = entry.getKey();
accessStateExpr[count] = entry.getValue();
count++;
}
return new BindingMatchResult(methodPairs, accessReads, targetStates, accessStateExpr, agentArr);
}
private static String findColumnNameForAggregation(Map selectClauseNamedNodes, List declaredExpressions, ExprAggregateNode aggregationNode) {
if (selectClauseNamedNodes.containsKey(aggregationNode)) {
return selectClauseNamedNodes.get(aggregationNode);
}
for (ExprDeclaredNode node : declaredExpressions) {
if (node.getBody() == aggregationNode) {
return node.getPrototype().getName();
}
}
return null;
}
private static void validateIntoTableCompatible(String tableName, String columnName, TableMetadataColumnAggregation columnMetadata, AggregationServiceAggExpressionDesc aggDesc)
throws ExprValidationException {
AggregationMethodFactory factoryProvided = aggDesc.getFactory();
AggregationMethodFactory factoryRequired = columnMetadata.getFactory();
try {
factoryRequired.validateIntoTableCompatible(factoryProvided);
} catch (ExprValidationException ex) {
String text = getMessage(tableName, columnName, factoryRequired.getAggregationExpression(), factoryProvided.getAggregationExpression());
throw new ExprValidationException(text + ": " + ex.getMessage(), ex);
}
}
private static String getMessage(String tableName, String columnName, ExprAggregateNodeBase aggregationRequired, ExprAggregateNodeBase aggregationProvided) {
return "Incompatible aggregation function for table '" +
tableName +
"' column '" +
columnName + "', expecting '" +
ExprNodeUtility.toExpressionStringMinPrecedenceSafe(aggregationRequired) +
"' and received '" +
ExprNodeUtility.toExpressionStringMinPrecedenceSafe(aggregationProvided) +
"'";
}
private static void addEquivalent(ExprAggregateNode aggNodeToAdd, List equivalencyList) {
// Check any same aggregation nodes among all aggregation clauses
boolean foundEquivalent = false;
for (AggregationServiceAggExpressionDesc existing : equivalencyList) {
ExprAggregateNode aggNode = existing.getAggregationNode();
// we have equivalence when:
// (a) equals on node returns true
// (b) positional parameters are the same
// (c) non-positional (group-by over, if present, are the same ignoring duplicates)
if (!aggNode.equalsNode(aggNodeToAdd, false)) {
continue;
}
if (!ExprNodeUtility.deepEquals(aggNode.getPositionalParams(), aggNodeToAdd.getPositionalParams(), false)) {
continue;
}
if (aggNode.getOptionalLocalGroupBy() != null || aggNodeToAdd.getOptionalLocalGroupBy() != null) {
if ((aggNode.getOptionalLocalGroupBy() == null && aggNodeToAdd.getOptionalLocalGroupBy() != null) ||
(aggNode.getOptionalLocalGroupBy() != null && aggNodeToAdd.getOptionalLocalGroupBy() == null)) {
continue;
}
if (!ExprNodeUtility.deepEqualsIgnoreDupAndOrder(aggNode.getOptionalLocalGroupBy().getPartitionExpressions(), aggNodeToAdd.getOptionalLocalGroupBy().getPartitionExpressions())) {
continue;
}
}
existing.addEquivalent(aggNodeToAdd);
foundEquivalent = true;
break;
}
if (!foundEquivalent) {
equivalencyList.add(new AggregationServiceAggExpressionDesc(aggNodeToAdd, aggNodeToAdd.getFactory()));
}
}
public static ExprValidationException getRollupReclaimEx() {
return new ExprValidationException("Reclaim hints are not available with rollup");
}
private static class BindingMatchResult {
private final TableColumnMethodPair[] methodPairs;
private final AggregationAccessorSlotPair[] accessors;
private final int[] targetStates;
private final ExprNode[] accessStateExpr;
private final AggregationAgent[] agents;
private BindingMatchResult(TableColumnMethodPair[] methodPairs, AggregationAccessorSlotPair[] accessors, int[] targetStates, ExprNode[] accessStateExpr, AggregationAgent[] agents) {
this.methodPairs = methodPairs;
this.accessors = accessors;
this.targetStates = targetStates;
this.accessStateExpr = accessStateExpr;
this.agents = agents;
}
public TableColumnMethodPair[] getMethodPairs() {
return methodPairs;
}
public AggregationAccessorSlotPair[] getAccessors() {
return accessors;
}
public int[] getTargetStates() {
return targetStates;
}
public AggregationAgent[] getAgents() {
return agents;
}
public ExprNode[] getAccessStateExpr() {
return accessStateExpr;
}
}
}