blob: ab427cb5568841468a5db6530d8e1226f8515cd0 [file] [log] [blame]
/*
* For work developed by the HSQL Development Group:
*
* Copyright (c) 2001-2010, 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.
*
*
*
* For work originally developed by the Hypersonic SQL Group:
*
* Copyright (c) 1995-2000, The Hypersonic SQL 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 Hypersonic SQL 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 THE HYPERSONIC SQL GROUP,
* 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.
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the Hypersonic SQL Group.
*/
package org.hsqldb;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.RangeVariable.RangeIteratorBase;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.OrderedHashSet;
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;
// fredt@users 20020225 - patch 1.7.0 by boucherb@users - named constraints
// fredt@users 20020320 - doc 1.7.0 - update
// tony_lai@users 20020820 - patch 595156 - violation of Integrity constraint name
/**
* Implementation of a table constraint with references to the indexes used
* by the constraint.<p>
*
* Partly based on Hypersonic code.
*
* @author Thomas Mueller (Hypersonic SQL Group)
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since Hypersonic SQL
*/
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;
//
final public static Constraint[] emptyArray = new Constraint[]{};
private Constraint() {}
/**
* 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)
*/
public Constraint(HsqlName name, Constraint fkconstraint) {
this.name = name;
constType = SchemaObject.ConstraintTypes.MAIN;
core = fkconstraint.core;
}
/**
* 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;
}
switch (core.updateAction) {
case SchemaObject.ReferentialAction.CASCADE :
case SchemaObject.ReferentialAction.SET_DEFAULT :
case SchemaObject.ReferentialAction.SET_NULL :
core.hasUpdateAction = true;
}
}
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) throws HsqlException {
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 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);
}
}
} 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.
*/
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() {
StringBuffer sb = new StringBuffer();
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;
}
return sb.toString();
}
public long getChangeTimestamp() {
return 0;
}
/**
* Generates the foreign key declaration for a given Constraint object.
*/
private void getFKStatement(StringBuffer 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
*/
public int getConstraintType() {
return constType;
}
/**
* Returns the main table
*/
public Table getMain() {
return core.mainTable;
}
/**
* Returns the main index
*/
Index getMainIndex() {
return core.mainIndex;
}
/**
* Returns the reference table
*/
public Table getRef() {
return core.refTable;
}
/**
* Returns the reference index
*/
Index getRefIndex() {
return core.refIndex;
}
/**
* 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
*/
public int getDeleteAction() {
return core.deleteAction;
}
public String getDeleteActionString() {
return getActionString(core.deleteAction);
}
/**
* 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) {
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
*/
public int[] getMainColumns() {
return core.mainCols;
}
/**
* Returns the reference table column index array
*/
public int[] getRefColumns() {
return core.refCols;
}
/**
* 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
*/
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 core.mainCols.length == 1
&& core.mainCols[0] == colIndex
&& core.mainTable == core.refTable;
case SchemaObject.ConstraintTypes.FOREIGN_KEY :
return core.refCols.length == 1 && core.refCols[0] == colIndex
&& core.mainTable == core.refTable;
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
&& (core.mainCols.length != 1
|| core.mainTable != core.refTable);
case SchemaObject.ConstraintTypes.FOREIGN_KEY :
return ArrayUtil.find(core.refCols, colIndex) != -1
&& (core.mainCols.length != 1
|| core.mainTable == core.refTable);
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");
}
}
// fredt@users 20020225 - patch 1.7.0 by fredt - duplicate constraints
/**
* Compares this with another constraint column set. This is used only for
* UNIQUE constraints.
*/
boolean isUniqueWithColumns(int[] cols) {
if (constType != SchemaObject.ConstraintTypes.UNIQUE
|| core.mainCols.length != cols.length) {
return false;
}
return ArrayUtil.haveEqualSets(core.mainCols, cols, cols.length);
}
/**
* Compares this with another constraint column set. This implementation
* only checks FOREIGN KEY constraints.
*/
boolean isEquivalent(Table mainTable, int[] mainCols, Table refTable,
int[] refCols) {
if (constType != SchemaObject.ConstraintTypes.MAIN
&& constType != SchemaObject.ConstraintTypes.FOREIGN_KEY) {
return false;
}
if (mainTable != core.mainTable || refTable != core.refTable) {
return false;
}
return ArrayUtil.areEqualSets(core.mainCols, mainCols)
&& ArrayUtil.areEqualSets(core.refCols, refCols);
}
/**
* 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 referenct to the new version of the table
* @param colIndex index 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.getIndex(core.mainIndex.getName().name);
core.mainCols = ArrayUtil.toAdjustedColumnArray(core.mainCols,
colIndex, adjust);
core.mainIndex.setTable(newTable);
}
}
if (oldTable == core.refTable) {
core.refTable = newTable;
if (core.refIndex != null) {
core.refIndex =
core.refTable.getIndex(core.refIndex.getName().name);
core.refCols = ArrayUtil.toAdjustedColumnArray(core.refCols,
colIndex, adjust);
core.refIndex.setTable(newTable);
}
}
// 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.
*/
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 =
session.sessionData.getRowStore(core.mainTable);
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) {
/*
if (session.compiledStatementExecutor.rangeIterators[1] == null) {
session.compiledStatementExecutor.rangeIterators[1] =
rangeVariable.getIterator(session);
}
*/
RangeIteratorBase it =
session.sessionContext.getCheckIterator(rangeVariable);
it.currentData = data;
boolean nomatch = Boolean.FALSE.equals(check.getValue(session));
it.currentData = null;
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, Object data) {
session.sessionData.currentValue = data;
boolean nomatch = Boolean.FALSE.equals(check.getValue(session));
session.sessionData.currentValue = null;
if (nomatch) {
if (table == null) {
throw Error.error(ErrorCode.X_23513, name.name);
} else {
String[] info = new String[] {
name.name, table.getName().name
};
throw Error.error(null, ErrorCode.X_23513,
ErrorCode.CONSTRAINT, info);
}
}
}
public HsqlException getException(Object[] data) {
switch (this.constType) {
case SchemaObject.ConstraintTypes.CHECK : {
String[] info = new String[]{ name.name };
return Error.error(null, ErrorCode.X_23513,
ErrorCode.CONSTRAINT, info);
}
case SchemaObject.ConstraintTypes.FOREIGN_KEY : {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < core.refCols.length; i++) {
Object o = data[core.refCols[i]];
sb.append(core.refTable.getColumnTypes()[core.refCols[i]]
.convertToString(o));
sb.append(',');
}
String[] info = new String[] {
getName().statementName,
core.refTable.getName().statementName, sb.toString()
};
return Error.error(null, ErrorCode.X_23503,
ErrorCode.CONSTRAINT, info);
}
case SchemaObject.ConstraintTypes.PRIMARY_KEY :
case SchemaObject.ConstraintTypes.UNIQUE : {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < core.mainCols.length; i++) {
Object o = data[core.mainCols[i]];
sb.append(core.mainTable.colTypes[core.mainCols[i]]
.convertToString(o));
sb.append(',');
}
return Error.error(null, ErrorCode.X_23505,
ErrorCode.CONSTRAINT, new String[] {
getName().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)
*
* @param session Session
* @param row array of objects for a database row
* @param delete should we allow 'ON DELETE CASCADE' or 'ON UPDATE CASCADE'
* @return iterator
* @
*/
RowIterator findFkRef(Session session, Object[] row) {
if (row == null || ArrayUtil.hasNull(row, core.mainCols)) {
return core.refIndex.emptyIterator();
}
PersistentStore store = session.sessionData.getRowStore(core.refTable);
return core.refIndex.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.
*/
void checkReferencedRows(Session session, Table table) {
RowIterator it = table.rowIterator(session);
while (true) {
Row row = it.getNextRow();
if (row == null) {
break;
}
Object[] rowData = row.getData();
checkInsert(session, table, rowData, false);
}
}
public Expression getCheckExpression() {
return check;
}
public OrderedHashSet getCheckColumnExpressions() {
OrderedHashSet set = new OrderedHashSet();
check.collectAllExpressions(set, Expression.columnExpressionSet,
Expression.emptyExpressionSet);
return set;
}
void recompile(Session session, Table newTable) {
String ddl = check.getSQL();
Scanner scanner = new Scanner(ddl);
ParserDQL parser = new ParserDQL(session, scanner);
parser.read();
parser.isCheckOrTriggerCondition = true;
Expression condition = parser.XreadBooleanValueExpression();
check = condition;
// this workaround is here to stop LIKE optimisation (for proper scripting)
QuerySpecification s = Expression.getCheckSelect(session, newTable,
check);
rangeVariable = s.rangeVariables[0];
rangeVariable.setForCheckConstraint();
}
void prepareCheckConstraint(Session session, Table table,
boolean checkValues) {
// to ensure no subselects etc. are in condition
check.checkValidCheckConstraint();
if (table == null) {
check.resolveTypes(session, null);
} else {
QuerySpecification s = Expression.getCheckSelect(session, table,
check);
Result r = s.getResult(session, 1);
if (r.getNavigator().getSize() != 0) {
String[] info = new String[] {
table.getName().name, ""
};
throw Error.error(null, ErrorCode.X_23513,
ErrorCode.CONSTRAINT, info);
}
rangeVariable = s.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;
}
}
}