001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.schema;
007:
008: import java.sql.Connection;
009: import java.sql.SQLException;
010:
011: import org.h2.api.Trigger;
012: import org.h2.command.Parser;
013: import org.h2.constant.ErrorCode;
014: import org.h2.engine.DbObject;
015: import org.h2.engine.Session;
016: import org.h2.message.Message;
017: import org.h2.message.Trace;
018: import org.h2.result.Row;
019: import org.h2.table.Table;
020: import org.h2.value.DataType;
021: import org.h2.value.Value;
022:
023: /**
024: *A trigger is created using the statement
025: * CREATE TRIGGER
026: */
027: public class TriggerObject extends SchemaObjectBase {
028:
029: public static final int DEFAULT_QUEUE_SIZE = 1024;
030:
031: private boolean before;
032: private int typeMask;
033: private boolean rowBased;
034: // TODO trigger: support queue and noWait = false as well
035: private int queueSize = DEFAULT_QUEUE_SIZE;
036: private boolean noWait;
037: private Table table;
038: private String triggerClassName;
039: private Trigger triggerCallback;
040:
041: public TriggerObject(Schema schema, int id, String name, Table table) {
042: super (schema, id, name, Trace.TRIGGER);
043: this .table = table;
044: setTemporary(table.getTemporary());
045: }
046:
047: public void setBefore(boolean before) {
048: this .before = before;
049: }
050:
051: private synchronized void load(Session session) throws SQLException {
052: if (triggerCallback != null) {
053: return;
054: }
055: try {
056: Connection c2 = session.createConnection(false);
057: Object obj = session.getDatabase().loadUserClass(
058: triggerClassName).newInstance();
059: triggerCallback = (Trigger) obj;
060: triggerCallback.init(c2, getSchema().getName(), getName(),
061: table.getName(), before, typeMask);
062: } catch (Throwable e) {
063: throw Message.getSQLException(
064: ErrorCode.ERROR_CREATING_TRIGGER_OBJECT_3,
065: new String[] { getName(), triggerClassName,
066: e.toString() }, e);
067: }
068: }
069:
070: public void setTriggerClassName(Session session,
071: String triggerClassName, boolean force) throws SQLException {
072: this .triggerClassName = triggerClassName;
073: try {
074: load(session);
075: } catch (SQLException e) {
076: if (!force) {
077: throw e;
078: }
079: }
080: }
081:
082: public void fire(Session session, boolean beforeAction)
083: throws SQLException {
084: if (rowBased || before != beforeAction) {
085: return;
086: }
087: load(session);
088: Connection c2 = session.createConnection(false);
089: boolean old = session.setCommitOrRollbackDisabled(true);
090: try {
091: triggerCallback.fire(c2, null, null);
092: } catch (Throwable e) {
093: throw Message.getSQLException(
094: ErrorCode.ERROR_EXECUTING_TRIGGER_3,
095: new String[] { getName(), triggerClassName,
096: e.toString() }, e);
097: } finally {
098: session.setCommitOrRollbackDisabled(old);
099: }
100: }
101:
102: private Object[] convertToObjectList(Row row) throws SQLException {
103: if (row == null) {
104: return null;
105: }
106: int len = row.getColumnCount();
107: Object[] list = new Object[len];
108: for (int i = 0; i < len; i++) {
109: list[i] = row.getValue(i).getObject();
110: }
111: return list;
112: }
113:
114: /**
115: * Call the fire method of the user defined trigger class.
116: *
117: * @param session the session
118: * @param oldRow the old row
119: * @param newRow the new row
120: * @param beforeAction true if this method is called before the operation is
121: * applied
122: */
123: public void fireRow(Session session, Row oldRow, Row newRow,
124: boolean beforeAction) throws SQLException {
125: if (!rowBased || before != beforeAction) {
126: return;
127: }
128: Object[] oldList;
129: Object[] newList;
130: boolean fire = false;
131: if ((typeMask & Trigger.INSERT) != 0) {
132: if (oldRow == null && newRow != null) {
133: fire = true;
134: }
135: }
136: if ((typeMask & Trigger.UPDATE) != 0) {
137: if (oldRow != null && newRow != null) {
138: fire = true;
139: }
140: }
141: if ((typeMask & Trigger.DELETE) != 0) {
142: if (oldRow != null && newRow == null) {
143: fire = true;
144: }
145: }
146: if (!fire) {
147: return;
148: }
149: oldList = convertToObjectList(oldRow);
150: newList = convertToObjectList(newRow);
151: Object[] newListBackup;
152: if (before && newList != null) {
153: newListBackup = new Object[newList.length];
154: for (int i = 0; i < newList.length; i++) {
155: newListBackup[i] = newList[i];
156: }
157: } else {
158: newListBackup = null;
159: }
160: Connection c2 = session.createConnection(false);
161: boolean old = session.getAutoCommit();
162: boolean oldDisabled = session.setCommitOrRollbackDisabled(true);
163: try {
164: session.setAutoCommit(false);
165: triggerCallback.fire(c2, oldList, newList);
166: if (newListBackup != null) {
167: for (int i = 0; i < newList.length; i++) {
168: Object o = newList[i];
169: if (o != newListBackup[i]) {
170: Value v = DataType.convertToValue(session, o,
171: Value.UNKNOWN);
172: newRow.setValue(i, v);
173: }
174: }
175: }
176: } finally {
177: session.setCommitOrRollbackDisabled(oldDisabled);
178: session.setAutoCommit(old);
179: }
180: }
181:
182: public void setTypeMask(int typeMask) {
183: this .typeMask = typeMask;
184: }
185:
186: public void setRowBased(boolean rowBased) {
187: this .rowBased = rowBased;
188: }
189:
190: public void setQueueSize(int size) {
191: this .queueSize = size;
192: }
193:
194: public int getQueueSize() {
195: return queueSize;
196: }
197:
198: public void setNoWait(boolean noWait) {
199: this .noWait = noWait;
200: }
201:
202: public boolean getNoWait() {
203: return noWait;
204: }
205:
206: public String getDropSQL() {
207: return null;
208: }
209:
210: public String getCreateSQLForCopy(Table table, String quotedName) {
211: StringBuffer buff = new StringBuffer();
212: buff.append("CREATE FORCE TRIGGER ");
213: buff.append(quotedName);
214: if (before) {
215: buff.append(" BEFORE ");
216: } else {
217: buff.append(" AFTER ");
218: }
219: buff.append(getTypeNameList());
220: buff.append(" ON ");
221: buff.append(table.getSQL());
222: if (rowBased) {
223: buff.append(" FOR EACH ROW");
224: }
225: if (noWait) {
226: buff.append(" NOWAIT");
227: } else {
228: buff.append(" QUEUE ");
229: buff.append(queueSize);
230: }
231: buff.append(" CALL ");
232: buff.append(Parser.quoteIdentifier(triggerClassName));
233: return buff.toString();
234: }
235:
236: public String getTypeNameList() {
237: StringBuffer buff = new StringBuffer();
238: if ((typeMask & Trigger.INSERT) != 0) {
239: buff.append("INSERT");
240: }
241: if ((typeMask & Trigger.UPDATE) != 0) {
242: if (buff.length() > 0) {
243: buff.append(", ");
244: }
245: buff.append("UPDATE");
246: }
247: if ((typeMask & Trigger.DELETE) != 0) {
248: if (buff.length() > 0) {
249: buff.append(", ");
250: }
251: buff.append("DELETE");
252: }
253: return buff.toString();
254: }
255:
256: public String getCreateSQL() {
257: return getCreateSQLForCopy(table, getSQL());
258: }
259:
260: public int getType() {
261: return DbObject.TRIGGER;
262: }
263:
264: public void removeChildrenAndResources(Session session)
265: throws SQLException {
266: table.removeTrigger(session, this );
267: database.removeMeta(session, getId());
268: table = null;
269: triggerClassName = null;
270: triggerCallback = null;
271: invalidate();
272: }
273:
274: public void checkRename() {
275: // nothing to do
276: }
277:
278: public Table getTable() {
279: return table;
280: }
281:
282: public boolean getBefore() {
283: return before;
284: }
285:
286: public String getTriggerClassName() {
287: return triggerClassName;
288: }
289:
290: }
|