protected OrderByEvaluator<Collection<Order>, Object> compileOrderBy(OrderBy orderBy) {
    final LinkedHashMap<String, Boolean> orders = new LinkedHashMap<String, Boolean>();

    for (OrderByElement idOrder : orderBy.getOrders()) {
      if (!(idOrder.getIdentifier() instanceof Field)) {
        throw new IllegalArgumentException("Only field identifier supported by order by operator.");
      }
      String idName = idOrder.getIdentifier().getName();
      if (INSTANCE_STATUS_FIELD.equals(idName)) {
        if (orderBy.getOrders().size() > 1) {
          // TODO throw appropriate exception
          throw new RuntimeException(
              "Status field should be used alone in <order by> construction.");
        }
        orderByStatus = true;
        orderByStatusDesc = idOrder.getType() == OrderByType.DESC;
        return null;
      }
      String dbField = getDBField(idName);

      orders.put(dbField, idOrder.getType() == null || idOrder.getType() == OrderByType.ASC);
    }

    return new OrderByEvaluator<Collection<Order>, Object>() {
      public Collection<Order> evaluate(Object paramValue) {
        Collection<Order> hibernateOrders = new ArrayList<Order>(orders.size());
        for (Map.Entry<String, Boolean> order : orders.entrySet()) {
          hibernateOrders.add(
              order.getValue() ? Order.asc(order.getKey()) : Order.desc(order.getKey()));
        }
        return hibernateOrders;
      }
    };
  }
  public <T> List<T> findByNamedQuery(SearchParameters sp) {
    if (sp == null || !sp.hasNamedQuery()) {
      throw new IllegalArgumentException(
          "searchParameters must be non null and must have a namedQuery");
    }

    Query query = entityManager.createNamedQuery(sp.getNamedQuery());
    String queryString = getQueryString(query);

    // append order by if needed
    if (queryString != null && sp.hasOrders()) {
      // create the sql restriction clausis
      StringBuilder orderClausis = new StringBuilder("order by ");
      boolean first = true;
      for (OrderBy orderBy : sp.getOrders()) {
        if (!first) {
          orderClausis.append(", ");
        }
        orderClausis.append(orderBy.getColumn());
        orderClausis.append(orderBy.isOrderDesc() ? " desc" : " asc");
        first = false;
      }

      if (log.isDebugEnabled()) {
        log.debug("appending: [" + orderClausis.toString() + "] to " + queryString);
      }

      query = recreateQuery(query, queryString + " " + orderClausis.toString());
    }

    // pagination
    if (sp.getFirstResult() >= 0) {
      query.setFirstResult(sp.getFirstResult());
    }
    if (sp.getMaxResults() > 0) {
      query.setMaxResults(sp.getMaxResults());
    }

    // named parameters
    setQueryParameters(query, sp);

    // execute
    @SuppressWarnings("unchecked")
    List<T> result = (List<T>) query.getResultList();

    if (result != null && log.isDebugEnabled()) {
      log.debug(sp.getNamedQuery() + " returned a List of size: " + result.size());
    }

    return result;
  }
 /**
  * please use #setOrderBy instead
  *
  * @param column
  * @return
  */
 @Deprecated
 public SearchTemplate setOrderDesc(boolean desc) {
   int orderBySize = this.orderBys.size();
   if (orderBySize == 0) {
     this.orderBys.add(new OrderBy("", desc ? DESC : ASC));
   } else if (orderBySize == 1) {
     OrderBy orderBy = this.orderBys.get(0);
     this.orderBys.clear();
     this.orderBys.add(new OrderBy(orderBy.getColumn(), desc ? DESC : ASC));
   } else {
     throw new IllegalStateException("Please use addOderBy");
   }
   return this;
 }
 /**
  * please use #setOrderBy instead
  *
  * @param column
  * @return
  */
 @Deprecated
 public SearchTemplate setOrderBy(String column) {
   int orderBySize = this.orderBys.size();
   if (orderBySize == 0) {
     this.orderBys.add(new OrderBy(column));
   } else if (orderBySize == 1) {
     OrderBy orderBy = this.orderBys.get(0);
     this.orderBys.clear();
     this.orderBys.add(new OrderBy(column, orderBy.getDirection()));
   } else {
     throw new IllegalStateException("Please use addOderBy");
   }
   return this;
 }
 @Override
 public StorageResults visit(OrderBy orderBy) {
   TypedExpression field = orderBy.getExpression();
   if (field instanceof Field) {
     FieldMetadata fieldMetadata = ((Field) field).getFieldMetadata();
     SortField sortField =
         new SortField(
             fieldMetadata.getName(),
             getSortType(fieldMetadata),
             orderBy.getDirection() == OrderBy.Direction.DESC);
     query.setSort(new Sort(sortField));
     return null;
   } else {
     throw new NotImplementedException(
         "No support for order by for full text search on non-field.");
   }
 }
Пример #6
0
  private static String getSelectString(final Class<?> klass, final SelectFrom from) {
    final String columnString = getColumnString(klass);
    final String fromString = getFromString(klass, from);

    final String selectString = String.format("SELECT %s FROM (%s)", columnString, fromString);

    final StringBuilder selectBuilder = new StringBuilder();
    selectBuilder.append(selectString);

    final GroupBy groupBy = klass.getAnnotation(GroupBy.class);
    if (groupBy != null) {
      selectBuilder.append(String.format(" GROUP BY %s", groupBy.value()));
    }

    final OrderBy orderBy = klass.getAnnotation(OrderBy.class);
    if (orderBy != null) {
      selectBuilder.append(String.format(" ORDER BY %s", orderBy.value()));
    }

    return selectBuilder.toString();
  }
