context: ///////////////////////////////////////////////////////////////////////////////////////
  // Copyright (C) 2006-2019 Esper Team. All rights reserved.                           /
  // http://esper.codehaus.org                                                          /
  // ---------------------------------------------------------------------------------- /
  // 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.  /
  ///////////////////////////////////////////////////////////////////////////////////////
  
  using System;
  using System.Collections.Generic;
  using System.Reflection;
  
  using com.espertech.esper.common.client;
  using com.espertech.esper.common.@internal.compile.multikey;
  using com.espertech.esper.common.@internal.compile.stage1;
  using com.espertech.esper.common.@internal.compile.stage1.spec;
  using com.espertech.esper.common.@internal.compile.stage2;
  using com.espertech.esper.common.@internal.compile.stage3;
  using com.espertech.esper.common.@internal.context.aifactory.select;
  using com.espertech.esper.common.@internal.epl.expression.core;
  using com.espertech.esper.common.@internal.epl.expression.subquery;
  using com.espertech.esper.common.@internal.epl.expression.table;
  using com.espertech.esper.common.@internal.epl.fafquery.processor;
  using com.espertech.esper.common.@internal.epl.historical.common;
  using com.espertech.esper.common.@internal.epl.join.analyze;
  using com.espertech.esper.common.@internal.epl.join.@base;
  using com.espertech.esper.common.@internal.epl.join.hint;
  using com.espertech.esper.common.@internal.epl.join.querygraph;
  using com.espertech.esper.common.@internal.epl.namedwindow.path;
  using com.espertech.esper.common.@internal.epl.resultset.core;
  using com.espertech.esper.common.@internal.epl.streamtype;
  using com.espertech.esper.common.@internal.epl.subselect;
  using com.espertech.esper.common.@internal.epl.table.strategy;
  using com.espertech.esper.common.@internal.metrics.audit;
  using com.espertech.esper.common.@internal.serde.compiletime.resolve;
  using com.espertech.esper.common.@internal.statement.helper;
  using com.espertech.esper.compat.collections;
  using com.espertech.esper.compat.logging;
  
  namespace com.espertech.esper.common.@internal.epl.fafquery.querymethod
  {
      /// <summary>
      ///     Starts and provides the stop method for EPL statements.
      /// </summary>
      public class FAFQueryMethodSelectDesc
      {
          private static readonly ILog QUERY_PLAN_LOG = LogManager.GetLogger(AuditPath.QUERYPLAN_LOG);
          private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  
          public FAFQueryMethodSelectDesc(
              StatementSpecCompiled statementSpec,
              Compilable compilable,
              StatementRawInfo statementRawInfo,
              StatementCompileTimeServices services)
          {
              Annotations = statementSpec.Annotations;
              ContextName = statementSpec.Raw.OptionalContextName;
  
              var queryPlanLogging = services.Configuration.Common.Logging.IsEnableQueryPlan;
              if (queryPlanLogging) {
                  QUERY_PLAN_LOG.Info("Query plans for Fire-and-forget query '" + compilable.ToEPL() + "'");
              }
  
              HasTableAccess = statementSpec.TableAccessNodes != null && statementSpec.TableAccessNodes.Count > 0;
              foreach (var streamSpec in statementSpec.StreamSpecs) {
                  HasTableAccess |= streamSpec is TableQueryStreamSpec;
              }
  
              HasTableAccess |= StatementLifecycleSvcUtil.IsSubqueryWithTable(
                  statementSpec.SubselectNodes, services.TableCompileTimeResolver);
              IsDistinct = statementSpec.SelectClauseCompiled.IsDistinct;
  
              FAFQueryMethodHelper.ValidateFAFQuery(statementSpec);
  
              var numStreams = statementSpec.StreamSpecs.Length;
              var typesPerStream = new EventType[numStreams];
              var namesPerStream = new string[numStreams];
              var eventTypeNames = new string[numStreams];
              Processors = new FireAndForgetProcessorForge[numStreams];
              ConsumerFilters = new ExprNode[numStreams];
  
              // check context partition use
              if (statementSpec.Raw.OptionalContextName != null) {
                  if (numStreams > 1) {
                      throw new ExprValidationException(
                          "Joins in runtime queries for context partitions are not supported");
                  }
              }
  
              // resolve types and processors
              for (var i = 0; i < numStreams; i++) {
                  var streamSpec = statementSpec.StreamSpecs[i];
                  Processors[i] = FireAndForgetProcessorForgeFactory.ValidateResolveProcessor(streamSpec);
                  if (numStreams > 1 && Processors[i].ContextName != null) {
                      throw new ExprValidationException(
                          "Joins against named windows that are under context are not supported");
                  }
  
                  var streamName = Processors[i].NamedWindowOrTableName;
                  if (streamSpec.OptionalStreamName != null) {
                      streamName = streamSpec.OptionalStreamName;
                  }
  
                  namesPerStream[i] = streamName;
                  typesPerStream[i] = Processors[i].EventTypeRspInputEvents;
                  eventTypeNames[i] = typesPerStream[i].Name;
  
                  IList<ExprNode> consumerFilterExprs;
                  if (streamSpec is NamedWindowConsumerStreamSpec) {
                      var namedSpec = (NamedWindowConsumerStreamSpec) streamSpec;
                      consumerFilterExprs = namedSpec.FilterExpressions;
                  }
                  else {
                      var tableSpec = (TableQueryStreamSpec) streamSpec;
                      consumerFilterExprs = tableSpec.FilterExpressions;
                  }
  
                  ConsumerFilters[i] = ExprNodeUtilityMake.ConnectExpressionsByLogicalAndWhenNeeded(consumerFilterExprs);
              }
  
              // compile filter to optimize access to named window
              var optionalStreamsIfAny = OuterJoinAnalyzer.OptionalStreamsIfAny(statementSpec.Raw.OuterJoinDescList);
              var types = new StreamTypeServiceImpl(
                  typesPerStream,
                  namesPerStream,
                  new bool[numStreams],
                  false,
                  optionalStreamsIfAny);
              var excludePlanHint = ExcludePlanHint.GetHint(types.StreamNames, statementRawInfo, services);
              QueryGraph = new QueryGraphForge(numStreams, excludePlanHint, false);
              if (statementSpec.Raw.WhereClause != null) {
                  for (var i = 0; i < numStreams; i++) {
                      try {
                          var validationContext = new ExprValidationContextBuilder(types, statementRawInfo, services)
                              .WithAllowBindingConsumption(true)
                              .WithIsFilterExpression(true)
                              .Build();
                          var validated = ExprNodeUtilityValidate.GetValidatedSubtree(
                              ExprNodeOrigin.FILTER,
                              statementSpec.Raw.WhereClause,
                              validationContext);
                          FilterExprAnalyzer.Analyze(validated, QueryGraph, false);
                      }
                      catch (Exception ex) {
                          Log.Warn("Unexpected exception analyzing filter paths: " + ex.Message, ex);
                      }
                  }
              }
              
              // handle subselects
              // first we create streams for subselects, if there are any
              var @base = new StatementBaseInfo(compilable, statementSpec, null, statementRawInfo, null);
              var subqueryNamedWindowConsumers = new List<NamedWindowConsumerStreamSpec>();
              SubSelectActivationDesc subSelectActivationDesc = SubSelectHelperActivations.CreateSubSelectActivation(
                  EmptyList<FilterSpecCompiled>.Instance, subqueryNamedWindowConsumers, @base, services);
              IDictionary<ExprSubselectNode, SubSelectActivationPlan> subselectActivation = subSelectActivationDesc.Subselects;
              AdditionalForgeables.AddAll(subSelectActivationDesc.AdditionalForgeables);
  
              SubSelectHelperForgePlan subSelectForgePlan = SubSelectHelperForgePlanner.PlanSubSelect(
                  @base, subselectActivation, namesPerStream, typesPerStream, eventTypeNames, services);
              SubselectForges = subSelectForgePlan.Subselects;
              AdditionalForgeables.AddAll(subSelectForgePlan.AdditionalForgeables);
  
              // obtain result set processor
              var isIStreamOnly = new bool[namesPerStream.Length];
              isIStreamOnly.Fill(true);
              StreamTypeService typeService = new StreamTypeServiceImpl(
                  typesPerStream,
                  namesPerStream,
                  isIStreamOnly,
                  true,
                  optionalStreamsIfAny);
              WhereClause = EPStatementStartMethodHelperValidate.ValidateNodes(
                  statementSpec.Raw,
                  typeService,
                  null,
                  statementRawInfo,
                  services);
  
              var resultSetSpec = new ResultSetSpec(statementSpec);
              ResultSetProcessor = ResultSetProcessorFactoryFactory.GetProcessorPrototype(
                  resultSetSpec,
                  typeService,
                  null,
                  new bool[0],
                  true,
                  null,
                  true,
                  false,
                  statementRawInfo,
                  services);
              AdditionalForgeables.AddAll(ResultSetProcessor.AdditionalForgeables);
  
              // plan table access
              TableAccessForges = ExprTableEvalHelperPlan.PlanTableAccess(statementSpec.Raw.TableExpressions);
  
              // plan joins or simple queries
              if (numStreams > 1) {
                  var streamJoinAnalysisResult = new StreamJoinAnalysisResultCompileTime(numStreams);
                  CompatExtensions.Fill(streamJoinAnalysisResult.NamedWindowsPerStream, (NamedWindowMetaData) null);
  
gt:                 for (var i = 0; i < numStreams; i++) {