pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Nathan Bossart <nathandbossart@gmail.com>
To: Christoph Berg <myon@debian.org>
Cc: Laurenz Albe <laurenz.albe@cybertec.at>
Cc: Fujii Masao <masao.fujii@oss.nttdata.com>
Cc: Andres Freund <andres@anarazel.de>
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Subject: Re: CHECKPOINT unlogged data
Date: Wed, 18 Jun 2025 11:15:15 -0500
Message-ID: <aFLmE2zl038I7-F1@nathan> (raw)
In-Reply-To: <aFA7ustE2APIMehs@msg.df7cb.de>
References: <aEMIrLUDOKgmE_0P@nathan>
	<aEMVRbmqqg-aaxAN@msg.df7cb.de>
	<aEMYYEkQb2VeQoBo@nathan>
	<aEmIijETyhj9DsWG@msg.df7cb.de>
	<aEmLzinvZ-W8EPiW@nathan>
	<aEmma1MPm_UgmLP3@msg.df7cb.de>
	<086ab9ad5a2c06eb16f6f4e50a04764ba6fafd1b.camel@cybertec.at>
	<aFAsCyMj-mybBlv3@msg.df7cb.de>
	<aFA10XSdJ9hPGfD1@nathan>
	<aFA7ustE2APIMehs@msg.df7cb.de>

I broke this up into several small patches.  Notable changes are as
follows:

* Adjusted to the tab completion code to work more like the VACUUM utility
  options.

* Introduced a new ExecCheckpoint() function in checkpointer.c and moved
  the privilege check and options parsing there.

* Removed the notes in the docs about when to use the CHECKPOINT command.
  I'm not opposed to adding something like that (in fact, I think it's a
  good idea), but IMHO we should bikeshed on that separately, maybe even in
  a new thread.

Thoughts?

-- 
nathan
From e2ce33136bc7c994839ea07ba0679271cc68a31e Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Tue, 17 Jun 2025 14:36:40 -0500
Subject: [PATCH v7 1/5] rename CHECKPOINT_FLUSH_ALL to
 CHECKPOINT_FLUSH_UNLOGGED

---
 src/backend/access/transam/xlog.c   | 6 +++---
 src/backend/commands/dbcommands.c   | 4 ++--
 src/backend/storage/buffer/bufmgr.c | 6 +++---
 src/include/access/xlog.h           | 3 +--
 4 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 47ffc0a2307..7f816ce4a84 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6847,7 +6847,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
 						(flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
-						(flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" : "")));
+						(flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : "")));
 	else
 		ereport(LOG,
 		/* translator: the placeholders show checkpoint options */
@@ -6859,7 +6859,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
 						(flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
-						(flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" : "")));
+						(flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : "")));
 }
 
 /*
@@ -7047,7 +7047,7 @@ update_checkpoint_display(int flags, bool restartpoint, bool reset)
  *	CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred
  *		since the last one (implied by CHECKPOINT_IS_SHUTDOWN or
  *		CHECKPOINT_END_OF_RECOVERY).
- *	CHECKPOINT_FLUSH_ALL: also flush buffers of unlogged tables.
+ *	CHECKPOINT_FLUSH_UNLOGGED: also flush buffers of unlogged tables.
  *
  * Note: flags contains other bits, of interest here only for logging purposes.
  * In particular note that this routine is synchronous and does not pay
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c95eb945016..2d32ffd02c7 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -571,7 +571,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 */
 	if (!IsBinaryUpgrade)
 		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
-						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_ALL);
+						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/*
 	 * Iterate through all tablespaces of the template database, and copy each
@@ -2121,7 +2121,7 @@ movedb(const char *dbname, const char *tblspcname)
 	 * files, which would cause rmdir() to fail.
 	 */
 	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
-					  | CHECKPOINT_FLUSH_ALL);
+					  | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/* Close all smgr fds in all backends. */
 	WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 667aa0c0c78..5c4331688cc 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3341,8 +3341,8 @@ UnpinBufferNoOwner(BufferDesc *buf)
  * This is called at checkpoint time to write out all dirty shared buffers.
  * The checkpoint request flags should be passed in.  If CHECKPOINT_IMMEDIATE
  * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
- * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_ALL is set, we write even
- * unlogged buffers, which are otherwise skipped.  The remaining flags
+ * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_UNLOGGED is set, we write
+ * even unlogged buffers, which are otherwise skipped.  The remaining flags
  * currently have no effect here.
  */
 static void
@@ -3367,7 +3367,7 @@ BufferSync(int flags)
 	 * recovery, we write all dirty buffers.
 	 */
 	if (!((flags & (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY |
-					CHECKPOINT_FLUSH_ALL))))
+					CHECKPOINT_FLUSH_UNLOGGED))))
 		mask |= BM_PERMANENT;
 
 	/*
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d313099c027..80c42b5f80f 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -141,8 +141,7 @@ extern PGDLLIMPORT bool XLOG_DEBUG;
 											 * issued at end of WAL recovery */
 #define CHECKPOINT_IMMEDIATE	0x0004	/* Do it without delays */
 #define CHECKPOINT_FORCE		0x0008	/* Force even if no activity */
-#define CHECKPOINT_FLUSH_ALL	0x0010	/* Flush all pages, including those
-										 * belonging to unlogged tables */
+#define CHECKPOINT_FLUSH_UNLOGGED	0x0010	/* Flush unlogged tables */
 /* These are important to RequestCheckpoint */
 #define CHECKPOINT_WAIT			0x0020	/* Wait for completion */
 #define CHECKPOINT_REQUESTED	0x0040	/* Checkpoint request has been made */
-- 
2.39.5 (Apple Git-154)
From ccb17be3955c4b3b88277cdc581ce10d29c940cb Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Wed, 11 Jun 2025 16:32:23 +0200
Subject: [PATCH v7 2/5] rename CHECKPOINT_IMMEDIATE to CHECKPOINT_FAST

---
 doc/src/sgml/backup.sgml                      |  2 +-
 doc/src/sgml/func.sgml                        |  2 +-
 doc/src/sgml/ref/checkpoint.sgml              |  2 +-
 doc/src/sgml/ref/pg_basebackup.sgml           |  2 +-
 src/backend/access/transam/xlog.c             | 25 +++++++++----------
 src/backend/commands/dbcommands.c             | 10 ++++----
 src/backend/commands/tablespace.c             |  2 +-
 src/backend/postmaster/checkpointer.c         | 24 +++++++++---------
 src/backend/storage/buffer/bufmgr.c           |  4 +--
 src/backend/tcop/utility.c                    |  2 +-
 src/include/access/xlog.h                     |  2 +-
 .../recovery/t/041_checkpoint_at_promote.pl   |  2 +-
 12 files changed, 39 insertions(+), 40 deletions(-)

diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 25b8904baf7..5f7489afbd1 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -991,7 +991,7 @@ SELECT pg_backup_start(label => 'label', fast => false);
      usually preferable as it minimizes the impact on the running system.  If you
      want to start the backup as soon as possible, pass <literal>true</literal> as
      the second parameter to <function>pg_backup_start</function> and it will
-     request an immediate checkpoint, which will finish as fast as possible using
+     request a fast checkpoint, which will finish as fast as possible using
      as much I/O as possible.
     </para>
 
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c67688cbf5f..a49eb8a2af9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -28920,7 +28920,7 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
         will be stored.)
         If the optional second parameter is given as <literal>true</literal>,
         it specifies executing <function>pg_backup_start</function> as quickly
-        as possible.  This forces an immediate checkpoint which will cause a
+        as possible.  This forces a fast checkpoint which will cause a
         spike in I/O operations, slowing any concurrently executing queries.
        </para>
        <para>
diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index db011a47d04..10a433e4757 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -37,7 +37,7 @@ CHECKPOINT
   </para>
 
   <para>
-   The <command>CHECKPOINT</command> command forces an immediate
+   The <command>CHECKPOINT</command> command forces a fast
    checkpoint when the command is issued, without waiting for a
    regular checkpoint scheduled by the system (controlled by the settings in
    <xref linkend="runtime-config-wal-checkpoints"/>).
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index 9659f76042c..9be752fc12b 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -500,7 +500,7 @@ PostgreSQL documentation
       <term><option>--checkpoint={fast|spread}</option></term>
       <listitem>
        <para>
-        Sets checkpoint mode to fast (immediate) or spread (the default)
+        Sets checkpoint mode to fast or spread (the default)
         (see <xref linkend="backup-lowlevel-base-backup"/>).
        </para>
       </listitem>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7f816ce4a84..3740bc5a4a8 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6505,7 +6505,7 @@ PerformRecoveryXLogAction(void)
 	else
 	{
 		RequestCheckpoint(CHECKPOINT_END_OF_RECOVERY |
-						  CHECKPOINT_IMMEDIATE |
+						  CHECKPOINT_FAST |
 						  CHECKPOINT_WAIT);
 	}
 
