Package org.apache.derby.impl.sql.conn

Source Code of org.apache.derby.impl.sql.conn.GenericStatementContext$CancelQueryTask

/*

   Derby - Class org.apache.derby.impl.sql.conn.GenericStatementContext

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to you under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

package org.apache.derby.impl.sql.conn;

import org.apache.derby.iapi.services.context.Context;

import org.apache.derby.iapi.services.sanity.SanityManager;

import org.apache.derby.iapi.services.monitor.Monitor;

import org.apache.derby.iapi.services.timer.TimerFactory;

import org.apache.derby.iapi.error.StandardException;

import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.conn.StatementContext;
import org.apache.derby.iapi.sql.conn.SQLSessionContext;

import org.apache.derby.iapi.sql.depend.Dependency;
import org.apache.derby.iapi.sql.depend.DependencyManager;

import org.apache.derby.iapi.sql.execute.NoPutResultSet;

import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.sql.ResultSet;
import org.apache.derby.iapi.sql.ParameterValueSet;

import org.apache.derby.iapi.services.context.ContextImpl;

import org.apache.derby.iapi.error.ExceptionSeverity;
import org.apache.derby.iapi.reference.SQLState;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;

/**
* GenericStatementContext is pushed/popped around a statement prepare and execute
* so that any statement specific clean up can be performed.
*
*
*/
final class GenericStatementContext
  extends ContextImpl implements StatementContext
{
  private boolean    setSavePoint;
  private String    internalSavePointName;
  private ResultSet  topResultSet;
  private ArrayList    dependencies;
  private NoPutResultSet[] subqueryTrackingArray;
  private NoPutResultSet[] materializedSubqueries;
  private  final LanguageConnectionContext lcc;
  private boolean    inUse = true;

    // This flag satisfies all the conditions
    // for using volatile instead of synchronized.
    // (Source: Doug Lea, Concurrent Programming in Java, Second Edition,
    // section 2.2.7.4, page 97)
    // true if statement has been cancelled
    private volatile boolean cancellationFlag = false;

    // Reference to the TimerTask that will time out this statement.
    // Needed for stopping the task when execution completes before timeout.
    private CancelQueryTask cancelTask = null;
       
    private  boolean    parentInTrigger;  // whetherparent started with a trigger on stack
    private  boolean    isForReadOnly = false
    private  boolean    isAtomic; 
  private boolean    isSystemCode;
  private boolean    rollbackParentContext;
    private boolean     statementWasInvalidated;
    private  String    stmtText;
    private  ParameterValueSet      pvs;

  /**
    Set to one of RoutineAliasInfo.{MODIFIES_SQL_DATA, READS_SQL_DATA, CONTAINS_SQL, NO_SQL}
  */
  private short      sqlAllowed = -1;

  /**
   * The activation associated with this context, or null
   */
  private Activation activation;

  /**
   * The SQLSessionContext associated with a statement context.
   */
  private SQLSessionContext sqlSessionContext;

  /*
     constructor
    @param tc transaction
  */
  GenericStatementContext(LanguageConnectionContext lcc)
  {
    super(lcc.getContextManager(), org.apache.derby.iapi.reference.ContextId.LANG_STATEMENT);
    this.lcc = lcc;

    if (SanityManager.DEBUG)
    {
      SanityManager.ASSERT((lcc != null),
          "Failed to get language connection context");
    }

        internalSavePointName = lcc.getUniqueSavepointName();
  }

    /**
     * This is a TimerTask that is responsible for timing out statements,
     * typically when an application has called Statement.setQueryTimeout().
     *
     * When the application invokes execute() on a statement object, or
     * fetches data on a ResultSet, a StatementContext object is allocated
     * for the duration of the execution in the engine (until control is
     * returned to the application).
     *
     * When the StatementContext object is assigned with setInUse(),
     * a CancelQueryTask is scheduled if a timeout > 0 has been set.
     */
    private static class CancelQueryTask
        extends
            TimerTask
    {
        /**
         * Reference to the StatementContext for the executing statement
         * which might time out.
         */
        private StatementContext statementContext;

        /**
         * Initializes a new task for timing out a statement's execution.
         * This does not schedule it for execution, the caller is
         * responsible for calling Timer.schedule() with this object
         * as parameter.
         */
        public CancelQueryTask(StatementContext ctx)
        {
            statementContext = ctx;
        }

        /**
         * Invoked by a Timer class to cancel an executing statement.
         * This method just sets a volatile flag in the associated
         * StatementContext object by calling StatementContext.cancel();
         * it is the responsibility of the thread executing the statement
         * to check this flag regularly.
         */
        public void run()
        {
            synchronized (this) {
                if (statementContext != null) {
                    statementContext.cancel();
                }
            }
        }

        /**
         * Stops this task and prevents it from cancelling a statement.
         * Guarantees that after this method returns, the associated
         * StatementContext object will not be tampered with by this task.
         * Thus, the StatementContext object may safely be allocated to
         * other executing statements.
         */
        public void forgetContext() {
            synchronized (this) {
                statementContext = null;
            }
            cancel();
        }
    }

  // StatementContext Interface

  public void setInUse
  (
    boolean parentInTrigger,
    boolean isAtomic,
                boolean isForReadOnly,
    String stmtText,
    ParameterValueSet pvs,
        long timeoutMillis
  )
  {
    inUse = true;

    this.parentInTrigger = parentInTrigger;
    this.isForReadOnly = isForReadOnly;
    this.isAtomic = isAtomic;
    this.stmtText = stmtText;
    this.pvs = pvs;
    rollbackParentContext = false;
        if (timeoutMillis > 0) {
            TimerFactory factory = Monitor.getMonitor().getTimerFactory();
            Timer timer = factory.getCancellationTimer();
            cancelTask = new CancelQueryTask(this);
            timer.schedule(cancelTask, timeoutMillis);
        }
  }

  public void clearInUse() {
    /* We must clear out the current top ResultSet to prepare for
     * reusing a StatementContext.
     */
    stuffTopResultSet( null, null );
    inUse = false;

    parentInTrigger = false;
    isAtomic = false;
    isForReadOnly = false;
    this.stmtText = null;
    sqlAllowed = -1;
    isSystemCode = false;
    rollbackParentContext = false;
        statementWasInvalidated = false;

        if (cancelTask != null) {
            cancelTask.forgetContext();
            cancelTask = null;
        }
        cancellationFlag = false;
        activation = null;
    sqlSessionContext = null;
    }

  /**
   * @see StatementContext#setSavePoint
   * @exception StandardException Thrown on error
   */
  public void setSavePoint() throws StandardException {
   
    if (SanityManager.DEBUG)
    {
      if (SanityManager.DEBUG_ON("traceSavepoints"))
      {
        SanityManager.DEBUG_PRINT(
                  "GenericStatementContext.setSavePoint()",
                  internalSavePointName);
      }
    }
     
    pleaseBeOnStack();
   

    lcc.getTransactionExecute().setSavePoint(internalSavePointName, null);
    setSavePoint = true;
  }

  /**
   * Resets the savepoint to the current spot if it is
   * set, otherwise, noop.  Used when a commit is
   * done on a nested connection.
   *
   * @see StatementContext#resetSavePoint
   * @exception StandardException Thrown on error
   */
  public void resetSavePoint() throws StandardException {
    if (SanityManager.DEBUG)
    {
      if (SanityManager.DEBUG_ON("traceSavepoints"))
      {
        SanityManager.DEBUG_PRINT(
          "GenericStatementContext.resetSavePoint()",
          internalSavePointName);
      }
    }
     
    if (inUse && setSavePoint)
    {   
      // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller
      lcc.getTransactionExecute().setSavePoint(internalSavePointName, null);
      // stage buffer management
    }
  }

  /**
   * @see StatementContext#clearSavePoint
   * @exception StandardException Thrown on error
   */
  public void clearSavePoint() throws StandardException {

    if (SanityManager.DEBUG)
    {
      if (SanityManager.DEBUG_ON("traceSavepoints"))
      {
        SanityManager.DEBUG_PRINT("GenericStatementContext.clearSavePoint()",
                      internalSavePointName);
      }
    }

    pleaseBeOnStack();

    if (SanityManager.DEBUG)
    {
      SanityManager.ASSERT(setSavePoint, "setSavePoint is expected to be true");
    }

    // RESOLVE PLUGIN ???. For the plugin, there will be no transaction controller
    lcc.getTransactionExecute().releaseSavePoint(internalSavePointName, null);
    setSavePoint = false;
  }

  /**
   * Set the top ResultSet in the ResultSet tree for close down on
   * an error.
   *
   * @exception StandardException thrown on error.
   */
  public void setTopResultSet(ResultSet topResultSet,
                  NoPutResultSet[] subqueryTrackingArray)
     throws StandardException
  {
    pleaseBeOnStack();

    /* We have to handle both materialize and non-materialized subqueries.
     * Materialized subqueries are attached before the top result set is
     * set.  If there are any, then we must copy them into the new
     * subqueryTrackingArray.
     */
    if (materializedSubqueries != null)
    {
      // Do the merging into the passed in array.
      if (subqueryTrackingArray != null)
      {
        if (SanityManager.DEBUG)
        {
          if (this.materializedSubqueries.length != subqueryTrackingArray.length)
          {
            SanityManager.THROWASSERT(
              "this.ms.length (" + this.materializedSubqueries.length +
              ") expected to = sta.length(" + subqueryTrackingArray.length +
              ")");
          }
        }
        for (int index = 0; index < subqueryTrackingArray.length; index++)
        {
          if (this.subqueryTrackingArray[index] != null)
          {
            subqueryTrackingArray[index] = this.materializedSubqueries[index];
          }
        }
      }
      else
      {
        subqueryTrackingArray = this.materializedSubqueries;
      }
      materializedSubqueries = null;
    }

    stuffTopResultSet( topResultSet, subqueryTrackingArray );
  }

  /**
    *  Private minion of setTopResultSet() and clearInUse()
    *
    *  @param  topResultSet  make this the top result set
    *  @param  subqueryTrackingArray  where to keep track of subqueries in this statement
    */
  private  void  stuffTopResultSet(ResultSet topResultSet,
                    NoPutResultSet[] subqueryTrackingArray)
  {
    this.topResultSet = topResultSet;
    this.subqueryTrackingArray = subqueryTrackingArray;
    dependencies = null;
  }


  /**
   * Set the appropriate entry in the subquery tracking array for
   * the specified subquery.
   * Useful for closing down open subqueries on an exception.
   *
   * @param subqueryNumber  The subquery # for this subquery
   * @param subqueryResultSet  The ResultSet at the top of the subquery
   * @param numSubqueries    The total # of subqueries in the entire query
   *
   * @exception StandardException thrown on error.
   */
  public void setSubqueryResultSet(int subqueryNumber,
                   NoPutResultSet subqueryResultSet,
                   int numSubqueries)
    throws StandardException
  {
    pleaseBeOnStack();
   
    /* NOTE: In degenerate cases, it is possible that there is no top
     * result set.  For example:
     *    call (select 1 from systables).valueOf('111');
     * In that case, we allocate our own subquery tracking array on
     * each call. (Gross!)
     * (Trust me, this is only done in degenerate cases.  The tests passed,
     * except for the degenerate cases, before making this change, so we
     * know that the top result set and array reuse is working for
     * the non-degenerate cases.)
     */
    if (subqueryTrackingArray == null)
    {
      if (topResultSet == null)
      {
        subqueryTrackingArray = new NoPutResultSet[numSubqueries];
        materializedSubqueries = new NoPutResultSet[numSubqueries];
      }
      else
      {
        subqueryTrackingArray =
          topResultSet.getSubqueryTrackingArray(numSubqueries);
      }
    }
    subqueryTrackingArray[subqueryNumber] = subqueryResultSet;
    if (materializedSubqueries != null)
    {
      materializedSubqueries[subqueryNumber] = subqueryResultSet;
    }
  }

  /**
   * Get the subquery tracking array for this query.
   * (Useful for runtime statistics.)
   *
   * @return NoPutResultSet[]  The  (sparse) array of tops of subquery ResultSet trees
   * @exception StandardException thrown on error.
   */
  public NoPutResultSet[] getSubqueryTrackingArray()
    throws StandardException
  {
    pleaseBeOnStack();
   
    return subqueryTrackingArray;
  }

  /**
   * Track a Dependency within this StatementContext.
   * (We need to clear any dependencies added within this
   * context on an error.
   *
   * @param dy  The dependency to track.
   *
   * @exception StandardException thrown on error.
   */
  public void addDependency(Dependency dy)
    throws StandardException
  {
    pleaseBeOnStack();
   
    if (dependencies == null)
    {
      dependencies = new ArrayList();
    }
    dependencies.add(dy);
  }

  /**
   * Returns whether we started from within the context of a trigger
   * or not.
   *
   * @return  true if we are in a trigger context
   */
  public  boolean  inTrigger()
  {
    return  parentInTrigger;
  }

  //
  // Context interface
  //
  /**
   * Close down the top ResultSet, if relevant, and rollback to the
   * internal savepoint, if one was set.
   *
   * @exception StandardException thrown on error. REVISIT: don't want
   * cleanupOnError's to throw exceptions.
   */
  public void cleanupOnError(Throwable error) throws StandardException
  {

    if (SanityManager.DEBUG)
    {
      if (SanityManager.DEBUG_ON("traceSavepoints"))
      {
        SanityManager.DEBUG_PRINT(
            "GenericStatementContext.cleanupOnError()",
            String.valueOf( hashCode() ) );
      }
    }

    /*
    ** If it isn't a StandardException, then assume
    ** session severity.  It is probably an unexpected
    ** java error somewhere in the language.
        ** Store layer treats JVM error as session severity,
        ** hence to be consistent and to avoid getting rawstore
        ** protocol violation errors, we treat java errors here
        ** to be of session severity. 
        */
        int severity = ExceptionSeverity.SESSION_SEVERITY;
        if (error instanceof StandardException) {
            StandardException se = (StandardException)error;
            // Update the severity.
            severity = se.getSeverity();
            // DERBY-4849: Remember that the plan was invalidated, such that
            // we can avoid performing certain actions more than once
            // (for correctness, not optimization).
            if (SQLState.LANG_STATEMENT_NEEDS_RECOMPILE.equals(
                    se.getMessageId())) {
                statementWasInvalidated = true;
            }
        }


    /**
     * Don't clean up this statement context if it's not in use.
     * This can happen if you get an error while calling one of
     * the JDBC getxxxx() methods on a ResultSet, since no statement
     * context is pushed when those calls occur.
     */
    if (! inUse)
    {
      return;
    }

    /* Clean up the ResultSet, if one exists */
    if (topResultSet != null)
    {
      topResultSet.cleanUp();
    }

    /* Close down any open subqueries */
    if (subqueryTrackingArray != null)
    {
      for (int index = 0; index < subqueryTrackingArray.length; index++)
      {
        /* Remember, the array is sparse, so only check
         * non-null entries.
         */
        if (subqueryTrackingArray[index] != null)
        {
          subqueryTrackingArray[index].cleanUp();
        }
      }
    }

    /* Clean up any dependencies */
    if (dependencies != null)
    {
      DependencyManager dmgr = lcc.getDataDictionary().getDependencyManager();

      for (Iterator iterator = dependencies.iterator(); iterator.hasNext(); )
      {
        Dependency dy = (Dependency) iterator.next();
        dmgr.clearInMemoryDependency(dy);
      }

      dependencies = null;
    }

    if (severity <= ExceptionSeverity.STATEMENT_SEVERITY
      && setSavePoint)
    {
      if (SanityManager.DEBUG)
      {
        if (SanityManager.DEBUG_ON("traceSavepoints"))
        {
          SanityManager.DEBUG_PRINT(
            "GenericStatementContext.cleanupOnError",
            "rolling back to: " + internalSavePointName);
        }
      }

      lcc.internalRollbackToSavepoint( internalSavePointName, false, null);

      clearSavePoint();
    }

    if (severity >= ExceptionSeverity.TRANSACTION_SEVERITY )
    {
      // transaction severity errors roll back the transaction.

      /*
      ** We call clearSavePoint() above only for statement errors.
      ** We don't call clearSavePoint() for transaction errors because
      ** the savepoint will be rolled back anyway.  So in this case,
      ** we need to indicate that the savepoint is not set.
      */
      setSavePoint = false;
    }

    /* Pop the context */
    lcc.popStatementContext(this, error);
  }

  /**
   * @see Context#isLastHandler
   */
  public boolean isLastHandler(int severity)
  {
        // For JVM errors, severity gets mapped to
        // ExceptionSeverity.NO_APPLICABLE_SEVERITY
        // in ContextManager.cleanupOnError. It is necessary to
        // let outer contexts take corrective action for jvm errors, so
        // return false as this will not be the last handler for such
        // errors.
    return inUse && !rollbackParentContext &&
            ( severity == ExceptionSeverity.STATEMENT_SEVERITY );
  }

  /**
    *  Reports whether this StatementContext is on the context stack.
    *
    *  @return  true if this StatementContext is on the context stack. false otherwise.
    */
    public  boolean  onStack() { return inUse; }

  /**
   * Indicates whether the statement needs to be executed atomically
   * or not, i.e., whether a commit/rollback is permitted by a
    * connection nested in this statement.
   *
   * @return true if needs to be atomic
   */
  public boolean isAtomic()
  {
    return isAtomic;
  }

  /**
   * Return the text of the current statement.
   * Note that this may be null.  It is currently
   * not set up correctly for ResultSets that aren't
   * single row result sets (e.g SELECT), replication,
   * and setXXXX/getXXXX jdbc methods.
   *
   * @return the statement text
   */
  public String getStatementText()
  {
    return stmtText;
  }

  //
  // class implementation
  //

  /**
    *  Raise an exception if this Context is not in use, that is, on the
    * Context Stack.
    *
    * @exception StandardException thrown on error.
    */
  private  void  pleaseBeOnStack() throws StandardException
  {
    if ( !inUse ) { throw StandardException.newException(SQLState.LANG_DEAD_STATEMENT); }
  }

  public boolean inUse()
  {
    return inUse;
  }
    public boolean isForReadOnly()
    {
  return isForReadOnly;
    }
       
    /**
     * Tests whether the statement which has allocated this StatementContext
     * object has been cancelled. This method is typically called from the
     * thread which is executing the statement, to test whether execution
     * should continue or stop.
     *
     * @return whether the statement which has allocated this StatementContext
     *  object has been cancelled.
     */
    public boolean isCancelled()
    {
        return cancellationFlag;
    }

    /**
     * Cancels the statement which has allocated this StatementContext object.
     * This is done by setting a flag in the StatementContext object. For
     * this to have any effect, it is the responsibility of the executing
     * statement to check this flag regularly.
     */
    public void cancel()
    {
        cancellationFlag = true;
    }

  public void setSQLAllowed(short allow, boolean force) {

    // cannot override a stricter setting.
    // -1 is no routine restriction in place
    // 0 is least restrictive
    // 4 is most
    if (force || (allow > sqlAllowed))
      sqlAllowed = allow;

  }
  public short getSQLAllowed() {
    if (!inUse)
      return org.apache.derby.catalog.types.RoutineAliasInfo.NO_SQL;

    return sqlAllowed;
  }

  /**
   * Indicate that, in the event of a statement-level exception,
   * this context is NOT the last one that needs to be rolled
   * back--rather, it is nested within some other statement
   * context, and that other context needs to be rolled back,
   * too.
  */
  public void setParentRollback() {
    rollbackParentContext = true;
  }

  /**
    Set to indicate statement is system code.
    For example a system procedure, view, function etc.
  */
  public void setSystemCode() {
    isSystemCode = true;
  }

  /**
    Return true if this statement is system code.
  */
  public boolean getSystemCode() {
    return isSystemCode;
  }

  public StringBuffer appendErrorInfo() {

    StringBuffer sb = ((ContextImpl) lcc).appendErrorInfo();
    if (sb != null) {

      sb.append("Failed Statement is: ");

      sb.append(getStatementText());

      if ((pvs != null) && pvs.getParameterCount() > 0)
      {
        String pvsString = " with " + pvs.getParameterCount() +
            " parameters " + pvs.toString();
        sb.append(pvsString);
      }
    }
    return sb;

  }

  /**
   * @see StatementContext#setActivation(Activation a)
   */
  public void setActivation(Activation a) {
    activation = a;
  }

  /**
   * @see StatementContext#getActivation
   */
  public Activation getActivation() {
    return activation;
  }

  /**
   * @see StatementContext#getSQLSessionContext
   */
  public SQLSessionContext getSQLSessionContext() {
    return sqlSessionContext;
  }

  /**
   * @see StatementContext#setSQLSessionContext(SQLSessionContext ctx)
   */
  public void setSQLSessionContext(SQLSessionContext ctx) {
    sqlSessionContext = ctx;
  }

    public boolean getStatementWasInvalidated() {
        return statementWasInvalidated;
    }
}
TOP

Related Classes of org.apache.derby.impl.sql.conn.GenericStatementContext$CancelQueryTask

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.