Source code: javatools/db/DbConstraint.java
1 /*
2 * DbConstraint.java
3 *
4 * Created on 12 febbraio 2002, 12.16
5 *
6 Javatools (modified version) - Some useful general classes.
7 Copyright (C) 2002-2003 Chris Bitmead (original) Antonio Petrelli (modified)
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23 Contact me at: brenmcguire@users.sourceforge.net
24 */
25
26 package javatools.db;
27
28 import java.util.*;
29 import javatools.util.ArrayComparator;
30 import javatools.util.NullUtils;
31
32 /**
33 * This abstract class represents a generic set of constraints for a table. It
34 * contains a lot of code already written for generic purposes, i.e. it can be
35 * derived with few code. If you want to add additional checks, you can easily add
36 * your own code, rewriting <CODE>check</CODE> and <CODE>update</CODE> methods.
37 * @author Antonio Petrelli
38 * @version 0.2.0
39 */
40 public abstract class DbConstraint {
41 /** It represents an INSERT operation.
42 */
43 public static final int INSERT_OPERATION = 1;
44 /** It represents a DELETE operation.
45 */
46 public static final int DELETE_OPERATION = 2;
47 /** It represents a normal UPDATE operation.
48 */
49 public static final int UPDATE_OPERATION = 3;
50 /** It means that an UPDATE-CASCADE operation is outgoing. It is protected because
51 * it is useful only for internal checks-updates.
52 */
53 protected static final int UPDATE_CASCADE = 4;
54
55 /** It means that, in case of an UPDATE/DELETE operation, the table MUST NOT do
56 * anything to repair itself.
57 *
58 */
59 public static final int DO_NOTHING = 0;
60 /** It means that, in case of an UPDATE/DELETE operation, the constraint MUST set
61 * an array of fields in involved rows to NULL values, to repair referenced table.
62 *
63 */
64 public static final int SET_NULL = 1;
65 /** It means that, in case of an UPDATE/DELETE operation, the constraint MUST set
66 * an array of fields in involved rows to DEFAULT values, to repair referenced
67 * table.
68 *
69 */
70 public static final int SET_DEFAULT = 2;
71 /** It means that, in case of an UPDATE/DELETE operation, the constraint MUST
72 * cascade the operation, that is updating/deleting in cascade all involved rows.
73 *
74 */
75 public static final int CASCADE = 3;
76
77 /** Creates new DbConstraint */
78
79 public DbConstraint() {
80 table = null;
81 automaticChecking = true;
82 refs = null;
83 deleteCascadeChildren = null;
84 updateCascadeChildren = null;
85 canDoUpdate = true;
86 cloned = false;
87 }
88
89 /** Creates a new DbConstraint for a specified table.
90 * @param tbl The table to be checked.
91 */
92 public DbConstraint(DbAbstractTable tbl) {
93 table = tbl;
94 automaticChecking = true;
95 refs = null;
96 deleteCascadeChildren = null;
97 updateCascadeChildren = null;
98 canDoUpdate = true;
99 cloned = false;
100 }
101
102 /** Returns the referenced table.
103 * @return The referenced table.
104 */
105 public DbAbstractTable getTable() {
106 return table;
107 }
108
109 /** Sets the "where" clause for an DELETE/UPDATE operation.
110 * @param pWhere The requested clause.
111 */
112 public void setWhere(DbExpr pWhere) {
113 where = pWhere;
114 }
115
116 /** Sets the selector for an INSERT operation.
117 * @param pSelector The requested selector.
118 */
119 public void setSelector(DbSelector pSelector) {
120 selector = pSelector;
121 }
122
123 /** Sets the lists to perform INSERT/UPDATE operations.
124 * @param pIntoList It will be put into <CODE>intoList</CODE>.
125 * @param pFromList It will be put into <CODE>fromList</CODE>.
126 */
127 public void setValueLists(List pIntoList, List pFromList) {
128 intoList = pIntoList;
129 fromList = pFromList;
130 }
131
132 /** Sets a flag to represent automatic checking at all times.
133 * @param value <CODE>true</CODE>: check is done at all times;
134 * <CODE>false</CODE>: check is performed only with an explicit call to
135 * <CODE>check</CODE>.
136 */
137 public void setAutomaticChecking (boolean value) {
138 automaticChecking = value;
139 }
140
141 /** Returns the current automatic checking setting.
142 * @return <CODE>true</CODE>: check is done at all times;
143 * <CODE>false</CODE>: check is performed only with an explicit call to
144 * <CODE>check</CODE>.
145 */
146 public boolean getAutomaticChecking () {
147 return automaticChecking;
148 }
149
150 /** Sets the constraint emulation.
151 * @param value <CODE>true</CODE>: constraint emulation is done as needed;
152 * <CODE>false</CODE>: constraint emulation is NEVER done.
153 */
154 public void setConstraintEmulation(boolean value) {
155 constraintEmulation = value;
156 }
157
158 /** Returns if constraint emulation is enabled.
159 * @return <CODE>true</CODE>: constraint emulation is done as needed;
160 * <CODE>false</CODE>: constraint emulation is NEVER done.
161 */
162 public boolean getConstraintEmulation() {
163 return constraintEmulation;
164 }
165
166 /** Returns the number of referenced father tables.
167 * @return The count of referenced father tables.
168 */
169 public int getFatherTablesCount() {
170 return fatherTables.length;
171 }
172
173 /** Returns the number of son tables.
174 * @return The son tables count.
175 */
176 public int getSonTablesCount() {
177 return sonTables.length;
178 }
179
180 /** Returns a father table whose index is specified.
181 * @param index The index of a father table.
182 * @throws DbException If something goes wrong.
183 * @return The requested father table.
184 */
185 public DbAbstractTable getFatherTable(int index) throws DbException {
186 if (index >= 0 && index < fatherTables.length) {
187 return fatherTables[index];
188 }
189 else
190 throw new DbException("Index out of bounds in getting a father table");
191 }
192
193 /** Returns a son table whose index is specified.
194 * @param index The index of a son table.
195 * @throws DbException If something goes wrong.
196 * @return The requested son table.
197 */
198 public DbAbstractTable getSonTable(int index) throws DbException {
199 if (index >= 0 && index < sonTables.length) {
200 return sonTables[index];
201 }
202 else
203 throw new DbException("Index out of bounds in getting a son table");
204 }
205
206 /** Returns in which position a son table references THIS table in its constraint.
207 * @param index The son table index.
208 * @throws DbException If something goes wrong.
209 * @return The needed index.
210 */
211 public int getSonFatherIndex(int index) throws DbException {
212 if (index >= 0 && index < sonIndexes.length) {
213 return sonIndexes[index].intValue();
214 }
215 else
216 throw new DbException("Index out of bounds in getting a son table");
217 }
218
219 /** Returns what kind of operation will be done in case of an update of the father
220 * table specified by <CODE>index</CODE>.
221 * @param index The index of the father table.
222 * @throws DbException If something goes wrong.
223 * @return The requested operation.
224 */
225 public int getUpdateOperation(int index) throws DbException {
226 if (index >= 0 && index < updateOperations.length) {
227 return updateOperations[index].intValue();
228 }
229 else
230 throw new DbException("Index out of bounds in getting an update operation");
231 }
232
233 /** Returns what kind of operation will be done in case of a deletion of the father
234 * table specified by <CODE>index</CODE>.
235 * @param index The index of the father table.
236 * @throws DbException If something goes wrong.
237 * @return The requested operation.
238 */
239 public int getDeleteOperation(int index) throws DbException {
240 if (index >= 0 && index < deleteOperations.length) {
241 return deleteOperations[index].intValue();
242 }
243 else
244 throw new DbException("Index out of bounds in getting a delete operation");
245 }
246
247 /** Returns the referenced father columns of a father table whose index is
248 * specified.
249 * @param index The index of the father table.
250 * @throws DbException If something goes wrong.
251 * @return An array containing the referenced father columns.
252 */
253 public DbColumn[] getFatherColumns(int index) throws DbException {
254 if (index >= 0 && index < fatherTablesColumns.length) {
255 return fatherTablesColumns[index];
256 }
257 else
258 throw new DbException("Index out of bounds in getting a father-columns-array");
259 }
260
261 /** Returns the referencing columns of THIS table for a father table whose index
262 * is specified.
263 * @param index The index of the father column.
264 * @throws DbException If something goes wrong.
265 * @return The referencing columns.
266 */
267 public DbColumn[] getRefColumns(int index) throws DbException {
268 if (index >= 0 && index < refColumns.length) {
269 return refColumns[index];
270 }
271 else
272 throw new DbException("Index out of bounds in getting a referenced-columns-array");
273 }
274
275 /** Returns an array containing the default values for fields of a father table
276 * whose index is specified.
277 * @param index The index of the father table.
278 * @throws DbException If something goes wrong.
279 * @return An array containing the default values for referencing columns related to
280 * requested father table.
281 */
282 public Object[] getDefaultValues(int index) throws DbException {
283 if (index >= 0 && index < defaultValues.length) {
284 return defaultValues[index];
285 }
286 else
287 throw new DbException("Index out of bounds in getting a default-values-array");
288 }
289
290 /** Clears everything.
291 */
292 public void clear() {
293 automaticChecking = true;
294 currentOperation = 0;
295 where = null;
296 selector = null;
297 intoList = null;
298 fromList = null;
299 searchSets = null;
300 sonsWhere = null;
301 fatherRecords = null;
302 deleteCascadeChildren = null;
303 updateCascadeChildren = null;
304 constraints = null;
305 }
306
307 /** After all settings, it finally builds all static references.
308 * @throws DbException If something goes wrong.
309 */
310 public void build() throws DbException {
311 initStaticLists();
312 }
313
314 /** After setting all dynamic (i.e. operations, lists etc.) information, it checks
315 * explicitly if all operations can be done. I suggest to call
316 * <CODE>checkThis</CODE> method to perform base checking.
317 * @param operation The kind of operation (INSERT, UPDATE etc.) that should be done.
318 * @throws DbException If something goes wrong.
319 */
320 public abstract void check(int operation) throws DbException;
321
322 /** Actually updates the table and all of its son tables. I suggest to call
323 * <CODE>updateThis</CODE> method to perform base update.
324 * @param operation The kind of operation (INSERT, UPDATE etc.) that should be done.
325 * @throws DbException If something goes wrong.
326 * @return It should contain the final result of <CODE>execute</CODE> calls to inserters,
327 * deleters of updaters.
328 */
329 public abstract int update(int operation) throws DbException;
330
331 /** The referenced table to check.
332 */
333 protected DbAbstractTable table;
334 /** A DbExpr clause used int DELETE/UPDATE operations.
335 */
336 protected DbExpr where;
337 /** A selector used in INSERT operations.
338 */
339 protected DbSelector selector;
340 /** A list of columns to put data into (used in INSERT/UPDATE operations).
341 */
342 protected List intoList;
343 /** A list of values/expression to put data into (used in INSERT/UPDATE operations).
344 */
345 protected List fromList;
346 /** <CODE>true</CODE>: check is done at all times;
347 * <CODE>false</CODE>: check is performed only with an explicit call to
348 * <CODE>check</CODE>.
349 */
350 protected boolean automaticChecking;
351 /** <CODE>true</CODE>: emulation made as needed;
352 * <CODE>false</CODE>: emulation is not made.
353 */
354 protected boolean constraintEmulation;
355 /** Represents the current operation. (INSERT, DELETE, UPDATE, UPDATE-CASCADE).
356 */
357 protected int currentOperation;
358 /** <CODE>true</CODE>: update can be done.
359 * <CODE>false</CODE>: update CANNOT be done.
360 * It is put to <CODE>false</CODE> whenever the DBMS can perform referencial
361 * integrity check by itself; e.g. MySQL cannot, so it must be done in a
362 * "software" way.
363 */
364 protected boolean canDoUpdate;
365
366 /** It contains all father tables of the one referenced by this constraint.
367 */
368 protected DbAbstractTable[] fatherTables;
369 /** It contains all son tables of the one referenced by this constraint.
370 */
371 protected DbAbstractTable[] sonTables;
372 /** It contains the update operations for each father table.
373 */
374 protected Integer[] updateOperations;
375 /** It contains the delete operations for each father table.
376 */
377 protected Integer[] deleteOperations;
378 /** It contains the father keys for each father table.
379 */
380 protected DbColumn[][] fatherTablesColumns;
381 /** It contains for each father table, the referenced column in contained table.
382 * That is each element in <CODE>fatherTablesColumns</CODE>, columns of father
383 * tables, must be equal to each element in <CODE>refColumns</CODE>, columns of
384 * THIS table.
385 */
386 protected DbColumn[][] refColumns;
387 /** For each son, it contains the referenced column of THIS table. It is built
388 * automatically, to ease checking.
389 */
390 protected DbColumn[][] fatherColsChildren;
391 /** For each son it contains the referenced columns of son tables. It is built
392 * automatically, to ease checking.
393 */
394 protected DbColumn[][] refColsChildren;
395 /** For each son, it contains the position in the <CODE>fatherTables</CODE> of
396 * the son table itself.
397 */
398 protected Integer[] sonIndexes;
399 /** It's an array built at runtime to represent, for each father, the referenced
400 * fields if they are specified in <CODE>intoList</CODE> for INSERT or UPDATE
401 * operations.
402 */
403 protected Integer[][] refs;
404 /** Contains the search sets used to perform checks for each son table.
405 */
406 protected TreeSet[] searchSets;
407 /** Contains the "where" clauses for each son.
408 */
409 protected DbExpr[] sonsWhere;
410 /** Contains the father records used to find eventually involved rows.
411 */
412 protected DbTable fatherRecords;
413 /** Currently unused.
414 */
415 protected Integer deleteCascadeChildren[];
416 /** Currently unused.
417 */
418 protected Integer updateCascadeChildren[];
419 /** Contains an array of constraint to perform cascade-check and cascade-updates.
420 */
421 protected DbConstraint constraints[];
422 /** Contains default values for each referenced field of each father table.
423 */
424 protected Object[][] defaultValues;
425 /** Contains an array of <CODE>intoList</CODE>-like lists, to perform update
426 * operations in son tables.
427 */
428 protected List[] updateIntoLists;
429 /** Contains an array of <CODE>fromList</CODE>-like lists, to perform update
430 * operations in son tables.
431 */
432 protected List[] updateFromLists;
433
434 /** <CODE>true</CODE>: the object is cloned.
435 * <CODE>false</CODE>: the object is NOT cloned.
436 */
437 protected boolean cloned;
438
439 /** Performs base update. It also checks if automatic checking is set.
440 * @param operation The operation to be done.
441 * @throws DbException If something goes wrong.
442 * @return The real result from an inserter, updater, deleter.
443 */
444 protected int updateThis(int operation) throws DbException {
445 DbInserter ins;
446 DbDeleter del;
447 DbUpdater upd;
448 DbSelector tempSelector;
449 int response;
450
451 currentOperation = operation;
452 if (automaticChecking && constraintEmulation)
453 checkThis(operation);
454 else
455 if (sonTables != null && currentOperation != INSERT_OPERATION &&
456 constraintEmulation)
457 initLists();
458 if (currentOperation == INSERT_OPERATION) {
459 if (selector != null)
460 ins = table.simpleInserter(selector);
461 else
462 ins = table.simpleInserter();
463 ins.setLists(intoList, fromList);
464 response = ins.execute();
465 }
466 else if (currentOperation == DELETE_OPERATION) {
467 response = 0;
468 if (canDoUpdate) {
469 del = table.simpleDeleter();
470 del.setWhere(where);
471 response = del.execute();
472 }
473 if (constraintEmulation)
474 cascade();
475 }
476 else if (currentOperation == UPDATE_OPERATION) {
477 response = 0;
478 if (canDoUpdate) {
479 upd = table.simpleUpdater();
480 upd.setLists(intoList, fromList);
481 upd.setWhere(where);
482 response = upd.execute();
483 }
484 if (constraintEmulation)
485 cascade();
486 }
487 else
488 response = -1;
489 return response;
490 }
491
492 /** Performs base checks.
493 * @param operation The operations that should be done.
494 * @throws DbException If something goes wrong.
495 */
496 protected void checkThis(int operation) throws DbException {
497 DbSelector tempSelector;
498 DbDatabase tempDb;
499
500 currentOperation = operation;
501 tempDb = table.getDatabase();
502 if (sonTables != null && currentOperation != INSERT_OPERATION)
503 initLists();
504 if (!tempDb.getForeignKey()) {
505 if (currentOperation != DELETE_OPERATION && currentOperation != UPDATE_CASCADE) {
506 tempSelector = null;
507 if (currentOperation == UPDATE_OPERATION) {
508 tempSelector = selector;
509 selector = null;
510 }
511 checkFathers();
512 if (currentOperation == UPDATE_OPERATION)
513 selector = tempSelector;
514 }
515 if (currentOperation != INSERT_OPERATION)
516 checkChildren();
517 }
518 }
519
520 /** Performs checking for father tables. Used in INSERT and UPDATE operations.
521 * N.B. Not used in UPDATE-CASCADE operation.
522 * @throws DbException If checking fails.
523 */
524 protected void checkFathers () throws DbException {
525 int i, j, numFathers, numCols, oneIn;
526 Iterator intoit;
527 DbSelector finder;
528 DbExpr tempExpr, tempWhere;
529 DbIterator res, resFather;
530 Iterator fkIterator;
531 DbDatabase tempDb;
532 DbAbstractTable father;
533 TreeSet fkSet;
534 DbColumn[] fatherCols;
535 DbTable resTable;
536
537 tempDb = table.getDatabase();
538 if (fatherTables != null) {
539 buildReferences();
540 numFathers = fatherTables.length;
541 }
542 else
543 numFathers = 0;
544
545 if (selector == null) {
546 for (i=0; i < numFathers; i++) {
547 finder = new DbSelector(tempDb);
548 if (!onlyNullValues(i)) {
549 numCols = refs[i].length;
550 oneIn = 0;
551 tempExpr = null;
552 father = null;
553 fatherCols = null;
554 if (numCols > 0) {
555 father = fatherTables[i];
556 fatherCols = fatherTablesColumns[i];
557 if (refs[i][0].intValue() >= 0) {
558 finder.addColumn(fatherCols[0]);
559 tempExpr = fatherCols[0].equal(fromList.get(refs[i][0].intValue()));
560 }
561 else {
562 oneIn = 1;
563 }
564 }
565 for (j=1; j < numCols && oneIn != 2; j++) {
566 if (refs[i][j].intValue() >= 0) {
567 if (oneIn == 1)
568 oneIn = 2;
569 else {
570 finder.addColumn(fatherCols[j]);
571 tempExpr = new DbAndExpr(tempDb, tempExpr,
572 fatherCols[j].equal(fromList.get(refs[i][j].intValue())));
573 }
574 }
575 else
576 if (oneIn == 0)
577 oneIn = 2;
578 }
579 if (oneIn == 2)
580 throw new DbException ("Key data not complete!");
581 if (oneIn == 0) {
582 finder.setWhere(tempExpr);
583 res = finder.execute().iterator();
584 if (!res.hasNext())
585 throw new DbException ("Father key not found!");
586 }
587 }
588 }
589 }
590 else {
591 resTable = selector.execute();
592
593 for (i=0; i < numFathers; i++) {
594 finder = new DbSelector(tempDb);
595 res = resTable.iterator();
596 fkSet = buildSet(refs[i], res);
597 if (!(fkSet.size() == 1 && NullUtils.isArrayNull((Object[]) fkSet.first()))) {
598 numCols = refs[i].length;
599 fatherCols = fatherTablesColumns[i];
600 tempExpr = null;
601 if (numCols > 0) {
602 finder.addColumn(fatherCols[0]);
603 finder.addGroupBy(fatherCols[0]);
604 tempExpr = fatherCols[0].equal(selector.getColumn(refs[i][0].intValue()));
605 }
606 for (j=1; j < numCols; j++) {
607 finder.addColumn(fatherCols[j]);
608 finder.addGroupBy(fatherCols[j]);
609 tempExpr = new DbAndExpr(tempDb, tempExpr,
610 fatherCols[j].equal(selector.getColumn(refs[i][j].intValue())));
611 }
612 tempWhere = selector.getWhere();
613 if (tempExpr != null)
614 if (tempWhere != null)
615 finder.setWhere(tempExpr.and(tempWhere));
616 else
617 finder.setWhere(tempExpr);
618 else
619 finder.setWhere(tempWhere);
620 resFather = finder.execute().iterator();
621 if (iteratorSize(resFather) != fkSet.size())
622 throw new DbException("Father keys not found!");
623 }
624 }
625 }
626 }
627
628 /** Checks recursively each son.
629 * @throws DbException If checking fails.
630 */
631 protected void checkChildren() throws DbException {
632 int i, numSons, operation, refFather;
633 DbAbstractTable tempSon;
634 DbReferencedDeleter sonDeleter;
635 DbIterator sonRecords;
636 DbConstraint tempConstraint;
637 Iterator sonDeleters;
638 Object[] defValues;
639
640 if (sonTables != null)
641 numSons = sonTables.length;
642 else
643 numSons = 0;
644 for (i=0; i < numSons; i++) {
645 tempSon = sonTables[i];
646 tempConstraint = constraints[i];
647 if (tempConstraint != null) {
648 refFather = sonIndexes[i].intValue();
649 if (currentOperation == DELETE_OPERATION)
650 operation = tempConstraint.getDeleteOperation(refFather);
651 else
652 operation = tempConstraint.getUpdateOperation(refFather);
653 if (operation == CASCADE) {
654 if (currentOperation == DELETE_OPERATION)
655 tempConstraint.check(DELETE_OPERATION);
656 else
657 tempConstraint.check(UPDATE_CASCADE);
658 }
659 else {
660 sonRecords = buildSonRecords(i).iterator();
661 if (sonRecords.hasNextRow())
662 if (operation == DO_NOTHING)
663 throw new DbException("Delete impossible: child key found!");
664 else {
665 if (operation == SET_DEFAULT) {
666 defValues = tempConstraint.getDefaultValues(i);
667 if (searchSets[i].contains(defValues))
668 throw new DbException("Trying to delete a default-father!");
669 }
670 tempConstraint.check(UPDATE_OPERATION);
671 }
672 }
673 }
674 }
675 }
676
677 /** Clones this object. It is used to build a temporary DbConstraint in
678 * self-referencing tables.
679 * @throws CloneNotSupportedException Never thrown... usually...
680 * @return The cloned object.
681 */
682 protected Object clone() throws CloneNotSupportedException {
683 DbConstraint tempConstraint;
684
685 try {
686 tempConstraint = (DbConstraint) this.getClass().newInstance();
687 }
688 catch (InstantiationException e) {
689 throw new CloneNotSupportedException(e.getMessage());
690 }
691 catch (IllegalAccessException e) {
692 throw new CloneNotSupportedException(e.getMessage());
693 }
694 tempConstraint.table = table;
695 tempConstraint.fatherTables = fatherTables;
696 tempConstraint.sonTables = sonTables;
697 tempConstraint.updateOperations = updateOperations;
698 tempConstraint.deleteOperations = deleteOperations;
699 tempConstraint.fatherTablesColumns = fatherTablesColumns;
700 tempConstraint.refColumns = refColumns;
701 tempConstraint.fatherColsChildren = fatherColsChildren;
702 tempConstraint.refColsChildren = refColsChildren;
703 tempConstraint.defaultValues = defaultValues;
704 tempConstraint.sonIndexes = sonIndexes;
705 tempConstraint.cloned = true;
706 return tempConstraint;
707 }
708
709 private void buildReferences() throws DbException {
710 int i, numFathers;
711 Iterator intoit;
712 DbColumn col;
713
714 if (intoList != null && fromList != null) {
715 numFathers = fatherTables.length;
716 refs = new Integer[numFathers][];
717 for (i=0; i < numFathers; i++)
718 refs[i] = findRefPosition(i);
719 }
720 }
721
722 private Integer[] findRefPosition(int index) throws DbException {
723 int i, j, numCols, numFatherCols, tempValue;
724 Integer[] response;
725 DbColumn[] cols;
726 String columnName;
727 boolean found;
728
729 cols = refColumns[index];
730 numFatherCols = cols.length;
731 numCols = intoList.size();
732 response = new Integer[numFatherCols];
733 for (i=0; i < numFatherCols; i++) {
734 found = false;
735 j = 0;
736 columnName = (cols[i].getName());
737 tempValue = -1;
738 while (j < numCols && !found) {
739 if (cols[i].equals(intoList.get(j))) {
740 found = true;
741 tempValue = j;
742 }
743 else
744 j++;
745 }
746 response[i] = new Integer(tempValue);
747 }
748 return response;
749 }
750
751 private int iteratorSize(Iterator refIt) {
752 int i;
753
754 for(i=0; refIt.hasNext(); i++)
755 refIt.next();
756 return i;
757 }
758
759 private TreeSet buildSet(Integer[] refs, DbIterator it) throws DbException {
760 int i, numCols;
761 TreeSet tempTreeSet;
762 ArrayComparator comp;
763 Comparable[] tempArray;
764 DbRow tempRow;
765
766 comp = new ArrayComparator();
767 tempTreeSet = new TreeSet(comp);
768 numCols = refs.length;
769 while (it.hasNextRow()) {
770 tempArray = new Comparable[numCols];
771 tempRow = it.nextRow();
772 for (i=0; i < numCols; i++)
773 tempArray[i] = (Comparable) tempRow.getValue(refs[i].intValue());
774 tempTreeSet.add(tempArray);
775 }
776 return tempTreeSet;
777 }
778
779
780 private void cascade() throws DbException {
781 int i, numSons, operation, refFather;
782 DbAbstractTable tempSon;
783 DbConstraint tempConstraint;
784 DbDeleter sonDeleter;
785 DbIterator sonRecords;
786 Iterator sonDeleters;
787
788 if (sonTables != null)
789 numSons = sonTables.length;
790 else
791 numSons = 0;
792 for (i=0; i < numSons; i++) {
793 tempSon = sonTables[i];
794 tempConstraint = constraints[i];
795 if (tempConstraint != null) {
796 refFather = sonIndexes[i].intValue();
797 if (currentOperation == DELETE_OPERATION)
798 operation = tempConstraint.getDeleteOperation(refFather);
799 else
800 operation = tempConstraint.getUpdateOperation(refFather);
801 if (tempConstraint != null) {
802 tempConstraint.setAutomaticChecking(false);
803 if (operation == CASCADE)
804 tempConstraint.update(currentOperation);
805 else if (operation != DO_NOTHING)
806 tempConstraint.update(UPDATE_OPERATION);
807 }
808 }
809 }
810 }
811
812 private void buildSonColsList() throws DbException {
813 int i, numSons, refIndex;
814 DbAbstractTable tempSon;
815 DbConstraint tempConstraint;
816
817 numSons = sonTables.length;
818 fatherColsChildren = new DbColumn[numSons][];
819 refColsChildren = new DbColumn[numSons][];
820
821 for (i=0; i < numSons; i++) {
822 tempSon = sonTables[i];
823 tempConstraint = tempSon.getConstraint();
824 refIndex = sonIndexes[i].intValue();
825 fatherColsChildren[i] = tempConstraint.getFatherColumns(refIndex);
826 refColsChildren[i] = tempConstraint.getRefColumns(refIndex);
827 }
828 }
829
830 private void buildFatherRecords() throws DbException {
831 int i, j, k, numCols, numSons, numColsSons;
832 boolean[] colNeeded;
833 boolean found;
834 DbColumn[] tempCols;
835 DbColumn tempCol;
836 DbSelector fatherFinder;
837
838 numSons = sonTables.length;
839 if (numSons > 0) {
840 numCols = table.getColumnCount();
841 colNeeded = new boolean[numCols];
842 for (i=0; i < numCols; i++) {
843 found = false;
844 tempCol = table.getColumn(i);
845 for (j=0; j < numSons && !found; j++) {
846 tempCols = fatherColsChildren[j];
847 numColsSons = tempCols.length;
848 for (k=0; k < numColsSons && ! found; k++) {
849 if (tempCol.equals(tempCols[k]))
850 found = true;
851 }
852 }
853 colNeeded[i] = found;
854 }
855 fatherFinder = new DbSelector(table.getDatabase());
856 for (i=0; i < numCols; i++) {
857 if (colNeeded[i])
858 fatherFinder.addColumn(table.getColumn(i));
859 else
860 fatherFinder.addColumn(new Integer(0));
861 }
862 fatherFinder.setWhere(where);
863 fatherRecords = fatherFinder.execute();
864 }
865 }
866
867 private void buildSearchSets() throws DbException {
868 int i, j, numSons, numCols;
869 TreeSet tempSet;
870 DbIterator fatherIt;
871 DbRow tempRow;
872 DbColumn[] cols;
873 ArrayComparator ac;
874 Comparable[] colValues;
875
876 numSons = sonTables.length;
877 searchSets = new TreeSet[numSons];
878 ac = new ArrayComparator();
879 for (i=0; i < numSons; i++) {
880 cols = fatherColsChildren[i];
881 numCols = cols.length;
882 tempSet = new TreeSet(ac);
883 fatherIt = fatherRecords.iterator();
884 while (fatherIt.hasNextRow()) {
885 colValues = new Comparable[numCols];
886 tempRow = fatherIt.nextRow();
887 for (j=0; j < numCols; j++)
888 colValues[j] = (Comparable) tempRow.getValue(cols[j].getName());
889 tempSet.add(colValues);
890 }
891 searchSets[i] = tempSet;
892 }
893 }
894
895 private DbTable buildSonRecords(int numSon) throws DbException {
896 int i, numValues;
897 DbExpr tempExpr;
898 DbColumn[] cols;
899 DbSelector finder;
900 DbDatabase tempDb;
901
902 tempDb = table.getDatabase();
903 cols = refColsChildren[numSon];
904 numValues = cols.length;
905 finder = new DbSelector(tempDb);
906 for (i=0; i < numValues; i++)
907 finder.addColumn(cols[i]);
908 finder.setWhere(sonsWhere[numSon]);
909 return finder.execute();
910 }
911
912 private DbExpr buildSonWhere(int numSon) {
913 int i, numValues;
914 Iterator searchIt;
915 Comparable[] colValues;
916 DbExpr tempExpr, innerTempExpr;
917 DbColumn[] cols;
918 DbDatabase tempDb;
919
920 searchIt = searchSets[numSon].iterator();
921 tempExpr = null;
922 tempDb = table.getDatabase();
923 cols = refColsChildren[numSon];
924 numValues = cols.length;
925 while (searchIt.hasNext()) {
926 colValues = (Comparable[]) searchIt.next();
927 innerTempExpr = null;
928 for (i=0; i < numValues; i++) {
929 if (innerTempExpr != null)
930 innerTempExpr = innerTempExpr.and(cols[i].equal(colValues[i]));
931 else
932 innerTempExpr = cols[i].equal(colValues[i]);
933 }
934 if (innerTempExpr != null) {
935 innerTempExpr = new DbParenthesis(tempDb, innerTempExpr);
936 if (tempExpr != null)
937 tempExpr = tempExpr.or(innerTempExpr);
938 else
939 tempExpr = innerTempExpr;
940 }
941 }
942 return tempExpr;
943 }
944
945 private void buildSonsWhereList() {
946 int i, numSons;
947
948 numSons = sonTables.length;
949 sonsWhere = new DbExpr[numSons];
950 for (i=0; i < numSons; i++)
951 sonsWhere[i] = buildSonWhere(i);
952 }
953
954 private void buildDeleteCascadeChildren() {
955 LinkedList tempList;
956 int i, numSons;
957
958 tempList = new LinkedList();
959 numSons = sonTables.length;
960 for (i=0; i < numSons; i++)
961 if (deleteOperations[i].intValue() == CASCADE)
962 tempList.add(new Integer(i));
963 deleteCascadeChildren = (Integer[]) tempList.toArray();
964 }
965
966 private void buildUpdateCascadeChildren() {
967 LinkedList tempList;
968 int i, numSons;
969
970 tempList = new LinkedList();
971 numSons = sonTables.length;
972 for (i=0; i < numSons; i++)
973 if (updateOperations[i].intValue() == CASCADE)
974 tempList.add(new Integer(i));
975 updateCascadeChildren = (Integer[]) tempList.toArray();
976 }
977
978 private void buildConstraints() throws DbException {
979 int i, numSons, refFather, operation;
980 DbAbstractTable tempTable;
981 DbConstraint tempConstraint;
982 List defList, defIntoList;
983 Object[] defValues;
984
985 numSons = sonTables.length;
986 constraints = new DbConstraint[numSons];
987 for (i=0; i < numSons; i++) {
988 tempConstraint = null;
989 if (sonsWhere[i] != null) {
990 tempTable = sonTables[i];
991 try {
992 if (tempTable != table) {
993 if (!cloned)
994 tempConstraint = tempTable.getConstraint();
995 else
996 tempConstraint = (DbConstraint) tempTable.getConstraint().clone();
997 }
998 else {
999 tempConstraint = (DbConstraint) this.clone();
1000 }
1001 }
1002 catch (CloneNotSupportedException e) {
1003 tempConstraint = null;
1004 }
1005 if (tempConstraint != null) {
1006 refFather = sonIndexes[i].intValue();
1007 defList = null;
1008 defIntoList = Arrays.asList(refColsChildren[i]);
1009 if (currentOperation == DELETE_OPERATION)
1010 operation = tempConstraint.getDeleteOperation(refFather);
1011 else {
1012 operation = tempConstraint.getUpdateOperation(refFather);
1013 if (operation == CASCADE) {
1014 defIntoList = updateIntoLists[i];
1015 defList = updateFromLists[i];
1016 }
1017 }
1018 if (operation == SET_DEFAULT) {
1019 defValues = tempConstraint.getDefaultValues(i);
1020 defList = Arrays.asList(defValues);
1021 }
1022 else if (operation == SET_NULL) {
1023 defList = NullUtils.nullList(refColsChildren[i].length);
1024 }
1025 tempConstraint.setWhere(sonsWhere[i]);
1026 if (defList != null) {
1027 tempConstraint.setValueLists(defIntoList, defList);
1028 }
1029 else if (currentOperation == UPDATE_OPERATION && operation == CASCADE)
1030 tempConstraint = null;
1031 }
1032 }
1033 constraints[i] = tempConstraint;
1034 }
1035 }
1036
1037 private void buildDefaultValues() throws DbException {
1038 int i, j, numFathers, numRefs;
1039
1040 numFathers = fatherTables.length;
1041 defaultValues = new Object[numFathers][];
1042 for (i=0; i < numFathers; i++) {
1043 numRefs = refColumns[i].length;
1044 defaultValues[i] = new Object[numRefs];
1045 for (j=0; j < numRefs; j++) {
1046 defaultValues[i][j] = table.getDefault(refColumns[i][j].getName());
1047 }
1048 }
1049 }
1050
1051 private void buildUpdateLists() throws DbException {
1052 int i, j, numSons, numCols;
1053 boolean found;
1054 Iterator intoIt, fromIt;
1055 DbColumn tempColumn;
1056 DbColumn cols[];
1057 Object tempObject;
1058
1059 numSons = sonTables.length;
1060 updateIntoLists = new ArrayList[numSons];
1061 updateFromLists = new ArrayList[numSons];
1062 for (i=0; i < numSons; i++) {
1063 updateIntoLists[i] = new ArrayList();
1064 updateFromLists[i] = new ArrayList();
1065 }
1066 intoIt = intoList.iterator();
1067 fromIt = fromList.iterator();
1068 while (intoIt.hasNext()) {
1069 tempColumn = (DbColumn) intoIt.next();
1070 tempObject = fromIt.next();
1071 for (i=0; i < numSons; i++) {
1072 found = false;
1073 cols = fatherColsChildren[i];
1074 numCols = cols.length;
1075 for (j=0; j < numCols && !found; j++)
1076 if (cols[j].equals(tempColumn)) {
1077 found = true;
1078 updateIntoLists[i].add(refColsChildren[i][j]);
1079 updateFromLists[i].add(tempObject);
1080 }
1081 }
1082 }
1083 for (i=0; i < numSons; i++)
1084 if (updateIntoLists[i].size() == 0) {
1085 updateIntoLists[i] = null;
1086 updateFromLists[i] = null;
1087 }
1088 }
1089
1090 private void optimizeConstraint(DbConstraint constraint, int operation) {
1091 DbDatabase tempDb;
1092
1093 tempDb = table.getDatabase();
1094 if (currentOperation == DELETE_OPERATION) {
1095 switch (operation) {
1096 case CASCADE:
1097 constraint.canDoUpdate = ! tempDb.getOnDeleteCascade();
1098 break;
1099 case SET_DEFAULT:
1100 constraint.canDoUpdate = ! tempDb.getOnDeleteSetDefault();
1101 break;
1102 case SET_NULL:
1103 constraint.canDoUpdate = ! tempDb.getOnDeleteSetNull();
1104 }
1105 }
1106 else {
1107 switch (operation) {
1108 case CASCADE:
1109 constraint.canDoUpdate = ! tempDb.getOnUpdateCascade();
1110 break;
1111 case SET_DEFAULT:
1112 constraint.canDoUpdate = ! tempDb.getOnUpdateSetDefault();
1113 break;
1114 case SET_NULL:
1115 constraint.canDoUpdate = ! tempDb.getOnUpdateSetNull();
1116 }
1117 }
1118 }
1119
1120 private boolean onlyNullValues(int numFather) {
1121 int i, numCols, tempRef;
1122 boolean response;
1123
1124 numCols = refs[numFather].length;
1125 response = true;
1126 for (i=0; i < numCols && response; i++) {
1127 tempRef = refs[numFather][i].intValue();
1128 if (tempRef >= 0 && fromList.get(tempRef) != null)
1129 response = false;
1130 }
1131 return response;
1132 }
1133
1134
1135 /** Inits static lists, like indexes of son tables, default values of father tables
1136 * etc.
1137 * @throws DbException If something goes wrong.
1138 */
1139 protected void initStaticLists() throws DbException {
1140 if (fatherTables != null)
1141 buildDefaultValues();
1142 if (sonTables != null)
1143 buildSonColsList();
1144 // buildDeleteCascadeChildren();
1145 // buildUpdateCascadeChildren();
1146 }
1147
1148 /** Builds all dynamic lists (where clauses, constraints etc.).
1149 * @throws DbException If something goes wrong.
1150 */
1151 protected void initLists() throws DbException {
1152 buildFatherRecords();
1153 buildSearchSets();
1154 buildSonsWhereList();
1155 if (intoList != null && fromList != null)
1156 buildUpdateLists();
1157 buildConstraints();
1158 }
1159}