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

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

/*

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

   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.util.Properties;

import org.apache.derby.iapi.db.TriggerExecutionContext;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.sql.ResultDescription;
import org.apache.derby.iapi.sql.ResultSet;
import org.apache.derby.iapi.sql.execute.ConstantAction;
import org.apache.derby.iapi.sql.execute.CursorResultSet;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.store.access.ConglomerateController;
import org.apache.derby.iapi.store.access.TransactionController;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.RowLocation;

/**
* Delete the rows from the specified
* base table. This will cause constraints to be checked
* and triggers to be executed based on the c's and t's
* compiled into the insert plan.
*/
class DeleteResultSet extends DMLWriteResultSet
{
  private TransactionController     tc;
  DeleteConstantAction    constants;
    protected ResultDescription         resultDescription;
  protected  NoPutResultSet      source;
  NoPutResultSet      savedSource;
  int               numIndexes;
  protected RowChanger       rc;
  private ExecRow          row;

  protected ConglomerateController  deferredBaseCC;

  protected TemporaryRowHolderImpl  rowHolder;

  private int            numOpens; // number of opens w/o a close
  private boolean         firstExecute;

  // cached across opens()s
  private FormatableBitSet         baseRowReadList;
  private int            rlColumnNumber;
  protected FKInfo[]        fkInfoArray;
  private TriggerInfo       triggerInfo;
  private  RISetChecker      fkChecker;
  private TriggerEventActivator  triggerActivator;
  private boolean          noTriggersOrFks;

  ExecRow    deferredSparseRow;
  ExecRow    deferredBaseRow;
  int lockMode;
  protected  boolean cascadeDelete;
  ExecRow    deferredRLRow = null;
  int  numberOfBaseColumns = 0;

  /**
     * Returns the description of the deleted rows.
     * REVISIT: Do we want this to return NULL instead?
   */
  public ResultDescription getResultDescription()
  {
      return resultDescription;
  }

    /*
     * class interface
     *
     */
    DeleteResultSet
  (
    NoPutResultSet    source,
    Activation      activation
  )
    throws StandardException
    {
    this(source, activation.getConstantAction(), activation);
  }
    /**
     * REMIND: At present this takes just the conglomerate id
     * of the table. We can expect this to expand to include
     * passing information about triggers, constraints, and
     * any additional conglomerates on the underlying table
     * for access methods.
     *
   * @exception StandardException    Thrown on error
     */
    DeleteResultSet
  (
    NoPutResultSet    source,
    ConstantAction    passedInConstantAction,
    Activation      activation
  )
    throws StandardException
    {
    super(activation, passedInConstantAction);
    this.source = source;

    tc = activation.getTransactionController();
    constants = (DeleteConstantAction) constantAction;
    fkInfoArray = constants.getFKInfo();
    triggerInfo = constants.getTriggerInfo();
    noTriggersOrFks = ((fkInfoArray == null) && (triggerInfo == null));
    baseRowReadList = constants.getBaseRowReadList();
    if(source != null)
      resultDescription = source.getResultDescription();
    else
      resultDescription = constants.resultDescription;

  }

  /**
    @exception StandardException Standard Derby error policy
  */
  public void open() throws StandardException
  {

    setup();
    boolean rowsFound = collectAffectedRows(); //this call also deletes rows , if not deferred
    if (! rowsFound)
    {
      activation.addWarning(
            StandardException.newWarning(
              SQLState.LANG_NO_ROW_FOUND));
    }

    /*
    ** If the delete is deferred, scan the temporary conglomerate to
    ** get the RowLocations of the rows to be deleted.  Re-fetch the
    ** rows and delete them using the RowChanger.
    */
    if (constants.deferred)
    {
      runFkChecker(true); //check for only RESTRICT referential action rule violations
      fireBeforeTriggers();
      deleteDeferredRows();
      runFkChecker(false); //check for all constraint violations
      // apply
      rc.finish();
      fireAfterTriggers();
    }

 
    /* Cache query plan text for source, before it gets blown away */
    if (lcc.getRunTimeStatisticsMode())
    {
      /* savedSource nulled after run time statistics generation */
      savedSource = source;
    }

    cleanUp();
    endTime = getCurrentTimeMillis();

    }
 

