Skip to content

Commit a604ac2

Browse files
committed
Support for triggers.
1 parent 3716a5e commit a604ac2

File tree

9 files changed

+407
-21
lines changed

9 files changed

+407
-21
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* FB/Java plugin
3+
*
4+
* Distributable under LGPL license.
5+
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* LGPL License for more details.
11+
*
12+
* This file was created by members of the Firebird development team.
13+
* All individual contributions remain the Copyright (C) of those
14+
* individuals. Contributors to this file are either listed here or
15+
* can be obtained from a git log command.
16+
*
17+
* All rights reserved.
18+
*/
19+
package example;
20+
21+
import java.sql.SQLException;
22+
23+
import org.firebirdsql.fbjava.TriggerContext;
24+
import org.firebirdsql.fbjava.Values;
25+
import org.firebirdsql.fbjava.ValuesMetadata;
26+
27+
28+
public class Triggers
29+
{
30+
public static void trigger1() throws SQLException
31+
{
32+
TriggerContext context = TriggerContext.get();
33+
ValuesMetadata fieldsMetadata = context.getFieldsMetadata();
34+
Values oldValues = context.getOldValues();
35+
Values newValues = context.getNewValues();
36+
37+
System.out.println("--> " + context.getTableName() + ", " + context.getType() + ", " +
38+
context.getAction() + ", " + context.getFieldsMetadata() + ", " +
39+
context.getOldValues() + ", " + context.getNewValues());
40+
41+
if (fieldsMetadata != null)
42+
{
43+
int count = fieldsMetadata.getParameterCount();
44+
45+
for (int i = 1; i <= count; ++i)
46+
{
47+
System.out.println(">>> " + i + ", " + fieldsMetadata.getName(i) + ", " +
48+
(oldValues == null ? null : oldValues.get(i)) + ", " +
49+
(newValues == null ? null : newValues.get(i)));
50+
51+
/***
52+
if (newValues != null)
53+
newValues.set(2, BigDecimal.valueOf(((BigDecimal) newValues.get(2)).intValue() + 1));
54+
***/
55+
}
56+
}
57+
58+
System.out.flush();
59+
}
60+
}

src/fbjava/src/main/java/org/firebirdsql/fbjava/TriggerContext.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,65 @@
2626
*/
2727
public interface TriggerContext extends Context
2828
{
29+
public static enum Type
30+
{
31+
BEFORE,
32+
AFTER,
33+
DATABASE
34+
}
35+
36+
public static enum Action
37+
{
38+
INSERT,
39+
UPDATE,
40+
DELETE,
41+
CONNECT,
42+
DISCONNECT,
43+
TRANS_START,
44+
TRANS_COMMIT,
45+
TRANS_ROLLBACK,
46+
DDL
47+
}
48+
2949
/**
3050
* Gets the Context instance associated with the current call.
3151
*/
3252
public static TriggerContext get()
3353
{
3454
return (TriggerContext) Context.get();
3555
}
56+
57+
/**
58+
* Gets the table that fired the trigger.
59+
* Returns null for database and DDL triggers.
60+
*/
61+
public String getTableName();
62+
63+
/**
64+
* Gets the type of the trigger.
65+
*/
66+
public Type getType();
67+
68+
/**
69+
* Gets the action that fired the trigger.
70+
*/
71+
public Action getAction();
72+
73+
/**
74+
* Gets the fields metadata.
75+
* Returns null for database/ddl triggers.
76+
*/
77+
public ValuesMetadata getFieldsMetadata();
78+
79+
/**
80+
* Gets the fields old values.
81+
* Returns null for database/ddl and insert triggers.
82+
*/
83+
public Values getOldValues();
84+
85+
/**
86+
* Gets the fields new values.
87+
* Returns null for database/ddl and delete triggers.
88+
*/
89+
public Values getNewValues();
3690
}

src/fbjava/src/main/java/org/firebirdsql/fbjava/impl/CallableRoutineContextImpl.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@
2525