Пример #7
0
  @SuppressWarnings("rawtypes")
  public static List<Object> performSearch(
      Condition where, OrderBy orderby, String cacheName, InfinispanConnection conn)
      throws TranslatorException {

    QueryBuilder qb = getQueryBuilder(cacheName, conn);

    if (orderby != null) {
      List<SortSpecification> sss = orderby.getSortSpecifications();
      for (SortSpecification spec : sss) {
        Expression exp = spec.getExpression();
        Column mdIDElement = ((ColumnReference) exp).getMetadataObject();
        SortOrder so = SortOrder.ASC;
        if (spec.getOrdering().name().equalsIgnoreCase(SortOrder.DESC.name())) {
          so = SortOrder.DESC;
        }
        qb = qb.orderBy(mdIDElement.getSourceName(), so);
      }
    }

    FilterConditionContext fcc = buildQueryFromWhereClause(where, qb, null);

    List<Object> results = null;

    Query query = null;
    if (fcc != null) {
      query = fcc.toBuilder().build();
      results = query.list();

      if (results == null) {
        return Collections.emptyList();
      }

    } else if (orderby != null) {
      query = qb.build();
      results = query.list();
      if (results == null) {
        return Collections.emptyList();
      }

    } else {
      results = new ArrayList<Object>();
      RemoteCache<?, Object> c = (RemoteCache<?, Object>) conn.getCache();
      for (Object id : c.keySet()) {
        results.add(c.get(id));
      }
    }

    return results;
  }
