@@ -194,6 +194,11 @@ private LeakedConnectionException() {
194
194
*/
195
195
private final ConnectionOptions options ;
196
196
197
+ enum Caller {
198
+ APPLICATION ,
199
+ TRANSACTION_RUNNER ,
200
+ }
201
+
197
202
/** The supported batch modes. */
198
203
enum BatchMode {
199
204
NONE ,
@@ -267,6 +272,9 @@ static UnitOfWorkType of(TransactionMode transactionMode) {
267
272
*/
268
273
private boolean transactionBeginMarked = false ;
269
274
275
+ /** This field is set to true when a transaction runner is active for this connection. */
276
+ private boolean transactionRunnerActive = false ;
277
+
270
278
private BatchMode batchMode ;
271
279
private UnitOfWorkType unitOfWorkType ;
272
280
private final Stack <UnitOfWork > transactionStack = new Stack <>();
@@ -1164,16 +1172,19 @@ public void onFailure() {
1164
1172
1165
1173
@ Override
1166
1174
public void commit () {
1167
- get (commitAsync (CallType .SYNC ));
1175
+ get (commitAsync (CallType .SYNC , Caller . APPLICATION ));
1168
1176
}
1169
1177
1170
1178
@ Override
1171
1179
public ApiFuture <Void > commitAsync () {
1172
- return commitAsync (CallType .ASYNC );
1180
+ return commitAsync (CallType .ASYNC , Caller . APPLICATION );
1173
1181
}
1174
1182
1175
- private ApiFuture <Void > commitAsync (CallType callType ) {
1183
+ ApiFuture <Void > commitAsync (CallType callType , Caller caller ) {
1176
1184
ConnectionPreconditions .checkState (!isClosed (), CLOSED_ERROR_MSG );
1185
+ ConnectionPreconditions .checkState (
1186
+ !transactionRunnerActive || caller == Caller .TRANSACTION_RUNNER ,
1187
+ "Cannot call commit when a transaction runner is active" );
1177
1188
maybeAutoCommitOrFlushCurrentUnitOfWork (COMMIT_STATEMENT .getType (), COMMIT_STATEMENT );
1178
1189
return endCurrentTransactionAsync (callType , commit , COMMIT_STATEMENT );
1179
1190
}
@@ -1201,16 +1212,19 @@ public void onFailure() {
1201
1212
1202
1213
@ Override
1203
1214
public void rollback () {
1204
- get (rollbackAsync (CallType .SYNC ));
1215
+ get (rollbackAsync (CallType .SYNC , Caller . APPLICATION ));
1205
1216
}
1206
1217
1207
1218
@ Override
1208
1219
public ApiFuture <Void > rollbackAsync () {
1209
- return rollbackAsync (CallType .ASYNC );
1220
+ return rollbackAsync (CallType .ASYNC , Caller . APPLICATION );
1210
1221
}
1211
1222
1212
- private ApiFuture <Void > rollbackAsync (CallType callType ) {
1223
+ ApiFuture <Void > rollbackAsync (CallType callType , Caller caller ) {
1213
1224
ConnectionPreconditions .checkState (!isClosed (), CLOSED_ERROR_MSG );
1225
+ ConnectionPreconditions .checkState (
1226
+ !transactionRunnerActive || caller == Caller .TRANSACTION_RUNNER ,
1227
+ "Cannot call rollback when a transaction runner is active" );
1214
1228
maybeAutoCommitOrFlushCurrentUnitOfWork (ROLLBACK_STATEMENT .getType (), ROLLBACK_STATEMENT );
1215
1229
return endCurrentTransactionAsync (callType , rollback , ROLLBACK_STATEMENT );
1216
1230
}
@@ -1243,6 +1257,27 @@ private ApiFuture<Void> endCurrentTransactionAsync(
1243
1257
return res ;
1244
1258
}
1245
1259
1260
+ @ Override
1261
+ public <T > T runTransaction (TransactionCallable <T > callable ) {
1262
+ ConnectionPreconditions .checkState (!isClosed (), CLOSED_ERROR_MSG );
1263
+ ConnectionPreconditions .checkState (!isBatchActive (), "Cannot run transaction while in a batch" );
1264
+ ConnectionPreconditions .checkState (
1265
+ !isTransactionStarted (), "Cannot run transaction when a transaction is already active" );
1266
+ ConnectionPreconditions .checkState (
1267
+ !transactionRunnerActive , "A transaction runner is already active for this connection" );
1268
+ this .transactionRunnerActive = true ;
1269
+ try {
1270
+ return new TransactionRunnerImpl (this ).run (callable );
1271
+ } finally {
1272
+ this .transactionRunnerActive = false ;
1273
+ }
1274
+ }
1275
+
1276
+ void resetForRetry (UnitOfWork retryUnitOfWork ) {
1277
+ retryUnitOfWork .resetForRetry ();
1278
+ this .currentUnitOfWork = retryUnitOfWork ;
1279
+ }
1280
+
1246
1281
@ Override
1247
1282
public SavepointSupport getSavepointSupport () {
1248
1283
return getConnectionPropertyValue (SAVEPOINT_SUPPORT );
@@ -2000,7 +2035,7 @@ private UnitOfWork maybeStartAutoDmlBatch(UnitOfWork transaction) {
2000
2035
return transaction ;
2001
2036
}
2002
2037
2003
- private UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork () {
2038
+ UnitOfWork getCurrentUnitOfWorkOrStartNewUnitOfWork () {
2004
2039
return getCurrentUnitOfWorkOrStartNewUnitOfWork (
2005
2040
StatementType .UNKNOWN , /* parsedStatement = */ null , /* internalMetadataQuery = */ false );
2006
2041
}
0 commit comments