/* *************************************************************************************** * 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.epl.expression.subquery; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.client.FragmentEventType; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.spec.StatementSpecRaw; import com.espertech.esper.epl.table.mgmt.TableMetadata; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.util.JavaClassHelper; import java.util.*; /** * Represents a subselect in an expression tree. */ public class ExprSubselectRowNode extends ExprSubselectNode { private static final long serialVersionUID = -7865711714805807559L; public static final SubselectEvalStrategyRow UNFILTERED_SELECTED = new SubselectEvalStrategyRowUnfilteredSelected(); public static final SubselectEvalStrategyRow FILTERED_UNSELECTED = new SubselectEvalStrategyRowFilteredUnselected(); public static final SubselectEvalStrategyRow FILTERED_SELECTED = new SubselectEvalStrategyRowFilteredSelected(); public static final SubselectEvalStrategyRow HAVING_SELECTED = new SubselectEvalStrategyRowHavingSelected(); public static final SubselectEvalStrategyRow UNFILTERED_SELECTED_GROUPED = new SubselectEvalStrategyRowUnfilteredSelectedGroupedNoHaving(); protected transient SubselectMultirowType subselectMultirowType; private transient SubselectEvalStrategyRow evalStrategy; /** * Ctor. * * @param statementSpec is the lookup statement spec from the parser, unvalidated */ public ExprSubselectRowNode(StatementSpecRaw statementSpec) { super(statementSpec); } public Class getType() { if (selectClause == null) { // wildcards allowed return rawEventType.getUnderlyingType(); } if (selectClause.length == 1) { return JavaClassHelper.getBoxedType(selectClause[0].getExprEvaluator().getType()); } return null; } public void validateSubquery(ExprValidationContext validationContext) throws ExprValidationException { // Strategy for subselect depends on presence of filter + presence of select clause expressions // the filter expression is handled elsewhere if there is any aggregation if (filterExpr == null) { if (selectClause == null) { TableMetadata tableMetadata = validationContext.getTableService().getTableMetadataFromEventType(rawEventType); if (tableMetadata != null) { evalStrategy = new SubselectEvalStrategyRowUnfilteredUnselectedTable(tableMetadata); } else { evalStrategy = SubselectEvalStrategyRowUnfilteredUnselected.INSTANCE; } } else { if (getStatementSpecCompiled().getGroupByExpressions() != null && getStatementSpecCompiled().getGroupByExpressions().getGroupByNodes().length > 0) { if (havingExpr != null) { evalStrategy = new SubselectEvalStrategyRowUnfilteredSelectedGroupedWHaving(havingExpr); } else { evalStrategy = UNFILTERED_SELECTED_GROUPED; } } else { if (havingExpr != null) { evalStrategy = HAVING_SELECTED; } else { evalStrategy = UNFILTERED_SELECTED; } } } } else { if (selectClause == null) { TableMetadata tableMetadata = validationContext.getTableService().getTableMetadataFromEventType(rawEventType); if (tableMetadata != null) { evalStrategy = new SubselectEvalStrategyRowFilteredUnselectedTable(tableMetadata); } else { evalStrategy = FILTERED_UNSELECTED; } } else { evalStrategy = FILTERED_SELECTED; } } } public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { if (matchingEvents == null || matchingEvents.size() == 0) { return null; } return evalStrategy.evaluate(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); } public Collection<EventBean> evaluateGetCollEvents(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext context) { if (matchingEvents == null) { return null; } if (matchingEvents.size() == 0) { return Collections.emptyList(); } return evalStrategy.evaluateGetCollEvents(eventsPerStream, isNewData, matchingEvents, context, this); } public Collection evaluateGetCollScalar(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext context) { if (matchingEvents == null) { return null; } if (matchingEvents.size() == 0) { return Collections.emptyList(); } return evalStrategy.evaluateGetCollScalar(eventsPerStream, isNewData, matchingEvents, context, this); } public EventBean evaluateGetEventBean(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { if (matchingEvents == null || matchingEvents.size() == 0) { return null; } return evalStrategy.evaluateGetEventBean(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); } public Object[] evaluateTypableSingle(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { if (matchingEvents == null || matchingEvents.size() == 0) { return null; } return evalStrategy.typableEvaluate(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); } public Object[][] evaluateTypableMulti(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { if (matchingEvents == null) { return null; } if (matchingEvents.size() == 0) { return new Object[0][]; } return evalStrategy.typableEvaluateMultirow(eventsPerStream, isNewData, matchingEvents, exprEvaluatorContext, this); } public LinkedHashMap<String, Object> typableGetRowProperties() throws ExprValidationException { if ((selectClause == null) || (selectClause.length < 2)) { return null; } return getRowType(); } public EventType getEventTypeSingle(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException { if (selectClause == null) { return null; } if (this.getSubselectAggregationType() != SubqueryAggregationType.FULLY_AGGREGATED_NOPROPS) { return null; } return getAssignAnonymousType(eventAdapterService, statementId); } public EventType getEventTypeCollection(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException { if (selectClause == null) { // wildcards allowed return rawEventType; } // special case: selecting a single property that is itself an event if (selectClause.length == 1 && selectClause[0] instanceof ExprIdentNode) { ExprIdentNode identNode = (ExprIdentNode) selectClause[0]; FragmentEventType fragment = rawEventType.getFragmentType(identNode.getResolvedPropertyName()); if (fragment != null && !fragment.isIndexed()) { return fragment.getFragmentType(); } } // select of a single value otherwise results in a collection of scalar values if (selectClause.length == 1) { return null; } // fully-aggregated always returns zero or one row if (this.getSubselectAggregationType() == SubqueryAggregationType.FULLY_AGGREGATED_NOPROPS) { return null; } return getAssignAnonymousType(eventAdapterService, statementId); } private EventType getAssignAnonymousType(EventAdapterService eventAdapterService, int statementId) throws ExprValidationException { Map<String, Object> rowType = getRowType(); EventType resultEventType = eventAdapterService.createAnonymousMapType(statementId + "_subquery_" + this.getSubselectNumber(), rowType, true); subselectMultirowType = new SubselectMultirowType(resultEventType, eventAdapterService); return resultEventType; } public Class getComponentTypeCollection() throws ExprValidationException { if (selectClause == null) { // wildcards allowed return null; } if (selectClauseEvaluator.length > 1) { return null; } return selectClauseEvaluator[0].getType(); } public boolean isAllowMultiColumnSelect() { return true; } private LinkedHashMap<String, Object> getRowType() throws ExprValidationException { Set<String> uniqueNames = new HashSet<String>(); LinkedHashMap<String, Object> type = new LinkedHashMap<String, Object>(); for (int i = 0; i < selectClause.length; i++) { String assignedName = this.selectAsNames[i]; if (assignedName == null) { assignedName = ExprNodeUtility.toExpressionStringMinPrecedenceSafe(selectClause[i]); } if (uniqueNames.add(assignedName)) { type.put(assignedName, selectClause[i].getExprEvaluator().getType()); } else { throw new ExprValidationException("Column " + i + " in subquery does not have a unique column name assigned"); } } return type; } public String getMultirowMessage() { return "Subselect of statement '" + statementName + "' returned more then one row in subselect " + subselectNumber + " '" + ExprNodeUtility.toExpressionStringMinPrecedenceSafe(this) + "', returning null result"; } protected Map<String, Object> evaluateRow(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { Map<String, Object> map = new HashMap<String, Object>(); for (int i = 0; i < selectClauseEvaluator.length; i++) { Object resultEntry = selectClauseEvaluator[i].evaluate(eventsPerStream, isNewData, context); map.put(selectAsNames[i], resultEntry); } return map; } protected static class SubselectMultirowType { private final EventType eventType; private final EventAdapterService eventAdapterService; private SubselectMultirowType(EventType eventType, EventAdapterService eventAdapterService) { this.eventType = eventType; this.eventAdapterService = eventAdapterService; } public EventType getEventType() { return eventType; } public EventAdapterService getEventAdapterService() { return eventAdapterService; } } }