/* * 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; import org.teiid.api.exception.query.QueryMetadataException; import org.teiid.api.exception.query.QueryPlannerException; import org.teiid.api.exception.query.QueryResolverException; import org.teiid.core.TeiidComponentException; import org.teiid.core.TeiidProcessingException; import org.teiid.core.TeiidRuntimeException; import org.teiid.core.id.IDGenerator; import org.teiid.dqp.internal.process.PreparedPlan; import org.teiid.metadata.FunctionMethod.Determinism; import org.teiid.metadata.Procedure; import org.teiid.query.QueryPlugin; import org.teiid.query.analysis.AnalysisRecord; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.metadata.TempCapabilitiesFinder; import org.teiid.query.metadata.TempMetadataAdapter; import org.teiid.query.metadata.TempMetadataID; import org.teiid.query.metadata.TempMetadataStore; import org.teiid.query.optimizer.capabilities.CapabilitiesFinder; import org.teiid.query.optimizer.capabilities.SourceCapabilities.Capability; import org.teiid.query.optimizer.relational.RelationalPlanner; import org.teiid.query.optimizer.relational.rules.CapabilitiesUtil; import org.teiid.query.optimizer.xml.XMLPlanner; import org.teiid.query.processor.ProcessorPlan; import org.teiid.query.resolver.QueryResolver; import org.teiid.query.rewriter.QueryRewriter; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.Insert; import org.teiid.query.sql.lang.Query; import org.teiid.query.sql.proc.CreateProcedureCommand; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.util.CommandContext; /** * <p>This Class produces a ProcessorPlan object (a plan for query execution) from a * user's command and a source of metadata.</p> */ public class QueryOptimizer { private static final CommandPlanner XML_PLANNER = new XMLPlanner(); private static final CommandPlanner PROCEDURE_PLANNER = new ProcedurePlanner(); private static final CommandPlanner BATCHED_UPDATE_PLANNER = new BatchedUpdatePlanner(); private static final CommandPlanner DDL_PLANNER = new DdlPlanner(); private static final CommandPlanner SOURCE_EVENT_PLANNER = new SourceTriggerActionPlanner(); // Can't construct private QueryOptimizer() {} public static ProcessorPlan optimizePlan(Command command, QueryMetadataInterface metadata, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws QueryMetadataException, TeiidComponentException, QueryPlannerException { if (analysisRecord == null) { analysisRecord = new AnalysisRecord(false, false); } if (context == null) { context = new CommandContext(); } if (!(capFinder instanceof TempCapabilitiesFinder)) { capFinder = new TempCapabilitiesFinder(capFinder); } boolean debug = analysisRecord.recordDebug(); if (!(metadata instanceof TempMetadataAdapter)) { metadata = new TempMetadataAdapter(metadata, new TempMetadataStore()); } if (context.getMetadata() == null) { context.setMetadata(metadata); } // Create an ID generator that can be used for all plans to generate unique data node IDs if(idGenerator == null) { idGenerator = new IDGenerator(); } if(debug) { analysisRecord.println("\n----------------------------------------------------------------------------"); //$NON-NLS-1$ analysisRecord.println("OPTIMIZE: \n" + command); //$NON-NLS-1$ } if (command instanceof Insert) { Insert insert = (Insert)command; if (insert.isUpsert()) { //if not supported or there are trigger actions, then rewrite as a procedure //we do this here since we're changing the command type. //TODO: we could push this back into the rewrite, but it will need to be capabilities aware GroupSymbol group = insert.getGroup(); Object modelId = metadata.getModelID(group.getMetadataID()); boolean supportsUpsert = CapabilitiesUtil.supports(Capability.UPSERT, modelId, metadata, capFinder); if (!supportsUpsert) { try { command = QueryRewriter.rewriteAsUpsertProcedure(insert, metadata, context); } catch (TeiidProcessingException e) { throw new QueryPlannerException(e); } if(debug) { analysisRecord.println("\n----------------------------------------------------------------------------"); //$NON-NLS-1$ analysisRecord.println("OPTIMIZE UPSERT PROCEDURE: \n" + command); //$NON-NLS-1$ } } } } ProcessorPlan result = null; switch (command.getType()) { case Command.TYPE_UPDATE_PROCEDURE: CreateProcedureCommand cupc = (CreateProcedureCommand)command; if (cupc.getUpdateType() != Command.TYPE_UNKNOWN || cupc.getVirtualGroup() == null) { //row update procedure or anon block result = planProcedure(command, metadata, idGenerator, capFinder, analysisRecord, context); } else { Object pid = cupc.getVirtualGroup().getMetadataID(); if (pid instanceof TempMetadataID) { TempMetadataID tid = (TempMetadataID)pid; if (tid.getOriginalMetadataID() != null) { pid = tid.getOriginalMetadataID(); } } String fullName = metadata.getFullName(pid); fullName = "procedure cache:" + fullName; //$NON-NLS-1$ PreparedPlan pp = context.getPlan(fullName); if (pp == null) { Determinism determinismLevel = context.resetDeterminismLevel(); try { CommandContext clone = context.clone(); ProcessorPlan plan = planProcedure(command, metadata, idGenerator, capFinder, analysisRecord, clone); //note that this is not a full prepared plan. It is not usable by user queries. if (pid instanceof Procedure) { clone.accessedPlanningObject(pid); } pp = new PreparedPlan(); pp.setPlan(plan, clone); context.putPlan(fullName, pp, context.getDeterminismLevel()); } finally { context.setDeterminismLevel(determinismLevel); } } result = pp.getPlan().clone(); for (Object id : pp.getAccessInfo().getObjectsAccessed()) { context.accessedPlanningObject(id); } } break; case Command.TYPE_BATCHED_UPDATE: result = BATCHED_UPDATE_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context); break; case Command.TYPE_ALTER_PROC: case Command.TYPE_ALTER_TRIGGER: case Command.TYPE_ALTER_VIEW: result = DDL_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context); break; case Command.TYPE_SOURCE_EVENT: result = SOURCE_EVENT_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context); break; default: try { if (command.getType() == Command.TYPE_QUERY && command instanceof Query && QueryResolver.isXMLQuery((Query)command, metadata)) { result = XML_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context); } else { RelationalPlanner planner = new RelationalPlanner(); planner.initialize(command, idGenerator, metadata, capFinder, analysisRecord, context); result = planner.optimize(command); } } catch (QueryResolverException e) { throw new TeiidRuntimeException(QueryPlugin.Event.TEIID30245, e); } } if(debug) { analysisRecord.println("\n----------------------------------------------------------------------------"); //$NON-NLS-1$ analysisRecord.println("OPTIMIZATION COMPLETE:"); //$NON-NLS-1$ analysisRecord.println("PROCESSOR PLAN:\n" + result); //$NON-NLS-1$ analysisRecord.println("============================================================================"); //$NON-NLS-1$ } return result; } private static ProcessorPlan planProcedure(Command command, QueryMetadataInterface metadata, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws TeiidComponentException, QueryPlannerException, QueryMetadataException { ProcessorPlan result; try { command = QueryRewriter.rewrite(command, metadata, context); } catch (TeiidProcessingException e) { throw new QueryPlannerException(e); } result = PROCEDURE_PLANNER.optimize(command, idGenerator, metadata, capFinder, analysisRecord, context); return result; } }