@@ -6814,7 +6814,7 @@ ShutdownXLOG(int code, Datum arg)
 	WalSndWaitStopping();
 
 	if (RecoveryInProgress())
-		CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
+		CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST);
 	else
 	{
 		/*
@@ -6826,7 +6826,7 @@ ShutdownXLOG(int code, Datum arg)
 		if (XLogArchivingActive())
 			RequestXLogSwitch(false);
 
-		CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
+		CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST);
 	}
 }
 
@@ -6842,7 +6842,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 				(errmsg("restartpoint starting:%s%s%s%s%s%s%s%s",
 						(flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
 						(flags & CHECKPOINT_END_OF_RECOVERY) ? " end-of-recovery" : "",
-						(flags & CHECKPOINT_IMMEDIATE) ? " immediate" : "",
+						(flags & CHECKPOINT_FAST) ? " fast" : "",
 						(flags & CHECKPOINT_FORCE) ? " force" : "",
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
@@ -6854,7 +6854,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 				(errmsg("checkpoint starting:%s%s%s%s%s%s%s%s",
 						(flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
 						(flags & CHECKPOINT_END_OF_RECOVERY) ? " end-of-recovery" : "",
-						(flags & CHECKPOINT_IMMEDIATE) ? " immediate" : "",
+						(flags & CHECKPOINT_FAST) ? " fast" : "",
 						(flags & CHECKPOINT_FORCE) ? " force" : "",
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
@@ -7042,8 +7042,8 @@ update_checkpoint_display(int flags, bool restartpoint, bool reset)
  * flags is a bitwise OR of the following:
  *	CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown.
  *	CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery.
- *	CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP,
- *		ignoring checkpoint_completion_target parameter.
+ *	CHECKPOINT_FAST: finish the checkpoint ASAP, ignoring
+ *		checkpoint_completion_target parameter.
  *	CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred
  *		since the last one (implied by CHECKPOINT_IS_SHUTDOWN or
  *		CHECKPOINT_END_OF_RECOVERY).
@@ -8947,9 +8947,8 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
  * backup state and tablespace map.
  *
  * Input parameters are "state" (the backup state), "fast" (if true, we do
- * the checkpoint in immediate mode to make it faster), and "tablespaces"
- * (if non-NULL, indicates a list of tablespaceinfo structs describing the
- * cluster's tablespaces.).
+ * the checkpoint in fast mode), and "tablespaces" (if non-NULL, indicates a
+ * list of tablespaceinfo structs describing the cluster's tablespaces.).
  *
  * The tablespace map contents are appended to passed-in parameter
  * tablespace_map and the caller is responsible for including it in the backup
@@ -9077,11 +9076,11 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 			 * during recovery means that checkpointer is running, we can use
 			 * RequestCheckpoint() to establish a restartpoint.
 			 *
-			 * We use CHECKPOINT_IMMEDIATE only if requested by user (via
-			 * passing fast = true).  Otherwise this can take awhile.
+			 * We use CHECKPOINT_FAST only if requested by user (via passing
+			 * fast = true).  Otherwise this can take awhile.
 			 */
 			RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |
-							  (fast ? CHECKPOINT_IMMEDIATE : 0));
+							  (fast ? CHECKPOINT_FAST : 0));
 
 			/*
 			 * Now we need to fetch the checkpoint record location, and also
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 2d32ffd02c7..502a45163c8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -570,7 +570,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 * any CREATE DATABASE commands.
 	 */
 	if (!IsBinaryUpgrade)
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE |
 						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/*
@@ -673,7 +673,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 * strategy that avoids these problems.
 	 */
 	if (!IsBinaryUpgrade)
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE |
 						  CHECKPOINT_WAIT);
 }
 
@@ -1870,7 +1870,7 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	 * Force a checkpoint to make sure the checkpointer has received the
 	 * message sent by ForgetDatabaseSyncRequests.
 	 */
-	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+	RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 	/* Close all smgr fds in all backends. */
 	WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
@@ -2120,7 +2120,7 @@ movedb(const char *dbname, const char *tblspcname)
 	 * On Windows, this also ensures that background procs don't hold any open
 	 * files, which would cause rmdir() to fail.
 	 */
-	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
+	RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT
 					  | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/* Close all smgr fds in all backends. */
@@ -2252,7 +2252,7 @@ movedb(const char *dbname, const char *tblspcname)
 		 * any unlogged operations done in the new DB tablespace before the
 		 * next checkpoint.
 		 */
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 		/*
 		 * Force synchronous commit, thus minimizing the window between
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index a9005cc7212..df31eace47a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -500,7 +500,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 		 * mustn't delete.  So instead, we force a checkpoint which will clean
 		 * out any lingering files, and try again.
 		 */
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 		/*
 		 * On Windows, an unlinked file persists in the directory listing
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index fda91ffd1ce..0d8696bfb5e 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -161,7 +161,7 @@ static pg_time_t last_xlog_switch_time;
 static void ProcessCheckpointerInterrupts(void);
 static void CheckArchiveTimeout(void);
 static bool IsCheckpointOnSchedule(double progress);
-static bool ImmediateCheckpointRequested(void);
+static bool FastCheckpointRequested(void);
 static bool CompactCheckpointerRequestQueue(void);
 static void UpdateSharedMemoryConfig(void);
 
@@ -734,12 +734,12 @@ CheckArchiveTimeout(void)
 }
 
 /*
- * Returns true if an immediate checkpoint request is pending.  (Note that
- * this does not check the *current* checkpoint's IMMEDIATE flag, but whether
- * there is one pending behind it.)
+ * Returns true if a fast checkpoint request is pending.  (Note that this does
+ * not check the *current* checkpoint's FAST flag, but whether there is one
+ * pending behind it.)
  */
 static bool
-ImmediateCheckpointRequested(void)
+FastCheckpointRequested(void)
 {
 	volatile CheckpointerShmemStruct *cps = CheckpointerShmem;
 
@@ -747,7 +747,7 @@ ImmediateCheckpointRequested(void)
 	 * We don't need to acquire the ckpt_lck in this case because we're only
 	 * looking at a single flag bit.
 	 */
-	if (cps->ckpt_flags & CHECKPOINT_IMMEDIATE)
+	if (cps->ckpt_flags & CHECKPOINT_FAST)
 		return true;
 	return false;
 }
@@ -760,7 +760,7 @@ ImmediateCheckpointRequested(void)
  * checkpoint_completion_target.
  *
  * The checkpoint request flags should be passed in; currently the only one
- * examined is CHECKPOINT_IMMEDIATE, which disables delays between writes.
+ * examined is CHECKPOINT_FAST, which disables delays between writes.
  *
  * 'progress' is an estimate of how much of the work has been done, as a
  * fraction between 0.0 meaning none, and 1.0 meaning all done.
@@ -778,10 +778,10 @@ CheckpointWriteDelay(int flags, double progress)
 	 * Perform the usual duties and take a nap, unless we're behind schedule,
 	 * in which case we just try to catch up as quickly as possible.
 	 */
-	if (!(flags & CHECKPOINT_IMMEDIATE) &&
+	if (!(flags & CHECKPOINT_FAST) &&
 		!ShutdownXLOGPending &&
 		!ShutdownRequestPending &&
-		!ImmediateCheckpointRequested() &&
+		!FastCheckpointRequested() &&
 		IsCheckpointOnSchedule(progress))
 	{
 		if (ConfigReloadPending)
@@ -983,11 +983,11 @@ CheckpointerShmemInit(void)
  * flags is a bitwise OR of the following:
  *	CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown.
  *	CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery.
- *	CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP,
+ *	CHECKPOINT_FAST: finish the checkpoint ASAP,
  *		ignoring checkpoint_completion_target parameter.
  *	CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred
  *		since the last one (implied by CHECKPOINT_IS_SHUTDOWN or
- *		CHECKPOINT_END_OF_RECOVERY).
+ *		CHECKPOINT_END_OF_RECOVERY, and the CHECKPOINT command).
  *	CHECKPOINT_WAIT: wait for completion before returning (otherwise,
  *		just signal checkpointer to do it, and return).
  *	CHECKPOINT_CAUSE_XLOG: checkpoint is requested due to xlog filling.
@@ -1009,7 +1009,7 @@ RequestCheckpoint(int flags)
 		 * There's no point in doing slow checkpoints in a standalone backend,
 		 * because there's no other backends the checkpoint could disrupt.
 		 */
-		CreateCheckPoint(flags | CHECKPOINT_IMMEDIATE);
+		CreateCheckPoint(flags | CHECKPOINT_FAST);
 
 		/* Free all smgr objects, as CheckpointerMain() normally would. */
 		smgrdestroyall();
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 5c4331688cc..8f80d160c54 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3339,8 +3339,8 @@ UnpinBufferNoOwner(BufferDesc *buf)
  * BufferSync -- Write out all dirty buffers in the pool.
  *
  * This is called at checkpoint time to write out all dirty shared buffers.
- * The checkpoint request flags should be passed in.  If CHECKPOINT_IMMEDIATE
- * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
+ * The checkpoint request flags should be passed in.  If CHECKPOINT_FAST is
+ * set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
  * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_UNLOGGED is set, we write
  * even unlogged buffers, which are otherwise skipped.  The remaining flags
  * currently have no effect here.
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 25fe3d58016..cda86ad44b0 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -952,7 +952,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 						 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
 								   "pg_checkpoint")));
 
