提交 c9e8941a authored 作者: Thomas Mueller's avatar Thomas Mueller

TriggerAdapter: in "before" triggers, values can be changed using the ResultSet.updateX methods.

上级 c34aff38
...@@ -18,7 +18,14 @@ Change Log ...@@ -18,7 +18,14 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>The script created by SCRIPT DROP did not always work if multiple views <ul><li>TriggerAdapter: in "before" triggers, values can be changed using the ResultSet.updateX methods.
</li><li>Creating a table with column data type NULL now works (even if not very useful).
</li><li>ALTER TABLE ALTER COLUMN no longer copies the data for widening conversions
(for example if only the precision was increased) unless necessary.
</li><li>Multi-threaded kernel: concurrently running an online backup
and updating the database resulted in a broken (transactionally incorrect)
backup file in some cases.
</li><li>The script created by SCRIPT DROP did not always work if multiple views
existed that depend on each other. existed that depend on each other.
</li><li>MathUtils.getSecureRandom could log a warning to System.err in case </li><li>MathUtils.getSecureRandom could log a warning to System.err in case
the /dev/random is very slow, and the System.getProperties().toString() the /dev/random is very slow, and the System.getProperties().toString()
......
...@@ -17,6 +17,33 @@ import org.h2.api.Trigger; ...@@ -17,6 +17,33 @@ import org.h2.api.Trigger;
*/ */
public abstract class TriggerAdapter implements Trigger { public abstract class TriggerAdapter implements Trigger {
/**
* The schema name.
*/
protected String schemaName;
/**
* The name of the trigger.
*/
protected String triggerName;
/**
* The name of the table.
*/
protected String tableName;
/**
* Whether the fire method is called before or after the operation is
* performed.
*/
protected boolean before;
/**
* The trigger type: INSERT, UPDATE, DELETE, SELECT, or a combination (a bit
* field).
*/
protected int type;
private SimpleResultSet oldResultSet, newResultSet; private SimpleResultSet oldResultSet, newResultSet;
private TriggerRowSource oldSource, newSource; private TriggerRowSource oldSource, newSource;
...@@ -33,7 +60,8 @@ public abstract class TriggerAdapter implements Trigger { ...@@ -33,7 +60,8 @@ public abstract class TriggerAdapter implements Trigger {
* @param tableName the name of the table * @param tableName the name of the table
* @param before whether the fire method is called before or after the * @param before whether the fire method is called before or after the
* operation is performed * operation is performed
* @param type the operation type: INSERT, UPDATE, or DELETE * @param type the operation type: INSERT, UPDATE, DELETE, SELECT, or a
* combination (this parameter is a bit field)
*/ */
public void init(Connection conn, String schemaName, public void init(Connection conn, String schemaName,
String triggerName, String tableName, String triggerName, String tableName,
...@@ -52,6 +80,11 @@ public abstract class TriggerAdapter implements Trigger { ...@@ -52,6 +80,11 @@ public abstract class TriggerAdapter implements Trigger {
oldResultSet.addColumn(column, dataType, precision, scale); oldResultSet.addColumn(column, dataType, precision, scale);
newResultSet.addColumn(column, dataType, precision, scale); newResultSet.addColumn(column, dataType, precision, scale);
} }
this.schemaName = schemaName;
this.triggerName = triggerName;
this.tableName = tableName;
this.before = before;
this.type = type;
} }
/** /**
...@@ -112,6 +145,10 @@ public abstract class TriggerAdapter implements Trigger { ...@@ -112,6 +145,10 @@ public abstract class TriggerAdapter implements Trigger {
* fire(Connection conn, Object[] oldRow, Object[] newRow) method. * fire(Connection conn, Object[] oldRow, Object[] newRow) method.
* ResultSet.next does not need to be called (and calling it has no effect; * ResultSet.next does not need to be called (and calling it has no effect;
* it will always return true). * it will always return true).
* <p>
* For "before" triggers, the new values of the new row may be changed
* using the ResultSet.updateX methods.
* </p>
* *
* @param conn a connection to the database * @param conn a connection to the database
* @param oldRow the old row, or null if no old row is available (for * @param oldRow the old row, or null if no old row is available (for
......
...@@ -121,19 +121,25 @@ public class TestTriggersConstraints extends TestBase implements Trigger { ...@@ -121,19 +121,25 @@ public class TestTriggersConstraints extends TestBase implements Trigger {
stat.execute("drop table if exists test"); stat.execute("drop table if exists test");
stat.execute("create table test(id int)"); stat.execute("create table test(id int)");
stat.execute("create table message(name varchar)"); stat.execute("create table message(name varchar)");
stat.execute("create trigger test_insert after insert, update, delete on test " + stat.execute("create trigger test_insert before insert, update, delete on test " +
"for each row call \"" + TestTriggerAdapter.class.getName() + "\""); "for each row call \"" + TestTriggerAdapter.class.getName() + "\"");
stat.execute("insert into test values(1)"); stat.execute("insert into test values(1)");
ResultSet rs;
rs = stat.executeQuery("select * from test");
rs.next();
assertEquals(10, rs.getInt(1));
stat.execute("update test set id = 2"); stat.execute("update test set id = 2");
rs = stat.executeQuery("select * from test");
rs.next();
assertEquals(20, rs.getInt(1));
stat.execute("delete from test"); stat.execute("delete from test");
ResultSet rs;
rs = stat.executeQuery("select * from message"); rs = stat.executeQuery("select * from message");
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals("+1;", rs.getString(1)); assertEquals("+1;", rs.getString(1));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals("-1;+2;", rs.getString(1)); assertEquals("-10;+2;", rs.getString(1));
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals("-2;", rs.getString(1)); assertEquals("-20;", rs.getString(1));
assertFalse(rs.next()); assertFalse(rs.next());
stat.execute("drop table test, message"); stat.execute("drop table test, message");
conn.close(); conn.close();
...@@ -182,6 +188,30 @@ public class TestTriggersConstraints extends TestBase implements Trigger { ...@@ -182,6 +188,30 @@ public class TestTriggersConstraints extends TestBase implements Trigger {
if (newRow != null) { if (newRow != null) {
buff.append("+").append(newRow.getString("id")).append(';'); buff.append("+").append(newRow.getString("id")).append(';');
} }
if (!"TEST_INSERT".equals(triggerName)) {
throw new RuntimeException("Wrong trigger name: " + triggerName);
}
if (!"TEST".equals(tableName)) {
throw new RuntimeException("Wrong table name: " + tableName);
}
if (!"PUBLIC".equals(schemaName)) {
throw new RuntimeException("Wrong schema name: " + schemaName);
}
if (type != (Trigger.INSERT | Trigger.UPDATE | Trigger.DELETE)) {
throw new RuntimeException("Wrong type: " + type);
}
if (newRow != null) {
if (oldRow == null) {
if (newRow.getInt(1) != 1) {
throw new RuntimeException("Expected: 1 got: " + newRow.getString(1));
}
} else {
if (newRow.getInt(1) != 2) {
throw new RuntimeException("Expected: 2 got: " + newRow.getString(1));
}
}
newRow.updateInt(1, newRow.getInt(1) * 10);
}
conn.createStatement().execute("insert into message values('" + buff.toString() + "')"); conn.createStatement().execute("insert into message values('" + buff.toString() + "')");
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论