Re: Nathan Bossart
> IMO we should try to make the terminology consistent everywhere.  I'd
> suggest putting the renaming stuff in separate prerequisite patches for
> your new CHECKPOINT option.

Ack, done in v4.

I haven't implemented a WAIT option yet since I didn't want to decide
that without more votes in either direction.

Christoph
>From d3194009b9c27c5741695b8149d00ffc56ff7f8c Mon Sep 17 00:00:00 2001
From: Christoph Berg <m...@debian.org>
Date: Wed, 11 Jun 2025 16:32:23 +0200
Subject: [PATCH v4 1/2] Rename checkpoint options "immediate" and "flush-all"

There were two names in use for fast checkpoints, "immediate" and
"fast". While "immediate" was the prevalent spelling, one of the two
user-visible places was "pg_basebackup --checkpoint=fast" using the
other spelling. Moreover, the "immediate" naming clashed with shutdowns
where a "fast" shutdown used an "immediate" checkpoint and an
"immediate" shutdown doesn't write a checkpoint at all.

Rename "immediate" checkpoints to "fast" throughout the code. The
user-visible change here is that checkpoint log records will now also
say that a "fast" checkpoint is starting.

The CHECKPOINT_FLUSH_ALL flag was really all about also flushing
UNLOGGED buffers, so rename it to reflect that. Likewise, the checkpoint
start log message now says "flush-unlogged" instead of "flush-all".
---
 doc/src/sgml/backup.sgml                      |  2 +-
 doc/src/sgml/func.sgml                        |  2 +-
 doc/src/sgml/ref/checkpoint.sgml              |  2 +-
 src/backend/access/transam/xlog.c             | 24 +++++++++----------
 src/backend/commands/dbcommands.c             | 14 +++++------
 src/backend/commands/tablespace.c             |  2 +-
 src/backend/postmaster/checkpointer.c         | 22 ++++++++---------
 src/backend/storage/buffer/bufmgr.c           |  6 ++---
 src/backend/tcop/utility.c                    |  2 +-
 src/include/access/xlog.h                     |  6 ++---
 .../recovery/t/041_checkpoint_at_promote.pl   |  2 +-
 11 files changed, 42 insertions(+), 42 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/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 1914859b2ee..2a9ae202dd9 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,24 +6842,24 @@ 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" : "",
 						(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 */
 				(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" : "",
 						(flags & CHECKPOINT_CAUSE_TIME) ? " time" : "",
-						(flags & CHECKPOINT_FLUSH_ALL) ? " flush-all" : "")));
+						(flags & CHECKPOINT_FLUSH_UNLOGGED) ? " flush-unlogged" : "")));
 }
 
 /*
@@ -7042,12 +7042,12 @@ 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,
+ *	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_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
@@ -8943,7 +8943,7 @@ 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"
+ * the checkpoint in fast mode), and "tablespaces"
  * (if non-NULL, indicates a list of tablespaceinfo structs describing the
  * cluster's tablespaces.).
  *
@@ -9073,11 +9073,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
+			 * 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 c95eb945016..502a45163c8 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -570,8 +570,8 @@ CreateDatabaseUsingFileCopy(Oid src_dboid, Oid dst_dboid, Oid src_tsid,
 	 * any CREATE DATABASE commands.
 	 */
 	if (!IsBinaryUpgrade)
-		RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE |
-						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_ALL);
+		RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE |
+						  CHECKPOINT_WAIT | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/*
 	 * Iterate through all tablespaces of the template database, and copy each
@@ -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,8 +2120,8 @@ 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
-					  | CHECKPOINT_FLUSH_ALL);
+	RequestCheckpoint(CHECKPOINT_FAST | CHECKPOINT_FORCE | CHECKPOINT_WAIT
+					  | CHECKPOINT_FLUSH_UNLOGGED);
 
 	/* Close all smgr fds in all backends. */
 	WaitForProcSignalBarrier(EmitProcSignalBarrier(PROCSIGNAL_BARRIER_SMGRRELEASE));
@@ -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..aa65a86d52d 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
+ * 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 667aa0c0c78..c33bbfd27b6 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -3339,9 +3339,9 @@ 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
+ * 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_ALL is set, we write even
+ * 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.
  */
@@ -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/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 d313099c027..82bdf34a911 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -139,10 +139,10 @@ 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_ALL	0x0010	/* Flush all pages, including those
-										 * belonging to unlogged tables */
+#define CHECKPOINT_FLUSH_UNLOGGED	0x0010	/* Flush all pages, including those
+											 * belonging to unlogged tables */
 /* These are important to RequestCheckpoint */
 #define CHECKPOINT_WAIT			0x0020	/* Wait for completion */
 #define CHECKPOINT_REQUESTED	0x0040	/* Checkpoint request has been made */
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.47.2

