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

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

/*

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

   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 org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.sql.execute.ConstantAction;
import org.apache.derby.iapi.sql.execute.CursorResultSet;
import org.apache.derby.iapi.sql.execute.RowChanger;
import org.apache.derby.iapi.sql.execute.NoPutResultSet;
import org.apache.derby.iapi.sql.Activation;
import org.apache.derby.iapi.sql.ResultDescription;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.sql.ResultSet;
import org.apache.derby.iapi.store.access.ConglomerateController;
import org.apache.derby.iapi.store.access.TransactionController;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;

import org.apache.derby.iapi.reference.SQLState;

import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;

/**
* Delete the rows from the specified  base table and executes delete/update
* on dependent tables depending on the referential actions specified.
* Note:(beetle:5197) Dependent Resultsets of DeleteCascade Resultset can  in
* any one of the multiple resultsets generated for the same table because of
* multiple foreign key relationship to  the same table. At the bind time ,
* dependents are binded only once per table.
* We can not depend on mainNodeTable Flag to fire actions on dependents,
* it should be done based on whether the resultset has dependent resultsets or not.
*
*/
public class DeleteCascadeResultSet extends DeleteResultSet
{


  public ResultSet[] dependentResultSets;
  private int noDependents =0;
  private CursorResultSet parentSource;
  private FKInfo parentFKInfo;
  private long fkIndexConglomNumber;
  private String resultSetId;
  private boolean mainNodeForTable = true;
  private boolean affectedRows = false;
  private int tempRowHolderId; //this result sets temporary row holder id

    /*
     * class interface
   * @exception StandardException    Thrown on error
     */
    public DeleteCascadeResultSet
  (
    NoPutResultSet    source,
    Activation      activation,
    int         constantActionItem,
    ResultSet[]      dependentResultSets,
    String            resultSetId
  )
    throws StandardException
    {

    super(source,
        ((constantActionItem == -1) ?activation.getConstantAction() :
        (ConstantAction)activation.getPreparedStatement().getSavedObject(constantActionItem)),
        activation);

    ConstantAction passedInConstantAction;
    if(constantActionItem == -1)
      passedInConstantAction = activation.getConstantAction(); //root table
    else
    {
      passedInConstantAction =
        (ConstantAction) activation.getPreparedStatement().getSavedObject(constantActionItem);
      resultDescription = constants.resultDescription;
    }
    cascadeDelete = true;
    this.resultSetId = resultSetId;
   
    if(dependentResultSets != null)
    {
      noDependents = dependentResultSets.length;
      this.dependentResultSets = dependentResultSets;
    }

  }



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


    try{
      setup();
      if(isMultipleDeletePathsExist())
      {
        setRowHoldersTypeToUniqueStream();
        //collect until there are no more rows to found
        while(collectAffectedRows(false));
      }else
      {
        collectAffectedRows(false);
      }
      if (! affectedRows)
      {
        activation.addWarning(
              StandardException.newWarning(
                SQLState.LANG_NO_ROW_FOUND));
      }

      runFkChecker(true); //check for only RESTRICT referential action rule violations
      Hashtable mntHashTable = new Hashtable(); //Hash Table to identify  mutiple node for same table cases.
      mergeRowHolders(mntHashTable);
      fireBeforeTriggers(mntHashTable);
      deleteDeferredRows();
      runFkChecker(false); //check for all constraint violations
      rowChangerFinish();
      fireAfterTriggers();
    }finally
    {
      cleanUp();

      //clear the parent result sets hash table
      activation.clearParentResultSets();
    }

