blob: 32b02ce0b51d7792f083d27396dbc53eabc34efd [file] [log] [blame]
/*
Derby - Class org.apache.derby.iapi.sql.dictionary.DDUtils
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.iapi.sql.dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
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.sql.StatementType;
/**
* Static Data dictionary utilities.
*
* @version 0.1
*/
public class DDUtils
{
/*
** For a foreign key, this is used to locate the referenced
** key using the ConstraintInfo. If it doesn't find the
** correct constraint it will throw an error.
*/
public static ReferencedKeyConstraintDescriptor locateReferencedConstraint
(
DataDictionary dd,
TableDescriptor td,
String myConstraintName, // for error messages
String[] myColumnNames,
ConsInfo otherConstraintInfo
)
throws StandardException
{
TableDescriptor refTd = otherConstraintInfo.getReferencedTableDescriptor(dd);
if (refTd == null)
{
throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_REF_TAB,
myConstraintName,
otherConstraintInfo.getReferencedTableName());
}
ReferencedKeyConstraintDescriptor refCd;
String[] refColumnNames = otherConstraintInfo.getReferencedColumnNames();
if (refColumnNames == null ||
refColumnNames.length == 0)
{
/*
** There were no column names specified, just find
** the primary key on the table in question
*/
refCd = refTd.getPrimaryKey();
if (refCd == null)
{
throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_PK,
myConstraintName,
refTd.getQualifiedName());
}
ColumnDescriptorList cdl = getColumnDescriptors(dd, td, myColumnNames);
/*
** Check the column list length to give a more informative
** error in case they aren't the same.
*/
if (cdl.size() != refCd.getColumnDescriptors().size())
{
throw StandardException.newException(SQLState.LANG_INVALID_FK_DIFFERENT_COL_COUNT,
myConstraintName, String.valueOf(cdl.size()),
String.valueOf(refCd.getColumnDescriptors().size()));
}
/*
** Make sure all types are the same.
*/
if (!refCd.areColumnsComparable(cdl))
{
throw StandardException.newException(SQLState.LANG_INVALID_FK_COL_TYPES_DO_NOT_MATCH,
myConstraintName);
}
return refCd;
}
else
{
/*
** Check the referenced columns vs. each unique or primary key to
** see if they match the foreign key.
*/
ConstraintDescriptor cd;
ColumnDescriptorList colDl = getColumnDescriptors(dd, td, myColumnNames);
ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(refTd);
int refCDLSize = refCDL.size();
for (int index = 0; index < refCDLSize; index++)
{
cd = refCDL.elementAt(index);
/*
** Matches if it is not a check or fk, and
** all the types line up.
*/
if ((cd instanceof ReferencedKeyConstraintDescriptor) &&
cd.areColumnsComparable(colDl) &&
columnNamesMatch(refColumnNames,
cd.getColumnDescriptors()))
{
if (cd.deferrable()) {
final int onDelete = otherConstraintInfo.
getReferentialActionDeleteRule();
if (onDelete == StatementType.RA_CASCADE ||
onDelete == StatementType.RA_SETNULL) {
// DERBY-532: Not yet implemented
throw StandardException.newException(
SQLState.LANG_INVALID_FK_REF_KEY,
myConstraintName,
refTd.getQualifiedName());
}
}
return (ReferencedKeyConstraintDescriptor)cd;
}
}
/*
** If we got here, we didn't find anything
*/
throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_REF_KEY, myConstraintName,
refTd.getQualifiedName());
}
}
public static ColumnDescriptorList getColumnDescriptors
(
DataDictionary dd,
TableDescriptor td,
String[] columnNames
)
throws StandardException
{
ColumnDescriptorList cdl = new ColumnDescriptorList();
for (int colCtr = 0; colCtr < columnNames.length; colCtr++)
{
ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]);
cdl.add(td.getUUID(), cd);
}
return cdl;
}
public static boolean columnNamesMatch(String []columnNames, ColumnDescriptorList cdl)
throws StandardException
{
if (columnNames.length != cdl.size())
{
return false;
}
for (int index = 0; index < columnNames.length; index++)
{
String name = cdl.elementAt(index).getColumnName();
if (!name.equals(columnNames[index]))
{
return false;
}
}
return true;
}
/*
**checks whether the foreign key relation ships referential action
**is violating the restrictions we have in the current system.
**/
@SuppressWarnings("UseOfObsoleteCollectionType")
public static void validateReferentialActions
(
DataDictionary dd,
TableDescriptor td,
String myConstraintName, // for error messages
ConsInfo otherConstraintInfo,
String[] columnNames
)
throws StandardException
{
int refAction = otherConstraintInfo.getReferentialActionDeleteRule();
//Do not allow ON DELETE SET NULL as a referential action
//if none of the foreign key columns are nullable.
if(refAction == StatementType.RA_SETNULL)
{
boolean foundNullableColumn = false;
//check if we have a nullable foreign key column
for (int colCtr = 0; colCtr < columnNames.length; colCtr++)
{
ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]);
if ((cd.getType().isNullable()))
{
foundNullableColumn = true;
break;
}
}
if(!foundNullableColumn)
{
throw StandardException.newException(SQLState.LANG_INVALID_FK_COL_FOR_SETNULL,
myConstraintName);
}
}
//check whether the foreign key relation ships referential action
//is not violating the restrictions we have in the current system.
TableDescriptor refTd = otherConstraintInfo.getReferencedTableDescriptor(dd);
Hashtable<String,Integer> deleteConnHashtable = new Hashtable<String,Integer>();
//find whether the foreign key is self referencing.
boolean isSelfReferencingFk = (refTd.getUUID().equals(td.getUUID()));
String refTableName = refTd.getSchemaName() + "." + refTd.getName();
//look for the other foreign key constraints on this table first
int currentSelfRefValue = getCurrentDeleteConnections(dd, td, -1, deleteConnHashtable, false, true);
validateDeleteConnection(dd, td, refTd,
refAction,
deleteConnHashtable,
new Hashtable<String, Integer>(deleteConnHashtable),
true, myConstraintName, false ,
new StringBuffer(0), refTableName,
isSelfReferencingFk,
currentSelfRefValue);
//if it not a selfreferencing key check for violation of exiting connections.
if(!isSelfReferencingFk)
{
checkForAnyExistingDeleteConnectionViolations(dd, td,
refAction,
deleteConnHashtable,
myConstraintName);
}
}
/**
* Finds the existing delete connection for the table and the referential
* actions that will occur and stores the information in the hash table.
* HashTable (key , value) = ( table name that this table is delete
* connected to, referential action that will occur if there is a delete on
* the table this table connected to [CASCADE, SET NULL, RESTRICT, NO
* ACTION] )
*/
@SuppressWarnings("UseOfObsoleteCollectionType")
private static int getCurrentDeleteConnections
(
DataDictionary dd,
TableDescriptor td,
int refActionType,
Hashtable<String,Integer> dch,
boolean prevNotCascade,
boolean findSelfRef
)
throws StandardException
{
int selfRefValue = -1; //store the self reference referential action
//make sure we get any foreign key constraints added earlier in the same statement.
td.emptyConstraintDescriptorList();
ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);
int cdlSize = cdl.size();
boolean passedInPrevNotCascade = prevNotCascade;
for (int index = 0; index < cdlSize; index++)
{
ConstraintDescriptor cd = cdl.elementAt(index);
//look for foreign keys
if ((cd instanceof ForeignKeyConstraintDescriptor))
{
ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd;
String constraintName = fkcd.getConstraintName();
int raDeleteRule = fkcd.getRaDeleteRule();
int raUpdateRule = fkcd.getRaUpdateRule();
if(findSelfRef && fkcd.isSelfReferencingFK())
{
//All self references will have same referential actions type
selfRefValue = raDeleteRule;
findSelfRef = false;
}
ReferencedKeyConstraintDescriptor refcd =
fkcd.getReferencedConstraint();
TableDescriptor refTd = refcd.getTableDescriptor();
int childRefAction = refActionType == -1 ? raDeleteRule : refActionType;
String refTableName = refTd.getSchemaName() + "." + refTd.getName();
//check with the existing references.
Integer rAction = dch.get(refTableName);
if(rAction != null) // we already looked at this table
{
prevNotCascade = passedInPrevNotCascade;
continue;
}
//if we are not cascading, check whether the link before
//this was cascade or not. If we travel through two NON CASCADE ACTION
//links then the delete connection is broken(only a delete can have further
// referential effects)
if(raDeleteRule != StatementType.RA_CASCADE)
{
if(prevNotCascade)
{
prevNotCascade = passedInPrevNotCascade;
continue;
}
else
prevNotCascade = true;
}
//store the delete connection info in the hash table,
//note that the referential action value is not what is
//not specified on the current link. It is actually the
//value of what happens to the table whose delete
// connections we are finding.
dch.put(refTableName, Integer.valueOf(childRefAction));
//find the next delete conectiions on this path for non
//self referencig delete connections.
if(!fkcd.isSelfReferencingFK())
getCurrentDeleteConnections(dd , refTd, childRefAction,
dch, true, false);
prevNotCascade = passedInPrevNotCascade;
}
}
return selfRefValue;
}
/**
* The following function validates whether the new foreign key relationship
* violates any restriction on the referential actions. The current
* refAction implementation does not allow cases where we can possible land
* up having multiple action for the same row in a table. This could happen
* because the user can possibly define different actions through multiple
* foreign key paths. The following function throws an error while creating
* foreign keys if the new releationship leads to any such conditions.
* NOTE: The SQL99 standard also does not clearly says what we are supposed
* to do in these cases. Our implementation just follows what is done in
* DB2 and throws error messaged similar to DB2 (sql0632N, sql0633N,
* sql0634N).
*/
@SuppressWarnings("UseOfObsoleteCollectionType")
private static void validateDeleteConnection
(
DataDictionary dd,
TableDescriptor actualTd, // the table we are adding the foriegn key.
TableDescriptor refTd,
int refActionType,
Hashtable<String,Integer> dch,
Hashtable<String,Integer> ech, //existing delete connections
boolean checkImmediateRefTable,
String myConstraintName,
boolean prevNotCascade,
StringBuffer cycleString,
String currentRefTableName, //the name of the table we are referring too.
boolean isSelfReferencingFk,
int currentSelfRefValue
)
throws StandardException
{
Integer rAction;
/*
** Validate the new referentail action value with respect to the
** already existing connections to this table we gathered from
** the getCurrentDeleteConnections() call.
*/
if(checkImmediateRefTable)
{
String refTableName = refTd.getSchemaName() + "." + refTd.getName();
rAction = dch.get(refTableName);
// check possible invalide cases incase of self referencing foreign key
if(isSelfReferencingFk)
{
//All the relation ship referring to a table should have the
//same refaction except incase of SET NULL. In this case
//it is the same table , so we have to check with existing self
//referencing actions.
if(currentSelfRefValue != -1)
{
if(currentSelfRefValue != refActionType)
{
//If there is a SET NULL relation ship we can not have any
// other relation ship with it.
if(currentSelfRefValue == StatementType.RA_SETNULL)
throw
generateError(SQLState.LANG_CANT_BE_DEPENDENT_ESELF,
myConstraintName, currentRefTableName);
else
{
/*
** case where we can cleary say what the
** referential actions should be. Like,
** if there is NO ACTION relationsip
**already, new relation ship also shold be NO ACTION.
*/
throw
generateError(SQLState.LANG_DELETE_RULE_MUSTBE_ESELF,
myConstraintName, currentSelfRefValue);
}
}else
{
//more than one ON DELET SET NULL to the same table is not allowed
if(currentSelfRefValue == StatementType.RA_SETNULL &&
refActionType == StatementType.RA_SETNULL)
{
throw
generateError(SQLState.LANG_CANT_BE_DEPENDENT_ESELF,
myConstraintName, currentRefTableName);
}
}
}
/*
** If the new releation ship is self referencing and if
** the current existing relation ship to other tables is
** CASCADE type them new self reference should be of type
** CASCADE, otherwise we should throw error.
*/
if( isSelfReferencingFk &&
dch.contains(Integer.valueOf(StatementType.RA_CASCADE)) &&
refActionType != StatementType.RA_CASCADE)
{
throw
generateError(SQLState.LANG_DELETE_RULE_MUSTBE_ECASCADE,
myConstraintName,StatementType.RA_CASCADE);
}
//end of possible error case scenarios for self reference key additions
return;
}
//cases where the new reference is referring to another table
//check whether it matched with existing self references.
// If A self-referencing constraint exists with a delete rule of
// SET NULL, NO ACTION or RESTRICT. We can not add CASCADE
// relationship with another table.
if(currentSelfRefValue != -1)
{
if(refActionType == StatementType.RA_CASCADE &&
currentSelfRefValue != StatementType.RA_CASCADE)
{
throw generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_ESELF, myConstraintName);
}
}
//check for the cases with existing relationships to the
//referenced table
if(rAction != null)
{
checkForMultiplePathInvalidCases(rAction.intValue(),
refActionType,
myConstraintName,currentRefTableName);
}
//mark the current connect to the reference table to identify the cycle.
if(refActionType != StatementType.RA_CASCADE)
{
prevNotCascade = true;
}
/*
** cycle string is used to keep track of the referential actions of
** the nodes we visited, this is required to make sure that in case
** of cycles , all the nodes in the cycle have same type of
** referential action.
**/
cycleString.append(refActionType);
}
boolean passedInPrevNotCascade = prevNotCascade;
//delete connection is broken for if we see ON DELET SET NULL link
// one level deeper than the table we are adding the foreing key
//Where as to check for cycles we need to go for more level also;
// To check cases like CASCADE CASCADE SET NULL cycle is not valid.
//Following variable is used make the distinction.
boolean multiPathCheck = true;
// check for cases where the new connection we are forming to the
// reference table could create invalid any cycles or mutiple paths
// with the delete-connections the referencing table might have already.
ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(refTd);
int refCDLSize = refCDL.size();
for (int index = 0; index < refCDLSize; index++)
{
ConstraintDescriptor cd = refCDL.elementAt(index);
if ((cd instanceof ForeignKeyConstraintDescriptor))
{
ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd;
String constraintName = fkcd.getConstraintName();
int raDeleteRule = fkcd.getRaDeleteRule();
int raUpdateRule = fkcd.getRaUpdateRule();
ReferencedKeyConstraintDescriptor refcd =
fkcd.getReferencedConstraint();
TableDescriptor nextRefTd = refcd.getTableDescriptor();
//if we are not cascading, check whether the link before
//this was cascade or not. If we travel through two NON CASCADE ACTION
//links then the delete connection is broken(only a delete can have further
//referential effects)
if(raDeleteRule != StatementType.RA_CASCADE)
{
if(prevNotCascade)
{
prevNotCascade = passedInPrevNotCascade;
continue;
}
else
{
prevNotCascade = true;
multiPathCheck = false;
}
}
//check whether the current link is a self referencing one
boolean isSelfRefLink = fkcd.isSelfReferencingFK();
//check for this is non self referencing cycles case
//In cases of cycle, whole cycle should have the same refAction
// value. Other wise we should throw an exception
cycleString.append(raDeleteRule);
boolean isFormingCycle = (nextRefTd.getUUID().equals(actualTd.getUUID()));
if(isFormingCycle)
{
//make sure that all the nodes in the cycle have the same
//referential action value, otherwise we should throw an error.
for(int i = 0 ; i < cycleString.length(); i++)
{
int otherRefAction = Character.getNumericValue(cycleString.charAt(i));
if(otherRefAction != refActionType)
{
//cases where one of the existing relation ships in
//the cycle is not cascade , so we can not have
// cascade relation ship.
if(otherRefAction != StatementType.RA_CASCADE)
{
throw generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_ECYCLE, myConstraintName);
}
else
{
//possibly all the other nodes in the cycle has
//cascade relationsship , we can not add a non
//cascade relation ship.
throw
generateError(SQLState.LANG_CANT_BE_DEPENDENT_ECYCLE,
myConstraintName, currentRefTableName);
}
}
}
}
String nextRefTableName = nextRefTd.getSchemaName() + "." + nextRefTd.getName();
rAction = ech.get(nextRefTableName);
if(rAction != null)
{
/*
** If the table name has entry in the hash table means, there
** is already a path to this table exists from the table
** the new foreign key relation ship is being formed.
** Note: refValue in the hash table is how the table we are
** adding the new relationsship is going to affected not
** current path refvalue.
**/
if(!isSelfRefLink && multiPathCheck)
checkForMultiplePathInvalidCases(rAction.intValue(),
refActionType,
myConstraintName,currentRefTableName);
}else
{
rAction = dch.get(nextRefTableName);
if(rAction == null)
{
if (multiPathCheck) {
dch.put(nextRefTableName,
Integer.valueOf(refActionType));
}
if(!isSelfRefLink)
{
validateDeleteConnection(dd, actualTd, nextRefTd,
refActionType, dch, ech, false,
myConstraintName,prevNotCascade,
cycleString, currentRefTableName,
isSelfReferencingFk, currentSelfRefValue);
}
}
}
prevNotCascade = passedInPrevNotCascade;
//removes the char added for the current call
cycleString.setLength(cycleString.length() -1);
}
}
}
/*
**Check whether the mulitple path case is valid or not following
** cases are invalid:
** case 1: The relationship causes the table to be delete-connected to
** the indicated table through multiple relationships and the
** delete rule of the existing relationship is SET NULL.
** case 2: The relationship would cause the table to be
** delete-connected to the same table through multiple
** relationships and such relationships must have the same
** delete rule (NO ACTION, RESTRICT or CASCADE).
** case 3: The relationship would cause another table to be
** delete-connected to the same table through multiple paths
** with different delete rules or with delete rule equal to SET NULL.
**/
private static void checkForMultiplePathInvalidCases(int currentRefAction,
int refActionType,
String myConstraintName,
String currentRefTableName)
throws StandardException
{
//All the relation ship referring to a table should have the
//same refaction except incase of SET NULL
if(currentRefAction != refActionType)
{
//If there is a SET NULL relation ship we can not have any
// other relation ship with it.
if(currentRefAction == StatementType.RA_SETNULL)
throw generateError(SQLState.LANG_CANT_BE_DEPENDENT_MPATH,
myConstraintName, currentRefTableName);
else
//This error say what the delete rule must be for the
// foreign key be valid
throw generateError(SQLState.LANG_DELETE_RULE_MUSTBE_MPATH,
myConstraintName, currentRefAction);
}else
{
//more than one ON DELET SET NULL to the same table is not allowed
if(currentRefAction == StatementType.RA_SETNULL &&
refActionType == StatementType.RA_SETNULL)
{
throw
generateError(SQLState.LANG_CANT_BE_DEPENDENT_MPATH,
myConstraintName, currentRefTableName);
}
}
}
/*
** Check whether the delete rule of FOREIGN KEY must not be CASCADE because
** the new relationship would cause another table to be delete-connected to
** the same table through multiple paths with different delete rules or with
** delete rule equal to SET NULL.
**
** For example :
** t1
** CASCADE / \ CASCADE
** / \
** t2 t3
** \ /
** SET NULL \ / CASCADE (Can we add this one ? NO)
** \ /
\t4/
**
** existing links:
** t2 references t1 ON DELETE CASCADE (fkey1)
** t3 references t1 ON DELETE CASCADE (fkey2)
** t2 reference t4 ON DELETE SET NULL (fkey3)
** Now if if try to add a new link i.e
** t4 references t3 ON DELETE SET NULL (fkey4)
** Say if we add it, then if we execute 'delete from t1'
** Because of referential actions , we will try to delete a row through
** one path and tries to update through another path.
** Nothing in standard that say whether we are suppose to delete the row
** or update the row. DB2UDB raises error when we try to create the
** foreign key fkey4, Derby also does the same.
**
** How we catch the error case ?
** Point to note here is the table(t4) we are adding the foreign key does
** not have a problem in this scenarion because we are adding a
** a CASACDE link , some other table(t2) that is referring
** can get multiple referential action paths. We can not
** this error case for self referencing links.
** Algorithm:
** -Gather the foreign keys that are
** referring(ReferencedKeyConstraintDescriptor) to the table we are adding
** foreign key, in our example case we get (fkey3 - table t2 -t4 link)
** for each ReferencedKeyConstraintDescriptor
** {
** 1)find the delete connections of the referring table.
** [getCurrentDeleteConnections() will return this hash table]
** 2) we already have collected the Delete connections
** in validDeleteConnections() for the actual table we are adding the
** foreign key.
** 3) Now check whether the referring table is also
** referring any table that the table we are adding
** foreign key has delete connection.
**
** for each table referring table delete connection hash table
** {
** if it is there in the actual table delete connection hash table
** {
** //In our example case we find t1 in both the hash tables.
** make sure we are having valid referential action
** from the existing path and the new path we got from
** new foreign key relation ship.
** //In our example case t2 has CASCADE relations with t1
** //Because of new foreign key added we also get
** //SET NULL relation ship with t1. This is not valid
** //so we should throw error.
** }
** }
** }
**/
@SuppressWarnings("UseOfObsoleteCollectionType")
private static void checkForAnyExistingDeleteConnectionViolations
(
DataDictionary dd,
TableDescriptor td,
int refActionType,
Hashtable<String,Integer> newDconnHashTable,
String myConstraintName
)
throws StandardException
{
//We need to check for the condition in this function only when we are
//adding ref action of type CASCADE
if(refActionType != StatementType.RA_CASCADE)
return;
//find the tables that are referring to the table we
//are adding the foreign key and check whether we violate their existing rules.
String addTableName = td.getSchemaName() + "." + td.getName();;
for (ConstraintDescriptor cd : dd.getConstraintDescriptors(td))
{
if ((cd instanceof ReferencedKeyConstraintDescriptor))
{
ConstraintDescriptorList fkcdl = dd.getActiveConstraintDescriptors
( ((ReferencedKeyConstraintDescriptor)cd).getForeignKeyConstraints(ConstraintDescriptor.ALL));
int size = fkcdl.size();
if (size == 0)
{
continue;
}
//Note: More than one table can refer to the same
//ReferencedKeyConstraintDescriptor, so we need to find all the tables.
Hashtable<String,Integer> dConnHashtable = new Hashtable<String,Integer>();
for (int inner = 0; inner < size; inner++)
{
ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) fkcdl.elementAt(inner);
TableDescriptor fktd = fkcd.getTableDescriptor();
//Delete rule that we have to the table we are adding the
// foreign key relation shop
int raDeleteRuleToAddTable = fkcd.getRaDeleteRule();
//This check should not be done on self referencing references.
if(!fkcd.isSelfReferencingFK())
{
//gather the delete connections of the table that is
//referring to the table we are adding foreign key relation ship
getCurrentDeleteConnections(dd, fktd, -1, dConnHashtable, false, true);
/*
**Find out if we introduced more than one delete connection
**paths to the table that are referring the table we adding
**the foreign key relatiosn ship.
**If we have multiple paths they should have the same type
**referential action and only one SET NULL path.
**/
for (Enumeration<String> e = dConnHashtable.keys() ; e.hasMoreElements() ;)
{
String tName = e.nextElement();
//we should not check for the table name to which we are
//adding the foreign key relation ship.
if(!tName.equals(addTableName))
{
if(newDconnHashTable.containsKey(tName))
{
int currentDeleteRule = (dConnHashtable.get(tName)).intValue();
if((currentDeleteRule == StatementType.RA_SETNULL
&& raDeleteRuleToAddTable == StatementType.RA_SETNULL) ||
currentDeleteRule != raDeleteRuleToAddTable)
{
throw
generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_MPATH,
myConstraintName);
}
}
}
}
}
//same hash table can be used for the other referring tables
//so clear the hash table.
dConnHashtable.clear();
}
}
}
}
private static StandardException generateError(String messageId,
String myConstraintName)
{
String message = MessageService.getTextMessage(messageId);
return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION,
myConstraintName, message);
}
private static StandardException generateError(String messageId,
String myConstraintName,
int raRule)
{
String raRuleStringId;
switch (raRule){
case StatementType.RA_CASCADE:
raRuleStringId = "CASCADE";
break;
case StatementType.RA_RESTRICT:
raRuleStringId = "RESTRICT";
break;
case StatementType.RA_SETNULL:
raRuleStringId = "SET NULL";
break;
case StatementType.RA_SETDEFAULT:
raRuleStringId = "SET DEFAULT";
break;
case StatementType.RA_NOACTION:
default:
raRuleStringId = "NO ACTION";
break;
}
String raRuleMessageString = MessageService.getTextMessage(raRuleStringId);
String message = MessageService.getTextMessage(messageId, raRuleMessageString);
return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION,
myConstraintName, message);
}
private static StandardException generateError(String messageId,
String myConstraintName,
String refTableName)
{
String message = MessageService.getTextMessage(messageId, refTableName);
return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION,
myConstraintName, message);
}
}