Package org.apache.derby.impl.sql.execute

Source Code of org.apache.derby.impl.sql.execute.NoRowsResultSetImpl

/*

   Derby - Class org.apache.derby.impl.sql.execute.NoRowsResultSetImpl

   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.execute;

import java.sql.SQLWarning;
import java.sql.Timestamp;

import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.i18n.MessageService;
import org.apache.derby.iapi.services.loader.GeneratedMethod;
import org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.sql.ResultColumnDescriptor;
import org.apache.derby.iapi.sql.ResultDescription;
import org.apache.derby.iapi.sql.ResultSet;
import org.apache.derby.iapi.sql.Row;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.sql.conn.StatementContext;
import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
import org.apache.derby.iapi.sql.execute.ResultSetStatisticsFactory;
import org.apache.derby.iapi.sql.execute.RunTimeStatistics;
import org.apache.derby.iapi.sql.execute.xplain.XPLAINVisitor;
import org.apache.derby.iapi.types.DataValueDescriptor;

/**
* Abstract ResultSet for implementations that do not return rows.
* Examples are DDL statements, CALL statements and DML.
* <P>
* An implementation must provide a ResultSet.open() method
* that performs the required action.
* <P>
* ResultSet.returnsRows() returns false and any method
* that fetches a row will throw an exception.
*
*/
abstract class NoRowsResultSetImpl implements ResultSet
{
  final Activation    activation;
    private NoPutResultSet[] subqueryTrackingArray;

  private final boolean statisticsTimingOn;
  /** True if the result set has been opened, and not yet closed. */
  private boolean isOpen;

  /* Run time statistics variables */
  final LanguageConnectionContext lcc;
  protected long beginTime;
  protected long endTime;
  protected long beginExecutionTime;
  protected long endExecutionTime;

    private int                             firstColumn = -1;    // First column being stuffed. For UPDATES, this lies in the second half of the row.
    private int[]                           generatedColumnPositions; // 1-based positions of generated columns in the target row

    // One cell for  each slot in generatedColumnPositions. These are temporary
    // values which hold the result of running the generation clause before we
    // stuff the result into the target row.
    private DataValueDescriptor[]  normalizedGeneratedValues;

  NoRowsResultSetImpl(Activation activation)
  {
    this.activation = activation;

    if (SanityManager.DEBUG) {
      if (activation == null)
        SanityManager.THROWASSERT("activation is null in result set " + getClass());
    }

    lcc = activation.getLanguageConnectionContext();
    statisticsTimingOn = lcc.getStatisticsTiming();

    /* NOTE - We can't get the current time until after setting up the
     * activation, as we end up using the activation to get the
     * LanguageConnectionContext.
     */
    beginTime = getCurrentTimeMillis();
    beginExecutionTime = beginTime;
  }

  /**
   * Set up the result set for use. Should always be called from
   * <code>open()</code>.
   *
   * @exception StandardException thrown on error
   */
  void setup() throws StandardException {
    isOpen = true;

        StatementContext sc = lcc.getStatementContext();
        sc.setTopResultSet(this, subqueryTrackingArray);

        // Pick up any materialized subqueries
        if (subqueryTrackingArray == null) {
            subqueryTrackingArray = sc.getSubqueryTrackingArray();
        }
  }

    /**
   * Returns FALSE
   */
   public final boolean  returnsRows() { return false; }

  /**
   * Returns zero.
   */
  public int  modifiedRowCount() { return 0; }

  /**
   * Returns null.
   */
  public ResultDescription  getResultDescription()
  {
      return (ResultDescription)null;
  }
 
  public final Activation getActivation()
  {
    return activation;
  }