  //this routine open the source and find the dependent rows
  void  setup() throws StandardException
  {
    super.setup();

    // Remember if this is the 1st execution
    firstExecute = (rc == null);

    try {

      //open the source for the parent tables
      if (numOpens++ == 0)
      {
        source.openCore();
      }
      else
      {
            source.reopenCore();
      }
    } catch (StandardException se) {
      activation.checkStatementValidity();
      throw se;

    }

    activation.checkStatementValidity();

    /* Get or re-use the row changer.
     */
    if (firstExecute)
    {
      rc = lcc.getLanguageConnectionFactory().getExecutionFactory().
               getRowChanger(
                constants.conglomId,
                constants.heapSCOCI,
                heapDCOCI,
                constants.irgs,
                constants.indexCIDS,
                constants.indexSCOCIs,
                  indexDCOCIs,
                constants.numColumns,
                tc,
                (int[])null,
                baseRowReadList,
                constants.getBaseRowReadMap(),
                constants.getStreamStorableHeapColIds(),
                activation);
    }

    /* decode the lock mode for the execution isolation level */
    lockMode = decodeLockMode(constants.lockMode);

    /* Open the RowChanger before the source ResultSet so that
     * the store will see the RowChanger's lock as a covering lock
     * if it is a table lock.
     */
    rc.open(lockMode);

    /* The source does not know whether or not we are doing a
     * deferred mode delete.  If we are, then we must clear the
     * index scan info from the activation so that the row changer
     * does not re-use that information (which won't be valid for
     * a deferred mode delete).
     */
    if (constants.deferred || cascadeDelete)
    {
      activation.clearIndexScanInfo();
    }

        rowCount = 0;
        if(!cascadeDelete)
      row = getNextRowCore(source);

    /*
    ** We need the number of columns even if there are
    ** no rows. Note that source.ressultDescription() may
    ** be null on a rep target doing a refresh.
    */
    if (resultDescription == null)
    {
      if (SanityManager.DEBUG)
      {
        /*
        ** We NEED a result description when we are going to
        ** to have to kick off a trigger.  In a replicated environment
        ** we don't get a result description when we are replaying
        ** source xacts on the target, but we shouldn't be firing
        ** a trigger in that case anyway.
        */
        SanityManager.ASSERT(triggerInfo == null, "result description is needed to supply to trigger result sets");
      }
      numberOfBaseColumns = (row == null) ? 0 : row.nColumns();
    }
    else
    {
      numberOfBaseColumns = resultDescription.getColumnCount();
    }

    numIndexes = constants.irgs.length;

    if (constants.deferred || cascadeDelete)
    {
      Properties properties = new Properties();

      // Get the properties on the old heap
      rc.getHeapConglomerateController().getInternalTablePropertySet(properties);

      /*
      ** If deferred and fk or trigger, we are going to grab
      ** the entire row. 
      **
      ** If we are deferred w/o a fk, then we only
      ** save the row location.
      */
      deferredRLRow = RowUtil.getEmptyValueRow(1, lcc);
      rlColumnNumber = noTriggersOrFks ? 1: numberOfBaseColumns;
      if(cascadeDelete)
      {
        rowHolder = new TemporaryRowHolderImpl(activation, properties,
            (resultDescription != null) ?
              resultDescription.truncateColumns(rlColumnNumber) :
              null, false);


      }else
      {

        rowHolder = new TemporaryRowHolderImpl(activation, properties,
            (resultDescription != null) ?
              resultDescription.truncateColumns(rlColumnNumber) :
              null);

      }

      rc.setRowHolder(rowHolder);
    }

    if (fkInfoArray != null)
    {
      if (fkChecker == null)
      {
        fkChecker = new RISetChecker(tc, fkInfoArray);
      }
      else
      {
        fkChecker.reopen();
      }
    }
  }


  boolean  collectAffectedRows() throws StandardException
 

    DataValueDescriptor    rlColumn;
    RowLocation  baseRowLocation;
    boolean rowsFound = false;

    if(cascadeDelete)
      row = getNextRowCore(source);

    while ( row != null )
    {
      /* By convention, the last column for a delete contains a SQLRef
       * containing the RowLocation of the row to be deleted.  If we're
       * doing a deferred delete, store the RowLocations in the
       * temporary conglomerate.  If we're not doing a deferred delete,
       * just delete the rows immediately.
       */

      rowsFound = true;

      rlColumn = row.getColumn( row.nColumns() );
   
      if (constants.deferred || cascadeDelete)
      {

        /*
        ** If we are deferred because of a trigger or foreign
        ** key, we need to save off the entire row.  Otherwise,
        ** we just save the RID.
        */
        if (noTriggersOrFks)
        {
          deferredRLRow.setColumn(1, rlColumn);
          rowHolder.insert(deferredRLRow);
        }
        else
        {
          rowHolder.insert(row);
        }
       
        /*
        ** If we haven't already, lets get a template to
        ** use as a template for our rescan of the base table.
        ** Do this now while we have a real row to use
        ** as a copy.
        **
        ** There is one less column in the base row than
        ** there is in source row, because the base row
        ** doesn't contain the row location.
        */
        if (deferredBaseRow == null)
        {
          deferredBaseRow = RowUtil.getEmptyValueRow(numberOfBaseColumns - 1, lcc);
     
          RowUtil.copyCloneColumns(deferredBaseRow, row,
                      numberOfBaseColumns - 1);
          deferredSparseRow = makeDeferredSparseRow(deferredBaseRow,
                                baseRowReadList,
                                lcc);
        }
      }
      else
      {
        if (fkChecker != null)
        {
          fkChecker.doPKCheck(row, false);
        }

        baseRowLocation =
          (RowLocation) (rlColumn).getObject();

        if (SanityManager.DEBUG)
        {
          SanityManager.ASSERT(baseRowLocation != null,
              "baseRowLocation is null");
        }

        rc.deleteRow(row,baseRowLocation);
        source.markRowAsDeleted();
      }

            rowCount++;

      // No need to do a next on a single row source
      if (constants.singleRowSource)
      {
        row = null;
      }
      else
      {
        row = getNextRowCore(source);
      }
    }

