use of org.apache.derby.iapi.sql.dictionary.TriggerDescriptor in project derby by apache.
the class SYSTRIGGERSRowFactory method buildDescriptor.
// /////////////////////////////////////////////////////////////////////////
//
// ABSTRACT METHODS TO BE IMPLEMENTED BY CHILDREN OF CatalogRowFactory
//
// /////////////////////////////////////////////////////////////////////////
/**
* Make an Tuple Descriptor out of a SYSTRIGGERS row
*
* @param row a SYSTRIGGERS row
* @param parentTupleDescriptor unused
* @param dd dataDictionary
*
* @return a descriptor equivalent to a SYSTRIGGERS row
*
* @exception StandardException thrown on failure
*/
public TupleDescriptor buildDescriptor(ExecRow row, TupleDescriptor parentTupleDescriptor, DataDictionary dd) throws StandardException {
DataValueDescriptor col;
String name;
char theChar;
String uuidStr;
String triggerDefinition;
String oldReferencingName;
String newReferencingName;
UUID uuid;
// schema
UUID suuid;
// referenced table
UUID tuuid;
// action sps uuid string
UUID actionSPSID = null;
// when clause sps uuid string
UUID whenSPSID = null;
Timestamp createTime;
int eventMask = 0;
boolean isBefore;
boolean isRow;
boolean isEnabled;
boolean referencingOld;
boolean referencingNew;
ReferencedColumns rcd;
TriggerDescriptor descriptor;
DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
if (SanityManager.DEBUG) {
// The expected number of columns depends on the version of the
// data dictionary. The WHENCLAUSETEXT column was added in version
// 10.11 (DERBY-534).
int expectedCols = dd.checkVersion(DataDictionary.DD_VERSION_DERBY_10_11, null) ? SYSTRIGGERS_COLUMN_COUNT : (SYSTRIGGERS_COLUMN_COUNT - 1);
SanityManager.ASSERT(row.nColumns() == expectedCols, "Wrong number of columns for a SYSTRIGGERS row");
}
// 1st column is TRIGGERID (UUID - char(36))
col = row.getColumn(1);
uuidStr = col.getString();
uuid = getUUIDFactory().recreateUUID(uuidStr);
// 2nd column is TRIGGERNAME (varchar(128))
col = row.getColumn(2);
name = col.getString();
// 3rd column is SCHEMAID (UUID - char(36))
col = row.getColumn(3);
uuidStr = col.getString();
suuid = getUUIDFactory().recreateUUID(uuidStr);
// 4th column is CREATIONTIMESTAMP (TIMESTAMP)
col = row.getColumn(4);
createTime = col.getTimestamp(getCalendarForCreationTimestamp());
// 5th column is EVENT (char(1))
col = row.getColumn(5);
theChar = col.getString().charAt(0);
switch(theChar) {
case 'U':
eventMask = TriggerDescriptor.TRIGGER_EVENT_UPDATE;
break;
case 'I':
eventMask = TriggerDescriptor.TRIGGER_EVENT_INSERT;
break;
case 'D':
eventMask = TriggerDescriptor.TRIGGER_EVENT_DELETE;
break;
default:
if (SanityManager.DEBUG) {
SanityManager.THROWASSERT("bad event mask: " + theChar);
}
}
// 6th column is FIRINGTIME (char(1))
isBefore = getCharBoolean(row.getColumn(6), 'B', 'A');
// 7th column is TYPE (char(1))
isRow = getCharBoolean(row.getColumn(7), 'R', 'S');
// 8th column is STATE (char(1))
isEnabled = getCharBoolean(row.getColumn(8), 'E', 'D');
// 9th column is TABLEID (UUID - char(36))
col = row.getColumn(9);
uuidStr = col.getString();
tuuid = getUUIDFactory().recreateUUID(uuidStr);
// 10th column is WHENSTMTID (UUID - char(36))
col = row.getColumn(10);
uuidStr = col.getString();
if (uuidStr != null)
whenSPSID = getUUIDFactory().recreateUUID(uuidStr);
// 11th column is ACTIONSTMTID (UUID - char(36))
col = row.getColumn(11);
uuidStr = col.getString();
if (uuidStr != null)
actionSPSID = getUUIDFactory().recreateUUID(uuidStr);
// 12th column is REFERENCEDCOLUMNS user type org.apache.derby.catalog.ReferencedColumns
col = row.getColumn(12);
rcd = (ReferencedColumns) col.getObject();
// 13th column is TRIGGERDEFINITION (longvarchar)
col = row.getColumn(13);
triggerDefinition = col.getString();
// 14th column is REFERENCINGOLD (boolean)
col = row.getColumn(14);
referencingOld = col.getBoolean();
// 15th column is REFERENCINGNEW (boolean)
col = row.getColumn(15);
referencingNew = col.getBoolean();
// 16th column is REFERENCINGNAME (varchar(128))
col = row.getColumn(16);
oldReferencingName = col.getString();
// 17th column is REFERENCINGNAME (varchar(128))
col = row.getColumn(17);
newReferencingName = col.getString();
// 18th column is WHENCLAUSETEXT (longvarchar)
String whenClauseText = null;
if (row.nColumns() >= 18) {
// This column is present only if the data dictionary version is
// 10.11 or higher.
col = row.getColumn(18);
whenClauseText = col.getString();
}
descriptor = ddg.newTriggerDescriptor(dd.getSchemaDescriptor(suuid, null), uuid, name, eventMask, isBefore, isRow, isEnabled, dd.getTableDescriptor(tuuid), whenSPSID, actionSPSID, createTime, (rcd == null) ? (int[]) null : rcd.getReferencedColumnPositions(), (rcd == null) ? (int[]) null : rcd.getTriggerActionReferencedColumnPositions(), triggerDefinition, referencingOld, referencingNew, oldReferencingName, newReferencingName, whenClauseText);
return descriptor;
}
use of org.apache.derby.iapi.sql.dictionary.TriggerDescriptor in project derby by apache.
the class DeleteNode method getDeleteReadMap.
/**
* Builds a bitmap of all columns which should be read from the
* Store in order to satisfy an DELETE statement.
*
* 1) finds all indices on this table
* 2) adds the index columns to a bitmap of affected columns
* 3) adds the index descriptors to a list of conglomerate
* descriptors.
* 4) finds all DELETE triggers on the table
* 5) if there are any DELETE triggers, then do one of the following
* a)If all of the triggers have MISSING referencing clause, then that
* means that the trigger actions do not have access to before and
* after values. In that case, there is no need to blanketly decide
* to include all the columns in the read map just because there are
* triggers defined on the table.
* b)Since one/more triggers have REFERENCING clause on them, get all
* the columns because we don't know what the user will ultimately
* reference.
* 6) adds the triggers to an evolving list of triggers
*
* @param conglomerates OUT: list of affected indices
* @param relevantTriggers IN/OUT. Passed in as an empty list. Filled in as we go.
* @param needsDeferredProcessing IN/OUT. true if the statement already needs
* deferred processing. set while evaluating this
* routine if a trigger requires
* deferred processing
*
* @return a FormatableBitSet of columns to be read out of the base table
*
* @exception StandardException Thrown on error
*/
private static FormatableBitSet getDeleteReadMap(TableDescriptor baseTable, List<ConglomerateDescriptor> conglomerates, TriggerDescriptorList relevantTriggers, boolean[] needsDeferredProcessing) throws StandardException {
int columnCount = baseTable.getMaxColumnID();
FormatableBitSet columnMap = new FormatableBitSet(columnCount + 1);
/*
** Get a list of the indexes that need to be
** updated. ColumnMap contains all indexed
** columns where 1 or more columns in the index
** are going to be modified.
**
** Notice that we don't need to add constraint
** columns. This is because we add all key constraints
** (e.g. foreign keys) as a side effect of adding their
** indexes above. And we don't need to deal with
** check constraints on a delete.
**
** Adding indexes also takes care of the replication
** requirement of having the primary key.
*/
DMLModStatementNode.getXAffectedIndexes(baseTable, null, columnMap, conglomerates);
/*
** If we have any DELETE triggers, then do one of the following
** 1)If all of the triggers have MISSING referencing clause, then that
** means that the trigger actions do not have access to before and
** after values. In that case, there is no need to blanketly decide to
** include all the columns in the read map just because there are
** triggers defined on the table.
** 2)Since one/more triggers have REFERENCING clause on them, get all
** the columns because we don't know what the user will ultimately reference.
*/
baseTable.getAllRelevantTriggers(StatementType.DELETE, (int[]) null, relevantTriggers);
if (relevantTriggers.size() > 0) {
needsDeferredProcessing[0] = true;
boolean needToIncludeAllColumns = false;
for (TriggerDescriptor trd : relevantTriggers) {
// If yes, then read all the columns from the trigger table.
if (!trd.getReferencingNew() && !trd.getReferencingOld())
continue;
else {
needToIncludeAllColumns = true;
break;
}
}
if (needToIncludeAllColumns) {
for (int i = 1; i <= columnCount; i++) {
columnMap.set(i);
}
}
}
return columnMap;
}
use of org.apache.derby.iapi.sql.dictionary.TriggerDescriptor in project derby by apache.
the class DropTriggerConstantAction method executeConstantAction.
/**
* This is the guts of the Execution-time logic for DROP STATEMENT.
*
* @see ConstantAction#executeConstantAction
*
* @exception StandardException Thrown on failure
*/
public void executeConstantAction(Activation activation) throws StandardException {
TriggerDescriptor triggerd;
LanguageConnectionContext lcc = activation.getLanguageConnectionContext();
DataDictionary dd = lcc.getDataDictionary();
/*
** Inform the data dictionary that we are about to write to it.
** There are several calls to data dictionary "get" methods here
** that might be done in "read" mode in the data dictionary, but
** it seemed safer to do this whole operation in "write" mode.
**
** We tell the data dictionary we're done writing at the end of
** the transaction.
*/
dd.startWriting(lcc);
TableDescriptor td = dd.getTableDescriptor(tableId);
if (td == null) {
throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tableId.toString());
}
TransactionController tc = lcc.getTransactionExecute();
lockTableForDDL(tc, td.getHeapConglomerateId(), true);
// get td again in case table shape is changed before lock is acquired
td = dd.getTableDescriptor(tableId);
if (td == null) {
throw StandardException.newException(SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION, tableId.toString());
}
/*
** Get the trigger descriptor. We're responsible for raising
** the error if it isn't found
*/
triggerd = dd.getTriggerDescriptor(triggerName, sd);
if (triggerd == null) {
throw StandardException.newException(SQLState.LANG_OBJECT_NOT_FOUND_DURING_EXECUTION, "TRIGGER", (sd.getSchemaName() + "." + triggerName));
}
/*
** Prepare all dependents to invalidate. (This is there chance
** to say that they can't be invalidated. For example, an open
** cursor referencing a table/trigger that the user is attempting to
** drop.) If no one objects, then invalidate any dependent objects.
*/
triggerd.drop(lcc);
}
use of org.apache.derby.iapi.sql.dictionary.TriggerDescriptor in project derby by apache.
the class AlterTableConstantAction method dropColumnFromTable.
/**
* Workhorse for dropping a column from a table.
*
* This routine drops a column from a table, taking care
* to properly handle the various related schema objects.
*
* The syntax which gets you here is:
*
* ALTER TABLE tbl DROP [COLUMN] col [CASCADE|RESTRICT]
*
* The keyword COLUMN is optional, and if you don't
* specify CASCADE or RESTRICT, the default is CASCADE
* (the default is chosen in the parser, not here).
*
* If you specify RESTRICT, then the column drop should be
* rejected if it would cause a dependent schema object
* to become invalid.
*
* If you specify CASCADE, then the column drop should
* additionally drop other schema objects which have
* become invalid.
*
* You may not drop the last (only) column in a table.
*
* Schema objects of interest include:
* - views
* - triggers
* - constraints
* - check constraints
* - primary key constraints
* - foreign key constraints
* - unique key constraints
* - not null constraints
* - privileges
* - indexes
* - default values
*
* Dropping a column may also change the column position
* numbers of other columns in the table, which may require
* fixup of schema objects (such as triggers and column
* privileges) which refer to columns by column position number.
*
* Indexes are a bit interesting. The official SQL spec
* doesn't talk about indexes; they are considered to be
* an imlementation-specific performance optimization.
* The current Derby behavior is that:
* - CASCADE/RESTRICT doesn't matter for indexes
* - when a column is dropped, it is removed from any indexes
* which contain it.
* - if that column was the only column in the index, the
* entire index is dropped.
*
* @param columnName the name of the column specfication in the ALTER
* statement-- currently we allow only one.
* @exception StandardException thrown on failure.
*/
private void dropColumnFromTable(String columnName) throws StandardException {
boolean cascade = (behavior == StatementType.DROP_CASCADE);
// drop any generated columns which reference this column
ColumnDescriptorList generatedColumnList = td.getGeneratedColumns();
int generatedColumnCount = generatedColumnList.size();
ArrayList<String> cascadedDroppedColumns = new ArrayList<String>();
for (int i = 0; i < generatedColumnCount; i++) {
ColumnDescriptor generatedColumn = generatedColumnList.elementAt(i);
String[] referencedColumnNames = generatedColumn.getDefaultInfo().getReferencedColumnNames();
int referencedColumnCount = referencedColumnNames.length;
for (int j = 0; j < referencedColumnCount; j++) {
if (columnName.equals(referencedColumnNames[j])) {
String generatedColumnName = generatedColumn.getColumnName();
// we're trying to drop
if (!cascade) {
//
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm.getActionString(DependencyManager.DROP_COLUMN), columnName, "GENERATED COLUMN", generatedColumnName);
} else {
cascadedDroppedColumns.add(generatedColumnName);
}
}
}
}
DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
int cascadedDrops = cascadedDroppedColumns.size();
int sizeAfterCascadedDrops = td.getColumnDescriptorList().size() - cascadedDrops;
// can NOT drop a column if it is the only one in the table
if (sizeAfterCascadedDrops == 1) {
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm.getActionString(DependencyManager.DROP_COLUMN), "THE *LAST* COLUMN " + columnName, "TABLE", td.getQualifiedName());
}
// now drop dependent generated columns
for (int i = 0; i < cascadedDrops; i++) {
String generatedColumnName = cascadedDroppedColumns.get(i);
activation.addWarning(StandardException.newWarning(SQLState.LANG_GEN_COL_DROPPED, generatedColumnName, td.getName()));
//
// We can only recurse 2 levels since a generation clause cannot
// refer to other generated columns.
//
dropColumnFromTable(generatedColumnName);
}
/*
* Cascaded drops of dependent generated columns may require us to
* rebuild the table descriptor.
*/
td = dd.getTableDescriptor(tableId);
ColumnDescriptor columnDescriptor = td.getColumnDescriptor(columnName);
// We already verified this in bind, but do it again
if (columnDescriptor == null) {
throw StandardException.newException(SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, columnName, td.getQualifiedName());
}
int size = td.getColumnDescriptorList().size();
droppedColumnPosition = columnDescriptor.getPosition();
FormatableBitSet toDrop = new FormatableBitSet(size + 1);
toDrop.set(droppedColumnPosition);
td.setReferencedColumnMap(toDrop);
dm.invalidateFor(td, (cascade ? DependencyManager.DROP_COLUMN : DependencyManager.DROP_COLUMN_RESTRICT), lcc);
// If column has a default we drop the default and any dependencies
if (columnDescriptor.getDefaultInfo() != null) {
dm.clearDependencies(lcc, columnDescriptor.getDefaultDescriptor(dd));
}
// then we need to drop the system-generated sequence backing it.
if (columnDescriptor.isAutoincrement() && dd.checkVersion(DataDictionary.DD_VERSION_DERBY_10_11, null)) {
DropTableConstantAction.dropIdentitySequence(dd, td, activation);
}
// columns which are used through REFERENCING clause
for (TriggerDescriptor trd : dd.getTriggerDescriptors(td)) {
// If we find that the trigger is dependent on the column being
// dropped because column is part of trigger columns list, then
// we will give a warning or drop the trigger based on whether
// ALTER TABLE DROP COLUMN is RESTRICT or CASCADE. In such a
// case, no need to check if the trigger action columns referenced
// through REFERENCING clause also used the column being dropped.
boolean triggerDroppedAlready = false;
int[] referencedCols = trd.getReferencedCols();
if (referencedCols != null) {
int refColLen = referencedCols.length, j;
boolean changed = false;
for (j = 0; j < refColLen; j++) {
if (referencedCols[j] > droppedColumnPosition) {
// Trigger is not defined on the column being dropped
// but the column position of trigger column is changing
// because the position of the column being dropped is
// before the the trigger column
changed = true;
} else if (referencedCols[j] == droppedColumnPosition) {
// the trigger is defined on the column being dropped
if (cascade) {
trd.drop(lcc);
triggerDroppedAlready = true;
activation.addWarning(StandardException.newWarning(SQLState.LANG_TRIGGER_DROPPED, trd.getName(), td.getName()));
} else {
// otherwsie there would be unexpected behaviors
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm.getActionString(DependencyManager.DROP_COLUMN), columnName, "TRIGGER", trd.getName());
}
break;
}
}
// drop column.
if (j == refColLen && changed) {
dd.dropTriggerDescriptor(trd, tc);
for (j = 0; j < refColLen; j++) {
if (referencedCols[j] > droppedColumnPosition)
referencedCols[j]--;
}
trd.setReferencedCols(referencedCols);
dd.addDescriptor(trd, sd, DataDictionary.SYSTRIGGERS_CATALOG_NUM, false, tc);
}
}
// loop above, then move to next trigger
if (triggerDroppedAlready)
continue;
// Column being dropped is not one of trigger columns. Check if
// that column is getting used inside the trigger action through
// REFERENCING clause. This can be tracked only for triggers
// created in 10.7 and higher releases. Derby releases prior to
// that did not keep track of trigger action columns used
// through the REFERENCING clause.
int[] referencedColsInTriggerAction = trd.getReferencedColsInTriggerAction();
if (referencedColsInTriggerAction != null) {
int refColInTriggerActionLen = referencedColsInTriggerAction.length, j;
boolean changedColPositionInTriggerAction = false;
for (j = 0; j < refColInTriggerActionLen; j++) {
if (referencedColsInTriggerAction[j] > droppedColumnPosition) {
changedColPositionInTriggerAction = true;
} else if (referencedColsInTriggerAction[j] == droppedColumnPosition) {
if (cascade) {
trd.drop(lcc);
triggerDroppedAlready = true;
activation.addWarning(StandardException.newWarning(SQLState.LANG_TRIGGER_DROPPED, trd.getName(), td.getName()));
} else {
// we'd better give an error if don't drop it,
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm.getActionString(DependencyManager.DROP_COLUMN), columnName, "TRIGGER", trd.getName());
}
break;
}
}
// column has been actually dropped from the table descriptor.
if (j == refColInTriggerActionLen && changedColPositionInTriggerAction) {
dd.dropTriggerDescriptor(trd, tc);
for (j = 0; j < refColInTriggerActionLen; j++) {
if (referencedColsInTriggerAction[j] > droppedColumnPosition)
referencedColsInTriggerAction[j]--;
}
trd.setReferencedColsInTriggerAction(referencedColsInTriggerAction);
dd.addDescriptor(trd, sd, DataDictionary.SYSTRIGGERS_CATALOG_NUM, false, tc);
}
}
}
ConstraintDescriptorList csdl = dd.getConstraintDescriptors(td);
int csdl_size = csdl.size();
ArrayList<ConstantAction> newCongloms = new ArrayList<ConstantAction>();
// we want to remove referenced primary/unique keys in the second
// round. This will ensure that self-referential constraints will
// work OK.
int tbr_size = 0;
ConstraintDescriptor[] toBeRemoved = new ConstraintDescriptor[csdl_size];
// let's go downwards, don't want to get messed up while removing
for (int i = csdl_size - 1; i >= 0; i--) {
ConstraintDescriptor cd = csdl.elementAt(i);
int[] referencedColumns = cd.getReferencedColumns();
int numRefCols = referencedColumns.length, j;
boolean changed = false;
for (j = 0; j < numRefCols; j++) {
if (referencedColumns[j] > droppedColumnPosition)
changed = true;
if (referencedColumns[j] == droppedColumnPosition)
break;
}
if (// column not referenced
j == numRefCols) {
if ((cd instanceof CheckConstraintDescriptor) && changed) {
dd.dropConstraintDescriptor(cd, tc);
for (j = 0; j < numRefCols; j++) {
if (referencedColumns[j] > droppedColumnPosition)
referencedColumns[j]--;
}
((CheckConstraintDescriptor) cd).setReferencedColumnsDescriptor(new ReferencedColumnsDescriptorImpl(referencedColumns));
dd.addConstraintDescriptor(cd, tc);
}
continue;
}
if (!cascade) {
//
throw StandardException.newException(SQLState.LANG_PROVIDER_HAS_DEPENDENT_OBJECT, dm.getActionString(DependencyManager.DROP_COLUMN), columnName, "CONSTRAINT", cd.getConstraintName());
}
if (cd instanceof ReferencedKeyConstraintDescriptor) {
// restrict will raise an error in invalidate if referenced
toBeRemoved[tbr_size++] = cd;
continue;
}
// drop now in all other cases
dm.invalidateFor(cd, DependencyManager.DROP_CONSTRAINT, lcc);
dropConstraint(cd, td, newCongloms, activation, lcc, true);
activation.addWarning(StandardException.newWarning(SQLState.LANG_CONSTRAINT_DROPPED, cd.getConstraintName(), td.getName()));
}
for (int i = tbr_size - 1; i >= 0; i--) {
ConstraintDescriptor cd = toBeRemoved[i];
dropConstraint(cd, td, newCongloms, activation, lcc, false);
activation.addWarning(StandardException.newWarning(SQLState.LANG_CONSTRAINT_DROPPED, cd.getConstraintName(), td.getName()));
if (cascade) {
ConstraintDescriptorList fkcdl = dd.getForeignKeys(cd.getUUID());
for (ConstraintDescriptor fkcd : fkcdl) {
dm.invalidateFor(fkcd, DependencyManager.DROP_CONSTRAINT, lcc);
dropConstraint(fkcd, td, newCongloms, activation, lcc, true);
activation.addWarning(StandardException.newWarning(SQLState.LANG_CONSTRAINT_DROPPED, fkcd.getConstraintName(), fkcd.getTableDescriptor().getName()));
}
}
dm.invalidateFor(cd, DependencyManager.DROP_CONSTRAINT, lcc);
dm.clearDependencies(lcc, cd);
}
/* If there are new backing conglomerates which must be
* created to replace a dropped shared conglomerate
* (where the shared conglomerate was dropped as part
* of a "drop constraint" call above), then create them
* now. We do this *after* dropping all dependent
* constraints because we don't want to waste time
* creating a new conglomerate if it's just going to be
* dropped again as part of another "drop constraint".
*/
createNewBackingCongloms(newCongloms, (long[]) null);
/*
* The work we've done above, specifically the possible
* dropping of primary key, foreign key, and unique constraints
* and their underlying indexes, may have affected the table
* descriptor. By re-reading the table descriptor here, we
* ensure that the compressTable code is working with an
* accurate table descriptor. Without this line, we may get
* conglomerate-not-found errors and the like due to our
* stale table descriptor.
*/
td = dd.getTableDescriptor(tableId);
compressTable();
ColumnDescriptorList tab_cdl = td.getColumnDescriptorList();
// drop the column from syscolumns
dd.dropColumnDescriptor(td.getUUID(), columnName, tc);
ColumnDescriptor[] cdlArray = new ColumnDescriptor[size - columnDescriptor.getPosition()];
//
for (int i = columnDescriptor.getPosition(), j = 0; i < size; i++, j++) {
ColumnDescriptor cd = tab_cdl.elementAt(i);
dd.dropColumnDescriptor(td.getUUID(), cd.getColumnName(), tc);
cd.setPosition(i);
if (cd.isAutoincrement()) {
cd.setAutoinc_create_or_modify_Start_Increment(ColumnDefinitionNode.CREATE_AUTOINCREMENT);
}
cdlArray[j] = cd;
}
dd.addDescriptorArray(cdlArray, td, DataDictionary.SYSCOLUMNS_CATALOG_NUM, false, tc);
// By this time, the column has been removed from the table descriptor.
// Now, go through all the triggers and regenerate their trigger action
// SPS and rebind the generated trigger action sql. If the trigger
// action is using the dropped column, it will get detected here. If
// not, then we will have generated the internal trigger action sql
// which matches the trigger action sql provided by the user.
//
// eg of positive test case
// create table atdc_16_tab1 (a1 integer, b1 integer, c1 integer);
// create table atdc_16_tab2 (a2 integer, b2 integer, c2 integer);
// create trigger atdc_16_trigger_1
// after update of b1 on atdc_16_tab1
// REFERENCING NEW AS newt
// for each row
// update atdc_16_tab2 set c2 = newt.c1
// The internal representation for the trigger action before the column
// is dropped is as follows
// update atdc_16_tab2 set c2 =
// org.apache.derby.iapi.db.Factory::getTriggerExecutionContext().
// getONewRow().getInt(3)
// After the drop column shown as below
// alter table DERBY4998_SOFT_UPGRADE_RESTRICT drop column c11
// The above internal representation of tigger action sql is not
// correct anymore because column position of c1 in atdc_16_tab1 has
// now changed from 3 to 2. Following while loop will regenerate it and
// change it to as follows
// update atdc_16_tab2 set c2 =
// org.apache.derby.iapi.db.Factory::getTriggerExecutionContext().
// getONewRow().getInt(2)
//
// We could not do this before the actual column drop, because the
// rebind would have still found the column being dropped in the
// table descriptor and hence use of such a column in the trigger
// action rebind would not have been caught.
// For the table on which ALTER TABLE is getting performed, find out
// all the SPSDescriptors that use that table as a provider. We are
// looking for SPSDescriptors that have been created internally for
// trigger action SPSes. Through those SPSDescriptors, we will be
// able to get to the triggers dependent on the table being altered
// Following will get all the dependent objects that are using
// ALTER TABLE table as provider
List<DependencyDescriptor> depsOnAlterTableList = dd.getProvidersDescriptorList(td.getObjectID().toString());
for (DependencyDescriptor depOnAT : depsOnAlterTableList) {
// Go through all the dependent objects on the table being altered
DependableFinder dependent = depOnAT.getDependentFinder();
// stored prepared statement.
if (dependent.getSQLObjectType().equals(Dependable.STORED_PREPARED_STATEMENT)) {
// Look for all the dependent objects that are using this
// stored prepared statement as provider. We are only
// interested in dependents that are triggers.
List<DependencyDescriptor> depsTrigger = dd.getProvidersDescriptorList(depOnAT.getUUID().toString());
for (DependencyDescriptor depsTriggerDesc : depsTrigger) {
DependableFinder providerIsTrigger = depsTriggerDesc.getDependentFinder();
// it is a trigger
if (providerIsTrigger.getSQLObjectType().equals(Dependable.TRIGGER)) {
// Drop and recreate the trigger after regenerating
// it's trigger action plan. If the trigger action
// depends on the column being dropped, it will be
// caught here.
TriggerDescriptor trdToBeDropped = dd.getTriggerDescriptor(depsTriggerDesc.getUUID());
// First check for dependencies in the trigger's WHEN
// clause, if there is one.
UUID whenClauseId = trdToBeDropped.getWhenClauseId();
boolean gotDropped = false;
if (whenClauseId != null) {
gotDropped = columnDroppedAndTriggerDependencies(trdToBeDropped, whenClauseId, true, cascade, columnName);
}
// dependencies.
if (!gotDropped) {
columnDroppedAndTriggerDependencies(trdToBeDropped, trdToBeDropped.getActionId(), false, cascade, columnName);
}
}
}
}
}
// Adjust the column permissions rows in SYSCOLPERMS to reflect the
// changed column positions due to the dropped column:
dd.updateSYSCOLPERMSforDropColumn(td.getUUID(), tc, columnDescriptor);
// remove column descriptor from table descriptor. this fixes up the
// list in case we were called recursively in order to cascade-drop a
// dependent generated column.
tab_cdl.remove(td.getColumnDescriptor(columnName));
}
use of org.apache.derby.iapi.sql.dictionary.TriggerDescriptor in project derby by apache.
the class AlterTableConstantAction method truncateTable.
/*
* TRUNCATE TABLE TABLENAME; (quickly removes all the rows from table and
* it's correctponding indexes).
* Truncate is implemented by dropping the existing conglomerates(heap,indexes) and recreating a
* new ones with the properties of dropped conglomerates. Currently Store
* does not have support to truncate existing conglomerated until store
* supports it , this is the only way to do it.
* Error Cases: Truncate error cases same as other DDL's statements except
* 1)Truncate is not allowed when the table is references by another table.
* 2)Truncate is not allowed when there are enabled delete triggers on the table.
* Note: Because conglomerate number is changed during recreate process all the statements will be
* marked as invalide and they will get recompiled internally on their next
* execution. This is okay because truncate makes the number of rows to zero
* it may be good idea to recompile them becuase plans are likely to be
* incorrect. Recompile is done internally by Derby, user does not have
* any effect.
*/
private void truncateTable() throws StandardException {
ExecRow emptyHeapRow;
long newHeapConglom;
Properties properties = new Properties();
RowLocation rl;
if (SanityManager.DEBUG) {
if (lockGranularity != '\0') {
SanityManager.THROWASSERT("lockGranularity expected to be '\0', not " + lockGranularity);
}
SanityManager.ASSERT(columnInfo == null, "columnInfo expected to be null");
SanityManager.ASSERT(constraintActions == null, "constraintActions expected to be null");
}
// and the ON DELETE action is NO ACTION.
for (ConstraintDescriptor cd : dd.getConstraintDescriptors(td)) {
if (cd instanceof ReferencedKeyConstraintDescriptor) {
final ReferencedKeyConstraintDescriptor rfcd = (ReferencedKeyConstraintDescriptor) cd;
for (ConstraintDescriptor fkcd : rfcd.getNonSelfReferencingFK(ConstraintDescriptor.ENABLED)) {
final ForeignKeyConstraintDescriptor fk = (ForeignKeyConstraintDescriptor) fkcd;
throw StandardException.newException(SQLState.LANG_NO_TRUNCATE_ON_FK_REFERENCE_TABLE, td.getName());
}
}
}
// truncate is not allowed when there are enabled DELETE triggers
for (TriggerDescriptor trd : dd.getTriggerDescriptors(td)) {
if (trd.listensForEvent(TriggerDescriptor.TRIGGER_EVENT_DELETE) && trd.isEnabled()) {
throw StandardException.newException(SQLState.LANG_NO_TRUNCATE_ON_ENABLED_DELETE_TRIGGERS, td.getName(), trd.getName());
}
}
// gather information from the existing conglomerate to create new one.
emptyHeapRow = td.getEmptyExecRow();
compressHeapCC = tc.openConglomerate(td.getHeapConglomerateId(), false, TransactionController.OPENMODE_FORUPDATE, TransactionController.MODE_TABLE, TransactionController.ISOLATION_SERIALIZABLE);
rl = compressHeapCC.newRowLocationTemplate();
// Get the properties on the old heap
compressHeapCC.getInternalTablePropertySet(properties);
compressHeapCC.close();
compressHeapCC = null;
// create new conglomerate
newHeapConglom = tc.createConglomerate("heap", emptyHeapRow.getRowArray(), // column sort order - not required for heap
null, td.getColumnCollationIds(), properties, TransactionController.IS_DEFAULT);
/* Set up index info to perform truncate on them*/
getAffectedIndexes();
if (numIndexes > 0) {
indexRows = new ExecIndexRow[numIndexes];
ordering = new ColumnOrdering[numIndexes][];
collation = new int[numIndexes][];
for (int index = 0; index < numIndexes; index++) {
IndexRowGenerator curIndex = compressIRGs[index];
// create a single index row template for each index
indexRows[index] = curIndex.getIndexRowTemplate();
curIndex.getIndexRow(emptyHeapRow, rl, indexRows[index], (FormatableBitSet) null);
/* For non-unique indexes, we order by all columns + the RID.
* For unique indexes, we just order by the columns.
* No need to try to enforce uniqueness here as
* index should be valid.
*/
int[] baseColumnPositions = curIndex.baseColumnPositions();
boolean[] isAscending = curIndex.isAscending();
int numColumnOrderings;
numColumnOrderings = baseColumnPositions.length + 1;
ordering[index] = new ColumnOrdering[numColumnOrderings];
collation[index] = curIndex.getColumnCollationIds(td.getColumnDescriptorList());
for (int ii = 0; ii < numColumnOrderings - 1; ii++) {
ordering[index][ii] = new IndexColumnOrder(ii, isAscending[ii]);
}
ordering[index][numColumnOrderings - 1] = new IndexColumnOrder(numColumnOrderings - 1);
}
}
/*
** Inform the data dictionary that we are about to write to it.
** There are several calls to data dictionary "get" methods here
** that might be done in "read" mode in the data dictionary, but
** it seemed safer to do this whole operation in "write" mode.
**
** We tell the data dictionary we're done writing at the end of
** the transaction.
*/
dd.startWriting(lcc);
// truncate all indexes
if (numIndexes > 0) {
long[] newIndexCongloms = new long[numIndexes];
for (int index = 0; index < numIndexes; index++) {
updateIndex(newHeapConglom, dd, index, newIndexCongloms);
}
}
// Update the DataDictionary
// Get the ConglomerateDescriptor for the heap
long oldHeapConglom = td.getHeapConglomerateId();
ConglomerateDescriptor cd = td.getConglomerateDescriptor(oldHeapConglom);
// Update sys.sysconglomerates with new conglomerate #
dd.updateConglomerateDescriptor(cd, newHeapConglom, tc);
// Now that the updated information is available in the system tables,
// we should invalidate all statements that use the old conglomerates
dm.invalidateFor(td, DependencyManager.TRUNCATE_TABLE, lcc);
// Drop the old conglomerate
tc.dropConglomerate(oldHeapConglom);
cleanUp();
}
Aggregations