blob: 07dfe9a6f306c491e5a909fa2548b2bdf108e92c [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.compile.ConstraintDefinitionNode
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to you under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package org.apache.derby.impl.sql.compile;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Properties;
import org.apache.derby.catalog.UUID;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.reference.SQLState;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.monitor.ModuleFactory;
import org.apache.derby.iapi.services.monitor.Monitor;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.services.uuid.UUIDFactory;
import org.apache.derby.iapi.sql.compile.Visitor;
import org.apache.derby.iapi.sql.depend.ProviderList;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
/**
* A ConstraintDefintionNode is a class for all nodes that can represent
* constraint definitions.
*
*/
public class ConstraintDefinitionNode extends TableElementNode
{
private TableName constraintName;
protected int constraintType;
protected Properties properties;
ProviderList apl;
UUIDFactory uuidFactory;
String backingIndexName;
UUID backingIndexUUID;
ResultColumnList columnList;
String constraintText;
ValueNode checkCondition;
private int behavior;
private int verifyType;
public final static boolean DEFERRABLE_DEFAULT = false;
public final static boolean INITIALLY_DEFERRED_DEFAULT = false;
public final static boolean ENFORCED_DEFAULT = true;
/**
* boolean[3]: {deferrable?, initiallyDeferred?, enforced?}
*/
private boolean[] characteristics;
ConstraintDefinitionNode(
TableName constraintName,
int constraintType,
ResultColumnList rcl,
Properties properties,
ValueNode checkCondition,
String constraintText,
int behavior,
int verifyType,
ContextManager cm)
{
/* We need to pass null as name to TableElementNode's constructor
* since constraintName may be null.
*/
super(null, cm);
this.constraintName = constraintName;
if (this.constraintName != null)
{
this.name = this.constraintName.getTableName();
}
this.constraintType = constraintType;
this.properties = properties;
this.columnList = rcl;
this.checkCondition = checkCondition;
this.constraintText = constraintText;
this.behavior = behavior;
this.verifyType = verifyType;
}
void setCharacteristics(boolean[] cc) {
characteristics = cc.clone();
}
boolean[] getCharacteristics() {
if (characteristics == null) {
characteristics = new boolean[]{
ConstraintDefinitionNode.DEFERRABLE_DEFAULT,
ConstraintDefinitionNode.INITIALLY_DEFERRED_DEFAULT,
ConstraintDefinitionNode.ENFORCED_DEFAULT
};
}
return characteristics.clone();
}
/**
* Convert this object to a String. See comments in QueryTreeNode.java
* for how this should be done for tree printing.
*
* @return This object as a String
*/
@Override
public String toString()
{
if (SanityManager.DEBUG)
{
return "constraintName: " +
( ( constraintName != null) ?
constraintName.toString() : "null" ) + "\n" +
"constraintType: " + constraintType + "\n" +
"properties: " +
((properties != null) ? properties.toString() : "null") + "\n" +
super.toString();
}
else
{
return "";
}
}
/**
* Bind this constraint definition.
*
@param ddlNode the create or alter table node
* @param dd the dd
*
* @exception StandardException on error
*/
void bind(DDLStatementNode ddlNode, DataDictionary dd) throws StandardException
{
// we need to allow drops on constraints with different schemas
// to support removing constraints created pre 5.2.
if (constraintType == DataDictionary.DROP_CONSTRAINT)
return;
// ensure the schema of the constraint matches the schema of the table
if (constraintName != null) {
String constraintSchema = constraintName.getSchemaName();
if (constraintSchema != null) {
TableName tableName = ddlNode.getObjectName();
String tableSchema = tableName.getSchemaName();
if (tableSchema == null) {
tableSchema = getSchemaDescriptor((String) null).getSchemaName();
tableName.setSchemaName(tableSchema);
}
if (!constraintSchema.equals(tableSchema)) {
throw StandardException.newException(SQLState.LANG_CONSTRAINT_SCHEMA_MISMATCH,
constraintName, tableName);
}
}
}
else {
name = getBackingIndexName(dd);
}
}
/**
* Get the name of the constraint. If the user didn't provide one, we make one up. This allows Replication
* to agree with the core compiler on the names of constraints.
*
* @return constraint name
*/
String getConstraintMoniker()
{
return name;
}
/**
To support dropping existing constraints that may have mismatched schema names
we need to support ALTER TABLE S1.T DROP CONSTRAINT S2.C.
If a constraint name was specified this returns it, otherwise it returns null.
*/
String getDropSchemaName() {
if (constraintName != null)
return constraintName.getSchemaName();
return null;
}
/**
* Allocates a UUID if one doesn't already exist for the index backing this constraint. This allows Replication
* logic to agree with the core compiler on what the UUIDs of indices are.
*
* @return a UUID for the constraint. allocates one if this is the first time this method is called.
*/
UUID getBackingIndexUUID()
{
if ( backingIndexUUID == null )
{
backingIndexUUID = getUUIDFactory().createUUID();
}
return backingIndexUUID;
}
/**
* Gets a unique name for the backing index for this constraint of the form SQLyymmddhhmmssxxn
* yy - year, mm - month, dd - day of month, hh - hour, mm - minute, ss - second,
* xx - the first 2 digits of millisec because we don't have enough space to keep the exact millisec value,
* n - number between 0-9
*
* @return name of backing index
*/
String getBackingIndexName(DataDictionary dd)
{
if ( backingIndexName == null )
backingIndexName = dd.getSystemSQLName();
return backingIndexName;
}
/**
* Set the auxiliary provider list.
*
* @param apl The new auxiliary provider list.
*/
void setAuxiliaryProviderList(ProviderList apl)
{
this.apl = apl;
}
/**
* Return the auxiliary provider list.
*
* @return The auxiliary provider list.
*/
ProviderList getAuxiliaryProviderList()
{
return apl;
}
/**
* Is this a primary key constraint.
*
* @return boolean Whether or not this is a primary key constraint
*/
@Override
boolean hasPrimaryKeyConstraint()
{
return constraintType == DataDictionary.PRIMARYKEY_CONSTRAINT;
}
/**
* Is this a unique key constraint.
*
* @return boolean Whether or not this is a unique key constraint
*/
@Override
boolean hasUniqueKeyConstraint()
{
return constraintType == DataDictionary.UNIQUE_CONSTRAINT;
}
/**
* Is this a foreign key constraint.
*
* @return boolean Whether or not this is a unique key constraint
*/
@Override
boolean hasForeignKeyConstraint()
{
return constraintType == DataDictionary.FOREIGNKEY_CONSTRAINT;
}
/**
* Does this element have a check constraint.
*
* @return boolean Whether or not this element has a check constraint
*/
@Override
boolean hasCheckConstraint()
{
return constraintType == DataDictionary.CHECK_CONSTRAINT;
}
/**
* Does this element have a constraint on it.
*
* @return boolean Whether or not this element has a constraint on it
*/
@Override
boolean hasConstraint()
{
return true;
}
/**
* Does this constraint require a backing index for its implementation?
*
* @return boolean {@code true} if this constraint requires a backing
* index, i.e. if is a foreign key, primary key or
* unique key constraint
*/
boolean requiresBackingIndex()
{
switch (constraintType)
{
case DataDictionary.FOREIGNKEY_CONSTRAINT:
case DataDictionary.PRIMARYKEY_CONSTRAINT:
case DataDictionary.UNIQUE_CONSTRAINT:
return true;
default:
return false;
}
}
/**
* Is this a primary key or unique constraint?
*
* @return boolean {@code true} if this is a primary key or
* unique key constraint
*/
boolean requiresUniqueIndex()
{
switch (constraintType)
{
case DataDictionary.PRIMARYKEY_CONSTRAINT:
case DataDictionary.UNIQUE_CONSTRAINT:
return true;
default:
return false;
}
}
/**
* Get the constraint type
*
* @return constraintType The constraint type.
*/
int getConstraintType()
{
return constraintType;
}
/**
* Set the optional properties for the backing index to this constraint.
*
* @param properties The optional Properties for this constraint.
*/
void setProperties(Properties properties)
{
this.properties = properties;
}
/**
* Get the optional properties for the backing index to this constraint.
*
*
* @return The optional properties for the backing index to this constraint
*/
Properties getProperties()
{
return properties;
}
/**
* Is this constraint referenced.
*
* @return true/false
*/
boolean isReferenced()
{
return false;
}
/**
* Get the count of enabled fks
* that reference this constraint
*
* @return the number
*/
int getReferenceCount()
{
return 0;
}
/**
* Is this constraint enabled.
*
* @return true/false
*/
boolean isEnabled()
{
return true;
}
/**
* Get the column list from this node.
*
* @return ResultColumnList The column list from this table constraint.
*/
ResultColumnList getColumnList()
{
return columnList;
}
/**
* Set the column list for this node. This is useful for check constraints
* where the list of referenced columns is built at bind time.
*
* @param columnList The new columnList.
*/
void setColumnList(ResultColumnList columnList)
{
this.columnList = columnList;
}
/**
* Get the check condition from this table constraint.
*
* @return The check condition from this node.
*/
ValueNode getCheckCondition()
{
return checkCondition;
}
/**
* Set the check condition for this table constraint.
*
* @param checkCondition The check condition
*/
void setCheckCondition(ValueNode checkCondition)
{
this.checkCondition = checkCondition;
}
/**
* Get the text of the constraint. (Only meaningful for check constraints.)
*
* @return The constraint text.
*/
String getConstraintText()
{
return constraintText;
}
/**
* Return the behavior of this constraint.
* See {@link org.apache.derby.iapi.sql.StatementType#DROP_CASCADE} etc.
*
* @return the behavior
*/
int getDropBehavior()
{
return behavior;
}
/**
* @return the expected type of the constraint, DataDictionary.DROP_CONSTRAINT if the constraint is
* to be dropped without checking its type.
*/
int getVerifyType()
{
return verifyType;
}
///////////////////////////////////////////////////////////////////////////
//
// MINIONS
//
///////////////////////////////////////////////////////////////////////////
/**
* Get the UUID factory
*
* @return the UUID factory
*
*/
private UUIDFactory getUUIDFactory()
{
if ( uuidFactory == null )
{
uuidFactory = getMonitor().getUUIDFactory();
}
return uuidFactory;
}
@Override
void acceptChildren(Visitor v) throws StandardException {
super.acceptChildren(v);
if (constraintName != null) {
constraintName = (TableName) constraintName.accept(v);
}
}
/**
* Qualify all SQL object names in a CHECK constraint with schema name.
* @throws StandardException if an error occurs
*/
void qualifyNames() throws StandardException {
// Get all references to SQL object names in the CHECK constraint,
// ordered as they appear in the constraint definition.
OffsetOrderVisitor<TableName> visitor =
new OffsetOrderVisitor<TableName>(TableName.class,
checkCondition.getBeginOffset(),
checkCondition.getEndOffset() + 1);
checkCondition.accept(visitor);
StringBuilder sb = new StringBuilder();
int pos = 0;
int offset = checkCondition.getBeginOffset();
// Replace all names with fully qualified names.
for (TableName tableName : visitor.getNodes()) {
sb.append(constraintText, pos, tableName.getBeginOffset() - offset);
sb.append(tableName.getFullSQLName());
pos = tableName.getEndOffset() + 1 - offset;
}
sb.append(constraintText, pos, constraintText.length());
constraintText = sb.toString();
}
/**
* Privileged Monitor lookup. Must be private so that user code
* can't call this entry point.
*/
private static ModuleFactory getMonitor()
{
return AccessController.doPrivileged
(
new PrivilegedAction<ModuleFactory>()
{
public ModuleFactory run()
{
return Monitor.getMonitor();
}
}
);
}
}