1 21 22 package org.apache.derby.iapi.sql.dictionary; 23 24 import org.apache.derby.iapi.error.StandardException; 25 26 import org.apache.derby.iapi.reference.SQLState; 27 import org.apache.derby.iapi.sql.StatementType; 28 import java.util.Hashtable ; 29 import org.apache.derby.iapi.services.sanity.SanityManager; 30 import org.apache.derby.iapi.services.i18n.MessageService; 31 import java.util.Enumeration ; 32 33 39 40 public class DDUtils 41 { 42 43 48 public static ReferencedKeyConstraintDescriptor locateReferencedConstraint 49 ( 50 DataDictionary dd, 51 TableDescriptor td, 52 String myConstraintName, String [] myColumnNames, 54 ConsInfo otherConstraintInfo 55 ) 56 throws StandardException 57 { 58 TableDescriptor refTd = otherConstraintInfo.getReferencedTableDescriptor(dd); 59 if (refTd == null) 60 { 61 throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_REF_TAB, 62 myConstraintName, 63 otherConstraintInfo.getReferencedTableName()); 64 } 65 66 67 ReferencedKeyConstraintDescriptor refCd = null; 68 69 73 String [] refColumnNames = otherConstraintInfo.getReferencedColumnNames(); 74 if (refColumnNames == null || 75 refColumnNames.length == 0) 76 { 77 refCd = refTd.getPrimaryKey(); 78 if (refCd == null) 79 { 80 throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_PK, 81 myConstraintName, 82 refTd.getQualifiedName()); 83 } 84 85 ColumnDescriptorList cdl = getColumnDescriptors(dd, td, myColumnNames); 86 87 91 if (cdl.size() != refCd.getColumnDescriptors().size()) 92 { 93 throw StandardException.newException(SQLState.LANG_INVALID_FK_DIFFERENT_COL_COUNT, 94 myConstraintName, String.valueOf(cdl.size()), 95 String.valueOf(refCd.getColumnDescriptors().size())); 96 } 97 98 101 if (!refCd.areColumnsComparable(cdl)) 102 { 103 throw StandardException.newException(SQLState.LANG_INVALID_FK_COL_TYPES_DO_NOT_MATCH, 104 myConstraintName); 105 } 106 107 return refCd; 108 } 109 110 114 else 115 { 116 ConstraintDescriptor cd; 117 118 ColumnDescriptorList colDl = getColumnDescriptors(dd, td, myColumnNames); 119 ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(refTd); 120 121 int refCDLSize = refCDL.size(); 122 for (int index = 0; index < refCDLSize; index++) 123 { 124 cd = refCDL.elementAt(index); 125 126 130 if ((cd instanceof ReferencedKeyConstraintDescriptor) && 131 cd.areColumnsComparable(colDl) && 132 columnNamesMatch(refColumnNames, 133 cd.getColumnDescriptors())) 134 { 135 return (ReferencedKeyConstraintDescriptor)cd; 136 } 137 } 138 139 142 throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_REF_KEY, myConstraintName, 143 refTd.getQualifiedName()); 144 } 145 } 146 147 public static ColumnDescriptorList getColumnDescriptors 148 ( 149 DataDictionary dd, 150 TableDescriptor td, 151 String [] columnNames 152 ) 153 throws StandardException 154 { 155 ColumnDescriptorList cdl = new ColumnDescriptorList(); 156 for (int colCtr = 0; colCtr < columnNames.length; colCtr++) 157 { 158 ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]); 159 cdl.add(td.getUUID(), cd); 160 } 161 return cdl; 162 } 163 164 public static boolean columnNamesMatch(String []columnNames, ColumnDescriptorList cdl) 165 throws StandardException 166 { 167 if (columnNames.length != cdl.size()) 168 { 169 return false; 170 } 171 172 String name; 173 for (int index = 0; index < columnNames.length; index++) 174 { 175 name = ((ColumnDescriptor) cdl.elementAt(index)).getColumnName(); 176 if (!name.equals(columnNames[index])) 177 { 178 return false; 179 } 180 } 181 182 return true; 183 } 184 185 186 190 public static void validateReferentialActions 191 ( 192 DataDictionary dd, 193 TableDescriptor td, 194 String myConstraintName, ConsInfo otherConstraintInfo, 196 String [] columnNames 197 ) 198 throws StandardException 199 { 200 201 202 int refAction = otherConstraintInfo.getReferentialActionDeleteRule(); 203 204 if(refAction == StatementType.RA_SETNULL) 207 { 208 boolean foundNullableColumn = false; 209 for (int colCtr = 0; colCtr < columnNames.length; colCtr++) 211 { 212 ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]); 213 if ((cd.getType().isNullable())) 214 { 215 foundNullableColumn = true; 216 break; 217 } 218 } 219 220 if(!foundNullableColumn) 221 { 222 throw StandardException.newException(SQLState.LANG_INVALID_FK_COL_FOR_SETNULL, 223 myConstraintName); 224 } 225 } 226 227 TableDescriptor refTd = otherConstraintInfo.getReferencedTableDescriptor(dd); 230 Hashtable deleteConnHashtable = new Hashtable (); 231 boolean isSelfReferencingFk = (refTd.getUUID().equals(td.getUUID())); 233 String refTableName = refTd.getSchemaName() + "." + refTd.getName(); 234 int currentSelfRefValue = getCurrentDeleteConnections(dd, td, -1, deleteConnHashtable, false, true); 236 validateDeleteConnection(dd, td, refTd, 237 refAction, 238 deleteConnHashtable, (Hashtable ) deleteConnHashtable.clone(), 239 true, myConstraintName, false , 240 new StringBuffer (0), refTableName, 241 isSelfReferencingFk, 242 currentSelfRefValue); 243 244 if(!isSelfReferencingFk) 246 { 247 checkForAnyExistingDeleteConnectionViolations(dd, td, 248 refAction, 249 deleteConnHashtable, 250 myConstraintName); 251 } 252 } 253 254 261 262 private static int getCurrentDeleteConnections 263 ( 264 DataDictionary dd, 265 TableDescriptor td, 266 int refActionType, 267 Hashtable dch, 268 boolean prevNotCascade, 269 boolean findSelfRef 270 ) 271 throws StandardException 272 { 273 274 int selfRefValue = -1; 276 td.emptyConstraintDescriptorList(); 278 ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td); 279 int cdlSize = cdl.size(); 280 281 boolean passedInPrevNotCascade = prevNotCascade; 282 for (int index = 0; index < cdlSize; index++) 283 { 284 ConstraintDescriptor cd = cdl.elementAt(index); 285 286 if ((cd instanceof ForeignKeyConstraintDescriptor)) 288 { 289 ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd; 290 String constraintName = fkcd.getConstraintName(); 291 int raDeleteRule = fkcd.getRaDeleteRule(); 292 int raUpdateRule = fkcd.getRaUpdateRule(); 293 294 if(findSelfRef && fkcd.isSelfReferencingFK()) 295 { 296 selfRefValue = raDeleteRule; 298 findSelfRef = false; 299 } 300 301 ReferencedKeyConstraintDescriptor refcd = 302 fkcd.getReferencedConstraint(); 303 TableDescriptor refTd = refcd.getTableDescriptor(); 304 int childRefAction = refActionType == -1 ? raDeleteRule : refActionType; 305 306 String refTableName = refTd.getSchemaName() + "." + refTd.getName(); 307 Integer rAction = ((Integer )dch.get(refTableName)); 309 if(rAction != null) { 311 prevNotCascade = passedInPrevNotCascade; 312 continue; 313 } 314 315 if(raDeleteRule != StatementType.RA_CASCADE) 320 { 321 if(prevNotCascade) 322 { 323 prevNotCascade = passedInPrevNotCascade; 324 continue; 325 } 326 else 327 prevNotCascade = true; 328 } 329 330 dch.put(refTableName, (new Integer (childRefAction))); 336 337 if(!fkcd.isSelfReferencingFK()) 340 getCurrentDeleteConnections(dd , refTd, childRefAction, 341 dch, true, false); 342 prevNotCascade = passedInPrevNotCascade; 343 } 344 } 345 346 return selfRefValue; 347 } 348 349 350 363 364 private static void validateDeleteConnection 365 ( 366 DataDictionary dd, 367 TableDescriptor actualTd, TableDescriptor refTd, 369 int refActionType, 370 Hashtable dch, 371 Hashtable ech, boolean checkImmediateRefTable, 373 String myConstraintName, 374 boolean prevNotCascade, 375 StringBuffer cycleString, 376 String currentRefTableName, boolean isSelfReferencingFk, 378 int currentSelfRefValue 379 ) 380 throws StandardException 381 { 382 383 Integer rAction; 384 385 String refTableName = refTd.getSchemaName() + "." + refTd.getName(); 386 387 388 393 394 if(checkImmediateRefTable) 395 { 396 rAction = ((Integer )dch.get(refTableName)); 397 398 if(isSelfReferencingFk) 400 { 401 if(currentSelfRefValue != -1) 406 { 407 if(currentSelfRefValue != refActionType) 408 { 409 if(currentSelfRefValue == StatementType.RA_SETNULL) 412 throw 413 generateError(SQLState.LANG_CANT_BE_DEPENDENT_ESELF, 414 myConstraintName, currentRefTableName); 415 else 416 { 417 423 throw 424 generateError(SQLState.LANG_DELETE_RULE_MUSTBE_ESELF, 425 myConstraintName, currentSelfRefValue); 426 } 427 }else 428 { 429 if(currentSelfRefValue == StatementType.RA_SETNULL && 431 refActionType == StatementType.RA_SETNULL) 432 { 433 throw 434 generateError(SQLState.LANG_CANT_BE_DEPENDENT_ESELF, 435 myConstraintName, currentRefTableName); 436 } 437 } 438 } 439 440 446 447 if(isSelfReferencingFk && dch.contains(new Integer (StatementType.RA_CASCADE)) && 448 refActionType!= StatementType.RA_CASCADE) 449 { 450 throw 451 generateError(SQLState.LANG_DELETE_RULE_MUSTBE_ECASCADE, 452 myConstraintName,StatementType.RA_CASCADE); 453 } 454 455 return; 457 } 458 459 461 466 if(currentSelfRefValue != -1) 467 { 468 if(refActionType == StatementType.RA_CASCADE && 469 currentSelfRefValue != StatementType.RA_CASCADE) 470 { 471 throw generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_ESELF, myConstraintName); 472 473 } 474 475 } 476 477 478 if(rAction != null) 481 { 482 checkForMultiplePathInvalidCases(rAction.intValue(), 483 refActionType, 484 myConstraintName,currentRefTableName); 485 } 486 487 488 if(refActionType != StatementType.RA_CASCADE) 490 { 491 prevNotCascade = true; 492 } 493 494 500 cycleString = cycleString.append(refActionType); 501 } 502 503 504 boolean passedInPrevNotCascade = prevNotCascade; 505 506 boolean multiPathCheck = true; 512 513 ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(refTd); 517 int refCDLSize = refCDL.size(); 518 for (int index = 0; index < refCDLSize; index++) 519 { 520 ConstraintDescriptor cd = refCDL.elementAt(index); 521 522 if ((cd instanceof ForeignKeyConstraintDescriptor)) 523 { 524 ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd; 525 String constraintName = fkcd.getConstraintName(); 526 int raDeleteRule = fkcd.getRaDeleteRule(); 527 int raUpdateRule = fkcd.getRaUpdateRule(); 528 529 ReferencedKeyConstraintDescriptor refcd = 530 fkcd.getReferencedConstraint(); 531 TableDescriptor nextRefTd = refcd.getTableDescriptor(); 532 533 if(raDeleteRule != StatementType.RA_CASCADE) 538 { 539 if(prevNotCascade) 540 { 541 prevNotCascade = passedInPrevNotCascade; 542 continue; 543 } 544 else 545 { 546 prevNotCascade = true; 547 multiPathCheck = false; 548 } 549 550 } 551 552 boolean isSelfRefLink = fkcd.isSelfReferencingFK(); 554 555 cycleString = cycleString.append(raDeleteRule); 559 boolean isFormingCycle = (nextRefTd.getUUID().equals(actualTd.getUUID())); 560 if(isFormingCycle) 561 { 562 for(int i = 0 ; i < cycleString.length(); i++) 565 { 566 int otherRefAction = Character.getNumericValue(cycleString.charAt(i)); 567 if(otherRefAction != refActionType) 568 { 569 if(otherRefAction != StatementType.RA_CASCADE) 573 { 574 throw generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_ECYCLE, myConstraintName); 575 } 576 else 577 { 578 throw 582 generateError(SQLState.LANG_CANT_BE_DEPENDENT_ECYCLE, 583 myConstraintName, currentRefTableName); 584 } 585 } 586 } 587 } 588 589 590 591 592 String nextRefTableName = nextRefTd.getSchemaName() + "." + nextRefTd.getName(); 593 rAction = ((Integer )ech.get(nextRefTableName)); 594 if(rAction != null) 595 { 596 604 if(!isSelfRefLink && multiPathCheck) 605 checkForMultiplePathInvalidCases(rAction.intValue(), 606 refActionType, 607 myConstraintName,currentRefTableName); 608 609 }else 610 { 611 rAction = ((Integer )dch.get(nextRefTableName)); 612 if(rAction == null) 613 { 614 if(multiPathCheck) 615 dch.put(nextRefTableName, (new Integer (refActionType))); 616 if(!isSelfRefLink) 617 { 618 validateDeleteConnection(dd, actualTd, nextRefTd, 619 refActionType, dch, ech, false, 620 myConstraintName,prevNotCascade, 621 cycleString, currentRefTableName, 622 isSelfReferencingFk, currentSelfRefValue); 623 } 624 } 625 } 626 prevNotCascade = passedInPrevNotCascade; 627 cycleString.setLength(cycleString.length() -1); 629 630 } 631 } 632 } 633 634 635 649 650 private static void checkForMultiplePathInvalidCases(int currentRefAction, 651 int refActionType, 652 String myConstraintName, 653 String currentRefTableName) 654 throws StandardException 655 { 656 657 if(currentRefAction != refActionType) 660 { 661 662 if(currentRefAction == StatementType.RA_SETNULL) 665 throw generateError(SQLState.LANG_CANT_BE_DEPENDENT_MPATH, 666 myConstraintName, currentRefTableName); 667 else 668 throw generateError(SQLState.LANG_DELETE_RULE_MUSTBE_MPATH, 671 myConstraintName, currentRefAction); 672 673 }else 674 { 675 if(currentRefAction == StatementType.RA_SETNULL && 677 refActionType == StatementType.RA_SETNULL) 678 { 679 throw 680 generateError(SQLState.LANG_CANT_BE_DEPENDENT_MPATH, 681 myConstraintName, currentRefTableName); 682 } 683 } 684 } 685 686 687 688 754 755 756 private static void checkForAnyExistingDeleteConnectionViolations 757 ( 758 DataDictionary dd, 759 TableDescriptor td, 760 int refActionType, 761 Hashtable newDconnHashTable, 762 String myConstraintName 763 ) 764 throws StandardException 765 { 766 767 if(refActionType != StatementType.RA_CASCADE) 770 return; 771 772 String addTableName = td.getSchemaName() + "." + td.getName();; 775 ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(td); 776 int refCDLSize = refCDL.size(); 777 for (int index = 0; index < refCDLSize; index++) 778 { 779 ConstraintDescriptor cd = refCDL.elementAt(index); 780 781 if ((cd instanceof ReferencedKeyConstraintDescriptor)) 782 { 783 ConstraintDescriptorList fkcdl = dd.getActiveConstraintDescriptors 784 ( ((ReferencedKeyConstraintDescriptor)cd).getForeignKeyConstraints(ConstraintDescriptor.ALL)); 785 786 int size = fkcdl.size(); 787 if (size == 0) 788 { 789 continue; 790 } 791 792 Hashtable dConnHashtable = new Hashtable (); 795 for (int inner = 0; inner < size; inner++) 796 { 797 ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) fkcdl.elementAt(inner); 798 TableDescriptor fktd = fkcd.getTableDescriptor(); 799 int raDeleteRuleToAddTable = fkcd.getRaDeleteRule(); 802 803 if(!fkcd.isSelfReferencingFK()) 805 { 806 807 810 getCurrentDeleteConnections(dd, fktd, -1, dConnHashtable, false, true); 811 812 819 820 for (Enumeration e = dConnHashtable.keys() ; e.hasMoreElements() ;) 821 { 822 String tName = (String ) e.nextElement(); 823 if(!tName.equals(addTableName)) 826 { 827 if(newDconnHashTable.containsKey(tName)) 828 { 829 int currentDeleteRule = ((Integer ) dConnHashtable.get(tName)).intValue(); 830 if((currentDeleteRule == StatementType.RA_SETNULL 831 && raDeleteRuleToAddTable == StatementType.RA_SETNULL) || 832 currentDeleteRule != raDeleteRuleToAddTable) 833 { 834 throw 835 generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_MPATH, 836 myConstraintName); 837 } 838 } 839 } 840 } 841 } 842 dConnHashtable.clear(); 845 } 846 } 847 } 848 } 849 850 851 852 private static StandardException generateError(String messageId, 853 String myConstraintName) 854 { 855 String message = MessageService.getTextMessage(messageId); 856 return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION, 857 myConstraintName, message); 858 } 859 860 private static StandardException generateError(String messageId, 861 String myConstraintName, 862 int raRule) 863 { 864 String raRuleStringId; 865 switch (raRule){ 866 case StatementType.RA_CASCADE: 867 raRuleStringId = SQLState.LANG_DELETE_RULE_CASCADE; 868 break; 869 case StatementType.RA_RESTRICT: 870 raRuleStringId = SQLState.LANG_DELETE_RULE_RESTRICT; 871 break; 872 case StatementType.RA_NOACTION: 873 raRuleStringId = SQLState.LANG_DELETE_RULE_NOACTION; 874 break; 875 case StatementType.RA_SETNULL: 876 raRuleStringId = SQLState.LANG_DELETE_RULE_SETNULL; 877 break; 878 case StatementType.RA_SETDEFAULT: 879 raRuleStringId = SQLState.LANG_DELETE_RULE_SETDEFAULT; 880 break; 881 default: 882 raRuleStringId =SQLState.LANG_DELETE_RULE_NOACTION ; } 884 885 String raRuleMessageString = MessageService.getTextMessage(raRuleStringId); 886 String message = MessageService.getTextMessage(messageId, raRuleMessageString); 887 return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION, 888 myConstraintName, message); 889 } 890 891 private static StandardException generateError(String messageId, 892 String myConstraintName, 893 String refTableName) 894 { 895 896 String message = MessageService.getTextMessage(messageId, refTableName); 897 return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION, 898 myConstraintName, message); 899 } 900 901 } 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 | Popular Tags |