@@ -171,6 +171,7 @@ typedef struct GlobalTransactionData
171
171
bool ondisk ; /* TRUE if prepare state file is on disk */
172
172
bool inredo ; /* TRUE if entry was added via xlog_redo */
173
173
char gid [GIDSIZE ]; /* The GID assigned to the prepared xact */
174
+ char state_3pc [MAX_3PC_STATE_SIZE ]; /* 3PC transaction state */
174
175
} GlobalTransactionData ;
175
176
176
177
/*
@@ -422,6 +423,7 @@ MarkAsPreparing(TransactionId xid, const char *gid,
422
423
TwoPhaseState -> freeGXacts = gxact -> next ;
423
424
424
425
MarkAsPreparingGuts (gxact , xid , gid , prepared_at , owner , databaseid );
426
+ * gxact -> state_3pc = '\0' ;
425
427
426
428
gxact -> ondisk = false;
427
429
@@ -735,7 +737,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
735
737
736
738
/* build tupdesc for result tuples */
737
739
/* this had better match pg_prepared_xacts view in system_views.sql */
738
- tupdesc = CreateTemplateTupleDesc (5 , false);
740
+ tupdesc = CreateTemplateTupleDesc (6 , false);
739
741
TupleDescInitEntry (tupdesc , (AttrNumber ) 1 , "transaction" ,
740
742
XIDOID , -1 , 0 );
741
743
TupleDescInitEntry (tupdesc , (AttrNumber ) 2 , "gid" ,
@@ -746,6 +748,8 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
746
748
OIDOID , -1 , 0 );
747
749
TupleDescInitEntry (tupdesc , (AttrNumber ) 5 , "dbid" ,
748
750
OIDOID , -1 , 0 );
751
+ TupleDescInitEntry (tupdesc , (AttrNumber ) 6 , "state3pc" ,
752
+ TEXTOID , -1 , 0 );
749
753
750
754
funcctx -> tuple_desc = BlessTupleDesc (tupdesc );
751
755
@@ -770,8 +774,8 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
770
774
GlobalTransaction gxact = & status -> array [status -> currIdx ++ ];
771
775
PGPROC * proc = & ProcGlobal -> allProcs [gxact -> pgprocno ];
772
776
PGXACT * pgxact = & ProcGlobal -> allPgXact [gxact -> pgprocno ];
773
- Datum values [5 ];
774
- bool nulls [5 ];
777
+ Datum values [6 ];
778
+ bool nulls [6 ];
775
779
HeapTuple tuple ;
776
780
Datum result ;
777
781
@@ -789,6 +793,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
789
793
values [2 ] = TimestampTzGetDatum (gxact -> prepared_at );
790
794
values [3 ] = ObjectIdGetDatum (gxact -> owner );
791
795
values [4 ] = ObjectIdGetDatum (proc -> databaseId );
796
+ values [5 ] = CStringGetTextDatum (gxact -> state_3pc );
792
797
793
798
tuple = heap_form_tuple (funcctx -> tuple_desc , values , nulls );
794
799
result = HeapTupleGetDatum (tuple );
@@ -916,6 +921,7 @@ typedef struct TwoPhaseFileHeader
916
921
uint16 gidlen ; /* length of the GID - GID follows the header */
917
922
XLogRecPtr origin_lsn ; /* lsn of this record at origin node */
918
923
TimestampTz origin_timestamp ; /* time of prepare at origin node */
924
+ char state_3pc [MAX_3PC_STATE_SIZE ]; /* 3PC state */
919
925
} TwoPhaseFileHeader ;
920
926
921
927
/*
@@ -1026,7 +1032,8 @@ StartPrepare(GlobalTransaction gxact)
1026
1032
hdr .nabortrels = smgrGetPendingDeletes (false, & abortrels );
1027
1033
hdr .ninvalmsgs = xactGetCommittedInvalidationMessages (& invalmsgs ,
1028
1034
& hdr .initfileinval );
1029
- hdr .gidlen = strlen (gxact -> gid ) + 1 ; /* Include '\0' */
1035
+ hdr .gidlen = strlen (gxact -> gid ) + 1 ; /* Include '\0' */
1036
+ * hdr .state_3pc = '\0' ;
1030
1037
1031
1038
save_state_data (& hdr , sizeof (TwoPhaseFileHeader ));
1032
1039
save_state_data (gxact -> gid , hdr .gidlen );
@@ -1330,6 +1337,7 @@ ParsePrepareRecord(uint8 info, char *xlrec, xl_xact_parsed_prepare *parsed)
1330
1337
parsed -> ncommitrels = hdr -> ncommitrels ;
1331
1338
parsed -> nabortrels = hdr -> nabortrels ;
1332
1339
parsed -> nmsgs = hdr -> ninvalmsgs ;
1340
+ strncpy (parsed -> state_3pc , hdr -> state_3pc , MAX_3PC_STATE_SIZE );
1333
1341
1334
1342
strncpy (parsed -> twophase_gid , bufptr , hdr -> gidlen );
1335
1343
bufptr += MAXALIGN (hdr -> gidlen );
@@ -1657,11 +1665,27 @@ RecreateTwoPhaseFile(TransactionId xid, void *content, int len)
1657
1665
pg_crc32c statefile_crc ;
1658
1666
int fd ;
1659
1667
1668
+ /* Crutch to fix crc and len check on 2pc file reading */
1669
+ Assert ( ((TwoPhaseFileHeader * ) content )-> total_len - sizeof (pg_crc32c ) <= len );
1670
+ len = ((TwoPhaseFileHeader * ) content )-> total_len - sizeof (pg_crc32c );
1671
+
1660
1672
/* Recompute CRC */
1661
1673
INIT_CRC32C (statefile_crc );
1662
1674
COMP_CRC32C (statefile_crc , content , len );
1663
1675
FIN_CRC32C (statefile_crc );
1664
1676
1677
+ /*
1678
+ * 3PC hacky support. Xid for xlog record is set during xlog insert
1679
+ * via GetCurrentTransactionIdIfAny() call. However this tx isn't already
1680
+ * active so allow it to be zero in xlog, but override here during recovery
1681
+ * so the file name will be valid xid.
1682
+ */
1683
+ if (!TransactionIdIsValid (xid ))
1684
+ {
1685
+ Assert ( * (((TwoPhaseFileHeader * ) content )-> state_3pc ) != '\0' );
1686
+ xid = ((TwoPhaseFileHeader * ) content )-> xid ;
1687
+ }
1688
+
1665
1689
TwoPhaseFilePath (path , xid );
1666
1690
1667
1691
fd = OpenTransientFile (path ,
@@ -2388,6 +2412,34 @@ RecordTransactionAbortPrepared(TransactionId xid,
2388
2412
SyncRepWaitForLSN (recptr , false);
2389
2413
}
2390
2414
2415
+
2416
+ int
2417
+ GetPreparedTransactions (PreparedTransaction * pxacts )
2418
+ {
2419
+ int num ;
2420
+ int i ;
2421
+
2422
+ LWLockAcquire (TwoPhaseStateLock , LW_SHARED );
2423
+
2424
+ num = TwoPhaseState -> numPrepXacts ;
2425
+ if (num == 0 )
2426
+ {
2427
+ LWLockRelease (TwoPhaseStateLock );
2428
+ * pxacts = NULL ;
2429
+ return 0 ;
2430
+ }
2431
+
2432
+ * pxacts = (PreparedTransaction )palloc (sizeof (PreparedTransactionData ) * num );
2433
+ for (i = 0 ; i < num ; i ++ ) {
2434
+ (* pxacts )[i ].owner = TwoPhaseState -> prepXacts [i ]-> owner ;
2435
+ strcpy ((* pxacts )[i ].gid , TwoPhaseState -> prepXacts [i ]-> gid );
2436
+ strcpy ((* pxacts )[i ].state_3pc , TwoPhaseState -> prepXacts [i ]-> state_3pc );
2437
+ }
2438
+ LWLockRelease (TwoPhaseStateLock );
2439
+
2440
+ return num ;
2441
+ }
2442
+
2391
2443
/*
2392
2444
* PrepareRedoAdd
2393
2445
*
@@ -2404,6 +2456,7 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
2404
2456
char * bufptr ;
2405
2457
const char * gid ;
2406
2458
GlobalTransaction gxact ;
2459
+ int i ;
2407
2460
2408
2461
Assert (LWLockHeldByMeInMode (TwoPhaseStateLock , LW_EXCLUSIVE ));
2409
2462
Assert (RecoveryInProgress ());
@@ -2422,6 +2475,24 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
2422
2475
* that it got added in the redo phase
2423
2476
*/
2424
2477
2478
+ /*
2479
+ * MTM-SPECIFIC.
2480
+ * In our case 3PC is done via logging prepare record twice with different state_3pc.
2481
+ * So try to find state with same xid and change the state. Other option is to use
2482
+ * twice bigger numPrepXacts, but then we anyway need iterate whole array upon
2483
+ * deletion to wipe all mentions of this tx.
2484
+ */
2485
+ for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
2486
+ {
2487
+ gxact = TwoPhaseState -> prepXacts [i ];
2488
+ if (gxact -> xid == hdr -> xid )
2489
+ {
2490
+ Assert (gxact -> inredo );
2491
+ strncpy (gxact -> state_3pc , hdr -> state_3pc , MAX_3PC_STATE_SIZE );
2492
+ return ;
2493
+ }
2494
+ }
2495
+
2425
2496
/* Get a free gxact from the freelist */
2426
2497
if (TwoPhaseState -> freeGXacts == NULL )
2427
2498
ereport (ERROR ,
@@ -2442,6 +2513,7 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
2442
2513
gxact -> ondisk = XLogRecPtrIsInvalid (start_lsn );
2443
2514
gxact -> inredo = true; /* yes, added in redo */
2444
2515
strcpy (gxact -> gid , gid );
2516
+ strncpy (gxact -> state_3pc , hdr -> state_3pc , MAX_3PC_STATE_SIZE );
2445
2517
2446
2518
/* And insert it into the active array */
2447
2519
Assert (TwoPhaseState -> numPrepXacts < max_prepared_xacts );
@@ -2607,3 +2679,84 @@ pg_prepared_xact_status(PG_FUNCTION_ARGS)
2607
2679
XLogReaderFree (xlogreader );
2608
2680
PG_RETURN_TEXT_P (cstring_to_text (xact_status ));
2609
2681
}
2682
+
2683
+ bool GetPreparedTransactionState (char const * gid , char * state )
2684
+ {
2685
+ int i ;
2686
+ GlobalTransaction gxact ;
2687
+ bool result = false;
2688
+
2689
+ LWLockAcquire (TwoPhaseStateLock , LW_SHARED );
2690
+ for (i = 0 ; i < TwoPhaseState -> numPrepXacts ; i ++ )
2691
+ {
2692
+ gxact = TwoPhaseState -> prepXacts [i ];
2693
+ if (strcmp (gxact -> gid , gid ) == 0 )
2694
+ {
2695
+ strcpy (state , gxact -> state_3pc );
2696
+ result = true;
2697
+ break ;
2698
+ }
2699
+ }
2700
+ LWLockRelease (TwoPhaseStateLock );
2701
+ return result ;
2702
+ }
2703
+
2704
+
2705
+ /*
2706
+ * SetPrepareTransactionState
2707
+ * Alter 3PC state of prepared transaction
2708
+ */
2709
+ void SetPreparedTransactionState (char const * gid , char const * state )
2710
+ {
2711
+ GlobalTransaction gxact ;
2712
+ PGXACT * pgxact ;
2713
+ TwoPhaseFileHeader * hdr ;
2714
+ char * buf ;
2715
+ bool replorigin ;
2716
+
2717
+ if (strlen (state ) >= MAX_3PC_STATE_SIZE )
2718
+ ereport (ERROR ,
2719
+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
2720
+ errmsg ("transaction state \"%s\" is too long" ,
2721
+ state )));
2722
+
2723
+ replorigin = (replorigin_session_origin != InvalidRepOriginId &&
2724
+ replorigin_session_origin != DoNotReplicateId );
2725
+
2726
+ gxact = LockGXact (gid , GetUserId (), false);
2727
+ pgxact = & ProcGlobal -> allPgXact [gxact -> pgprocno ];
2728
+ strcpy (gxact -> state_3pc , state );
2729
+
2730
+ if (gxact -> ondisk )
2731
+ buf = ReadTwoPhaseFile (pgxact -> xid , true);
2732
+ else
2733
+ XlogReadTwoPhaseData (gxact -> prepare_start_lsn , & buf , NULL );
2734
+ hdr = (TwoPhaseFileHeader * )buf ;
2735
+ strcpy (hdr -> state_3pc , state );
2736
+
2737
+ START_CRIT_SECTION ();
2738
+ MyPgXact -> delayChkpt = true;
2739
+
2740
+ hdr -> origin_lsn = replorigin_session_origin_lsn ;
2741
+ hdr -> origin_timestamp = replorigin_session_origin_timestamp ;
2742
+
2743
+ XLogBeginInsert ();
2744
+ XLogRegisterData (buf , hdr -> total_len - sizeof (pg_crc32c ));
2745
+ XLogSetRecordFlags (XLOG_INCLUDE_ORIGIN );
2746
+
2747
+ gxact -> prepare_end_lsn = XLogInsert (RM_XACT_ID , XLOG_XACT_PREPARE );
2748
+
2749
+ if (replorigin )
2750
+ /* Move LSNs forward for this replication origin */
2751
+ replorigin_session_advance (replorigin_session_origin_lsn ,
2752
+ gxact -> prepare_end_lsn );
2753
+
2754
+ XLogFlush (gxact -> prepare_end_lsn );
2755
+
2756
+ gxact -> prepare_start_lsn = ProcLastRecPtr ;
2757
+ MyPgXact -> delayChkpt = false;
2758
+
2759
+ END_CRIT_SECTION ();
2760
+
2761
+ PostPrepare_Twophase ();
2762
+ }
0 commit comments