/* *************************************************************************************** * 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.service; import com.espertech.esper.client.*; import com.espertech.esper.client.annotation.Hint; import com.espertech.esper.client.annotation.HintEnum; import com.espertech.esper.client.annotation.Name; import com.espertech.esper.client.hook.ExceptionHandlerExceptionType; import com.espertech.esper.client.soda.EPStatementObjectModel; import com.espertech.esper.client.util.EventUnderlyingType; import com.espertech.esper.collection.NameParameterCountKey; import com.espertech.esper.collection.Pair; import com.espertech.esper.core.service.multimatch.MultiMatchHandler; import com.espertech.esper.core.start.*; import com.espertech.esper.epl.agg.rollup.GroupByExpressionHelper; import com.espertech.esper.epl.annotation.AnnotationUtil; import com.espertech.esper.epl.core.EngineImportService; import com.espertech.esper.epl.core.StreamTypeService; import com.espertech.esper.epl.core.StreamTypeServiceImpl; import com.espertech.esper.epl.declexpr.ExprDeclaredNode; import com.espertech.esper.epl.expression.core.*; import com.espertech.esper.epl.expression.dot.ExprDotNode; import com.espertech.esper.epl.expression.ops.ExprAndNode; import com.espertech.esper.epl.expression.ops.ExprAndNodeImpl; import com.espertech.esper.epl.expression.subquery.ExprSubselectNode; import com.espertech.esper.epl.expression.subquery.ExprSubselectRowNode; import com.espertech.esper.epl.expression.table.ExprTableAccessNode; import com.espertech.esper.epl.expression.visitor.*; import com.espertech.esper.epl.named.NamedWindowMgmtService; import com.espertech.esper.epl.script.jsr223.JSR223Helper; import com.espertech.esper.epl.script.mvel.MVELHelper; import com.espertech.esper.epl.script.mvel.MVELInvoker; import com.espertech.esper.epl.spec.*; import com.espertech.esper.epl.spec.util.StatementSpecCompiledAnalyzer; import com.espertech.esper.epl.spec.util.StatementSpecCompiledAnalyzerResult; import com.espertech.esper.epl.spec.util.StatementSpecRawAnalyzer; import com.espertech.esper.event.*; import com.espertech.esper.event.arr.ObjectArrayEventType; import com.espertech.esper.event.avro.AvroSchemaEventType; import com.espertech.esper.event.bean.BeanEventType; import com.espertech.esper.event.map.MapEventType; import com.espertech.esper.filter.FilterNonPropertyRegisteryService; import com.espertech.esper.filter.FilterSpecCompiled; import com.espertech.esper.filter.FilterSpecParam; import com.espertech.esper.metrics.instrumentation.InstrumentationHelper; import com.espertech.esper.pattern.EvalFilterFactoryNode; import com.espertech.esper.pattern.EvalNodeAnalysisResult; import com.espertech.esper.pattern.EvalNodeUtil; import com.espertech.esper.util.CollectionUtil; import com.espertech.esper.util.EventRepresentationUtil; import com.espertech.esper.util.ManagedReadWriteLock; import com.espertech.esper.view.ViewProcessingException; import com.espertech.esper.view.Viewable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.annotation.Annotation; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; /** * Provides statement lifecycle services. */ public class StatementLifecycleSvcImpl implements StatementLifecycleSvc { private static Logger log = LoggerFactory.getLogger(StatementLifecycleSvcImpl.class); /** * Services context for statement lifecycle management. */ protected final EPServicesContext services; /** * Maps of statement id to descriptor. */ protected final Map<Integer, EPStatementDesc> stmtIdToDescMap; /** * Map of statement name to statement. */ protected final Map<String, EPStatement> stmtNameToStmtMap; private final EPServiceProviderSPI epServiceProvider; private final ManagedReadWriteLock eventProcessingRWLock; private final Map<String, Integer> stmtNameToIdMap; // Observers to statement-related events private final Set<StatementLifecycleObserver> observers; private int lastStatementId; /** * Ctor. * * @param epServiceProvider is the engine instance to hand to statement-aware listeners * @param services is engine services */ public StatementLifecycleSvcImpl(EPServiceProvider epServiceProvider, EPServicesContext services) { this.services = services; this.epServiceProvider = (EPServiceProviderSPI) epServiceProvider; // lock for starting and stopping statements this.eventProcessingRWLock = services.getEventProcessingRWLock(); this.stmtIdToDescMap = new HashMap<Integer, EPStatementDesc>(); this.stmtNameToStmtMap = new HashMap<String, EPStatement>(); this.stmtNameToIdMap = new LinkedHashMap<String, Integer>(); observers = new CopyOnWriteArraySet<StatementLifecycleObserver>(); } public void addObserver(StatementLifecycleObserver observer) { observers.add(observer); } public void removeObserver(StatementLifecycleObserver observer) { observers.remove(observer); } public void destroy() { this.destroyAllStatements(); } public void init() { // called after services are activated, to begin statement loading from store } public Map<String, EPStatement> getStmtNameToStmt() { return stmtNameToStmtMap; } public synchronized EPStatement createAndStart(StatementSpecRaw statementSpec, String expression, boolean isPattern, String optStatementName, Object userObject, EPIsolationUnitServices isolationUnitServices, Integer optionalStatementId, EPStatementObjectModel optionalModel) { Integer assignedStatementId = optionalStatementId; if (assignedStatementId == null) { do { lastStatementId++; assignedStatementId = lastStatementId; } while (stmtIdToDescMap.containsKey(assignedStatementId)); } EPStatementDesc desc = createStoppedAssignName(statementSpec, expression, isPattern, optStatementName, assignedStatementId, null, userObject, isolationUnitServices, optionalModel); start(assignedStatementId, desc, true, false, false); return desc.getEpStatement(); } /** * Creates and starts statement. * * @param statementSpec defines the statement * @param expression is the EPL * @param isPattern is true for patterns * @param optStatementName is the optional statement name * @param statementId is the statement id * @param optAdditionalContext additional context for use by the statement context * @param userObject the application define user object associated to each statement, if supplied * @param isolationUnitServices isolated service services * @param optionalModel model * @return started statement */ protected synchronized EPStatementDesc createStoppedAssignName(StatementSpecRaw statementSpec, String expression, boolean isPattern, String optStatementName, int statementId, Map<String, Object> optAdditionalContext, Object userObject, EPIsolationUnitServices isolationUnitServices, EPStatementObjectModel optionalModel) { boolean nameProvided = false; String statementName = "stmt_" + Integer.toString(statementId); // compile annotations, can produce a null array Annotation[] annotations = AnnotationUtil.compileAnnotations(statementSpec.getAnnotations(), services.getEngineImportService(), expression); // find name annotation if (optStatementName == null) { if (annotations != null && annotations.length != 0) { for (Annotation annotation : annotations) { if (annotation instanceof Name) { Name name = (Name) annotation; if (name.value() != null) { optStatementName = name.value(); } } } } } // Determine a statement name, i.e. use the id or use/generate one for the name passed in if (optStatementName != null) { optStatementName = optStatementName.trim(); statementName = getUniqueStatementName(optStatementName, statementId); nameProvided = true; } if (statementSpec.getFireAndForgetSpec() != null) { throw new EPStatementException("Provided EPL expression is an on-demand query expression (not a continuous query), please use the runtime executeQuery API instead", expression); } return createStopped(statementSpec, annotations, expression, isPattern, statementName, nameProvided, statementId, optAdditionalContext, userObject, isolationUnitServices, false, optionalModel); } /** * Create stopped statement. * * @param statementSpec - statement definition * @param expression is the expression text * @param isPattern is true for patterns, false for non-patterns * @param statementName is the statement name assigned or given * @param statementId is the statement id * @param optAdditionalContext additional context for use by the statement context * @param statementUserObject the application define user object associated to each statement, if supplied * @param isolationUnitServices isolated service services * @param isFailed to start the statement in failed state * @param nameProvided true when an explicit statement name is provided * @param annotations annotations * @param optionalModel model * @return stopped statement */ protected synchronized EPStatementDesc createStopped(StatementSpecRaw statementSpec, Annotation[] annotations, String expression, boolean isPattern, String statementName, boolean nameProvided, int statementId, Map<String, Object> optAdditionalContext, Object statementUserObject, EPIsolationUnitServices isolationUnitServices, boolean isFailed, EPStatementObjectModel optionalModel) { EPStatementDesc statementDesc; EPStatementStartMethod startMethod; // Hint annotations are often driven by variables if (annotations != null) { for (Annotation annotation : annotations) { if (annotation instanceof Hint) { statementSpec.setHasVariables(true); } } } // walk subselects, alias expressions, declared expressions, dot-expressions ExprNodeSubselectDeclaredDotVisitor visitor; try { visitor = StatementSpecRawAnalyzer.walkSubselectAndDeclaredDotExpr(statementSpec); } catch (ExprValidationException ex) { throw new EPStatementException(ex.getMessage(), expression); } // Determine table access nodes Set<ExprTableAccessNode> tableAccessNodes = determineTableAccessNodes(statementSpec.getTableExpressions(), visitor); new HashSet<ExprTableAccessNode>(); if (statementSpec.getTableExpressions() != null) { tableAccessNodes.addAll(statementSpec.getTableExpressions()); } if (visitor.getDeclaredExpressions() != null) { ExprNodeTableAccessVisitor tableAccessVisitor = new ExprNodeTableAccessVisitor(tableAccessNodes); for (ExprDeclaredNode declared : visitor.getDeclaredExpressions()) { declared.getBody().accept(tableAccessVisitor); } } for (ExprSubselectNode subselectNode : visitor.getSubselects()) { if (subselectNode.getStatementSpecRaw().getTableExpressions() != null) { tableAccessNodes.addAll(subselectNode.getStatementSpecRaw().getTableExpressions()); } } // Determine Subselects for compilation, and lambda-expression shortcut syntax for named windows List<ExprSubselectNode> subselectNodes = visitor.getSubselects(); if (!visitor.getChainedExpressionsDot().isEmpty()) { rewriteNamedWindowSubselect(visitor.getChainedExpressionsDot(), subselectNodes, services.getNamedWindowMgmtService()); } // compile foreign scripts validateScripts(expression, statementSpec.getScriptExpressions(), statementSpec.getExpressionDeclDesc()); // Determine statement type StatementType statementType = StatementMetadataFactoryDefault.getStatementType(statementSpec, isPattern); // Determine stateless statement boolean stateless = determineStatelessSelect(statementType, statementSpec, !subselectNodes.isEmpty(), isPattern); // Determine table use boolean writesToTables = StatementLifecycleSvcUtil.isWritesToTables(statementSpec, services.getTableService()); // Make context StatementContext statementContext = services.getStatementContextFactory().makeContext(statementId, statementName, expression, statementType, services, optAdditionalContext, false, annotations, isolationUnitServices, stateless, statementSpec, subselectNodes, writesToTables, statementUserObject); StatementSpecCompiled compiledSpec; try { compiledSpec = compile(statementSpec, expression, statementContext, false, false, annotations, visitor.getSubselects(), visitor.getDeclaredExpressions(), tableAccessNodes, services); } catch (RuntimeException ex) { handleRemove(statementId, statementName); throw ex; } // We keep a reference of the compiled spec as part of the statement context statementContext.setStatementSpecCompiled(compiledSpec); // For insert-into streams, create a lock taken out as soon as an event is inserted // Makes the processing between chained statements more predictable. if (statementSpec.getInsertIntoDesc() != null || statementSpec.getOnTriggerDesc() instanceof OnTriggerMergeDesc) { String insertIntoStreamName; if (statementSpec.getInsertIntoDesc() != null) { insertIntoStreamName = statementSpec.getInsertIntoDesc().getEventTypeName(); } else { insertIntoStreamName = "merge"; } String latchFactoryNameBack = "insert_stream_B_" + insertIntoStreamName + "_" + statementName; String latchFactoryNameFront = "insert_stream_F_" + insertIntoStreamName + "_" + statementName; long msecTimeout = services.getEngineSettingsService().getEngineSettings().getThreading().getInsertIntoDispatchTimeout(); ConfigurationEngineDefaults.Threading.Locking locking = services.getEngineSettingsService().getEngineSettings().getThreading().getInsertIntoDispatchLocking(); InsertIntoLatchFactory latchFactoryFront = new InsertIntoLatchFactory(latchFactoryNameFront, stateless, msecTimeout, locking, services.getTimeSource()); InsertIntoLatchFactory latchFactoryBack = new InsertIntoLatchFactory(latchFactoryNameBack, stateless, msecTimeout, locking, services.getTimeSource()); statementContext.getEpStatementHandle().setInsertIntoFrontLatchFactory(latchFactoryFront); statementContext.getEpStatementHandle().setInsertIntoBackLatchFactory(latchFactoryBack); } // determine overall filters, assign the filter spec index to filter boolean expressions boolean needDedup = false; StatementSpecCompiledAnalyzerResult streamAnalysis = StatementSpecCompiledAnalyzer.analyzeFilters(compiledSpec); FilterSpecCompiled[] filterSpecAll = streamAnalysis.getFilters().toArray(new FilterSpecCompiled[streamAnalysis.getFilters().size()]); NamedWindowConsumerStreamSpec[] namedWindowConsumersAll = streamAnalysis.getNamedWindowConsumers().toArray(new NamedWindowConsumerStreamSpec[streamAnalysis.getNamedWindowConsumers().size()]); compiledSpec.setFilterSpecsOverall(filterSpecAll); compiledSpec.setNamedWindowConsumersAll(namedWindowConsumersAll); for (FilterSpecCompiled filter : filterSpecAll) { if (filter.getParameters().length > 1) { needDedup = true; } StatementLifecycleSvcUtil.assignFilterSpecIds(filter, filterSpecAll); registerNonPropertyGetters(filter, statementName, services.getFilterNonPropertyRegisteryService()); } MultiMatchHandler multiMatchHandler; boolean isSubselectPreeval = services.getEngineSettingsService().getEngineSettings().getExpression().isSelfSubselectPreeval(); if (!needDedup) { // no dedup if (subselectNodes.isEmpty()) { multiMatchHandler = services.getMultiMatchHandlerFactory().makeNoDedupNoSubq(); } else { if (isSubselectPreeval) { multiMatchHandler = services.getMultiMatchHandlerFactory().makeNoDedupSubselectPreval(); } else { multiMatchHandler = services.getMultiMatchHandlerFactory().makeNoDedupSubselectPosteval(); } } } else { // with dedup if (subselectNodes.isEmpty()) { multiMatchHandler = services.getMultiMatchHandlerFactory().makeDedupNoSubq(); } else { multiMatchHandler = services.getMultiMatchHandlerFactory().makeDedupSubq(isSubselectPreeval); } } statementContext.getEpStatementHandle().setMultiMatchHandler(multiMatchHandler); // In a join statements if the same event type or it's deep super types are used in the join more then once, // then this is a self-join and the statement handle must know to dispatch the results together boolean canSelfJoin = isPotentialSelfJoin(compiledSpec) || needDedup; statementContext.getEpStatementHandle().setCanSelfJoin(canSelfJoin); // add statically typed event type references: those in the from clause; Dynamic (created) types collected by statement context and added on start services.getStatementEventTypeRefService().addReferences(statementName, compiledSpec.getEventTypeReferences()); // add variable references services.getStatementVariableRefService().addReferences(statementName, compiledSpec.getVariableReferences(), compiledSpec.getTableNodes()); // create metadata StatementMetadata statementMetadata = services.getStatementMetadataFactory().create(new StatementMetadataFactoryContext(statementName, statementId, statementContext, statementSpec, expression, isPattern, optionalModel)); eventProcessingRWLock.acquireWriteLock(); try { // create statement - may fail for parser and simple validation errors boolean preserveDispatchOrder = services.getEngineSettingsService().getEngineSettings().getThreading().isListenerDispatchPreserveOrder() && !stateless; boolean isSpinLocks = services.getEngineSettingsService().getEngineSettings().getThreading().getListenerDispatchLocking() == ConfigurationEngineDefaults.Threading.Locking.SPIN; long blockingTimeout = services.getEngineSettingsService().getEngineSettings().getThreading().getListenerDispatchTimeout(); long timeLastStateChange = services.getSchedulingService().getTime(); EPStatementSPI statement = services.getEpStatementFactory().make(statementSpec.getExpressionNoAnnotations(), isPattern, services.getDispatchService(), this, timeLastStateChange, preserveDispatchOrder, isSpinLocks, blockingTimeout, services.getTimeSource(), statementMetadata, statementUserObject, statementContext, isFailed, nameProvided); statementContext.setStatement(statement); boolean isInsertInto = statementSpec.getInsertIntoDesc() != null; boolean isDistinct = statementSpec.getSelectClauseSpec().isDistinct(); boolean isForClause = statementSpec.getForClauseSpec() != null; statementContext.getStatementResultService().setContext(statement, epServiceProvider, isInsertInto, isPattern, isDistinct, isForClause, statementContext.getEpStatementHandle().getMetricsHandle()); // create start method startMethod = EPStatementStartMethodFactory.makeStartMethod(compiledSpec); statementDesc = new EPStatementDesc(statement, startMethod, statementContext); stmtIdToDescMap.put(statementId, statementDesc); stmtNameToStmtMap.put(statementName, statement); stmtNameToIdMap.put(statementName, statementId); dispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.CREATE)); } catch (RuntimeException ex) { stmtIdToDescMap.remove(statementId); stmtNameToIdMap.remove(statementName); stmtNameToStmtMap.remove(statementName); throw ex; } finally { eventProcessingRWLock.releaseWriteLock(); } return statementDesc; } private Set<ExprTableAccessNode> determineTableAccessNodes(Set<ExprTableAccessNode> statementDirectTableAccess, ExprNodeSubselectDeclaredDotVisitor visitor) { Set<ExprTableAccessNode> tableAccessNodes = new HashSet<ExprTableAccessNode>(); if (statementDirectTableAccess != null) { tableAccessNodes.addAll(statementDirectTableAccess); } // include all declared expression usages ExprNodeTableAccessVisitor tableAccessVisitor = new ExprNodeTableAccessVisitor(tableAccessNodes); for (ExprDeclaredNode declared : visitor.getDeclaredExpressions()) { declared.getBody().accept(tableAccessVisitor); } // include all subqueries (and their declared expressions) // This is nested as declared expressions can have more subqueries, however all subqueries are in this list. for (ExprSubselectNode subselectNode : visitor.getSubselects()) { if (subselectNode.getStatementSpecRaw().getTableExpressions() != null) { tableAccessNodes.addAll(subselectNode.getStatementSpecRaw().getTableExpressions()); } } return tableAccessNodes; } // All scripts get compiled/verfied - to ensure they compile (and not just when they are referred to my an expression). private void validateScripts(String epl, List<ExpressionScriptProvided> scripts, ExpressionDeclDesc expressionDeclDesc) { if (scripts == null) { return; } try { Set<NameParameterCountKey> scriptsSet = new HashSet<NameParameterCountKey>(); for (ExpressionScriptProvided script : scripts) { validateScript(script); NameParameterCountKey key = new NameParameterCountKey(script.getName(), script.getParameterNames().size()); if (scriptsSet.contains(key)) { throw new ExprValidationException("Script name '" + script.getName() + "' has already been defined with the same number of parameters"); } scriptsSet.add(key); } if (expressionDeclDesc != null) { for (ExpressionDeclItem declItem : expressionDeclDesc.getExpressions()) { if (scriptsSet.contains(new NameParameterCountKey(declItem.getName(), 0))) { throw new ExprValidationException("Script name '" + declItem.getName() + "' overlaps with another expression of the same name"); } } } } catch (ExprValidationException ex) { throw new EPStatementException(ex.getMessage(), ex, epl); } } private void validateScript(ExpressionScriptProvided script) throws ExprValidationException { String dialect = script.getOptionalDialect() == null ? services.getConfigSnapshot().getEngineDefaults().getScripts().getDefaultDialect() : script.getOptionalDialect(); if (dialect == null) { throw new ExprValidationException("Failed to determine script dialect for script '" + script.getName() + "', please configure a default dialect or provide a dialect explicitly"); } if (dialect.trim().toLowerCase(Locale.ENGLISH).equals("mvel")) { if (!MVELInvoker.isMVELInClasspath(services.getEngineImportService())) { throw new ExprValidationException("MVEL scripting engine not found in classpath, script dialect 'mvel' requires mvel in classpath for script '" + script.getName() + "'"); } MVELHelper.verifyScript(script, services.getEngineImportService()); } else { JSR223Helper.verifyCompileScript(script, dialect); } if (!script.getParameterNames().isEmpty()) { HashSet<String> parameters = new HashSet<String>(); for (String param : script.getParameterNames()) { if (parameters.contains(param)) { throw new ExprValidationException("Invalid script parameters for script '" + script.getName() + "', parameter '" + param + "' is defined more then once"); } parameters.add(param); } } } private boolean isPotentialSelfJoin(StatementSpecCompiled spec) { // Include create-context as nested contexts that have pattern-initiated sub-contexts may change filters during execution if (spec.getContextDesc() != null && spec.getContextDesc().getContextDetail() instanceof ContextDetailNested) { return true; } // if order-by is specified, ans since multiple output rows may produce, ensure dispatch if (spec.getOrderByList().length > 0) { return true; } for (StreamSpecCompiled streamSpec : spec.getStreamSpecs()) { if (streamSpec instanceof PatternStreamSpecCompiled) { return true; } } // not a self join if ((spec.getStreamSpecs().length <= 1) && (spec.getSubSelectExpressions().length == 0)) { return false; } // join - determine types joined List<EventType> filteredTypes = new ArrayList<EventType>(); // consider subqueryes Set<EventType> optSubselectTypes = populateSubqueryTypes(spec.getSubSelectExpressions()); boolean hasFilterStream = false; for (StreamSpecCompiled streamSpec : spec.getStreamSpecs()) { if (streamSpec instanceof FilterStreamSpecCompiled) { EventType type = ((FilterStreamSpecCompiled) streamSpec).getFilterSpec().getFilterForEventType(); filteredTypes.add(type); hasFilterStream = true; } } if ((filteredTypes.size() == 1) && (optSubselectTypes.isEmpty())) { return false; } // pattern-only streams are not self-joins if (!hasFilterStream) { return false; } // is type overlap in filters for (int i = 0; i < filteredTypes.size(); i++) { for (int j = i + 1; j < filteredTypes.size(); j++) { EventType typeOne = filteredTypes.get(i); EventType typeTwo = filteredTypes.get(j); if (typeOne == typeTwo) { return true; } if (typeOne.getSuperTypes() != null) { for (EventType typeOneSuper : typeOne.getSuperTypes()) { if (typeOneSuper == typeTwo) { return true; } } } if (typeTwo.getSuperTypes() != null) { for (EventType typeTwoSuper : typeTwo.getSuperTypes()) { if (typeOne == typeTwoSuper) { return true; } } } } } // analyze subselect types if (!optSubselectTypes.isEmpty()) { for (EventType typeOne : filteredTypes) { if (optSubselectTypes.contains(typeOne)) { return true; } if (typeOne.getSuperTypes() != null) { for (EventType typeOneSuper : typeOne.getSuperTypes()) { if (optSubselectTypes.contains(typeOneSuper)) { return true; } } } } } return false; } private Set<EventType> populateSubqueryTypes(ExprSubselectNode[] subSelectExpressions) { Set<EventType> set = null; for (ExprSubselectNode subselect : subSelectExpressions) { for (StreamSpecCompiled streamSpec : subselect.getStatementSpecCompiled().getStreamSpecs()) { if (streamSpec instanceof FilterStreamSpecCompiled) { EventType type = ((FilterStreamSpecCompiled) streamSpec).getFilterSpec().getFilterForEventType(); if (set == null) { set = new HashSet<EventType>(); } set.add(type); } else if (streamSpec instanceof PatternStreamSpecCompiled) { EvalNodeAnalysisResult evalNodeAnalysisResult = EvalNodeUtil.recursiveAnalyzeChildNodes(((PatternStreamSpecCompiled) streamSpec).getEvalFactoryNode()); List<EvalFilterFactoryNode> filterNodes = evalNodeAnalysisResult.getFilterNodes(); for (EvalFilterFactoryNode filterNode : filterNodes) { if (set == null) { set = new HashSet<EventType>(); } set.add(filterNode.getFilterSpec().getFilterForEventType()); } } } } if (set == null) { return Collections.emptySet(); } return set; } public synchronized void start(int statementId) { if (log.isDebugEnabled()) { log.debug(".start Starting statement " + statementId); } // Acquire a lock for event processing as threads may be in the views used by the statement // and that could conflict with the destroy of views eventProcessingRWLock.acquireWriteLock(); try { EPStatementDesc desc = stmtIdToDescMap.get(statementId); if (desc == null) { throw new IllegalStateException("Cannot start statement, statement is in destroyed state"); } startInternal(statementId, desc, false, false, false); } finally { eventProcessingRWLock.releaseWriteLock(); } } /** * Start the given statement. * * @param statementId is the statement id * @param desc is the cached statement info * @param isNewStatement indicator whether the statement is new or a stop-restart statement * @param isRecoveringStatement if the statement is recovering or new * @param isResilient true if recovering a resilient stmt */ public void start(int statementId, EPStatementDesc desc, boolean isNewStatement, boolean isRecoveringStatement, boolean isResilient) { if (log.isDebugEnabled()) { log.debug(".start Starting statement " + statementId + " from desc=" + desc); } // Acquire a lock for event processing as threads may be in the views used by the statement // and that could conflict with the destroy of views if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qEngineManagementStmtCompileStart( services.getEngineURI(), statementId, desc.getEpStatement().getName(), desc.getEpStatement().getText(), services.getSchedulingService().getTime()); } eventProcessingRWLock.acquireWriteLock(); try { startInternal(statementId, desc, isNewStatement, isRecoveringStatement, isResilient); if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qaEngineManagementStmtStarted( services.getEngineURI(), statementId, desc.getEpStatement().getName(), desc.getEpStatement().getText(), services.getSchedulingService().getTime()); } if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aEngineManagementStmtCompileStart(true, null); } } catch (RuntimeException ex) { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aEngineManagementStmtCompileStart(false, ex.getMessage()); } throw ex; } finally { eventProcessingRWLock.releaseWriteLock(); } } private void startInternal(int statementId, EPStatementDesc desc, boolean isNewStatement, boolean isRecoveringStatement, boolean isResilient) { if (log.isDebugEnabled()) { log.debug(".startInternal Starting statement " + statementId + " from desc=" + desc); } if (desc.getStartMethod() == null) { throw new IllegalStateException("Statement start method not found for id " + statementId); } EPStatementSPI statement = desc.getEpStatement(); if (statement.getState() == EPStatementState.STARTED) { log.debug(".startInternal - Statement already started"); return; } EPStatementStartResult startResult; try { // start logically startResult = desc.getStartMethod().start(services, desc.getStatementContext(), isNewStatement, isRecoveringStatement, isResilient); // start named window consumers services.getNamedWindowConsumerMgmtService().start(desc.getStatementContext().getStatementName()); } catch (EPStatementException ex) { handleRemove(statementId, statement.getName()); log.debug(".start Error starting statement", ex); throw ex; } catch (ExprValidationException ex) { handleRemove(statementId, statement.getName()); log.debug(".start Error starting statement", ex); throw new EPStatementException("Error starting statement: " + ex.getMessage(), ex, statement.getText()); } catch (ViewProcessingException ex) { handleRemove(statementId, statement.getName()); log.debug(".start Error starting statement", ex); throw new EPStatementException("Error starting statement: " + ex.getMessage(), ex, statement.getText()); } catch (RuntimeException ex) { handleRemove(statementId, statement.getName()); log.debug(".start Error starting statement", ex); throw new EPStatementException("Unexpected exception starting statement: " + ex.getMessage(), ex, statement.getText()); } // hook up Viewable parentView = startResult.getViewable(); desc.setStopMethod(startResult.getStopMethod()); desc.setDestroyMethod(startResult.getDestroyMethod()); statement.setParentView(parentView); long timeLastStateChange = services.getSchedulingService().getTime(); statement.setCurrentState(EPStatementState.STARTED, timeLastStateChange); dispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.STATECHANGE)); } private void handleRemove(int statementId, String statementName) { stmtIdToDescMap.remove(statementId); stmtNameToIdMap.remove(statementName); stmtNameToStmtMap.remove(statementName); services.getStatementEventTypeRefService().removeReferencesStatement(statementName); services.getStatementVariableRefService().removeReferencesStatement(statementName); services.getFilterNonPropertyRegisteryService().removeReferencesStatement(statementName); services.getNamedWindowConsumerMgmtService().removeReferences(statementName); } public synchronized void stop(int statementId) { // Acquire a lock for event processing as threads may be in the views used by the statement // and that could conflict with the destroy of views eventProcessingRWLock.acquireWriteLock(); try { EPStatementDesc desc = stmtIdToDescMap.get(statementId); if (desc == null) { throw new IllegalStateException("Cannot stop statement, statement is in destroyed state"); } EPStatementSPI statement = desc.getEpStatement(); EPStatementStopMethod stopMethod = desc.getStopMethod(); if (stopMethod == null) { throw new IllegalStateException("Stop method not found for statement " + statementId); } if (statement.getState() == EPStatementState.STOPPED) { log.debug(".startInternal - Statement already stopped"); return; } // stop named window consumers services.getNamedWindowConsumerMgmtService().stop(desc.getStatementContext().getStatementName()); // fire the statement stop if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qEngineManagementStmtStop(EPStatementState.STOPPED, services.getEngineURI(), statementId, statement.getName(), statement.getText(), services.getSchedulingService().getTime()); } desc.getStatementContext().getStatementStopService().fireStatementStopped(); // invoke start-provided stop method stopMethod.stop(); statement.setParentView(null); desc.setStopMethod(null); long timeLastStateChange = services.getSchedulingService().getTime(); statement.setCurrentState(EPStatementState.STOPPED, timeLastStateChange); ((EPRuntimeSPI) epServiceProvider.getEPRuntime()).clearCaches(); dispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.STATECHANGE)); } finally { eventProcessingRWLock.releaseWriteLock(); if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aEngineManagementStmtStop(); } } } public synchronized void destroy(int statementId) { // Acquire a lock for event processing as threads may be in the views used by the statement // and that could conflict with the destroy of views eventProcessingRWLock.acquireWriteLock(); try { EPStatementDesc desc = stmtIdToDescMap.get(statementId); if (desc == null) { log.debug(".destroy - Statement already destroyed"); return; } destroyInternal(desc); } finally { eventProcessingRWLock.releaseWriteLock(); } } public synchronized EPStatement getStatementByName(String name) { return stmtNameToStmtMap.get(name); } public synchronized StatementSpecCompiled getStatementSpec(int statementId) { EPStatementDesc desc = stmtIdToDescMap.get(statementId); if (desc != null) { return desc.getStartMethod().getStatementSpec(); } return null; } /** * Returns the statement given a statement id. * * @param statementId is the statement id * @return statement */ public EPStatementSPI getStatementById(int statementId) { EPStatementDesc statementDesc = this.stmtIdToDescMap.get(statementId); if (statementDesc == null) { log.warn("Could not locate statement descriptor for statement id '" + statementId + "'"); return null; } return statementDesc.getEpStatement(); } public StatementContext getStatementContextById(int statementId) { EPStatementDesc statementDesc = this.stmtIdToDescMap.get(statementId); if (statementDesc == null) { return null; } return statementDesc.getEpStatement().getStatementContext(); } public synchronized String[] getStatementNames() { String[] statements = new String[stmtNameToStmtMap.size()]; int count = 0; for (String key : stmtNameToStmtMap.keySet()) { statements[count++] = key; } return statements; } public synchronized void startAllStatements() throws EPException { int[] statementIds = getStatementIds(); for (int i = 0; i < statementIds.length; i++) { EPStatement statement = stmtIdToDescMap.get(statementIds[i]).getEpStatement(); if (statement.getState() == EPStatementState.STOPPED) { start(statementIds[i]); } } } public synchronized void stopAllStatements() throws EPException { int[] statementIds = getStatementIds(); for (int i = 0; i < statementIds.length; i++) { EPStatement statement = stmtIdToDescMap.get(statementIds[i]).getEpStatement(); if (statement.getState() == EPStatementState.STARTED) { stop(statementIds[i]); } } } public synchronized void destroyAllStatements() throws EPException { // Acquire a lock for event processing as threads may be in the views used by the statement // and that could conflict with the destroy of views eventProcessingRWLock.acquireWriteLock(); try { int[] statementIds = getStatementIds(); for (int statementId : statementIds) { EPStatementDesc desc = stmtIdToDescMap.get(statementId); if (desc == null) { continue; } try { destroyInternal(desc); } catch (RuntimeException ex) { services.getExceptionHandlingService().handleException(ex, desc.getEpStatement().getName(), desc.getEpStatement().getText(), ExceptionHandlerExceptionType.STOP, null); } } } finally { eventProcessingRWLock.releaseWriteLock(); } } private int[] getStatementIds() { int[] statementIds = new int[stmtNameToIdMap.size()]; int count = 0; for (int id : stmtNameToIdMap.values()) { statementIds[count++] = id; } return statementIds; } private String getUniqueStatementName(String statementName, int statementId) { String finalStatementName; if (stmtNameToIdMap.containsKey(statementName)) { int count = 0; while (true) { finalStatementName = statementName + "--" + count; if (!(stmtNameToIdMap.containsKey(finalStatementName))) { break; } if (count > Integer.MAX_VALUE - 2) { throw new EPException("Failed to establish a unique statement name"); } count++; } } else { finalStatementName = statementName; } stmtNameToIdMap.put(finalStatementName, statementId); return finalStatementName; } @Override public String getStatementNameById(int statementId) { EPStatementDesc desc = stmtIdToDescMap.get(statementId); if (desc != null) { return desc.getEpStatement().getName(); } return null; } public void updatedListeners(EPStatement statement, EPStatementListenerSet listeners, boolean isRecovery) { log.debug(".updatedListeners No action for base implementation"); } /** * Compiles a statement returning the compile (verified, non-serializable) form of a statement. * * @param spec is the statement specification * @param eplStatement the statement to compile * @param statementContext the statement services * @param isSubquery is true for subquery compilation or false for statement compile * @param annotations statement annotations * @param isOnDemandQuery indicator whether on-demand query * @param subselectNodes subselects * @param declaredNodes declared expressions * @param tableAccessNodes table nodes * @param servicesContext services * @return compiled statement * @throws EPStatementException if the statement cannot be compiled */ protected static StatementSpecCompiled compile(StatementSpecRaw spec, String eplStatement, StatementContext statementContext, boolean isSubquery, boolean isOnDemandQuery, Annotation[] annotations, List<ExprSubselectNode> subselectNodes, List<ExprDeclaredNode> declaredNodes, Set<ExprTableAccessNode> tableAccessNodes, EPServicesContext servicesContext) throws EPStatementException { List<StreamSpecCompiled> compiledStreams; Set<String> eventTypeReferences = new HashSet<String>(); // If not using a join and not specifying a data window, make the where-clause, if present, the filter of the stream // if selecting using filter spec, and not subquery in where clause if ((spec.getStreamSpecs().size() == 1) && (spec.getStreamSpecs().get(0) instanceof FilterStreamSpecRaw) && (spec.getStreamSpecs().get(0).getViewSpecs().length == 0) && (spec.getFilterRootNode() != null) && (spec.getOnTriggerDesc() == null) && !isSubquery && !isOnDemandQuery && (tableAccessNodes == null || tableAccessNodes.isEmpty())) { boolean disqualified; ExprNode whereClause = spec.getFilterRootNode(); ExprNodeSubselectDeclaredDotVisitor visitor = new ExprNodeSubselectDeclaredDotVisitor(); whereClause.accept(visitor); disqualified = visitor.getSubselects().size() > 0 || HintEnum.DISABLE_WHEREEXPR_MOVETO_FILTER.getHint(annotations) != null; if (!disqualified) { ExprNodeViewResourceVisitor viewResourceVisitor = new ExprNodeViewResourceVisitor(); whereClause.accept(viewResourceVisitor); disqualified = viewResourceVisitor.getExprNodes().size() > 0; } if (!disqualified) { // If an alias is provided, find all properties to ensure the alias gets removed String alias = spec.getStreamSpecs().get(0).getOptionalStreamName(); if (alias != null) { ExprNodeIdentifierCollectVisitor v = new ExprNodeIdentifierCollectVisitor(); whereClause.accept(v); for (ExprIdentNode node : v.getExprProperties()) { if (node.getStreamOrPropertyName() != null && (node.getStreamOrPropertyName().equals(alias))) { node.setStreamOrPropertyName(null); } } } spec.setFilterExprRootNode(null); FilterStreamSpecRaw streamSpec = (FilterStreamSpecRaw) spec.getStreamSpecs().get(0); streamSpec.getRawFilterSpec().getFilterExpressions().add(whereClause); } } // compile select-clause SelectClauseSpecCompiled selectClauseCompiled = StatementLifecycleSvcUtil.compileSelectClause(spec.getSelectClauseSpec()); // Determine subselects in filter streams, these may need special handling for locking ExprNodeSubselectDeclaredDotVisitor visitor = new ExprNodeSubselectDeclaredDotVisitor(); StatementLifecycleSvcUtil.walkStreamSpecs(spec, visitor); for (ExprSubselectNode subselectNode : visitor.getSubselects()) { subselectNode.setFilterStreamSubselect(true); } // Determine subselects for compilation, and lambda-expression shortcut syntax for named windows visitor.reset(); GroupByClauseExpressions groupByRollupExpressions; try { StatementLifecycleSvcUtil.walkStatement(spec, visitor); groupByRollupExpressions = GroupByExpressionHelper.getGroupByRollupExpressions(spec.getGroupByExpressions(), spec.getSelectClauseSpec(), spec.getHavingExprRootNode(), spec.getOrderByList(), visitor); List<ExprSubselectNode> subselects = visitor.getSubselects(); if (!visitor.getChainedExpressionsDot().isEmpty()) { rewriteNamedWindowSubselect(visitor.getChainedExpressionsDot(), subselects, statementContext.getNamedWindowMgmtService()); } } catch (ExprValidationException ex) { throw new EPStatementException(ex.getMessage(), eplStatement); } if (isSubquery && !visitor.getSubselects().isEmpty()) { throw new EPStatementException("Invalid nested subquery, subquery-within-subquery is not supported", eplStatement); } if (isOnDemandQuery && !visitor.getSubselects().isEmpty()) { throw new EPStatementException("Subqueries are not a supported feature of on-demand queries", eplStatement); } for (ExprSubselectNode subselectNode : visitor.getSubselects()) { if (!subselectNodes.contains(subselectNode)) { subselectNodes.add(subselectNode); } } // Compile subselects found int subselectNumber = 0; for (ExprSubselectNode subselect : subselectNodes) { StatementSpecRaw raw = subselect.getStatementSpecRaw(); StatementSpecCompiled compiled = compile(raw, eplStatement, statementContext, true, isOnDemandQuery, new Annotation[0], Collections.<ExprSubselectNode>emptyList(), Collections.<ExprDeclaredNode>emptyList(), raw.getTableExpressions(), servicesContext); subselectNumber++; subselect.setStatementSpecCompiled(compiled, subselectNumber); } // compile each stream used try { compiledStreams = new ArrayList<StreamSpecCompiled>(spec.getStreamSpecs().size()); int streamNum = 0; for (StreamSpecRaw rawSpec : spec.getStreamSpecs()) { streamNum++; StreamSpecCompiled compiled = rawSpec.compile(statementContext, eventTypeReferences, spec.getInsertIntoDesc() != null, Collections.singleton(streamNum), spec.getStreamSpecs().size() > 1, false, spec.getOnTriggerDesc() != null, rawSpec.getOptionalStreamName()); compiledStreams.add(compiled); } } catch (ExprValidationException ex) { log.info("Failed to compile statement: " + ex.getMessage(), ex); if (ex.getMessage() == null) { throw new EPStatementException("Unexpected exception compiling statement, please consult the log file and report the exception", eplStatement); } else { throw new EPStatementException(ex.getMessage(), ex, eplStatement); } } catch (RuntimeException ex) { String text = "Unexpected error compiling statement"; log.error(text, ex); throw new EPStatementException(text + ": " + ex.getClass().getName() + ":" + ex.getMessage(), eplStatement); } // for create window statements, we switch the filter to a new event type if (spec.getCreateWindowDesc() != null) { try { StreamSpecCompiled createWindowTypeSpec = compiledStreams.get(0); EventType selectFromType; String selectFromTypeName; if (createWindowTypeSpec instanceof FilterStreamSpecCompiled) { FilterStreamSpecCompiled filterStreamSpec = (FilterStreamSpecCompiled) createWindowTypeSpec; selectFromType = filterStreamSpec.getFilterSpec().getFilterForEventType(); selectFromTypeName = filterStreamSpec.getFilterSpec().getFilterForEventTypeName(); if (spec.getCreateWindowDesc().isInsert() || spec.getCreateWindowDesc().getInsertFilter() != null) { throw new EPStatementException("A named window by name '" + selectFromTypeName + "' could not be located, use the insert-keyword with an existing named window", eplStatement); } } else { NamedWindowConsumerStreamSpec consumerStreamSpec = (NamedWindowConsumerStreamSpec) createWindowTypeSpec; selectFromType = statementContext.getEventAdapterService().getExistsTypeByName(consumerStreamSpec.getWindowName()); selectFromTypeName = consumerStreamSpec.getWindowName(); if (spec.getCreateWindowDesc().getInsertFilter() != null) { ExprNode insertIntoFilter = spec.getCreateWindowDesc().getInsertFilter(); String checkMinimal = ExprNodeUtility.isMinimalExpression(insertIntoFilter); if (checkMinimal != null) { throw new ExprValidationException("Create window where-clause may not have " + checkMinimal); } StreamTypeService streamTypeService = new StreamTypeServiceImpl(selectFromType, selectFromTypeName, true, statementContext.getEngineURI()); ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); ExprValidationContext validationContext = new ExprValidationContext(streamTypeService, statementContext.getEngineImportService(), statementContext.getStatementExtensionServicesContext(), null, statementContext.getSchedulingService(), statementContext.getVariableService(), statementContext.getTableService(), evaluatorContextStmt, statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getContextDescriptor(), false, false, false, false, null, false); ExprNode insertFilter = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.CREATEWINDOWFILTER, spec.getCreateWindowDesc().getInsertFilter(), validationContext); spec.getCreateWindowDesc().setInsertFilter(insertFilter); } // set the window to insert from spec.getCreateWindowDesc().setInsertFromWindow(consumerStreamSpec.getWindowName()); } Pair<FilterSpecCompiled, SelectClauseSpecRaw> newFilter = handleCreateWindow(selectFromType, selectFromTypeName, spec.getCreateWindowDesc().getColumns(), spec, eplStatement, statementContext, servicesContext); eventTypeReferences.add(((EventTypeSPI) newFilter.getFirst().getFilterForEventType()).getMetadata().getPrimaryName()); // view must be non-empty list if (spec.getCreateWindowDesc().getViewSpecs().isEmpty()) { throw new ExprValidationException(NamedWindowMgmtService.ERROR_MSG_DATAWINDOWS); } // use the filter specification of the newly created event type and the views for the named window compiledStreams.clear(); ViewSpec[] views = ViewSpec.toArray(spec.getCreateWindowDesc().getViewSpecs()); compiledStreams.add(new FilterStreamSpecCompiled(newFilter.getFirst(), views, null, spec.getCreateWindowDesc().getStreamSpecOptions())); spec.setSelectClauseSpec(newFilter.getSecond()); } catch (ExprValidationException e) { throw new EPStatementException(e.getMessage(), eplStatement); } } return new StatementSpecCompiled( spec.getOnTriggerDesc(), spec.getCreateWindowDesc(), spec.getCreateIndexDesc(), spec.getCreateVariableDesc(), spec.getCreateTableDesc(), spec.getCreateSchemaDesc(), spec.getInsertIntoDesc(), spec.getSelectStreamSelectorEnum(), selectClauseCompiled, compiledStreams.toArray(new StreamSpecCompiled[compiledStreams.size()]), OuterJoinDesc.toArray(spec.getOuterJoinDescList()), spec.getFilterRootNode(), spec.getHavingExprRootNode(), spec.getOutputLimitSpec(), OrderByItem.toArray(spec.getOrderByList()), ExprSubselectNode.toArray(subselectNodes), ExprNodeUtility.toArray(declaredNodes), spec.getScriptExpressions() == null || spec.getScriptExpressions().isEmpty() ? ExprNodeUtility.EMPTY_SCRIPTS : spec.getScriptExpressions().toArray(new ExpressionScriptProvided[spec.getScriptExpressions().size()]), spec.getReferencedVariables(), spec.getRowLimitSpec(), CollectionUtil.toArray(eventTypeReferences), annotations, spec.getUpdateDesc(), spec.getMatchRecognizeSpec(), spec.getForClauseSpec(), spec.getSqlParameters(), spec.getCreateContextDesc(), spec.getOptionalContextName(), spec.getCreateDataFlowDesc(), spec.getCreateExpressionDesc(), spec.getFireAndForgetSpec(), groupByRollupExpressions, spec.getIntoTableSpec(), tableAccessNodes == null ? null : tableAccessNodes.toArray(new ExprTableAccessNode[tableAccessNodes.size()])); } private static boolean determineStatelessSelect(StatementType type, StatementSpecRaw spec, boolean hasSubselects, boolean isPattern) { if (hasSubselects || isPattern) { return false; } if (type != StatementType.SELECT && type != StatementType.INSERT_INTO) { return false; } if (spec.getStreamSpecs() == null || spec.getStreamSpecs().size() > 1 || spec.getStreamSpecs().isEmpty()) { return false; } StreamSpecRaw singleStream = spec.getStreamSpecs().get(0); if (!(singleStream instanceof FilterStreamSpecRaw) && !(singleStream instanceof NamedWindowConsumerStreamSpec)) { return false; } if (singleStream.getViewSpecs() != null && singleStream.getViewSpecs().length > 0) { return false; } if (spec.getOutputLimitSpec() != null) { return false; } if (spec.getMatchRecognizeSpec() != null) { return false; } List<ExprNode> expressions = StatementSpecRawAnalyzer.collectExpressionsShallow(spec); if (expressions.isEmpty()) { return true; } ExprNodeSummaryVisitor visitor = new ExprNodeSummaryVisitor(); for (ExprNode expr : expressions) { if (expr == null) { continue; } expr.accept(visitor); } return !visitor.isHasAggregation() && !visitor.isHasPreviousPrior() && !visitor.isHasSubselect(); } private static void rewriteNamedWindowSubselect(List<ExprDotNode> chainedExpressionsDot, List<ExprSubselectNode> subselects, NamedWindowMgmtService service) { for (ExprDotNode dotNode : chainedExpressionsDot) { String proposedWindow = dotNode.getChainSpec().get(0).getName(); if (!service.isNamedWindow(proposedWindow)) { continue; } // build spec for subselect StatementSpecRaw raw = new StatementSpecRaw(SelectClauseStreamSelectorEnum.ISTREAM_ONLY); FilterSpecRaw filter = new FilterSpecRaw(proposedWindow, Collections.<ExprNode>emptyList(), null); raw.getStreamSpecs().add(new FilterStreamSpecRaw(filter, ViewSpec.EMPTY_VIEWSPEC_ARRAY, proposedWindow, StreamSpecOptions.DEFAULT)); ExprChainedSpec firstChain = dotNode.getChainSpec().remove(0); if (!firstChain.getParameters().isEmpty()) { if (firstChain.getParameters().size() == 1) { raw.setFilterExprRootNode(firstChain.getParameters().get(0)); } else { ExprAndNode andNode = new ExprAndNodeImpl(); for (ExprNode node : firstChain.getParameters()) { andNode.addChildNode(node); } raw.setFilterExprRootNode(andNode); } } // activate subselect ExprSubselectNode subselect = new ExprSubselectRowNode(raw); subselects.add(subselect); dotNode.setChildNodes(subselect); } } /** * Compile a select clause allowing subselects. * * @param spec to compile * @return select clause compiled * @throws ExprValidationException when validation fails */ public static SelectClauseSpecCompiled compileSelectAllowSubselect(SelectClauseSpecRaw spec) throws ExprValidationException { // Look for expressions with sub-selects in select expression list and filter expression // Recursively compile the statement within the statement. ExprNodeSubselectDeclaredDotVisitor visitor = new ExprNodeSubselectDeclaredDotVisitor(); List<SelectClauseElementCompiled> selectElements = new ArrayList<SelectClauseElementCompiled>(); for (SelectClauseElementRaw raw : spec.getSelectExprList()) { if (raw instanceof SelectClauseExprRawSpec) { SelectClauseExprRawSpec rawExpr = (SelectClauseExprRawSpec) raw; rawExpr.getSelectExpression().accept(visitor); selectElements.add(new SelectClauseExprCompiledSpec(rawExpr.getSelectExpression(), rawExpr.getOptionalAsName(), rawExpr.getOptionalAsName(), rawExpr.isEvents())); } else if (raw instanceof SelectClauseStreamRawSpec) { SelectClauseStreamRawSpec rawExpr = (SelectClauseStreamRawSpec) raw; selectElements.add(new SelectClauseStreamCompiledSpec(rawExpr.getStreamName(), rawExpr.getOptionalAsName())); } else if (raw instanceof SelectClauseElementWildcard) { SelectClauseElementWildcard wildcard = (SelectClauseElementWildcard) raw; selectElements.add(wildcard); } else { throw new IllegalStateException("Unexpected select clause element class : " + raw.getClass().getName()); } } return new SelectClauseSpecCompiled(selectElements.toArray(new SelectClauseElementCompiled[selectElements.size()]), spec.isDistinct()); } // The create window command: // create window windowName[.window_view_list] as [select properties from] type // // This section expected s single FilterStreamSpecCompiled representing the selected type. // It creates a new event type representing the window type and a sets the type selected on the filter stream spec. private static Pair<FilterSpecCompiled, SelectClauseSpecRaw> handleCreateWindow(EventType selectFromType, String selectFromTypeName, List<ColumnDesc> columns, StatementSpecRaw spec, String eplStatement, StatementContext statementContext, EPServicesContext servicesContext) throws ExprValidationException { String typeName = spec.getCreateWindowDesc().getWindowName(); EventType targetType; // determine that the window name is not already in use as an event type name EventType existingType = servicesContext.getEventAdapterService().getExistsTypeByName(typeName); if (existingType != null && ((EventTypeSPI) existingType).getMetadata().getTypeClass() != EventTypeMetadata.TypeClass.NAMED_WINDOW) { throw new ExprValidationException("Error starting statement: An event type or schema by name '" + typeName + "' already exists"); } // Validate the select expressions which consists of properties only ExprEvaluatorContextStatement evaluatorContextStmt = new ExprEvaluatorContextStatement(statementContext, false); List<NamedWindowSelectedProps> select = compileLimitedSelect(spec.getSelectClauseSpec(), eplStatement, selectFromType, selectFromTypeName, statementContext.getEngineURI(), evaluatorContextStmt, statementContext.getEngineImportService(), statementContext.getEventAdapterService(), statementContext.getStatementName(), statementContext.getStatementId(), statementContext.getAnnotations(), statementContext.getStatementExtensionServicesContext()); // Create Map or Wrapper event type from the select clause of the window. // If no columns selected, simply create a wrapper type // Build a list of properties SelectClauseSpecRaw newSelectClauseSpecRaw = new SelectClauseSpecRaw(); LinkedHashMap<String, Object> properties; boolean hasProperties = false; if ((columns != null) && (!columns.isEmpty())) { properties = EventTypeUtility.buildType(columns, statementContext.getEventAdapterService(), null, statementContext.getEngineImportService()); hasProperties = true; } else { properties = new LinkedHashMap<String, Object>(); for (NamedWindowSelectedProps selectElement : select) { if (selectElement.getFragmentType() != null) { properties.put(selectElement.getAssignedName(), selectElement.getFragmentType()); } else { properties.put(selectElement.getAssignedName(), selectElement.getSelectExpressionType()); } // Add any properties to the new select clause for use by consumers to the statement itself newSelectClauseSpecRaw.add(new SelectClauseExprRawSpec(new ExprIdentNodeImpl(selectElement.getAssignedName()), null, false)); hasProperties = true; } } // Create Map or Wrapper event type from the select clause of the window. // If no columns selected, simply create a wrapper type boolean isOnlyWildcard = spec.getSelectClauseSpec().isOnlyWildcard(); boolean isWildcard = spec.getSelectClauseSpec().isUsingWildcard(); if (statementContext.getValueAddEventService().isRevisionTypeName(selectFromTypeName)) { targetType = statementContext.getValueAddEventService().createRevisionType(typeName, selectFromTypeName, statementContext.getStatementStopService(), statementContext.getEventAdapterService(), servicesContext.getEventTypeIdGenerator()); } else if (isWildcard && !isOnlyWildcard) { targetType = statementContext.getEventAdapterService().addWrapperType(typeName, selectFromType, properties, true, false); } else { // Some columns selected, use the types of the columns if (hasProperties && !isOnlyWildcard) { Map<String, Object> compiledProperties = EventTypeUtility.compileMapTypeProperties(properties, statementContext.getEventAdapterService()); EventUnderlyingType representation = EventRepresentationUtil.getRepresentation(statementContext.getAnnotations(), servicesContext.getConfigSnapshot(), CreateSchemaDesc.AssignedType.NONE); if (representation == EventUnderlyingType.MAP) { targetType = statementContext.getEventAdapterService().addNestableMapType(typeName, compiledProperties, null, false, false, false, true, false); } else if (representation == EventUnderlyingType.OBJECTARRAY) { targetType = statementContext.getEventAdapterService().addNestableObjectArrayType(typeName, compiledProperties, null, false, false, false, true, false, false, null); } else if (representation == EventUnderlyingType.AVRO) { targetType = statementContext.getEventAdapterService().addAvroType(typeName, compiledProperties, false, false, false, true, false, statementContext.getAnnotations(), null, statementContext.getStatementName(), statementContext.getEngineURI()); } else { throw new IllegalStateException("Unrecognized representation " + representation); } } else { // No columns selected, no wildcard, use the type as is or as a wrapped type if (selectFromType instanceof ObjectArrayEventType) { ObjectArrayEventType objectArrayEventType = (ObjectArrayEventType) selectFromType; targetType = statementContext.getEventAdapterService().addNestableObjectArrayType(typeName, objectArrayEventType.getTypes(), null, false, false, false, true, false, false, null); } else if (selectFromType instanceof AvroSchemaEventType) { AvroSchemaEventType avroSchemaEventType = (AvroSchemaEventType) selectFromType; ConfigurationEventTypeAvro avro = new ConfigurationEventTypeAvro(); avro.setAvroSchema(avroSchemaEventType.getSchema()); targetType = statementContext.getEventAdapterService().addAvroType(typeName, avro, false, false, false, true, false); } else if (selectFromType instanceof MapEventType) { MapEventType mapType = (MapEventType) selectFromType; targetType = statementContext.getEventAdapterService().addNestableMapType(typeName, mapType.getTypes(), null, false, false, false, true, false); } else if (selectFromType instanceof BeanEventType) { BeanEventType beanType = (BeanEventType) selectFromType; targetType = statementContext.getEventAdapterService().addBeanTypeByName(typeName, beanType.getUnderlyingType(), true); } else { Map<String, Object> addOnTypes = new HashMap<String, Object>(); targetType = statementContext.getEventAdapterService().addWrapperType(typeName, selectFromType, addOnTypes, true, false); } } } FilterSpecCompiled filter = new FilterSpecCompiled(targetType, typeName, new List[0], null); return new Pair<FilterSpecCompiled, SelectClauseSpecRaw>(filter, newSelectClauseSpecRaw); } private static List<NamedWindowSelectedProps> compileLimitedSelect(SelectClauseSpecRaw spec, String eplStatement, EventType singleType, String selectFromTypeName, String engineURI, ExprEvaluatorContext exprEvaluatorContext, EngineImportService engineImportService, EventAdapterService eventAdapterService, String statementName, int statementId, Annotation[] annotations, StatementExtensionSvcContext statementExtensionSvcContext) { List<NamedWindowSelectedProps> selectProps = new LinkedList<NamedWindowSelectedProps>(); StreamTypeService streams = new StreamTypeServiceImpl(new EventType[]{singleType}, new String[]{"stream_0"}, new boolean[]{false}, engineURI, false); ExprValidationContext validationContext = new ExprValidationContext(streams, engineImportService, statementExtensionSvcContext, null, null, null, null, exprEvaluatorContext, eventAdapterService, statementName, statementId, annotations, null, false, false, false, false, null, false); for (SelectClauseElementRaw raw : spec.getSelectExprList()) { if (!(raw instanceof SelectClauseExprRawSpec)) { continue; } SelectClauseExprRawSpec exprSpec = (SelectClauseExprRawSpec) raw; ExprNode validatedExpression; try { validatedExpression = ExprNodeUtility.getValidatedSubtree(ExprNodeOrigin.SELECT, exprSpec.getSelectExpression(), validationContext); } catch (ExprValidationException e) { throw new EPStatementException(e.getMessage(), e, eplStatement); } // determine an element name if none assigned String asName = exprSpec.getOptionalAsName(); if (asName == null) { asName = ExprNodeUtility.toExpressionStringMinPrecedenceSafe(validatedExpression); } // check for fragments EventType fragmentType = null; if ((validatedExpression instanceof ExprIdentNode) && (!(singleType instanceof NativeEventType))) { ExprIdentNode identNode = (ExprIdentNode) validatedExpression; FragmentEventType fragmentEventType = singleType.getFragmentType(identNode.getFullUnresolvedName()); if ((fragmentEventType != null) && (!fragmentEventType.isNative())) { fragmentType = fragmentEventType.getFragmentType(); } } NamedWindowSelectedProps validatedElement = new NamedWindowSelectedProps(validatedExpression.getExprEvaluator().getType(), asName, fragmentType); selectProps.add(validatedElement); } return selectProps; } private static void registerNonPropertyGetters(FilterSpecCompiled filter, String statementName, FilterNonPropertyRegisteryService filterNonPropertyRegisteryService) { for (FilterSpecParam[] row : filter.getParameters()) { for (FilterSpecParam col : row) { if (col.getLookupable().isNonPropertyGetter()) { filterNonPropertyRegisteryService.registerNonPropertyExpression(statementName, filter.getFilterForEventType(), col.getLookupable()); } } } } protected void destroyInternal(EPStatementDesc desc) { try { // fire the statement stop if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().qEngineManagementStmtStop(EPStatementState.DESTROYED, services.getEngineURI(), desc.getEpStatement().getStatementId(), desc.getEpStatement().getName(), desc.getEpStatement().getText(), services.getSchedulingService().getTime()); } // remove referenced event types services.getStatementEventTypeRefService().removeReferencesStatement(desc.getEpStatement().getName()); // remove the named window lock services.getNamedWindowMgmtService().removeNamedWindowLock(desc.getEpStatement().getName()); // remove any pattern subexpression counts if (services.getPatternSubexpressionPoolSvc() != null) { services.getPatternSubexpressionPoolSvc().removeStatement(desc.getEpStatement().getName()); } // remove any match-recognize counts if (services.getMatchRecognizeStatePoolEngineSvc() != null) { services.getMatchRecognizeStatePoolEngineSvc().removeStatement(desc.getEpStatement().getName()); } EPStatementSPI statement = desc.getEpStatement(); if (statement.getState() == EPStatementState.STARTED) { // fire the statement stop desc.getStatementContext().getStatementStopService().fireStatementStopped(); // invoke start-provided stop method EPStatementStopMethod stopMethod = desc.getStopMethod(); statement.setParentView(null); stopMethod.stop(); } // call any destroy method that is registered for the statement: this destroy context partitions but not metadata if (desc.getDestroyMethod() != null) { desc.getDestroyMethod().destroy(); } // remove referenced non-property getters (after stop to allow lookup of these during stop) services.getFilterNonPropertyRegisteryService().removeReferencesStatement(desc.getEpStatement().getName()); // remove referenced variables (after stop to allow lookup of these during stop) services.getStatementVariableRefService().removeReferencesStatement(desc.getEpStatement().getName()); // destroy named window consumers services.getNamedWindowConsumerMgmtService().destroy(desc.getStatementContext().getStatementName()); long timeLastStateChange = services.getSchedulingService().getTime(); statement.setCurrentState(EPStatementState.DESTROYED, timeLastStateChange); stmtNameToStmtMap.remove(statement.getName()); stmtNameToIdMap.remove(statement.getName()); stmtIdToDescMap.remove(statement.getStatementId()); if (!epServiceProvider.isDestroyed()) { ((EPRuntimeSPI) epServiceProvider.getEPRuntime()).clearCaches(); } dispatchStatementLifecycleEvent(new StatementLifecycleEvent(statement, StatementLifecycleEvent.LifecycleEventType.STATECHANGE)); } finally { if (InstrumentationHelper.ENABLED) { InstrumentationHelper.get().aEngineManagementStmtStop(); } } } public void dispatchStatementLifecycleEvent(StatementLifecycleEvent theEvent) { for (StatementLifecycleObserver observer : observers) { observer.observe(theEvent); } } /** * Statement information. */ public static class EPStatementDesc { private final EPStatementSPI epStatement; private final EPStatementStartMethod startMethod; private final StatementContext statementContext; private EPStatementStopMethod stopMethod; private EPStatementDestroyMethod destroyMethod; /** * Ctor. * * @param epStatement the statement * @param startMethod the start method * @param statementContext statement context */ public EPStatementDesc(EPStatementSPI epStatement, EPStatementStartMethod startMethod, StatementContext statementContext) { this.epStatement = epStatement; this.startMethod = startMethod; this.statementContext = statementContext; } /** * Returns the statement. * * @return statement. */ public EPStatementSPI getEpStatement() { return epStatement; } /** * Returns the start method. * * @return start method */ public EPStatementStartMethod getStartMethod() { return startMethod; } /** * Returns the stop method. * * @return stop method */ public EPStatementStopMethod getStopMethod() { return stopMethod; } /** * Sets the stop method. * * @param stopMethod to set */ public void setStopMethod(EPStatementStopMethod stopMethod) { this.stopMethod = stopMethod; } /** * Returns the statement context. * * @return statement context */ public StatementContext getStatementContext() { return statementContext; } /** * Set method to call when destroyed. * * @param destroyMethod method */ public void setDestroyMethod(EPStatementDestroyMethod destroyMethod) { this.destroyMethod = destroyMethod; } /** * Return destroy method. * * @return method. */ public EPStatementDestroyMethod getDestroyMethod() { return destroyMethod; } } }