  /**
   * Returns the row at the absolute position from the query,
   * and returns NULL when there is no such position.
   * (Negative position means from the end of the result set.)
   * Moving the cursor to an invalid position leaves the cursor
   * positioned either before the first row (negative position)
   * or after the last row (positive position).
   * NOTE: An exception will be thrown on 0.
   *
   * @param row  The position.
   * @return  The row at the absolute position, or NULL if no such position.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  getAbsoluteRow(int row) throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "absolute");
  }

  /**
   * Returns the row at the relative position from the current
   * cursor position, and returns NULL when there is no such position.
   * (Negative position means toward the beginning of the result set.)
   * Moving the cursor to an invalid position leaves the cursor
   * positioned either before the first row (negative position)
   * or after the last row (positive position).
   * NOTE: 0 is valid.
   * NOTE: An exception is thrown if the cursor is not currently
   * positioned on a row.
   *
   * @param row  The position.
   * @return  The row at the relative position, or NULL if no such position.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  getRelativeRow(int row) throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "relative");
  }

  /**
   * Sets the current position to before the first row and returns NULL
   * because there is no current row.
   *
   * @return  NULL.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  setBeforeFirstRow()
    throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "beforeFirst");
  }

  /**
   * Returns the first row from the query, and returns NULL when there
   * are no rows.
   *
   * @return  The first row, or NULL if no rows.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  getFirstRow()
    throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "first");
  }

  /**
     * No rows to return, so throw an exception.
   *
   * @exception StandardException    Always throws a
   *                  StandardException to indicate
   *                  that this method is not intended to
   *                  be used.
   */
  public final ExecRow  getNextRow() throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "next");
  }

  /**
   * Returns the previous row from the query, and returns NULL when there
   * are no more previous rows.
   *
   * @return  The previous row, or NULL if no more previous rows.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  getPreviousRow()
    throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "previous");
  }

  /**
   * Returns the last row from the query, and returns NULL when there
   * are no rows.
   *
   * @return  The last row, or NULL if no rows.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  getLastRow()
    throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "last");
  }

  /**
   * Sets the current position to after the last row and returns NULL
   * because there is no current row.
   *
   * @return  NULL.
   *
   * @exception StandardException    Thrown on failure
   * @see Row
   */
  public final ExecRow  setAfterLastRow()
    throws StandardException
  {
    /*
      The JDBC use of this class will never call here.
      Only the DB API used directly can get this exception.
     */
    throw StandardException.newException(SQLState.LANG_DOES_NOT_RETURN_ROWS, "afterLast");
  }

  /**
   * Clear the current row. This is done after a commit on holdable
   * result sets.
   * This is a no-op on result set which do not provide rows.
   */
  public final void clearCurrentRow()
  {
   
  }

    /**
     * Determine if the cursor is before the first row in the result
     * set.  
     *
     * @return true if before the first row, false otherwise. Returns
     * false when the result set contains no rows.
     */
    public final boolean checkRowPosition(int isType)
  {
    return false;
  }

  /**
   * Returns the row number of the current row.  Row
   * numbers start from 1 and go to 'n'.  Corresponds
   * to row numbering used to position current row
   * in the result set (as per JDBC).
   *
   * @return  the row number, or 0 if not on a row
   *
   */
  public final int getRowNumber()
  {
    return 0;
  }

  /**
     * Dump the stat if not already done so. Close all of the open subqueries.
   *
   * @exception StandardException thrown on error
   */
  public void  close() throws StandardException
  {
    if (!isOpen)
      return;

    {
      /*
      ** If run time statistics tracing is turned on, then now is the
      ** time to dump out the information.
      ** NOTE - We make a special exception for commit.  If autocommit
      ** is on, then the run time statistics from the autocommit is the
      ** only one that the user would ever see.  So, we don't overwrite
      ** the run time statistics object for a commit.
            ** DERBY-2353: Also make an exception when the activation is
            ** closed. If the activation is closed, the run time statistics
            ** object is null and there's nothing to print. This may happen
            ** if a top-level result set closes the activation and close() is
            ** subsequently called on the child result sets. The information
            ** about the children is also printed by the parent, so it's safe
            ** to skip printing it.
      */
      if (lcc.getRunTimeStatisticsMode() &&
                !doesCommit() && !activation.isClosed() &&
                !lcc.getStatementContext().getStatementWasInvalidated())
      {
        endExecutionTime = getCurrentTimeMillis();

        ResultSetStatisticsFactory rssf =
                    lcc.getLanguageConnectionFactory().
                         getExecutionFactory().getResultSetStatisticsFactory();

                // get the RuntimeStatisticsImpl object which is the wrapper for all
                // statistics
                RunTimeStatistics rsImpl = rssf.getRunTimeStatistics(activation, this, subqueryTrackingArray);

                // save RTS object in lcc
                lcc.setRunTimeStatisticsObject(rsImpl);
               
                // explain gathered statistics
                XPLAINVisitor visitor =  lcc.getLanguageConnectionFactory().getExecutionFactory().getXPLAINFactory().getXPLAINVisitor();
                visitor.doXPLAIN(rsImpl,activation);

      }
    }

    /* This is the top ResultSet,
     * close all of the open subqueries.
     */
    int staLength = (subqueryTrackingArray == null) ? 0 :
              subqueryTrackingArray.length;

    for (int index = 0; index < staLength; index++)
    {
      if (subqueryTrackingArray[index] == null)
      {
        continue;
      }
      if (subqueryTrackingArray[index].isClosed())
      {
        continue;
      }
      subqueryTrackingArray[index].close();
    }

    isOpen = false;

    if (activation.isSingleExecution())
      activation.close();
  }

  /**
   * Find out if the <code>ResultSet</code> is closed.
   *
   * @return <code>true</code> if closed, <code>false</code> otherwise
   */
  public boolean isClosed() {
    return !isOpen;
  }

  public void  finish() throws StandardException
  {
  }

  /**
   * Get the execution time in milliseconds.
   *
   * @return long    The execution time in milliseconds.
   */
  public long getExecuteTime()
  {
    return endTime - beginTime;
  }

  /**
   * Get the Timestamp for the beginning of execution.
   *
   * @return Timestamp    The Timestamp for the beginning of execution.
   */
  public Timestamp getBeginExecutionTimestamp()
  {
    if (beginExecutionTime == 0)
    {
      return null;
    }
    else
    {
      return new Timestamp(beginExecutionTime);
    }
  }

  /**
   * Get the Timestamp for the end of execution.
   *
   * @return Timestamp    The Timestamp for the end of execution.
   */
  public Timestamp getEndExecutionTimestamp()
  {
    if (endExecutionTime == 0)
    {
      return null;
    }
    else
    {
      return new Timestamp(endExecutionTime);
    }
  }

  /**
   * RESOLVE - This method will go away once it is overloaded in all subclasses.
   * Return the query plan as a String.
   *
   * @param depth  Indentation level.
   *
   * @return String  The query plan as a String.
   */
  public String getQueryPlanText(int depth)
  {
    return MessageService.getTextMessage(
        SQLState.LANG_GQPT_NOT_SUPPORTED,
        getClass().getName());
  }

  /**
   * Return the total amount of time spent in this ResultSet
   *
   * @param type  CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
   *        ENTIRE_RESULTSET_TREE  - time spent in this ResultSet and below.
   *
   * @return long    The total amount of time spent (in milliseconds).
   */
  public long getTimeSpent(int type)
  {
    /* RESOLVE - this should be overloaded in all subclasses */
    return 0;
  }

  /**
   * @see ResultSet#getSubqueryTrackingArray
   */
  public final NoPutResultSet[] getSubqueryTrackingArray(int numSubqueries)
  {
    if (subqueryTrackingArray == null)
    {
      subqueryTrackingArray = new NoPutResultSet[numSubqueries];
    }

    return subqueryTrackingArray;
  }

  /**
   * @see ResultSet#getAutoGeneratedKeysResultset
   */
  public ResultSet getAutoGeneratedKeysResultset()
  {
    //A non-null resultset would be returned only for an insert statement
    return (ResultSet)null;
  }

  /**
    Return the cursor name, null in this case.

    @see ResultSet#getCursorName
  */
  public String getCursorName() {
    return null;
  }

  // class implementation

  /**
   * Return the current time in milliseconds, if DEBUG and RunTimeStats is
   * on, else return 0.  (Only pay price of system call if need to.)
   *
   * @return long    Current time in milliseconds.
   */
  protected final long getCurrentTimeMillis()
  {
    if (statisticsTimingOn)
    {
      return System.currentTimeMillis();
    }
    else
    {
      return 0;
    }
  }

  /**
    *  Run a check constraint against the current row. Raise an error if
    * the check constraint is violated.
    *
    *  @param  checkGM      Generated code to run the check constraint.
    * @param  checkName    Name of the constraint to check.
    *  @param  heapConglom    Number of heap conglomerate.
    *  @param  activation    Class in which checkGM lives.
    *
    * @exception StandardException thrown on error
    */
  public  static  void  evaluateACheckConstraint
  (
    GeneratedMethod checkGM,
    String checkName,
    long heapConglom,
    Activation activation
  )
    throws StandardException
  {
    if (checkGM != null)
    {
      DataValueDescriptor checkBoolean;

      checkBoolean = (DataValueDescriptor) checkGM.invoke(activation);

      /* Throw exception if check constraint is violated.
       * (Only if check constraint evaluates to false.)
       */
      if ((checkBoolean != null) &&
        (! checkBoolean.isNull()) &&
        (! checkBoolean.getBoolean()))
      {
        /* Now we have a lot of painful work to get the
         * table name for the error message.  All we have
         * is the conglomerate number to work with.
         */
        DataDictionary dd = activation.getLanguageConnectionContext().getDataDictionary();
        ConglomerateDescriptor cd = dd.getConglomerateDescriptor( heapConglom );
        TableDescriptor td = dd.getTableDescriptor(cd.getTableID());

        StandardException se = StandardException.newException(SQLState.LANG_CHECK_CONSTRAINT_VIOLATED,
          td.getQualifiedName(), checkName);

        throw se;
      }
    }

  }

  /**
    * Compute the generation clauses on the current row in order to fill in
    * computed columns.
      *
      * @param generationClauses    the generated method which evaluates generation clauses
      * @param activation               the thread-specific instance of the generated class
      * @param source                   the tuple stream driving this INSERT/UPDATE
      * @param newRow                   the base row being stuffed
      * @param isUpdate                 true if this is an UPDATE. false otherwise.
    */
  public  void  evaluateGenerationClauses
  (
    GeneratedMethod generationClauses,
    Activation activation,
      NoPutResultSet    source,
      ExecRow           newRow,
      boolean           isUpdate
  )
    throws StandardException
  {
    if (generationClauses != null)
    {
            ExecRow oldRow = (ExecRow) activation.getCurrentRow( source.resultSetNumber() );

            //
            // We may need to poke the current row into the Activation so that
            // it is visible to the method which evaluates the generation
            // clause. This is because the generation clause may refer to other
            // columns in that row.
            //
            try {
                source.setCurrentRow( newRow );
                generationClauses.invoke(activation);

                //
                // Now apply NOT NULL checks and other coercions. For non-generated columns, these
                // are performed in the driving ResultSet.
                //
                if ( firstColumn < 0 ) { firstColumn = NormalizeResultSet.computeStartColumn( isUpdate, activation.getResultDescription() ); }
                if ( generatedColumnPositions == null ) { setupGeneratedColumns( activation, (ValueRow) newRow ); }
               
                ResultDescription   resultDescription = activation.getResultDescription();
                int                         count = generatedColumnPositions.length;

                for ( int i = 0; i < count; i++ )
                {
                    int         position = generatedColumnPositions[ i ];

                    DataValueDescriptor normalizedColumn = NormalizeResultSet.normalizeColumn
                        (
                         resultDescription.getColumnDescriptor( position ).getType(),
                         newRow,
                         position,
                         normalizedGeneratedValues[ i ],
                         resultDescription
                         );

                    newRow.setColumn( position, normalizedColumn );
                }
            }
            finally
            {
                //
                // We restore the Activation to its state before we ran the generation
                // clause. This may not be necessary but I don't understand all of
                // the paths through the Insert and Update result sets. This
                // defensive coding seems prudent to me.
                //
                if ( oldRow == null ) { source.clearCurrentRow(); }
                else { source.setCurrentRow( oldRow ); }
            }
    }
  }

  /**
    * Construct support for normalizing generated columns. This figures out
    * which columns in the target row have generation clauses which need to be run.
    */
    private void    setupGeneratedColumns( Activation activation, ValueRow newRow )
        throws StandardException
    {
        ResultDescription   resultDescription = activation.getResultDescription();
        int                         columnCount = resultDescription.getColumnCount();
        ExecRow                 emptyRow = newRow.getNewNullRow();
        int                         generatedColumnCount = 0;

        // first count the number of generated columns
        for ( int i = 1; i <= columnCount; i++ )
        {
            if ( i < firstColumn ) { continue; }
           
            ResultColumnDescriptor  rcd = resultDescription.getColumnDescriptor( i );

            if ( rcd.hasGenerationClause() ) { generatedColumnCount++; }
        }

        // now allocate and populate support structures
        generatedColumnPositions = new int[ generatedColumnCount ];
        normalizedGeneratedValues = new DataValueDescriptor[ generatedColumnCount ];

        int     idx = 0;
        for ( int i = 1; i <= columnCount; i++ )
        {
            if ( i < firstColumn ) { continue; }
           
            ResultColumnDescriptor  rcd = resultDescription.getColumnDescriptor( i );

            if ( rcd.hasGenerationClause() )
            {
                generatedColumnPositions[ idx ] = i;
                normalizedGeneratedValues[ idx ] = emptyRow.getColumn( i );

                idx++;
            }
        }
    }
   
  /**
    *  Run check constraints against the current row. Raise an error if
    * a check constraint is violated.
    *
    *  @param  checkGM      Generated code to run the check constraint.
    *  @param  activation    Class in which checkGM lives.
    *
    * @exception StandardException thrown on error
    */
  public  static  void  evaluateCheckConstraints
  (
    GeneratedMethod checkGM,
    Activation activation
  )
    throws StandardException
  {
    if (checkGM != null)
    {
      // Evaluate the expression containing the check constraints.
      // This expression will throw an exception if there is a
      // violation, so there is no need to check the result.
      checkGM.invoke(activation);
    }

  }
   
  /**
   * Does this ResultSet cause a commit or rollback.
   *
   * @return Whether or not this ResultSet cause a commit or rollback.
   */
  public boolean doesCommit()
  {
    return false;
  }

    public void addWarning(SQLWarning w) {
        // We're not returning a (JDBC) ResultSet, so add the warning to
        // the Activation so that it's included in the warning chain of the
        // executing Statement.
        getActivation().addWarning(w);
    }

  public SQLWarning getWarnings() {
    return null;
  }

}
TOP

Related Classes of org.apache.derby.impl.sql.execute.NoRowsResultSetImpl

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.