File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\schema\TriggerObject.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.schema;
8
9 import java.sql.Connection;
10 import java.sql.SQLException;
11 import org.h2.api.Trigger;
12 import org.h2.command.Parser;
13 import org.h2.constant.ErrorCode;
14 import org.h2.engine.DbObject;
15 import org.h2.engine.Session;
16 import org.h2.message.DbException;
17 import org.h2.message.Trace;
18 import org.h2.result.Row;
19 import org.h2.table.Table;
20 import org.h2.util.Utils;
21 import org.h2.util.StatementBuilder;
22 import org.h2.value.DataType;
23 import org.h2.value.Value;
24
25 /**
26 *A trigger is created using the statement
27 * CREATE TRIGGER
28 */
29 public class TriggerObject extends SchemaObjectBase {
30
31 /**
32 * The default queue size.
33 */
34 public static final int DEFAULT_QUEUE_SIZE = 1024;
35
36 private boolean insteadOf;
37 private boolean before;
38 private int typeMask;
39 private boolean rowBased;
40 private boolean onRollback;
41 // TODO trigger: support queue and noWait = false as well
42 private int queueSize = DEFAULT_QUEUE_SIZE;
43 private boolean noWait;
44 private Table table;
45 private String triggerClassName;
46 private Trigger triggerCallback;
47
48 public TriggerObject(Schema schema, int id, String name, Table table) {
49 initSchemaObjectBase(schema, id, name, Trace.TRIGGER);
50 this.table = table;
51 setTemporary(table.isTemporary());
52 }
53
54 public void setBefore(boolean before) {
55 this.before = before;
56 }
57
58 public void setInsteadOf(boolean insteadOf) {
59 this.insteadOf = insteadOf;
60 }
61
62 private synchronized void load() {
63 if (triggerCallback != null) {
64 return;
65 }
66 try {
67 Session session = database.getSystemSession();
68 Connection c2 = session.createConnection(false);
69 Object obj = Utils.loadUserClass(triggerClassName).newInstance();
70 triggerCallback = (Trigger) obj;
71 triggerCallback.init(c2, getSchema().getName(), getName(), table.getName(), before, typeMask);
72 } catch (Throwable e) {
73 // try again later
74 triggerCallback = null;
75 throw DbException.get(ErrorCode.ERROR_CREATING_TRIGGER_OBJECT_3, e, getName(),
76 triggerClassName, e.toString());
77 }
78 }
79
80 /**
81 * Set the trigger class name and load the class if possible.
82 *
83 * @param triggerClassName the name of the trigger class
84 * @param force whether exceptions (due to missing class or access rights)
85 * should be ignored
86 */
87 public void setTriggerClassName(String triggerClassName, boolean force) {
88 this.triggerClassName = triggerClassName;
89 try {
90 load();
91 } catch (DbException e) {
92 if (!force) {
93 throw e;
94 }
95 }
96 }
97
98 /**
99 * Call the trigger class if required. This method does nothing if the
100 * trigger is not defined for the given action. This method is called before
101 * or after any rows have been processed, once for each statement.
102 *
103 * @param session the session
104 * @param type the trigger type
105 * @param beforeAction if this method is called before applying the changes
106 */
107 public void fire(Session session, int type, boolean beforeAction) {
108 if (rowBased || before != beforeAction || (typeMask & type) == 0) {
109 return;
110 }
111 load();
112 Connection c2 = session.createConnection(false);
113 boolean old = false;
114 if (type != Trigger.SELECT) {
115 old = session.setCommitOrRollbackDisabled(true);
116 }
117 Value identity = session.getLastScopeIdentity();
118 try {
119 triggerCallback.fire(c2, null, null);
120 } catch (Throwable e) {
121 throw DbException.get(ErrorCode.ERROR_EXECUTING_TRIGGER_3, e, getName(),
122 triggerClassName, e.toString());
123 } finally {
124 session.setLastScopeIdentity(identity);
125 if (type != Trigger.SELECT) {
126 session.setCommitOrRollbackDisabled(old);
127 }
128 }
129 }
130
131 private static Object[] convertToObjectList(Row row) {
132 if (row == null) {
133 return null;
134 }
135 int len = row.getColumnCount();
136 Object[] list = new Object[len];
137 for (int i = 0; i < len; i++) {
138 list[i] = row.getValue(i).getObject();
139 }
140 return list;
141 }
142
143 /**
144 * Call the fire method of the user-defined trigger class if required. This
145 * method does nothing if the trigger is not defined for the given action.
146 * This method is called before or after a row is processed, possibly many
147 * times for each statement.
148 *
149 * @param session the session
150 * @param oldRow the old row
151 * @param newRow the new row
152 * @param beforeAction true if this method is called before the operation is
153 * applied
154 * @param rollback when the operation occurred within a rollback
155 * @return true if no further action is required (for 'instead of' triggers)
156 */
157 public boolean fireRow(Session session, Row oldRow, Row newRow, boolean beforeAction, boolean rollback) {
158 if (!rowBased || before != beforeAction) {
159 return false;
160 }
161 if (rollback && !onRollback) {
162 return false;
163 }
164 load();
165 Object[] oldList;
166 Object[] newList;
167 boolean fire = false;
168 if ((typeMask & Trigger.INSERT) != 0) {
169 if (oldRow == null && newRow != null) {
170 fire = true;
171 }
172 }
173 if ((typeMask & Trigger.UPDATE) != 0) {
174 if (oldRow != null && newRow != null) {
175 fire = true;
176 }
177 }
178 if ((typeMask & Trigger.DELETE) != 0) {
179 if (oldRow != null && newRow == null) {
180 fire = true;
181 }
182 }
183 if (!fire) {
184 return false;
185 }
186 oldList = convertToObjectList(oldRow);
187 newList = convertToObjectList(newRow);
188 Object[] newListBackup;
189 if (before && newList != null) {
190 newListBackup = new Object[newList.length];
191 System.arraycopy(newList, 0, newListBackup, 0, newList.length);
192 } else {
193 newListBackup = null;
194 }
195 Connection c2 = session.createConnection(false);
196 boolean old = session.getAutoCommit();
197 boolean oldDisabled = session.setCommitOrRollbackDisabled(true);
198 Value identity = session.getLastScopeIdentity();
199 try {
200 session.setAutoCommit(false);
201 triggerCallback.fire(c2, oldList, newList);
202 if (newListBackup != null) {
203 for (int i = 0; i < newList.length; i++) {
204 Object o = newList[i];
205 if (o != newListBackup[i]) {
206 Value v = DataType.convertToValue(session, o, Value.UNKNOWN);
207 newRow.setValue(i, v);
208 }
209 }
210 }
211 } catch (Exception e) {
212 if (onRollback) {
213 // ignore
214 } else {
215 throw DbException.convert(e);
216 }
217 } finally {
218 session.setLastScopeIdentity(identity);
219 session.setCommitOrRollbackDisabled(oldDisabled);
220 session.setAutoCommit(old);
221 }
222 return insteadOf;
223 }
224
225 /**
226 * Set the trigger type.
227 *
228 * @param typeMask the type
229 */
230 public void setTypeMask(int typeMask) {
231 this.typeMask = typeMask;
232 }
233
234 public void setRowBased(boolean rowBased) {
235 this.rowBased = rowBased;
236 }
237
238 public void setQueueSize(int size) {
239 this.queueSize = size;
240 }
241
242 public int getQueueSize() {
243 return queueSize;
244 }
245
246 public void setNoWait(boolean noWait) {
247 this.noWait = noWait;
248 }
249
250 public boolean isNoWait() {
251 return noWait;
252 }
253
254 public void setOnRollback(boolean onRollback) {
255 this.onRollback = onRollback;
256 }
257
258 public String getDropSQL() {
259 return null;
260 }
261
262 public String getCreateSQLForCopy(Table targetTable, String quotedName) {
263 StringBuilder buff = new StringBuilder("CREATE FORCE TRIGGER ");
264 buff.append(quotedName);
265 if (insteadOf) {
266 buff.append(" INSTEAD OF ");
267 } else if (before) {
268 buff.append(" BEFORE ");
269 } else {
270 buff.append(" AFTER ");
271 }
272 buff.append(getTypeNameList());
273 buff.append(" ON ").append(targetTable.getSQL());
274 if (rowBased) {
275 buff.append(" FOR EACH ROW");
276 }
277 if (noWait) {
278 buff.append(" NOWAIT");
279 } else {
280 buff.append(" QUEUE ").append(queueSize);
281 }
282 buff.append(" CALL ").append(Parser.quoteIdentifier(triggerClassName));
283 return buff.toString();
284 }
285
286 public String getTypeNameList() {
287 StatementBuilder buff = new StatementBuilder();
288 if ((typeMask & Trigger.INSERT) != 0) {
289 buff.appendExceptFirst(", ");
290 buff.append("INSERT");
291 }
292 if ((typeMask & Trigger.UPDATE) != 0) {
293 buff.appendExceptFirst(", ");
294 buff.append("UPDATE");
295 }
296 if ((typeMask & Trigger.DELETE) != 0) {
297 buff.appendExceptFirst(", ");
298 buff.append("DELETE");
299 }
300 if ((typeMask & Trigger.SELECT) != 0) {
301 buff.appendExceptFirst(", ");
302 buff.append("SELECT");
303 }
304 if (onRollback) {
305 buff.appendExceptFirst(", ");
306 buff.append("ROLLBACK");
307 }
308 return buff.toString();
309 }
310
311 public String getCreateSQL() {
312 return getCreateSQLForCopy(table, getSQL());
313 }
314
315 public int getType() {
316 return DbObject.TRIGGER;
317 }
318
319 public void removeChildrenAndResources(Session session) {
320 table.removeTrigger(this);
321 database.removeMeta(session, getId());
322 if (triggerCallback != null) {
323 try {
324 triggerCallback.remove();
325 } catch (SQLException e) {
326 throw DbException.convert(e);
327 }
328 }
329 table = null;
330 triggerClassName = null;
331 triggerCallback = null;
332 invalidate();
333 }
334
335 public void checkRename() {
336 // nothing to do
337 }
338
339 /**
340 * Get the table of this trigger.
341 *
342 * @return the table
343 */
344 public Table getTable() {
345 return table;
346 }
347
348 /**
349 * Check if this is a before trigger.
350 *
351 * @return true if it is
352 */
353 public boolean isBefore() {
354 return before;
355 }
356
357 /**
358 * Get the trigger class name.
359 *
360 * @return the class name
361 */
362 public String getTriggerClassName() {
363 return triggerClassName;
364 }
365
366 /**
367 * Close the trigger.
368 */
369 public void close() throws SQLException {
370 if (triggerCallback != null) {
371 triggerCallback.close();
372 }
373 }
374
375 /**
376 * Check whether this is a select trigger.
377 *
378 * @return true if it is
379 */
380 public boolean isSelectTrigger() {
381 return (typeMask & Trigger.SELECT) != 0;
382 }
383
384 }
385