LCOV - code coverage report
Current view: top level - src/bin/pg_upgrade - multixact_rewrite.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 0 51 0.0 %
Date: 2025-12-13 07:18:15 Functions: 0 3 0.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, 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 1.16