-			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
+			RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_WAIT |
 							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
 			break;
 
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 80c42b5f80f..d12798be3d8 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -139,7 +139,7 @@ extern PGDLLIMPORT bool XLOG_DEBUG;
 #define CHECKPOINT_IS_SHUTDOWN	0x0001	/* Checkpoint is for shutdown */
 #define CHECKPOINT_END_OF_RECOVERY	0x0002	/* Like shutdown checkpoint, but
 											 * issued at end of WAL recovery */
-#define CHECKPOINT_IMMEDIATE	0x0004	/* Do it without delays */
+#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 */
 /* These are important to RequestCheckpoint */
diff --git a/src/test/recovery/t/041_checkpoint_at_promote.pl b/src/test/recovery/t/041_checkpoint_at_promote.pl
index cb63ac8d5c9..12750ff7d4f 100644
--- a/src/test/recovery/t/041_checkpoint_at_promote.pl
+++ b/src/test/recovery/t/041_checkpoint_at_promote.pl
@@ -91,7 +91,7 @@ $node_standby->wait_for_event('checkpointer', 'create-restart-point');
 # Check the logs that the restart point has started on standby.  This is
 # optional, but let's be sure.
 ok( $node_standby->log_contains(
-		"restartpoint starting: immediate wait", $logstart),
+		"restartpoint starting: fast wait", $logstart),
 	"restartpoint has started");
 
 # Trigger promotion during the restart point.
-- 
2.39.5 (Apple Git-154)
From 815634c9ade5aa89351beb876d05a261c3db7e92 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Tue, 17 Jun 2025 15:36:29 -0500
Subject: [PATCH v7 3/5] add option list to checkpoint command

---
 doc/src/sgml/ref/checkpoint.sgml      | 12 +++++++++-
 src/backend/parser/gram.y             |  7 ++++++
 src/backend/postmaster/checkpointer.c | 33 +++++++++++++++++++++++++++
 src/backend/tcop/utility.c            | 12 +---------
 src/bin/psql/tab-complete.in.c        |  3 +++
 src/include/nodes/parsenodes.h        |  1 +
 src/include/postmaster/bgwriter.h     |  2 ++
 7 files changed, 58 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..1294b7c528e 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -21,7 +21,9 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CHECKPOINT
+CHECKPOINT [ ( option [, ...] ) ]
+
+<phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
 </synopsis>
  </refsynopsisdiv>
 
@@ -58,6 +60,14 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <para>
+   Coming soon...
+  </para>
+ </refsect1>
+
  <refsect1>
   <title>Compatibility</title>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 50f53159d58..5934810e805 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2034,6 +2034,13 @@ CheckPointStmt:
 
 					$$ = (Node *) n;
 				}
+			| CHECKPOINT '(' utility_option_list ')'
+				{
+					CheckPointStmt *n = makeNode(CheckPointStmt);
+
+					$$ = (Node *) n;
+					n->options = $3;
+				}
 		;
 
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 0d8696bfb5e..58d863847c5 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -42,6 +42,7 @@
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
 #include "access/xlogrecovery.h"
+#include "catalog/pg_authid.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -61,6 +62,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "utils/acl.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -976,6 +978,37 @@ CheckpointerShmemInit(void)
 	}
 }
 
+/*
+ * ExecCheckpoint
+ *		Primary entry point for manual CHECKPOINT commands
+ *
+ * This is mainly a wrapper for RequestCheckpoint().
+ */
+void
+ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
+{
+	foreach_ptr(DefElem, opt, stmt->options)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+				 parser_errposition(pstate, opt->location)));
+	}
+
+	if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+		/* translator: %s is name of an SQL command (e.g., CHECKPOINT) */
+				 errmsg("permission denied to execute %s command",
+						"CHECKPOINT"),
+				 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
+						   "pg_checkpoint")));
+
+	RequestCheckpoint(CHECKPOINT_WAIT |
+					  CHECKPOINT_FAST |
+					  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+}
+
 /*
  * RequestCheckpoint
  *		Called in backend processes to request a checkpoint
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index cda86ad44b0..2268709c5a7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -943,17 +943,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 			break;
 
 		case T_CheckPointStmt:
-			if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
-				ereport(ERROR,
-						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				/* translator: %s is name of a SQL command, eg CHECKPOINT */
-						 errmsg("permission denied to execute %s command",
-								"CHECKPOINT"),
-						 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
-								   "pg_checkpoint")));
-
-			RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_WAIT |
-							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+			ExecCheckpoint(pstate, (CheckPointStmt *) parsetree);
 			break;
 
 			/*
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 2c0b4f28c14..f4b9ad3b26d 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3125,6 +3125,9 @@ match_previous_words(int pattern_id,
 		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
 	else if (Matches("CALL", MatchAny))
 		COMPLETE_WITH("(");
+/* CHECKPOINT */
+	else if (Matches("CHECKPOINT"))
+		COMPLETE_WITH("(");
 /* CLOSE */
 	else if (Matches("CLOSE"))
 		COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ba12678d1cb..c4d1e9c8006 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -4015,6 +4015,7 @@ typedef struct RefreshMatViewStmt
 typedef struct CheckPointStmt
 {
 	NodeTag		type;
+	List	   *options;		/* list of DefElem nodes */
 } CheckPointStmt;
 
 /* ----------------------
diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h
index 800ecbfd13b..97001f4e7f6 100644
--- a/src/include/postmaster/bgwriter.h
+++ b/src/include/postmaster/bgwriter.h
@@ -15,6 +15,7 @@
 #ifndef _BGWRITER_H
 #define _BGWRITER_H
 
+#include "parser/parse_node.h"
 #include "storage/block.h"
 #include "storage/relfilelocator.h"
 #include "storage/smgr.h"
@@ -30,6 +31,7 @@ extern PGDLLIMPORT double CheckPointCompletionTarget;
 pg_noreturn extern void BackgroundWriterMain(const void *startup_data, size_t startup_data_len);
 pg_noreturn extern void CheckpointerMain(const void *startup_data, size_t startup_data_len);
 
+extern void ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt);
 extern void RequestCheckpoint(int flags);
 extern void CheckpointWriteDelay(int flags, double progress);
 
-- 
2.39.5 (Apple Git-154)
From bafe6274481c008727db9412088208f811d0ba4d Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 18 Jun 2025 10:40:27 -0500
Subject: [PATCH v7 4/5] add mode option to checkpoint command

---
 doc/src/sgml/ref/checkpoint.sgml      | 35 ++++++++++++++++++++++++---
 src/backend/postmaster/checkpointer.c | 26 ++++++++++++++++----
 src/bin/psql/tab-complete.in.c        | 13 ++++++++++
 src/test/regress/expected/stats.out   |  5 +++-
 src/test/regress/sql/stats.sql        |  5 +++-
 5 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 1294b7c528e..d66aa967afc 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -24,6 +24,8 @@ PostgreSQL documentation
 CHECKPOINT [ ( option [, ...] ) ]
 
 <phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
+
+    MODE { FAST | SPREAD }
 </synopsis>
  </refsynopsisdiv>
 
@@ -39,7 +41,8 @@ CHECKPOINT [ ( option [, ...] ) ]
   </para>
 
   <para>
-   The <command>CHECKPOINT</command> command forces a fast
+   When <literal>MODE</literal> is set to <literal>FAST</literal>, which is the
+   default, the <command>CHECKPOINT</command> command forces a fast
    checkpoint when the command is issued, without waiting for a
    regular checkpoint scheduled by the system (controlled by the settings in
    <xref linkend="runtime-config-wal-checkpoints"/>).
@@ -47,6 +50,14 @@ CHECKPOINT [ ( option [, ...] ) ]
    operation.
   </para>
 
+  <para>
+   The server may consolidate concurrently requested checkpoints.  Such
+   consolidated requests will contain a combined set of options.  For example,
+   if one session requests a fast checkpoint and another requests a spread
+   checkpoint, the server may combine those requests and perform one fast
+   checkpoint.
+  </para>
+
   <para>
    If executed during recovery, the <command>CHECKPOINT</command> command
    will force a restartpoint (see <xref linkend="wal-configuration"/>)
@@ -63,9 +74,25 @@ CHECKPOINT [ ( option [, ...] ) ]
  <refsect1>
   <title>Parameters</title>
 
-  <para>
-   Coming soon...
-  </para>
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      When set to <literal>FAST</literal>, which is the default, the requested
+      checkpoint will be completed as fast as possible, which may result in a
+      significantly higher rate of I/O during the checkpoint.
+     </para>
+     <para>
+      <literal>MODE</literal> can also be set to <literal>SPREAD</literal> to
+      request the checkpoint be spread over a longer interval (controlled via
+      the settings in <xref linkend="runtime-config-wal-checkpoints"/>), like a
+      regular checkpoint scheduled by the system.  This can reduce the rate of
+      I/O during the checkpoint.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 58d863847c5..723322e3e03 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -43,6 +43,7 @@
 #include "access/xlog_internal.h"
 #include "access/xlogrecovery.h"
 #include "catalog/pg_authid.h"
+#include "commands/defrem.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -987,12 +988,27 @@ CheckpointerShmemInit(void)
 void
 ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 {
+	bool		fast = true;
+
 	foreach_ptr(DefElem, opt, stmt->options)
 	{
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
-				 parser_errposition(pstate, opt->location)));
+		if (strcmp(opt->defname, "mode") == 0)
+		{
+			char	   *mode = defGetString(opt);
+
+			if (strcmp(mode, "spread") == 0)
+				fast = false;
+			else if (strcmp(mode, "fast") != 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("unrecognized MODE option \"%s\"", opt->defname),
+						 parser_errposition(pstate, opt->location)));
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+					 parser_errposition(pstate, opt->location)));
 	}
 
 	if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
@@ -1005,7 +1021,7 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 						   "pg_checkpoint")));
 
 	RequestCheckpoint(CHECKPOINT_WAIT |
-					  CHECKPOINT_FAST |
+					  (fast ? CHECKPOINT_FAST : 0) |
 					  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
 }
 
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index f4b9ad3b26d..177615aff27 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3128,6 +3128,19 @@ match_previous_words(int pattern_id,
 /* CHECKPOINT */
 	else if (Matches("CHECKPOINT"))
 		COMPLETE_WITH("(");
