else if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec)
{
subselecteventTypeName = ((NamedWindowConsumerStreamSpec) filterStreamSpec).getWindowName();
}
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)
{
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 = false;
if (selectClauseSpec.getSelectExprList().length > 0)
{
List<ExprAggregateNode> aggExprNodes = new LinkedList<ExprAggregateNode>();
ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext);
ExprValidationContext validationContext = new ExprValidationContext(subselectTypeService, statementContext.getMethodResolutionService(), viewResourceDelegateSubselect, statementContext.getSchedulingService(), statementContext.getVariableService(), evaluatorContextStmt, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false);
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.toExpressionStringMinPrecedence(selectExpression));
}
else {
assignedNames.add(compiled.getAssignedName());
}
// handle aggregation
ExprAggregateNodeUtil.getAggregatesBottomUp(selectExpression, aggExprNodes);
if (aggExprNodes.size() > 0)
{
// This stream (stream 0) properties must either all be under aggregation, or all not be.
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
// Figure out all non-aggregated event properties in the select clause (props not under a sum/avg/max aggregation node)
Set<Pair<Integer, String>> 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 &&
aggExprNodes.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
if (aggExprNodes.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
Set<Pair<Integer, String>> propertiesGroupBy = ExprNodeUtility.getGroupByPropertiesValidateHasOne(groupByNodes);
// Validated all group-by properties come from stream itself
for (Pair<Integer, String> pair : propertiesGroupBy) {
if (pair.getFirst() == null || pair.getFirst() != 0) {
throw new ExprValidationException("Subselect with group-by requires that group-by properties are provided by the subselect stream only ('" + pair.getSecond() + "' is not)");
}
}
// Validate that this is a grouped full-aggregated case
boolean allInGroupBy = true;
for (Pair<Integer, String> nonAggregatedProp : nonAggregatedPropsSelect) {
if (!propertiesGroupBy.contains(nonAggregatedProp)) {
allInGroupBy = false;
}
}
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.
for (ExprAggregateNode aggNode : aggExprNodes) {
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");
}
}
}
// 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
}
List<ExprAggregateNode> havingAgg = Collections.emptyList();
List<ExprAggregateNode> orderByAgg = Collections.emptyList();
aggregationServiceFactoryDesc = AggregationServiceFactoryFactory.getService(aggExprNodes, groupByExpressions, havingAgg, orderByAgg, groupKeyExpressions, hasGroupBy, evaluatorContextStmt, annotations, statementContext.getVariableService(), false, true, statementSpec.getFilterRootNode(), statementSpec.getHavingExprRootNode(), statementContext.getAggregationServiceFactoryService(), subselectTypeService.getEventTypes(), statementContext.getMethodResolutionService(), null, statementSpec.getOptionalContextName());
// 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 insert-into instead");
}
}
// Validate filter expression, if there is one
ExprNode filterExpr = statementSpec.getFilterRootNode();
boolean correlatedSubquery = false;
if (filterExpr != null)
{
ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext);
ExprValidationContext validationContext = new ExprValidationContext(subselectTypeService, statementContext.getMethodResolutionService(), viewResourceDelegateSubselect, statementContext.getSchedulingService(), statementContext.getVariableService(), evaluatorContextStmt, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false);
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.AGGREGATED : ExprSubselectNode.SubqueryAggregationType.FULLY_AGGREGATED);
}
// 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");
}
}
// Determine strategy factories
//
// handle named window index share first
if (filterStreamSpec instanceof NamedWindowConsumerStreamSpec) {
NamedWindowConsumerStreamSpec namedSpec = (NamedWindowConsumerStreamSpec) filterStreamSpec;
if (namedSpec.getFilterExpressions().isEmpty()) {
NamedWindowProcessor processor = services.getNamedWindowService().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()) {
String contextName = statementContext.getContextDescriptor() == null ? null :
statementContext.getContextDescriptor().getContextName();
String msg = processor.validateContextAssociation(contextName);
if (msg != null) {
throw new ExprValidationException(msg);
}
if (queryPlanLogging && queryPlanLog.isInfoEnabled()) {
queryPlanLog.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 SubSelectStrategyFactoryNamedWinIndexShare(subqueryNum, outerEventTypesSelect,
processor, fullTableScan, indexHint, joinedPropPlan, filterExprEval, aggregationServiceFactoryDesc, groupByEvaluators);
return new SubSelectStrategyFactoryDesc(subSelectActivation, factory, aggregationServiceFactoryDesc, priorNodes, previousNodes);
}
}
}
// 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.getNamedWindowService().getProcessor(namedSpec.getWindowName());
optionalUniqueProps = processor.getOptionalUniqueKeyProps();