    endTime = getCurrentTimeMillis();

    }
 

  /**
   *Gathers the rows that needs to be deleted/updated
   *and creates a temporary resulsets that will be passed
   *as source to its  dependent result sets.
   */
  void  setup() throws StandardException
  {

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

    super.setup();
    activation.setParentResultSet(rowHolder, resultSetId);
    Vector sVector = (Vector) activation.getParentResultSet(resultSetId);
    tempRowHolderId = sVector.size() -1;
    for(int i =0 ; i < noDependents; i++)
    {
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).setup();
      }else
      {
        ((DeleteCascadeResultSet) dependentResultSets[i]).setup();
      }
    }

  }


  boolean  collectAffectedRows(boolean rowsFound) throws StandardException
  {
    if(super.collectAffectedRows())
    {
      affectedRows = true;
      rowsFound = true;
    }

    for(int i =0 ; i < noDependents; i++)
    {
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        if(((UpdateResultSet)dependentResultSets[i]).collectAffectedRows())
          rowsFound = true;
      }else
      {
        if(((DeleteCascadeResultSet)
          dependentResultSets[i]).collectAffectedRows(rowsFound))
          rowsFound = true;
      }
    }

    return rowsFound;
  }


  void fireBeforeTriggers(Hashtable msht) throws StandardException
  {
    if(!mainNodeForTable)
    {
      /*to handle case where no table node had qualified rows, in which case no node for
       * the table get marked as mainNodeFor table , one way to identify
       * such case is to look at the mutinode hash table and see if the result id exist ,
       *if it does not means none of the table nodes resulsets got marked
       * as main node for table. If that is the case we mark this
       * resultset as mainNodeTable and put entry in the hash table.
       */
      if(!msht.containsKey(resultSetId))
      {
        mainNodeForTable = true;
        msht.put(resultSetId, resultSetId);
      }
    }
   
    //execute the before triggers on the dependents
    //Defect 5743: Before enabling BEFORE triggers, check DB2 behavior.
    for(int i =0 ; i < noDependents; i++)
    {
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).fireBeforeTriggers();
      }
      else{
        ((DeleteCascadeResultSet)dependentResultSets[i]).fireBeforeTriggers(msht);
      }
    }

    //If there is more than one node for the same table
    //only one node fires the triggers
    if(mainNodeForTable && constants.deferred)
      super.fireBeforeTriggers();
  }

    void fireAfterTriggers() throws StandardException
  {
    //fire the After Triggers on the dependent tables, if any rows changed
    for(int i=0 ; i<noDependents && affectedRows; i++){
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).fireAfterTriggers();
      }
      else{

        ((DeleteCascadeResultSet)dependentResultSets[i]).fireAfterTriggers();
      }
    }

    //If there is more than one node for the same table
    //, we let only one node fire the triggers.
    if(mainNodeForTable && constants.deferred)
      super.fireAfterTriggers();
  }

  void deleteDeferredRows() throws StandardException
  {
   
    //delete the rows in the  dependents tables
    for(int i =0 ; i < noDependents; i++)
    {
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).updateDeferredRows();
      }
      else{
        ((DeleteCascadeResultSet)dependentResultSets[i]).deleteDeferredRows();
      }
    }

     
    //If there is more than one node for the same table
    //only one node deletes all the rows.
    if(mainNodeForTable)
      super.deleteDeferredRows();
  }

 
  void runFkChecker(boolean restrictCheckOnly) throws StandardException
  {

    //run the Foreign key or primary key Checker on the dependent tables
    for(int i =0 ; i < noDependents; i++)
    {   
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).runChecker(restrictCheckOnly);
      }
      else{
        ((DeleteCascadeResultSet)dependentResultSets[i]).runFkChecker(restrictCheckOnly);
      }
    }

    //If there  is more than one node for the same table
    //only one node does all foreign key checks.
    if(mainNodeForTable)
      super.runFkChecker(restrictCheckOnly);
  }


  public void cleanUp() throws StandardException
  {

    super.cleanUp();
    for(int i =0 ; i < noDependents; i++)
    {
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).cleanUp();
      }else
      {
        ((DeleteCascadeResultSet) dependentResultSets[i]).cleanUp();
      }
    }
   
    endTime = getCurrentTimeMillis();
  }


  private void rowChangerFinish() throws StandardException
  {

    rc.finish();
    for(int i =0 ; i < noDependents; i++)
    {
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        ((UpdateResultSet) dependentResultSets[i]).rowChangerFinish();
      }else
      {
        ((DeleteCascadeResultSet) dependentResultSets[i]).rowChangerFinish();
      }
    }
  }



  //if there is more than one node for the same table, copy the rows
  // into one node , so that we don't fire trigger more than once.
  private void mergeRowHolders(Hashtable msht) throws StandardException
  {
    if(msht.containsKey(resultSetId) || rowCount ==0)
    {
      //there is already another resultset node that is marked as main
      //node for this table or this resultset has no rows qualified.
      //when none of the  resultset nodes for the table has any rows then
      //we mark them as one them as main node in fireBeforeTriggers().
      mainNodeForTable = false;
    }else
    {
      mergeResultSets();
      mainNodeForTable = true;
      msht.put(resultSetId, resultSetId);
    }
   
    for(int i =0 ; i < noDependents; i++)
    {   
      if(dependentResultSets[i] instanceof UpdateResultSet)
      {
        return;
      }
      else{
        ((DeleteCascadeResultSet)dependentResultSets[i]).mergeRowHolders(msht);
      }
    }
  }



  private void mergeResultSets() throws StandardException
  {
    Vector sVector = (Vector) activation.getParentResultSet(resultSetId);
    int size = sVector.size();
    // if there is more than one source, we need to merge them into onc
    // temporary result set.
    if(size > 1)
    {
      ExecRow    row = null;
      int rowHolderId = 0 ;
      //copy all the vallues in the result set to the current resultset row holder
      while(rowHolderId <  size)
      {
        if(rowHolderId == tempRowHolderId )
        {
          //skipping the row holder that  we are copying the rows into.
          rowHolderId++;
          continue;
        }
        TemporaryRowHolder currentRowHolder = (TemporaryRowHolder)sVector.elementAt(rowHolderId)
        CursorResultSet rs = currentRowHolder.getResultSet();
        rs.open();
        while ((row = rs.getNextRow()) != null)
        {
          rowHolder.insert(row);
        }
        rs.close();
        rowHolderId++;
      }
     
    }
  }


  public void finish() throws StandardException {
    super.finish();
   
    //clear the parent result sets hash table
    //This is necessary in case if we hit any error conditions
    activation.clearParentResultSets();
  }


  /* check whether we have mutiple path delete scenario, if
  ** find any retun true.  Multiple delete paths exist if we find more than
  ** one parent source resultset for a table involved in the delete cascade
  **/
  private boolean isMultipleDeletePathsExist()
  {
    Hashtable parentResultSets = activation.getParentResultSets();
    for (Enumeration e = parentResultSets.keys() ; e.hasMoreElements() ;)
    {
      String rsId  = (String) e.nextElement();
      Vector sVector = (Vector) activation.getParentResultSet(rsId);
      int size = sVector.size();
      if(size > 1)
      {
        return true;
      }
    }
    return false;
  }

  /*
  **Incases where we have multiple paths we could get the same
  **rows to be deleted  mutiple time and also in case of cycles
  **there might be new rows getting added to the row holders through
  **multiple iterations. To handle these case we set the temporary row holders
  ** to be  'uniqStream' type.
  **/
  private void setRowHoldersTypeToUniqueStream()
  {
    Hashtable parentResultSets = activation.getParentResultSets();
    for (Enumeration e = parentResultSets.keys() ; e.hasMoreElements() ;)
    {
      String rsId  = (String) e.nextElement();
      Vector sVector = (Vector) activation.getParentResultSet(rsId);
      int size = sVector.size();
      int rowHolderId = 0 ;
      while(rowHolderId <  size)
      {
        TemporaryRowHolder currentRowHolder = (TemporaryRowHolder)sVector.elementAt(rowHolderId)
        currentRowHolder.setRowHolderTypeToUniqueStream();
        rowHolderId++;
      }
    }
  }

}











TOP

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

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.