Package org.teiid.query.processor.proc

Source Code of org.teiid.query.processor.proc.ProcedurePlan

/*
* 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.processor.proc;

import static org.teiid.query.analysis.AnalysisRecord.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.teiid.api.exception.query.QueryMetadataException;
import org.teiid.api.exception.query.QueryValidatorException;
import org.teiid.client.plan.PlanNode;
import org.teiid.common.buffer.BlockedException;
import org.teiid.common.buffer.BufferManager;
import org.teiid.common.buffer.IndexedTupleSource;
import org.teiid.common.buffer.TupleBatch;
import org.teiid.common.buffer.TupleBuffer;
import org.teiid.common.buffer.TupleSource;
import org.teiid.common.buffer.BufferManager.TupleSourceType;
import org.teiid.core.TeiidComponentException;
import org.teiid.core.TeiidProcessingException;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.Assertion;
import org.teiid.logging.LogManager;
import org.teiid.query.QueryPlugin;
import org.teiid.query.analysis.AnalysisRecord;
import org.teiid.query.metadata.QueryMetadataInterface;
import org.teiid.query.metadata.SupportConstants;
import org.teiid.query.processor.BatchIterator;
import org.teiid.query.processor.CollectionTupleSource;
import org.teiid.query.processor.ProcessorDataManager;
import org.teiid.query.processor.ProcessorPlan;
import org.teiid.query.processor.QueryProcessor;
import org.teiid.query.processor.relational.SubqueryAwareEvaluator;
import org.teiid.query.sql.ProcedureReservedWords;
import org.teiid.query.sql.lang.Criteria;
import org.teiid.query.sql.symbol.ElementSymbol;
import org.teiid.query.sql.symbol.Expression;
import org.teiid.query.sql.symbol.Reference;
import org.teiid.query.sql.util.VariableContext;
import org.teiid.query.tempdata.TempTableStore;
import org.teiid.query.util.CommandContext;

/**
*/
public class ProcedurePlan extends ProcessorPlan {

  private static class CursorState {
    QueryProcessor processor;
    IndexedTupleSource ts;
    List<?> currentRow;
    TupleBuffer resultsBuffer;
  }
 
    private Program originalProgram;

  // State initialized by processor
  private ProcessorDataManager dataMgr;
  private ProcessorDataManager parentDataMrg;
    private BufferManager bufferMgr;
    private int batchSize;

    private boolean done = false;
    private CursorState currentState;

    // Temp state for final results
    private TupleSource finalTupleSource;
    private int beginBatch = 1;
    private List<Object> batchRows;
    private boolean lastBatch = false;
    private LinkedHashMap<ElementSymbol, Expression> params;
    private List<ElementSymbol> outParams;
    private Map<ElementSymbol, Reference> implicitParams;
    private QueryMetadataInterface metadata;

    private Map<String, CursorState> cursorStates = new HashMap<String, CursorState>();

  private static ElementSymbol ROWS_UPDATED =
      new ElementSymbol(ProcedureReservedWords.VARIABLES+"."+ProcedureReservedWords.ROWS_UPDATED); //$NON-NLS-1$

  static ElementSymbol ROWCOUNT =
    new ElementSymbol(ProcedureReservedWords.VARIABLES+"."+ProcedureReservedWords.ROWCOUNT); //$NON-NLS-1$
 
  static {
    ROWS_UPDATED.setType(DataTypeManager.DefaultDataClasses.INTEGER);
    ROWCOUNT.setType(DataTypeManager.DefaultDataClasses.INTEGER);
  }

  private VariableContext currentVarContext;
    private boolean isUpdateProcedure = true;

    private TupleSource lastTupleSource;
   
    private List outputElements;
   
    private TempTableStore tempTableStore;
   
    private LinkedList<Set<String>> tempContext = new LinkedList<Set<String>>();
  private SubqueryAwareEvaluator evaluator;
 
    // Stack of programs, with current program on top
    private Stack<Program> programs = new Stack<Program>();
   
    private boolean evaluatedParams;
   
    private boolean requiresTransaction = true;

    /**
     * Constructor for ProcedurePlan.
     */
    public ProcedurePlan(Program originalProgram) {
      this.originalProgram = originalProgram;
      this.programs.add(originalProgram);
      createVariableContext();
    }
   
    public Program getOriginalProgram() {
    return originalProgram;
  }