Пример #8
0
  protected RelationalNode convertNode(PlanNode node)
      throws TeiidComponentException, TeiidProcessingException {

    RelationalNode processNode = null;

    switch (node.getType()) {
      case NodeConstants.Types.PROJECT:
        GroupSymbol intoGroup = (GroupSymbol) node.getProperty(NodeConstants.Info.INTO_GROUP);
        if (intoGroup != null) {
          try {
            Insert insert = (Insert) node.getFirstChild().getProperty(Info.VIRTUAL_COMMAND);
            List<ElementSymbol> allIntoElements = insert.getVariables();

            Object groupID = intoGroup.getMetadataID();
            Object modelID = metadata.getModelID(groupID);
            String modelName = metadata.getFullName(modelID);
            if (metadata.isVirtualGroup(groupID)) {
              InsertPlanExecutionNode ipen = new InsertPlanExecutionNode(getID(), metadata);
              ipen.setProcessorPlan(
                  (ProcessorPlan) node.getFirstChild().getProperty(Info.PROCESSOR_PLAN));
              ipen.setReferences(insert.getValues());
              processNode = ipen;
            } else {
              ProjectIntoNode pinode = new ProjectIntoNode(getID());
              pinode.setIntoGroup(intoGroup);
              pinode.setIntoElements(allIntoElements);
              pinode.setModelName(modelName);
              pinode.setConstraint((Criteria) node.getProperty(Info.CONSTRAINT));
              processNode = pinode;
              SourceCapabilities caps = capFinder.findCapabilities(modelName);
              if (caps.supportsCapability(Capability.INSERT_WITH_ITERATOR)) {
                pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.ITERATOR);
              } else if (caps.supportsCapability(Capability.BATCHED_UPDATES)) {
                pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.BATCH);
              } else {
                pinode.setMode(org.teiid.query.processor.relational.ProjectIntoNode.Mode.SINGLE);
              }
            }
          } catch (QueryMetadataException e) {
            throw new TeiidComponentException(QueryPlugin.Event.TEIID30247, e);
          }

        } else {
          List<Expression> symbols = (List) node.getProperty(NodeConstants.Info.PROJECT_COLS);

          ProjectNode pnode = new ProjectNode(getID());
          pnode.setSelectSymbols(symbols);
          processNode = pnode;

          if (node.hasBooleanProperty(Info.HAS_WINDOW_FUNCTIONS)) {
            WindowFunctionProjectNode wfpn = new WindowFunctionProjectNode(getID());

            // with partial projection the window function may already be pushed, we'll check for
            // that here
            ArrayList<Expression> filtered = new ArrayList<Expression>();
            List<Expression> childSymbols =
                (List) node.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
            for (Expression ex : symbols) {
              ex = SymbolMap.getExpression(ex);
              if (childSymbols.contains(ex)) {
                continue;
              }
              filtered.add(ex);
            }
            Set<WindowFunction> windowFunctions =
                RuleAssignOutputElements.getWindowFunctions(filtered);
            if (!windowFunctions.isEmpty()) {
              // TODO: check for selecting all window functions
              List<Expression> outputElements = new ArrayList<Expression>(windowFunctions);
              // collect the other projected expressions
              for (Expression singleElementSymbol :
                  (List<Expression>) node.getFirstChild().getProperty(Info.OUTPUT_COLS)) {
                outputElements.add(singleElementSymbol);
              }
              wfpn.setElements(outputElements);
              wfpn.init();
              pnode.addChild(wfpn);
            }
          }
        }
        break;

      case NodeConstants.Types.JOIN:
        JoinType jtype = (JoinType) node.getProperty(NodeConstants.Info.JOIN_TYPE);
        JoinStrategyType stype =
            (JoinStrategyType) node.getProperty(NodeConstants.Info.JOIN_STRATEGY);

        JoinNode jnode = new JoinNode(getID());
        jnode.setJoinType(jtype);
        jnode.setLeftDistinct(node.hasBooleanProperty(NodeConstants.Info.IS_LEFT_DISTINCT));
        jnode.setRightDistinct(node.hasBooleanProperty(NodeConstants.Info.IS_RIGHT_DISTINCT));
        List joinCrits = (List) node.getProperty(NodeConstants.Info.JOIN_CRITERIA);
        String depValueSource =
            (String) node.getProperty(NodeConstants.Info.DEPENDENT_VALUE_SOURCE);
        SortOption leftSort = (SortOption) node.getProperty(NodeConstants.Info.SORT_LEFT);
        if (stype == JoinStrategyType.MERGE || stype == JoinStrategyType.ENHANCED_SORT) {
          MergeJoinStrategy mjStrategy = null;
          if (stype.equals(JoinStrategyType.ENHANCED_SORT)) {
            EnhancedSortMergeJoinStrategy esmjStrategy =
                new EnhancedSortMergeJoinStrategy(
                    leftSort, (SortOption) node.getProperty(NodeConstants.Info.SORT_RIGHT));
            esmjStrategy.setSemiDep(node.hasBooleanProperty(Info.IS_SEMI_DEP));
            mjStrategy = esmjStrategy;
          } else {
            mjStrategy =
                new MergeJoinStrategy(
                    leftSort, (SortOption) node.getProperty(NodeConstants.Info.SORT_RIGHT), false);
          }
          jnode.setJoinStrategy(mjStrategy);
          List leftExpressions = (List) node.getProperty(NodeConstants.Info.LEFT_EXPRESSIONS);
          List rightExpressions = (List) node.getProperty(NodeConstants.Info.RIGHT_EXPRESSIONS);
          jnode.setJoinExpressions(leftExpressions, rightExpressions);
          joinCrits = (List) node.getProperty(NodeConstants.Info.NON_EQUI_JOIN_CRITERIA);
        } else if (stype == JoinStrategyType.NESTED_TABLE) {
          NestedTableJoinStrategy ntjStrategy = new NestedTableJoinStrategy();
          jnode.setJoinStrategy(ntjStrategy);
          SymbolMap references = (SymbolMap) node.getProperty(Info.LEFT_NESTED_REFERENCES);
          ntjStrategy.setLeftMap(references);
          references = (SymbolMap) node.getProperty(Info.RIGHT_NESTED_REFERENCES);
          ntjStrategy.setRightMap(references);
        } else {
          NestedLoopJoinStrategy nljStrategy = new NestedLoopJoinStrategy();
          jnode.setJoinStrategy(nljStrategy);
        }
        Criteria joinCrit = Criteria.combineCriteria(joinCrits);
        jnode.setJoinCriteria(joinCrit);

        processNode = jnode;

        jnode.setDependentValueSource(depValueSource);

        break;

      case NodeConstants.Types.ACCESS:
        ProcessorPlan plan = (ProcessorPlan) node.getProperty(NodeConstants.Info.PROCESSOR_PLAN);
        if (plan != null) {

          PlanExecutionNode peNode = null;

          Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.PROCEDURE_CRITERIA);

          if (crit != null) {
            List references = (List) node.getProperty(NodeConstants.Info.PROCEDURE_INPUTS);
            List defaults = (List) node.getProperty(NodeConstants.Info.PROCEDURE_DEFAULTS);

            peNode = new DependentProcedureExecutionNode(getID(), crit, references, defaults);
          } else {
            peNode = new PlanExecutionNode(getID());
          }

          peNode.setProcessorPlan(plan);
          processNode = peNode;

        } else {
          AccessNode aNode = null;
          Command command = (Command) node.getProperty(NodeConstants.Info.ATOMIC_REQUEST);
          Object modelID = node.getProperty(NodeConstants.Info.MODEL_ID);
          if (modelID != null) {
            String fullName = metadata.getFullName(modelID);
            if (!capFinder.isValid(fullName)) {
              // TODO: we ideally want to handle the partial resutls case here differently
              //      by adding a null node / and a source warning
              //      for now it's just as easy to say that the user needs to take steps to
              //      return static capabilities
              SourceCapabilities caps = capFinder.findCapabilities(fullName);
              Exception cause = null;
              if (caps != null) {
                cause = (Exception) caps.getSourceProperty(Capability.INVALID_EXCEPTION);
              }
              throw new QueryPlannerException(
                  QueryPlugin.Event.TEIID30498,
                  cause,
                  QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30498, fullName));
            }
          }
          EvaluatableVisitor ev = null;
          if (node.hasBooleanProperty(NodeConstants.Info.IS_DEPENDENT_SET)) {
            if (command instanceof StoredProcedure) {
              List references = (List) node.getProperty(NodeConstants.Info.PROCEDURE_INPUTS);
              List defaults = (List) node.getProperty(NodeConstants.Info.PROCEDURE_DEFAULTS);
              Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.PROCEDURE_CRITERIA);

              DependentProcedureAccessNode depAccessNode =
                  new DependentProcedureAccessNode(getID(), crit, references, defaults);
              processNode = depAccessNode;
              aNode = depAccessNode;
            } else {
              // create dependent access node
              DependentAccessNode depAccessNode = new DependentAccessNode(getID());

              if (modelID != null) {
                depAccessNode.setPushdown(
                    CapabilitiesUtil.supports(
                        Capability.DEPENDENT_JOIN, modelID, metadata, capFinder));
                depAccessNode.setMaxSetSize(
                    CapabilitiesUtil.getMaxInCriteriaSize(modelID, metadata, capFinder));
                depAccessNode.setMaxPredicates(
                    CapabilitiesUtil.getMaxDependentPredicates(modelID, metadata, capFinder));
                depAccessNode.setUseBindings(
                    CapabilitiesUtil.supports(
                        Capability.DEPENDENT_JOIN_BINDINGS, modelID, metadata, capFinder));
                // TODO: allow the translator to drive this property
                // simplistic check of whether this query is complex to re-execute
                Query query = (Query) command;
                if (query.getGroupBy() != null
                    || query.getFrom().getClauses().size() > 1
                    || !(query.getFrom().getClauses().get(0) instanceof UnaryFromClause)
                    || query.getWith() != null) {
                  depAccessNode.setComplexQuery(true);
                } else {
                  // check to see if there in an index on at least one of the dependent sets
                  Set<GroupSymbol> groups = new HashSet<GroupSymbol>(query.getFrom().getGroups());
                  boolean found = false;
                  for (Criteria crit : Criteria.separateCriteriaByAnd(query.getCriteria())) {
                    if (crit instanceof DependentSetCriteria) {
                      DependentSetCriteria dsc = (DependentSetCriteria) crit;
                      if (NewCalculateCostUtil.getKeyUsed(
                              ElementCollectorVisitor.getElements(dsc.getExpression(), true),
                              groups,
                              metadata,
                              null)
                          != null) {
                        found = true;
                        break;
                      }
                    }
                  }
                  if (!found) {
                    depAccessNode.setComplexQuery(true);
                  }
                }
              }
              processNode = depAccessNode;
              aNode = depAccessNode;
            }
            aNode.setShouldEvaluateExpressions(true);
          } else {

            // create access node
            aNode = new AccessNode(getID());
            processNode = aNode;
          }
          // -- special handling for system tables. currently they cannot perform projection
          try {
            if (command instanceof Query) {
              processNode = correctProjectionInternalTables(node, aNode);
            }
          } catch (QueryMetadataException err) {
            throw new TeiidComponentException(QueryPlugin.Event.TEIID30248, err);
          }
          setRoutingName(aNode, node, command);
          boolean shouldEval = false;
          if (command instanceof Insert) {
            Insert insert = (Insert) command;
            if (insert.getQueryExpression() != null) {
              insert.setQueryExpression(
                  (QueryCommand) aliasCommand(aNode, insert.getQueryExpression(), modelID));
            } else {
              for (int i = 0; i < insert.getValues().size(); i++) {
                Expression ex = (Expression) insert.getValues().get(i);
                if (!CriteriaCapabilityValidatorVisitor.canPushLanguageObject(
                    ex, modelID, metadata, capFinder, analysisRecord)) {
                  // replace with an expression symbol to let the rewriter know that it should be
                  // replaced
                  insert.getValues().set(i, new ExpressionSymbol("x", ex));
                  shouldEval = true;
                }
              }
            }
          } else if (command instanceof QueryCommand) {
            command = aliasCommand(aNode, command, modelID);
          }
          ev = EvaluatableVisitor.needsEvaluation(command, modelID, metadata, capFinder);
          aNode.setShouldEvaluateExpressions(
              ev.requiresEvaluation(EvaluationLevel.PROCESSING) || shouldEval);
          aNode.setCommand(command);
          Map<GroupSymbol, PlanNode> subPlans =
              (Map<GroupSymbol, PlanNode>) node.getProperty(Info.SUB_PLANS);

          // it makes more sense to allow the multisource affect to be elevated above just access
          // nodes
          if (aNode.getModelId() != null && metadata.isMultiSource(aNode.getModelId())) {
            VDBMetaData vdb = context.getVdb();
            aNode.setShouldEvaluateExpressions(true); // forces a rewrite
            aNode.setElements((List) node.getProperty(NodeConstants.Info.OUTPUT_COLS));
            if (node.hasBooleanProperty(Info.IS_MULTI_SOURCE)) {
              Expression ex = rewriteMultiSourceCommand(aNode.getCommand());
              aNode.setConnectorBindingExpression(ex);
              aNode.setMultiSource(true);
            } else {
              String sourceName = (String) node.getProperty(Info.SOURCE_NAME);
              aNode.setConnectorBindingExpression(new Constant(sourceName));
            }
          } else if (subPlans == null) {
            if (!aNode.isShouldEvaluate()) {
              aNode.minimizeProject(command);
            }
            // check if valid to share this with other nodes
            if (ev != null
                && ev.getDeterminismLevel().compareTo(Determinism.COMMAND_DETERMINISTIC) >= 0
                && command.areResultsCachable()) {
              checkForSharedSourceCommand(aNode);
            }
          }
          if (subPlans != null) {
            QueryCommand qc = (QueryCommand) command;
            if (qc.getWith() == null) {
              qc.setWith(new ArrayList<WithQueryCommand>(subPlans.size()));
            }
            Map<GroupSymbol, RelationalPlan> plans =
                new LinkedHashMap<GroupSymbol, RelationalPlan>();
            for (Map.Entry<GroupSymbol, PlanNode> entry : subPlans.entrySet()) {
              RelationalPlan subPlan = convert(entry.getValue());
              List<ElementSymbol> elems =
                  ResolverUtil.resolveElementsInGroup(entry.getKey(), metadata);
              subPlan.setOutputElements(elems);
              plans.put(entry.getKey(), subPlan);
              WithQueryCommand withQueryCommand = new WithQueryCommand(entry.getKey(), elems, null);
              qc.getWith().add(withQueryCommand);
            }
            aNode.setSubPlans(plans);
          }
        }
        break;

      case NodeConstants.Types.SELECT:
        Criteria crit = (Criteria) node.getProperty(NodeConstants.Info.SELECT_CRITERIA);
        if (!node.hasCollectionProperty(Info.OUTPUT_COLS)) {
          // the late optimization to create a dependent join from a subquery introduces
          // criteria that have no output elements set
          // TODO that should be cleaner, but the logic currently expects to be run after join
          // implementation
          // and rerunning assign output elements seems excessive
          node.setProperty(Info.OUTPUT_COLS, node.getFirstChild().getProperty(Info.OUTPUT_COLS));
        }
        SelectNode selnode = new SelectNode(getID());
        selnode.setCriteria(crit);
        // in case the parent was a source
        selnode.setProjectedExpressions(
            (List<Expression>) node.getProperty(NodeConstants.Info.PROJECT_COLS));
        processNode = selnode;

        break;

      case NodeConstants.Types.SORT:
      case NodeConstants.Types.DUP_REMOVE:
        if (node.getType() == NodeConstants.Types.DUP_REMOVE) {
          processNode = new DupRemoveNode(getID());
        } else {
          SortNode sortNode = new SortNode(getID());
          OrderBy orderBy = (OrderBy) node.getProperty(NodeConstants.Info.SORT_ORDER);
          if (orderBy != null) {
            sortNode.setSortElements(orderBy.getOrderByItems());
          }
          if (node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL)) {
            sortNode.setMode(Mode.DUP_REMOVE_SORT);
          }

          processNode = sortNode;
        }
        break;
      case NodeConstants.Types.GROUP:
        GroupingNode gnode = new GroupingNode(getID());
        gnode.setRollup(node.hasBooleanProperty(Info.ROLLUP));
        SymbolMap groupingMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
        gnode.setOutputMapping(groupingMap);
        gnode.setRemoveDuplicates(node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL));
        List<Expression> gCols = (List) node.getProperty(NodeConstants.Info.GROUP_COLS);
        OrderBy orderBy = (OrderBy) node.getProperty(Info.SORT_ORDER);
        if (orderBy == null) {
          if (gCols != null) {
            LinkedHashSet<Expression> exprs = new LinkedHashSet<Expression>();
            for (Expression ex : gCols) {
              exprs.add(SymbolMap.getExpression(ex));
            }
            orderBy =
                new OrderBy(
                    RuleChooseJoinStrategy.createExpressionSymbols(
                        new ArrayList<Expression>(exprs)));
          }
        } else {
          HashSet<Expression> seen = new HashSet<Expression>();
          for (int i = 0; i < gCols.size(); i++) {
            if (i < orderBy.getOrderByItems().size()) {
              OrderByItem orderByItem = orderBy.getOrderByItems().get(i);
              Expression ex = SymbolMap.getExpression(orderByItem.getSymbol());
              if (!seen.add(ex)) {
                continue;
              }
              if (ex instanceof ElementSymbol) {
                ex = groupingMap.getMappedExpression((ElementSymbol) ex);
                orderByItem.setSymbol(new ExpressionSymbol("expr", ex)); // $NON-NLS-1$
              }
            } else {
              orderBy.addVariable(
                  new ExpressionSymbol("expr", gCols.get(i)), OrderBy.ASC); // $NON-NLS-1$
            }
          }
        }
        if (orderBy != null) {
          gnode.setOrderBy(orderBy.getOrderByItems());
        }
        processNode = gnode;
        break;

      case NodeConstants.Types.SOURCE:
        Object source = node.getProperty(NodeConstants.Info.TABLE_FUNCTION);
        if (source instanceof XMLTable) {
          XMLTable xt = (XMLTable) source;
          XMLTableNode xtn = new XMLTableNode(getID());
          // we handle the projection filtering once here rather than repeating the
          // path analysis on a per plan basis
          updateGroupName(node, xt);
          Map<Expression, Integer> elementMap =
              RelationalNode.createLookupMap(xt.getProjectedSymbols());
          List cols = (List) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
          int[] projectionIndexes = RelationalNode.getProjectionIndexes(elementMap, cols);
          ArrayList<XMLColumn> filteredColumns = new ArrayList<XMLColumn>(projectionIndexes.length);
          for (int col : projectionIndexes) {
            filteredColumns.add(xt.getColumns().get(col));
          }
          xt.getXQueryExpression().useDocumentProjection(filteredColumns, analysisRecord);
          xtn.setProjectedColumns(filteredColumns);
          xtn.setTable(xt);
          processNode = xtn;
          break;
        }
        if (source instanceof ObjectTable) {
          ObjectTable ot = (ObjectTable) source;
          ObjectTableNode otn = new ObjectTableNode(getID());
          // we handle the projection filtering once here rather than repeating the
          // path analysis on a per plan basis
          updateGroupName(node, ot);
          Map<Expression, Integer> elementMap =
              RelationalNode.createLookupMap(ot.getProjectedSymbols());
          List<Expression> cols =
              (List<Expression>) node.getProperty(NodeConstants.Info.OUTPUT_COLS);
          int[] projectionIndexes = RelationalNode.getProjectionIndexes(elementMap, cols);
          ArrayList<ObjectColumn> filteredColumns =
              new ArrayList<ObjectColumn>(projectionIndexes.length);
          for (int col : projectionIndexes) {
            filteredColumns.add(ot.getColumns().get(col));
          }
          otn.setProjectedColumns(filteredColumns);
          otn.setTable(ot);
          processNode = otn;
          break;
        }
        if (source instanceof TextTable) {
          TextTableNode ttn = new TextTableNode(getID());
          TextTable tt = (TextTable) source;
          updateGroupName(node, tt);
          ttn.setTable(tt);
          processNode = ttn;
          break;
        }
        if (source instanceof ArrayTable) {
          ArrayTableNode atn = new ArrayTableNode(getID());
          ArrayTable at = (ArrayTable) source;
          updateGroupName(node, at);
          atn.setTable(at);
          processNode = atn;
          break;
        }
        SymbolMap symbolMap = (SymbolMap) node.getProperty(NodeConstants.Info.SYMBOL_MAP);
        if (symbolMap != null) {
          PlanNode child = node.getLastChild();

          if (child.getType() == NodeConstants.Types.PROJECT
              || child.getType() == NodeConstants.Types.SELECT) {
            // update the project cols based upon the original output
            child.setProperty(
                NodeConstants.Info.PROJECT_COLS, child.getProperty(NodeConstants.Info.OUTPUT_COLS));
          }
          child.setProperty(
              NodeConstants.Info.OUTPUT_COLS, node.getProperty(NodeConstants.Info.OUTPUT_COLS));
        }
        return null;
      case NodeConstants.Types.SET_OP:
        Operation setOp = (Operation) node.getProperty(NodeConstants.Info.SET_OPERATION);
        boolean useAll = ((Boolean) node.getProperty(NodeConstants.Info.USE_ALL)).booleanValue();
        if (setOp == Operation.UNION) {
          RelationalNode unionAllNode = new UnionAllNode(getID());

          if (useAll) {
            processNode = unionAllNode;
          } else {
            boolean onlyDupRemoval = node.hasBooleanProperty(NodeConstants.Info.IS_DUP_REMOVAL);
            if (onlyDupRemoval) {
              processNode = new DupRemoveNode(getID());
            } else {
              SortNode sNode = new SortNode(getID());
              sNode.setMode(Mode.DUP_REMOVE_SORT);
              processNode = sNode;
            }

            unionAllNode.setElements((List) node.getProperty(NodeConstants.Info.OUTPUT_COLS));
            processNode.addChild(unionAllNode);
          }
        } else {
          JoinNode joinAsSet = new JoinNode(getID());
          joinAsSet.setJoinStrategy(
              new MergeJoinStrategy(SortOption.SORT_DISTINCT, SortOption.SORT_DISTINCT, true));
          // If we push these sorts, we will have to enforce null order, since nulls are equal here
          List leftExpressions =
              (List) node.getFirstChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
          List rightExpressions =
              (List) node.getLastChild().getProperty(NodeConstants.Info.OUTPUT_COLS);
          joinAsSet.setJoinType(
              setOp == Operation.EXCEPT ? JoinType.JOIN_ANTI_SEMI : JoinType.JOIN_SEMI);
          joinAsSet.setJoinExpressions(leftExpressions, rightExpressions);
          processNode = joinAsSet;
        }

        break;

      case NodeConstants.Types.TUPLE_LIMIT:
        Expression rowLimit = (Expression) node.getProperty(NodeConstants.Info.MAX_TUPLE_LIMIT);
        Expression offset = (Expression) node.getProperty(NodeConstants.Info.OFFSET_TUPLE_COUNT);
        LimitNode ln = new LimitNode(getID(), rowLimit, offset);
        ln.setImplicit(node.hasBooleanProperty(Info.IS_IMPLICIT_LIMIT));
        processNode = ln;
        break;

      case NodeConstants.Types.NULL:
        processNode = new NullNode(getID());
        break;

      default:
        throw new QueryPlannerException(
            QueryPlugin.Event.TEIID30250,
            QueryPlugin.Util.gs(
                QueryPlugin.Event.TEIID30250, NodeConstants.getNodeTypeString(node.getType())));
    }

    if (processNode != null) {
      processNode = prepareToAdd(node, processNode);
    }

    return processNode;
  }
 @Override
 public StorageResults visit(Select select) {
   // TMDM-4654: Checks if entity has a composite PK.
   Set<ComplexTypeMetadata> compositeKeyTypes = new HashSet<ComplexTypeMetadata>();
   // TMDM-7496: Search should include references to reused types
   Collection<ComplexTypeMetadata> types =
       new HashSet<ComplexTypeMetadata>(select.accept(new SearchTransitiveClosure()));
   for (ComplexTypeMetadata type : types) {
     if (type.getKeyFields().size() > 1) {
       compositeKeyTypes.add(type);
     }
   }
   if (!compositeKeyTypes.isEmpty()) {
     StringBuilder message = new StringBuilder();
     Iterator it = compositeKeyTypes.iterator();
     while (it.hasNext()) {
       ComplexTypeMetadata compositeKeyType = (ComplexTypeMetadata) it.next();
       message.append(compositeKeyType.getName());
       if (it.hasNext()) {
         message.append(',');
       }
     }
     throw new FullTextQueryCompositeKeyException(message.toString());
   }
   // Removes Joins and joined fields.
   List<Join> joins = select.getJoins();
   if (!joins.isEmpty()) {
     Set<ComplexTypeMetadata> joinedTypes = new HashSet<ComplexTypeMetadata>();
     for (Join join : joins) {
       joinedTypes.add(join.getRightField().getFieldMetadata().getContainingType());
     }
     for (ComplexTypeMetadata joinedType : joinedTypes) {
       types.remove(joinedType);
     }
     List<TypedExpression> filteredFields = new LinkedList<TypedExpression>();
     for (TypedExpression expression : select.getSelectedFields()) {
       if (expression instanceof Field) {
         FieldMetadata fieldMetadata = ((Field) expression).getFieldMetadata();
         if (joinedTypes.contains(fieldMetadata.getContainingType())) {
           TypeMapping mapping =
               mappings.getMappingFromDatabase(fieldMetadata.getContainingType());
           filteredFields.add(
               new Alias(
                   new StringConstant(StringUtils.EMPTY),
                   mapping.getUser(fieldMetadata).getName()));
         } else {
           filteredFields.add(expression);
         }
       } else {
         filteredFields.add(expression);
       }
     }
     selectedFields.clear();
     selectedFields.addAll(filteredFields);
   }
   // Handle condition
   Condition condition = select.getCondition();
   if (condition == null) {
     throw new IllegalArgumentException("Expected a condition in select clause but got 0.");
   }
   // Create Lucene query (concatenates all sub queries together).
   FullTextSession fullTextSession = Search.getFullTextSession(session);
   Query parsedQuery = select.getCondition().accept(new LuceneQueryGenerator(types));
   // Create Hibernate Search query
   Set<Class> classes = new HashSet<Class>();
   for (ComplexTypeMetadata type : types) {
     String className = ClassCreator.getClassName(type.getName());
     try {
       ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
       classes.add(contextClassLoader.loadClass(className));
     } catch (ClassNotFoundException e) {
       throw new RuntimeException("Could not find class '" + className + "'.", e);
     }
   }
   FullTextQuery fullTextQuery =
       fullTextSession.createFullTextQuery(
           parsedQuery, classes.toArray(new Class<?>[classes.size()]));
   // Very important to leave this null (would disable ability to search across different types)
   fullTextQuery.setCriteriaQuery(null);
   fullTextQuery.setSort(Sort.RELEVANCE); // Default sort (if no order by specified).
   query =
       EntityFinder.wrap(
           fullTextQuery,
           (HibernateStorage) storage,
           session); // ensures only MDM entity objects are returned.
   // Order by
   for (OrderBy current : select.getOrderBy()) {
     current.accept(this);
   }
   // Paging
   Paging paging = select.getPaging();
   paging.accept(this);
   pageSize = paging.getLimit();
   boolean hasPaging = pageSize < Integer.MAX_VALUE;
   if (!hasPaging) {
     return createResults(query.scroll(ScrollMode.FORWARD_ONLY));
   } else {
     return createResults(query.list());
   }
 }
  private SubTreeDescriptor buildTree(
      final int queryPlanDepth,
      final int currentDepth,
      final DbIterator queryPlan,
      final int currentStartPosition,
      final int parentUpperBarStartShift) {
    if (queryPlan == null) return null;

    int adjustDepth = currentDepth == 0 ? -1 : 0;
    SubTreeDescriptor thisNode = new SubTreeDescriptor(null, null);

    if (queryPlan instanceof SeqScan) {
      SeqScan s = (SeqScan) queryPlan;
      String tableName = s.getTableName();
      String alias = s.getAlias();
      // TupleDesc td = s.getTupleDesc();
      if (!tableName.equals(alias)) alias = " " + alias;
      else alias = "";
      thisNode.text = String.format("%1$s(%2$s)", SCAN, tableName + alias);
      if (SCAN.length() / 2 < parentUpperBarStartShift) {
        thisNode.upBarPosition = currentStartPosition + parentUpperBarStartShift;
        thisNode.textStartPosition = thisNode.upBarPosition - SCAN.length() / 2;
      } else {
        thisNode.upBarPosition = currentStartPosition + SCAN.length() / 2;
        thisNode.textStartPosition = currentStartPosition;
      }
      thisNode.width = thisNode.textStartPosition - currentStartPosition + thisNode.text.length();
      int embedHeight = (queryPlanDepth - currentDepth) / 2 - 1;
      thisNode.height = currentDepth + 2 * embedHeight;
      int currentHeight = thisNode.height;
      SubTreeDescriptor parentNode = thisNode;
      for (int i = 0; i < embedHeight; i++) {
        parentNode = new SubTreeDescriptor(parentNode, null);
        parentNode.text = "|";
        parentNode.upBarPosition = thisNode.upBarPosition;
        parentNode.width = thisNode.width;
        parentNode.height = currentHeight - 2;
        parentNode.textStartPosition = thisNode.upBarPosition;
        currentHeight -= 2;
      }
      thisNode = parentNode;
    } else {

      Operator plan = (Operator) queryPlan;
      DbIterator[] children = plan.getChildren();

      if (plan instanceof Join) {
        Join j = (Join) plan;
        TupleDesc td = j.getTupleDesc();
        JoinPredicate jp = j.getJoinPredicate();
        String field1 = td.getFieldName(jp.getField1());
        String field2 = td.getFieldName(jp.getField2() + children[0].getTupleDesc().numFields());
        thisNode.text =
            String.format(
                "%1$s(%2$s),card:%3$d",
                JOIN, field1 + jp.getOperator() + field2, j.getEstimatedCardinality());
        int upBarShift = parentUpperBarStartShift;
        if (JOIN.length() / 2 > parentUpperBarStartShift) upBarShift = JOIN.length() / 2;

        SubTreeDescriptor left =
            buildTree(
                queryPlanDepth,
                currentDepth + adjustDepth + 3,
                children[0],
                currentStartPosition,
                upBarShift);
        SubTreeDescriptor right =
            buildTree(
                queryPlanDepth,
                currentDepth + adjustDepth + 3,
                children[1],
                currentStartPosition + left.width + SPACE.length(),
                0);
        thisNode.upBarPosition = (left.upBarPosition + right.upBarPosition) / 2;
        thisNode.textStartPosition = thisNode.upBarPosition - JOIN.length() / 2;
        thisNode.width =
            Math.max(
                left.width + right.width + SPACE.length(),
                thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
        thisNode.leftChild = left;
        thisNode.rightChild = right;
        thisNode.height = currentDepth;
      } else if (plan instanceof HashEquiJoin) {
        HashEquiJoin j = (HashEquiJoin) plan;
        JoinPredicate jp = j.getJoinPredicate();
        TupleDesc td = j.getTupleDesc();
        String field1 = td.getFieldName(jp.getField1());
        String field2 = td.getFieldName(jp.getField2() + children[0].getTupleDesc().numFields());
        thisNode.text =
            String.format(
                "%1$s(%2$s),card:%3$d",
                HASH_JOIN, field1 + jp.getOperator() + field2, j.getEstimatedCardinality());
        int upBarShift = parentUpperBarStartShift;
        if (HASH_JOIN.length() / 2 > parentUpperBarStartShift) upBarShift = HASH_JOIN.length() / 2;
        SubTreeDescriptor left =
            buildTree(
                queryPlanDepth,
                currentDepth + 3 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        SubTreeDescriptor right =
            buildTree(
                queryPlanDepth,
                currentDepth + 3 + adjustDepth,
                children[1],
                currentStartPosition + left.width + SPACE.length(),
                0);
        thisNode.upBarPosition = (left.upBarPosition + right.upBarPosition) / 2;
        thisNode.textStartPosition = thisNode.upBarPosition - HASH_JOIN.length() / 2;
        thisNode.width =
            Math.max(
                left.width + right.width + SPACE.length(),
                thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
        thisNode.leftChild = left;
        thisNode.rightChild = right;
        thisNode.height = currentDepth;
      } else if (plan instanceof Aggregate) {
        Aggregate a = (Aggregate) plan;
        int upBarShift = parentUpperBarStartShift;
        String alignTxt;
        TupleDesc td = a.getTupleDesc();
        int gfield = a.groupField();

        if (gfield == Aggregator.NO_GROUPING) {
          thisNode.text =
              String.format(
                  "%1$s(%2$s),card:%3$d",
                  a.aggregateOp(), a.aggregateFieldName(), a.getEstimatedCardinality());
          alignTxt = td.getFieldName(00);
        } else {
          thisNode.text =
              String.format(
                  "%1$s(%2$s), %3$s(%4$s),card:%5$d",
                  GROUPBY,
                  a.groupFieldName(),
                  a.aggregateOp(),
                  a.aggregateFieldName(),
                  a.getEstimatedCardinality());
          alignTxt = GROUPBY;
        }
        if (alignTxt.length() / 2 > parentUpperBarStartShift) upBarShift = alignTxt.length() / 2;

        SubTreeDescriptor child =
            buildTree(
                queryPlanDepth,
                currentDepth + 2 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        thisNode.upBarPosition = child.upBarPosition;
        thisNode.textStartPosition = thisNode.upBarPosition - alignTxt.length() / 2;
        thisNode.width =
            Math.max(
                child.width,
                thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
        thisNode.leftChild = child;
        thisNode.height = currentDepth;
      } else if (plan instanceof Filter) {
        Filter f = (Filter) plan;
        Predicate p = f.getPredicate();
        thisNode.text =
            String.format(
                "%1$s(%2$s),card:%3$d",
                SELECT,
                children[0].getTupleDesc().getFieldName(p.getField()) + p.getOp() + p.getOperand(),
                f.getEstimatedCardinality());
        int upBarShift = parentUpperBarStartShift;
        if (SELECT.length() / 2 > parentUpperBarStartShift) upBarShift = SELECT.length() / 2;
        SubTreeDescriptor child =
            buildTree(
                queryPlanDepth,
                currentDepth + 2 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        thisNode.upBarPosition = child.upBarPosition;
        thisNode.textStartPosition = thisNode.upBarPosition - SELECT.length() / 2;
        thisNode.width =
            Math.max(
                child.width,
                thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
        thisNode.leftChild = child;
        thisNode.height = currentDepth;
      } else if (plan instanceof OrderBy) {
        OrderBy o = (OrderBy) plan;
        thisNode.text =
            String.format(
                "%1$s(%2$s),card:%3$d",
                ORDERBY,
                children[0].getTupleDesc().getFieldName(o.getOrderByField()),
                o.getEstimatedCardinality());
        int upBarShift = parentUpperBarStartShift;
        if (ORDERBY.length() / 2 > parentUpperBarStartShift) upBarShift = ORDERBY.length() / 2;
        SubTreeDescriptor child =
            buildTree(
                queryPlanDepth,
                currentDepth + 2 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        thisNode.upBarPosition = child.upBarPosition;
        thisNode.textStartPosition = thisNode.upBarPosition - ORDERBY.length() / 2;
        thisNode.width =
            Math.max(
                child.width,
                thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
        thisNode.leftChild = child;
        thisNode.height = currentDepth;
      } else if (plan instanceof Project) {
        Project p = (Project) plan;
        String fields = "";
        Iterator<TDItem> it = p.getTupleDesc().iterator();
        while (it.hasNext()) fields += it.next().fieldName + ",";
        fields = fields.substring(0, fields.length() - 1);
        thisNode.text =
            String.format("%1$s(%2$s),card:%3$d", PROJECT, fields, p.getEstimatedCardinality());
        int upBarShift = parentUpperBarStartShift;
        if (PROJECT.length() / 2 > parentUpperBarStartShift) upBarShift = PROJECT.length() / 2;
        SubTreeDescriptor child =
            buildTree(
                queryPlanDepth,
                currentDepth + 2 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        thisNode.upBarPosition = child.upBarPosition;
        thisNode.textStartPosition = thisNode.upBarPosition - PROJECT.length() / 2;
        thisNode.width =
            Math.max(
                child.width,
                thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
        thisNode.leftChild = child;
        thisNode.height = currentDepth;
      } else if (plan.getClass()
          .getSuperclass()
          .getSuperclass()
          .getSimpleName()
          .equals("Exchange")) {
        String name = "Exchange";
        int card = 0;
        try {
          name = (String) plan.getClass().getMethod("getName").invoke(plan);
          card = (Integer) plan.getClass().getMethod("getEstimatedCardinality").invoke(plan);
        } catch (Exception e) {
          e.printStackTrace();
        }

        thisNode.text = String.format("%1$s,card:%2$d", name, card);
        int upBarShift = parentUpperBarStartShift;
        if (name.length() / 2 > parentUpperBarStartShift) upBarShift = name.length() / 2;
        SubTreeDescriptor child =
            buildTree(
                queryPlanDepth,
                currentDepth + 2 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        if (child == null) {
          thisNode.upBarPosition = upBarShift;
          thisNode.textStartPosition = thisNode.upBarPosition - name.length() / 2;
          thisNode.width =
              thisNode.textStartPosition + thisNode.text.length() - currentStartPosition;
        } else {
          thisNode.upBarPosition = child.upBarPosition;
          thisNode.textStartPosition = thisNode.upBarPosition - name.length() / 2;
          thisNode.width =
              Math.max(
                  child.width,
                  thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
          thisNode.leftChild = child;
        }
        thisNode.height = currentDepth;
      } else if (plan.getClass().getName().equals("simpledb.Rename")) {
        String newName = null;
        int fieldIdx = 0;
        try {
          newName = (String) plan.getClass().getMethod("newName", (Class<?>[]) null).invoke(plan);
          fieldIdx =
              (Integer) plan.getClass().getMethod("renamedField", (Class<?>[]) null).invoke(plan);
        } catch (Exception e) {
          e.printStackTrace();
        }
        String oldName = plan.getChildren()[0].getTupleDesc().getFieldName(fieldIdx);
        thisNode.text =
            String.format(
                "%1$s,%2$s->%3$s,card:%4$d",
                RENAME, oldName, newName, plan.getEstimatedCardinality());
        int upBarShift = parentUpperBarStartShift;
        if (RENAME.length() / 2 > parentUpperBarStartShift) upBarShift = RENAME.length() / 2;
        SubTreeDescriptor child =
            buildTree(
                queryPlanDepth,
                currentDepth + 2 + adjustDepth,
                children[0],
                currentStartPosition,
                upBarShift);
        if (child == null) {
          thisNode.upBarPosition = upBarShift;
          thisNode.textStartPosition = thisNode.upBarPosition - RENAME.length() / 2;
          thisNode.width =
              thisNode.textStartPosition + thisNode.text.length() - currentStartPosition;
        } else {
          thisNode.upBarPosition = child.upBarPosition;
          thisNode.textStartPosition = thisNode.upBarPosition - RENAME.length() / 2;
          thisNode.width =
              Math.max(
                  child.width,
                  thisNode.textStartPosition + thisNode.text.length() - currentStartPosition);
          thisNode.leftChild = child;
        }
        thisNode.height = currentDepth;
      }
    }
    return thisNode;
  }