+ /*
+ * There is at least one savepoint, so proceed.
+ */
+ case TBLOCK_SUBINPROGRESS:
+ case TBLOCK_SUBABORT:
+ break;
+
+ /* These cases are invalid. */
+ case TBLOCK_DEFAULT:
+ case TBLOCK_STARTED:
+ case TBLOCK_BEGIN:
+ case TBLOCK_SUBBEGIN:
+ case TBLOCK_END:
+ case TBLOCK_SUBEND:
+ case TBLOCK_ABORT_END:
+ case TBLOCK_SUBABORT_END:
+ case TBLOCK_ABORT_PENDING:
+ case TBLOCK_SUBABORT_PENDING:
+ case TBLOCK_SUBRESTART:
+ case TBLOCK_SUBABORT_RESTART:
+ case TBLOCK_PREPARE:
+ elog(FATAL, "RollbackToSavepoint: unexpected state %s",
+ BlockStateAsString(s->blockState));
+ break;
+ }
+
+ foreach(cell, options)
+ {
+ DefElem *elem = lfirst(cell);
+
+ if (strcmp(elem->defname, "savepoint_name") == 0)
+ name = strVal(elem->arg);
+ }
+
+ Assert(PointerIsValid(name));
+
+ for (target = s; PointerIsValid(target); target = target->parent)
+ {
+ if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
+ break;
+ }
+
+ if (!PointerIsValid(target))
+ ereport(ERROR,
+ (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
+ errmsg("no such savepoint")));
+
+ /* disallow crossing savepoint level boundaries */
+ if (target->savepointLevel != s->savepointLevel)
+ ereport(ERROR,
+ (errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
+ errmsg("no such savepoint")));
+
+ /*
+ * Mark "abort pending" all subtransactions up to the target
+ * subtransaction. The actual aborts will happen when control gets to
+ * CommitTransactionCommand.
+ */
+ xact = CurrentTransactionState;
+ for (;;)
+ {
+ if (xact == target)
+ break;
+ if (xact->blockState == TBLOCK_SUBINPROGRESS)
+ xact->blockState = TBLOCK_SUBABORT_PENDING;
+ else if (xact->blockState == TBLOCK_SUBABORT)
+ xact->blockState = TBLOCK_SUBABORT_END;
+ else
+ elog(FATAL, "RollbackToSavepoint: unexpected state %s",
+ BlockStateAsString(xact->blockState));
+ xact = xact->parent;
+ Assert(PointerIsValid(xact));
+ }
+
+ /* And mark the target as "restart pending" */
+ if (xact->blockState == TBLOCK_SUBINPROGRESS)
+ xact->blockState = TBLOCK_SUBRESTART;
+ else if (xact->blockState == TBLOCK_SUBABORT)
+ xact->blockState = TBLOCK_SUBABORT_RESTART;
+ else
+ elog(FATAL, "RollbackToSavepoint: unexpected state %s",
+ BlockStateAsString(xact->blockState));
+}
+
+/*
+ * BeginInternalSubTransaction
+ * This is the same as DefineSavepoint except it allows TBLOCK_STARTED,
+ * TBLOCK_END, and TBLOCK_PREPARE states, and therefore it can safely be
+ * used in functions that might be called when not inside a BEGIN block
+ * or when running deferred triggers at COMMIT/PREPARE time. Also, it
+ * automatically does CommitTransactionCommand/StartTransactionCommand
+ * instead of expecting the caller to do it.
+ */
+void
+BeginInternalSubTransaction(char *name)
+{
+ TransactionState s = CurrentTransactionState;
+
+ switch (s->blockState)
+ {
+ case TBLOCK_STARTED:
+ case TBLOCK_INPROGRESS:
+ case TBLOCK_END:
+ case TBLOCK_PREPARE:
+ case TBLOCK_SUBINPROGRESS:
+ /* Normal subtransaction start */
+ PushTransaction();
+ s = CurrentTransactionState; /* changed by push */
+
+ /*
+ * Savepoint names, like the TransactionState block itself, live
+ * in TopTransactionContext.
+ */
+ if (name)
+ s->name = MemoryContextStrdup(TopTransactionContext, name);
+ break;
+
+ /* These cases are invalid. */
+ case TBLOCK_DEFAULT:
+ case TBLOCK_BEGIN:
+ case TBLOCK_SUBBEGIN:
+ case TBLOCK_SUBEND:
+ case TBLOCK_ABORT:
+ case TBLOCK_SUBABORT:
+ case TBLOCK_ABORT_END:
+ case TBLOCK_SUBABORT_END:
+ case TBLOCK_ABORT_PENDING:
+ case TBLOCK_SUBABORT_PENDING:
+ case TBLOCK_SUBRESTART:
+ case TBLOCK_SUBABORT_RESTART:
+ elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
+ BlockStateAsString(s->blockState));
+ break;
+ }
+
+ CommitTransactionCommand();
+ StartTransactionCommand();
+}
+
+/*
+ * ReleaseCurrentSubTransaction
+ *
+ * RELEASE (ie, commit) the innermost subtransaction, regardless of its
+ * savepoint name (if any).
+ * NB: do NOT use CommitTransactionCommand/StartTransactionCommand with this.
+ */
+void
+ReleaseCurrentSubTransaction(void)
+{
+ TransactionState s = CurrentTransactionState;
+
+ if (s->blockState != TBLOCK_SUBINPROGRESS)
+ elog(ERROR, "ReleaseCurrentSubTransaction: unexpected state %s",
+ BlockStateAsString(s->blockState));
+ Assert(s->state == TRANS_INPROGRESS);
+ MemoryContextSwitchTo(CurTransactionContext);
+ CommitSubTransaction();
+ s = CurrentTransactionState; /* changed by pop */
+ Assert(s->state == TRANS_INPROGRESS);
+}
+
+/*
+ * RollbackAndReleaseCurrentSubTransaction
+ *
+ * ROLLBACK and RELEASE (ie, abort) the innermost subtransaction, regardless
+ * of its savepoint name (if any).
+ * NB: do NOT use CommitTransactionCommand/StartTransactionCommand with this.
+ */
+void
+RollbackAndReleaseCurrentSubTransaction(void)
+{
+ TransactionState s = CurrentTransactionState;
+
+ switch (s->blockState)
+ {
+ /* Must be in a subtransaction */