/* *************************************************************************************** * 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.core.start; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.client.annotation.HintEnum; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.context.activator.ViewableActivationResult; import com.espertech.esper.core.context.activator.ViewableActivator; import com.espertech.esper.core.context.subselect.*; import com.espertech.esper.core.context.util.AgentInstanceContext; import com.espertech.esper.core.context.util.ContextPropertyRegistry; import com.espertech.esper.core.service.EPServicesContext; import com.espertech.esper.core.service.ExprEvaluatorContextStatement; import com.espertech.esper.core.service.StatementContext; import com.espertech.esper.epl.agg.service.AggregationService; import com.espertech.esper.epl.agg.service.AggregationServiceFactoryDesc; import com.espertech.esper.epl.agg.service.AggregationServiceFactoryFactory; import com.espertech.esper.epl.core.StreamTypeService; import com.espertech.esper.epl.core.StreamTypeServiceImpl; import com.espertech.esper.epl.core.ViewResourceDelegateUnverified; import com.espertech.esper.epl.core.ViewResourceDelegateVerified; import com.espertech.esper.epl.declexpr.ExprDeclaredNode; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNode; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeGroupKey; import com.espertech.esper.epl.expression.baseagg.ExprAggregateNodeUtil; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.prev.ExprPreviousNode; import com.espertech.esper.epl.expression.prior.ExprPriorNode; import com.espertech.esper.epl.expression.subquery.ExprSubselectNode; import com.espertech.esper.epl.expression.subquery.ExprSubselectStrategy; import com.espertech.esper.epl.expression.visitor.ExprNodeIdentifierVisitor; import com.espertech.esper.epl.expression.visitor.ExprNodeSubselectDeclaredNoTraverseVisitor; import com.espertech.esper.epl.join.hint.ExcludePlanHint; import com.espertech.esper.epl.join.hint.IndexHint; import com.espertech.esper.epl.join.plan.CoercionDesc; import com.espertech.esper.epl.join.plan.CoercionUtil; import com.espertech.esper.epl.join.plan.QueryPlanIndexBuilder; import com.espertech.esper.epl.join.table.EventTableFactory; import com.espertech.esper.epl.join.util.IndexNameAndDescPair; import com.espertech.esper.epl.join.util.QueryPlanIndexDescSubquery; import com.espertech.esper.epl.join.util.QueryPlanIndexHook; import com.espertech.esper.epl.join.util.QueryPlanIndexHookUtil; import com.espertech.esper.epl.lookup.*; import com.espertech.esper.epl.named.NamedWindowProcessor; import com.espertech.esper.epl.spec.*; import com.espertech.esper.epl.subquery.SubselectAggregationPreprocessorBase; import com.espertech.esper.epl.table.mgmt.TableMetadata; import com.espertech.esper.metrics.instrumentation.InstrumentationAgent; import com.espertech.esper.metrics.instrumentation.InstrumentationHelper; import com.espertech.esper.util.AuditPath; import com.espertech.esper.util.CollectionUtil; import com.espertech.esper.util.JavaClassHelper; import com.espertech.esper.util.StopCallback; import com.espertech.esper.view.StoppableView; import com.espertech.esper.view.ViewFactoryChain; import com.espertech.esper.view.ViewProcessingException; import com.espertech.esper.view.ViewServiceHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.annotation.Annotation; import java.util.*; public class EPStatementStartMethodHelperSubselect { private static final Logger QUERY_PLAN_LOG = LoggerFactory.getLogger(AuditPath.QUERYPLAN_LOG); private final static String MSG_SUBQUERY_REQUIRES_WINDOW = "Subqueries require one or more views to limit the stream, consider declaring a length or time window (applies to correlated or non-fully-aggregated subqueries)"; protected static SubSelectActivationCollection createSubSelectActivation(EPServicesContext services, StatementSpecCompiled statementSpecContainer, StatementContext statementContext, EPStatementDestroyCallbackList destroyCallbacks) throws ExprValidationException, ViewProcessingException { SubSelectActivationCollection subSelectStreamDesc = new SubSelectActivationCollection(); int subselectStreamNumber = 1024; // Process all subselect expression nodes for (ExprSubselectNode subselect : statementSpecContainer.getSubSelectExpressions()) { StatementSpecCompiled statementSpec = subselect.getStatementSpecCompiled(); StreamSpecCompiled streamSpec = statementSpec.getStreamSpecs()[0]; if (streamSpec instanceof FilterStreamSpecCompiled) { FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) statementSpec.getStreamSpecs()[0]; subselectStreamNumber++; InstrumentationAgent instrumentationAgentSubquery = null; if (InstrumentationHelper.ENABLED) { final String eventTypeName = filterStreamSpec.getFilterSpec().getFilterForEventType().getName(); final ExprSubselectNode exprSubselectNode = subselect; instrumentationAgentSubquery = new InstrumentationAgent() { public void indicateQ() { InstrumentationHelper.get().qFilterActivationSubselect(eventTypeName, exprSubselectNode); } public void indicateA() { InstrumentationHelper.get().aFilterActivationSubselect(); } }; } // Register filter, create view factories ViewableActivator activatorDeactivator = services.getViewableActivatorFactory().createFilterProxy(services, filterStreamSpec.getFilterSpec(), statementSpec.getAnnotations(), true, instrumentationAgentSubquery, false, null); ViewFactoryChain viewFactoryChain = services.getViewService().createFactories(subselectStreamNumber, filterStreamSpec.getFilterSpec().getResultEventType(), filterStreamSpec.getViewSpecs(), filterStreamSpec.getOptions(), statementContext, true, subselect.getSubselectNumber()); subselect.setRawEventType(viewFactoryChain.getEventType()); // Add lookup to list, for later starts subSelectStreamDesc.add(subselect, new SubSelectActivationHolder(subselectStreamNumber, filterStreamSpec.getFilterSpec().getResultEventType(), viewFactoryChain, activatorDeactivator, streamSpec)); } else if (streamSpec instanceof TableQueryStreamSpec) { TableQueryStreamSpec table = (TableQueryStreamSpec) streamSpec; TableMetadata metadata = services.getTableService().getTableMetadata(table.getTableName()); ViewFactoryChain viewFactoryChain = ViewFactoryChain.fromTypeNoViews(metadata.getInternalEventType()); ViewableActivator viewableActivator = services.getViewableActivatorFactory().createTable(metadata, null); subSelectStreamDesc.add(subselect, new SubSelectActivationHolder(subselectStreamNumber, metadata.getInternalEventType(), viewFactoryChain, viewableActivator, streamSpec)); subselect.setRawEventType(metadata.getInternalEventType()); destroyCallbacks.addCallback(new EPStatementDestroyCallbackTableIdxRef(services.getTableService(), metadata, statementContext.getStatementName())); services.getStatementVariableRefService().addReferences(statementContext.getStatementName(), metadata.getTableName()); } else { NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) statementSpec.getStreamSpecs()[0]; NamedWindowProcessor processor = services.getNamedWindowMgmtService().getProcessor(namedSpec.getWindowName()); EventType namedWindowType = processor.getTailView().getEventType(); if (namedSpec.getOptPropertyEvaluator() != null) { namedWindowType = namedSpec.getOptPropertyEvaluator().getFragmentEventType(); } // if named-window index sharing is disabled (the default) or filter expressions are provided then consume the insert-remove stream boolean disableIndexShare = HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE.getHint(statementSpecContainer.getAnnotations()) != null; if (disableIndexShare && processor.isVirtualDataWindow()) { disableIndexShare = false; } if (!namedSpec.getFilterExpressions().isEmpty() || !processor.isEnableSubqueryIndexShare() || disableIndexShare) { ViewableActivator activatorNamedWindow = services.getViewableActivatorFactory().createNamedWindow(processor, namedSpec, statementContext); ViewFactoryChain viewFactoryChain = services.getViewService().createFactories(0, namedWindowType, namedSpec.getViewSpecs(), namedSpec.getOptions(), statementContext, true, subselect.getSubselectNumber()); subselect.setRawEventType(viewFactoryChain.getEventType()); subSelectStreamDesc.add(subselect, new SubSelectActivationHolder(subselectStreamNumber, namedWindowType, viewFactoryChain, activatorNamedWindow, streamSpec)); services.getNamedWindowConsumerMgmtService().addConsumer(statementContext, namedSpec); } else { // else if there are no named window stream filter expressions and index sharing is enabled ViewFactoryChain viewFactoryChain = services.getViewService().createFactories(0, processor.getNamedWindowType(), namedSpec.getViewSpecs(), namedSpec.getOptions(), statementContext, true, subselect.getSubselectNumber()); subselect.setRawEventType(processor.getNamedWindowType()); ViewableActivator activator = services.getViewableActivatorFactory().makeSubqueryNWIndexShare(); subSelectStreamDesc.add(subselect, new SubSelectActivationHolder(subselectStreamNumber, namedWindowType, viewFactoryChain, activator, streamSpec)); services.getStatementVariableRefService().addReferences(statementContext.getStatementName(), processor.getNamedWindowType().getName()); } } } return subSelectStreamDesc; } protected static SubSelectStrategyCollection planSubSelect(EPServicesContext services, StatementContext statementContext, boolean queryPlanLogging, SubSelectActivationCollection subSelectStreamDesc, String[] outerStreamNames, EventType[] outerEventTypesSelect, String[] outerEventTypeNamees, ExprDeclaredNode[] declaredExpressions, ContextPropertyRegistry contextPropertyRegistry) throws ExprValidationException, ViewProcessingException { int subqueryNum = -1; SubSelectStrategyCollection collection = new SubSelectStrategyCollection(); Map<ExprDeclaredNode, List<ExprDeclaredNode>> declaredExpressionCallHierarchy = null; if (declaredExpressions.length > 0) { declaredExpressionCallHierarchy = ExprNodeUtility.getDeclaredExpressionCallHierarchy(declaredExpressions); } for (Map.Entry<ExprSubselectNode, SubSelectActivationHolder> entry : subSelectStreamDesc.getSubqueries().entrySet()) { subqueryNum++; ExprSubselectNode subselect = entry.getKey(); SubSelectActivationHolder subSelectActivation = entry.getValue(); try { SubSelectStrategyFactoryDesc factoryDesc = planSubSelectInternal(subqueryNum, subselect, subSelectActivation, services, statementContext, queryPlanLogging, subSelectStreamDesc, outerStreamNames, outerEventTypesSelect, outerEventTypeNamees, declaredExpressions, contextPropertyRegistry, declaredExpressionCallHierarchy); collection.add(subselect, factoryDesc); } catch (Exception ex) { throw new ExprValidationException("Failed to plan " + getSubqueryInfoText(subqueryNum, subselect) + ": " + ex.getMessage(), ex); } } return collection; } public static Map<ExprSubselectNode, SubSelectStrategyHolder> startSubselects( EPServicesContext services, SubSelectStrategyCollection subSelectStrategyCollection, final AgentInstanceContext agentInstanceContext, List<StopCallback> stopCallbackList, boolean isRecoveringResilient) { Map<ExprSubselectNode, SubSelectStrategyHolder> subselectStrategies = new HashMap<ExprSubselectNode, SubSelectStrategyHolder>(); for (Map.Entry<ExprSubselectNode, SubSelectStrategyFactoryDesc> subselectEntry : subSelectStrategyCollection.getSubqueries().entrySet()) { ExprSubselectNode subselectNode = subselectEntry.getKey(); SubSelectStrategyFactoryDesc factoryDesc = subselectEntry.getValue(); SubSelectActivationHolder holder = factoryDesc.getSubSelectActivationHolder(); // activate viewable ViewableActivationResult subselectActivationResult = holder.getActivator().activate(agentInstanceContext, true, isRecoveringResilient); stopCallbackList.add(subselectActivationResult.getStopCallback()); // apply returning the strategy instance SubSelectStrategyRealization result = factoryDesc.getFactory().instantiate(services, subselectActivationResult.getViewable(), agentInstanceContext, stopCallbackList, factoryDesc.getSubqueryNumber(), isRecoveringResilient); // handle stoppable view if (result.getSubselectView() instanceof StoppableView) { stopCallbackList.add((StoppableView) result.getSubselectView()); } if (result.getSubselectAggregationService() != null) { final AggregationService subselectAggregationService = result.getSubselectAggregationService(); stopCallbackList.add(new StopCallback() { public void stop() { subselectAggregationService.stop(); } }); } // set aggregation final SubordTableLookupStrategy lookupStrategy = result.getStrategy(); final SubselectAggregationPreprocessorBase aggregationPreprocessor = result.getSubselectAggregationPreprocessor(); // determine strategy ExprSubselectStrategy strategy; if (aggregationPreprocessor != null) { strategy = new ExprSubselectStrategy() { public Collection<EventBean> evaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { Collection<EventBean> matchingEvents = lookupStrategy.lookup(eventsPerStream, exprEvaluatorContext); aggregationPreprocessor.evaluate(eventsPerStream, matchingEvents, exprEvaluatorContext); return CollectionUtil.SINGLE_NULL_ROW_EVENT_SET; } }; } else { strategy = new ExprSubselectStrategy() { public Collection<EventBean> evaluateMatching(EventBean[] eventsPerStream, ExprEvaluatorContext exprEvaluatorContext) { return lookupStrategy.lookup(eventsPerStream, exprEvaluatorContext); } }; } SubSelectStrategyHolder instance = new SubSelectStrategyHolder(strategy, result.getSubselectAggregationService(), result.getPriorNodeStrategies(), result.getPreviousNodeStrategies(), result.getSubselectView(), result.getPostLoad(), subselectActivationResult); subselectStrategies.put(subselectNode, instance); } return subselectStrategies; } private static Pair<EventTableFactory, SubordTableLookupStrategyFactory> determineSubqueryIndexFactory(ExprNode filterExpr, EventType viewableEventType, EventType[] outerEventTypes, StreamTypeService subselectTypeService, boolean fullTableScan, boolean queryPlanLogging, Set<String> optionalUniqueProps, StatementContext statementContext, int subqueryNum) throws ExprValidationException { Pair<EventTableFactory, SubordTableLookupStrategyFactory> result = determineSubqueryIndexInternalFactory(filterExpr, viewableEventType, outerEventTypes, subselectTypeService, fullTableScan, optionalUniqueProps, statementContext); QueryPlanIndexHook hook = QueryPlanIndexHookUtil.getHook(statementContext.getAnnotations(), statementContext.getEngineImportService()); if (queryPlanLogging && (QUERY_PLAN_LOG.isInfoEnabled() || hook != null)) { QUERY_PLAN_LOG.info("local index"); QUERY_PLAN_LOG.info("strategy " + result.getSecond().toQueryPlan()); QUERY_PLAN_LOG.info("table " + result.getFirst().toQueryPlan()); if (hook != null) { String strategyName = result.getSecond().getClass().getSimpleName(); hook.subquery(new QueryPlanIndexDescSubquery( new IndexNameAndDescPair[]{ new IndexNameAndDescPair(null, result.getFirst().getEventTableClass().getSimpleName()) }, subqueryNum, strategyName)); } } return result; } private static Pair<EventTableFactory, SubordTableLookupStrategyFactory> determineSubqueryIndexInternalFactory(ExprNode filterExpr, EventType viewableEventType, EventType[] outerEventTypes, StreamTypeService subselectTypeService, boolean fullTableScan, Set<String> optionalUniqueProps, StatementContext statementContext) throws ExprValidationException { // No filter expression means full table scan if ((filterExpr == null) || fullTableScan) { EventTableFactory tableFactory = statementContext.getEventTableIndexService().createUnindexed(0, null, false); SubordFullTableScanLookupStrategyFactory strategy = new SubordFullTableScanLookupStrategyFactory(); return new Pair<EventTableFactory, SubordTableLookupStrategyFactory>(tableFactory, strategy); } // Build a list of streams and indexes ExcludePlanHint excludePlanHint = ExcludePlanHint.getHint(subselectTypeService.getStreamNames(), statementContext); SubordPropPlan joinPropDesc = QueryPlanIndexBuilder.getJoinProps(filterExpr, outerEventTypes.length, subselectTypeService.getEventTypes(), excludePlanHint); Map<String, SubordPropHashKey> hashKeys = joinPropDesc.getHashProps(); Map<String, SubordPropRangeKey> rangeKeys = joinPropDesc.getRangeProps(); List<SubordPropHashKey> hashKeyList = new ArrayList<SubordPropHashKey>(hashKeys.values()); List<SubordPropRangeKey> rangeKeyList = new ArrayList<SubordPropRangeKey>(rangeKeys.values()); boolean unique = false; ExprNode[] inKeywordSingleIdxKeys = null; ExprNode inKeywordMultiIdxKey = null; // If this is a unique-view and there are unique criteria, use these if (optionalUniqueProps != null && !optionalUniqueProps.isEmpty()) { boolean found = true; for (String uniqueProp : optionalUniqueProps) { if (!hashKeys.containsKey(uniqueProp)) { found = false; break; } } if (found) { String[] hashKeysArray = hashKeys.keySet().toArray(new String[hashKeys.keySet().size()]); for (String hashKey : hashKeysArray) { if (!optionalUniqueProps.contains(hashKey)) { hashKeys.remove(hashKey); } } hashKeyList = new ArrayList<SubordPropHashKey>(hashKeys.values()); unique = true; rangeKeyList.clear(); rangeKeys.clear(); } } // build table (local table) EventTableFactory eventTableFactory; CoercionDesc hashCoercionDesc; CoercionDesc rangeCoercionDesc; if (hashKeys.size() != 0 && rangeKeys.isEmpty()) { String[] indexedProps = hashKeys.keySet().toArray(new String[hashKeys.keySet().size()]); hashCoercionDesc = CoercionUtil.getCoercionTypesHash(viewableEventType, indexedProps, hashKeyList); rangeCoercionDesc = new CoercionDesc(false, null); if (hashKeys.size() == 1) { if (!hashCoercionDesc.isCoerce()) { eventTableFactory = statementContext.getEventTableIndexService().createSingle(0, viewableEventType, indexedProps[0], unique, null, null, false); } else { eventTableFactory = statementContext.getEventTableIndexService().createSingleCoerceAdd(0, viewableEventType, indexedProps[0], hashCoercionDesc.getCoercionTypes()[0], null, false); } } else { if (!hashCoercionDesc.isCoerce()) { eventTableFactory = statementContext.getEventTableIndexService().createMultiKey(0, viewableEventType, indexedProps, unique, null, null, false); } else { eventTableFactory = statementContext.getEventTableIndexService().createMultiKeyCoerceAdd(0, viewableEventType, indexedProps, hashCoercionDesc.getCoercionTypes(), false); } } } else if (hashKeys.isEmpty() && rangeKeys.isEmpty()) { hashCoercionDesc = new CoercionDesc(false, null); rangeCoercionDesc = new CoercionDesc(false, null); if (joinPropDesc.getInKeywordSingleIndex() != null) { eventTableFactory = statementContext.getEventTableIndexService().createSingle(0, viewableEventType, joinPropDesc.getInKeywordSingleIndex().getIndexedProp(), unique, null, null, false); inKeywordSingleIdxKeys = joinPropDesc.getInKeywordSingleIndex().getExpressions(); } else if (joinPropDesc.getInKeywordMultiIndex() != null) { eventTableFactory = statementContext.getEventTableIndexService().createInArray(0, viewableEventType, joinPropDesc.getInKeywordMultiIndex().getIndexedProp(), unique); inKeywordMultiIdxKey = joinPropDesc.getInKeywordMultiIndex().getExpression(); } else { eventTableFactory = statementContext.getEventTableIndexService().createUnindexed(0, null, false); } } else if (hashKeys.isEmpty() && rangeKeys.size() == 1) { String indexedProp = rangeKeys.keySet().iterator().next(); CoercionDesc coercionRangeTypes = CoercionUtil.getCoercionTypesRange(viewableEventType, rangeKeys, outerEventTypes); if (!coercionRangeTypes.isCoerce()) { eventTableFactory = statementContext.getEventTableIndexService().createSorted(0, viewableEventType, indexedProp, false); } else { eventTableFactory = statementContext.getEventTableIndexService().createSortedCoerce(0, viewableEventType, indexedProp, coercionRangeTypes.getCoercionTypes()[0], false); } hashCoercionDesc = new CoercionDesc(false, null); rangeCoercionDesc = coercionRangeTypes; } else { String[] indexedKeyProps = hashKeys.keySet().toArray(new String[hashKeys.keySet().size()]); Class[] coercionKeyTypes = SubordPropUtil.getCoercionTypes(hashKeys.values()); String[] indexedRangeProps = rangeKeys.keySet().toArray(new String[rangeKeys.keySet().size()]); CoercionDesc coercionRangeTypes = CoercionUtil.getCoercionTypesRange(viewableEventType, rangeKeys, outerEventTypes); eventTableFactory = statementContext.getEventTableIndexService().createComposite(0, viewableEventType, indexedKeyProps, coercionKeyTypes, indexedRangeProps, coercionRangeTypes.getCoercionTypes(), false); hashCoercionDesc = CoercionUtil.getCoercionTypesHash(viewableEventType, indexedKeyProps, hashKeyList); rangeCoercionDesc = coercionRangeTypes; } SubordTableLookupStrategyFactory subqTableLookupStrategyFactory = SubordinateTableLookupStrategyUtil.getLookupStrategy(outerEventTypes, hashKeyList, hashCoercionDesc, rangeKeyList, rangeCoercionDesc, inKeywordSingleIdxKeys, inKeywordMultiIdxKey, false); return new Pair<EventTableFactory, SubordTableLookupStrategyFactory>(eventTableFactory, subqTableLookupStrategyFactory); } private static StreamTypeService getDeclaredExprTypeService(ExprDeclaredNode[] declaredExpressions, Map<ExprDeclaredNode, List<ExprDeclaredNode>> declaredExpressionCallHierarchy, String[] outerStreamNames, EventType[] outerEventTypesSelect, String engineURI, ExprSubselectNode subselect, String subexpressionStreamName, EventType eventType) throws ExprValidationException { // Find that subselect within that any of the expression declarations for (ExprDeclaredNode declaration : declaredExpressions) { ExprNodeSubselectDeclaredNoTraverseVisitor visitor = new ExprNodeSubselectDeclaredNoTraverseVisitor(declaration); visitor.reset(); declaration.accept(visitor); if (!visitor.getSubselects().contains(subselect)) { continue; } // no type service for "alias" if (declaration.getPrototype().isAlias()) { return null; } // subselect found - compute outer stream names // initialize from the outermost provided stream names Map<String, Integer> outerStreamNamesMap = new LinkedHashMap<String, Integer>(); int count = 0; for (String outerStreamName : outerStreamNames) { outerStreamNamesMap.put(outerStreamName, count++); } // give each declared expression a chance to change the names (unless alias expression) Map<String, Integer> outerStreamNamesForSubselect = outerStreamNamesMap; List<ExprDeclaredNode> callers = declaredExpressionCallHierarchy.get(declaration); for (ExprDeclaredNode caller : callers) { outerStreamNamesForSubselect = caller.getOuterStreamNames(outerStreamNamesForSubselect); } outerStreamNamesForSubselect = declaration.getOuterStreamNames(outerStreamNamesForSubselect); // compile a new StreamTypeService for use in validating that particular subselect EventType[] eventTypes = new EventType[outerStreamNamesForSubselect.size() + 1]; String[] streamNames = new String[outerStreamNamesForSubselect.size() + 1]; eventTypes[0] = eventType; streamNames[0] = subexpressionStreamName; count = 0; for (Map.Entry<String, Integer> entry : outerStreamNamesForSubselect.entrySet()) { eventTypes[count + 1] = outerEventTypesSelect[entry.getValue()]; streamNames[count + 1] = entry.getKey(); count++; } StreamTypeServiceImpl availableTypes = new StreamTypeServiceImpl(eventTypes, streamNames, new boolean[eventTypes.length], engineURI, false); availableTypes.setRequireStreamNames(true); return availableTypes; } return null; } private static SubSelectStrategyFactoryDesc planSubSelectInternal(int subqueryNum, ExprSubselectNode subselect, SubSelectActivationHolder subSelectActivation, EPServicesContext services, StatementContext statementContext, boolean queryPlanLogging, SubSelectActivationCollection subSelectStreamDesc, String[] outerStreamNames, EventType[] outerEventTypesSelect, String[] outerEventTypeNamees, ExprDeclaredNode[] declaredExpressions, ContextPropertyRegistry contextPropertyRegistry, Map<ExprDeclaredNode, List<ExprDeclaredNode>> declaredExpressionCallHierarchy) throws ExprValidationException { if (queryPlanLogging && QUERY_PLAN_LOG.isInfoEnabled()) { QUERY_PLAN_LOG.info("For statement '" + statementContext.getStatementName() + "' subquery " + subqueryNum); } Annotation[] annotations = statementContext.getAnnotations(); IndexHint indexHint = IndexHint.getIndexHint(statementContext.getAnnotations()); StatementSpecCompiled statementSpec = subselect.getStatementSpecCompiled(); StreamSpecCompiled filterStreamSpec = statementSpec.getStreamSpecs()[0]; String subselecteventTypeName = null; if (filterStreamSpec instanceof FilterStreamSpecCompiled) { subselecteventTypeName = ((FilterStreamSpecCompiled) filterStreamSpec).getFilterSpec().getFilterForEventTypeName(); } else if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec) { subselecteventTypeName = ((NamedWindowConsumerStreamSpec) filterStreamSpec).getWindowName(); } else if (filterStreamSpec instanceof TableQueryStreamSpec) { subselecteventTypeName = ((TableQueryStreamSpec) filterStreamSpec).getTableName(); } ViewFactoryChain viewFactoryChain = subSelectStreamDesc.getViewFactoryChain(subselect); EventType eventType = viewFactoryChain.getEventType(); // determine a stream name unless one was supplied String subexpressionStreamName = filterStreamSpec.getOptionalStreamName(); int subselectStreamNumber = subSelectStreamDesc.getStreamNumber(subselect); if (subexpressionStreamName == null) { subexpressionStreamName = "$subselect_" + subselectStreamNumber; } String[] allStreamNames = new String[outerStreamNames.length + 1]; System.arraycopy(outerStreamNames, 0, allStreamNames, 1, outerStreamNames.length); allStreamNames[0] = subexpressionStreamName; // Named windows don't allow data views if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec || filterStreamSpec instanceof TableQueryStreamSpec) { EPStatementStartMethodHelperValidate.validateNoDataWindowOnNamedWindow(viewFactoryChain.getViewFactoryChain()); } // Expression declarations are copies of a predefined expression body with their own stream context. // Should only be invoked if the subselect belongs to that instance. StreamTypeService subselectTypeService = null; EventType[] outerEventTypes = null; // determine subselect type information from the enclosing declared expression, if possibly enclosed if (declaredExpressions.length > 0) { subselectTypeService = getDeclaredExprTypeService(declaredExpressions, declaredExpressionCallHierarchy, outerStreamNames, outerEventTypesSelect, services.getEngineURI(), subselect, subexpressionStreamName, eventType); if (subselectTypeService != null) { outerEventTypes = new EventType[subselectTypeService.getEventTypes().length - 1]; System.arraycopy(subselectTypeService.getEventTypes(), 1, outerEventTypes, 0, subselectTypeService.getEventTypes().length - 1); } } // Use the override provided by the subselect if present if (subselectTypeService == null) { if (subselect.getFilterSubqueryStreamTypes() != null) { subselectTypeService = subselect.getFilterSubqueryStreamTypes(); outerEventTypes = new EventType[subselectTypeService.getEventTypes().length - 1]; System.arraycopy(subselectTypeService.getEventTypes(), 1, outerEventTypes, 0, subselectTypeService.getEventTypes().length - 1); } else { // Streams event types are the original stream types with the stream zero the subselect stream LinkedHashMap<String, Pair<EventType, String>> namesAndTypes = new LinkedHashMap<String, Pair<EventType, String>>(); namesAndTypes.put(subexpressionStreamName, new Pair<EventType, String>(eventType, subselecteventTypeName)); for (int i = 0; i < outerEventTypesSelect.length; i++) { Pair<EventType, String> pair = new Pair<EventType, String>(outerEventTypesSelect[i], outerEventTypeNamees[i]); namesAndTypes.put(outerStreamNames[i], pair); } subselectTypeService = new StreamTypeServiceImpl(namesAndTypes, services.getEngineURI(), true, true); outerEventTypes = outerEventTypesSelect; } } // Validate select expression ViewResourceDelegateUnverified viewResourceDelegateSubselect = new ViewResourceDelegateUnverified(); SelectClauseSpecCompiled selectClauseSpec = subselect.getStatementSpecCompiled().getSelectClauseSpec(); AggregationServiceFactoryDesc aggregationServiceFactoryDesc = null; List<ExprNode> selectExpressions = new ArrayList<ExprNode>(); List<String> assignedNames = new ArrayList<String>(); boolean isWildcard = false; boolean isStreamWildcard = false; ExprEvaluator[] groupByEvaluators = null; boolean hasNonAggregatedProperties; ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); ExprValidationContext validationContext = new ExprValidationContext(subselectTypeService, statementContext.getEngineImportService(), statementContext.getStatementExtensionServicesContext(), viewResourceDelegateSubselect, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext.getTableService(), evaluatorContextStmt, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false, true, false, null, false); List<ExprAggregateNode> aggExprNodesSelect = new ArrayList<>(2); for (int i = 0; i < selectClauseSpec.getSelectExprList().length; i++) { SelectClauseElementCompiled element = selectClauseSpec.getSelectExprList()[i]; if (element instanceof SelectClauseExprCompiledSpec) { // validate SelectClauseExprCompiledSpec compiled = (SelectClauseExprCompiledSpec) element; ExprNode selectExpression = compiled.getSelectExpression(); selectExpression = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.SELECT, selectExpression, validationContext); selectExpressions.add(selectExpression); if (compiled.getAssignedName() == null) { assignedNames.add(ExprNodeUtility.toExpressionStringMinPrecedenceSafe(selectExpression)); } else { assignedNames.add(compiled.getAssignedName()); } // handle aggregation ExprAggregateNodeUtil.getAggregatesBottomUp(selectExpression, aggExprNodesSelect); // This stream (stream 0) properties must either all be under aggregation, or all not be. if (aggExprNodesSelect.size() > 0) { List<Pair<Integer, String>> propertiesNotAggregated = ExprNodeUtility.getExpressionProperties(selectExpression, false); for (Pair<Integer, String> pair : propertiesNotAggregated) { if (pair.getFirst() == 0) { throw new ExprValidationException("Subselect properties must all be within aggregation functions"); } } } } else if (element instanceof SelectClauseElementWildcard) { isWildcard = true; } else if (element instanceof SelectClauseStreamCompiledSpec) { isStreamWildcard = true; } } // end of for loop // validate having-clause and collect aggregations List<ExprAggregateNode> aggExpressionNodesHaving = Collections.emptyList(); if (statementSpec.getHavingExprRootNode() != null) { ExprNode validatedHavingClause = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.HAVING, statementSpec.getHavingExprRootNode(), validationContext); if (JavaClassHelper.getBoxedType(validatedHavingClause.getExprEvaluator().getType()) != Boolean.class) { throw new ExprValidationException("Subselect having-clause expression must return a boolean value"); } aggExpressionNodesHaving = new ArrayList<>(); ExprAggregateNodeUtil.getAggregatesBottomUp(validatedHavingClause, aggExpressionNodesHaving); validateAggregationPropsAndLocalGroup(aggExpressionNodesHaving); // if the having-clause does not have aggregations, it becomes part of the filter if (aggExpressionNodesHaving.isEmpty()) { ExprNode filter = statementSpec.getFilterRootNode(); if (filter == null) { statementSpec.setFilterExprRootNode(statementSpec.getHavingExprRootNode()); } else { statementSpec.setFilterExprRootNode(ExprNodeUtility.connectExpressionsByLogicalAnd(Arrays.asList(statementSpec.getFilterRootNode(), statementSpec.getHavingExprRootNode()))); } statementSpec.setHavingExprRootNode(null); } else { subselect.setHavingExpr(validatedHavingClause.getExprEvaluator()); ExprNodePropOrStreamSet nonAggregatedPropsHaving = ExprNodeUtility.getNonAggregatedProps(validationContext.getStreamTypeService().getEventTypes(), Collections.singletonList(validatedHavingClause), contextPropertyRegistry); for (ExprNodePropOrStreamPropDesc prop : nonAggregatedPropsHaving.getProperties()) { if (prop.getStreamNum() == 0) { throw new ExprValidationException("Subselect having-clause requires that all properties are under aggregation, consider using the 'first' aggregation function instead"); } } } } // Figure out all non-aggregated event properties in the select clause (props not under a sum/avg/max aggregation node) ExprNodePropOrStreamSet nonAggregatedPropsSelect = ExprNodeUtility.getNonAggregatedProps(validationContext.getStreamTypeService().getEventTypes(), selectExpressions, contextPropertyRegistry); hasNonAggregatedProperties = !nonAggregatedPropsSelect.isEmpty(); // Validate and set select-clause names and expressions if (!selectExpressions.isEmpty()) { if (isWildcard || isStreamWildcard) { throw new ExprValidationException("Subquery multi-column select does not allow wildcard or stream wildcard when selecting multiple columns."); } if (selectExpressions.size() > 1 && !subselect.isAllowMultiColumnSelect()) { throw new ExprValidationException("Subquery multi-column select is not allowed in this context."); } if (statementSpec.getGroupByExpressions() == null && selectExpressions.size() > 1 && aggExprNodesSelect.size() > 0 && hasNonAggregatedProperties) { throw new ExprValidationException("Subquery with multi-column select requires that either all or none of the selected columns are under aggregation, unless a group-by clause is also specified"); } subselect.setSelectClause(selectExpressions.toArray(new ExprNode[selectExpressions.size()])); subselect.setSelectAsNames(assignedNames.toArray(new String[assignedNames.size()])); } // Handle aggregation ExprNodePropOrStreamSet propertiesGroupBy = null; if (aggExprNodesSelect.size() > 0 || aggExpressionNodesHaving.size() > 0) { if (statementSpec.getGroupByExpressions() != null && statementSpec.getGroupByExpressions().getGroupByRollupLevels() != null) { throw new ExprValidationException("Group-by expressions in a subselect may not have rollups"); } ExprNode[] theGroupBy = statementSpec.getGroupByExpressions() == null ? null : statementSpec.getGroupByExpressions().getGroupByNodes(); boolean hasGroupBy = theGroupBy != null && theGroupBy.length > 0; if (hasGroupBy) { ExprNode[] groupByNodes = statementSpec.getGroupByExpressions().getGroupByNodes(); groupByEvaluators = new ExprEvaluator[groupByNodes.length]; // validate group-by for (int i = 0; i < groupByNodes.length; i++) { groupByNodes[i] = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.GROUPBY, groupByNodes[i], validationContext); groupByEvaluators[i] = groupByNodes[i].getExprEvaluator(); String minimal = ExprNodeUtility.isMinimalExpression(groupByNodes[i]); if (minimal != null) { throw new ExprValidationException("Group-by expressions in a subselect may not have " + minimal); } } // Get a list of event properties being aggregated in the select clause, if any propertiesGroupBy = ExprNodeUtility.getGroupByPropertiesValidateHasOne(groupByNodes); // Validated all group-by properties come from stream itself ExprNodePropOrStreamDesc firstNonZeroGroupBy = propertiesGroupBy.getFirstWithStreamNumNotZero(); if (firstNonZeroGroupBy != null) { throw new ExprValidationException("Subselect with group-by requires that group-by properties are provided by the subselect stream only (" + firstNonZeroGroupBy.getTextual() + " is not)"); } // Validate that this is a grouped full-aggregated case String reasonMessage = propertiesGroupBy.notContainsAll(nonAggregatedPropsSelect); boolean allInGroupBy = reasonMessage == null; if (!allInGroupBy) { throw new ExprValidationException("Subselect with group-by requires non-aggregated properties in the select-clause to also appear in the group-by clause"); } } // Other stream properties, if there is aggregation, cannot be under aggregation. validateAggregationPropsAndLocalGroup(aggExprNodesSelect); // determine whether select-clause has grouped-by expressions List<ExprAggregateNodeGroupKey> groupKeyExpressions = null; ExprNode[] groupByExpressions = new ExprNode[0]; if (hasGroupBy) { groupByExpressions = statementSpec.getGroupByExpressions().getGroupByNodes(); for (int i = 0; i < selectExpressions.size(); i++) { ExprNode selectExpression = selectExpressions.get(i); boolean revalidate = false; for (int j = 0; j < groupByExpressions.length; j++) { List<Pair<ExprNode, ExprNode>> foundPairs = ExprNodeUtility.findExpression(selectExpression, groupByExpressions[j]); for (Pair<ExprNode, ExprNode> pair : foundPairs) { ExprAggregateNodeGroupKey replacement = new ExprAggregateNodeGroupKey(j, groupByEvaluators[j].getType()); if (pair.getFirst() == null) { selectExpressions.set(i, replacement); } else { ExprNodeUtility.replaceChildNode(pair.getFirst(), pair.getSecond(), replacement); revalidate = true; } if (groupKeyExpressions == null) { groupKeyExpressions = new ArrayList<ExprAggregateNodeGroupKey>(); } groupKeyExpressions.add(replacement); } } // if the select-clause expression changed, revalidate it if (revalidate) { selectExpression = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.SELECT, selectExpression, validationContext); selectExpressions.set(i, selectExpression); } } // end of for loop } aggregationServiceFactoryDesc = AggregationServiceFactoryFactory.getService(aggExprNodesSelect, Collections.<ExprNode, String>emptyMap(), Collections.<ExprDeclaredNode>emptyList(), groupByExpressions, aggExpressionNodesHaving, Collections.<ExprAggregateNode>emptyList(), groupKeyExpressions, hasGroupBy, annotations, statementContext.getVariableService(), false, true, statementSpec.getFilterRootNode(), statementSpec.getHavingExprRootNode(), statementContext.getAggregationServiceFactoryService(), subselectTypeService.getEventTypes(), null, statementSpec.getOptionalContextName(), null, null, false, false, false, statementContext.getEngineImportService()); // assign select-clause if (!selectExpressions.isEmpty()) { subselect.setSelectClause(selectExpressions.toArray(new ExprNode[selectExpressions.size()])); subselect.setSelectAsNames(assignedNames.toArray(new String[assignedNames.size()])); } } // no aggregation functions allowed in filter if (statementSpec.getFilterRootNode() != null) { List<ExprAggregateNode> aggExprNodesFilter = new LinkedList<ExprAggregateNode>(); ExprAggregateNodeUtil.getAggregatesBottomUp(statementSpec.getFilterRootNode(), aggExprNodesFilter); if (aggExprNodesFilter.size() > 0) { throw new ExprValidationException("Aggregation functions are not supported within subquery filters, consider using a having-clause or insert-into instead"); } } // validate filter expression, if there is one ExprNode filterExpr = statementSpec.getFilterRootNode(); // add the table filter for tables if (filterStreamSpec instanceof TableQueryStreamSpec) { TableQueryStreamSpec table = (TableQueryStreamSpec) filterStreamSpec; filterExpr = ExprNodeUtility.connectExpressionsByLogicalAnd(table.getFilterExpressions(), filterExpr); } // determine correlated boolean correlatedSubquery = false; if (filterExpr != null) { filterExpr = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.FILTER, filterExpr, validationContext); if (JavaClassHelper.getBoxedType(filterExpr.getExprEvaluator().getType()) != Boolean.class) { throw new ExprValidationException("Subselect filter expression must return a boolean value"); } // check the presence of a correlated filter, not allowed with aggregation ExprNodeIdentifierVisitor visitor = new ExprNodeIdentifierVisitor(true); filterExpr.accept(visitor); List<Pair<Integer, String>> propertiesNodes = visitor.getExprProperties(); for (Pair<Integer, String> pair : propertiesNodes) { if (pair.getFirst() != 0) { correlatedSubquery = true; break; } } } ViewResourceDelegateVerified viewResourceDelegateVerified = EPStatementStartMethodHelperViewResources.verifyPreviousAndPriorRequirements(new ViewFactoryChain[]{viewFactoryChain}, viewResourceDelegateSubselect); List<ExprPriorNode> priorNodes = viewResourceDelegateVerified.getPerStream()[0].getPriorRequestsAsList(); List<ExprPreviousNode> previousNodes = viewResourceDelegateVerified.getPerStream()[0].getPreviousRequests(); // Set the aggregated flag // This must occur here as some analysis of return type depends on aggregated or not. if (aggregationServiceFactoryDesc == null) { subselect.setSubselectAggregationType(ExprSubselectNode.SubqueryAggregationType.NONE); } else { subselect.setSubselectAggregationType(hasNonAggregatedProperties ? ExprSubselectNode.SubqueryAggregationType.FULLY_AGGREGATED_WPROPS : ExprSubselectNode.SubqueryAggregationType.FULLY_AGGREGATED_NOPROPS); } // Set the filter. ExprEvaluator filterExprEval = (filterExpr == null) ? null : filterExpr.getExprEvaluator(); ExprEvaluator assignedFilterExpr = aggregationServiceFactoryDesc != null ? null : filterExprEval; subselect.setFilterExpr(assignedFilterExpr); // validation for correlated subqueries against named windows contained-event syntax if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec && correlatedSubquery) { NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec; if (namedSpec.getOptPropertyEvaluator() != null) { throw new ExprValidationException("Failed to validate named window use in subquery, contained-event is only allowed for named windows when not correlated"); } } // Validate presence of a data window validateSubqueryDataWindow(subselect, correlatedSubquery, hasNonAggregatedProperties, propertiesGroupBy, nonAggregatedPropsSelect); // Determine strategy factories // // handle named window index share first if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec) { NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec; if (namedSpec.getFilterExpressions().isEmpty()) { NamedWindowProcessor processor = services.getNamedWindowMgmtService().getProcessor(namedSpec.getWindowName()); if (processor == null) { throw new ExprValidationException("A named window by name '" + namedSpec.getWindowName() + "' does not exist"); } boolean disableIndexShare = HintEnum.DISABLE_WINDOW_SUBQUERY_INDEXSHARE.getHint(annotations) != null; if (disableIndexShare && processor.isVirtualDataWindow()) { disableIndexShare = false; } if (!disableIndexShare && processor.isEnableSubqueryIndexShare()) { validateContextAssociation(statementContext, processor.getContextName(), "named window '" + processor.getNamedWindowName() + "'"); if (queryPlanLogging && QUERY_PLAN_LOG.isInfoEnabled()) { QUERY_PLAN_LOG.info("prefering shared index"); } boolean fullTableScan = HintEnum.SET_NOINDEX.getHint(annotations) != null; ExcludePlanHint excludePlanHint = ExcludePlanHint.getHint(allStreamNames, statementContext); SubordPropPlan joinedPropPlan = QueryPlanIndexBuilder.getJoinProps(filterExpr, outerEventTypes.length, subselectTypeService.getEventTypes(), excludePlanHint); SubSelectStrategyFactory factory = new SubSelectStrategyFactoryIndexShare(statementContext.getStatementName(), statementContext.getStatementId(), subqueryNum, outerEventTypesSelect, processor, null, fullTableScan, indexHint, joinedPropPlan, filterExprEval, aggregationServiceFactoryDesc, groupByEvaluators, services.getTableService(), statementContext.getAnnotations(), statementContext.getStatementStopService(), statementContext.getEngineImportService()); return new SubSelectStrategyFactoryDesc(subSelectActivation, factory, aggregationServiceFactoryDesc, priorNodes, previousNodes, subqueryNum); } } } // handle table-subselect if (filterStreamSpec instanceof TableQueryStreamSpec) { TableQueryStreamSpec tableSpec = (TableQueryStreamSpec) filterStreamSpec; TableMetadata metadata = services.getTableService().getTableMetadata(tableSpec.getTableName()); if (metadata == null) { throw new ExprValidationException("A table by name '" + tableSpec.getTableName() + "' does not exist"); } validateContextAssociation(statementContext, metadata.getContextName(), "table '" + tableSpec.getTableName() + "'"); boolean fullTableScan = HintEnum.SET_NOINDEX.getHint(annotations) != null; ExcludePlanHint excludePlanHint = ExcludePlanHint.getHint(allStreamNames, statementContext); SubordPropPlan joinedPropPlan = QueryPlanIndexBuilder.getJoinProps(filterExpr, outerEventTypes.length, subselectTypeService.getEventTypes(), excludePlanHint); SubSelectStrategyFactory factory = new SubSelectStrategyFactoryIndexShare(statementContext.getStatementName(), statementContext.getStatementId(), subqueryNum, outerEventTypesSelect, null, metadata, fullTableScan, indexHint, joinedPropPlan, filterExprEval, aggregationServiceFactoryDesc, groupByEvaluators, services.getTableService(), statementContext.getAnnotations(), statementContext.getStatementStopService(), statementContext.getEngineImportService()); return new SubSelectStrategyFactoryDesc(subSelectActivation, factory, aggregationServiceFactoryDesc, priorNodes, previousNodes, subqueryNum); } // determine unique keys, if any Set<String> optionalUniqueProps = null; if (viewFactoryChain.getDataWindowViewFactoryCount() > 0) { optionalUniqueProps = ViewServiceHelper.getUniqueCandidateProperties(viewFactoryChain.getViewFactoryChain(), annotations); } if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec) { NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec; NamedWindowProcessor processor = services.getNamedWindowMgmtService().getProcessor(namedSpec.getWindowName()); optionalUniqueProps = processor.getOptionalUniqueKeyProps(); } // handle local stream + named-window-stream boolean fullTableScan = HintEnum.SET_NOINDEX.getHint(annotations) != null; Pair<EventTableFactory, SubordTableLookupStrategyFactory> indexPair = determineSubqueryIndexFactory(filterExpr, eventType, outerEventTypes, subselectTypeService, fullTableScan, queryPlanLogging, optionalUniqueProps, statementContext, subqueryNum); SubSelectStrategyFactory factory = new SubSelectStrategyFactoryLocalViewPreloaded(subqueryNum, subSelectActivation, indexPair, filterExpr, filterExprEval, correlatedSubquery, aggregationServiceFactoryDesc, viewResourceDelegateVerified, groupByEvaluators); return new SubSelectStrategyFactoryDesc(subSelectActivation, factory, aggregationServiceFactoryDesc, priorNodes, previousNodes, subqueryNum); } public static String getSubqueryInfoText(int subqueryNum, ExprSubselectNode subselect) { String text = "subquery number " + (subqueryNum + 1); StreamSpecRaw streamRaw = subselect.getStatementSpecRaw().getStreamSpecs().get(0); if (streamRaw instanceof FilterStreamSpecRaw) { text += " querying " + ((FilterStreamSpecRaw) streamRaw).getRawFilterSpec().getEventTypeName(); } return text; } private static String validateContextAssociation(StatementContext statementContext, String entityDeclaredContextName, String entityDesc) throws ExprValidationException { String optionalProvidedContextName = statementContext.getContextDescriptor() == null ? null : statementContext.getContextDescriptor().getContextName(); if (entityDeclaredContextName != null) { if (optionalProvidedContextName == null || !optionalProvidedContextName.equals(entityDeclaredContextName)) { throw new ExprValidationException("Mismatch in context specification, the context for the " + entityDesc + " is '" + entityDeclaredContextName + "' and the query specifies " + (optionalProvidedContextName == null ? "no context " : "context '" + optionalProvidedContextName + "'")); } } return null; } private static void validateSubqueryDataWindow(ExprSubselectNode subselectNode, boolean correlatedSubquery, boolean hasNonAggregatedProperties, ExprNodePropOrStreamSet propertiesGroupBy, ExprNodePropOrStreamSet nonAggregatedPropsSelect) throws ExprValidationException { // validation applies only to type+filter subqueries that have no data window StreamSpecCompiled streamSpec = subselectNode.getStatementSpecCompiled().getStreamSpecs()[0]; if (!(streamSpec instanceof FilterStreamSpecCompiled) || streamSpec.getViewSpecs().length > 0) { return; } if (correlatedSubquery) { throw new ExprValidationException(MSG_SUBQUERY_REQUIRES_WINDOW); } // we have non-aggregated properties if (hasNonAggregatedProperties) { if (propertiesGroupBy == null) { throw new ExprValidationException(MSG_SUBQUERY_REQUIRES_WINDOW); } String reason = nonAggregatedPropsSelect.notContainsAll(propertiesGroupBy); if (reason != null) { throw new ExprValidationException(MSG_SUBQUERY_REQUIRES_WINDOW); } } } private static void validateAggregationPropsAndLocalGroup(List<ExprAggregateNode> aggregateNodes) throws ExprValidationException { for (ExprAggregateNode aggNode : aggregateNodes) { List<Pair<Integer, String>> propertiesNodesAggregated = ExprNodeUtility.getExpressionProperties(aggNode, true); for (Pair<Integer, String> pair : propertiesNodesAggregated) { if (pair.getFirst() != 0) { throw new ExprValidationException("Subselect aggregation functions cannot aggregate across correlated properties"); } } if (aggNode.getOptionalLocalGroupBy() != null) { throw new ExprValidationException("Subselect aggregations functions cannot specify a group-by"); } } } }