public void Planned( AggregationGroupByLocalGroupDesc localGroupDesc, AggregationLocalGroupByPlanForge localGroupByPlan) { desc = new Pair<AggregationGroupByLocalGroupDesc, AggregationLocalGroupByPlanForge>( localGroupDesc, localGroupByPlan); }
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); }