>From 71e4253f2c383d92639fdbeb6983c9343825e8c6 Mon Sep 17 00:00:00 2001
From: Christoph Berg <m...@debian.org>
Date: Fri, 30 May 2025 17:58:35 +0200
Subject: [PATCH v4 2/2] Add mode and flush_unlogged options to CHECKPOINT

Field reports indicate that some users are running CHECKPOINT just
before shutting down to reduce the amount of data that the shutdown
checkpoint has to write out, making restarts faster.

That works well unless big unlogged tables are in play; a regular
CHECKPOINT does not flush these. Hence, add a CHECKPOINT option to force
flushing of all relations. To control the write load during these
checkpoints, add an MODE option to choose between FAST and SPREAD.
---
 doc/src/sgml/ref/checkpoint.sgml    | 81 +++++++++++++++++++++++++----
 src/backend/parser/gram.y           |  8 +++
 src/backend/tcop/utility.c          | 60 +++++++++++++++++----
 src/bin/psql/tab-complete.in.c      |  7 +++
 src/include/nodes/parsenodes.h      |  1 +
 src/test/regress/expected/stats.out |  6 +--
 src/test/regress/sql/stats.sql      |  6 +--
 7 files changed, 141 insertions(+), 28 deletions(-)

diff --git a/doc/src/sgml/ref/checkpoint.sgml b/doc/src/sgml/ref/checkpoint.sgml
index 10a433e4757..66277a97f36 100644
--- a/doc/src/sgml/ref/checkpoint.sgml
+++ b/doc/src/sgml/ref/checkpoint.sgml
@@ -21,7 +21,12 @@ PostgreSQL documentation
 
  <refsynopsisdiv>
 <synopsis>
-CHECKPOINT
+CHECKPOINT [ ( option [, ...] ) ]
+
+<phrase>where <replaceable class="parameter">option</replaceable> can be one of:</phrase>
+
+    MODE { FAST | SPREAD }
+    FLUSH_UNLOGGED [ <replaceable class="parameter">boolean</replaceable> ]
 </synopsis>
  </refsynopsisdiv>
 
@@ -31,24 +36,30 @@ CHECKPOINT
   <para>
    A checkpoint is a point in the write-ahead log sequence at which
    all data files have been updated to reflect the information in the
-   log.  All data files will be flushed to disk.  Refer to
-   <xref linkend="wal-configuration"/> for more details about what happens
+   log.  Should the system crash, recovery will start at the last checkpoint.
+   Refer to <xref linkend="wal-configuration"/> for more details about what happens
    during a checkpoint.
   </para>
 
   <para>
-   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"/>).
-   <command>CHECKPOINT</command> is not intended for use during normal
-   operation.
+   Running <command>CHECKPOINT</command> is not required during normal
+   operation; the system schedules checkpoints automatically (controlled by
+   the settings in <xref linkend="runtime-config-wal-checkpoints"/>).
+   <command>CHECKPOINT</command> can be beneficial to run before operations
+   such as binary backups or shutting down <productname>PostgreSQL</productname>
+   as this can reduce the amount of data to be flushed for the next checkpoint
+   within these operations. Specifically, <literal>UNLOGGED</literal> table
+   data is otherwise only flushed by the shutdown checkpoint.
   </para>
 
   <para>
    If executed during recovery, the <command>CHECKPOINT</command> command
-   will force a restartpoint (see <xref linkend="wal-configuration"/>)
-   rather than writing a new checkpoint.
+   will force a restartpoint rather than writing a new checkpoint.  (The
+   operation will be a no-op if there is no corresponding checkpoint in the
+   write-ahead log.)  If a checkpoint is already being written when a
+   <command>CHECKPOINT</command> is issued, the running checkpoint is upgraded
+   with this command's <literal>MODE</literal> and
+   <literal>FLUSH_UNLOGGED</literal> options.
   </para>
 
   <para>
