/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb;

import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HsqlList;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.navigator.RangeIterator;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.result.Result;
import org.hsqldb.rights.Grantee;
import org.hsqldb.types.Type;

Implementation of a table constraint with references to the indexes used by the constraint.

Author:Fred Toussi (fredt@users dot sourceforge.net)
Version:2.5.0
Since:1.6.0
/** * Implementation of a table constraint with references to the indexes used * by the constraint.<p> * * @author Fred Toussi (fredt@users dot sourceforge.net) * @version 2.5.0 * @since 1.6.0 */
public final class Constraint implements SchemaObject { ConstraintCore core; private HsqlName name; int constType; boolean isForward; // Expression check; private boolean isNotNull; int notNullColumnIndex; RangeVariable rangeVariable; // for temp constraints only OrderedHashSet mainColSet; OrderedHashSet refColSet; boolean isSimpleIdentityPK; // public static final Constraint[] emptyArray = new Constraint[]{}; private Constraint() {}
Constructor declaration for PK and UNIQUE
/** * Constructor declaration for PK and UNIQUE */
public Constraint(HsqlName name, Table t, Index index, int type) { this.name = name; constType = type; core = new ConstraintCore(); core.mainTable = t; core.mainIndex = index; core.mainCols = index.getColumns(); for (int i = 0; i < core.mainCols.length; i++) { Type dataType = t.getColumn(core.mainCols[i]).getDataType(); if (dataType.isLobType()) { throw Error.error(ErrorCode.X_42534); } } } public Constraint(HsqlName name, Table table, int[] cols, int type) { this.name = name; constType = type; core = new ConstraintCore(); core.mainTable = table; core.mainCols = cols; }
Constructor for main constraints (foreign key references in PK table)
/** * Constructor for main constraints (foreign key references in PK table) */
public Constraint(HsqlName name, Constraint fkconstraint) { this.name = name; constType = SchemaObject.ConstraintTypes.MAIN; core = fkconstraint.core; }
General constructor for foreign key constraints.
Params:
  • name – name of constraint
  • refCols – list of referencing columns
  • mainTableName – referenced table
  • mainCols – list of referenced columns
  • type – constraint type
  • deleteAction – triggered action on delete
  • updateAction – triggered action on update
/** * General constructor for foreign key constraints. * * @param name name of constraint * @param refCols list of referencing columns * @param mainTableName referenced table * @param mainCols list of referenced columns * @param type constraint type * @param deleteAction triggered action on delete * @param updateAction triggered action on update * */
public Constraint(HsqlName name, HsqlName refTableName, OrderedHashSet refCols, HsqlName mainTableName, OrderedHashSet mainCols, int type, int deleteAction, int updateAction, int matchType) { this.name = name; constType = type; mainColSet = mainCols; refColSet = refCols; core = new ConstraintCore(); core.refTableName = refTableName; core.mainTableName = mainTableName; core.deleteAction = deleteAction; core.updateAction = updateAction; core.matchType = matchType; switch (core.deleteAction) { case SchemaObject.ReferentialAction.CASCADE : case SchemaObject.ReferentialAction.SET_DEFAULT : case SchemaObject.ReferentialAction.SET_NULL : core.hasDeleteAction = true; break; default : } switch (core.updateAction) { case SchemaObject.ReferentialAction.CASCADE : case SchemaObject.ReferentialAction.SET_DEFAULT : case SchemaObject.ReferentialAction.SET_NULL : core.hasUpdateAction = true; break; default : } } public Constraint(HsqlName name, OrderedHashSet mainCols, int type) { this.name = name; constType = type; mainColSet = mainCols; core = new ConstraintCore(); } public Constraint(HsqlName uniqueName, HsqlName mainName, HsqlName refName, Table mainTable, Table refTable, int[] mainCols, int[] refCols, Index mainIndex, Index refIndex, int deleteAction, int updateAction) { this.name = refName; constType = SchemaObject.ConstraintTypes.FOREIGN_KEY; core = new ConstraintCore(); core.uniqueName = uniqueName; core.mainName = mainName; core.refName = refName; core.mainTable = mainTable; core.refTable = refTable; core.mainCols = mainCols; core.refCols = refCols; core.mainIndex = mainIndex; core.refIndex = refIndex; core.deleteAction = deleteAction; core.updateAction = updateAction; } Constraint duplicate() { Constraint copy = new Constraint(); copy.core = core.duplicate(); copy.name = name; copy.constType = constType; copy.isForward = isForward; // copy.check = check; copy.isNotNull = isNotNull; copy.notNullColumnIndex = notNullColumnIndex; copy.rangeVariable = rangeVariable; return copy; } void setSimpleIdentityPK() { isSimpleIdentityPK = true; } void setColumnsIndexes(Table table) { if (constType == SchemaObject.ConstraintTypes.FOREIGN_KEY) { if (mainColSet == null) { core.mainCols = core.mainTable.getPrimaryKey(); if (core.mainCols == null) { throw Error.error(ErrorCode.X_42581); } } else if (core.mainCols == null) { core.mainCols = core.mainTable.getColumnIndexes(mainColSet); } if (core.refCols == null) { core.refCols = table.getColumnIndexes(refColSet); } for (int i = 0; i < core.refCols.length; i++) { Type dataType = table.getColumn(core.refCols[i]).getDataType(); if (dataType.isLobType()) { throw Error.error(ErrorCode.X_42534); } } if (core.mainCols.length != core.refCols.length) { throw Error.error(ErrorCode.X_42593); } } else if (mainColSet != null) { core.mainCols = table.getColumnIndexes(mainColSet); for (int i = 0; i < core.mainCols.length; i++) { Type dataType = table.getColumn(core.mainCols[i]).getDataType(); if (dataType.isLobType()) { throw Error.error(ErrorCode.X_42534); } } } } public int getType() { return SchemaObject.CONSTRAINT; }
Returns the HsqlName.
/** * Returns the HsqlName. */
public HsqlName getName() { return name; } public HsqlName getCatalogName() { return name.schema.schema; } public HsqlName getSchemaName() { return name.schema; } public Grantee getOwner() { return name.schema.owner; } public OrderedHashSet getReferences() { switch (constType) { case SchemaObject.ConstraintTypes.CHECK : OrderedHashSet refs = new OrderedHashSet(); check.collectObjectNames(refs); for (int j = refs.size() - 1; j >= 0; j--) { HsqlName name = (HsqlName) refs.get(j); if (name.type == SchemaObject.COLUMN || name.type == SchemaObject.TABLE) { refs.remove(j); } } return refs; case SchemaObject.ConstraintTypes.FOREIGN_KEY : OrderedHashSet set = new OrderedHashSet(); set.add(core.uniqueName); return set; } return new OrderedHashSet(); } public OrderedHashSet getComponents() { return null; } public void compile(Session session, SchemaObject parentObject) {} public String getSQL() { StringBuilder sb = new StringBuilder(); switch (getConstraintType()) { case SchemaObject.ConstraintTypes.PRIMARY_KEY : if (getMainColumns().length > 1 || (getMainColumns().length == 1 && !getName().isReservedName())) { if (!getName().isReservedName()) { sb.append(Tokens.T_CONSTRAINT).append(' '); sb.append(getName().statementName).append(' '); } sb.append(Tokens.T_PRIMARY).append(' ').append( Tokens.T_KEY); sb.append( getMain().getColumnListSQL( getMainColumns(), getMainColumns().length)); } break; case SchemaObject.ConstraintTypes.UNIQUE : if (!getName().isReservedName()) { sb.append(Tokens.T_CONSTRAINT).append(' '); sb.append(getName().statementName); sb.append(' '); } sb.append(Tokens.T_UNIQUE); int[] col = getMainColumns(); sb.append(getMain().getColumnListSQL(col, col.length)); break; case SchemaObject.ConstraintTypes.FOREIGN_KEY : if (isForward) { sb.append(Tokens.T_ALTER).append(' ').append( Tokens.T_TABLE).append(' '); sb.append( getRef().getName().getSchemaQualifiedStatementName()); sb.append(' ').append(Tokens.T_ADD).append(' '); getFKStatement(sb); } else { getFKStatement(sb); } break; case SchemaObject.ConstraintTypes.CHECK : if (isNotNull()) { break; } if (!getName().isReservedName()) { sb.append(Tokens.T_CONSTRAINT).append(' '); sb.append(getName().statementName).append(' '); } sb.append(Tokens.T_CHECK).append('('); sb.append(check.getSQL()); sb.append(')'); // should not throw as it is already tested OK break; default : } return sb.toString(); } public long getChangeTimestamp() { return 0; }
Generates the foreign key declaration for a given Constraint object.
/** * Generates the foreign key declaration for a given Constraint object. */
private void getFKStatement(StringBuilder sb) { if (!getName().isReservedName()) { sb.append(Tokens.T_CONSTRAINT).append(' '); sb.append(getName().statementName); sb.append(' '); } sb.append(Tokens.T_FOREIGN).append(' ').append(Tokens.T_KEY); int[] col = getRefColumns(); sb.append(getRef().getColumnListSQL(col, col.length)); sb.append(' ').append(Tokens.T_REFERENCES).append(' '); sb.append(getMain().getName().getSchemaQualifiedStatementName()); col = getMainColumns(); sb.append(getMain().getColumnListSQL(col, col.length)); if (getDeleteAction() != SchemaObject.ReferentialAction.NO_ACTION) { sb.append(' ').append(Tokens.T_ON).append(' ').append( Tokens.T_DELETE).append(' '); sb.append(getDeleteActionString()); } if (getUpdateAction() != SchemaObject.ReferentialAction.NO_ACTION) { sb.append(' ').append(Tokens.T_ON).append(' ').append( Tokens.T_UPDATE).append(' '); sb.append(getUpdateActionString()); } } public HsqlName getMainTableName() { return core.mainTableName; } public HsqlName getMainName() { return core.mainName; } public HsqlName getRefName() { return core.refName; } public HsqlName getUniqueName() { return core.uniqueName; }
Returns the type of constraint
/** * Returns the type of constraint */
public int getConstraintType() { return constType; } public boolean isUniqueOrPK() { return constType == SchemaObject.ConstraintTypes.UNIQUE || constType == SchemaObject.ConstraintTypes.PRIMARY_KEY; }
Returns the main table
/** * Returns the main table */
public Table getMain() { return core.mainTable; }
Returns the main index
/** * Returns the main index */
public Index getMainIndex() { return core.mainIndex; }
Returns the reference table
/** * Returns the reference table */
public Table getRef() { return core.refTable; }
Returns the reference index
/** * Returns the reference index */
public Index getRefIndex() { return core.refIndex; }
Returns the foreign key action rule.
/** * Returns the foreign key action rule. */
private static String getActionString(int action) { switch (action) { case SchemaObject.ReferentialAction.RESTRICT : return Tokens.T_RESTRICT; case SchemaObject.ReferentialAction.CASCADE : return Tokens.T_CASCADE; case SchemaObject.ReferentialAction.SET_DEFAULT : return Tokens.T_SET + ' ' + Tokens.T_DEFAULT; case SchemaObject.ReferentialAction.SET_NULL : return Tokens.T_SET + ' ' + Tokens.T_NULL; default : return Tokens.T_NO + ' ' + Tokens.T_ACTION; } }
The ON DELETE triggered action of (foreign key) constraint
/** * The ON DELETE triggered action of (foreign key) constraint */
public int getDeleteAction() { return core.deleteAction; } public String getDeleteActionString() { return getActionString(core.deleteAction); }
The ON UPDATE triggered action of (foreign key) constraint
/** * The ON UPDATE triggered action of (foreign key) constraint */
public int getUpdateAction() { return core.updateAction; } public String getUpdateActionString() { return getActionString(core.updateAction); } public boolean hasTriggeredAction() { if (constType == SchemaObject.ConstraintTypes.FOREIGN_KEY) { return hasCoreTriggeredAction(); } return false; } public boolean hasCoreTriggeredAction() { switch (core.deleteAction) { case SchemaObject.ReferentialAction.CASCADE : case SchemaObject.ReferentialAction.SET_DEFAULT : case SchemaObject.ReferentialAction.SET_NULL : return true; } switch (core.updateAction) { case SchemaObject.ReferentialAction.CASCADE : case SchemaObject.ReferentialAction.SET_DEFAULT : case SchemaObject.ReferentialAction.SET_NULL : return true; } return false; } public int getDeferability() { return SchemaObject.Deferable.NOT_DEFERRABLE; }
Returns the main table column index array
/** * Returns the main table column index array */
public int[] getMainColumns() { return core.mainCols; }
Returns the reference table column index array
/** * Returns the reference table column index array */
public int[] getRefColumns() { return core.refCols; }
Returns the SQL for the expression in CHECK clause
/** * Returns the SQL for the expression in CHECK clause */
public String getCheckSQL() { return check.getSQL(); }
Returns true if the expression in CHECK is a simple IS NOT NULL
/** * Returns true if the expression in CHECK is a simple IS NOT NULL */
public boolean isNotNull() { return isNotNull; } boolean hasColumnOnly(int colIndex) { switch (constType) { case SchemaObject.ConstraintTypes.CHECK : return rangeVariable.usedColumns[colIndex] && ArrayUtil .countTrueElements(rangeVariable.usedColumns) == 1; case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : return core.mainCols.length == 1 && core.mainCols[0] == colIndex; case SchemaObject.ConstraintTypes.MAIN : return false; case SchemaObject.ConstraintTypes.FOREIGN_KEY : return core.refCols.length == 1 && core.refCols[0] == colIndex; default : throw Error.runtimeError(ErrorCode.U_S0500, "Constraint"); } } boolean hasColumnPlus(int colIndex) { switch (constType) { case SchemaObject.ConstraintTypes.CHECK : return rangeVariable.usedColumns[colIndex] && ArrayUtil .countTrueElements(rangeVariable.usedColumns) > 1; case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : return core.mainCols.length != 1 && ArrayUtil.find(core.mainCols, colIndex) != -1; case SchemaObject.ConstraintTypes.MAIN : return ArrayUtil.find(core.mainCols, colIndex) != -1; case SchemaObject.ConstraintTypes.FOREIGN_KEY : return core.refCols.length != 1 && ArrayUtil.find(core.refCols, colIndex) != -1; default : throw Error.runtimeError(ErrorCode.U_S0500, "Constraint"); } } boolean hasColumn(int colIndex) { switch (constType) { case SchemaObject.ConstraintTypes.CHECK : return rangeVariable.usedColumns[colIndex]; case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : case SchemaObject.ConstraintTypes.MAIN : return ArrayUtil.find(core.mainCols, colIndex) != -1; case SchemaObject.ConstraintTypes.FOREIGN_KEY : return ArrayUtil.find(core.refCols, colIndex) != -1; default : throw Error.runtimeError(ErrorCode.U_S0500, "Constraint"); } }
Compares this with another constraint column set. This is used only for UNIQUE constraints.
/** * Compares this with another constraint column set. This is used only for * UNIQUE constraints. */
boolean isUniqueWithColumns(int[] cols) { switch (constType) { case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : if (core.mainCols.length == cols.length) { return ArrayUtil.haveEqualSets(core.mainCols, cols, cols.length); } } return false; }
Compares this with another constraint column set. This implementation only checks FOREIGN KEY constraints.
/** * Compares this with another constraint column set. This implementation * only checks FOREIGN KEY constraints. */
boolean isEquivalent(Table mainTable, int[] mainCols, Table refTable, int[] refCols) { switch (constType) { case SchemaObject.ConstraintTypes.MAIN : case SchemaObject.ConstraintTypes.FOREIGN_KEY : if (mainTable != core.mainTable || refTable != core.refTable) { return false; } if (core.mainCols.length == mainCols.length && core.refCols.length == refCols.length) { return ArrayUtil.areEqualSets(core.mainCols, mainCols) && ArrayUtil.areEqualSets(core.refCols, refCols); } } return false; }
Used to update constrains to reflect structural changes in a table. Prior checks must ensure that this method does not throw.
Params:
  • session – Session
  • oldTable – reference to the old version of the table
  • newTable – reference to the new version of the table
  • colIndex – indexes at which table column is added or removed
  • adjust – -1, 0, +1 to indicate if column is added or removed
/** * Used to update constrains to reflect structural changes in a table. Prior * checks must ensure that this method does not throw. * * @param session Session * @param oldTable reference to the old version of the table * @param newTable reference to the new version of the table * @param colIndex indexes at which table column is added or removed * @param adjust -1, 0, +1 to indicate if column is added or removed */
void updateTable(Session session, Table oldTable, Table newTable, int[] colIndex, int adjust) { if (oldTable == core.mainTable) { core.mainTable = newTable; if (core.mainIndex != null) { core.mainIndex = core.mainTable.getSystemIndex( core.mainIndex.getName().name); core.mainCols = ArrayUtil.toAdjustedColumnArray(core.mainCols, colIndex, adjust); } } if (oldTable == core.refTable) { core.refTable = newTable; if (core.refIndex != null) { core.refIndex = core.refTable.getSystemIndex(core.refIndex.getName().name); core.refCols = ArrayUtil.toAdjustedColumnArray(core.refCols, colIndex, adjust); } } // CHECK if (constType == SchemaObject.ConstraintTypes.CHECK) { recompile(session, newTable); } }
Checks for foreign key or check constraint violation when inserting a row into the child table.
/** * Checks for foreign key or check constraint violation when * inserting a row into the child table. */
void checkInsert(Session session, Table table, Object[] data, boolean isNew) { switch (constType) { case SchemaObject.ConstraintTypes.CHECK : if (!isNotNull) { checkCheckConstraint(session, table, data); } return; case SchemaObject.ConstraintTypes.FOREIGN_KEY : PersistentStore store = core.mainTable.getRowStore(session); if (ArrayUtil.hasNull(data, core.refCols)) { if (core.matchType == OpTypes.MATCH_SIMPLE) { return; } if (core.refCols.length == 1) { return; } if (ArrayUtil.hasAllNull(data, core.refCols)) { return; } // core.matchType == OpTypes.MATCH_FULL } else if (core.mainIndex.existsParent(session, store, data, core.refCols)) { return; } throw getException(data); } } /* * Tests a row against this CHECK constraint. */ void checkCheckConstraint(Session session, Table table, Object[] data) { RangeIterator it = session.sessionContext.getCheckIterator(rangeVariable); it.setCurrent(data); boolean nomatch = Boolean.FALSE.equals(check.getValue(session)); if (nomatch) { String[] info = new String[] { name.name, table.getName().name }; throw Error.error(null, ErrorCode.X_23513, ErrorCode.CONSTRAINT, info); } } void checkCheckConstraint(Session session, Table table, ColumnSchema column, Object data) { session.sessionData.currentValue = data; boolean nomatch = Boolean.FALSE.equals(check.getValue(session)); session.sessionData.currentValue = null; if (nomatch) { String[] info = new String[] { name.statementName, table == null ? "" : table.getName().statementName, column == null ? "" : column.getName().statementName, }; throw Error.error(null, ErrorCode.X_23513, ErrorCode.COLUMN_CONSTRAINT, info); } } public HsqlException getException(Object[] data) { switch (this.constType) { case SchemaObject.ConstraintTypes.CHECK : { String[] info = new String[]{ name.statementName }; return Error.error(null, ErrorCode.X_23513, ErrorCode.CONSTRAINT, info); } case SchemaObject.ConstraintTypes.FOREIGN_KEY : { StringBuilder sb = new StringBuilder(); for (int i = 0; i < core.refCols.length; i++) { Object o = data[core.refCols[i]]; if (i > 0) { sb.append(','); } sb.append(core.refTable.getColumnTypes()[core.refCols[i]] .convertToString(o)); } String[] info = new String[] { name.statementName, core.refTable.getName().statementName, sb.toString() }; return Error.error(null, ErrorCode.X_23503, ErrorCode.FK_CONSTRAINT, info); } case SchemaObject.ConstraintTypes.PRIMARY_KEY : case SchemaObject.ConstraintTypes.UNIQUE : { StringBuilder sb = new StringBuilder(); for (int i = 0; i < core.mainCols.length; i++) { Object o = data[core.mainCols[i]]; if (i > 0) { sb.append(','); } sb.append(core.mainTable.colTypes[core.mainCols[i]] .convertToString(o)); } return Error.error(null, ErrorCode.X_23505, ErrorCode.CONSTRAINT, new String[] { name.statementName, core.mainTable.getName().statementName, sb.toString() }); } default : throw Error.runtimeError(ErrorCode.U_S0500, "Constraint"); } } // fredt@users 20020225 - patch 1.7.0 - cascading deletes
New method to find any referencing row for a foreign key (finds row in child table). If ON DELETE CASCADE is specified for this constraint, then the method finds the first row among the rows of the table ordered by the index and doesn't throw. Without ON DELETE CASCADE, the method attempts to finds any row that exists. If no row is found, null is returned. (fredt@users)
Params:
  • session – Session
  • row – array of objects for a database row
Returns:iterator
/** * New method to find any referencing row for a foreign key (finds row in * child table). If ON DELETE CASCADE is specified for this constraint, then * the method finds the first row among the rows of the table ordered by the * index and doesn't throw. Without ON DELETE CASCADE, the method attempts * to finds any row that exists. If no * row is found, null is returned. (fredt@users) * * @param session Session * @param row array of objects for a database row * @return iterator */
RowIterator findFkRef(Session session, Object[] row) { if (row == null || ArrayUtil.hasNull(row, core.mainCols)) { return core.refIndex.emptyIterator(); } PersistentStore store = core.refTable.getRowStore(session); return core.refIndex.findFirstRow(session, store, row, core.mainCols); }
Finds a row matching the values in UNIQUE columns.
/** * Finds a row matching the values in UNIQUE columns. */
RowIterator findUniqueRows(Session session, Object[] row) { if (row == null || ArrayUtil.hasNull(row, core.mainCols)) { return core.mainIndex.emptyIterator(); } PersistentStore store = core.mainTable.getRowStore(session); return core.mainIndex.findFirstRow(session, store, row, core.mainCols); }
Check used before creating a new foreign key cosntraint, this method checks all rows of a table to ensure they all have a corresponding row in the main table.
/** * Check used before creating a new foreign key cosntraint, this method * checks all rows of a table to ensure they all have a corresponding * row in the main table. */
void checkReferencedRows(Session session, Table table) { RowIterator it = table.rowIterator(session); while (it.next()) { Object[] rowData = it.getCurrent(); checkInsert(session, table, rowData, false); } } public Expression getCheckExpression() { return check; } public OrderedHashSet getCheckColumnExpressions() { OrderedHashSet set = new OrderedHashSet(); check.collectAllExpressions(set, OpTypes.columnExpressionSet, OpTypes.emptyExpressionSet); return set; } void recompile(Session session, Table newTable) { check = getNewCheckExpression(session); // this workaround is here to stop LIKE optimisation (for proper scripting) QuerySpecification checkSelect = Expression.getCheckSelect(session, newTable, check); rangeVariable = checkSelect.rangeVariables[0]; rangeVariable.setForCheckConstraint(); } private Expression getNewCheckExpression(Session session) { String ddl = check.getSQL(); Scanner scanner = new Scanner(session, ddl); ParserDQL parser = new ParserDQL(session, scanner, null); parser.compileContext.setNextRangeVarIndex(0); parser.read(); parser.isCheckOrTriggerCondition = true; Expression condition = parser.XreadBooleanValueExpression(); return condition; } void prepareCheckConstraint(Session session, Table table) { // to ensure no subselects etc. are in condition check.checkValidCheckConstraint(); QuerySpecification checkSelect = Expression.getCheckSelect(session, table, check); rangeVariable = checkSelect.rangeVariables[0]; // removes reference to the Index object in range variable rangeVariable.setForCheckConstraint(); if (check.getType() == OpTypes.NOT && check.getLeftNode().getType() == OpTypes.IS_NULL && check.getLeftNode().getLeftNode().getType() == OpTypes.COLUMN) { notNullColumnIndex = check.getLeftNode().getLeftNode().getColumnIndex(); isNotNull = true; } } void prepareDomainCheckConstraint(Session session) { // to ensure no subselects etc. are in condition check.checkValidCheckConstraint(); HsqlList list = check.resolveColumnReferences(session, RangeGroup.emptyGroup, 0, RangeGroup.emptyArray, null, false); if (list != null) { Expression e = ((Expression) list.get(0)); throw Error.error(ErrorCode.X_42501, e.getSQL()); } check.resolveTypes(session, null); } void checkCheckConstraint(Session session, Table table) { if (table.getRowStore(session).elementCount() > 0) { Expression newCheck = getNewCheckExpression(session); QuerySpecification checkSelect = Expression.getCheckSelect(session, table, newCheck); Result r = checkSelect.getResult(session, 1); if (r.getNavigator().getSize() != 0) { String[] info = new String[] { name.statementName, table.getName().statementName }; throw Error.error(null, ErrorCode.X_23513, ErrorCode.CONSTRAINT, info); } } } }