📄 ddutils.java
字号:
/* Derby - Class org.apache.derby.iapi.sql.dictionary.DDUtils Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed 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 org.apache.derby.iapi.error.StandardException;import org.apache.derby.iapi.reference.SQLState;import org.apache.derby.iapi.sql.StatementType;import java.util.Hashtable;import org.apache.derby.iapi.services.sanity.SanityManager;import org.apache.derby.iapi.services.i18n.MessageService;import java.util.Enumeration;/** * Static Data dictionary utilities. * * @version 0.1 * @author Rick Hillegas */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 = null; /* ** There were no column names specified, just find ** the primary key on the table in question */ String[] refColumnNames = otherConstraintInfo.getReferencedColumnNames(); if (refColumnNames == null || refColumnNames.length == 0) { 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; } /* ** Check the referenced columns vs. each unique or primary key to ** see if they match the foreign key. */ else { 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())) { 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; } String name; for (int index = 0; index < columnNames.length; index++) { name = ((ColumnDescriptor) 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. **/ 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 deleteConnHashtable = new Hashtable(); //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, (Hashtable) deleteConnHashtable.clone(), 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[CASACDE, SETNULL , RESTRICT ...etc).) **/ private static int getCurrentDeleteConnections ( DataDictionary dd, TableDescriptor td, int refActionType, Hashtable 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 = ((Integer)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, (new Integer(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; } /* ** Following function validates whether the new foreign key relation ship ** violates any restriction on the referential actions. Current refAction ** implementation does not allow cases where we can possible land up ** having multiple action for the same row in a table, this happens becase ** user can possibly define differential action through multiple paths. ** Following function throws error while creating foreign keys if the new ** releations ship leads to any such conditions. ** NOTE : SQL99 standard also does not cleary says what we are suppose to do ** in these non determenistic cases. ** Our implementation just follows what is did in DB2 and throws error ** messaged similar to DB2 (sql0632N, sql0633N, sql0634N) */ private static void validateDeleteConnection ( DataDictionary dd, TableDescriptor actualTd, // the table we are adding the foriegn key. TableDescriptor refTd, int refActionType, Hashtable dch, Hashtable 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; String refTableName = refTd.getSchemaName() + "." + refTd.getName(); /* ** Validate the new referentail action value with respect to the ** already existing connections to this table we gathered from ** the getCurrentDeleteConnections() call. */ if(checkImmediateRefTable) { rAction = ((Integer)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(new Integer(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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -