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);
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(selectExpression, validationContext);
selectExpressions.add(selectExpression);
if (compiled.getAssignedName() == null) {
assignedNames.add(selectExpression.toExpressionString());
}
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().length == 0 && 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)
{
boolean hasGroupBy = statementSpec.getGroupByExpressions() != null && statementSpec.getGroupByExpressions().length > 0;
if (hasGroupBy) {
ExprNode[] groupByNodes = statementSpec.getGroupByExpressions();
groupByEvaluators = new ExprEvaluator[groupByNodes.length];
// validate group-by
for (int i = 0; i < groupByNodes.length; i++) {
groupByNodes[i] = ExprNodeUtility.getValidatedSubtree(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;
if (hasGroupBy) {
ExprNode[] groupByExpressions = statementSpec.getGroupByExpressions();
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(selectExpression, validationContext);
selectExpressions.set(i, selectExpression);
}
} // end of for loop
}
List<ExprAggregateNode> havingAgg = Collections.emptyList();
List<ExprAggregateNode> orderByAgg = Collections.emptyList();
aggregationServiceFactoryDesc = AggregationServiceFactoryFactory.getService(aggExprNodes, statementSpec.getGroupByExpressions(), havingAgg, orderByAgg, groupKeyExpressions, hasGroupBy, evaluatorContextStmt, annotations, statementContext.getVariableService(), false, true, statementSpec.getFilterRootNode(), statementSpec.getHavingExprRootNode(), statementContext.getAggregationServiceFactoryService(), subselectTypeService.getEventTypes(), statementContext.getMethodResolutionService());
// 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);
filterExpr = ExprNodeUtility.getValidatedSubtree(filterExpr, validationContext);
if (JavaClassHelper.getBoxedType(filterExpr.getExprEvaluator().getType()) != Boolean.class)
{
throw new ExprValidationException("Subselect filter expression must return a boolean value");