/* * 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 java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.teiid.api.exception.query.QueryMetadataException; import org.teiid.api.exception.query.QueryPlannerException; import org.teiid.core.TeiidComponentException; import org.teiid.core.id.IDGenerator; import org.teiid.core.util.Assertion; import org.teiid.query.analysis.AnalysisRecord; import org.teiid.query.metadata.QueryMetadataInterface; import org.teiid.query.metadata.TempMetadataAdapter; import org.teiid.query.optimizer.capabilities.CapabilitiesFinder; import org.teiid.query.processor.ProcessorPlan; import org.teiid.query.processor.proc.*; import org.teiid.query.processor.proc.CreateCursorResultSetInstruction.Mode; import org.teiid.query.sql.lang.Command; import org.teiid.query.sql.lang.DynamicCommand; import org.teiid.query.sql.lang.SPParameter; import org.teiid.query.sql.lang.StoredProcedure; import org.teiid.query.sql.proc.*; import org.teiid.query.sql.symbol.ElementSymbol; import org.teiid.query.sql.symbol.Expression; import org.teiid.query.sql.symbol.GroupSymbol; import org.teiid.query.sql.symbol.Reference; import org.teiid.query.sql.visitor.CommandCollectorVisitor; import org.teiid.query.sql.visitor.GroupCollectorVisitor; import org.teiid.query.util.CommandContext; /** * <p> This prepares an {@link org.teiid.query.processor.proc.ProcedurePlan ProcedurePlan} from * a CreateUpdateProcedureCommand {@link org.teiid.query.sql.proc.CreateProcedureCommand CreateUpdateProcedureCommand}. * </p> */ public final class ProcedurePlanner implements CommandPlanner { /** * <p>Produce a ProcessorPlan for the CreateUpdateProcedureCommand on the current node * of the CommandTreeNode, the procedure plan construction involves using the child * processor plans.</p> * @param metadata source of metadata * @param debug whether or not to generate verbose debug output during planning * @return ProcessorPlan This processorPlan is a <code>ProcedurePlan</code> * @throws QueryPlannerException indicating a problem in planning * @throws QueryMetadataException indicating an exception in accessing the metadata * @throws TeiidComponentException indicating an unexpected exception */ public ProcessorPlan optimize(Command procCommand, IDGenerator idGenerator, QueryMetadataInterface metadata, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { boolean debug = analysisRecord.recordDebug(); if(debug) { analysisRecord.println("\n####################################################"); //$NON-NLS-1$ analysisRecord.println("PROCEDURE COMMAND: " + procCommand); //$NON-NLS-1$ } CreateProcedureCommand cupc = Assertion.isInstanceOf(procCommand, CreateProcedureCommand.class, "Wrong command type"); //$NON-NLS-1$ if(debug) { analysisRecord.println("OPTIMIZING SUB-COMMANDS: "); //$NON-NLS-1$ } for (Command command : CommandCollectorVisitor.getCommands(procCommand)) { if (!(command instanceof DynamicCommand)) { command.setProcessorPlan(QueryOptimizer.optimizePlan(command, metadata, idGenerator, capFinder, analysisRecord, context)); } } Block block = cupc.getBlock(); Program programBlock = planBlock(cupc, block, metadata, debug, idGenerator, capFinder, analysisRecord, context); if(debug) { analysisRecord.println("\n####################################################"); //$NON-NLS-1$ } // create plan from program and initialized environment ProcedurePlan plan = new ProcedurePlan(programBlock); plan.setMetadata(metadata); plan.setOutputElements(cupc.getProjectedSymbols()); if(debug) { analysisRecord.println("####################################################"); //$NON-NLS-1$ analysisRecord.println("PROCEDURE PLAN :"+plan); //$NON-NLS-1$ analysisRecord.println("####################################################"); //$NON-NLS-1$ } return plan; } /** * <p> Plan a {@link Block} object, recursively plan each statement in the given block and * add the resulting {@link ProgramInstruction} a new {@link Program} for the block.</p> * @param block The <code>Block</code> to be planned * @param metadata Metadata used during planning * @param childNodes list of CommandTreeNode objects that contain the ProcessorPlans of the child nodes of this procedure * @param debug Boolean determining if procedure plan needs to be printed for debug purposes * @param analysisRecord * @return A Program resulting in the block planning * @throws QueryPlannerException if invalid statement is encountered in the block * @throws QueryMetadataException if there is an error accessing metadata * @throws TeiidComponentException if unexpected error occurs */ private Program planBlock(CreateProcedureCommand parentProcCommand, Block block, QueryMetadataInterface metadata, boolean debug, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { // Generate program and add instructions // this program represents the block on the procedure // instruction in the program would correspond to statements in the block Program programBlock = new Program(block.isAtomic()); programBlock.setLabel(block.getLabel()); // plan each statement in the block planStatements(parentProcCommand, block.getStatements(), metadata, debug, idGenerator, capFinder, analysisRecord, context, programBlock); if (block.getExceptionGroup() != null) { programBlock.setExceptionGroup(block.getExceptionGroup()); if (block.getExceptionStatements() != null) { Program exceptionBlock = new Program(false); planStatements(parentProcCommand, block.getExceptionStatements(), metadata, debug, idGenerator, capFinder, analysisRecord, context, exceptionBlock); programBlock.setExceptionProgram(exceptionBlock); } } return programBlock; } private void planStatements(CreateProcedureCommand parentProcCommand, List<Statement> stmts, QueryMetadataInterface metadata, boolean debug, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context, Program programBlock) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { for (Statement statement : stmts) { Object instruction = planStatement(parentProcCommand, statement, metadata, debug, idGenerator, capFinder, analysisRecord, context); if(instruction instanceof ProgramInstruction){ programBlock.addInstruction((ProgramInstruction)instruction); }else{ //an array of ProgramInstruction ProgramInstruction[] insts = (ProgramInstruction[])instruction; for(int i=0; i<insts.length; i++){ programBlock.addInstruction(insts[i]); } } } } /** * <p> Plan a {@link Statement} object, depending on the type of the statement construct the appropriate * {@link ProgramInstruction} return it to added to a {@link Program}. If the statement references a * <code>Command</code>, it looks up the child CommandTreeNodes to get appropriate child's ProcessrPlan * and uses it for constructing the necessary instruction.</p> * @param statement The statement to be planned * @param metadata Metadata used during planning * @param childNodes list of CommandTreeNode objects that contain the ProcessorPlans of the child nodes of this procedure * @param debug Boolean determining if procedure plan needs to be printed for debug purposes * @param analysisRecord * @return An array containing index of the next child to be accessed and the ProgramInstruction resulting * in the statement planning * @throws QueryPlannerException if invalid statement is encountered * @throws QueryMetadataException if there is an error accessing metadata * @throws TeiidComponentException if unexpected error occurs */ private Object planStatement(CreateProcedureCommand parentProcCommand, Statement statement, QueryMetadataInterface metadata, boolean debug, IDGenerator idGenerator, CapabilitiesFinder capFinder, AnalysisRecord analysisRecord, CommandContext context) throws QueryPlannerException, QueryMetadataException, TeiidComponentException { int stmtType = statement.getType(); // object array containing updated child index and the process instruction //Object array[] = new Object[2]; // program instr resulting in planning this statement Object instruction = null; switch(stmtType) { case Statement.TYPE_ASSIGNMENT: case Statement.TYPE_DECLARE: case Statement.TYPE_RETURN: { AssignmentStatement assignStmt = (AssignmentStatement)statement; if (stmtType == Statement.TYPE_RETURN && assignStmt.getVariable() == null) { return new ReturnInstruction(); } AssignmentInstruction assignInstr = new AssignmentInstruction(); instruction = assignInstr; assignInstr.setVariable(assignStmt.getVariable()); Expression asigExpr = assignStmt.getExpression(); assignInstr.setExpression(asigExpr); if(debug) { analysisRecord.println("\tASSIGNMENT\n" + statement); //$NON-NLS-1$ } if (stmtType == Statement.TYPE_RETURN) { ReturnInstruction ri = new ReturnInstruction(); instruction = new ProgramInstruction[] {assignInstr, ri}; } break; } case Statement.TYPE_ERROR: { ErrorInstruction error = new ErrorInstruction(); instruction = error; RaiseStatement res = (RaiseStatement)statement; Expression asigExpr = res.getExpression(); error.setExpression(asigExpr); error.setWarning(res.isWarning()); if(debug) { analysisRecord.println("\tRAISE STATEMENT:\n" + statement); //$NON-NLS-1$ } break; } case Statement.TYPE_COMMAND: { CommandStatement cmdStmt = (CommandStatement) statement; Command command = cmdStmt.getCommand(); ProcessorPlan commandPlan = cmdStmt.getCommand().getProcessorPlan(); if (command.getType() == Command.TYPE_DYNAMIC){ instruction = new ExecDynamicSqlInstruction(parentProcCommand,((DynamicCommand)command), metadata, idGenerator, capFinder, ((DynamicCommand)command).getIntoGroup() == null && cmdStmt.isReturnable() && parentProcCommand.returnsResultSet()); }else{ CreateCursorResultSetInstruction cursor = new CreateCursorResultSetInstruction(null, commandPlan, getMode(parentProcCommand, cmdStmt, command)); if (cursor.getMode() == Mode.HOLD) { for (GroupSymbol gs : GroupCollectorVisitor.getGroupsIgnoreInlineViews(command, false)) { if (gs.isTempTable() && metadata.getModelID(gs.getMetadataID()) == TempMetadataAdapter.TEMP_MODEL) { cursor.setUsesLocalTemp(true); break; } } } instruction = cursor; //handle stored procedure calls if (command.getType() == Command.TYPE_STORED_PROCEDURE) { StoredProcedure sp = (StoredProcedure)command; if (sp.isCallableStatement()) { Map<ElementSymbol, ElementSymbol> assignments = new LinkedHashMap<ElementSymbol, ElementSymbol>(); for (SPParameter param : sp.getParameters()) { if (param.getParameterType() == SPParameter.RESULT_SET || param.getParameterType() == SPParameter.IN) { continue; } Expression expr = param.getExpression(); if (expr instanceof Reference) { expr = ((Reference)expr).getExpression(); } ElementSymbol symbol = null; if (expr instanceof ElementSymbol) { symbol = (ElementSymbol)expr; } assignments.put(param.getParameterSymbol(), symbol); } ((CreateCursorResultSetInstruction)instruction).setProcAssignments(assignments); } } } if(debug) { analysisRecord.println("\tCOMMAND STATEMENT:\n " + statement); //$NON-NLS-1$ analysisRecord.println("\t\tSTATEMENT COMMAND PROCESS PLAN:\n " + commandPlan); //$NON-NLS-1$ } break; } case Statement.TYPE_IF: { IfStatement ifStmt = (IfStatement)statement; Program ifProgram = planBlock(parentProcCommand, ifStmt.getIfBlock(), metadata, debug, idGenerator, capFinder, analysisRecord, context); Program elseProgram = null; if(ifStmt.hasElseBlock()) { elseProgram = planBlock(parentProcCommand, ifStmt.getElseBlock(), metadata, debug, idGenerator, capFinder, analysisRecord, context); } instruction = new IfInstruction(ifStmt.getCondition(), ifProgram, elseProgram); if(debug) { analysisRecord.println("\tIF STATEMENT:\n" + statement); //$NON-NLS-1$ } break; } case Statement.TYPE_BREAK: case Statement.TYPE_CONTINUE: case Statement.TYPE_LEAVE: { BranchingStatement bs = (BranchingStatement)statement; if(debug) { analysisRecord.println("\t" + statement); //$NON-NLS-1$ } instruction = new BranchingInstruction(bs); break; } case Statement.TYPE_LOOP: { LoopStatement loopStmt = (LoopStatement)statement; if(debug) { analysisRecord.println("\tLOOP STATEMENT:\n" + statement); //$NON-NLS-1$ } String rsName = loopStmt.getCursorName(); ProcessorPlan commandPlan = loopStmt.getCommand().getProcessorPlan(); Program loopProgram = planBlock(parentProcCommand, loopStmt.getBlock(), metadata, debug, idGenerator, capFinder, analysisRecord, context); instruction = new LoopInstruction(loopProgram, rsName, commandPlan, loopStmt.getLabel()); break; } case Statement.TYPE_WHILE: { WhileStatement whileStmt = (WhileStatement)statement; Program whileProgram = planBlock(parentProcCommand, whileStmt.getBlock(), metadata, debug, idGenerator, capFinder, analysisRecord, context); if(debug) { analysisRecord.println("\tWHILE STATEMENT:\n" + statement); //$NON-NLS-1$ } instruction = new WhileInstruction(whileProgram, whileStmt.getCondition(), whileStmt.getLabel()); break; } case Statement.TYPE_COMPOUND: { Block block = (Block)statement; instruction = new BlockInstruction(planBlock(parentProcCommand, block, metadata, debug, idGenerator, capFinder, analysisRecord, context)); break; } default: throw new AssertionError("Error while planning update procedure, unknown statement type encountered: " + statement); //$NON-NLS-1$ } return instruction; } private Mode getMode(CreateProcedureCommand parentProcCommand, CommandStatement cmdStmt, Command command) { if (!command.returnsResultSet()&&!(command instanceof StoredProcedure)) { return Mode.UPDATE; } if (parentProcCommand.returnsResultSet()&&cmdStmt.isReturnable()&&cmdStmt.getCommand().returnsResultSet()) { return Mode.HOLD; } return Mode.NOHOLD; } } // END CLASS