+	else if (HeadMatches("CHECKPOINT", "(*") &&
+			 !HeadMatches("CHECKPOINT", "(*)"))
+	{
+		/*
+		 * This fires if we're in an unfinished parenthesized option list.
+		 * get_previous_words treats a completed parenthesized option list as
+		 * one word, so the above test is correct.
+		 */
+		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+			COMPLETE_WITH("MODE");
+		else if (TailMatches("MODE"))
+			COMPLETE_WITH("FAST", "SPREAD");
+	}
 /* CLOSE */
 	else if (Matches("CLOSE"))
 		COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 776f1ad0e53..02cdc3cbcf6 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -926,7 +926,10 @@ DROP TABLE test_stats_temp;
 -- Checkpoint twice: The checkpointer reports stats after reporting completion
 -- of the checkpoint. But after a second checkpoint we'll see at least the
 -- results of the first.
-CHECKPOINT;
+--
+-- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
+-- because it would prolong the test.
+CHECKPOINT (MODE FAST);
 CHECKPOINT;
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
  ?column? 
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 232ab8db8fa..5ed71d7054e 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -439,7 +439,10 @@ DROP TABLE test_stats_temp;
 -- Checkpoint twice: The checkpointer reports stats after reporting completion
 -- of the checkpoint. But after a second checkpoint we'll see at least the
 -- results of the first.
-CHECKPOINT;
+--
+-- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
+-- because it would prolong the test.
+CHECKPOINT (MODE FAST);
 CHECKPOINT;
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
-- 
2.39.5 (Apple Git-154)
From 159dcdc121523d10aa6bf1030c35052a3a8c5e2b Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 18 Jun 2025 11:08:26 -0500
Subject: [PATCH v7 5/5] add flush_unlogged option to checkpoint command

---
 doc/src/sgml/ref/checkpoint.sgml      | 26 ++++++++++++++++++++++++++
 src/backend/postmaster/checkpointer.c |  4 ++++
 src/bin/psql/tab-complete.in.c        |  2 +-
 src/test/regress/expected/stats.out   |  2 +-
 src/test/regress/sql/stats.sql        |  2 +-
 5 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index d66aa967afc..8054de78a9b 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -25,6 +25,7 @@ CHECKPOINT [ ( option [, ...] ) ]
 
 <phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
 
+    FLUSH_UNLOGGED [ <replaceable class="parameter">boolean</replaceable> ]
     MODE { FAST | SPREAD }
 </synopsis>
  </refsynopsisdiv>
@@ -75,6 +76,17 @@ CHECKPOINT [ ( option [, ...] ) ]
   <title>Parameters</title>
 
   <variablelist>
+   <varlistentry>
+    <term><literal>FLUSH_UNLOGGED</literal></term>
+    <listitem>
+     <para>
+      Normally, <command>CHECKPOINT</command> does not flush data files for
+      unlogged relations.  This option, which is disabled by default, enables
+      flushing unlogged relations.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>MODE</literal></term>
     <listitem>
@@ -92,6 +104,20 @@ CHECKPOINT [ ( option [, ...] ) ]
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">boolean</replaceable></term>
+    <listitem>
+     <para>
+      Specifies whether the selected option should be turned on or off.
+      You can write <literal>TRUE</literal>, <literal>ON</literal>, or
+      <literal>1</literal> to enable the option, and <literal>FALSE</literal>,
+      <literal>OFF</literal>, or <literal>0</literal> to disable it.  The
+      <replaceable class="parameter">boolean</replaceable> value can also
+      be omitted, in which case <literal>TRUE</literal> is assumed.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 723322e3e03..a3e4e70ad66 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -989,6 +989,7 @@ void
 ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 {
 	bool		fast = true;
+	bool		unlogged = false;
 
 	foreach_ptr(DefElem, opt, stmt->options)
 	{
@@ -1004,6 +1005,8 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 						 errmsg("unrecognized MODE option \"%s\"", opt->defname),
 						 parser_errposition(pstate, opt->location)));
 		}
+		else if (strcmp(opt->defname, "flush_unlogged") == 0)
+			unlogged = defGetBoolean(opt);
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1022,6 +1025,7 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 
 	RequestCheckpoint(CHECKPOINT_WAIT |
 					  (fast ? CHECKPOINT_FAST : 0) |
+					  (unlogged ? CHECKPOINT_FLUSH_UNLOGGED : 0) |
 					  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
 }
 
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 177615aff27..a3f46a205ec 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3137,7 +3137,7 @@ match_previous_words(int pattern_id,
 		 * one word, so the above test is correct.
 		 */
 		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-			COMPLETE_WITH("MODE");
+			COMPLETE_WITH("MODE, FLUSH_UNLOGGED");
 		else if (TailMatches("MODE"))
 			COMPLETE_WITH("FAST", "SPREAD");
 	}
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 02cdc3cbcf6..6ebcc292834 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -930,7 +930,7 @@ DROP TABLE test_stats_temp;
 -- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
 -- because it would prolong the test.
 CHECKPOINT (MODE FAST);
-CHECKPOINT;
+CHECKPOINT (FLUSH_UNLOGGED);
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
  ?column? 
 ----------
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 5ed71d7054e..dab19918056 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -443,7 +443,7 @@ DROP TABLE test_stats_temp;
 -- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
 -- because it would prolong the test.
 CHECKPOINT (MODE FAST);
-CHECKPOINT;
+CHECKPOINT (FLUSH_UNLOGGED);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
-- 
2.39.5 (Apple Git-154)

Attachments:

  [text/plain] v7-0001-rename-CHECKPOINT_FLUSH_ALL-to-CHECKPOINT_FLUSH_U.patch (4.8K, ../aFLmE2zl038I7-F1@nathan/2-v7-0001-rename-CHECKPOINT_FLUSH_ALL-to-CHECKPOINT_FLUSH_U.patch)
  download | inline diff:
From e2ce33136bc7c994839ea07ba0679271cc68a31e Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Tue, 17 Jun 2025 14:36:40 -0500
Subject: [PATCH v7 1/5] rename CHECKPOINT_FLUSH_ALL to
 CHECKPOINT_FLUSH_UNLOGGED

---
 src/backend/access/transam/xlog.c   | 6 +++---
 src/backend/commands/dbcommands.c   | 4 ++--
 src/backend/storage/buffer/bufmgr.c | 6 +++---
 src/include/access/xlog.h           | 3 +--
 4 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 47ffc0a2307..7f816ce4a84 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6847,7 +6847,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
 						(flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
-						(flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" : "")));
+						(flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : "")));
 	else
 		ereport(LOG,
 		/* translator: the placeholders show checkpoint options */
@@ -6859,7 +6859,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
 						(flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
-						(flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" : "")));
+						(flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : "")));
 }
 
 /*
@@ -7047,7 +7047,7 @@ update_checkpoint_display(int flags, bool restartpoint, bool reset)
  *	CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred
  *		since the last one (implied by CHECKPOINT_IS_SHUTDOWN or
  *		CHECKPOINT_END_OF_RECOVERY).
- *	CHECKPOINT_FLUSH_ALL: also flush buffers of unlogged tables.
+ *	CHECKPOINT_FLUSH_UNLOGGED: also flush buffers of unlogged tables.
  *
  * Note: flags contains other bits, of interest here only for logging purposes.
  * In particular note that this routine is synchronous and does not pay
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c95eb945016..2d32ffd02c7 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -571,7 +571,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 */
 	if (!IsBinaryUpgrade)
 		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