    /**
     * @see ProcessorPlan#initialize(ProcessorDataManager, Object)
     */
    public void initialize(CommandContext context, ProcessorDataManager dataMgr, BufferManager bufferMgr) {      
        this.bufferMgr = bufferMgr;
        this.batchSize = bufferMgr.getProcessorBatchSize();
        setContext(context.clone());
        this.dataMgr = dataMgr;
        this.parentDataMrg = dataMgr;
        if (evaluator == null) {
          this.evaluator = new SubqueryAwareEvaluator(Collections.emptyMap(), getDataManager(), getContext(), this.bufferMgr);
        }
    }

    public void reset() {
        super.reset();
        if (evaluator != null) {
          evaluator.reset();
        }
        evaluatedParams = false;
        cursorStates.clear();
        createVariableContext();
        lastTupleSource = null;
       
        done = false;
        currentState = null;

        finalTupleSource = null;
        beginBatch = 1;
        batchRows = null;
        lastBatch = false;

        //reset program stack
        originalProgram.resetProgramCounter();
        this.tempContext.clear();
        programs.clear();
      programs.push(originalProgram);
    LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "ProcedurePlan reset"); //$NON-NLS-1$
    }

    public ProcessorDataManager getDataManager() {
        return this.dataMgr;
    }

    public void open() throws TeiidProcessingException, TeiidComponentException {
      if (!this.evaluatedParams) {
        if (this.outParams != null) {
          for (ElementSymbol param : this.outParams) {
          setParameterValue(param, getCurrentVariableContext(), null);
        }
        }
        if (this.params != null) {
            for (Map.Entry<ElementSymbol, Expression> entry : this.params.entrySet()) {
                ElementSymbol param = entry.getKey();
                Expression expr = entry.getValue();
               
                VariableContext context = getCurrentVariableContext();
                Object value = this.evaluateExpression(expr);
   
                //check constraint
                checkNotNull(param, value);
                setParameterValue(param, context, value);
            }
        }
        if (this.implicitParams != null) {
          for (Map.Entry<ElementSymbol, Reference> entry : this.implicitParams.entrySet()) {
                VariableContext context = getCurrentVariableContext();
                Object value = this.evaluateExpression(entry.getValue());
                context.setValue(entry.getKey(), value);
        }
        }
        tempTableStore = new TempTableStore(getContext().getConnectionID());
        getContext().setTempTableStore(tempTableStore);
      }
      this.evaluatedParams = true;
    }

  private void checkNotNull(ElementSymbol param, Object value)
      throws TeiidComponentException, QueryMetadataException,
      QueryValidatorException {
    if (value == null && !metadata.elementSupports(param.getMetadataID(), SupportConstants.Element.NULL)) {
        throw new QueryValidatorException(QueryPlugin.Util.getString("ProcedurePlan.nonNullableParam", param)); //$NON-NLS-1$
    }
  }

  protected void setParameterValue(ElementSymbol param,
      VariableContext context, Object value) {
    context.setValue(param, value);
  }

    /**
     * @see ProcessorPlan#nextBatch()
     */
    public TupleBatch nextBatch()
        throws TeiidComponentException, TeiidProcessingException, BlockedException {

        // Already returned results?
        if(done) {
            // Already returned all results
            TupleBatch emptyTerminationBatch = new TupleBatch(beginBatch, new List[0]);
            emptyTerminationBatch.setTerminationFlag(true);
            return emptyTerminationBatch;

        }
        // First attempt to process
        if(this.finalTupleSource == null) {
            // Still need to process - this should either
            // throw a BlockedException or return a finalTupleSource
            this.finalTupleSource = processProcedure();
        }

        // Next, attempt to return batches if processing completed
        while(! isBatchFull()) {
            // May throw BlockedException and exit here
            List tuple = this.finalTupleSource.nextTuple();
            if(tuple == null) {
              if (outParams != null) {
                VariableContext vc = getCurrentVariableContext();
                List<Object> paramTuple = Arrays.asList(new Object[this.getOutputElements().size()]);
                int i = this.getOutputElements().size() - this.outParams.size();
                for (ElementSymbol param : outParams) {
                  Object value = vc.getValue(param);
                  checkNotNull(param, value);
            paramTuple.set(i++, value);
          }
                addBatchRow(paramTuple, true);
              }
                terminateBatches();
                done = true;
                break;
            }
            addBatchRow(tuple, false);
        }

        return pullBatch();
    }

    /**
     * <p>Process the procedure, using the stack of Programs supplied by the
     * ProcessorEnvironment.  With each pass through the loop, the
     * current Program is gotten off the top of the stack, and the
     * current instruction is gotten from that program; each call
     * to an instruction's process method may alter the Program
     * Stack and/or the current instruction pointer of a Program,
     * so it's important that this method's loop refer to the
     * call stack of the ProcessorEnvironment each time, and not
     * cache things in local variables.  If the current Program's
     * current instruction is null, then it's time to pop that
     * Program off the stack.</p>
     *
     * @return List a single tuple containing one Integer: the update
     * count resulting from the procedure execution.
     */
    private TupleSource processProcedure()
        throws TeiidComponentException, TeiidProcessingException, BlockedException {

        // execute plan
      ProgramInstruction inst = null;

      while (!this.programs.empty()){
            Program program = peek();
            inst = program.getCurrentInstruction();
          if (inst == null){
            LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Finished program", program); //$NON-NLS-1$
                this.pop();
                continue;
            }
            if (inst instanceof RepeatedInstruction) {
              LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Executing repeated instruction", inst); //$NON-NLS-1$
                RepeatedInstruction loop = (RepeatedInstruction)inst;
                if (loop.testCondition(this)) {
                    LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Passed condition, executing program " + loop.getNestedProgram()); //$NON-NLS-1$
                    inst.process(this);
                    this.push(loop.getNestedProgram());
                    continue;
                }
                LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Exiting repeated instruction", inst); //$NON-NLS-1$
                loop.postInstruction(this);
            } else {
              LogManager.logTrace(org.teiid.logging.LogConstants.CTX_DQP, "Executing instruction", inst); //$NON-NLS-1$
                inst.process(this);
            }
            program.incrementProgramCounter();
      }

        if(this.isUpdateProcedure){
            return this.getUpdateCountAsToupleSource();
        }

        if(lastTupleSource == null){
            return CollectionTupleSource.createNullTupleSource();
        }
        return lastTupleSource;
    }

    public void close()
        throws TeiidComponentException {
        if (!this.cursorStates.isEmpty()) {
          List<String> cursors = new ArrayList<String>(this.cursorStates.keySet());
          for (String rsName : cursors) {
          removeResults(rsName);
      }
        }
        if(getTempTableStore()!=null) {
          getTempTableStore().removeTempTables();
        }
        if (this.evaluator != null) {
          this.evaluator.close();
        }
        this.tempTableStore = null;
        this.dataMgr = parentDataMrg;
    }

    public String toString() {
        return "ProcedurePlan:\n" + this.originalProgram; //$NON-NLS-1$
    }

  public ProcessorPlan clone(){
        ProcedurePlan plan = new ProcedurePlan((Program)originalProgram.clone());
        plan.setUpdateProcedure(this.isUpdateProcedure());
        plan.setOutputElements(this.getOutputElements());
        plan.setParams(params);
        plan.setOutParams(outParams);
        plan.setImplicitParams(implicitParams);
        plan.setMetadata(metadata);
        plan.requiresTransaction = requiresTransaction;
        return plan;
    }

  private void addBatchRow(List<?> row, boolean last) {
        if(this.batchRows == null) {
            this.batchRows = new ArrayList<Object>(this.batchSize/4);
        }
        if (!last && this.outParams != null) {
          List<Object> newRow = Arrays.asList(new Object[row.size() + this.outParams.size()]);
          for (int i = 0; i < row.size(); i++) {
        newRow.set(i, row.get(i));
      }
          row = newRow;
        }
        this.batchRows.add(row);
    }

    protected void terminateBatches() {
        this.lastBatch = true;
    }

    protected boolean isBatchFull() {
        return (this.batchRows != null) && (this.batchRows.size() == this.batchSize);
    }

    protected TupleBatch pullBatch() {
        TupleBatch batch = null;
        if(this.batchRows != null) {
            batch = new TupleBatch(this.beginBatch, this.batchRows);
            beginBatch += this.batchRows.size();
        } else {
            batch = new TupleBatch(this.beginBatch, Collections.EMPTY_LIST);
        }

        batch.setTerminationFlag(this.lastBatch);

        // Reset batch state
        this.batchRows = null;
        this.lastBatch = false;

        // Return batch
        return batch;
    }

    public PlanNode getDescriptionProperties() {
      PlanNode node = this.originalProgram.getDescriptionProperties();
      node.addProperty(PROP_OUTPUT_COLS, AnalysisRecord.getOutputColumnProperties(getOutputElements()));
      return node;
    }
   
    public void setMetadata( QueryMetadataInterface metadata ) {
        this.metadata = metadata;
    }
   
    public void setOutParams(List<ElementSymbol> outParams) {
    this.outParams = outParams;
  }

    public void setParams( LinkedHashMap<ElementSymbol, Expression> params ) {
        this.params = params;
    }
   
    public void setImplicitParams(Map<ElementSymbol, Reference> implicitParams) {
    this.implicitParams = implicitParams;
  }
       
  private void createVariableContext() {
    this.currentVarContext = new VariableContext(true);
        this.currentVarContext.setValue(ROWS_UPDATED, 0);
        this.currentVarContext.setValue(ROWCOUNT, 0);
  }

    private TupleSource getUpdateCountAsToupleSource() {
      Object rowCount = currentVarContext.getValue(ROWS_UPDATED);
      if(rowCount == null) {
      rowCount = 0;
      }
        return CollectionTupleSource.createUpdateCountTupleSource((Integer)rowCount);
    }

    /**
     * <p> Get the current <code>VariavleContext</code> on this environment.
     * The VariableContext is updated with variables and their values by
     * {@link ProgramInstruction}s that are part of the ProcedurePlan that use
     * this environment.</p>
     * @return The current <code>VariariableContext</code>.
     */
    public VariableContext getCurrentVariableContext() {
    return this.currentVarContext;
    }

    public void executePlan(ProcessorPlan command, String rsName, Map<ElementSymbol, ElementSymbol> procAssignments, boolean keepRs)
        throws TeiidComponentException, TeiidProcessingException {
     
        CursorState state = this.cursorStates.get(rsName.toUpperCase());
        if (state == null) {
          if (this.currentState == null) {
            //this may not be the first time the plan is being run
            command.reset();
   
            CommandContext subContext = getContext().clone();
            subContext.setVariableContext(this.currentVarContext);
            subContext.setTempTableStore(getTempTableStore());
            state = new CursorState();
            state.processor = new QueryProcessor(command, subContext, this.bufferMgr, this.dataMgr);
            state.ts = new BatchIterator(state.processor);
            if (procAssignments != null && state.processor.getOutputElements().size() - procAssignments.size() > 0) {
              state.resultsBuffer = bufferMgr.createTupleBuffer(state.processor.getOutputElements().subList(0, state.processor.getOutputElements().size() - procAssignments.size()), getContext().getConnectionID(), TupleSourceType.PROCESSOR);
            }
              this.currentState = state;
          }
          //force execution to the first batch
          this.currentState.ts.hasNext();
            if (procAssignments != null) {
              while (this.currentState.ts.hasNext()) {
                if (this.currentState.currentRow != null && this.currentState.resultsBuffer != null) {
                  this.currentState.resultsBuffer.addTuple(this.currentState.currentRow.subList(0, this.currentState.resultsBuffer.getSchema().size()));
                  this.currentState.currentRow = null;
                }
                this.currentState.currentRow = this.currentState.ts.nextTuple();
              }
              //process assignments
              Assertion.assertTrue(this.currentState.currentRow != null);
              for (Map.Entry<ElementSymbol, ElementSymbol> entry : procAssignments.entrySet()) {
                if (entry.getValue() == null || !metadata.elementSupports(entry.getValue().getMetadataID(), SupportConstants.Element.UPDATE)) {
                  continue;
                }
                int index = this.currentState.processor.getOutputElements().indexOf(entry.getKey());
                getCurrentVariableContext().setValue(entry.getValue(), DataTypeManager.transformValue(this.currentState.currentRow.get(index), entry.getValue().getType()));
        }
              //no resultset
              if (this.currentState.resultsBuffer == null) {
                this.currentState.processor.closeProcessing();
                this.currentState = null;
                return;
              }
              this.currentState.resultsBuffer.close();
              this.currentState.ts = this.currentState.resultsBuffer.createIndexedTupleSource();
            }
          this.cursorStates.put(rsName.toUpperCase(), this.currentState);
          //keep a reference to the tuple source
            //it may be the last one
          if (keepRs) {
            this.lastTupleSource = this.currentState.ts;
          }
          this.currentState = null;
        }
    }
   
    /**
     * @throws TeiidComponentException
     */
    public void pop() throws TeiidComponentException {
      Program program = this.programs.pop();
        if (this.currentVarContext.getParentContext() != null) {
          this.currentVarContext = this.currentVarContext.getParentContext();
        }
        Set<String> current = getTempContext();

        Set<String> tempTables = getLocalTempTables();

        tempTables.addAll(current);
       
        if (program != originalProgram) {
          for (String table : tempTables) {
              this.tempTableStore.removeTempTableByName(table);
          }
        }
        this.tempContext.removeLast();
    }
   
    public void push(Program program) {
      program.resetProgramCounter();
        this.programs.push(program);
        VariableContext context = new VariableContext(true);
        context.setParentContext(this.currentVarContext);
        this.currentVarContext = context;
       
        Set<String> current = getTempContext();
       
        Set<String> tempTables = getLocalTempTables();
       
        current.addAll(tempTables);
        this.tempContext.add(new HashSet<String>());
    }
   
    public void incrementProgramCounter() throws TeiidComponentException {
        Program program = peek();
        ProgramInstruction instr = program.getCurrentInstruction();
        if (instr instanceof RepeatedInstruction) {
            RepeatedInstruction repeated = (RepeatedInstruction)instr;
            repeated.postInstruction(this);
        }
        peek().incrementProgramCounter();
    }

    /**
     * @return
     */
    private Set<String> getLocalTempTables() {
        Set<String> tempTables = this.tempTableStore.getAllTempTables();
       
        //determine what was created in this scope
        for (int i = 0; i < tempContext.size() - 1; i++) {
            tempTables.removeAll(tempContext.get(i));
        }
        return tempTables;
    }

    public Set<String> getTempContext() {
        if (this.tempContext.isEmpty()) {
            tempContext.addLast(new HashSet<String>());
        }
        return this.tempContext.getLast();
    }

    public List getCurrentRow(String rsName) throws TeiidComponentException {
        return getCursorState(rsName.toUpperCase()).currentRow;
    }

    public boolean iterateCursor(String rsName)
        throws TeiidComponentException, TeiidProcessingException {

        String rsKey = rsName.toUpperCase();

        CursorState state = getCursorState(rsKey);
       
        state.currentRow = state.ts.nextTuple();
        return (state.currentRow != null);
    }

  private CursorState getCursorState(String rsKey) throws TeiidComponentException {
    CursorState state = this.cursorStates.get(rsKey);
    if (state == null) {
      throw new TeiidComponentException(QueryPlugin.Util.getString("ERR.015.006.0037", rsKey)); //$NON-NLS-1$
    }
    return state;
  }

    public void removeResults(String rsName) {
        String rsKey = rsName.toUpperCase();
        CursorState state = this.cursorStates.remove(rsKey);
        if (state != null) {
          state.processor.closeProcessing();
          if (state.resultsBuffer != null) {
            state.resultsBuffer.remove();
          }
        }
    }

    /**
     * Get the schema from the tuple source that
     * represents the columns in a result set
     * @param rsName the ResultSet name (not a temp group)
     * @return List of elements
     * @throws QueryProcessorException if the list of elements is null
     */
    public List getSchema(String rsName) throws TeiidComponentException {

        // get the tuple source
        String rsKey = rsName.toUpperCase();
       
        CursorState cursorState = getCursorState(rsKey);
        // get the schema from the tuple source
        List schema = cursorState.processor.getOutputElements();
        return schema;
    }

    public boolean resultSetExists(String rsName) {
        String rsKey = rsName.toUpperCase();
        boolean exists = this.cursorStates.containsKey(rsKey);
        return exists;
    }

    public CommandContext getContext() {
      CommandContext context = super.getContext();
      if (evaluatedParams) {
        context.setVariableContext(currentVarContext);
      }
      return context;
    }

    /**
     * @return
     */
    public boolean isUpdateProcedure() {
        return isUpdateProcedure;
    }

    /**
     * @param b
     */
    public void setUpdateProcedure(boolean b) {
        isUpdateProcedure = b;
    }

    public List getOutputElements() {
    return outputElements;
  }

  public void setOutputElements(List outputElements) {
    this.outputElements = outputElements;
  }

    /**
     * @return Returns the tempTableStore.
     * @since 5.5
     */
    public TempTableStore getTempTableStore() {
        return this.tempTableStore;
    }

    boolean evaluateCriteria(Criteria condition) throws BlockedException, TeiidProcessingException, TeiidComponentException {
      evaluator.initialize(getContext(), getDataManager());
    boolean result = evaluator.evaluate(condition, Collections.emptyList());
    this.evaluator.close();
    return result;
    }
   
    Object evaluateExpression(Expression expression) throws BlockedException, TeiidProcessingException, TeiidComponentException {
      evaluator.initialize(getContext(), getDataManager());
      Object result = evaluator.evaluate(expression, Collections.emptyList());
      this.evaluator.close();
      return result;
    }
              
    public Program peek() {
        return programs.peek();
    }
   
    public void setRequiresTransaction(boolean requiresTransaction) {
    this.requiresTransaction = requiresTransaction;
  }
   
    @Override
    public boolean requiresTransaction(boolean transactionalReads) {
      //TODO: detect simple select case
      return requiresTransaction || transactionalReads;
    }
   
}
TOP

Related Classes of org.teiid.query.processor.proc.ProcedurePlan

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.