All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.espertech.esper.epl.agg.rollup.GroupByExpressionHelper Maven / Gradle / Ivy

There is a newer version: 7.1.0
Show newest version
/*
 ***************************************************************************************
 *  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.rollup;

import com.espertech.esper.collection.CombinationEnumeration;
import com.espertech.esper.collection.MultiKeyInt;
import com.espertech.esper.epl.expression.core.ExprNode;
import com.espertech.esper.epl.expression.core.ExprNodeUtility;
import com.espertech.esper.epl.expression.core.ExprValidationException;
import com.espertech.esper.epl.expression.visitor.ExprNodeSubselectDeclaredDotVisitor;
import com.espertech.esper.epl.spec.*;
import com.espertech.esper.util.CollectionUtil;
import com.espertech.esper.util.SerializableObjectCopier;

import java.io.StringWriter;
import java.util.*;

public class GroupByExpressionHelper {
    public static GroupByClauseExpressions getGroupByRollupExpressions(List groupByElements,
                                                                       SelectClauseSpecRaw selectClauseSpec, ExprNode optionalHavingNode, List orderByList,
                                                                       ExprNodeSubselectDeclaredDotVisitor visitor)
            throws ExprValidationException {
        if (groupByElements == null || groupByElements.size() == 0) {
            return null;
        }

        // walk group-by-elements, determine group-by expressions and rollup nodes
        GroupByExpressionInfo groupByExpressionInfo = groupByToRollupNodes(groupByElements);

        // obtain expression nodes, collect unique nodes and assign index
        List distinctGroupByExpressions = new ArrayList();
        Map expressionToIndex = new HashMap();
        for (ExprNode exprNode : groupByExpressionInfo.getExpressions()) {
            boolean found = false;
            for (int i = 0; i < distinctGroupByExpressions.size(); i++) {
                ExprNode other = distinctGroupByExpressions.get(i);
                // find same expression
                if (ExprNodeUtility.deepEquals(exprNode, other, false)) {
                    expressionToIndex.put(exprNode, i);
                    found = true;
                    break;
                }
            }

            // not seen before
            if (!found) {
                expressionToIndex.put(exprNode, distinctGroupByExpressions.size());
                distinctGroupByExpressions.add(exprNode);
            }
        }

        // determine rollup, validate it is either (not both)
        boolean hasGroupingSet = false;
        boolean hasRollup = false;
        for (GroupByClauseElement element : groupByElements) {
            if (element instanceof GroupByClauseElementGroupingSet) {
                hasGroupingSet = true;
            }
            if (element instanceof GroupByClauseElementRollupOrCube) {
                hasRollup = true;
            }
        }

        // no-rollup or grouping-sets means simply validate
        ExprNode[] groupByExpressions = distinctGroupByExpressions.toArray(new ExprNode[distinctGroupByExpressions.size()]);
        if (!hasRollup && !hasGroupingSet) {
            return new GroupByClauseExpressions(groupByExpressions);
        }

        // evaluate rollup node roots
        List nodes = groupByExpressionInfo.getNodes();
        Object[][] perNodeCombinations = new Object[nodes.size()][];
        GroupByRollupEvalContext context = new GroupByRollupEvalContext(expressionToIndex);
        try {
            for (int i = 0; i < nodes.size(); i++) {
                GroupByRollupNodeBase node = nodes.get(i);
                List combinations = node.evaluate(context);
                perNodeCombinations[i] = new Object[combinations.size()];
                for (int j = 0; j < combinations.size(); j++) {
                    perNodeCombinations[i][j] = combinations.get(j);
                }
            }
        } catch (GroupByRollupDuplicateException ex) {
            if (ex.getIndexes().length == 0) {
                throw new ExprValidationException("Failed to validate the group-by clause, found duplicate specification of the overall grouping '()'");
            } else {
                StringWriter writer = new StringWriter();
                String delimiter = "";
                for (int i = 0; i < ex.getIndexes().length; i++) {
                    writer.append(delimiter);
                    writer.append(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(groupByExpressions[ex.getIndexes()[i]]));
                    delimiter = ", ";
                }
                throw new ExprValidationException("Failed to validate the group-by clause, found duplicate specification of expressions (" + writer.toString() + ")");
            }
        }

        // enumerate combinations building an index list
        CombinationEnumeration combinationEnumeration = new CombinationEnumeration(perNodeCombinations);
        Set combination = new TreeSet();
        Set indexList = new LinkedHashSet();
        for (; combinationEnumeration.hasMoreElements(); ) {
            combination.clear();
            Object[] combinationOA = combinationEnumeration.nextElement();
            for (Object indexes : combinationOA) {
                int[] indexarr = (int[]) indexes;
                for (int anIndex : indexarr) {
                    combination.add(anIndex);
                }
            }
            int[] indexArr = CollectionUtil.intArray(combination);
            indexList.add(new MultiKeyInt(indexArr));
        }

        // obtain rollup levels
        int[][] rollupLevels = new int[indexList.size()][];
        int count = 0;
        for (MultiKeyInt mk : indexList) {
            rollupLevels[count++] = mk.getKeys();
        }
        int numberOfLevels = rollupLevels.length;
        if (numberOfLevels == 1 && rollupLevels[0].length == 0) {
            throw new ExprValidationException("Failed to validate the group-by clause, the overall grouping '()' cannot be the only grouping");
        }

        // obtain select-expression copies for rewrite
        List expressions = selectClauseSpec.getSelectExprList();
        ExprNode[][] selects = new ExprNode[numberOfLevels][];
        for (int i = 0; i < numberOfLevels; i++) {
            selects[i] = new ExprNode[expressions.size()];
            for (int j = 0; j < expressions.size(); j++) {
                SelectClauseElementRaw selectRaw = expressions.get(j);
                if (!(selectRaw instanceof SelectClauseExprRawSpec)) {
                    throw new ExprValidationException("Group-by with rollup requires that the select-clause does not use wildcard");
                }
                SelectClauseExprRawSpec compiled = (SelectClauseExprRawSpec) selectRaw;
                selects[i][j] = copyVisitExpression(compiled.getSelectExpression(), visitor);
            }
        }

        // obtain having-expression copies for rewrite
        ExprNode[] optHavingNodeCopy = null;
        if (optionalHavingNode != null) {
            optHavingNodeCopy = new ExprNode[numberOfLevels];
            for (int i = 0; i < numberOfLevels; i++) {
                optHavingNodeCopy[i] = copyVisitExpression(optionalHavingNode, visitor);
            }
        }

        // obtain orderby-expression copies for rewrite
        ExprNode[][] optOrderByCopy = null;
        if (orderByList != null && orderByList.size() > 0) {
            optOrderByCopy = new ExprNode[numberOfLevels][];
            for (int i = 0; i < numberOfLevels; i++) {
                optOrderByCopy[i] = new ExprNode[orderByList.size()];
                for (int j = 0; j < orderByList.size(); j++) {
                    OrderByItem element = orderByList.get(j);
                    optOrderByCopy[i][j] = copyVisitExpression(element.getExprNode(), visitor);
                }
            }
        }

        return new GroupByClauseExpressions(groupByExpressions, rollupLevels, selects, optHavingNodeCopy, optOrderByCopy);
    }

    private static GroupByExpressionInfo groupByToRollupNodes(List groupByExpressions) {
        List parents = new ArrayList(groupByExpressions.size());
        List exprNodes = new ArrayList();

        for (GroupByClauseElement element : groupByExpressions) {

            GroupByRollupNodeBase parent;
            if (element instanceof GroupByClauseElementExpr) {
                GroupByClauseElementExpr expr = (GroupByClauseElementExpr) element;
                exprNodes.add(expr.getExpr());
                parent = new GroupByRollupNodeSingleExpr(expr.getExpr());
            } else if (element instanceof GroupByClauseElementRollupOrCube) {
                GroupByClauseElementRollupOrCube spec = (GroupByClauseElementRollupOrCube) element;
                parent = new GroupByRollupNodeRollupOrCube(spec.isCube());
                groupByAddRollup(spec, parent, exprNodes);
            } else if (element instanceof GroupByClauseElementGroupingSet) {
                GroupByClauseElementGroupingSet spec = (GroupByClauseElementGroupingSet) element;
                parent = new GroupByRollupNodeGroupingSet();
                for (GroupByClauseElement groupElement : spec.getElements()) {
                    if (groupElement instanceof GroupByClauseElementExpr) {
                        GroupByClauseElementExpr single = (GroupByClauseElementExpr) groupElement;
                        exprNodes.add(single.getExpr());
                        parent.add(new GroupByRollupNodeSingleExpr(single.getExpr()));
                    }
                    if (groupElement instanceof GroupByClauseElementCombinedExpr) {
                        GroupByClauseElementCombinedExpr combined = (GroupByClauseElementCombinedExpr) groupElement;
                        exprNodes.addAll(combined.getExpressions());
                        parent.add(new GroupByRollupNodeCombinedExpr(combined.getExpressions()));
                    }
                    if (groupElement instanceof GroupByClauseElementRollupOrCube) {
                        GroupByClauseElementRollupOrCube rollup = (GroupByClauseElementRollupOrCube) groupElement;
                        GroupByRollupNodeRollupOrCube node = new GroupByRollupNodeRollupOrCube(rollup.isCube());
                        groupByAddRollup(rollup, node, exprNodes);
                        parent.add(node);
                    }
                }
            } else {
                throw new IllegalStateException("Unexpected group-by clause element " + element);
            }
            parents.add(parent);
        }

        return new GroupByExpressionInfo(exprNodes, parents);
    }

    private static void groupByAddRollup(GroupByClauseElementRollupOrCube spec, GroupByRollupNodeBase parent, List exprNodes) {
        for (GroupByClauseElement rolledUp : spec.getRollupExpressions()) {
            if (rolledUp instanceof GroupByClauseElementExpr) {
                GroupByClauseElementExpr expr = (GroupByClauseElementExpr) rolledUp;
                exprNodes.add(expr.getExpr());
                parent.add(new GroupByRollupNodeSingleExpr(expr.getExpr()));
            } else {
                GroupByClauseElementCombinedExpr combined = (GroupByClauseElementCombinedExpr) rolledUp;
                exprNodes.addAll(combined.getExpressions());
                parent.add(new GroupByRollupNodeCombinedExpr(combined.getExpressions()));
            }
        }
    }

    private static ExprNode copyVisitExpression(ExprNode expression, ExprNodeSubselectDeclaredDotVisitor visitor) {
        try {
            ExprNode node = (ExprNode) SerializableObjectCopier.copy(expression);
            node.accept(visitor);
            return node;
        } catch (Exception e) {
            throw new RuntimeException("Internal error providing expression tree: " + e.getMessage(), e);
        }
    }

    private static class GroupByExpressionInfo {
        private final List expressions;
        private final List nodes;

        private GroupByExpressionInfo(List expressions, List nodes) {
            this.expressions = expressions;
            this.nodes = nodes;
        }

        public List getExpressions() {
            return expressions;
        }

        public List getNodes() {
            return nodes;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy