KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > Constraint


1 /* Copyright (c) 1995-2000, The Hypersonic SQL Group.
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the Hypersonic SQL Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE HYPERSONIC SQL GROUP,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  * This software consists of voluntary contributions made by many individuals
31  * on behalf of the Hypersonic SQL Group.
32  *
33  *
34  * For work added by the HSQL Development Group:
35  *
36  * Copyright (c) 2001-2005, The HSQL Development Group
37  * All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions are met:
41  *
42  * Redistributions of source code must retain the above copyright notice, this
43  * list of conditions and the following disclaimer.
44  *
45  * Redistributions in binary form must reproduce the above copyright notice,
46  * this list of conditions and the following disclaimer in the documentation
47  * and/or other materials provided with the distribution.
48  *
49  * Neither the name of the HSQL Development Group nor the names of its
50  * contributors may be used to endorse or promote products derived from this
51  * software without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
54  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
57  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
58  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
59  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
60  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
61  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
62  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
63  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64  */

65
66
67 package org.hsqldb;
68
69 import org.hsqldb.HsqlNameManager.HsqlName;
70 import org.hsqldb.index.RowIterator;
71 import org.hsqldb.lib.ArrayUtil;
72 import org.hsqldb.lib.Iterator;
73
74 // fredt@users 20020225 - patch 1.7.0 by boucherb@users - named constraints
75
// fredt@users 20020320 - doc 1.7.0 - update
76
// tony_lai@users 20020820 - patch 595156 - violation of Integrity constraint name
77

78 /**
79  * Implementation of a table constraint with references to the indexes used
80  * by the constraint.<p>
81  *
82  * @author Thomas Mueller (Hypersonic SQL Group)
83  * @version 1.8.0
84  * @since Hypersonic SQL
85  */

86 class Constraint {
87
88     /*
89      SQL CLI codes
90
91      Referential Constraint 0 CASCADE
92      Referential Constraint 1 RESTRICT
93      Referential Constraint 2 SET NULL
94      Referential Constraint 3 NO ACTION
95      Referential Constraint 4 SET DEFAULT
96      */

97     static final int CASCADE = 0,
98                      SET_NULL = 2,
99                      NO_ACTION = 3,
100                      SET_DEFAULT = 4,
101                      INIT_DEFERRED = 5,
102                      INIT_IMMEDIATE = 6,
103                      NOT_DEFERRABLE = 7;
104     static final int FOREIGN_KEY = 0,
105                      MAIN = 1,
106                      UNIQUE = 2,
107                      CHECK = 3,
108                      PRIMARY_KEY = 4;
109     ConstraintCore core;
110     HsqlName constName;
111     int constType;
112
113     /**
114      * Constructor declaration for PK and UNIQUE
115      */

116     Constraint(HsqlName name, Table t, Index index, int type) {
117
118         core = new ConstraintCore();
119         constName = name;
120         constType = type;
121         core.mainTable = t;
122         core.mainIndex = index;
123         /* fredt - in unique constraints column list for iColMain is the
124            visible columns of iMain
125          */

126         core.mainColArray = ArrayUtil.arraySlice(index.getColumns(), 0,
127                 index.getVisibleColumns());
128         core.colLen = core.mainColArray.length;
129     }
130
131     /**
132      * Constructor for main constraints (foreign key references in PK table)
133      */

134     Constraint(HsqlName name, Constraint fkconstraint) {
135
136         constName = name;
137         constType = MAIN;
138         core = fkconstraint.core;
139     }
140
141     /**
142      * Constructor for foreign key constraints.
143      *
144      * @param pkname name in the main (referenced) table, used internally
145      * @param name name in the referencing table, public name of the constraint
146      * @param mainTable referenced table
147      * @param refTable referencing talbe
148      * @param mainCols array of column indexes in main table
149      * @param refCols array of column indexes in referencing table
150      * @param mainIndex index on the main table
151      * @param refIndex index on the referencing table
152      * @param deleteAction triggered action on delete
153      * @param updateAction triggered action on update
154      * @exception HsqlException
155      */

156     Constraint(HsqlName pkname, HsqlName name, Table mainTable,
157                Table refTable, int[] mainCols, int[] refCols,
158                Index mainIndex, Index refIndex, int deleteAction,
159                int updateAction) throws HsqlException {
160
161         core = new ConstraintCore();
162         core.pkName = pkname;
163         core.fkName = name;
164         constName = name;
165         constType = FOREIGN_KEY;
166         core.mainTable = mainTable;
167         core.refTable = refTable;
168         /* fredt - in FK constraints column lists for iColMain and iColRef have
169            identical sets to visible columns of iMain and iRef respectively
170            but the order of columns can be different and must be preserved
171          */

172         core.mainColArray = mainCols;
173         core.colLen = core.mainColArray.length;
174         core.refColArray = refCols;
175         core.mainIndex = mainIndex;
176         core.refIndex = refIndex;
177         core.deleteAction = deleteAction;
178         core.updateAction = updateAction;
179     }
180
181     /**
182      * temp constraint constructor
183      */

184     Constraint(HsqlName name, int[] mainCols, Table refTable, int[] refCols,
185                int type, int deleteAction, int updateAction) {
186
187         core = new ConstraintCore();
188         constName = name;
189         constType = type;
190         core.mainColArray = mainCols;
191         core.refTable = refTable;
192         core.refColArray = refCols;
193         core.deleteAction = deleteAction;
194         core.updateAction = updateAction;
195     }
196
197     private Constraint() {}
198
199     /**
200      * Returns the HsqlName.
201      */

202     HsqlName getName() {
203         return constName;
204     }
205
206     /**
207      * Changes constraint name.
208      */

209     private void setName(String JavaDoc name, boolean isquoted) throws HsqlException {
210         constName.rename(name, isquoted);
211     }
212
213     /**
214      * probably a misnomer, but DatabaseMetaData.getCrossReference specifies
215      * it this way (I suppose because most FKs are declared against the PK of
216      * another table)
217      *
218      * @return name of the index refereneced by a foreign key
219      */

220     String JavaDoc getPkName() {
221         return core.pkName == null ? null
222                                    : core.pkName.name;
223     }
224
225     /**
226      * probably a misnomer, but DatabaseMetaData.getCrossReference specifies
227      * it this way (I suppose because most FKs are declared against the PK of
228      * another table)
229      *
230      * @return name of the index for the referencing foreign key
231      */

232     String JavaDoc getFkName() {
233         return core.fkName == null ? null
234                                    : core.fkName.name;
235     }
236
237     /**
238      * Returns the type of constraint
239      */

240     int getType() {
241         return constType;
242     }
243
244     /**
245      * Returns the main table
246      */

247     Table getMain() {
248         return core.mainTable;
249     }
250
251     /**
252      * Returns the main index
253      */

254     Index getMainIndex() {
255         return core.mainIndex;
256     }
257
258     /**
259      * Returns the reference table
260      */

261     Table getRef() {
262         return core.refTable;
263     }
264
265     /**
266      * Returns the reference index
267      */

268     Index getRefIndex() {
269         return core.refIndex;
270     }
271
272     /**
273      * The ON DELETE triggered action of (foreign key) constraint
274      */

275     int getDeleteAction() {
276         return core.deleteAction;
277     }
278
279     /**
280      * The ON UPDATE triggered action of (foreign key) constraint
281      */

282     int getUpdateAction() {
283         return core.updateAction;
284     }
285
286     /**
287      * Returns the main table column index array
288      */

289     int[] getMainColumns() {
290         return core.mainColArray;
291     }
292
293     /**
294      * Returns the reference table column index array
295      */

296     int[] getRefColumns() {
297         return core.refColArray;
298     }
299
300     /**
301      * Returns true if an index is part this constraint and the constraint is set for
302      * a foreign key. Used for tests before dropping an index.
303      */

304     boolean isIndexFK(Index index) {
305
306         if (constType == FOREIGN_KEY || constType == MAIN) {
307             if (core.mainIndex == index || core.refIndex == index) {
308                 return true;
309             }
310         }
311
312         return false;
313     }
314
315     /**
316      * Returns true if an index is part this constraint and the constraint is set for
317      * a unique constraint. Used for tests before dropping an index.
318      */

319     boolean isIndexUnique(Index index) {
320         return (constType == UNIQUE && core.mainIndex == index);
321     }
322
323     /**
324      * Only for check constraints
325      */

326     boolean hasColumn(Table table, String JavaDoc colname) {
327
328         if (constType != CHECK) {
329             return false;
330         }
331
332         Expression.Collector coll = new Expression.Collector();
333
334         coll.addAll(core.check, Expression.COLUMN);
335
336         Iterator it = coll.iterator();
337
338         for (; it.hasNext(); ) {
339             Expression e = (Expression) it.next();
340
341             if (e.getColumnName().equals(colname)
342                     && table.tableName.name.equals(e.getTableName())) {
343                 return true;
344             }
345         }
346
347         return false;
348     }
349
350     boolean hasColumn(int colIndex) {
351
352         if (constType == MAIN) {
353             return ArrayUtil.find(core.mainColArray, colIndex) != -1;
354         } else if (constType == FOREIGN_KEY) {
355             return ArrayUtil.find(core.refColArray, colIndex) != -1;
356         }
357
358         return false;
359     }
360
361 // fredt@users 20020225 - patch 1.7.0 by fredt - duplicate constraints
362

363     /**
364      * Compares this with another constraint column set. This implementation
365      * only checks UNIQUE constraints.
366      */

367     boolean isEquivalent(int[] col, int type) {
368
369         if (type != constType || constType != UNIQUE
370                 || core.colLen != col.length) {
371             return false;
372         }
373
374         return ArrayUtil.haveEqualSets(core.mainColArray, col, core.colLen);
375     }
376
377     /**
378      * Compares this with another constraint column set. This implementation
379      * only checks FOREIGN KEY constraints.
380      */

381     boolean isEquivalent(Table tablemain, int[] colmain, Table tableref,
382                          int[] colref) {
383
384         if (constType != Constraint.MAIN
385                 && constType != Constraint.FOREIGN_KEY) {
386             return false;
387         }
388
389         if (tablemain != core.mainTable || tableref != core.refTable) {
390             return false;
391         }
392
393         return ArrayUtil.areEqualSets(core.mainColArray, colmain)
394                && ArrayUtil.areEqualSets(core.refColArray, colref);
395     }
396
397     /**
398      * Used to update constrains to reflect structural changes in a table.
399      * Prior checks must ensure that this method does not throw.
400      *
401      * @param oldt reference to the old version of the table
402      * @param newt referenct to the new version of the table
403      * @param colindex index at which table column is added or removed
404      * @param adjust -1, 0, +1 to indicate if column is added or removed
405      * @throws HsqlException
406      */

407     void replaceTable(Table oldt, Table newt, int colindex,
408                       int adjust) throws HsqlException {
409
410         if (oldt == core.mainTable) {
411             core.mainTable = newt;
412
413             // exclude CHECK
414
if (core.mainIndex != null) {
415                 core.mainIndex =
416                     core.mainTable.getIndex(core.mainIndex.getName().name);
417                 core.mainColArray =
418                     ArrayUtil.toAdjustedColumnArray(core.mainColArray,
419                                                     colindex, adjust);
420             }
421         }
422
423         if (oldt == core.refTable) {
424             core.refTable = newt;
425
426             if (core.refIndex != null) {
427                 core.refIndex =
428                     core.refTable.getIndex(core.refIndex.getName().name);
429
430                 if (core.refIndex != core.mainIndex) {
431                     core.refColArray =
432                         ArrayUtil.toAdjustedColumnArray(core.refColArray,
433                                                         colindex, adjust);
434                 }
435             }
436         }
437     }
438
439     /**
440      * Checks for foreign key or check constraint violation when
441      * inserting a row into the child table.
442      */

443     void checkInsert(Session session, Object JavaDoc[] row) throws HsqlException {
444
445         if (constType == Constraint.MAIN || constType == Constraint.UNIQUE
446                 || constType == Constraint.PRIMARY_KEY) {
447
448             // inserts in the main table are never a problem
449
// unique constraints are checked by the unique index
450
return;
451         }
452
453         if (constType == Constraint.CHECK) {
454             checkCheckConstraint(session, row);
455
456             return;
457         }
458
459         if (Index.isNull(row, core.refColArray)) {
460             return;
461         }
462
463         // a record must exist in the main table
464
boolean exists = core.mainIndex.exists(session, row,
465                                                core.refColArray);
466
467         if (!exists) {
468
469             // special case: self referencing table and self referencing row
470
if (core.mainTable == core.refTable) {
471                 boolean match = true;
472
473                 for (int i = 0; i < core.colLen; i++) {
474                     if (!row[core.refColArray[i]].equals(
475                             row[core.mainColArray[i]])) {
476                         match = false;
477
478                         break;
479                     }
480                 }
481
482                 if (match) {
483                     return;
484                 }
485             }
486
487             throw Trace.error(Trace.INTEGRITY_CONSTRAINT_VIOLATION_NOPARENT,
488                               Trace.Constraint_violation, new Object JavaDoc[] {
489                 core.fkName.name, core.mainTable.getName().name
490             });
491         }
492     }
493
494     /*
495      * Tests a row against this CHECK constraint.
496      */

497     void checkCheckConstraint(Session session,
498                               Object JavaDoc[] row) throws HsqlException {
499
500         core.checkFilter.currentData = row;
501
502         boolean nomatch = Boolean.FALSE.equals(core.check.test(session));
503
504         core.checkFilter.currentData = null;
505
506         if (nomatch) {
507             throw Trace.error(Trace.CHECK_CONSTRAINT_VIOLATION,
508                               Trace.Constraint_violation, new Object JavaDoc[] {
509                 constName.name, core.mainTable.tableName.name
510             });
511         }
512     }
513
514 // fredt@users 20020225 - patch 1.7.0 - cascading deletes
515

516     /**
517      * New method to find any referencing row for a
518      * foreign key (finds row in child table). If ON DELETE CASCADE is
519      * supported by this constraint, then the method finds the first row
520      * among the rows of the table ordered by the index and doesn't throw.
521      * Without ON DELETE CASCADE, the method attempts to finds any row that
522      * exists, in which case it throws an exception. If no row is found,
523      * null is returned.
524      * (fredt@users)
525      *
526      * @param row array of objects for a database row
527      * @param forDelete should we allow 'ON DELETE CASCADE' or 'ON UPDATE CASCADE'
528      * @return Node object or null
529      * @throws HsqlException
530      */

531     RowIterator findFkRef(Session session, Object JavaDoc[] row,
532                           boolean delete) throws HsqlException {
533
534         if (row == null || Index.isNull(row, core.mainColArray)) {
535             return core.refIndex.emptyIterator();
536         }
537
538         return delete
539                ? core.refIndex.findFirstRowForDelete(session, row,
540                    core.mainColArray)
541                : core.refIndex.findFirstRow(session, row, core.mainColArray);
542     }
543
544     /**
545      * For the candidate table row, finds any referring node in the main table.
546      * This is used to check referential integrity when updating a node. We
547      * have to make sure that the main table still holds a valid main record.
548      * If a valid row is found the corresponding <code>Node</code> is returned.
549      * Otherwise a 'INTEGRITY VIOLATION' Exception gets thrown.
550      */

551     boolean hasMainRef(Session session, Object JavaDoc[] row) throws HsqlException {
552
553         if (Index.isNull(row, core.refColArray)) {
554             return false;
555         }
556
557         boolean exists = core.mainIndex.exists(session, row,
558                                                core.refColArray);
559
560         // -- there has to be a valid node in the main table
561
// --
562
if (!exists) {
563             throw Trace.error(Trace.INTEGRITY_CONSTRAINT_VIOLATION_NOPARENT,
564                               Trace.Constraint_violation, new Object JavaDoc[] {
565                 core.fkName.name, core.refTable.getName().name
566             });
567         }
568
569         return exists;
570     }
571
572     /**
573      * Test used before adding a new foreign key constraint. This method
574      * returns true if the given row has a corresponding row in the main
575      * table. Also returns true if any column covered by the foreign key
576      * constraint has a null value.
577      */

578     private static boolean hasReferencedRow(Session session,
579             Object JavaDoc[] rowdata, int[] rowColArray,
580             Index mainIndex) throws HsqlException {
581
582         if (Index.isNull(rowdata, rowColArray)) {
583             return true;
584         }
585
586         // else a record must exist in the main index
587
return mainIndex.exists(session, rowdata, rowColArray);
588     }
589
590     /**
591      * Check used before creating a new foreign key cosntraint, this method
592      * checks all rows of a table to ensure they all have a corresponding
593      * row in the main table.
594      */

595     static void checkReferencedRows(Session session, Table table,
596                                     int[] rowColArray,
597                                     Index mainIndex) throws HsqlException {
598
599         RowIterator it = table.getPrimaryIndex().firstRow(session);
600
601         while (true) {
602             Row row = it.next();
603
604             if (row == null) {
605                 break;
606             }
607
608             Object JavaDoc[] rowdata = row.getData();
609
610             if (!Constraint.hasReferencedRow(session, rowdata, rowColArray,
611                                              mainIndex)) {
612                 String JavaDoc colvalues = "";
613
614                 for (int i = 0; i < rowColArray.length; i++) {
615                     Object JavaDoc o = rowdata[rowColArray[i]];
616
617                     colvalues += o;
618                     colvalues += ",";
619                 }
620
621                 throw Trace.error(
622                     Trace.INTEGRITY_CONSTRAINT_VIOLATION_NOPARENT,
623                     Trace.Constraint_violation, new Object JavaDoc[] {
624                     colvalues, table.getName().name
625                 });
626             }
627         }
628     }
629 }
630
Popular Tags