-						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_ALL);
+						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/*
 	 * Iterate through all tablespaces of the template database, and copy each
@@ -2121,7 +2121,7 @@ movedb(const char *dbname, const char *tblspcname)
 	 * files, which would cause rmdir() to fail.
 	 */
 	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
-					  | CHECKPOINT_FLUSH_ALL);
+					  | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/* Close all smgr fds in all backends. */
 	WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 667aa0c0c78..5c4331688cc 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3341,8 +3341,8 @@ UnpinBufferNoOwner(BufferDesc *buf)
  * This is called at checkpoint time to write out all dirty shared buffers.
  * The checkpoint request flags should be passed in.  If CHECKPOINT_IMMEDIATE
  * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
- * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_ALL is set, we write even
- * unlogged buffers, which are otherwise skipped.  The remaining flags
+ * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_UNLOGGED is set, we write
+ * even unlogged buffers, which are otherwise skipped.  The remaining flags
  * currently have no effect here.
  */
 static void
@@ -3367,7 +3367,7 @@ BufferSync(int flags)
 	 * recovery, we write all dirty buffers.
 	 */
 	if (!((flags & (CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_END_OF_RECOVERY |
-					CHECKPOINT_FLUSH_ALL))))
+					CHECKPOINT_FLUSH_UNLOGGED))))
 		mask |= BM_PERMANENT;
 
 	/*
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d313099c027..80c42b5f80f 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -141,8 +141,7 @@ extern PGDLLIMPORT bool XLOG_DEBUG;
 											 * issued at end of WAL recovery */
 #define CHECKPOINT_IMMEDIATE	0x0004	/* Do it without delays */
 #define CHECKPOINT_FORCE		0x0008	/* Force even if no activity */
-#define CHECKPOINT_FLUSH_ALL	0x0010	/* Flush all pages, including those
-										 * belonging to unlogged tables */
+#define CHECKPOINT_FLUSH_UNLOGGED	0x0010	/* Flush unlogged tables */
 /* These are important to RequestCheckpoint */
 #define CHECKPOINT_WAIT			0x0020	/* Wait for completion */
 #define CHECKPOINT_REQUESTED	0x0040	/* Checkpoint request has been made */
-- 
2.39.5 (Apple Git-154)

  [text/plain] v7-0002-rename-CHECKPOINT_IMMEDIATE-to-CHECKPOINT_FAST.patch (16.5K, ../aFLmE2zl038I7-F1@nathan/3-v7-0002-rename-CHECKPOINT_IMMEDIATE-to-CHECKPOINT_FAST.patch)
  download | inline diff:
From ccb17be3955c4b3b88277cdc581ce10d29c940cb Mon Sep 17 00:00:00 2001
From: Christoph Berg <myon@debian.org>
Date: Wed, 11 Jun 2025 16:32:23 +0200
Subject: [PATCH v7 2/5] rename CHECKPOINT_IMMEDIATE to CHECKPOINT_FAST

---
 doc/src/sgml/backup.sgml                      |  2 +-
 doc/src/sgml/func.sgml                        |  2 +-
 doc/src/sgml/ref/checkpoint.sgml              |  2 +-
 doc/src/sgml/ref/pg_basebackup.sgml           |  2 +-
 src/backend/access/transam/xlog.c             | 25 +++++++++----------
 src/backend/commands/dbcommands.c             | 10 ++++----
 src/backend/commands/tablespace.c             |  2 +-
 src/backend/postmaster/checkpointer.c         | 24 +++++++++---------
 src/backend/storage/buffer/bufmgr.c           |  4 +--
 src/backend/tcop/utility.c                    |  2 +-
 src/include/access/xlog.h                     |  2 +-
 .../recovery/t/041_checkpoint_at_promote.pl   |  2 +-
 12 files changed, 39 insertions(+), 40 deletions(-)

diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml
index 25b8904baf7..5f7489afbd1 100644
--- a/doc/src/sgml/backup.sgml
+++ b/doc/src/sgml/backup.sgml
@@ -991,7 +991,7 @@ SELECT pg_backup_start(label => 'label', fast => false);
      usually preferable as it minimizes the impact on the running system.  If you
      want to start the backup as soon as possible, pass <literal>true</literal> as
      the second parameter to <function>pg_backup_start</function> and it will
-     request an immediate checkpoint, which will finish as fast as possible using
+     request a fast checkpoint, which will finish as fast as possible using
      as much I/O as possible.
     </para>
 
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c67688cbf5f..a49eb8a2af9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -28920,7 +28920,7 @@ LOG:  Grand total: 1651920 bytes in 201 blocks; 622360 free (88 chunks); 1029560
         will be stored.)
         If the optional second parameter is given as <literal>true</literal>,
         it specifies executing <function>pg_backup_start</function> as quickly
-        as possible.  This forces an immediate checkpoint which will cause a
+        as possible.  This forces a fast checkpoint which will cause a
         spike in I/O operations, slowing any concurrently executing queries.
        </para>
        <para>
diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index db011a47d04..10a433e4757 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -37,7 +37,7 @@ CHECKPOINT
   </para>
 
   <para>
-   The <command>CHECKPOINT</command> command forces an immediate
+   The <command>CHECKPOINT</command> command forces a fast
    checkpoint when the command is issued, without waiting for a
    regular checkpoint scheduled by the system (controlled by the settings in
    <xref linkend="runtime-config-wal-checkpoints"/>).
diff --git a/doc/src/sgml/ref/pg_basebackup.sgml b/doc/src/sgml/ref/pg_basebackup.sgml
index 9659f76042c..9be752fc12b 100644
--- a/doc/src/sgml/ref/pg_basebackup.sgml
+++ b/doc/src/sgml/ref/pg_basebackup.sgml
@@ -500,7 +500,7 @@ PostgreSQL documentation
       <term><option>--checkpoint={fast|spread}</option></term>
       <listitem>
        <para>
-        Sets checkpoint mode to fast (immediate) or spread (the default)
+        Sets checkpoint mode to fast or spread (the default)
         (see <xref linkend="backup-lowlevel-base-backup"/>).
        </para>
       </listitem>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 7f816ce4a84..3740bc5a4a8 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -6505,7 +6505,7 @@ PerformRecoveryXLogAction(void)
 	else
 	{
 		RequestCheckpoint(CHECKPOINT_END_OF_RECOVERY |
-						  CHECKPOINT_IMMEDIATE |
+						  CHECKPOINT_FAST |
 						  CHECKPOINT_WAIT);
 	}
 
@@ -6814,7 +6814,7 @@ ShutdownXLOG(int code, Datum arg)
 	WalSndWaitStopping();
 
 	if (RecoveryInProgress())
-		CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
+		CreateRestartPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST);
 	else
 	{
 		/*
@@ -6826,7 +6826,7 @@ ShutdownXLOG(int code, Datum arg)
 		if (XLogArchivingActive())
 			RequestXLogSwitch(false);
 
-		CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);
+		CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_FAST);
 	}
 }
 
@@ -6842,7 +6842,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 				(errmsg("restartpoint starting:%s%s%s%s%s%s%s%s",
 						(flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
 						(flags & CHECKPOINT_END_OF_RECOVERY) ? " end-of-recovery" : "",
-						(flags & CHECKPOINT_IMMEDIATE) ? " immediate" : "",
+						(flags & CHECKPOINT_FAST) ? " fast" : "",
 						(flags & CHECKPOINT_FORCE) ? " force" : "",
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
@@ -6854,7 +6854,7 @@ LogCheckpointStart(int flags, bool restartpoint)
 				(errmsg("checkpoint starting:%s%s%s%s%s%s%s%s",
 						(flags & CHECKPOINT_IS_SHUTDOWN) ? " shutdown" : "",
 						(flags & CHECKPOINT_END_OF_RECOVERY) ? " end-of-recovery" : "",
-						(flags & CHECKPOINT_IMMEDIATE) ? " immediate" : "",
+						(flags & CHECKPOINT_FAST) ? " fast" : "",
 						(flags & CHECKPOINT_FORCE) ? " force" : "",
 						(flags & CHECKPOINT_WAIT) ? " wait" : "",
 						(flags & CHECKPOINT_CAUSE_XLOG) ? " wal" : "",
@@ -7042,8 +7042,8 @@ update_checkpoint_display(int flags, bool restartpoint, bool reset)
  * flags is a bitwise OR of the following:
  *	CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown.
  *	CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery.
- *	CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP,
- *		ignoring checkpoint_completion_target parameter.
+ *	CHECKPOINT_FAST: finish the checkpoint ASAP, ignoring
+ *		checkpoint_completion_target parameter.
  *	CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred
  *		since the last one (implied by CHECKPOINT_IS_SHUTDOWN or
  *		CHECKPOINT_END_OF_RECOVERY).
@@ -8947,9 +8947,8 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
  * backup state and tablespace map.
  *
  * Input parameters are "state" (the backup state), "fast" (if true, we do
- * the checkpoint in immediate mode to make it faster), and "tablespaces"
- * (if non-NULL, indicates a list of tablespaceinfo structs describing the
- * cluster's tablespaces.).
+ * the checkpoint in fast mode), and "tablespaces" (if non-NULL, indicates a
+ * list of tablespaceinfo structs describing the cluster's tablespaces.).
  *
  * The tablespace map contents are appended to passed-in parameter
  * tablespace_map and the caller is responsible for including it in the backup
@@ -9077,11 +9076,11 @@ do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
 			 * during recovery means that checkpointer is running, we can use
 			 * RequestCheckpoint() to establish a restartpoint.
 			 *
-			 * We use CHECKPOINT_IMMEDIATE only if requested by user (via
-			 * passing fast = true).  Otherwise this can take awhile.
+			 * We use CHECKPOINT_FAST only if requested by user (via passing
+			 * fast = true).  Otherwise this can take awhile.
 			 */
 			RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |
-							  (fast ? CHECKPOINT_IMMEDIATE : 0));
+							  (fast ? CHECKPOINT_FAST : 0));
 
 			/*
 			 * Now we need to fetch the checkpoint record location, and also
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 2d32ffd02c7..502a45163c8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -570,7 +570,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 * any CREATE DATABASE commands.
 	 */
 	if (!IsBinaryUpgrade)
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE |
 						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/*
@@ -673,7 +673,7 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 * strategy that avoids these problems.
 	 */
 	if (!IsBinaryUpgrade)
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE |
 						  CHECKPOINT_WAIT);
 }
 
