Source code: org/hsqldb/TriggerDef.java
1 /* Copyright (c) 2001-2002, The HSQL Development Group
2 * All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of the HSQL Development Group nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32 package org.hsqldb;
33
34 // peterhudson@users 20020130 - patch 478657 by peterhudson - triggers support
35 // fredt@users 20020130 - patch 1.7.0 by fredt
36 // added new class as jdk 1.1 does not allow use of LinkedList
37 import org.hsqldb.lib.HsqlDeque;
38
39 /**
40 * TriggerDef class declaration Definition and execution of triggers
41 * Development of the trigger implementation sponsored by Logicscope
42 * Realisations Ltd
43 *
44 * @author Logicscope Realisations Ltd
45 * @version 1.7.0 (1.0.0.3) Revision History: 1.0.0.1 First release in hsqldb 1.61
46 * 1.0.0.2 'nowait' support to prevent deadlock 1.0.0.3 multiple row
47 * queue for each trigger
48 */
49 class TriggerDef extends Thread {
50
51 /**
52 * member variables
53 */
54 static final int NUM_TRIGGER_OPS = 3; // ie ins,del,upd
55 static final int NUM_TRIGS = NUM_TRIGGER_OPS * 2 * 2;
56
57 // indexes into the triggers list
58 static final int INSERT_AFTER = 0;
59 static final int DELETE_AFTER = 1;
60 static final int UPDATE_AFTER = 2;
61 static final int INSERT_BEFORE = INSERT_AFTER + NUM_TRIGGER_OPS;
62 static final int DELETE_BEFORE = DELETE_AFTER + NUM_TRIGGER_OPS;
63 static final int UPDATE_BEFORE = UPDATE_AFTER + NUM_TRIGGER_OPS;
64 static final int INSERT_AFTER_ROW = INSERT_AFTER + 2 * NUM_TRIGGER_OPS;
65 static final int DELETE_AFTER_ROW = DELETE_AFTER + 2 * NUM_TRIGGER_OPS;
66 static final int UPDATE_AFTER_ROW = UPDATE_AFTER + 2 * NUM_TRIGGER_OPS;
67 static final int INSERT_BEFORE_ROW = INSERT_BEFORE + 2 * NUM_TRIGGER_OPS;
68 static final int DELETE_BEFORE_ROW = DELETE_BEFORE + 2 * NUM_TRIGGER_OPS;
69 static final int UPDATE_BEFORE_ROW = UPDATE_BEFORE + 2 * NUM_TRIGGER_OPS;
70
71 // other variables
72 String name;
73 String when;
74 String operation;
75 boolean forEachRow;
76 boolean nowait; // block or overwrite if queue full
77 int maxRowsQueued; // max size of queue of pending triggers
78
79 public static int getDefaultQueueSize() {
80 return defaultQueueSize;
81 }
82
83 protected static int defaultQueueSize = 1024;
84 Table table;
85 Trigger trig;
86 String fire;
87 int vectorIndx; // index into Vector[]
88
89 //protected boolean busy; // firing trigger in progress
90 protected HsqlDeque pendingQueue; // row triggers pending
91 protected int rowsQueued; // rows in pendingQueue
92 protected boolean valid; // parsing valid
93
94 /**
95 * Constructor declaration create an object from the components of an
96 * SQL CREATE TRIGGER statement
97 *
98 * @param sName
99 * @param sWhen
100 * @param sOper
101 * @param bForEach
102 * @param pTab
103 * @param pTrig
104 * @param sFire
105 * @param bNowait Description of the Parameter
106 * @param nQueueSize Description of the Parameter
107 */
108 public TriggerDef(String sName, String sWhen, String sOper,
109 boolean bForEach, Table pTab, Trigger pTrig,
110 String sFire, boolean bNowait, int nQueueSize) {
111
112 name = sName.toUpperCase();
113 when = sWhen.toUpperCase();
114 operation = sOper.toUpperCase();
115 forEachRow = bForEach;
116 nowait = bNowait;
117 maxRowsQueued = nQueueSize;
118 table = pTab;
119 trig = pTrig;
120 fire = sFire;
121 vectorIndx = SqlToIndex();
122
123 //busy = false;
124 rowsQueued = 0;
125 pendingQueue = new HsqlDeque();
126
127 if (vectorIndx < 0) {
128 valid = false;
129 } else {
130 valid = true;
131 }
132 }
133
134 /**
135 * Method declaration
136 *
137 * @return
138 */
139 public StringBuffer toBuf() {
140
141 StringBuffer a = new StringBuffer(256);
142
143 a.append("CREATE TRIGGER ");
144 a.append(name);
145 a.append(" ");
146 a.append(when);
147 a.append(" ");
148 a.append(operation);
149 a.append(" ON ");
150 a.append(table.getName().statementName);
151
152 if (forEachRow) {
153 a.append(" FOR EACH ROW ");
154 }
155
156 if (nowait) {
157 a.append(" NOWAIT ");
158 }
159
160 if (maxRowsQueued != getDefaultQueueSize()) {
161 a.append(" QUEUE ");
162 a.append(maxRowsQueued); // no need for trailing space
163 }
164
165 a.append(" CALL ");
166 a.append(fire);
167
168 return a;
169 }
170
171 /**
172 * SqlToIndex method declaration <P>
173 *
174 * Given the SQL creating the trigger, say what the index to the
175 * Vector[] is
176 *
177 * @return index to the Vector[]
178 */
179 public int SqlToIndex() {
180
181 int indx;
182
183 if (operation.equals("INSERT")) {
184 indx = INSERT_AFTER;
185 } else if (operation.equals("DELETE")) {
186 indx = DELETE_AFTER;
187 } else if (operation.equals("UPDATE")) {
188 indx = UPDATE_AFTER;
189 } else {
190 indx = -1;
191 }
192
193 if (when.equals("BEFORE")) {
194 indx += NUM_TRIGGER_OPS; // number of operations
195 } else if (!when.equals("AFTER")) {
196 indx = -1;
197 }
198
199 if (forEachRow) {
200 indx += 2 * NUM_TRIGGER_OPS;
201 }
202
203 return indx;
204 }
205
206 /**
207 * run method declaration <P>
208 *
209 * the trigger JSP is run in its own thread here. Its job is simply to
210 * wait until it is told by the main thread that it should fire the
211 * trigger.
212 */
213 public void run() {
214
215 boolean keepGoing = true;
216
217 while (keepGoing) {
218 Object trigRow[] = pop();
219
220 trig.fire(name, table.getName().name, trigRow);
221 }
222 }
223
224 /**
225 * pop method declaration <P>
226 *
227 * The consumer (trigger) thread waits for an event to be queued <P>
228 *
229 * <B>Note: </B> This push/pop pairing assumes a single producer thread
230 * and a single consumer thread _only_.
231 *
232 * @return Description of the Return Value
233 */
234 synchronized Object[] pop() {
235
236 if (rowsQueued == 0) {
237 try {
238 wait(); // this releases the lock monitor
239 } catch (InterruptedException e) {
240
241 /* ignore and resume */
242 }
243 }
244
245 rowsQueued--;
246
247 notify(); // notify push's wait
248
249 return (Object[]) pendingQueue.removeFirst();
250 }
251
252 /**
253 * push method declaration <P>
254 *
255 * The main thread tells the trigger thread to fire by this call
256 *
257 * @param row Description of the Parameter
258 */
259 synchronized void push(Object row[]) {
260
261 if (rowsQueued >= maxRowsQueued) {
262 if (nowait) {
263 pendingQueue.removeLast(); // overwrite last
264 } else {
265 try {
266 wait();
267 } catch (InterruptedException e) {
268
269 /* ignore and resume */
270 }
271
272 rowsQueued++;
273 }
274 } else {
275 rowsQueued++;
276 }
277
278 pendingQueue.add(row);
279 notify(); // notify pop's wait
280 }
281
282 /**
283 * Method declaration
284 *
285 * @return
286 */
287 public static int numTrigs() {
288 return NUM_TRIGS;
289 }
290
291 /**
292 * Method declaration
293 *
294 * @return
295 */
296 public boolean isBusy() {
297 return rowsQueued != 0;
298 }
299
300 /**
301 * Method declaration
302 *
303 * @return
304 */
305 public boolean isValid() {
306 return valid;
307 }
308 }