/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.query.optimizer.xml; import java.util.*; import org.teiid.api.exception.query.QueryMetadataException; import org.teiid.api.exception.query.QueryPlannerException; import org.teiid.api.exception.query.QueryResolverException; import org.teiid.client.plan.Annotation.Priority; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidRuntimeException; import org.teiid.query.QueryPlugin; import org.teiid.query.mapping.relational.QueryNode; import org.teiid.query.mapping.xml.MappingBaseNode; import org.teiid.query.mapping.xml.MappingDocument; import org.teiid.query.mapping.xml.MappingSourceNode; import org.teiid.query.mapping.xml.MappingVisitor; import org.teiid.query.mapping.xml.Navigator; import org.teiid.query.mapping.xml.ResultSetInfo; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.metadata.TempMetadataAdapter; import org.teiid.query.metadata.TempMetadataID; import org.teiid.query.metadata.TempMetadataStore; import org.teiid.query.optimizer.QueryOptimizer; import org.teiid.query.optimizer.relational.rules.NewCalculateCostUtil; import org.teiid.query.processor.ProcessorPlan; import org.teiid.query.processor.relational.AccessNode; import org.teiid.query.processor.relational.RelationalNode; import org.teiid.query.processor.relational.RelationalPlan; import org.teiid.query.resolver.QueryResolver; import org.teiid.query.resolver.util.ResolverUtil; import org.teiid.query.sql.lang.*; import org.teiid.query.sql.symbol.Constant; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.sql.symbol.Reference; import org.teiid.query.sql.visitor.ElementCollectorVisitor; import org.teiid.query.sql.visitor.GroupsUsedByElementsVisitor; import org.teiid.query.sql.visitor.PredicateCollectorVisitor; import org.teiid.query.sql.visitor.StaticSymbolMappingVisitor; public class XMLQueryPlanner { private static final class MappingSourceNodeFinder extends MappingVisitor { private final GroupSymbol gs; MappingSourceNode msn; private MappingSourceNodeFinder(GroupSymbol gs) { this.gs = gs; } @Override public void visit(MappingSourceNode element) { if (element.getAliasResultName() == null && element.getResultSetInfo().getResultSetName().equalsIgnoreCase(gs.getNonCorrelationName())) { msn = element; setAbort(true); } } } static void prePlanQueries(MappingDocument doc, final XMLPlannerEnvironment planEnv) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { MappingVisitor queryPlanVisitor = new MappingVisitor() { public void visit(MappingBaseNode baseNode) { try { // first if there are any explicit staging tables plan them first List<String> stagingTables = baseNode.getStagingTables(); for (String tableName : stagingTables) { planStagingTable(tableName, planEnv); } // now if this is of they source node plan; all other nodes // do not need query planning. if (baseNode instanceof MappingSourceNode) { planQueries((MappingSourceNode)baseNode, planEnv); } } catch (Exception e) { throw new TeiidRuntimeException(e); } } }; planWalk(doc, queryPlanVisitor); } static void optimizeQueries(MappingDocument doc, final XMLPlannerEnvironment planEnv) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { final boolean hasExplicitStagingTables = planEnv.hasExplicitStagingTables(); final Map<Object, Object> reverseMap = new HashMap<Object, Object>(); for (Map.Entry<Object, Object> entry : planEnv.getStagingTableIds().entrySet()) { reverseMap.put(entry.getValue(), entry.getKey()); } MappingVisitor queryPlanVisitor = new MappingVisitor() { public void visit(MappingSourceNode sourceNode) { try { ResultSetInfo rsInfo = sourceNode.getResultSetInfo(); Query command = (Query)rsInfo.getCommand(); prepareQuery(sourceNode, planEnv, command); Command cmd = QueryUtil.rewriteQuery(command, planEnv.getGlobalMetadata(), planEnv.context); // Plan the result set. ProcessorPlan queryPlan = optimizePlan(cmd, planEnv); /* * Since Designer does not provide us with additional metadata we check the query plans * to see if we can improve staging table performance (which can be an issue with large staging tables). * * This is a somewhat low level optimization since it introspects the plans rather than looking * at the original XML structure. * * There are several cases here for optimizing staging table access. * * 1. if the staging table is used in a subquery - we'll ignore that case for now * in theory that would be similar to the logic below or could be pushed to the accessnode to detect * bind eligible equi criteria against a temp table and issue an add index * 2. TODO: if the staging table is used in a join which implies an order by clause or a parent join * - the join could be performed repeatedly and thus we would need a covering index to speed up performance * 3. if the staging table access is filtered by input set criteria - this should be * the most common case. In theory this analysis could be done at a higher level since we are basically * looking at how the input set maps to the staging table - however that can be complicated if the * staging table is used in a join and the projected column used in the criteria is transitively * related to the staging table. * * TODO: the staging plans created for non-global auto staged results should be checked as well, since their * execution can be repeated - although at a lesser factor */ if (hasExplicitStagingTables && !sourceNode.isRootSourceNode() && queryPlan instanceof RelationalPlan && !rsInfo.isAutoStaged()) { RelationalNode node = ((RelationalPlan)queryPlan).getRootNode(); optimizeStagingTableAccess(node); } rsInfo.setPlan(queryPlan); } catch (Exception e) { throw new TeiidRuntimeException(e); } } private void optimizeStagingTableAccess(RelationalNode node) throws QueryMetadataException, TeiidComponentException { checkNode(node); for (RelationalNode child : node.getChildren()) { if (child != null) { optimizeStagingTableAccess(child); } } } private void checkNode(RelationalNode node) throws TeiidComponentException, QueryMetadataException, AssertionError { if (!(node instanceof AccessNode)) { return; } AccessNode anode = (AccessNode)node; if (!(anode.getCommand() instanceof Query)) { return; } Query query = (Query)anode.getCommand(); if (query.getFrom() == null || query.getFrom().getClauses().size() != 1 || !(query.getFrom().getClauses().get(0) instanceof UnaryFromClause)) { return; } UnaryFromClause ufc = (UnaryFromClause)query.getFrom().getClauses().get(0); Object id = reverseMap.get(ufc.getGroup().getMetadataID()); if (id == null) { return; } String name = planEnv.getGlobalMetadata().getFullName(id); ResultSetInfo info = planEnv.getStagingTableResultsInfo(name); //this is a staging table access List<ElementSymbol> keyCols = new ArrayList<ElementSymbol>(2); if (query.getCriteria() != null) { Criteria crit = query.getCriteria(); for (Criteria part : Criteria.separateCriteriaByAnd(crit)) { if (!(part instanceof CompareCriteria)) { continue; } CompareCriteria cc = (CompareCriteria)part; if (cc.getOperator() != CompareCriteria.EQ) { continue; } if (!(cc.getLeftExpression() instanceof ElementSymbol) || !(cc.getRightExpression() instanceof Reference)) { continue; } keyCols.add((ElementSymbol) cc.getLeftExpression()); } if (!keyCols.isEmpty()) { //info.addFkColumns(keyCols); return; //assume that filtering is the most important operation } } //TODO //if (anode.getParent() != null && anode.getParent() instanceof JoinNode) { //} } }; planWalk(doc, queryPlanVisitor); } private static void planWalk(MappingDocument doc, MappingVisitor visitor) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { try { Navigator walker = new Navigator(true, visitor); doc.acceptVisitor(walker); } catch (TeiidRuntimeException e) { if (e.getCause() instanceof QueryPlannerException) { throw (QueryPlannerException)e.getCause(); } else if (e.getCause() instanceof QueryMetadataException) { throw (QueryMetadataException)e.getCause(); } else if (e.getCause() instanceof TeiidComponentException) { throw (TeiidComponentException)e.getCause(); } else { throw e; } } } static void planQueries(final MappingSourceNode sourceNode, XMLPlannerEnvironment planEnv) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { ResultSetInfo rsInfo = sourceNode.getResultSetInfo(); // Create sql: SELECT * FROM rsName Query rsQuery = (Query)rsInfo.getCommand(); // add user order by to base query rsQuery.setOrderBy(rsInfo.getOrderBy()); // add user criteria to base query from model Criteria crit = rsInfo.getCriteria(); try { if(crit != null) { planQueryWithCriteria(sourceNode, planEnv); } } catch (QueryResolverException e) { throw new TeiidComponentException(QueryPlugin.Event.TEIID30294, e); } if (rsInfo.getUserRowLimit() != -1) { int limit = rsInfo.getUserRowLimit(); if (rsInfo.exceptionOnRowlimit()) { limit++; } rsQuery.setLimit(new Limit(null, new Constant(limit))); } // this query is not eligible for staging; proceed normally. rsInfo.setCommand(rsQuery); } static ProcessorPlan optimizePlan(Command query, XMLPlannerEnvironment planEnv) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { TempMetadataAdapter metadata = planEnv.getGlobalMetadata(); ProcessorPlan plan = QueryOptimizer.optimizePlan(query, metadata, planEnv.idGenerator, planEnv.capFinder, planEnv.analysisRecord, planEnv.context); return plan; } static void prepareQuery(MappingSourceNode sourceNode, XMLPlannerEnvironment planEnv, QueryCommand rsQuery) throws TeiidComponentException, QueryResolverException { Collection<GroupSymbol> externalGroups = getExternalGroups(sourceNode); rsQuery.setExternalGroupContexts(new GroupContext(null, externalGroups)); QueryResolver.resolveCommand(rsQuery, planEnv.getGlobalMetadata()); } private static Collection<GroupSymbol> getExternalGroups(MappingSourceNode sourceNode) { Collection<GroupSymbol> externalGroups = new HashSet<GroupSymbol>(); MappingSourceNode parentSource = sourceNode.getParentSourceNode(); while (parentSource != null) { externalGroups.add(new GroupSymbol(parentSource.getActualResultSetName())); parentSource = parentSource.getParentSourceNode(); } return externalGroups; } /** * The Criteria Source nodes are source nodes underneath the context Node. */ private static boolean getResultSets(MappingSourceNode contextNode, Set<MappingSourceNode> criteriaSourceNodes, LinkedHashSet<MappingSourceNode> allResultSets) { boolean singleParentage = true; for (Iterator<MappingSourceNode> i = criteriaSourceNodes.iterator(); i.hasNext();) { MappingSourceNode node = i.next(); List<MappingSourceNode> rsStack = getResultSetStack(contextNode, node); if (allResultSets.containsAll(rsStack)) { continue; } if (!rsStack.containsAll(allResultSets)) { singleParentage = false; } allResultSets.addAll(rsStack); } return singleParentage; } private static LinkedList<MappingSourceNode> getResultSetStack(MappingSourceNode contextNode, MappingBaseNode node) { LinkedList<MappingSourceNode> rsStack = new LinkedList<MappingSourceNode>(); while (node != null && node != contextNode) { if (node instanceof MappingSourceNode) { rsStack.add(0, (MappingSourceNode)node); } node = node.getParentNode(); } return rsStack; } private static void planQueryWithCriteria(MappingSourceNode contextNode, XMLPlannerEnvironment planEnv) throws QueryPlannerException, TeiidComponentException, QueryMetadataException, QueryResolverException { Map symbolMap = new HashMap(); ResultSetInfo rsInfo = contextNode.getResultSetInfo(); // this list of all the source nodes below the context, which are directly ro indirectly // involved in the criteria LinkedHashSet<MappingSourceNode> resultSets = new LinkedHashSet<MappingSourceNode>(); boolean singleParentage = getResultSets(contextNode, rsInfo.getCriteriaResultSets(), resultSets); Query contextQuery = null; if (rsInfo.isCriteriaRaised()) { contextQuery = (Query)QueryUtil.getQueryFromQueryNode(rsInfo.getResultSetName(), planEnv); String inlineViewName = planEnv.getAliasName(rsInfo.getResultSetName()); updateSymbolMap(symbolMap, rsInfo.getResultSetName(), inlineViewName, planEnv.getGlobalMetadata()); } else { contextQuery = (Query)rsInfo.getCommand(); } Query currentQuery = contextQuery; for (MappingSourceNode rsNode : resultSets) { ResultSetInfo childRsInfo = rsNode.getResultSetInfo(); QueryNode planNode = QueryUtil.getQueryNode(childRsInfo.getResultSetName(), planEnv.getGlobalMetadata()); Command command = QueryUtil.getQuery(childRsInfo.getResultSetName(), planNode, planEnv); String inlineViewName = planEnv.getAliasName(childRsInfo.getResultSetName()); updateSymbolMap(symbolMap, childRsInfo.getResultSetName(), inlineViewName, planEnv.getGlobalMetadata()); // check if the criteria has been raised, if it is then we can update this as a join. if (childRsInfo.hasInputSet() && childRsInfo.isCriteriaRaised()) { Query transformationQuery = (Query) command; SubqueryFromClause sfc = (SubqueryFromClause)transformationQuery.getFrom().getClauses().get(0); Criteria joinCriteria = ((Query)childRsInfo.getCommand()).getCriteria(); joinCriteria = (Criteria)joinCriteria.clone(); //update the from clause FromClause clause = currentQuery.getFrom().getClauses().remove(0); JoinPredicate join = new JoinPredicate(clause, sfc, JoinType.JOIN_LEFT_OUTER, Criteria.separateCriteriaByAnd(joinCriteria)); currentQuery.getFrom().addClause(join); currentQuery.getSelect().setDistinct(true); continue; } if (!singleParentage) { throw new QueryPlannerException(QueryPlugin.Event.TEIID30295, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30295, rsInfo.getCriteria())); } Query subQuery = QueryUtil.wrapQuery(new SubqueryFromClause(inlineViewName, command), inlineViewName); currentQuery.setCriteria(Criteria.combineCriteria(currentQuery.getCriteria(), new ExistsCriteria(subQuery))); currentQuery = subQuery; } Criteria userCrit = (Criteria)rsInfo.getCriteria().clone(); currentQuery.setCriteria(Criteria.combineCriteria(currentQuery.getCriteria(), userCrit)); StaticSymbolMappingVisitor.mapSymbols(contextQuery, symbolMap); if (rsInfo.isCriteriaRaised()) { //if allowing ancestor bindings, we need to update the bindings for the query node... prepareQuery(contextNode, planEnv, contextQuery); QueryUtil.rewriteQuery(contextQuery, planEnv.getGlobalMetadata(), planEnv.context); //selectively replace correlated references with their actual element symbols List<Reference> bindings = QueryUtil.getReferences(contextQuery); QueryNode modifiedNode = new QueryNode(null); modifiedNode.setCommand(contextQuery); for (Iterator<Reference> i = bindings.iterator(); i.hasNext();) { Reference ref = i.next(); modifiedNode.addBinding(ref.getExpression().toString()); } GroupSymbol groupSymbol = QueryUtil.createResolvedGroup(rsInfo.getResultSetName(), planEnv.getGlobalMetadata()); planEnv.addQueryNodeToMetadata(groupSymbol.getMetadataID(), modifiedNode); } for (Criteria crit : PredicateCollectorVisitor.getPredicates(userCrit)) { handleXmlSubqueries(planEnv, crit); } } private static void handleXmlSubqueries(XMLPlannerEnvironment planEnv, Criteria userCrit) throws QueryPlannerException { if (!(userCrit instanceof SubqueryContainer<?>)) { return; } SubqueryContainer<?> subquery = (SubqueryContainer<?>)userCrit; if (!(subquery.getCommand() instanceof Query)) { return; } Query q = (Query)subquery.getCommand(); if (q.getFrom() == null || q.getCriteria() == null) { return; } List<GroupSymbol> groups = q.getFrom().getGroups(); if (groups.size() != 1) { return; } final GroupSymbol gs = groups.get(0); LinkedHashSet<GroupSymbol> allGroups = new LinkedHashSet<GroupSymbol>(); allGroups.add(gs); //TODO: this group should have been marked as xml, or could attempt this step prior to place user criteria if (planEnv.getGlobalMetadata().getMetadataStore().getTempGroupID(gs.getNonCorrelationName()) == null) { return; } MappingSourceNode parentMsn = findMappingSourceNode(planEnv, gs); for (Criteria crit : PredicateCollectorVisitor.getPredicates(q.getCriteria())) { Collection<ElementSymbol> elems = ElementCollectorVisitor.getElements(crit, false); Collection<GroupSymbol> critGroups = new LinkedList<GroupSymbol>(); for (ElementSymbol elementSymbol : elems) { if (!elementSymbol.isExternalReference()) { critGroups.add(elementSymbol.getGroupSymbol()); } } for (GroupSymbol groupSymbol : critGroups) { if (allGroups.contains(groupSymbol)) { continue; } MappingSourceNode childMsn = findMappingSourceNode(planEnv, groupSymbol); while (childMsn != parentMsn) { if (childMsn == null) { throw new QueryPlannerException(QueryPlugin.Event.TEIID30296, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30296, crit, parentMsn)); } if (!childMsn.getResultSetInfo().isCriteriaRaised()) { throw new QueryPlannerException(QueryPlugin.Event.TEIID30297, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30297, crit, childMsn)); } Query parentQuery = (Query)childMsn.getResultSetInfo().getCommand(); if (parentQuery.getCriteria() != null && allGroups.addAll(GroupsUsedByElementsVisitor.getGroups(parentQuery.getCriteria()))) { q.setCriteria(Criteria.combineCriteria(q.getCriteria(), (Criteria) parentQuery.getCriteria().clone())); } childMsn = childMsn.getParentSourceNode(); } } q.getFrom().getClauses().clear(); for (GroupSymbol groupSymbol : allGroups) { q.getFrom().addClause(new UnaryFromClause(groupSymbol)); } handleXmlSubqueries(planEnv, crit); } } private static MappingSourceNode findMappingSourceNode( XMLPlannerEnvironment planEnv, final GroupSymbol gs) { MappingSourceNodeFinder finder = new MappingSourceNodeFinder(gs); planEnv.mappingDoc.acceptVisitor(new Navigator(true, finder)); return finder.msn; } static void updateSymbolMap(Map symbolMap, String oldGroup, final String newGroup, QueryMetadataInterface metadata) throws QueryResolverException,QueryMetadataException,TeiidComponentException { GroupSymbol oldGroupSymbol = new GroupSymbol(oldGroup); ResolverUtil.resolveGroup(oldGroupSymbol, metadata); HashSet<ElementSymbol> projectedElements = new HashSet<ElementSymbol>(ResolverUtil.resolveElementsInGroup(oldGroupSymbol, metadata)); symbolMap.putAll(QueryUtil.createSymbolMap(oldGroupSymbol, newGroup, projectedElements)); } /** * Currently any virtual/physical table can be planned as a staged table. A Staged * table only means that is has been preped to load the data into a temp table; when the other * transformations use this staged table, they will be redirected to use the temp table instead. * however note that it is still up to the plan to make sure the temp table is loaded. * @param groupName * @param planEnv * @return {@link GroupSymbol} the temptable which has been planned. * @throws QueryResolverException */ static void planStagingTable(String groupName, XMLPlannerEnvironment planEnv) throws QueryPlannerException, QueryMetadataException, TeiidComponentException, QueryResolverException { ResultSetInfo rsInfo = planEnv.getStagingTableResultsInfo(groupName); FromClause fromClause = new UnaryFromClause(new GroupSymbol(groupName)); Query query = QueryUtil.wrapQuery(fromClause, groupName); if (rsInfo.getCriteria() != null) { query.setCriteria(rsInfo.getCriteria()); } planStagaingQuery(false, groupName, groupName, query, planEnv); } /** * This method takes given query and adds the "into" symbol to query and resolves it * and registers it with planner env as the staging table. Also, builds a unload query * to unload the staging table. * @throws QueryResolverException */ static boolean planStagaingQuery(boolean implicit, String srcGroupName, String stageGroupName, Query query, XMLPlannerEnvironment planEnv) throws QueryPlannerException, QueryMetadataException, TeiidComponentException, QueryResolverException { GroupSymbol srcGroup = QueryUtil.createResolvedGroup(srcGroupName, planEnv.getGlobalMetadata()); String intoGroupName = getTempTableName(stageGroupName); GroupSymbol intoGroupSymbol = new GroupSymbol(intoGroupName); query.setInto(new Into(intoGroupSymbol)); QueryResolver.resolveCommand(query, planEnv.getGlobalMetadata()); Command cmd = QueryUtil.rewriteQuery(query, planEnv.getGlobalMetadata(), planEnv.context); ProcessorPlan plan = null; boolean debug = planEnv.analysisRecord.recordAnnotations(); try { // register with env plan = optimizePlan(cmd, planEnv); } catch (QueryPlannerException e) { if (implicit) { if (debug) { planEnv.analysisRecord.addAnnotation("XML Planning", "Planning failded for staging otable " + srcGroupName + " due to " + e.getMessage(), "Implicit staging table will not be used", Priority.LOW); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } return false; } throw e; } if (debug) { planEnv.analysisRecord.addAnnotation("XML Planning", "Planning succeeded for staging of " + srcGroupName, (implicit?"Implicit ":"") + "staging table will be used", Priority.MEDIUM); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ } int cardinality = QueryMetadataInterface.UNKNOWN_CARDINALITY; if (plan instanceof RelationalPlan) { RelationalPlan relationalPlan = (RelationalPlan)plan; RelationalNode root = relationalPlan.getRootNode(); //since the root will be a project into node, get the cost from its child if (root.getChildren()[0] != null) { root = root.getChildren()[0]; } Number planCardinality = root.getEstimateNodeCardinality(); if (planCardinality == null || planCardinality.floatValue() == NewCalculateCostUtil.UNKNOWN_VALUE) { //don't stage unknown cost without criteria if (implicit && query.getCriteria() == null) { return false; } } else if (planCardinality.floatValue() < planEnv.context.getProcessorBatchSize()) { //the staging table seems small cardinality = planCardinality.intValue(); } else if (implicit) { return false; } } // inject the new metadata id into global metadata store for rest of the queries to use. TempMetadataStore tempMetadata = query.getTemporaryMetadata(); if (tempMetadata != null && !tempMetadata.getData().isEmpty()) { planEnv.addToGlobalMetadata(tempMetadata.getData()); } ResultSetInfo rsInfo = planEnv.getStagingTableResultsInfo(stageGroupName); rsInfo.setCommand(cmd); rsInfo.setPlan(plan); //set the cardinality on the temp group. TempMetadataID intoGroupID = (TempMetadataID)intoGroupSymbol.getMetadataID(); intoGroupID.setCardinality(cardinality); // add the materialization hook for the staged table to original one. planEnv.addStagingTable(srcGroup.getMetadataID(), intoGroupID); // plan the unload of the staging table String unloadName = planEnv.unLoadResultName(stageGroupName); ResultSetInfo rsUnloadInfo = planEnv.getStagingTableResultsInfo(unloadName); Command command = wrapStagingTableUnloadQuery(intoGroupSymbol); QueryResolver.resolveCommand(command, planEnv.getGlobalMetadata()); command = QueryUtil.rewriteQuery(command, planEnv.getGlobalMetadata(), planEnv.context); plan = optimizePlan(command, planEnv); rsUnloadInfo.setCommand(command); rsUnloadInfo.setPlan(plan); return true; } public static String getTempTableName(String stageGroupName) { String intoGroupName = "#"+stageGroupName.replace('.', '_'); //$NON-NLS-1$ return intoGroupName; } /** * This builds a command in the following form; If staging table name is "FOO" * the command built is "Delete FROM #FOO" */ private static Command wrapStagingTableUnloadQuery(GroupSymbol intoGroupSymbol) { Drop drop = new Drop(); drop.setTable(intoGroupSymbol); return drop; } }