@@ -58,6 +69,54 @@ CHECKPOINT
   </para>
  </refsect1>
 
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><literal>MODE</literal></term>
+    <listitem>
+     <para>
+      The <command>CHECKPOINT</command> command forces a fast checkpoint by
+      default when the command is issued, without waiting for a regular
+      checkpoint scheduled by the system.
+     </para>
+     <para>
+      A <literal>SPREAD</literal> checkpoint will instead spread out the write
+      load as determined by the
+      <xref linkend="guc-checkpoint-completion-target"/> setting, like the
+      system-scheduled checkpoints.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>FLUSH_UNLOGGED</literal></term>
+    <listitem>
+     <para>
+      All data files except for those marked as <literal>UNLOGGED</literal>
+      are flushed to disk during a checkpoint.  Enabling this option will also
+      flush <literal>UNLOGGED</literal> relations; the default is off.
+     </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>
+
  <refsect1>
   <title>Compatibility</title>
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0b5652071d1..731d844231a 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -2034,6 +2034,14 @@ CheckPointStmt:
 					CheckPointStmt *n = makeNode(CheckPointStmt);
 
 					$$ = (Node *) n;
+					n->options = NULL;
+				}
+			| CHECKPOINT '(' utility_option_list ')'
+				{
+					CheckPointStmt *n = makeNode(CheckPointStmt);
+
+					$$ = (Node *) n;
+					n->options = $3;
 				}
 		;
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index cda86ad44b0..73184d6bf88 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -943,17 +943,55 @@ 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));
+			{
+				CheckPointStmt   *stmt = (CheckPointStmt *) parsetree;
+				ListCell   *lc;
+				bool		fast = true;
+				bool		flush_unlogged = false;
+
+				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")));
+
+				/* Parse options list */
+				foreach(lc, stmt->options)
+				{
+					DefElem    *opt = (DefElem *) lfirst(lc);
+
+					if (strcmp(opt->defname, "mode") == 0)
+					{
+						char   *mode = defGetString(opt);
+						if (strcmp(mode, "fast") == 0)
+							fast = true;
+						else if (strcmp(mode, "spread") == 0)
+							fast = false;
+						else
+							ereport(ERROR,
+									(errcode(ERRCODE_SYNTAX_ERROR),
+									 errmsg("CHECKPOINT option \"%s\" argument \"%s\" is invalid", opt->defname, mode),
+									 errhint("valid arguments are \"FAST\" and \"SPREAD\""),
+									 parser_errposition(pstate, opt->location)));
+					}
+					else if (strcmp(opt->defname, "flush_unlogged") == 0)
+						flush_unlogged = defGetBoolean(opt);
+					else
+						ereport(ERROR,
+								(errcode(ERRCODE_SYNTAX_ERROR),
+								 errmsg("unrecognized CHECKPOINT option \"%s\"", opt->defname),
+								 errhint("valid options are \"MODE\" and \"FLUSH_UNLOGGED\""),
+								 parser_errposition(pstate, opt->location)));
+				}
+
+				RequestCheckpoint(CHECKPOINT_WAIT |
+								  (fast ? CHECKPOINT_FAST : 0) |
+								  (flush_unlogged ? CHECKPOINT_FLUSH_UNLOGGED : 0) |
+								  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
+			}
 			break;
 
 			/*
diff --git a/src/bin/psql/tab-complete.in.c b/src/bin/psql/tab-complete.in.c
index 620830feb9d..eea09b62615 100644
--- a/src/bin/psql/tab-complete.in.c
+++ b/src/bin/psql/tab-complete.in.c
@@ -3125,6 +3125,13 @@ 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("(");
+	else if (Matches("CHECKPOINT", "("))
+		COMPLETE_WITH("MODE", "FLUSH_UNLOGGED");
+	else if (Matches("CHECKPOINT", "(", "MODE"))
+		COMPLETE_WITH("FAST", "SPREAD");
 /* 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 dd00ab420b8..3553b49e53c 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;
 } CheckPointStmt;
 
 /* ----------------------
diff --git a/src/test/regress/expected/stats.out b/src/test/regress/expected/stats.out
index 776f1ad0e53..e1203b78ff5 100644
--- a/src/test/regress/expected/stats.out
+++ b/src/test/regress/expected/stats.out
@@ -925,9 +925,9 @@ CREATE TEMP TABLE test_stats_temp AS SELECT 17;
 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;
-CHECKPOINT;
+-- results of the first. And while at it, test checkpoint options.
+CHECKPOINT (mode fast);
+CHECKPOINT (mode spread, 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 232ab8db8fa..79c0e51c19c 100644
--- a/src/test/regress/sql/stats.sql
+++ b/src/test/regress/sql/stats.sql
@@ -438,9 +438,9 @@ 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;
-CHECKPOINT;
+-- results of the first. And while at it, test checkpoint options.
+CHECKPOINT (mode fast);
+CHECKPOINT (mode spread, flush_unlogged);
 
 SELECT num_requested > :rqst_ckpts_before FROM pg_stat_checkpointer;
 SELECT wal_bytes > :wal_bytes_before FROM pg_stat_wal;
-- 
2.47.2

Reply via email to