/derby-10.6.2.1/db-derby-10.6.2.1-src/java/engine/org/apache/derby/impl/sql/execute/DeleteCascadeResultSet.java
Java | 488 lines | 319 code | 79 blank | 90 comment | 48 complexity | 1ed17aba7146175aa6e7c296124c36a7 MD5 | raw file
Possible License(s): Apache-2.0
- /*
-
- 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++;
- }
- }
- }
-
- }
-
-
-
-
-
-
-
-
-
-
-
-