Example #1
0
        private static AggregationGroupByLocalGroupDesc AnalyzeLocalGroupBy(
            IList<AggregationServiceAggExpressionDesc> aggregations,
            ExprNode[] groupByNodes,
            AggregationGroupByRollupDescForge groupByRollupDesc,
            IntoTableSpec intoTableSpec)
        {
            var hasOver = false;
            foreach (var desc in aggregations) {
                if (desc.AggregationNode.OptionalLocalGroupBy != 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");
            }

            IList<AggregationGroupByLocalGroupLevel> partitions = new List<AggregationGroupByLocalGroupLevel>();
            foreach (var desc in aggregations) {
                var localGroupBy = desc.AggregationNode.OptionalLocalGroupBy;

                var partitionExpressions = localGroupBy == null ? groupByNodes : localGroupBy.PartitionExpressions;
                var found = FindPartition(partitions, partitionExpressions);
                if (found == null) {
                    found = new List<AggregationServiceAggExpressionDesc>();
                    var 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.Count == 1 &&
                ExprNodeUtilityCompare.DeepEqualsIgnoreDupAndOrder(partitions[0].PartitionExpr, groupByNodes)) {
                return null;
            }

            return new AggregationGroupByLocalGroupDesc(aggregations.Count, partitions.ToArray());
        }
Example #2
0
        public static AggregationServiceForgeDesc GetService(
            IList<ExprAggregateNode> selectAggregateExprNodes,
            IDictionary<ExprNode, string> selectClauseNamedNodes,
            IList<ExprDeclaredNode> declaredExpressions,
            ExprNode[] groupByNodes,
            MultiKeyClassRef groupByMultiKey,
            IList<ExprAggregateNode> havingAggregateExprNodes,
            IList<ExprAggregateNode> orderByAggregateExprNodes,
            IList<ExprAggregateNodeGroupKey> groupKeyExpressions,
            bool hasGroupByClause,
            Attribute[] annotations,
            VariableCompileTimeResolver variableCompileTimeResolver,
            bool isDisallowNoReclaim,
            ExprNode whereClause,
            ExprNode havingClause,
            EventType[] typesPerStream,
            AggregationGroupByRollupDescForge groupByRollupDesc,
            string optionalContextName,
            IntoTableSpec intoTableSpec,
            TableCompileTimeResolver tableCompileTimeResolver,
            bool isUnidirectional,
            bool isFireAndForget,
            bool isOnSelect,
            ImportServiceCompileTime importService,
            StatementRawInfo raw,
            SerdeCompileTimeResolver serdeResolver)
        {
            // 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 AggregationServiceForgeDesc(
                    AggregationServiceNullFactory.INSTANCE,
                    EmptyList<AggregationServiceAggExpressionDesc>.Instance,
                    EmptyList<ExprAggregateNodeGroupKey>.Instance,
                    EmptyList<StmtClassForgeableFactory>.Instance);
            }

            // 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) {
                var visitor = new ExprNodePreviousVisitorWParent();
                whereClause?.Accept(visitor);

                havingClause?.Accept(visitor);

                if (visitor.Previous != null && !visitor.Previous.IsEmpty()) {
                    string funcname = visitor.Previous[0]
                        .Second.PreviousType.ToString()
                        .ToLowerInvariant();
                    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.
            var aggregations = new List<AggregationServiceAggExpressionDesc>();
            var intoTableNonRollup = groupByRollupDesc == null && intoTableSpec != null;
            foreach (var selectAggNode in selectAggregateExprNodes) {
                AddEquivalent(selectAggNode, aggregations, intoTableNonRollup);
            }

            foreach (var havingAggNode in havingAggregateExprNodes) {
                AddEquivalent(havingAggNode, aggregations, intoTableNonRollup);
            }

            foreach (var orderByAggNode in orderByAggregateExprNodes) {
                AddEquivalent(orderByAggNode, aggregations, intoTableNonRollup);
            }

            // Construct a list of evaluation node for the aggregation functions (regular agg).
            // For example "sum(2 * 3)" would make the sum an evaluation node.
            IList<ExprForge[]> methodAggForgesList = new List<ExprForge[]>();
            foreach (var aggregation in aggregations) {
                var aggregateNode = aggregation.AggregationNode;
                if (!aggregateNode.Factory.IsAccessAggregation) {
                    var forges = aggregateNode.Factory.GetMethodAggregationForge(
                        typesPerStream.Length > 1,
                        typesPerStream);
                    methodAggForgesList.Add(forges);
                }
            }

            // determine local group-by, report when hook provided
            var localGroupDesc = AnalyzeLocalGroupBy(aggregations, groupByNodes, groupByRollupDesc, intoTableSpec);

            // determine binding
            if (intoTableSpec != null) {
                // obtain metadata
                var metadata = tableCompileTimeResolver.Resolve(intoTableSpec.Name);
                if (metadata == null) {
                    throw new ExprValidationException(
                        "Invalid into-table clause: Failed to find table by name '" + intoTableSpec.Name + "'");
                }

                EPLValidationUtil.ValidateContextName(
                    true,
                    intoTableSpec.Name,
                    metadata.OptionalContextName,
                    optionalContextName,
                    false);

                // validate group keys
                var groupByTypes = ExprNodeUtilityQuery.GetExprResultTypes(groupByNodes);
                var keyTypes = metadata.IsKeyed ? metadata.KeyTypes : new Type[0];
                ExprTableNodeUtil.ValidateExpressions(
                    intoTableSpec.Name,
                    groupByTypes,
                    "group-by",
                    groupByNodes,
                    keyTypes,
                    "group-by");

                // determine how this binds to existing aggregations, assign column numbers
                var bindingMatchResult = MatchBindingsAssignColumnNumbers(
                    intoTableSpec,
                    metadata,
                    aggregations,
                    selectClauseNamedNodes,
                    methodAggForgesList,
                    declaredExpressions,
                    importService,
                    raw.StatementName);

                // return factory
                AggregationServiceFactoryForge serviceForgeX = new AggregationServiceFactoryForgeTable(
                    metadata,
                    bindingMatchResult.MethodPairs,
                    bindingMatchResult.TargetStates,
                    bindingMatchResult.Agents,
                    groupByRollupDesc);
                return new AggregationServiceForgeDesc(serviceForgeX, aggregations, groupKeyExpressions, EmptyList<StmtClassForgeableFactory>.Instance);
            }

            // Assign a column number to each aggregation node. The regular aggregation goes first followed by access-aggregation.
            var columnNumber = 0;
            foreach (var entry in aggregations) {
                if (!entry.Factory.IsAccessAggregation) {
                    entry.SetColumnNum(columnNumber++);
                }
            }

            foreach (var entry in aggregations) {
                if (entry.Factory.IsAccessAggregation) {
                    entry.SetColumnNum(columnNumber++);
                }
            }

            // determine method aggregation factories and evaluators(non-access)
            var methodAggForges = methodAggForgesList.ToArray();
            var methodAggFactories = new AggregationForgeFactory[methodAggForges.Length];
            var count = 0;
            foreach (var aggregation in aggregations) {
                var aggregateNode = aggregation.AggregationNode;
                if (!aggregateNode.Factory.IsAccessAggregation) {
                    methodAggFactories[count] = aggregateNode.Factory;
                    count++;
                }
            }

            // handle access aggregations
            var multiFunctionAggPlan = AggregationMultiFunctionAnalysisHelper.AnalyzeAccessAggregations(
                aggregations,
                importService,
                isFireAndForget,
                raw.StatementName,
                groupByNodes);
            var accessorPairsForge = multiFunctionAggPlan.AccessorPairsForge;
            var accessFactories = multiFunctionAggPlan.StateFactoryForges;
            var hasAccessAgg = accessorPairsForge.Length > 0;
            var hasMethodAgg = methodAggFactories.Length > 0;

            AggregationServiceFactoryForge serviceForge;
            var useFlags = new AggregationUseFlags(isUnidirectional, isFireAndForget, isOnSelect);
            var additionalForgeables = new List<StmtClassForgeableFactory>();

            // analyze local group by
            AggregationLocalGroupByPlanForge localGroupByPlan = null;
            if (localGroupDesc != null) {
                AggregationLocalGroupByPlanDesc plan = AggregationGroupByLocalGroupByAnalyzer.Analyze(
                    methodAggForges,
                    methodAggFactories,
                    accessFactories,
                    localGroupDesc,
                    groupByNodes,
                    groupByMultiKey,
                    accessorPairsForge,
                    raw,
                    serdeResolver);
                localGroupByPlan = plan.Forge;
                additionalForgeables.AddAll(plan.AdditionalForgeables);

                try {
                    var hook = (AggregationLocalLevelHook) ImportUtil.GetAnnotationHook(
                        annotations,
                        HookType.INTERNAL_AGGLOCALLEVEL,
                        typeof(AggregationLocalLevelHook),
                        importService);
                    hook?.Planned(localGroupDesc, localGroupByPlan);
                }
                catch (ExprValidationException) {
                    throw new EPException("Failed to obtain hook for " + HookType.INTERNAL_AGGLOCALLEVEL);
                }
            }

            // Handle without a group-by clause: we group all into the same pot
            var rowStateDesc = new AggregationRowStateForgeDesc(
                hasMethodAgg ? methodAggFactories : null,
                hasMethodAgg ? methodAggForges : null,
                hasAccessAgg ? accessFactories : null,
                hasAccessAgg ? accessorPairsForge : null,
                useFlags);
            if (!hasGroupByClause) {
                if (localGroupByPlan != null) {
                    serviceForge = new AggSvcLocalGroupByForge(false, localGroupByPlan, useFlags);
                }
                else {
                    serviceForge = new AggregationServiceGroupAllForge(rowStateDesc);
                }
            }
            else {
                var groupDesc = new AggGroupByDesc(
                    rowStateDesc,
                    isUnidirectional,
                    isFireAndForget,
                    isOnSelect,
                    groupByNodes,
                    groupByMultiKey);
                var hasNoReclaim = HintEnum.DISABLE_RECLAIM_GROUP.GetHint(annotations) != null;
                var reclaimGroupAged = HintEnum.RECLAIM_GROUP_AGED.GetHint(annotations);
                var reclaimGroupFrequency = HintEnum.RECLAIM_GROUP_AGED.GetHint(annotations);
                if (localGroupByPlan != null) {
                    serviceForge = new AggSvcLocalGroupByForge(true, localGroupByPlan, useFlags);
                }
                else {
                    if (!isDisallowNoReclaim && hasNoReclaim) {
                        if (groupByRollupDesc != null) {
                            throw GetRollupReclaimEx();
                        }

                        serviceForge = new AggregationServiceGroupByForge(groupDesc, importService.TimeAbacus);
                    }
                    else if (!isDisallowNoReclaim && reclaimGroupAged != null) {
                        if (groupByRollupDesc != null) {
                            throw GetRollupReclaimEx();
                        }

                        CompileReclaim(
                            groupDesc,
                            reclaimGroupAged,
                            reclaimGroupFrequency,
                            variableCompileTimeResolver,
                            optionalContextName);
                        serviceForge = new AggregationServiceGroupByForge(groupDesc, importService.TimeAbacus);
                    }
                    else if (groupByRollupDesc != null) {
                        serviceForge = new AggSvcGroupByRollupForge(rowStateDesc, groupByRollupDesc, groupByNodes);
                    }
                    else {
                        groupDesc.IsRefcounted = true;
                        serviceForge = new AggregationServiceGroupByForge(groupDesc, importService.TimeAbacus);
                    }
                }
            }

            return new AggregationServiceForgeDesc(serviceForge, aggregations, groupKeyExpressions, additionalForgeables);
        }