LCOV - code coverage report
Current view: top level - src/backend/postmaster - datachecksum_state.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 77.1 % 502 387
Test Date: 2026-07-21 09:15:43 Functions: 90.0 % 20 18
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 53.1 % 294 156

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * datachecksum_state.c
       4                 :             :  *    Background worker for enabling or disabling data checksums online as
       5                 :             :  *    well as functionality for manipulating data checksum state
       6                 :             :  *
       7                 :             :  * When enabling data checksums on a cluster at initdb time or when shut down
       8                 :             :  * with pg_checksums, no extra process is required as each page is checksummed,
       9                 :             :  * and verified, when accessed.  When enabling checksums on an already running
      10                 :             :  * cluster, this worker will ensure that all pages are checksummed before
      11                 :             :  * verification of the checksums is turned on. In the case of disabling
      12                 :             :  * checksums, the state transition is performed only in the control file, no
      13                 :             :  * changes are performed on the data pages.
      14                 :             :  *
      15                 :             :  * Checksums can be either enabled or disabled cluster-wide, with on/off being
      16                 :             :  * the end state for data_checksums.
      17                 :             :  *
      18                 :             :  * 1. Enabling checksums
      19                 :             :  * ---------------------
      20                 :             :  * When enabling checksums in an online cluster, data_checksums will be set to
      21                 :             :  * "inprogress-on" which signals that write operations MUST compute and write
      22                 :             :  * the checksum on the data page, but during reading the checksum SHALL NOT be
      23                 :             :  * verified. This ensures that all objects created while checksums are being
      24                 :             :  * enabled will have checksums set, but reads won't fail due to missing or
      25                 :             :  * invalid checksums. Invalid checksums can be present in case the cluster had
      26                 :             :  * checksums enabled, then disabled them and updated the page while they were
      27                 :             :  * disabled.
      28                 :             :  *
      29                 :             :  * The DataChecksumsWorker will compile a list of all databases at the start,
      30                 :             :  * any databases created concurrently will see the in-progress state and will
      31                 :             :  * be checksummed automatically.  All databases from the original list MUST BE
      32                 :             :  * successfully processed in order for data checksums to be enabled, the only
      33                 :             :  * exception are databases which are dropped before having been processed.
      34                 :             :  *
      35                 :             :  * For each database, all relations which have storage are read and every data
      36                 :             :  * page is marked dirty to force a write with the checksum. This will generate
      37                 :             :  * a lot of WAL as the entire database is read and written.
      38                 :             :  *
      39                 :             :  * If the processing is interrupted by a cluster crash or restart, it needs to
      40                 :             :  * be restarted from the beginning again as state isn't persisted.
      41                 :             :  *
      42                 :             :  * 2. Disabling checksums
      43                 :             :  * ----------------------
      44                 :             :  * When disabling checksums, data_checksums will be set to "inprogress-off"
      45                 :             :  * which signals that checksums are written but no longer need to be verified.
      46                 :             :  * This ensures that backends which have not yet transitioned to the
      47                 :             :  * "inprogress-off" state will still see valid checksums on pages.
      48                 :             :  *
      49                 :             :  * 3. Synchronization and Correctness
      50                 :             :  * ----------------------------------
      51                 :             :  * The processes involved in enabling or disabling data checksums in an
      52                 :             :  * online cluster must be properly synchronized with the normal backends
      53                 :             :  * serving concurrent queries to ensure correctness. Correctness is defined
      54                 :             :  * as the following:
      55                 :             :  *
      56                 :             :  *    - Backends SHALL NOT violate the data_checksums state they have agreed to
      57                 :             :  *      by acknowledging the procsignalbarrier:  This means that all backends
      58                 :             :  *      MUST calculate and write data checksums during all states except off;
      59                 :             :  *      MUST validate checksums only in the 'on' state.
      60                 :             :  *    - Data checksums SHALL NOT be considered enabled cluster-wide until all
      61                 :             :  *      currently connected backends have state "on": This means that all
      62                 :             :  *      backends must wait on the procsignalbarrier to be acknowledged by all
      63                 :             :  *      before proceeding to validate data checksums.
      64                 :             :  *
      65                 :             :  * There are two steps of synchronization required for changing data_checksums
      66                 :             :  * in an online cluster: (i) changing state in the active backends ("on",
      67                 :             :  * "off", "inprogress-on" and "inprogress-off"), and (ii) ensuring no
      68                 :             :  * incompatible objects and processes are left in a database when workers end.
      69                 :             :  * The former deals with cluster-wide agreement on data checksum state and the
      70                 :             :  * latter with ensuring that any concurrent activity cannot break the data
      71                 :             :  * checksum contract during processing.
      72                 :             :  *
      73                 :             :  * Synchronizing the state change is done with procsignal barriers. Before
      74                 :             :  * updating the data_checksums state in the control file, all other backends must absorb the
      75                 :             :  * barrier.  Barrier absorption will happen during interrupt processing, which
      76                 :             :  * means that connected backends will change state at different times.  If
      77                 :             :  * waiting for a barrier is done during startup, for example during replay, it
      78                 :             :  * is important to realize that any locks held by the startup process might
      79                 :             :  * cause deadlocks if backends end up waiting for those locks while startup
      80                 :             :  * is waiting for a procsignalbarrier.
      81                 :             :  *
      82                 :             :  * 3.1 When Enabling Data Checksums
      83                 :             :  * --------------------------------
      84                 :             :  * A process which fails to observe data checksums being enabled can induce two
      85                 :             :  * types of errors: failing to write the checksum when modifying the page and
      86                 :             :  * failing to validate the data checksum on the page when reading it.
      87                 :             :  *
      88                 :             :  * When processing starts all backends belong to one of the below sets, with
      89                 :             :  * one of Bd and Bi being empty:
      90                 :             :  *
      91                 :             :  * Bg: Backend updating the global state and emitting the procsignalbarrier
      92                 :             :  * Bd: Backends in "off" state
      93                 :             :  * Bi: Backends in "inprogress-on" state
      94                 :             :  *
      95                 :             :  * If processing is started in an online cluster then all backends are in Bd.
      96                 :             :  * If processing was halted by the cluster shutting down (due to a crash or
      97                 :             :  * intentional restart), the controlfile state "inprogress-on" will be observed
      98                 :             :  * on system startup and all backends will be placed in Bd. The controlfile
      99                 :             :  * state will also be set to "off".
     100                 :             :  *
     101                 :             :  * Backends transition Bd -> Bi via a procsignalbarrier which is emitted by the
     102                 :             :  * DataChecksumsWorkerLauncherMain.  When all backends have acknowledged the
     103                 :             :  * barrier then Bd will be empty and the next phase can begin: calculating and
     104                 :             :  * writing data checksums with DataChecksumsWorkers.  When the
     105                 :             :  * DataChecksumsWorker processes have finished writing checksums on all pages,
     106                 :             :  * data checksums are enabled cluster-wide via another procsignalbarrier.
     107                 :             :  * There are four sets of backends where Bd shall be an empty set:
     108                 :             :  *
     109                 :             :  * Bg: Backend updating the global state and emitting the procsignalbarrier
     110                 :             :  * Bd: Backends in "off" state
     111                 :             :  * Be: Backends in "on" state
     112                 :             :  * Bi: Backends in "inprogress-on" state
     113                 :             :  *
     114                 :             :  * Backends in Bi and Be will write checksums when modifying a page, but only
     115                 :             :  * backends in Be will verify the checksum during reading. The Bg backend is
     116                 :             :  * blocked waiting for all backends in Bi to process interrupts and move to
     117                 :             :  * Be. Any backend starting while Bg is waiting on the procsignalbarrier will
     118                 :             :  * observe the global state being "on" and will thus automatically belong to
     119                 :             :  * Be.  Checksums are enabled cluster-wide when Bi is an empty set. Bi and Be
     120                 :             :  * are compatible sets while still operating based on their local state as
     121                 :             :  * both write data checksums.
     122                 :             :  *
     123                 :             :  * 3.2 When Disabling Data Checksums
     124                 :             :  * ---------------------------------
     125                 :             :  * A process which fails to observe that data checksums have been disabled
     126                 :             :  * can induce two types of errors: writing the checksum when modifying the
     127                 :             :  * page and validating a data checksum which is no longer correct due to
     128                 :             :  * modifications to the page. The former is not an error per se as data
     129                 :             :  * integrity is maintained, but it is wasteful.  The latter will cause errors
     130                 :             :  * in user operations.  Assuming the following sets of backends:
     131                 :             :  *
     132                 :             :  * Bg: Backend updating the global state and emitting the procsignalbarrier
     133                 :             :  * Bd: Backends in "off" state
     134                 :             :  * Be: Backends in "on" state
     135                 :             :  * Bo: Backends in "inprogress-off" state
     136                 :             :  * Bi: Backends in "inprogress-on" state
     137                 :             :  *
     138                 :             :  * Backends transition from the Be state to Bd like so: Be -> Bo -> Bd.  From
     139                 :             :  * all other states, the transition can be straight to Bd.
     140                 :             :  *
     141                 :             :  * The goal is to transition all backends to Bd making the others empty sets.
     142                 :             :  * Backends in Bo write data checksums, but don't validate them, such that
     143                 :             :  * backends still in Be can continue to validate pages until the barrier has
     144                 :             :  * been absorbed such that they are in Bo. Once all backends are in Bo, the
     145                 :             :  * barrier to transition to "off" can be raised and all backends can safely
     146                 :             :  * stop writing data checksums as no backend is enforcing data checksum
     147                 :             :  * validation any longer.
     148                 :             :  *
     149                 :             :  * 4. Future opportunities for optimizations
     150                 :             :  * -----------------------------------------
     151                 :             :  * Below are some potential optimizations and improvements which were brought
     152                 :             :  * up during reviews of this feature, but which weren't implemented in the
     153                 :             :  * initial version. These are ideas listed without any validation on their
     154                 :             :  * feasibility or potential payoff. More discussion on (most of) these can be
     155                 :             :  * found on the -hackers threads linked to in the commit message of this
     156                 :             :  * feature.
     157                 :             :  *
     158                 :             :  *   * Launching datachecksumsworker for resuming operation from the startup
     159                 :             :  *     process: Currently users have to restart processing manually after a
     160                 :             :  *     restart since dynamic background worker cannot be started from the
     161                 :             :  *     postmaster. Changing the startup process could make restarting the
     162                 :             :  *     processing automatic on cluster restart.
     163                 :             :  *   * Avoid dirtying the page when checksums already match: Iff the checksum
     164                 :             :  *     on the page happens to already match we still dirty the page. It should
     165                 :             :  *     be enough to only do the log_newpage_buffer() call in that case.
     166                 :             :  *   * Teach pg_checksums to avoid checksummed pages when pg_checksums is used
     167                 :             :  *     to enable checksums on a cluster which is in inprogress-on state and
     168                 :             :  *     may have checksummed pages (make pg_checksums be able to resume an
     169                 :             :  *     online operation). This should only be attempted for wal_level minimal.
     170                 :             :  *   * Restartability (not necessarily with page granularity).
     171                 :             :  *   * Avoid processing databases which were created during inprogress-on.
     172                 :             :  *     Right now all databases are processed regardless to be safe.
     173                 :             :  *   * Teach CREATE DATABASE to calculate checksums for databases created
     174                 :             :  *     during inprogress-on with a template database which has yet to be
     175                 :             :  *     processed.
     176                 :             :  *
     177                 :             :  *
     178                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
     179                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
     180                 :             :  *
     181                 :             :  *
     182                 :             :  * IDENTIFICATION
     183                 :             :  *    src/backend/postmaster/datachecksum_state.c
     184                 :             :  *
     185                 :             :  *-------------------------------------------------------------------------
     186                 :             :  */
     187                 :             : #include "postgres.h"
     188                 :             : 
     189                 :             : #include "access/genam.h"
     190                 :             : #include "access/heapam.h"
     191                 :             : #include "access/htup_details.h"
     192                 :             : #include "access/xact.h"
     193                 :             : #include "access/xlog.h"
     194                 :             : #include "access/xloginsert.h"
     195                 :             : #include "catalog/indexing.h"
     196                 :             : #include "catalog/pg_class.h"
     197                 :             : #include "catalog/pg_database.h"
     198                 :             : #include "commands/progress.h"
     199                 :             : #include "commands/vacuum.h"
     200                 :             : #include "common/relpath.h"
     201                 :             : #include "miscadmin.h"
     202                 :             : #include "pgstat.h"
     203                 :             : #include "postmaster/bgworker.h"
     204                 :             : #include "postmaster/bgwriter.h"
     205                 :             : #include "postmaster/datachecksum_state.h"
     206                 :             : #include "storage/bufmgr.h"
     207                 :             : #include "storage/checksum.h"
     208                 :             : #include "storage/ipc.h"
     209                 :             : #include "storage/latch.h"
     210                 :             : #include "storage/lmgr.h"
     211                 :             : #include "storage/lwlock.h"
     212                 :             : #include "storage/procarray.h"
     213                 :             : #include "storage/smgr.h"
     214                 :             : #include "storage/subsystems.h"
     215                 :             : #include "tcop/tcopprot.h"
     216                 :             : #include "utils/builtins.h"
     217                 :             : #include "utils/fmgroids.h"
     218                 :             : #include "utils/injection_point.h"
     219                 :             : #include "utils/lsyscache.h"
     220                 :             : #include "utils/ps_status.h"
     221                 :             : #include "utils/syscache.h"
     222                 :             : #include "utils/wait_event.h"
     223                 :             : 
     224                 :             : /*
     225                 :             :  * Configuration of conditions which must match when absorbing a procsignal
     226                 :             :  * barrier during data checksum enable/disable operations.  A single function
     227                 :             :  * is used for absorbing all barriers, and the current and target states must
     228                 :             :  * be defined as a from/to tuple in the checksum_barriers struct.
     229                 :             :  */
     230                 :             : typedef struct ChecksumBarrierCondition
     231                 :             : {
     232                 :             :     /* Current state of data checksums */
     233                 :             :     int         from;
     234                 :             :     /* Target state for data checksums */
     235                 :             :     int         to;
     236                 :             : } ChecksumBarrierCondition;
     237                 :             : 
     238                 :             : static const ChecksumBarrierCondition checksum_barriers[9] =
     239                 :             : {
     240                 :             :     /*
     241                 :             :      * Disabling checksums: If checksums are currently enabled, disabling must
     242                 :             :      * go through the 'inprogress-off' state.
     243                 :             :      */
     244                 :             :     {PG_DATA_CHECKSUM_VERSION, PG_DATA_CHECKSUM_INPROGRESS_OFF},
     245                 :             :     {PG_DATA_CHECKSUM_INPROGRESS_OFF, PG_DATA_CHECKSUM_OFF},
     246                 :             : 
     247                 :             :     /*
     248                 :             :      * If checksums are in the process of being enabled, but are not yet being
     249                 :             :      * verified, we can abort by going back to 'off' state.
     250                 :             :      */
     251                 :             :     {PG_DATA_CHECKSUM_INPROGRESS_ON, PG_DATA_CHECKSUM_OFF},
     252                 :             : 
     253                 :             :     /*
     254                 :             :      * Enabling checksums must normally go through the 'inprogress-on' state.
     255                 :             :      */
     256                 :             :     {PG_DATA_CHECKSUM_OFF, PG_DATA_CHECKSUM_INPROGRESS_ON},
     257                 :             :     {PG_DATA_CHECKSUM_INPROGRESS_ON, PG_DATA_CHECKSUM_VERSION},
     258                 :             : 
     259                 :             :     /*
     260                 :             :      * If checksums are being disabled but all backends are still computing
     261                 :             :      * checksums, we can go straight back to 'on'
     262                 :             :      */
     263                 :             :     {PG_DATA_CHECKSUM_INPROGRESS_OFF, PG_DATA_CHECKSUM_VERSION},
     264                 :             : 
     265                 :             :     /*
     266                 :             :      * If checksums are being enabled when launcher_exit is executed, state is
     267                 :             :      * set to off since we cannot reach on at that point.
     268                 :             :      */
     269                 :             :     {PG_DATA_CHECKSUM_INPROGRESS_ON, PG_DATA_CHECKSUM_INPROGRESS_OFF},
     270                 :             : 
     271                 :             :     /*
     272                 :             :      * Transitions that can happen when a new request is made while another is
     273                 :             :      * currently being processed.
     274                 :             :      */
     275                 :             :     {PG_DATA_CHECKSUM_INPROGRESS_OFF, PG_DATA_CHECKSUM_INPROGRESS_ON},
     276                 :             :     {PG_DATA_CHECKSUM_OFF, PG_DATA_CHECKSUM_INPROGRESS_OFF},
     277                 :             : };
     278                 :             : 
     279                 :             : /* Possible operations the DataChecksumsWorker can perform */
     280                 :             : typedef enum DataChecksumsWorkerOperation
     281                 :             : {
     282                 :             :     ENABLE_DATACHECKSUMS,
     283                 :             :     DISABLE_DATACHECKSUMS,
     284                 :             : } DataChecksumsWorkerOperation;
     285                 :             : 
     286                 :             : /* Possible states for a database entry which has been processed */
     287                 :             : typedef enum
     288                 :             : {
     289                 :             :     DATACHECKSUMSWORKER_SUCCESSFUL = 0,
     290                 :             :     DATACHECKSUMSWORKER_ABORTED,
     291                 :             :     DATACHECKSUMSWORKER_FAILED,
     292                 :             :     DATACHECKSUMSWORKER_DROPDB,
     293                 :             : } DataChecksumsWorkerResult;
     294                 :             : 
     295                 :             : /*
     296                 :             :  * Signaling between backends calling pg_enable/disable_data_checksums, the
     297                 :             :  * checksums launcher process, and the checksums worker process.
     298                 :             :  *
     299                 :             :  * This struct is protected by DataChecksumsWorkerLock
     300                 :             :  */
     301                 :             : typedef struct DataChecksumsStateStruct
     302                 :             : {
     303                 :             :     /*
     304                 :             :      * These are set by pg_{enable|disable}_data_checksums, to tell the
     305                 :             :      * launcher what the target state is.
     306                 :             :      */
     307                 :             :     DataChecksumsWorkerOperation launch_operation;
     308                 :             :     int         launch_cost_delay;
     309                 :             :     int         launch_cost_limit;
     310                 :             : 
     311                 :             :     /*
     312                 :             :      * Is a launcher process currently running?  This is set by the main
     313                 :             :      * launcher process, after it has read the above launch_* parameters.
     314                 :             :      */
     315                 :             :     bool        launcher_running;
     316                 :             : 
     317                 :             :     /*
     318                 :             :      * Every time a new worker is launched, it's assigned a unique invocation
     319                 :             :      * number by incrementing this counter.
     320                 :             :      */
     321                 :             :     uint64      worker_invocation_counter;
     322                 :             : 
     323                 :             :     /*
     324                 :             :      * Information about the current worker, if it's currently running.  These
     325                 :             :      * are set by the worker launcher.
     326                 :             :      */
     327                 :             :     uint64      worker_invocation;  /* unique invocation number */
     328                 :             :     Oid         database_oid;   /* database it's processing */
     329                 :             :     pid_t       worker_pid;     /* worker process's PID */
     330                 :             : 
     331                 :             :     /*
     332                 :             :      * These fields indicate the target state that the worker is currently
     333                 :             :      * running with.  They can be different from the corresponding launch_*
     334                 :             :      * fields, if a new pg_enable/disable_data_checksums() call was made while
     335                 :             :      * the launcher/worker was already running.  The worker will periodically
     336                 :             :      * check if new cost settings have been requested, and if so will copy
     337                 :             :      * them from the launch_* fields and reset cost throttling to match the
     338                 :             :      * new values.
     339                 :             :      */
     340                 :             :     DataChecksumsWorkerOperation operation;
     341                 :             :     int         cost_delay;
     342                 :             :     int         cost_limit;
     343                 :             : 
     344                 :             :     /*
     345                 :             :      * Signaling between the launcher and the worker process. Protected by
     346                 :             :      * DataChecksumsWorkerLock.
     347                 :             :      */
     348                 :             : 
     349                 :             :     /* result, set by worker before exiting */
     350                 :             :     DataChecksumsWorkerResult worker_result;
     351                 :             : 
     352                 :             :     /*
     353                 :             :      * Tells the worker process whether it should also process the shared
     354                 :             :      * catalogs
     355                 :             :      */
     356                 :             :     bool        process_shared_catalogs;
     357                 :             : } DataChecksumsStateStruct;
     358                 :             : 
     359                 :             : /* Shared memory segment for datachecksumsworker */
     360                 :             : static DataChecksumsStateStruct *DataChecksumState;
     361                 :             : 
     362                 :             : typedef struct DataChecksumsWorkerDatabase
     363                 :             : {
     364                 :             :     Oid         dboid;
     365                 :             :     char       *dbname;
     366                 :             : } DataChecksumsWorkerDatabase;
     367                 :             : 
     368                 :             : /* Flag set by the interrupt handler */
     369                 :             : static volatile sig_atomic_t abort_requested = false;
     370                 :             : 
     371                 :             : static uint64 worker_invocation;
     372                 :             : 
     373                 :             : /*
     374                 :             :  * Have we set the DataChecksumsStateStruct->launcher_running flag?
     375                 :             :  * If we have, we need to clear it before exiting!
     376                 :             :  */
     377                 :             : static volatile sig_atomic_t launcher_running = false;
     378                 :             : 
     379                 :             : /* Are we enabling data checksums, or disabling them? */
     380                 :             : static DataChecksumsWorkerOperation operation;
     381                 :             : 
     382                 :             : /* Prototypes */
     383                 :             : static void StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op,
     384                 :             :                                              int cost_delay,
     385                 :             :                                              int cost_limit);
     386                 :             : static void DataChecksumsShmemRequest(void *arg);
     387                 :             : static bool DatabaseExists(Oid dboid);
     388                 :             : static List *BuildDatabaseList(void);
     389                 :             : static List *BuildRelationList(bool temp_relations, bool include_shared);
     390                 :             : static void FreeDatabaseList(List *dblist);
     391                 :             : static DataChecksumsWorkerResult ProcessDatabase(DataChecksumsWorkerDatabase *db);
     392                 :             : static bool ProcessAllDatabases(void);
     393                 :             : static bool ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy);
     394                 :             : static void ResetDataChecksumsProgressCounters(void);
     395                 :             : static void launcher_cancel_handler(SIGNAL_ARGS);
     396                 :             : static void WaitForAllTransactionsToFinish(void);
     397                 :             : 
     398                 :             : const ShmemCallbacks DataChecksumsShmemCallbacks = {
     399                 :             :     .request_fn = DataChecksumsShmemRequest,
     400                 :             : };
     401                 :             : 
     402                 :             : #define CHECK_FOR_LAUNCHER_ABORT_REQUEST() \
     403                 :             :     do {                                                            \
     404                 :             :         Assert(MyBackendType == B_DATACHECKSUMSWORKER_LAUNCHER);    \
     405                 :             :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);          \
     406                 :             :         if (DataChecksumState->launch_operation != operation)        \
     407                 :             :             abort_requested = true;                                 \
     408                 :             :         LWLockRelease(DataChecksumsWorkerLock);                     \
     409                 :             :     } while (0)
     410                 :             : 
     411                 :             : #define CHECK_FOR_WORKER_ABORT_REQUEST() \
     412                 :             :     do {                                                            \
     413                 :             :         Assert(MyBackendType == B_DATACHECKSUMSWORKER_WORKER);      \
     414                 :             :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);          \
     415                 :             :         if (DataChecksumState->worker_invocation != worker_invocation || \
     416                 :             :             DataChecksumState->launch_operation != operation)        \
     417                 :             :             abort_requested = true;                                 \
     418                 :             :         LWLockRelease(DataChecksumsWorkerLock);                     \
     419                 :             :     } while (0)
     420                 :             : 
     421                 :             : 
     422                 :             : /*****************************************************************************
     423                 :             :  * Functionality for manipulating the data checksum state in the cluster
     424                 :             :  */
     425                 :             : 
     426                 :             : void
     427                 :           8 : EmitAndWaitDataChecksumsBarrier(uint32 state)
     428                 :             : {
     429                 :             :     uint64      barrier;
     430                 :             : 
     431   [ +  +  +  +  :           8 :     switch (state)
                      - ]
     432                 :             :     {
     433                 :           3 :         case PG_DATA_CHECKSUM_INPROGRESS_ON:
     434                 :           3 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_ON);
     435                 :           3 :             WaitForProcSignalBarrier(barrier);
     436                 :           3 :             break;
     437                 :             : 
     438                 :           1 :         case PG_DATA_CHECKSUM_INPROGRESS_OFF:
     439                 :           1 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_OFF);
     440                 :           1 :             WaitForProcSignalBarrier(barrier);
     441                 :           1 :             break;
     442                 :             : 
     443                 :           2 :         case PG_DATA_CHECKSUM_VERSION:
     444                 :           2 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_ON);
     445                 :           2 :             WaitForProcSignalBarrier(barrier);
     446                 :           2 :             break;
     447                 :             : 
     448                 :           2 :         case PG_DATA_CHECKSUM_OFF:
     449                 :           2 :             barrier = EmitProcSignalBarrier(PROCSIGNAL_BARRIER_CHECKSUM_OFF);
     450                 :           2 :             WaitForProcSignalBarrier(barrier);
     451                 :           2 :             break;
     452                 :             : 
     453                 :           8 :         default:
     454                 :             :             Assert(false);
     455                 :             :     }
     456                 :           8 : }
     457                 :             : 
     458                 :             : /*
     459                 :             :  * AbsorbDataChecksumsBarrier
     460                 :             :  *      Generic function for absorbing data checksum state changes
     461                 :             :  *
     462                 :             :  * All procsignalbarriers regarding data checksum state changes are absorbed
     463                 :             :  * with this function.  The set of conditions required for the state change to
     464                 :             :  * be accepted are listed in the checksum_barriers struct, target_state is
     465                 :             :  * used to look up the relevant entry.
     466                 :             :  */
     467                 :             : bool
     468                 :         273 : AbsorbDataChecksumsBarrier(ProcSignalBarrierType barrier)
     469                 :             : {
     470                 :             :     uint32      target_state;
     471                 :         273 :     int         current = data_checksums;
     472                 :         273 :     bool        found = false;
     473                 :             : 
     474                 :             :     /*
     475                 :             :      * Translate the barrier condition to the target state, doing it here
     476                 :             :      * instead of in the procsignal code saves the latter from knowing about
     477                 :             :      * checksum states.
     478                 :             :      */
     479   [ +  +  +  +  :         273 :     switch (barrier)
                      - ]
     480                 :             :     {
     481                 :          94 :         case PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_ON:
     482                 :          94 :             target_state = PG_DATA_CHECKSUM_INPROGRESS_ON;
     483                 :          94 :             break;
     484                 :          71 :         case PROCSIGNAL_BARRIER_CHECKSUM_ON:
     485                 :          71 :             target_state = PG_DATA_CHECKSUM_VERSION;
     486                 :          71 :             break;
     487                 :          51 :         case PROCSIGNAL_BARRIER_CHECKSUM_INPROGRESS_OFF:
     488                 :          51 :             target_state = PG_DATA_CHECKSUM_INPROGRESS_OFF;
     489                 :          51 :             break;
     490                 :          57 :         case PROCSIGNAL_BARRIER_CHECKSUM_OFF:
     491                 :          57 :             target_state = PG_DATA_CHECKSUM_OFF;
     492                 :          57 :             break;
     493                 :           0 :         default:
     494         [ #  # ]:           0 :             elog(ERROR, "incorrect barrier \"%i\" received", barrier);
     495                 :             :     }
     496                 :             : 
     497                 :             :     /*
     498                 :             :      * If the target state matches the current state then the barrier has been
     499                 :             :      * repeated.
     500                 :             :      */
     501         [ +  + ]:         273 :     if (current == target_state)
     502                 :           1 :         return true;
     503                 :             : 
     504                 :             :     /*
     505                 :             :      * If the cluster is in recovery we skip the validation of current state
     506                 :             :      * since the replay is trusted.
     507                 :             :      */
     508         [ +  + ]:         272 :     if (RecoveryInProgress())
     509                 :             :     {
     510                 :          48 :         SetLocalDataChecksumState(target_state);
     511                 :          48 :         return true;
     512                 :             :     }
     513                 :             : 
     514                 :             :     /*
     515                 :             :      * Find the barrier condition definition for the target state. Not finding
     516                 :             :      * a condition would be a grave programmer error as the states are a
     517                 :             :      * discrete set.
     518                 :             :      */
     519   [ +  -  +  + ]:        1020 :     for (size_t i = 0; i < lengthof(checksum_barriers) && !found; i++)
     520                 :             :     {
     521   [ +  +  +  + ]:         796 :         if (checksum_barriers[i].from == current && checksum_barriers[i].to == target_state)
     522                 :         224 :             found = true;
     523                 :             :     }
     524                 :             : 
     525                 :             :     /*
     526                 :             :      * If the relevant state criteria aren't satisfied, throw an error which
     527                 :             :      * will be caught by the procsignal machinery for a later retry.
     528                 :             :      */
     529         [ -  + ]:         224 :     if (!found)
     530         [ #  # ]:           0 :         ereport(ERROR,
     531                 :             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     532                 :             :                 errmsg("incorrect data checksum state %i for target state %i",
     533                 :             :                        current, target_state));
     534                 :             : 
     535                 :         224 :     SetLocalDataChecksumState(target_state);
     536                 :         224 :     return true;
     537                 :             : }
     538                 :             : 
     539                 :             : 
     540                 :             : /*
     541                 :             :  * Disables data checksums for the cluster, if applicable. Starts a background
     542                 :             :  * worker which turns off the data checksums.
     543                 :             :  */
     544                 :             : Datum
     545                 :           7 : disable_data_checksums(PG_FUNCTION_ARGS)
     546                 :             : {
     547                 :           7 :     PreventCommandDuringRecovery("pg_disable_data_checksums()");
     548                 :             : 
     549         [ -  + ]:           7 :     if (!superuser())
     550         [ #  # ]:           0 :         ereport(ERROR,
     551                 :             :                 errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     552                 :             :                 errmsg("must be superuser to change data checksum state"));
     553                 :             : 
     554                 :           7 :     StartDataChecksumsWorkerLauncher(DISABLE_DATACHECKSUMS, 0, 0);
     555                 :           7 :     PG_RETURN_VOID();
     556                 :             : }
     557                 :             : 
     558                 :             : /*
     559                 :             :  * Enables data checksums for the cluster, if applicable.  Supports vacuum-
     560                 :             :  * like cost based throttling to limit system load. Starts a background worker
     561                 :             :  * which updates data checksums on existing data.
     562                 :             :  */
     563                 :             : Datum
     564                 :          11 : enable_data_checksums(PG_FUNCTION_ARGS)
     565                 :             : {
     566                 :          11 :     int         cost_delay = PG_GETARG_INT32(0);
     567                 :          11 :     int         cost_limit = PG_GETARG_INT32(1);
     568                 :             : 
     569                 :          11 :     PreventCommandDuringRecovery("pg_enable_data_checksums()");
     570                 :             : 
     571         [ -  + ]:          11 :     if (!superuser())
     572         [ #  # ]:           0 :         ereport(ERROR,
     573                 :             :                 errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     574                 :             :                 errmsg("must be superuser to change data checksum state"));
     575                 :             : 
     576         [ -  + ]:          11 :     if (cost_delay < 0)
     577         [ #  # ]:           0 :         ereport(ERROR,
     578                 :             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     579                 :             :                 errmsg("cost delay cannot be a negative value"));
     580                 :             : 
     581         [ -  + ]:          11 :     if (cost_limit <= 0)
     582         [ #  # ]:           0 :         ereport(ERROR,
     583                 :             :                 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     584                 :             :                 errmsg("cost limit must be greater than zero"));
     585                 :             : 
     586                 :          11 :     StartDataChecksumsWorkerLauncher(ENABLE_DATACHECKSUMS, cost_delay, cost_limit);
     587                 :             : 
     588                 :          11 :     PG_RETURN_VOID();
     589                 :             : }
     590                 :             : 
     591                 :             : 
     592                 :             : /*****************************************************************************
     593                 :             :  * Functionality for running the datachecksumsworker and associated launcher
     594                 :             :  */
     595                 :             : 
     596                 :             : /*
     597                 :             :  * StartDataChecksumsWorkerLauncher
     598                 :             :  *      Start the datachecksumsworker launcher process, if not running yet
     599                 :             :  *
     600                 :             :  * This is called to start data checksums processing for enabling as well as
     601                 :             :  * disabling.
     602                 :             :  */
     603                 :             : static void
     604                 :          18 : StartDataChecksumsWorkerLauncher(DataChecksumsWorkerOperation op,
     605                 :             :                                  int cost_delay,
     606                 :             :                                  int cost_limit)
     607                 :             : {
     608                 :             :     BackgroundWorker bgw;
     609                 :             :     BackgroundWorkerHandle *bgw_handle;
     610                 :             :     bool        running;
     611                 :             : 
     612                 :             : #ifdef USE_ASSERT_CHECKING
     613                 :             :     /* The cost delay settings have no effect when disabling */
     614                 :             :     if (op == DISABLE_DATACHECKSUMS)
     615                 :             :         Assert(cost_delay == 0 && cost_limit == 0);
     616                 :             : #endif
     617                 :             : 
     618                 :          18 :     INJECTION_POINT("datachecksumsworker-startup-delay", NULL);
     619                 :             : 
     620                 :             :     /* Store the desired state in shared memory */
     621                 :          18 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     622                 :             : 
     623                 :          18 :     DataChecksumState->launch_operation = op;
     624                 :          18 :     DataChecksumState->launch_cost_delay = cost_delay;
     625                 :          18 :     DataChecksumState->launch_cost_limit = cost_limit;
     626                 :             : 
     627                 :             :     /* Is the launcher already running? If so, what is it doing? */
     628                 :          18 :     running = DataChecksumState->launcher_running;
     629                 :             : 
     630                 :          18 :     LWLockRelease(DataChecksumsWorkerLock);
     631                 :             : 
     632                 :             :     /*
     633                 :             :      * Launch a new launcher process, if it's not running already.
     634                 :             :      *
     635                 :             :      * If the launcher is currently busy enabling the checksums, and we want
     636                 :             :      * them disabled (or vice versa), the launcher will notice that at latest
     637                 :             :      * when it's about to exit, and will loop back to process the new request.
     638                 :             :      * So if the launcher is already running, we don't need to do anything
     639                 :             :      * more here to abort it.
     640                 :             :      *
     641                 :             :      * If you call pg_enable/disable_data_checksums() twice in a row, before
     642                 :             :      * the launcher has had a chance to start up, we still end up launching it
     643                 :             :      * twice.  That's OK, the second invocation will see that a launcher is
     644                 :             :      * already running and exit quickly.
     645                 :             :      */
     646         [ +  - ]:          18 :     if (!running)
     647                 :             :     {
     648   [ +  +  +  +  :          18 :         if ((op == ENABLE_DATACHECKSUMS && DataChecksumsOn()) ||
                   +  + ]
     649         [ +  + ]:           7 :             (op == DISABLE_DATACHECKSUMS && DataChecksumsOff()))
     650                 :             :         {
     651         [ +  - ]:           3 :             ereport(LOG,
     652                 :             :                     errmsg("data checksums already in desired state, exiting"));
     653                 :           3 :             return;
     654                 :             :         }
     655                 :             : 
     656                 :             :         /*
     657                 :             :          * Prepare the BackgroundWorker and launch it.
     658                 :             :          */
     659                 :          15 :         memset(&bgw, 0, sizeof(bgw));
     660                 :          15 :         bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
     661                 :          15 :         bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
     662                 :          15 :         snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
     663                 :          15 :         snprintf(bgw.bgw_function_name, BGW_MAXLEN, "DataChecksumsWorkerLauncherMain");
     664                 :          15 :         snprintf(bgw.bgw_name, BGW_MAXLEN, "datachecksums launcher");
     665                 :          15 :         snprintf(bgw.bgw_type, BGW_MAXLEN, "datachecksums launcher");
     666                 :          15 :         bgw.bgw_restart_time = BGW_NEVER_RESTART;
     667                 :          15 :         bgw.bgw_notify_pid = MyProcPid;
     668                 :          15 :         bgw.bgw_main_arg = (Datum) 0;
     669                 :             : 
     670         [ -  + ]:          15 :         if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
     671         [ #  # ]:           0 :             ereport(ERROR,
     672                 :             :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
     673                 :             :                     errmsg("failed to start background worker to process data checksums"));
     674                 :             :     }
     675                 :             :     else
     676                 :             :     {
     677         [ #  # ]:           0 :         ereport(LOG,
     678                 :             :                 errmsg("data checksum processing already running"));
     679                 :             :     }
     680                 :             : }
     681                 :             : 
     682                 :             : /*
     683                 :             :  * ProcessSingleRelationFork
     684                 :             :  *      Enable data checksums in a single relation/fork.
     685                 :             :  *
     686                 :             :  * Returns true if successful, and false if *aborted*. On error, an actual
     687                 :             :  * error is raised in the lower levels.
     688                 :             :  */
     689                 :             : static bool
     690                 :        7847 : ProcessSingleRelationFork(Relation reln, ForkNumber forkNum, BufferAccessStrategy strategy)
     691                 :             : {
     692                 :        7847 :     BlockNumber numblocks = RelationGetNumberOfBlocksInFork(reln, forkNum);
     693                 :             :     char        activity[NAMEDATALEN * 2 + 128];
     694                 :             :     char       *relns;
     695                 :             : 
     696                 :        7847 :     relns = get_namespace_name(RelationGetNamespace(reln));
     697                 :             : 
     698                 :             :     /* Report the current relation to pg_stat_activity */
     699                 :        7847 :     snprintf(activity, sizeof(activity) - 1, "processing: %s.%s (%s, %u blocks)",
     700         [ +  - ]:        7847 :              (relns ? relns : ""), RelationGetRelationName(reln), forkNames[forkNum], numblocks);
     701                 :        7847 :     pgstat_report_activity(STATE_RUNNING, activity);
     702                 :             :     {
     703                 :        7847 :         const int   index[] = {
     704                 :             :             PROGRESS_DATACHECKSUMS_BLOCKS_TOTAL,
     705                 :             :             PROGRESS_DATACHECKSUMS_BLOCKS_DONE
     706                 :             :         };
     707                 :             : 
     708                 :             :         int64       vals[2];
     709                 :             : 
     710                 :        7847 :         vals[0] = numblocks;
     711                 :        7847 :         vals[1] = 0;
     712                 :             : 
     713                 :        7847 :         pgstat_progress_update_multi_param(2, index, vals);
     714                 :             :     }
     715         [ +  - ]:        7847 :     if (relns)
     716                 :        7847 :         pfree(relns);
     717                 :             : 
     718                 :             :     /*
     719                 :             :      * We are looping over the blocks which existed at the time of process
     720                 :             :      * start, which is safe since new blocks are created with checksums set
     721                 :             :      * already due to the state being "inprogress-on".
     722                 :             :      */
     723         [ +  + ]:       48898 :     for (BlockNumber blknum = 0; blknum < numblocks; blknum++)
     724                 :             :     {
     725                 :       41051 :         Buffer      buf = ReadBufferExtended(reln, forkNum, blknum, RBM_NORMAL, strategy);
     726                 :             : 
     727                 :             :         /* Need to get an exclusive lock to mark the buffer as dirty */
     728                 :       41051 :         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     729                 :             : 
     730                 :             :         /*
     731                 :             :          * Mark the buffer as dirty and force a full page write.  We have to
     732                 :             :          * re-write the page to WAL even if the checksum hasn't changed,
     733                 :             :          * because if there is a replica it might have a slightly different
     734                 :             :          * version of the page with an invalid checksum, caused by unlogged
     735                 :             :          * changes (e.g. hint bits) on the primary happening while checksums
     736                 :             :          * were off. This can happen if there was a valid checksum on the page
     737                 :             :          * at one point in the past, so only when checksums are first on, then
     738                 :             :          * off, and then turned on again.  TODO: investigate if this could be
     739                 :             :          * avoided if the checksum is calculated to be correct and wal_level
     740                 :             :          * is set to "minimal".
     741                 :             :          *
     742                 :             :          * Unlogged relations don't need WAL since they are reset to their
     743                 :             :          * init fork on recovery.  We still dirty the buffer so that the
     744                 :             :          * checksum is written to disk at the next checkpoint.
     745                 :             :          *
     746                 :             :          * The init fork is an exception: it is WAL-logged so the standby can
     747                 :             :          * materialize the relation after promotion (see
     748                 :             :          * ResetUnloggedRelations()).  Skipping it here would leave the
     749                 :             :          * standby with a stale init fork that, once copied to the main fork
     750                 :             :          * on promotion, would fail checksum verification on every read.
     751                 :             :          */
     752                 :       41051 :         START_CRIT_SECTION();
     753                 :       41051 :         MarkBufferDirty(buf);
     754   [ +  +  +  +  :       41051 :         if (RelationNeedsWAL(reln) || forkNum == INIT_FORKNUM)
          +  -  -  +  +  
                      + ]
     755                 :       41017 :             log_newpage_buffer(buf, false);
     756                 :       41051 :         END_CRIT_SECTION();
     757                 :             : 
     758                 :       41051 :         UnlockReleaseBuffer(buf);
     759                 :             : 
     760                 :             :         /* Check if we are asked to abort, the abortion will bubble up. */
     761                 :             :         Assert(operation == ENABLE_DATACHECKSUMS);
     762   [ +  -  -  + ]:       41051 :         CHECK_FOR_WORKER_ABORT_REQUEST();
     763         [ -  + ]:       41051 :         if (abort_requested)
     764                 :           0 :             return false;
     765                 :             : 
     766                 :             :         /* update the block counter */
     767                 :       41051 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_BLOCKS_DONE,
     768                 :       41051 :                                      (blknum + 1));
     769                 :             : 
     770                 :             :         /*
     771                 :             :          * Processing is re-using the vacuum cost delay for process
     772                 :             :          * throttling, hence why we call vacuum APIs here.
     773                 :             :          */
     774                 :       41051 :         vacuum_delay_point(false);
     775                 :             :     }
     776                 :             : 
     777                 :        7847 :     return true;
     778                 :             : }
     779                 :             : 
     780                 :             : /*
     781                 :             :  * Initialize all data checksum progress counters to be displayed as NULL.
     782                 :             :  */
     783                 :             : static void
     784                 :          37 : ResetDataChecksumsProgressCounters(void)
     785                 :             : {
     786                 :          37 :     const int   index[] = {
     787                 :             :         PROGRESS_DATACHECKSUMS_DBS_TOTAL,
     788                 :             :         PROGRESS_DATACHECKSUMS_DBS_DONE,
     789                 :             :         PROGRESS_DATACHECKSUMS_RELS_TOTAL,
     790                 :             :         PROGRESS_DATACHECKSUMS_RELS_DONE,
     791                 :             :         PROGRESS_DATACHECKSUMS_BLOCKS_TOTAL,
     792                 :             :         PROGRESS_DATACHECKSUMS_BLOCKS_DONE,
     793                 :             :     };
     794                 :             : 
     795                 :             :     int64       vals[lengthof(index)];
     796                 :             : 
     797         [ +  + ]:         259 :     for (size_t i = 0; i < lengthof(index); i++)
     798                 :         222 :         vals[i] = -1;
     799                 :             : 
     800                 :          37 :     pgstat_progress_update_multi_param(lengthof(index), index, vals);
     801                 :          37 : }
     802                 :             : 
     803                 :             : /*
     804                 :             :  * ProcessSingleRelationByOid
     805                 :             :  *      Process a single relation based on oid.
     806                 :             :  *
     807                 :             :  * Returns true if successful, and false if *aborted*. On error, an actual
     808                 :             :  * error is raised in the lower levels.
     809                 :             :  */
     810                 :             : static bool
     811                 :        6040 : ProcessSingleRelationByOid(Oid relationId, BufferAccessStrategy strategy)
     812                 :             : {
     813                 :             :     Relation    rel;
     814                 :        6040 :     bool        aborted = false;
     815                 :             : 
     816                 :        6040 :     StartTransactionCommand();
     817                 :             : 
     818                 :        6040 :     rel = try_relation_open(relationId, AccessShareLock);
     819         [ -  + ]:        6040 :     if (rel == NULL)
     820                 :             :     {
     821                 :             :         /*
     822                 :             :          * Relation no longer exists. We don't consider this an error since
     823                 :             :          * there are no pages in it that need data checksums, and thus return
     824                 :             :          * true. The worker operates off a list of relations generated at the
     825                 :             :          * start of processing, so relations being dropped in the meantime is
     826                 :             :          * to be expected.
     827                 :             :          */
     828                 :           0 :         CommitTransactionCommand();
     829                 :           0 :         pgstat_report_activity(STATE_IDLE, NULL);
     830                 :           0 :         return true;
     831                 :             :     }
     832                 :        6040 :     RelationGetSmgr(rel);
     833                 :             : 
     834         [ +  + ]:       30200 :     for (ForkNumber fnum = 0; fnum <= MAX_FORKNUM; fnum++)
     835                 :             :     {
     836         [ +  + ]:       24160 :         if (smgrexists(rel->rd_smgr, fnum))
     837                 :             :         {
     838         [ -  + ]:        7847 :             if (!ProcessSingleRelationFork(rel, fnum, strategy))
     839                 :             :             {
     840                 :           0 :                 aborted = true;
     841                 :           0 :                 break;
     842                 :             :             }
     843                 :             :         }
     844                 :             :     }
     845                 :        6040 :     relation_close(rel, AccessShareLock);
     846                 :             : 
     847                 :        6040 :     CommitTransactionCommand();
     848                 :        6040 :     pgstat_report_activity(STATE_IDLE, NULL);
     849                 :             : 
     850                 :        6040 :     return !aborted;
     851                 :             : }
     852                 :             : 
     853                 :             : /*
     854                 :             :  * ProcessDatabase
     855                 :             :  *      Enable data checksums in a single database.
     856                 :             :  *
     857                 :             :  * We do this by launching a dynamic background worker into this database, and
     858                 :             :  * waiting for it to finish.  We have to do this in a separate worker, since
     859                 :             :  * each process can only be connected to one database during its lifetime.
     860                 :             :  */
     861                 :             : static DataChecksumsWorkerResult
     862                 :          23 : ProcessDatabase(DataChecksumsWorkerDatabase *db)
     863                 :             : {
     864                 :             :     BackgroundWorker bgw;
     865                 :             :     BackgroundWorkerHandle *bgw_handle;
     866                 :             :     BgwHandleStatus status;
     867                 :             :     pid_t       pid;
     868                 :             :     uint64      invocation;
     869                 :             :     char        activity[NAMEDATALEN + 64];
     870                 :             :     DataChecksumsWorkerResult result;
     871                 :             : 
     872                 :          23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     873                 :             : 
     874                 :             :     /*
     875                 :             :      * Initialize result to FAILED.  The worker will change it to SUCCESSFUL
     876                 :             :      * if it completes successfully.
     877                 :             :      */
     878                 :          23 :     DataChecksumState->worker_result = DATACHECKSUMSWORKER_FAILED;
     879                 :          23 :     DataChecksumState->worker_pid = InvalidPid;
     880                 :             : 
     881                 :          23 :     invocation = ++DataChecksumState->worker_invocation_counter;
     882                 :          23 :     DataChecksumState->worker_invocation = invocation;
     883                 :          23 :     DataChecksumState->database_oid = db->dboid;
     884                 :             : 
     885                 :          23 :     LWLockRelease(DataChecksumsWorkerLock);
     886                 :             : 
     887                 :          23 :     memset(&bgw, 0, sizeof(bgw));
     888                 :          23 :     bgw.bgw_flags = BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
     889                 :          23 :     bgw.bgw_start_time = BgWorkerStart_RecoveryFinished;
     890                 :          23 :     snprintf(bgw.bgw_library_name, BGW_MAXLEN, "postgres");
     891                 :          23 :     snprintf(bgw.bgw_function_name, BGW_MAXLEN, "%s", "DataChecksumsWorkerMain");
     892                 :          23 :     snprintf(bgw.bgw_name, BGW_MAXLEN, "datachecksums worker");
     893                 :          23 :     snprintf(bgw.bgw_type, BGW_MAXLEN, "datachecksums worker");
     894                 :          23 :     bgw.bgw_restart_time = BGW_NEVER_RESTART;
     895                 :          23 :     bgw.bgw_notify_pid = MyProcPid;
     896                 :             :     /* pass the invocation number to the worker process */
     897                 :          23 :     bgw.bgw_main_arg = UInt64GetDatum(invocation);
     898                 :             : 
     899                 :             :     /*
     900                 :             :      * If there are no worker slots available, there is little we can do.  If
     901                 :             :      * we retry in a bit it's still unlikely that the user has managed to
     902                 :             :      * reconfigure in the meantime and we'd be run through retries fast.
     903                 :             :      */
     904         [ -  + ]:          23 :     if (!RegisterDynamicBackgroundWorker(&bgw, &bgw_handle))
     905                 :             :     {
     906         [ #  # ]:           0 :         ereport(WARNING,
     907                 :             :                 errmsg("could not start background worker for enabling data checksums in database \"%s\"",
     908                 :             :                        db->dbname),
     909                 :             :                 errhint("The \"%s\" setting might be too low.", "max_worker_processes"));
     910                 :           0 :         return DATACHECKSUMSWORKER_FAILED;
     911                 :             :     }
     912                 :             : 
     913                 :          23 :     status = WaitForBackgroundWorkerStartup(bgw_handle, &pid);
     914         [ -  + ]:          23 :     if (status == BGWH_STOPPED)
     915                 :             :     {
     916                 :             :         /*
     917                 :             :          * If the worker managed to start, and stop, before we got to waiting
     918                 :             :          * for it we can see a STOPPED status here without it being a failure.
     919                 :             :          */
     920                 :           0 :         LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
     921                 :             :         Assert(DataChecksumState->worker_invocation == invocation);
     922         [ #  # ]:           0 :         if (DataChecksumState->worker_result == DATACHECKSUMSWORKER_SUCCESSFUL)
     923                 :             :         {
     924                 :           0 :             LWLockRelease(DataChecksumsWorkerLock);
     925                 :           0 :             pgstat_report_activity(STATE_IDLE, NULL);
     926                 :           0 :             return DATACHECKSUMSWORKER_SUCCESSFUL;
     927                 :             :         }
     928                 :           0 :         LWLockRelease(DataChecksumsWorkerLock);
     929                 :             : 
     930         [ #  # ]:           0 :         ereport(WARNING,
     931                 :             :                 errmsg("could not start background worker for enabling data checksums in database \"%s\"",
     932                 :             :                        db->dbname),
     933                 :             :                 errhint("More details on the error might be found in the server log."));
     934                 :             : 
     935                 :             :         /*
     936                 :             :          * Heuristic to see if the database was dropped, and if it was we can
     937                 :             :          * treat it as not an error, else treat as fatal and error out.
     938                 :             :          */
     939         [ #  # ]:           0 :         if (DatabaseExists(db->dboid))
     940                 :           0 :             return DATACHECKSUMSWORKER_FAILED;
     941                 :             :         else
     942                 :           0 :             return DATACHECKSUMSWORKER_DROPDB;
     943                 :             :     }
     944                 :             : 
     945                 :             :     /*
     946                 :             :      * If the postmaster crashed we cannot end up with a processed database so
     947                 :             :      * we have no alternative other than exiting. When enabling checksums we
     948                 :             :      * won't at this time have changed the data checksums state in pg_control
     949                 :             :      * to enabled so when the cluster comes back up processing will have to be
     950                 :             :      * restarted.
     951                 :             :      */
     952         [ -  + ]:          23 :     if (status == BGWH_POSTMASTER_DIED)
     953         [ #  # ]:           0 :         ereport(FATAL,
     954                 :             :                 errcode(ERRCODE_ADMIN_SHUTDOWN),
     955                 :             :                 errmsg("cannot enable data checksums without the postmaster process"),
     956                 :             :                 errhint("Restart the database and restart data checksum processing by calling pg_enable_data_checksums()."));
     957                 :             : 
     958                 :             :     Assert(status == BGWH_STARTED);
     959         [ +  - ]:          23 :     ereport(LOG,
     960                 :             :             errmsg("initiating data checksum processing in database \"%s\"",
     961                 :             :                    db->dbname));
     962                 :             : 
     963                 :             :     /* Save the pid of the worker so we can signal it later */
     964                 :          23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     965                 :             :     Assert(DataChecksumState->worker_invocation == invocation);
     966                 :          23 :     DataChecksumState->worker_pid = pid;
     967                 :          23 :     LWLockRelease(DataChecksumsWorkerLock);
     968                 :             : 
     969                 :          23 :     snprintf(activity, sizeof(activity) - 1,
     970                 :             :              "Waiting for worker in database %s (pid %ld)", db->dbname, (long) pid);
     971                 :          23 :     pgstat_report_activity(STATE_RUNNING, activity);
     972                 :             : 
     973                 :          23 :     status = WaitForBackgroundWorkerShutdown(bgw_handle);
     974         [ -  + ]:          22 :     if (status == BGWH_POSTMASTER_DIED)
     975         [ #  # ]:           0 :         ereport(FATAL,
     976                 :             :                 errcode(ERRCODE_ADMIN_SHUTDOWN),
     977                 :             :                 errmsg("postmaster exited during data checksum processing in \"%s\"",
     978                 :             :                        db->dbname),
     979                 :             :                 errhint("Restart the database and restart data checksum processing by calling pg_enable_data_checksums()."));
     980                 :             : 
     981                 :          22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
     982                 :             :     Assert(DataChecksumState->worker_invocation == invocation);
     983                 :          22 :     result = DataChecksumState->worker_result;
     984                 :          22 :     DataChecksumState->worker_pid = InvalidPid;
     985                 :          22 :     LWLockRelease(DataChecksumsWorkerLock);
     986                 :             : 
     987         [ -  + ]:          22 :     if (result == DATACHECKSUMSWORKER_ABORTED)
     988         [ #  # ]:           0 :         ereport(LOG,
     989                 :             :                 errmsg("data checksums processing was aborted in database \"%s\"",
     990                 :             :                        db->dbname));
     991                 :          22 :     pgstat_report_activity(STATE_IDLE, NULL);
     992                 :          22 :     return result;
     993                 :             : }
     994                 :             : 
     995                 :             : /*
     996                 :             :  * launcher_exit
     997                 :             :  *
     998                 :             :  * Internal routine for cleaning up state when a launcher process which has
     999                 :             :  * performed checksum operations exits. A launcher process which is exiting due
    1000                 :             :  * to a duplicate started launcher does not need to perform any cleanup and
    1001                 :             :  * this function should not be called. Otherwise, we need to clean up the abort
    1002                 :             :  * flag to ensure that processing can be started again if it was previously
    1003                 :             :  * aborted (note: started again, *not* restarted from where it left off).
    1004                 :             :  */
    1005                 :             : static void
    1006                 :          14 : launcher_exit(int code, Datum arg)
    1007                 :             : {
    1008                 :          14 :     abort_requested = false;
    1009                 :             : 
    1010         [ +  + ]:          14 :     if (launcher_running)
    1011                 :             :     {
    1012                 :           2 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1013         [ +  + ]:           2 :         if (DataChecksumState->worker_pid != InvalidPid)
    1014                 :             :         {
    1015         [ +  - ]:           1 :             ereport(LOG,
    1016                 :             :                     errmsg("data checksums launcher exiting while worker is still running, signalling worker"));
    1017                 :           1 :             kill(DataChecksumState->worker_pid, SIGTERM);
    1018                 :           1 :             DataChecksumState->worker_pid = InvalidPid;
    1019                 :             :         }
    1020                 :           2 :         LWLockRelease(DataChecksumsWorkerLock);
    1021                 :             :     }
    1022                 :             : 
    1023                 :             :     /*
    1024                 :             :      * If the launcher is exiting before data checksums are enabled then set
    1025                 :             :      * the state to off since processing cannot be resumed.
    1026                 :             :      */
    1027         [ +  + ]:          14 :     if (DataChecksumsInProgressOn())
    1028                 :           1 :         SetDataChecksumsOff();
    1029                 :             : 
    1030                 :          14 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1031                 :          14 :     launcher_running = false;
    1032                 :          14 :     DataChecksumState->launcher_running = false;
    1033                 :          14 :     LWLockRelease(DataChecksumsWorkerLock);
    1034                 :          14 : }
    1035                 :             : 
    1036                 :             : /*
    1037                 :             :  * launcher_cancel_handler
    1038                 :             :  *
    1039                 :             :  * Internal routine for reacting to SIGINT and flagging the worker to abort.
    1040                 :             :  * The worker won't be interrupted immediately but will check for abort flag
    1041                 :             :  * between each block in a relation.
    1042                 :             :  */
    1043                 :             : static void
    1044                 :           0 : launcher_cancel_handler(SIGNAL_ARGS)
    1045                 :             : {
    1046                 :           0 :     int         save_errno = errno;
    1047                 :             : 
    1048                 :           0 :     abort_requested = true;
    1049                 :             : 
    1050                 :             :     /*
    1051                 :             :      * There is no sleeping in the main loop, the flag will be checked
    1052                 :             :      * periodically in ProcessSingleRelationFork. The worker does however
    1053                 :             :      * sleep when waiting for concurrent transactions to end so we still need
    1054                 :             :      * to set the latch.
    1055                 :             :      */
    1056                 :           0 :     SetLatch(MyLatch);
    1057                 :             : 
    1058                 :           0 :     errno = save_errno;
    1059                 :           0 : }
    1060                 :             : 
    1061                 :             : /*
    1062                 :             :  * WaitForAllTransactionsToFinish
    1063                 :             :  *      Blocks awaiting all current transactions to finish
    1064                 :             :  *
    1065                 :             :  * Returns when all transactions which are active at the call of the function
    1066                 :             :  * have ended.
    1067                 :             :  *
    1068                 :             :  * NB: this will return early, if aborted by SIGINT or if the target state
    1069                 :             :  * is changed while we're running.
    1070                 :             :  */
    1071                 :             : static void
    1072                 :           9 : WaitForAllTransactionsToFinish(void)
    1073                 :             : {
    1074                 :             :     TransactionId waitforxid;
    1075                 :             : 
    1076                 :           9 :     LWLockAcquire(XidGenLock, LW_SHARED);
    1077                 :           9 :     waitforxid = XidFromFullTransactionId(TransamVariables->nextXid);
    1078                 :           9 :     LWLockRelease(XidGenLock);
    1079                 :             : 
    1080         [ -  + ]:           9 :     while (TransactionIdPrecedes(GetOldestActiveTransactionId(false, true), waitforxid))
    1081                 :             :     {
    1082                 :             :         char        activity[64];
    1083                 :             :         int         rc;
    1084                 :             : 
    1085                 :             :         /* Oldest running xid is older than us, so wait */
    1086                 :           0 :         snprintf(activity,
    1087                 :             :                  sizeof(activity),
    1088                 :             :                  "Waiting for transactions older than %u to end",
    1089                 :             :                  waitforxid);
    1090                 :           0 :         pgstat_report_activity(STATE_RUNNING, activity);
    1091                 :             : 
    1092                 :             :         /* Retry every 3 seconds */
    1093                 :           0 :         ResetLatch(MyLatch);
    1094                 :           0 :         rc = WaitLatch(MyLatch,
    1095                 :             :                        WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH,
    1096                 :             :                        3000,
    1097                 :             :                        WAIT_EVENT_CHECKSUM_ENABLE_STARTCONDITION);
    1098                 :             : 
    1099                 :             :         /*
    1100                 :             :          * If the postmaster died, bail out.  But first print a log message to
    1101                 :             :          * note that the checksumming didn't complete.
    1102                 :             :          */
    1103         [ #  # ]:           0 :         if (rc & WL_POSTMASTER_DEATH)
    1104         [ #  # ]:           0 :             ereport(FATAL,
    1105                 :             :                     errcode(ERRCODE_ADMIN_SHUTDOWN),
    1106                 :             :                     errmsg("postmaster exited during data checksums processing"),
    1107                 :             :                     errhint("Data checksums processing must be restarted manually after cluster restart."));
    1108                 :             : 
    1109         [ #  # ]:           0 :         CHECK_FOR_INTERRUPTS();
    1110         [ #  # ]:           0 :         CHECK_FOR_LAUNCHER_ABORT_REQUEST();
    1111                 :             : 
    1112         [ #  # ]:           0 :         if (abort_requested)
    1113                 :           0 :             break;
    1114                 :             :     }
    1115                 :             : 
    1116                 :           9 :     pgstat_report_activity(STATE_IDLE, NULL);
    1117                 :           9 :     return;
    1118                 :             : }
    1119                 :             : 
    1120                 :             : /*
    1121                 :             :  * DataChecksumsWorkerLauncherMain
    1122                 :             :  *
    1123                 :             :  * Main function for launching dynamic background workers for processing data
    1124                 :             :  * checksums in databases. This function has the bgworker management, with
    1125                 :             :  * ProcessAllDatabases being responsible for looping over the databases and
    1126                 :             :  * initiating processing.
    1127                 :             :  */
    1128                 :             : void
    1129                 :          14 : DataChecksumsWorkerLauncherMain(Datum arg)
    1130                 :             : {
    1131                 :             : 
    1132         [ -  + ]:          14 :     ereport(DEBUG1,
    1133                 :             :             errmsg("background worker \"datachecksums launcher\" started"));
    1134                 :             : 
    1135                 :          14 :     pqsignal(SIGTERM, die);
    1136                 :          14 :     pqsignal(SIGINT, launcher_cancel_handler);
    1137                 :          14 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    1138                 :          14 :     pqsignal(SIGUSR2, PG_SIG_IGN);
    1139                 :             : 
    1140                 :          14 :     BackgroundWorkerUnblockSignals();
    1141                 :             : 
    1142                 :          14 :     MyBackendType = B_DATACHECKSUMSWORKER_LAUNCHER;
    1143                 :          14 :     init_ps_display(NULL);
    1144                 :             : 
    1145                 :          14 :     INJECTION_POINT("datachecksumsworker-launcher-delay", NULL);
    1146                 :             : 
    1147                 :          14 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1148                 :             : 
    1149         [ -  + ]:          14 :     if (DataChecksumState->launcher_running)
    1150                 :             :     {
    1151         [ #  # ]:           0 :         ereport(LOG,
    1152                 :             :                 errmsg("background worker \"datachecksums launcher\" already running, exiting"));
    1153                 :             :         /* Launcher was already running, let it finish */
    1154                 :           0 :         LWLockRelease(DataChecksumsWorkerLock);
    1155                 :           0 :         return;
    1156                 :             :     }
    1157                 :             : 
    1158                 :          14 :     on_shmem_exit(launcher_exit, 0);
    1159                 :          14 :     launcher_running = true;
    1160                 :             : 
    1161                 :             :     /* Initialize a connection to shared catalogs only */
    1162                 :          14 :     BackgroundWorkerInitializeConnectionByOid(InvalidOid, InvalidOid, 0);
    1163                 :             : 
    1164                 :          14 :     operation = DataChecksumState->launch_operation;
    1165                 :          14 :     DataChecksumState->launcher_running = true;
    1166                 :          14 :     DataChecksumState->operation = operation;
    1167                 :          14 :     DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1168                 :          14 :     DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1169                 :          14 :     LWLockRelease(DataChecksumsWorkerLock);
    1170                 :             : 
    1171                 :             :     /*
    1172                 :             :      * The target state can change while we are busy enabling/disabling
    1173                 :             :      * checksums, if the user calls pg_disable/enable_data_checksums() before
    1174                 :             :      * we are finished with the previous request. In that case, we will loop
    1175                 :             :      * back here, to process the new request.
    1176                 :             :      */
    1177                 :          14 : again:
    1178                 :             : 
    1179                 :          14 :     pgstat_progress_start_command(PROGRESS_COMMAND_DATACHECKSUMS,
    1180                 :             :                                   InvalidOid);
    1181                 :          14 :     ResetDataChecksumsProgressCounters();
    1182                 :             : 
    1183         [ +  + ]:          14 :     if (operation == ENABLE_DATACHECKSUMS)
    1184                 :             :     {
    1185                 :             :         /*
    1186                 :             :          * If we are asked to enable checksums in a cluster which already has
    1187                 :             :          * checksums enabled, exit immediately as there is nothing more to do.
    1188                 :             :          */
    1189         [ -  + ]:           9 :         if (DataChecksumsNeedVerify())
    1190                 :           0 :             goto done;
    1191                 :             : 
    1192         [ +  - ]:           9 :         ereport(LOG,
    1193                 :             :                 errmsg("enabling data checksums requested, starting data checksum calculation"));
    1194                 :             : 
    1195                 :             :         /*
    1196                 :             :          * Set the state to inprogress-on and wait on the procsignal barrier.
    1197                 :             :          */
    1198                 :           9 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1199                 :             :                                      PROGRESS_DATACHECKSUMS_PHASE_ENABLING);
    1200                 :           9 :         SetDataChecksumsOnInProgress();
    1201                 :             : 
    1202                 :             :         /*
    1203                 :             :          * All backends are now in inprogress-on state and are writing data
    1204                 :             :          * checksums.  Start processing all data at rest.
    1205                 :             :          */
    1206         [ -  + ]:           9 :         if (!ProcessAllDatabases())
    1207                 :             :         {
    1208                 :             :             /*
    1209                 :             :              * If the target state changed during processing then it's not a
    1210                 :             :              * failure, so restart processing instead.
    1211                 :             :              */
    1212         [ #  # ]:           0 :             CHECK_FOR_LAUNCHER_ABORT_REQUEST();
    1213         [ #  # ]:           0 :             if (abort_requested)
    1214                 :           0 :                 goto done;
    1215         [ #  # ]:           0 :             ereport(ERROR,
    1216                 :             :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
    1217                 :             :                     errmsg("unable to enable data checksums in cluster"));
    1218                 :             :         }
    1219                 :             : 
    1220                 :             :         /*
    1221                 :             :          * Data checksums have been set on all pages, set the state to on in
    1222                 :             :          * order to instruct backends to validate checksums on reading.
    1223                 :             :          */
    1224                 :           7 :         SetDataChecksumsOn();
    1225                 :             : 
    1226         [ +  - ]:           7 :         ereport(LOG,
    1227                 :             :                 errmsg("data checksums are now enabled"));
    1228                 :             :     }
    1229         [ -  + ]:           5 :     else if (operation == DISABLE_DATACHECKSUMS)
    1230                 :             :     {
    1231         [ +  - ]:           5 :         ereport(LOG,
    1232                 :             :                 errmsg("disabling data checksums requested"));
    1233                 :             : 
    1234                 :           5 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1235                 :             :                                      PROGRESS_DATACHECKSUMS_PHASE_DISABLING);
    1236                 :           5 :         SetDataChecksumsOff();
    1237         [ +  - ]:           5 :         ereport(LOG,
    1238                 :             :                 errmsg("data checksums are now disabled"));
    1239                 :             :     }
    1240                 :             :     else
    1241                 :             :         Assert(false);
    1242                 :             : 
    1243                 :           0 : done:
    1244                 :             : 
    1245                 :             :     /*
    1246                 :             :      * This state will only be displayed for a fleeting moment, but for the
    1247                 :             :      * sake of correctness it is still added before ending the command.
    1248                 :             :      */
    1249                 :          12 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1250                 :             :                                  PROGRESS_DATACHECKSUMS_PHASE_DONE);
    1251                 :             : 
    1252                 :             :     /*
    1253                 :             :      * All done. But before we exit, check if the target state was changed
    1254                 :             :      * while we were running. In that case we will have to start all over
    1255                 :             :      * again.
    1256                 :             :      */
    1257                 :          12 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1258         [ -  + ]:          12 :     if (DataChecksumState->launch_operation != operation)
    1259                 :             :     {
    1260                 :           0 :         DataChecksumState->operation = DataChecksumState->launch_operation;
    1261                 :           0 :         operation = DataChecksumState->launch_operation;
    1262                 :           0 :         DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1263                 :           0 :         DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1264                 :           0 :         LWLockRelease(DataChecksumsWorkerLock);
    1265                 :           0 :         goto again;
    1266                 :             :     }
    1267                 :             : 
    1268                 :             :     /* Shut down progress reporting as we are done */
    1269                 :          12 :     pgstat_progress_end_command();
    1270                 :             : 
    1271                 :          12 :     launcher_running = false;
    1272                 :          12 :     DataChecksumState->launcher_running = false;
    1273                 :          12 :     LWLockRelease(DataChecksumsWorkerLock);
    1274                 :             : }
    1275                 :             : 
    1276                 :             : /*
    1277                 :             :  * ProcessAllDatabases
    1278                 :             :  *      Compute the list of all databases and process checksums in each
    1279                 :             :  *
    1280                 :             :  * This will generate a list of databases to process for enabling checksums.
    1281                 :             :  * If a database encounters a failure then processing will end immediately and
    1282                 :             :  * return an error.
    1283                 :             :  */
    1284                 :             : static bool
    1285                 :           9 : ProcessAllDatabases(void)
    1286                 :             : {
    1287                 :             :     List       *DatabaseList;
    1288                 :           9 :     int         cumulative_total = 0;
    1289                 :             : 
    1290                 :             :     /* Set up so first run processes shared catalogs, not once in every db */
    1291                 :           9 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1292                 :           9 :     DataChecksumState->process_shared_catalogs = true;
    1293                 :           9 :     LWLockRelease(DataChecksumsWorkerLock);
    1294                 :             : 
    1295                 :             :     /* Get a list of all databases to process */
    1296                 :           9 :     WaitForAllTransactionsToFinish();
    1297                 :           9 :     DatabaseList = BuildDatabaseList();
    1298                 :             : 
    1299                 :             :     /*
    1300                 :             :      * Update progress reporting with the total number of databases we need to
    1301                 :             :      * process.  This number should not be changed during processing, the
    1302                 :             :      * columns for processed databases is instead increased such that it can
    1303                 :             :      * be compared against the total.
    1304                 :             :      */
    1305                 :             :     {
    1306                 :           9 :         const int   index[] = {
    1307                 :             :             PROGRESS_DATACHECKSUMS_DBS_TOTAL,
    1308                 :             :             PROGRESS_DATACHECKSUMS_DBS_DONE,
    1309                 :             :         };
    1310                 :             : 
    1311                 :             :         int64       vals[2];
    1312                 :             : 
    1313                 :           9 :         vals[0] = list_length(DatabaseList);
    1314                 :           9 :         vals[1] = 0;
    1315                 :             : 
    1316                 :           9 :         pgstat_progress_update_multi_param(2, index, vals);
    1317                 :             :     }
    1318                 :             : 
    1319   [ +  -  +  +  :          37 :     foreach_ptr(DataChecksumsWorkerDatabase, db, DatabaseList)
                   +  + ]
    1320                 :             :     {
    1321                 :             :         DataChecksumsWorkerResult result;
    1322                 :             : 
    1323                 :          23 :         result = ProcessDatabase(db);
    1324                 :             : 
    1325                 :             : #ifdef USE_INJECTION_POINTS
    1326                 :             :         /* Allow a test process to alter the result of the operation */
    1327         [ +  + ]:          22 :         if (IS_INJECTION_POINT_ATTACHED("datachecksumsworker-fail-db-result"))
    1328                 :             :         {
    1329                 :           1 :             result = DATACHECKSUMSWORKER_FAILED;
    1330                 :           1 :             INJECTION_POINT_CACHED("datachecksumsworker-fail-db-result",
    1331                 :             :                                    db->dbname);
    1332                 :             :         }
    1333                 :             : #endif
    1334                 :             : 
    1335                 :          22 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_DBS_DONE,
    1336                 :             :                                      ++cumulative_total);
    1337                 :             : 
    1338         [ +  + ]:          22 :         if (result == DATACHECKSUMSWORKER_FAILED)
    1339                 :             :         {
    1340                 :             :             /*
    1341                 :             :              * Disable checksums on cluster, because we failed one of the
    1342                 :             :              * databases and this is an all or nothing process.
    1343                 :             :              */
    1344                 :           1 :             SetDataChecksumsOff();
    1345         [ +  - ]:           1 :             ereport(ERROR,
    1346                 :             :                     errcode(ERRCODE_INSUFFICIENT_RESOURCES),
    1347                 :             :                     errmsg("data checksums failed to get enabled in all databases, aborting"),
    1348                 :             :                     errhint("The server log might have more information on the cause of the error."));
    1349                 :             :         }
    1350   [ +  -  -  + ]:          21 :         else if (result == DATACHECKSUMSWORKER_ABORTED || abort_requested)
    1351                 :             :         {
    1352                 :             :             /* Abort flag set, so exit the whole process */
    1353                 :           0 :             return false;
    1354                 :             :         }
    1355         [ -  + ]:          21 :         else if (result == DATACHECKSUMSWORKER_DROPDB)
    1356                 :             :         {
    1357                 :             :             /*
    1358                 :             :              * Ignore databases that were dropped before their worker could
    1359                 :             :              * process them, and continue with the remaining databases.
    1360                 :             :              */
    1361                 :           0 :             continue;
    1362                 :             :         }
    1363                 :             : 
    1364                 :             :         /*
    1365                 :             :          * When one database has completed, it will have done shared catalogs
    1366                 :             :          * so we don't have to process them again.
    1367                 :             :          */
    1368                 :          21 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1369                 :          21 :         DataChecksumState->process_shared_catalogs = false;
    1370                 :          21 :         LWLockRelease(DataChecksumsWorkerLock);
    1371                 :             :     }
    1372                 :             : 
    1373                 :           7 :     FreeDatabaseList(DatabaseList);
    1374                 :             : 
    1375                 :           7 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1376                 :             :                                  PROGRESS_DATACHECKSUMS_PHASE_WAITING_BARRIER);
    1377                 :           7 :     return true;
    1378                 :             : }
    1379                 :             : 
    1380                 :             : /*
    1381                 :             :  * DataChecksumsShmemRequest
    1382                 :             :  *      Request datachecksumsworker-related shared memory
    1383                 :             :  */
    1384                 :             : static void
    1385                 :        1259 : DataChecksumsShmemRequest(void *arg)
    1386                 :             : {
    1387                 :        1259 :     ShmemRequestStruct(.name = "DataChecksumsWorker Data",
    1388                 :             :                        .size = sizeof(DataChecksumsStateStruct),
    1389                 :             :                        .ptr = (void **) &DataChecksumState,
    1390                 :             :         );
    1391                 :        1259 : }
    1392                 :             : 
    1393                 :             : /*
    1394                 :             :  * DatabaseExists
    1395                 :             :  *
    1396                 :             :  * Scans the system catalog to check if a database with the given Oid exists
    1397                 :             :  * and returns true if it is found and valid, else false. Note, we cannot use
    1398                 :             :  * database_is_invalid_oid here as it will ERROR out, and we want to gracefully
    1399                 :             :  * handle errors.
    1400                 :             :  */
    1401                 :             : static bool
    1402                 :           0 : DatabaseExists(Oid dboid)
    1403                 :             : {
    1404                 :             :     Relation    rel;
    1405                 :             :     ScanKeyData skey;
    1406                 :             :     SysScanDesc scan;
    1407                 :             :     bool        found;
    1408                 :             :     HeapTuple   tuple;
    1409                 :             :     Form_pg_database pg_database_tuple;
    1410                 :             : 
    1411                 :           0 :     StartTransactionCommand();
    1412                 :             : 
    1413                 :           0 :     rel = table_open(DatabaseRelationId, AccessShareLock);
    1414                 :           0 :     ScanKeyInit(&skey,
    1415                 :             :                 Anum_pg_database_oid,
    1416                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
    1417                 :             :                 ObjectIdGetDatum(dboid));
    1418                 :           0 :     scan = systable_beginscan(rel, DatabaseOidIndexId, true, SnapshotSelf,
    1419                 :             :                               1, &skey);
    1420                 :           0 :     tuple = systable_getnext(scan);
    1421                 :           0 :     found = HeapTupleIsValid(tuple);
    1422                 :             : 
    1423                 :             :     /* If the Oid exists, ensure that it's not partially dropped */
    1424         [ #  # ]:           0 :     if (found)
    1425                 :             :     {
    1426                 :           0 :         pg_database_tuple = (Form_pg_database) GETSTRUCT(tuple);
    1427         [ #  # ]:           0 :         if (database_is_invalid_form(pg_database_tuple))
    1428                 :           0 :             found = false;
    1429                 :             :     }
    1430                 :             : 
    1431                 :           0 :     systable_endscan(scan);
    1432                 :           0 :     table_close(rel, AccessShareLock);
    1433                 :             : 
    1434                 :           0 :     CommitTransactionCommand();
    1435                 :             : 
    1436                 :           0 :     return found;
    1437                 :             : }
    1438                 :             : 
    1439                 :             : /*
    1440                 :             :  * BuildDatabaseList
    1441                 :             :  *      Compile a list of all currently available databases in the cluster
    1442                 :             :  *
    1443                 :             :  * This creates the list of databases for the datachecksumsworker workers to
    1444                 :             :  * add checksums to. If the caller wants to ensure that no concurrently
    1445                 :             :  * running CREATE DATABASE calls exist, this needs to be preceded by a call
    1446                 :             :  * to WaitForAllTransactionsToFinish().
    1447                 :             :  */
    1448                 :             : static List *
    1449                 :           9 : BuildDatabaseList(void)
    1450                 :             : {
    1451                 :           9 :     List       *DatabaseList = NIL;
    1452                 :             :     Relation    rel;
    1453                 :             :     TableScanDesc scan;
    1454                 :             :     HeapTuple   tup;
    1455                 :           9 :     MemoryContext ctx = CurrentMemoryContext;
    1456                 :             :     MemoryContext oldctx;
    1457                 :             : 
    1458                 :           9 :     StartTransactionCommand();
    1459                 :             : 
    1460                 :           9 :     rel = table_open(DatabaseRelationId, AccessShareLock);
    1461                 :           9 :     scan = table_beginscan_catalog(rel, 0, NULL);
    1462                 :             : 
    1463         [ +  + ]:          36 :     while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    1464                 :             :     {
    1465                 :          27 :         Form_pg_database pgdb = (Form_pg_database) GETSTRUCT(tup);
    1466                 :             :         DataChecksumsWorkerDatabase *db;
    1467                 :             : 
    1468                 :          27 :         oldctx = MemoryContextSwitchTo(ctx);
    1469                 :             : 
    1470                 :          27 :         db = (DataChecksumsWorkerDatabase *) palloc0(sizeof(DataChecksumsWorkerDatabase));
    1471                 :             : 
    1472                 :          27 :         db->dboid = pgdb->oid;
    1473                 :          27 :         db->dbname = pstrdup(NameStr(pgdb->datname));
    1474                 :             : 
    1475                 :          27 :         DatabaseList = lappend(DatabaseList, db);
    1476                 :             : 
    1477                 :          27 :         MemoryContextSwitchTo(oldctx);
    1478                 :             :     }
    1479                 :             : 
    1480                 :           9 :     table_endscan(scan);
    1481                 :           9 :     table_close(rel, AccessShareLock);
    1482                 :             : 
    1483                 :           9 :     CommitTransactionCommand();
    1484                 :             : 
    1485                 :           9 :     return DatabaseList;
    1486                 :             : }
    1487                 :             : 
    1488                 :             : static void
    1489                 :           7 : FreeDatabaseList(List *dblist)
    1490                 :             : {
    1491         [ -  + ]:           7 :     if (!dblist)
    1492                 :           0 :         return;
    1493                 :             : 
    1494   [ +  -  +  +  :          35 :     foreach_ptr(DataChecksumsWorkerDatabase, db, dblist)
                   +  + ]
    1495                 :             :     {
    1496         [ +  - ]:          21 :         if (db->dbname != NULL)
    1497                 :          21 :             pfree(db->dbname);
    1498                 :             :     }
    1499                 :             : 
    1500                 :           7 :     list_free_deep(dblist);
    1501                 :             : }
    1502                 :             : 
    1503                 :             : /*
    1504                 :             :  * BuildRelationList
    1505                 :             :  *      Compile a list of relations in the database
    1506                 :             :  *
    1507                 :             :  * Returns a list of OIDs for the requested relation types. If temp_relations
    1508                 :             :  * is True then only temporary relations with storage are returned.  If
    1509                 :             :  * temp_relations is False then non-temporary relations with storage are
    1510                 :             :  * returned.  If include_shared is True then shared relations are included as
    1511                 :             :  * well in a non-temporary list. include_shared has no relevance when building
    1512                 :             :  * a list of temporary relations.
    1513                 :             :  */
    1514                 :             : static List *
    1515                 :          69 : BuildRelationList(bool temp_relations, bool include_shared)
    1516                 :             : {
    1517                 :          69 :     List       *RelationList = NIL;
    1518                 :             :     Relation    rel;
    1519                 :             :     TableScanDesc scan;
    1520                 :             :     HeapTuple   tup;
    1521                 :          69 :     MemoryContext ctx = CurrentMemoryContext;
    1522                 :             :     MemoryContext oldctx;
    1523                 :             : 
    1524                 :          69 :     StartTransactionCommand();
    1525                 :             : 
    1526                 :          69 :     rel = table_open(RelationRelationId, AccessShareLock);
    1527                 :          69 :     scan = table_beginscan_catalog(rel, 0, NULL);
    1528                 :             : 
    1529         [ +  + ]:       31371 :     while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
    1530                 :             :     {
    1531                 :       31302 :         Form_pg_class pgc = (Form_pg_class) GETSTRUCT(tup);
    1532                 :             : 
    1533   [ +  +  +  +  :       31302 :         if (!RELKIND_HAS_STORAGE(pgc->relkind))
          +  -  +  +  +  
                      - ]
    1534                 :       11247 :             continue;
    1535                 :             : 
    1536                 :             :         /* Only include temporary relations when explicitly asked to */
    1537         [ +  + ]:       20055 :         if (pgc->relpersistence == RELPERSISTENCE_TEMP)
    1538                 :             :         {
    1539         [ +  + ]:           3 :             if (!temp_relations)
    1540                 :           1 :                 continue;
    1541                 :             :         }
    1542                 :             :         else
    1543                 :             :         {
    1544                 :             :             /*
    1545                 :             :              * If we are only interested in temp relations then continue
    1546                 :             :              * immediately as the current relation isn't a temp relation.
    1547                 :             :              */
    1548         [ +  + ]:       20052 :             if (temp_relations)
    1549                 :       13368 :                 continue;
    1550                 :             : 
    1551   [ +  +  +  + ]:        6684 :             if (pgc->relisshared && !include_shared)
    1552                 :         644 :                 continue;
    1553                 :             :         }
    1554                 :             : 
    1555                 :        6042 :         oldctx = MemoryContextSwitchTo(ctx);
    1556                 :        6042 :         RelationList = lappend_oid(RelationList, pgc->oid);
    1557                 :        6042 :         MemoryContextSwitchTo(oldctx);
    1558                 :             :     }
    1559                 :             : 
    1560                 :          69 :     table_endscan(scan);
    1561                 :          69 :     table_close(rel, AccessShareLock);
    1562                 :             : 
    1563                 :          69 :     CommitTransactionCommand();
    1564                 :             : 
    1565                 :          69 :     return RelationList;
    1566                 :             : }
    1567                 :             : 
    1568                 :             : /*
    1569                 :             :  * DataChecksumsWorkerMain
    1570                 :             :  *
    1571                 :             :  * Main function for enabling checksums in a single database. This is the
    1572                 :             :  * function set as the bgw_function_name in the dynamic background worker
    1573                 :             :  * process initiated for each database by the worker launcher. After enabling
    1574                 :             :  * data checksums in each applicable relation in the database, it will wait for
    1575                 :             :  * all temporary relations that were present when the function started to
    1576                 :             :  * disappear before returning. This is required since we cannot rewrite
    1577                 :             :  * existing temporary relations with data checksums.
    1578                 :             :  */
    1579                 :             : void
    1580                 :          23 : DataChecksumsWorkerMain(Datum arg)
    1581                 :             : {
    1582                 :             :     Oid         dboid;
    1583                 :          23 :     List       *RelationList = NIL;
    1584                 :          23 :     List       *InitialTempTableList = NIL;
    1585                 :             :     BufferAccessStrategy strategy;
    1586                 :          23 :     bool        aborted = false;
    1587                 :             :     int64       rels_done;
    1588                 :             :     bool        process_shared;
    1589                 :             : #ifdef USE_INJECTION_POINTS
    1590                 :          23 :     bool        retried = false;
    1591                 :             : #endif
    1592                 :             : 
    1593                 :          23 :     worker_invocation = DatumGetUInt64(arg);
    1594                 :             : 
    1595                 :          23 :     operation = ENABLE_DATACHECKSUMS;
    1596                 :             : 
    1597                 :          23 :     pqsignal(SIGTERM, die);
    1598                 :          23 :     pqsignal(SIGUSR1, procsignal_sigusr1_handler);
    1599                 :             : 
    1600                 :          23 :     BackgroundWorkerUnblockSignals();
    1601                 :             : 
    1602                 :          23 :     MyBackendType = B_DATACHECKSUMSWORKER_WORKER;
    1603                 :          23 :     init_ps_display(NULL);
    1604                 :             : 
    1605                 :          23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_SHARED);
    1606         [ -  + ]:          23 :     if (DataChecksumState->worker_invocation != worker_invocation)
    1607                 :             :     {
    1608                 :           0 :         LWLockRelease(DataChecksumsWorkerLock);
    1609                 :           0 :         return;
    1610                 :             :     }
    1611                 :          23 :     dboid = DataChecksumState->database_oid;
    1612                 :          23 :     LWLockRelease(DataChecksumsWorkerLock);
    1613                 :             : 
    1614                 :          23 :     BackgroundWorkerInitializeConnectionByOid(dboid, InvalidOid,
    1615                 :             :                                               BGWORKER_BYPASS_ALLOWCONN);
    1616                 :             : 
    1617                 :             :     /* worker will have a separate entry in pg_stat_progress_data_checksums */
    1618                 :          23 :     pgstat_progress_start_command(PROGRESS_COMMAND_DATACHECKSUMS,
    1619                 :             :                                   InvalidOid);
    1620                 :          23 :     ResetDataChecksumsProgressCounters();
    1621                 :             : 
    1622                 :             :     /*
    1623                 :             :      * Get a list of all temp tables present as we start in this database. We
    1624                 :             :      * need to wait until they are all gone before we exit.  For the list of
    1625                 :             :      * relations to enable checksums in, check if shared catalogs have been
    1626                 :             :      * processed already.
    1627                 :             :      */
    1628                 :          23 :     InitialTempTableList = BuildRelationList(true, false);
    1629                 :          23 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1630         [ -  + ]:          23 :     if (DataChecksumState->worker_invocation != worker_invocation)
    1631                 :             :     {
    1632                 :           0 :         LWLockRelease(DataChecksumsWorkerLock);
    1633                 :           0 :         return;
    1634                 :             :     }
    1635                 :          23 :     process_shared = DataChecksumState->process_shared_catalogs;
    1636                 :             : 
    1637                 :             :     /*
    1638                 :             :      * Enable vacuum cost delay, if any.  While this process isn't doing any
    1639                 :             :      * vacuuming, we are re-using the infrastructure that vacuum cost delay
    1640                 :             :      * provides rather than inventing something bespoke. This is an internal
    1641                 :             :      * implementation detail and care should be taken to avoid it bleeding
    1642                 :             :      * through to the user to avoid confusion.
    1643                 :             :      *
    1644                 :             :      * VacuumUpdateCosts() propagates the values to the variables actually
    1645                 :             :      * read by vacuum_delay_point().
    1646                 :             :      */
    1647                 :          23 :     VacuumCostDelay = DataChecksumState->cost_delay;
    1648                 :          23 :     VacuumCostLimit = DataChecksumState->cost_limit;
    1649                 :          23 :     LWLockRelease(DataChecksumsWorkerLock);
    1650                 :          23 :     VacuumUpdateCosts();
    1651                 :          23 :     VacuumCostBalance = 0;
    1652                 :             : 
    1653                 :             :     /*
    1654                 :             :      * Create and set the vacuum strategy as our buffer strategy.
    1655                 :             :      */
    1656                 :          23 :     strategy = GetAccessStrategy(BAS_VACUUM);
    1657                 :             : 
    1658                 :          23 :     RelationList = BuildRelationList(false, process_shared);
    1659                 :             : 
    1660                 :             :     /* Update the total number of relations to be processed in this DB. */
    1661                 :             :     {
    1662                 :          23 :         const int   index[] = {
    1663                 :             :             PROGRESS_DATACHECKSUMS_RELS_TOTAL,
    1664                 :             :             PROGRESS_DATACHECKSUMS_RELS_DONE
    1665                 :             :         };
    1666                 :             : 
    1667                 :             :         int64       vals[2];
    1668                 :             : 
    1669                 :          23 :         vals[0] = list_length(RelationList);
    1670                 :          23 :         vals[1] = 0;
    1671                 :             : 
    1672                 :          23 :         pgstat_progress_update_multi_param(2, index, vals);
    1673                 :             :     }
    1674                 :             : 
    1675                 :             :     /* Process the relations */
    1676                 :          23 :     rels_done = 0;
    1677   [ +  -  +  +  :        6086 :     foreach_oid(reloid, RelationList)
                   +  + ]
    1678                 :             :     {
    1679                 :        6040 :         bool        costs_updated = false;
    1680                 :             : 
    1681         [ -  + ]:        6040 :         if (!ProcessSingleRelationByOid(reloid, strategy))
    1682                 :             :         {
    1683                 :           0 :             aborted = true;
    1684                 :           0 :             break;
    1685                 :             :         }
    1686                 :             : 
    1687                 :        6040 :         pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_RELS_DONE,
    1688                 :             :                                      ++rels_done);
    1689         [ -  + ]:        6040 :         CHECK_FOR_INTERRUPTS();
    1690   [ +  -  -  + ]:        6040 :         CHECK_FOR_WORKER_ABORT_REQUEST();
    1691                 :             : 
    1692         [ -  + ]:        6040 :         if (abort_requested)
    1693                 :           0 :             break;
    1694                 :             : 
    1695                 :             :         /*
    1696                 :             :          * Check if the cost settings changed during runtime and if so, update
    1697                 :             :          * to reflect the new values and signal that the access strategy needs
    1698                 :             :          * to be refreshed.
    1699                 :             :          */
    1700                 :        6040 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1701         [ -  + ]:        6040 :         if (DataChecksumState->worker_invocation != worker_invocation)
    1702                 :             :         {
    1703                 :           0 :             LWLockRelease(DataChecksumsWorkerLock);
    1704                 :           0 :             break;
    1705                 :             :         }
    1706         [ +  - ]:        6040 :         if ((DataChecksumState->launch_cost_delay != DataChecksumState->cost_delay)
    1707         [ -  + ]:        6040 :             || (DataChecksumState->launch_cost_limit != DataChecksumState->cost_limit))
    1708                 :             :         {
    1709                 :           0 :             costs_updated = true;
    1710                 :           0 :             VacuumCostDelay = DataChecksumState->launch_cost_delay;
    1711                 :           0 :             VacuumCostLimit = DataChecksumState->launch_cost_limit;
    1712                 :           0 :             VacuumUpdateCosts();
    1713                 :             : 
    1714                 :           0 :             DataChecksumState->cost_delay = DataChecksumState->launch_cost_delay;
    1715                 :           0 :             DataChecksumState->cost_limit = DataChecksumState->launch_cost_limit;
    1716                 :             :         }
    1717                 :             :         else
    1718                 :        6040 :             costs_updated = false;
    1719                 :        6040 :         LWLockRelease(DataChecksumsWorkerLock);
    1720                 :             : 
    1721         [ -  + ]:        6040 :         if (costs_updated)
    1722                 :             :         {
    1723                 :           0 :             FreeAccessStrategy(strategy);
    1724                 :           0 :             strategy = GetAccessStrategy(BAS_VACUUM);
    1725                 :             :         }
    1726                 :             :     }
    1727                 :             : 
    1728                 :          23 :     list_free(RelationList);
    1729                 :          23 :     FreeAccessStrategy(strategy);
    1730                 :             : 
    1731   [ +  -  -  + ]:          23 :     if (aborted || abort_requested)
    1732                 :             :     {
    1733                 :           0 :         LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1734         [ #  # ]:           0 :         if (DataChecksumState->worker_invocation == worker_invocation)
    1735                 :           0 :             DataChecksumState->worker_result = DATACHECKSUMSWORKER_ABORTED;
    1736                 :           0 :         LWLockRelease(DataChecksumsWorkerLock);
    1737         [ #  # ]:           0 :         ereport(DEBUG1,
    1738                 :             :                 errmsg("data checksum processing aborted in database OID %u",
    1739                 :             :                        dboid));
    1740                 :           0 :         return;
    1741                 :             :     }
    1742                 :             : 
    1743                 :             :     /* The worker is about to wait for temporary tables to go away. */
    1744                 :          23 :     pgstat_progress_update_param(PROGRESS_DATACHECKSUMS_PHASE,
    1745                 :             :                                  PROGRESS_DATACHECKSUMS_PHASE_WAITING_TEMPREL);
    1746                 :             : 
    1747                 :             :     /*
    1748                 :             :      * Wait for all temp tables that existed when we started to go away. This
    1749                 :             :      * is necessary since we cannot "reach" them to enable checksums. Any temp
    1750                 :             :      * tables created after we started will already have checksums in them
    1751                 :             :      * (due to the "inprogress-on" state), so no need to wait for those.
    1752                 :             :      */
    1753                 :             :     for (;;)
    1754                 :           0 :     {
    1755                 :             :         List       *CurrentTempTables;
    1756                 :             :         int         numleft;
    1757                 :             :         char        activity[64];
    1758                 :             : 
    1759                 :          23 :         CurrentTempTables = BuildRelationList(true, false);
    1760                 :          23 :         numleft = 0;
    1761   [ +  +  +  +  :          47 :         foreach_oid(tmptbloid, InitialTempTableList)
                   +  + ]
    1762                 :             :         {
    1763         [ +  - ]:           1 :             if (list_member_oid(CurrentTempTables, tmptbloid))
    1764                 :           1 :                 numleft++;
    1765                 :             :         }
    1766                 :          23 :         list_free(CurrentTempTables);
    1767                 :             : 
    1768                 :             : #ifdef USE_INJECTION_POINTS
    1769         [ -  + ]:          23 :         if (IS_INJECTION_POINT_ATTACHED("datachecksumsworker-fake-temptable-wait"))
    1770                 :             :         {
    1771                 :             :             /* Make sure to just cause one retry */
    1772   [ #  #  #  # ]:           0 :             if (!retried && numleft == 0)
    1773                 :             :             {
    1774                 :           0 :                 numleft = 1;
    1775                 :           0 :                 retried = true;
    1776                 :             : 
    1777                 :           0 :                 INJECTION_POINT_CACHED("datachecksumsworker-fake-temptable-wait", NULL);
    1778                 :             :             }
    1779                 :             :         }
    1780                 :             : #endif
    1781                 :             : 
    1782         [ +  + ]:          23 :         if (numleft == 0)
    1783                 :          22 :             break;
    1784                 :             : 
    1785                 :             :         /*
    1786                 :             :          * At least one temp table is left to wait for, indicate in pgstat
    1787                 :             :          * activity and progress reporting.
    1788                 :             :          */
    1789                 :           1 :         snprintf(activity,
    1790                 :             :                  sizeof(activity),
    1791                 :             :                  "Waiting for %d temp tables to be removed", numleft);
    1792                 :           1 :         pgstat_report_activity(STATE_RUNNING, activity);
    1793                 :             : 
    1794                 :             :         /* Retry every 3 seconds */
    1795                 :           1 :         ResetLatch(MyLatch);
    1796                 :           1 :         (void) WaitLatch(MyLatch,
    1797                 :             :                          WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
    1798                 :             :                          3000,
    1799                 :             :                          WAIT_EVENT_CHECKSUM_ENABLE_TEMPTABLE_WAIT);
    1800                 :             : 
    1801         [ +  - ]:           1 :         CHECK_FOR_INTERRUPTS();
    1802   [ #  #  #  # ]:           0 :         CHECK_FOR_WORKER_ABORT_REQUEST();
    1803                 :             : 
    1804   [ #  #  #  # ]:           0 :         if (aborted || abort_requested)
    1805                 :             :         {
    1806                 :           0 :             LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1807         [ #  # ]:           0 :             if (DataChecksumState->worker_invocation == worker_invocation)
    1808                 :           0 :                 DataChecksumState->worker_result = DATACHECKSUMSWORKER_ABORTED;
    1809                 :           0 :             LWLockRelease(DataChecksumsWorkerLock);
    1810         [ #  # ]:           0 :             ereport(LOG,
    1811                 :             :                     errmsg("data checksum processing aborted in database OID %u",
    1812                 :             :                            dboid));
    1813                 :           0 :             return;
    1814                 :             :         }
    1815                 :             :     }
    1816                 :             : 
    1817                 :          22 :     list_free(InitialTempTableList);
    1818                 :             : 
    1819                 :             :     /* worker done */
    1820                 :          22 :     pgstat_progress_end_command();
    1821                 :             : 
    1822                 :          22 :     LWLockAcquire(DataChecksumsWorkerLock, LW_EXCLUSIVE);
    1823         [ +  - ]:          22 :     if (DataChecksumState->worker_invocation == worker_invocation)
    1824                 :          22 :         DataChecksumState->worker_result = DATACHECKSUMSWORKER_SUCCESSFUL;
    1825                 :          22 :     LWLockRelease(DataChecksumsWorkerLock);
    1826                 :             : }
        

Generated by: LCOV version 2.0-1