001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.GenericTriggerExecutor
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.error.StandardException;
025: import org.apache.derby.iapi.sql.dictionary.SPSDescriptor;
026: import org.apache.derby.iapi.sql.dictionary.TriggerDescriptor;
027: import org.apache.derby.iapi.sql.execute.CursorResultSet;
028: import org.apache.derby.iapi.sql.execute.ExecPreparedStatement;
029: import org.apache.derby.iapi.sql.Activation;
030: import org.apache.derby.iapi.sql.ResultSet;
031: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
032: import org.apache.derby.iapi.sql.conn.StatementContext;
033:
034: import org.apache.derby.iapi.reference.SQLState;
035:
036: /**
037: * A trigger executor is an object that executes
038: * a trigger. It is subclassed by row and statement
039: * executors.
040: */
041: public abstract class GenericTriggerExecutor {
042: protected InternalTriggerExecutionContext tec;
043: protected TriggerDescriptor triggerd;
044: protected Activation activation;
045: protected LanguageConnectionContext lcc;
046:
047: private boolean whenClauseRetrieved;
048: private boolean actionRetrieved;
049: private SPSDescriptor whenClause;
050: private SPSDescriptor action;
051:
052: private ExecPreparedStatement ps;
053: private Activation spsActivation;
054:
055: /**
056: * Constructor
057: *
058: * @param tec the execution context
059: * @param triggerd the trigger descriptor
060: * @param activation the activation
061: * @param lcc the lcc
062: */
063: GenericTriggerExecutor(InternalTriggerExecutionContext tec,
064: TriggerDescriptor triggerd, Activation activation,
065: LanguageConnectionContext lcc) {
066: this .tec = tec;
067: this .triggerd = triggerd;
068: this .activation = activation;
069: this .lcc = lcc;
070: }
071:
072: /**
073: * Fire the trigger based on the event.
074: *
075: * @param event the trigger event
076: * @param brs the before result set
077: * @param ars the after result set
078: *
079: * @exception StandardException on error or user exception
080: * from trigger action
081: */
082: abstract void fireTrigger(TriggerEvent event, CursorResultSet brs,
083: CursorResultSet ars) throws StandardException;
084:
085: protected SPSDescriptor getWhenClause() throws StandardException {
086: if (!whenClauseRetrieved) {
087: whenClauseRetrieved = true;
088: whenClause = triggerd.getWhenClauseSPS();
089: }
090: return whenClause;
091: }
092:
093: protected SPSDescriptor getAction() throws StandardException {
094: if (!actionRetrieved) {
095: actionRetrieved = true;
096: action = triggerd.getActionSPS(lcc);
097: }
098: return action;
099: }
100:
101: /**
102: * Execute the given stored prepared statement. We
103: * just grab the prepared statement from the spsd,
104: * get a new activation holder and let er rip.
105: *
106: * @exception StandardException on error
107: */
108: protected void executeSPS(SPSDescriptor sps)
109: throws StandardException {
110: boolean recompile = false;
111:
112: while (true) {
113: /*
114: ** Only grab the ps the 1st time through. This
115: ** way a row trigger doesn't do any unnecessary
116: ** setup work.
117: */
118: if (ps == null || recompile) {
119: /*
120: ** We need to clone the prepared statement so we don't
121: ** wind up marking that ps that is tied to sps as finished
122: ** during the course of execution.
123: */
124: ps = sps.getPreparedStatement();
125: ps = ps.getClone();
126: // it should be valid since we've just prepared for it
127: ps.setValid();
128: spsActivation = ps.getActivation(lcc, false);
129:
130: /*
131: ** Normally, we want getSource() for an sps invocation
132: ** to be EXEC STATEMENT xxx, but in this case, since
133: ** we are executing the SPS in our own fashion, we want
134: ** the text to be the trigger action. So set it accordingly.
135: */
136: ps.setSource(sps.getText());
137: ps.setSPSAction();
138: }
139:
140: /*
141: ** Execute the activation. If we have an error, we
142: ** are going to go to some extra work to pop off
143: ** our statement context. This is because we are
144: ** a nested statement (we have 2 activations), but
145: ** we aren't a nested connection, so we have to
146: ** pop off our statementcontext to get error handling
147: ** to work correctly. This is normally a no-no, but
148: ** we are an unusual case.
149: */
150: try {
151: // This is a substatement; for now, we do not set any timeout
152: // for it. We might change this behaviour later, by linking
153: // timeout to its parent statement's timeout settings.
154: ResultSet rs = ps.execute(spsActivation, false, 0L);
155: if (rs.returnsRows()) {
156: // Fetch all the data to ensure that functions in the select list or values statement will
157: // be evaluated and side effects will happen. Why else would the trigger action return
158: // rows, but for side effects?
159: // The result set was opened in ps.execute()
160: while (rs.getNextRow() != null) {
161: }
162: }
163: rs.close();
164: } catch (StandardException e) {
165: /* Handle dynamic recompiles */
166: if (e.getMessageId().equals(
167: SQLState.LANG_STATEMENT_NEEDS_RECOMPILE)) {
168: StatementContext sc = lcc.getStatementContext();
169: sc.cleanupOnError(e);
170: recompile = true;
171: sps.revalidate(lcc);
172: continue;
173: }
174: lcc.popStatementContext(lcc.getStatementContext(), e);
175: spsActivation.close();
176: throw e;
177: }
178:
179: /* Done with execution without any recompiles */
180: break;
181: }
182: }
183:
184: /**
185: * Cleanup after executing an sps.
186: *
187: * @exception StandardException on error
188: */
189: protected void clearSPS() throws StandardException {
190: if (spsActivation != null) {
191: spsActivation.close();
192: }
193: ps = null;
194: spsActivation = null;
195: }
196: }
|