1 | /* |
2 | * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, |
3 | * and the EPL 1.0 (http://h2database.com/html/license.html). |
4 | * Initial Developer: H2 Group |
5 | */ |
6 | package org.h2.tools; |
7 | |
8 | import java.sql.Connection; |
9 | import java.sql.ResultSet; |
10 | import java.sql.SQLException; |
11 | import org.h2.api.Trigger; |
12 | |
13 | /** |
14 | * An adapter for the trigger interface that allows to use the ResultSet |
15 | * interface instead of a row array. |
16 | */ |
17 | public abstract class TriggerAdapter implements Trigger { |
18 | |
19 | /** |
20 | * The schema name. |
21 | */ |
22 | protected String schemaName; |
23 | |
24 | /** |
25 | * The name of the trigger. |
26 | */ |
27 | protected String triggerName; |
28 | |
29 | /** |
30 | * The name of the table. |
31 | */ |
32 | protected String tableName; |
33 | |
34 | /** |
35 | * Whether the fire method is called before or after the operation is |
36 | * performed. |
37 | */ |
38 | protected boolean before; |
39 | |
40 | /** |
41 | * The trigger type: INSERT, UPDATE, DELETE, SELECT, or a combination (a bit |
42 | * field). |
43 | */ |
44 | protected int type; |
45 | |
46 | private SimpleResultSet oldResultSet, newResultSet; |
47 | private TriggerRowSource oldSource, newSource; |
48 | |
49 | /** |
50 | * This method is called by the database engine once when initializing the |
51 | * trigger. It is called when the trigger is created, as well as when the |
52 | * database is opened. The default implementation initialized the result |
53 | * sets. |
54 | * |
55 | * @param conn a connection to the database |
56 | * @param schemaName the name of the schema |
57 | * @param triggerName the name of the trigger used in the CREATE TRIGGER |
58 | * statement |
59 | * @param tableName the name of the table |
60 | * @param before whether the fire method is called before or after the |
61 | * operation is performed |
62 | * @param type the operation type: INSERT, UPDATE, DELETE, SELECT, or a |
63 | * combination (this parameter is a bit field) |
64 | */ |
65 | @Override |
66 | public void init(Connection conn, String schemaName, |
67 | String triggerName, String tableName, |
68 | boolean before, int type) throws SQLException { |
69 | ResultSet rs = conn.getMetaData().getColumns( |
70 | null, schemaName, tableName, null); |
71 | oldSource = new TriggerRowSource(); |
72 | newSource = new TriggerRowSource(); |
73 | oldResultSet = new SimpleResultSet(oldSource); |
74 | newResultSet = new SimpleResultSet(newSource); |
75 | while (rs.next()) { |
76 | String column = rs.getString("COLUMN_NAME"); |
77 | int dataType = rs.getInt("DATA_TYPE"); |
78 | int precision = rs.getInt("COLUMN_SIZE"); |
79 | int scale = rs.getInt("DECIMAL_DIGITS"); |
80 | oldResultSet.addColumn(column, dataType, precision, scale); |
81 | newResultSet.addColumn(column, dataType, precision, scale); |
82 | } |
83 | this.schemaName = schemaName; |
84 | this.triggerName = triggerName; |
85 | this.tableName = tableName; |
86 | this.before = before; |
87 | this.type = type; |
88 | } |
89 | |
90 | /** |
91 | * A row source that allows to set the next row. |
92 | */ |
93 | static class TriggerRowSource implements SimpleRowSource { |
94 | |
95 | private Object[] row; |
96 | |
97 | void setRow(Object[] row) { |
98 | this.row = row; |
99 | } |
100 | |
101 | @Override |
102 | public Object[] readRow() { |
103 | return row; |
104 | } |
105 | |
106 | @Override |
107 | public void close() { |
108 | // ignore |
109 | } |
110 | |
111 | @Override |
112 | public void reset() { |
113 | // ignore |
114 | } |
115 | |
116 | } |
117 | |
118 | /** |
119 | * This method is called for each triggered action. The method is called |
120 | * immediately when the operation occurred (before it is committed). A |
121 | * transaction rollback will also rollback the operations that were done |
122 | * within the trigger, if the operations occurred within the same database. |
123 | * If the trigger changes state outside the database, a rollback trigger |
124 | * should be used. |
125 | * <p> |
126 | * The row arrays contain all columns of the table, in the same order |
127 | * as defined in the table. |
128 | * </p> |
129 | * <p> |
130 | * The default implementation calls the fire method with the ResultSet |
131 | * parameters. |
132 | * </p> |
133 | * |
134 | * @param conn a connection to the database |
135 | * @param oldRow the old row, or null if no old row is available (for |
136 | * INSERT) |
137 | * @param newRow the new row, or null if no new row is available (for |
138 | * DELETE) |
139 | * @throws SQLException if the operation must be undone |
140 | */ |
141 | @Override |
142 | public void fire(Connection conn, Object[] oldRow, Object[] newRow) |
143 | throws SQLException { |
144 | fire(conn, wrap(oldResultSet, oldSource, oldRow), |
145 | wrap(newResultSet, newSource, newRow)); |
146 | } |
147 | |
148 | /** |
149 | * This method is called for each triggered action by the default |
150 | * fire(Connection conn, Object[] oldRow, Object[] newRow) method. |
151 | * ResultSet.next does not need to be called (and calling it has no effect; |
152 | * it will always return true). |
153 | * <p> |
154 | * For "before" triggers, the new values of the new row may be changed |
155 | * using the ResultSet.updateX methods. |
156 | * </p> |
157 | * |
158 | * @param conn a connection to the database |
159 | * @param oldRow the old row, or null if no old row is available (for |
160 | * INSERT) |
161 | * @param newRow the new row, or null if no new row is available (for |
162 | * DELETE) |
163 | * @throws SQLException if the operation must be undone |
164 | */ |
165 | public abstract void fire(Connection conn, ResultSet oldRow, |
166 | ResultSet newRow) throws SQLException; |
167 | |
168 | private static SimpleResultSet wrap(SimpleResultSet rs, |
169 | TriggerRowSource source, Object[] row) throws SQLException { |
170 | if (row == null) { |
171 | return null; |
172 | } |
173 | source.setRow(row); |
174 | rs.next(); |
175 | return rs; |
176 | } |
177 | |
178 | /** |
179 | * This method is called when the database is closed. |
180 | * If the method throws an exception, it will be logged, but |
181 | * closing the database will continue. |
182 | * The default implementation does nothing. |
183 | */ |
184 | @Override |
185 | public void remove() throws SQLException { |
186 | // do nothing by default |
187 | } |
188 | |
189 | /** |
190 | * This method is called when the trigger is dropped. |
191 | * The default implementation does nothing. |
192 | */ |
193 | @Override |
194 | public void close() throws SQLException { |
195 | // do nothing by default |
196 | } |
197 | |
198 | } |