LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - multixact_rewrite.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 0.0 % 51 0
Test Date: 2026-03-03 09:14:47 Functions: 0.0 % 3 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * multixact_rewrite.c
       3              :  *
       4              :  * Functions to convert multixact SLRUs from the pre-v19 format to the current
       5              :  * format with 64-bit MultiXactOffsets.
       6              :  *
       7              :  * Copyright (c) 2025-2026, PostgreSQL Global Development Group
       8              :  * src/bin/pg_upgrade/multixact_rewrite.c
       9              :  */
      10              : 
      11              : #include "postgres_fe.h"
      12              : 
      13              : #include "access/multixact_internal.h"
      14              : #include "multixact_read_v18.h"
      15              : #include "pg_upgrade.h"
      16              : 
      17              : static void RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi,
      18              :                                   MultiXactOffset offset);
      19              : static void RecordMultiXactMembers(SlruSegState *members_writer,
      20              :                                    MultiXactOffset offset,
      21              :                                    int nmembers, MultiXactMember *members);
      22              : 
      23              : /*
      24              :  * Convert pg_multixact/offset and /members from the old pre-v19 format with
      25              :  * 32-bit offsets to the current format.
      26              :  *
      27              :  * Multixids in the range [from_multi, to_multi) are read from the old
      28              :  * cluster, and written in the new format.  An important edge case is that if
      29              :  * from_multi == to_multi, this initializes the new pg_multixact files in the
      30              :  * new format without trying to open any old files.  (We rely on that when
      31              :  * upgrading from PostgreSQL version 9.2 or below.)
      32              :  *
      33              :  * Returns the new nextOffset value; the caller should set it in the new
      34              :  * control file.  The new members always start from offset 1, regardless of
      35              :  * the offset range used in the old cluster.
      36              :  */
      37              : MultiXactOffset
      38            0 : rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
      39              : {
      40              :     MultiXactOffset next_offset;
      41              :     SlruSegState *offsets_writer;
      42              :     SlruSegState *members_writer;
      43            0 :     char        dir[MAXPGPATH] = {0};
      44            0 :     bool        prev_multixid_valid = false;
      45              : 
      46              :     /*
      47              :      * The range of valid multi XIDs is unchanged by the conversion (they are
      48              :      * referenced from the heap tables), but the members SLRU is rewritten to
      49              :      * start from offset 1.
      50              :      */
      51            0 :     next_offset = 1;
      52              : 
      53              :     /* Prepare to write the new SLRU files */
      54            0 :     pg_sprintf(dir, "%s/pg_multixact/offsets", new_cluster.pgdata);
      55            0 :     offsets_writer = AllocSlruWrite(dir, false);
      56            0 :     SlruWriteSwitchPage(offsets_writer, MultiXactIdToOffsetPage(from_multi));
      57              : 
      58            0 :     pg_sprintf(dir, "%s/pg_multixact/members", new_cluster.pgdata);
      59            0 :     members_writer = AllocSlruWrite(dir, true /* use long segment names */ );
      60            0 :     SlruWriteSwitchPage(members_writer, MXOffsetToMemberPage(next_offset));
      61              : 
      62              :     /*
      63              :      * Convert old multixids, if needed, by reading them one-by-one from the
      64              :      * old cluster.
      65              :      */
      66            0 :     if (to_multi != from_multi)
      67              :     {
      68              :         OldMultiXactReader *old_reader;
      69              : 
      70            0 :         old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
      71              :                                            old_cluster.controldata.chkpnt_nxtmulti,
      72            0 :                                            old_cluster.controldata.chkpnt_nxtmxoff);
      73              : 
      74            0 :         for (MultiXactId multi = from_multi; multi != to_multi;)
      75              :         {
      76              :             MultiXactMember member;
      77              :             bool        multixid_valid;
      78              : 
      79              :             /*
      80              :              * Read this multixid's members.
      81              :              *
      82              :              * Locking-only XIDs that may be part of multi-xids don't matter
      83              :              * after upgrade, as there can be no transactions running across
      84              :              * upgrade.  So as a small optimization, we only read one member
      85              :              * from each multixid: the one updating one, or if there was no
      86              :              * update, arbitrarily the first locking xid.
      87              :              */
      88            0 :             multixid_valid = GetOldMultiXactIdSingleMember(old_reader, multi, &member);
      89              : 
      90              :             /*
      91              :              * Write the new offset to pg_multixact/offsets.
      92              :              *
      93              :              * Even if this multixid is invalid, we still need to write its
      94              :              * offset if the *previous* multixid was valid.  That's because
      95              :              * when reading a multixid, the number of members is calculated
      96              :              * from the difference between the two offsets.
      97              :              */
      98            0 :             RecordMultiXactOffset(offsets_writer, multi,
      99            0 :                                   (multixid_valid || prev_multixid_valid) ? next_offset : 0);
     100              : 
     101              :             /* Write the members */
     102            0 :             if (multixid_valid)
     103              :             {
     104            0 :                 RecordMultiXactMembers(members_writer, next_offset, 1, &member);
     105            0 :                 next_offset += 1;
     106              :             }
     107              : 
     108              :             /* Advance to next multixid, handling wraparound */
     109            0 :             multi++;
     110            0 :             if (multi < FirstMultiXactId)
     111            0 :                 multi = FirstMultiXactId;
     112            0 :             prev_multixid_valid = multixid_valid;
     113              :         }
     114              : 
     115            0 :         FreeOldMultiXactReader(old_reader);
     116              :     }
     117              : 
     118              :     /* Write the final 'next' offset to the last SLRU page */
     119            0 :     RecordMultiXactOffset(offsets_writer, to_multi,
     120              :                           prev_multixid_valid ? next_offset : 0);
     121              : 
     122              :     /* Flush the last SLRU pages */
     123            0 :     FreeSlruWrite(offsets_writer);
     124            0 :     FreeSlruWrite(members_writer);
     125              : 
     126            0 :     return next_offset;
     127              : }
     128              : 
     129              : 
     130              : /*
     131              :  * Write one offset to the offset SLRU
     132              :  */
     133              : static void
     134            0 : RecordMultiXactOffset(SlruSegState *offsets_writer, MultiXactId multi,
     135              :                       MultiXactOffset offset)
     136              : {
     137              :     int64       pageno;
     138              :     int         entryno;
     139              :     char       *buf;
     140              :     MultiXactOffset *offptr;
     141              : 
     142            0 :     pageno = MultiXactIdToOffsetPage(multi);
     143            0 :     entryno = MultiXactIdToOffsetEntry(multi);
     144              : 
     145            0 :     buf = SlruWriteSwitchPage(offsets_writer, pageno);
     146            0 :     offptr = (MultiXactOffset *) buf;
     147            0 :     offptr[entryno] = offset;
     148            0 : }
     149              : 
     150              : /*
     151              :  * Write the members for one multixid in the members SLRU
     152              :  *
     153              :  * (Currently, this is only ever called with nmembers == 1)
     154              :  */
     155              : static void
     156            0 : RecordMultiXactMembers(SlruSegState *members_writer,
     157              :                        MultiXactOffset offset,
     158              :                        int nmembers, MultiXactMember *members)
     159              : {
     160            0 :     for (int i = 0; i < nmembers; i++, offset++)
     161              :     {
     162              :         int64       pageno;
     163              :         char       *buf;
     164              :         TransactionId *memberptr;
     165              :         uint32     *flagsptr;
     166              :         uint32      flagsval;
     167              :         int         bshift;
     168              :         int         flagsoff;
     169              :         int         memberoff;
     170              : 
     171              :         Assert(members[i].status <= MultiXactStatusUpdate);
     172              : 
     173            0 :         pageno = MXOffsetToMemberPage(offset);
     174            0 :         memberoff = MXOffsetToMemberOffset(offset);
     175            0 :         flagsoff = MXOffsetToFlagsOffset(offset);
     176            0 :         bshift = MXOffsetToFlagsBitShift(offset);
     177              : 
     178            0 :         buf = SlruWriteSwitchPage(members_writer, pageno);
     179              : 
     180            0 :         memberptr = (TransactionId *) (buf + memberoff);
     181              : 
     182            0 :         *memberptr = members[i].xid;
     183              : 
     184            0 :         flagsptr = (uint32 *) (buf + flagsoff);
     185              : 
     186            0 :         flagsval = *flagsptr;
     187            0 :         flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
     188            0 :         flagsval |= (members[i].status << bshift);
     189            0 :         *flagsptr = flagsval;
     190              :     }
     191            0 : }
        

Generated by: LCOV version 2.0-1