/************************************************************************************** * Copyright (C) 2008 EsperTech, Inc. All rights reserved. * * http://esper.codehaus.org * * 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; import com.espertech.esper.client.EventBean; import com.espertech.esper.client.EventType; import com.espertech.esper.epl.spec.StatementSpecRaw; import com.espertech.esper.event.EventAdapterService; import com.espertech.esper.util.JavaClassHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.*; /** * Represents a subselect in an expression tree. */ public class ExprSubselectRowNode extends ExprSubselectNode { private static final Log log = LogFactory.getLog(ExprSubselectRowNode.class); private static final long serialVersionUID = -7865711714805807559L; private transient SubselectMultirowType subselectMultirowType; /** * 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 Map<String, Object> getEventType() throws ExprValidationException { if ((selectClause == null) || (selectClause.length < 2)) { return null; } return getRowType(); } public void validateSubquery(ExprValidationContext validationContext) throws ExprValidationException { } 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(); } if (filterExpr == null && selectClause == null) { return matchingEvents; } // Evaluate filter if (filterExpr != null) { EventBean[] events = new EventBean[eventsPerStream.length + 1]; System.arraycopy(eventsPerStream, 0, events, 1, eventsPerStream.length); ArrayDeque<EventBean> filtered = new ArrayDeque<EventBean>(); for (EventBean subselectEvent : matchingEvents) { // Prepare filter expression event list events[0] = subselectEvent; Boolean pass = (Boolean) filterExpr.evaluate(events, true, context); if ((pass != null) && (pass)) { filtered.add(subselectEvent); } } if (selectClause == null) { return filtered; } } return null; // should not get here, as there is no event type returned when there is a select-clause } public Collection evaluateGetCollScalar(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext context) { if (matchingEvents == null) { return null; } if (matchingEvents.size() == 0) { return Collections.emptyList(); } if (selectClauseEvaluator == null || selectClauseEvaluator.length < 1) { return null; } List result = new ArrayList(); EventBean[] events = new EventBean[eventsPerStream.length + 1]; System.arraycopy(eventsPerStream, 0, events, 1, eventsPerStream.length); if (filterExpr != null) { for (EventBean subselectEvent : matchingEvents) { // Prepare filter expression event list events[0] = subselectEvent; Boolean pass = (Boolean) filterExpr.evaluate(events, true, context); if ((pass != null) && (pass)) { result.add(selectClauseEvaluator[0].evaluate(events, isNewData, context)); } } } else { for (EventBean subselectEvent : matchingEvents) { // Prepare filter expression event list events[0] = subselectEvent; result.add(selectClauseEvaluator[0].evaluate(events, isNewData, context)); } } return result; } public EventType getEventTypeSingle(EventAdapterService eventAdapterService, String statementId) throws ExprValidationException { if (!this.isAggregatedSubquery() || selectClause == null) { return null; } Map<String, Object> rowType = getRowType(); EventType resultEventType = eventAdapterService.createAnonymousMapType(statementId + "_subquery_" + this.getSubselectNumber(), rowType); subselectMultirowType = new SubselectMultirowType(resultEventType, eventAdapterService); return resultEventType; } public EventBean evaluateGetEventBean(EventBean[] eventsPerStream, boolean isNewData, ExprEvaluatorContext context) { Map<String, Object> row = evaluateRow(eventsPerStream, true, context); return subselectMultirowType.getEventAdapterService().adapterForTypedMap(row, subselectMultirowType.getEventType()); } public EventType getEventTypeCollection(EventAdapterService eventAdapterService) throws ExprValidationException { if (selectClause == null) // wildcards allowed { return rawEventType; } // only if selecting wildcard do we allow lambda functions return null; } public Class getComponentTypeCollection() throws ExprValidationException { if (selectClause == null) // wildcards allowed { return null; } if (selectClauseEvaluator.length > 1) { return null; } return selectClauseEvaluator[0].getType(); } public Object evaluate(EventBean[] eventsPerStream, boolean isNewData, Collection<EventBean> matchingEvents, ExprEvaluatorContext exprEvaluatorContext) { if (matchingEvents == null) { return null; } if (matchingEvents.size() == 0) { return null; } if ((filterExpr == null) && (matchingEvents.size() > 1)) { log.warn(getMultirowMessage()); return null; } // Evaluate filter EventBean subSelectResult = null; EventBean[] events = new EventBean[eventsPerStream.length + 1]; System.arraycopy(eventsPerStream, 0, events, 1, eventsPerStream.length); if (filterExpr != null) { for (EventBean subselectEvent : matchingEvents) { // Prepare filter expression event list events[0] = subselectEvent; Boolean pass = (Boolean) filterExpr.evaluate(events, true, exprEvaluatorContext); if ((pass != null) && (pass)) { if (subSelectResult != null) { log.warn(getMultirowMessage()); return null; } subSelectResult = subselectEvent; } } if (subSelectResult == null) { return null; } } else { subSelectResult = matchingEvents.iterator().next(); } events[0] = subSelectResult; Object result; if (selectClause != null) { if (selectClause.length == 1) { result = selectClauseEvaluator[0].evaluate(events, true, exprEvaluatorContext); } else { result = evaluateRow(events, true, exprEvaluatorContext); } } else { result = events[0].getUnderlying(); } return result; } @Override public boolean isAllowMultiColumnSelect() { return true; } private 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(this.selectAsNames[i], resultEntry); } return map; } private Map<String, Object> getRowType() throws ExprValidationException { Set<String> uniqueNames = new HashSet<String>(); Map<String, Object> type = new LinkedHashMap<String, Object>(); for (int i = 0; i < selectClause.length; i++) { String assignedName = this.selectAsNames[i]; if (assignedName == null) { assignedName = selectClause[i].toExpressionString(); } 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 Object getMultirowMessage() { return "Subselect of statement '" + statementName + "' returned more then one row in subselect " + subselectNumber + " '" + toExpressionString() + "', returning null result"; } private 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; } } }