@@ -145,6 +145,7 @@ typedef enum TBlockState
145
145
/* transaction block states */
146
146
TBLOCK_BEGIN , /* starting transaction block */
147
147
TBLOCK_INPROGRESS , /* live transaction */
148
+ TBLOCK_IMPLICIT_INPROGRESS , /* live transaction after implicit BEGIN */
148
149
TBLOCK_PARALLEL_INPROGRESS , /* live transaction inside parallel worker */
149
150
TBLOCK_END , /* COMMIT received */
150
151
TBLOCK_ABORT , /* failed xact, awaiting ROLLBACK */
@@ -2700,6 +2701,7 @@ StartTransactionCommand(void)
2700
2701
* previous CommitTransactionCommand.)
2701
2702
*/
2702
2703
case TBLOCK_INPROGRESS :
2704
+ case TBLOCK_IMPLICIT_INPROGRESS :
2703
2705
case TBLOCK_SUBINPROGRESS :
2704
2706
break ;
2705
2707
@@ -2790,6 +2792,7 @@ CommitTransactionCommand(void)
2790
2792
* counter and return.
2791
2793
*/
2792
2794
case TBLOCK_INPROGRESS :
2795
+ case TBLOCK_IMPLICIT_INPROGRESS :
2793
2796
case TBLOCK_SUBINPROGRESS :
2794
2797
CommandCounterIncrement ();
2795
2798
break ;
@@ -3014,10 +3017,12 @@ AbortCurrentTransaction(void)
3014
3017
break ;
3015
3018
3016
3019
/*
3017
- * if we aren't in a transaction block, we just do the basic abort
3018
- * & cleanup transaction.
3020
+ * If we aren't in a transaction block, we just do the basic abort
3021
+ * & cleanup transaction. For this purpose, we treat an implicit
3022
+ * transaction block as if it were a simple statement.
3019
3023
*/
3020
3024
case TBLOCK_STARTED :
3025
+ case TBLOCK_IMPLICIT_INPROGRESS :
3021
3026
AbortTransaction ();
3022
3027
CleanupTransaction ();
3023
3028
s -> blockState = TBLOCK_DEFAULT ;
@@ -3148,9 +3153,8 @@ AbortCurrentTransaction(void)
3148
3153
* completes). Subtransactions are verboten too.
3149
3154
*
3150
3155
* isTopLevel: passed down from ProcessUtility to determine whether we are
3151
- * inside a function or multi-query querystring. (We will always fail if
3152
- * this is false, but it's convenient to centralize the check here instead of
3153
- * making callers do it.)
3156
+ * inside a function. (We will always fail if this is false, but it's
3157
+ * convenient to centralize the check here instead of making callers do it.)
3154
3158
* stmtType: statement type name, for error messages.
3155
3159
*/
3156
3160
void
@@ -3183,8 +3187,7 @@ PreventTransactionChain(bool isTopLevel, const char *stmtType)
3183
3187
ereport (ERROR ,
3184
3188
(errcode (ERRCODE_ACTIVE_SQL_TRANSACTION ),
3185
3189
/* translator: %s represents an SQL statement name */
3186
- errmsg ("%s cannot be executed from a function or multi-command string" ,
3187
- stmtType )));
3190
+ errmsg ("%s cannot be executed from a function" , stmtType )));
3188
3191
3189
3192
/* If we got past IsTransactionBlock test, should be in default state */
3190
3193
if (CurrentTransactionState -> blockState != TBLOCK_DEFAULT &&
@@ -3428,6 +3431,15 @@ BeginTransactionBlock(void)
3428
3431
s -> blockState = TBLOCK_BEGIN ;
3429
3432
break ;
3430
3433
3434
+ /*
3435
+ * BEGIN converts an implicit transaction block to a regular one.
3436
+ * (Note that we allow this even if we've already done some
3437
+ * commands, which is a bit odd but matches historical practice.)
3438
+ */
3439
+ case TBLOCK_IMPLICIT_INPROGRESS :
3440
+ s -> blockState = TBLOCK_BEGIN ;
3441
+ break ;
3442
+
3431
3443
/*
3432
3444
* Already a transaction block in progress.
3433
3445
*/
@@ -3503,7 +3515,8 @@ PrepareTransactionBlock(char *gid)
3503
3515
* ignore case where we are not in a transaction;
3504
3516
* EndTransactionBlock already issued a warning.
3505
3517
*/
3506
- Assert (s -> blockState == TBLOCK_STARTED );
3518
+ Assert (s -> blockState == TBLOCK_STARTED ||
3519
+ s -> blockState == TBLOCK_IMPLICIT_INPROGRESS );
3507
3520
/* Don't send back a PREPARE result tag... */
3508
3521
result = false;
3509
3522
}
@@ -3541,6 +3554,18 @@ EndTransactionBlock(void)
3541
3554
result = true;
3542
3555
break ;
3543
3556
3557
+ /*
3558
+ * In an implicit transaction block, commit, but issue a warning
3559
+ * because there was no explicit BEGIN before this.
3560
+ */
3561
+ case TBLOCK_IMPLICIT_INPROGRESS :
3562
+ ereport (WARNING ,
3563
+ (errcode (ERRCODE_NO_ACTIVE_SQL_TRANSACTION ),
3564
+ errmsg ("there is no transaction in progress" )));
3565
+ s -> blockState = TBLOCK_END ;
3566
+ result = true;
3567
+ break ;
3568
+
3544
3569
/*
3545
3570
* We are in a failed transaction block. Tell
3546
3571
* CommitTransactionCommand it's time to exit the block.
@@ -3705,8 +3730,14 @@ UserAbortTransactionBlock(void)
3705
3730
* WARNING and go to abort state. The upcoming call to
3706
3731
* CommitTransactionCommand() will then put us back into the
3707
3732
* default state.
3733
+ *
3734
+ * We do the same thing with ABORT inside an implicit transaction,
3735
+ * although in this case we might be rolling back actual database
3736
+ * state changes. (It's debatable whether we should issue a
3737
+ * WARNING in this case, but we have done so historically.)
3708
3738
*/
3709
3739
case TBLOCK_STARTED :
3740
+ case TBLOCK_IMPLICIT_INPROGRESS :
3710
3741
ereport (WARNING ,
3711
3742
(errcode (ERRCODE_NO_ACTIVE_SQL_TRANSACTION ),
3712
3743
errmsg ("there is no transaction in progress" )));
@@ -3743,6 +3774,58 @@ UserAbortTransactionBlock(void)
3743
3774
}
3744
3775
}
3745
3776
3777
+ /*
3778
+ * BeginImplicitTransactionBlock
3779
+ * Start an implicit transaction block if we're not already in one.
3780
+ *
3781
+ * Unlike BeginTransactionBlock, this is called directly from the main loop
3782
+ * in postgres.c, not within a Portal. So we can just change blockState
3783
+ * without a lot of ceremony. We do not expect caller to do
3784
+ * CommitTransactionCommand/StartTransactionCommand.
3785
+ */
3786
+ void
3787
+ BeginImplicitTransactionBlock (void )
3788
+ {
3789
+ TransactionState s = CurrentTransactionState ;
3790
+
3791
+ /*
3792
+ * If we are in STARTED state (that is, no transaction block is open),
3793
+ * switch to IMPLICIT_INPROGRESS state, creating an implicit transaction
3794
+ * block.
3795
+ *
3796
+ * For caller convenience, we consider all other transaction states as
3797
+ * legal here; otherwise the caller would need its own state check, which
3798
+ * seems rather pointless.
3799
+ */
3800
+ if (s -> blockState == TBLOCK_STARTED )
3801
+ s -> blockState = TBLOCK_IMPLICIT_INPROGRESS ;
3802
+ }
3803
+
3804
+ /*
3805
+ * EndImplicitTransactionBlock
3806
+ * End an implicit transaction block, if we're in one.
3807
+ *
3808
+ * Like EndTransactionBlock, we just make any needed blockState change here.
3809
+ * The real work will be done in the upcoming CommitTransactionCommand().
3810
+ */
3811
+ void
3812
+ EndImplicitTransactionBlock (void )
3813
+ {
3814
+ TransactionState s = CurrentTransactionState ;
3815
+
3816
+ /*
3817
+ * If we are in IMPLICIT_INPROGRESS state, switch back to STARTED state,
3818
+ * allowing CommitTransactionCommand to commit whatever happened during
3819
+ * the implicit transaction block as though it were a single statement.
3820
+ *
3821
+ * For caller convenience, we consider all other transaction states as
3822
+ * legal here; otherwise the caller would need its own state check, which
3823
+ * seems rather pointless.
3824
+ */
3825
+ if (s -> blockState == TBLOCK_IMPLICIT_INPROGRESS )
3826
+ s -> blockState = TBLOCK_STARTED ;
3827
+ }
3828
+
3746
3829
/*
3747
3830
* DefineSavepoint
3748
3831
* This executes a SAVEPOINT command.
@@ -3780,6 +3863,28 @@ DefineSavepoint(char *name)
3780
3863
s -> name = MemoryContextStrdup (TopTransactionContext , name );
3781
3864
break ;
3782
3865
3866
+ /*
3867
+ * We disallow savepoint commands in implicit transaction blocks.
3868
+ * There would be no great difficulty in allowing them so far as
3869
+ * this module is concerned, but a savepoint seems inconsistent
3870
+ * with exec_simple_query's behavior of abandoning the whole query
3871
+ * string upon error. Also, the point of an implicit transaction
3872
+ * block (as opposed to a regular one) is to automatically close
3873
+ * after an error, so it's hard to see how a savepoint would fit
3874
+ * into that.
3875
+ *
3876
+ * The error messages for this are phrased as if there were no
3877
+ * active transaction block at all, which is historical but
3878
+ * perhaps could be improved.
3879
+ */
3880
+ case TBLOCK_IMPLICIT_INPROGRESS :
3881
+ ereport (ERROR ,
3882
+ (errcode (ERRCODE_NO_ACTIVE_SQL_TRANSACTION ),
3883
+ /* translator: %s represents an SQL statement name */
3884
+ errmsg ("%s can only be used in transaction blocks" ,
3885
+ "SAVEPOINT" )));
3886
+ break ;
3887
+
3783
3888
/* These cases are invalid. */
3784
3889
case TBLOCK_DEFAULT :
3785
3890
case TBLOCK_STARTED :
@@ -3834,15 +3939,23 @@ ReleaseSavepoint(List *options)
3834
3939
switch (s -> blockState )
3835
3940
{
3836
3941
/*
3837
- * We can't rollback to a savepoint if there is no savepoint
3838
- * defined.
3942
+ * We can't release a savepoint if there is no savepoint defined.
3839
3943
*/
3840
3944
case TBLOCK_INPROGRESS :
3841
3945
ereport (ERROR ,
3842
3946
(errcode (ERRCODE_S_E_INVALID_SPECIFICATION ),
3843
3947
errmsg ("no such savepoint" )));
3844
3948
break ;
3845
3949
3950
+ case TBLOCK_IMPLICIT_INPROGRESS :
3951
+ /* See comment about implicit transactions in DefineSavepoint */
3952
+ ereport (ERROR ,
3953
+ (errcode (ERRCODE_NO_ACTIVE_SQL_TRANSACTION ),
3954
+ /* translator: %s represents an SQL statement name */
3955
+ errmsg ("%s can only be used in transaction blocks" ,
3956
+ "RELEASE SAVEPOINT" )));
3957
+ break ;
3958
+
3846
3959
/*
3847
3960
* We are in a non-aborted subtransaction. This is the only valid
3848
3961
* case.
@@ -3957,6 +4070,15 @@ RollbackToSavepoint(List *options)
3957
4070
errmsg ("no such savepoint" )));
3958
4071
break ;
3959
4072
4073
+ case TBLOCK_IMPLICIT_INPROGRESS :
4074
+ /* See comment about implicit transactions in DefineSavepoint */
4075
+ ereport (ERROR ,
4076
+ (errcode (ERRCODE_NO_ACTIVE_SQL_TRANSACTION ),
4077
+ /* translator: %s represents an SQL statement name */
4078
+ errmsg ("%s can only be used in transaction blocks" ,
4079
+ "ROLLBACK TO SAVEPOINT" )));
4080
+ break ;
4081
+
3960
4082
/*
3961
4083
* There is at least one savepoint, so proceed.
3962
4084
*/
@@ -4046,11 +4168,12 @@ RollbackToSavepoint(List *options)
4046
4168
/*
4047
4169
* BeginInternalSubTransaction
4048
4170
* This is the same as DefineSavepoint except it allows TBLOCK_STARTED,
4049
- * TBLOCK_END, and TBLOCK_PREPARE states, and therefore it can safely be
4050
- * used in functions that might be called when not inside a BEGIN block
4051
- * or when running deferred triggers at COMMIT/PREPARE time. Also, it
4052
- * automatically does CommitTransactionCommand/StartTransactionCommand
4053
- * instead of expecting the caller to do it.
4171
+ * TBLOCK_IMPLICIT_INPROGRESS, TBLOCK_END, and TBLOCK_PREPARE states,
4172
+ * and therefore it can safely be used in functions that might be called
4173
+ * when not inside a BEGIN block or when running deferred triggers at
4174
+ * COMMIT/PREPARE time. Also, it automatically does
4175
+ * CommitTransactionCommand/StartTransactionCommand instead of expecting
4176
+ * the caller to do it.
4054
4177
*/
4055
4178
void
4056
4179
BeginInternalSubTransaction (char * name )
@@ -4076,6 +4199,7 @@ BeginInternalSubTransaction(char *name)
4076
4199
{
4077
4200
case TBLOCK_STARTED :
4078
4201
case TBLOCK_INPROGRESS :
4202
+ case TBLOCK_IMPLICIT_INPROGRESS :
4079
4203
case TBLOCK_END :
4080
4204
case TBLOCK_PREPARE :
4081
4205
case TBLOCK_SUBINPROGRESS :
@@ -4180,6 +4304,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
4180
4304
case TBLOCK_DEFAULT :
4181
4305
case TBLOCK_STARTED :
4182
4306
case TBLOCK_BEGIN :
4307
+ case TBLOCK_IMPLICIT_INPROGRESS :
4183
4308
case TBLOCK_PARALLEL_INPROGRESS :
4184
4309
case TBLOCK_SUBBEGIN :
4185
4310
case TBLOCK_INPROGRESS :
@@ -4211,6 +4336,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
4211
4336
s = CurrentTransactionState ; /* changed by pop */
4212
4337
AssertState (s -> blockState == TBLOCK_SUBINPROGRESS ||
4213
4338
s -> blockState == TBLOCK_INPROGRESS ||
4339
+ s -> blockState == TBLOCK_IMPLICIT_INPROGRESS ||
4214
4340
s -> blockState == TBLOCK_STARTED );
4215
4341
}
4216
4342
@@ -4259,6 +4385,7 @@ AbortOutOfAnyTransaction(void)
4259
4385
case TBLOCK_STARTED :
4260
4386
case TBLOCK_BEGIN :
4261
4387
case TBLOCK_INPROGRESS :
4388
+ case TBLOCK_IMPLICIT_INPROGRESS :
4262
4389
case TBLOCK_PARALLEL_INPROGRESS :
4263
4390
case TBLOCK_END :
4264
4391
case TBLOCK_ABORT_PENDING :
@@ -4369,6 +4496,7 @@ TransactionBlockStatusCode(void)
4369
4496
case TBLOCK_BEGIN :
4370
4497
case TBLOCK_SUBBEGIN :
4371
4498
case TBLOCK_INPROGRESS :
4499
+ case TBLOCK_IMPLICIT_INPROGRESS :
4372
4500
case TBLOCK_PARALLEL_INPROGRESS :
4373
4501
case TBLOCK_SUBINPROGRESS :
4374
4502
case TBLOCK_END :
@@ -5036,6 +5164,8 @@ BlockStateAsString(TBlockState blockState)
5036
5164
return "BEGIN" ;
5037
5165
case TBLOCK_INPROGRESS :
5038
5166
return "INPROGRESS" ;
5167
+ case TBLOCK_IMPLICIT_INPROGRESS :
5168
+ return "IMPLICIT_INPROGRESS" ;
5039
5169
case TBLOCK_PARALLEL_INPROGRESS :
5040
5170
return "PARALLEL_INPROGRESS" ;
5041
5171
case TBLOCK_END :
0 commit comments