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); }
private static BindingMatchResult MatchBindingsAssignColumnNumbers( IntoTableSpec bindings, TableMetaData metadata, IList<AggregationServiceAggExpressionDesc> aggregations, IDictionary<ExprNode, string> selectClauseNamedNodes, IList<ExprForge[]> methodAggForgesList, IList<ExprDeclaredNode> declaredExpressions, ImportService importService, string statementName) { IDictionary<AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation> methodAggs = new LinkedHashMap<AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation>(); IDictionary<AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation> accessAggs = new LinkedHashMap<AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation>(); foreach (var aggDesc in aggregations) { // determine assigned name var columnName = FindColumnNameForAggregation( selectClauseNamedNodes, declaredExpressions, aggDesc.AggregationNode); if (columnName == null) { throw new ExprValidationException( "Failed to find an expression among the select-clause expressions for expression '" + ExprNodeUtilityPrint.ToExpressionStringMinPrecedenceSafe(aggDesc.AggregationNode) + "'"); } // determine binding metadata var columnMetadata = (TableMetadataColumnAggregation) metadata.Columns.Get(columnName); if (columnMetadata == null) { throw new ExprValidationException( "Failed to find name '" + columnName + "' among the columns for table '" + bindings.Name + "'"); } // validate compatible ValidateIntoTableCompatible(bindings.Name, columnName, columnMetadata, aggDesc); if (columnMetadata.IsMethodAgg) { methodAggs.Put(aggDesc, columnMetadata); } else { accessAggs.Put(aggDesc, columnMetadata); } } // handle method-aggs var methodPairs = new TableColumnMethodPairForge[methodAggForgesList.Count]; var methodIndex = -1; foreach (var methodEntry in methodAggs) { methodIndex++; var column = methodEntry.Value.Column; ExprForge[] forges = methodAggForgesList[methodIndex]; methodPairs[methodIndex] = new TableColumnMethodPairForge( forges, column, methodEntry.Key.AggregationNode); methodEntry.Key.SetColumnNum(column); } // handle access-aggs IDictionary<int, ExprNode> accessSlots = new LinkedHashMap<int, ExprNode>(); IList<AggregationAccessorSlotPairForge> accessReadPairs = new List<AggregationAccessorSlotPairForge>(); var accessIndex = -1; IList<AggregationAgentForge> agents = new List<AggregationAgentForge>(); foreach (var accessEntry in accessAggs) { accessIndex++; var column = accessEntry.Value.Column; // Slot is zero-based as we enter with zero-offset var aggregationMethodFactory = accessEntry.Key.Factory; var accessorForge = aggregationMethodFactory.AccessorForge; accessSlots.Put(column, accessEntry.Key.AggregationNode); accessReadPairs.Add(new AggregationAccessorSlotPairForge(column, accessorForge)); accessEntry.Key.SetColumnNum(column); agents.Add(aggregationMethodFactory.GetAggregationStateAgent(importService, statementName)); } var agentArr = agents.ToArray(); var accessReads = accessReadPairs.ToArray(); var targetStates = new int[accessSlots.Count]; var accessStateExpr = new ExprNode[accessSlots.Count]; var count = 0; foreach (var entry in accessSlots) { targetStates[count] = entry.Key; accessStateExpr[count] = entry.Value; count++; } return new BindingMatchResult(methodPairs, accessReads, targetStates, accessStateExpr, agentArr); }
private static BindingMatchResult MatchBindingsAssignColumnNumbers( IntoTableSpec bindings, TableMetadata metadata, IList <AggregationServiceAggExpressionDesc> aggregations, IDictionary <ExprNode, string> selectClauseNamedNodes, IList <ExprEvaluator> methodAggEvaluatorsList, IList <ExprDeclaredNode> declaredExpressions) { IDictionary <AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation> methodAggs = new LinkedHashMap <AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation>(); IDictionary <AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation> accessAggs = new LinkedHashMap <AggregationServiceAggExpressionDesc, TableMetadataColumnAggregation>(); foreach (var aggDesc in aggregations) { // determine assigned name var columnName = FindColumnNameForAggregation(selectClauseNamedNodes, declaredExpressions, aggDesc.AggregationNode); if (columnName == null) { throw new ExprValidationException("Failed to find an expression among the select-clause expressions for expression '" + aggDesc.AggregationNode.ToExpressionStringMinPrecedenceSafe() + "'"); } // determine binding metadata var columnMetadata = (TableMetadataColumnAggregation)metadata.TableColumns.Get(columnName); if (columnMetadata == null) { throw new ExprValidationException("Failed to find name '" + columnName + "' among the columns for table '" + bindings.Name + "'"); } // validate compatible ValidateIntoTableCompatible(bindings.Name, columnName, columnMetadata, aggDesc); if (!columnMetadata.Factory.IsAccessAggregation) { methodAggs.Put(aggDesc, columnMetadata); } else { accessAggs.Put(aggDesc, columnMetadata); } } // handle method-aggs var methodPairs = new TableColumnMethodPair[methodAggEvaluatorsList.Count]; var methodIndex = -1; foreach (var methodEntry in methodAggs) { methodIndex++; var targetIndex = methodEntry.Value.MethodOffset; methodPairs[methodIndex] = new TableColumnMethodPair(methodAggEvaluatorsList[methodIndex], targetIndex, methodEntry.Key.AggregationNode); methodEntry.Key.ColumnNum = targetIndex; } // handle access-aggs var accessSlots = new LinkedHashMap <int, ExprNode>(); var accessReadPairs = new List <AggregationAccessorSlotPair>(); var accessIndex = -1; var agents = new List <AggregationAgent>(); foreach (var accessEntry in accessAggs) { accessIndex++; var slot = accessEntry.Value.AccessAccessorSlotPair.Slot; var aggregationMethodFactory = accessEntry.Key.Factory; var accessor = aggregationMethodFactory.Accessor; accessSlots.Put(slot, accessEntry.Key.AggregationNode); accessReadPairs.Add(new AggregationAccessorSlotPair(slot, accessor)); accessEntry.Key.ColumnNum = metadata.NumberMethodAggregations + accessIndex; agents.Add(aggregationMethodFactory.AggregationStateAgent); } AggregationAgent[] agentArr = agents.ToArray(); AggregationAccessorSlotPair[] accessReads = accessReadPairs.ToArray(); var targetStates = new int[accessSlots.Count]; var accessStateExpr = new ExprNode[accessSlots.Count]; var count = 0; foreach (var entry in accessSlots) { targetStates[count] = entry.Key; accessStateExpr[count] = entry.Value; count++; } return(new BindingMatchResult(methodPairs, accessReads, targetStates, accessStateExpr, agentArr)); }
/// <summary> /// Returns the processor to use for a given select-clause. /// </summary> /// <param name="assignedTypeNumberStack">The assigned type number stack.</param> /// <param name="selectionList">the list of select clause elements/items, which are expected to have been validated</param> /// <param name="isUsingWildcard">true if the wildcard (*) occurs in the select clause</param> /// <param name="insertIntoDesc">contains column names for the optional insert-into clause (if supplied)</param> /// <param name="optionalInsertIntoEventType">Type of the optional insert into event.</param> /// <param name="forClauseSpec">For clause spec.</param> /// <param name="typeService">serves stream type information</param> /// <param name="eventAdapterService">for generating wrapper instances for events</param> /// <param name="statementResultService">handles listeners/subscriptions awareness to reduce output result generation</param> /// <param name="valueAddEventService">service that handles update events and variant events</param> /// <param name="selectExprEventTypeRegistry">registry for event type to statements</param> /// <param name="engineImportService"></param> /// <param name="exprEvaluatorContext">context for expression evalauation</param> /// <param name="variableService">The variable service.</param> /// <param name="scriptingService">The scripting service.</param> /// <param name="tableService">The table service.</param> /// <param name="timeProvider">The time provider.</param> /// <param name="engineURI">The engine URI.</param> /// <param name="statementId">The statement identifier.</param> /// <param name="statementName">Name of the statement.</param> /// <param name="annotations">The annotations.</param> /// <param name="contextDescriptor">The context descriptor.</param> /// <param name="configuration">The configuration.</param> /// <param name="selectExprProcessorCallback">The select expr processor callback.</param> /// <param name="namedWindowMgmtService">The named window service.</param> /// <param name="intoTableClause">The into table clause.</param> /// <param name="groupByRollupInfo"></param> /// <param name="statementExtensionSvcContext"></param> /// <returns> /// select-clause expression processor /// </returns> /// <exception cref="ExprValidationException">Expected any of the + Arrays.ToString(ForClauseKeyword.Values()).ToLowerCase() + for-clause keywords after reserved keyword 'for' /// or /// The for-clause with the + ForClauseKeyword.GROUPED_DELIVERY.Name + keyword requires one or more grouping expressions /// or /// The for-clause with the + ForClauseKeyword.DISCRETE_DELIVERY.Name + keyword does not allow grouping expressions /// or /// The for-clause with delivery keywords may only occur once in a statement /// or /// Expected any of the + Arrays.ToString(ForClauseKeyword.Values()).ToLowerCase() + for-clause keywords after reserved keyword 'for'</exception> /// <throws>ExprValidationException to indicate the select expression cannot be validated</throws> public static SelectExprProcessor GetProcessor( ICollection <int> assignedTypeNumberStack, SelectClauseElementCompiled[] selectionList, bool isUsingWildcard, InsertIntoDesc insertIntoDesc, EventType optionalInsertIntoEventType, ForClauseSpec forClauseSpec, StreamTypeService typeService, EventAdapterService eventAdapterService, StatementResultService statementResultService, ValueAddEventService valueAddEventService, SelectExprEventTypeRegistry selectExprEventTypeRegistry, EngineImportService engineImportService, ExprEvaluatorContext exprEvaluatorContext, VariableService variableService, ScriptingService scriptingService, TableService tableService, TimeProvider timeProvider, string engineURI, int statementId, string statementName, Attribute[] annotations, ContextDescriptor contextDescriptor, ConfigurationInformation configuration, SelectExprProcessorDeliveryCallback selectExprProcessorCallback, NamedWindowMgmtService namedWindowMgmtService, IntoTableSpec intoTableClause, GroupByRollupInfo groupByRollupInfo, StatementExtensionSvcContext statementExtensionSvcContext) { if (selectExprProcessorCallback != null) { var bindProcessor = new BindProcessor(selectionList, typeService.EventTypes, typeService.StreamNames, tableService); IDictionary <string, object> properties = new LinkedHashMap <string, object>(); for (var i = 0; i < bindProcessor.ColumnNamesAssigned.Length; i++) { properties.Put(bindProcessor.ColumnNamesAssigned[i], bindProcessor.ExpressionTypes[i]); } var eventType = eventAdapterService.CreateAnonymousObjectArrayType("Output_" + statementName, properties); return(new SelectExprProcessorWDeliveryCallback(eventType, bindProcessor, selectExprProcessorCallback)); } var synthetic = GetProcessorInternal( assignedTypeNumberStack, selectionList, isUsingWildcard, insertIntoDesc, optionalInsertIntoEventType, typeService, eventAdapterService, valueAddEventService, selectExprEventTypeRegistry, engineImportService, statementId, statementName, annotations, configuration, namedWindowMgmtService, tableService, groupByRollupInfo); // Handle table as an optional service if (statementResultService != null) { // Handle for-clause delivery contract checking ExprNode[] groupedDeliveryExpr = null; var forDelivery = false; if (forClauseSpec != null) { foreach (var item in forClauseSpec.Clauses) { if (item.Keyword == null) { throw new ExprValidationException("Expected any of the " + EnumHelper.GetValues <ForClauseKeyword>().Render().ToLower() + " for-clause keywords after reserved keyword 'for'"); } try { ForClauseKeyword keyword = EnumHelper.Parse <ForClauseKeyword>(item.Keyword); if ((keyword == ForClauseKeyword.GROUPED_DELIVERY) && (item.Expressions.IsEmpty())) { throw new ExprValidationException( "The for-clause with the " + ForClauseKeyword.GROUPED_DELIVERY.GetName() + " keyword requires one or more grouping expressions"); } if ((keyword == ForClauseKeyword.DISCRETE_DELIVERY) && (!item.Expressions.IsEmpty())) { throw new ExprValidationException( "The for-clause with the " + ForClauseKeyword.DISCRETE_DELIVERY.GetName() + " keyword does not allow grouping expressions"); } if (forDelivery) { throw new ExprValidationException( "The for-clause with delivery keywords may only occur once in a statement"); } } catch (ExprValidationException) { throw; } catch (EPException) { throw; } catch (Exception ex) { throw new ExprValidationException("Expected any of the " + EnumHelper.GetValues <ForClauseKeyword>().Render().ToLower() + " for-clause keywords after reserved keyword 'for'", ex); } StreamTypeService type = new StreamTypeServiceImpl(synthetic.ResultEventType, null, false, engineURI); groupedDeliveryExpr = new ExprNode[item.Expressions.Count]; var validationContext = new ExprValidationContext(type, engineImportService, statementExtensionSvcContext, null, timeProvider, variableService, tableService, exprEvaluatorContext, eventAdapterService, statementName, statementId, annotations, null, scriptingService, false, false, true, false, intoTableClause == null ? null : intoTableClause.Name, false); // no context descriptor available for (var i = 0; i < item.Expressions.Count; i++) { groupedDeliveryExpr[i] = ExprNodeUtility.GetValidatedSubtree(ExprNodeOrigin.FORCLAUSE, item.Expressions[i], validationContext); } forDelivery = true; } } var bindProcessor = new BindProcessor(selectionList, typeService.EventTypes, typeService.StreamNames, tableService); statementResultService.SetSelectClause(bindProcessor.ExpressionTypes, bindProcessor.ColumnNamesAssigned, forDelivery, ExprNodeUtility.GetEvaluators(groupedDeliveryExpr), exprEvaluatorContext); return(new SelectExprResultProcessor(statementResultService, synthetic, bindProcessor)); } return(synthetic); }
/// <summary> /// Returns an instance to handle the aggregation required by the aggregation expression nodes, depending on /// whether there are any group-by nodes. /// </summary> /// <param name="selectAggregateExprNodes">aggregation nodes extracted out of the select expression</param> /// <param name="selectClauseNamedNodes">The select clause named nodes.</param> /// <param name="declaredExpressions">The declared expressions.</param> /// <param name="groupByNodes">The group by nodes.</param> /// <param name="havingAggregateExprNodes">aggregation nodes extracted out of the select expression</param> /// <param name="orderByAggregateExprNodes">aggregation nodes extracted out of the select expression</param> /// <param name="groupKeyExpressions">The group key expressions.</param> /// <param name="hasGroupByClause">indicator on whethere there is group-by required, or group-all</param> /// <param name="annotations">statement annotations</param> /// <param name="variableService">variable</param> /// <param name="isJoin">true for joins</param> /// <param name="isDisallowNoReclaim">if set to <c>true</c> [is disallow no reclaim].</param> /// <param name="whereClause">the where-clause function if any</param> /// <param name="havingClause">the having-clause function if any</param> /// <param name="factoryService">The factory service.</param> /// <param name="typesPerStream">The types per stream.</param> /// <param name="methodResolutionService">The method resolution service.</param> /// <param name="groupByRollupDesc">The group by rollup desc.</param> /// <param name="optionalContextName">Name of the optional context.</param> /// <param name="intoTableSpec">The into table spec.</param> /// <param name="tableService">The table service.</param> /// <returns> /// instance for aggregation handling /// </returns> /// <throws>com.espertech.esper.epl.expression.core.ExprValidationException if validation fails</throws> public static AggregationServiceFactoryDesc GetService( IList <ExprAggregateNode> selectAggregateExprNodes, IDictionary <ExprNode, string> selectClauseNamedNodes, IList <ExprDeclaredNode> declaredExpressions, ExprNode[] groupByNodes, IList <ExprAggregateNode> havingAggregateExprNodes, IList <ExprAggregateNode> orderByAggregateExprNodes, IList <ExprAggregateNodeGroupKey> groupKeyExpressions, bool hasGroupByClause, Attribute[] annotations, VariableService variableService, bool isJoin, bool isDisallowNoReclaim, ExprNode whereClause, ExprNode havingClause, AggregationServiceFactoryService factoryService, EventType[] typesPerStream, MethodResolutionService methodResolutionService, AggregationGroupByRollupDesc groupByRollupDesc, string optionalContextName, IntoTableSpec intoTableSpec, TableService tableService) { // 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.GetEmptyList <AggregationServiceAggExpressionDesc>(), Collections.GetEmptyList <ExprAggregateNodeGroupKey>())); } // 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(); if (whereClause != null) { whereClause.Accept(visitor); } if (havingClause != null) { havingClause.Accept(visitor); } if ((visitor.Previous != null) && (!visitor.Previous.IsEmpty())) { string funcname = visitor.Previous[0].Second.PreviousType.ToString().ToLower(); 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. IList <AggregationServiceAggExpressionDesc> aggregations = new List <AggregationServiceAggExpressionDesc>(); foreach (var selectAggNode in selectAggregateExprNodes) { AddEquivalent(selectAggNode, aggregations); } foreach (var havingAggNode in havingAggregateExprNodes) { AddEquivalent(havingAggNode, aggregations); } foreach (var orderByAggNode in 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. IList <ExprEvaluator> methodAggEvaluatorsList = new List <ExprEvaluator>(); foreach (var aggregation in aggregations) { var aggregateNode = aggregation.AggregationNode; if (!aggregateNode.Factory.IsAccessAggregation) { var evaluator = aggregateNode.Factory.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 var metadata = tableService.GetTableMetadata(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.ContextName, optionalContextName, false); // validate group keys var groupByTypes = ExprNodeUtility.GetExprResultTypes(groupByNodes); ExprTableNodeUtil.ValidateExpressions(intoTableSpec.Name, groupByTypes, "group-by", groupByNodes, metadata.KeyTypes, "group-by"); // determine how this binds to existing aggregations, assign column numbers var bindingMatchResult = MatchBindingsAssignColumnNumbers(intoTableSpec, metadata, aggregations, selectClauseNamedNodes, methodAggEvaluatorsList, declaredExpressions); // return factory AggregationServiceFactory aggregationServiceFactory; if (!hasGroupByClause) { aggregationServiceFactory = factoryService.GetNoGroupWBinding(bindingMatchResult.Accessors, isJoin, bindingMatchResult.MethodPairs, intoTableSpec.Name, bindingMatchResult.TargetStates, bindingMatchResult.AccessStateExpr, bindingMatchResult.Agents); } else { aggregationServiceFactory = factoryService.GetGroupWBinding(metadata, bindingMatchResult.MethodPairs, bindingMatchResult.Accessors, isJoin, intoTableSpec, bindingMatchResult.TargetStates, bindingMatchResult.AccessStateExpr, bindingMatchResult.Agents, groupByRollupDesc); } return(new AggregationServiceFactoryDesc(aggregationServiceFactory, aggregations, groupKeyExpressions)); } // 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.ColumnNum = columnNumber++; } } foreach (var entry in aggregations) { if (entry.Factory.IsAccessAggregation) { entry.ColumnNum = columnNumber++; } } // determine method aggregation factories and evaluators(non-access) ExprEvaluator[] methodAggEvaluators = methodAggEvaluatorsList.ToArray(); var methodAggFactories = new AggregationMethodFactory[methodAggEvaluators.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); var accessorPairs = multiFunctionAggPlan.AccessorPairs; var accessAggregations = multiFunctionAggPlan.StateFactories; 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)TypeHelper.GetAnnotationHook(annotations, HookType.INTERNAL_AGGLOCALLEVEL, typeof(AggregationLocalLevelHook), null); 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) { var groupKeyBinding = methodResolutionService.GetGroupKeyBinding(localGroupByPlan); serviceFactory = factoryService.GetNoGroupLocalGroupBy(isJoin, localGroupByPlan, groupKeyBinding); } else if ((methodAggEvaluators.Length > 0) && (accessorPairs.Length == 0)) { serviceFactory = factoryService.GetNoGroupNoAccess(methodAggEvaluators, methodAggFactories); } else if ((methodAggEvaluators.Length == 0) && (accessorPairs.Length > 0)) { serviceFactory = factoryService.GetNoGroupAccessOnly(accessorPairs, accessAggregations, isJoin); } else { serviceFactory = factoryService.GetNoGroupAccessMixed(methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin); } } else { 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) { var groupKeyBinding = methodResolutionService.GetGroupKeyBinding(localGroupByPlan); serviceFactory = factoryService.GetGroupLocalGroupBy(isJoin, localGroupByPlan, groupKeyBinding); } else { var groupKeyBinding = methodResolutionService.GetGroupKeyBinding(groupByNodes, groupByRollupDesc); if (!isDisallowNoReclaim && hasNoReclaim) { if (groupByRollupDesc != null) { throw GetRollupReclaimEx(); } if ((methodAggEvaluators.Length > 0) && (accessorPairs.Length == 0)) { serviceFactory = factoryService.GetGroupedNoReclaimNoAccess(methodAggEvaluators, methodAggFactories, groupKeyBinding); } else if ((methodAggEvaluators.Length == 0) && (accessorPairs.Length > 0)) { serviceFactory = factoryService.GetGroupNoReclaimAccessOnly(accessorPairs, accessAggregations, groupKeyBinding, isJoin); } else { serviceFactory = factoryService.GetGroupNoReclaimMixed(methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, groupKeyBinding); } } else if (!isDisallowNoReclaim && reclaimGroupAged != null) { if (groupByRollupDesc != null) { throw GetRollupReclaimEx(); } serviceFactory = factoryService.GetGroupReclaimAged(methodAggEvaluators, methodAggFactories, reclaimGroupAged, reclaimGroupFrequency, variableService, accessorPairs, accessAggregations, isJoin, groupKeyBinding, optionalContextName); } else if (groupByRollupDesc != null) { serviceFactory = factoryService.GetGroupReclaimMixableRollup(methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, groupKeyBinding, groupByRollupDesc); } else { if ((methodAggEvaluators.Length > 0) && (accessorPairs.Length == 0)) { serviceFactory = factoryService.GetGroupReclaimNoAccess(methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, groupKeyBinding); } else { serviceFactory = factoryService.GetGroupReclaimMixable(methodAggEvaluators, methodAggFactories, accessorPairs, accessAggregations, isJoin, groupKeyBinding); } } } } return(new AggregationServiceFactoryDesc(serviceFactory, aggregations, groupKeyExpressions)); }