From 9074b812f4060e22683d18c38bc34fc234050cad Mon Sep 17 00:00:00 2001 From: John Naylor Date: Mon, 20 Jul 2026 17:15:11 -0400 Subject: [PATCH v20261003 1/2] Allow marking a shutdown checkpoint as an upgrade boundary A major version upgrade that keeps standbys current by replaying WAL, instead of rebuilding them, needs a well-defined end to the old server's WAL: the final shutdown checkpoint written by the old binary. Standbys must replay up to that point and go no further, because whatever follows is written by the new version. pg_request_upgrade_boundary() records an upgrade request in shared memory; the next shutdown checkpoint consumes it and records itself as an upgrade boundary in a new CheckPoint field. pg_cancel_upgrade_boundary() withdraws a pending request, and pg_upgrade_boundary_requested() reports the current state. The request intentionally lives only in shared memory: a crash writes no shutdown checkpoint, so it forgets the request. Online checkpoints and end-of-recovery checkpoints are never marked. The flag is part of the checkpoint copy in pg_control, so pg_controldata reports it after shutdown. pg_waldump shows it in the checkpoint record's description. pg_resetwal clears the flag, since it discards the WAL that contains the marked checkpoint. TODO: pg_control_checkpoint() Nothing acts on the marking yet; a follow-up commit will teach standbys to pause when encountering it. XXX: bump PG_CONTROL_VERSION --- doc/src/sgml/func/func-admin.sgml | 92 +++++++++++++++++ src/backend/access/rmgrdesc/xlogdesc.c | 26 +++++ src/backend/access/transam/xlog.c | 67 ++++++++++++- src/backend/access/transam/xlogfuncs.c | 58 +++++++++++ src/bin/pg_controldata/pg_controldata.c | 19 ++++ src/bin/pg_resetwal/pg_resetwal.c | 6 ++ src/include/access/xlog.h | 4 + src/include/catalog/pg_control.h | 18 ++++ src/include/catalog/pg_proc.dat | 13 +++ src/test/recovery/meson.build | 1 + src/test/recovery/t/057_upgrade_boundary.pl | 105 ++++++++++++++++++++ src/tools/pgindent/typedefs.list | 1 + 12 files changed, 407 insertions(+), 3 deletions(-) create mode 100644 src/test/recovery/t/057_upgrade_boundary.pl diff --git a/doc/src/sgml/func/func-admin.sgml b/doc/src/sgml/func/func-admin.sgml index 64b0e7bb972..5fb55b1e14f 100644 --- a/doc/src/sgml/func/func-admin.sgml +++ b/doc/src/sgml/func/func-admin.sgml @@ -888,6 +888,98 @@ postgres=# SELECT '0/0'::pg_lsn + pd.segment_number * ps.setting::int + :offset + + Upgrade Boundary Functions + + + The functions shown in control the marking of an + upgrade boundary: a shutdown checkpoint that is + the deliberate end of the current major version's write-ahead log, + written in preparation for an in-place major version upgrade. + + + + Upgrade Boundary Functions + + + + + Function + + + Description + + + + + + + + + pg_request_upgrade_boundary + + pg_request_upgrade_boundary () + void + + + Requests that the next shutdown checkpoint be marked as an upgrade + boundary. The request only takes effect at a smart or fast + shutdown; it does not survive a crash, and it does not affect + checkpoints of a running server. This function cannot be executed + during recovery. + + + This function is restricted to superusers by default, but other users + can be granted EXECUTE to run the function. + + + + + + + pg_cancel_upgrade_boundary + + pg_cancel_upgrade_boundary () + void + + + Cancels a requested upgrade boundary. It is not an error to call + this when no boundary has been requested. This function cannot be + executed during recovery. + + + This function is restricted to superusers by default, but other users + can be granted EXECUTE to run the function. + + + + + + + pg_upgrade_boundary_requested + + pg_upgrade_boundary_requested () + boolean + + + Returns true if an upgrade boundary has been requested for the next + shutdown checkpoint. During recovery this always returns + false. + + + + +
+ + + If a server that was shut down at an upgrade boundary is simply started + again with the same major version, the boundary is cancelled: writing + new write-ahead log on the same timeline supersedes it. + + +
+ Snapshot Synchronization Functions diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c index 64e749c1e6b..b227da79e63 100644 --- a/src/backend/access/rmgrdesc/xlogdesc.c +++ b/src/backend/access/rmgrdesc/xlogdesc.c @@ -55,6 +55,27 @@ get_wal_level_string(int wal_level) return wal_level_str; } +/* + * Find a string representation for a checkpoint's upgrade flag + */ +static const char * +get_upgrade_flag_string(uint8 flag) +{ + switch (flag) + { + case UPGRADE_FLAG_NONE: + return "none"; + case UPGRADE_FLAG_BOUNDARY: + return "boundary"; + case UPGRADE_FLAG_START: + return "start"; + case UPGRADE_FLAG_GOLIVE: + return "go-live"; + } + + return "?"; +} + const char * get_checksum_state_string(uint32 state) { @@ -126,6 +147,11 @@ xlog_desc(StringInfo buf, XLogReaderState *record) checkpoint->oldestActiveXid, get_checksum_state_string(checkpoint->dataChecksumState), (info == XLOG_CHECKPOINT_SHUTDOWN) ? "shutdown" : "online"); + + /* Only upgrade machinery writes a flag, so keep quiet otherwise */ + if (checkpoint->upgradeFlag != UPGRADE_FLAG_NONE) + appendStringInfo(buf, "; upgrade flag %s", + get_upgrade_flag_string(checkpoint->upgradeFlag)); } else if (info == XLOG_NEXTOID) { diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 9ec0be77ca0..83bba8220c5 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -538,6 +538,13 @@ typedef struct XLogCtlData */ bool WalWriterSleeping; + /* + * upgradeBoundaryRequested indicates that the next shutdown checkpoint is + * to be marked as a major version upgrade boundary. Protected by + * info_lck. + */ + bool upgradeBoundaryRequested; + /* * During recovery, we keep a copy of the latest checkpoint record here. * lastCheckPointRecPtr points to start of checkpoint record and @@ -6161,6 +6168,17 @@ StartupXLOG(void) errmsg("control file contains invalid database cluster state"))); } + /* + * If the last shutdown checkpoint was marked as an upgrade boundary but + * we are starting up as usual, no upgrade happened. The boundary is + * cancelled as a side effect of writing new WAL on this timeline. + */ + if (ControlFile->state == DB_SHUTDOWNED && + ControlFile->checkPointCopy.upgradeFlag == UPGRADE_FLAG_BOUNDARY) + ereport(LOG, + (errmsg("database system was shut down at an upgrade boundary"), + errdetail("Starting up normally cancels the upgrade boundary."))); + /* This is just to allow attaching to startup process with a debugger */ #ifdef XLOG_REPLAY_DELAY if (ControlFile->state != DB_SHUTDOWNED) @@ -7374,6 +7392,36 @@ GetLastSegSwitchData(XLogRecPtr *lastSwitchLSN) return result; } +/* + * Request or cancel an upgrade boundary: when requested, the next shutdown + * checkpoint is marked as an upgrade boundary. + * + * The request lives only in shared memory: a crash, which writes no + * shutdown checkpoint, forgets it. + */ +void +SetUpgradeBoundaryRequest(bool request) +{ + SpinLockAcquire(&XLogCtl->info_lck); + XLogCtl->upgradeBoundaryRequested = request; + SpinLockRelease(&XLogCtl->info_lck); +} + +/* + * Report whether an upgrade boundary is currently requested. + */ +bool +UpgradeBoundaryActive(void) +{ + bool active; + + SpinLockAcquire(&XLogCtl->info_lck); + active = XLogCtl->upgradeBoundaryRequested; + SpinLockRelease(&XLogCtl->info_lck); + + return active; +} + /* * This must be called ONCE during postmaster or standalone-backend shutdown */ @@ -7408,6 +7456,8 @@ ShutdownXLOG(int code, Datum arg) CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST); else { + int flags = CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST; + /* * If archiving is enabled, rotate the last XLOG file so that all the * remaining records are archived (postmaster wakes up the archiver @@ -7417,7 +7467,11 @@ ShutdownXLOG(int code, Datum arg) if (XLogArchivingActive()) RequestXLogSwitch(false); - CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST); + /* Mark the shutdown checkpoint as an upgrade boundary if requested. */ + if (UpgradeBoundaryActive()) + flags |= CHECKPOINT_UPGRADE_BOUNDARY; + + CreateCheckPoint(flags); } } @@ -7430,7 +7484,7 @@ CheckpointFlagsString(int flags) { static char buf[128]; - snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s", + snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s%s%s", (flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "", (flags & CHECKPOINT_END_OF_RECOVERY) ? " end-of-recovery" : "", (flags & CHECKPOINT_FAST) ? " fast" : "", @@ -7438,7 +7492,8 @@ CheckpointFlagsString(int flags) (flags & CHECKPOINT_WAIT) ? " wait" : "", (flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "", (flags & CHECKPOINT_CAUSE_TIME) ? " time" : "", - (flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : ""); + (flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : "", + (flags & CHECKPOINT_UPGRADE_BOUNDARY) ? " upgrade-boundary" : ""); return buf; } @@ -7948,6 +8003,12 @@ CreateCheckPoint(int flags) checkPoint.logicalDecodingEnabled = IsLogicalDecodingEnabled(); + if ((flags & CHECKPOINT_IS_SHUTDOWN) != 0 && + (flags & CHECKPOINT_UPGRADE_BOUNDARY) != 0) + checkPoint.upgradeFlag = UPGRADE_FLAG_BOUNDARY; + else + checkPoint.upgradeFlag = UPGRADE_FLAG_NONE; + MultiXactGetCheckptMulti(shutdown, &checkPoint.nextMulti, &checkPoint.nextMultiOffset, diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c index 1f52bf7b420..bffd724b473 100644 --- a/src/backend/access/transam/xlogfuncs.c +++ b/src/backend/access/transam/xlogfuncs.c @@ -222,6 +222,64 @@ pg_switch_wal(PG_FUNCTION_ARGS) PG_RETURN_LSN(switchpoint); } +/* + * pg_request_upgrade_boundary: request an upgrade boundary + * + * When requested, the next shutdown checkpoint is marked as an upgrade + * boundary. + * + * Permission checking for this function is managed through the normal + * GRANT system. + */ +Datum +pg_request_upgrade_boundary(PG_FUNCTION_ARGS) +{ + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery."))); + + SetUpgradeBoundaryRequest(true); + + PG_RETURN_VOID(); +} + +/* + * pg_cancel_upgrade_boundary: cancel a requested upgrade boundary + * + * Permission checking for this function is managed through the normal + * GRANT system. + */ +Datum +pg_cancel_upgrade_boundary(PG_FUNCTION_ARGS) +{ + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery."))); + + SetUpgradeBoundaryRequest(false); + + PG_RETURN_VOID(); +} + +/* + * pg_upgrade_boundary_requested: report whether an upgrade boundary is active + * + * During recovery there is no requesting a boundary, so simply return + * false rather than raising an error. + */ +Datum +pg_upgrade_boundary_requested(PG_FUNCTION_ARGS) +{ + if (RecoveryInProgress()) + PG_RETURN_BOOL(false); + + PG_RETURN_BOOL(UpgradeBoundaryActive()); +} + /* * pg_log_standby_snapshot: call LogStandbySnapshot() * diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c index 6a0f848d8d0..cfea8d7325c 100644 --- a/src/bin/pg_controldata/pg_controldata.c +++ b/src/bin/pg_controldata/pg_controldata.c @@ -84,6 +84,23 @@ wal_level_str(WalLevel wal_level) return _("unrecognized \"wal_level\""); } +static const char * +upgrade_flag_str(uint8 flag) +{ + switch (flag) + { + case UPGRADE_FLAG_NONE: + return "none"; + case UPGRADE_FLAG_BOUNDARY: + return "boundary"; + case UPGRADE_FLAG_START: + return "start"; + case UPGRADE_FLAG_GOLIVE: + return "go-live"; + } + return _("unrecognized upgrade flag"); +} + int main(int argc, char *argv[]) @@ -291,6 +308,8 @@ main(int argc, char *argv[]) ControlFile->checkPointCopy.newestCommitTsXid); printf(_("Latest checkpoint's data_checksum_version:%u\n"), ControlFile->checkPointCopy.dataChecksumState); + printf(_("Latest checkpoint's upgrade flag: %s\n"), + upgrade_flag_str(ControlFile->checkPointCopy.upgradeFlag)); printf(_("Time of latest checkpoint: %s\n"), ckpttime_str); printf(_("Fake LSN counter for unlogged rels: %X/%08X\n"), diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c index 41afc4c1316..b3df37830ce 100644 --- a/src/bin/pg_resetwal/pg_resetwal.c +++ b/src/bin/pg_resetwal/pg_resetwal.c @@ -930,6 +930,12 @@ RewriteControlFile(void) */ ControlFile.data_checksum_lsn = InvalidXLogRecPtr; + /* + * The checkpoint being rewritten is no longer the one that was marked as + * an upgrade boundary, if any; the new empty WAL starts fresh. + */ + ControlFile.checkPointCopy.upgradeFlag = UPGRADE_FLAG_NONE; + /* * Force the defaults for max_* settings. The values don't really matter * as long as wal_level='minimal'; the postmaster will reset these fields diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 7a590b7e1ea..23d788b427b 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -167,6 +167,8 @@ extern PGDLLIMPORT bool XLOG_DEBUG; #define CHECKPOINT_FAST 0x0004 /* Do it without delays */ #define CHECKPOINT_FORCE 0x0008 /* Force even if no activity */ #define CHECKPOINT_FLUSH_UNLOGGED 0x0010 /* Flush unlogged tables */ +#define CHECKPOINT_UPGRADE_BOUNDARY 0x0200 /* Mark shutdown checkpoint as an + * upgrade boundary */ /* These are important to RequestCheckpoint */ #define CHECKPOINT_WAIT 0x0020 /* Wait for completion */ #define CHECKPOINT_REQUESTED 0x0040 /* Checkpoint request has been made */ @@ -295,6 +297,8 @@ extern XLogRecPtr GetFlushRecPtr(TimeLineID *insertTLI); extern TimeLineID GetWALInsertionTimeLine(void); extern TimeLineID GetWALInsertionTimeLineIfSet(void); extern XLogRecPtr GetLastImportantRecPtr(void); +extern void SetUpgradeBoundaryRequest(bool request); +extern bool UpgradeBoundaryActive(void); extern void SetWalWriterSleeping(bool sleeping); diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index c3c934d0012..2a5e37d47e6 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -66,8 +66,26 @@ typedef struct CheckPoint /* data checksums state at the time of the checkpoint */ uint32 dataChecksumState; + + + /* + * Role of this checkpoint during an in-place major version upgrade, or + * UPGRADE_FLAG_NONE otherwise. + */ + uint8 upgradeFlag; /* one of the UPGRADE_FLAG_* values below */ } CheckPoint; +/* Values for CheckPoint.upgradeFlag. */ +typedef enum UpgradeFlag +{ + UPGRADE_FLAG_NONE = 0, /* no in-place major version upgrade */ + UPGRADE_FLAG_BOUNDARY = 1, /* final shutdown checkpoint of the old major + * version */ + UPGRADE_FLAG_START = 2, /* first checkpoint written in the new major + * version's format */ + UPGRADE_FLAG_GOLIVE = 3, /* checkpoint that ends the migration */ +} UpgradeFlag; + /* XLOG info values for XLOG rmgr */ #define XLOG_CHECKPOINT_SHUTDOWN 0x00 #define XLOG_CHECKPOINT_ONLINE 0x10 diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index f46427258e3..9a178c05465 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -6867,6 +6867,19 @@ { oid => '2848', descr => 'switch to new wal file', proname => 'pg_switch_wal', provolatile => 'v', prorettype => 'pg_lsn', proargtypes => '', prosrc => 'pg_switch_wal', proacl => '{POSTGRES=X}' }, +{ oid => '9819', + descr => 'request an upgrade boundary at the next shutdown checkpoint', + proname => 'pg_request_upgrade_boundary', provolatile => 'v', + prorettype => 'void', proargtypes => '', + prosrc => 'pg_request_upgrade_boundary', proacl => '{POSTGRES=X}' }, +{ oid => '9822', descr => 'cancel a requested upgrade boundary', + proname => 'pg_cancel_upgrade_boundary', provolatile => 'v', + prorettype => 'void', proargtypes => '', + prosrc => 'pg_cancel_upgrade_boundary', proacl => '{POSTGRES=X}' }, +{ oid => '9820', descr => 'true if an upgrade boundary is active', + proname => 'pg_upgrade_boundary_requested', provolatile => 'v', + prorettype => 'bool', proargtypes => '', + prosrc => 'pg_upgrade_boundary_requested' }, { oid => '6305', descr => 'log details of the current snapshot to WAL', proname => 'pg_log_standby_snapshot', provolatile => 'v', prorettype => 'pg_lsn', proargtypes => '', diff --git a/src/test/recovery/meson.build b/src/test/recovery/meson.build index 72113c5ac6e..28bcca86345 100644 --- a/src/test/recovery/meson.build +++ b/src/test/recovery/meson.build @@ -65,6 +65,7 @@ tests += { 't/054_unlogged_sequence_promotion.pl', 't/055_cascade_reconnect.pl', 't/056_standby_snapshot_export.pl', + 't/057_upgrade_boundary.pl', ], }, } diff --git a/src/test/recovery/t/057_upgrade_boundary.pl b/src/test/recovery/t/057_upgrade_boundary.pl new file mode 100644 index 00000000000..4e40e3bb3f1 --- /dev/null +++ b/src/test/recovery/t/057_upgrade_boundary.pl @@ -0,0 +1,105 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Test marking a shutdown checkpoint as an upgrade boundary: the +# deliberate end of this major version's WAL, pending an in-place upgrade. +# The marking is visible in the control file and in the WAL itself, and a +# cluster simply restarted on the same binary cancels it. + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +# Fetch one field from the control file +sub control_field +{ + my ($node, $field) = @_; + + my ($stdout, $stderr) = + run_command([ 'pg_controldata', $node->data_dir ]); + die "$field not found in control file\n" + unless $stdout =~ /^\Q$field\E:\s*(.*)$/m; + + return $1; +} + +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init; +$primary->start; + +# Request state round trip on the primary +is($primary->safe_psql('postgres', 'SELECT pg_upgrade_boundary_requested()'), + 'f', 'initially not active'); +$primary->safe_psql('postgres', 'SELECT pg_request_upgrade_boundary()'); +is($primary->safe_psql('postgres', 'SELECT pg_upgrade_boundary_requested()'), + 't', 'active after request'); +$primary->safe_psql('postgres', 'SELECT pg_cancel_upgrade_boundary()'); +is($primary->safe_psql('postgres', 'SELECT pg_upgrade_boundary_requested()'), + 'f', 'cancelled again'); +$primary->safe_psql('postgres', 'SELECT pg_cancel_upgrade_boundary()'); +is($primary->safe_psql('postgres', 'SELECT pg_upgrade_boundary_requested()'), + 'f', 'cancelling with nothing requested is a no-op'); + +# An online checkpoint neither consumes nor acts on a pending request; +# only a shutdown checkpoint does. +$primary->safe_psql('postgres', 'SELECT pg_request_upgrade_boundary()'); +$primary->safe_psql('postgres', 'CHECKPOINT'); +is(control_field($primary, "Latest checkpoint's upgrade flag"), + 'none', 'online checkpoint is not marked as a boundary'); +is($primary->safe_psql('postgres', 'SELECT pg_upgrade_boundary_requested()'), + 't', 'request stays active across an online checkpoint'); +$primary->safe_psql('postgres', 'SELECT pg_cancel_upgrade_boundary()'); + +# Shutting down with a request pending marks the shutdown checkpoint, and +# the control file keeps the marking, so the next binary started on this +# data directory can see it. +$primary->safe_psql('postgres', 'SELECT pg_request_upgrade_boundary()'); +$primary->stop('fast'); +is(control_field($primary, "Latest checkpoint's upgrade flag"), + 'boundary', 'shutdown checkpoint is marked as a boundary'); + +# The flag is in the WAL itself, not just the control file. +command_like( + [ + 'pg_waldump', '-p', $primary->data_dir . '/pg_wal', + '-s', control_field($primary, 'Latest checkpoint location'), + '-n', '1' + ], + qr/CHECKPOINT_SHUTDOWN .*upgrade flag boundary/, + 'pg_waldump shows the flag on the boundary checkpoint record'); + +# pg_resetwal discards the WAL after the boundary checkpoint and writes a +# new one in its place, so the marking must not carry over to it. +command_ok([ 'pg_resetwal', $primary->data_dir ], 'pg_resetwal succeeds'); +is(control_field($primary, "Latest checkpoint's upgrade flag"), + 'none', 'pg_resetwal clears the boundary marking'); +command_like( + [ + 'pg_waldump', '-p', $primary->data_dir . '/pg_wal', + '-s', control_field($primary, 'Latest checkpoint location'), + '-n', '1' + ], + qr/CHECKPOINT_SHUTDOWN (?!.*upgrade flag)/, + 'checkpoint written by pg_resetwal is not marked'); + +# A boundary can be requested again on the reset cluster. +$primary->start; +$primary->safe_psql('postgres', 'SELECT pg_request_upgrade_boundary()'); +$primary->stop('fast'); +is(control_field($primary, "Latest checkpoint's upgrade flag"), + 'boundary', 'boundary re-established after pg_resetwal'); + +# Abandon the upgrade: starting the primary on the same binary cancels the +# boundary as a side effect of writing new WAL on this timeline. +$primary->start; +ok( $primary->log_contains( + 'database system was shut down at an upgrade boundary'), + 'primary logged the cancelled boundary'); +is($primary->safe_psql('postgres', 'SELECT pg_upgrade_boundary_requested()'), + 'f', 'restarting does not re-request'); +$primary->safe_psql('postgres', 'CHECKPOINT'); +is(control_field($primary, "Latest checkpoint's upgrade flag"), + 'none', 'cancelled boundary is gone from the primary control file'); + +done_testing(); diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 5d432074c2c..95ccfec4a8e 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3320,6 +3320,7 @@ UnresolvedTup UnresolvedTupData UpdateContext UpdateStmt +UpgradeFlag UpgradeTask UpgradeTaskProcessCB UpgradeTaskReport -- 2.55.0