@@ -1870,7 +1870,7 @@ dropdb(const char *dbname, bool missing_ok, bool force)
 	 * Force a checkpoint to make sure the checkpointer has received the
 	 * message sent by ForgetDatabaseSyncRequests.
 	 */
-	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+	RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 	/* Close all smgr fds in all backends. */
 	WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
@@ -2120,7 +2120,7 @@ movedb(const char *dbname, const char *tblspcname)
 	 * On Windows, this also ensures that background procs don't hold any open
 	 * files, which would cause rmdir() to fail.
 	 */
-	RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT
+	RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT
 					  | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/* Close all smgr fds in all backends. */
@@ -2252,7 +2252,7 @@ movedb(const char *dbname, const char *tblspcname)
 		 * any unlogged operations done in the new DB tablespace before the
 		 * next checkpoint.
 		 */
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 		/*
 		 * Force synchronous commit, thus minimizing the window between
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index a9005cc7212..df31eace47a 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -500,7 +500,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
 		 * mustn't delete.  So instead, we force a checkpoint which will clean
 		 * out any lingering files, and try again.
 		 */
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
 
 		/*
 		 * On Windows, an unlinked file persists in the directory listing
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index fda91ffd1ce..0d8696bfb5e 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -161,7 +161,7 @@ static pg_time_t last_xlog_switch_time;
 static void ProcessCheckpointerInterrupts(void);
 static void CheckArchiveTimeout(void);
 static bool IsCheckpointOnSchedule(double progress);
-static bool ImmediateCheckpointRequested(void);
+static bool FastCheckpointRequested(void);
 static bool CompactCheckpointerRequestQueue(void);
 static void UpdateSharedMemoryConfig(void);
 
@@ -734,12 +734,12 @@ CheckArchiveTimeout(void)
 }
 
 /*
- * Returns true if an immediate checkpoint request is pending.  (Note that
- * this does not check the *current* checkpoint's IMMEDIATE flag, but whether
- * there is one pending behind it.)
+ * Returns true if a fast checkpoint request is pending.  (Note that this does
+ * not check the *current* checkpoint's FAST flag, but whether there is one
+ * pending behind it.)
  */
 static bool
-ImmediateCheckpointRequested(void)
+FastCheckpointRequested(void)
 {
 	volatile CheckpointerShmemStruct *cps = CheckpointerShmem;
 
@@ -747,7 +747,7 @@ ImmediateCheckpointRequested(void)
 	 * We don't need to acquire the ckpt_lck in this case because we're only
 	 * looking at a single flag bit.
 	 */
-	if (cps->ckpt_flags & CHECKPOINT_IMMEDIATE)
+	if (cps->ckpt_flags & CHECKPOINT_FAST)
 		return true;
 	return false;
 }
@@ -760,7 +760,7 @@ ImmediateCheckpointRequested(void)
  * checkpoint_completion_target.
  *
  * The checkpoint request flags should be passed in; currently the only one
- * examined is CHECKPOINT_IMMEDIATE, which disables delays between writes.
+ * examined is CHECKPOINT_FAST, which disables delays between writes.
  *
  * 'progress' is an estimate of how much of the work has been done, as a
  * fraction between 0.0 meaning none, and 1.0 meaning all done.
@@ -778,10 +778,10 @@ CheckpointWriteDelay(int flags, double progress)
 	 * Perform the usual duties and take a nap, unless we're behind schedule,
 	 * in which case we just try to catch up as quickly as possible.
 	 */
-	if (!(flags & CHECKPOINT_IMMEDIATE) &&
+	if (!(flags & CHECKPOINT_FAST) &&
 		!ShutdownXLOGPending &&
 		!ShutdownRequestPending &&
-		!ImmediateCheckpointRequested() &&
+		!FastCheckpointRequested() &&
 		IsCheckpointOnSchedule(progress))
 	{
 		if (ConfigReloadPending)
@@ -983,11 +983,11 @@ CheckpointerShmemInit(void)
  * flags is a bitwise OR of the following:
  *	CHECKPOINT_IS_SHUTDOWN: checkpoint is for database shutdown.
  *	CHECKPOINT_END_OF_RECOVERY: checkpoint is for end of WAL recovery.
- *	CHECKPOINT_IMMEDIATE: finish the checkpoint ASAP,
+ *	CHECKPOINT_FAST: finish the checkpoint ASAP,
  *		ignoring checkpoint_completion_target parameter.
  *	CHECKPOINT_FORCE: force a checkpoint even if no XLOG activity has occurred
  *		since the last one (implied by CHECKPOINT_IS_SHUTDOWN or
- *		CHECKPOINT_END_OF_RECOVERY).
+ *		CHECKPOINT_END_OF_RECOVERY, and the CHECKPOINT command).
  *	CHECKPOINT_WAIT: wait for completion before returning (otherwise,
  *		just signal checkpointer to do it, and return).
  *	CHECKPOINT_CAUSE_XLOG: checkpoint is requested due to xlog filling.
@@ -1009,7 +1009,7 @@ RequestCheckpoint(int flags)
 		 * There's no point in doing slow checkpoints in a standalone backend,
 		 * because there's no other backends the checkpoint could disrupt.
 		 */
-		CreateCheckPoint(flags | CHECKPOINT_IMMEDIATE);
+		CreateCheckPoint(flags | CHECKPOINT_FAST);
 
 		/* Free all smgr objects, as CheckpointerMain() normally would. */
 		smgrdestroyall();
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 5c4331688cc..8f80d160c54 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3339,8 +3339,8 @@ UnpinBufferNoOwner(BufferDesc *buf)
  * BufferSync -- Write out all dirty buffers in the pool.
  *
  * This is called at checkpoint time to write out all dirty shared buffers.
- * The checkpoint request flags should be passed in.  If CHECKPOINT_IMMEDIATE
- * is set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
+ * The checkpoint request flags should be passed in.  If CHECKPOINT_FAST is
+ * set, we disable delays between writes; if CHECKPOINT_IS_SHUTDOWN,
  * CHECKPOINT_END_OF_RECOVERY or CHECKPOINT_FLUSH_UNLOGGED is set, we write
  * even unlogged buffers, which are otherwise skipped.  The remaining flags
  * currently have no effect here.
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 25fe3d58016..cda86ad44b0 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -952,7 +952,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 						 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
 								   "pg_checkpoint")));
 
-			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
+			RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_WAIT |
 							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
 			break;
 
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index 80c42b5f80f..d12798be3d8 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -139,7 +139,7 @@ extern PGDLLIMPORT bool XLOG_DEBUG;
 #define CHECKPOINT_IS_SHUTDOWN	0x0001	/* Checkpoint is for shutdown */
 #define CHECKPOINT_END_OF_RECOVERY	0x0002	/* Like shutdown checkpoint, but
 											 * issued at end of WAL recovery */
-#define CHECKPOINT_IMMEDIATE	0x0004	/* Do it without delays */
+#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 */
 /* These are important to RequestCheckpoint */
