1 /* Copyright (c) 2001-2016, The HSQL Development 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 HSQL Development 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 HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
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 
31 
32 package org.hsqldb;
33 
34 import org.hsqldb.HsqlNameManager.HsqlName;
35 import org.hsqldb.RangeVariable.RangeIteratorBase;
36 import org.hsqldb.error.Error;
37 import org.hsqldb.error.ErrorCode;
38 import org.hsqldb.index.Index;
39 import org.hsqldb.lib.ArrayUtil;
40 import org.hsqldb.lib.OrderedHashSet;
41 import org.hsqldb.navigator.RowIterator;
42 import org.hsqldb.persist.PersistentStore;
43 import org.hsqldb.result.Result;
44 import org.hsqldb.rights.Grantee;
45 import org.hsqldb.types.Type;
46 
47 /**
48  * Implementation of a table constraint with references to the indexes used
49  * by the constraint.<p>
50  *
51  * @author Fred Toussi (fredt@users dot sourceforge.net)
52  * @version 2.3.4
53  * @since 1.6.0
54  */
55 public final class Constraint implements SchemaObject {
56 
57     ConstraintCore   core;
58     private HsqlName name;
59     int              constType;
60     boolean          isForward;
61 
62     //
63     Expression      check;
64     private boolean isNotNull;
65     int             notNullColumnIndex;
66     RangeVariable   rangeVariable;
67 
68     // for temp constraints only
69     OrderedHashSet mainColSet;
70     OrderedHashSet refColSet;
71     boolean        isSimpleIdentityPK;
72 
73     //
74     public static final Constraint[] emptyArray = new Constraint[]{};
75 
Constraint()76     private Constraint() {}
77 
78     /**
79      *  Constructor declaration for PK and UNIQUE
80      */
Constraint(HsqlName name, Table t, Index index, int type)81     public Constraint(HsqlName name, Table t, Index index, int type) {
82 
83         this.name      = name;
84         constType      = type;
85         core           = new ConstraintCore();
86         core.mainTable = t;
87         core.mainIndex = index;
88         core.mainCols  = index.getColumns();
89 
90         for (int i = 0; i < core.mainCols.length; i++) {
91             Type dataType = t.getColumn(core.mainCols[i]).getDataType();
92 
93             if (dataType.isLobType()) {
94                 throw Error.error(ErrorCode.X_42534);
95             }
96         }
97     }
98 
Constraint(HsqlName name, Table table, int[] cols, int type)99     public Constraint(HsqlName name, Table table, int[] cols, int type) {
100 
101         this.name      = name;
102         constType      = type;
103         core           = new ConstraintCore();
104         core.mainTable = table;
105         core.mainCols  = cols;
106     }
107 
108     /**
109      *  Constructor for main constraints (foreign key references in PK table)
110      */
Constraint(HsqlName name, Constraint fkconstraint)111     public Constraint(HsqlName name, Constraint fkconstraint) {
112 
113         this.name = name;
114         constType = SchemaObject.ConstraintTypes.MAIN;
115         core      = fkconstraint.core;
116     }
117 
118     /**
119      * General constructor for foreign key constraints.
120      *
121      * @param name name of constraint
122      * @param refCols list of referencing columns
123      * @param mainTableName referenced table
124      * @param mainCols list of referenced columns
125      * @param type constraint type
126      * @param deleteAction triggered action on delete
127      * @param updateAction triggered action on update
128      *
129      */
Constraint(HsqlName name, HsqlName refTableName, OrderedHashSet refCols, HsqlName mainTableName, OrderedHashSet mainCols, int type, int deleteAction, int updateAction, int matchType)130     public Constraint(HsqlName name, HsqlName refTableName,
131                       OrderedHashSet refCols, HsqlName mainTableName,
132                       OrderedHashSet mainCols, int type, int deleteAction,
133                       int updateAction, int matchType) {
134 
135         this.name          = name;
136         constType          = type;
137         mainColSet         = mainCols;
138         refColSet          = refCols;
139         core               = new ConstraintCore();
140         core.refTableName  = refTableName;
141         core.mainTableName = mainTableName;
142         core.deleteAction  = deleteAction;
143         core.updateAction  = updateAction;
144         core.matchType     = matchType;
145 
146         switch (core.deleteAction) {
147 
148             case SchemaObject.ReferentialAction.CASCADE :
149             case SchemaObject.ReferentialAction.SET_DEFAULT :
150             case SchemaObject.ReferentialAction.SET_NULL :
151                 core.hasDeleteAction = true;
152                 break;
153 
154             default :
155         }
156 
157         switch (core.updateAction) {
158 
159             case SchemaObject.ReferentialAction.CASCADE :
160             case SchemaObject.ReferentialAction.SET_DEFAULT :
161             case SchemaObject.ReferentialAction.SET_NULL :
162                 core.hasUpdateAction = true;
163                 break;
164 
165             default :
166         }
167     }
168 
Constraint(HsqlName name, OrderedHashSet mainCols, int type)169     public Constraint(HsqlName name, OrderedHashSet mainCols, int type) {
170 
171         this.name  = name;
172         constType  = type;
173         mainColSet = mainCols;
174         core       = new ConstraintCore();
175     }
176 
Constraint(HsqlName uniqueName, HsqlName mainName, HsqlName refName, Table mainTable, Table refTable, int[] mainCols, int[] refCols, Index mainIndex, Index refIndex, int deleteAction, int updateAction)177     public Constraint(HsqlName uniqueName, HsqlName mainName,
178                       HsqlName refName, Table mainTable, Table refTable,
179                       int[] mainCols, int[] refCols, Index mainIndex,
180                       Index refIndex, int deleteAction, int updateAction) {
181 
182         this.name         = refName;
183         constType         = SchemaObject.ConstraintTypes.FOREIGN_KEY;
184         core              = new ConstraintCore();
185         core.uniqueName   = uniqueName;
186         core.mainName     = mainName;
187         core.refName      = refName;
188         core.mainTable    = mainTable;
189         core.refTable     = refTable;
190         core.mainCols     = mainCols;
191         core.refCols      = refCols;
192         core.mainIndex    = mainIndex;
193         core.refIndex     = refIndex;
194         core.deleteAction = deleteAction;
195         core.updateAction = updateAction;
196     }
197 
duplicate()198     Constraint duplicate() {
199 
200         Constraint copy = new Constraint();
201 
202         copy.core      = core.duplicate();
203         copy.name      = name;
204         copy.constType = constType;
205         copy.isForward = isForward;
206 
207         //
208         copy.check              = check;
209         copy.isNotNull          = isNotNull;
210         copy.notNullColumnIndex = notNullColumnIndex;
211         copy.rangeVariable      = rangeVariable;
212 
213         return copy;
214     }
215 
setSimpleIdentityPK()216     void setSimpleIdentityPK() {
217         isSimpleIdentityPK = true;
218     }
219 
setColumnsIndexes(Table table)220     void setColumnsIndexes(Table table) {
221 
222         if (constType == SchemaObject.ConstraintTypes.FOREIGN_KEY) {
223             if (mainColSet == null) {
224                 core.mainCols = core.mainTable.getPrimaryKey();
225 
226                 if (core.mainCols == null) {
227                     throw Error.error(ErrorCode.X_42581);
228                 }
229             } else if (core.mainCols == null) {
230                 core.mainCols = core.mainTable.getColumnIndexes(mainColSet);
231             }
232 
233             if (core.refCols == null) {
234                 core.refCols = table.getColumnIndexes(refColSet);
235             }
236 
237             for (int i = 0; i < core.refCols.length; i++) {
238                 Type dataType = table.getColumn(core.refCols[i]).getDataType();
239 
240                 if (dataType.isLobType()) {
241                     throw Error.error(ErrorCode.X_42534);
242                 }
243             }
244 
245             if (core.mainCols.length != core.refCols.length) {
246                 throw Error.error(ErrorCode.X_42593);
247             }
248         } else if (mainColSet != null) {
249             core.mainCols = table.getColumnIndexes(mainColSet);
250 
251             for (int i = 0; i < core.mainCols.length; i++) {
252                 Type dataType =
253                     table.getColumn(core.mainCols[i]).getDataType();
254 
255                 if (dataType.isLobType()) {
256                     throw Error.error(ErrorCode.X_42534);
257                 }
258             }
259         }
260     }
261 
getType()262     public int getType() {
263         return SchemaObject.CONSTRAINT;
264     }
265 
266     /**
267      * Returns the HsqlName.
268      */
getName()269     public HsqlName getName() {
270         return name;
271     }
272 
getCatalogName()273     public HsqlName getCatalogName() {
274         return name.schema.schema;
275     }
276 
getSchemaName()277     public HsqlName getSchemaName() {
278         return name.schema;
279     }
280 
getOwner()281     public Grantee getOwner() {
282         return name.schema.owner;
283     }
284 
getReferences()285     public OrderedHashSet getReferences() {
286 
287         switch (constType) {
288 
289             case SchemaObject.ConstraintTypes.CHECK :
290                 OrderedHashSet refs = new OrderedHashSet();
291 
292                 check.collectObjectNames(refs);
293 
294                 for (int j = refs.size() - 1; j >= 0; j--) {
295                     HsqlName name = (HsqlName) refs.get(j);
296 
297                     if (name.type == SchemaObject.COLUMN
298                             || name.type == SchemaObject.TABLE) {
299                         refs.remove(j);
300                     }
301                 }
302 
303                 return refs;
304 
305             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
306                 OrderedHashSet set = new OrderedHashSet();
307 
308                 set.add(core.uniqueName);
309 
310                 return set;
311         }
312 
313         return new OrderedHashSet();
314     }
315 
getComponents()316     public OrderedHashSet getComponents() {
317         return null;
318     }
319 
compile(Session session, SchemaObject parentObject)320     public void compile(Session session, SchemaObject parentObject) {}
321 
getSQL()322     public String getSQL() {
323 
324         StringBuffer sb = new StringBuffer();
325 
326         switch (getConstraintType()) {
327 
328             case SchemaObject.ConstraintTypes.PRIMARY_KEY :
329                 if (getMainColumns().length > 1
330                         || (getMainColumns().length == 1
331                             && !getName().isReservedName())) {
332                     if (!getName().isReservedName()) {
333                         sb.append(Tokens.T_CONSTRAINT).append(' ');
334                         sb.append(getName().statementName).append(' ');
335                     }
336 
337                     sb.append(Tokens.T_PRIMARY).append(' ').append(
338                         Tokens.T_KEY);
339                     sb.append(
340                         getMain().getColumnListSQL(
341                             getMainColumns(), getMainColumns().length));
342                 }
343                 break;
344 
345             case SchemaObject.ConstraintTypes.UNIQUE :
346                 if (!getName().isReservedName()) {
347                     sb.append(Tokens.T_CONSTRAINT).append(' ');
348                     sb.append(getName().statementName);
349                     sb.append(' ');
350                 }
351 
352                 sb.append(Tokens.T_UNIQUE);
353 
354                 int[] col = getMainColumns();
355 
356                 sb.append(getMain().getColumnListSQL(col, col.length));
357                 break;
358 
359             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
360                 if (isForward) {
361                     sb.append(Tokens.T_ALTER).append(' ').append(
362                         Tokens.T_TABLE).append(' ');
363                     sb.append(
364                         getRef().getName().getSchemaQualifiedStatementName());
365                     sb.append(' ').append(Tokens.T_ADD).append(' ');
366                     getFKStatement(sb);
367                 } else {
368                     getFKStatement(sb);
369                 }
370                 break;
371 
372             case SchemaObject.ConstraintTypes.CHECK :
373                 if (isNotNull()) {
374                     break;
375                 }
376 
377                 if (!getName().isReservedName()) {
378                     sb.append(Tokens.T_CONSTRAINT).append(' ');
379                     sb.append(getName().statementName).append(' ');
380                 }
381 
382                 sb.append(Tokens.T_CHECK).append('(');
383                 sb.append(check.getSQL());
384                 sb.append(')');
385 
386                 // should not throw as it is already tested OK
387                 break;
388 
389             default :
390         }
391 
392         return sb.toString();
393     }
394 
getChangeTimestamp()395     public long getChangeTimestamp() {
396         return 0;
397     }
398 
399     /**
400      * Generates the foreign key declaration for a given Constraint object.
401      */
getFKStatement(StringBuffer sb)402     private void getFKStatement(StringBuffer sb) {
403 
404         if (!getName().isReservedName()) {
405             sb.append(Tokens.T_CONSTRAINT).append(' ');
406             sb.append(getName().statementName);
407             sb.append(' ');
408         }
409 
410         sb.append(Tokens.T_FOREIGN).append(' ').append(Tokens.T_KEY);
411 
412         int[] col = getRefColumns();
413 
414         sb.append(getRef().getColumnListSQL(col, col.length));
415         sb.append(' ').append(Tokens.T_REFERENCES).append(' ');
416         sb.append(getMain().getName().getSchemaQualifiedStatementName());
417 
418         col = getMainColumns();
419 
420         sb.append(getMain().getColumnListSQL(col, col.length));
421 
422         if (getDeleteAction() != SchemaObject.ReferentialAction.NO_ACTION) {
423             sb.append(' ').append(Tokens.T_ON).append(' ').append(
424                 Tokens.T_DELETE).append(' ');
425             sb.append(getDeleteActionString());
426         }
427 
428         if (getUpdateAction() != SchemaObject.ReferentialAction.NO_ACTION) {
429             sb.append(' ').append(Tokens.T_ON).append(' ').append(
430                 Tokens.T_UPDATE).append(' ');
431             sb.append(getUpdateActionString());
432         }
433     }
434 
getMainTableName()435     public HsqlName getMainTableName() {
436         return core.mainTableName;
437     }
438 
getMainName()439     public HsqlName getMainName() {
440         return core.mainName;
441     }
442 
getRefName()443     public HsqlName getRefName() {
444         return core.refName;
445     }
446 
getUniqueName()447     public HsqlName getUniqueName() {
448         return core.uniqueName;
449     }
450 
451     /**
452      *  Returns the type of constraint
453      */
getConstraintType()454     public int getConstraintType() {
455         return constType;
456     }
457 
isUniqueOrPK()458     public boolean isUniqueOrPK() {
459         return constType == SchemaObject.ConstraintTypes.UNIQUE
460                || constType == SchemaObject.ConstraintTypes.PRIMARY_KEY;
461     }
462 
463     /**
464      *  Returns the main table
465      */
getMain()466     public Table getMain() {
467         return core.mainTable;
468     }
469 
470     /**
471      *  Returns the main index
472      */
getMainIndex()473     Index getMainIndex() {
474         return core.mainIndex;
475     }
476 
477     /**
478      *  Returns the reference table
479      */
getRef()480     public Table getRef() {
481         return core.refTable;
482     }
483 
484     /**
485      *  Returns the reference index
486      */
getRefIndex()487     Index getRefIndex() {
488         return core.refIndex;
489     }
490 
491     /**
492      * Returns the foreign key action rule.
493      */
getActionString(int action)494     private static String getActionString(int action) {
495 
496         switch (action) {
497 
498             case SchemaObject.ReferentialAction.RESTRICT :
499                 return Tokens.T_RESTRICT;
500 
501             case SchemaObject.ReferentialAction.CASCADE :
502                 return Tokens.T_CASCADE;
503 
504             case SchemaObject.ReferentialAction.SET_DEFAULT :
505                 return Tokens.T_SET + ' ' + Tokens.T_DEFAULT;
506 
507             case SchemaObject.ReferentialAction.SET_NULL :
508                 return Tokens.T_SET + ' ' + Tokens.T_NULL;
509 
510             default :
511                 return Tokens.T_NO + ' ' + Tokens.T_ACTION;
512         }
513     }
514 
515     /**
516      *  The ON DELETE triggered action of (foreign key) constraint
517      */
getDeleteAction()518     public int getDeleteAction() {
519         return core.deleteAction;
520     }
521 
getDeleteActionString()522     public String getDeleteActionString() {
523         return getActionString(core.deleteAction);
524     }
525 
526     /**
527      *  The ON UPDATE triggered action of (foreign key) constraint
528      */
getUpdateAction()529     public int getUpdateAction() {
530         return core.updateAction;
531     }
532 
getUpdateActionString()533     public String getUpdateActionString() {
534         return getActionString(core.updateAction);
535     }
536 
hasTriggeredAction()537     public boolean hasTriggeredAction() {
538 
539         if (constType == SchemaObject.ConstraintTypes.FOREIGN_KEY) {
540             return hasCoreTriggeredAction();
541         }
542 
543         return false;
544     }
545 
hasCoreTriggeredAction()546     public boolean hasCoreTriggeredAction() {
547 
548         switch (core.deleteAction) {
549 
550             case SchemaObject.ReferentialAction.CASCADE :
551             case SchemaObject.ReferentialAction.SET_DEFAULT :
552             case SchemaObject.ReferentialAction.SET_NULL :
553                 return true;
554         }
555 
556         switch (core.updateAction) {
557 
558             case SchemaObject.ReferentialAction.CASCADE :
559             case SchemaObject.ReferentialAction.SET_DEFAULT :
560             case SchemaObject.ReferentialAction.SET_NULL :
561                 return true;
562         }
563 
564         return false;
565     }
566 
getDeferability()567     public int getDeferability() {
568         return SchemaObject.Deferable.NOT_DEFERRABLE;
569     }
570 
571     /**
572      *  Returns the main table column index array
573      */
getMainColumns()574     public int[] getMainColumns() {
575         return core.mainCols;
576     }
577 
578     /**
579      *  Returns the reference table column index array
580      */
getRefColumns()581     public int[] getRefColumns() {
582         return core.refCols;
583     }
584 
585     /**
586      * Returns the SQL for the expression in CHECK clause
587      */
getCheckSQL()588     public String getCheckSQL() {
589         return check.getSQL();
590     }
591 
592     /**
593      * Returns true if the expression in CHECK is a simple IS NOT NULL
594      */
isNotNull()595     public boolean isNotNull() {
596         return isNotNull;
597     }
598 
hasColumnOnly(int colIndex)599     boolean hasColumnOnly(int colIndex) {
600 
601         switch (constType) {
602 
603             case SchemaObject.ConstraintTypes.CHECK :
604                 return rangeVariable.usedColumns[colIndex] && ArrayUtil
605                     .countTrueElements(rangeVariable.usedColumns) == 1;
606 
607             case SchemaObject.ConstraintTypes.PRIMARY_KEY :
608             case SchemaObject.ConstraintTypes.UNIQUE :
609                 return core.mainCols.length == 1
610                        && core.mainCols[0] == colIndex;
611 
612             case SchemaObject.ConstraintTypes.MAIN :
613                 return false;
614 
615             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
616                 return core.refCols.length == 1 && core.refCols[0] == colIndex;
617 
618             default :
619                 throw Error.runtimeError(ErrorCode.U_S0500, "Constraint");
620         }
621     }
622 
hasColumnPlus(int colIndex)623     boolean hasColumnPlus(int colIndex) {
624 
625         switch (constType) {
626 
627             case SchemaObject.ConstraintTypes.CHECK :
628                 return rangeVariable.usedColumns[colIndex] && ArrayUtil
629                     .countTrueElements(rangeVariable.usedColumns) > 1;
630 
631             case SchemaObject.ConstraintTypes.PRIMARY_KEY :
632             case SchemaObject.ConstraintTypes.UNIQUE :
633                 return core.mainCols.length != 1
634                        && ArrayUtil.find(core.mainCols, colIndex) != -1;
635 
636             case SchemaObject.ConstraintTypes.MAIN :
637                 return ArrayUtil.find(core.mainCols, colIndex) != -1;
638 
639             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
640                 return core.refCols.length != 1
641                        && ArrayUtil.find(core.refCols, colIndex) != -1;
642 
643             default :
644                 throw Error.runtimeError(ErrorCode.U_S0500, "Constraint");
645         }
646     }
647 
hasColumn(int colIndex)648     boolean hasColumn(int colIndex) {
649 
650         switch (constType) {
651 
652             case SchemaObject.ConstraintTypes.CHECK :
653                 return rangeVariable.usedColumns[colIndex];
654 
655             case SchemaObject.ConstraintTypes.PRIMARY_KEY :
656             case SchemaObject.ConstraintTypes.UNIQUE :
657             case SchemaObject.ConstraintTypes.MAIN :
658                 return ArrayUtil.find(core.mainCols, colIndex) != -1;
659 
660             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
661                 return ArrayUtil.find(core.refCols, colIndex) != -1;
662 
663             default :
664                 throw Error.runtimeError(ErrorCode.U_S0500, "Constraint");
665         }
666     }
667 
668     /**
669      * Compares this with another constraint column set. This is used only for
670      * UNIQUE constraints.
671      */
isUniqueWithColumns(int[] cols)672     boolean isUniqueWithColumns(int[] cols) {
673 
674         switch (constType) {
675 
676             case SchemaObject.ConstraintTypes.PRIMARY_KEY :
677             case SchemaObject.ConstraintTypes.UNIQUE :
678                 if (core.mainCols.length == cols.length) {
679                     return ArrayUtil.haveEqualSets(core.mainCols, cols,
680                                                    cols.length);
681                 }
682         }
683 
684         return false;
685     }
686 
687     /**
688      * Compares this with another constraint column set. This implementation
689      * only checks FOREIGN KEY constraints.
690      */
isEquivalent(Table mainTable, int[] mainCols, Table refTable, int[] refCols)691     boolean isEquivalent(Table mainTable, int[] mainCols, Table refTable,
692                          int[] refCols) {
693 
694         switch (constType) {
695 
696             case SchemaObject.ConstraintTypes.MAIN :
697             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
698                 if (mainTable != core.mainTable || refTable != core.refTable) {
699                     return false;
700                 }
701 
702                 if (core.mainCols.length == mainCols.length
703                         && core.refCols.length == refCols.length) {
704                     return ArrayUtil.areEqualSets(core.mainCols, mainCols)
705                            && ArrayUtil.areEqualSets(core.refCols, refCols);
706                 }
707         }
708 
709         return false;
710     }
711 
712     /**
713      * Used to update constrains to reflect structural changes in a table. Prior
714      * checks must ensure that this method does not throw.
715      *
716      * @param session Session
717      * @param oldTable reference to the old version of the table
718      * @param newTable reference to the new version of the table
719      * @param colIndex index at which table column is added or removed
720      * @param adjust -1, 0, +1 to indicate if column is added or removed
721      */
updateTable(Session session, Table oldTable, Table newTable, int colIndex, int adjust)722     void updateTable(Session session, Table oldTable, Table newTable,
723                      int colIndex, int adjust) {
724 
725         if (oldTable == core.mainTable) {
726             core.mainTable = newTable;
727 
728             if (core.mainIndex != null) {
729                 core.mainIndex =
730                     core.mainTable.getIndex(core.mainIndex.getName().name);
731                 core.mainCols = ArrayUtil.toAdjustedColumnArray(core.mainCols,
732                         colIndex, adjust);
733             }
734         }
735 
736         if (oldTable == core.refTable) {
737             core.refTable = newTable;
738 
739             if (core.refIndex != null) {
740                 core.refIndex =
741                     core.refTable.getIndex(core.refIndex.getName().name);
742                 core.refCols = ArrayUtil.toAdjustedColumnArray(core.refCols,
743                         colIndex, adjust);
744             }
745         }
746 
747         // CHECK
748         if (constType == SchemaObject.ConstraintTypes.CHECK) {
749             recompile(session, newTable);
750         }
751     }
752 
753     /**
754      * Checks for foreign key or check constraint violation when
755      * inserting a row into the child table.
756      */
checkInsert(Session session, Table table, Object[] data, boolean isNew)757     void checkInsert(Session session, Table table, Object[] data,
758                      boolean isNew) {
759 
760         switch (constType) {
761 
762             case SchemaObject.ConstraintTypes.CHECK :
763                 if (!isNotNull) {
764                     checkCheckConstraint(session, table, data);
765                 }
766 
767                 return;
768 
769             case SchemaObject.ConstraintTypes.FOREIGN_KEY :
770                 PersistentStore store = core.mainTable.getRowStore(session);
771 
772                 if (ArrayUtil.hasNull(data, core.refCols)) {
773                     if (core.matchType == OpTypes.MATCH_SIMPLE) {
774                         return;
775                     }
776 
777                     if (core.refCols.length == 1) {
778                         return;
779                     }
780 
781                     if (ArrayUtil.hasAllNull(data, core.refCols)) {
782                         return;
783                     }
784 
785                     // core.matchType == OpTypes.MATCH_FULL
786                 } else if (core.mainIndex.existsParent(session, store, data,
787                                                        core.refCols)) {
788                     return;
789                 }
790 
791                 throw getException(data);
792         }
793     }
794 
795     /*
796      * Tests a row against this CHECK constraint.
797      */
checkCheckConstraint(Session session, Table table, Object[] data)798     void checkCheckConstraint(Session session, Table table, Object[] data) {
799 
800         RangeIteratorBase it =
801             session.sessionContext.getCheckIterator(rangeVariable);
802 
803         it.setCurrent(data);
804 
805         boolean nomatch = Boolean.FALSE.equals(check.getValue(session));
806 
807         it.setCurrent(null);
808 
809         if (nomatch) {
810             String[] info = new String[] {
811                 name.name, table.getName().name
812             };
813 
814             throw Error.error(null, ErrorCode.X_23513, ErrorCode.CONSTRAINT,
815                               info);
816         }
817     }
818 
checkCheckConstraint(Session session, Table table, ColumnSchema column, Object data)819     void checkCheckConstraint(Session session, Table table,
820                               ColumnSchema column, Object data) {
821 
822         session.sessionData.currentValue = data;
823 
824         boolean nomatch = Boolean.FALSE.equals(check.getValue(session));
825 
826         session.sessionData.currentValue = null;
827 
828         if (nomatch) {
829             String[] info = new String[] {
830                 name.statementName,
831                 table == null ? ""
832                               : table.getName().statementName,
833                 column == null ? ""
834                                : column.getName().statementName,
835             };
836 
837             throw Error.error(null, ErrorCode.X_23513,
838                               ErrorCode.COLUMN_CONSTRAINT, info);
839         }
840     }
841 
getException(Object[] data)842     public HsqlException getException(Object[] data) {
843 
844         switch (this.constType) {
845 
846             case SchemaObject.ConstraintTypes.CHECK : {
847                 String[] info = new String[]{ name.statementName };
848 
849                 return Error.error(null, ErrorCode.X_23513,
850                                    ErrorCode.CONSTRAINT, info);
851             }
852             case SchemaObject.ConstraintTypes.FOREIGN_KEY : {
853                 StringBuffer sb = new StringBuffer();
854 
855                 for (int i = 0; i < core.refCols.length; i++) {
856                     Object o = data[core.refCols[i]];
857 
858                     sb.append(core.refTable.getColumnTypes()[core.refCols[i]]
859                         .convertToString(o));
860                     sb.append(',');
861                 }
862 
863                 String[] info = new String[] {
864                     name.statementName, core.refTable.getName().statementName,
865                     sb.toString()
866                 };
867 
868                 return Error.error(null, ErrorCode.X_23503,
869                                    ErrorCode.CONSTRAINT, info);
870             }
871             case SchemaObject.ConstraintTypes.PRIMARY_KEY :
872             case SchemaObject.ConstraintTypes.UNIQUE : {
873                 StringBuffer sb = new StringBuffer();
874 
875                 for (int i = 0; i < core.mainCols.length; i++) {
876                     Object o = data[core.mainCols[i]];
877 
878                     sb.append(core.mainTable.colTypes[core.mainCols[i]]
879                         .convertToString(o));
880                     sb.append(',');
881                 }
882 
883                 return Error.error(null, ErrorCode.X_23505,
884                                    ErrorCode.CONSTRAINT, new String[] {
885                     name.statementName, core.mainTable.getName().statementName,
886                     sb.toString()
887                 });
888             }
889             default :
890                 throw Error.runtimeError(ErrorCode.U_S0500, "Constraint");
891         }
892     }
893 
894 // fredt@users 20020225 - patch 1.7.0 - cascading deletes
895 
896     /**
897      * New method to find any referencing row for a foreign key (finds row in
898      * child table). If ON DELETE CASCADE is specified for this constraint, then
899      * the method finds the first row among the rows of the table ordered by the
900      * index and doesn't throw. Without ON DELETE CASCADE, the method attempts
901      * to finds any row that exists. If no
902      * row is found, null is returned. (fredt@users)
903      *
904      * @param session Session
905      * @param row array of objects for a database row
906      * @return iterator
907      */
findFkRef(Session session, Object[] row)908     RowIterator findFkRef(Session session, Object[] row) {
909 
910         if (row == null || ArrayUtil.hasNull(row, core.mainCols)) {
911             return core.refIndex.emptyIterator();
912         }
913 
914         PersistentStore store = core.refTable.getRowStore(session);
915 
916         return core.refIndex.findFirstRow(session, store, row, core.mainCols);
917     }
918 
919     /**
920      * Finds a row matching the values in UNIQUE columns.
921      */
findUniqueRows(Session session, Object[] row)922     RowIterator findUniqueRows(Session session, Object[] row) {
923 
924         if (row == null || ArrayUtil.hasNull(row, core.mainCols)) {
925             return core.mainIndex.emptyIterator();
926         }
927 
928         PersistentStore store = core.mainTable.getRowStore(session);
929 
930         return core.mainIndex.findFirstRow(session, store, row, core.mainCols);
931     }
932 
933     /**
934      * Check used before creating a new foreign key cosntraint, this method
935      * checks all rows of a table to ensure they all have a corresponding
936      * row in the main table.
937      */
checkReferencedRows(Session session, Table table)938     void checkReferencedRows(Session session, Table table) {
939 
940         RowIterator it = table.rowIterator(session);
941 
942         while (true) {
943             Row row = it.getNextRow();
944 
945             if (row == null) {
946                 break;
947             }
948 
949             Object[] rowData = row.getData();
950 
951             checkInsert(session, table, rowData, false);
952         }
953     }
954 
getCheckExpression()955     public Expression getCheckExpression() {
956         return check;
957     }
958 
getCheckColumnExpressions()959     public OrderedHashSet getCheckColumnExpressions() {
960 
961         OrderedHashSet set = new OrderedHashSet();
962 
963         check.collectAllExpressions(set, Expression.columnExpressionSet,
964                                     Expression.emptyExpressionSet);
965 
966         return set;
967     }
968 
recompile(Session session, Table newTable)969     void recompile(Session session, Table newTable) {
970 
971         check = getNewCheckExpression(session);
972 
973         // this workaround is here to stop LIKE optimisation (for proper scripting)
974         QuerySpecification checkSelect = Expression.getCheckSelect(session,
975             newTable, check);
976 
977         rangeVariable = checkSelect.rangeVariables[0];
978 
979         rangeVariable.setForCheckConstraint();
980     }
981 
getNewCheckExpression(Session session)982     private Expression getNewCheckExpression(Session session) {
983 
984         String    ddl     = check.getSQL();
985         Scanner   scanner = new Scanner(session, ddl);
986         ParserDQL parser  = new ParserDQL(session, scanner, null);
987 
988         parser.compileContext.setNextRangeVarIndex(0);
989         parser.read();
990 
991         parser.isCheckOrTriggerCondition = true;
992 
993         Expression condition = parser.XreadBooleanValueExpression();
994 
995         return condition;
996     }
997 
prepareCheckConstraint(Session session, Table table)998     void prepareCheckConstraint(Session session, Table table) {
999 
1000         // to ensure no subselects etc. are in condition
1001         check.checkValidCheckConstraint();
1002 
1003         if (table == null) {
1004             check.resolveTypes(session, null);
1005         } else {
1006             QuerySpecification checkSelect = Expression.getCheckSelect(session,
1007                 table, check);
1008 
1009             rangeVariable = checkSelect.rangeVariables[0];
1010 
1011             // removes reference to the Index object in range variable
1012             rangeVariable.setForCheckConstraint();
1013         }
1014 
1015         if (check.getType() == OpTypes.NOT
1016                 && check.getLeftNode().getType() == OpTypes.IS_NULL
1017                 && check.getLeftNode().getLeftNode().getType()
1018                    == OpTypes.COLUMN) {
1019             notNullColumnIndex =
1020                 check.getLeftNode().getLeftNode().getColumnIndex();
1021             isNotNull = true;
1022         }
1023     }
1024 
checkCheckConstraint(Session session, Table table)1025     void checkCheckConstraint(Session session, Table table) {
1026 
1027         if (table.getRowStore(session).elementCount() > 0) {
1028             Expression newCheck = getNewCheckExpression(session);
1029             QuerySpecification checkSelect = Expression.getCheckSelect(session,
1030                 table, newCheck);
1031             Result r = checkSelect.getResult(session, 1);
1032 
1033             if (r.getNavigator().getSize() != 0) {
1034                 String[] info = new String[] {
1035                     name.statementName, table.getName().statementName
1036                 };
1037 
1038                 throw Error.error(null, ErrorCode.X_23513,
1039                                   ErrorCode.CONSTRAINT, info);
1040             }
1041         }
1042     }
1043 }
1044