@@ -145,6 +145,7 @@ typedef enum TBlockState
145145 /* transaction block states */
146146 TBLOCK_BEGIN , /* starting transaction block */
147147 TBLOCK_INPROGRESS , /* live transaction */
148+ TBLOCK_IMPLICIT_INPROGRESS , /* live transaction after implicit BEGIN */
148149 TBLOCK_PARALLEL_INPROGRESS , /* live transaction inside parallel worker */
149150 TBLOCK_END , /* COMMIT received */
150151 TBLOCK_ABORT , /* failed xact, awaiting ROLLBACK */
@@ -2700,6 +2701,7 @@ StartTransactionCommand(void)
27002701 * previous CommitTransactionCommand.)
27012702 */
27022703 case TBLOCK_INPROGRESS :
2704+ case TBLOCK_IMPLICIT_INPROGRESS :
27032705 case TBLOCK_SUBINPROGRESS :
27042706 break ;
27052707
@@ -2790,6 +2792,7 @@ CommitTransactionCommand(void)
27902792 * counter and return.
27912793 */
27922794 case TBLOCK_INPROGRESS :
2795+ case TBLOCK_IMPLICIT_INPROGRESS :
27932796 case TBLOCK_SUBINPROGRESS :
27942797 CommandCounterIncrement ();
27952798 break ;
@@ -3014,10 +3017,12 @@ AbortCurrentTransaction(void)
30143017 break ;
30153018
30163019 /*
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.
30193023 */
30203024 case TBLOCK_STARTED :
3025+ case TBLOCK_IMPLICIT_INPROGRESS :
30213026 AbortTransaction ();
30223027 CleanupTransaction ();
30233028 s -> blockState = TBLOCK_DEFAULT ;
@@ -3148,9 +3153,8 @@ AbortCurrentTransaction(void)
31483153 * completes). Subtransactions are verboten too.
31493154 *
31503155 * 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.)
31543158 * stmtType: statement type name, for error messages.
31553159 */
31563160void
@@ -3183,8 +3187,7 @@ PreventTransactionChain(bool isTopLevel, const char *stmtType)
31833187 ereport (ERROR ,
31843188 (errcode (ERRCODE_ACTIVE_SQL_TRANSACTION ),
31853189 /* 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 )));
31883191
31893192 /* If we got past IsTransactionBlock test, should be in default state */
31903193 if (CurrentTransactionState -> blockState != TBLOCK_DEFAULT &&
@@ -3428,6 +3431,15 @@ BeginTransactionBlock(void)
34283431 s -> blockState = TBLOCK_BEGIN ;
34293432 break ;
34303433
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+
34313443 /*
34323444 * Already a transaction block in progress.
34333445 */
@@ -3503,7 +3515,8 @@ PrepareTransactionBlock(char *gid)
35033515 * ignore case where we are not in a transaction;
35043516 * EndTransactionBlock already issued a warning.
35053517 */
3506- Assert (s -> blockState == TBLOCK_STARTED );
3518+ Assert (s -> blockState == TBLOCK_STARTED ||
3519+ s -> blockState == TBLOCK_IMPLICIT_INPROGRESS );
35073520 /* Don't send back a PREPARE result tag... */
35083521 result = false;
35093522 }
@@ -3541,6 +3554,18 @@ EndTransactionBlock(void)
35413554 result = true;
35423555 break ;
35433556
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+
35443569 /*
35453570 * We are in a failed transaction block. Tell
35463571 * CommitTransactionCommand it's time to exit the block.
@@ -3705,8 +3730,14 @@ UserAbortTransactionBlock(void)
37053730 * WARNING and go to abort state. The upcoming call to
37063731 * CommitTransactionCommand() will then put us back into the
37073732 * 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.)
37083738 */
37093739 case TBLOCK_STARTED :
3740+ case TBLOCK_IMPLICIT_INPROGRESS :
37103741 ereport (WARNING ,
37113742 (errcode (ERRCODE_NO_ACTIVE_SQL_TRANSACTION ),
37123743 errmsg ("there is no transaction in progress" )));
@@ -3743,6 +3774,58 @@ UserAbortTransactionBlock(void)
37433774 }
37443775}
37453776
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+
37463829/*
37473830 * DefineSavepoint
37483831 * This executes a SAVEPOINT command.
@@ -3780,6 +3863,28 @@ DefineSavepoint(char *name)
37803863 s -> name = MemoryContextStrdup (TopTransactionContext , name );
37813864 break ;
37823865
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+
37833888 /* These cases are invalid. */
37843889 case TBLOCK_DEFAULT :
37853890 case TBLOCK_STARTED :
@@ -3834,15 +3939,23 @@ ReleaseSavepoint(List *options)
38343939 switch (s -> blockState )
38353940 {
38363941 /*
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.
38393943 */
38403944 case TBLOCK_INPROGRESS :
38413945 ereport (ERROR ,
38423946 (errcode (ERRCODE_S_E_INVALID_SPECIFICATION ),
38433947 errmsg ("no such savepoint" )));
38443948 break ;
38453949
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+
38463959 /*
38473960 * We are in a non-aborted subtransaction. This is the only valid
38483961 * case.
@@ -3957,6 +4070,15 @@ RollbackToSavepoint(List *options)
39574070 errmsg ("no such savepoint" )));
39584071 break ;
39594072
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+
39604082 /*
39614083 * There is at least one savepoint, so proceed.
39624084 */
@@ -4046,11 +4168,12 @@ RollbackToSavepoint(List *options)
40464168/*
40474169 * BeginInternalSubTransaction
40484170 * 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.
40544177 */
40554178void
40564179BeginInternalSubTransaction (char * name )
@@ -4076,6 +4199,7 @@ BeginInternalSubTransaction(char *name)
40764199 {
40774200 case TBLOCK_STARTED :
40784201 case TBLOCK_INPROGRESS :
4202+ case TBLOCK_IMPLICIT_INPROGRESS :
40794203 case TBLOCK_END :
40804204 case TBLOCK_PREPARE :
40814205 case TBLOCK_SUBINPROGRESS :
@@ -4180,6 +4304,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
41804304 case TBLOCK_DEFAULT :
41814305 case TBLOCK_STARTED :
41824306 case TBLOCK_BEGIN :
4307+ case TBLOCK_IMPLICIT_INPROGRESS :
41834308 case TBLOCK_PARALLEL_INPROGRESS :
41844309 case TBLOCK_SUBBEGIN :
41854310 case TBLOCK_INPROGRESS :
@@ -4211,6 +4336,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
42114336 s = CurrentTransactionState ; /* changed by pop */
42124337 AssertState (s -> blockState == TBLOCK_SUBINPROGRESS ||
42134338 s -> blockState == TBLOCK_INPROGRESS ||
4339+ s -> blockState == TBLOCK_IMPLICIT_INPROGRESS ||
42144340 s -> blockState == TBLOCK_STARTED );
42154341}
42164342
@@ -4259,6 +4385,7 @@ AbortOutOfAnyTransaction(void)
42594385 case TBLOCK_STARTED :
42604386 case TBLOCK_BEGIN :
42614387 case TBLOCK_INPROGRESS :
4388+ case TBLOCK_IMPLICIT_INPROGRESS :
42624389 case TBLOCK_PARALLEL_INPROGRESS :
42634390 case TBLOCK_END :
42644391 case TBLOCK_ABORT_PENDING :
@@ -4369,6 +4496,7 @@ TransactionBlockStatusCode(void)
43694496 case TBLOCK_BEGIN :
43704497 case TBLOCK_SUBBEGIN :
43714498 case TBLOCK_INPROGRESS :
4499+ case TBLOCK_IMPLICIT_INPROGRESS :
43724500 case TBLOCK_PARALLEL_INPROGRESS :
43734501 case TBLOCK_SUBINPROGRESS :
43744502 case TBLOCK_END :
@@ -5036,6 +5164,8 @@ BlockStateAsString(TBlockState blockState)
50365164 return "BEGIN" ;
50375165 case TBLOCK_INPROGRESS :
50385166 return "INPROGRESS" ;
5167+ case TBLOCK_IMPLICIT_INPROGRESS :
5168+ return "IMPLICIT_INPROGRESS" ;
50395169 case TBLOCK_PARALLEL_INPROGRESS :
50405170 return "PARALLEL_INPROGRESS" ;
50415171 case TBLOCK_END :
0 commit comments