diff --git a/src/test/recovery/t/041_checkpoint_at_promote.pl b/src/test/recovery/t/041_checkpoint_at_promote.pl
index cb63ac8d5c9..12750ff7d4f 100644
--- a/src/test/recovery/t/041_checkpoint_at_promote.pl
+++ b/src/test/recovery/t/041_checkpoint_at_promote.pl
@@ -91,7 +91,7 @@ $node_standby->wait_for_event('checkpointer', 'create-restart-point');
 # Check the logs that the restart point has started on standby.  This is
 # optional, but let's be sure.
 ok( $node_standby->log_contains(
-		"restartpoint starting: immediate wait", $logstart),
+		"restartpoint starting: fast wait", $logstart),
 	"restartpoint has started");
 
 # Trigger promotion during the restart point.
-- 
2.39.5 (Apple Git-154)

  [text/plain] v7-0003-add-option-list-to-checkpoint-command.patch (6.0K, ../aFLmE2zl038I7-F1@nathan/4-v7-0003-add-option-list-to-checkpoint-command.patch)
  download | inline diff:
From 815634c9ade5aa89351beb876d05a261c3db7e92 Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Tue, 17 Jun 2025 15:36:29 -0500
Subject: [PATCH v7 3/5] add option list to checkpoint command

---
 doc/src/sgml/ref/checkpoint.sgml      | 12 +++++++++-
 src/backend/parser/gram.y             |  7 ++++++
 src/backend/postmaster/checkpointer.c | 33 +++++++++++++++++++++++++++
 src/backend/tcop/utility.c            | 12 +---------
 src/bin/psql/tab-complete.in.c        |  3 +++
 src/include/nodes/parsenodes.h        |  1 +
 src/include/postmaster/bgwriter.h     |  2 ++
 7 files changed, 58 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..1294b7c528e 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -21,7 +21,9 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CHECKPOINT
+CHECKPOINT [ ( option [, ...] ) ]
+
+<phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
 </synopsis>
  </refsynopsisdiv>
 
@@ -58,6 +60,14 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <para>
+   Coming soon...
+  </para>
+ </refsect1>
+
  <refsect1>
   <title>Compatibility</title>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 50f53159d58..5934810e805 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2034,6 +2034,13 @@ CheckPointStmt:
 
 					$$ = (Node *) n;
 				}
+			| CHECKPOINT '(' utility_option_list ')'
+				{
+					CheckPointStmt *n = makeNode(CheckPointStmt);
+
+					$$ = (Node *) n;
+					n->options = $3;
+				}
 		;
 
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 0d8696bfb5e..58d863847c5 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -42,6 +42,7 @@
 #include "access/xlog.h"
 #include "access/xlog_internal.h"
 #include "access/xlogrecovery.h"
+#include "catalog/pg_authid.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -61,6 +62,7 @@
 #include "storage/shmem.h"
 #include "storage/smgr.h"
 #include "storage/spin.h"
+#include "utils/acl.h"
 #include "utils/guc.h"
 #include "utils/memutils.h"
 #include "utils/resowner.h"
@@ -976,6 +978,37 @@ CheckpointerShmemInit(void)
 	}
 }
 
+/*
+ * ExecCheckpoint
+ *		Primary entry point for manual CHECKPOINT commands
+ *
+ * This is mainly a wrapper for RequestCheckpoint().
+ */
+void
+ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
+{
+	foreach_ptr(DefElem, opt, stmt->options)
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+				 parser_errposition(pstate, opt->location)));
+	}
+
+	if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
+		ereport(ERROR,
+				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+		/* translator: %s is name of an SQL command (e.g., CHECKPOINT) */
+				 errmsg("permission denied to execute %s command",
+						"CHECKPOINT"),
+				 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
+						   "pg_checkpoint")));
+
+	RequestCheckpoint(CHECKPOINT_WAIT |
+					  CHECKPOINT_FAST |
+					  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+}
+
 /*
  * RequestCheckpoint
  *		Called in backend processes to request a checkpoint
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index cda86ad44b0..2268709c5a7 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -943,17 +943,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 			break;
 
 		case T_CheckPointStmt:
-			if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
-				ereport(ERROR,
-						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				/* translator: %s is name of a SQL command, eg CHECKPOINT */
-						 errmsg("permission denied to execute %s command",
-								"CHECKPOINT"),
-						 errdetail("Only roles with privileges of the \"%s\" role may execute this command.",
-								   "pg_checkpoint")));
-
-			RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_WAIT |
-							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+			ExecCheckpoint(pstate, (CheckPointStmt *) parsetree);
 			break;
 
 			/*
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 2c0b4f28c14..f4b9ad3b26d 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3125,6 +3125,9 @@ match_previous_words(int pattern_id,
 		COMPLETE_WITH_VERSIONED_SCHEMA_QUERY(Query_for_list_of_procedures);
 	else if (Matches("CALL", MatchAny))
 		COMPLETE_WITH("(");
+/* CHECKPOINT */
+	else if (Matches("CHECKPOINT"))
+		COMPLETE_WITH("(");
 /* CLOSE */
 	else if (Matches("CLOSE"))
 		COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ba12678d1cb..c4d1e9c8006 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -4015,6 +4015,7 @@ typedef struct RefreshMatViewStmt
 typedef struct CheckPointStmt
 {
 	NodeTag		type;
+	List	   *options;		/* list of DefElem nodes */
 } CheckPointStmt;
 
 /* ----------------------
diff --git a/src/include/postmaster/bgwriter.h b/src/include/postmaster/bgwriter.h
index 800ecbfd13b..97001f4e7f6 100644
--- a/src/include/postmaster/bgwriter.h
+++ b/src/include/postmaster/bgwriter.h
@@ -15,6 +15,7 @@
 #ifndef _BGWRITER_H
 #define _BGWRITER_H
 
+#include "parser/parse_node.h"
 #include "storage/block.h"
 #include "storage/relfilelocator.h"
 #include "storage/smgr.h"
@@ -30,6 +31,7 @@ extern PGDLLIMPORT double CheckPointCompletionTarget;
 pg_noreturn extern void BackgroundWriterMain(const void *startup_data, size_t startup_data_len);
 pg_noreturn extern void CheckpointerMain(const void *startup_data, size_t startup_data_len);
 
+extern void ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt);
 extern void RequestCheckpoint(int flags);
 extern void CheckpointWriteDelay(int flags, double progress);
 
-- 
2.39.5 (Apple Git-154)

  [text/plain] v7-0004-add-mode-option-to-checkpoint-command.patch (6.8K, ../aFLmE2zl038I7-F1@nathan/5-v7-0004-add-mode-option-to-checkpoint-command.patch)
  download | inline diff:
From bafe6274481c008727db9412088208f811d0ba4d Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 18 Jun 2025 10:40:27 -0500
Subject: [PATCH v7 4/5] add mode option to checkpoint command

---
 doc/src/sgml/ref/checkpoint.sgml      | 35 ++++++++++++++++++++++++---
 src/backend/postmaster/checkpointer.c | 26 ++++++++++++++++----
 src/bin/psql/tab-complete.in.c        | 13 ++++++++++
 src/test/regress/expected/stats.out   |  5 +++-
 src/test/regress/sql/stats.sql        |  5 +++-
 5 files changed, 73 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 1294b7c528e..d66aa967afc 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -24,6 +24,8 @@ PostgreSQL documentation
 CHECKPOINT [ ( option [, ...] ) ]
 
 <phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
+
+    MODE { FAST | SPREAD }
 </synopsis>
  </refsynopsisdiv>
 
@@ -39,7 +41,8 @@ CHECKPOINT [ ( option [, ...] ) ]
   </para>
 
   <para>
-   The <command>CHECKPOINT</command> command forces a fast
+   When <literal>MODE</literal> is set to <literal>FAST</literal>, which is the
+   default, the <command>CHECKPOINT</command> command forces a fast
    checkpoint when the command is issued, without waiting for a
    regular checkpoint scheduled by the system (controlled by the settings in
    <xref linkend="runtime-config-wal-checkpoints"/>).
@@ -47,6 +50,14 @@ CHECKPOINT [ ( option [, ...] ) ]
    operation.
   </para>
 
+  <para>
+   The server may consolidate concurrently requested checkpoints.  Such
+   consolidated requests will contain a combined set of options.  For example,
+   if one session requests a fast checkpoint and another requests a spread
+   checkpoint, the server may combine those requests and perform one fast
+   checkpoint.
+  </para>
+
   <para>
    If executed during recovery, the <command>CHECKPOINT</command> command
    will force a restartpoint (see <xref linkend="wal-configuration"/>)
@@ -63,9 +74,25 @@ CHECKPOINT [ ( option [, ...] ) ]
  <refsect1>
   <title>Parameters</title>
 
-  <para>
-   Coming soon...
-  </para>
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      When set to <literal>FAST</literal>, which is the default, the requested
+      checkpoint will be completed as fast as possible, which may result in a
+      significantly higher rate of I/O during the checkpoint.
+     </para>
+     <para>
+      <literal>MODE</literal> can also be set to <literal>SPREAD</literal> to
+      request the checkpoint be spread over a longer interval (controlled via
+      the settings in <xref linkend="runtime-config-wal-checkpoints"/>), like a
+      regular checkpoint scheduled by the system.  This can reduce the rate of
+      I/O during the checkpoint.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
  </refsect1>
 
  <refsect1>
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 58d863847c5..723322e3e03 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -43,6 +43,7 @@
 #include "access/xlog_internal.h"
 #include "access/xlogrecovery.h"
 #include "catalog/pg_authid.h"
+#include "commands/defrem.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "pgstat.h"
@@ -987,12 +988,27 @@ CheckpointerShmemInit(void)
 void
 ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 {
+	bool		fast = true;
+
 	foreach_ptr(DefElem, opt, stmt->options)
 	{
-		ereport(ERROR,
-				(errcode(ERRCODE_SYNTAX_ERROR),
-				 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
-				 parser_errposition(pstate, opt->location)));
+		if (strcmp(opt->defname, "mode") == 0)
+		{
+			char	   *mode = defGetString(opt);
+
+			if (strcmp(mode, "spread") == 0)
+				fast = false;
+			else if (strcmp(mode, "fast") != 0)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("unrecognized MODE option \"%s\"", opt->defname),
+						 parser_errposition(pstate, opt->location)));
+		}
+		else
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+					 parser_errposition(pstate, opt->location)));
 	}
 
 	if (!has_privs_of_role(GetUserId(), ROLE_PG_CHECKPOINT))
@@ -1005,7 +1021,7 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 						   "pg_checkpoint")));
 
 	RequestCheckpoint(CHECKPOINT_WAIT |
-					  CHECKPOINT_FAST |
+					  (fast ? CHECKPOINT_FAST : 0) |
 					  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
 }
 
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index f4b9ad3b26d..177615aff27 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3128,6 +3128,19 @@ match_previous_words(int pattern_id,
 /* CHECKPOINT */
 	else if (Matches("CHECKPOINT"))
 		COMPLETE_WITH("(");