    return rowsFound;
  }


  // execute the before triggers set on the table
    void fireBeforeTriggers() throws StandardException
  {

    if (triggerInfo != null)
    {
      if (triggerActivator == null)
      {
        triggerActivator = new TriggerEventActivator(lcc,
                               tc,
                               constants.targetUUID,
                               triggerInfo,
                               TriggerExecutionContext.DELETE_EVENT,
                               activation, null
                               );
      }
      else
      {
        triggerActivator.reopen();
      }

      // fire BEFORE trigger
      triggerActivator.notifyEvent(TriggerEvents.BEFORE_DELETE,
                     rowHolder.getResultSet(),
                     (CursorResultSet)null);
      triggerActivator.cleanup();

    }

  }

  //execute the after triggers set on the table.
  void fireAfterTriggers() throws StandardException
  {

    // fire AFTER trigger
    if (triggerActivator != null)
    {
      triggerActivator.reopen();
      triggerActivator.notifyEvent(TriggerEvents.AFTER_DELETE,
                     rowHolder.getResultSet(),
                     (CursorResultSet)null);
      triggerActivator.cleanup();
    }
   
  }


  //delete the rows that in case deferred case and
  //during cascade delete (All deletes are deferred during cascade action)
  void deleteDeferredRows() throws StandardException
  {
   
    DataValueDescriptor    rlColumn;
     RowLocation  baseRowLocation;
    ExecRow    deferredRLRow = null;

    deferredBaseCC = tc.openCompiledConglomerate(false,
                           tc.OPENMODE_FORUPDATE|tc.OPENMODE_SECONDARY_LOCKED,
                           lockMode,
                           TransactionController.ISOLATION_SERIALIZABLE,
                           constants.heapSCOCI,
                           heapDCOCI);
     
    CursorResultSet rs = rowHolder.getResultSet();
    try
    {
      /*
      ** We need to do a fetch doing a partial row
      ** read.  We need to shift our 1-based bit
      ** set to a zero based bit set like the store
      ** expects.
      */
      FormatableBitSet readBitSet = RowUtil.shift(baseRowReadList, 1);

      rs.open();
      while ((deferredRLRow = rs.getNextRow()) != null)
      {
        rlColumn = deferredRLRow.getColumn(rlColumnNumber);
        baseRowLocation =
          (RowLocation) (rlColumn).getObject();
 
        /* Get the base row at the given RowLocation */
        boolean row_exists =
          deferredBaseCC.fetch(
                     baseRowLocation, deferredSparseRow.getRowArray(),
                     readBitSet);

        // In case of cascade delete , things like before triggers can delete
        // the rows before the dependent result get a chance to delete
        if(cascadeDelete && !row_exists)
          continue;

        if (SanityManager.DEBUG)
        {
          if (!row_exists)
          {
                          SanityManager.THROWASSERT("could not find row "+baseRowLocation);
          }
        }
 
        rc.deleteRow(deferredBaseRow, baseRowLocation);
        source.markRowAsDeleted();
      }
    } finally
    {
        rs.close();
    }
  }


  // make sure foreign key constraints are not violated
    void runFkChecker(boolean restrictCheckOnly) throws StandardException
  {

    ExecRow    deferredRLRow = null;
    if (fkChecker != null)
    {
      /*
      ** Second scan to make sure all the foreign key
      ** constraints are ok.  We have to do this after
      ** we have completed the deletes in case of self referencing
      ** constraints.
      */
      CursorResultSet rs = rowHolder.getResultSet();
      try
      {
        rs.open();
        while ((deferredRLRow = rs.getNextRow()) != null)
        {
          fkChecker.doPKCheck(deferredRLRow, restrictCheckOnly);
        }
      } finally
      {
        rs.close();
      }
    }
  }

  /**
    *  create a source for the dependent table
    *
    * <P>Delete Cascade ResultSet class will override this method.
    *
    * @exception StandardException    Thrown on error
    */
  NoPutResultSet createDependentSource(RowChanger rc)
    throws StandardException
  {
    return null;
  }


  /**
   * @see ResultSet#cleanUp
   *
   * @exception StandardException    Thrown on error
   */
  public void  cleanUp() throws StandardException
  {
    numOpens = 0;

    /* Close down the source ResultSet tree */
    if (source != null)
    {
      source.close();
      // source is reused across executions
    }
    if (rc != null)
    {
      rc.close();
      // rc is reused across executions
    }

    if (rowHolder != null)
    {
      rowHolder.close();
      // rowHolder is reused across executions
    }

    if (fkChecker != null)
    {
      fkChecker.close();
      // fkcheckers is reused across executions
    }

    if (deferredBaseCC != null)
      deferredBaseCC.close();
    deferredBaseCC = null;

    if (rc != null) {
      rc.close();
    }
    super.close();
  }

  public void finish() throws StandardException {
    if (source != null)
      source.finish();
    super.finish();
  }

}











TOP

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

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.