blob: eef47bcc84bc4aa42fffc0d415edd0004d2fde74 [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.catalog.SYSCONSTRAINTSRowFactory
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.catalog;
import java.sql.Types;
import org.apache.derby.catalog.UUID;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.services.uuid.UUIDFactory;
import org.apache.derby.iapi.sql.dictionary.CatalogRowFactory;
import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
import org.apache.derby.iapi.sql.dictionary.SubCheckConstraintDescriptor;
import org.apache.derby.iapi.sql.dictionary.SubConstraintDescriptor;
import org.apache.derby.iapi.sql.dictionary.SubKeyConstraintDescriptor;
import org.apache.derby.iapi.sql.dictionary.SystemColumn;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
import org.apache.derby.iapi.sql.dictionary.TupleDescriptor;
import org.apache.derby.iapi.sql.execute.ExecRow;
import org.apache.derby.iapi.sql.execute.ExecutionFactory;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.DataValueFactory;
import org.apache.derby.iapi.types.SQLChar;
import org.apache.derby.iapi.types.SQLInteger;
import org.apache.derby.iapi.types.SQLVarchar;
import org.apache.derby.impl.sql.compile.ConstraintDefinitionNode;
/**
* Factory for creating a SYSCONTRAINTS row.
*
*/
public class SYSCONSTRAINTSRowFactory extends CatalogRowFactory
{
private static final String TABLENAME_STRING = "SYSCONSTRAINTS";
protected static final int SYSCONSTRAINTS_COLUMN_COUNT = 7;
protected static final int SYSCONSTRAINTS_CONSTRAINTID = 1;
protected static final int SYSCONSTRAINTS_TABLEID = 2;
protected static final int SYSCONSTRAINTS_CONSTRAINTNAME = 3;
protected static final int SYSCONSTRAINTS_TYPE = 4;
protected static final int SYSCONSTRAINTS_SCHEMAID = 5;
public static final int SYSCONSTRAINTS_STATE = ConstraintDescriptor.SYSCONSTRAINTS_STATE_FIELD;
protected static final int SYSCONSTRAINTS_REFERENCECOUNT = 7;
protected static final int SYSCONSTRAINTS_INDEX1_ID = 0;
protected static final int SYSCONSTRAINTS_INDEX2_ID = 1;
protected static final int SYSCONSTRAINTS_INDEX3_ID = 2;
private static final boolean[] uniqueness = {
true,
true,
false
};
private static final int[][] indexColumnPositions =
{
{SYSCONSTRAINTS_CONSTRAINTID},
{SYSCONSTRAINTS_CONSTRAINTNAME, SYSCONSTRAINTS_SCHEMAID},
{SYSCONSTRAINTS_TABLEID}
};
private static final String[] uuids =
{
"8000002f-00d0-fd77-3ed8-000a0a0b1900" // catalog UUID
,"80000036-00d0-fd77-3ed8-000a0a0b1900" // heap UUID
,"80000031-00d0-fd77-3ed8-000a0a0b1900" // SYSCONSTRAINTS_INDEX1
,"80000033-00d0-fd77-3ed8-000a0a0b1900" // SYSCONSTRAINTS_INDEX2
,"80000035-00d0-fd77-3ed8-000a0a0b1900" // SYSCONSTRAINTS_INDEX3
};
/////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTORS
//
/////////////////////////////////////////////////////////////////////////////
SYSCONSTRAINTSRowFactory(UUIDFactory uuidf, ExecutionFactory ef, DataValueFactory dvf)
{
super(uuidf,ef,dvf);
initInfo(SYSCONSTRAINTS_COLUMN_COUNT, TABLENAME_STRING,
indexColumnPositions, uniqueness, uuids );
}
/////////////////////////////////////////////////////////////////////////////
//
// METHODS
//
/////////////////////////////////////////////////////////////////////////////
/**
* Make a SYSCONTRAINTS row
*
* @return Row suitable for inserting into SYSCONTRAINTS.
*
* @exception StandardException thrown on failure
*/
public ExecRow makeRow(TupleDescriptor td, TupleDescriptor parent)
throws StandardException
{
DataValueDescriptor col;
ExecRow row;
int constraintIType;
UUID oid;
String constraintSType = null;
String constraintID = null;
String tableID = null;
String constraintName = null;
String schemaID = null;
boolean deferrable =
ConstraintDefinitionNode.DEFERRABLE_DEFAULT;
boolean initiallyDeferred =
ConstraintDefinitionNode.INITIALLY_DEFERRED_DEFAULT;
boolean enforced =
ConstraintDefinitionNode.ENFORCED_DEFAULT;
int referenceCount = 0;
if (td != null)
{
ConstraintDescriptor constraint = (ConstraintDescriptor)td;
/*
** We only allocate a new UUID if the descriptor doesn't already have one.
** For descriptors replicated from a Source system, we already have an UUID.
*/
oid = constraint.getUUID();
constraintID = oid.toString();
oid = constraint.getTableId();
tableID = oid.toString();
constraintName = constraint.getConstraintName();
constraintIType = constraint.getConstraintType();
switch (constraintIType)
{
case DataDictionary.PRIMARYKEY_CONSTRAINT:
constraintSType = "P";
break;
case DataDictionary.UNIQUE_CONSTRAINT:
constraintSType = "U";
break;
case DataDictionary.CHECK_CONSTRAINT:
constraintSType = "C";
break;
case DataDictionary.FOREIGNKEY_CONSTRAINT:
constraintSType = "F";
break;
default:
if (SanityManager.DEBUG)
{
SanityManager.THROWASSERT("invalid constraint type");
}
}
schemaID = constraint.getSchemaDescriptor().getUUID().toString();
// constraint characteristics
deferrable = constraint.deferrable();
initiallyDeferred = constraint.initiallyDeferred();
enforced = constraint.enforced();
referenceCount = constraint.getReferenceCount();
}
/* Insert info into sysconstraints */
/* RESOLVE - It would be nice to require less knowledge about sysconstraints
* and have this be more table driven.
*/
/* Build the row to insert */
row = getExecutionFactory().getValueRow(SYSCONSTRAINTS_COLUMN_COUNT);
/* 1st column is CONSTRAINTID (UUID - char(36)) */
row.setColumn(SYSCONSTRAINTS_CONSTRAINTID, new SQLChar(constraintID));
/* 2nd column is TABLEID (UUID - char(36)) */
row.setColumn(SYSCONSTRAINTS_TABLEID, new SQLChar(tableID));
/* 3rd column is NAME (varchar(128)) */
row.setColumn(SYSCONSTRAINTS_CONSTRAINTNAME, new SQLVarchar(constraintName));
/* 4th column is TYPE (char(1)) */
row.setColumn(SYSCONSTRAINTS_TYPE, new SQLChar(constraintSType));
/* 5th column is SCHEMAID (UUID - char(36)) */
row.setColumn(SYSCONSTRAINTS_SCHEMAID, new SQLChar(schemaID));
/* 6th column is STATE (char(1)) */
row.setColumn(SYSCONSTRAINTS_STATE,
new SQLChar(encodeCharacteristics(deferrable, initiallyDeferred, enforced)));
/* 7th column is REFERENCED */
row.setColumn(SYSCONSTRAINTS_REFERENCECOUNT, new SQLInteger(referenceCount));
return row;
}
/*
* Encode the characteristics of the constraints into a single character.
*
* {deferrable, initiallyDeferred, enforced} -> 'e'
* {deferrable, initiallyDeferred, not enforced} -> 'd'
* {deferrable, immediate, enforced} -> 'i'
* {deferrable, immediate, not enforced} -> 'j'
* {not deferrable, immediate, enforced} -> 'E'
* {not deferrable, immediate, not enforced -> 'D'
*
* Other combinations are prohibited and not used. Note that the
* value 'E' is only value used prior to version 10.11, and as
* such upward compatibily since by default, constraints are {not
* deferrable, immediate, enforced}.
*/
private String encodeCharacteristics(
boolean deferrable, boolean initiallyDeferred, boolean enforced) {
char c;
if (deferrable) {
if (initiallyDeferred) {
if (enforced) {
c = 'e'; // deferrable initially deferred enforced
} else {
c = 'd'; // deferrable initially deferred not enforced
}
} else {
if (enforced) {
c = 'i'; // deferrable initially immediate enforced
} else {
c = 'j'; // deferrable initially immediate not enforced
}
}
} else {
if (initiallyDeferred) {
if (SanityManager.DEBUG) {
SanityManager.NOTREACHED();
}
c = 'E';
} else {
if (enforced) {
c = 'E'; // not deferrable initially immediate enforced
} else {
c = 'D'; // not deferrable initially immediate not enforced
}
}
}
return String.valueOf(c);
}
///////////////////////////////////////////////////////////////////////////
//
// ABSTRACT METHODS TO BE IMPLEMENTED BY CHILDREN OF CatalogRowFactory
//
///////////////////////////////////////////////////////////////////////////
/**
* Make a ConstraintDescriptor out of a SYSCONSTRAINTS row
*
* @param row a SYSCONSTRAINTS row
* @param parentTupleDescriptor Subconstraint descriptor with auxiliary info.
* @param dd dataDictionary
*
* @exception StandardException thrown on failure
*/
public TupleDescriptor buildDescriptor(
ExecRow row,
TupleDescriptor parentTupleDescriptor,
DataDictionary dd )
throws StandardException
{
ConstraintDescriptor constraintDesc = null;
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(
row.nColumns() == SYSCONSTRAINTS_COLUMN_COUNT,
"Wrong number of columns for a SYSCONSTRAINTS row");
}
DataValueDescriptor col;
ConglomerateDescriptor conglomDesc;
DataDescriptorGenerator ddg;
TableDescriptor td = null;
int constraintIType = -1;
int[] keyColumns = null;
UUID constraintUUID;
UUID schemaUUID;
UUID tableUUID;
UUID referencedConstraintId = null;
SchemaDescriptor schema;
String tableUUIDString;
String constraintName;
String constraintSType;
String constraintStateStr;
boolean deferrable =
ConstraintDefinitionNode.DEFERRABLE_DEFAULT;
boolean initiallyDeferred =
ConstraintDefinitionNode.INITIALLY_DEFERRED_DEFAULT;
boolean enforced =
ConstraintDefinitionNode.ENFORCED_DEFAULT;
int referenceCount;
String constraintUUIDString;
String schemaUUIDString;
SubConstraintDescriptor scd;
if (SanityManager.DEBUG)
{
if (!(parentTupleDescriptor instanceof SubConstraintDescriptor))
{
SanityManager.THROWASSERT(
"parentTupleDescriptor expected to be instanceof " +
"SubConstraintDescriptor, not " +
parentTupleDescriptor.getClass().getName());
}
}
scd = (SubConstraintDescriptor) parentTupleDescriptor;
ddg = dd.getDataDescriptorGenerator();
/* 1st column is CONSTRAINTID (UUID - char(36)) */
col = row.getColumn(SYSCONSTRAINTS_CONSTRAINTID);
constraintUUIDString = col.getString();
constraintUUID = getUUIDFactory().recreateUUID(constraintUUIDString);
/* 2nd column is TABLEID (UUID - char(36)) */
col = row.getColumn(SYSCONSTRAINTS_TABLEID);
tableUUIDString = col.getString();
tableUUID = getUUIDFactory().recreateUUID(tableUUIDString);
/* Get the TableDescriptor.
* It may be cached in the SCD,
* otherwise we need to go to the
* DD.
*/
if (scd != null)
{
td = scd.getTableDescriptor();
}
if (td == null)
{
td = dd.getTableDescriptor(tableUUID);
}
/* 3rd column is NAME (varchar(128)) */
col = row.getColumn(SYSCONSTRAINTS_CONSTRAINTNAME);
constraintName = col.getString();
/* 4th column is TYPE (char(1)) */
col = row.getColumn(SYSCONSTRAINTS_TYPE);
constraintSType = col.getString();
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(constraintSType.length() == 1,
"Fourth column type incorrect");
}
boolean typeSet = false;
switch (constraintSType.charAt(0))
{
case 'P' :
constraintIType = DataDictionary.PRIMARYKEY_CONSTRAINT;
typeSet = true;
// fall through
case 'U' :
if (! typeSet)
{
constraintIType = DataDictionary.UNIQUE_CONSTRAINT;
typeSet = true;
}
// fall through
case 'F' :
if (! typeSet)
constraintIType = DataDictionary.FOREIGNKEY_CONSTRAINT;
if (SanityManager.DEBUG)
{
if (!(parentTupleDescriptor instanceof SubKeyConstraintDescriptor))
{
SanityManager.THROWASSERT(
"parentTupleDescriptor expected to be instanceof " +
"SubKeyConstraintDescriptor, not " +
parentTupleDescriptor.getClass().getName());
}
}
conglomDesc = td.getConglomerateDescriptor(
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getIndexId());
/* Take care the rare case of conglomDesc being null. The
* reason is that our "td" is out of date. Another thread
* which was adding a constraint committed between the moment
* we got the table descriptor (conglomerate list) and the
* moment we scanned and got the constraint desc list. Since
* that thread just added a new row to SYSCONGLOMERATES,
* SYSCONSTRAINTS, etc. We wouldn't have wanted to lock the
* system tables just to prevent other threads from adding new
* rows.
*/
if (conglomDesc == null)
{
// we can't be getting td from cache because if we are
// here, we must have been in dd's ddl mode (that's why
// the ddl thread went through), we are not done yet, the
// dd ref count is not 0, hence it couldn't have turned
// into COMPILE_ONLY mode
td = dd.getTableDescriptor(tableUUID);
if (scd != null)
scd.setTableDescriptor(td);
// try again now
conglomDesc = td.getConglomerateDescriptor(
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getIndexId());
}
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(conglomDesc != null,
"conglomDesc is expected to be non-null for backing index");
}
keyColumns = conglomDesc.getIndexDescriptor().baseColumnPositions();
referencedConstraintId = ((SubKeyConstraintDescriptor)
parentTupleDescriptor).getKeyConstraintId();
keyColumns = conglomDesc.getIndexDescriptor().baseColumnPositions();
break;
case 'C' :
constraintIType = DataDictionary.CHECK_CONSTRAINT;
if (SanityManager.DEBUG)
{
if (!(parentTupleDescriptor instanceof SubCheckConstraintDescriptor))
{
SanityManager.THROWASSERT("parentTupleDescriptor expected to be instanceof " +
"SubCheckConstraintDescriptor, not " +
parentTupleDescriptor.getClass().getName());
}
}
break;
default:
if (SanityManager.DEBUG)
{
SanityManager.THROWASSERT("Fourth column value invalid");
}
}
/* 5th column is SCHEMAID (UUID - char(36)) */
col = row.getColumn(SYSCONSTRAINTS_SCHEMAID);
schemaUUIDString = col.getString();
schemaUUID = getUUIDFactory().recreateUUID(schemaUUIDString);
schema = dd.getSchemaDescriptor(schemaUUID, null);
/* 6th column is STATE (char(1)) */
col = row.getColumn(SYSCONSTRAINTS_STATE);
constraintStateStr = col.getString();
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(constraintStateStr.length() == 1,
"Sixth column (state) type incorrect");
}
// Cf. the encoding description in javadoc for
// #encodeCharacteristics.
//
// This code is cloned by DB_Key.makeDeferredClauses().
//
switch (constraintStateStr.charAt(0))
{
case 'E':
deferrable = false;
initiallyDeferred = false;
enforced = true;
break;
case 'D':
deferrable = false;
initiallyDeferred = false;
enforced = false;
break;
case 'e':
deferrable = true;
initiallyDeferred = true;
enforced = true;
break;
case 'd':
deferrable = true;
initiallyDeferred = true;
enforced = false;
break;
case 'i':
deferrable = true;
initiallyDeferred = false;
enforced = true;
break;
case 'j':
deferrable = true;
initiallyDeferred = false;
enforced = false;
break;
default:
if (SanityManager.DEBUG)
{
SanityManager.THROWASSERT("Invalidate state value '"
+constraintStateStr+ "' for constraint");
}
}
/* 7th column is REFERENCECOUNT, boolean */
col = row.getColumn(SYSCONSTRAINTS_REFERENCECOUNT);
referenceCount = col.getInt();
/* now build and return the descriptor */
switch (constraintIType)
{
case DataDictionary.PRIMARYKEY_CONSTRAINT :
constraintDesc = ddg.newPrimaryKeyConstraintDescriptor(
td,
constraintName,
deferrable,
initiallyDeferred,
keyColumns,//genReferencedColumns(dd, td), //int referencedColumns[],
constraintUUID,
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getIndexId(),
schema,
enforced,
referenceCount);
break;
case DataDictionary.UNIQUE_CONSTRAINT :
constraintDesc = ddg.newUniqueConstraintDescriptor(
td,
constraintName,
deferrable,
initiallyDeferred,
keyColumns,//genReferencedColumns(dd, td), //int referencedColumns[],
constraintUUID,
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getIndexId(),
schema,
enforced,
referenceCount);
break;
case DataDictionary.FOREIGNKEY_CONSTRAINT :
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(referenceCount == 0,
"REFERENCECOUNT column is nonzero for fk constraint");
}
constraintDesc = ddg.newForeignKeyConstraintDescriptor(
td,
constraintName,
deferrable,
initiallyDeferred,
keyColumns,//genReferencedColumns(dd, td), //int referencedColumns[],
constraintUUID,
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getIndexId(),
schema,
referencedConstraintId,
enforced,
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getRaDeleteRule(),
((SubKeyConstraintDescriptor)
parentTupleDescriptor).getRaUpdateRule()
);
break;
case DataDictionary.CHECK_CONSTRAINT :
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(referenceCount == 0,
"REFERENCECOUNT column is nonzero for check constraint");
}
constraintDesc = ddg.newCheckConstraintDescriptor(
td,
constraintName,
deferrable,
initiallyDeferred,
constraintUUID,
((SubCheckConstraintDescriptor)
parentTupleDescriptor).getConstraintText(),
((SubCheckConstraintDescriptor)
parentTupleDescriptor).getReferencedColumnsDescriptor(),
schema,
enforced);
break;
}
return constraintDesc;
}
/**
* Get the constraint ID of the row.
*
* @param row The row from sysconstraints
*
* @return UUID The constraint id
*
* @exception StandardException thrown on failure
*/
protected UUID getConstraintId(ExecRow row)
throws StandardException
{
DataValueDescriptor col;
String constraintUUIDString;
/* 1st column is CONSTRAINTID (UUID - char(36)) */
col = row.getColumn(SYSCONSTRAINTS_CONSTRAINTID);
constraintUUIDString = col.getString();
return getUUIDFactory().recreateUUID(constraintUUIDString);
}
/**
* Get the constraint name of the row.
*
* @param row The row from sysconstraints
*
* @return UUID The constraint name
*
* @exception StandardException thrown on failure
*/
protected String getConstraintName(ExecRow row)
throws StandardException
{
DataValueDescriptor col;
String constraintName;
/* 3rd column is CONSTRAINTNAME (char(128)) */
col = row.getColumn(SYSCONSTRAINTS_CONSTRAINTNAME);
constraintName = col.getString();
return constraintName;
}
/**
* Get the schema ID of the row.
*
* @param row The row from sysconstraints
*
* @return UUID The schema
*
* @exception StandardException thrown on failure
*/
protected UUID getSchemaId(ExecRow row)
throws StandardException
{
DataValueDescriptor col;
String schemaUUIDString;
/* 5th column is SCHEMAID (UUID - char(36)) */
col = row.getColumn(SYSCONSTRAINTS_SCHEMAID);
schemaUUIDString =col.getString();
return getUUIDFactory().recreateUUID(schemaUUIDString);
}
/**
* Get the table ID of the row.
*
* @param row The row from sysconstraints
*
* @return UUID The table id
*
* @exception StandardException thrown on failure
*/
protected UUID getTableId(ExecRow row)
throws StandardException
{
DataValueDescriptor col;
String tableUUIDString;
/* 2nd column is TABLEID (UUID - char(36)) */
col = row.getColumn(SYSCONSTRAINTS_TABLEID);
tableUUIDString = col.getString();
return getUUIDFactory().recreateUUID(tableUUIDString);
}
/**
* Get the constraint type out of the row.
*
* @param row The row from sysconstraints
*
* @return int The constraint type as an int
*
* @exception StandardException thrown on failure
*/
protected int getConstraintType(ExecRow row)
throws StandardException
{
DataValueDescriptor col;
int constraintIType;
String constraintSType;
/* 4th column is TYPE (char(1)) */
col = row.getColumn(SYSCONSTRAINTS_TYPE);
constraintSType = col.getString();
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(constraintSType.length() == 1,
"Fourth column type incorrect");
}
switch (constraintSType.charAt(0))
{
case 'P' :
constraintIType = DataDictionary.PRIMARYKEY_CONSTRAINT;
break;
case 'U' :
constraintIType = DataDictionary.UNIQUE_CONSTRAINT;
break;
case 'C' :
constraintIType = DataDictionary.CHECK_CONSTRAINT;
break;
case 'F' :
constraintIType = DataDictionary.FOREIGNKEY_CONSTRAINT;
break;
default:
if (SanityManager.DEBUG)
{
SanityManager.THROWASSERT("Fourth column value invalid");
}
constraintIType = -1;
}
return constraintIType;
}
/**
* Builds a list of columns suitable for creating this Catalog.
*
*
* @return array of SystemColumn suitable for making this catalog.
*/
public SystemColumn[] buildColumnList()
{
return new SystemColumn[] {
SystemColumnImpl.getUUIDColumn("CONSTRAINTID", false),
SystemColumnImpl.getUUIDColumn("TABLEID", false),
SystemColumnImpl.getIdentifierColumn("CONSTRAINTNAME", false),
SystemColumnImpl.getIndicatorColumn("TYPE"),
SystemColumnImpl.getUUIDColumn("SCHEMAID", false),
SystemColumnImpl.getIndicatorColumn("STATE"),
SystemColumnImpl.getColumn("REFERENCECOUNT", Types.INTEGER, false)
};
}
}