001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.CreateTriggerConstantAction
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.execute;
023:
024: import org.apache.derby.iapi.store.access.TransactionController;
025:
026: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
027:
028: import org.apache.derby.iapi.sql.execute.ConstantAction;
029:
030: import org.apache.derby.iapi.sql.dictionary.DataDescriptorGenerator;
031: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
032: import org.apache.derby.iapi.sql.dictionary.SchemaDescriptor;
033: import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
034: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
035: import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
036:
037: import org.apache.derby.iapi.types.DataValueFactory;
038:
039: import org.apache.derby.iapi.sql.depend.DependencyManager;
040:
041: import org.apache.derby.iapi.sql.execute.ExecutionFactory;
042:
043: import org.apache.derby.iapi.sql.Activation;
044:
045: import org.apache.derby.iapi.error.StandardException;
046: import org.apache.derby.iapi.reference.SQLState;
047:
048: import org.apache.derby.iapi.services.context.ContextService;
049:
050: import org.apache.derby.iapi.services.sanity.SanityManager;
051:
052: import org.apache.derby.catalog.UUID;
053:
054: import java.sql.Timestamp;
055:
056: /**
057: * This class describes actions that are ALWAYS performed for a
058: * CREATE TRIGGER Statement at Execution time.
059: *
060: * @author Jamie
061: */
062: class CreateTriggerConstantAction extends DDLSingleTableConstantAction {
063:
064: private String triggerName;
065: private String triggerSchemaName;
066: private TableDescriptor triggerTable; // null after readExternal
067: private UUID triggerTableId; // set in readExternal
068: private int eventMask;
069: private boolean isBefore;
070: private boolean isRow;
071: private boolean isEnabled;
072: private boolean referencingOld;
073: private boolean referencingNew;
074: private UUID whenSPSId;
075: private String whenText;
076: private UUID actionSPSId;
077: private String actionText;
078: private String originalActionText;
079: private String oldReferencingName;
080: private String newReferencingName;
081: private UUID spsCompSchemaId;
082: private Timestamp creationTimestamp;
083: private int[] referencedCols;
084:
085: // CONSTRUCTORS
086:
087: /**
088: * Make the ConstantAction for a CREATE TRIGGER statement.
089: *
090: * @param triggerSchemaName name for the schema that trigger lives in.
091: * @param triggerName Name of trigger
092: * @param eventMask TriggerDescriptor.TRIGGER_EVENT_XXXX
093: * @param isBefore is this a before (as opposed to after) trigger
094: * @param isRow is this a row trigger or statement trigger
095: * @param isEnabled is this trigger enabled or disabled
096: * @param triggerTable the table upon which this trigger is defined
097: * @param whenSPSId the sps id for the when clause (may be null)
098: * @param whenText the text of the when clause (may be null)
099: * @param actionSPSId the spsid for the trigger action (may be null)
100: * @param actionText the text of the trigger action
101: * @param spsCompSchemaId the compilation schema for the action and when
102: * spses. If null, will be set to the current default
103: * schema
104: * @param creationTimestamp when was this trigger created? if null, will be
105: * set to the time that executeConstantAction() is invoked
106: * @param referencedCols what columns does this trigger reference (may be null)
107: * @param originalActionText The original user text of the trigger action
108: * @param referencingOld whether or not OLD appears in REFERENCING clause
109: * @param referencingNew whether or not NEW appears in REFERENCING clause
110: * @param oldReferencingName old referencing table name, if any, that appears in REFERENCING clause
111: * @param newReferencingName new referencing table name, if any, that appears in REFERENCING clause
112: */
113: CreateTriggerConstantAction(String triggerSchemaName,
114: String triggerName, int eventMask, boolean isBefore,
115: boolean isRow, boolean isEnabled,
116: TableDescriptor triggerTable, UUID whenSPSId,
117: String whenText, UUID actionSPSId, String actionText,
118: UUID spsCompSchemaId, Timestamp creationTimestamp,
119: int[] referencedCols, String originalActionText,
120: boolean referencingOld, boolean referencingNew,
121: String oldReferencingName, String newReferencingName) {
122: super (triggerTable.getUUID());
123: this .triggerName = triggerName;
124: this .triggerSchemaName = triggerSchemaName;
125: this .triggerTable = triggerTable;
126: this .eventMask = eventMask;
127: this .isBefore = isBefore;
128: this .isRow = isRow;
129: this .isEnabled = isEnabled;
130: this .whenSPSId = whenSPSId;
131: this .whenText = whenText;
132: this .actionSPSId = actionSPSId;
133: this .actionText = actionText;
134: this .spsCompSchemaId = spsCompSchemaId;
135: this .creationTimestamp = creationTimestamp;
136: this .referencedCols = referencedCols;
137: this .originalActionText = originalActionText;
138: this .referencingOld = referencingOld;
139: this .referencingNew = referencingNew;
140: this .oldReferencingName = oldReferencingName;
141: this .newReferencingName = newReferencingName;
142: if (SanityManager.DEBUG) {
143: SanityManager.ASSERT(triggerSchemaName != null,
144: "triggerSchemaName sd is null");
145: SanityManager.ASSERT(triggerName != null,
146: "trigger name is null");
147: SanityManager.ASSERT(triggerTable != null,
148: "triggerTable is null");
149: SanityManager.ASSERT(actionText != null,
150: "actionText is null");
151: }
152: }
153:
154: /**
155: * This is the guts of the Execution-time logic for CREATE TRIGGER.
156: *
157: * @see ConstantAction#executeConstantAction
158: *
159: * @exception StandardException Thrown on failure
160: */
161: public void executeConstantAction(Activation activation)
162: throws StandardException {
163: SPSDescriptor whenspsd = null;
164: SPSDescriptor actionspsd;
165:
166: LanguageConnectionContext lcc = activation
167: .getLanguageConnectionContext();
168: DataDictionary dd = lcc.getDataDictionary();
169: DependencyManager dm = dd.getDependencyManager();
170: TransactionController tc = lcc.getTransactionExecute();
171:
172: /*
173: ** Indicate that we are about to modify the data dictionary.
174: **
175: ** We tell the data dictionary we're done writing at the end of
176: ** the transaction.
177: */
178: dd.startWriting(lcc);
179:
180: SchemaDescriptor triggerSd = getSchemaDescriptorForCreate(dd,
181: activation, triggerSchemaName);
182:
183: if (spsCompSchemaId == null) {
184: SchemaDescriptor def = lcc.getDefaultSchema();
185: if (def.getUUID() == null) {
186: // Descriptor for default schema is stale,
187: // look it up in the dictionary
188: def = dd.getSchemaDescriptor(def.getDescriptorName(),
189: tc, false);
190: }
191: spsCompSchemaId = def.getUUID();
192: }
193: if (SanityManager.DEBUG) {
194: SanityManager.ASSERT(spsCompSchemaId != null,
195: "spsCompSchemaId is null");
196: }
197:
198: String tabName;
199: if (triggerTable != null) {
200: triggerTableId = triggerTable.getUUID();
201: tabName = triggerTable.getName();
202: } else
203: tabName = "with UUID " + triggerTableId;
204:
205: /* We need to get table descriptor again. We simply can't trust the
206: * one we got at compile time, the lock on system table was released
207: * when compile was done, and the table might well have been dropped.
208: */
209: triggerTable = dd.getTableDescriptor(triggerTableId);
210: if (triggerTable == null) {
211: throw StandardException.newException(
212: SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION,
213: tabName);
214: }
215: /* Lock the table for DDL. Otherwise during our execution, the table
216: * might be changed, even dropped. Beetle 4269
217: */
218: lockTableForDDL(tc, triggerTable.getHeapConglomerateId(), true);
219: /* get triggerTable again for correctness, in case it's changed before
220: * the lock is aquired
221: */
222: triggerTable = dd.getTableDescriptor(triggerTableId);
223: if (triggerTable == null) {
224: throw StandardException.newException(
225: SQLState.LANG_TABLE_NOT_FOUND_DURING_EXECUTION,
226: tabName);
227: }
228:
229: /*
230: ** Send an invalidate on the table from which
231: ** the triggering event emanates. This it
232: ** to make sure that DML statements on this table
233: ** will be recompiled. Do this before we create
234: ** our trigger spses lest we invalidate them just
235: ** after creating them.
236: */
237: dm.invalidateFor(triggerTable,
238: DependencyManager.CREATE_TRIGGER, lcc);
239:
240: /*
241: ** Lets get our trigger id up front, we'll use it when
242: ** we create our spses.
243: */
244: UUID tmpTriggerId = dd.getUUIDFactory().createUUID();
245:
246: actionSPSId = (actionSPSId == null) ? dd.getUUIDFactory()
247: .createUUID() : actionSPSId;
248:
249: DataDescriptorGenerator ddg = dd.getDataDescriptorGenerator();
250:
251: /*
252: ** Create the trigger descriptor first so the trigger action
253: ** compilation can pick up the relevant trigger especially in
254: ** the case of self triggering.
255: */
256: TriggerDescriptor triggerd = ddg.newTriggerDescriptor(
257: triggerSd, tmpTriggerId, triggerName, eventMask,
258: isBefore, isRow, isEnabled, triggerTable,
259: whenspsd == null ? null : whenspsd.getUUID(),
260: actionSPSId,
261: creationTimestamp == null ? new Timestamp(System
262: .currentTimeMillis()) : creationTimestamp,
263: referencedCols, originalActionText, referencingOld,
264: referencingNew, oldReferencingName, newReferencingName);
265:
266: dd.addDescriptor(triggerd, triggerSd,
267: DataDictionary.SYSTRIGGERS_CATALOG_NUM, false, tc);
268:
269: /*
270: ** If we have a WHEN action we create it now.
271: */
272: if (whenText != null) {
273: whenspsd = createSPS(lcc, ddg, dd, tc, tmpTriggerId,
274: triggerSd, whenSPSId, spsCompSchemaId, whenText,
275: true, triggerTable);
276: }
277:
278: /*
279: ** Create the trigger action
280: */
281: actionspsd = createSPS(lcc, ddg, dd, tc, tmpTriggerId,
282: triggerSd, actionSPSId, spsCompSchemaId, actionText,
283: false, triggerTable);
284:
285: /*
286: ** Make underlying spses dependent on the trigger.
287: */
288: if (whenspsd != null) {
289: dm.addDependency(triggerd, whenspsd, lcc
290: .getContextManager());
291: }
292: dm.addDependency(triggerd, actionspsd, lcc.getContextManager());
293: dm.addDependency(triggerd, triggerTable, lcc
294: .getContextManager());
295: dm.addDependency(actionspsd, triggerTable, lcc
296: .getContextManager());
297: //store trigger's dependency on various privileges in the dependeny system
298: storeViewTriggerDependenciesOnPrivileges(activation, triggerd);
299: }
300:
301: /*
302: ** Create an sps that is used by the trigger.
303: */
304: private SPSDescriptor createSPS(LanguageConnectionContext lcc,
305: DataDescriptorGenerator ddg, DataDictionary dd,
306: TransactionController tc, UUID triggerId,
307: SchemaDescriptor sd, UUID spsId, UUID compSchemaId,
308: String text, boolean isWhen, TableDescriptor triggerTable)
309: throws StandardException {
310: if (text == null) {
311: return null;
312: }
313:
314: /*
315: ** Note: the format of this string is very important.
316: ** Dont change it arbitrarily -- see sps code.
317: */
318: String spsName = "TRIGGER" + (isWhen ? "WHEN_" : "ACTN_")
319: + triggerId + "_" + triggerTable.getUUID().toString();
320:
321: SPSDescriptor spsd = new SPSDescriptor(dd, spsName,
322: (spsId == null) ? dd.getUUIDFactory().createUUID()
323: : spsId, sd.getUUID(),
324: compSchemaId == null ? lcc.getDefaultSchema().getUUID()
325: : compSchemaId, SPSDescriptor.SPS_TYPE_TRIGGER,
326: true, // it is valid
327: text, // the text
328: true); // no defaults
329:
330: /*
331: ** Prepared the stored prepared statement
332: ** and release the activation class -- we
333: ** know we aren't going to execute statement
334: ** after create it, so for now we are finished.
335: */
336: spsd.prepareAndRelease(lcc, triggerTable);
337:
338: dd.addSPSDescriptor(spsd, tc, true);
339:
340: return spsd;
341: }
342:
343: public String toString() {
344: return constructToString("CREATE TRIGGER ", triggerName);
345: }
346: }
|