EMMA Coverage Report (generated Wed Jun 28 22:15:27 PDT 2006)
[all classes][org.apache.derby.iapi.sql.dictionary]

COVERAGE SUMMARY FOR SOURCE FILE [DDUtils.java]

nameclass, %method, %block, %line, %
DDUtils.java100% (1/1)92%  (11/12)96%  (812/846)95%  (197/207)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class DDUtils100% (1/1)92%  (11/12)96%  (812/846)95%  (197/207)
DDUtils (): void 0%   (0/1)0%   (0/3)0%   (0/1)
checkForAnyExistingDeleteConnectionViolations (DataDictionary, TableDescripto... 100% (1/1)100% (124/124)100% (28/28)
checkForMultiplePathInvalidCases (int, int, String, String): void 100% (1/1)100% (28/28)100% (7/7)
columnNamesMatch (String [], ColumnDescriptorList): boolean 100% (1/1)93%  (28/30)86%  (6/7)
generateError (String, String): StandardException 100% (1/1)100% (8/8)100% (2/2)
generateError (String, String, String): StandardException 100% (1/1)100% (9/9)100% (2/2)
generateError (String, String, int): StandardException 100% (1/1)74%  (23/31)67%  (10/15)
getColumnDescriptors (DataDictionary, TableDescriptor, String []): ColumnDesc... 100% (1/1)100% (25/25)100% (5/5)
getCurrentDeleteConnections (DataDictionary, TableDescriptor, int, Hashtable,... 100% (1/1)100% (116/116)100% (33/33)
locateReferencedConstraint (DataDictionary, TableDescriptor, String, String [... 100% (1/1)81%  (88/109)87%  (20/23)
validateDeleteConnection (DataDictionary, TableDescriptor, TableDescriptor, i... 100% (1/1)100% (265/265)100% (65/65)
validateReferentialActions (DataDictionary, TableDescriptor, String, ConsInfo... 100% (1/1)100% (98/98)100% (19/19)

1/*
2 
3   Derby - Class org.apache.derby.iapi.sql.dictionary.DDUtils
4 
5   Copyright 2000, 2004 The Apache Software Foundation or its licensors, as applicable.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11      http://www.apache.org/licenses/LICENSE-2.0
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 */
20 
21package org.apache.derby.iapi.sql.dictionary;
22 
23import org.apache.derby.iapi.error.StandardException;
24 
25import org.apache.derby.iapi.reference.SQLState;
26import org.apache.derby.iapi.sql.StatementType;
27import java.util.Hashtable;
28import org.apache.derby.iapi.services.sanity.SanityManager;
29import org.apache.derby.iapi.services.i18n.MessageService;
30import java.util.Enumeration;
31 
32/**
33 *        Static Data dictionary utilities.
34 *
35 * @version 0.1
36 * @author Rick Hillegas
37 */
38 
39public        class        DDUtils
40{
41 
42        /*
43        ** For a foreign key, this is used to locate the referenced
44        ** key using the ConstraintInfo.  If it doesn't find the
45        ** correct constraint it will throw an error.
46        */
47        public        static ReferencedKeyConstraintDescriptor locateReferencedConstraint
48        (
49                DataDictionary        dd,
50                TableDescriptor        td,
51                String                        myConstraintName,        // for error messages
52                String[]                myColumnNames,
53                ConsInfo                otherConstraintInfo
54        )
55                throws StandardException
56        {
57                TableDescriptor refTd = otherConstraintInfo.getReferencedTableDescriptor(dd);
58                if (refTd == null)
59                {
60                                throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_REF_TAB, 
61                                                                                                myConstraintName, 
62                                                                                                otherConstraintInfo.getReferencedTableName());
63                }
64 
65 
66                ReferencedKeyConstraintDescriptor refCd = null;
67 
68                /*
69                ** There were no column names specified, just find
70                ** the primary key on the table in question
71                */
72                String[]        refColumnNames = otherConstraintInfo.getReferencedColumnNames();
73                if (refColumnNames == null ||
74                        refColumnNames.length == 0)
75                {
76                        refCd = refTd.getPrimaryKey();
77                        if (refCd == null)
78                        {
79                                throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_PK, 
80                                                                                                myConstraintName, 
81                                                                                                refTd.getQualifiedName());
82                        }
83 
84                        ColumnDescriptorList cdl = getColumnDescriptors(dd, td, myColumnNames);
85 
86                        /*
87                        ** Check the column list length to give a more informative
88                        ** error in case they aren't the same.
89                        */
90                        if (cdl.size() != refCd.getColumnDescriptors().size())
91                        {
92                                throw StandardException.newException(SQLState.LANG_INVALID_FK_DIFFERENT_COL_COUNT, 
93                                                                                                myConstraintName, String.valueOf(cdl.size()), 
94                                                                                                String.valueOf(refCd.getColumnDescriptors().size())); 
95                        }
96        
97                        /*
98                        ** Make sure all types are the same.
99                        */        
100                        if (!refCd.areColumnsComparable(cdl))
101                        {
102                                throw StandardException.newException(SQLState.LANG_INVALID_FK_COL_TYPES_DO_NOT_MATCH, 
103                                                                                                myConstraintName);
104                        }
105 
106                        return refCd;        
107                }
108 
109                /*
110                ** Check the referenced columns vs. each unique or primary key to
111                ** see if they match the foreign key.
112                */
113                else
114                {
115                        ConstraintDescriptor cd;
116 
117                        ColumnDescriptorList colDl = getColumnDescriptors(dd, td, myColumnNames);
118                        ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(refTd);
119 
120                        int refCDLSize = refCDL.size();
121                        for (int index = 0; index < refCDLSize; index++)
122                        {
123                                cd = refCDL.elementAt(index);
124 
125                                /*
126                                ** Matches if it is not a check or fk, and
127                                ** all the types line up.
128                                */
129                                if ((cd instanceof ReferencedKeyConstraintDescriptor) &&
130                                         cd.areColumnsComparable(colDl) &&
131                                         columnNamesMatch(refColumnNames, 
132                                                                                cd.getColumnDescriptors()))
133                                {
134                                        return (ReferencedKeyConstraintDescriptor)cd;
135                                }
136                        }
137 
138                        /*
139                        ** If we got here, we didn't find anything
140                        */
141                        throw StandardException.newException(SQLState.LANG_INVALID_FK_NO_REF_KEY, myConstraintName, 
142                                                                                                                refTd.getQualifiedName());
143                }
144        }
145 
146    public        static ColumnDescriptorList getColumnDescriptors
147        (
148                DataDictionary        dd,
149                TableDescriptor td,
150                String[]                 columnNames
151        )
152                throws StandardException
153        {
154                ColumnDescriptorList cdl = new ColumnDescriptorList();
155                for (int colCtr = 0; colCtr < columnNames.length; colCtr++)
156                {
157                        ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]);
158                        cdl.add(td.getUUID(), cd);
159                }
160                return cdl;
161        }
162 
163        public        static boolean columnNamesMatch(String []columnNames, ColumnDescriptorList cdl)
164                throws StandardException
165        {
166                if (columnNames.length != cdl.size())
167                {
168                        return false;
169                }
170                
171                String name;
172                for (int index = 0; index < columnNames.length; index++)
173                {
174                        name = ((ColumnDescriptor) cdl.elementAt(index)).getColumnName();
175                        if (!name.equals(columnNames[index]))
176                        {
177                                return false;
178                        }
179                }
180 
181                return true;
182        }
183 
184 
185        /*
186        **checks whether the foreign key relation ships referential action
187        **is violating the restrictions we have in the current system.
188        **/
189        public static void validateReferentialActions
190    (
191                DataDictionary        dd,
192                TableDescriptor        td,
193                String                        myConstraintName,        // for error messages
194                ConsInfo                otherConstraintInfo,
195                String[]        columnNames
196        )
197                throws StandardException
198        {
199 
200 
201                int refAction = otherConstraintInfo.getReferentialActionDeleteRule();
202 
203                //Do not allow ON DELETE SET NULL as a referential action 
204                //if none of the foreign key columns are  nullable.
205                if(refAction == StatementType.RA_SETNULL)
206                {
207                        boolean foundNullableColumn = false;
208                        //check if we have a nullable foreign key column
209                        for (int colCtr = 0; colCtr < columnNames.length; colCtr++)
210                        {
211                                ColumnDescriptor cd = td.getColumnDescriptor(columnNames[colCtr]);
212                                if ((cd.getType().isNullable()))
213                                {
214                                        foundNullableColumn = true;
215                                        break;
216                                }
217                        }
218 
219                        if(!foundNullableColumn)
220                        {
221                                throw StandardException.newException(SQLState.LANG_INVALID_FK_COL_FOR_SETNULL, 
222                                                                                                         myConstraintName);
223                        }
224                }
225 
226                //check whether the foreign key relation ships referential action
227                //is not violating the restrictions we have in the current system.
228                TableDescriptor refTd = otherConstraintInfo.getReferencedTableDescriptor(dd);
229                Hashtable deleteConnHashtable = new Hashtable();
230                //find whether the foreign key is self referencing.
231                boolean isSelfReferencingFk = (refTd.getUUID().equals(td.getUUID()));
232                String refTableName = refTd.getSchemaName() + "." + refTd.getName();
233                //look for the other foreign key constraints on this table first
234                int currentSelfRefValue = getCurrentDeleteConnections(dd, td, -1, deleteConnHashtable, false, true);
235                validateDeleteConnection(dd, td, refTd, 
236                                                                 refAction, 
237                                                                 deleteConnHashtable, (Hashtable) deleteConnHashtable.clone(),
238                                                                 true, myConstraintName, false , 
239                                                                 new StringBuffer(0), refTableName,
240                                                                 isSelfReferencingFk,
241                                                                 currentSelfRefValue);
242 
243                //if it not a selfreferencing key check for violation of exiting connections.
244                if(!isSelfReferencingFk)
245                {
246                        checkForAnyExistingDeleteConnectionViolations(dd, td,
247                                                                                                                  refAction, 
248                                                                                                                  deleteConnHashtable, 
249                                                                                                                  myConstraintName);
250                }        
251        }
252 
253        /*
254        ** Finds the existing delete connection for the table and the referential
255        ** actions that will occur  and stores the information in the hash table.
256        ** HashTable (key , value) = ( table name that this table is delete
257        ** connected to, referential action that will occur if there is a delete on
258        ** the table this table connected to[CASACDE, SETNULL , RESTRICT ...etc).)
259        **/
260 
261        private        static int  getCurrentDeleteConnections
262        (
263         DataDictionary        dd,
264         TableDescriptor        td,
265         int refActionType,
266         Hashtable dch,
267         boolean prevNotCascade,
268         boolean findSelfRef
269         )
270                throws StandardException
271        {
272 
273                int selfRefValue = -1; //store the self reference referential action 
274 
275                //make sure we get any foreign key constraints added earlier in the same statement.
276                td.emptyConstraintDescriptorList();
277                ConstraintDescriptorList cdl = dd.getConstraintDescriptors(td);
278                int cdlSize = cdl.size();
279 
280                boolean passedInPrevNotCascade = prevNotCascade;
281                for (int index = 0; index < cdlSize; index++)
282                {
283                                ConstraintDescriptor cd = cdl.elementAt(index);
284 
285                                //look for  foreign keys
286                                if ((cd instanceof ForeignKeyConstraintDescriptor))
287                                {
288                                        ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd;
289                                        String constraintName = fkcd.getConstraintName();
290                                        int raDeleteRule = fkcd.getRaDeleteRule();
291                                        int raUpdateRule = fkcd.getRaUpdateRule();
292 
293                                         if(findSelfRef && fkcd.isSelfReferencingFK())
294                                         {
295                                                 //All self references will have same  referential actions type
296                                                 selfRefValue = raDeleteRule;
297                                                 findSelfRef = false;
298                                         }
299 
300                                        ReferencedKeyConstraintDescriptor refcd =
301                                                fkcd.getReferencedConstraint(); 
302                                        TableDescriptor refTd = refcd.getTableDescriptor();
303                                        int childRefAction = refActionType == -1 ? raDeleteRule : refActionType;
304                                   
305                                        String refTableName = refTd.getSchemaName() + "." + refTd.getName();
306                                        //check with  the existing references.
307                                        Integer rAction = ((Integer)dch.get(refTableName));
308                                        if(rAction != null) // we already looked at this table
309                                        {
310                                                prevNotCascade = passedInPrevNotCascade;
311                                                continue;
312                                        }
313 
314                                        //if we are not cascading, check whether the link before
315                                        //this was cascade or not. If we travel through  two NON CASCADE ACTION
316                                        //links then the  delete connection is broken(only a delete can have further
317                                        // referential effects)
318                                        if(raDeleteRule != StatementType.RA_CASCADE)
319                                        {
320                                                if(prevNotCascade)
321                                                {
322                                                        prevNotCascade = passedInPrevNotCascade;
323                                                        continue;
324                                                }
325                                                else
326                                                        prevNotCascade = true;
327                                        }
328 
329                                        //store the delete connection info in the hash table,
330                                        //note that the referential action value is not what is
331                                        //not specified on the current link. It is actually the 
332                                        //value of what happens to the table whose delete
333                                        // connections we are finding.
334                                        dch.put(refTableName, (new Integer(childRefAction)));
335                                        
336                                        //find the next delete conectiions on this path for non
337                                        //self referencig delete connections.
338                                        if(!fkcd.isSelfReferencingFK())
339                                                getCurrentDeleteConnections(dd , refTd, childRefAction,
340                                                                                                        dch, true, false);
341                                        prevNotCascade = passedInPrevNotCascade;
342                                }
343                }
344                
345                return selfRefValue;
346        }
347 
348 
349        /*
350        ** Following function validates whether the new foreign key relation ship
351        ** violates any restriction on the referential actions. Current refAction
352        ** implementation does not allow cases where we can possible land up
353        ** having multiple action for the same row in a table, this happens becase
354        ** user can possibly define differential action through multiple paths.
355        ** Following function throws error while creating foreign keys if the new
356        ** releations ship leads to any such conditions.
357        ** NOTE : SQL99 standard also does not cleary says what we are suppose to do
358        ** in these non determenistic cases. 
359        ** Our implementation just follows what is did in DB2 and throws error
360        ** messaged similar to DB2 (sql0632N, sql0633N, sql0634N)
361        */
362 
363        private        static void validateDeleteConnection
364        (
365                DataDictionary        dd,
366                TableDescriptor actualTd,  // the table we are adding the foriegn key.
367                TableDescriptor        refTd,
368                int refActionType,
369                Hashtable dch,
370                Hashtable ech,  //existing delete connections
371                boolean checkImmediateRefTable,
372                String myConstraintName,
373                boolean prevNotCascade,
374                StringBuffer cycleString, 
375                String currentRefTableName, //the name of the table we are referring too.
376                boolean isSelfReferencingFk,
377                int currentSelfRefValue
378                )
379                throws StandardException
380        {
381 
382                Integer rAction;
383 
384                String refTableName = refTd.getSchemaName() + "." + refTd.getName();
385 
386 
387                /*
388                ** Validate the new referentail action value with respect to the 
389                ** already existing connections to this table we gathered  from
390                ** the getCurrentDeleteConnections() call.
391                */
392 
393                if(checkImmediateRefTable)
394                {
395                        rAction = ((Integer)dch.get(refTableName));
396                        
397                        // check possible invalide cases incase of self referencing foreign key
398                        if(isSelfReferencingFk)
399                        {
400                                //All the relation ship referring to a table should have the
401                                //same refaction except incase of SET NULL. In this case
402                                //it is the same table , so we have to check with existing self
403                                //referencing actions.
404                                if(currentSelfRefValue !=  -1)
405                                {
406                                        if(currentSelfRefValue != refActionType)
407                                        {
408                                                //If there is a SET NULL relation ship we can not have any
409                                                // other relation ship with it.
410                                                if(currentSelfRefValue == StatementType.RA_SETNULL)
411                                                        throw
412                                                                generateError(SQLState.LANG_CANT_BE_DEPENDENT_ESELF, 
413                                                                                  myConstraintName, currentRefTableName);
414                                                else
415                                                {
416                                                                /*
417                                                                ** case where we can cleary say what the
418                                                                ** referential actions should be. Like,           
419                                                                ** if there is NO ACTION relationsip
420                                                                **already, new relation ship also shold be NO ACTION. 
421                                                                */
422                                                        throw
423                                                                generateError(SQLState.LANG_DELETE_RULE_MUSTBE_ESELF,
424                                                                                          myConstraintName, currentSelfRefValue);
425                                                }
426                                        }else
427                                        {
428                                                //more than one  ON DELET SET NULL to the same table is not allowed
429                                                if(currentSelfRefValue == StatementType.RA_SETNULL &&
430                                                   refActionType == StatementType.RA_SETNULL)
431                                                {
432                                                        throw
433                                                                generateError(SQLState.LANG_CANT_BE_DEPENDENT_ESELF,
434                                                                                          myConstraintName, currentRefTableName);
435                                                }
436                                        }
437                                }
438 
439                                /*
440                                ** If the new releation ship is self referencing and if
441                                ** the current existing relation ship to other tables is
442                                ** CASCADE type them  new self reference should be of type
443                                ** CASCADE, otherwise we should throw error.
444                                */
445 
446                                if(isSelfReferencingFk && dch.contains(new Integer(StatementType.RA_CASCADE)) && 
447                                   refActionType!=  StatementType.RA_CASCADE)
448                                {
449                                        throw
450                                                generateError(SQLState.LANG_DELETE_RULE_MUSTBE_ECASCADE,
451                                                                          myConstraintName,StatementType.RA_CASCADE);        
452                                }
453 
454                                //end of possible error case scenarios for self reference key additions
455                                return;
456                        }
457                
458                        //cases where the new  reference is referring to  another table
459 
460                        //check whether it matched with existing self references.
461                        // If A self-referencing constraint exists with a delete rule of
462                        // SET NULL,  NO ACTION or RESTRICT. We can not add CASCADE
463                        // relationship with another table.
464                                
465                        if(currentSelfRefValue !=  -1)
466                        {
467                                if(refActionType == StatementType.RA_CASCADE && 
468                                   currentSelfRefValue != StatementType.RA_CASCADE) 
469                                {
470                                        throw generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_ESELF,  myConstraintName);
471                                        
472                                }
473 
474                        }
475 
476                        
477                        //check for the cases with existing relationships to the
478                        //referenced table
479                        if(rAction != null)
480                        {
481                                checkForMultiplePathInvalidCases(rAction.intValue(),
482                                                                                                  refActionType,
483                                                                                                  myConstraintName,currentRefTableName);
484                        }
485 
486                        
487                        //mark the current connect to the reference table to identify the cycle.
488                        if(refActionType != StatementType.RA_CASCADE)
489                        {
490                                prevNotCascade = true;
491                        }
492                        
493                        /*
494                        ** cycle string is used to keep track of the referential actions of 
495                        ** the nodes we visited, this is required to make sure that in case
496                        ** of cycles , all the nodes in the cycle have same type of
497                        ** referential action.
498                        **/
499                        cycleString = cycleString.append(refActionType);
500                }
501 
502 
503                boolean passedInPrevNotCascade = prevNotCascade;
504 
505                //delete connection is broken for if we see ON DELET SET NULL link
506                // one level deeper than the table we are adding the foreing key
507                //Where as to check for cycles we need to go for more level also;
508        // To check cases like CASCADE CASCADE SET NULL cycle is not valid. 
509                //Following variable is used make the distinction.
510                boolean multiPathCheck = true;
511 
512                // check for cases where the new connection we are forming to the 
513                // reference table could create invalid any cycles or mutiple paths 
514                // with the delete-connections the  referencing table might have already.
515                ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(refTd);
516                int refCDLSize = refCDL.size();
517                for (int index = 0; index < refCDLSize; index++)
518                {
519                        ConstraintDescriptor cd = refCDL.elementAt(index);
520 
521                        if ((cd instanceof ForeignKeyConstraintDescriptor))
522                        {
523                                ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) cd;
524                                String constraintName = fkcd.getConstraintName();
525                                int raDeleteRule = fkcd.getRaDeleteRule();
526                                int raUpdateRule = fkcd.getRaUpdateRule();
527                                
528                                ReferencedKeyConstraintDescriptor refcd =
529                                        fkcd.getReferencedConstraint(); 
530                                TableDescriptor nextRefTd = refcd.getTableDescriptor();
531 
532                                //if we are not cascading, check  whether the link before
533                                //this was cascade or not. If we travel through  two NON CASCADE ACTION
534                                //links then the delete connection is broken(only a delete can have further
535                                //referential effects)
536                                if(raDeleteRule != StatementType.RA_CASCADE)
537                                {
538                                        if(prevNotCascade)
539                                        {
540                                                prevNotCascade = passedInPrevNotCascade;
541                                                continue;
542                                        }
543                                        else
544                                        {
545                                                prevNotCascade = true;
546                                                multiPathCheck = false;
547                                        }
548 
549                                }
550 
551                                //check whether the current link is a self referencing one
552                                boolean isSelfRefLink = fkcd.isSelfReferencingFK();
553                                
554                                //check for this is non self referencing cycles case
555                                //In cases of cycle, whole cycle should have the same refAction
556                                // value. Other wise we should throw an exception
557                                cycleString = cycleString.append(raDeleteRule);
558                                boolean isFormingCycle = (nextRefTd.getUUID().equals(actualTd.getUUID()));
559                                if(isFormingCycle)
560                                {
561                                        //make sure that all the nodes in the cycle have the same
562                                        //referential action  value, otherwise we should throw an error. 
563                                        for(int i = 0 ; i < cycleString.length(); i++)
564                                        {
565                                                int otherRefAction = Character.getNumericValue(cycleString.charAt(i));
566                                                if(otherRefAction != refActionType)
567                                                {
568                                                        //cases where one of the existing relation ships in
569                                                        //the cycle is not cascade , so we can not have
570                                                        // cascade relation ship.
571                                                        if(otherRefAction != StatementType.RA_CASCADE)
572                                                        {
573                                                                throw generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_ECYCLE, myConstraintName);
574                                                        }
575                                                        else
576                                                        {
577                                                                //possibly all the other nodes in the cycle has
578                                                                //cascade relationsship , we can not add a non
579                                                                //cascade relation ship.
580                                                                throw
581                                                                        generateError(SQLState.LANG_CANT_BE_DEPENDENT_ECYCLE, 
582                                                                                                  myConstraintName, currentRefTableName);
583                                                        }
584                                                }
585                                        }
586                                }        
587 
588 
589                                
590 
591                                String nextRefTableName =  nextRefTd.getSchemaName() + "." + nextRefTd.getName();
592                                rAction = ((Integer)ech.get(nextRefTableName));
593                                if(rAction != null)
594                                {
595                                        /*
596                                        ** If the table name has entry in the hash table means, there
597                                        ** is already  a path to this table exists from the table
598                                        ** the new foreign key relation ship is being formed.
599                                        ** Note: refValue in the hash table is how the table we are
600                                        ** adding the new relationsship is going to affected not
601                                        ** current path refvalue.
602                                        **/
603                                        if(!isSelfRefLink && multiPathCheck)
604                                                checkForMultiplePathInvalidCases(rAction.intValue(),
605                                                                                                                 refActionType, 
606                                                                                                                 myConstraintName,currentRefTableName);
607 
608                                }else
609                                {
610                                        rAction = ((Integer)dch.get(nextRefTableName));
611                                        if(rAction == null)
612                                        {
613                                                if(multiPathCheck)
614                                                        dch.put(nextRefTableName, (new Integer(refActionType)));
615                                                if(!isSelfRefLink)
616                                                {
617                                                        validateDeleteConnection(dd, actualTd,  nextRefTd,
618                                                                                                 refActionType, dch, ech, false,
619                                                                                                 myConstraintName,prevNotCascade,
620                                                                                                 cycleString, currentRefTableName, 
621                                                                                                 isSelfReferencingFk, currentSelfRefValue); 
622                                                }
623                                        }
624                                }
625                                prevNotCascade = passedInPrevNotCascade;
626                                //removes the char added for the current call
627                                cycleString.setLength(cycleString.length() -1);
628                                
629                        }
630                }
631        }
632 
633 
634        /*
635        **Check whether the mulitple path case is valid or not following
636        ** cases are invalid:
637        ** case 1: The relationship causes the table to be delete-connected to
638        ** the indicated table through multiple relationships and the
639        ** delete rule of the existing relationship is SET NULL. 
640        ** case 2: The relationship would cause the table to be
641        ** delete-connected to the same table through multiple
642        ** relationships and such relationships must have the same 
643        ** delete rule (NO ACTION, RESTRICT or CASCADE). 
644        ** case 3: The relationship would cause another table to be
645        ** delete-connected to the same table through multiple paths
646        ** with different delete rules or with delete rule equal to SET NULL. 
647        **/
648 
649        private static void checkForMultiplePathInvalidCases(int currentRefAction,
650                                                                                                                  int refActionType,
651                                                                                                                  String myConstraintName,
652                                                                                                                  String currentRefTableName)
653                throws StandardException
654        {
655 
656                //All the relation ship referring to a table should have the
657                //same refaction except incase of SET NULL
658                if(currentRefAction != refActionType)
659                {
660 
661                        //If there is a SET NULL relation ship we can not have any
662                        // other relation ship with it.
663                        if(currentRefAction == StatementType.RA_SETNULL)
664                                throw generateError(SQLState.LANG_CANT_BE_DEPENDENT_MPATH,
665                                                                        myConstraintName, currentRefTableName);
666                        else
667                                //This error say what the delete rule must be for the
668                                // foreign key be valid 
669                                throw generateError(SQLState.LANG_DELETE_RULE_MUSTBE_MPATH,
670                                                                        myConstraintName, currentRefAction);
671 
672                }else
673                {
674                        //more than one  ON DELET SET NULL to the same table is not allowed
675                        if(currentRefAction == StatementType.RA_SETNULL &&
676                           refActionType == StatementType.RA_SETNULL)
677                        {
678                                throw                
679                                        generateError(SQLState.LANG_CANT_BE_DEPENDENT_MPATH,
680                                                                  myConstraintName, currentRefTableName);
681                        }
682                }
683        }
684 
685 
686 
687    /*
688        ** Check whether the delete rule of FOREIGN KEY  must not be CASCADE because
689        ** the  new relationship would cause another table to be delete-connected to
690        ** the same table through multiple paths with different delete rules or with 
691        ** delete rule equal to SET NULL. 
692        **
693        ** For example :
694        **                      t1
695    **                   CASCADE   /  \  CASCADE
696    **                    /    \ 
697        **                  t2      t3
698    **                   \      /   
699    **          SET NULL  \    /  CASCADE (Can we add this one ? NO)
700         **                                  \   /
701                               \t4/
702        **                                        
703    **   existing links:
704        **   t2 references t1   ON DELETE CASCADE  (fkey1)
705        **   t3 references t1   ON DELETE CASCADE  (fkey2)
706        **   t2 reference  t4   ON DELETE SET NULL (fkey3)
707        **   Now if if try to add a new link i.e
708        **   t4 references t3   ON DELETE SET NULL  (fkey4)
709        **   Say if we add it,  then if we execute 'delete from t1' 
710        **   Because of referential actions , we will try to delete a row through
711        **   one path and tries to update  through another path. 
712        **   Nothing in standard that say whether we are suppose to delete the row
713        **   or update the row.  DB2UDB raises error when we try to create the
714        **   foreign key fkey4, cloudscape also does the same.
715        ** 
716        **   How we catch the error case ?
717        **   Point to note here is the table(t4) we are  adding the foreign key does
718        **   not have a problem in this scenarion because we are adding a
719    **   a CASACDE link , some other table(t2) that is referring  
720        **   can get multiple referential action paths. We can not
721        **   this error case for self referencing links.
722        **   Algorithm:
723        **   -Gather the foreign keys that are
724        **   referring(ReferencedKeyConstraintDescriptor) to the table we are adding
725        **   foreign key, in our example case we get (fkey3 - table t2 -t4 link)
726        **   for each ReferencedKeyConstraintDescriptor
727        **   {
728        **    1)find the delete connections of the referring table.
729        **    [getCurrentDeleteConnections() will return this hash table]
730        **          2) we already have collected the Delete connections 
731    **       in validDeleteConnections() for the actual table we are adding the 
732    **       foreign key.
733        **    3) Now check whether the referring table  is also 
734    **       referring  any table that the table we are adding
735    **       foreign key has delete connection.
736        **
737        **     for each table referring table delete connection hash table
738        **     {
739        **      if it is there in the actual table delete connection hash table
740    **      {
741        **         //In our example case we find t1 in both the hash tables.
742        **         make sure we are having valid referential action
743    **         from the existing path and the new path we got from 
744        **         new foreign key relation ship.
745        **        //In our example case t2 has CASCADE relations with t1
746        **        //Because of new foreign key added we also get 
747        **        //SET NULL relation ship with t1. This is not valid
748        **        //so we should throw error.
749    **      }  
750        **     }
751        ** }        
752        **/
753 
754 
755        private static void checkForAnyExistingDeleteConnectionViolations
756        (
757         DataDictionary        dd,
758         TableDescriptor td,
759         int refActionType,
760         Hashtable newDconnHashTable,
761         String myConstraintName
762         )
763        throws StandardException
764        {
765 
766                //We need to check for the condition in this function only when we are
767                //adding ref action of type CASCADE
768                if(refActionType != StatementType.RA_CASCADE)
769                        return;
770                
771                //find the tables that are referring to the table we 
772                //are adding the foreign key and check whether we violate their existing rules.
773                String addTableName = td.getSchemaName() + "." + td.getName();;
774                ConstraintDescriptorList refCDL = dd.getConstraintDescriptors(td);
775                int refCDLSize = refCDL.size();
776                for (int index = 0; index < refCDLSize; index++)
777                {
778                        ConstraintDescriptor cd = refCDL.elementAt(index);
779 
780                        if ((cd instanceof ReferencedKeyConstraintDescriptor))
781                        {
782                                ConstraintDescriptorList fkcdl = dd.getActiveConstraintDescriptors
783                                        ( ((ReferencedKeyConstraintDescriptor)cd).getForeignKeyConstraints(ConstraintDescriptor.ALL));
784        
785                                int size = fkcdl.size();
786                                if (size == 0) 
787                                { 
788                                        continue; 
789                                }
790                                
791                                //Note: More than one table can refer to the same
792                                //ReferencedKeyConstraintDescriptor, so we need to find all the tables.
793                                Hashtable dConnHashtable = new Hashtable();
794                                for (int inner = 0; inner < size; inner++)
795                                {
796                                        ForeignKeyConstraintDescriptor fkcd = (ForeignKeyConstraintDescriptor) fkcdl.elementAt(inner);
797                                        TableDescriptor fktd = fkcd.getTableDescriptor();
798                                        //Delete rule that we have to the table we are adding the
799                                        // foreign key relation shop
800                                        int raDeleteRuleToAddTable = fkcd.getRaDeleteRule();
801 
802                                        //This check should not be done on self referencing references.
803                                        if(!fkcd.isSelfReferencingFK())
804                                        {
805 
806                                                //gather the delete connections of the table that is
807                                                //referring to the table we are adding foreign key relation ship
808 
809                                                getCurrentDeleteConnections(dd, fktd, -1, dConnHashtable, false, true);
810 
811                                                /*
812                                                **Find out if we introduced more than one delete connection
813                                                **paths to the table that are referring the table we adding
814                                                **the foreign key relatiosn ship.
815                                                **If we have multiple paths they should have the same type
816                                                **referential action and only one SET NULL path.
817                                                **/
818 
819                                                for (Enumeration e = dConnHashtable.keys() ; e.hasMoreElements() ;) 
820                                                {
821                                                        String tName = (String) e.nextElement();
822                                                        //we should not check for the table name to which  we are
823                                                        //adding the foreign key relation ship.
824                                                        if(!tName.equals(addTableName))
825                                                        {
826                                                                if(newDconnHashTable.containsKey(tName))
827                                                                {
828                                                                        int currentDeleteRule = ((Integer)        dConnHashtable.get(tName)).intValue();
829                                                                        if((currentDeleteRule == StatementType.RA_SETNULL
830                                                                                && raDeleteRuleToAddTable == StatementType.RA_SETNULL) ||
831                                                                           currentDeleteRule  != raDeleteRuleToAddTable)
832                                                                        {
833                                                                                throw
834                                                                                        generateError(SQLState.LANG_DELETE_RULE_CANT_BE_CASCADE_MPATH, 
835                                                                                                                  myConstraintName);
836                                                                        }
837                                                                }
838                                                        }
839                                                }
840                                        }
841                                        //same hash table can be used for the other referring tables
842                                        //so clear the hash table.
843                                        dConnHashtable.clear();
844                                }
845                        }
846                }
847        }
848 
849 
850        
851        private static StandardException generateError(String messageId, 
852                                                                                                   String myConstraintName)
853        {
854                String message = MessageService.getTextMessage(messageId);
855                return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION, 
856                                                                                                myConstraintName, message);
857        }        
858 
859        private static StandardException generateError(String messageId, 
860                                                                                                   String myConstraintName, 
861                                                                                                   int raRule)
862        {
863                String raRuleStringId;
864                switch (raRule){
865                case StatementType.RA_CASCADE:
866                        raRuleStringId = SQLState.LANG_DELETE_RULE_CASCADE;
867                        break;
868                case StatementType.RA_RESTRICT:
869                        raRuleStringId = SQLState.LANG_DELETE_RULE_RESTRICT;
870                                break;
871                case StatementType.RA_NOACTION:
872                        raRuleStringId = SQLState.LANG_DELETE_RULE_NOACTION;
873                        break;
874                case StatementType.RA_SETNULL:
875                        raRuleStringId = SQLState.LANG_DELETE_RULE_SETNULL;
876                        break;
877                case StatementType.RA_SETDEFAULT:
878                        raRuleStringId = SQLState.LANG_DELETE_RULE_SETDEFAULT;
879                        break;
880                default: 
881                        raRuleStringId =SQLState.LANG_DELETE_RULE_NOACTION ; // NO ACTION (default value)
882                }
883 
884                String raRuleMessageString = MessageService.getTextMessage(raRuleStringId); 
885                String message = MessageService.getTextMessage(messageId, raRuleMessageString);
886                return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION, 
887                                                                                                myConstraintName, message);
888        }        
889 
890        private static StandardException generateError(String messageId, 
891                                                                                                   String myConstraintName,
892                                                                                                   String refTableName)
893        {
894 
895                String message = MessageService.getTextMessage(messageId, refTableName);
896                return StandardException.newException(SQLState.LANG_DELETE_RULE_VIOLATION, 
897                                                                                                myConstraintName, message);
898        }        
899 
900}
901 
902 
903 
904 
905 
906 
907 
908 
909 
910 
911 
912 
913 
914 
915 

[all classes][org.apache.derby.iapi.sql.dictionary]
EMMA 2.0.5312 (C) Vladimir Roubtsov