+	else if (HeadMatches("CHECKPOINT", "(*") &&
+			 !HeadMatches("CHECKPOINT", "(*)"))
+	{
+		/*
+		 * This fires if we're in an unfinished parenthesized option list.
+		 * get_previous_words treats a completed parenthesized option list as
+		 * one word, so the above test is correct.
+		 */
+		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
+			COMPLETE_WITH("MODE");
+		else if (TailMatches("MODE"))
+			COMPLETE_WITH("FAST", "SPREAD");
+	}
 /* CLOSE */
 	else if (Matches("CLOSE"))
 		COMPLETE_WITH_QUERY_PLUS(Query_for_list_of_cursors,
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 776f1ad0e53..02cdc3cbcf6 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -926,7 +926,10 @@ DROP TABLE test_stats_temp;
 -- Checkpoint twice: The checkpointer reports stats after reporting completion
 -- of the checkpoint. But after a second checkpoint we'll see at least the
 -- results of the first.
-CHECKPOINT;
+--
+-- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
+-- because it would prolong the test.
+CHECKPOINT (MODE FAST);
 CHECKPOINT;
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
  ?column? 
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 232ab8db8fa..5ed71d7054e 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -439,7 +439,10 @@ DROP TABLE test_stats_temp;
 -- Checkpoint twice: The checkpointer reports stats after reporting completion
 -- of the checkpoint. But after a second checkpoint we'll see at least the
 -- results of the first.
-CHECKPOINT;
+--
+-- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
+-- because it would prolong the test.
+CHECKPOINT (MODE FAST);
 CHECKPOINT;
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
-- 
2.39.5 (Apple Git-154)

  [text/plain] v7-0005-add-flush_unlogged-option-to-checkpoint-command.patch (4.8K, ../aFLmE2zl038I7-F1@nathan/6-v7-0005-add-flush_unlogged-option-to-checkpoint-command.patch)
  download | inline diff:
From 159dcdc121523d10aa6bf1030c35052a3a8c5e2b Mon Sep 17 00:00:00 2001
From: Nathan Bossart <nathan@postgresql.org>
Date: Wed, 18 Jun 2025 11:08:26 -0500
Subject: [PATCH v7 5/5] add flush_unlogged option to checkpoint command

---
 doc/src/sgml/ref/checkpoint.sgml      | 26 ++++++++++++++++++++++++++
 src/backend/postmaster/checkpointer.c |  4 ++++
 src/bin/psql/tab-complete.in.c        |  2 +-
 src/test/regress/expected/stats.out   |  2 +-
 src/test/regress/sql/stats.sql        |  2 +-
 5 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index d66aa967afc..8054de78a9b 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -25,6 +25,7 @@ CHECKPOINT [ ( option [, ...] ) ]
 
 <phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
 
+    FLUSH_UNLOGGED [ <replaceable class="parameter">boolean</replaceable> ]
     MODE { FAST | SPREAD }
 </synopsis>
  </refsynopsisdiv>
@@ -75,6 +76,17 @@ CHECKPOINT [ ( option [, ...] ) ]
   <title>Parameters</title>
 
   <variablelist>
+   <varlistentry>
+    <term><literal>FLUSH_UNLOGGED</literal></term>
+    <listitem>
+     <para>
+      Normally, <command>CHECKPOINT</command> does not flush data files for
+      unlogged relations.  This option, which is disabled by default, enables
+      flushing unlogged relations.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>MODE</literal></term>
     <listitem>
@@ -92,6 +104,20 @@ CHECKPOINT [ ( option [, ...] ) ]
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">boolean</replaceable></term>
+    <listitem>
+     <para>
+      Specifies whether the selected option should be turned on or off.
+      You can write <literal>TRUE</literal>, <literal>ON</literal>, or
+      <literal>1</literal> to enable the option, and <literal>FALSE</literal>,
+      <literal>OFF</literal>, or <literal>0</literal> to disable it.  The
+      <replaceable class="parameter">boolean</replaceable> value can also
+      be omitted, in which case <literal>TRUE</literal> is assumed.
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>
  </refsect1>
 
diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c
index 723322e3e03..a3e4e70ad66 100644
--- a/src/backend/postmaster/checkpointer.c
+++ b/src/backend/postmaster/checkpointer.c
@@ -989,6 +989,7 @@ void
 ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 {
 	bool		fast = true;
+	bool		unlogged = false;
 
 	foreach_ptr(DefElem, opt, stmt->options)
 	{
@@ -1004,6 +1005,8 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 						 errmsg("unrecognized MODE option \"%s\"", opt->defname),
 						 parser_errposition(pstate, opt->location)));
 		}
+		else if (strcmp(opt->defname, "flush_unlogged") == 0)
+			unlogged = defGetBoolean(opt);
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1022,6 +1025,7 @@ ExecCheckpoint(ParseState *pstate, CheckPointStmt *stmt)
 
 	RequestCheckpoint(CHECKPOINT_WAIT |
 					  (fast ? CHECKPOINT_FAST : 0) |
+					  (unlogged ? CHECKPOINT_FLUSH_UNLOGGED : 0) |
 					  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
 }
 
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 177615aff27..a3f46a205ec 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3137,7 +3137,7 @@ match_previous_words(int pattern_id,
 		 * one word, so the above test is correct.
 		 */
 		if (ends_with(prev_wd, '(') || ends_with(prev_wd, ','))
-			COMPLETE_WITH("MODE");
+			COMPLETE_WITH("MODE, FLUSH_UNLOGGED");
 		else if (TailMatches("MODE"))
 			COMPLETE_WITH("FAST", "SPREAD");
 	}
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 02cdc3cbcf6..6ebcc292834 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -930,7 +930,7 @@ DROP TABLE test_stats_temp;
 -- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
 -- because it would prolong the test.
 CHECKPOINT (MODE FAST);
-CHECKPOINT;
+CHECKPOINT (FLUSH_UNLOGGED);
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
  ?column? 
 ----------
diff --git a/src/test/regress/sql/stats.sql b/src/test/regress/sql/stats.sql
index 5ed71d7054e..dab19918056 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -443,7 +443,7 @@ DROP TABLE test_stats_temp;
 -- While at it, test checkpoint options.  Note that we don't test MODE SPREAD
 -- because it would prolong the test.
 CHECKPOINT (MODE FAST);
-CHECKPOINT;
+CHECKPOINT (FLUSH_UNLOGGED);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
-- 
2.39.5 (Apple Git-154)

view thread (34+ messages)  latest in thread

Message-ID: <aFLmE2zl038I7-F1@nathan>
Permalink:  ../aFLmE2zl038I7-F1@nathan/
Also on:    postgresql.org/message-id/aFLmE2zl038I7-F1@nathan

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: nathandbossart@gmail.com, myon@debian.org, laurenz.albe@cybertec.at, masao.fujii@oss.nttdata.com, andres@anarazel.de, pgsql-hackers@lists.postgresql.org
  Subject: Re: CHECKPOINT unlogged data
  In-Reply-To: <aFLmE2zl038I7-F1@nathan>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox