|
77 | 77 | #include <unistd.h>
|
78 | 78 |
|
79 | 79 | #include "access/commit_ts.h"
|
| 80 | +#include "access/clog.h" |
80 | 81 | #include "access/htup_details.h"
|
81 | 82 | #include "access/subtrans.h"
|
82 | 83 | #include "access/transam.h"
|
@@ -2577,106 +2578,182 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
|
2577 | 2578 | return;
|
2578 | 2579 | }
|
2579 | 2580 |
|
| 2581 | +/* |
| 2582 | + * pg_precommit_prepared |
| 2583 | + * |
| 2584 | + * Alter state of prepared transaction. This function can be used to implement |
| 2585 | + * 3PC at user level. |
| 2586 | + */ |
2580 | 2587 | Datum
|
2581 |
| -pg_prepared_xact_status(PG_FUNCTION_ARGS) |
| 2588 | +pg_precommit_prepared(PG_FUNCTION_ARGS) |
| 2589 | +{ |
| 2590 | + char const* gid = text_to_cstring(PG_GETARG_TEXT_PP(0)); |
| 2591 | + char const* state = text_to_cstring(PG_GETARG_TEXT_PP(1)); |
| 2592 | + SetPreparedTransactionState(gid, state); |
| 2593 | + PG_RETURN_VOID(); |
| 2594 | +} |
| 2595 | + |
| 2596 | +XidStatus |
| 2597 | +GetLoggedPreparedXactState(char const *gid) |
2582 | 2598 | {
|
2583 |
| - char const* gid = text_to_cstring(PG_GETARG_TEXT_PP(0)); |
2584 | 2599 | XLogRecord *record;
|
2585 | 2600 | XLogReaderState *xlogreader;
|
2586 |
| - char *errormsg; |
| 2601 | + char *errormsg; |
2587 | 2602 | XLogRecPtr start_lsn;
|
2588 | 2603 | XLogRecPtr lsn;
|
2589 |
| - char const* xact_status = "unknown"; |
2590 |
| - bool done = false; |
| 2604 | + XidStatus xact_status = TRANSACTION_STATUS_UNKNOWN; |
2591 | 2605 | TimeLineID timeline;
|
2592 | 2606 | TransactionId xid = InvalidTransactionId;
|
2593 |
| - XLogRecPtr end_lsn = GetFlushRecPtr(); |
| 2607 | + XLogRecPtr end_wal_lsn = GetFlushRecPtr(); |
| 2608 | + XLogRecPtr end_lsn = end_wal_lsn; |
2594 | 2609 |
|
2595 | 2610 | GetOldestRestartPoint(&start_lsn, &timeline);
|
2596 |
| - |
2597 |
| - xlogreader = XLogReaderAllocate(&read_local_xlog_page, NULL); |
2598 |
| - if (!xlogreader) |
2599 |
| - ereport(ERROR, |
2600 |
| - (errcode(ERRCODE_OUT_OF_MEMORY), |
2601 |
| - errmsg("out of memory"), |
2602 |
| - errdetail("Failed while allocating a WAL reading processor."))); |
2603 |
| - while (true) |
| 2611 | + if (start_lsn != InvalidXLogRecPtr) |
2604 | 2612 | {
|
2605 |
| - lsn = start_lsn; |
2606 |
| - do |
| 2613 | + xlogreader = XLogReaderAllocate(&read_local_xlog_page, NULL); |
| 2614 | + if (!xlogreader) |
| 2615 | + ereport(ERROR, |
| 2616 | + (errcode(ERRCODE_OUT_OF_MEMORY), |
| 2617 | + errmsg("out of memory"), |
| 2618 | + errdetail("Failed while allocating a WAL reading processor."))); |
| 2619 | + |
| 2620 | + PG_TRY(); |
2607 | 2621 | {
|
2608 |
| - record = XLogReadRecord(xlogreader, lsn, &errormsg); |
2609 |
| - if (record == NULL) |
2610 |
| - break; |
2611 |
| - lsn = InvalidXLogRecPtr; /* continue after the record */ |
2612 |
| - if (XLogRecGetRmid(xlogreader) == RM_XACT_ID) |
| 2622 | + /* |
| 2623 | + * If checkpoint interval is large enough it may be more efficient |
| 2624 | + * to start scanning from last WAL segment |
| 2625 | + */ |
| 2626 | + XLogSegNoOffsetToRecPtr(end_lsn / XLogSegSize, 0, lsn); |
| 2627 | + lsn = XLogFindNextRecord(xlogreader, lsn); |
| 2628 | + if (lsn != InvalidXLogRecPtr && lsn > start_lsn) |
| 2629 | + start_lsn = lsn; |
| 2630 | + |
| 2631 | + while (start_lsn != InvalidXLogRecPtr) |
2613 | 2632 | {
|
2614 |
| - uint32 info = XLogRecGetInfo(xlogreader); |
2615 |
| - switch (info & XLOG_XACT_OPMASK) |
| 2633 | + lsn = start_lsn; |
| 2634 | + do |
2616 | 2635 | {
|
2617 |
| - case XLOG_XACT_PREPARE: |
| 2636 | + record = XLogReadRecord(xlogreader, lsn, &errormsg); |
| 2637 | + if (record == NULL) |
| 2638 | + break; |
| 2639 | + lsn = InvalidXLogRecPtr; /* continue after the record */ |
| 2640 | + if (XLogRecGetRmid(xlogreader) == RM_XACT_ID) |
2618 | 2641 | {
|
2619 |
| - TwoPhaseFileHeader *hdr = (TwoPhaseFileHeader *)XLogRecGetData(xlogreader); |
2620 |
| - char* xact_gid = (char*)hdr + MAXALIGN(sizeof(TwoPhaseFileHeader)); |
2621 |
| - if (strcmp(xact_gid, gid) == 0) |
| 2642 | + uint32 info = XLogRecGetInfo(xlogreader); |
| 2643 | + switch (info & XLOG_XACT_OPMASK) |
| 2644 | + { |
| 2645 | + case XLOG_XACT_PREPARE: |
2622 | 2646 | {
|
2623 |
| - xid = hdr->xid; |
2624 |
| - xact_status = "prepared"; |
| 2647 | + TwoPhaseFileHeader *hdr = (TwoPhaseFileHeader *)XLogRecGetData(xlogreader); |
| 2648 | + char *xact_gid = (char *)hdr + MAXALIGN(sizeof(TwoPhaseFileHeader)); |
| 2649 | + if (strcmp(xact_gid, gid) == 0) |
| 2650 | + { |
| 2651 | + xid = hdr->xid; |
| 2652 | + /* |
| 2653 | + * continue traversal until we find commit/rollback |
| 2654 | + * prepared or end of WAL |
| 2655 | + */ |
| 2656 | + end_lsn = end_wal_lsn; |
| 2657 | + xact_status = TRANSACTION_STATUS_IN_PROGRESS; |
| 2658 | + } |
| 2659 | + break; |
2625 | 2660 | }
|
2626 |
| - break; |
2627 |
| - } |
2628 |
| - case XLOG_XACT_COMMIT_PREPARED: |
2629 |
| - { |
2630 |
| - xl_xact_commit *xlrec; |
2631 |
| - xl_xact_parsed_commit parsed; |
2632 |
| - |
2633 |
| - xlrec = (xl_xact_commit *) XLogRecGetData(xlogreader); |
2634 |
| - ParseCommitRecord(info, xlrec, &parsed); |
2635 |
| - if (xid == parsed.twophase_xid) |
| 2661 | + case XLOG_XACT_COMMIT_PREPARED: |
2636 | 2662 | {
|
2637 |
| - Assert(TransactionIdIsValid(xid)); |
2638 |
| - xact_status = "committed"; |
2639 |
| - done = true; |
| 2663 | + xl_xact_commit *xlrec; |
| 2664 | + xl_xact_parsed_commit parsed; |
| 2665 | + |
| 2666 | + xlrec = (xl_xact_commit *)XLogRecGetData(xlogreader); |
| 2667 | + ParseCommitRecord(info, xlrec, &parsed); |
| 2668 | + if (xid == parsed.twophase_xid) |
| 2669 | + { |
| 2670 | + Assert(TransactionIdIsValid(xid)); |
| 2671 | + xact_status = TRANSACTION_STATUS_COMMITTED; |
| 2672 | + } |
| 2673 | + break; |
2640 | 2674 | }
|
2641 |
| - break; |
2642 |
| - } |
2643 |
| - case XLOG_XACT_ABORT_PREPARED: |
2644 |
| - { |
2645 |
| - xl_xact_abort *xlrec; |
2646 |
| - xl_xact_parsed_abort parsed; |
2647 | 2675 |
|
2648 |
| - xlrec = (xl_xact_abort *) XLogRecGetData(xlogreader); |
2649 |
| - ParseAbortRecord(info, xlrec, &parsed); |
2650 |
| - if (xid == parsed.twophase_xid) |
| 2676 | + case XLOG_XACT_ABORT_PREPARED: |
2651 | 2677 | {
|
2652 |
| - Assert(TransactionIdIsValid(xid)); |
2653 |
| - xact_status = "aborted"; |
2654 |
| - done = true; |
| 2678 | + xl_xact_abort *xlrec; |
| 2679 | + xl_xact_parsed_abort parsed; |
| 2680 | + |
| 2681 | + xlrec = (xl_xact_abort *)XLogRecGetData(xlogreader); |
| 2682 | + ParseAbortRecord(info, xlrec, &parsed); |
| 2683 | + if (xid == parsed.twophase_xid) |
| 2684 | + { |
| 2685 | + Assert(TransactionIdIsValid(xid)); |
| 2686 | + xact_status = TRANSACTION_STATUS_ABORTED; |
| 2687 | + } |
| 2688 | + break; |
| 2689 | + } |
| 2690 | + default: |
| 2691 | + break; |
2655 | 2692 | }
|
2656 |
| - break; |
2657 | 2693 | }
|
2658 |
| - default: |
2659 |
| - break; |
| 2694 | + } while ((xact_status == TRANSACTION_STATUS_UNKNOWN |
| 2695 | + || xact_status == TRANSACTION_STATUS_IN_PROGRESS) |
| 2696 | + && xlogreader->EndRecPtr < end_lsn); |
| 2697 | + |
| 2698 | + if (xact_status != TRANSACTION_STATUS_UNKNOWN) |
| 2699 | + break; |
| 2700 | + |
| 2701 | + end_lsn = start_lsn; |
| 2702 | + /* Get LSN of first record in the current segment */ |
| 2703 | + XLogSegNoOffsetToRecPtr(end_lsn / XLogSegSize, 0, start_lsn); |
| 2704 | + start_lsn = XLogFindNextRecord(xlogreader, start_lsn); |
| 2705 | + /* |
| 2706 | + * If we didn't start from the beginning of segment, then restart |
| 2707 | + * scan from the beginning of segment |
| 2708 | + */ |
| 2709 | + if (start_lsn == end_lsn) |
| 2710 | + { |
| 2711 | + /* ... otherwise check if it is not the first segment */ |
| 2712 | + if (end_lsn <= XLogSegSize * 2) |
| 2713 | + break; |
| 2714 | + /* ... and if not: shift to previous segment */ |
| 2715 | + XLogSegNoOffsetToRecPtr(end_lsn / XLogSegSize - 1, 0, start_lsn); |
| 2716 | + /* ... and check that pending segment is actually exists */ |
| 2717 | + if (start_lsn / XLogSegSize <= XLogGetLastRemovedSegno()) |
| 2718 | + break; |
| 2719 | + start_lsn = XLogFindNextRecord(xlogreader, start_lsn); |
2660 | 2720 | }
|
2661 | 2721 | }
|
2662 |
| - } while (!done && xlogreader->EndRecPtr < end_lsn); |
2663 |
| - |
2664 |
| - if (done) |
2665 |
| - break; |
2666 |
| - |
2667 |
| - end_lsn = start_lsn; |
2668 |
| - XLogSegNoOffsetToRecPtr(end_lsn/XLogSegSize, 0, start_lsn); |
2669 |
| - start_lsn = XLogFindNextRecord(xlogreader, start_lsn); |
2670 |
| - if (start_lsn == end_lsn) |
| 2722 | + } |
| 2723 | + PG_CATCH(); |
2671 | 2724 | {
|
2672 |
| - if (end_lsn <= XLogSegSize) |
2673 |
| - break; |
2674 |
| - XLogSegNoOffsetToRecPtr(lsn/XLogSegSize-1, 0, start_lsn); |
2675 |
| - start_lsn = XLogFindNextRecord(xlogreader, start_lsn); |
| 2725 | + /* Catch access to unexisted WAL segment */ |
| 2726 | + FlushErrorState(); |
2676 | 2727 | }
|
| 2728 | + PG_END_TRY(); |
| 2729 | + XLogReaderFree(xlogreader); |
| 2730 | + } |
| 2731 | + |
| 2732 | + return xact_status; |
| 2733 | +} |
| 2734 | + |
| 2735 | +Datum |
| 2736 | +pg_prepared_xact_status(PG_FUNCTION_ARGS) |
| 2737 | +{ |
| 2738 | + char const* gid = text_to_cstring(PG_GETARG_TEXT_PP(0)); |
| 2739 | + char const* xact_status; |
| 2740 | + |
| 2741 | + switch (GetLoggedPreparedXactState(gid)) |
| 2742 | + { |
| 2743 | + case TRANSACTION_STATUS_IN_PROGRESS: |
| 2744 | + xact_status = "prepared"; |
| 2745 | + break; |
| 2746 | + case TRANSACTION_STATUS_COMMITTED: |
| 2747 | + xact_status = "committed"; |
| 2748 | + break; |
| 2749 | + case TRANSACTION_STATUS_ABORTED: |
| 2750 | + xact_status = "aborted"; |
| 2751 | + break; |
| 2752 | + case TRANSACTION_STATUS_UNKNOWN: |
| 2753 | + xact_status = "unknown"; |
| 2754 | + break; |
2677 | 2755 | }
|
2678 | 2756 |
|
2679 |
| - XLogReaderFree(xlogreader); |
2680 | 2757 | PG_RETURN_TEXT_P(cstring_to_text(xact_status));
|
2681 | 2758 | }
|
2682 | 2759 |
|
|
0 commit comments