2626
abstract class CallableRoutineContextImpl extends ContextImpl implements CallableRoutineContext
2727
{
28-
//// TODO: input/output parameters
29-
3028
public CallableRoutineContextImpl(InternalContext internalContext)
3129
{
3230
super(internalContext);

src/fbjava/src/main/java/org/firebirdsql/fbjava/impl/ExternalEngine.java

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,8 +1138,8 @@ public IExternalProcedure makeProcedure(IStatus status, IExternalContext context
11381138
public IExternalTrigger makeTrigger(IStatus status, IExternalContext context,
11391139
IRoutineMetadata metadata, IMetadataBuilder fieldsBuilder) throws FbException
11401140
{
1141-
System.out.println("makeTrigger");
1142-
return null;
1141+
Routine routine = getRoutine(status, context, metadata, fieldsBuilder, null, Routine.Type.TRIGGER);
1142+
return ExternalTrigger.create(routine);
11431143
}
11441144

11451145
private Routine getRoutine(IStatus status, IExternalContext context, IRoutineMetadata metadata,
@@ -1150,7 +1150,15 @@ private Routine getRoutine(IStatus status, IExternalContext context, IRoutineMet
11501150
String entryPoint = metadata.getEntryPoint(status);
11511151
String invalidMethodSignatureMsg = String.format("Invalid method signature: '%s'", entryPoint);
11521152
int paramsStart = entryPoint.indexOf('(');
1153+
1154+
if (paramsStart == -1)
1155+
throw new FbException(invalidMethodSignatureMsg);
1156+
11531157
int methodStart = entryPoint.lastIndexOf('.', paramsStart) + 1;
1158+
1159+
if (methodStart == 0)
1160+
throw new FbException(invalidMethodSignatureMsg);
1161+
11541162
String className = entryPoint.substring(0, methodStart - 1).trim();
11551163
String methodName = entryPoint.substring(methodStart, paramsStart).trim();
11561164

@@ -1164,20 +1172,23 @@ private Routine getRoutine(IStatus status, IExternalContext context, IRoutineMet
11641172

11651173
skipBlanks(entryPoint, pos);
11661174

1167-
IMessageMetadata inMetadata = metadata.getInputMetadata(status);
1175+
IMessageMetadata inMetadata = type == Routine.Type.TRIGGER ? null : metadata.getInputMetadata(status);
11681176
try
11691177
{
1170-
int inCount = inMetadata.getCount(status);
1178+
int inCount = inMetadata == null ? 0 : inMetadata.getCount(status);
11711179

1172-
IMessageMetadata outMetadata = metadata.getOutputMetadata(status);
1180+
IMessageMetadata outMetadata = type == Routine.Type.TRIGGER ? null : metadata.getOutputMetadata(status);
11731181
try
11741182
{
1175-
int outCount = outMetadata.getCount(status);
1183+
int outCount = outMetadata == null ? 0 : outMetadata.getCount(status);
11761184

11771185
if (peekChar(entryPoint, pos) == ')')
11781186
++pos[0];
11791187
else
11801188
{
1189+
if (type == Routine.Type.TRIGGER)
1190+
throw new FbException(invalidMethodSignatureMsg);
1191+
11811192
int n = 0;
11821193

11831194
do
@@ -1237,6 +1248,9 @@ else if (c == ',')
12371248
else if (pos[0] != entryPoint.length())
12381249
throw new FbException(invalidMethodSignatureMsg);
12391250

1251+
//// TODO: Parameters prefixed with a Context parameter.
1252+
//// TODO: Zero parameters.
1253+
12401254
switch (type)
12411255
{
12421256
case FUNCTION:
@@ -1268,6 +1282,7 @@ else if (pos[0] != entryPoint.length())
12681282
}
12691283

12701284
case PROCEDURE:
1285+
{
12711286
if (paramTypes.size() != inCount + outCount)
12721287
{
12731288
throw new FbException(String.format("Number of parameters (%d) in the Java method " +
@@ -1292,27 +1307,70 @@ else if (pos[0] != entryPoint.length())
12921307
if (routine.method.getReturnType() != void.class &&
12931308
!ExternalResultSet.class.isAssignableFrom(routine.method.getReturnType()))
12941309
{
1295-
throw new FbException("Java method for a procedure must return void or an class " +
1296-
"implementing ExternalResultSet interface"); //// TODO: package name
1310+
throw new FbException(String.format(
1311+
"Java method for a procedure must return void or an class implementing %s interface",
1312+
ExternalResultSet.class.getName()));
12971313
}
12981314

12991315
break;
1316+
}
1317+
1318+
case TRIGGER:
1319+
{
1320+
routine.method = clazz.getMethod(methodName);
1321+
1322+
if (routine.method.getReturnType() != void.class)
1323+
throw new FbException("Java method for a trigger must return void");
1324+
1325+
if (inBuilder != null)
1326+
{
1327+
IMessageMetadata triggerMetadata = metadata.getTriggerMetadata(status);
1328+
try
1329+
{
1330+
int count = triggerMetadata.getCount(status);
1331+
1332+
for (int index = 0; index < count; ++index)
1333+
{
1334+
Parameter parameter = new Parameter(
1335+
dataTypesByClass.get(Object.class), Object.class);
1336+
parameter.name = triggerMetadata.getField(status, index);
1337+
parameter.type = fbTypeNames.get(triggerMetadata.getType(status, index));
1338+
1339+
routine.inputParameters.add(parameter);
1340+
}
1341+
1342+
routine.setupParameters(status, routine.inputParameters, routine.inputMetadata,
1343+
triggerMetadata, inBuilder);
1344+
}
1345+
finally
1346+
{
1347+
triggerMetadata.release();
1348+
}
1349+
}
1350+
1351+
break;
1352+
}
13001353
}
13011354

1302-
routine.setupParameters(status, routine.inputParameters, routine.inputMetadata,
1303-
inMetadata, inBuilder);
1355+
if (type != Routine.Type.TRIGGER)
1356+
{
1357+
routine.setupParameters(status, routine.inputParameters, routine.inputMetadata,
1358+
inMetadata, inBuilder);
13041359

1305-
routine.setupParameters(status, routine.outputParameters, routine.outputMetadata,
1306-
outMetadata, outBuilder);
1360+
routine.setupParameters(status, routine.outputParameters, routine.outputMetadata,
1361+
outMetadata, outBuilder);
1362+
}
13071363
}
13081364
finally
13091365
{
1310-
outMetadata.release();
1366+
if (outMetadata != null)
1367+
outMetadata.release();
13111368
}
13121369
}
13131370
finally
13141371
{
1315-
inMetadata.release();
1372+
if (inMetadata != null)
1373+
inMetadata.release();
13161374
}
13171375

13181376
return routine;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* FB/Java plugin
3+
*
4+
* Distributable under LGPL license.
5+
* You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
6+
*
7+
* This program is distributed in the hope that it will be useful,
8+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
* LGPL License for more details.
11+
*
12+
* This file was created by members of the Firebird development team.
13+
* All individual contributions remain the Copyright (C) of those
14+
* individuals. Contributors to this file are either listed here or
15+
* can be obtained from a git log command.
16+
*
17+
* All rights reserved.
18+
*/
19+
package org.firebirdsql.fbjava.impl;
20+
21+
import java.lang.reflect.InvocationTargetException;
22+
import java.util.List;
23+
24+
import org.firebirdsql.fbjava.impl.FbClientLibrary.IExternalContext;
25+
import org.firebirdsql.fbjava.impl.FbClientLibrary.IExternalTrigger;
26+
import org.firebirdsql.fbjava.impl.FbClientLibrary.IExternalTriggerIntf;
27+
import org.firebirdsql.fbjava.impl.FbClientLibrary.IStatus;
28+
29+
import com.sun.jna.Pointer;
30+
31+
32+
final class ExternalTrigger implements IExternalTriggerIntf
33+
{
34+
private IExternalTrigger wrapper;
35+
private Routine routine;
36+
37+
private ExternalTrigger(Routine routine)
38+
{
39+
this.routine = routine;
40+
}
41+
42+
public static IExternalTrigger create(Routine routine)
43+
{
44+
ExternalTrigger wrapped = new ExternalTrigger(routine);
45+
wrapped.wrapper = JnaUtil.pin(new IExternalTrigger(wrapped));
46+
return wrapped.wrapper;
47+
}
48+
49+
@Override
50+
public void dispose()
51+
{
52+
JnaUtil.unpin(wrapper);
53+
}
54+
55+
@Override
56+
public void getCharSet(IStatus status, IExternalContext context, Pointer name, int nameSize) throws FbException
57+
{
58+
name.setString(0, "UTF8");
59+
}
60+
61+
@Override
62+
public void execute(IStatus status, IExternalContext context, int action, Pointer oldMsg, Pointer newMsg)
63+
throws FbException
64+
{
65+
try
66+
{
67+
List<Parameter> fieldParameters = routine.inputParameters;
68+
int count = fieldParameters.size();
69+
Object[] oldValues = oldMsg == null ? null : new Object[count];
70+
Object[] newValues = newMsg == null ? null : new Object[count];
71+
72+
try (InternalContext internalContext = InternalContext.createTrigger(status, context, routine, action,
73+
(oldMsg == null ? null : new ValuesImpl(oldValues, count)),
74+
(newMsg == null ? null : new ValuesImpl(newValues, count))))
75+
{
76+
if (oldMsg != null)
77+
routine.getFromMessage(status, context, fieldParameters, oldMsg, oldValues);
78+
79+
if (newMsg != null)
80+
routine.getFromMessage(status, context, fieldParameters, newMsg, newValues);
81+
82+
routine.run(status, context, null);
83+
84+
if (newMsg != null)
85+
routine.putInMessage(status, context, fieldParameters, newValues, 0, newMsg);
86+
}
87+
}
88+
catch (InvocationTargetException e)
89+
{
90+
FbException.rethrow(e.getCause());
91+
}
92+
catch (Throwable t)
93+
{
94+
FbException.rethrow(t);
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)