blob: 12e8eef1d41f322eb6f7d2b247e68b61ec99ac42 [file] [log] [blame]
/*
Derby - Class org.apache.derby.impl.sql.catalog.SYSTRIGGERSRowFactory
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 org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.SQLBoolean;
import org.apache.derby.iapi.types.SQLChar;
import org.apache.derby.iapi.types.SQLVarchar;
import org.apache.derby.iapi.types.UserType;
import org.apache.derby.iapi.types.DataValueFactory;
import org.apache.derby.iapi.sql.dictionary.CatalogRowFactory;
import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.SystemColumn;
import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
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.error.StandardException;
import org.apache.derby.catalog.ReferencedColumns;
import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
import org.apache.derby.catalog.UUID;
import org.apache.derby.iapi.services.uuid.UUIDFactory;
import org.apache.derby.shared.common.sanity.SanityManager;
import org.apache.derby.iapi.types.SQLTimestamp;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.Calendar;
import java.util.TimeZone;
/**
* Factory for creating a SYSTRIGGERS row.
*
*
* @version 0.1
*/
public class SYSTRIGGERSRowFactory extends CatalogRowFactory
{
static final String TABLENAME_STRING = "SYSTRIGGERS";
/* Column #s for sysinfo (1 based) */
public static final int SYSTRIGGERS_TRIGGERID = 1;
public static final int SYSTRIGGERS_TRIGGERNAME = 2;
public static final int SYSTRIGGERS_SCHEMAID = 3;
public static final int SYSTRIGGERS_CREATIONTIMESTAMP = 4;
public static final int SYSTRIGGERS_EVENT = 5;
public static final int SYSTRIGGERS_FIRINGTIME = 6;
public static final int SYSTRIGGERS_TYPE = 7;
public static final int SYSTRIGGERS_STATE = TriggerDescriptor.SYSTRIGGERS_STATE_FIELD;
public static final int SYSTRIGGERS_TABLEID = 9;
public static final int SYSTRIGGERS_WHENSTMTID = 10;
public static final int SYSTRIGGERS_ACTIONSTMTID = 11;
public static final int SYSTRIGGERS_REFERENCEDCOLUMNS = 12;
public static final int SYSTRIGGERS_TRIGGERDEFINITION = 13;
public static final int SYSTRIGGERS_REFERENCINGOLD = 14;
public static final int SYSTRIGGERS_REFERENCINGNEW = 15;
public static final int SYSTRIGGERS_OLDREFERENCINGNAME = 16;
public static final int SYSTRIGGERS_NEWREFERENCINGNAME = 17;
public static final int SYSTRIGGERS_WHENCLAUSETEXT = 18;
public static final int SYSTRIGGERS_COLUMN_COUNT = SYSTRIGGERS_WHENCLAUSETEXT;
public static final int SYSTRIGGERS_INDEX1_ID = 0;
public static final int SYSTRIGGERS_INDEX2_ID = 1;
public static final int SYSTRIGGERS_INDEX3_ID = 2;
private static final int[][] indexColumnPositions =
{
{SYSTRIGGERS_TRIGGERID},
{SYSTRIGGERS_TRIGGERNAME, SYSTRIGGERS_SCHEMAID},
{SYSTRIGGERS_TABLEID, SYSTRIGGERS_CREATIONTIMESTAMP}
};
private static final boolean[] uniqueness = {
true,
true,
false,
};
private static final String[] uuids =
{
"c013800d-00d7-c025-4809-000a0a411200" // catalog UUID
,"c013800d-00d7-c025-480a-000a0a411200" // heap UUID
,"c013800d-00d7-c025-480b-000a0a411200" // SYSTRIGGERS_INDEX1
,"c013800d-00d7-c025-480c-000a0a411200" // SYSTRIGGERS_INDEX2
,"c013800d-00d7-c025-480d-000a0a411200" // SYSTRIGGERS_INDEX3
};
/** TimeZone object representing Coordinated Universal Time. */
private final static TimeZone UTC = TimeZone.getTimeZone("UTC");
private final DataDictionary dataDictionary;
/////////////////////////////////////////////////////////////////////////////
//
// CONSTRUCTORS
//
/////////////////////////////////////////////////////////////////////////////
SYSTRIGGERSRowFactory(
DataDictionary dd,
UUIDFactory uuidf,
ExecutionFactory ef,
DataValueFactory dvf)
throws StandardException
{
super(uuidf,ef,dvf);
this.dataDictionary = dd;
initInfo(SYSTRIGGERS_COLUMN_COUNT, TABLENAME_STRING,
indexColumnPositions, uniqueness, uuids);
}
/////////////////////////////////////////////////////////////////////////////
//
// METHODS
//
/////////////////////////////////////////////////////////////////////////////
/**
* Make a SYSTRIGGERS row.
*
* @return Row suitable for inserting into SYSTRIGGERS.
*
* @exception StandardException thrown on failure
*/
@Override
public ExecRow makeRow(TupleDescriptor td, TupleDescriptor parent)
throws StandardException
{
return makeRow(td, getHeapColumnCount());
}
@Override
public ExecRow makeEmptyRowForCurrentVersion() throws StandardException {
return makeRow(null, SYSTRIGGERS_COLUMN_COUNT);
}
/**
* Helper method that contains common logic for {@code makeRow()} and
* {@code makeEmptyRowForCurrentVersion()}. Creates a row for the
* SYSTRIGGERS conglomerate.
*
* @param td the {@code TriggerDescriptor} to create a row from (can be
* {@code null} if the returned row should be empty)
* @param columnCount the number of columns in the returned row (used for
* trimming off columns in soft upgrade mode to match the format in
* the old dictionary version)
* @return a row for the SYSTRIGGERS conglomerate
* @throws StandardException if an error happens when creating the row
*/
private ExecRow makeRow(TupleDescriptor td, int columnCount)
throws StandardException {
String name = null;
UUID uuid = null;
UUID suuid = null; // schema
UUID tuuid = null; // referenced table
UUID actionSPSID = null; // action sps uuid string
UUID whenSPSID = null; // when clause sps uuid string
Timestamp createTime = null;
String event = null;
String time = null;
String type = null;
String enabled = null;
String triggerDefinition = null;
String oldReferencingName = null;
String newReferencingName = null;
ReferencedColumns rcd = null;
boolean referencingOld = false;
boolean referencingNew = false;
String whenClauseText = null;
if (td != null)
{
TriggerDescriptor triggerDescriptor = (TriggerDescriptor)td;
name = triggerDescriptor.getName();
uuid = triggerDescriptor.getUUID();
suuid = triggerDescriptor.getSchemaDescriptor().getUUID();
createTime = triggerDescriptor.getCreationTimestamp();
// for now we are assuming that a trigger can only listen to a single event
event = triggerDescriptor.listensForEvent(TriggerDescriptor.TRIGGER_EVENT_UPDATE) ? "U" :
triggerDescriptor.listensForEvent(TriggerDescriptor.TRIGGER_EVENT_DELETE) ? "D" : "I";
time = triggerDescriptor.isBeforeTrigger() ? "B" : "A";
type = triggerDescriptor.isRowTrigger() ? "R" : "S";
enabled = triggerDescriptor.isEnabled() ? "E" : "D";
tuuid = triggerDescriptor.getTableDescriptor().getUUID();
int[] refCols = triggerDescriptor.getReferencedCols();
int[] refColsInTriggerAction = triggerDescriptor.getReferencedColsInTriggerAction();
rcd = (refCols != null || refColsInTriggerAction != null) ? new
ReferencedColumnsDescriptorImpl(refCols, refColsInTriggerAction) : null;
actionSPSID = triggerDescriptor.getActionId();
whenSPSID = triggerDescriptor.getWhenClauseId();
triggerDefinition = triggerDescriptor.getTriggerDefinition();
referencingOld = triggerDescriptor.getReferencingOld();
referencingNew = triggerDescriptor.getReferencingNew();
oldReferencingName = triggerDescriptor.getOldReferencingName();
newReferencingName = triggerDescriptor.getNewReferencingName();
whenClauseText = triggerDescriptor.getWhenClauseText();
}
/* Build the row to insert */
ExecRow row = getExecutionFactory().getValueRow(columnCount);
/* 1st column is TRIGGERID */
row.setColumn(1, new SQLChar((uuid == null) ? null : uuid.toString()));
/* 2nd column is TRIGGERNAME */
row.setColumn(2, new SQLVarchar(name));
/* 3rd column is SCHEMAID */
row.setColumn(3, new SQLChar((suuid == null) ? null : suuid.toString()));
/* 4th column is CREATIONTIMESTAMP */
SQLTimestamp creationTimestamp = (createTime == null)
? new SQLTimestamp(null)
: new SQLTimestamp(createTime, getCalendarForCreationTimestamp());
row.setColumn(4, creationTimestamp);
/* 5th column is EVENT */
row.setColumn(5, new SQLChar(event));
/* 6th column is FIRINGTIME */
row.setColumn(6, new SQLChar(time));
/* 7th column is TYPE */
row.setColumn(7, new SQLChar(type));
/* 8th column is STATE */
row.setColumn(8, new SQLChar(enabled));
/* 9th column is TABLEID */
row.setColumn(9, new SQLChar((tuuid == null) ? null : tuuid.toString()));
/* 10th column is WHENSTMTID */
row.setColumn(10, new SQLChar((whenSPSID == null) ? null : whenSPSID.toString()));
/* 11th column is ACTIONSTMTID */
row.setColumn(11, new SQLChar((actionSPSID == null) ? null : actionSPSID.toString()));
/* 12th column is REFERENCEDCOLUMNS
* (user type org.apache.derby.catalog.ReferencedColumns)
*/
row.setColumn(12, new UserType(rcd));
/* 13th column is TRIGGERDEFINITION */
row.setColumn(13, dvf.getLongvarcharDataValue(triggerDefinition));
/* 14th column is REFERENCINGOLD */
row.setColumn(14, new SQLBoolean(referencingOld));
/* 15th column is REFERENCINGNEW */
row.setColumn(15, new SQLBoolean(referencingNew));
/* 16th column is OLDREFERENCINGNAME */
row.setColumn(16, new SQLVarchar(oldReferencingName));
/* 17th column is NEWREFERENCINGNAME */
row.setColumn(17, new SQLVarchar(newReferencingName));
/* 18th column is WHENCLAUSETEXT */
if (row.nColumns() >= 18) {
// This column is present only if the data dictionary version is
// 10.11 or higher.
row.setColumn(18, dvf.getLongvarcharDataValue(whenClauseText));
}
return row;
}
/**
* Get a calendar instance with the correct time zone for storing and
* retrieving creation timestamps. Creation timestamps are stored in UTC
* to avoid ambiguities around the change from daylight saving time to
* standard time, or other time zone changes. If the data dictionary
* version is less than 10.11, however, the timestamps are stored in the
* local time zone.
*
* @return a calendar instance that can be used for storing and retrieving
* trigger creation timestamps
* @throws StandardException if an error occurs
*/
private Calendar getCalendarForCreationTimestamp()
throws StandardException {
if (dataDictionary.checkVersion(
DataDictionary.DD_VERSION_DERBY_10_11, null)) {
return Calendar.getInstance(UTC);
} else {
return Calendar.getInstance();
}
}
///////////////////////////////////////////////////////////////////////////
//
// 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;
UUID suuid; // schema
UUID tuuid; // referenced table
UUID actionSPSID = null; // action sps uuid string
UUID whenSPSID = null; // when clause sps uuid string
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;
}
/**
* Builds a list of columns suitable for creating this Catalog.
* The last column, the serialized statement, is not added
* to the column list. This is done deliberately to make it
* a 'hidden' column -- one that is not visible to customers,
* but is visible to the system.
*
*
* @return array of SystemColumn suitable for making this catalog.
*/
public SystemColumn[] buildColumnList()
throws StandardException
{
return new SystemColumn[] {
SystemColumnImpl.getUUIDColumn("TRIGGERID", false),
SystemColumnImpl.getIdentifierColumn("TRIGGERNAME", false),
SystemColumnImpl.getUUIDColumn("SCHEMAID", false),
SystemColumnImpl.getColumn("CREATIONTIMESTAMP", Types.TIMESTAMP, false),
SystemColumnImpl.getIndicatorColumn("EVENT"),
SystemColumnImpl.getIndicatorColumn("FIRINGTIME"),
SystemColumnImpl.getIndicatorColumn("TYPE"),
SystemColumnImpl.getIndicatorColumn("STATE"),
SystemColumnImpl.getUUIDColumn("TABLEID", false),
SystemColumnImpl.getUUIDColumn("WHENSTMTID", true),
SystemColumnImpl.getUUIDColumn("ACTIONSTMTID", true),
SystemColumnImpl.getJavaColumn("REFERENCEDCOLUMNS",
"org.apache.derby.catalog.ReferencedColumns", true),
SystemColumnImpl.getColumn("TRIGGERDEFINITION",
Types.LONGVARCHAR, true, Integer.MAX_VALUE),
SystemColumnImpl.getColumn("REFERENCINGOLD", Types.BOOLEAN, true),
SystemColumnImpl.getColumn("REFERENCINGNEW", Types.BOOLEAN, true),
SystemColumnImpl.getIdentifierColumn("OLDREFERENCINGNAME", true),
SystemColumnImpl.getIdentifierColumn("NEWREFERENCINGNAME", true),
SystemColumnImpl.getColumn("WHENCLAUSETEXT",
Types.LONGVARCHAR, true, Integer.MAX_VALUE),
};
}
// a little helper
private boolean getCharBoolean(DataValueDescriptor col, char trueValue, char falseValue) throws StandardException
{
char theChar = col.getString().charAt(0);
if (theChar == trueValue)
{
return true;
}
else if (theChar == falseValue)
{
return false;
}
else
{
if (SanityManager.DEBUG)
SanityManager.THROWASSERT("bad char value "+theChar);
return true;
}
}
@Override
public int getHeapColumnCount() throws StandardException {
// The WHEN clause (DERBY-534) is only supported if the dictionary
// version is 10.11 or higher. Older versions of the dictionary don't
// include the WHENCLAUSETEXT column, so adjust the column count
// accordingly.
boolean supportsWhenClause = dataDictionary
.checkVersion(DataDictionary.DD_VERSION_DERBY_10_11, null);
int heapCols = super.getHeapColumnCount();
return supportsWhenClause ? heapCols : (heapCols - 1);
}
}